Java Assignment

Programming Lab 2: A Mail User Agent in Java In this lab you will implement a mail user agent that sends mail to other users. Your task is to program the SMTP interaction between the MUA and the local SMTP server. Th e client provides a graphical user interface containing fields for entering the sender and recipient addresses, th e subject of the message and the message itself. Here's what the user interface looks like:

With this interface, when yo u want to send a mail, you must fill in comp lete addresses for both the sender and the recipient, i.e., [email protected] , not just simply user. You can send mail to only one recipient. You will also need to give the name (or IP address) of your lo cal mailserver. If you do not know the name of your local mailserver, see Querying the DNS below for more information on how to obtain the address of the local mailserver. When you have finished composing your mail, press Send to send it. The Code The program consists of four classes:

You will need to complete the code in the SMTPConnectionclass so that in the end yo u will have a program that is capable of sending mail to any recipient. The code for the SMTPConnection class is at the end of this page .

The code for the other three classes is provided on this page . The places where you need to complete the code have been marked with the comments /* Fill in */. Each of the places requires one or more lines of code. MailClient The user interface Message Mail message Envelope SMTP envelo pe around the Message SMTPConnection Connection to the SMTP server Pa ge 1 of 5 A Mail User A gent in Java The MailClient class provides the user interface and calls the other classes as needed. When you press Send, the MailClient class constructs a Message class object to hold the mail message. The Message object holds the actual message headers and body. Then the MailClient object builds the SMTP envelope using the Envelope class. This class holds the SMTP sender and recipi ent information, the SMTP server of the recipient's domain, and the Message object. Then the MailClient object creates the SMTPConnection object which opens a connection to the SMTP server and the MailClient object sends the message over the connection.

The sending of the mail happens in three phases: 1. The MailClient object creates the SMTPConnection object and opens the connection to the SMTP server. 2. The MailClient object sends the message using the function SMTPConnection.send(). 3. The MailClient object closes the SMTP connection. The Message class contains the function isValid() which is used to check the addresses of the sender and recipient to make sure that there is only one address a nd that the address contains the @-sign. The provided code does not do any other error checking. Reply Codes For the basic interact ion of sending one message, you will only need to implement a part of SMTP. In this lab you need only to implement the following SMTP commands:

The above table also lists the accepted reply codes for each of the SMTP commands you need to implement. For simplicity, you can assume that any othe r reply from the server indicates a fatal error and abort the sending of the message. In reality, SMTP distinguishes between transi ent (reply codes 4xx) and permanent (reply codes 5xx) errors, and the sender is allowed to repeat commands that yielded in a transient error. See Appendix E of RFC 821 for more details. In addition, when you open a co nnection to the server, it will reply with the code 220. Note: RFC 821 allows the code 251 as a response to a RCPT TO-command to indicate that the recipient is not a local user. You may want to verify manually with the telnet command what your local SMTP server replies. Hints Most of the code you will need to fill in is similar to the code you wrote in the WebSer ver lab. You may want to use the code you have written there to help you. To make it easier to debug your program, do not, at first, include the code that opens the socket, but use the following definitions for fromServer and toServer . This way, your program sends the commands to the terminal. Acting as the SMTP server, you will need to give the correct reply codes. When your program works, add the code to open the socket to the server. fromServer = new BufferedReader(new InputStreamReader(System.in)); Command Reply Code DATA 354 HELO 250 MAIL FROM 250 QUIT 221 RCPT TO 250 Pa ge 2 of 5 A Mail User A gent in Java toServer = System.out; The lines for opening and closing the socket, i.e., the lines connection = ... in the constructor and the line connection.close() in function close(), have been commented out by default. Start by completing the function parseReply(). You will need this function in many plac es. In the function parseReply() , you should use the StringTokenizer-class for parsing the repl y strings. You can convert a string to an integer as follows: int i = Integer.parseInt(argv[0]); In the function sendCommand() , you should use the function writeBytes() to write the commands to the server. The advantage of using writeBytes() instead of write() is that the former automatically converts the strings to bytes which is what the server expects. Do not forget to terminate each command with the string CRLF.

