Article 10162 of comp.lang.perl:
Xref: feenix.metronet.com comp.lang.perl:10162 comp.mail.sendmail:4507 alt.sources:2703 comp.unix.admin:9062
Path: feenix.metronet.com!news.ecn.bgu.edu!usenet.ins.cwru.edu!agate!overload.lbl.gov!lll-winken.llnl.gov!fastrac.llnl.gov!usenet.ee.pdx.edu!usenet.ee.pdx.edu!trent
From: [email protected] (Trent A. Fisher)
Newsgroups: comp.lang.perl,comp.mail.sendmail,alt.sources,comp.unix.admin
Subject: Re: expn program for sendmail
Date: 26 Jan 1994 01:05:12 GMT
Organization: Portland State University, CS Dept.
Lines: 333
Distribution: world
Message-ID: <[email protected]>
References: <[email protected]>
NNTP-Posting-Host: sirius.cs.pdx.edu
In-reply-to: Tom Christiansen's message of Tue, 25 Jan 1994 20:14:19 GMT
X-spook-bait: munitions Kennedy Khaddafi NSA Waco, Texas Ft. Bragg


Archive-name: chkaddr
Submitted-by: [email protected]

In article <[email protected]> Tom Christiansen writes:
> Here's a brief program to expand remote aliases using the
> smtp daemon on the farside, e.g.:  [...]

Well, here's my program to do something similar, except mine will
recursively resolve each address until it hits non-forwarding login or
an error.  For example, running it on [email protected] will give
you (indentation shows recursion level):
       [email protected]
         \trent
         [email protected]
           "|/usr/local/mh/lib/slocal -user trent"

