1#! /bin/sh
2
3# Copyright (c) University of Cambridge, 1995 - 2007
4# See the file NOTICE for conditions of use and distribution.
5
6# Except when they appear in comments, the following placeholders in this
7# source are replaced when it is turned into a runnable script:
8#
9# CONFIGURE_FILE_USE_NODE
10# CONFIGURE_FILE
11# BIN_DIRECTORY
12# PERL_COMMAND
13
14# PROCESSED_FLAG
15
16# A shell+perl wrapper script to run an automated -bh test to check out
17# ACLs for incoming addresses.
18
19# Save the shell arguments because we are going to need the shell variables
20# while sorting out the configuration file.
21
22args="$@"
23
24# See if this installation is using the esoteric "USE_NODE" feature of Exim,
25# in which it uses the host's name as a suffix for the configuration file name.
26
27if [ "CONFIGURE_FILE_USE_NODE" = "yes" ]; then
28  hostsuffix=.`uname -n`
29fi
30
31# Now find the configuration file name. This has got complicated because
32# CONFIGURE_FILE may now be a list of files. The one that is used is the first
33# one that exists. Mimic the code in readconf.c by testing first for the
34# suffixed file in each case.
35
36set `awk -F: '{ for (i = 1; i <= NF; i++) print $i }' <<End
37CONFIGURE_FILE
38End
39`
40while [ "$config" = "" -a $# -gt 0 ] ; do
41  if [ -f "$1$hostsuffix" ] ; then
42    config="$1$hostsuffix"
43  elif [ -f "$1" ] ; then
44    config="$1"
45  fi
46  shift
47done
48
49# Search for an exim_path setting in the configure file; otherwise use the bin
50# directory. BEWARE: a tab character is needed in the command below. It has had
51# a nasty tendency to get lost in the past. Use a variable to hold a space and
52# a tab to keep the tab in one place.
53
54exim_path=`perl -ne 'chop;if (/^\s*exim_path\s*=\s*(.*)/){print "$1\n";last;}' $config`
55if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim; fi
56
57
58#########################################################################
59
60
61# Now run the perl script, passing in the Exim path and the arguments given
62# to the overall script.
63
64PERL_COMMAND - $exim_path $args <<'End'
65
66BEGIN { pop @INC if $INC[-1] eq '.' };
67use FileHandle;
68use File::Basename;
69use IPC::Open2;
70
71if ($ARGV[0] eq '--version') {
72    print basename($0) . ": $0\n",
73          "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n",
74          "perl(runtime): $]\n";
75          exit 0;
76}
77
78if (scalar(@ARGV) < 3)
79  {
80  print "Usage: exim_checkaccess <IP address> <email address> [exim options]\n";
81  exit(1);
82  }
83
84$exim_path = $ARGV[0];          # Set up by the calling shell script
85$host      = $ARGV[1];          # Mandatory original first argument
86$recipient = $ARGV[2];          # Mandatory original second argument
87
88$c4 = qr/2 (?:[0-4]\d | 5[0-5]) | 1\d\d | \d{1,2}/x;  # IPv4 component
89$a4 = qr/^$c4\.$c4\.$c4\.$c4$/;                       # IPv4 address
90
91$c6 = qr/[0-9a-f]{1,4}/i;                             # IPv6 component
92
93# Split the various formats of IPv6 addresses into several cases. I don't
94# think I can graft regex that matches all of them without using alternatives.
95
96# 1. Starts with :: followed by up to 7 components
97
98$a6_0 = qr/^::(?:$c6:){0,6}$c6$/x;
99
100# 2. 8 non-empty components
101
102$a6_1 = qr/^(?:$c6:){7}$c6$/x;
103
104# 3. This is the cunning one. Up to 7 components, one (and only one) of which
105# can be empty. We use 0 to cause a failure when we've already matched
106# an empty component and may be hitting other. This has to fail, because we
107# know we've just failed to match a component. We also do a final check to
108# ensure that there has been an empty component.
109
110$a6_2 = qr/^(?: (?: $c6 | (?(1)0 | () ) ) : ){1,7}$c6 $ (?(1)|.)/x;
111
112if ($host !~ /$a4 | $a6_0 | $a6_1 | $a6_2/x)
113  {
114  print "** Invalid IP address \"$host\"\n";
115  print "Usage: exim_checkaccess <IP address> <email address> [exim options]\n";
116  exit(1);
117  }
118
119# Build any remaining original arguments into a string for passing over
120# as Exim options.
121
122$opt = "";
123for ($i = 3; $i < scalar(@ARGV); $i++) { $opt .= "$ARGV[$i] "; }
124
125# If the string contains "-f xxxx", extract that as the sender. Otherwise
126# the sender is <>.
127
128$sender    = "";
129if ($opt =~ /(?:^|\s)-f\s+(\S+|"[^"]*")/)
130  {
131  $sender = $1;
132  $opt = $` . $';
133  }
134
135# Run a -bh test in Exim, passing the test data
136
137$pid = open2(*IN, *OUT, "$exim_path -bh $host $opt 2>/dev/null");
138print OUT "HELO [$host]\r\n";
139print OUT "MAIL FROM:<$sender>\r\n";
140print OUT "RCPT TO:<$recipient>\r\n";
141print OUT "QUIT\r\n";
142close OUT;
143
144# Read the output, ignoring anything but the SMTP response to the RCPT
145# command.
146
147$count = 0;
148$reply = "";
149
150while (<IN>)
151  {
152  next if !/^\d\d\d/;
153  $reply .= $_;
154  next if /^\d\d\d\-/;
155
156  if (++$count != 4)
157    {
158    $reply = "";
159    next;
160    }
161
162  # We have the response we want. Interpret it.
163
164  if ($reply =~ /^2\d\d/)
165    {
166    print "Accepted\n";
167    }
168  else
169    {
170    print "Rejected:\n";
171    $reply =~ s/\n(.)/\n  $1/g;
172    print "  $reply";
173    }
174  last;
175  }
176
177# Reap the child process
178
179waitpid $pid, 0;
180
181End
182