Deleted Added
full compact
expn.1 (82794) expn.1 (119679)
1#!@PERL@
2'di ';
3'ds 00 \\"';
4'ig 00 ';
5#
6# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin.
7#
8
9# hardcoded constants, should work fine for BSD-based systems
10#require 'sys/socket.ph'; # perl 4
11use Socket; # perl 5
12$AF_INET = &AF_INET;
13$SOCK_STREAM = &SOCK_STREAM;
14$sockaddr = 'S n a4 x8';
15
16# system requirements:
17# must have 'nslookup' and 'hostname' programs.
18
1#!@PERL@
2'di ';
3'ds 00 \\"';
4'ig 00 ';
5#
6# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin.
7#
8
9# hardcoded constants, should work fine for BSD-based systems
10#require 'sys/socket.ph'; # perl 4
11use Socket; # perl 5
12$AF_INET = &AF_INET;
13$SOCK_STREAM = &SOCK_STREAM;
14$sockaddr = 'S n a4 x8';
15
16# system requirements:
17# must have 'nslookup' and 'hostname' programs.
18
19# $Header: /proj/bank/cvsroot/am-utils/scripts/expn.1,v 1.1.1.1 1998/11/05 02:04:58 ezk Exp $
19# $Header: /proj/bank/cvsroot/am-utils/scripts/expn.1,v 1.1.1.1.2.2 2003/03/16 01:47:01 ezk Exp $
20
21# TODO:
22# less magic should apply to command-line addresses
23# less magic should apply to local addresses
24# add magic to deal with cross-domain cnames
25
26# Checklist: (hard addresses)
27# 250 Kimmo Suominen <"|/usr/local/mh/lib/slocal -user kim"@grendel.tac.nyc.ny.us>

--- 10 unchanged lines hidden (view full) ---

38# modification, are permitted provided that the following conditions
39# are met:
40# 1. Redistributions of source code must retain the above copyright
41# notice, this list of conditions and the following disclaimer.
42# 2. Redistributions in binary form must reproduce the above copyright
43# notice, this list of conditions and the following disclaimer in the
44# documentation and/or other materials provided with the distribution.
45# 3. All advertising materials mentioning features or use of this software
20
21# TODO:
22# less magic should apply to command-line addresses
23# less magic should apply to local addresses
24# add magic to deal with cross-domain cnames
25
26# Checklist: (hard addresses)
27# 250 Kimmo Suominen <"|/usr/local/mh/lib/slocal -user kim"@grendel.tac.nyc.ny.us>

--- 10 unchanged lines hidden (view full) ---

38# modification, are permitted provided that the following conditions
39# are met:
40# 1. Redistributions of source code must retain the above copyright
41# notice, this list of conditions and the following disclaimer.
42# 2. Redistributions in binary form must reproduce the above copyright
43# notice, this list of conditions and the following disclaimer in the
44# documentation and/or other materials provided with the distribution.
45# 3. All advertising materials mentioning features or use of this software
46# must display the following acknowledgement:
46# must display the following acknowledgment:
47# This product includes software developed by the David Muir Sharnoff.
48# 4. The name of David Sharnoff may not be used to endorse or promote products
49# derived from this software without specific prior written permission.
50#
51# THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``AS IS'' AND
52# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54# ARE DISCLAIMED. IN NO EVENT SHALL DAVID MUIR SHARNOFF BE LIABLE
55# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61# SUCH DAMAGE.
62#
47# This product includes software developed by the David Muir Sharnoff.
48# 4. The name of David Sharnoff may not be used to endorse or promote products
49# derived from this software without specific prior written permission.
50#
51# THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``AS IS'' AND
52# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54# ARE DISCLAIMED. IN NO EVENT SHALL DAVID MUIR SHARNOFF BE LIABLE
55# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61# SUCH DAMAGE.
62#
63# This copyright notice derrived from material copyrighted by the Regents
63# This copyright notice derived from material copyrighted by the Regents
64# of the University of California.
65#
66# Contributions accepted.
67#
68#############################################################################
69
70# overall structure:
71# in an effort to not trace each address individually, but rather

--- 11 unchanged lines hidden (view full) ---

83#
84# @hosts : list of servers still to be contacted
85# $server : name of the current we are currently looking at
86# @users = $users{@hosts[0]} : addresses to expand at this server
87# $u = $users[0] : the current address being expanded
88# $names{"$users[0] *** $server"} : the 'name' associated with the address
89# $mxbacktrace{"$users[0] *** $server"} : record of mx expansion
90# $mx_secondary{$server} : other mx relays at the same priority
64# of the University of California.
65#
66# Contributions accepted.
67#
68#############################################################################
69
70# overall structure:
71# in an effort to not trace each address individually, but rather

--- 11 unchanged lines hidden (view full) ---

83#
84# @hosts : list of servers still to be contacted
85# $server : name of the current we are currently looking at
86# @users = $users{@hosts[0]} : addresses to expand at this server
87# $u = $users[0] : the current address being expanded
88# $names{"$users[0] *** $server"} : the 'name' associated with the address
89# $mxbacktrace{"$users[0] *** $server"} : record of mx expansion
90# $mx_secondary{$server} : other mx relays at the same priority
91# $domainify_fallback{"$users[0] *** $server"} : alternative names to try
91# $domainify_fallback{"$users[0] *** $server"} : alternative names to try
92# instead of $server if $server doesn't work
93# $temporary_redirect{"$users[0] *** $server"} : when trying alternates,
94# temporarily channel all tries along current path
95# $giveup{$server} : do not bother expanding addresses at $server
96# $verbose : -v
97# $watch : -w
98# $vw : -v or -w
99# $debug : -d

--- 72 unchanged lines hidden (view full) ---

172 $0 = "$av0 - gethostbyname($server)";
173
174 ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server);
175 # if we can't get an A record, try for an MX record.
176 unless($thataddr) {
177 &mxlookup(1,$server,"$server: could not resolve name",*users);
178 next HOST;
179 }
92# instead of $server if $server doesn't work
93# $temporary_redirect{"$users[0] *** $server"} : when trying alternates,
94# temporarily channel all tries along current path
95# $giveup{$server} : do not bother expanding addresses at $server
96# $verbose : -v
97# $watch : -w
98# $vw : -v or -w
99# $debug : -d

