#! /bin/bash

if (($# == 0)) ; then
  echo "Script to test DNS records for consistency."
  echo "If given a numeric IP address, check for strong F(R()) consistency."
  echo "If given a hostname, check for strong F(R((F())) consistency."
  echo "Usage:  $0 host [...]"
  echo "Options include:"
  echo "  -MX   look up MX records for hostnames that follow."
  echo "  -A    look up A records for hostnames that follow."
  echo "  -AAAA look up AAAAA records for hostnames that follow."
  echo "  -A+   look up both A and AAAAA records [default]."
  exit
fi

digMode="A AAAA"

# Call as: a_records server host_ID modeSet
# The server can be "--" to use the default DNS server
# The host_ID can be a numeric IP address or a hostname
# If numeric, we just return it.  Trivial.
# Otherwise, we return ALL the IP addresses associated
# with that name.
# The modeSet can be one of: "A" "AAAA" "MX" "NS"
# or a combination such as "A AAAA"
function a_records() {
  local server="$1" host="$2" modeSet="$3" srv=""
  : ${server:=--}
  if test "_$server" != "_--" ; then
    srv="@$server"
  fi

  if test -z "$modeSet" ; then modeSet="A AAAA"; fi
  for oneMode in $modeSet ; do
    local num="" cname="" line
    while read line ; do
      set xx $line ; shift
      if test "_$4" = "_A" -o "_$4" = "_AAAA" ; then
        num="$5"
        echo $num
        continue
      fi
      if test "_$4" = "_MX" ; then
        mxname="$6"
        #xx 1>&2 echo "Chasing MX $mxname"
        a_records "$server" $mxname "A AAAA"
        continue
      fi
      if test "_$4" = "_NS" ; then
        nsname="$5"
        #xx 1>&2 echo "Chasing NS $nsname"
        a_records "$server" $nsname "A AAAA"
        continue
      fi
      if test "_$4" = "_CNAME" ; then
        cname=$5
        # dig does the chasing for us:
        #!!! 1>&2 echo "NOT Chasing CNAME $cname"
        continue
      fi
      1>&2 echo "Unexpected response '$4' from forward lookup on '$host'"
      exit 2
    done < <(dig +noall +answer $srv "$host" $oneMode)
  done # loop over modes (as in "A AAAA")
}

allArgsOK=1
for host in "$@" ; do
# convert to numeric form:
  case $host in
    -a|-A) digMode=A; continue ;;
    -a+|-A+) digMode="A AAAA"; continue ;;
    -aaaa|-AAAA) digMode="AAAA"; continue ;;
    -m|-M|-mx|-MX) digMode=MX; continue ;;
    -*) 1>&2 echo "Unrecognized option: '$host'" ; exit 1;;
  esac

  is_numeric=""
  if test -z "${host%%[0-9.]*}" ; then
    nums=$host  # already numeric
    is_numeric=1
    showHost=''
  else
    nums=$( a_records "--" "$host" "$digMode" )
    showHost="$host ==> "
  fi
  if test -z "$nums" ; then
    1>&2 echo "Failed:  No A records:  ${showHost}()"
    continue
  fi

  if test -z "$modeSet" ; then modeSet="A AAAA"; fi

  for num in $nums ; do
    allNamesOK=""
    names=$( dig +short -x $num )
    if test -z "$names" ; then
      1>&2 echo "Failed:  No PTR records:  ${showHost}$num --> ()"
      allArgsOK=""
    else
      allNamesOK=1
    fi
    for name in $names ; do
      addrs=""
      anyAddrMatch=""
      if test -n "$name" ; then
        for oneMode in $modeSet ; do
          addrs="$addrs $(dig +short $name $oneMode)"
        done
        for addr in $addrs; do
          if test "_$addr" = "_$num" ; then
            echo "name OK:  ${showHost}$num --> $name ==> $addr"
            anyAddrMatch=1
            break;
          fi
        done
      fi

      if test -z "$addrs" ; then
        addrs='()'
      fi

      if test -z "$anyAddrMatch" ; then
        1>&2 echo "Failed:  ${showHost}$num --> $name ==> $addrs "
        allArgsOK=""
        allNamesOK=""
        break
      fi
    done
  done

  if test -n "$allNamesOK" ; then
    if test -n "$is_numeric" ; then
      1>&2 echo "Strong F(R()) consistency: $host"
    else
      1>&2 echo "Strong F(R(F())) consistency: $host $digMode"
    fi
  fi

done # loop over hosts

if test -z "$allArgsOK" ; then exit 1; fi
exit 0
