diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7ba1969 --- /dev/null +++ b/LICENSE @@ -0,0 +1,61 @@ +http://www.opensource.org/licenses/pythonpl.php + +IMPORTANT: PLEASE READ THE FOLLOWING AGREEMENT CAREFULLY. + +BY CLICKING ON "ACCEPT" WHERE INDICATED BELOW, OR BY COPYING, INSTALLING OR +OTHERWISE USING PYTHON 1.6, beta 1 SOFTWARE, YOU ARE DEEMED TO HAVE AGREED TO +THE TERMS AND CONDITIONS OF THIS LICENSE AGREEMENT. + +1. This LICENSE AGREEMENT is between the Corporation for National Research +Initiatives, having an office at 1895 Preston White Drive, Reston, VA 20191 +("CNRI"), and the Individual or Organization ("Licensee") accessing and +otherwise using Python 1.6, beta 1 software in source or binary form and its +associated documentation, as released at the www.python.org Internet site on +August 4, 2000 ("Python 1.6b1"). + +2. Subject to the terms and conditions of this License Agreement, CNRI hereby +grants Licensee a non-exclusive, royalty-free, world-wide license to +reproduce, analyze, test, perform and/or display publicly, prepare derivative +works, distribute, and otherwise use Python 1.6b1 alone or in any derivative +version, provided, however, that CNRIs License Agreement is retained in Python +1.6b1, alone or in any derivative version prepared by Licensee. + +Alternately, in lieu of CNRIs License Agreement, Licensee may substitute the +following text (omitting the quotes): "Python 1.6, beta 1, is made available +subject to the terms and conditions in CNRIs License Agreement. This Agreement +may be located on the Internet using the following unique, persistent +identifier (known as a handle): 1895.22/1011. This Agreement may also be +obtained from a proxy server on the Internet using the +URL:http://hdl.handle.net/1895.22/1011". + +3. In the event Licensee prepares a derivative work that is based on or +incorporates Python 1.6b1or any part thereof, and wants to make the derivative +work available to the public as provided herein, then Licensee hereby agrees +to indicate in any such work the nature of the modifications made to Python +1.6b1. + +4. CNRI is making Python 1.6b1 available to Licensee on an "AS IS" basis. CNRI +MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, +BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY +OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF +PYTHON 1.6b1WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE FOR +ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF +USING, MODIFYING OR DISTRIBUTING PYTHON 1.6b1, OR ANY DERIVATIVE THEREOF, EVEN +IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach +of its terms and conditions. + +7. This License Agreement shall be governed by and interpreted in all respects +by the law of the State of Virginia, excluding conflict of law provisions. +Nothing in this License Agreement shall be deemed to create any relationship +of agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or trade +name in a trademark sense to endorse or promote products or services of +Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6b1, Licensee agrees to be bound by the +terms and conditions of this License Agreement. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..1aba38f --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include LICENSE diff --git a/README b/README new file mode 100644 index 0000000..bc1c064 --- /dev/null +++ b/README @@ -0,0 +1,14 @@ +Copyright, Michael P. Soulier, 2006. + +This is a simple tftp library for Python. The project has no hosting as of +yet, so for now, simply direct any problems to me at +msoulier@digitaltorque.ca. + +This library was developed against Python 2.4.1. If you have a previous +version, don't bother contacting me. You're welcome to backport it if you +like. + +License is the CNRI Python License. +http://www.opensource.org/licenses/pythonpl.php + +See LICENSE in this distribution. diff --git a/bin/tftpy_client.py b/bin/tftpy_client.py new file mode 100644 index 0000000..e69de29 diff --git a/doc/rfc1350.txt b/doc/rfc1350.txt new file mode 100644 index 0000000..f114928 --- /dev/null +++ b/doc/rfc1350.txt @@ -0,0 +1,551 @@ + + RFC 1350 (RFC1350) + + Internet RFC/STD/FYI/BCP Archives + +[ [1]RFC Index | [2]RFC Search | [3]Usenet FAQs | [4]Web FAQs | [5]Documents + | [6]Cities ] + + Alternate Formats: [7]rfc1350.txt | [8]rfc1350.txt.pdf + + RFC 1350 - The TFTP Protocol (Revision 2) + _________________________________________________________________ + + +Network Working Group K. Sollins +Request For Comments: 1350 MIT +STD: 33 July 1992 +Obsoletes: [9]RFC 783 + + THE TFTP PROTOCOL (REVISION 2) + +Status of this Memo + + This RFC specifies an IAB standards track protocol for the Internet + community, and requests discussion and suggestions for improvements. + Please refer to the current edition of the "IAB Official Protocol + Standards" for the standardization state and status of this protocol. + Distribution of this memo is unlimited. + +Summary + + TFTP is a very simple protocol used to transfer files. It is from + this that its name comes, Trivial File Transfer Protocol or TFTP. + Each nonterminal packet is acknowledged separately. This document + describes the protocol and its types of packets. The document also + explains the reasons behind some of the design decisions. + +Acknowlegements + + The protocol was originally designed by Noel Chiappa, and was + redesigned by him, Bob Baldwin and Dave Clark, with comments from + Steve Szymanski. The current revision of the document includes + modifications stemming from discussions with and suggestions from + Larry Allen, Noel Chiappa, Dave Clark, Geoff Cooper, Mike Greenwald, + Liza Martin, David Reed, Craig Milo Rogers (of USC-ISI), Kathy + Yellick, and the author. The acknowledgement and retransmission + scheme was inspired by TCP, and the error mechanism was suggested by + PARC's EFTP abort message. + + The May, 1992 revision to fix the "Sorcerer's Apprentice" protocol + bug [4] and other minor document problems was done by Noel Chiappa. + + This research was supported by the Advanced Research Projects Agency + of the Department of Defense and was monitored by the Office of Naval + Research under contract number N00014-75-C-0661. + +1. Purpose + + TFTP is a simple protocol to transfer files, and therefore was named + the Trivial File Transfer Protocol or TFTP. It has been implemented + on top of the Internet User Datagram protocol (UDP or Datagram) [2] + + so it may be used to move files between machines on different + networks implementing UDP. (This should not exclude the possibility + of implementing TFTP on top of other datagram protocols.) It is + designed to be small and easy to implement. Therefore, it lacks most + of the features of a regular FTP. The only thing it can do is read + and write files (or mail) from/to a remote server. It cannot list + directories, and currently has no provisions for user authentication. + In common with other Internet protocols, it passes 8 bit bytes of + data. + + Three modes of transfer are currently supported: netascii (This is + ascii as defined in "USA Standard Code for Information Interchange" + [1] with the modifications specified in "Telnet Protocol + Specification" [3].) Note that it is 8 bit ascii. The term + "netascii" will be used throughout this document to mean this + particular version of ascii.); octet (This replaces the "binary" mode + of previous versions of this document.) raw 8 bit bytes; mail, + netascii characters sent to a user rather than a file. (The mail + mode is obsolete and should not be implemented or used.) Additional + modes can be defined by pairs of cooperating hosts. + + Reference [4] (section 4.2) should be consulted for further valuable + directives and suggestions on TFTP. + +2. Overview of the Protocol + + Any transfer begins with a request to read or write a file, which + also serves to request a connection. If the server grants the + request, the connection is opened and the file is sent in fixed + length blocks of 512 bytes. Each data packet contains one block of + data, and must be acknowledged by an acknowledgment packet before the + next packet can be sent. A data packet of less than 512 bytes + signals termination of a transfer. If a packet gets lost in the + network, the intended recipient will timeout and may retransmit his + last packet (which may be data or an acknowledgment), thus causing + the sender of the lost packet to retransmit that lost packet. The + sender has to keep just one packet on hand for retransmission, since + the lock step acknowledgment guarantees that all older packets have + been received. Notice that both machines involved in a transfer are + considered senders and receivers. One sends data and receives + acknowledgments, the other sends acknowledgments and receives data. + + Most errors cause termination of the connection. An error is + signalled by sending an error packet. This packet is not + acknowledged, and not retransmitted (i.e., a TFTP server or user may + terminate after sending an error message), so the other end of the + connection may not get it. Therefore timeouts are used to detect + such a termination when the error packet has been lost. Errors are + + caused by three types of events: not being able to satisfy the + request (e.g., file not found, access violation, or no such user), + receiving a packet which cannot be explained by a delay or + duplication in the network (e.g., an incorrectly formed packet), and + losing access to a necessary resource (e.g., disk full or access + denied during a transfer). + + TFTP recognizes only one error condition that does not cause + termination, the source port of a received packet being incorrect. + In this case, an error packet is sent to the originating host. + + This protocol is very restrictive, in order to simplify + implementation. For example, the fixed length blocks make allocation + straight forward, and the lock step acknowledgement provides flow + control and eliminates the need to reorder incoming data packets. + +3. Relation to other Protocols + + As mentioned TFTP is designed to be implemented on top of the + Datagram protocol (UDP). Since Datagram is implemented on the + Internet protocol, packets will have an Internet header, a Datagram + header, and a TFTP header. Additionally, the packets may have a + header (LNI, ARPA header, etc.) to allow them through the local + transport medium. As shown in Figure 3-1, the order of the contents + of a packet will be: local medium header, if used, Internet header, + Datagram header, TFTP header, followed by the remainder of the TFTP + packet. (This may or may not be data depending on the type of packet + as specified in the TFTP header.) TFTP does not specify any of the + values in the Internet header. On the other hand, the source and + destination port fields of the Datagram header (its format is given + in the appendix) are used by TFTP and the length field reflects the + size of the TFTP packet. The transfer identifiers (TID's) used by + TFTP are passed to the Datagram layer to be used as ports; therefore + they must be between 0 and 65,535. The initialization of TID's is + discussed in the section on initial connection protocol. + + The TFTP header consists of a 2 byte opcode field which indicates + the packet's type (e.g., DATA, ERROR, etc.) These opcodes and the + formats of the various types of packets are discussed further in the + section on TFTP packets. + + --------------------------------------------------- + | Local Medium | Internet | Datagram | TFTP | + --------------------------------------------------- + + Figure 3-1: Order of Headers + +4. Initial Connection Protocol + + A transfer is established by sending a request (WRQ to write onto a + foreign file system, or RRQ to read from it), and receiving a + positive reply, an acknowledgment packet for write, or the first data + packet for read. In general an acknowledgment packet will contain + the block number of the data packet being acknowledged. Each data + packet has associated with it a block number; block numbers are + consecutive and begin with one. Since the positive response to a + write request is an acknowledgment packet, in this special case the + block number will be zero. (Normally, since an acknowledgment packet + is acknowledging a data packet, the acknowledgment packet will + contain the block number of the data packet being acknowledged.) If + the reply is an error packet, then the request has been denied. + + In order to create a connection, each end of the connection chooses a + TID for itself, to be used for the duration of that connection. The + TID's chosen for a connection should be randomly chosen, so that the + probability that the same number is chosen twice in immediate + succession is very low. Every packet has associated with it the two + TID's of the ends of the connection, the source TID and the + destination TID. These TID's are handed to the supporting UDP (or + other datagram protocol) as the source and destination ports. A + requesting host chooses its source TID as described above, and sends + its initial request to the known TID 69 decimal (105 octal) on the + serving host. The response to the request, under normal operation, + uses a TID chosen by the server as its source TID and the TID chosen + for the previous message by the requestor as its destination TID. + The two chosen TID's are then used for the remainder of the transfer. + + As an example, the following shows the steps used to establish a + connection to write a file. Note that WRQ, ACK, and DATA are the + names of the write request, acknowledgment, and data types of packets + respectively. The appendix contains a similar example for reading a + file. + + 1. Host A sends a "WRQ" to host B with source= A's TID, + destination= 69. + + 2. Host B sends a "ACK" (with block number= 0) to host A with + source= B's TID, destination= A's TID. + + At this point the connection has been established and the first data + packet can be sent by Host A with a sequence number of 1. In the + next step, and in all succeeding steps, the hosts should make sure + that the source TID matches the value that was agreed on in steps 1 + and 2. If a source TID does not match, the packet should be + discarded as erroneously sent from somewhere else. An error packet + should be sent to the source of the incorrect packet, while not + disturbing the transfer. This can be done only if the TFTP in fact + receives a packet with an incorrect TID. If the supporting protocols + do not allow it, this particular error condition will not arise. + + The following example demonstrates a correct operation of the + protocol in which the above situation can occur. Host A sends a + request to host B. Somewhere in the network, the request packet is + duplicated, and as a result two acknowledgments are returned to host + A, with different TID's chosen on host B in response to the two + requests. When the first response arrives, host A continues the + connection. When the second response to the request arrives, it + should be rejected, but there is no reason to terminate the first + connection. Therefore, if different TID's are chosen for the two + connections on host B and host A checks the source TID's of the + messages it receives, the first connection can be maintained while + the second is rejected by returning an error packet. + +5. TFTP Packets + + TFTP supports five types of packets, all of which have been mentioned + above: + + opcode operation + 1 Read request (RRQ) + 2 Write request (WRQ) + 3 Data (DATA) + 4 Acknowledgment (ACK) + 5 Error (ERROR) + + The TFTP header of a packet contains the opcode associated with + that packet. + + 2 bytes string 1 byte string 1 byte + ------------------------------------------------ + | Opcode | Filename | 0 | Mode | 0 | + ------------------------------------------------ + + Figure 5-1: RRQ/WRQ packet + + RRQ and WRQ packets (opcodes 1 and 2 respectively) have the format + shown in Figure 5-1. The file name is a sequence of bytes in + netascii terminated by a zero byte. The mode field contains the + string "netascii", "octet", or "mail" (or any combination of upper + and lower case, such as "NETASCII", NetAscii", etc.) in netascii + indicating the three modes defined in the protocol. A host which + receives netascii mode data must translate the data to its own + format. Octet mode is used to transfer a file that is in the 8-bit + format of the machine from which the file is being transferred. It + is assumed that each type of machine has a single 8-bit format that + is more common, and that that format is chosen. For example, on a + DEC-20, a 36 bit machine, this is four 8-bit bytes to a word with + four bits of breakage. If a host receives a octet file and then + returns it, the returned file must be identical to the original. + Mail mode uses the name of a mail recipient in place of a file and + must begin with a WRQ. Otherwise it is identical to netascii mode. + The mail recipient string should be of the form "username" or + "username@hostname". If the second form is used, it allows the + option of mail forwarding by a relay computer. + + The discussion above assumes that both the sender and recipient are + operating in the same mode, but there is no reason that this has to + be the case. For example, one might build a storage server. There + is no reason that such a machine needs to translate netascii into its + own form of text. Rather, the sender might send files in netascii, + but the storage server might simply store them without translation in + 8-bit format. Another such situation is a problem that currently + exists on DEC-20 systems. Neither netascii nor octet accesses all + the bits in a word. One might create a special mode for such a + machine which read all the bits in a word, but in which the receiver + stored the information in 8-bit format. When such a file is + retrieved from the storage site, it must be restored to its original + form to be useful, so the reverse mode must also be implemented. The + user site will have to remember some information to achieve this. In + both of these examples, the request packets would specify octet mode + to the foreign host, but the local host would be in some other mode. + No such machine or application specific modes have been specified in + TFTP, but one would be compatible with this specification. + + It is also possible to define other modes for cooperating pairs of + + hosts, although this must be done with care. There is no requirement + that any other hosts implement these. There is no central authority + that will define these modes or assign them names. + + 2 bytes 2 bytes n bytes + ---------------------------------- + | Opcode | Block # | Data | + ---------------------------------- + + Figure 5-2: DATA packet + + Data is actually transferred in DATA packets depicted in Figure 5-2. + DATA packets (opcode = 3) have a block number and data field. The + block numbers on data packets begin with one and increase by one for + each new block of data. This restriction allows the program to use a + single number to discriminate between new packets and duplicates. + The data field is from zero to 512 bytes long. If it is 512 bytes + long, the block is not the last block of data; if it is from zero to + 511 bytes long, it signals the end of the transfer. (See the section + on Normal Termination for details.) + + All packets other than duplicate ACK's and those used for + termination are acknowledged unless a timeout occurs [4]. Sending a + DATA packet is an acknowledgment for the first ACK packet of the + previous DATA packet. The WRQ and DATA packets are acknowledged by + ACK or ERROR packets, while RRQ + + 2 bytes 2 bytes + --------------------- + | Opcode | Block # | + --------------------- + + Figure 5-3: ACK packet + + and ACK packets are acknowledged by DATA or ERROR packets. Figure + 5-3 depicts an ACK packet; the opcode is 4. The block number in + an ACK echoes the block number of the DATA packet being + acknowledged. A WRQ is acknowledged with an ACK packet having a + block number of zero. + + 2 bytes 2 bytes string 1 byte + ----------------------------------------- + | Opcode | ErrorCode | ErrMsg | 0 | + ----------------------------------------- + + Figure 5-4: ERROR packet + + An ERROR packet (opcode 5) takes the form depicted in Figure 5-4. An + ERROR packet can be the acknowledgment of any other type of packet. + The error code is an integer indicating the nature of the error. A + table of values and meanings is given in the appendix. (Note that + several error codes have been added to this version of this + document.) The error message is intended for human consumption, and + should be in netascii. Like all other strings, it is terminated with + a zero byte. + +6. Normal Termination + + The end of a transfer is marked by a DATA packet that contains + between 0 and 511 bytes of data (i.e., Datagram length < 516). This + packet is acknowledged by an ACK packet like all other DATA packets. + The host acknowledging the final DATA packet may terminate its side + of the connection on sending the final ACK. On the other hand, + dallying is encouraged. This means that the host sending the final + ACK will wait for a while before terminating in order to retransmit + the final ACK if it has been lost. The acknowledger will know that + the ACK has been lost if it receives the final DATA packet again. + The host sending the last DATA must retransmit it until the packet is + acknowledged or the sending host times out. If the response is an + ACK, the transmission was completed successfully. If the sender of + the data times out and is not prepared to retransmit any more, the + transfer may still have been completed successfully, after which the + acknowledger or network may have experienced a problem. It is also + possible in this case that the transfer was unsuccessful. In any + case, the connection has been closed. + +7. Premature Termination + + If a request can not be granted, or some error occurs during the + transfer, then an ERROR packet (opcode 5) is sent. This is only a + courtesy since it will not be retransmitted or acknowledged, so it + may never be received. Timeouts must also be used to detect errors. + +I. Appendix + +Order of Headers + + 2 bytes + ---------------------------------------------------------- + | Local Medium | Internet | Datagram | TFTP Opcode | + ---------------------------------------------------------- + +TFTP Formats + + Type Op # Format without header + + 2 bytes string 1 byte string 1 byte + ----------------------------------------------- + RRQ/ | 01/02 | Filename | 0 | Mode | 0 | + WRQ ----------------------------------------------- + 2 bytes 2 bytes n bytes + --------------------------------- + DATA | 03 | Block # | Data | + --------------------------------- + 2 bytes 2 bytes + ------------------- + ACK | 04 | Block # | + -------------------- + 2 bytes 2 bytes string 1 byte + ---------------------------------------- + ERROR | 05 | ErrorCode | ErrMsg | 0 | + ---------------------------------------- + +Initial Connection Protocol for reading a file + + 1. Host A sends a "RRQ" to host B with source= A's TID, + destination= 69. + + 2. Host B sends a "DATA" (with block number= 1) to host A with + source= B's TID, destination= A's TID. + +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. + +Internet User Datagram Header [2] + + (This has been included only for convenience. TFTP need not be + implemented on top of the Internet User Datagram Protocol.) + + Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Values of Fields + + Source Port Picked by originator of packet. + + Dest. Port Picked by destination machine (69 for RRQ or WRQ). + + Length Number of bytes in UDP packet, including UDP header. + + Checksum Reference 2 describes rules for computing checksum. + (The implementor of this should be sure that the + correct algorithm is used here.) + Field contains zero if unused. + + Note: TFTP passes transfer identifiers (TID's) to the Internet User + Datagram protocol to be used as the source and destination ports. + +References + + [1] USA Standard Code for Information Interchange, USASI X3.4-1968. + + [2] Postel, J., "User Datagram Protocol," [10]RFC 768, USC/Information + Sciences Institute, 28 August 1980. + + [3] Postel, J., "Telnet Protocol Specification," [11]RFC 764, + USC/Information Sciences Institute, June, 1980. + + [4] Braden, R., Editor, "Requirements for Internet Hosts -- + Application and Support", [12]RFC 1123, USC/Information Sciences + Institute, October 1989. + +Security Considerations + + Since TFTP includes no login or access control mechanisms, care must + be taken in the rights granted to a TFTP server process so as not to + violate the security of the server hosts file system. TFTP is often + installed with controls such that only files that have public read + access are available via TFTP and writing files via TFTP is + disallowed. + +Author's Address + + Karen R. Sollins + Massachusetts Institute of Technology + Laboratory for Computer Science + 545 Technology Square + Cambridge, MA 02139-1986 + + Phone: (617) 253-6006 + + EMail: [13]SOLLINS@LCS.MIT.EDU + + + Comments about this RFC: + * [14]RFC 1350: How to setup TFTP across firewall. Why to spawn + connections on several different... by WahJava (2/7/2006) + * [15]RFC 1350: Since there is no master slave arrangement. What happens + if both ends of the... by pg (3/30/2005) + * [16]RFC 1350: What happens when the TFTP client or server detects that + the ethernet link which... by Sekhar Nori (12/29/2003) + * [17]RFC 1350: This RFC may benefit from an update in the style of PPP or + SSCOP protocol... by Chris (10/4/2004) + * [18]RFC 1350: What happens if the server receives any packet other than + RRQ/WRQ in the initial... by Orgad (10/24/2003) + * [19]RFC 1350: In the following para,it is considered duplication of the + acknowledgment packet... by KiranKumar (1/1/2006) + * [20]RFC 1350: What happens if the data on the file is a multiple 512 + bytes? When will the... by yara (2/23/2005) + * [21]RFC 1350: I tried WhiteHorn TFTP server (www.whitehorns.net) and + really liked it. Does... by bartley (7/29/2005) + * [22]RFC 1350: I'd like to see more discussion about the benefits and + drawbacks of using... by Mike Cepek (4/19/2005) + * [23]RFC 1350: Any words on the ipv6 support in TFTP..It might be not the + correct question here... by Piyush Yaduvanshi (2/23/2006) + + Previous: [24]RFC 1349 - Type of Service in the Internet Protocol Suite + Next: [25]RFC 1351 - SNMP Administrative Model + + _________________________________________________________________ + + [ [26]RFC Index | [27]RFC Search | [28]Usenet FAQs | [29]Web FAQs | + [30]Documents | [31]Cities ] + +References + + 1. http://www.faqs.org/rfcs/ + 2. http://www.faqs.org/rfcs/rfcsearch.html + 3. http://www.faqs.org/faqs/ + 4. http://www.faqs.org/contrib/ + 5. http://www.faqs.org/docs/ + 6. http://www.city-data.com/ + 7. http://www.faqs.org/ftp/rfc/rfc1350.txt + 8. http://www.faqs.org/ftp/rfc/pdf/rfc1350.txt.pdf + 9. http://www.faqs.org/rfcs/rfc783.html + 10. http://www.faqs.org/rfcs/rfc768.html + 11. http://www.faqs.org/rfcs/rfc764.html + 12. http://www.faqs.org/rfcs/rfc1123.html + 13. mailto:SOLLINS@LCS.MIT.EDU + 14. http://www.faqs.org/qa/rfcc-2889.html + 15. http://www.faqs.org/qa/rfcc-1841.html + 16. http://www.faqs.org/qa/rfcc-433.html + 17. http://www.faqs.org/qa/rfcc-1281.html + 18. http://www.faqs.org/qa/rfcc-255.html + 19. http://www.faqs.org/qa/rfcc-2798.html + 20. http://www.faqs.org/qa/rfcc-1734.html + 21. http://www.faqs.org/qa/rfcc-2297.html + 22. http://www.faqs.org/qa/rfcc-1916.html + 23. http://www.faqs.org/qa/rfcc-2933.html + 24. http://www.faqs.org/rfcs/rfc1349.html + 25. http://www.faqs.org/rfcs/rfc1351.html + 26. http://www.faqs.org/rfcs/ + 27. http://www.faqs.org/rfcs/rfcsearch.html + 28. http://www.faqs.org/faqs/ + 29. http://www.faqs.org/contrib/ + 30. http://www.faqs.org/docs/ + 31. http://www.city-data.com/ diff --git a/doc/rfc2347.txt b/doc/rfc2347.txt new file mode 100644 index 0000000..dbc0f8c --- /dev/null +++ b/doc/rfc2347.txt @@ -0,0 +1,395 @@ + + + + + + +Network Working Group G. Malkin +Request for Commments: 2347 Bay Networks +Updates: 1350 A. Harkin +Obsoletes: 1782 Hewlett Packard Co. +Category: Standards Track May 1998 + + + TFTP Option Extension + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (1998). All Rights Reserved. + +Abstract + + The Trivial File Transfer Protocol [1] is a simple, lock-step, file + transfer protocol which allows a client to get or put a file onto a + remote host. This document describes a simple extension to TFTP to + allow option negotiation prior to the file transfer. + +Introduction + + The option negotiation mechanism proposed in this document is a + backward-compatible extension to the TFTP protocol. It allows file + transfer options to be negotiated prior to the transfer using a + mechanism which is consistent with TFTP's Request Packet format. The + mechanism is kept simple by enforcing a request-respond-acknowledge + sequence, similar to the lock-step approach taken by TFTP itself. + + While the option negotiation mechanism is general purpose, in that + many types of options may be negotiated, it was created to support + the Blocksize option defined in [2]. Additional options are defined + in [3]. + +Packet Formats + + TFTP options are appended to the Read Request and Write Request + packets. A new type of TFTP packet, the Option Acknowledgment + (OACK), is used to acknowledge a client's option negotiation request. + A new error code, 8, is hereby defined to indicate that a transfer + + + +Malkin & Harkin Standards Track [Page 1] + +RFC 2347 TFTP Option Extension May 1998 + + + should be terminated due to option negotiation. + + Options are appended to a TFTP Read Request or Write Request packet + as follows: + + +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> + | opc |filename| 0 | mode | 0 | opt1 | 0 | value1 | 0 | < + +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> + + >-------+---+---~~---+---+ + < optN | 0 | valueN | 0 | + >-------+---+---~~---+---+ + + opc + The opcode field contains either a 1, for Read Requests, or 2, + for Write Requests, as defined in [1]. + + filename + The name of the file to be read or written, as defined in [1]. + This is a NULL-terminated field. + + mode + The mode of the file transfer: "netascii", "octet", or "mail", + as defined in [1]. This is a NULL-terminated field. + + opt1 + The first option, in case-insensitive ASCII (e.g., blksize). + This is a NULL-terminated field. + + value1 + The value associated with the first option, in case- + insensitive ASCII. This is a NULL-terminated field. + + optN, valueN + The final option/value pair. Each NULL-terminated field is + specified in case-insensitive ASCII. + + The options and values are all NULL-terminated, in keeping with the + original request format. If multiple options are to be negotiated, + they are appended to each other. The order in which options are + specified is not significant. The maximum size of a request packet + is 512 octets. + + The OACK packet has the following format: + + + + + + + +Malkin & Harkin Standards Track [Page 2] + +RFC 2347 TFTP Option Extension May 1998 + + + +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ + | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | + +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ + + opc + The opcode field contains a 6, for Option Acknowledgment. + + opt1 + The first option acknowledgment, copied from the original + request. + + value1 + The acknowledged value associated with the first option. If + and how this value may differ from the original request is + detailed in the specification for the option. + + optN, valueN + The final option/value acknowledgment pair. + +Negotiation Protocol + + The client appends options at the end of the Read Request or Write + request packet, as shown above. Any number of options may be + specified; however, an option may only be specified once. The order + of the options is not significant. + + If the server supports option negotiation, and it recognizes one or + more of the options specified in the request packet, the server may + respond with an Options Acknowledgment (OACK). Each option the + server recognizes, and accepts the value for, is included in the + OACK. Some options may allow alternate values to be proposed, but + this is an option specific feature. The server must not include in + the OACK any option which had not been specifically requested by the + client; that is, only the client may initiate option negotiation. + Options which the server does not support should be omitted from the + OACK; they should not cause an ERROR packet to be generated. If the + value of a supported option is invalid, the specification for that + option will indicate whether the server should simply omit the option + from the OACK, respond with an alternate value, or send an ERROR + packet, with error code 8, to terminate the transfer. + + An option not acknowledged by the server must be ignored by the + client and server as if it were never requested. If multiple options + were requested, the client must use those options which were + acknowledged by the server and must not use those options which were + not acknowledged by the server. + + + + + +Malkin & Harkin Standards Track [Page 3] + +RFC 2347 TFTP Option Extension May 1998 + + + When the client appends options to the end of a Read Request packet, + three possible responses may be returned by the server: + + OACK - acknowledge of Read Request and the options; + + DATA - acknowledge of Read Request, but not the options; + + ERROR - the request has been denied. + + When the client appends options to the end of a Write Request packet, + three possible responses may be returned by the server: + + OACK - acknowledge of Write Request and the options; + + ACK - acknowledge of Write Request, but not the options; + + ERROR - the request has been denied. + + If a server implementation does not support option negotiation, it + will likely ignore any options appended to the client's request. In + this case, the server will return a DATA packet for a Read Request + and an ACK packet for a Write Request establishing normal TFTP data + transfer. In the event that a server returns an error for a request + which carries an option, the client may attempt to repeat the request + without appending any options. This implementation option would + handle servers which consider extraneous data in the request packet + to be erroneous. + + Depending on the original transfer request there are two ways for a + client to confirm acceptance of a server's OACK. If the transfer was + initiated with a Read Request, then an ACK (with the data block + number set to 0) is sent by the client to confirm the values in the + server's OACK packet. If the transfer was initiated with a Write + Request, then the client begins the transfer with the first DATA + packet, using the negotiated values. If the client rejects the OACK, + then it sends an ERROR packet, with error code 8, to the server and + the transfer is terminated. + + Once a client acknowledges an OACK, with an appropriate non-error + response, that client has agreed to use only the options and values + returned by the server. Remember that the server cannot request an + option; it can only respond to them. If the client receives an OACK + containing an unrequested option, it should respond with an ERROR + packet, with error code 8, and terminate the transfer. + + + + + + + +Malkin & Harkin Standards Track [Page 4] + +RFC 2347 TFTP Option Extension May 1998 + + +Examples + + Read Request + + client server + ------------------------------------------------------- + |1|foofile|0|octet|0|blksize|0|1432|0| --> RRQ + <-- |6|blksize|0|1432|0| OACK + |4|0| --> ACK + <-- |3|1| 1432 octets of data | DATA + |4|1| --> ACK + <-- |3|2| 1432 octets of data | DATA + |4|2| --> ACK + <-- |3|3|<1432 octets of data | DATA + |4|3| --> ACK + + Write Request + + client server + ------------------------------------------------------- + |2|barfile|0|octet|0|blksize|0|2048|0| --> RRQ + <-- |6|blksize|0|2048|0| OACK + |3|1| 2048 octets of data | --> DATA + <-- |4|1| ACK + |3|2| 2048 octets of data | --> DATA + <-- |4|2| ACK + |3|3|<2048 octets of data | --> DATA + <-- |4|3| ACK + +Security Considerations + + The basic TFTP protocol has no security mechanism. This is why it + has no rename, delete, or file overwrite capabilities. This document + does not add any security to TFTP; however, the specified extensions + do not add any additional security risks. + +References + + [1] Sollins, K., "The TFTP Protocol (Revision 2)", STD 33, RFC 1350, + October 1992. + + [2] Malkin, G., and A. Harkin, "TFTP Blocksize Option", RFC 2348, + May 1998. + + [3] Malkin, G., and A. Harkin, "TFTP Timeout Interval and Transfer + Size Options", RFC 2349, May 1998. + + + + + +Malkin & Harkin Standards Track [Page 5] + +RFC 2347 TFTP Option Extension May 1998 + + +Authors' Addresses + + Gary Scott Malkin + Bay Networks + 8 Federal Street + Billerica, MA 01821 + + Phone: (978) 916-4237 + EMail: gmalkin@baynetworks.com + + + Art Harkin + Internet Services Project + Information Networks Division + 19420 Homestead Road MS 43LN + Cupertino, CA 95014 + + Phone: (408) 447-3755 + EMail: ash@cup.hp.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Malkin & Harkin Standards Track [Page 6] + +RFC 2347 TFTP Option Extension May 1998 + + +Full Copyright Statement + + Copyright (C) The Internet Society (1998). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + + + + + + + + + + + + + + + + + + + + + + + +Malkin & Harkin Standards Track [Page 7] + diff --git a/doc/rfc2348.txt b/doc/rfc2348.txt new file mode 100644 index 0000000..b38c56d --- /dev/null +++ b/doc/rfc2348.txt @@ -0,0 +1,283 @@ + + + + + + +Network Working Group G. Malkin +Request for Commments: 2348 Bay Networks +Updates: 1350 A. Harkin +Obsoletes: 1783 Hewlett Packard Co. +Category: Standards Track May 1998 + + + TFTP Blocksize Option + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (1998). All Rights Reserved. + +Abstract + + The Trivial File Transfer Protocol [1] is a simple, lock-step, file + transfer protocol which allows a client to get or put a file onto a + remote host. One of its primary uses is the booting of diskless + nodes on a Local Area Network. TFTP is used because it is very + simple to implement in a small node's limited ROM space. However, + the choice of a 512-octet blocksize is not the most efficient for use + on a LAN whose MTU may 1500 octets or greater. + + This document describes a TFTP option which allows the client and + server to negotiate a blocksize more applicable to the network + medium. The TFTP Option Extension mechanism is described in [2]. + +Blocksize Option Specification + + The TFTP Read Request or Write Request packet is modified to include + the blocksize option as follows. Note that all fields except "opc" + are NULL-terminated. + + +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ + | opc |filename| 0 | mode | 0 | blksize| 0 | #octets| 0 | + +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ + + opc + The opcode field contains either a 1, for Read Requests, or 2, + for Write Requests, as defined in [1]. + + + +Malkin & Harkin Standards Track [Page 1] + +RFC 2348 TFTP Blocksize Option May 1998 + + + filename + The name of the file to be read or written, as defined in [1]. + + mode + The mode of the file transfer: "netascii", "octet", or "mail", + as defined in [1]. + + blksize + The Blocksize option, "blksize" (case in-sensitive). + + #octets + The number of octets in a block, specified in ASCII. Valid + values range between "8" and "65464" octets, inclusive. The + blocksize refers to the number of data octets; it does not + include the four octets of TFTP header. + + For example: + + +-------+--------+---+--------+---+--------+---+--------+---+ + | 1 | foobar | 0 | octet | 0 | blksize| 0 | 1428 | 0 | + +-------+--------+---+--------+---+--------+---+--------+---+ + + is a Read Request, for the file named "foobar", in octet (binary) + transfer mode, with a block size of 1428 octets (Ethernet MTU, less + the TFTP, UDP and IP header lengths). + + If the server is willing to accept the blocksize option, it sends an + Option Acknowledgment (OACK) to the client. The specified value must + be less than or equal to the value specified by the client. The + client must then either use the size specified in the OACK, or send + an ERROR packet, with error code 8, to terminate the transfer. + + The rules for determining the final packet are unchanged from [1]. + The reception of a data packet with a data length less than the + negotiated blocksize is the final packet. If the blocksize is + greater than the amount of data to be transfered, the first packet is + the final packet. If the amount of data to be transfered is an + integral multiple of the blocksize, an extra data packet containing + no data is sent to end the transfer. + +Proof of Concept + + Performance tests were run on the prototype implementation using a + variety of block sizes. The tests were run on a lightly loaded + Ethernet, between two HP-UX 9000, in "octet" mode, on 2.25MB files. + The average (5x) transfer times for paths with (g-time) and without + (n-time) a intermediate gateway are graphed as follows: + + + + +Malkin & Harkin Standards Track [Page 2] + +RFC 2348 TFTP Blocksize Option May 1998 + + + | + 37 + g + | + 35 + + | + 33 + + | + 31 + + | + 29 + + | + 27 + + | g blocksize n-time g-time + 25 + --------- ------ ------ + s | n 512 23.85 37.05 + e 23 + g 1024 16.15 25.65 + c | 1428 13.70 23.10 + o 21 + 2048 10.90 16.90 + n | 4096 6.85 9.65 + d 19 + 8192 4.90 6.15 + s | + 17 + g + | n + 15 + + | n + 13 + + | + 11 + n + | g + 9 + + | + 7 + n + | g + 5 + n + " + 0 +------+------+--+---+------+------+--- + 512 1K | 2K 4K 8K + 1428 + blocksize (octets) + + The comparisons between transfer times (without a gateway) between + the standard 512-octet blocksize and the negotiated blocksizes are: + + 1024 2x -32% + 1428 2.8x -42% + 2048 4x -54% + 4096 8x -71% + 8192 16x -80% + + + +Malkin & Harkin Standards Track [Page 3] + +RFC 2348 TFTP Blocksize Option May 1998 + + + As was anticipated, the transfer time decreases with an increase in + blocksize. The reason for the reduction in time is the reduction in + the number of packets sent. For example, by increasing the blocksize + from 512 octets to 1024 octets, not only are the number of data + packets halved, but the number of acknowledgement packets is also + halved (along with the number of times the data transmitter must wait + for an ACK). A secondary effect is the efficiency gained by reducing + the per-packet framing and processing overhead. + + Of course, if the blocksize exceeds the path MTU, IP fragmentation + and reassembly will begin to add more overhead. This will be more + noticable the greater the number of gateways in the path. + +Security Considerations + + The basic TFTP protocol has no security mechanism. This is why it + has no rename, delete, or file overwrite capabilities. This document + does not add any security to TFTP; however, the specified extensions + do not add any additional security risks. + +References + + [1] Sollins, K., "The TFTP Protocol (Revision 2)", STD 33, RFC 1350, + October 1992. + + [2] Malkin, G., and A. Harkin, "TFTP Option Extension", RFC 2347, + May 1998. + +Authors' Addresses + + Gary Scott Malkin + Bay Networks + 8 Federal Street + Billerica, MA 10821 + + Phone: (978) 916-4237 + EMail: gmalkin@baynetworks.com + + + Art Harkin + Networked Computing Division + Hewlett-Packard Company + 19420 Homestead Road MS 43LN + Cupertino, CA 95014 + + Phone: (408) 447-3755 + EMail: ash@cup.hp.com + + + + +Malkin & Harkin Standards Track [Page 4] + +RFC 2348 TFTP Blocksize Option May 1998 + + +Full Copyright Statement + + Copyright (C) The Internet Society (1998). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + + + + + + + + + + + + + + + + + + + + + + + +Malkin & Harkin Standards Track [Page 5] + diff --git a/lib/tftpy.py b/lib/tftpy.py new file mode 100755 index 0000000..a881dfe --- /dev/null +++ b/lib/tftpy.py @@ -0,0 +1,497 @@ +#!/usr/bin/python + +"""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. +""" + +import struct, random, socket, sys, logging, time +from optparse import OptionParser + +# Make sure that this is at least Python 2.4 +verlist = sys.version_info +if not verlist[0] >= 2 or not verlist[1] >= 4: + raise AssertionError, "Requires at least Python 2.4" + +# Change this as desired. FIXME - make this a command-line arg +LOG_LEVEL = logging.INFO +MIN_BLKSIZE = 8 +DEF_BLKSIZE = 512 +MAX_BLKSIZE = 65536 +SOCK_TIMEOUT = 5 +MAX_DUPS = 20 + +# Initialize the logger. +logging.basicConfig( + level=LOG_LEVEL, + format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', + datefmt='%m-%d %H:%M') +logger = logging.getLogger('tftplib') + +class TftpException(Exception): + pass + +def tftpassert(condition, msg): + if not condition: + raise TftpException, msg + +class TftpPacket(object): + def __init__(self): + self.opcode = 0 + self.buffer = None + + def encode(self): + """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.""" + raise NotImplementedError, "Abstract method" + + def decode(self): + """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.""" + raise NotImplementedError, "Abstract method" + +class TftpPacketInitial(TftpPacket): + """This class is a common parent class for the RRQ and WRQ packets, as they share + quite a bit of code.""" + def __init__(self): + TftpPacket.__init__(self) + self.filename = None + self.mode = None + self.options = {} + + def encode(self): + """Encode the packet's buffer from the instance variables.""" + tftpassert(self.filename, "filename required in initial packet") + tftpassert(self.mode, "mode required in initial packet") + + format = "!H" + length = len(self.filename) + format += "%ds" % length + format += "B" + if self.mode == "octet": + format += "5s" + else: + raise AssertionError, "Unsupported mode: %s" % mode + format += "B" + logger.debug("format is %s" % format) + logger.debug("size of struct is %d" % struct.calcsize(format)) + + self.buffer = struct.pack(format, self.opcode, self.filename, 0, self.mode, 0) + return self + + def decode(self): + tftpassert(self.buffer, "Can't decode, buffer is empty") + + # FIXME - this shares a lot of code with decode_with_options + nulls = 0 + # 2 byte opcode, followed by filename and mode strings, optionally followed + # by options. + format = "" + nulls = length = tlength = 0 + logger.debug("about to iterate buffer counting nulls") + for c in self.buffer: + if ord(c) == 0: + nulls += 1 + logger.debug("found a null at length %d, now have %d" % (length, nulls)) + length = 0 + format += "%dsx" % length + # At 2 nulls, we want to mark that position for decoding. + if nulls == 2: + break + length += 1 + tlength += 1 + logger.debug("hopefully found end of mode at length %d" % tlength) + # length should now be the end of the mode. + tftpassert(nulls == 2, "malformed packet") + shortbuf = self.buffer[2:tlength] + mystruct = struct.unpack(format, shortbuf) + for key in mystruct: + logger.debug("option name is %s, value is %s" % (key, mystruct[key])) + + tftpassert(len(mystruct) == 2, "malformed packet") + self.options = self.decode_with_options(self.buffer[tlength:]) + return self + + def decode_with_options(self, 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.""" + nulls = 0 + format = "" + options = {} + + # Count the nulls in the buffer. Each one terminates a string. + self.debug("about to iterate options buffer counting nulls") + length = 0 + for c in buffer: + if ord(c) == 0: + self.debug("found a null at length %d" % length) + if length > 0: + format += "%dsx" % length + length = 0 + else: + raise TftpException, "Invalid options buffer" + length += 1 + + # Unpack the buffer. + mystruct = struct.unpack(format, buffer) + for key in mystruct: + self.debug("option name is %s, value is %s" % (key, mystruct[key])) + + tftpassert(len(mystruct) % 2 == 0, "packet with odd number of option/value pairs") + + for i in range(0, len(mystruct), 2): + options[mystruct[i]] = mystruct[i+1] + + return options + +class TftpPacketRRQ(TftpPacketInitial): + """ + 2 bytes string 1 byte string 1 byte + ----------------------------------------------- +RRQ/ | 01/02 | Filename | 0 | Mode | 0 | +WRQ ----------------------------------------------- + """ + def __init__(self): + TftpPacketInitial.__init__(self) + self.opcode = 1 + +class TftpPacketWRQ(TftpPacketInitial): + """ + 2 bytes string 1 byte string 1 byte + ----------------------------------------------- +RRQ/ | 01/02 | Filename | 0 | Mode | 0 | +WRQ ----------------------------------------------- + """ + def __init__(self): + TftpPacketInitial.__init__(self) + self.opcode = 2 + +class TftpPacketDAT(TftpPacket): + """ + 2 bytes 2 bytes n bytes + --------------------------------- +DATA | 03 | Block # | Data | + --------------------------------- + """ + def __init__(self): + TftpPacket.__init__(self) + self.opcode = 3 + self.blocknumber = 0 + self.data = None + + def encode(self): + """Encode the DAT packet. This method populates self.buffer, and returns + self for easy method chaining.""" + tftpassert(len(self.data) > 0, "no point encoding empty data packet") + format = "!HH%ds" % len(self.data) + self.buffer = struct.pack(format, self.opcode, self.blocknumber, self.data) + return self + + def decode(self): + """Decode self.buffer into instance variables. It returns self for + easy method chaining.""" + # We know the first 2 bytes are the opcode. The second two are the + # block number. + (self.blocknumber,) = struct.unpack("!H", self.buffer[2:4]) + logger.info("decoding DAT packet, block number %d" % self.blocknumber) + logger.debug("should be %d bytes in the packet total" % len(self.buffer)) + # Everything else is data. + self.data = self.buffer[4:] + logger.debug("found %d bytes of data" \ + % len(self.data)) + return self + +class TftpPacketACK(TftpPacket): + """ + 2 bytes 2 bytes + ------------------- +ACK | 04 | Block # | + -------------------- + """ + def __init__(self): + TftpPacket.__init__(self) + self.opcode = 4 + self.blocknumber = 0 + + def encode(self): + logger.debug("encoding ACK: opcode = %d, block = %d" \ + % (self.opcode, self.blocknumber)) + self.buffer = struct.pack("!HH", self.opcode, self.blocknumber) + return self + + def decode(self): + self.opcode, self.blocknumber = struct.unpack("!HH", self.buffer) + logger.debug("decoded ACK packet: opcode = %d, block = %d" \ + % (self.opcode, self.blocknumber)) + return self + +class TftpPacketERR(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. + """ + def __init__(self): + TftpPacket.__init__(self) + self.opcode = 5 + self.errorcode = 0 + self.errmsg = None + self.errmsgs = { + 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" + } + + def encode(self): + """Encode the DAT packet based on instance variables, populating self.buffer, + returning self.""" + format = "!HH%dsx" % len(self.errmsgs[self.errorcode]) + self.debug("encoding ERR packet with format %s" % format) + self.buffer = struct.pack(format, + self.opcode, + self.errorcode, + self.errmsgs[self.errorcode]) + return self + + def decode(self): + "Decode self.buffer, populating instance variables and return self." + tftpassert(len(self.buffer) >= 5, "malformed ERR packet") + format = "!HH%dsx" % len(self.buffer)-5 + self.opcode, self.errorcode, self.errmsg = struct.unpack(format, self.buffer) + logger.error("ERR packet - errorcode: %d, message: %s" \ + % (errorcode, self.errmsg)) + return self + +class TftpPacketFactory(object): + """This class generates TftpPacket objects.""" + def __init__(self): + self.classes = { + 1: TftpPacketRRQ, + 2: TftpPacketWRQ, + 3: TftpPacketDAT, + 4: TftpPacketACK, + 5: TftpPacketERR + } + + def create(self, opcode): + tftpassert(self.classes.has_key(opcode), "Unsupported opcode: %d" % opcode) + packet = self.classes[opcode]() + logger.debug("packet is %s" % packet) + return packet + + def parse(self, buffer): + """This method is used to parse an existing datagram into its + corresponding TftpPacket object.""" + logger.debug("parsing a %d byte packet" % len(buffer)) + (opcode,) = struct.unpack("!H", buffer[:2]) + logger.debug("opcode is %d" % opcode) + packet = self.create(opcode) + packet.buffer = buffer + return packet.decode() + +class Tftp(object): + """This class is the base class for the tftp client and server. Any shared + code should be in this class.""" + def __init__(self): + self.options = None + +class TftpClient(Tftp): + """This class is an implementation of a tftp client.""" + def __init__(self, host, port, options): + """This constructor returns an instance of TftpClient, taking the + remote host, the remote port, and the filename to fetch.""" + Tftp.__init__(self) + self.host = host + self.port = port + self.options = options + + def download(self, filename, output, packethook=None): + """This method initiates a tftp download from the configured remote + host, requesting the filename passed.""" + # Open the output file. + outputfile = open(output, "wb") + recvpkt = None + curblock = 0 + dups = {} + start_time = time.time() + bytes = 0 + + tftp_factory = TftpPacketFactory() + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.settimeout(SOCK_TIMEOUT) + + logger.debug("Sending tftp download request to %s" % self.host) + pkt = TftpPacketRRQ() + pkt.filename = filename + pkt.mode = "octet" # FIXME - shouldn't hardcode this + sock.sendto(pkt.encode().buffer, (self.host, self.port)) + + # FIXME - need to do option negotiation here + + # Read the initial response datagram to see if we're in business. + (buffer, (raddress, rport)) = sock.recvfrom(MAX_BLKSIZE) + recvpkt = tftp_factory.parse(buffer) + + while isinstance(recvpkt, TftpPacketDAT): + logger.debug("Received %d bytes from %s:%s" \ + % (len(buffer), raddress, rport)) + # FIXME - check sender port and ip address + # FIXME - can we refactor this into below? + logger.debug("recvpkt.blocknumber = %d" % recvpkt.blocknumber) + logger.debug("curblock = %d" % curblock) + if recvpkt.blocknumber == curblock+1: + logger.debug("good, received block %d in sequence" % recvpkt.blocknumber) + curblock += 1 + # ACK the packet, and save the data. + logger.info("sending ACK to block %d" % curblock) + logger.debug("ip = %s, port = %s" % (self.host, self.port)) + ackpkt = TftpPacketACK() + ackpkt.blocknumber = curblock + sock.sendto(ackpkt.encode().buffer, (self.host, self.port)) + + logger.debug("writing %d bytes to output file" % len(recvpkt.data)) + outputfile.write(recvpkt.data) + bytes += len(recvpkt.data) + # If there is a packethook defined, call it. + if packethook: + packethook(recvpkt) + # Check for end-of-file, any less than full data packet. + if len(recvpkt.data) < DEF_BLKSIZE: + logger.info("end of file detected") + break + + elif recvpkt.blocknumber == curblock: + logger.warn("dropping duplicate block %d" % curblock) + if dups.has_key(curblock): + dups[curblock] += 1 + else: + dups[curblock] = 1 + if dups[curblock] >= MAX_DUPS: + raise TftpException, "Max duplicates for block %d reached" % curblock + logger.debug("ACKing block %d again, just in case" % curblock) + ackpkt = TftpPacketACK() + ackpkt.blocknumber = curblock + sock.sendto(ackpkt.encode().buffer, (self.host, self.port)) + + else: + msg = "Whoa! Received block %d but expected %d" % (recvpkt.blocknumber, + curblock+1) + logger.error(msg) + raise TftpException, msg + + (buffer, (raddress, rport)) = sock.recvfrom(MAX_BLKSIZE) + logger.info("Received %d bytes from %s:%s" % (len(buffer), + raddress, + rport)) + recvpkt = tftp_factory.parse(buffer) + # FIXME - check sender port and ip address + + # end while + # Check for errors + if isinstance(recvpkt, TftpPacketERR): + logger.error("received ERR packet") + + end_time = time.time() + duration = end_time - start_time + outputfile.close() + logger.info("Downloaded %d bytes in %d seconds" % (bytes, duration)) + bps = (bytes * 8.0) / duration + kbps = bps / 1024.0 + logger.info("Average rate: %.2f kbps" % kbps) + dupcount = 0 + for key in dups: + dupcount += dups[key] + logger.info("Received %d duplicate packets" % dupcount) + +def main(): + usage="" + parser = OptionParser(usage=usage) + parser.add_option('-t', + '--test', + action='store_true', + dest='test', + help='run test case(s)', + default=False) + parser.add_option('-H', + '--host', + action='store', + dest='host', + help='remote host or ip address') + parser.add_option('-p', + '--port', + action='store', + dest='port', + help='remote port to use (default: 69)', + default=69) + parser.add_option('-f', + '--filename', + action='store', + dest='filename', + help='filename to fetch') + parser.add_option('-b', + '--blocksize', + action='store', + dest='blocksize', + help='udp packet size to use (default: 512)', + default=512) + parser.add_option('-o', + '--output', + action='store', + dest='output', + help='output file (default: out)', + default='out') + options, args = parser.parse_args() + if options.test: + options.host = "216.191.234.113" + options.port = 20001 + options.filename = 'ipp510main.bin' + options.output = 'ipp510main.bin' + if not options.host or not options.filename: + parser.print_help() + sys.exit(1) + + class Progress(object): + def __init__(self, out): + self.progress = 0 + self.out = out + def progresshook(self, pkt): + self.progress += len(pkt.data) + self.out("Downloaded %d bytes" % self.progress) + + progresshook = Progress(logger.info).progresshook + + tclient = TftpClient(options.host, + options.port, + options.blocksize) + tclient.download(options.filename, + options.output, + progresshook) + +if __name__ == '__main__': + main() diff --git a/lib/tftpy_twisted.py b/lib/tftpy_twisted.py new file mode 100755 index 0000000..b8a80df --- /dev/null +++ b/lib/tftpy_twisted.py @@ -0,0 +1,19 @@ +#!/usr/bin/python + +from twisted.internet import reactor, protocol + +class TFTPProtocol(protocol.DatagramProtocol): + pass + +class TFTPProtocolFactory(protocol.ClientFactory): + protocol = TFTPProtocol + +def main(): + host = '216.191.234.113' + port = 20001 + + reactor.listenUDP(port, TFTPProtocolFactory()) + reactor.run() + +if __name__ == '__main__': + main() diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..aa94ed4 --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +from distutils.core import setup + +setup(name='tftpy', + version='0.1', + description='Python TFTP library', + author='Michael P. Soulier', + author_email='msoulier@digitaltorque.ca', + url='http://digitaltorque.ca', + py_modules=['lib/tftpy'], + scripts=['bin/tftpy_client.py'], + classifiers=[ + 'Development Status :: Alpha', + 'License :: OSI Approved :: CNRI Python License', + 'Topic :: Networking :: TFTP', + 'Intended Audience :: Developers', + 'Operating System :: POSIX', + 'Environment :: Console', + ] + )