Chris Erway, Ronald Tse.
{cce, rhtse}@cs.brown.edu
Last updated: Saturday, September 17, 2005
Questions: cs161tas@cs.brown.edu
Help sessions (held in the fishbowl, 2nd floor):
Chris - Thursday, 12-2pm
Ron - Wednesday, 6-8pm
Due date: Monday, October 3rd, 10pm.
Your second programming task is to implement the start of a simple asynchronous IMAP sever with NMSTL. Your server should provide service to your client that you have already written (which you will want to improve to support LIST).
Before you get hacking away, you'll need to set up your CS account to be able to access the software you'll be using throughout the semester. Please add this line to your shell startup scripts (and don't be shy to ask the TAs if you have problems!):
source /course/cs161/startups/cs161student
The complete protocol description for IMAP version 4, revision 1 is described in RFC 3501 (and a slightly prettier version is provided for your convenience).
For this assignment you'll start the lengthy task of building a full fledged IMAP server by building a "login server". You will implement thes commands from your login client (LOGIN, LOGOUT, CAPABILITY, and NOOP), plus the LIST command which reports the existence of mail folders. The RFC is the best place to learn how these commands (especially LIST) are intended to operate.
Your server will interact with the filesystem in two ways. For LOGIN, you will read a a password file. For LIST, you will read a directory's contents. Your server should avoid blocking. It should use subprocesses, but handle an arbitrary number of clients without starting a process per connection. For now, you should not cache any filesystem interactions. Each LOGIN or LIST command should reexamine the password file or the user's mailbox.
In the last assignment you wrote the began client side of IMAP, and now you'll begin the server. This time you'll use C++ and the NMSTL library by Jon Salz.
Here are the features we'd like to see in your server:
jj:pssw0rd joe:eoj clyde:inky
You can use your java login client to test your daemon.
Consider the use of ulimit to test that you don't acquire too many file descriptors.
For this assignment, you will be using the networking, messaging and threading library NMSTL. This library provides a simple API to be used in order to implement asynchronous I/O. Here are some the highlights:
io_event_loop is responsible for making all the callbacks on your asynchronous I/O handling objects. You should construct one of these for use throughout your program, and pass it to all instances of you handler subclasses.
run begins the event loop. Call this once you've done all appropriate initialization.
term_handler creates a terminal on stdin through which you can enter commands. You may find this useful for testing and debugging.
on_eof registers a callback that will be made when the term receives EOF (i.e. user enters ctrl+d). This can be used for cleanup.
add_command adds a command to the terminal. Pass it the name of the command, the minimum number of arguments, maximum number of arguments, and the callback to be made when the user enters the command in the terminal. Refer to the online documentation for exact callback format.
These classes are for constant, mutable, and dynamically allocated buffers respectively. These classes will be used throughout NMSTL, refer to online documentation for more details.
this is the io handling superclass on which callbacks are made by the io_event_loop. This class takes an iohandle and io_event_loop, and if appropriate flags are set, it will make callbacks when the iohandle is readable or writable.
This an io_handler subclass responsible for performing asynchronous IO on a stream based connection. Since you will be writing a logind server that implements a simple stream based protocol, this class should be of extreme interest to you. In addition to the methods inherited from io_handler, there are serveral other methods of note.
This callback is invoked when the connection either succeeds or fails. However, since you will be using a tcp_acceptor (see below), this method should be less important to you.
This callback is invoked when the socket to which the net_handler is bound has incoming data. The return value of this method indicates the number of bytes that have been consumed by you object, and can thus be safely discarded from the passed buffer.
This callback is made when the connection is closed by the peer. Any data remaining in the stream is contained in the passed constbuf.
These methods write the passed string/buf to the connection, and buffer any data that has not been sent. The remaining data is sent as the connection becomes readable again. This method allows you to queue data for an asynchronous send. Unfortunately, for these method to function properly, you cannot overwrite wavail, and (to my knowledge) you do not receive a callback when your write has completed.
This helper class sits on a socket waiting for connections. When one is recieved, it accepts this connection, and passes the socket and the io_event_loop to a new instance of the object specified as a template parameter. However, it is your responsibility to ensure that the socket specified in the constructor is valid, this object will not inform you. You specify the type of object to create, along with the type of any argument you wish passed (though this may be omitted). Upon connect, the tcp_acceptor will create a new instance using the constructor new T(io_loop, socket, arg) or new T(io_loop, socket) if no argument is specified. You will probably use an instance of this class to sit on top of your login port.
The constructor of the object. If you wish an argument to be passed into your object's constructor upon the initiation of a new connection, you specify this argument with arg.
This is class to wrap a function callback. The method signature of the callback is specified in the template parameters where R is the return type, and T1 is the first argument, T2 is the second, etc. For a more detailed treatment of the callback class, refer to the online documentation and sample applications.
A smart pointer implementation. Basically a reference counting pointer that will delete the underlying object when no references to it remain. For more discussion and a sample application, refer to the online documentation.
We have provided some basic structure to get you started
(including a Makefile), and a barebones
proc_handler class in proc.h and proc.cc that inherits from net_handler to
provide a buffered interface to subprocesses (that you may use like
the subprocesses of Flash). You should not be at all shy about
modifying and extending this code. It's just something to get started
with. It is available in:
/course/cs161/asgn/logind
Please include a README file explaining briefly which files contain what code, as well as how to run your code. To submit your assignment, place your source files in a directory, and run the following script while in that directory:
/course/cs161/bin/cs161_handin logind
This will cause all the files in the current directory to be submitted. Please contact the TAs if you have problems submitting. For your own sanity, don't leave your submission until the minute before the deadline.
We will be testing your daemon against our custom client that tries to break your deamon. Your code will be graded on the following factors (and their weights).
Documentation and code legibility: | 20% |
Functionality (non-blocking, error reporting, etc): | 50% |
Robustness (malicious input, many connections, etc): | 30% |