--- 72 unchanged lines hidden (view full) ---

172 $0 = "$av0 - gethostbyname($server)";
173
174 ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server);
175 # if we can't get an A record, try for an MX record.
176 unless($thataddr) {
177 &mxlookup(1,$server,"$server: could not resolve name",*users);
178 next HOST;
179 }
180
180
181 # get a connection, or look for an mx
182 $0 = "$av0 - socket to $server";
183 $that = pack($sockaddr, &AF_INET, $port, $thataddr);
184 socket(S, &AF_INET, &SOCK_STREAM, $proto)
185 || die "socket: $!";
186 $0 = "$av0 - bind to $server";
181 # get a connection, or look for an mx
182 $0 = "$av0 - socket to $server";
183 $that = pack($sockaddr, &AF_INET, $port, $thataddr);
184 socket(S, &AF_INET, &SOCK_STREAM, $proto)
185 || die "socket: $!";
186 $0 = "$av0 - bind to $server";
187 bind(S, $this)
187 bind(S, $this)
188 || die "bind $hostname,0: $!";
189 $0 = "$av0 - connect to $server";
190 print "debug = $debug server = $server\n" if $debug > 8;
191 if (! connect(S, $that) || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) {
192 $0 = "$av0 - $server: could not connect: $!\n";
193 $emsg = $!;
194 unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) {
195 &giveup('mx',"$server: Could not connect: $emsg");

--- 30 unchanged lines hidden (view full) ---

226 &giveup('',"$server: did not talk SMTP");
227 }
228 close(S);
229 next HOST;
230 }
231 &alarm("greeting with $server",'');
232 }
233 alarm(0);
188 || die "bind $hostname,0: $!";
189 $0 = "$av0 - connect to $server";
190 print "debug = $debug server = $server\n" if $debug > 8;
191 if (! connect(S, $that) || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) {
192 $0 = "$av0 - $server: could not connect: $!\n";
193 $emsg = $!;
194 unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) {
195 &giveup('mx',"$server: Could not connect: $emsg");

--- 30 unchanged lines hidden (view full) ---

226 &giveup('',"$server: did not talk SMTP");
227 }
228 close(S);
229 next HOST;
230 }
231 &alarm("greeting with $server",'');
232 }
233 alarm(0);
234
234
235 # if this causes problems, remove it
236 $0 = "$av0 - sending helo to $server";
237 &alarm("sending helo to $server","");
238 &ps("helo $hostname");
239 while(<S>) {
240 print if $watch;
241 last if /^\d+ /;
242 }

--- 6 unchanged lines hidden (view full) ---

249 $0 = "$av0 - expanding $u [\@$server]";
250
251 # do we already have a name for this user?
252 $oldname = $names{"$u *** $server"};
253
254 print &compact($u,$server)." ->\n" if ($verbose && ! $valid);
255 if ($valid) {
256 #
235 # if this causes problems, remove it
236 $0 = "$av0 - sending helo to $server";
237 &alarm("sending helo to $server","");
238 &ps("helo $hostname");
239 while(<S>) {
240 print if $watch;
241 last if /^\d+ /;
242 }

--- 6 unchanged lines hidden (view full) ---

249 $0 = "$av0 - expanding $u [\@$server]";
250
251 # do we already have a name for this user?
252 $oldname = $names{"$u *** $server"};
253
254 print &compact($u,$server)." ->\n" if ($verbose && ! $valid);
255 if ($valid) {
256 #
257 # when running with -a, we delay taking any action
257 # when running with -a, we delay taking any action
258 # on the results of our query until we have looked
259 # at the complete output. @toFinal stores expansions
260 # that will be final if we take them. @toExpn stores
258 # on the results of our query until we have looked
259 # at the complete output. @toFinal stores expansions
260 # that will be final if we take them. @toExpn stores
261 # expnansions that are not final. @isValid keeps
261 # expansions that are not final. @isValid keeps
262 # track of our ability to send mail to each of the
263 # expansions.
264 #
265 @isValid = ();
266 @toFinal = ();
267 @toExpn = ();
268 }
269

--- 29 unchanged lines hidden (view full) ---

299 if ($valid) {
300 push(@isValid, &validAddr($newaddr));
301 push(@toFinal,$newaddr,$server,$newname);
302 } else {
303 &verbose(&final($newaddr,$server,$newname));
304 }
305 } else {
306 $newmxhost = &mx($newhost,$newaddr);
262 # track of our ability to send mail to each of the
263 # expansions.
264 #
265 @isValid = ();
266 @toFinal = ();
267 @toExpn = ();
268 }
269

--- 29 unchanged lines hidden (view full) ---

