home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #19 / NN_1992_19.iso / spool / comp / lang / perl / 5479 < prev    next >
Encoding:
Internet Message Format  |  1992-08-25  |  23.3 KB

  1. Xref: sparky comp.lang.perl:5479 comp.mail.sendmail:2142
  2. Newsgroups: comp.lang.perl,comp.mail.sendmail
  3. Path: sparky!uunet!cis.ohio-state.edu!magnus.acs.ohio-state.edu!usenet.ins.cwru.edu!eagle!sandman.lerc.nasa.gov!drich
  4. From: drich@sandman.lerc.nasa.gov (Daniel Rich)
  5. Subject: SCRIPT: perl sendmail utilities
  6. Message-ID: <1992Aug25.212550.10384@eagle.lerc.nasa.gov>
  7. Summary: log parsing and alias verification in perl
  8. Keywords: sendmail,perl
  9. Sender: news@eagle.lerc.nasa.gov
  10. Nntp-Posting-Host: sandman.lerc.nasa.gov
  11. Organization: NASA Lewis Research Center [Cleveland, Ohio]
  12. Date: Tue, 25 Aug 1992 21:25:50 GMT
  13. Lines: 734
  14.  
  15.  
  16. As a thank you to all of the people who have helped me with my
  17. perl/sendmail questions over the last couple of weeks, I am posting
  18. the scripts that have resulted.  These are two utilities that I have
  19. needed for some time, and I want to thank everyone who was helpful in
  20. their creating.
  21.  
  22. First of all, we have a script for parsing the sendmail syslog.  I
  23. have had several occasions where managers have asked me if people are
  24. using e-mail to communicate, or asked how much of the e-mail was being
  25. sent off site.  This script will take a syslog file, and output a mail
  26. summary including the total use statistics, and a summary by user and
  27. host (anything within your domain is defined as "local").  In order to
  28. run it, you will  need to change the $local_domain line, and either
  29. change the $systype to match your system (no guarantees that this will
  30. work :-), or hardcode the value for $MAILLOG (the name of your syslog
  31. output file for sendmail).
  32.  
  33. The second script will validate user mail ids/aliases.  It should work
  34. as-is, and will accept the following command line options:
  35.     -n - noresolv: don't resolve to a local address
  36.     -s - short output format (don't print "real name" and <>)
  37.     -v - verbose output
  38. It will find mail loops, expand mailing lists, and resolve all
  39. addresses down to a local delivery (at least, what sendmail thinks is
  40. local).  If it gets an unknown hostname from the host in the address,
  41. it will attempt to find an MX record for that host, and try that.
  42. This may produce unexpected mail loop messages, however; I haven't
  43. come up with a good way of handling some of the MX hosts.  Also, you
  44. may occasionally get a mail loop message if the same user is used
  45. twice (ie. using addrcheck drich@lerc.nasa.gov drich@lerc.nasa.gov
  46. will print a loop message for the second address).  This is a bug, but
  47. the loop checking is rather simplistic at this stage.
  48.  
  49. Please let me know if you find these scripts useful, and I would
  50. appreciate hearing of any suggestions/modifications you might have.
  51.  
  52. Just cut on the dotted lines below, and enjoy!
  53.  
  54. -- 
  55. Dan Rich                    | drich@lerc.nasa.gov   |  (216) 433-4000
  56. Sr. Systems Engineer        | "Danger, you haven't seen the last of me!"
  57. RMS Technologies, Inc.      |    "No, but the first of you turns my stomach!"
  58. NASA Lewis Research Center  | -- The Firesign Theatre's Nick Danger
  59.  
  60.  
  61. 8<- sm.logger ------------------ Cut Here ------------------------------>8
  62. #! /usr/local/bin/perl
  63. # $Id: sm.logger,v 1.3 92/08/25 16:00:34 drich Exp Locker: drich $
  64. #
  65. # Copyright (C) 1992  Daniel Rich
  66. #
  67. # Author: Daniel Rich (drich@lerc.nasa.gov)
  68. #
  69. # This program is free software; you can redistribute it and/or modify
  70. # it under the terms of the GNU General Public License as published by
  71. # the Free Software Foundation; either version 1, or (at your option)
  72. # any later version.
  73. #
  74. # This program is distributed in the hope that it will be useful,
  75. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  76. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  77. # GNU General Public License for more details.
  78. #
  79. # A copy of the GNU General Public License can be obtained from this
  80. # program's author (send electronic mail to ange@hplb.hpl.hp.com) or from
  81. # the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
  82. # 02139, USA.
  83. #
  84. # sm.logger - parse the sendmail log and produce a summary
  85. #
  86. # $Log:    sm.logger,v $
  87. # Revision 1.3  92/08/25  16:00:34  drich
  88. # Fixed divide by zero error if no mail either delivered or sent.
  89. # Revision 1.2  92/08/05  14:03:14  drich
  90. # Replaced '=' with '-' in output report
  91. # Revision 1.1  92/07/22  16:36:54  drich
  92. # Logfile processor for sendmail
  93. #
  94. #
  95. # Written by Dan Rich - drich@lerc.nasa.gov
  96. #     Wed July 22, 1992
  97. #
  98.  
  99. # Change the following for the appropriate system type.
  100. $systype = "SGI";        # Valid are Ultrix, Sun, SGI, RS6000
  101.  
  102. # Change the following to the local sendmail domain
  103. $local_domain = "lerc.nasa.gov";
  104.  
  105. LOOP: {
  106.     if ( $systype eq "SGI")    { $MAILLOG = "/usr/adm/SYSLOG"; 
  107.                  last LOOP; }
  108.     if ( $systype eq "Ultrix") { $MAILLOG = "/usr/spool/mqueue/syslog";
  109.                  last LOOP; }
  110.     if ( $systype eq "Sun")    { $MAILLOG = "/var/adm/messages";
  111.                  last LOOP; }
  112.     if ( $systype eq "RS6000") { $MAILLOG = "/var/adm/messages";
  113.                  last LOOP; }
  114.     print "This script does not support the system type: $systype"; exit(1);
  115. }
  116.  
  117. sub parse_mail_addr {
  118.     local($addr) = @_;
  119.     # Attempt to parse an address down to an *originating* user and host.
  120.     # Assume the address takes one of the following forms:
  121.     #     user@host.domain
  122.     #     host!host!host!user
  123.     #     host!host!host!user@host.domain
  124.     #      @host.domain:user@host.domain
  125.  
  126.     $user = "";
  127.     $host = "";
  128.     $domain = "";
  129.  
  130.     # Get rid of <> in address
  131.     $addr =~ s/\<//g;
  132.     $addr =~ s/\>//g;
  133.  
  134.     # Get rid of (Real Name)
  135.     $addr =~ s/\(.*\)//;
  136.  
  137.     # Strip spaces
  138.     $addr =~ s/ //g;
  139.  
  140.     # Split user and host
  141.     if ( $addr =~ /[@!]/ ) {        # If we have and @ or ! address
  142.         if ( (($user,$host) = ($addr =~ /@.*:(.*)@(.*)/ )) || 
  143.          (($user,$host) = ($addr =~ /(.*)@(.*)/ )) ) {
  144.             if ( $user =~ /!/ ) {
  145.             ( $host, $user ) = ( $user =~ /([^!]*)!([^!]*)$/ );
  146.             }
  147.         } else {            # Ok, it is uucp format
  148.             if ( $addr =~ /!/ ) {
  149.             ( $host, $user ) = ( $addr =~ /([^!]*)!([^!]*)$/ );
  150.             }
  151.         }
  152.     } else {                # This had better be local...
  153.         $user = $addr;
  154.     $host = "";
  155.     $domain = "";
  156.     }
  157.  
  158.     # Split host and domain
  159.     if ( $host =~ /\./ ) {
  160.         ($host,$domain) = ( $host =~ /([^.]*)\.(.*)/ );
  161.     }
  162.  
  163.     return ($user, $host, $domain);
  164. }
  165.  
  166. sub format_addr {
  167.     local($username, $host, $domain) = @_;
  168.  
  169.     $addr = "";
  170.     $username =~ tr/A-Z/a-z/;
  171.     $host =~ tr/A-Z/a-z/;
  172.     $domain =~ tr/A-Z/a-z/;
  173.  
  174.     if ((length($domain) != 0) && ($domain ne $local_domain)) {
  175.         $addr = $username. "@" . $host . "." . $domain;
  176.     } elsif (length($host) != 0 ) {
  177.     $addr = $username. "@" . $host;
  178.     } else {
  179.         $addr = $username;
  180.     }
  181.     return $addr;
  182. }
  183.  
  184. sub format_host {
  185.     local($host, $domain) = @_;
  186.  
  187.     $addr = "";
  188.     $host =~ tr/A-Z/a-z/;
  189.     $domain =~ tr/A-Z/a-z/;
  190.  
  191.     if (($domain eq $local_domain) || 
  192.     (($host . "." . $domain) eq $local_domain) ||
  193.     (length($domain) == 0)) {
  194.         $addr = "local";
  195.     } else {
  196.         $addr = $host . "." . $domain;
  197.     }
  198.     return $addr;
  199. }
  200.  
  201. open MAILLOG || die "failed to open log file: $!\n";
  202.  
  203. $start_date = "";
  204. $start_time = "";
  205.  
  206. while ( <MAILLOG> ) {
  207.     $line = "";
  208.     ( $systype eq "SGI" ) && ( /sendmail\[[0-9]*\]:/ && ($line = $_) );
  209.     ( $systype eq "Ultrix") && ( /sendmail:/ && ($line = $_) );
  210.     if ( length($line) == 0 ) {
  211.     next;
  212.     }
  213.  
  214.     if ( length($start_date) == 0 ) {
  215.         ($start_date, $start_time) = 
  216.         ($line =~ /^([A-Z][a-z]* *[0-9]*) ([0-9][0-9]:[0-9][0-9]:[0-9][0-9])/);
  217.     }
  218.     ($end_date, $end_time) = 
  219.     ($line =~ /^([A-Z][a-z]* *[0-9]*) ([0-9][0-9]:[0-9][0-9]:[0-9][0-9])/);
  220.     
  221.     if (($ID, $addr) = ($line =~/: ([A-Za-z0-9]*): to=(.*), delay/)) {
  222.         if ( $line =~ /stat=Sent/ ) {
  223.             foreach $taddr (split(/,/, $addr)) {
  224.                 ($username, $host, $domain) = &parse_mail_addr($taddr);
  225.                 $user = &format_addr($username, $host, $domain);
  226.                 $host = &format_host($host, $domain);
  227.                 $userlist{$user} = 1;
  228.                 $delivered{$user} .= $ID . ' ';
  229.                 $hostlist{$host} = 1;
  230.                 $delivered{$host} .= $ID . ' ';
  231.                 # Check if it was deferred
  232.                 $IDlist = $deferred{$user};
  233.                 foreach $tID (split(/ /, $IDlist)) {
  234.                     if ( $tID == $ID ) {
  235.                         $deferred{$user} =~ s/$ID //g;
  236.                     }
  237.                 }
  238.                 $IDlist = $deferred{$host};
  239.                 foreach $tID (split(/ /, $IDlist)) {
  240.                     if ( $tID == $ID ) {
  241.                         $deferred{$host} =~ s/$ID //g;
  242.                     }
  243.                 }
  244.             }
  245.         } elsif ( $line =~ /stat=Deferred/ ) {
  246.             foreach $taddr (split(/,/, $addr)) {
  247.                 ($username, $host, $domain) = &parse_mail_addr($taddr);
  248.                 $user = &format_addr($username, $host, $domain);
  249.                 $host = &format_host($host, $domain);
  250.                 $userlist{$user} = 1;
  251.                 $hostlist{$host} = 1;
  252.                 # Check if it was deferred earlier
  253.                 $IDlist = $deferred{$user};
  254.         $IDdeferred = 0;
  255.                 foreach $tID (split(/ /, $IDlist)) {
  256.                     if ( $tID == $ID ) {
  257.                         $IDdeferred = 1;
  258.                     }
  259.                 }
  260.         if ( $IDdeferred == 0 ) {$deferred{$user} .= $ID . ' ';}
  261.                 $IDlist = $deferred{$host};
  262.         $IDdeferred = 0;
  263.                 foreach $tID (split(/ /, $IDlist)) {
  264.                     if ( $tID == $ID ) {
  265.                         $IDdeferred = 1;
  266.                     }
  267.                 }
  268.         if ( $IDdeferred == 0 ) {$deferred{$host} .= $ID . ' ';}
  269.             }
  270.         } else {
  271.             foreach $taddr (split(/,/, $addr)) {
  272.                 ($username, $host, $domain) = &parse_mail_addr($taddr);
  273.                 $user = &format_addr($username, $host, $domain);
  274.                 $host = &format_host($host, $domain);
  275.                 $IDlist = $deferred{$user};
  276.                 foreach $tID (split(/ /, $IDlist)) {
  277.                     if ( $tID == $ID ) {
  278.                         $deferred{$user} =~ s/$ID //g;
  279.                         last;
  280.                     }
  281.                 }
  282.                 $IDlist = $deferred{$host};
  283.                 foreach $tID (split(/ /, $IDlist)) {
  284.                     if ( $tID == $ID ) {
  285.                     $deferred{$host} =~ s/$ID //g;
  286.                         last;
  287.                     }
  288.                 }
  289.             }
  290.         }
  291.     }
  292.     if (($ID, $addr, $size) = ($line =~/: ([A-Za-z0-9]*): from=(.*), size=([0-9]*)/)) {
  293.         ($username, $host, $domain) = &parse_mail_addr($addr);
  294.         $user = &format_addr($username, $host, $domain);
  295.     $host = &format_host($host, $domain);
  296.         $userlist{$user} = 1;
  297.         $sent{$user} .= $ID . ' ';
  298.         $hostlist{$host} = 1;
  299.         $sent{$host} .= $ID . ' ';
  300.         $size{$ID} = $size;
  301.     }
  302. }
  303.  
  304. printf ("\n\n\t\t\tSendmail activity report\n");
  305. printf ("Starting: %s %s\n",$start_date,$start_time);
  306. printf ("Ending: %s %s\n",$end_date,$end_time);
  307.  
  308. #
  309. # User statistics
  310. $totsent = 0;
  311. $totdelivered = 0;
  312. $totpctsent =0;
  313. $totpctdelivered = 0;
  314. $countsent = 0;
  315. $countdelivered = 0;
  316. $countdeferred = 0;
  317. foreach $user (sort keys(%userlist)) {
  318.     $totsent{$user} = 0;
  319.     $totdelivered{$user} = 0;
  320.     $countsent{$user} = 0;
  321.     $countdelivered{$user} = 0;
  322.     $countdeferred{$user} = 0;
  323.  
  324.     # Count total messages sent by each user
  325.     $IDlist = $sent{$user};
  326.     foreach $ID (split(/ /, $IDlist)) {
  327.     $totsent{$user} += $size{$ID};
  328.         $countsent{$user}++;
  329.     }
  330.     $totsent += $totsent{$user};
  331.     $countsent += $countsent{$user};
  332.  
  333.     # Count total messages received by each user
  334.     $IDlist = $delivered{$user};
  335.     foreach $ID (split(/ /, $IDlist)) {
  336.     $totdelivered{$user} += $size{$ID};
  337.         $countdelivered{$user}++;
  338.     }
  339.     $totdelivered += $totdelivered{$user};
  340.     $countdelivered += $countdelivered{$user};
  341.  
  342.     # Count deferred messages for each user
  343.     $IDlist = $deferred{$user};
  344.     foreach $ID (split(/ /, $IDlist)) {
  345.         $countdeferred{$user}++;
  346.     }
  347.     $countdeferred += $countdeferred{$user};
  348. }
  349.  
  350. printf ("\n\n");
  351. $REPORTFMT = "%-20s %5s %8s\n";
  352. $REPORTNUM = "%-20s %5d %8d\n";
  353. printf ($REPORTFMT,"Message Status","Total","Size");
  354. printf ("-----------------------------------\n");
  355. printf ($REPORTNUM,"Received",$countsent,$totsent);
  356. printf ($REPORTNUM,"Delivered",$countdelivered,$totdelivered);
  357. printf ($REPORTNUM,"Deferred",$countdeferred);
  358.  
  359. printf ("\n\nUser Statistics:\n\n");
  360. $REPORTFMT = "%-30s %6s %8s %7s %6s %8s %7s\n";
  361. $REPORTNUM = "%-30s %6d %8d %6.2f%% %6d %8d %6.2f%%\n";
  362. printf ($REPORTFMT,"User Name","# from","size","%","# to","size","%");
  363. printf ($REPORTFMT,"---------","------","--------","------","------","--------","------");
  364. foreach $user (sort keys(%userlist)) {
  365.     $percent1 = 0;
  366.     $percent2 = 0;
  367.  
  368.     $percent1 = ($totsent{$user}/$totsent)*100            if ($totsent != 0);
  369.     $percent2 = ($totdelivered{$user}/$totdelivered)*100    if ($totdelivered != 0);
  370.     printf ($REPORTNUM,substr($user,0,30),
  371.     $countsent{$user},$totsent{$user},$percent1,
  372.     $countdelivered{$user},$totdelivered{$user},$percent2);
  373.     $totpctsent += $percent1;
  374.     $totpctdelivered += $percent2;
  375. }
  376. printf ("------------------------------------------------------------------------------\n");
  377. printf ($REPORTNUM,"Totals",$countsent,$totsent,$totpctsent,$countdelivered,$totdelivered,$totpctdelivered);
  378.  
  379. # Host statistics
  380. foreach $host (sort keys(%hostlist)) {
  381.     $totsent{$host} = 0;
  382.     $totdelivered{$host} = 0;
  383.     $countsent{$host} = 0;
  384.     $countdelivered{$host} = 0;
  385.     $countdeferred{$host} = 0;
  386.  
  387.     # Count total messages sent by each host
  388.     $IDlist = $sent{$host};
  389.     foreach $ID (split(/ /, $IDlist)) {
  390.     $totsent{$host} += $size{$ID};
  391.         $countsent{$host}++;
  392.     }
  393.  
  394.     # Count total messages received by each host
  395.     $IDlist = $delivered{$host};
  396.     foreach $ID (split(/ /, $IDlist)) {
  397.     $totdelivered{$host} += $size{$ID};
  398.         $countdelivered{$host}++;
  399.     }
  400. }
  401.  
  402. printf ("\n\nHost Statistics:\n\n");
  403. $REPORTFMT = "%-30s %6s %8s %7s %6s %8s %7s\n";
  404. $REPORTNUM = "%-30s %6d %8d %6.2f%% %6d %8d %6.2f%%\n";
  405. printf ($REPORTFMT,"Host Name","# from","size","%","# to","size","%");
  406. printf ($REPORTFMT,"---------","------","--------","------","------","--------","------");
  407. foreach $host (sort keys(%hostlist)) {
  408.     $percent1 = 0;
  409.     $percent2 = 0;
  410.  
  411.     $percent1 = ($totsent{$host}/$totsent)*100            if ($totsent != 0);
  412.     $percent2 = ($totdelivered{$host}/$totdelivered)*100    if ($totdelivered != 0);
  413.     printf ($REPORTNUM,substr($host,0,30),
  414.     $countsent{$host},$totsent{$host},$percent1,
  415.     $countdelivered{$host},$totdelivered{$host},$percent2);
  416. }
  417. printf ("------------------------------------------------------------------------------\n");
  418. printf ($REPORTNUM,"Totals",$countsent,$totsent,$totpctsent,$countdelivered,$totdelivered,$totpctdelivered);
  419.  
  420. 8<- addrcheck ------------------ Cut Here ------------------------------>8
  421. #!/usr/local/bin/perl
  422. # $Id: addrcheck,v 1.2 92/08/25 16:56:59 drich Exp $
  423. #
  424. # Copyright (C) 1992  Daniel Rich
  425. #
  426. # Author: Daniel Rich (drich@lerc.nasa.gov)
  427. #
  428. # This program is free software; you can redistribute it and/or modify
  429. # it under the terms of the GNU General Public License as published by
  430. # the Free Software Foundation; either version 1, or (at your option)
  431. # any later version.
  432. #
  433. # This program is distributed in the hope that it will be useful,
  434. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  435. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  436. # GNU General Public License for more details.
  437. #
  438. # A copy of the GNU General Public License can be obtained from this
  439. # program's author (send electronic mail to ange@hplb.hpl.hp.com) or from
  440. # the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
  441. # 02139, USA.
  442. #
  443. # addrcheck - verify a user's e-mail address (will continue contacting hosts
  444. #          until it either finds a local address, or a loop).
  445. #          It will also expand any mailing lists it finds.
  446. #    -n - noresolv: don't resolve to a local address
  447. #    -s - short output format (don't print "real name" and <>)
  448. #    -v - verbose output
  449. #
  450. # $Log:    addrcheck,v $
  451. # Revision 1.2  92/08/25  16:56:59  drich
  452. # Added MX record lookup for unknown hosts.
  453. # Revision 1.1  92/08/21  11:13:04  drich
  454. # Initial revision
  455. # Written by:
  456. #     Dan Rich (drich@lerc.nasa.gov)
  457. #    Friday August 21, 1992
  458. # Based on an original perl script by:
  459. #     Nick Holloway (alfie@dcs.warwick.ac.uk)
  460. #
  461.  
  462. $0 =~ s%.*/%%;            # $0 = basname $0
  463.  
  464. require('getopts.pl');
  465.  
  466. sub nslookup {
  467.     local($mxhost) = "";
  468.     local($pref) = 10000;
  469.     local($c) = '';
  470.  
  471.     # Use nslookup to find MX record for host name.
  472.     pipe ( CHLD_RD, NSL_WR );
  473.     pipe ( NSL_RD, CHLD_WR );
  474.     if ( ( $pid = fork () ) < 0 ) {
  475.     die ( "$0: fork failed: $!\n" );
  476.     }
  477.     if ( $pid == 0 ) {            # child
  478.     open ( STDOUT, ">&CHLD_WR" );
  479.     open ( STDERR, ">&CHLD_WR" );
  480.     open ( STDIN,  "<&CHLD_RD" );
  481.     close ( CHLD_RD ); close ( NSL_WR );
  482.     close ( NSL_RD ); close ( CHLD_WR );
  483.     select ( STDIN );  $| = 1;
  484.     select ( STDOUT ); $| = 1;
  485.     select ( STDERR ); $| = 1;
  486.     exec "nslookup";
  487.     exit ( 1 );
  488.     } else {                # parent
  489.     close ( CHLD_RD ); select ( NSL_WR ); $| = 1;
  490.     close ( CHLD_WR ); select ( NSL_RD ); $| = 1;
  491.     select ( STDOUT );
  492.     }
  493.  
  494.     # Use read() since we have prompts
  495.     $c = '';
  496.     while ( ! (<NSL_RD> =~ /Default Server:/) ) {}
  497.     while (read(NSL_RD, $c, 1) && ($c ne '>'))  {}    # Skip opening text
  498.     if ( ! read (NSL_RD, $c, 1) ) {            # and prompt
  499.         return 0;
  500.     }
  501.     print NSL_WR "set type=MX\n";            # Set type=MX
  502.     if ( ! read (NSL_RD, $c, 1) ) {            # Skip prompt
  503.     return 0;
  504.     }
  505.     print NSL_WR $node,"\n";                # Send nodename
  506.  
  507.     $mxhost = ""; $c = "";
  508.     while ($c ne ">") {
  509.         $line = "";
  510.         while (read(NSL_RD, $c, 1) && ( ($c ne ">") && ($c ne "\n") ))  {
  511.         $line = $line . $c;
  512.         }
  513.         if ( $line =~ /exchanger =/ ) {
  514.         $line =~ /preference = ([0-9]*)/;
  515.         if ( $1 < $pref ) {
  516.             $pref = $1;
  517.             ( $line =~ /exchanger = (.*)/ ) && ( $mxhost = $1 );
  518.         }
  519.     }
  520.     }
  521.  
  522.     if ( $mxhost eq "" ) {
  523.         # Ok, so it isn't an MX host, how about an A record?
  524.         if ( ! read (NSL_RD, $c, 1) ) {    # Skip prompt
  525.         return 0;
  526.         }
  527.         print NSL_WR "set type=A\n";
  528.         if (! read (NSL_RD, $c, 1) ) {    # Skip prompt
  529.         return 0;
  530.     }
  531.         print NSL_WR $node,"\n";
  532.  
  533.         $mxhost = ""; $c = "";
  534.         while ($c ne ">") {
  535.             $line = "";
  536.             while (read(NSL_RD, $c, 1) && ( ($c ne ">") && ($c ne "\n") ))  {
  537.             $line = $line . $c;
  538.             }
  539.             if ( $line =~ /Name:/ ) {
  540.             ( $line =~ /Name:[     ]*(.*)/ ) && ( $mxhost = $1 );
  541.         }
  542.         }
  543.     }
  544.  
  545.     close ( NSL_RD );    close ( NSL_WR );
  546.     while ( wait != -1 ) { ; }
  547.     if ( $mxhost eq "" ) {
  548.         0;
  549.     } else {
  550.         $node = $mxhost;
  551.     1;
  552.     }
  553. }
  554.  
  555. sub openhost {
  556.     pipe ( CHLD_RD, SMTP_WR );
  557.     pipe ( SMTP_RD, CHLD_WR );
  558.     if ( ( $pid = fork () ) < 0 ) {
  559.     die ( "$0: fork failed: $!\n" );
  560.     }
  561.     if ( $pid == 0 ) {            # child
  562.     open ( STDOUT, ">&CHLD_WR" );
  563.     open ( STDERR, ">&CHLD_WR" );
  564.     open ( STDIN,  "<&CHLD_RD" );
  565.     close ( CHLD_RD ); close ( SMTP_WR );
  566.     close ( SMTP_RD ); close ( CHLD_WR );
  567.     select ( STDIN );  $| = 1;
  568.     select ( STDOUT ); $| = 1;
  569.     select ( STDERR ); $| = 1;
  570.     exec "telnet $node smtp";
  571.     exit ( 1 );
  572.     } else {                # parent
  573.     close ( CHLD_RD ); select ( SMTP_WR ); $| = 1;
  574.     close ( CHLD_WR ); select ( SMTP_RD ); $| = 1;
  575.     select ( STDOUT );
  576.     }
  577.     1
  578. }
  579.  
  580. # Parse command line args
  581. if (! do Getopts('nsv') ) {
  582.     die ( "usage: $0 [ -nsv ] alias ...\n" .
  583.       "       where alias is of the form \"user@node\" or \"user\"\n" );
  584. }
  585. $noresolv = 1     if ( $opt_n );
  586. $short = 1    if ( $opt_s );
  587. $verbose = 1    if ( $opt_v ); 
  588.  
  589. $SIG{'CHLD'} = 'fireman';
  590. $SIG{'HUP'}  = 'interrupt';
  591. $SIG{'INT'}  = 'interrupt';
  592. $SIG{'TERM'} = 'interrupt';
  593.  
  594. @aliaslist = @ARGV;        # The list of aliases to check
  595. $aliaslist_pos = -1;        # Our position in the list
  596.  
  597. # Loop through the list of aliases, parsing each one.  If we find one that 
  598. # is doesn't resolve locally, insert it into the list.  Also expand mailing
  599. # lists by inserting them after the current address.
  600. for $alias ( @aliaslist ) {
  601.     $aliaslist_pos++;
  602.  
  603.     ( $user, $node ) = split ( '@', $alias );
  604.  
  605.     $node = "localhost"    if ( $node eq "" );    # Localhost if null
  606.  
  607.     if ( $nodelist{$node} ) {
  608.     # Check for mail loops (any host/user pair that we have seen before)
  609.     if ( $nodelist{$node} =~ /$user/ ) {
  610.         print "ERROR: mail loop: $alias\n";
  611.         next;
  612.     } else {
  613.         # Build a list of users on this node
  614.         $nodelist{$node} = $nodelist{$node} . ":" . $user;
  615.     }
  616.     } else {
  617.     $nodelist{$node} = $user
  618.     }
  619.  
  620.     do openhost();        # Open a connection to $node
  621.  
  622.     $_ = <SMTP_RD>;
  623.     if ( /[Uu]nknown host/ ) {
  624.     if ( do nslookup() ) {    # If host unknown, try for MX record
  625.         do openhost();    # and open it again
  626.         $_ = <SMTP_RD>;
  627.     } else {
  628.             warn ( "$0: $_" );
  629.         close ( SMTP_WR ); close ( SMTP_RD );
  630.         next;
  631.         }
  632.     }
  633.     $_ = <SMTP_RD>        if /^Trying/;
  634.     $_ = <SMTP_RD>        if /^Connected/;
  635.  
  636.     $_ = <SMTP_RD>        if /^Escape/;
  637.     if ( ! /^220/ ) {
  638.     warn ( "$0: unable to connect to host \"$node\"\n" );
  639.     print SMTP_WR "quit\n";
  640.     close ( SMTP_WR ); close ( SMTP_RD );
  641.     next;
  642.     }
  643.  
  644.     if ( $verbose ) {
  645.     # Set verbose mode
  646.         print SMTP_WR "VERB\n";
  647.         $_ = <SMTP_RD>; chop;
  648.     if (! /^200/ ) {
  649.         print "$0: error setting verbose: $_\n";
  650.     }
  651.     }
  652.     print SMTP_WR "VRFY $user\n";
  653.     $_ = <SMTP_RD>; chop;
  654.  
  655.     while ( $verbose && /^050/ ) {
  656.     print "$alias -> "; 
  657.     s/^\d{3} //;
  658.     print " $_\n";
  659.         $_ = <SMTP_RD>; chop;
  660.     }
  661.     if ( /^250/ ) {        # alias expansion
  662.     print "$alias ->";
  663.     $len = length ( $alias ) + 3;
  664.     @addrlist = ();
  665.     while ( /^250-/ ) {
  666.         s/^250-//; s/\s+/ /g; 
  667.         push (@addrlist, $_);
  668.         s/.*<(.*)>.*/\1/    if $short;
  669.         $len += length ( $_ ) + 2;
  670.         if ( $len > 79 ) {
  671.             print "\n ";
  672.             $len = length ( $_ ) + 3;
  673.         }
  674.         print " $_,";
  675.         $_ = <SMTP_RD>; chop;
  676.     }
  677.     s/^250 //; s/\s+/ /g; 
  678.         push(@addrlist, $_);
  679.     s/.*<(.*)>.*/\1/    if $short;
  680.     if ( length ( $_ ) + $len > 79 ) {
  681.         print "\n ";
  682.     }
  683.     print " $_\n";
  684.     if ( ! $noresolv ) {
  685.         # The following mess is what puts the new address into @aliaslist
  686.         if ( $#addrlist != 0 ) {
  687.         # Process list
  688.         for $talias ( reverse @addrlist ) {
  689.             $talias =~ s/.*<(.*)>.*/\1/;
  690.             if ( $#aliaslist > $aliaslist_pos ) {
  691.                 @taliaslist = splice(@aliaslist, $aliaslist_pos + 1);
  692.                 push (@aliaslist, $talias);
  693.                 push (@aliaslist, @taliaslist);
  694.             } else {
  695.                 push (@aliaslist, $talias);
  696.             }
  697.         }
  698.         } else {
  699.                 if (($addrlist[0] =~ /<.*@(.*)>$/) eq "") {
  700.             } else {
  701.                 $addrlist[0] =~ /<(.*)>$/;
  702.             $1 =~ s/.*<(.*)>.*/\1/;
  703.             if ( $#aliaslist > $aliaslist_pos ) {
  704.                 @taliaslist = splice(@aliaslist, $aliaslist_pos + 1);
  705.                 push (@aliaslist, $1);
  706.                 push (@aliaslist, @taliaslist);
  707.             } else {
  708.                 push (@aliaslist, $1);
  709.             }
  710.         }
  711.         }
  712.         }
  713.     } else {        # Return code not 250
  714.         s/^\d{3} //;
  715.         print "ERROR: $alias -> $_\n";
  716.     }
  717.     
  718.     print SMTP_WR "QUIT\n";
  719.     $_ = <SMTP_RD>;            # 221 ... closing connection
  720.     $_ = <SMTP_RD>;            # Connection closed ...
  721.     close ( SMTP_WR ); close ( SMTP_RD );
  722. } continue {
  723.     while ( wait != -1 ) { ; }
  724. }
  725.  
  726. sub fireman {
  727.     # while ( wait != -1 ) { ; }
  728. }
  729.  
  730. sub interrupt {
  731.     kill 'TERM', $pid;
  732.     print STDERR "$0: interrupted\n";
  733.     exit ( 1 );
  734. }
  735. 8<------------------------------ Cut Here ------------------------------>8
  736.  
  737. -- 
  738. -- 
  739. Dan Rich                    | drich@lerc.nasa.gov   |  (216) 433-4000
  740. Sr. Systems Engineer        | "Danger, you haven't seen the last of me!"
  741. RMS Technologies, Inc.      |    "No, but the first of you turns my stomach!"
  742. NASA Lewis Research Center  | -- The Firesign Theatre's Nick Danger
  743.  
  744.