changelog:

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 (for its server socket) 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 can run the ls command to do this; you do not need to reimplement it.
    • 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”.)
    • Your server’s data connections may be initiated from any port number (but must connect to the client).
    • We will not test if you support the default client data port (the port number used if no PORT command is run).
  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 from 9 April and 11 April and their lecture recordings

  5. the slides and lecture recording from 15 November Fall 2018

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. Lines sent over the network typically end with a CRLF, which is a two byte sequence.

  2. The specification requires that clients not send anything after they connect until they receive an initial message from the server. This is the most common cause of the client hanging.

  3. Unless the specification clearly specifies an exact status code to use (like it does for MKD), you can generally supply any sensible status code listed in the specification’s list of status codes with the same first digit.

  4. The specification has a very long list of potential error codes. We do not expect you to be able to produce all of these; for example, it is acceptable to allow additional variants of a command and therefore never report a “syntax error” for a particular command.

    What is most important is that you do not claim operations are successful (2xx status) when they are not, and that your server produces codes that accurately reflect the state of any data connection.

make directory

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

on QUIT

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

on strace

  1. The strace command can show you all the system calls your server is making. Notably, this will show you everything being read/written to the network, so I find it helpful for debugging servers that seem not to be behaving correctly.