299 if ($valid) {
300 push(@isValid, &validAddr($newaddr));
301 push(@toFinal,$newaddr,$server,$newname);
302 } else {
303 &verbose(&final($newaddr,$server,$newname));
304 }
305 } else {
306 $newmxhost = &mx($newhost,$newaddr);
307 print "$newmxhost = &mx($newhost)\n"
307 print "$newmxhost = &mx($newhost)\n"
308 if ($debug && $newhost ne $newmxhost);
309 $0 = "$av0 - parsing $newaddr [@$newmxhost]";
310 print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1);
308 if ($debug && $newhost ne $newmxhost);
309 $0 = "$av0 - parsing $newaddr [@$newmxhost]";
310 print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1);
311 # If the new server is the current one,
311 # If the new server is the current one,
312 # it would have expanded things for us
313 # if it could have. Mx records must be
314 # followed to compare server names.
315 # We are also done if the recursion
316 # count has been exceeded.
317 if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) {
318 if ($valid) {
319 push(@isValid, &validAddr($newaddr));

--- 12 unchanged lines hidden (view full) ---

332 }
333 }
334 last if ($done eq " ");
335 next;
336 }
337 # 550 is a known code... Should the be
338 # included in -a output? Might be a bug
339 # here. Does it matter? Can assume that
312 # it would have expanded things for us
313 # if it could have. Mx records must be
314 # followed to compare server names.
315 # We are also done if the recursion
316 # count has been exceeded.
317 if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) {
318 if ($valid) {
319 push(@isValid, &validAddr($newaddr));

--- 12 unchanged lines hidden (view full) ---

332 }
333 }
334 last if ($done eq " ");
335 next;
336 }
337 # 550 is a known code... Should the be
338 # included in -a output? Might be a bug
339 # here. Does it matter? Can assume that
340 # there won't be UNKNOWN USER responses
340 # there won't be UNKNOWN USER responses
341 # mixed with valid users?
342 if ($s =~ /^(550)([- ])/) {
343 if ($valid) {
344 print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n";
345 } else {
346 &verbose(&final($u,$server,$oldname,"USER UNKNOWN"));
347 }
348 last if ($2 eq " ");
349 next;
341 # mixed with valid users?
342 if ($s =~ /^(550)([- ])/) {
343 if ($valid) {
344 print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n";
345 } else {
346 &verbose(&final($u,$server,$oldname,"USER UNKNOWN"));
347 }
348 last if ($2 eq " ");
349 next;
350 }
351 # 553 is a known code...
350 }
351 # 553 is a known code...
352 if ($s =~ /^(553)([- ])/) {
353 if ($valid) {
354 print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n";
355 } else {
356 &verbose(&final($u,$server,$oldname,"USER AMBIGUOUS"));
357 }
358 last if ($2 eq " ");
359 next;
352 if ($s =~ /^(553)([- ])/) {
353 if ($valid) {
354 print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n";
355 } else {
356 &verbose(&final($u,$server,$oldname,"USER AMBIGUOUS"));
357 }
358 last if ($2 eq " ");
359 next;
360 }
361 # 252 is a known code...
360 }
361 # 252 is a known code...
362 if ($s =~ /^(252)([- ])/) {
363 if ($valid) {
364 print STDERR "\@$server:$u ($oldname) REFUSED TO VRFY\n";
365 } else {
366 &verbose(&final($u,$server,$oldname,"REFUSED TO VRFY"));
367 }
368 last if ($2 eq " ");
369 next;
362 if ($s =~ /^(252)([- ])/) {
363 if ($valid) {
364 print STDERR "\@$server:$u ($oldname) REFUSED TO VRFY\n";
365 } else {
366 &verbose(&final($u,$server,$oldname,"REFUSED TO VRFY"));
367 }
368 last if ($2 eq " ");
369 next;
370 }
370 }
371 &giveup('',"$server: did not grok '$s'",$u);
372 last USER;
373 }
374
375 if ($valid) {
376 #
377 # now we decide if we are going to take these
378 # expansions or roll them back.

--- 83 unchanged lines hidden (view full) ---

462 for $u (@users) {
463 print &compact($u,$server)." ->\n" if ($verbose && $valid && $u);
464 &verbose(&final($u,$server,$names{"$u *** $server"},$reason));
465 }
466}
467#
468# This routine is used only within &giveup. It checks to
469# see if we really have to giveup or if there is a second
371 &giveup('',"$server: did not grok '$s'",$u);
372 last USER;
373 }
374
375 if ($valid) {
376 #
377 # now we decide if we are going to take these
378 # expansions or roll them back.

--- 83 unchanged lines hidden (view full) ---

462 for $u (@users) {
463 print &compact($u,$server)." ->\n" if ($verbose && $valid && $u);
464 &verbose(&final($u,$server,$names{"$u *** $server"},$reason));
465 }
466}
467#
468# This routine is used only within &giveup. It checks to
469# see if we really have to giveup or if there is a second
470# chance because we did something before that can be
470# chance because we did something before that can be
471# backtracked.
472#
473# %fallback{"$user *** $host"} tracks what is able to fallback
474# %fellback{"$user *** $host"} tracks what has fallen back
475#
476# If there is a valid backtrack, then queue up the new possibility
477#
478sub try_fallback

--- 7 unchanged lines hidden (view full) ---

486 print "\t'$i'\t\t'$fall_table{$i}'\n";
487 }
488 print "Fellback table $method:\n";
489 for $i (sort keys %fellback) {
490 print "\t'$i'\t\t'$fellback{$i}'\n";
491 }
492 print "U: $user H: $host\n";
493 }
471# backtracked.
472#
473# %fallback{"$user *** $host"} tracks what is able to fallback
474# %fellback{"$user *** $host"} tracks what has fallen back
475#
476# If there is a valid backtrack, then queue up the new possibility
477#
478sub try_fallback

--- 7 unchanged lines hidden (view full) ---

