TFTPy

TFTPy is a pure python TFTP implementation.

Requirements

Python 2.3+, I think. I haven’t tested in Python 2.3 in a while but it should still work. Let me know if it doesn’t.

Installation

If you download the source distribution, you can simply use distutils to install, via:

python setup.py build
python setup.py install

Or, as this has been uploaded to pypi, you can use easy_install or pip:

easy_install tftpy
pip install tftpy

Once installed you should have the sample client and server scripts in bin, and you should be able to import the tftpy module.

Examples

The simplest tftp client:

import tftpy

client = tftpy.TftpClient('tftp.digitaltorque.ca', 69)
client.download('remote_filename', 'local_filename')

The simplest tftp server:

import tftpy

server = tftpy.TftpServer('/tftpboot')
server.listen('0.0.0.0', 69)

See the sample client and server for slightly more complex examples.

API Documentation

Front-end Modules

These modules are the ones that you will need to use directly to implement a TFTP client or server.

The tftpy Module

This library implements the tftp protocol, based on rfc 1350. http://www.faqs.org/rfcs/rfc1350.html At the moment it implements only a client class, but will include a server, with support for variable block sizes.

As a client of tftpy, this is the only module that you should need to import directly. The TftpClient and TftpServer classes can be reached through it.

The TftpClient Module

This module implements the TFTP Client functionality. Instantiate an instance of the client, and then use its upload or download method. Logging is performed via a standard logging object set in TftpShared.

class tftpy.TftpClient.TftpClient(host, port, options={})

Bases: tftpy.TftpPacketTypes.TftpSession

This class is an implementation of a tftp client. Once instantiated, a download can be initiated via the download() method, or an upload via the upload() method.

download(filename, output, packethook=None, timeout=5)

This method initiates a tftp download from the configured remote host, requesting the filename passed. It writes the file to output, which can be a file-like object or a path to a local file. If a packethook is provided, it must be a function that takes a single parameter, which will be a copy of each DAT packet received in the form of a TftpPacketDAT object. The timeout parameter may be used to override the default SOCK_TIMEOUT setting, which is the amount of time that the client will wait for a receive packet to arrive.

Note: If output is a hyphen, stdout is used.

upload(filename, input, packethook=None, timeout=5)

This method initiates a tftp upload to the configured remote host, uploading the filename passed. It reads the file from input, which can be a file-like object or a path to a local file. If a packethook is provided, it must be a function that takes a single parameter, which will be a copy of each DAT packet sent in the form of a TftpPacketDAT object. The timeout parameter may be used to override the default SOCK_TIMEOUT setting, which is the amount of time that the client will wait for a DAT packet to be ACKd by the server.

Note: If input is a hyphen, stdin is used.

The TftpServer Module

This module implements the TFTP Server functionality. Instantiate an instance of the server, and then run the listen() method to listen for client requests. Logging is performed via a standard logging object set in TftpShared.

class tftpy.TftpServer.TftpServer(tftproot='/tftpboot', dyn_file_func=None)

Bases: tftpy.TftpPacketTypes.TftpSession

This class implements a tftp server object. Run the listen() method to listen for client requests. It takes two optional arguments. tftproot is the path to the tftproot directory to serve files from and/or write them to. dyn_file_func is a callable that must return a file-like object to read from during downloads. This permits the serving of dynamic content.

listen(listenip='', listenport=69, timeout=5)

Start a server listening on the supplied interface and port. This defaults to INADDR_ANY (all interfaces) and UDP port 69. You can also supply a different socket timeout value, if desired.

stop(now=False)

Stop the server gracefully. Do not take any new transfers, but complete the existing ones. If force is True, drop everything and stop. Note, immediately will not interrupt the select loop, it will happen when the server returns on ready data, or a timeout. ie. SOCK_TIMEOUT

Back-end Modules

The TftpPacketFactory Module

This module implements the TftpPacketFactory class, which can take a binary buffer, and return the appropriate TftpPacket object to represent it, via the parse() method.

class tftpy.TftpPacketFactory.TftpPacketFactory

Bases: object

This class generates TftpPacket objects. It is responsible for parsing raw buffers off of the wire and returning objects representing them, via the parse() method.

parse(buffer)

This method is used to parse an existing datagram into its corresponding TftpPacket object. The buffer is the raw bytes off of the network.

The TftpPacketTypes Module

This module implements the packet types of TFTP itself, and the corresponding encode and decode methods for them.

class tftpy.TftpPacketTypes.TftpPacket

Bases: object

This class is the parent class of all tftp packet classes. It is an abstract class, providing an interface, and should not be instantiated directly.

decode()

The decode method of a TftpPacket takes a buffer off of the wire in network-byte order, and decodes it, populating internal properties as appropriate. This can only be done once the first 2-byte opcode has already been decoded, but the data section does include the entire datagram.

This is an abstract method.

encode()

The encode method of a TftpPacket takes keyword arguments specific to the type of packet, and packs an appropriate buffer in network-byte order suitable for sending over the wire.

This is an abstract method.

class tftpy.TftpPacketTypes.TftpPacketACK

