Upcoming Events

Cloud Connect
Santa Clara
Feb 13-16, 2012

Cloud Connect brings together the entire cloud eco-system to better understand the transformation we're experiencing and promises to be the defining event of the cloud computing industry. Learn about the latest cloud technologies and platforms from thought leaders in Cloud Connect’s comprehensive conference.

Register Now!

More Events »

Subscribe to Newsletter

  • Keep up with all of the latest news and analysis on the fast-moving IT industry with Network Computing newsletters.
Sign Up


Untying the Sendmail Gordian Knot: 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 (<INTFILE>) {
101              if (/^ADDRESS TEST MODE|^Enter <ruleset>/) { 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 (<ADDRESSFILE>) {
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 (<ADDRESSFILE>) {
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 (<SENDMAIL>) {                        # Get next line (into $_)
170      chop;                                   # remove the newline from $_
171      if (/^ADDRESS TEST MODE|^Enter <ruleset>/) { 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 (<CONFIGFILE>)
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

# []
Print This Page


e-mail Send as e-mail

Research and Reports

Hypervisor Derby
August 2011

Network Computing: August 2011

TechWeb Careers