This is a small utility that uses Python’s SMTP module to send RFC1985 ETRN requests to one or more mail servers. This would normally be used to tell the backup mail servers for a domain to attempt delivery after the primary MX comes back online. I wrote it, because after much searching, the best I could find were scripts using telnet or nc, and with no error handling. This program should speak SMTP correctly, and will return error codes and messages in the case of failure.
1 #!/usr/bin/python 2 3 # Andrew Baumann andrewb@cse.unsw.edu.au, 2007/04/29 4 5 import sys, smtplib, socket 6 from optparse import OptionParser 7 8 def parse_args(): 9 mydomain = socket.getfqdn() 10 p = OptionParser(usage='%prog [option] host...', 11 description=sends an ETRN request (RFC1985) to one or more mail servers) 12 p.add_option('-d', dest='domain', 13 help='domain to request mail for, default: %s' % mydomain) 14 p.add_option('-v', action='store_const', dest='verbose', const=True, 15 help='verbose/debug output') 16 p.set_defaults(domain=mydomain, verbose=False) 17 options, args = p.parse_args() 18 if len(args) == 0: 19 p.error('no host specified') 20 return options, args 21 22 def do_etrn(host, domain, smtpobj=None): 23 if smtpobj is None: 24 smtpobj = smtplib.SMTP() 25 smtpobj.connect(host) 26 smtpobj.ehlo() 27 (retcode, retmsg) = smtpobj.docmd('ETRN', domain) 28 if retcode / 10 != 25: 29 raise smtplib.SMTPResponseException(retcode, retmsg) 30 smtpobj.quit() 31 32 def main(): 33 options, hosts = parse_args() 34 errorflag = False 35 smtpobj = smtplib.SMTP() 36 smtpobj.set_debuglevel(options.verbose) 37 38 for host in hosts: 39 def error(msg): 40 sys.stderr.write('Error: %s: %s\n' % (host, msg)) 41 42 try: 43 do_etrn(host, options.domain, smtpobj) 44 except socket.error, (errnum, errmsg): 45 error(errmsg) 46 errorflag = True 47 except smtplib.SMTPResponseException, e: 48 error('%d %s' % (e.smtp_code, e.smtp_error)) 49 errorflag = True 50 except smtplib.SMTPException: 51 error('Generic SMTP error') 52 errorflag = True 53 54 return errorflag 55 56 if __name__ == __main__: 57 if main(): 58 sys.exit(1)