AXL MXLookup DNS API

com.theorem.smtp
Class MXLookup

Object
  extended byMXLookup

public class MXLookup
extends Object

Looks up MX (Mail Exchange) records for a given host.

MX records are used to determine the SMTP server addresses for a give email address. For example, mail to mjl@theorem.com will actually go to a machine named mail.theorem.com, or a secondary (overflow or backup) server mail.cooldog.com. Typically a SMTP client will either use a "Smart Host" which can do MX record lookups on a "dumb host's" behalf or will use it's own internal MX record lookup. This class allows your SMTP client to become a Smart Host.

Note: The MX record for domain.com, a real domain, will return the name loopback which generally translates to your own IP address. This can cause mail loops. MXLookup returns a successful, but empty result to prevent this problem.

Although the example program below illustrates how to use a single thread performing a single lookup MXLookup can use one constructor with multiple .mxGet() methods, either sequentially in a single thread or concurrently by multiple threads. By default MXLookup can handle fifty concurrent lookups.

You can use MXLookup manually from the command line: java com.theorem.smtp.MXLookup acm.org This uses the name server ns1.bellatlantic.net. You may wish to write your own main() to use a closer name server.

For systems with multiple threads, first create a new instance of MXLookup() and read the cache file in. Discard this instance. Each thread requires a new instance of MXLookup() to keep the DNS connections straight. The disk version of the cache should be saved periodically and certainly before the program terminates.

Here is an example of how to perform the lookups (the main of MXLookup, in fact).

 	/**
 	* Tests MX lookup.
 	* Lists the mail hosts from the domain names the command line.
 	* Eg: java com.theorem.smtp.MXLookup compuserve.com aol.com theorem.com
 	* Reads and writes the file savemx in the current directory.
 	* This file contains the cached lookups.
 	*
 	* @param args hosts for which we want MX records.
 	*/
 	public static void main(String[] args)
 	{
 		if (args.length < 2)
 		{
 			System.err.println("Usage: java -jar mxlookup.jar [nameserver] [hostname] ...");
 			System.exit(1);
 		}

 		String nameServer = args[0];

 		MXLookup mx = null;

 		try {
 			mx = new MXLookup(nameServer);
 		} catch (UnknownHostException uhne) { System.err.println("Can't find host " + nameServer + " " + uhne); }
 		catch (SocketException see) { System.err.println("Socket error " + see); }

 		if (new File("savemx").exists() && mx.readCache("savemx") == false)
 		{
 			System.out.println("savemx file had a problem being read");
 			new File("savemx").delete();
 		}

 		for (int j = 1; j < args.length; j++)
 		{
 			String mxlist[] = null;

 			try {
 				mxlist = mx.getMX(args[j]);
 			} catch (IOException ioe)
 			{
 				// DNS must have timed out quit often.
 				System.err.println("DNS timed out quite often");
 			}

 			if (mxlist == null)
 			{
 				System.out.println("Lookup failed for " + args[j]);
 				continue;
 			}

 			System.out.println();
 			for (int i = 0; i < mxlist.length; i++)
 			{
 				System.out.print(mxlist[i]);
 				try {
 					System.out.println(", IP address is " + InetAddress.getByName(mxlist[i]).getHostAddress());
 					mx.addIP(args[j], mxlist[i], InetAddress.getByName(mxlist[i]));
 				} catch (UnknownHostException uhne1) { System.err.println("Can't find host " + mxlist[i] + " " + uhne1); }
 			}
 		}

 		mx.close();

 		if (mx.writeCache("savemx") == false)
 		{
 			System.out.println("savemx file had a problem being written");
 		}

 	}
 

Author:
Michael Lecuyer

Field Summary
static String copyright
          "Copyright 1998 Michael Lecuyer. All rights reserved";
static String Version
          "2.26".
 
Constructor Summary
MXLookup()
          Constructor to get at the internals of MXLookup without doing a lookup.
MXLookup(String nameServer)
          Connects to name server.
MXLookup(String nameServer, int port)
          Connects to name a server on a particular port.
MXLookup(String nameServer, int port, int timeout, int retries)
          Connects to name server on a particular port with settable timeout and retry values.
 
Method Summary
 void addIP(String host, String mxHost, InetAddress ip)
          Adds an IP address to the cache.
 void clearStats()
          Clear statistics.
 void close()
          Closes down the MXLookup system.
