Typically, users configure their mail programs to send all mail through a single SMTP server set up by the network administrator. This server is sometimes referred to as a smart host because it has the ability to look at an email address, query the Domain Name System (DNS) enough to find out which mail server handles mail for that domain, and then deliver the message.
The server that accepts mail for a domain is sometimes at an obvious location, like mail.domain.com, but it can be any server, even one in a different domain. Domain administrators list their mail servers in their public DNS information by adding MX (short for Mail Exchange) records that list the SMTP servers that will accept mail addressed to that domain. For example, the DNS records for oreilly.com list two MX records, smtp1.oreilly.com and smtp2.oreilly.com. So when you want to send an email to email@example.com, it must be delivered to one of those two servers.
Typically, programs that send mail using SMTP are configured to use a smart host. The hostname of the outgoing SMTP server is stored in a configuration file, and all mail, regardless of the domain, is sent through that server. However, there are times when you might want to write a program that doesn't rely on a smart host. For example, you might want your application to be able to move between networks without having to be told where the local SMTP server is. Or you could actually be developing your own mail server. (For much more on mail servers, see Chapter 8.) In these cases, you'll need to query the DNS system to find the appropriate server to use for each address.
7.3.1. How Do I Do That?
Create a twisted.mail.relaymanager.MXCalculator object. Use its getMX method to look up the appropriate mail server for each address to which you need to deliver mail, as shown in Example 7-4.
Example 7-4. smtplookup.py
from twisted.mail import relaymanager mxCalc = relaymanager.MXCalculator( ) def getSMTPServer(emailAddress): username, domain = emailAddress.split("@") return mxCalc.getMX(domain).addCallback(_gotMXRecord) def _gotMXRecord(mxRecord): return mxRecord.exchange if __name__ == "_ _main_ _": from twisted.internet import reactor import sys address = sys.argv def printServer(server): print server reactor.stop( ) def handleError(error): print >> sys.stderr, error.getErrorMessage( ) reactor.stop( ) getSMTPServer(address).addCallback(printServer).addErrback(handleError) reactor.run( )
Run smtplookup.py with an email address as its only argument. It will print out the name of a server that accepts mail for the address's domain:
$ python smtplookup.py firstname.lastname@example.org smtp1.oreilly.com $ python smtplookup.py email@example.com barclay.textdrive.com
7.3.2. How Does That Work?
smtplookup.py creates a twisted.mail.relaymanager.MXCalculator object called mxCalc. Note that it is created at the module level, instead of inside the getSMTPServer function; this will allow for the MXCalculator to use cached results if the same domain is looked up more than once.
The getSMTPServer function extracts the domain from the email address, and then returns the Deferred result of mxCalc.getMX(domain), while adding _gotMXRecord as a callback handler. The _gotMXRecord function does some simple filtering on the result of getMX, taking the result (a twisted.protocols.dns.Record_MX object) and returning just the exchange attribute, which contains the hostname of the mail server. This technique of adding an extra callback handler up front can be quite useful, enabling you to process or transform the result of a deferred function.
Building Simple Clients and Servers
Web Services and RPC
NNTP Clients and Servers
Services, Processes, and Logging