 |
|  |
 |
 |  |  |
 |
Lab 2: A Concurrent MTA
Due: 11:00am, Tuesday, April 19th, 2011
Preliminaries
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.
Specification
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:
- 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
recipients.
- You do not need to handle mail messages larger than 65535
bytes.
- 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.
You do not need to bother with the following things. You should just
ignore them as best you can.
- 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
user@domain.name, you should refuse to accept the message.
- Your MTA may block (and not process any other messages or clients)
during a call to gethostbyname()/getmxrr() or the other provided MX record
retrieval functions.
- 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
line.
- You should ignore all headers in the DATA portion of the message.
Further, you do not need to implement any sort of loop detection.
If you are able to send multiple, simultaneous email messages from two
or more MTAs, you're in good shape.
Building your MTA (C/C++)
We have provided an initial skeleton directory to get you started. It
is available as /cse223b/sp11/labs/lab2/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
% gmake
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.
Building your MTA (Java)
For those of you who choose to use Java, we have provided example files to get
you started in /cse223b/sp11/labs/lab2/lab2.tgz.
You may not import JavaMail, SMTPClient, or any other mail-related or
asynchronous connection handling libraries in your code! If you are unsure
about a library that you want to use, ask the TA!
MXDomain.java provides a simple method for obtaining DNS MX records, similar
to what's provided in domain.c for the C/C++ skeleton code. It exports a
single function called getMXRecords which, when given domain name string,
returns a Java ArrayList of MXRecord
objects from the DNSJava library. Be
sure to include the DNSJava jar file (/usr/share/java/dnsjava.jar) in your classpath before
using this class!
MTA2.java contains the skeleton MTA code. Like the C version, it provides an
example of a select loop and a simple timer. Client.java and TimeHandler.java
contain skeleton client and timer handlers, respectively. Feel free to change
the object structure or define new objects as you see fit. You can build it by
running:
% javac MTA2.java
After a successful build, you can start the server by executing:
% java MTA 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.
Building your MTA (Python)
We also provide a skeleton Python file, mta2.py, in /cse223b/sp11/labs/lab2/lab2.tgz. Like the C and Java
versions, it provides a simple example of a select loop and a basic timer. If
you choose to use Python, you may not import email, mimelib, smtplib, asyncore,
asynchat, twisted, or any other mail-related or asynchronous connection
handling modules into your code! If you are unsure about a module that you
want to use, ask the TA!
Looking up MX records in Python is fairly straightforward:
import dns.resolver
dns_result = dns.resolver.query('cs.ucsd.edu', 'MX')
for mx_record in dns_result:
print mx_record
Outputs:
10 oec-vmmx.ucsd.edu
5 inbound.ucsd.edu
You can also access individual elements of the dns_result collection via
standard subscript notation: dns_result[0], dns_result[1], etc., and the number
of elements is available via len().
You can run the code by executing:
python mta2.py 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.
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 sysnet41.ucsd.edu; it 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.sysnet.ucsd.edu that points to sysnet41.ucsd.edu. So,
if you attempt to send a message to anyone@tarpit.sysnet.ucsd.edu, 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
specified delay.
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.,
sysnet30.ucsd.edu) 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.
Collaboration policy
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.
Turnin procedure
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 ^
 |
 |
|  |