You need to have 'nslookup' in your path, to do MX record checking (I
haven't tested this code thouroughly.  Other than this, I have been
using this for a year or two, and, hopefully, have most of the kinks
worked out.

Send any suggestion, comments, fixes to [email protected].

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#       chkaddr
# This archive created: Tue Jan 25 16:56:28 1994
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'chkaddr'" '(6009 characters)'
cat << \SATANOSCILATEMYMETALICSONATAS > 'chkaddr'
#!/usr/local/bin/perl
#
# Check a given (internet) e-mail address via smtp (vrfy command)
#
#  Copyright (C) 1991-4 Trent A. Fisher ([email protected])
#  With portions of code stolen from [email protected] (Eric Young)
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 1, or (at your option)
#  any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#  Usage:
#       chkaddr [-v] [-d n] addrs...
#       -v      lets you snoop on the smtp connection
#       -d n    maximum depth to search
#       adrrs are e-mail addresses, if there is no '@domain',
#       '@localhost' will be appended
#
#  Todo:
#       - do a HELO (need FQDN of hostname, how do we get that??)
#       - use EXPN instead of VRFY (both??)
#       - implement some way of caching smtp connections, e.g. if
#         three people in a mailing list have their mail forwarded to
#         the same machine, this program will open an smtp connection
#         three times.
#       - handle addresses which are not fully domained, i.e.
#               [email protected]
#                 foo@blatz (should be blatz.mit.edu)
#         (My systems don't do this, but some do)
#       - look up full names via finger?
#

require 'sys/socket.ph';
require 'sys/errno.ph';
require 'getopts.pl';

$snoop = 0;                     # Watch smtp conversations
$MAXDEPTH = 17;                 # maximum mail hops (to stop recursion)
$timeout = 30;

# get any MX records for a given host
# talk to nslookup to get the info
sub getmx
{
       local($host) = @_;
       local(@mxhosts);
       local($tmpfile)="/tmp/pmx$$z";

       # prepare the file to give to nslookup
       open(TMP, ">$tmpfile") || die "$tmpfile";
       print TMP "set type=MX\n$host\n";
       close TMP;

       # feed nslookup our input file...
       open(NS, "nslookup <$tmpfile |") || die "nslookup";
       while (<NS>)
       {
               if (/$host\s+preference\s+=\s+(\d+).*exchanger\s+=\s+([^\s]+)/)
               {
                       next if $host eq $2;
                       push(@mxhosts, $2);
                       # do something with preference, someday??
               }
       }
       close(NS);
       unlink($tmpfile);

       return(@mxhosts);
}


sub startsmtp
{
       local($name)=@_;
       local($status);
       local($n,$aliases,$proto,$port,$type,$len,$thisaddr,$thataddr);
       local($this, $that, $sockaddr);

       $sockaddr='Sna4x8';
       chop($hostname=`hostname`);

       ($n,$aliases,$proto)=getprotobyname('tcp');
       ($n,$aliases,$port)=getservbyname('smtp','tcp');
       ($n,$aliases,$type,$len,$thisaddr)=gethostbyname($hostname);
       ($n,$aliases,$type,$len,$thataddr)=gethostbyname($name);
       if (!defined $thataddr)
       {
               # at this point we check for an MX record
               @mxhosts = &getmx($name);
               if (@mxhosts)
               {
                       print "  "x$depth,
                             "Mail for $name is handled by @mxhosts\n";
               }
               else
               {
                       print "  "x$depth,
                             "I can't locate $name, hopefully sendmail can\n";
               }
               return(0);
       }

       $this=pack($sockaddr,&AF_INET,0,$thisaddr);
       $that=pack($sockaddr,&AF_INET,$port,$thataddr);

       socket(SMTP,&PF_INET,&SOCK_STREAM,$proto) || die "socket: $!";
       bind(SMTP,$this) || die "bind: $!";
       $status = connect(SMTP,$that);
       if ($status == 0)
       {
               print "  "x$depth, "Host $name is ";
               if ($! == &ECONNREFUSED)
               {
                       print "fascist\n";
               }
               elsif ($status == &ETIMEDOUT)
               {
                       print "down?\n";
               }
               else
               {
                       print "having problems: ", $!, "\n";
               }
               return(0);
       }

       select(SMTP); $|=1;
       select(STDIN); $|=1;
       select(STDOUT); $|=1;

       # get the header out of the way
       while (<SMTP>)
       {
               print if ($snoop);
               last if (/^220 /);
               if (/^[24]21 /)
               {
                       print "Host $name doesn't want to talk to us :-(\n";
                       alarm(0);
                       return(0);
               }
       }

       # be polite and do a HELO ??
       # need to get our own FQDN

       return(1);
}

# when  sendmail 8 came out I found out I should actually be using expn.
sub vrfy_cmd
{
       local($name)=@_;
       local(@ret);

       print SMTP "EXPN $name\n";
       while(<SMTP>)
       {
               print if ($snoop);
               if (/^250([ -]).*<([^>]+)>/)
               {
                       push(@ret, $2);
                       last if $1 ne "-";
               }
               else
               {
                       print;          # some sort of error
                       last;
               }
       }
       return(@ret);
}

sub quitsmtp
{
       print SMTP "QUIT\n";
       $_=<SMTP>;
       print if ($snoop);
       print STDOUT "close bad: $_" unless (/^221 /);
}

sub timeout
{
       die "  "x($depth+1)."host $host won't talk to us.\n";
}

#
# get a list of e-mail addresses
#
sub vrfy
{
       local($name, $host) = @_;
       local(@ret);

       # set up the timeout and the connection
       $SIG{'ALRM'}='timeout';
       eval 'alarm($timeout); $r = &startsmtp($host);' ||
               print $@;
       alarm(0);
       return() unless $r;

       @ret = &vrfy_cmd($name);
       do quitsmtp;
       return(@ret);
}

#
# this recursive routine will, given an e-mail address, find out what
# it resolves to (via smtp).
#
sub checkit
{
       local($name, $host, $depth) = @_;
       local(@addrs);

       # prevent infinite recursion
       if ($depth > $MAXDEPTH)
       {
               print "  "x($depth+1),
                     "This looks like a mail loop to me.\n";
               return;
       }

       @addrs = &vrfy($name, $host);
#       print "@addrs\n";

       foreach (@addrs)
       {
               print "  "x$depth, "$_\n";

               next if /^\\?[^@]+$/; # local address

               # some smtps return the same e-mail address
               # thus causing a loop
               if (/$name@$host/i)
               {
                       print "  "x($depth+1),
                        "warning! twisted smtp daemon.\n";
                       return;
               }

               if (/^([\w-]+)@([\w.-]+)$/)
               {
                       do checkit($1, $2, $depth+1);
               }
               else
               {
                       print "  "x($depth+1),
                        "I don't understand this address, I hope you do\n";
               }
       }
}

#------------------------------------------------------------------------
# main program
#

&Getopts("vd:");
$snoop    = $opt_v;
$MAXDEPTH = $opt_d if $opt_d;

foreach (@ARGV)
{
       if (/^([\w-]+)@([\w.-]+)$/)
       {
               print "$_\n";
               do checkit($1, $2, 1);
       }
       else                    # must be a local address
       {
               print "$_@localhost\n";
               do checkit($_, "localhost", 1);
       }
}
SATANOSCILATEMYMETALICSONATAS
if test 6009 -ne "`wc -c < 'chkaddr'`"
then
       echo shar: "error transmitting 'chkaddr'" '(should have been 6009 characters)'
fi
chmod 755 'chkaddr'
exit 0
#       End of shell archive

--
Trent A. Fisher, Systems Manager/Administrator/Programmer
Portland State University                                     [email protected]
Computer Science Dept.                 Stop Software Monopolies!  Join the LPF
    "Don't ask me how it works, or I'll start to whimper." -- Arthur Dent