486 print "\t'$i'\t\t'$fall_table{$i}'\n";
487 }
488 print "Fellback table $method:\n";
489 for $i (sort keys %fellback) {
490 print "\t'$i'\t\t'$fellback{$i}'\n";
491 }
492 print "U: $user H: $host\n";
493 }
494
494
495 $us = "$user *** $host";
496 if (defined $fellback{$us}) {
497 #
498 # Undo a previous fallback so that we can try again
499 # Nested fallbacks are avoided because they could
500 # lead to infinite loops
501 #
502 $fallhost = $fellback{$us};

--- 31 unchanged lines hidden (view full) ---

534 if (@so) {
535 print "Can still $method $us: @so\n" if $debug;
536 $fall_table{$ft} = join(' ',@so);
537 } else {
538 print "No more fallbacks for $us\n" if $debug;
539 delete $fall_table{$ft};
540 }
541 if (defined $create_host_backtrack{$us}) {
495 $us = "$user *** $host";
496 if (defined $fellback{$us}) {
497 #
498 # Undo a previous fallback so that we can try again
499 # Nested fallbacks are avoided because they could
500 # lead to infinite loops
501 #
502 $fallhost = $fellback{$us};

--- 31 unchanged lines hidden (view full) ---

534 if (@so) {
535 print "Can still $method $us: @so\n" if $debug;
536 $fall_table{$ft} = join(' ',@so);
537 } else {
538 print "No more fallbacks for $us\n" if $debug;
539 delete $fall_table{$ft};
540 }
541 if (defined $create_host_backtrack{$us}) {
542 $create_host_backtrack{"$user *** $newhost"}
542 $create_host_backtrack{"$user *** $newhost"}
543 = $create_host_backtrack{$us};
544 }
545 $fellback{"$user *** $newhost"} = $oldhost;
546 &expn($newhost,$user,$names{$us},$level{$us});
547 return 1;
548 }
549 delete $temporary_redirect{$us};
550 $host = $oldhost;

--- 63 unchanged lines hidden (view full) ---

614 if ($s !~ /^(\d+)([- ])/) {
615 return "$server: garbled reply to '$c $try_u'";
616 }
617 if ($1 == 250) {
618 $code = 250;
619 @ret = ("",$s);
620 push(@ret,&read_response($2,$debug));
621 return (@ret);
543 = $create_host_backtrack{$us};
544 }
545 $fellback{"$user *** $newhost"} = $oldhost;
546 &expn($newhost,$user,$names{$us},$level{$us});
547 return 1;
548 }
549 delete $temporary_redirect{$us};
550 $host = $oldhost;

--- 63 unchanged lines hidden (view full) ---

614 if ($s !~ /^(\d+)([- ])/) {
615 return "$server: garbled reply to '$c $try_u'";
616 }
617 if ($1 == 250) {
618 $code = 250;
619 @ret = ("",$s);
620 push(@ret,&read_response($2,$debug));
621 return (@ret);
622 }
622 }
623 if ($1 == 551 || $1 == 251) {
624 $code = $1;
625 @ret = ("",$s);
626 push(@ret,&read_response($2,$debug));
627 next;
628 }
629 if ($1 == 252 && ($code == 0 || $code == 550)) {
630 $code = 252;

--- 9 unchanged lines hidden (view full) ---

640 }
641 &read_response($2,$watch);
642 }
643 }
644 return "$server: expn/vrfy not implemented" unless @ret;
645 return @ret;
646}
647# sometimes the old parse routine (now parse2) didn't
623 if ($1 == 551 || $1 == 251) {
624 $code = $1;
625 @ret = ("",$s);
626 push(@ret,&read_response($2,$debug));
627 next;
628 }
629 if ($1 == 252 && ($code == 0 || $code == 550)) {
630 $code = 252;

--- 9 unchanged lines hidden (view full) ---

640 }
641 &read_response($2,$watch);
642 }
643 }
644 return "$server: expn/vrfy not implemented" unless @ret;
645 return @ret;
646}
647# sometimes the old parse routine (now parse2) didn't
648# reject funky addresses.
648# reject funky addresses.
649sub parse
650{
651 local($oldaddr,$server,$oldname,$one_to_one) = @_;
652 local($newhost, $newaddr, $newname, $um) = &parse2($oldaddr,$server,$oldname,$one_to_one);
653 if ($newaddr =~ m,^["/],) {
654 return (undef, $oldaddr, $newname) if $valid;
655 return (undef, $um, $newname);
656 }
657 return ($newhost, $newaddr, $newname);
658}
659
660# returns ($new_smtp_server,$new_address,$new_name)
649sub parse
650{
651 local($oldaddr,$server,$oldname,$one_to_one) = @_;
652 local($newhost, $newaddr, $newname, $um) = &parse2($oldaddr,$server,$oldname,$one_to_one);
653 if ($newaddr =~ m,^["/],) {
654 return (undef, $oldaddr, $newname) if $valid;
655 return (undef, $um, $newname);
656 }
657 return ($newhost, $newaddr, $newname);
658}
659
660# returns ($new_smtp_server,$new_address,$new_name)
661# given a response from a SMTP server ($newaddr), the
661# given a response from a SMTP server ($newaddr), the
662# current host ($server), the old "name" and a flag that
662# current host ($server), the old "name" and a flag that
663# indicates if it is being called during the initial
663# indicates if it is being called during the initial
664# command line parsing ($parsing_args)
665sub parse2
666{
667 local($newaddr,$context_host,$old_name,$parsing_args) = @_;
668 local(@names) = $old_name;
669 local($urx) = "[-A-Za-z_.0-9+]+";
670 local($unmangle);
671

--- 86 unchanged lines hidden (view full) ---

758 for $v (@v) {
759 $v =~ s/^\s+//;
760 $v =~ s/\s+$//;
761 push(@r,$v) if ($v =~ /\S/);
762 }
763 return(@r);
764}
765# using the host part of an address, and the server name, add the
664# command line parsing ($parsing_args)
665sub parse2
666{
667 local($newaddr,$context_host,$old_name,$parsing_args) = @_;
668 local(@names) = $old_name;
669 local($urx) = "[-A-Za-z_.0-9+]+";
670 local($unmangle);
671

--- 86 unchanged lines hidden (view full) ---

758 for $v (@v) {
759 $v =~ s/^\s+//;
760 $v =~ s/\s+$//;
761 push(@r,$v) if ($v =~ /\S/);
762 }
763 return(@r);
764}
765# using the host part of an address, and the server name, add the
766# servers' domain to the address if it doesn't already have a
766# servers' domain to the address if it doesn't already have a
767# domain. Since this sometimes fails, save a back reference so
768# it can be unrolled.
769sub domainify
770{
771 local($host,$domain_host,$u) = @_;
772 local($domain,$newhost);
773
767# domain. Since this sometimes fails, save a back reference so
768# it can be unrolled.
769sub domainify
770{
771 local($host,$domain_host,$u) = @_;
772 local($domain,$newhost);
773
774 # cut of trailing dots
774 # cut of trailing dots
775 $host =~ s/\.$//;
776 $domain_host =~ s/\.$//;
777
778 if ($domain_host !~ /\./) {
779 #
780 # domain host isn't, keep $host whatever it is
781 #
782 print "domainify($host,$domain_host) = $host\n" if $debug;
783 return $host;
784 }
785
775 $host =~ s/\.$//;
776 $domain_host =~ s/\.$//;
777
778 if ($domain_host !~ /\./) {
779 #
780 # domain host isn't, keep $host whatever it is
781 #
782 print "domainify($host,$domain_host) = $host\n" if $debug;
783 return $host;
784 }
785
786 #
787 # There are several weird situtations that need to be
786 #
787 # There are several weird situations that need to be
788 # accounted for. They have to do with domain relay hosts.
789 #
788 # accounted for. They have to do with domain relay hosts.
789 #
790 # Examples:
790 # Examples:
791 # host server "right answer"
791 # host server "right answer"
792 #
792 #
793 # shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu
794 # shiva cs.berkeley.edu shiva.cs.berekley.edu
795 # cumulus reed.edu @reed.edu:cumulus.uucp
796 # tiberius tc.cornell.edu tiberius.tc.cornell.edu
797 #
793 # shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu
794 # shiva cs.berkeley.edu shiva.cs.berekley.edu
795 # cumulus reed.edu @reed.edu:cumulus.uucp
796 # tiberius tc.cornell.edu tiberius.tc.cornell.edu
797 #
798 # The first try must always be to cut the domain part out of
798 # The first try must always be to cut the domain part out of
799 # the server and tack it onto the host.
800 #
801 # A reasonable second try is to tack the whole server part onto
799 # the server and tack it onto the host.
800 #
801 # A reasonable second try is to tack the whole server part onto
802 # the host and for each possible repeated element, eliminate
802 # the host and for each possible repeated element, eliminate
803 # just that part.
804 #
805 # These extra "guesses" get put into the %domainify_fallback
806 # array. They will be used to give addresses a second chance
807 # in the &giveup routine
808 #
809
810 local(%fallback);
811
803 # just that part.
804 #
805 # These extra "guesses" get put into the %domainify_fallback
806 # array. They will be used to give addresses a second chance
807 # in the &giveup routine
808 #
809
810 local(%fallback);
811
812 local($long);
812 local($long);
813 $long = "$host $domain_host";
814 $long =~ tr/A-Z/a-z/;
815 print "long = $long\n" if $debug;
816 if ($long =~ s/^([^ ]+\.)([^ ]+) \2(\.[^ ]+\.[^ ]+)/$1$2$3/) {
817 # matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu
818 print "condensed fallback $host $domain_host -> $long\n" if $debug;
819 $fallback{$long} = 9;
820 }

--- 28 unchanged lines hidden (view full) ---

849 $newhost = "$host$domain";
850
851 $create_host_backtrack{"$u *** $newhost"} = $domain_host;
852 print "domainify($host,$domain_host) = $newhost\n" if $debug;
853 delete $fallback{$newhost};
854 $domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
855 if ($debug) {
856 print "fallback = ";
813 $long = "$host $domain_host";
814 $long =~ tr/A-Z/a-z/;
815 print "long = $long\n" if $debug;
816 if ($long =~ s/^([^ ]+\.)([^ ]+) \2(\.[^ ]+\.[^ ]+)/$1$2$3/) {
817 # matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu
818 print "condensed fallback $host $domain_host -> $long\n" if $debug;
819 $fallback{$long} = 9;
820 }

--- 28 unchanged lines hidden (view full) ---

849 $newhost = "$host$domain";
850
851 $create_host_backtrack{"$u *** $newhost"} = $domain_host;
852 print "domainify($host,$domain_host) = $newhost\n" if $debug;
853 delete $fallback{$newhost};
854 $domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
855 if ($debug) {
856 print "fallback = ";
857 print $domainify_fallback{"$u *** $newhost"}
857 print $domainify_fallback{"$u *** $newhost"}
858 if defined($domainify_fallback{"$u *** $newhost"});
859 print "\n";
860 }
861 return $newhost;
862}
863# return the first non-empty element of an array
864sub firstname
865{

--- 40 unchanged lines hidden (view full) ---

906# print to the server (also to stdout, if -w)
907sub ps
908{
909 local($p) = @_;
910 print ">>> $p\n" if $watch;
911 print S "$p\n";
912}
913# return case-adjusted name for a host (for comparison purposes)
858 if defined($domainify_fallback{"$u *** $newhost"});
859 print "\n";
860 }
861 return $newhost;
862}
863# return the first non-empty element of an array
864sub firstname
865{

--- 40 unchanged lines hidden (view full) ---

906# print to the server (also to stdout, if -w)
907sub ps
908{
909 local($p) = @_;
910 print ">>> $p\n" if $watch;
911 print S "$p\n";
912}
913# return case-adjusted name for a host (for comparison purposes)
914sub trhost
914sub trhost
915{
916 # treat foo.bar as an alias for Foo.BAR
917 local($host) = @_;
918 local($trhost) = $host;
919 $trhost =~ tr/A-Z/a-z/;
920 if ($trhost{$trhost}) {
921 $host = $trhost{$trhost};
922 } else {

--- 27 unchanged lines hidden (view full) ---

950 return $nserver;
951 } else {
952 return undef;
953 }
954 }
955 return undef;
956}
957# follow mx records, return a hostname
915{
916 # treat foo.bar as an alias for Foo.BAR
917 local($host) = @_;
918 local($trhost) = $host;
919 $trhost =~ tr/A-Z/a-z/;
920 if ($trhost{$trhost}) {
921 $host = $trhost{$trhost};
922 } else {

--- 27 unchanged lines hidden (view full) ---

950 return $nserver;
951 } else {
952 return undef;
953 }
954 }
955 return undef;
956}
957# follow mx records, return a hostname
958# also follow temporary redirections comming from &domainify and
958# also follow temporary redirections coming from &domainify and
959# &mxlookup
960sub mx
961{
962 local($h,$u) = @_;
963
964 for (;;) {
965 if (defined $mx{&trhost($h)} && $h ne $mx{&trhost($h)}) {
966 $0 = "$av0 - mx expand $h";

--- 18 unchanged lines hidden (view full) ---

985 }
986 }
987 return $h;
988 }
989}
990# look up mx records with the name server.
991# re-queue expansion requests if possible
992# optionally give up on this host.
959# &mxlookup
960sub mx
961{
962 local($h,$u) = @_;
963
964 for (;;) {
965 if (defined $mx{&trhost($h)} && $h ne $mx{&trhost($h)}) {
966 $0 = "$av0 - mx expand $h";

--- 18 unchanged lines hidden (view full) ---

985 }
986 }
987 return $h;
988 }
989}
990# look up mx records with the name server.
991# re-queue expansion requests if possible
992# optionally give up on this host.
993sub mxlookup
993sub mxlookup
994{
995 local($lastchance,$server,$giveup,*users) = @_;
996 local(*T);
997 local(*NSLOOKUP);
998 local($nh, $pref,$cpref);
999 local($o0) = $0;
1000 local($nserver);
1001 local($name,$aliases,$type,$len,$thataddr);

--- 26 unchanged lines hidden (view full) ---

1028 $cpref = $pref;
1029 } elsif ($pref) {
1030 $fallback{$pref} .= " $nh";
1031 }
1032 }
1033 }
1034 if (/Non-existent domain/) {
1035 #
994{
995 local($lastchance,$server,$giveup,*users) = @_;
996 local(*T);
997 local(*NSLOOKUP);
998 local($nh, $pref,$cpref);
999 local($o0) = $0;
1000 local($nserver);
1001 local($name,$aliases,$type,$len,$thataddr);

--- 26 unchanged lines hidden (view full) ---

1028 $cpref = $pref;
1029 } elsif ($pref) {
1030 $fallback{$pref} .= " $nh";
1031 }
1032 }
1033 }
1034 if (/Non-existent domain/) {
1035 #
1036 # These addresss are hosed. Kaput! Dead!
1036 # These addresses are hosed. Kaput! Dead!
1037 # However, if we created the address in the
1037 # However, if we created the address in the
1038 # first place then there is a chance of
1038 # first place then there is a chance of
1039 # salvation.
1040 #
1039 # salvation.
1040 #
1041 1 while(<NSLOOKUP>);
1041 1 while();
1042 close(NSLOOKUP);
1043 return 0 unless $lastchance;
1044 &giveup('domainify',"$server: Non-existent domain",undef,1);
1042 close(NSLOOKUP);
1043 return 0 unless $lastchance;
1044 &giveup('domainify',"$server: Non-existent domain",undef,1);
1045 return 0;
1045 return 0;
1046 }
1046 }
1047
1047
1048 }
1049 close(NSLOOKUP);
1050 unlink("/tmp/expn$$");
1051 unless ($nserver) {
1052 $0 = "$o0 - finished mxlookup";
1053 return 0 unless $lastchance;
1054 &giveup('mx domainify',"$server: Could not resolve address");
1055 return 0;

--- 21 unchanged lines hidden (view full) ---

1077 return 0 unless $lastchance;
1078 &giveup('mx domainify',"$nserver: only one level of mx redirect allowed");
1079 return 0;
1080 }
1081 $0 = "$o0 - finished mxlookup";
1082 return 1;
1083}
1084# if mx expansion did not help to resolve an address
1048 }
1049 close(NSLOOKUP);
1050 unlink("/tmp/expn$$");
1051 unless ($nserver) {
1052 $0 = "$o0 - finished mxlookup";
1053 return 0 unless $lastchance;
1054 &giveup('mx domainify',"$server: Could not resolve address");
1055 return 0;

--- 21 unchanged lines hidden (view full) ---

1077 return 0 unless $lastchance;
1078 &giveup('mx domainify',"$nserver: only one level of mx redirect allowed");
1079 return 0;
1080 }
1081 $0 = "$o0 - finished mxlookup";
1082 return 1;
1083}
1084# if mx expansion did not help to resolve an address
1085# (ie: foo@bar became @baz:foo@bar, then undo the
1085# (ie: foo@bar became @baz:foo@bar, then undo the
1086# expansion).
1087# this is only used by &final
1088sub mxunroll
1089{
1090 local(*host,*addr) = @_;
1091 local($r) = 0;
1092 print "looking for mxbacktrace{$addr *** $host}\n"
1093 if ($debug > 1);
1094 while (defined $mxbacktrace{"$addr *** $host"}) {
1086# expansion).
1087# this is only used by &final
1088sub mxunroll
1089{
1090 local(*host,*addr) = @_;
1091 local($r) = 0;
1092 print "looking for mxbacktrace{$addr *** $host}\n"
1093 if ($debug > 1);
1094 while (defined $mxbacktrace{"$addr *** $host"}) {
1095 print "Unrolling MX expnasion: \@$host:$addr -> "
1095 print "Unrolling MX expansion: \@$host:$addr -> "
1096 if ($debug || $verbose);
1097 $host = $mxbacktrace{"$addr *** $host"};
1096 if ($debug || $verbose);
1097 $host = $mxbacktrace{"$addr *** $host"};
1098 print "\@$host:$addr\n"
1098 print "\@$host:$addr\n"
1099 if ($debug || $verbose);
1100 $r = 1;
1101 }
1102 return 1 if $r;
1103 $addr = "\@$host:$addr"
1104 if ($host =~ /\./);
1105 return 0;
1106}
1099 if ($debug || $verbose);
1100 $r = 1;
1101 }
1102 return 1 if $r;
1103 $addr = "\@$host:$addr"
1104 if ($host =~ /\./);
1105 return 0;
1106}
1107# register a completed expnasion. Make the final address as
1107# register a completed expansion. Make the final address as
1108# simple as possible.
1109sub final
1110{
1111 local($addr,$host,$name,$error) = @_;
1112 local($he);
1113 local($hb,$hr);
1114 local($au,$ah);
1115
1116 if ($error =~ /Non-existent domain/) {
1108# simple as possible.
1109sub final
1110{
1111 local($addr,$host,$name,$error) = @_;
1112 local($he);
1113 local($hb,$hr);
1114 local($au,$ah);
1115
1116 if ($error =~ /Non-existent domain/) {
1117 #
1117 #
1118 # If we created the domain, then let's undo the
1119 # damage...
1120 #
1121 if (defined $create_host_backtrack{"$addr *** $host"}) {
1122 while (defined $create_host_backtrack{"$addr *** $host"}) {
1123 print "Un&domainifying($host) = " if $debug;
1124 $host = $create_host_backtrack{"$addr *** $host"};
1125 print "$host\n" if $debug;
1126 }
1127 $error = "$host: could not locate";
1128 } else {
1118 # If we created the domain, then let's undo the
1119 # damage...
1120 #
1121 if (defined $create_host_backtrack{"$addr *** $host"}) {
1122 while (defined $create_host_backtrack{"$addr *** $host"}) {
1123 print "Un&domainifying($host) = " if $debug;
1124 $host = $create_host_backtrack{"$addr *** $host"};
1125 print "$host\n" if $debug;
1126 }
1127 $error = "$host: could not locate";
1128 } else {
1129 #
1129 #
1130 # If we only want valid addresses, toss out
1131 # bad host names.
1132 #
1133 if ($valid) {
1134 print STDERR "\@$host:$addr ($name) Non-existent domain\n";
1135 return "";
1136 }
1137 }

--- 18 unchanged lines hidden (view full) ---

1156 }
1157 }
1158 # addr does not contain full host
1159 if ($valid) {
1160 if ($host =~ /^([^\.]+)(\..+)$/) {
1161 # host part has a . in it - foo.bar
1162 ($hb, $hr) = ($1, $2);
1163 if ($addr =~ /\@([^\.\@]+)$/ && ($1 eq $hb)) {
1130 # If we only want valid addresses, toss out
1131 # bad host names.
1132 #
1133 if ($valid) {
1134 print STDERR "\@$host:$addr ($name) Non-existent domain\n";
1135 return "";
1136 }
1137 }

--- 18 unchanged lines hidden (view full) ---

1156 }
1157 }
1158 # addr does not contain full host
1159 if ($valid) {
1160 if ($host =~ /^([^\.]+)(\..+)$/) {
1161 # host part has a . in it - foo.bar
1162 ($hb, $hr) = ($1, $2);
1163 if ($addr =~ /\@([^\.\@]+)$/ && ($1 eq $hb)) {
1164 # addr part has not .
1164 # addr part has not .
1165 # and matches beginning of
1165 # and matches beginning of
1166 # host part -- tack on a
1166 # host part -- tack on a
1167 # domain name.
1168 $addr .= $hr;
1169 } else {
1167 # domain name.
1168 $addr .= $hr;
1169 } else {
1170 &mxunroll(*host,*addr)
1170 &mxunroll(*host,*addr)
1171 && redo MXUNWIND;
1172 }
1173 } else {
1171 && redo MXUNWIND;
1172 }
1173 } else {
1174 &mxunroll(*host,*addr)
1174 &mxunroll(*host,*addr)
1175 && redo MXUNWIND;
1176 }
1177 } else {
1178 $addr = "${addr}[\@$host]"
1179 if ($host =~ /\./);
1180 }
1181 }
1182 }

