HW 3: ftpread


CS 494/594 Homework Bart Massey

In this assignment, you will write ftpread, an FTP reader client in C in the style of the tftpread program written in class.

FTP is described in RFC 959. You should start by reading and thoroughly understanding the relevant portions of this RFC.

Your client will start by making a TCP "control" connection to the FTP daemon on a host specified on the command line. You will then establish a TCP "data" connection to retrieve a file at a path specified on the command line, writing the retrieved file to standard output as tftpread does.

Your client must authenticate itself with username "anonymous" (spelled correctly) and your email address as the "password", as per the specifications for anonymous FTP.

Your client must be able to retrieve a file in binary stream mode. There are two basic types of retrieve: "active" and "passive". In an active FTP retrieve, you listen for a data connection, then retrieve the file from that data connection when the server connects to you. In a passive FTP retrieve, you connect to a port specified by the server and retrieve the data from there.

For the purposes of this assignment, you are successful when your ftpread program can retrieve the file hello.txt from the host svcs.cs.pdx.edu using FTP. CS 494 students may choose to do only an active retrieve. The tftpread of CS 594 students must be able to do either an active or passive retrieve, as specified on the command line.


This is a fairly challenging assignment. Here's some hints to make it easier.


You may want to start with tftpread, available on my Github (https://github.com/BartMassey/). It contains a bunch of code that is similar to code you will need for ftpread. If you cannot use git, note that there is a "ZIP" button near the top left of the page that will create a ZIP archive to download.


You will be sending a lot of commands, and parsing a lot of responses. One good trick is to use fdopen() to turn the file descriptor of your socket into a FILE * suitable for use with standard IO functions such as printf() and scanf(). Note that you need to fdopen() the input and output of the socket separately, else you may get confusion with buffering.

    FILE *s_in = fdopen(s, "r");
    FILE *s_out = fdopen(s, "w");

expect / send

Parsing response codes gets pretty tedious too, so you will want some code to handle that. I recommend my "expect / send" library, also available on my GitHub. This (documented) C library provides simple functions for sending commands and dealing with expected responses.

You will probably want to just hardwire your client to not handle unexpected response codes gracefully: just exit. That's what my expect / send library does, and it's fine for this assignment. The easiest way to discover what response codes to expect is via experimentation.

The PORT command

Whether doing an active or passive transfer, you will need to use the PORT command to inform the server of the IP address and port number of your end of the socket. The best way to get this information is via the getsockname() call. Make sure you send the PORT bytes in network order.

Passive transfers

The order of operations for a passive transfer is pretty specific: you must send PORT, then PASV, then connect() the data socket, then RETR. Any other order is unlikely to work.


You will want to get a real FTP client and try retrieving the specified file that way. Use wireshark / tshark to watch the flow of commands and responses during the transaction; this is basically what you are trying to duplicate.

Make sure you turn on the cexpect_verbose variable in my expect / send library if you are using that library; it is really useful to see what your program is doing.

Misc Hints

It is really easy to get host and network byte-order problems. The routines htonl(), ntohl(), htons() and ntohs() do conversions between host and network byte order for short (16-bit, like port numbers) and long (32-bit, like IP addresses) integers.

The routine inet_ntoa() will convert a 32-bit IPv4 address into a string for printing purposes.


You must submit the following:

Your C source code, together with any other files needed to try it out.

A README.homework file containing a writeup in plain text of not more than 1000 words describing what you did, how it worked, and anything else you think we should know.