Commit | Line | Data |
---|---|---|
059ec3d9 | 1 | #! /bin/sh |
059ec3d9 | 2 | |
0a49a7a4 | 3 | # Copyright (c) University of Cambridge, 1995 - 2007 |
059ec3d9 PH |
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 | ||
22 | args="$@" | |
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 | ||
27 | if [ "CONFIGURE_FILE_USE_NODE" = "yes" ]; then | |
28 | hostsuffix=.`uname -n` | |
29 | fi | |
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 | ||
36 | set `awk -F: '{ for (i = 1; i <= NF; i++) print $i }' <<End | |
37 | CONFIGURE_FILE | |
38 | End | |
39 | ` | |
40 | while [ "$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 | |
47 | done | |
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 | ||
0a49a7a4 | 54 | exim_path=`perl -ne 'chop;if (/^\s*exim_path\s*=\s*(.*)/){print "$1\n";last;}' $config` |
059ec3d9 PH |
55 | if 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 | ||
64 | PERL_COMMAND - $exim_path $args <<'End' | |
65 | ||
4d3d955f | 66 | BEGIN { pop @INC if $INC[-1] eq '.' }; |
059ec3d9 | 67 | use FileHandle; |
983da878 | 68 | use File::Basename; |
059ec3d9 PH |
69 | use IPC::Open2; |
70 | ||
983da878 HSHR |
71 | if ($ARGV[0] eq '--version') { |
72 | print basename($0) . ": $0\n", | |
73 | "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", | |
02721dcd | 74 | "perl(runtime): $]\n"; |
983da878 HSHR |
75 | exit 0; |
76 | } | |
77 | ||
059ec3d9 PH |
78 | if (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 | ||
112 | if ($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 = ""; | |
123 | for ($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 = ""; | |
129 | if ($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"); | |
138 | print OUT "HELO [$host]\r\n"; | |
139 | print OUT "MAIL FROM:<$sender>\r\n"; | |
140 | print OUT "RCPT TO:<$recipient>\r\n"; | |
141 | print OUT "QUIT\r\n"; | |
142 | close OUT; | |
143 | ||
144 | # Read the output, ignoring anything but the SMTP response to the RCPT | |
145 | # command. | |
146 | ||
147 | $count = 0; | |
148 | $reply = ""; | |
149 | ||
150 | while (<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 | ||
179 | waitpid $pid, 0; | |
180 | ||
181 | End |