Skip to content

Instantly share code, notes, and snippets.

@gbot
Last active October 10, 2022 04:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gbot/53c9d5b4d79f75790443db5f0b665c91 to your computer and use it in GitHub Desktop.
Save gbot/53c9d5b4d79f75790443db5f0b665c91 to your computer and use it in GitHub Desktop.
Use 'nslookup' to query one or more domains, using a pre-defined list of nameservers
#!/bin/bash
# what's this all about then?
description="Use 'nslookup' to query one or more domains, using a pre-defined list of nameservers"
# set nameservers as associative array
declare -A nameservers
nameservers['AdGuard DNS']='94.140.14.14'
nameservers['Alternate DNS']='76.76.19.19'
nameservers['CloudFlare']='1.1.1.1'
nameservers['Control D']='76.76.2.0'
nameservers['Digital Ocean SG']='139.59.219.245'
nameservers['Google']='8.8.8.8'
nameservers['ICONZ']='210.48.77.68'
nameservers['OpenDNS']='208.67.222.222'
nameservers['Orcon NZ']='121.98.0.1'
nameservers['Quad9']='9.9.9.9'
nameservers['SiteHost NZ']='223.165.64.97'
nameservers['Telstra AU']='139.130.4.4'
# nameservers['Voyager NZ']='103.21.194.19'
nameservers['Yandex Basic DNS']='77.88.8.8'
nameservers['Yandex Safe DNS']='77.88.8.88'
# misc. vars / settings / default
lookup_type="A"
this_script=$(basename "${BASH_SOURCE[0]}")
# colors, cos we like colours!
clr_default="\e[0m"
clr_white="\e[1m"
clr_dim="\e[2m"
clr_inverse="\e[7m"
clr_red="\e[31m"
clr_green="\e[32m"
clr_yellow="\e[33m"
clr_cyan="\e[36m"
# help text
help() {
echo "$description"
echo -e "${clr_yellow}Either pass in a list of domains: \n\t${clr_default}${this_script} domain.com\n\t${this_script} 'domain.com domain2.com domain3.com'\n\t${this_script} -d domain.com \n\t${this_script} --domains='domain.com domain2.com'\e[0m"
echo -e "${clr_yellow}Or export a ENV variable 'domains': \n\t${clr_default}export domains=\"domain.com domain2.com\"\e[0m"
echo -e "${clr_yellow}OPTIONS:${clr_default}"
echo -e "\t -d | --domains \tDomain(s) to query"
echo -e "\t -t | --type \t\tSet query type [ A = default | AAAA | NS | MX ]"
echo -e "\t -e | --expected \tHighlight result RED if doesn't match 'expected'"
echo -e "\t -h | --help \t\tDisplay this help"
echo -e "${clr_yellow}EXAMPLES:${clr_default}"
echo -e "\t${this_script} -t NS domain.com"
echo -e "\t${this_script} -d domain.com -e 123.123.123.123"
echo -e "\t${this_script} --domains='domain.com domain2.com' --type=NS"
echo -e "${clr_yellow}NOTES:${clr_default} \n\tWhen passing domains without a preceding option (-d | --domains), it must be the last argument."
echo
exit
}
# write error to STDERR and exit
die() {
echo -e "${clr_red}ERROR:${clr_default} $1 ${clr_dim}${2}${clr_default}" >&2; exit 2;
}
# getopts required arg fallback
option_needs_arg() {
if [ -z "$OPTARG" ] ; then
die "Missing argument for --$OPT" "[ERR1:long-option-missing-arg]"
fi
}
while getopts :d:ht:e:-: OPT; do
# support long options: https://stackoverflow.com/a/28466267/519360
if [ "$OPT" = "-" ]; then # long option: reformulate OPT and OPTARG
OPT="${OPTARG%%=*}" # extract long option name
OPTARG="${OPTARG#$OPT}" # extract long option argument (may be empty)
OPTARG="${OPTARG#=}" # if long option argument, remove assigning "="
fi
case "$OPT" in
h | help )
help;;
e | expected )
option_needs_arg
expected_result="$OPTARG";;
t | type )
option_needs_arg
lookup_type="${OPTARG^^}";; # convert to uppercase "^^"
d | domains )
option_needs_arg
domains="$OPTARG";;
: )
die "Missing argument for -$OPTARG" "[ERR2:missing-arg]";;
??* )
die "Unrecognised option --$OPT" "[ERR3:bad-long-option]";;
? )
die "Unrecognised option -$OPTARG" "[ERR4:bad-short-option]";;
esac
done
shift $((OPTIND-1)) # remove parsed options and args from $@ list
# if there is a remaining argument, consider it as a list of domains to query
if [[ $1 != '' ]]; then
domains="${1}"
elif [[ $domains == '' ]]; then
echo -e "\e[31mNo domains to query!\e[0m"
help
fi
# check 'lookup_type' for accepted values
case "$lookup_type" in
NS | A | AAAA | MX ) ;;
* ) die "Unsupported lookup type";;
esac
do_nslookup_loop() {
for this_domain in $domains; do
echo -e "${clr_cyan}+ $this_domain ...${clr_default}"
echo -e " ${clr_yellow}Nameserver:\t\t\t\t\tResult:${clr_default}"
for key in "${!nameservers[@]}"; do
# run nslookup
local nslookup_result=$( nslookup -type="${lookup_type}" $this_domain ${nameservers[$key]} )
# check for failure
local failed="$( echo "$nslookup_result" | egrep "NXDOMAIN|No answer" )"
if [[ $failed != '' ]]; then
local result_output="NOT FOUND!"
else
# check if this is a canonical lookup
local canonical="$( echo "$nslookup_result" | grep "canonical name" | cut -f2 )"
# format result_output according to lookup type, i.e. NS, A
if [[ $lookup_type == "NS" ]]; then
local result_output=$( echo "${nslookup_result}" | grep "nameserver =" | cut -f2 -d'=' | sed 's/^ //' | sed 's/.$//' | sort )
elif [[ $lookup_type == "MX" ]]; then
local result_output="$( echo "$nslookup_result" | tail -4 | grep "mail exchanger =" | cut -f2 -d'=' | cut -f3 -d' ' | sed 's/.$//' | sort )"
else # default for 'A' or 'AAAA' lookup
local result_output="$( echo "$nslookup_result" | tail -3 | grep "Address:" | cut -f2 -d' ' )"
fi
fi
# output
echo -e -n "${clr_white} ${key} ${clr_dim}[${nameservers[$key]}]${clr_default}"
# hacky method faking column layout (pad spaces based on length of col 1 )
loops="$(( 42 - (( ${#key} + ${#nameservers[$key]} )) ))"
for i in $( seq 0 $loops ); do echo -n " "; done
if [[ $result_output == *"$expected_result"* ]] || [[ $expected_result == '' ]] && [[ $failed == '' ]]; then
echo -en "${clr_green}"
else
echo -en "${clr_red}"
fi
echo -en ${result_output}${clr_default}
if [[ $canonical != '' ]] && [[ $failed == '' ]] ; then
canonical="CNAME: $( echo $canonical | cut -f2 -d '=' | sed 's/^ //' )"
echo -en " ${clr_dim}${canonical}${clr_default}"
fi
echo # newline
done
done
}
# run the nslookup loop
do_nslookup_loop
# tidy up
unset domains
unset nameservers
exit
@gbot
Copy link
Author

gbot commented Oct 10, 2022

Added support for A, AAAA, NS and MX query types

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment