TTFTP Project

by: burt rosenberg
at: university of miami


The original best-effort, packetized data delivery

The Truly Trivial File Transfer Project

The Truly Trivial File Transfer Protocol is a simplification 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
    ttftp
    
SYNOPSIS
    ttftpd [-vL] [-s secret] port
    ttftp [-v -s secret] hostname:port filename    
    
DESCRIPTION
    Implements a client and a server for the tftp protocol. The deamon version (ttftpd)
    implements the server listening on port port, with an optional authenticaion secret
    
    If called as a client the program uses the tftp protocol to read file filename
    from host host 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. 
    
    The protocol is modified so that the mode field is a colon separated random 
    challenge and the MD5 hash of the challenge with the secret, base 64 encoded. 
    
	The block size is not as stated in the RFC, but will be 128 bytes.
    
OPTIONS
    -L If invoked as server, do not loop. Service one read request then exit.
    -s secret for authentication
    -v Verbose. Helpful debugging output to stdout. 
 
NOTES
    Implement only read. If requested return an error packet with 
    message "mode not supported". 
    
    Data requester to resend DATA on acknowledgement timeout.
    
    Acknowledgement timeout, and maximum acknowledgement retries to be a define in the
    code, and can be changed by recompiling with the -D compiler option.
    
    Default acknowledgement timeout is 4 seconds, and 4 retries.
    
    Maximum filename length is 256 characters, and cannot contain a pathname. 
    
    Server can be single-threaded.
    
    The block size is not as per protocol but is 128 bytes.
    
    The mode field is base64 encoded challenge:MD5(secret||challenge).
    
HISTORY
    First introduced in Fall 2003. Made Truly Trivial in Spring 2015.
    Authentication by challenge/response added Spring 2023.

LAST UPDATED
    February 24, 2023
	

Detailed description

Please read TFTP RFC 1350.

Create a single program, ttftp, that functions as client and server, according to command line options. Truly Trivial is a client server program supporting only reads. This means a file on the server will be sent to the client. The client initiates with a RRQ (read request) to a listen port number on the server machine, from a freshly chosen ephemeral port pc.

The server responds either with an error packet or with data block number 1, of up to 516 132 bytes, 4 bytes header and 512 128 bytes data. This data packet is sent not from the listen port but from a freshly chosen ephemeral port ps. All subsequence communication between client and server will be between ports pc and ps. This creates a session.

In response to the data packet, the client sends an acknowledgement packet with matching block number. This protocol continues alternating data packets from the server with acknowledgement packets to the server until the read file has no further bytes. The end of file is signaled by the server sending a data packet of less than 516 132 bytes. Note that this includes the possibility of the last data packet containing 0 bytes of data (being 4 bytes in length).

After the acknowledgement to the last data packet, the server closes the socket bound to ps. After acknowledging the last data packet, and the client lingers for linger-time in case the server retransmits the data packet, then closes the socket bound to pc. The protocol is then done.

Positive acknowledgement protocols

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

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. Each data packet sent needs to be positively acknowledged as arrived and correct before the next data packet is sent.

If the data is not acknowledged, one of the two participants, or both, will resend based on a timeout. After an excessive number of resends, the protocol terminates for that side.

Client-side timeouts

On all but the last round, the client resends the ACK/RRQ that opened the round if it does not get the corresponding DATA packet as a reply. On the last round, the client can send the ACK and close its side of the session immediately.

The server creates a session on the RRQ and waits for an ACK for this data block or the previous data block. It only needs match the block number of the ACK. However, to avoid the Sorcerer's Apprentice Syndrome, it will never resend an already acknowledge block. This represents an ACK arriving very delayed.

On the last data block, the server lingers waiting for either of,

  1. The ACK for the last data block and the server ends the session.
  2. Or the ACK for the next to last data block. The server resends the last data block and repeats its wait for the matching ACK.

The Listening, Session, and Client socket roles

