I feel the need to ponder the elegant simplicity of Net::SMTP, having written some nice support code using this module for testing routines against anti-spam servers at work.

Sadly, the systems I wrote my code for do not provide support for DSN's as an extension within initial conversation, so using
$smtp->recipients(@addresses, { Notify => ['SUCCESS','FAILURE','DELAY' +] });
or similar was not an option, which is a pity. Regardless, on with the pondering.

#!/usr/bin/perl # Use strictures use strict; use warnings; # Additional modules use Socket; use Net::DNS; use Net::SMTP; # At launch, ask for an email address print "Please enter an email address: "; my $address = <STDIN>; # Break apart the address and do MX lookup against the # domain our ($userpart, $domainpart) = split /@/, $address; our $res = Net::DNS::Resolver->new(); our @mx = mx($res, $domainpart) or die "Cannot resolve $domainpart: $!\n";
Within a few short lines of code, you already have something useful. You could at this point, for example, chuck in some code to simply print out the MX records that Net::DNS found, like:
for my $rr (@mx) { print $rr->preference, " ", $rr->exchange,"\n"; }
But this is not about resolving MX records. This is about talking to SMTP hosts!

What we want to do, is ask a server if it would accept mail from our address. It's not perfect, some hosts answer ambiguously with a 2.5.2 or 2.5.0 and bounce out of conversation, but many systems will reply correctly, with 250 or 550. In event of the latter, we wouldn't bother to offload email to a failure response, and would simply elect to bounce mail. See end notes for comments on sender verification through SMTP conversation ;)

So,in the beautiful prose of Perl, we would say:
# We assume that @mx contains something, since the die() # during MX resolution would have killed us by now for (@mx) { # It *would* be more elegant to test the primary server # in the event of round-robin MX or pick one in the # event of load-balanced MX, but I want to test for # any disparities in replies. my @mailservers = $rr->exchange; for my $mailserver (@mailservers) { # Connection plus EHLO my $smtp = Net::SMTP->new( Host => $mailserver, Hello => some.server.tld, Timeout => 30, Debug => 0 ) or die "Cannot connect to $mailserver!\n"; # MAIL FROM: $smtp->mail('me@somewhere.com'); # RCPT TO: my $response = $smtp->recipient($address); # Now go looking for response codes. On my system, # 1 = user valid, 0 = user invalid if ($response == 1) { # Do something, or return() something } else { # Do something, or return() something } # Be polite, RSET and QUIT properly $smtp->reset(); $smtp->quit(); # Slow down the rate between probes sleep(2); } }
This piece of code can work like an SMTP ping, where you could check to see if a host is up, or do as I have and use it for primitive sender verification, or to test the existence of mailboxes at a destination host. Or whatever. You know? ;-)

This meditation was never about using code for a specific purpose. This code was about worship, basking in the elegent simplicity of Perl and CPAN. It also inadvertently propagated some *bad* ideas in spam management...

The End Note:
I'm ambivalent about the practice of people trying to verify senders against my mail systems. On one hand, I think that a probe to test an address for sender authenticity is better than simply offloading an entire mail and having to bounce it later, or generate a DSN in response to a bad address. I just prefer it when people RSET and QUIT instead of leaving sessions open, which I consider a DoS attack, and blacklist accordingly. The practice breaks in a lot of scenarios though, such as outbound gateways that aren't linked, via static list or LDAP or inbound RCPT TO probes to valid address sources that inbound gateways have access to. Mostly, I use this kind of probe in tightly controlled scenarios, and am willing to tolerate it as incoming so long as my RSET/QUIT common courtesy request is met, and the person conducting the probe doesn't mind my ambiguous 2.7.1 response if they track back to the sending IP instead of connecting against the inbound gateways specified by MX.

Replies are listed 'Best First'.
Re: Pondering the elegant simplicity of Net::SMTP
by dwm042 (Priest) on Nov 30, 2007 at 16:40 UTC
    The questions these comments brings to mind are the effects on a three tier mail design a pair of us were playing with once. The idea was the outermost layer (MX points to these servers) just does nothing but ID whether email is talking to a legitimate address. In this case the address book would be some common source, such as a LDAP server. Second layer would accept messages and spam check them, and then the third layer would be the mailer customers interact with.

    In an entirely paranoid implementation the outermost layer accepts everything and drops anything illegitimate silently. Whether that violates RFCs aplenty I don't know. It's been a while since I've done email admin. But it would prevent harvesting of addresses via directory attacks.

      I believe in always giving responses within certain thresholds, so that any breaks in mail can be properly diagnosed and resolved. Preventing directory harvesting can be done by setting limitations on how many invalid recipients you're willing to accept within a given time scale from a single sending IP. I normally say 5 over a period of 60 seconds. If a user on someone else's mail systems has that bad a spelling impediment, then tough :)

      Dropping silently unless you're sure that you don't want the mail and don't care about the sender is not cool. A simple DSN with enhanced status codes goes a long way to making a humble mail admin's job that much easier. It gets tricky when you're trying to comply fully with the RFC's but also secure your mail systems against abuse. Not that I'm complaining. It keeps me employed :)