1 | #!/usr/bin/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 | use 5.001; |
---|
10 | use IO::Socket; |
---|
11 | |
---|
12 | # system requirements: |
---|
13 | # must have 'nslookup' and 'hostname' programs. |
---|
14 | |
---|
15 | # $OrigHeader: /home/muir/bin/RCS/expn,v 3.11 1997/09/10 08:14:02 muir Exp muir $ |
---|
16 | |
---|
17 | # TODO: |
---|
18 | # less magic should apply to command-line addresses |
---|
19 | # less magic should apply to local addresses |
---|
20 | # add magic to deal with cross-domain cnames |
---|
21 | # disconnect & reconnect after 25 commands to the same sendmail 8.8.* host |
---|
22 | |
---|
23 | # Checklist: (hard addresses) |
---|
24 | # 250 Kimmo Suominen <"|/usr/local/mh/lib/slocal -user kim"@grendel.tac.nyc.ny.us> |
---|
25 | # harry@hofmann.cs.Berkeley.EDU -> harry@tenet (.berkeley.edu) [dead] |
---|
26 | # bks@cs.berkeley.edu -> shiva.CS (.berkeley.edu) [dead] |
---|
27 | # dan@tc.cornell.edu -> brown@tiberius (.tc.cornell.edu) |
---|
28 | |
---|
29 | ############################################################################# |
---|
30 | # |
---|
31 | # Copyright (c) 1993 David Muir Sharnoff |
---|
32 | # All rights reserved. |
---|
33 | # |
---|
34 | # Redistribution and use in source and binary forms, with or without |
---|
35 | # modification, are permitted provided that the following conditions |
---|
36 | # are met: |
---|
37 | # 1. Redistributions of source code must retain the above copyright |
---|
38 | # notice, this list of conditions and the following disclaimer. |
---|
39 | # 2. Redistributions in binary form must reproduce the above copyright |
---|
40 | # notice, this list of conditions and the following disclaimer in the |
---|
41 | # documentation and/or other materials provided with the distribution. |
---|
42 | # 3. All advertising materials mentioning features or use of this software |
---|
43 | # must display the following acknowledgement: |
---|
44 | # This product includes software developed by the David Muir Sharnoff. |
---|
45 | # 4. The name of David Sharnoff may not be used to endorse or promote products |
---|
46 | # derived from this software without specific prior written permission. |
---|
47 | # |
---|
48 | # THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``AS IS'' AND |
---|
49 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
50 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
51 | # ARE DISCLAIMED. IN NO EVENT SHALL DAVID MUIR SHARNOFF BE LIABLE |
---|
52 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
53 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
54 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
55 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
56 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
57 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
58 | # SUCH DAMAGE. |
---|
59 | # |
---|
60 | # This copyright notice derrived from material copyrighted by the Regents |
---|
61 | # of the University of California. |
---|
62 | # |
---|
63 | # Contributions accepted. |
---|
64 | # |
---|
65 | ############################################################################# |
---|
66 | |
---|
67 | # overall structure: |
---|
68 | # in an effort to not trace each address individually, but rather |
---|
69 | # ask each server in turn a whole bunch of questions, addresses to |
---|
70 | # be expanded are queued up. |
---|
71 | # |
---|
72 | # This means that all accounting w.r.t. an address must be stored in |
---|
73 | # various arrays. Generally these arrays are indexed by the |
---|
74 | # string "$addr *** $server" where $addr is the address to be |
---|
75 | # expanded "foo" or maybe "foo@bar" and $server is the hostname |
---|
76 | # of the SMTP server to contact. |
---|
77 | # |
---|
78 | |
---|
79 | # important global variables: |
---|
80 | # |
---|
81 | # @hosts : list of servers still to be contacted |
---|
82 | # $server : name of the current we are currently looking at |
---|
83 | # @users = $users{@hosts[0]} : addresses to expand at this server |
---|
84 | # $u = $users[0] : the current address being expanded |
---|
85 | # $names{"$users[0] *** $server"} : the 'name' associated with the address |
---|
86 | # $mxbacktrace{"$users[0] *** $server"} : record of mx expansion |
---|
87 | # $mx_secondary{$server} : other mx relays at the same priority |
---|
88 | # $domainify_fallback{"$users[0] *** $server"} : alternative names to try |
---|
89 | # instead of $server if $server doesn't work |
---|
90 | # $temporary_redirect{"$users[0] *** $server"} : when trying alternates, |
---|
91 | # temporarily channel all tries along current path |
---|
92 | # $giveup{$server} : do not bother expanding addresses at $server |
---|
93 | # $verbose : -v |
---|
94 | # $watch : -w |
---|
95 | # $vw : -v or -w |
---|
96 | # $debug : -d |
---|
97 | # $valid : -a |
---|
98 | # $levels : -1 |
---|
99 | # $S : the socket connection to $server |
---|
100 | |
---|
101 | $have_nslookup = 1; # we have the nslookup program |
---|
102 | $port = 'smtp'; |
---|
103 | $av0 = $0; |
---|
104 | $ENV{'PATH'} .= ":/usr/etc" unless $ENV{'PATH'} =~ m,/usr/etc,; |
---|
105 | $ENV{'PATH'} .= ":/usr/ucb" unless $ENV{'PATH'} =~ m,/usr/ucb,; |
---|
106 | select(STDERR); |
---|
107 | |
---|
108 | $0 = "$av0 - running hostname"; |
---|
109 | chop($name = `hostname || uname -n`); |
---|
110 | |
---|
111 | $0 = "$av0 - lookup host FQDN and IP addr"; |
---|
112 | ($hostname,$aliases,$type,$len,$thisaddr) = gethostbyname($name); |
---|
113 | |
---|
114 | $0 = "$av0 - parsing args"; |
---|
115 | $usage = "Usage: $av0 [-1avwd] user[\@host] [user2[host2] ...]"; |
---|
116 | for $a (@ARGV) { |
---|
117 | die $usage if $a eq "-"; |
---|
118 | while ($a =~ s/^(-.*)([1avwd])/$1/) { |
---|
119 | eval '$'."flag_$2 += 1"; |
---|
120 | } |
---|
121 | next if $a eq "-"; |
---|
122 | die $usage if $a =~ /^-/; |
---|
123 | &expn(&parse($a,$hostname,undef,1)); |
---|
124 | } |
---|
125 | $verbose = $flag_v; |
---|
126 | $watch = $flag_w; |
---|
127 | $vw = $flag_v + $flag_w; |
---|
128 | $debug = $flag_d; |
---|
129 | $valid = $flag_a; |
---|
130 | $levels = $flag_1; |
---|
131 | |
---|
132 | die $usage unless @hosts; |
---|
133 | if ($valid) { |
---|
134 | if ($valid == 1) { |
---|
135 | $validRequirement = 0.8; |
---|
136 | } elsif ($valid == 2) { |
---|
137 | $validRequirement = 1.0; |
---|
138 | } elsif ($valid == 3) { |
---|
139 | $validRequirement = 0.9; |
---|
140 | } else { |
---|
141 | $validRequirement = (1 - (1/($valid-3))); |
---|
142 | print "validRequirement = $validRequirement\n" if $debug; |
---|
143 | } |
---|
144 | } |
---|
145 | |
---|
146 | HOST: |
---|
147 | while (@hosts) { |
---|
148 | $server = shift(@hosts); |
---|
149 | @users = split(' ',$users{$server}); |
---|
150 | delete $users{$server}; |
---|
151 | |
---|
152 | # is this server already known to be bad? |
---|
153 | $0 = "$av0 - looking up $server"; |
---|
154 | if ($giveup{$server}) { |
---|
155 | &giveup('mx domainify',$giveup{$server}); |
---|
156 | next; |
---|
157 | } |
---|
158 | |
---|
159 | # do we already have an mx record for this host? |
---|
160 | next HOST if &mxredirect($server,*users); |
---|
161 | |
---|
162 | # look it up, or try for an mx. |
---|
163 | $0 = "$av0 - gethostbyname($server)"; |
---|
164 | |
---|
165 | ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server); |
---|
166 | # if we can't get an A record, try for an MX record. |
---|
167 | unless($thataddr) { |
---|
168 | &mxlookup(1,$server,"$server: could not resolve name",*users); |
---|
169 | next HOST; |
---|
170 | } |
---|
171 | |
---|
172 | # get a connection, or look for an mx |
---|
173 | $0 = "$av0 - socket to $server"; |
---|
174 | |
---|
175 | $S = new IO::Socket::INET ( |
---|
176 | 'PeerAddr' => $server, |
---|
177 | 'PeerPort' => $port, |
---|
178 | 'Proto' => 'tcp'); |
---|
179 | |
---|
180 | if (! $S || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) { |
---|
181 | $0 = "$av0 - $server: could not connect: $!\n"; |
---|
182 | $emsg = $!; |
---|
183 | unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) { |
---|
184 | &giveup('mx',"$server: Could not connect: $emsg"); |
---|
185 | } |
---|
186 | next HOST; |
---|
187 | } |
---|
188 | $S->autoflush(1); |
---|
189 | |
---|
190 | # read the greeting |
---|
191 | $0 = "$av0 - talking to $server"; |
---|
192 | &alarm("greeting with $server",''); |
---|
193 | while(<$S>) { |
---|
194 | alarm(0); |
---|
195 | print if $watch; |
---|
196 | if (/^(\d+)([- ])/) { |
---|
197 | if ($1 != 220) { |
---|
198 | $0 = "$av0 - bad numeric response from $server"; |
---|
199 | &alarm("giving up after bad response from $server",''); |
---|
200 | &read_response($2,$watch); |
---|
201 | alarm(0); |
---|
202 | print STDERR "$server: NOT 220 greeting: $_" |
---|
203 | if ($debug || $vw); |
---|
204 | if (&mxlookup(0,$server,"$server: did not respond with a 220 greeting",*users)) { |
---|
205 | close($S); |
---|
206 | next HOST; |
---|
207 | } |
---|
208 | } |
---|
209 | last if ($2 eq " "); |
---|
210 | } else { |
---|
211 | $0 = "$av0 - bad response from $server"; |
---|
212 | print STDERR "$server: NOT 220 greeting: $_" |
---|
213 | if ($debug || $vw); |
---|
214 | unless (&mxlookup(0,$server,"$server: did not respond with SMTP codes",*users)) { |
---|
215 | &giveup('',"$server: did not talk SMTP"); |
---|
216 | } |
---|
217 | close($S); |
---|
218 | next HOST; |
---|
219 | } |
---|
220 | &alarm("greeting with $server",''); |
---|
221 | } |
---|
222 | alarm(0); |
---|
223 | |
---|
224 | # if this causes problems, remove it |
---|
225 | $0 = "$av0 - sending helo to $server"; |
---|
226 | &alarm("sending helo to $server",""); |
---|
227 | &ps("helo $hostname"); |
---|
228 | while(<$S>) { |
---|
229 | print if $watch; |
---|
230 | last if /^\d+ /; |
---|
231 | } |
---|
232 | alarm(0); |
---|
233 | |
---|
234 | # try the users, one by one |
---|
235 | USER: |
---|
236 | while(@users) { |
---|
237 | $u = shift(@users); |
---|
238 | $0 = "$av0 - expanding $u [\@$server]"; |
---|
239 | |
---|
240 | # do we already have a name for this user? |
---|
241 | $oldname = $names{"$u *** $server"}; |
---|
242 | |
---|
243 | print &compact($u,$server)." ->\n" if ($verbose && ! $valid); |
---|
244 | if ($valid) { |
---|
245 | # |
---|
246 | # when running with -a, we delay taking any action |
---|
247 | # on the results of our query until we have looked |
---|
248 | # at the complete output. @toFinal stores expansions |
---|
249 | # that will be final if we take them. @toExpn stores |
---|
250 | # expnansions that are not final. @isValid keeps |
---|
251 | # track of our ability to send mail to each of the |
---|
252 | # expansions. |
---|
253 | # |
---|
254 | @isValid = (); |
---|
255 | @toFinal = (); |
---|
256 | @toExpn = (); |
---|
257 | } |
---|
258 | |
---|
259 | # ($ecode,@expansion) = &expn_vrfy($u,$server); |
---|
260 | (@foo) = &expn_vrfy($u,$server); |
---|
261 | ($ecode,@expansion) = @foo; |
---|
262 | if ($ecode) { |
---|
263 | &giveup('',$ecode,$u); |
---|
264 | last USER; |
---|
265 | } |
---|
266 | |
---|
267 | for $s (@expansion) { |
---|
268 | $s =~ s/[\n\r]//g; |
---|
269 | $0 = "$av0 - parsing $server: $s"; |
---|
270 | |
---|
271 | $skipwatch = $watch; |
---|
272 | |
---|
273 | if ($s =~ /^[25]51([- ]).*<(.+)>/) { |
---|
274 | print "$s" if $watch; |
---|
275 | print "(pretending 250$1<$2>)" if ($debug && $watch); |
---|
276 | print "\n" if $watch; |
---|
277 | $s = "250$1<$2>"; |
---|
278 | $skipwatch = 0; |
---|
279 | } |
---|
280 | |
---|
281 | if ($s =~ /^250([- ])(.+)/) { |
---|
282 | print "$s\n" if $skipwatch; |
---|
283 | ($done,$addr) = ($1,$2); |
---|
284 | ($newhost, $newaddr, $newname) = &parse($addr,$server,$oldname, $#expansion == 0); |
---|
285 | print "($newhost, $newaddr, $newname) = &parse($addr, $server, $oldname)\n" if $debug; |
---|
286 | if (! $newhost) { |
---|
287 | # no expansion is possible w/o a new server to call |
---|
288 | if ($valid) { |
---|
289 | push(@isValid, &validAddr($newaddr)); |
---|
290 | push(@toFinal,$newaddr,$server,$newname); |
---|
291 | } else { |
---|
292 | &verbose(&final($newaddr,$server,$newname)); |
---|
293 | } |
---|
294 | } else { |
---|
295 | $newmxhost = &mx($newhost,$newaddr); |
---|
296 | print "$newmxhost = &mx($newhost)\n" |
---|
297 | if ($debug && $newhost ne $newmxhost); |
---|
298 | $0 = "$av0 - parsing $newaddr [@$newmxhost]"; |
---|
299 | print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1); |
---|
300 | # If the new server is the current one, |
---|
301 | # it would have expanded things for us |
---|
302 | # if it could have. Mx records must be |
---|
303 | # followed to compare server names. |
---|
304 | # We are also done if the recursion |
---|
305 | # count has been exceeded. |
---|
306 | if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) { |
---|
307 | if ($valid) { |
---|
308 | push(@isValid, &validAddr($newaddr)); |
---|
309 | push(@toFinal,$newaddr,$newmxhost,$newname); |
---|
310 | } else { |
---|
311 | &verbose(&final($newaddr,$newmxhost,$newname)); |
---|
312 | } |
---|
313 | } else { |
---|
314 | # more work to do... |
---|
315 | if ($valid) { |
---|
316 | push(@isValid, &validAddr($newaddr)); |
---|
317 | push(@toExpn,$newmxhost,$newaddr,$newname,$level{"$u *** $server"}); |
---|
318 | } else { |
---|
319 | &verbose(&expn($newmxhost,$newaddr,$newname,$level{"$u *** $server"})); |
---|
320 | } |
---|
321 | } |
---|
322 | } |
---|
323 | last if ($done eq " "); |
---|
324 | next; |
---|
325 | } |
---|
326 | # 550 is a known code... Should the be |
---|
327 | # included in -a output? Might be a bug |
---|
328 | # here. Does it matter? Can assume that |
---|
329 | # there won't be UNKNOWN USER responses |
---|
330 | # mixed with valid users? |
---|
331 | if ($s =~ /^(550)([- ])/) { |
---|
332 | if ($valid) { |
---|
333 | print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n"; |
---|
334 | } else { |
---|
335 | &verbose(&final($u,$server,$oldname,"USER UNKNOWN")); |
---|
336 | } |
---|
337 | last if ($2 eq " "); |
---|
338 | next; |
---|
339 | } |
---|
340 | # 553 is a known code... |
---|
341 | if ($s =~ /^(553)([- ])/) { |
---|
342 | if ($valid) { |
---|
343 | print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n"; |
---|
344 | } else { |
---|
345 | &verbose(&final($u,$server,$oldname,"USER AMBIGUOUS")); |
---|
346 | } |
---|
347 | last if ($2 eq " "); |
---|
348 | next; |
---|
349 | } |
---|
350 | # 252 is a known code... |
---|
351 | if ($s =~ /^(252)([- ])/) { |
---|
352 | if ($valid) { |
---|
353 | print STDERR "\@$server:$u ($oldname) REFUSED TO VRFY\n"; |
---|
354 | } else { |
---|
355 | &verbose(&final($u,$server,$oldname,"REFUSED TO VRFY")); |
---|
356 | } |
---|
357 | last if ($2 eq " "); |
---|
358 | next; |
---|
359 | } |
---|
360 | &giveup('',"$server: did not grok '$s'",$u); |
---|
361 | last USER; |
---|
362 | } |
---|
363 | |
---|
364 | if ($valid) { |
---|
365 | # |
---|
366 | # now we decide if we are going to take these |
---|
367 | # expansions or roll them back. |
---|
368 | # |
---|
369 | $avgValid = &average(@isValid); |
---|
370 | print "avgValid = $avgValid\n" if $debug; |
---|
371 | if ($avgValid >= $validRequirement) { |
---|
372 | print &compact($u,$server)." ->\n" if $verbose; |
---|
373 | while (@toExpn) { |
---|
374 | &verbose(&expn(splice(@toExpn,0,4))); |
---|
375 | } |
---|
376 | while (@toFinal) { |
---|
377 | &verbose(&final(splice(@toFinal,0,3))); |
---|
378 | } |
---|
379 | } else { |
---|
380 | print "Tossing some valid to avoid invalid ".&compact($u,$server)."\n" if ($avgValid > 0.0 && ($vw || $debug)); |
---|
381 | print &compact($u,$server)." ->\n" if $verbose; |
---|
382 | &verbose(&final($u,$server,$newname)); |
---|
383 | } |
---|
384 | } |
---|
385 | } |
---|
386 | |
---|
387 | &alarm("sending 'quit' to $server",''); |
---|
388 | $0 = "$av0 - sending 'quit' to $server"; |
---|
389 | &ps("quit"); |
---|
390 | while(<$S>) { |
---|
391 | print if $watch; |
---|
392 | last if /^\d+ /; |
---|
393 | } |
---|
394 | close($S); |
---|
395 | alarm(0); |
---|
396 | } |
---|
397 | |
---|
398 | $0 = "$av0 - printing final results"; |
---|
399 | print "----------\n" if $vw; |
---|
400 | select(STDOUT); |
---|
401 | for $f (sort @final) { |
---|
402 | print "$f\n"; |
---|
403 | } |
---|
404 | unlink("/tmp/expn$$"); |
---|
405 | exit(0); |
---|
406 | |
---|
407 | |
---|
408 | # abandon all attempts deliver to $server |
---|
409 | # register the current addresses as the final ones |
---|
410 | sub giveup |
---|
411 | { |
---|
412 | local($redirect_okay,$reason,$user) = @_; |
---|
413 | local($us,@so,$nh,@remaining_users); |
---|
414 | local($pk,$file,$line); |
---|
415 | ($pk, $file, $line) = caller; |
---|
416 | |
---|
417 | $0 = "$av0 - giving up on $server: $reason"; |
---|
418 | # |
---|
419 | # add back a user if we gave up in the middle |
---|
420 | # |
---|
421 | push(@users,$user) if $user; |
---|
422 | # |
---|
423 | # don't bother with this system anymore |
---|
424 | # |
---|
425 | unless ($giveup{$server}) { |
---|
426 | $giveup{$server} = $reason; |
---|
427 | print STDERR "$reason\n"; |
---|
428 | } |
---|
429 | print "Giveup at $file:$line!!! redirect okay = $redirect_okay; $reason\n" if $debug; |
---|
430 | # |
---|
431 | # Wait! |
---|
432 | # Before giving up, see if there is a chance that |
---|
433 | # there is another host to redirect to! |
---|
434 | # (Kids, don't do this at home! Hacking is a dangerous |
---|
435 | # crime and you could end up behind bars.) |
---|
436 | # |
---|
437 | for $u (@users) { |
---|
438 | if ($redirect_okay =~ /\bmx\b/) { |
---|
439 | next if &try_fallback('mx',$u,*server, |
---|
440 | *mx_secondary, |
---|
441 | *already_mx_fellback); |
---|
442 | } |
---|
443 | if ($redirect_okay =~ /\bdomainify\b/) { |
---|
444 | next if &try_fallback('domainify',$u,*server, |
---|
445 | *domainify_fallback, |
---|
446 | *already_domainify_fellback); |
---|
447 | } |
---|
448 | push(@remaining_users,$u); |
---|
449 | } |
---|
450 | @users = @remaining_users; |
---|
451 | for $u (@users) { |
---|
452 | print &compact($u,$server)." ->\n" if ($verbose && $valid && $u); |
---|
453 | &verbose(&final($u,$server,$names{"$u *** $server"},$reason)); |
---|
454 | } |
---|
455 | } |
---|
456 | # |
---|
457 | # This routine is used only within &giveup. It checks to |
---|
458 | # see if we really have to giveup or if there is a second |
---|
459 | # chance because we did something before that can be |
---|
460 | # backtracked. |
---|
461 | # |
---|
462 | # %fallback{"$user *** $host"} tracks what is able to fallback |
---|
463 | # %fellback{"$user *** $host"} tracks what has fallen back |
---|
464 | # |
---|
465 | # If there is a valid backtrack, then queue up the new possibility |
---|
466 | # |
---|
467 | sub try_fallback |
---|
468 | { |
---|
469 | local($method,$user,*host,*fall_table,*fellback) = @_; |
---|
470 | local($us,$fallhost,$oldhost,$ft,$i); |
---|
471 | |
---|
472 | if ($debug > 8) { |
---|
473 | print "Fallback table $method:\n"; |
---|
474 | for $i (sort keys %fall_table) { |
---|
475 | print "\t'$i'\t\t'$fall_table{$i}'\n"; |
---|
476 | } |
---|
477 | print "Fellback table $method:\n"; |
---|
478 | for $i (sort keys %fellback) { |
---|
479 | print "\t'$i'\t\t'$fellback{$i}'\n"; |
---|
480 | } |
---|
481 | print "U: $user H: $host\n"; |
---|
482 | } |
---|
483 | |
---|
484 | $us = "$user *** $host"; |
---|
485 | if (defined $fellback{$us}) { |
---|
486 | # |
---|
487 | # Undo a previous fallback so that we can try again |
---|
488 | # Nested fallbacks are avoided because they could |
---|
489 | # lead to infinite loops |
---|
490 | # |
---|
491 | $fallhost = $fellback{$us}; |
---|
492 | print "Already $method fell back from $us -> \n" if $debug; |
---|
493 | $us = "$user *** $fallhost"; |
---|
494 | $oldhost = $fallhost; |
---|
495 | } elsif (($method eq 'mx') && (defined $mxbacktrace{$us}) && (defined $mx_secondary{$mxbacktrace{$us}})) { |
---|
496 | print "Fallback an MX expansion $us -> \n" if $debug; |
---|
497 | $oldhost = $mxbacktrace{$us}; |
---|
498 | } else { |
---|
499 | print "Oldhost($host, $us) = " if $debug; |
---|
500 | $oldhost = $host; |
---|
501 | } |
---|
502 | print "$oldhost\n" if $debug; |
---|
503 | if (((defined $fall_table{$us}) && ($ft = $us)) || ((defined $fall_table{$oldhost}) && ($ft = $oldhost))) { |
---|
504 | print "$method Fallback = ".$fall_table{$ft}."\n" if $debug; |
---|
505 | local(@so,$newhost); |
---|
506 | @so = split(' ',$fall_table{$ft}); |
---|
507 | $newhost = shift(@so); |
---|
508 | print "Falling back ($method) $us -> $newhost (from $oldhost)\n" if $debug; |
---|
509 | if ($method eq 'mx') { |
---|
510 | if (! defined ($mxbacktrace{"$user *** $newhost"})) { |
---|
511 | if (defined $mxbacktrace{"$user *** $oldhost"}) { |
---|
512 | print "resetting oldhost $oldhost to the original: " if $debug; |
---|
513 | $oldhost = $mxbacktrace{"$user *** $oldhost"}; |
---|
514 | print "$oldhost\n" if $debug; |
---|
515 | } |
---|
516 | $mxbacktrace{"$user *** $newhost"} = $oldhost; |
---|
517 | print "mxbacktrace $user *** $newhost -> $oldhost\n" if $debug; |
---|
518 | } |
---|
519 | $mx{&trhost($oldhost)} = $newhost; |
---|
520 | } else { |
---|
521 | $temporary_redirect{$us} = $newhost; |
---|
522 | } |
---|
523 | if (@so) { |
---|
524 | print "Can still $method $us: @so\n" if $debug; |
---|
525 | $fall_table{$ft} = join(' ',@so); |
---|
526 | } else { |
---|
527 | print "No more fallbacks for $us\n" if $debug; |
---|
528 | delete $fall_table{$ft}; |
---|
529 | } |
---|
530 | if (defined $create_host_backtrack{$us}) { |
---|
531 | $create_host_backtrack{"$user *** $newhost"} |
---|
532 | = $create_host_backtrack{$us}; |
---|
533 | } |
---|
534 | $fellback{"$user *** $newhost"} = $oldhost; |
---|
535 | &expn($newhost,$user,$names{$us},$level{$us}); |
---|
536 | return 1; |
---|
537 | } |
---|
538 | delete $temporary_redirect{$us}; |
---|
539 | $host = $oldhost; |
---|
540 | return 0; |
---|
541 | } |
---|
542 | # return 1 if you could send mail to the address as is. |
---|
543 | sub validAddr |
---|
544 | { |
---|
545 | local($addr) = @_; |
---|
546 | $res = &do_validAddr($addr); |
---|
547 | print "validAddr($addr) = $res\n" if $debug; |
---|
548 | $res; |
---|
549 | } |
---|
550 | sub do_validAddr |
---|
551 | { |
---|
552 | local($addr) = @_; |
---|
553 | local($urx) = "[-A-Za-z_.0-9+]+"; |
---|
554 | |
---|
555 | # \u |
---|
556 | return 0 if ($addr =~ /^\\/); |
---|
557 | # ?@h |
---|
558 | return 1 if ($addr =~ /.\@$urx$/); |
---|
559 | # @h:? |
---|
560 | return 1 if ($addr =~ /^\@$urx\:./); |
---|
561 | # h!u |
---|
562 | return 1 if ($addr =~ /^$urx!./); |
---|
563 | # u |
---|
564 | return 1 if ($addr =~ /^$urx$/); |
---|
565 | # ? |
---|
566 | print "validAddr($addr) = ???\n" if $debug; |
---|
567 | return 0; |
---|
568 | } |
---|
569 | # Some systems use expn and vrfy interchangeably. Some only |
---|
570 | # implement one or the other. Some check expn against mailing |
---|
571 | # lists and vrfy against users. It doesn't appear to be |
---|
572 | # consistent. |
---|
573 | # |
---|
574 | # So, what do we do? We try everything! |
---|
575 | # |
---|
576 | # |
---|
577 | # Ranking of result codes: good: 250, 251/551, 252, 550, anything else |
---|
578 | # |
---|
579 | # Ranking of inputs: best: user@host.domain, okay: user |
---|
580 | # |
---|
581 | # Return value: $error_string, @responses_from_server |
---|
582 | sub expn_vrfy |
---|
583 | { |
---|
584 | local($u,$server) = @_; |
---|
585 | local(@c) = ('expn', 'vrfy'); |
---|
586 | local(@try_u) = $u; |
---|
587 | local(@ret,$code); |
---|
588 | |
---|
589 | if (($u =~ /(.+)@(.+)/) && (&trhost($2) eq &trhost($server))) { |
---|
590 | push(@try_u,$1); |
---|
591 | } |
---|
592 | |
---|
593 | TRY: |
---|
594 | for $c (@c) { |
---|
595 | for $try_u (@try_u) { |
---|
596 | &alarm("${c}'ing $try_u on $server",'',$u); |
---|
597 | &ps("$c $try_u"); |
---|
598 | alarm(0); |
---|
599 | $s = <$S>; |
---|
600 | if ($s eq '') { |
---|
601 | return "$server: lost connection"; |
---|
602 | } |
---|
603 | if ($s !~ /^(\d+)([- ])/) { |
---|
604 | return "$server: garbled reply to '$c $try_u'"; |
---|
605 | } |
---|
606 | if ($1 == 250) { |
---|
607 | $code = 250; |
---|
608 | @ret = ("",$s); |
---|
609 | push(@ret,&read_response($2,$debug)); |
---|
610 | return (@ret); |
---|
611 | } |
---|
612 | if ($1 == 551 || $1 == 251) { |
---|
613 | $code = $1; |
---|
614 | @ret = ("",$s); |
---|
615 | push(@ret,&read_response($2,$debug)); |
---|
616 | next; |
---|
617 | } |
---|
618 | if ($1 == 252 && ($code == 0 || $code == 550)) { |
---|
619 | $code = 252; |
---|
620 | @ret = ("",$s); |
---|
621 | push(@ret,&read_response($2,$watch)); |
---|
622 | next; |
---|
623 | } |
---|
624 | if ($1 == 550 && $code == 0) { |
---|
625 | $code = 550; |
---|
626 | @ret = ("",$s); |
---|
627 | push(@ret,&read_response($2,$watch)); |
---|
628 | next; |
---|
629 | } |
---|
630 | &read_response($2,$watch); |
---|
631 | } |
---|
632 | } |
---|
633 | return "$server: expn/vrfy not implemented" unless @ret; |
---|
634 | return @ret; |
---|
635 | } |
---|
636 | # sometimes the old parse routine (now parse2) didn't |
---|
637 | # reject funky addresses. |
---|
638 | sub parse |
---|
639 | { |
---|
640 | local($oldaddr,$server,$oldname,$one_to_one) = @_; |
---|
641 | local($newhost, $newaddr, $newname, $um) = &parse2($oldaddr,$server,$oldname,$one_to_one); |
---|
642 | if ($newaddr =~ m,^["/],) { |
---|
643 | return (undef, $oldaddr, $newname) if $valid; |
---|
644 | return (undef, $um, $newname); |
---|
645 | } |
---|
646 | return ($newhost, $newaddr, $newname); |
---|
647 | } |
---|
648 | |
---|
649 | # returns ($new_smtp_server,$new_address,$new_name) |
---|
650 | # given a response from a SMTP server ($newaddr), the |
---|
651 | # current host ($server), the old "name" and a flag that |
---|
652 | # indicates if it is being called during the initial |
---|
653 | # command line parsing ($parsing_args) |
---|
654 | sub parse2 |
---|
655 | { |
---|
656 | local($newaddr,$context_host,$old_name,$parsing_args) = @_; |
---|
657 | local(@names) = $old_name; |
---|
658 | local($urx) = "[-A-Za-z_.0-9+]+"; |
---|
659 | local($unmangle); |
---|
660 | |
---|
661 | # |
---|
662 | # first, separate out the address part. |
---|
663 | # |
---|
664 | |
---|
665 | # |
---|
666 | # [NAME] <ADDR [(NAME)]> |
---|
667 | # [NAME] <[(NAME)] ADDR |
---|
668 | # ADDR [(NAME)] |
---|
669 | # (NAME) ADDR |
---|
670 | # [(NAME)] <ADDR> |
---|
671 | # |
---|
672 | if ($newaddr =~ /^\<(.*)\>$/) { |
---|
673 | print "<A:$1>\n" if $debug; |
---|
674 | ($newaddr) = &trim($1); |
---|
675 | print "na = $newaddr\n" if $debug; |
---|
676 | } |
---|
677 | if ($newaddr =~ /^([^\<\>]*)\<([^\<\>]*)\>([^\<\>]*)$/) { |
---|
678 | # address has a < > pair in it. |
---|
679 | print "N:$1 <A:$2> N:$3\n" if $debug; |
---|
680 | ($newaddr) = &trim($2); |
---|
681 | unshift(@names, &trim($3,$1)); |
---|
682 | print "na = $newaddr\n" if $debug; |
---|
683 | } |
---|
684 | if ($newaddr =~ /^([^\(\)]*)\(([^\(\)]*)\)([^\(\)]*)$/) { |
---|
685 | # address has a ( ) pair in it. |
---|
686 | print "A:$1 (N:$2) A:$3\n" if $debug; |
---|
687 | unshift(@names,&trim($2)); |
---|
688 | local($f,$l) = (&trim($1),&trim($3)); |
---|
689 | if (($f && $l) || !($f || $l)) { |
---|
690 | # address looks like: |
---|
691 | # foo (bar) baz or (bar) |
---|
692 | # not allowed! |
---|
693 | print STDERR "Could not parse $newaddr\n" if $vw; |
---|
694 | return(undef,$newaddr,&firstname(@names)); |
---|
695 | } |
---|
696 | $newaddr = $f if $f; |
---|
697 | $newaddr = $l if $l; |
---|
698 | print "newaddr now = $newaddr\n" if $debug; |
---|
699 | } |
---|
700 | # |
---|
701 | # @foo:bar |
---|
702 | # j%k@l |
---|
703 | # a@b |
---|
704 | # b!a |
---|
705 | # a |
---|
706 | # |
---|
707 | $unmangle = $newaddr; |
---|
708 | if ($newaddr =~ /^\@($urx)\:(.+)$/) { |
---|
709 | print "(\@:)" if $debug; |
---|
710 | # this is a bit of a cheat, but it seems necessary |
---|
711 | return (&domainify($1,$context_host,$2),$2,&firstname(@names),$unmangle); |
---|
712 | } |
---|
713 | if ($newaddr =~ /^(.+)\@($urx)$/) { |
---|
714 | print "(\@)" if $debug; |
---|
715 | return (&domainify($2,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle); |
---|
716 | } |
---|
717 | if ($parsing_args) { |
---|
718 | if ($newaddr =~ /^($urx)\!(.+)$/) { |
---|
719 | return (&domainify($1,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle); |
---|
720 | } |
---|
721 | if ($newaddr =~ /^($urx)$/) { |
---|
722 | return ($context_host,$newaddr,&firstname(@names),$unmangle); |
---|
723 | } |
---|
724 | print STDERR "Could not parse $newaddr\n"; |
---|
725 | } |
---|
726 | print "(?)" if $debug; |
---|
727 | return(undef,$newaddr,&firstname(@names),$unmangle); |
---|
728 | } |
---|
729 | # return $u (@$server) unless $u includes reference to $server |
---|
730 | sub compact |
---|
731 | { |
---|
732 | local($u, $server) = @_; |
---|
733 | local($se) = $server; |
---|
734 | local($sp); |
---|
735 | $se =~ s/(\W)/\\$1/g; |
---|
736 | $sp = " (\@$server)"; |
---|
737 | if ($u !~ /$se/i) { |
---|
738 | return "$u$sp"; |
---|
739 | } |
---|
740 | return $u; |
---|
741 | } |
---|
742 | # remove empty (spaces don't count) members from an array |
---|
743 | sub trim |
---|
744 | { |
---|
745 | local(@v) = @_; |
---|
746 | local($v,@r); |
---|
747 | for $v (@v) { |
---|
748 | $v =~ s/^\s+//; |
---|
749 | $v =~ s/\s+$//; |
---|
750 | push(@r,$v) if ($v =~ /\S/); |
---|
751 | } |
---|
752 | return(@r); |
---|
753 | } |
---|
754 | # using the host part of an address, and the server name, add the |
---|
755 | # servers' domain to the address if it doesn't already have a |
---|
756 | # domain. Since this sometimes fails, save a back reference so |
---|
757 | # it can be unrolled. |
---|
758 | sub domainify |
---|
759 | { |
---|
760 | local($host,$domain_host,$u) = @_; |
---|
761 | local($domain,$newhost); |
---|
762 | |
---|
763 | # cut of trailing dots |
---|
764 | $host =~ s/\.$//; |
---|
765 | $domain_host =~ s/\.$//; |
---|
766 | |
---|
767 | if ($domain_host !~ /\./) { |
---|
768 | # |
---|
769 | # domain host isn't, keep $host whatever it is |
---|
770 | # |
---|
771 | print "domainify($host,$domain_host) = $host\n" if $debug; |
---|
772 | return $host; |
---|
773 | } |
---|
774 | |
---|
775 | # |
---|
776 | # There are several weird situtations that need to be |
---|
777 | # accounted for. They have to do with domain relay hosts. |
---|
778 | # |
---|
779 | # Examples: |
---|
780 | # host server "right answer" |
---|
781 | # |
---|
782 | # shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu |
---|
783 | # shiva cs.berkeley.edu shiva.cs.berekley.edu |
---|
784 | # cumulus reed.edu @reed.edu:cumulus.uucp |
---|
785 | # tiberius tc.cornell.edu tiberius.tc.cornell.edu |
---|
786 | # |
---|
787 | # The first try must always be to cut the domain part out of |
---|
788 | # the server and tack it onto the host. |
---|
789 | # |
---|
790 | # A reasonable second try is to tack the whole server part onto |
---|
791 | # the host and for each possible repeated element, eliminate |
---|
792 | # just that part. |
---|
793 | # |
---|
794 | # These extra "guesses" get put into the %domainify_fallback |
---|
795 | # array. They will be used to give addresses a second chance |
---|
796 | # in the &giveup routine |
---|
797 | # |
---|
798 | |
---|
799 | local(%fallback); |
---|
800 | |
---|
801 | local($long); |
---|
802 | $long = "$host $domain_host"; |
---|
803 | $long =~ tr/A-Z/a-z/; |
---|
804 | print "long = $long\n" if $debug; |
---|
805 | if ($long =~ s/^([^ ]+\.)([^ ]+) \2(\.[^ ]+\.[^ ]+)/$1$2$3/) { |
---|
806 | # matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu |
---|
807 | print "condensed fallback $host $domain_host -> $long\n" if $debug; |
---|
808 | $fallback{$long} = 9; |
---|
809 | } |
---|
810 | |
---|
811 | local($fh); |
---|
812 | $fh = $domain_host; |
---|
813 | while ($fh =~ /\./) { |
---|
814 | print "FALLBACK $host.$fh = 1\n" if $debug > 7; |
---|
815 | $fallback{"$host.$fh"} = 1; |
---|
816 | $fh =~ s/^[^\.]+\.//; |
---|
817 | } |
---|
818 | |
---|
819 | $fallback{"$host.$domain_host"} = 2; |
---|
820 | |
---|
821 | ($domain = $domain_host) =~ s/^[^\.]+//; |
---|
822 | $fallback{"$host$domain"} = 6 |
---|
823 | if ($domain =~ /\./); |
---|
824 | |
---|
825 | if ($host =~ /\./) { |
---|
826 | # |
---|
827 | # Host is already okay, but let's look for multiple |
---|
828 | # interpretations |
---|
829 | # |
---|
830 | print "domainify($host,$domain_host) = $host\n" if $debug; |
---|
831 | delete $fallback{$host}; |
---|
832 | $domainify_fallback{"$u *** $host"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback; |
---|
833 | return $host; |
---|
834 | } |
---|
835 | |
---|
836 | $domain = ".$domain_host" |
---|
837 | if ($domain !~ /\..*\./); |
---|
838 | $newhost = "$host$domain"; |
---|
839 | |
---|
840 | $create_host_backtrack{"$u *** $newhost"} = $domain_host; |
---|
841 | print "domainify($host,$domain_host) = $newhost\n" if $debug; |
---|
842 | delete $fallback{$newhost}; |
---|
843 | $domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback; |
---|
844 | if ($debug) { |
---|
845 | print "fallback = "; |
---|
846 | print $domainify_fallback{"$u *** $newhost"} |
---|
847 | if defined($domainify_fallback{"$u *** $newhost"}); |
---|
848 | print "\n"; |
---|
849 | } |
---|
850 | return $newhost; |
---|
851 | } |
---|
852 | # return the first non-empty element of an array |
---|
853 | sub firstname |
---|
854 | { |
---|
855 | local(@names) = @_; |
---|
856 | local($n); |
---|
857 | while(@names) { |
---|
858 | $n = shift(@names); |
---|
859 | return $n if $n =~ /\S/; |
---|
860 | } |
---|
861 | return undef; |
---|
862 | } |
---|
863 | # queue up more addresses to expand |
---|
864 | sub expn |
---|
865 | { |
---|
866 | local($host,$addr,$name,$level) = @_; |
---|
867 | if ($host) { |
---|
868 | $host = &trhost($host); |
---|
869 | |
---|
870 | if (($debug > 3) || (defined $giveup{$host})) { |
---|
871 | unshift(@hosts,$host) unless $users{$host}; |
---|
872 | } else { |
---|
873 | push(@hosts,$host) unless $users{$host}; |
---|
874 | } |
---|
875 | $users{$host} .= " $addr"; |
---|
876 | $names{"$addr *** $host"} = $name; |
---|
877 | $level{"$addr *** $host"} = $level + 1; |
---|
878 | print "expn($host,$addr,$name)\n" if $debug; |
---|
879 | return "\t$addr\n"; |
---|
880 | } else { |
---|
881 | return &final($addr,'NONE',$name); |
---|
882 | } |
---|
883 | } |
---|
884 | # compute the numerical average value of an array |
---|
885 | sub average |
---|
886 | { |
---|
887 | local(@e) = @_; |
---|
888 | return 0 unless @e; |
---|
889 | local($e,$sum); |
---|
890 | for $e (@e) { |
---|
891 | $sum += $e; |
---|
892 | } |
---|
893 | $sum / @e; |
---|
894 | } |
---|
895 | # print to the server (also to stdout, if -w) |
---|
896 | sub ps |
---|
897 | { |
---|
898 | local($p) = @_; |
---|
899 | print ">>> $p\n" if $watch; |
---|
900 | print $S "$p\n"; |
---|
901 | } |
---|
902 | # return case-adjusted name for a host (for comparison purposes) |
---|
903 | sub trhost |
---|
904 | { |
---|
905 | # treat foo.bar as an alias for Foo.BAR |
---|
906 | local($host) = @_; |
---|
907 | local($trhost) = $host; |
---|
908 | $trhost =~ tr/A-Z/a-z/; |
---|
909 | if ($trhost{$trhost}) { |
---|
910 | $host = $trhost{$trhost}; |
---|
911 | } else { |
---|
912 | $trhost{$trhost} = $host; |
---|
913 | } |
---|
914 | $trhost{$trhost}; |
---|
915 | } |
---|
916 | # re-queue users if an mx record dictates a redirect |
---|
917 | # don't allow a user to be redirected more than once |
---|
918 | sub mxredirect |
---|
919 | { |
---|
920 | local($server,*users) = @_; |
---|
921 | local($u,$nserver,@still_there); |
---|
922 | |
---|
923 | $nserver = &mx($server); |
---|
924 | |
---|
925 | if (&trhost($nserver) ne &trhost($server)) { |
---|
926 | $0 = "$av0 - mx redirect $server -> $nserver\n"; |
---|
927 | for $u (@users) { |
---|
928 | if (defined $mxbacktrace{"$u *** $nserver"}) { |
---|
929 | push(@still_there,$u); |
---|
930 | } else { |
---|
931 | $mxbacktrace{"$u *** $nserver"} = $server; |
---|
932 | print "mxbacktrace{$u *** $nserver} = $server\n" |
---|
933 | if ($debug > 1); |
---|
934 | &expn($nserver,$u,$names{"$u *** $server"}); |
---|
935 | } |
---|
936 | } |
---|
937 | @users = @still_there; |
---|
938 | if (! @users) { |
---|
939 | return $nserver; |
---|
940 | } else { |
---|
941 | return undef; |
---|
942 | } |
---|
943 | } |
---|
944 | return undef; |
---|
945 | } |
---|
946 | # follow mx records, return a hostname |
---|
947 | # also follow temporary redirections comming from &domainify and |
---|
948 | # &mxlookup |
---|
949 | sub mx |
---|
950 | { |
---|
951 | local($h,$u) = @_; |
---|
952 | |
---|
953 | for (;;) { |
---|
954 | if (defined $mx{&trhost($h)} && $h ne $mx{&trhost($h)}) { |
---|
955 | $0 = "$av0 - mx expand $h"; |
---|
956 | $h = $mx{&trhost($h)}; |
---|
957 | return $h; |
---|
958 | } |
---|
959 | if ($u) { |
---|
960 | if (defined $temporary_redirect{"$u *** $h"}) { |
---|
961 | $0 = "$av0 - internal redirect $h"; |
---|
962 | print "Temporary redirect taken $u *** $h -> " if $debug; |
---|
963 | $h = $temporary_redirect{"$u *** $h"}; |
---|
964 | print "$h\n" if $debug; |
---|
965 | next; |
---|
966 | } |
---|
967 | $htr = &trhost($h); |
---|
968 | if (defined $temporary_redirect{"$u *** $htr"}) { |
---|
969 | $0 = "$av0 - internal redirect $h"; |
---|
970 | print "temporary redirect taken $u *** $h -> " if $debug; |
---|
971 | $h = $temporary_redirect{"$u *** $htr"}; |
---|
972 | print "$h\n" if $debug; |
---|
973 | next; |
---|
974 | } |
---|
975 | } |
---|
976 | return $h; |
---|
977 | } |
---|
978 | } |
---|
979 | # look up mx records with the name server. |
---|
980 | # re-queue expansion requests if possible |
---|
981 | # optionally give up on this host. |
---|
982 | sub mxlookup |
---|
983 | { |
---|
984 | local($lastchance,$server,$giveup,*users) = @_; |
---|
985 | local(*T); |
---|
986 | local(*NSLOOKUP); |
---|
987 | local($nh, $pref,$cpref); |
---|
988 | local($o0) = $0; |
---|
989 | local($nserver); |
---|
990 | local($name,$aliases,$type,$len,$thataddr); |
---|
991 | local(%fallback); |
---|
992 | |
---|
993 | return 1 if &mxredirect($server,*users); |
---|
994 | |
---|
995 | if ((defined $mx{$server}) || (! $have_nslookup)) { |
---|
996 | return 0 unless $lastchance; |
---|
997 | &giveup('mx domainify',$giveup); |
---|
998 | return 0; |
---|
999 | } |
---|
1000 | |
---|
1001 | $0 = "$av0 - nslookup of $server"; |
---|
1002 | open(T,">/tmp/expn$$") || die "open > /tmp/expn$$: $!\n"; |
---|
1003 | print T "set querytype=MX\n"; |
---|
1004 | print T "$server\n"; |
---|
1005 | close(T); |
---|
1006 | $cpref = 1.0E12; |
---|
1007 | undef $nserver; |
---|
1008 | open(NSLOOKUP,"nslookup < /tmp/expn$$ 2>&1 |") || die "open nslookup: $!"; |
---|
1009 | while(<NSLOOKUP>) { |
---|
1010 | print if ($debug > 2); |
---|
1011 | if (/mail exchanger = ([-A-Za-z_.0-9+]+)/) { |
---|
1012 | $nh = $1; |
---|
1013 | if (/preference = (\d+)/) { |
---|
1014 | $pref = $1; |
---|
1015 | if ($pref < $cpref) { |
---|
1016 | $nserver = $nh; |
---|
1017 | $cpref = $pref; |
---|
1018 | } elsif ($pref) { |
---|
1019 | $fallback{$pref} .= " $nh"; |
---|
1020 | } |
---|
1021 | } |
---|
1022 | } |
---|
1023 | if (/Non-existent domain/) { |
---|
1024 | # |
---|
1025 | # These addresss are hosed. Kaput! Dead! |
---|
1026 | # However, if we created the address in the |
---|
1027 | # first place then there is a chance of |
---|
1028 | # salvation. |
---|
1029 | # |
---|
1030 | 1 while(<NSLOOKUP>); |
---|
1031 | close(NSLOOKUP); |
---|
1032 | return 0 unless $lastchance; |
---|
1033 | &giveup('domainify',"$server: Non-existent domain",undef,1); |
---|
1034 | return 0; |
---|
1035 | } |
---|
1036 | |
---|
1037 | } |
---|
1038 | close(NSLOOKUP); |
---|
1039 | unlink("/tmp/expn$$"); |
---|
1040 | unless ($nserver) { |
---|
1041 | $0 = "$o0 - finished mxlookup"; |
---|
1042 | return 0 unless $lastchance; |
---|
1043 | &giveup('mx domainify',"$server: Could not resolve address"); |
---|
1044 | return 0; |
---|
1045 | } |
---|
1046 | |
---|
1047 | # provide fallbacks in case $nserver doesn't work out |
---|
1048 | if (defined $fallback{$cpref}) { |
---|
1049 | $mx_secondary{$server} = $fallback{$cpref}; |
---|
1050 | } |
---|
1051 | |
---|
1052 | $0 = "$av0 - gethostbyname($nserver)"; |
---|
1053 | ($name,$aliases,$type,$len,$thataddr) = gethostbyname($nserver); |
---|
1054 | |
---|
1055 | unless ($thataddr) { |
---|
1056 | $0 = $o0; |
---|
1057 | return 0 unless $lastchance; |
---|
1058 | &giveup('mx domainify',"$nserver: could not resolve address"); |
---|
1059 | return 0; |
---|
1060 | } |
---|
1061 | print "MX($server) = $nserver\n" if $debug; |
---|
1062 | print "$server -> $nserver\n" if $vw && !$debug; |
---|
1063 | $mx{&trhost($server)} = $nserver; |
---|
1064 | # redeploy the users |
---|
1065 | unless (&mxredirect($server,*users)) { |
---|
1066 | return 0 unless $lastchance; |
---|
1067 | &giveup('mx domainify',"$nserver: only one level of mx redirect allowed"); |
---|
1068 | return 0; |
---|
1069 | } |
---|
1070 | $0 = "$o0 - finished mxlookup"; |
---|
1071 | return 1; |
---|
1072 | } |
---|
1073 | # if mx expansion did not help to resolve an address |
---|
1074 | # (ie: foo@bar became @baz:foo@bar, then undo the |
---|
1075 | # expansion). |
---|
1076 | # this is only used by &final |
---|
1077 | sub mxunroll |
---|
1078 | { |
---|
1079 | local(*host,*addr) = @_; |
---|
1080 | local($r) = 0; |
---|
1081 | print "looking for mxbacktrace{$addr *** $host}\n" |
---|
1082 | if ($debug > 1); |
---|
1083 | while (defined $mxbacktrace{"$addr *** $host"}) { |
---|
1084 | print "Unrolling MX expnasion: \@$host:$addr -> " |
---|
1085 | if ($debug || $verbose); |
---|
1086 | $host = $mxbacktrace{"$addr *** $host"}; |
---|
1087 | print "\@$host:$addr\n" |
---|
1088 | if ($debug || $verbose); |
---|
1089 | $r = 1; |
---|
1090 | } |
---|
1091 | return 1 if $r; |
---|
1092 | $addr = "\@$host:$addr" |
---|
1093 | if ($host =~ /\./); |
---|
1094 | return 0; |
---|
1095 | } |
---|
1096 | # register a completed expnasion. Make the final address as |
---|
1097 | # simple as possible. |
---|
1098 | sub final |
---|
1099 | { |
---|
1100 | local($addr,$host,$name,$error) = @_; |
---|
1101 | local($he); |
---|
1102 | local($hb,$hr); |
---|
1103 | local($au,$ah); |
---|
1104 | |
---|
1105 | if ($error =~ /Non-existent domain/) { |
---|
1106 | # |
---|
1107 | # If we created the domain, then let's undo the |
---|
1108 | # damage... |
---|
1109 | # |
---|
1110 | if (defined $create_host_backtrack{"$addr *** $host"}) { |
---|
1111 | while (defined $create_host_backtrack{"$addr *** $host"}) { |
---|
1112 | print "Un&domainifying($host) = " if $debug; |
---|
1113 | $host = $create_host_backtrack{"$addr *** $host"}; |
---|
1114 | print "$host\n" if $debug; |
---|
1115 | } |
---|
1116 | $error = "$host: could not locate"; |
---|
1117 | } else { |
---|
1118 | # |
---|
1119 | # If we only want valid addresses, toss out |
---|
1120 | # bad host names. |
---|
1121 | # |
---|
1122 | if ($valid) { |
---|
1123 | print STDERR "\@$host:$addr ($name) Non-existent domain\n"; |
---|
1124 | return ""; |
---|
1125 | } |
---|
1126 | } |
---|
1127 | } |
---|
1128 | |
---|
1129 | MXUNWIND: { |
---|
1130 | $0 = "$av0 - final parsing of \@$host:$addr"; |
---|
1131 | ($he = $host) =~ s/(\W)/\\$1/g; |
---|
1132 | if ($addr !~ /@/) { |
---|
1133 | # addr does not contain any host |
---|
1134 | $addr = "$addr@$host"; |
---|
1135 | } elsif ($addr !~ /$he/i) { |
---|
1136 | # if host part really something else, use the something |
---|
1137 | # else. |
---|
1138 | if ($addr =~ m/(.*)\@([^\@]+)$/) { |
---|
1139 | ($au,$ah) = ($1,$2); |
---|
1140 | print "au = $au ah = $ah\n" if $debug; |
---|
1141 | if (defined $temporary_redirect{"$addr *** $ah"}) { |
---|
1142 | $addr = "$au\@".$temporary_redirect{"$addr *** $ah"}; |
---|
1143 | print "Rewrite! to $addr\n" if $debug; |
---|
1144 | next MXUNWIND; |
---|
1145 | } |
---|
1146 | } |
---|
1147 | # addr does not contain full host |
---|
1148 | if ($valid) { |
---|
1149 | if ($host =~ /^([^\.]+)(\..+)$/) { |
---|
1150 | # host part has a . in it - foo.bar |
---|
1151 | ($hb, $hr) = ($1, $2); |
---|
1152 | if ($addr =~ /\@([^\.\@]+)$/ && ($1 eq $hb)) { |
---|
1153 | # addr part has not . |
---|
1154 | # and matches beginning of |
---|
1155 | # host part -- tack on a |
---|
1156 | # domain name. |
---|
1157 | $addr .= $hr; |
---|
1158 | } else { |
---|
1159 | &mxunroll(*host,*addr) |
---|
1160 | && redo MXUNWIND; |
---|
1161 | } |
---|
1162 | } else { |
---|
1163 | &mxunroll(*host,*addr) |
---|
1164 | && redo MXUNWIND; |
---|
1165 | } |
---|
1166 | } else { |
---|
1167 | $addr = "${addr}[\@$host]" |
---|
1168 | if ($host =~ /\./); |
---|
1169 | } |
---|
1170 | } |
---|
1171 | } |
---|
1172 | $name = "$name " if $name; |
---|
1173 | $error = " $error" if $error; |
---|
1174 | if ($valid) { |
---|
1175 | push(@final,"$name<$addr>"); |
---|
1176 | } else { |
---|
1177 | push(@final,"$name<$addr>$error"); |
---|
1178 | } |
---|
1179 | "\t$name<$addr>$error\n"; |
---|
1180 | } |
---|
1181 | |
---|
1182 | sub alarm |
---|
1183 | { |
---|
1184 | local($alarm_action,$alarm_redirect,$alarm_user) = @_; |
---|
1185 | alarm(3600); |
---|
1186 | $SIG{ALRM} = 'handle_alarm'; |
---|
1187 | } |
---|
1188 | # this involves one great big ugly hack. |
---|
1189 | # the "next HOST" unwinds the stack! |
---|
1190 | sub handle_alarm |
---|
1191 | { |
---|
1192 | &giveup($alarm_redirect,"Timed out during $alarm_action",$alarm_user); |
---|
1193 | next HOST; |
---|
1194 | } |
---|
1195 | |
---|
1196 | # read the rest of the current smtp daemon's response (and toss it away) |
---|
1197 | sub read_response |
---|
1198 | { |
---|
1199 | local($done,$watch) = @_; |
---|
1200 | local(@resp); |
---|
1201 | print $s if $watch; |
---|
1202 | while(($done eq "-") && ($s = <$S>) && ($s =~ /^\d+([- ])/)) { |
---|
1203 | print $s if $watch; |
---|
1204 | $done = $1; |
---|
1205 | push(@resp,$s); |
---|
1206 | } |
---|
1207 | return @resp; |
---|
1208 | } |
---|
1209 | # print args if verbose. Return them in any case |
---|
1210 | sub verbose |
---|
1211 | { |
---|
1212 | local(@tp) = @_; |
---|
1213 | print "@tp" if $verbose; |
---|
1214 | } |
---|
1215 | # to pass perl -w: |
---|
1216 | @tp; |
---|
1217 | $flag_a; |
---|
1218 | $flag_d; |
---|
1219 | $flag_1; |
---|
1220 | %already_domainify_fellback; |
---|
1221 | %already_mx_fellback; |
---|
1222 | &handle_alarm; |
---|
1223 | ################### BEGIN PERL/TROFF TRANSITION |
---|
1224 | .00 ; |
---|
1225 | |
---|
1226 | 'di |
---|
1227 | .nr nl 0-1 |
---|
1228 | .nr % 0 |
---|
1229 | .\\"'; __END__ |
---|
1230 | .\" ############## END PERL/TROFF TRANSITION |
---|
1231 | .TH EXPN 1 "March 11, 1993" |
---|
1232 | .AT 3 |
---|
1233 | .SH NAME |
---|
1234 | expn \- recursively expand mail aliases |
---|
1235 | .SH SYNOPSIS |
---|
1236 | .B expn |
---|
1237 | .RI [ -a ] |
---|
1238 | .RI [ -v ] |
---|
1239 | .RI [ -w ] |
---|
1240 | .RI [ -d ] |
---|
1241 | .RI [ -1 ] |
---|
1242 | .IR user [@ hostname ] |
---|
1243 | .RI [ user [@ hostname ]]... |
---|
1244 | .SH DESCRIPTION |
---|
1245 | .B expn |
---|
1246 | will use the SMTP |
---|
1247 | .B expn |
---|
1248 | and |
---|
1249 | .B vrfy |
---|
1250 | commands to expand mail aliases. |
---|
1251 | It will first look up the addresses you provide on the command line. |
---|
1252 | If those expand into addresses on other systems, it will |
---|
1253 | connect to the other systems and expand again. It will keep |
---|
1254 | doing this until no further expansion is possible. |
---|
1255 | .SH OPTIONS |
---|
1256 | The default output of |
---|
1257 | .B expn |
---|
1258 | can contain many lines which are not valid |
---|
1259 | email addresses. With the |
---|
1260 | .I -aa |
---|
1261 | flag, only expansions that result in legal addresses |
---|
1262 | are used. Since many mailing lists have an illegal |
---|
1263 | address or two, the single |
---|
1264 | .IR -a , |
---|
1265 | address, flag specifies that a few illegal addresses can |
---|
1266 | be mixed into the results. More |
---|
1267 | .I -a |
---|
1268 | flags vary the ratio. Read the source to track down |
---|
1269 | the formula. With the |
---|
1270 | .I -a |
---|
1271 | option, you should be able to construct a new mailing |
---|
1272 | list out of an existing one. |
---|
1273 | .LP |
---|
1274 | If you wish to limit the number of levels deep that |
---|
1275 | .B expn |
---|
1276 | will recurse as it traces addresses, use the |
---|
1277 | .I -1 |
---|
1278 | option. For each |
---|
1279 | .I -1 |
---|
1280 | another level will be traversed. So, |
---|
1281 | .I -111 |
---|
1282 | will traverse no more than three levels deep. |
---|
1283 | .LP |
---|
1284 | The normal mode of operation for |
---|
1285 | .B expn |
---|
1286 | is to do all of its work silently. |
---|
1287 | The following options make it more verbose. |
---|
1288 | It is not necessary to make it verbose to see what it is |
---|
1289 | doing because as it works, it changes its |
---|
1290 | .BR argv [0] |
---|
1291 | variable to reflect its current activity. |
---|
1292 | To see how it is expanding things, the |
---|
1293 | .IR -v , |
---|
1294 | verbose, flag will cause |
---|
1295 | .B expn |
---|
1296 | to show each address before |
---|
1297 | and after translation as it works. |
---|
1298 | The |
---|
1299 | .IR -w , |
---|
1300 | watch, flag will cause |
---|
1301 | .B expn |
---|
1302 | to show you its conversations with the mail daemons. |
---|
1303 | Finally, the |
---|
1304 | .IR -d , |
---|
1305 | debug, flag will expose many of the inner workings so that |
---|
1306 | it is possible to eliminate bugs. |
---|
1307 | .SH ENVIRONMENT |
---|
1308 | No environment variables are used. |
---|
1309 | .SH FILES |
---|
1310 | .PD 0 |
---|
1311 | .B /tmp/expn$$ |
---|
1312 | .B temporary file used as input to |
---|
1313 | .BR nslookup . |
---|
1314 | .SH SEE ALSO |
---|
1315 | .BR aliases (5), |
---|
1316 | .BR sendmail (8), |
---|
1317 | .BR nslookup (8), |
---|
1318 | RFC 823, and RFC 1123. |
---|
1319 | .SH BUGS |
---|
1320 | Not all mail daemons will implement |
---|
1321 | .B expn |
---|
1322 | or |
---|
1323 | .BR vrfy . |
---|
1324 | It is not possible to verify addresses that are served |
---|
1325 | by such daemons. |
---|
1326 | .LP |
---|
1327 | When attempting to connect to a system to verify an address, |
---|
1328 | .B expn |
---|
1329 | only tries one IP address. Most mail daemons |
---|
1330 | will try harder. |
---|
1331 | .LP |
---|
1332 | It is assumed that you are running domain names and that |
---|
1333 | the |
---|
1334 | .BR nslookup (8) |
---|
1335 | program is available. If not, |
---|
1336 | .B expn |
---|
1337 | will not be able to verify many addresses. It will also pause |
---|
1338 | for a long time unless you change the code where it says |
---|
1339 | .I $have_nslookup = 1 |
---|
1340 | to read |
---|
1341 | .I $have_nslookup = |
---|
1342 | .IR 0 . |
---|
1343 | .LP |
---|
1344 | Lastly, |
---|
1345 | .B expn |
---|
1346 | does not handle every valid address. If you have an example, |
---|
1347 | please submit a bug report. |
---|
1348 | .SH CREDITS |
---|
1349 | In 1986 or so, Jon Broome wrote a program of the same name |
---|
1350 | that did about the same thing. It has since suffered bit rot |
---|
1351 | and Jon Broome has dropped off the face of the earth! |
---|
1352 | (Jon, if you are out there, drop me a line) |
---|
1353 | .SH AVAILABILITY |
---|
1354 | The latest version of |
---|
1355 | .B expn |
---|
1356 | is available through anonymous ftp at |
---|
1357 | .IR ftp://ftp.idiom.com/pub/muir-programs/expn . |
---|
1358 | .SH AUTHOR |
---|
1359 | .I David Muir Sharnoff\ \ \ \ <muir@idiom.com> |
---|