You can throw exceptions like this: throw new Exception(); You do not need to worry about details, since the exceptions in this lab are only used to signal an error, not to give detailed information about what went wrong. Optional Exercises You may want to try the following optional exercises to make your program more sophisticated. For these exercises, you will need to modify also the othe r classes (MailClient, Message, and Envelope). z Verify sender address. Java's System-class contains information about the username and the InetAddress-class contains methods for finding the name of the local host. Use these to construct the sender address for the Envelope instead of usin g the user-supplied value in the From-header. z Additional headers. The generated mails have only four header fields, From, To, Subject, and Date. Add other header fields from RFC 822, e. g., Message-ID, Keywords. Check the RFC for the definitions of the different fields. z Multiple recipients . Currently the program only allows sending ma il to a single recipient. Modify the user interface to include a Cc-field and modify the pr ogram to send mail to both recipients. For a more challenging exercise, modify the program to send mail to an arbitrary number of recipients. z More error checking. The provided code assumes that all errors that occur during the SMTP connection are fatal. Add code to distinguish between fatal and non-fatal errors and add a mechanism for signaling them to the user. Check the RFC to see what the di fferent reply codes mean. This exercise may require large modifications to the send(), sendCommand() , and parseReply() functions. Querying the DNS The Domain Name System (DNS) stores information in re source records. Normal name to IP-address mappings are stored in type A (Address) resource records. Ty pe NS (NameServer) records hold information about nameservers and type MX (Mail eXchange) records tell whic h server is handling the mail delivery of the domain. The server you need to find is the server handling the mail for your school's domain. First, you must find the nameserver of your school and then query this namese rver for the MX-host. Assuming you are at Someschool and your domain is someschool.edu, you would do the following: Pa ge 3 of 5 A Mail User A gent in Java 1. Find the address of a nameserver for the top-level domain .edu (NS query) 2. Query the nameserver for .edu about the nameserver for the domain someschool.edu to get the address of Someschool's nameserver. (NS query) 3. Query Someschool's nameserver for MX-records for the domain someschool.edu. (MX query) Ask your local system administrator ho w to perform DNS queries manually. Under Unix you can query DNS manually with the nslookup-command. The syntax of the nslookup-command is as follows. Note that the argument host can also be a domain.

