📄 expn.pl
字号:
#!/usr/bin/perl
'di ';
'ds 00 \\"';
'ig 00 ';
#
# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin.
#
use 5.001;
use IO::Socket;
# system requirements:
# must have 'nslookup' and 'hostname' programs.
# $OrigHeader: /home/muir/bin/RCS/expn,v 3.11 1997/09/10 08:14:02 muir Exp muir $
# TODO:
# less magic should apply to command-line addresses
# less magic should apply to local addresses
# add magic to deal with cross-domain cnames
# disconnect & reconnect after 25 commands to the same sendmail 8.8.* host
# Checklist: (hard addresses)
# 250 Kimmo Suominen <"|/usr/local/mh/lib/slocal -user kim"@grendel.tac.nyc.ny.us>
# harry@hofmann.cs.Berkeley.EDU -> harry@tenet (.berkeley.edu) [dead]
# bks@cs.berkeley.edu -> shiva.CS (.berkeley.edu) [dead]
# dan@tc.cornell.edu -> brown@tiberius (.tc.cornell.edu)
#############################################################################
#
# Copyright (c) 1993 David Muir Sharnoff
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
# must display the following acknowledgement:
# This product includes software developed by the David Muir Sharnoff.
# 4. The name of David Sharnoff may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL DAVID MUIR SHARNOFF BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# This copyright notice derrived from material copyrighted by the Regents
# of the University of California.
#
# Contributions accepted.
#
#############################################################################
# overall structure:
# in an effort to not trace each address individually, but rather
# ask each server in turn a whole bunch of questions, addresses to
# be expanded are queued up.
#
# This means that all accounting w.r.t. an address must be stored in
# various arrays. Generally these arrays are indexed by the
# string "$addr *** $server" where $addr is the address to be
# expanded "foo" or maybe "foo@bar" and $server is the hostname
# of the SMTP server to contact.
#
# important global variables:
#
# @hosts : list of servers still to be contacted
# $server : name of the current we are currently looking at
# @users = $users{@hosts[0]} : addresses to expand at this server
# $u = $users[0] : the current address being expanded
# $names{"$users[0] *** $server"} : the 'name' associated with the address
# $mxbacktrace{"$users[0] *** $server"} : record of mx expansion
# $mx_secondary{$server} : other mx relays at the same priority
# $domainify_fallback{"$users[0] *** $server"} : alternative names to try
# instead of $server if $server doesn't work
# $temporary_redirect{"$users[0] *** $server"} : when trying alternates,
# temporarily channel all tries along current path
# $giveup{$server} : do not bother expanding addresses at $server
# $verbose : -v
# $watch : -w
# $vw : -v or -w
# $debug : -d
# $valid : -a
# $levels : -1
# $S : the socket connection to $server
$have_nslookup = 1; # we have the nslookup program
$port = 'smtp';
$av0 = $0;
$ENV{'PATH'} .= ":/usr/etc" unless $ENV{'PATH'} =~ m,/usr/etc,;
$ENV{'PATH'} .= ":/usr/ucb" unless $ENV{'PATH'} =~ m,/usr/ucb,;
select(STDERR);
$0 = "$av0 - running hostname";
chop($name = `hostname || uname -n`);
$0 = "$av0 - lookup host FQDN and IP addr";
($hostname,$aliases,$type,$len,$thisaddr) = gethostbyname($name);
$0 = "$av0 - parsing args";
$usage = "Usage: $av0 [-1avwd] user[\@host] [user2[host2] ...]";
for $a (@ARGV) {
die $usage if $a eq "-";
while ($a =~ s/^(-.*)([1avwd])/$1/) {
eval '$'."flag_$2 += 1";
}
next if $a eq "-";
die $usage if $a =~ /^-/;
&expn(&parse($a,$hostname,undef,1));
}
$verbose = $flag_v;
$watch = $flag_w;
$vw = $flag_v + $flag_w;
$debug = $flag_d;
$valid = $flag_a;
$levels = $flag_1;
die $usage unless @hosts;
if ($valid) {
if ($valid == 1) {
$validRequirement = 0.8;
} elsif ($valid == 2) {
$validRequirement = 1.0;
} elsif ($valid == 3) {
$validRequirement = 0.9;
} else {
$validRequirement = (1 - (1/($valid-3)));
print "validRequirement = $validRequirement\n" if $debug;
}
}
HOST:
while (@hosts) {
$server = shift(@hosts);
@users = split(' ',$users{$server});
delete $users{$server};
# is this server already known to be bad?
$0 = "$av0 - looking up $server";
if ($giveup{$server}) {
&giveup('mx domainify',$giveup{$server});
next;
}
# do we already have an mx record for this host?
next HOST if &mxredirect($server,*users);
# look it up, or try for an mx.
$0 = "$av0 - gethostbyname($server)";
($name,$aliases,$type,$len,$thataddr) = gethostbyname($server);
# if we can't get an A record, try for an MX record.
unless($thataddr) {
&mxlookup(1,$server,"$server: could not resolve name",*users);
next HOST;
}
# get a connection, or look for an mx
$0 = "$av0 - socket to $server";
$S = new IO::Socket::INET (
'PeerAddr' => $server,
'PeerPort' => $port,
'Proto' => 'tcp');
if (! $S || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) {
$0 = "$av0 - $server: could not connect: $!\n";
$emsg = $!;
unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) {
&giveup('mx',"$server: Could not connect: $emsg");
}
next HOST;
}
$S->autoflush(1);
# read the greeting
$0 = "$av0 - talking to $server";
&alarm("greeting with $server",'');
while(<$S>) {
alarm(0);
print if $watch;
if (/^(\d+)([- ])/) {
if ($1 != 220) {
$0 = "$av0 - bad numeric response from $server";
&alarm("giving up after bad response from $server",'');
&read_response($2,$watch);
alarm(0);
print STDERR "$server: NOT 220 greeting: $_"
if ($debug || $vw);
if (&mxlookup(0,$server,"$server: did not respond with a 220 greeting",*users)) {
close($S);
next HOST;
}
}
last if ($2 eq " ");
} else {
$0 = "$av0 - bad response from $server";
print STDERR "$server: NOT 220 greeting: $_"
if ($debug || $vw);
unless (&mxlookup(0,$server,"$server: did not respond with SMTP codes",*users)) {
&giveup('',"$server: did not talk SMTP");
}
close($S);
next HOST;
}
&alarm("greeting with $server",'');
}
alarm(0);
# if this causes problems, remove it
$0 = "$av0 - sending helo to $server";
&alarm("sending helo to $server","");
&ps("helo $hostname");
while(<$S>) {
print if $watch;
last if /^\d+ /;
}
alarm(0);
# try the users, one by one
USER:
while(@users) {
$u = shift(@users);
$0 = "$av0 - expanding $u [\@$server]";
# do we already have a name for this user?
$oldname = $names{"$u *** $server"};
print &compact($u,$server)." ->\n" if ($verbose && ! $valid);
if ($valid) {
#
# when running with -a, we delay taking any action
# on the results of our query until we have looked
# at the complete output. @toFinal stores expansions
# that will be final if we take them. @toExpn stores
# expnansions that are not final. @isValid keeps
# track of our ability to send mail to each of the
# expansions.
#
@isValid = ();
@toFinal = ();
@toExpn = ();
}
# ($ecode,@expansion) = &expn_vrfy($u,$server);
(@foo) = &expn_vrfy($u,$server);
($ecode,@expansion) = @foo;
if ($ecode) {
&giveup('',$ecode,$u);
last USER;
}
for $s (@expansion) {
$s =~ s/[\n\r]//g;
$0 = "$av0 - parsing $server: $s";
$skipwatch = $watch;
if ($s =~ /^[25]51([- ]).*<(.+)>/) {
print "$s" if $watch;
print "(pretending 250$1<$2>)" if ($debug && $watch);
print "\n" if $watch;
$s = "250$1<$2>";
$skipwatch = 0;
}
if ($s =~ /^250([- ])(.+)/) {
print "$s\n" if $skipwatch;
($done,$addr) = ($1,$2);
($newhost, $newaddr, $newname) = &parse($addr,$server,$oldname, $#expansion == 0);
print "($newhost, $newaddr, $newname) = &parse($addr, $server, $oldname)\n" if $debug;
if (! $newhost) {
# no expansion is possible w/o a new server to call
if ($valid) {
push(@isValid, &validAddr($newaddr));
push(@toFinal,$newaddr,$server,$newname);
} else {
&verbose(&final($newaddr,$server,$newname));
}
} else {
$newmxhost = &mx($newhost,$newaddr);
print "$newmxhost = &mx($newhost)\n"
if ($debug && $newhost ne $newmxhost);
$0 = "$av0 - parsing $newaddr [@$newmxhost]";
print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1);
# If the new server is the current one,
# it would have expanded things for us
# if it could have. Mx records must be
# followed to compare server names.
# We are also done if the recursion
# count has been exceeded.
if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) {
if ($valid) {
push(@isValid, &validAddr($newaddr));
push(@toFinal,$newaddr,$newmxhost,$newname);
} else {
&verbose(&final($newaddr,$newmxhost,$newname));
}
} else {
# more work to do...
if ($valid) {
push(@isValid, &validAddr($newaddr));
push(@toExpn,$newmxhost,$newaddr,$newname,$level{"$u *** $server"});
} else {
&verbose(&expn($newmxhost,$newaddr,$newname,$level{"$u *** $server"}));
}
}
}
last if ($done eq " ");
next;
}
# 550 is a known code... Should the be
# included in -a output? Might be a bug
# here. Does it matter? Can assume that
# there won't be UNKNOWN USER responses
# mixed with valid users?
if ($s =~ /^(550)([- ])/) {
if ($valid) {
print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n";
} else {
&verbose(&final($u,$server,$oldname,"USER UNKNOWN"));
}
last if ($2 eq " ");
next;
}
# 553 is a known code...
if ($s =~ /^(553)([- ])/) {
if ($valid) {
print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n";
} else {
&verbose(&final($u,$server,$oldname,"USER AMBIGUOUS"));
}
last if ($2 eq " ");
next;
}
# 252 is a known code...
if ($s =~ /^(252)([- ])/) {
if ($valid) {
print STDERR "\@$server:$u ($oldname) REFUSED TO VRFY\n";
} else {
&verbose(&final($u,$server,$oldname,"REFUSED TO VRFY"));
}
last if ($2 eq " ");
next;
}
&giveup('',"$server: did not grok '$s'",$u);
last USER;
}
if ($valid) {
#
# now we decide if we are going to take these
# expansions or roll them back.
#
$avgValid = &average(@isValid);
print "avgValid = $avgValid\n" if $debug;
if ($avgValid >= $validRequirement) {
print &compact($u,$server)." ->\n" if $verbose;
while (@toExpn) {
&verbose(&expn(splice(@toExpn,0,4)));
}
while (@toFinal) {
&verbose(&final(splice(@toFinal,0,3)));
}
} else {
print "Tossing some valid to avoid invalid ".&compact($u,$server)."\n" if ($avgValid > 0.0 && ($vw || $debug));
print &compact($u,$server)." ->\n" if $verbose;
&verbose(&final($u,$server,$newname));
}
}
}
&alarm("sending 'quit' to $server",'');
$0 = "$av0 - sending 'quit' to $server";
&ps("quit");
while(<$S>) {
print if $watch;
last if /^\d+ /;
}
close($S);
alarm(0);
}
$0 = "$av0 - printing final results";
print "----------\n" if $vw;
select(STDOUT);
for $f (sort @final) {
print "$f\n";
}
unlink("/tmp/expn$$");
exit(0);
# abandon all attempts deliver to $server
# register the current addresses as the final ones
sub giveup
{
local($redirect_okay,$reason,$user) = @_;
local($us,@so,$nh,@remaining_users);
local($pk,$file,$line);
($pk, $file, $line) = caller;
$0 = "$av0 - giving up on $server: $reason";
#
# add back a user if we gave up in the middle
#
push(@users,$user) if $user;
#
# don't bother with this system anymore
#
unless ($giveup{$server}) {
$giveup{$server} = $reason;
print STDERR "$reason\n";
}
print "Giveup at $file:$line!!! redirect okay = $redirect_okay; $reason\n" if $debug;
#
# Wait!
# Before giving up, see if there is a chance that
# there is another host to redirect to!
# (Kids, don't do this at home! Hacking is a dangerous
# crime and you could end up behind bars.)
#
for $u (@users) {
if ($redirect_okay =~ /\bmx\b/) {
next if &try_fallback('mx',$u,*server,
*mx_secondary,
*already_mx_fellback);
}
if ($redirect_okay =~ /\bdomainify\b/) {
next if &try_fallback('domainify',$u,*server,
*domainify_fallback,
*already_domainify_fellback);
}
push(@remaining_users,$u);
}
@users = @remaining_users;
for $u (@users) {
print &compact($u,$server)." ->\n" if ($verbose && $valid && $u);
&verbose(&final($u,$server,$names{"$u *** $server"},$reason));
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -