Changelog:

Late policy note: In order to ensure that we can get this assignment graded in time, late submissions after the final exam will not be accepted.

Your Task

  1. Download the skeleton code that includes a Makefile template that produces an ftp-server binary that does nothing but accept a port number from the command-line and print out a message about nothing being implemented. (The skeleton also includes a test program for a particular corner case, described below.)

  2. In C or C++, implement an FTP server that supports FTP as described in RFC 959 implementing at least the following commands:
    • USER (which should not require a password or any particular user name)
    • QUIT
    • PORT
    • TYPE
    • MODE
    • STRU
    • STOR
    • RETR
    • NOOP
    • LIST
    • MKD

    (Note that your implementation of some of these commands may be trivial.)

    If you choose, you may implement additional commands. For any command you do not implement, you must return a response indicating that the command is not implemented.

    In addition:

    • Your server must bind to the port number specified on the command line and always bind to 127.0.0.1 (IPv4 localhost).
    • The root directory and default directory of your server should be the current working directory when it is executed, so storing or retrieving either /foo or foo should access the foo file in the server’s current working directory.
    • The LIST command should send the result of running ls -l on the given directory if it exists or result in an error otherwise.
    • You do not need to support RECORD or PAGE structure (only FILE). A client using the STRU R command to change from FILE mode should result in the response “504 Command not implemented for that parameter”.
    • You do not need to support any transmission mode besides STREAM. (You may give an error if a client tries to set another transmission mode.)
    • Except for the LIST command, you only need to support the Image file type (“binary mode”); that is, you will return byte-for-byte copies of files, rather than havdling the NVT-ASCII (“text mode”) representation. If a client attempts at STOR or RETR without first switching the binary mode, it should result in the response “451 Requested action aborted: local error in processing”. (For LIST, you need to allow transfers with the ASCII Non-print type, but we will not check whether you transform newlines into CRLFs.)
    • Your server only needs to handle one active client at a time.
    • You server only needs to support IPv4. When/if using getaddrinfo, I’d recommend setting ai_family to AF_INET in the hints.
    • You may assume that FTP commands are no longer than 4096 bytes.
    • You may reject any pathnames containing “..” to prevent clients from accessing things outside the server’s current working directory. (It is okay if you reject pathnames which have “..” in the middle of a filename like “/foo..bar”.)
  3. Create a .tar.gz archive, like the one make submit would create and submit via the usual submission site.

FTP References

  1. RFC 959 — the official specification. Sections 4, 5 and 6 are most useful. Section 7 has examples.

  2. List of raw FTP commands at http://www.nsftools.com/tips/RawFTP.htm

  3. http://www.cs.colostate.edu/helpdocs/ftp.html

Sockets References

  1. the Wikipedia article on Berkeley Sockets

  2. the POSIX documentation — search for individual functions like socket, getaddrinfo, bind, accept, listen.

  3. this example echo server and example echo client.

  4. the slides and lecture recording from 15 November

Hints

Testing

  1. We suggest primarily testing with an FTP client like the ftp command in your VM. You can run a command line

     ftp 127.0.0.1 PORT-NUMBER
    

    where PORT-NUMBER is the same value you passed as an argument to your server program.

  2. You should be able to do at least the following in the FTP client: (This list may not be exhaustive.)

    • Create a directory
    • Get an error when trying to create a directory that already exists.
    • After setting binary mode, store a file
    • After setting binary mode, store a file that contains NUL characters in its contents
    • After setting binary mode, retrieve a file
    • After setting binary mode, retrieve a file that contains NUL characters in its contents
    • After setting binary mode, fail to retrieve a file that doesn’t exist
    • List the root directory by running ls without an argument
    • List the root directory by running ls /
    • List a subdirectory by running ls the-subdir or ls /the-subdir
    • Quit
    • Restart the client and connect again to the server after quitting (without restarting the server)
  3. Note that you will need to test your server with a port number higher than 1024.

test-partial-reads

  1. We have supplied, with the skeleton code, a test-partial-reads program which connects to your server and sends the following commands

     USER foo
     MKD testSplitName
     QUIT
    

    but the MKD command is written by writing MKD testSplit, waiting one second, then writing Name<CRLF>. This can help test whether your server handles the case where read returns less than expected in the control channel. (In practice, this would probably only be a problem for much longer commands or if someone were manually typing in commands or if you wanted to add support for a client sending multiple commands without waiting for replies to those commands.)

General advice on order of implementation

(This does not represent the only, nor perhaps the best approach.)

  1. I would suggest first getting the server working enough that the FTP client can connect and you can parse and reply to the commands it sends with a not implemented message.

  2. Commands that require a data connection are probably the hardest to implement, so I would save them for last. LIST is probably the most complex command to implement.

Dealing with partial reads

  1. When using read, it is possible that you will read less than a line or parts of multiple lines. To deal with this, I recommend keeping a buffer of bytes you’ve read but not processed, searching that buffer for the newline character sequence, and, as long newlines aren’t present, calling read again to add to the buffer.

Select specification details

  1. Note that lines sent over the network typically end with a CRLF, which is a two byte sequence.

make directory

  1. mkdir() is the POSIX functions creating and removing directories respectively

on QUIT

  1. QUIT should not exit the server, only close the current connection.