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.
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. 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. This includes the possibility of a data packet carrying zero bytes of data. We will modify this to do a padding scheme were every packet is the same length, but bytes are added to the end of the real data to show where it might end in the packet.
There is always room for one byte following the data. However there might be more. The first byte following the date is set to 0xff to mark the end. The remaining bytes are set to 0x00 until the end of the packet, to distinguish those byte both from the end of data marker and from any other data.
The unpacking algorithm is to go the last byte in the packet, and move towards lower indexed bytes while a 0x00 is seen. The first non-zero should be 0xff.
The termination condition then applies to the unpadded block; so that in effect, if the last byte is 0xff more blocks follow. If the last byte is 0x00 this is the last block.
The Mode string
The first byte in the protocol is from client to server. It includes whether the interaction will be a read or a write, and the name of the file to read or write. A read is a file on the server being sent to the client, and a write is a file on the client being sent to the server.
We will only implement reads.
This first packet also has a string for the mode of the data. It RFC 1350 it is either netascii, octet or mail. We will use none of these and set the mode to padded, and otherwise the data will be consistent with the octet transfer mode. The octet mode delivers raw bytes without conversion of processing. Any numerical value is permissible in octet mode, even the zero byte, carriage returns or new line codes, which might otherwise be treated as special.
The Foo Sockets
The Foo socket utility aids the project by organizing the sockets into three types.
create_foo_socket(0)
.
socket_sendto(sock_c, to_host, to_port, buf, msgsize)
.
socket_recvfrom(sock_c, buf, buf_len)
.
socket_replyto(sock_c, buf, numbytes)
, so that
packets to back to the server's selected ephemeral port.
create_foo_socket(listen_port)
, giving the port to listen on.
socket_recvfrom( sock_listen, buf, buf_len)
.
create_session_socket(sock_listen)
,
which binds to an ephemeral port, and has the client's port copied over from sock_listen
.
socket_replyto( sock_session, buf, numbytes )
.
socket_recvfrom( sock_session, buf, buf_len )
.
Security
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,
Implementation notes and restrictions
Debugging run
$ make basic-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 echo "The date is: `date` !" > hello.txt ./ttftpd -L -v 3333 & ./ttftp localhost:3333 hello.txt > hello.txt.out Line 100: server loop entered Line 101: listening on port 3333 socket_recvfrom (foo-socket.c:160) got 25 bytes on port 3333 from 127.0.0.1:41016 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 (ttftp-server.c,86): found file |hello.txt|, mode |4| goodbye diff hello.txt hello.txt.out socket_sendto (foo-socket.c:188) sent 48 bytes on port 37476 to 127.0.0.1:41016 00 03 00 01 54 68 65 20 64 61 74 65 20 69 73 3a 20 54 75 65 20 4d 61 72 20 20 35 20 32 32 3a 32 39 3a 34 39 20 55 54 43 20 32 30 32 34 20 21 0a socket_recvfrom (foo-socket.c:160) got 4 bytes on port 37476 from 127.0.0.1:41016 00 04 00 01 *** *** PASSES basic test ***
Author: Burton Rosenberg
Created: January 18, 2014 as mytftp
Last Update: 5 March 2024