TPTFTP Project

by: burt rosenberg
at: university of miami


The original best-effort, packetized data delivery

The Trivially Padded File Transfer Project

The Trivially Padded Trivial File Transfer Protocol is a simplification and modification of the Internet standard Trivial File Transfer Protocol (TFTP), defined in 1981 by RFC 783, and updated in RFC 1350.

Specific Objectives

Man Page

NAME
    tptftp, tptftpd
    
SYNOPSIS
    tptftpd [-vL] _port_
    tptftp [-v] _hostname_:_port_ _filename_    
    
DESCRIPTION
    Implements a client and a server for the tptftp protocol. The deamon executable,
    tptftpd, implements the server listening on port _port_.
    
    The client the program tptftp uses the tptftp protocol to read file _filename_
    from host _hostname_ connecting to the server listening on port _port_.
    
    The client writes the received bytes to standard out.

    The server implements basic security by requiring that filename is in the current
    directory and is world readable. Else an error is returned. 
    
OPTIONS
    -L If invoked as server, do not loop. Service one read request then exit.
    -v Verbose. Helpful debugging output to stdout. 
 
NOTES
    Implement only read. If requested return an error packet with 
    message "mode not supported". The mode is "padded". The size of the 
    data portion of the buffer is fixed, and that of the overall packet is
    5 bytes larger. 
    
    Padding is used to determine the actual data in the buffer. The padding
    scheme is that the byte following the last data byte of 0xff, followed by 
    sufficient 0x00 bytes to fill the packet to its fixed size.
    
    Maximum filename length is 256 characters, and cannot contain a pathname. 
    
    Server must be multi-thread, perhaps using the fork system call.
    
    Time-outs and resend are not implemented.

BUGS
    Due to the protocol being non-standard, you might have to open your firewall
    for all inbound UDP traffic. In the case of AWS, use the private IP addresses.
    
HISTORY
    First introduced in Fall 2003. Made Truly Trivial in Spring 2015.
    Authentication by challenge/response added Spring 2023.
    Spring 2024 padding added, other extensions removed.

LAST UPDATED
    March 5, 2024

Protocol Description

Please read TFTP RFC 1350.

UDP is a best effort delivery. Packets may be lost, duplicated, or arrive out of order. To create a reliable enough data channel for file transfer, the technique of positive acknowledgements is used.

The file is packetized into equal size packets, called a block, and each is sent individually with its block number. After a data packet is sent, the sender awaits an acknowledgement packet with matching block number. On receiving the acknowledgement it moves on to sending the next data packet.

The other side of the communication waits for data packets and responds with acknowledgement packets. The software pays attention to the block numbers, looking for exactly the block number it expects. If there is a problem, the parties can resend their packets, typically based on a timeout.

THE TFTP PROTOCOL ROUNDS FOR RRQ

           CLIENT   <---->  SERVER
           
round 0
-------
     client sends RRQ 
                    ----->
                           server receives RRQ and 
                           sends DATA block 1
                    <-----
     client gets DATA block 1
     
round 1
-------
     client ACK's block 1
                    ----->
                           server receives ACK 1 and 
                           sends DATA block 2
                    <-----
     client gets DATA block 2
     
round 2
-------

     ...

round n
-------
     client ACK's block n (last block)
                    ----->
                           server closes socket

The port numbers on both side of this communication protocol are taken as a pair to form, along with the IP addresses, a session. A block matching these four elements, two IP's and two port's, belong to that session. The client leads off by sending it request from an ephemeral port (x) to the well known port (69) of the server. The server responds by moving to an ephemeral port (y) in its reply. Between these two computers, the pair (x,y) represents the connection and are called the TIDs, transfer identifiers.

In the RFC 1350 protocol, the last packet is known by it being shorter than the maximum length. We will do a different padding scheme.

The padding scheme used is that the last byte of every data packets is either a 0xff or a 0x00. If it is 0x00, this is the last packet in the transfer; else expect another packet.

In both cases, starting from the last byte work backwards to the first 0xff byte. The data packet, after the first 4 bytes of the packet, up to but not including this byte, is data.

Note that it is possible that the entire packet is 0xff followed by 0x00's. That means the packet carried no user data, and is just sent to signal the end of data.

The Mode string

The first packet in the protocol is from client to server. In our implementation, it will be a RRQ packet with the name of the file to read. Read means to transfer the file from the server to the client.

Included in this first packet is an ascii string with the mode. We change RCF 1350 by not supporting any of the modes there described, and supporting only a newly defined mode called padded. The mode field must be set to the string padded for your program to be correct.

Security

You will have to open your SecurityGroup rules for UDP return packets coming from the 3333 port (or whatever is the server port) and the ephermal port space for the return packets. Best to use open for the network address of your AWS Private IP space. Ask in class or on slack about how this is done.

A general rule for accepting commands over the network:

Treat commands as suggestions, and only do explicitly allowed actions.

It would be as if the command received is a number 1 to N and the server does the N-th preprogrammed action. This obviously cannot be fully implemented. In our case, we have restricted to request to just one paramater, a file name. So we should make sure that the file,

  1. Is a regular files;
  2. The filename contains no directory information;
  3. A therefore the file is in the directory where the server was started;
  4. And is world readable, not a symlink, with a link count of 1.

Time Outs and Errors

