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.comD. 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 # []