vinegar.tftp.server

TFTP server component of Vinegar.

exception vinegar.tftp.server.TftpError(message: str = '', error_code: ErrorCode = ErrorCode.NOT_DEFINED)

Exception raised by TftpRequestHandler.handle to indicate that it cannot proceed with processing a request.

This exception can be constructed with an optional message and an optional error_code that must be an instance of ErrorCode.

class vinegar.tftp.server.TftpRequestHandler

Interface for a request handler. A request handler should be derived from this class and implement the can_handle and handle methods.

The can_handle and handle methods are separate, so that the TFTP server only has to create a new socket and thread when it can actually handle the request.

A request handler can also implement prepare_context. In this case, prepare_context is called before calling can_handle and the object returned by it is passed to can_handle and handle. This is useful when both function need to do some processing on the filename or client address. This processing can be implemented in prepare_context and passed to the two other methods through the context so that it does not have to be done twice.

abstract can_handle(filename: str, context: Any) bool

Tell whether the request can be handled by this request handler.

Returns True if the request can be handled and False if it cannot be handled and the next request handler should be tried.

Parameters:
  • filename – filename that has been requested by the client.

  • context – context object that was returned by prepare_context.

Returns:

True if this request handler can handle the specified request, False if the request should be deferred to the next handler.

abstract handle(filename: str, client_address: Tuple[str, int] | Tuple[str, int, int, int], server_address: Tuple[str, int] | Tuple[str, int, int, int], context: Any) BufferedIOBase

Handle the request. This method returns a file-like object from which the data for the requested file can be read. The returned file-like object must supply its data in binary form.

If the request handler detects that it actually cannot send data to the client (e.g. because the client lacks the required permissions), it should signal that by raising a TftpError.

Parameters:
  • filename – filename that has been requested by the client.

  • client_address – client address. The structure of the tuple depends on the address family in use, but typically the first element is the client’s host address and the second element is the client’s port number.

  • server_address – server address. The structure of the tuple depends on the address family in use, but typically the first element is the server’s host address and the second element is the server’s port number. Please note that determining the actual address specified by the client might not be possible if the sever binds to all interfaces (::) and the platform does not support the IPV6_PKTINFO socket option. In this case, this will be the address to which the server is bound and not the address on which the request was actually received.

  • context – context object that was returned by prepare_context.

Returns:

file-like object that provides the data that is transferred to the client. The file-like object must provide binary data.

prepare_context(filename: str) Any

Prepare a context object for use by can_handle and handle. This method is called for each request before calling can_handle.

This is useful when both function need to do some processing on the filename or client address. This processing can be implemented in prepare_context and passed to the two other methods through the context so that it does not have to be done twice.

The return value of this method is passed to can_handle and handle. The default implementation simply returns None.

Parameters:

filename – filename that has been requested by the client.

Returns:

context object that is passed to can_handle and handle.

class vinegar.tftp.server.TftpServer(request_handlers: List[TftpRequestHandler], bind_address: str = '::', bind_port: int = 69, default_timeout: float = 10.0, max_timeout: float = 30.0, max_retries: int = 3, max_block_size: int = 65464, block_counter_wrap_value: int = 0)

Server implementing the TFTP (RFC 1350) protocol. This server can serve arbitrary resources (read-only), not just files on the file system.

This implementation supports the TFTP blocksize option (RFC 2348), the TFTP timeout interval option (RFC 2349), and the TFTP transfer size option. Support for the transfer size option is limited to binary transfers and request handlers that provide a file object for which we can actually determine the size.

The server internally uses a daemon thread that processes incoming requests. For each request, it creates a new thread that processes this request and sends the requested data to the client.

start()

Starts this server instance. This opens the server socket, binds it, and creates a deamon thread that processes requests.

If the server is already running, this method does nothing.

stop()

Stops this server instance.

This closes the server socket and stops the daemon thread that has been created. Please note that this will not close the sockets or shutdown the threads that have been created for requests. Each of these threads will shutdown when its associated request is fully processed or its timeout is reached.

vinegar.tftp.server.create_tftp_server(request_handlers: List[TftpRequestHandler], bind_address: str = '::', bind_port: int = 69, default_timeout: float = 10.0, max_timeout: float = 30.0, max_retries: int = 3, max_block_size: int = 65464, block_counter_wrap_value: int = 0)

Create a new TFTP server. The server is not started and its socket is not opened or bound when constructing the server object. Instead, start() must be called to start the server.

Parameters:
  • request_handlers – List of request handlers than can handle read requests for this server. The request handlers are tried in order. The first request handler that can handle a request is used. If no request handler that can handle the request is found, an error is signaled to the client.

  • bind_address – Address of the interface on which the TFTP server shall listen for incoming connections. By default, the server listens on all local interfaces.

  • bind_port – Number of the UDP port on which the TFTP server shall listen for incoming connections. By default, the server listens on UDP port 69, this is the officially registered port for TFTP.

  • default_timeout – Timeout (in seconds) that is used for connections if the client does not specify a timeout. This number must be greater than or equal to 1 and less than max_timeout. If it it is outside this range, it is silently changed to be within this range. The default is 10.

  • max_timeout – Max. timeout interval (in seconds) that may be specified by a client. If a client requests a timeout that is greater, the timeout is limited to this number. This number must be greater than or equal to 1 and less than or equal to 255. The default is 30.

  • max_retries – Max. number of attempts to resend a packet before giving up. This limit is important because it keeps a connection (and the associated thread) from stalling forever when the connection to a client is lost. This number must be greater than or equal to 1. The default value is 3.

  • max_block_size – Max. size of a single block (in bytes). This setting is only used when a client requests a different than the default block size (512 bytes). In that case, if the client requests a block size that is greater than this setting, the block size is reduces to this setting. This can be useful when a client requests a block size that would result in IP fragmentation, but IP fragmentation is not desired. This setting must be a number between 512 (the default block size) and 65464 (the max. block size allowed by the protocol). The default value is 65464.

  • block_counter_wrap_value – Block at which to start counting again after reaching the max. possible block count. This value should be 0 or 1. As the TFTP standard (RFC 1350) does not specify what should happen if the block count range is exceeded, some clients expect it to wrap around to 0 while other expect it to wrap around to 1. If this parameter is set to None, the block counter will never wrap which means that large files cannot be transferred. This is only necessary if dealing with clients that show unexpected behavior when the block counter wraps. In the context of PXE boot, most clients seem to expect 0, so that is what we use by default.

Returns:

server object that is ready to be started.