Bases: tftpy.TftpPacketTypes.TftpPacket

        2 bytes    2 bytes
        -------------------
ACK   | 04    |   Block #  |
        --------------------
class tftpy.TftpPacketTypes.TftpPacketDAT

Bases: tftpy.TftpPacketTypes.TftpPacket

        2 bytes    2 bytes       n bytes
        ---------------------------------
DATA  | 03    |   Block #  |    Data    |
        ---------------------------------
decode()

Decode self.buffer into instance variables. It returns self for easy method chaining.

encode()

Encode the DAT packet. This method populates self.buffer, and returns self for easy method chaining.

class tftpy.TftpPacketTypes.TftpPacketERR

Bases: tftpy.TftpPacketTypes.TftpPacket

        2 bytes  2 bytes        string    1 byte
        ----------------------------------------
ERROR | 05    |  ErrorCode |   ErrMsg   |   0  |
        ----------------------------------------

Error Codes

Value     Meaning

0         Not defined, see error message (if any).
1         File not found.
2         Access violation.
3         Disk full or allocation exceeded.
4         Illegal TFTP operation.
5         Unknown transfer ID.
6         File already exists.
7         No such user.
8         Failed to negotiate options
decode()

Decode self.buffer, populating instance variables and return self.

encode()

Encode the DAT packet based on instance variables, populating self.buffer, returning self.

class tftpy.TftpPacketTypes.TftpPacketInitial

Bases: tftpy.TftpPacketTypes.TftpPacket, tftpy.TftpPacketTypes.TftpPacketWithOptions

This class is a common parent class for the RRQ and WRQ packets, as they share quite a bit of code.

encode()

Encode the packet’s buffer from the instance variables.

class tftpy.TftpPacketTypes.TftpPacketOACK

Bases: tftpy.TftpPacketTypes.TftpPacket, tftpy.TftpPacketTypes.TftpPacketWithOptions

+-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
|  opc  |  opt1  | 0 | value1 | 0 |  optN  | 0 | valueN | 0 |
+-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
match_options(options)

This method takes a set of options, and tries to match them with its own. It can accept some changes in those options from the server as part of a negotiation. Changed or unchanged, it will return a dict of the options so that the session can update itself to the negotiated options.

class tftpy.TftpPacketTypes.TftpPacketRRQ

Bases: tftpy.TftpPacketTypes.TftpPacketInitial

        2 bytes    string   1 byte     string   1 byte
        -----------------------------------------------
RRQ/  | 01/02 |  Filename  |   0  |    Mode    |   0  |
WRQ     -----------------------------------------------
class tftpy.TftpPacketTypes.TftpPacketWRQ

Bases: tftpy.TftpPacketTypes.TftpPacketInitial

        2 bytes    string   1 byte     string   1 byte
        -----------------------------------------------
RRQ/  | 01/02 |  Filename  |   0  |    Mode    |   0  |
WRQ     -----------------------------------------------
class tftpy.TftpPacketTypes.TftpPacketWithOptions

Bases: object

This class exists to permit some TftpPacket subclasses to share code regarding options handling. It does not inherit from TftpPacket, as the goal is just to share code here, and not cause diamond inheritance.

decode_options(buffer)

This method decodes the section of the buffer that contains an unknown number of options. It returns a dictionary of option names and values.

class tftpy.TftpPacketTypes.TftpSession

Bases: object

This class is the base class for the tftp client and server. Any shared code should be in this class.

The TftpShared Module

This module holds all objects shared by all other modules in tftpy.

class tftpy.TftpShared.TftpErrors

Bases: object

This class is a convenience for defining the common tftp error codes, and making them more readable in the code.

exception tftpy.TftpShared.TftpException

Bases: exceptions.Exception

This class is the parent class of all exceptions regarding the handling of the TFTP protocol.

exception tftpy.TftpShared.TftpTimeout

Bases: tftpy.TftpShared.TftpException

This class represents a timeout error waiting for a response from the other end.

tftpy.TftpShared.setLogLevel(level)

This function is a utility function for setting the internal log level. The log level defaults to logging.NOTSET, so unwanted output to stdout is not created.

tftpy.TftpShared.tftpassert(condition, msg)

This function is a simple utility that will check the condition passed for a false state. If it finds one, it throws a TftpException with the message passed. This just makes the code throughout cleaner by refactoring.

The TftpContexts Module

This module implements all contexts for state handling during uploads and downloads, the main interface to which being the TftpContext base class.

The concept is simple. Each context object represents a single upload or download, and the state object in the context object represents the current state of that transfer. The state object has a handle() method that expects the next packet in the transfer, and returns a state object until the transfer is complete, at which point it returns None. That is, unless there is a fatal error, in which case a TftpException is returned instead.

class tftpy.TftpContexts.TftpContext(host, port, timeout)

Bases: object

The base class of the contexts.

checkTimeout(now)

Compare current time with last_update time, and raise an exception if we’re over the timeout time.

cycle()

Here we wait for a response from the server after sending it something, and dispatch appropriate action to that response.

end()

Perform session cleanup, since the end method should always be called explicitely by the calling code, this works better than the destructor.

getBlocksize()

Fetch the current blocksize for this session.

