chiark / gitweb /
ef7c639bb2b90365b9e532078924438cf7bd2b8b
[modbot-ulm.git] / probes / modrelays-probe
1 #!/bin/bash
2
3 set -e$MODRELAYS_PROBE_SET_X
4
5 MODRELAYS=moderators.isc.org
6 PROBE_TIMEOUT=$(( 20 * 60 ))
7 PROBE_EXPIRE=$(( 32 * 86400 ))
8
9 . ../global-settings
10 . ./settings
11
12 id=$(date +%s)_$$
13 statedir=probes/probes
14 lockfile=$statedir/.lock
15
16 fail () {
17         printf >&2 "%s\n" "modrelays-probe: error: $1"
18         exit 16
19 }
20
21 record-probing () {
22         # implicitly uses newsgroup, id, domain
23         # caller must "local td", which will be set
24         local probeid=$1
25
26         probeid="$domain,${probeid//[^-=.,_0-9A-Za-z]/%/},$id"
27         case $probeid in
28         .*|*/*) fail "yikes, sanitisation bug!" ;;
29         esac
30
31         td="$statedir/$probeid"
32         mkdir -p $td
33 }
34
35 record-probing-start () {
36         record-probing "$@"
37         if ! [ -e "$td/started" ]; then
38                 date -R >"$td/started"
39         fi
40 }
41
42 record-outcome () {
43         local probeid=$1
44         local outcome=$2
45         local message=$3
46         local td
47         record-probing "$probeid"
48         printf "%s\n" >"$td"/"$outcome" "$message"
49 }
50
51 record-success () { record-outcome "$1" ok ''; }
52 record-tempfail () { record-outcome "$1" tempfail "$2"; }
53 record-permfail () { record-outcome "$1" permfail "$2"; }
54
55 probe-addr () {
56         local mx=$1
57         local addr=$2
58
59         local td
60         record-probing-start "mx=$mx,addr=$addr"
61
62         set +e
63         swaks   --to "${GROUP//./-}@$domain" \
64                 --server $addr \
65                 --tls-optional-strict \
66                 --header 'Subject: test modrelays probe test' \
67                 --header \
68         "X-WebSTUMP-Relay-Probe: $GROUP $id $domain $mx $addr" \
69                 -n >$td/swaks.log 2>$td/swaks.err
70         rc=$?
71         set -e
72
73         case $rc in
74         0) return ;; # record-success done by receiver
75         esac
76         local permfail=''
77
78         local rhs
79         local prefix
80         local expect_no_5xx='initial connection'
81         exec 4<$td/swaks.log
82         while read <&4 prefix rhs; do
83                 case "$prefix" in
84                 '<'*)
85                         case "$rhs" in
86                         5*)
87                                 if [ "x$expect_no_5xx" != x ] && \
88                                    [ "x$permfail" = x ]; then
89                                         permfail="$rhs ($expect_no_5xx)"
90                                 fi
91                                 ;;
92                         esac
93                         ;;
94                 *'>')
95                         case "$rhs" in
96                         EHLO*|STARTTLS*) expect_no_5xx='' ;;
97                         *) expect_no_5xx="after $rhs" ;;
98                         esac
99                         ;;
100                 *)
101                 esac
102         done
103
104         if [ "x$permfail" = x ]; then
105                 record-tempfail "mx=$mx,addr=$addr" "see swaks.log / swaks.err"
106         else
107                 record-permfail "mx=$mx,addr=$addr" "$permfail"
108         fi
109 }
110
111 probe-domain () {
112         local domain=$1
113         local td
114         record-probing-start dns
115         
116         set +e
117         adnshost -Fi -Tn +Do +Dt -t mx $domain >$td/dns
118         rc=$?
119         set -e
120
121         case $rc in
122         0)
123                 # have a list of MX's
124                 exec 3<$td/dns
125                 local pref
126                 local mx
127                 local statustype
128                 local rhs
129                 while read <&3 pref mx statustype statustypenum rhs; do
130                         case $statustypenum in
131                         0)
132                                 # have a list of relays
133                                 case $rhs in
134                                 *" ( "*")") ;;
135                                 *)
136                                         record-permfail "mx=$mx" \
137                                                 "dns format $rhs"
138                                         continue
139                                         ;;
140                                 esac
141                                 rhs=${rhs##* (}
142                                 rhs=${rhs% )}
143                                 local addr
144                                 for addr in $rhs; do
145                                         case $addr in
146                                         INET|INET6) continue ;;
147                                         esac
148                                         probe-addr $mx $addr
149                                 done
150                                 ;;
151                         [123])
152                                 # temporary errors
153                                 record-tempfail "mx=$mx" \
154                                         "dns $rc $statustype $rhs"
155                                 ;;
156                         *)
157                                 # yikes
158                                 record-permfail "mx=$mx" \
159                                         "dns $rc $statustype $rhs"
160                                 ;;
161                         esac
162                 done
163                 record-success dns
164                 return
165                 ;;
166         6)
167                 # permfail, try A
168                 set +e
169                 adnshost -Fi -Tn +Do +Dt -t a $domain >$td/dns
170                 rc=$?
171                 set -e
172                 ;;
173         esac
174
175         case $rc in
176         0)
177                 # have a list of A's (dealt with MXs above)
178                 exec 3<$td/dns
179                 local addr
180                 while read <&3 addr; do
181                         probe-addr 'NONE' $addr
182                 done
183                 record-success dns
184                 return
185                 ;;
186         [123])
187                 local emsg
188                 read <$td/dns emsg
189                 record-tempfail dns "dns <no-mx> $emsg"
190                 ;;
191         *)
192                 local emsg
193                 read <$td/dns emsg
194                 record-permfail dns "dns <no-mx> $emsg"
195                 ;;
196         esac
197 }
198
199 no_args () {
200         case $1 in
201         0) return ;;
202         *) fail "no arguments to $mode allowed" ;;
203         esac
204 }
205
206 acquire_lock () {
207         local lock_mode="$1"
208         if [ x"$WEBSTUMP_PROBE_LOCK" = x"$lockfile" ]; then return; fi
209         WEBSTUMP_PROBE_LOCK=$lockfile \
210         exec with-lock-ex $lock_mode "$lockfile" "$0" "$mode" "$@"
211 }
212
213 maybe-report () {
214         local outcome=$1
215
216         if $found_to_report; then return; fi
217         if ! [ -e "$attempt/$outcome" ]; then return; fi
218         found_to_report=true
219
220         read <"$attempt/$outcome" message
221
222         local reported
223         if [ -e "$attempt/reported" ]; then
224                 read <"$attempt/reported" reported
225         fi
226         if [ "x$outcome" = "x$reported" ]; then return; fi
227
228         if [ x"$outcome" = x"ok" ] && [ x"$reported" = x ]; then
229                 echo ok >"$attempt/reported"
230                 return
231         fi
232
233         local info=${attempt##*/}
234         info=${info//,/ }
235
236         delim=`od -N 50 -An -x -w50 </dev/urandom`
237         delim=${delim// /}
238
239         local email="$attempt/.report.$outcome"
240         cat >"$email" <<END
241 To: $ADMIN
242 Subject: mod relay probe $outcome $info
243 Content-Type: multipart/mixed; boundary="$delim"
244 MIME-Version: 1.0
245
246 --$delim
247 Content-Type: text/plain; charset="utf-8"
248 Content-Transfer-Encoding: 7bit
249
250 The moderation relay probe
251   $info
252 END
253
254         if [ -e "$attempt/started" ]; then
255                 local started
256                 read started <"$attempt/started"
257                 cat >>"$email" <<END
258 started at
259   $started
260 END
261         fi
262
263         cat >>"$email" <<END
264 resulted in the outcome
265   $outcome
266 END
267         if [ "x$message" != x ]; then
268                 cat >>"$email" <<END
269 with the message
270   $message
271 END
272         fi
273
274         if [ "x$reported" != x ]; then
275                 cat >>"$email" <<END
276 This is even though previously the outcome seemed to be
277   $reported
278 and this was reported previously.
279 END
280         fi
281
282         cat >>"$email" <<END
283
284 Logs are in
285   $attempt
286 and concatenated to this email.
287
288 END
289
290         local log
291         for log in "$attempt"/*; do
292                 cat >>"$email" <<END
293 --$delim
294 Content-Type: text/plain; charset="utf-8"
295 Content-Disposition: inline; filename="${log##*/}"
296 Content-Description: "${log##*/}"
297 Content-Transfer-Encoding: 8bit
298
299 END
300                 cat >>"$email" <"$log"
301                 echo >>"$email"
302         done
303
304         cat >>"$email" <<END
305 --$delim--
306 END
307
308         /usr/sbin/sendmail -odb -oem -oee -t <"$email"
309         echo "$outcome" >"$attempt"/reported
310 }
311
312 mode_report () {
313         acquire_lock -w "$@"
314
315         local attempt
316         for attempt in $statedir/*; do
317
318                 local now=$(date +%s)
319                 local age=$(stat -c %Y "$attempt")
320                 age=$(( $now - $age ))
321
322                 local found_to_report=false
323                 maybe-report ok
324                 maybe-report permfail
325                 maybe-report tempfail
326
327                 if ! [ -e $attempt/reported ] && \
328                      [ $age -gt $PROBE_TIMEOUT ]; then
329                         echo >"$attempt"/timeout \
330         "Message did not arrive after ${PROBE_TIMEOUT}s"
331                 fi
332
333                 maybe-report timeout
334
335                 if [ -e $attempt/reported ] && \
336                    [ $age -gt $PROBE_EXPIRE ]; then
337                         rm -rf "$attempt"
338                 fi
339         done
340 }
341
342 mode_all () {
343         no_args $#
344         for domain in $MODRELAYS; do
345                 probe-domain $domain
346         done
347 }
348
349 mode_auto () {
350         no_args $#
351         xxx do something to cause sleeping
352         mode_all
353 }
354
355 mode_domain () {
356         for domain in "$@"; do
357                 probe-domain $domain
358         done
359 }
360
361 mode=$1; shift||:
362
363 "mode_$mode" "$@"