#!/bin/bash set -e . settings id=$(date +%s)_$$ fail () { printf >&2 "%s\n" "modrelays-probe: error: $1" exit 16 } record-probing () { # implicitly uses newsgroup, id, domain # caller must "local td", which will be set local probeid=$1 probeid="$domain,${probeid//[^-=.,_0-9A-Za-z]/%/},$id" case $probeid in .*|*/*) fail "yikes, sanitisation bug!" ;; esac td="probes/probes/$probeid" mkdir -p $td } record-outcome () { local probeid=$1 local outcome=$2 local message=$3 local td record-probing "$probeid" printf "%s\n" >"$td"/"$outcome" "$message" } record-success () { record-outcome "$1" ok ''; } record-tempfail () { record-outcome "$1" tempfail "$2"; } record-permfail () { record-outcome "$1" permfail "$2"; } probe-addr () { local mx=$1 local addr=$2 local td record-probing "mx=$mx,addr=$addr" set +e swaks --to "${GROUP//./-/}@$domain" \ --server $addr \ --tls-optional-strict \ --header 'Subject: test modrelays probe test' \ --header \ "X-WebSTUMP-Relay-Probe: $GROUP $id $domain $mx $addr" \ -n >$td/swaks.log 2>$td/swaks.err rc=$? set -e case $rc in 0) return ;; # record-success done by receiver esac local permfail='' local rhs local prefix local expect_no_5xx='initial connection' exec <$td/swaks.log while read prefix rhs; do case "$prefix" in '<'*) case "$rhs" in 5*) if [ "x$expect_no_5xx" != x ] && \ [ "x$permfail" = x ]; then permfail="$rhs ($expect_no_5xx)" fi ;; esac ;; *'>') case "$rhs" in EHLO*|STARTTLS*) expect_no_5xx='' ;; *) expect_no_5xx="after $rhs" ;; esac ;; *) esac done if [ "x$permfail" = x ]; then record-tempfail "mx=$mx,addr=$addr" "see swaks.log / swaks.err" else record-permfail "mx=$mx,addr=$addr" "$permfail" fi } probe-domain () { local domain=$1 local td record-probing dns set +e adnshost -Fi -Tn +Do +Dt -t mx $domain >$td/dns rc=$? set -e case $rc in 0) # have a list of MX's exec <$td/dns local pref local mx local statustype local rhs while read pref mx statustype statustypenum rhs; do case $statustypenum in 0) # have a list of relays case $rhs in *" ( "*")") ;; *) record-permfail "mx=$mx" \ "dns format $rhs" continue ;; esac rhs=${rhs##* (} rhs=${rhs% )} local addr for addr in $rhs; do case $addr in INET|INET6) continue ;; esac probe-addr $mx $addr done ;; [123]) # temporary errors record-tempfail "mx=$mx" \ "dns $rc $statustype $rhs" ;; *) # yikes record-permfail "mx=$mx" \ "dns $rc $statustype $rhs" ;; esac done record-success dns return ;; 6) # permfail, try A set +e adnshost -Fi -Tn +Do +Dt -t a $domain >$td/dns rc=$? set -e ;; esac case $rc in 0) # have a list of A's (dealt with MXs above) exec <$td/dns local addr while read addr; do probe-addr 'NONE' $addr done record-success dns return ;; [123]) local emsg read <$td/dns emsg record-tempfail dns "dns $emsg" ;; *) local emsg read <$td/dns emsg record-permfail dns "dns $emsg" ;; esac } no_args () { case $1 in 0) return ;; *) fail "no arguments to $mode allowed" ;; esac } mode_all () { no_args $# for domain in $MODRELAYS; do probe-domain $domain done } mode_auto () { no_args $# xxx do something to cause sleeping mode_all } mode_domain () { for domain in "$@"; do probe-domain $domain done } mode=$1; shift||: "mode_$mode" "$@"