static long elapsedTime(Date start)
          Get elapsed time.
static String elapsedTimeToString(Date start)
          Get elapsed time of an event.
 void finalize()
          Cleans up after the constructor.
 int getARecordLookups()
          Return total number of successful A record (hostname) lookups.
 int getCacheHits()
          Return total number of cache hits.
 int getFailedLookups()
          Return total number of successful lookups.
 int getLookups()
          Return total number of lookups.
 String[] getMX(String host)
           
 MXData[] getMXFull(String host)
          Get an array of MXData objects describing the full MX records.
 int getRetries()
          Return total number of retries.
 int getSuccessfulLookups()
          Return total number of successful lookups.
static void main(String[] args)
          Tests MX lookup.
 void merge(MXLookup mx)
          Merge another MX cache list with the current one.
 boolean readCache(String fName)
          Read MX cache records from a file.
 void setCacheSize(int cacheSize)
          Sets cache size.
 void setMaximumRequests(int maxRequests)
          Set the maximum concurrent outstanding requests the MXLookup class may handle.
static Date startTime()
          Start timing an event.
 String toString()
          Return the string representation of MXLookup.
 void useMXOnly(boolean enable)
          Do not fetch A records when performing a lookup.
 boolean writeCache(String fName)
          Write MX cache records to a file.
 
Methods inherited from class Object
equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

Version

public static final String Version
"2.26".

See Also:
Constant Field Values

copyright

public static final String copyright
"Copyright 1998 Michael Lecuyer. All rights reserved";

See Also:
Constant Field Values
Constructor Detail

MXLookup

public MXLookup()
Constructor to get at the internals of MXLookup without doing a lookup.


MXLookup

public MXLookup(String nameServer,
                int port)
         throws SocketException,
                UnknownHostException
Connects to name a server on a particular port.

Parameters:
nameServer - namer server host as a domain name or IP address string.
port - Name server port. Uses default port 53 if port = 0
Throws:
SocketException - Socket creation error
UnknownHostException - Unknown name server

MXLookup

public MXLookup(String nameServer,
                int port,
                int timeout,
                int retries)
         throws SocketException,
                UnknownHostException
Connects to name server on a particular port with settable timeout and retry values.

Parameters:
nameServer - Namer server host as a domain name or IP address string.
port - Name server port. Uses default port 53 if port = 0
timeout - Time out in seconds for a packet. The default is 5 seconds. A value of 0 uses the default.
retries - Maximum number of retries allowed. The default is 3, the maximum is 16 A value of 0 uses the default number of retries..
Throws:
SocketException - Socket creation error
UnknownHostException - Unknown name server

MXLookup

public MXLookup(String nameServer)
         throws SocketException,
                UnknownHostException
Connects to name server. Uses default port of 53.

Parameters:
nameServer - namer server host as a domain name or IP address string.
Throws:
SocketException - Socket creation error
UnknownHostException - Unknown name server
Method Detail

main

public static void main(String[] args)
Tests MX lookup. Lists the mail hosts from the domain names the command line.
Eg: java com.theorem.smtp.MXLookup compuserve.com aol.com theorem.com Reads and writes the file savemx in the current directory. This file contains the cached lookups.

Parameters:
args - hosts for which we want MX records.

setCacheSize

public void setCacheSize(int cacheSize)
Sets cache size.
Trying to disable the cache by setting the size to 0 will not work.
Default cache size is 1024 entries.

Parameters:
cacheSize - number of entries in cache (default is 1024).

useMXOnly

public void useMXOnly(boolean enable)
Do not fetch A records when performing a lookup. The default is to fetch A records when there is no MX record.

When a domain does not return a valid MX record MXLookup attempts to resolve the full domain name. While this can properly resolve improper email addresses like @mail.theorem.com, the proper mailing address is @theorem.com. Some misconfigured systems don't use an MX record and this resolution is helpful.

When testing for a spam address from an ISP's dailup's and cable connections it's clear that the given address is certainly not a mail server because it doesn't have an MX record for itself, but does have one for the real SMPT server.

Warning: Before enabling this be aware that large ISP's mail sender machines are often not their mail receiver machines.

Parameters:
enable - True if MX records are the only valid records, false if the A records for the raw domain is to be resolved. The default is false. This should only be set once as it will affect all threads using the same MXLookup instance. .
Since:
2.25

getMXFull

public MXData[] getMXFull(String host)
                   throws IOException
