Restructuring
git-svn-id: https://tftpy.svn.sourceforge.net/svnroot/tftpy/trunk@3 63283fd4-ec1e-0410-9879-cb7f675518damaster
parent
b72d23ebc3
commit
cfa8e61fa3
|
@ -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.
|
|
@ -0,0 +1 @@
|
||||||
|
include LICENSE
|
|
@ -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.
|
|
@ -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/
|
|
@ -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]
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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()
|
|
@ -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()
|
|
@ -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',
|
||||||
|
]
|
||||||
|
)
|
Reference in New Issue