gethost()

Simple getter method for use in a property.

host

Simple getter method for use in a property.

sethost(host)

Setter method that also sets the address property as a result of the host that is set.

class tftpy.TftpContexts.TftpContextClientDownload(host, port, filename, output, options, packethook, timeout)

Bases: tftpy.TftpContexts.TftpContext

The download context for the client during a download. Note: If output is a hyphen, then the output will be sent to stdout.

end()

Finish up the context.

start()

Initiate the download.

class tftpy.TftpContexts.TftpContextClientUpload(host, port, filename, input, options, packethook, timeout)

Bases: tftpy.TftpContexts.TftpContext

The upload context for the client during an upload. Note: If input is a hyphen, then we will use stdin.

end()

Finish up the context.

class tftpy.TftpContexts.TftpContextServer(host, port, timeout, root, dyn_file_func=None)

Bases: tftpy.TftpContexts.TftpContext

The context for the server.

end()

Finish up the context.

start(buffer)

Start the state cycle. Note that the server context receives an initial packet in its start method. Also note that the server does not loop on cycle(), as it expects the TftpServer object to manage that.

class tftpy.TftpContexts.TftpMetrics

Bases: object

A class representing metrics of the transfer.

add_dup(pkt)

This method adds a dup for a packet to the metrics.

The TftpStates Module

This module implements all state handling during uploads and downloads, the main interface to which being the TftpState base class.

The concept is simple. Each context object represents a single upload or download, and the state object in the context object represents the current state of that transfer. The state object has a handle() method that expects the next packet in the transfer, and returns a state object until the transfer is complete, at which point it returns None. That is, unless there is a fatal error, in which case a TftpException is returned instead.

class tftpy.TftpStates.TftpServerState(context)

Bases: tftpy.TftpStates.TftpState

The base class for server states.

serverInitial(pkt, raddress, rport)

This method performs initial setup for a server context transfer, put here to refactor code out of the TftpStateServerRecvRRQ and TftpStateServerRecvWRQ classes, since their initial setup is identical. The method returns a boolean, sendoack, to indicate whether it is required to send an OACK to the client.

class tftpy.TftpStates.TftpState(context)

Bases: object

The base class for the states.

handle(pkt, raddress, rport)

An abstract method for handling a packet. It is expected to return a TftpState object, either itself or a new state.

handleDat(pkt)

This method handles a DAT packet during a client download, or a server upload.

handleOACK(pkt)

This method handles an OACK from the server, syncing any accepted options.

resendLast()

Resend the last sent packet due to a timeout.

returnSupportedOptions(options)

This method takes a requested options list from a client, and returns the ones that are supported.

sendACK(blocknumber=None)

This method sends an ack packet to the block number specified. If none is specified, it defaults to the next_block property in the parent context.

sendDAT()

This method sends the next DAT packet based on the data in the context. It returns a boolean indicating whether the transfer is finished.

sendError(errorcode)

This method uses the socket passed, and uses the errorcode to compose and send an error packet.

sendOACK()

This method sends an OACK packet with the options from the current context.

class tftpy.TftpStates.TftpStateExpectACK(context)

Bases: tftpy.TftpStates.TftpState

This class represents the state of the transfer when a DAT was just sent, and we are waiting for an ACK from the server. This class is the same one used by the client during the upload, and the server during the download.

handle(pkt, raddress, rport)

Handle a packet, hopefully an ACK since we just sent a DAT.

class tftpy.TftpStates.TftpStateExpectDAT(context)

Bases: tftpy.TftpStates.TftpState

Just sent an ACK packet. Waiting for DAT.

handle(pkt, raddress, rport)

Handle the packet in response to an ACK, which should be a DAT.

class tftpy.TftpStates.TftpStateSentRRQ(context)

Bases: tftpy.TftpStates.TftpState

Just sent an RRQ packet.

handle(pkt, raddress, rport)

Handle the packet in response to an RRQ to the server.

class tftpy.TftpStates.TftpStateSentWRQ(context)

Bases: tftpy.TftpStates.TftpState

Just sent an WRQ packet for an upload.

handle(pkt, raddress, rport)

Handle a packet we just received.

class tftpy.TftpStates.TftpStateServerRecvRRQ(context)

Bases: tftpy.TftpStates.TftpServerState

This class represents the state of the TFTP server when it has just received an RRQ packet.

handle(pkt, raddress, rport)

Handle an initial RRQ packet as a server.

class tftpy.TftpStates.TftpStateServerRecvWRQ(context)

Bases: tftpy.TftpStates.TftpServerState

This class represents the state of the TFTP server when it has just received a WRQ packet.

handle(pkt, raddress, rport)

Handle an initial WRQ packet as a server.

make_subdirs()

The purpose of this method is to, if necessary, create all of the subdirectories leading up to the file to the written.

class tftpy.TftpStates.TftpStateServerStart(context)

Bases: tftpy.TftpStates.TftpState

The start state for the server. This is a transitory state since at this point we don’t know if we’re handling an upload or a download. We will commit to one of them once we interpret the initial packet.

handle(pkt, raddress, rport)

Handle a packet we just received.