There are three socket roles,

You have some code to write, to clone out the listening socket into a new session socket. That code should be in ttftp-server.c.

Implementation notes and restrictions

  1. Of the transfer types, only RRQ is supported.
  2. Of the data types only OCTET is supported.
  3. The mode string in the RRQ should be set to "authenticate".
  4. The block size of 512 is changed to 128.
  5. Time outs need not be supported in the project; a multi-threaded server is.
  6. The network is big endian. Intel is little endian. Program accordingly.
  7. 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.
  8. Do not crash because of bad inputs.
  9. Never trust inputs from the network.
  10. 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.
  11. 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.
  12. All other errors send a terminating error with correct error number.
  13. 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.

Security

This paragraph was inserted after the due date, and therefore is not part of the assignment.

As a general rule, never let your program do anything you have not expressly allowed it to do.

We are permitting the sending of certain files; those files are,

  1. regular files existing in the current directory only,
  2. with world readability.
One approach is to list the current directory, for world-readable regular files, and send one of them. The one we will send is given by the hint of the filename in the read request. Here is the thing: we do not do what the read request tells us to do. We accept it only as a hint to choose among the things we agree to do.

As we list the current directory, for files that are acceptable to us, we then strcmp against the file request. If the names are equal, we then send the file that we independently stat'ed and opened.

Debugging run

% make random-test
cc  -c -o ttftp-client.o ttftp-client.c
cc -c -o foo-socket.o foo-socket.c
cc  -o ttftp ttftp.c ttftp-client.o foo-socket.o
cc  -c -o ttftp-server.o ttftp-server.c
gcc -o ttftpd ttftpd.c ttftp-server.o foo-socket.o
dd if=/dev/random bs=1 count=127 > hello.txt
127+0 records in
127+0 records out
127 bytes transferred in 0.000400 secs (317500 bytes/sec)
./ttftpd  -L -v 3333 &
./ttftp localhost:3333 hello.txt > hello.txt.out
Line 76: server loop entered
Line 77: listening on port 3333
socket_recvfrom (foo-socket.c:160)
	got 25 bytes on port 3333 from 127.0.0.1:63526
	00 01 68 65 6c 6c 6f 2e 74 78 74 00 61 75 74 68 
	65 6e 74 69 63 61 74 65 00 
goodbye
socket_sendto (foo-socket.c:188)
	sent 131 bytes on port 60281 to 127.0.0.1:63526
	00 03 00 01 59 70 24 fa ac ef 74 6e 93 86 72 59 
	00 01 43 d9 6f 00 bc 43 8a b3 6e ab b5 30 8e 66 
	9b 09 ec 6b 90 ef 45 0c e1 32 a2 d1 70 5b cc 21 
	83 12 81 ee bb ac 3a 87 f2 78 75 b1 1b 6b d6 6b 
	f3 06 79 54 e1 6f db 58 c3 06 e3 7b 84 3b 15 c8 
	67 19 d9 f2 a3 6c 67 3c 4c 20 f2 e7 dc e7 71 34 
	ee a9 68 e7 00 c5 66 49 20 f0 af 92 82 e3 2e c1 
	32 43 c7 a2 82 e8 05 cb 38 04 7a e1 40 c0 8c 7c 
	29 37 c2 
socket_recvfrom (foo-socket.c:160)
	got 4 bytes on port 60281 from 127.0.0.1:63526
	00 04 00 01 
cmp hello.txt hello.txt.out
***
*** PASSES random test 127 bytes
***
dd if=/dev/random bs=1 count=128 > hello.txt
128+0 records in
128+0 records out
128 bytes transferred in 0.000281 secs (455516 bytes/sec)
./ttftpd  -L -v 3333 &
./ttftp localhost:3333 hello.txt > hello.txt.out
Line 76: server loop entered
Line 77: listening on port 3333
socket_recvfrom (foo-socket.c:160)
	got 25 bytes on port 3333 from 127.0.0.1:54306
	00 01 68 65 6c 6c 6f 2e 74 78 74 00 61 75 74 68 
	65 6e 74 69 63 61 74 65 00 
