#!/bin/sh # checkmail 1.0.12 (1999-May-16-Sun) # Adam M. Costello # # Given a list of email addresses on stdin, one per line, checkmail # checks the mail status for each one (following forwarding chains, but # not trees), and prints the results to stdout in the format # # + OriginalEmailAddress # MailStatus # # MailStatus may be more than one line, but each will begin with two # spaces. # # There may also be various warning messages sent to stderr, composed of # lines beginning with "! ". # # checkmail depends on awk, egrep, finger, grep, hostname, nslookup, # ping (or fping, see below), sed, sort, telnet, and tr being in PATH. # # With the -q option, the MailStatus is not collected, saving time. # Time limits (in seconds): fingerlimit=20 telnetlimit=20 # Ping method: #ping=ping # Use this if "ping " works. ping=ping_via_fping # Otherwise use this (requires fping). # My hostname: myhostname=`hostname` myhostname=` echo "$myhostname" | nslookup 2>&- | awk '/^Name:/ {print $2}' ` if [ "$1" = -q ]; then quick=yes shift fi [ $# -eq 0 ] || { echo "usage:" >&2 echo "$0 [ -q ]" >&2 exit 1 } debug() { echo "# $1" >&2 # Comment this out to turn off debugging. : } ping_via_fping() { fping -r${2-20} -t1000 $1 } # timelimit [ ... ] # # Runs the command in the background, killing it after seconds. timelimit() { limit=$1 shift "$@" & pid=$! { sleep $limit; kill $pid 2>&-; } >&- & } # followaddr [ ... ] # # Checks whether mail to @ gets forwarded. If so, follows # the forward chain to the final address, which is printed to stdout. # Additional arguments containing all addresses that have been followed # (including the one being passed this time) are used to detect loops. # # If there is a problem, it will print a short error message to stdout # and return unsuccessfully. followaddr() { user=$1 host=$2 address=$user@$host debug "following address $address" [ $# -le 50 ] || { echo "! Forward chain too long: $*" >&2 return 1 } exchangers=` { echo "set qt=mx"; echo $host; } | nslookup 2>&- | grep 'mail exchanger' | sort +3 -4n | awk '/mail exchanger/ { printf "%s ", $NF }' ` [ -n "$exchangers" ] || { echo "! No mail exchanger for $host, will use $host" >&2 exchangers=$host } debug "exchangers are $exchangers" for exchanger in $exchangers; do if $ping $exchanger 5 >&- 2>&-; then response=` { echo "HELO $myhostname" echo "EXPN $user@$exchanger" echo "EXPN $user" echo "VRFY $user@$exchanger" echo "VRFY $user" echo quit } | timelimit $telnetlimit telnet $exchanger 25 2>&- ` forwards=`sed -e '1,/^.[05]. / d' -e '/^25[01]/ !d' \ -e 's/.*<\([^>]*\)>.*/\1/' -e 's/^[ ]*//' \ -e 's/[ ].*//' -e 's/^\\\\//' << EOF $response EOF ` if [ -z "$forwards" ]; then echo "! Cannot expand $user at $exchanger" >&2 [ -z "$response" ] || sed -e 's/^/! > /' >&2 << EOF $response EOF nexthop= else nexthop=`sed -e '/|/ d' -e q << EOF $forwards EOF ` break fi else echo "! Cannot reach $exchanger" >&2 nexthop= fi done debug "nexthop is <$nexthop>" if [ "$nexthop" = "$user" -o "$nexthop" = "" ]; then echo $user@$exchanger return 0 fi shift 2 history=$* oldIFS=$IFS IFS=@$oldIFS set -- $nexthop IFS=$oldIFS nextuser=$1 nexthost=$2 case $nexthost in "") nexthost=$exchanger debug "expanded $nexthop to $nextuser@$nexthost" ;; *.*) ;; *) rawnexthost=$nexthost nexthost=` { echo "set domain=$host"; echo "$nexthost"; } | nslookup 2>&- | awk '/^Name:/ {print $2}' ` [ -n "$nexthost" ] || { echo "! Unable to resolve host $rawnexthost" >&2 return 1 } debug "resolved $rawnexthost to $nexthost" ;; esac nexthop=$nextuser@$nexthost if [ "$nexthop" = "$user@$exchanger" -o "$nexthop" = "$address" ]; then echo $user@$exchanger return 0 fi for addr in $history; do [ "$nexthop" != "$addr" ] || { echo "! Mail loop detected: $history $nexthop" >&2 return 1 } done followaddr $nextuser $nexthost $history $nexthop } # fetchmailstatus
# # Returns lines, each beginning with two spaces, containing mail status # information about
, which must have the form user@host, and # should be a final address (as returned by followaddr). fetchmailstatus() { address=$1 oldIFS=$IFS IFS=@$oldIFS set -- $address IFS=$oldIFS user=$1 timelimit $fingerlimit finger -l $address 2>&- | tr -d '\015' | awk ' /[^a-zA-Z]'$user'[^a-zA-Z]/ { count = 1; } count > 10 { exit; } count >= 1 { if (match($0, /[Mm]ail|[Uu]nread/)) print " " $0; ++count; } ' } # Main loop. while read address; do oldIFS=$IFS IFS=@$oldIFS set -- $address IFS=$oldIFS [ $# -eq 2 ] || { echo "! Bad address (wrong number of @ signs): $address" >&2 continue } if finaladdr=`followaddr $1 $2 $1@$2`; then debug "address is $address" debug "finaladdr is $finaladdr" echo "+ $address <$finaladdr>" [ $quick ] || { mailstatus=`fetchmailstatus $finaladdr` [ -z "$mailstatus" ] || echo "$mailstatus" } else echo "+ $address ERROR" fi echo "" done