Get an array of MXData objects describing the full MX records.

Parameters:
host - Host to look up.
Returns:
array of MXData objects.
Throws:
IOException

getMX

public String[] getMX(String host)
                         throws IOException
Throws:
IOException

toString

public String toString()
Return the string representation of MXLookup.

Returns:
string.
Since:
2.25

addIP

public void addIP(String host,
                  String mxHost,
                  InetAddress ip)
Adds an IP address to the cache. This method is not necessary but makes cache usage more efficient. Without this the cache will return the domain name as the MX record. Using this call will cause the IP address to be returned which can be used directly without an additional lookup from the DNS server. This is thread safe.

Parameters:
host - original host name for lookup.
mxHost - mx host actually looked up.
ip - InetAddress object returned by InetAddress.getByName()

finalize

public void finalize()
Cleans up after the constructor.


close

public void close()
Closes down the MXLookup system. This will promptly shut down the pair of MXLookup server threads. If this method isn't called the finalizer will perform this service. Waiting for the finalizer may result in extra threads being visible until garbage collection is performed.


writeCache

public boolean writeCache(String fName)
Write MX cache records to a file.

Parameters:
fName - name of file to write.
Returns:
true if all went well, false if there was an error.

readCache

public boolean readCache(String fName)
                  throws Exception
Read MX cache records from a file.

Parameters:
fName - name of file containing cache records to read.
Returns:
true if all went well, false if there was an error.
Throws:
Exception

getRetries

public int getRetries()
Return total number of retries.

Returns:
number.

getLookups

public int getLookups()
Return total number of lookups. This is the number of lookup including all resolutions and failures.

Returns:
number.

getCacheHits

public int getCacheHits()
Return total number of cache hits.

Returns:
number.

getSuccessfulLookups

public int getSuccessfulLookups()
Return total number of successful lookups.

Returns:
number.

getFailedLookups

public int getFailedLookups()
Return total number of successful lookups.

Returns:
number.

getARecordLookups

public int getARecordLookups()
Return total number of successful A record (hostname) lookups.

A records are lookups of last resort when MX records cannot be found. This usually occurs when a host has no MX records (rare), the DNS lookup timed out (sometimes happens), and most commonly when someone mistakenly uses their mail server for the email address.

Returns:
number.

clearStats

public void clearStats()
Clear statistics.


merge

public void merge(MXLookup mx)
Merge another MX cache list with the current one. This would be used to save different thread's cache list with a master list in a multi-threaded environment where multiple copies of MXLookup are running. This is not required if a global MXLookup instance is shared among all threads.

This would appear in a globally available static class available to all threads created in the main thread:

 public class GLOBAL {
 static MXLookup masterLookup = new MXLookup();
 }
 
Start a new MXLookup in each send mail thread. Merge the current cache with the master cache periodically:
 MXLookup mx = new MXLookup(nameServer);
 ...
 mx.lookup("site.com");
 ...
 GLOBAL.masterLookup.merge(mx);
 GLOBAL.masterLookup.writeCache();
 
The thread's MXLookup simply reads the master MX cache file to preload it's cache.

See Also:
readCache(java.lang.String)

setMaximumRequests

public void setMaximumRequests(int maxRequests)
Set the maximum concurrent outstanding requests the MXLookup class may handle. The default value is 50.

This method can only increase the queue size. Attempts to shrink the queue size are ignored.

Parameters:
maxRequests - Maximum number of requests allowed in request queue. This is the maximum number of concurrent outstanding requests. The default is 50.
Since:
2.24

startTime

public static Date startTime()
Start timing an event.

Returns:
Start time of event.
See Also:
elapsedTime(java.util.Date start), elapsedTimeToString(java.util.Date start)

elapsedTime

public static long elapsedTime(Date start)
Get elapsed time.

Parameters:
start - The time the clock started running.
Returns:
run time in milliseconds.
See Also:
elapsedTimeToString(java.util.Date), startTime()

elapsedTimeToString

public static String elapsedTimeToString(Date start)
Get elapsed time of an event.

Parameters:
start - The time the clock started running.
Returns:
run time as a string.
See Also:
elapsedTime(java.util.Date start), startTime()

AXL MXLookup DNS API

Submit a bug report or feature request

Copyright 1998-2001 AXL Software. PO Box 97, Viola, Delaware 19979, U.S.A. All Rights Reserved.