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: trent@sirius.cs.pdx.edu (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: References: 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: trent@cs.pdx.edu In article 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 trent@ursula.ee.pdx.edu will give you (indentation shows recursion level): trent@ursula.ee.pdx.edu \trent trent@cs.pdx.edu "|/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 trent@cs.pdx.edu. #! /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 (trent@cs.pdx.edu) # With portions of code stolen from eay@psych.psy.uq.oz.au (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. # foo@bletch.mit.edu # 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 () { 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 () { 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() { 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"; $_=; 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 trent@cs.pdx.edu Computer Science Dept. Stop Software Monopolies! Join the LPF "Don't ask me how it works, or I'll start to whimper." -- Arthur Dent .