Open Computing ``Hands-On'': ``Wizard's Grabbag'' Column: October 1994: Listings Listing: The chkaddrs program summarizes Sendmail address processing. A. Listing of chkaddrs: 1 #!/usr/local/bin/perl 2 # @(#) chkaddrs Check sendmail address processing 3 # Based on checksendmail, which was written by 4 # Gene Kim, Rob Kolstad, and Jeff Polk, July 1990 5 # Repackaged by Becca Thomas, July 1994 6 7 # Configuration section: 8 $= = 24; # default report page length 9 $| = 1; # flush output after print,write 10 $addr_file = "address.resolve"; # default address input file 11 $config_file = "/etc/sendmail.cf"; # default sendmail config file 12 $tmpfile = "/tmp/chkaddrs.$$"; # temporary file 13 $usage = "Usage: $0 [ -a addr-file ] [ -c config-file ] [ -q queuedir ] 14 -a address-file File containing addresses for testing 15 -c configuration-file Alternate sendmail configuration file 16 -q queue directory Alternate mail queue directory\n"; 17 18 # Process command-line options: 19 require 'getopts.pl'; # library to process command-line options 20 &Getopts('a:c:q:') || die "$usage"; # opt x arg into opt_x variable 21 22 # Process option arguments: 23 if ($opt_a) { $addr_file = $opt_a; } # user-specified addr file 24 if ($opt_c) { $config_file = $opt_c; } # alternate config file 25 26 # Determine queue directory specified in sendmail.cf config file: 27 chop($queue_dir = `grep ^OQ $config_file`); # get dir value from file 28 $queue_dir =~ s/^OQ//; # store default for later use 29 30 if ($opt_q) { $queue_dir = $opt_q; } # overwrite default value 31 32 # Check for existence of these files and directory: 33 die "Can't find address file, $addr_file\n" unless -e $addr_file; 34 die "Can't find config file $config_file\n" unless -e $config_file; 35 die "Can't find queue directory $queue_dir\n" unless -e $queue_dir; 36 37 # Make sure the script user can access the queue directory: 38 if ((! -r $queue_dir) || (! -x _) || (! -w _)) { 39 die "$0: Aborting, can't access queue directory, $queue_dir!\n"; 40 } 41 42 # Display status information: 43 chop($hostname = `hostname`); 44 chop($pwd = `pwd`); 45 print "This system: $hostname\nCurrent directory: $pwd\n"; 46 print "Configuration file: $config_file\nAddress file: $addr_file\n"; 47 print "Queue directory: $queue_dir\n\n\n"; 48 49 # Trap keyboard-generated signals: 50 sub handler { 51 local($sig) = @_; # first argument is signal name 52 print STDERR "Caught a SIG$sig--shutting down\n"; 53 unlink($tmpfile); 54 exit(0); 55 } 56 $SIG{'INT'} = 'handler'; 57 $SIG{'QUIT'} = 'handler'; 58 59 # REPORT FORMAT SPECIFICATIONS: 60 # Override default screen length with LINES environment variable. 61 ($= = $ENV{"LINES"}) if defined($ENV{"LINES"}); 62 63 # Current line count must be decremented because 64 # print() already has been used to display five lines: 65 $- = -5; 66 67 format MAILERDEFINITIONS_TOP = 68 Delivery Input Dest Dest 69 Agent Address Host User 70 ------------------------------------------------------------------------ 71 . 72 73 format MAILERDEFINITIONS = 74 @<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<< 75 $mailer, $input, $dest_host, $dest_user 76 . 77 78 format ADDRESSREWRITING_TOP = 79 Delivery Input Output 80 Agent Address Address 81 ------------------------------------------------------------------------ 82 . 83 84 format ADDRESSREWRITING = 85 @<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 86 $mailer, $input, $output 87 . 88 89 # SUBROUTINE DEFINITIONS: 90 # Parse output of sendmail name resolution 91 92 sub parseaddress 93 { 94 local($mailer, $intfile) = @_; # store intermediate-file name 95 local($input, $output); # no conflict with global var. 96 97 open(INTFILE, $intfile) || die "Exiting, can't open $intfile\n"; 98 99 $~ = 'ADDRESSREWRITING'; 100 while () { 101 if (/^ADDRESS TEST MODE|^Enter /) { next; } # skip 102 chop; 103 if (/^>/) { # lines of interest 104 if ($output) { # contains output addr 105 $output =~ s/^.*returns: *//; # remove prefix 106 $output =~ s/[ "]//g; # no spaces, no quotes 107 write; # mailer: input=>output 108 } 109 s/>.*input: *//; s/[ "]//g; # isolate address 110 $input = $_; # save input address 111 } 112 $output = $_; # save previous line 113 } 114 close(INTFILE); 115 } 116 117 sub address_processing { 118 ($type) = @_; # either "Sender" or "Recipient" 119 $~ = 'STDOUT'; # back to standard output 120 printf("\n%s-address rewriting:\n", $type); 121 $- = 0; # force header display 122 $^ = 'ADDRESSREWRITING_TOP'; # new header format 123 write; # write header 124 foreach $mailer (keys %mailers) { # for each mailer 125 next if $mailer eq "error"; # ignore error mailer 126 if (($recvrules{$mailer} == 0) || ($sendrules{$mailer} == 0)) { 127 print STDERR "Ignoring $mailer: Can't locate rule set.\n"; 128 next; 129 } 130 open(SENDMAIL, 131 "|/usr/lib/sendmail -bt -C$config_file -oQ$queue_dir >$tmpfile") 132 || die "Can't run /usr/lib/sendmail program.\n"; 133 open(ADDRESSFILE, $addr_file) || die "Can't open $addr_file"; 134 while () { 135 next if /^$/; # skip blank lines 136 $address = $_; # save address 137 print SENDMAIL "2,$recvrules{$mailer},4 $address\n" 138 if $type eq "Recipient"; 139 print SENDMAIL "1,$sendrules{$mailer},4 $address\n" 140 if $type eq "Sender"; 141 } 142 close(SENDMAIL); close(ADDRESSFILE); # close, saving data 143 &parseaddress ($mailer, $tmpfile); # display result 144 } 145 } 146 147 # MAIN PROGRAM starts here: 148 149 # Prefix each line of address file with "0 " so 150 # sendmail will process the addresses with rule set zero: 151 open(ADDRESSFILE, $addr_file) 152 || die "Can't open $addr_file for reading\n"; 153 open(TMPFILE, ">$tmpfile") || die "Can't open $tmpfile for writing\n"; 154 while () { 155 print TMPFILE "0 $_" unless $_ eq "\n"; # skip blank lines 156 } 157 close(ADDRESSFILE); close(TMPFILE); 158 159 open(SENDMAIL, # open to read from temp file containing "0 addr" lines. 160 "/usr/lib/sendmail -bt -C$config_file -oQ$queue_dir < $tmpfile|") || 161 die "Can't exec /usr/lib/sendmail program...\n"; 162 163 # Get the mailers used from the Rule 0 tests and populate the 164 # the mailers associative array keyed by mailer, value arbitrary 165 $- = 0; # force header display 166 $^ = 'MAILERDEFINITIONS_TOP'; # header format 167 write; # display header 168 $~ = 'MAILERDEFINITIONS'; # data format 169 while () { # Get next line (into $_) 170 chop; # remove the newline from $_ 171 if (/^ADDRESS TEST MODE|^Enter /) { next; } # skip prompts 172 if (/^>/) { # only process these lines 173 if ($prevline) { # prev line had mailer defn. 174 $prevline =~ s/^.*returns: *//; # remove prefix text 175 $prevline =~ s/[ "]//g; # no spaces, no quotes 176 # $prevline may contain: $#mailer$@dest_host$:dest_user 177 @t = split(/\$/, $prevline); # divide into fields at "$" 178 $mailer = $dest_user = $dest_host = "XXX"; # defaults 179 for ($i = 1; $i <= $#t; $i++) { # for all fields 180 if ($t[$i] =~ /^#(.*)/) { # was $#mailer 181 $mailer = $1; 182 } elsif ($t[$i] =~ /^@(.*)/) { # was $@dest_host 183 $dest_host = $1; 184 } elsif ($t[$i] =~ /^:(.*)/) { # was $:dest_user 185 $dest_user = $1; 186 } 187 } 188 write; # display formatted report 189 if ($mailer ne "XXX") { 190 $mailers{$mailer} = 1; # add to list of mailers 191 } 192 } # no previous line means current line has input address 193 s/>.*input: *//; s/[ "]//g; # isolate address 194 $input = $_; # save input address 195 } # end of if (/^>) 196 $prevline = $_; # store previous line 197 } 198 close(SENDMAIL); 199 200 # Get delivery-agent specific sender and recipient rule-set numbers: 201 # Typical input line: Mether, P=[IPC], F=msDFMueCX, S=11, R=21, A=IPC $h 202 open(CONFIGFILE, "grep ^M $config_file|"); # mailer defn. lines 203 while () 204 { 205 ($mailer) = /^M(\w+),.*$/; # get mailer name 206 ($sendrule) = /^.*S=(\d+),.*$/; # get sender rule number 207 ($recvrule) = /^.*R=(\d+),.*$/; # and recipient rule number 208 $sendrules{$mailer} = $sendrule; # sender and recipient rules 209 $recvrules{$mailer} = $recvrule; # keyed by mailer name 210 } 211 close(CONFIGFILE); 212 213 # Display recipient-address processing: 214 &address_processing(Recipient); 215 216 # Display sender-address processing: 217 &address_processing(Sender); 218 219 unlink($tmpfile); # done so clean up B. General command-line format for invoking chkaddrs: chkaddrs [ -a addr-file ] [ -c config-file ] [ -q queuedir ] C. Sample input address file: beccat beccat@yang kolstad@bsdi.com D. Report generated by chkaddrs on a ``client'' machine using the addresses shown in Part C: # chkaddrs -a addressformats This system: yin Current directory: /usr/local/scripts Configuration file: /etc/sendmail.cf Address file: threeaddr Queue directory: /usr/spool/mqueue Delivery Input Dest Dest Agent Address Host User ------------------------------------------------------------------------ local beccat XXX beccat local beccat@yang XXX beccat ddn kolstad@bsdi.com bsdi.com kolstad<@bsdi.com> Recipient-address rewriting: Delivery Input Output Agent Address Address ------------------------------------------------------------------------ ddn beccat beccat@magicats.org ddn beccat@yang beccat@yang.magicats.org ddn kolstad@bsdi.com kolstad@bsdi.com local beccat beccat local beccat@yang beccat@yang local kolstad@bsdi.com kolstad@bsdi.com Sender-address rewriting: Delivery Input Output Agent Address Address ------------------------------------------------------------------------ ddn beccat beccat@magicats.org ddn beccat@yang beccat@yang.magicats.org ddn kolstad@bsdi.com kolstad@bsdi.com local beccat beccat local beccat@yang beccat@yang local kolstad@bsdi.com kolstad@bsdi.com # [] ------------------------------------------------------------------------------- Copyright © 1995 The McGraw-Hill Companies, Inc. All Rights Reserved. Edited by Becca Thomas / Online Editor / UnixWorld Online / beccat@wcmh.com [Go to Contents] [Search Editorial] Last Modified: Tuesday, 22-Aug-95 16:18:51 PDT