--- 43 unchanged lines hidden (view full) ---

1226# to pass perl -w:
1227@tp;
1228$flag_a;
1229$flag_d;
1230$flag_1;
1231%already_domainify_fellback;
1232%already_mx_fellback;
1233&handle_alarm;
1175 && redo MXUNWIND;
1176 }
1177 } else {
1178 $addr = "${addr}[\@$host]"
1179 if ($host =~ /\./);
1180 }
1181 }
1182 }

--- 43 unchanged lines hidden (view full) ---

1226# to pass perl -w:
1227@tp;
1228$flag_a;
1229$flag_d;
1230$flag_1;
1231%already_domainify_fellback;
1232%already_mx_fellback;
1233&handle_alarm;
1234################### BEGIN PERL/TROFF TRANSITION
1235.00 ;
1234################### BEGIN PERL/TROFF TRANSITION
1235.00 ;
1236
1237'di
1238.nr nl 0-1
1239.nr % 0
1236
1237'di
1238.nr nl 0-1
1239.nr % 0
1240.\\"'; __END__
1240.\\"'; __END__
1241.\" ############## END PERL/TROFF TRANSITION
1242.TH EXPN 1 "March 11, 1993"
1243.AT 3
1244.SH NAME
1245expn \- recursively expand mail aliases
1246.SH SYNOPSIS
1247.B expn
1248.RI [ -a ]
1249.RI [ -v ]
1250.RI [ -w ]
1251.RI [ -d ]
1252.RI [ -1 ]
1253.IR user [@ hostname ]
1254.RI [ user [@ hostname ]]...
1255.SH DESCRIPTION
1256.B expn
1257will use the SMTP
1258.B expn
1241.\" ############## END PERL/TROFF TRANSITION
1242.TH EXPN 1 "March 11, 1993"
1243.AT 3
1244.SH NAME
1245expn \- recursively expand mail aliases
1246.SH SYNOPSIS
1247.B expn
1248.RI [ -a ]
1249.RI [ -v ]
1250.RI [ -w ]
1251.RI [ -d ]
1252.RI [ -1 ]
1253.IR user [@ hostname ]
1254.RI [ user [@ hostname ]]...
1255.SH DESCRIPTION
1256.B expn
1257will use the SMTP
1258.B expn
1259and
1259and
1260.B vrfy
1260.B vrfy
1261commands to expand mail aliases.
1261commands to expand mail aliases.
1262It will first look up the addresses you provide on the command line.
1262It will first look up the addresses you provide on the command line.
1263If those expand into addresses on other systems, it will
1264connect to the other systems and expand again. It will keep
1263If those expand into addresses on other systems, it will
1264connect to the other systems and expand again. It will keep
1265doing this until no further expansion is possible.
1266.SH OPTIONS
1265doing this until no further expansion is possible.
1266.SH OPTIONS
1267The default output of
1267The default output of
1268.B expn
1269can contain many lines which are not valid
1268.B expn
1269can contain many lines which are not valid
1270email addresses. With the
1270email addresses. With the
1271.I -aa
1272flag, only expansions that result in legal addresses
1273are used. Since many mailing lists have an illegal
1274address or two, the single
1275.IR -a ,
1276address, flag specifies that a few illegal addresses can
1271.I -aa
1272flag, only expansions that result in legal addresses
1273are used. Since many mailing lists have an illegal
1274address or two, the single
1275.IR -a ,
1276address, flag specifies that a few illegal addresses can
1277be mixed into the results. More
1277be mixed into the results. More
1278.I -a
1279flags vary the ratio. Read the source to track down
1280the formula. With the
1281.I -a
1282option, you should be able to construct a new mailing
1283list out of an existing one.
1284.LP
1278.I -a
1279flags vary the ratio. Read the source to track down
1280the formula. With the
1281.I -a
1282option, you should be able to construct a new mailing
1283list out of an existing one.
1284.LP
1285If you wish to limit the number of levels deep that
1285If you wish to limit the number of levels deep that
1286.B expn
1287will recurse as it traces addresses, use the
1288.I -1
1286.B expn
1287will recurse as it traces addresses, use the
1288.I -1
1289option. For each
1289option. For each
1290.I -1
1290.I -1
1291another level will be traversed. So,
1291another level will be traversed. So,
1292.I -111
1293will traverse no more than three levels deep.
1294.LP
1295The normal mode of operation for
1296.B expn
1297is to do all of its work silently.
1298The following options make it more verbose.
1299It is not necessary to make it verbose to see what it is
1292.I -111
1293will traverse no more than three levels deep.
1294.LP
1295The normal mode of operation for
1296.B expn
1297is to do all of its work silently.
1298The following options make it more verbose.
1299It is not necessary to make it verbose to see what it is
1300doing because as it works, it changes its
1300doing because as it works, it changes its
1301.BR argv [0]
1302variable to reflect its current activity.
1301.BR argv [0]
1302variable to reflect its current activity.
1303To see how it is expanding things, the
1303To see how it is expanding things, the
1304.IR -v ,
1304.IR -v ,
1305verbose, flag will cause
1306.B expn
1305verbose, flag will cause
1306.B expn
1307to show each address before
1308and after translation as it works.
1307to show each address before
1308and after translation as it works.
1309The
1309The
1310.IR -w ,
1311watch, flag will cause
1312.B expn
1313to show you its conversations with the mail daemons.
1310.IR -w ,
1311watch, flag will cause
1312.B expn
1313to show you its conversations with the mail daemons.
1314Finally, the
1314Finally, the
1315.IR -d ,
1316debug, flag will expose many of the inner workings so that
1317it is possible to eliminate bugs.
1318.SH ENVIRONMENT
1315.IR -d ,
1316debug, flag will expose many of the inner workings so that
1317it is possible to eliminate bugs.
1318.SH ENVIRONMENT
1319No enviroment variables are used.
1319No environment variables are used.
1320.SH FILES
1321.PD 0
1322.B /tmp/expn$$
1320.SH FILES
1321.PD 0
1322.B /tmp/expn$$
1323.B temporary file used as input to
1323.B temporary file used as input to
1324.BR nslookup .
1325.SH SEE ALSO
1324.BR nslookup .
1325.SH SEE ALSO
1326.BR aliases (5),
1326.BR aliases (5),
1327.BR sendmail (8),
1328.BR nslookup (8),
1329RFC 823, and RFC 1123.
1330.SH BUGS
1327.BR sendmail (8),
1328.BR nslookup (8),
1329RFC 823, and RFC 1123.
1330.SH BUGS
1331Not all mail daemons will implement
1331Not all mail daemons will implement
1332.B expn
1333or
1334.BR vrfy .
1335It is not possible to verify addresses that are served
1336by such daemons.
1337.LP
1338When attempting to connect to a system to verify an address,
1339.B expn
1340only tries one IP address. Most mail daemons
1341will try harder.
1342.LP
1332.B expn
1333or
1334.BR vrfy .
1335It is not possible to verify addresses that are served
1336by such daemons.
1337.LP
1338When attempting to connect to a system to verify an address,
1339.B expn
1340only tries one IP address. Most mail daemons
1341will try harder.
1342.LP
1343It is assumed that you are running domain names and that
1344the
1345.BR nslookup (8)
1346program is available. If not,
1343It is assumed that you are running domain names and that
1344the
1345.BR nslookup (8)
1346program is available. If not,
1347.B expn
1348will not be able to verify many addresses. It will also pause
1349for a long time unless you change the code where it says
1350.I $have_nslookup = 1
1351to read
1347.B expn
1348will not be able to verify many addresses. It will also pause
1349for a long time unless you change the code where it says
1350.I $have_nslookup = 1
1351to read
1352.I $have_nslookup =
1352.I $have_nslookup =
1353.IR 0 .
1354.LP
1353.IR 0 .
1354.LP
1355Lastly,
1355Lastly,
1356.B expn
1357does not handle every valid address. If you have an example,
1358please submit a bug report.
1359.SH CREDITS
1360In 1986 or so, Jon Broome wrote a program of the same name
1361that did about the same thing. It has since suffered bit rot
1362and Jon Broome has dropped off the face of the earth!
1363(Jon, if you are out there, drop me a line)
1364.SH AVAILABILITY
1356.B expn
1357does not handle every valid address. If you have an example,
1358please submit a bug report.
1359.SH CREDITS
1360In 1986 or so, Jon Broome wrote a program of the same name
1361that did about the same thing. It has since suffered bit rot
1362and Jon Broome has dropped off the face of the earth!
1363(Jon, if you are out there, drop me a line)
1364.SH AVAILABILITY
1365The latest version of
1365The latest version of
1366.B expn
1367is available through anonymous ftp at
1368.IR ftp://ftp.idiom.com/pub/muir-programs/expn .
1369.SH AUTHOR
1370.I David Muir Sharnoff\ \ \ \ <muir@idiom.com>
1366.B expn
1367is available through anonymous ftp at
1368.IR ftp://ftp.idiom.com/pub/muir-programs/expn .
1369.SH AUTHOR
1370.I David Muir Sharnoff\ \ \ \ <muir@idiom.com>