Lab 2: A Concurrent MTA|
Due: 12:30pm, Thursday, April 15th, 2010
As promised, in this lab you're extending the functionality of the
simple Mail Transfer Agent (MTA) you wrote for Lab 1. In particular,
you're going to write an event-driven server that can processes
multiple messages concurrently. By concurrently, I mean that, if your
MTA is waiting for some additional information from one client, it
should be able to process requests from additional clients. In
particular, your server should be able to successfully receive and
forward a mail message from one client while receiving a message from
another. Further, your MTA should support concurrent forwarding.
Namely, if a message has multiple recipients, the lack of response
from one destination should not delay the delivery of the message to
the others. Finally, you should be able to receive additional
messages from the same client even before the previous one has been
forwarded. Note this may lead to out-of-order forwarding of messages
in many cases.
Your MTA will continue to use the Internet-standard Simple Mail
Transfer Protocol (SMTP) documented in RFC 2821.
You still don't need to handle local delivery of mail messages.
You are responsible for the following requirements. Most of them are the same as in Lab 1. New ones are in bold:
You do not need to bother with the following things. You should just
ignore them as best you can.
- Your MTA is able to properly receive multiple, simultaneous
messages from well-behaved MUAs like Netscape, Outlook Express, or
Mozilla. This will require you to support the following SMTP
commands: HELO, MAIL, DATA, RCPT, QUIT, and RSET.
- You may not use the fork() system call.
- You do not need to handle messages with more than 255
- You do not need to handle mail messages larger than 65535
- Your MTA should deliver each message to every specified recipient
if possible. If an error occurs, and it is possible for your MTA to
continue processing additional messages, it should do so. If it
encounters an unrecoverable error, i.e., socket(), bind(), or listen()
fails, it should print an error message and call exit(1).
- If message delivery for any particular recipient fails at all
the available mail exchangers, the
message should be queued for an additional attempt. The amount of
time to wait before attempting a second delivery should be specified
on the command line. If, after two attempts at every listed mail
exchanger, the message cannot be
delivered to a particular recipient, an "undeliverable mail" message
should be generated.
If you are able to send multiple, simultaneous email messages from two
or more MTAs, you're in good shape.
- You don't need to support local destinations or source-routed
messages. If the argument to the RCPT TO: command is not of the form
email@example.com, you should refuse to accept the message.
- Your MTA may block (and not process any other
messages or clients) during a call to gethostbyname() or getmxrr().
- You do not need to support the use of dotted-quad IP addresses instead of
domain names in both the HELO, MAIL, and RCPT commands.
- You don't need to implement any other SMTP commands like EXPN,
VRFY, HELP, ETRN, NOOP, or VERB. You may simply return an error
message if your MTA receives one of these commands.
- ESMTP extensions. You do not need to recognize EHLO or any of the
additional parameters to standard SMTP commands.
- You do not need to implement the data transparency procedure
described in Section 4.5.2 of the RFC. Basically, you can assume your
MTA will never receive a message with a period as the first character of a
- You should ignore all headers in the DATA portion of the message.
Further, you do not need to implement any sort of loop detection.
Building your MTA
We have provided an initial skeleton directory to get you started. It
is available as /net/cluster/cse223b/sp10/labs/lab2.tgz on the
class machines. You should copy this file to your working
directory. The following sequence of commands should extract the
files and build the initial (useless) executable:
% tar xzf lab2.tgz
% cd lab2
The tarball contains four files: domain.c, domain.h, Makefile, and
mta2.C. mta2.C contains some initial code to get you started.
Currently, it just accepts TCP connections, prints debugging
information, and waits for others. It also runs a toy timer set to
the delay value specified on the command line. You can run the
executable by typing:
% ./mta2 30 2225
Where 30 is the number of seconds to wait between delivery attempts,
and 2225 is the port you want it to listen on. In order not to
conflict with the already running MTA (sendmail), you should run your
MTA on a port other than 25 (larger than 1024). During testing you're
likely to want to use something other than 2225 as well so it doesn't
conflict with other students working on their projects.
The provided code has the makings of a select-based event loop, as
discussed in class. I have also included an example of how to set a
timer, although you will likely need to do something more
sophisticated since you may need keep track of timers for multiple
messages at once. As you can see, each connection is managed by its
own object, which defines callbacks to be called from the event loop.
Feel free to change our object structure around as you see fit.
One final note: by default, connect() is a blocking system call. In
order to prevent your MTA from hanging while attempting to contact a
remote MTA, you'll need to set the O_NONBLOCK flag on any socket you use to connect
to another server. I suggest you do a man on the fcntl() system call.
With the socket set non-blocking, the connect() call will return
immediately, whether or not it was successful. In order to determine
when the socket has successfully connected, you can check for
writability in the select() loop. Once select() indicates the socket is
writable, it has either connected or failed. You may find the
getpeername() function useful to determine whether the connection
succeeded or not.
Testing your MTA
The easiest way to test and debug your MTA is to use it interactively
just as we did above. Of course, once your MTA accepts messages,
you'll need to send them on. I'd suggest trying to send messages to
yourself so you can see if it actually succeeds! You might find
programs like Ethereal and Netcat (nc) useful to follow the communication between
your MTA and others.
In order to test the ability of your MTA to continue to process
messages while waiting for a slow peer MTA, I have provided an
infinitely slow MTA running on 10.cse223b.snoeren.usher.ucsdsys.net;
will never accept messages, just accept connections and hang. If
you'd like to test your MX handling as well, there is an MX record for
tarpit.usher.ucsdsys.net that points to
10.cse223b.snoeren.usher.ucsdsys.net. So, if you attempt to send a
message to firstname.lastname@example.org, your MTA should never
succeed, but continue processing other mail. If you find this infinite
wait unsatisfying, you are welcome to add an additional timeout to your
MTA that aborts open connections if they stall for longer than the
Once you think your MTA is working, you can hook it up to a real
MUA and give it a whirl. The simplest way is to use your normal mail
reader (Eudora, Netscape, Mozilla, Outlook Express, etc.) and just
configure it to use your MTA rather than your default one.
For Mozilla, you can do this by going to Edit->Mail and Newsgroup
Account Settings and selecting the Outgoing Server (SMTP) tab. Enter
the name of the machine your MTA is running on (e.g.,
7.cse223b.snoeren.usher.ucsdsys.net) and port 2225. Make sure you
turn off SSL (Never) and uncheck the user name and password option.
Similar changes can be made to other mailers. I'll post them here if
anyone wants to email me how to do this in your own mail reader.
All code for this assignment must be written individually. You
are not allowed to look at anyone else's solutions or solutions to
similar assignments you may find for courses at UCSD or other
institutions. You may discuss the assignment with fellow students,
but all code you submit must be either yours alone or code that was
provided to you as part of the assignment.
The turnin procedure is the same as for the previous lab. When you're
ready to submit your code, you can execute the following command:
% gmake turnin
which will copy your tar file to the submission directory.
|back to top ^