'di ';
'ds 00 \\"';
'ig 00 ';
#
# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin.
#
use 5.001;
use Fcntl;
# system requirements:
# must have 'nslookup' and 'hostname' programs.
# 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)
# 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
# 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
$port = 'smtp';
$av0 = $0;
select(STDERR);
$0 = "$av0 - running hostname";
$0 = "$av0 - lookup host FQDN and IP addr";
$0 = "$av0 - parsing args";
$usage = "Usage: $av0 [-1avwd] user[\@host] [user2[host2] ...]";
for $a (@ARGV) {
die $usage if $a eq "-";
eval '$'."flag_$2 += 1";
}
next if $a eq "-";
die $usage if $a =~ /^-/;
}
if ($valid) {
if ($valid == 1) {
$validRequirement = 0.8;
} elsif ($valid == 2) {
$validRequirement = 1.0;
} elsif ($valid == 3) {
$validRequirement = 0.9;
} else {
print "validRequirement = $validRequirement\n" if $debug;
}
}
HOST:
while (@hosts) {
# is this server already known to be bad?
$0 = "$av0 - looking up $server";
next;
}
# do we already have an mx record for this host?
# look it up, or try for an mx.
$0 = "$av0 - gethostbyname($server)";
# if we can't get an A record, try for an MX record.
unless($thataddr) {
next HOST;
}
# get a connection, or look for an mx
$0 = "$av0 - socket to $server";
'PeerAddr' => $server,
'PeerPort' => $port,
'Proto' => 'tcp');
$0 = "$av0 - $server: could not connect: $!\n";
$emsg = $!;
}
next HOST;
}
$S->autoflush(1);
# read the greeting
$0 = "$av0 - talking to $server";
while(<$S>) {
alarm(0);
print if $watch;
if (/^(\d+)([- ])/) {
if ($1 != 220) {
$0 = "$av0 - bad numeric response from $server";
alarm(0);
print STDERR "$server: NOT 220 greeting: $_"
close($S);
next HOST;
}
}
last if ($2 eq " ");
} else {
$0 = "$av0 - bad response from $server";
print STDERR "$server: NOT 220 greeting: $_"
}
close($S);
next HOST;
}
}
alarm(0);
# if this causes problems, remove it
$0 = "$av0 - 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?
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);
if ($ecode) {
last USER;
}
for $s (@expansion) {
$s =~ s/[\n\r]//g;
$0 = "$av0 - parsing $server: $s";
if ($s =~ /^[25]51([- ]).*<(.+)>/) {
print "$s" if $watch;
print "\n" if $watch;
$s = "250$1<$2>";
$skipwatch = 0;
}
if ($s =~ /^250([- ])(.+)/) {
print "$s\n" if $skipwatch;
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) {
} else {
}
} else {
print "$newmxhost = &mx($newhost)\n"
$0 = "$av0 - parsing $newaddr [@$newmxhost]";
# 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 ($valid) {
} else {
}
} else {
# more work to do...
if ($valid) {
} else {
}
}
}
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 {
}
last if ($2 eq " ");
next;
}
# 553 is a known code...
if ($s =~ /^(553)([- ])/) {
if ($valid) {
print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n";
} else {
}
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 {
}
last if ($2 eq " ");
next;
}
last USER;
}
if ($valid) {
#
# now we decide if we are going to take these
# expansions or roll them back.
#
print "avgValid = $avgValid\n" if $debug;
if ($avgValid >= $validRequirement) {
while (@toExpn) {
}
while (@toFinal) {
}
} else {
print "Tossing some valid to avoid invalid ".&compact($u,$server)."\n" if ($avgValid > 0.0 && ($vw || $debug));
}
}
}
$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";
}
exit(0);
# abandon all attempts deliver to $server
# register the current addresses as the final ones
sub giveup
{
$0 = "$av0 - giving up on $server: $reason";
#
# add back a user if we gave up in the middle
#
#
# don't bother with this system anymore
#
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/) {
}
if ($redirect_okay =~ /\bdomainify\b/) {
}
push(@remaining_users,$u);
}
@users = @remaining_users;
for $u (@users) {
}
}
#
# This routine is used only within &giveup. It checks to
# see if we really have to giveup or if there is a second
# chance because we did something before that can be
# backtracked.
#
# %fallback{"$user *** $host"} tracks what is able to fallback
# %fellback{"$user *** $host"} tracks what has fallen back
#
# If there is a valid backtrack, then queue up the new possibility
#
sub try_fallback
{
if ($debug > 8) {
print "Fallback table $method:\n";
for $i (sort keys %fall_table) {
print "\t'$i'\t\t'$fall_table{$i}'\n";
}
print "Fellback table $method:\n";
for $i (sort keys %fellback) {
print "\t'$i'\t\t'$fellback{$i}'\n";
}
print "U: $user H: $host\n";
}
$us = "$user *** $host";
#
# Undo a previous fallback so that we can try again
# Nested fallbacks are avoided because they could
# lead to infinite loops
#
print "Already $method fell back from $us -> \n" if $debug;
$us = "$user *** $fallhost";
} elsif (($method eq 'mx') && (defined $mxbacktrace{$us}) && (defined $mx_secondary{$mxbacktrace{$us}})) {
print "Fallback an MX expansion $us -> \n" if $debug;
} else {
print "Oldhost($host, $us) = " if $debug;
}
print "$oldhost\n" if $debug;
if (((defined $fall_table{$us}) && ($ft = $us)) || ((defined $fall_table{$oldhost}) && ($ft = $oldhost))) {
print "Falling back ($method) $us -> $newhost (from $oldhost)\n" if $debug;
if ($method eq 'mx') {
if (! defined ($mxbacktrace{"$user *** $newhost"})) {
if (defined $mxbacktrace{"$user *** $oldhost"}) {
print "resetting oldhost $oldhost to the original: " if $debug;
print "$oldhost\n" if $debug;
}
print "mxbacktrace $user *** $newhost -> $oldhost\n" if $debug;
}
} else {
}
if (@so) {
print "Can still $method $us: @so\n" if $debug;
} else {
print "No more fallbacks for $us\n" if $debug;
delete $fall_table{$ft};
}
if (defined $create_host_backtrack{$us}) {
$create_host_backtrack{"$user *** $newhost"}
= $create_host_backtrack{$us};
}
return 1;
}
delete $temporary_redirect{$us};
return 0;
}
# return 1 if you could send mail to the address as is.
sub validAddr
{
local($addr) = @_;
print "validAddr($addr) = $res\n" if $debug;
$res;
}
sub do_validAddr
{
local($addr) = @_;
local($urx) = "[-A-Za-z_.0-9+]+";
# \u
return 0 if ($addr =~ /^\\/);
# ?@h
# @h:?
# h!u
# u
# ?
print "validAddr($addr) = ???\n" if $debug;
return 0;
}
# Some systems use expn and vrfy interchangeably. Some only
# implement one or the other. Some check expn against mailing
# lists and vrfy against users. It doesn't appear to be
# consistent.
#
# So, what do we do? We try everything!
#
#
# Ranking of result codes: good: 250, 251/551, 252, 550, anything else
#
# Ranking of inputs: best: user@host.domain, okay: user
#
# Return value: $error_string, @responses_from_server
sub expn_vrfy
{
local($u,$server) = @_;
local(@c) = ('expn', 'vrfy');
local(@try_u) = $u;
push(@try_u,$1);
}
TRY:
for $c (@c) {
&ps("$c $try_u");
alarm(0);
$s = <$S>;
if ($s eq '') {
return "$server: lost connection";
}
if ($s !~ /^(\d+)([- ])/) {
return "$server: garbled reply to '$c $try_u'";
}
if ($1 == 250) {
$code = 250;
@ret = ("",$s);
return (@ret);
}
if ($1 == 551 || $1 == 251) {
$code = $1;
@ret = ("",$s);
next;
}
$code = 252;
@ret = ("",$s);
next;
}
$code = 550;
@ret = ("",$s);
next;
}
}
}
return @ret;
}
# sometimes the old parse routine (now parse2) didn't
# reject funky addresses.
sub parse
{
if ($newaddr =~ m,^["/],) {
}
}
# returns ($new_smtp_server,$new_address,$new_name)
# given a response from a SMTP server ($newaddr), the
# current host ($server), the old "name" and a flag that
# indicates if it is being called during the initial
# command line parsing ($parsing_args)
sub parse2
{
local($urx) = "[-A-Za-z_.0-9+]+";
local($unmangle);
#
# first, separate out the address part.
#
#
# [NAME] <ADDR [(NAME)]>
# [NAME] <[(NAME)] ADDR
# ADDR [(NAME)]
# (NAME) ADDR
# [(NAME)] <ADDR>
#
if ($newaddr =~ /^\<(.*)\>$/) {
print "<A:$1>\n" if $debug;
print "na = $newaddr\n" if $debug;
}
if ($newaddr =~ /^([^\<\>]*)\<([^\<\>]*)\>([^\<\>]*)$/) {
# address has a < > pair in it.
print "N:$1 <A:$2> N:$3\n" if $debug;
print "na = $newaddr\n" if $debug;
}
if ($newaddr =~ /^([^\(\)]*)\(([^\(\)]*)\)([^\(\)]*)$/) {
# address has a ( ) pair in it.
print "A:$1 (N:$2) A:$3\n" if $debug;
if (($f && $l) || !($f || $l)) {
# address looks like:
# foo (bar) baz or (bar)
# not allowed!
}
$newaddr = $f if $f;
$newaddr = $l if $l;
print "newaddr now = $newaddr\n" if $debug;
}
#
# @foo:bar
# j%k@l
# a@b
# b!a
# a
#
print "(\@:)" if $debug;
# this is a bit of a cheat, but it seems necessary
}
print "(\@)" if $debug;
}
if ($parsing_args) {
}
}
print STDERR "Could not parse $newaddr\n";
}
print "(?)" if $debug;
}
# return $u (@$server) unless $u includes reference to $server
sub compact
{
local($u, $server) = @_;
local($sp);
$se =~ s/(\W)/\\$1/g;
$sp = " (\@$server)";
if ($u !~ /$se/i) {
return "$u$sp";
}
return $u;
}
# remove empty (spaces don't count) members from an array
sub trim
{
local(@v) = @_;
local($v,@r);
for $v (@v) {
$v =~ s/^\s+//;
$v =~ s/\s+$//;
push(@r,$v) if ($v =~ /\S/);
}
return(@r);
}
# using the host part of an address, and the server name, add the
# servers' domain to the address if it doesn't already have a
# domain. Since this sometimes fails, save a back reference so
# it can be unrolled.
sub domainify
{
local($host,$domain_host,$u) = @_;
# cut of trailing dots
$host =~ s/\.$//;
$domain_host =~ s/\.$//;
if ($domain_host !~ /\./) {
#
# domain host isn't, keep $host whatever it is
#
print "domainify($host,$domain_host) = $host\n" if $debug;
return $host;
}
#
# There are several weird situtations that need to be
# accounted for. They have to do with domain relay hosts.
#
# Examples:
# host server "right answer"
#
# shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu
# shiva cs.berkeley.edu shiva.cs.berekley.edu
# cumulus reed.edu @reed.edu:cumulus.uucp
# tiberius tc.cornell.edu tiberius.tc.cornell.edu
#
# The first try must always be to cut the domain part out of
# the server and tack it onto the host.
#
# A reasonable second try is to tack the whole server part onto
# the host and for each possible repeated element, eliminate
# just that part.
#
# These extra "guesses" get put into the %domainify_fallback
# array. They will be used to give addresses a second chance
# in the &giveup routine
#
local(%fallback);
local($long);
$long = "$host $domain_host";
$long =~ tr/A-Z/a-z/;
print "long = $long\n" if $debug;
# matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu
print "condensed fallback $host $domain_host -> $long\n" if $debug;
}
local($fh);
$fh = $domain_host;
while ($fh =~ /\./) {
$fh =~ s/^[^\.]+\.//;
}
($domain = $domain_host) =~ s/^[^\.]+//;
if ($domain =~ /\./);
if ($host =~ /\./) {
#
# Host is already okay, but let's look for multiple
# interpretations
#
print "domainify($host,$domain_host) = $host\n" if $debug;
$domainify_fallback{"$u *** $host"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
return $host;
}
$domain = ".$domain_host"
if ($domain !~ /\..*\./);
$newhost = "$host$domain";
print "domainify($host,$domain_host) = $newhost\n" if $debug;
$domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
if ($debug) {
print "fallback = ";
print $domainify_fallback{"$u *** $newhost"}
if defined($domainify_fallback{"$u *** $newhost"});
print "\n";
}
return $newhost;
}
# return the first non-empty element of an array
sub firstname
{
local(@names) = @_;
local($n);
while(@names) {
$n = shift(@names);
return $n if $n =~ /\S/;
}
return undef;
}
# queue up more addresses to expand
sub expn
{
if ($host) {
} else {
}
print "expn($host,$addr,$name)\n" if $debug;
return "\t$addr\n";
} else {
}
}
# compute the numerical average value of an array
sub average
{
local(@e) = @_;
return 0 unless @e;
local($e,$sum);
for $e (@e) {
$sum += $e;
}
$sum / @e;
}
# print to the server (also to stdout, if -w)
sub ps
{
local($p) = @_;
print ">>> $p\n" if $watch;
print $S "$p\n";
}
# return case-adjusted name for a host (for comparison purposes)
sub trhost
{
# treat foo.bar as an alias for Foo.BAR
local($host) = @_;
$trhost =~ tr/A-Z/a-z/;
} else {
}
}
# re-queue users if an mx record dictates a redirect
# don't allow a user to be redirected more than once
sub mxredirect
{
local($u,$nserver,@still_there);
$0 = "$av0 - mx redirect $server -> $nserver\n";
for $u (@users) {
if (defined $mxbacktrace{"$u *** $nserver"}) {
push(@still_there,$u);
} else {
print "mxbacktrace{$u *** $nserver} = $server\n"
if ($debug > 1);
}
}
@users = @still_there;
if (! @users) {
return $nserver;
} else {
return undef;
}
}
return undef;
}
# follow mx records, return a hostname
# also follow temporary redirections comming from &domainify and
# &mxlookup
sub mx
{
local($h,$u) = @_;
for (;;) {
$0 = "$av0 - mx expand $h";
return $h;
}
if ($u) {
if (defined $temporary_redirect{"$u *** $h"}) {
$0 = "$av0 - internal redirect $h";
print "Temporary redirect taken $u *** $h -> " if $debug;
$h = $temporary_redirect{"$u *** $h"};
print "$h\n" if $debug;
next;
}
if (defined $temporary_redirect{"$u *** $htr"}) {
$0 = "$av0 - internal redirect $h";
print "temporary redirect taken $u *** $h -> " if $debug;
$h = $temporary_redirect{"$u *** $htr"};
print "$h\n" if $debug;
next;
}
}
return $h;
}
}
# look up mx records with the name server.
# re-queue expansion requests if possible
# optionally give up on this host.
sub mxlookup
{
local(*T);
local(*NSLOOKUP);
local($o0) = $0;
local($nserver);
local(%fallback);
return 0 unless $lastchance;
return 0;
}
$0 = "$av0 - nslookup of $server";
print T "set querytype=MX\n";
print T "$server\n";
close(T);
$cpref = 1.0E12;
undef $nserver;
while(<NSLOOKUP>) {
print if ($debug > 2);
$nh = $1;
if (/preference = (\d+)/) {
$pref = $1;
} elsif ($pref) {
}
}
}
#
# These addresss are hosed. Kaput! Dead!
# However, if we created the address in the
# first place then there is a chance of
# salvation.
#
1 while(<NSLOOKUP>);
close(NSLOOKUP);
return 0 unless $lastchance;
return 0;
}
}
close(NSLOOKUP);
unless ($nserver) {
$0 = "$o0 - finished mxlookup";
return 0 unless $lastchance;
return 0;
}
# provide fallbacks in case $nserver doesn't work out
}
$0 = "$av0 - gethostbyname($nserver)";
unless ($thataddr) {
$0 = $o0;
return 0 unless $lastchance;
return 0;
}
print "MX($server) = $nserver\n" if $debug;
# redeploy the users
return 0 unless $lastchance;
return 0;
}
$0 = "$o0 - finished mxlookup";
return 1;
}
# if mx expansion did not help to resolve an address
# (ie: foo@bar became @baz:foo@bar, then undo the
# expansion).
# this is only used by &final
sub mxunroll
{
local($r) = 0;
print "looking for mxbacktrace{$addr *** $host}\n"
if ($debug > 1);
while (defined $mxbacktrace{"$addr *** $host"}) {
print "Unrolling MX expnasion: \@$host:$addr -> "
print "\@$host:$addr\n"
$r = 1;
}
return 1 if $r;
$addr = "\@$host:$addr"
if ($host =~ /\./);
return 0;
}
# register a completed expnasion. Make the final address as
# simple as possible.
sub final
{
local($he);
#
# If we created the domain, then let's undo the
# damage...
#
if (defined $create_host_backtrack{"$addr *** $host"}) {
while (defined $create_host_backtrack{"$addr *** $host"}) {
print "Un&domainifying($host) = " if $debug;
print "$host\n" if $debug;
}
$error = "$host: could not locate";
} else {
#
# If we only want valid addresses, toss out
# bad host names.
#
if ($valid) {
print STDERR "\@$host:$addr ($name) Non-existent domain\n";
return "";
}
}
}
MXUNWIND: {
$0 = "$av0 - final parsing of \@$host:$addr";
if ($addr !~ /@/) {
# addr does not contain any host
$addr = "$addr@$host";
# if host part really something else, use the something
# else.
if ($addr =~ m/(.*)\@([^\@]+)$/) {
print "au = $au ah = $ah\n" if $debug;
if (defined $temporary_redirect{"$addr *** $ah"}) {
print "Rewrite! to $addr\n" if $debug;
next MXUNWIND;
}
}
# addr does not contain full host
if ($valid) {
if ($host =~ /^([^\.]+)(\..+)$/) {
# host part has a . in it - foo.bar
# addr part has not .
# and matches beginning of
# host part -- tack on a
# domain name.
} else {
&& redo MXUNWIND;
}
} else {
&& redo MXUNWIND;
}
} else {
$addr = "${addr}[\@$host]"
if ($host =~ /\./);
}
}
}
if ($valid) {
push(@final,"$name<$addr>");
} else {
push(@final,"$name<$addr>$error");
}
"\t$name<$addr>$error\n";
}
sub alarm
{
alarm(3600);
}
# this involves one great big ugly hack.
# the "next HOST" unwinds the stack!
sub handle_alarm
{
next HOST;
}
# read the rest of the current smtp daemon's response (and toss it away)
sub read_response
{
local(@resp);
print $s if $watch;
while(($done eq "-") && ($s = <$S>) && ($s =~ /^\d+([- ])/)) {
print $s if $watch;
$done = $1;
push(@resp,$s);
}
return @resp;
}
# print args if verbose. Return them in any case
sub verbose
{
local(@tp) = @_;
print "@tp" if $verbose;
}
# to pass perl -w:
@tp;
$flag_a;
$flag_d;
$flag_1;
.00 ;
'di
.nr % 0
.\\"'; __END__
.AT 3
.B expn
.RI [ -a ]
.RI [ -v ]
.RI [ -w ]
.RI [ -d ]
.RI [ -1 ]
.B expn
.B expn
and
.B vrfy
.B expn
.I -aa
.IR -a ,
.I -a
.I -a
.LP
.B expn
.I -1
.I -1
.I -111
.LP
.B expn
.IR -v ,
.B expn
.IR -w ,
.B expn
.IR -d ,
.PD 0
.B expn
or
.LP
.B expn
.LP
.B expn
.I $have_nslookup = 1
to read
.I $have_nslookup =
.IR 0 .
.LP
.B expn
.B expn