Packet resend rules for sender retries

The receiver (client) sends the RRQ and expects DATA 1. 
If the client receives DATA i,
   it sends ACK i and expects DATA i+1
If the client receives DATA i expecting DATA i+1,
   it resends ACK i.
The client does not respond to a DATA j if j is not
   the expected or the previously expected block.
After the client receives the last DATA packet and sends
   the last ACK, it can exit.
   
The sender (server) sends DATA i and expects ACK i.
If after the time out the server has not received 
   the expected ACK i, it resends DATA i
If the server receives ACK i it sends DATA i+1
   and expects ACK i+1
If after the third send of DATA i without and ACK i,
   the server aborts.
When the server receives the ACK for the last 
   DATA packet, the server exits
   
The protocol specification should specify what to do in case of deviations from expected behavior. The possibilities are,

After the RRQ, the server and client exchange DATA and ACK packets. The block number of the DATA packets should increase, and the ACK packets reply with the block number in the DATA packet.

Lost packets will break this pattern of exchange and one or the other side will resend their packet to try to start the pattern again. This must be done carefully to avoid the Sorcerer's Apprentice bug that was discovered in the original TFTP protocol.

If you are asked to implement times outs, we will implement a scheme where the sender is responsible for the resend. For a RRQ the sender is the server and the receiver is the client.

After the client has sent the RRQ, it only responds to packets received. (The exception to this is a resend of the RRQ of DATA 1 is not received.) The reason for sender retries is after the last DATA-ACK pair, the receiver has nothing to expect.

The Foo Sockets

The Foo socket utility aids the project by organizing the sockets into three types.

Implementation notes and restrictions

  1. Of the transfer types, only RRQ is supported.
  2. Of the modes, only the new padded mode is supported.
  3. The block size must be variable, and is set by the define TFTP_LEN in the header file.
  4. The network is big endian. Intel is little endian. Program accordingly.
  5. Do not assume the file transfered is text; do not assume nulls terminate byte buffers; do not assume just because it is written to standard out the characters are printable.
  6. Do not crash because of bad inputs.
  7. Never trust inputs from the network.
  8. It is not excusable that an error surprises your code, and your code crashes. If you want to bail out on bad data, in this class you are free to use an assertion, although user friendly code might handle the error more gracefully.
  9. Be consistent with the RFC in handling of all errrors. Check the TID of the sender and send a non-terminating error as directed in the RFC.
  10. All other errors send a terminating error with correct error number.
  11. Refer to the statment "receiving a packet which cannot be explained by a delay or duplication in the network" for the definition of what is an error.

Debugging run

$ make run-server
echo "The date is: `date` !" > hello.txt
./tptftpd  -v 3333
Line __: server loop entered
Line __: listening on port 3333


socket_recvfrom (foo-socket.c:184)
	got 19 bytes on port 3333 from 54.162.229.55:35195
	00 01 68 65 6c 6c 6f 2e 74 78 74 00 70 61 64 64 
	65 64 00 
(tptftp-server.c,__): found file |hello.txt|, mode |4|
socket_sendto (foo-socket.c:212)
	sent 37 bytes on port 51684 to 54.162.229.55:35195
	00 03 00 01 54 68 65 20 64 61 74 65 20 69 73 3a 
	20 4d 6f 6e 20 4d 61 72 20 32 34 20 30 30 3a 34 
	36 3a 35 38 ff 
socket_recvfrom (foo-socket.c:184)
	got 4 bytes on port 51684 from 54.162.229.55:35195
	00 04 00 01 
socket_sendto (foo-socket.c:212)
	sent 37 bytes on port 51684 to 54.162.229.55:35195
	00 03 00 02 20 55 54 43 20 32 30 32 35 20 21 0a 
	ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
	00 00 00 00 00 
socket_recvfrom (foo-socket.c:184)
	got 4 bytes on port 51684 from 54.162.229.55:35195
	00 04 00 02 


$ make run-client
./tptftp  -v 3.84.208.125:3333 hello.txt
(tptftp-client.c,__): tptftp_client entered
socket_sendto (foo-socket.c:157)
	sent 19 bytes on port 35195 to 3.84.208.125:3333
	00 01 68 65 6c 6c 6f 2e 74 78 74 00 70 61 64 64 
	65 64 00 
socket_recvfrom (foo-socket.c:184)
	got 37 bytes on port 35195 from 3.84.208.125:51684
	00 03 00 01 54 68 65 20 64 61 74 65 20 69 73 3a 
	20 4d 6f 6e 20 4d 61 72 20 32 34 20 30 30 3a 34 
	36 3a 35 38 ff 
The date is: Mon Mar 24 00:46:58socket_sendto (foo-socket.c:212)
	sent 4 bytes on port 35195 to 3.84.208.125:51684
	00 04 00 01 
socket_recvfrom (foo-socket.c:184)
	got 37 bytes on port 35195 from 3.84.208.125:51684
	00 03 00 02 20 55 54 43 20 32 30 32 35 20 21 0a 
	ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
	00 00 00 00 00 
socket_sendto (foo-socket.c:212)
	sent 4 bytes on port 35195 to 3.84.208.125:51684
	00 04 00 02 
client says goodbye

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

Author: Burton Rosenberg
Created: January 18, 2014 as mytftp
Last Update: 24 March 2025