goodbye
socket_sendto (foo-socket.c:188)
	sent 132 bytes on port 61291 to 127.0.0.1:54306
	00 03 00 01 7f 8a 35 7e 54 6c 28 69 d8 39 ef 56 
	98 f3 9d 88 db d6 35 46 de 4f d9 59 ea 47 66 c0 
	ce 1f 4c b3 98 a5 07 b1 0b 54 ac 21 8d 53 ef 11 
	ae d8 78 27 b1 d0 f5 96 c3 ba e8 54 3d 46 05 e0 
	96 d9 c6 a3 78 7c 80 b2 ff a1 bb 91 f8 c9 d2 3e 
	16 2c 1e ed 44 c5 f0 a8 c3 8f b7 ac 08 f2 91 4b 
	aa db da 62 71 fa 0a 53 9d d0 0f c3 5c 2d f2 97 
	8d bd 1c 0d 6b 3b 5a dc 58 b2 d7 35 a0 1b 02 5c 
	3d 0c de 90 
socket_recvfrom (foo-socket.c:160)
	got 4 bytes on port 61291 from 127.0.0.1:54306
	00 04 00 01 
socket_sendto (foo-socket.c:188)
	sent 4 bytes on port 61291 to 127.0.0.1:54306
	00 03 00 02 
socket_recvfrom (foo-socket.c:160)
	got 4 bytes on port 61291 from 127.0.0.1:54306
	00 04 00 02 
cmp hello.txt hello.txt.out
***
*** PASSES random test 128 bytes
***
dd if=/dev/random bs=1 count=129 > hello.txt
129+0 records in
129+0 records out
129 bytes transferred in 0.000278 secs (464029 bytes/sec)
./ttftpd  -L -v 3333 &
./ttftp localhost:3333 hello.txt > hello.txt.out
Line 76: server loop entered
Line 77: listening on port 3333
socket_recvfrom (foo-socket.c:160)
	got 25 bytes on port 3333 from 127.0.0.1:61356
	00 01 68 65 6c 6c 6f 2e 74 78 74 00 61 75 74 68 
	65 6e 74 69 63 61 74 65 00 
goodbye
socket_sendto (foo-socket.c:188)
	sent 132 bytes on port 61029 to 127.0.0.1:61356
	00 03 00 01 a8 c8 bc c9 c3 e4 d1 fc 09 1c 5a b9 
	8b bf cc 47 a2 65 70 a6 a7 b9 0a f6 e1 1d 7d 31 
	7f 37 7e 95 15 34 e8 5d 70 ff 85 f2 08 09 18 d8 
	1c f5 5b 14 ed e5 23 bf c4 c6 bb 7f 22 e9 03 5d 
	27 92 36 ff 4f 41 78 e5 69 53 24 1c fc 63 ef b6 
	b0 93 c1 7b ee 4b ff 56 b2 71 5a 3d 26 c2 b9 d4 
	56 be 78 cc 11 ec 28 0b 72 0d 6a 7d f3 6e 24 d7 
	43 e4 1f 3a 19 8a cf 67 20 e6 41 76 81 47 7f 44 
	7d 1d 2b 63 
socket_recvfrom (foo-socket.c:160)
	got 4 bytes on port 61029 from 127.0.0.1:61356
	00 04 00 01 
socket_sendto (foo-socket.c:188)
	sent 5 bytes on port 61029 to 127.0.0.1:61356
	00 03 00 02 8d 
socket_recvfrom (foo-socket.c:160)
	got 4 bytes on port 61029 from 127.0.0.1:61356
	00 04 00 02 
cmp hello.txt hello.txt.out
***
*** PASSES random test 129 bytes
***
rm hello.txt hello.txt.out

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:1 March 2023