The reply to the MX-query may contain multiple mail exchan gers. Each of them is preceded by a number which is the preference value for this server. Lower preference values indicate pref erred servers so you should use the server with the lowest preference value. SMTPConnection.java This is the code for the SMTPConnce tion class that you will need to complete. The code for the other three classes is provided on this page . import java.net.*; import java.io.*; import java.util.*; /** * Open an SMTP connection to a mailserver and send one mail.

* */ public class SMTPConnection { /* The socket to the server */ private Socket connection; /* Streams for reading and writing the socket */ private BufferedReader fromServer; private DataOutputStream toServer; private static final int SMTP_PORT = 25; private static final String CRLF = " "; /* Are we connected? Used in close() to determine what to do. */ private boolean isConnected = false; /* Create an SMTPConnection object. Create the socket and the associated streams. Initialize SMTP connection. */ public SMTPConnection(Envelope envelope) throws IOException { // connection = /* Fill in */; fromServer = /* Fill in */; toServer = /* Fill in */; /* Fill in */ /* Read a line from server and check that the reply code is 220.

If not, throw an IOException. */ Normal query nslookup host Normal query using a given server nslookup host server NS-query nslookup -type=NS host MX-query nslookup -type=MX host Pa ge 4 of 5 A Mail User A gent in Java /* Fill in */ /* SMTP handshake. We need the name of the local machine.

Send the appropriate SMTP handshake command. */ String localhost = /* Fill in */; sendCommand( /* Fill in */ ); isConnected = true; } /* Send the message. Write the correct SMTP-commands in the correct order. No checking for errors, just throw them to the caller. */ public void send(Envelope envelope) throws IOException { /* Fill in */ /* Send all the necessary commands to send a message. Call sendCommand() to do the dirty work. Do _not_ catch the exception thrown from sendCommand(). */ /* Fill in */ } /* Close the connection. First, terminate on SMTP level, then close the socket. */ public void close() { isConnected = false; try { sendCommand( /* Fill in */ ); // connection.close(); } catch (IOException e) { System.out.println("Unable to close connection: " + e); isConnected = true; } } /* Send an SMTP command to the server. Check that the reply code is what is is supposed to be according to RFC 821. */ private void sendCommand(String command, int rc) throws IOException { /* Fill in */ /* Write command to server and read reply from server. */ /* Fill in */ /* Fill in */ /* Check that the server's reply code is the same as the parameter rc. If not, throw an IOException. */ /* Fill in */ } /* Parse the reply line from the server. Returns the reply code. */ private int parseReply(String reply) { /* Fill in */ } /* Destructor. Closes the connection if something bad happens. */ protected void finalize() throws Throwable { if(isConnected) { close(); } super.finalize(); } } Pa ge 5 of 5 A Mail User A gent in Java Mail User Agent: Simplified Version This lab is divided into two parts. In the first part, you use telnet to manually send mail through an SMTP mail server. In the second part, you write a Java program that performs the same action.

Part 1: Sending Email with Telnet When you do this lab, you should try to send email to yourself. This means you need to know the host name of the mail server for your mail domain. To find out this information, you can query DNS for the MX record that maintains information about your mail domain. For example, [email protected] has mail domain someschool.edu . The following command queries DNS fo r the mail servers responsible for delivering mail to this domain:

nslookup -type=MX someschool.edu For the response to this command, th ere may be several mail servers that will deliver mail to mailboxes in the domain someschool.edu . Suppose that the name of one of them is mx1.someschool.edu. In this case, the following command will es tablish a TCP connection to this mail server. (Notice that the port number 25 is specified on the command line.) telnet mx1.someschol.edu 25 At this point, the telnet program will allow you to enter SMTP commands, and will display the responses from the mail server. For example, the fo llowing sequence of commands would send email to bob from alice HELO alice MAIL FROM: RCPT TO: DATA SUBJECT: hello Hi Bob, How's the weather? Alice.

.

QUIT The SMTP protocol was originally designed to allow people to manually interact with mail servers in a conversational manner. For this reason, if you ente r a command with incorrect syntax, or with unacceptable arguments, the se rver will return a message stating th is, and will allow you to try again. To complete this part of the lab, you should send an email message to yourself and verify that it was delivered.

Part 2: Sending Email with Java Java provides an API for interacting with the Intern et mail system, which is called JavaMail. However, we will not be using this API, because it hides the details of SMTP and socket programming. Instead, you should write a Java progr am that establishes a TCP connection wi th a mail server through the socket interface, and sends an email message. Pa ge 1 of 3 Email Lab You can place all of your code into the main method of a class called EmailSende r. Run your program with the following simple command: java EmailSender This means you will include in your code the details of the particular email message you are trying to send.

Here is a skeleton of the code you'll need to write:

import java.io.*; import java.net.*; public class EmailSender { public static void main(String[] args) throws Exception { // Establish a TCP connection with the mail server.

// Create a BufferedReader to read a line at a time.

InputStream is = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); // Read greeting from the server.

String response = br.readLine(); System.out.println(response); if (!response.startsWith("220")) { throw new Exception("220 reply not received from server."); } // Get a reference to the socket's output stream.

OutputStream os = socket.getOutputStream(); // Send HELO command and get server response.

String command = "HELO alice "; System.out.print(command); os.write(command.getBytes("US-ASCII")); response = br.readLine(); System.out.println(response); if (!response.startsWith("250")) { throw new Exception("250 reply not received from server."); } // Send MAIL FROM command.

// Send RCPT TO command.

// Send DATA command.

// Send message data. // End with line with a single period. Pa ge 2 of 3 Email Lab // Send QUIT command.

} } Pa ge 3 of 3 Email Lab