\
Introduction - Logistics - Assignment
- Time Line - Evaluation
Hand-in - Coding Style -
Resources - Hints - Extra
Credit
The purpose of this project is to give you experience in developing concurrent network applications. You will use the Berkeley Sockets API to write a Trivial File Transfer Protocol (TFTP) Server.
TFTP is a deliberately simplified file transfer protocol. Specifically, it supports the bare minimum set operations needed to get and put files. Compared to FTP, it does not support listing of directories, renaming, or access control. It is most commonly used in booting diskless workstations. The protocol is simple enough for a client to be implemented in the workstation's firmware, which uses it to fetch the operating system kernel to boot.
You are to work in groups of 1 (alone) on this project. You are free to discuss the project and references with your classmates, but your solution must be your own.
You must code your server in C (not C++). While other languages may provide features to make network programming easier, most kernel-level networking code is written in C and future assignments will require implementing such code in a simulated environment.
You may develop & test your code on any operating system you wish, however, we will grade your code on Andrew Linux machines, so please make sure your code compiles and runs as expected on them.
Your server must :
- Implement the protocol specified in RFC 1350 with the following changes :
- Only
octet
mode needs to be supported (see section 5 of the RFC for mode information). The other specified modes may be implemented for extra credit (see below).- The server should listen for initial connections on a UDP port that is a command-line parameter (instead of fixed at 69 as stated in the RFC). This is to allow multiple students to run their servers on the same machine without interference.
- Support simultaneous connections from multiple clients
- Not utilize more than a single thread or process. Future projects involving kernel-level networking code will have this requirement, so this is a good opportunity to practice working in that environment.
- Serve only those files contained in the directory it is run in.
- Terminate after one hour. In case you inadvertently leave your server running, at least it will eventually stop. Since the server process can access any files you let it, running it longer than necessary may present a security risk.
- PLEASE make sure your binary file is named tftpd. (note the "d" at the end)
- We will test your server using the following command:
./tftpd [port]The RFC leaves several details up to the implementor. You should justify the significant choices you make in your README file. You may also find yourself constrained by resource or OS limitations. For instance, you may not be able to support an infinite number of simultaneous clients, or certain file names. You should identify what these limits are and do something reasonable when they are reached. These should also be documented in the README.
As with all network services, your server should be robust against dropped packets, clients that do not correctly implement the protocol, and malicious attacks. Thus, a large part of this assignment (and programming in general) is knowing how to test and debug your work. There are many ways to do this; be creative. We would like to know how you tested your server and how you convinced yourself it actually works. To this end, you should submit your test code along with brief documentation describing what you did to test that your server works in a file called TEST. The test cases should include both generic ones that check the server functionality and those that test particular corner cases. Several paragraphs (or even a bulleted list of things done and why) should suffice for the test case documentation. If your server fails on some tests and you do not have time to fix it, this should also be documented in the README (we would rather you know and acknowledge the pitfalls of your server than miss them).
Most operating systems (including Andrew Linux and Solaris) include a TFTP client program, tftp, that you can use to test your server. By default, it assumes the server is on the standard port 69, but the default can be overridden by specifying a port number after the host name. You can get the complete manual through man tftp, but for a quick tutorial:
Running tftp presents you with a tftp> prompt.
At the tftp> prompt, connect host port tells the program to use host "host" and port "port" for all subsequent operations. Note that this step only sets a variable - it does not send any messages. Invoking tftp host port has the same effect, with no need to issue a connect.
The get and put commands start the sequence beginning with RRQ and WRQ packets respectively.
trace causes the client to print the header of every packet in receives or sends, which could come in handy for debugging.
binary tells the client to useoctet
mode.
If you are using a non-Andrew machine, be sure you're using the latest version of its tftp client - older versions included with some Linux distributions may be buggy. In particular, netkit-tftp-0.17 or earlier, and tftp-hpa-0.15 or earlier are known not to work. As far as we can tell, the versions on Andrew are correct.
We strongly recommend you start early. To help "guide" you through this lab, we have provided you some key milestones!
M T W R F S S 23 24 25 26 27 28 29 30 31 1 2 3 4 5 6 7 8 9 10
- Jan 23rd - Assignment is released. Start reading, and planning out your server
- Jan 25th - Discussion of the project in class, please complete what is listed under 1/23 by then.
- Jan 29th - Your server should be able to handle single-client GETs. We will release a test script for your personal use to check this!
- Feb 2nd - You should have a solid server. Handles GETs and PUTs. Also some robustness checking should have been done. We will release a test script testing multiple GETs for your upcoming working on multiple clients.
- Feb 8th - Your server should handle multiple clients. This allows your 2 days to do more robustness testing.
- Feb 10th 1159pm - Your code should be handed in.
Your executable must be named tftpd and accept the port number as its first argument. It must run correctly when given only one argument. Any additional arguments should be documented.
Your project must include a makefile called Makefile. We will build a binary from your source code using the makefile and GNU make. The makefile for this project should be simple. If you need help creating the makefile, everything you need to know (and much much more) about GNU make can be found in the GNU make manual. We will build your program by executing rm -f tftpd, and then make tftpd. Please test this before handing in to avoid unnecessary deductions.
Your makefile should run gcc with option -Wall and should produce no errors.
You should submit the following files:
- Makefile, *.c, *.h
- README (in standard Unix text file format, please!) discussing how you approached the assignment. Also include any known issues/bugs.
- TEST - A description of what tests you have run, how you ran them, and why you think your solution "works".
- Test code/scripts used to determine correctness of your server
- EXTRA - If you did anything for extra credit, describe it in this file.
Updated handin instructions will be provided via the course bulletin board and the project page.
As the course progresses, we expect you to develop and refine your personal coding style. By this we mean that there are many reasonable ways to write code so it is easy for people to read (not that anything you define as "your style" will be acceptable). Below we have provided you with a list of coding standards put forth by various groups at CMU and elsewhere. We strongly suggest that you spend some time looking through several of these before you start coding.
socket,
bind, connect, select, select_tut, getsockname, sendto, recvfrom, ip, udp
bind
, connect
and friends. In fact, pages 35-36 in the textbook contain a program that does
the similar steps, but for TCP.read, recv, send, printf
, and many others, block,
or halt, your program until the action the call is performing completes. For
example, recv
will wait until it has received at least some data
to return. If nobody sends your program any packets, recv
will
never return. While this is a perfectly logical behavior, it means that if
you're trying to listen to two sockets you can't simply alternate recv
ing
on both.select
syscall. It
waits until any one of a specified set of file descriptors (sockets) has data
waiting to be read, and tells you which ones are ready. Thus, you know which
sockets you can call recv
on without getting blocked. select
also takes a timeout parameter, which causes it to return if no data arrives
on any socket in the set within the time interval. After reading the select
manpage, you should be able to extend your previous program to handle multiple
connections. Note that it is possible to perform this task without using select
at all - for example, by using poll
or non-blocking sockets instead.
alarm()
early in your main()
function. alarm(n)
schedules
a SIGALRM
to be sent n
seconds in the future. Unless
you are using signals in your program, the default action for SIGALRM
is to terminate the program. Another way of doing this is to keep track of
the total time spent waiting in select()
and exiting when it
is time to.bind
parameters so that it only accepts connections from
127.0.0.1 instead of INADDR_ANY
. Once you are confident that
your server is secure, you can remove this restriction.