-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmanual_dns_lookup.sh
More file actions
executable file
·141 lines (118 loc) · 4.75 KB
/
manual_dns_lookup.sh
File metadata and controls
executable file
·141 lines (118 loc) · 4.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/env bash
# This script is a manual DNS lookup demonstration. It works by querying the
# DNS network from the root resolver servers on down. Obviously, there are
# tools like `host` and `nslookup` that do this better. This is a proof of
# concept / educational script.
# Written by John R., Oct. 2021
############ domainCheckAndPrep()
# The domainCheckAndPrep() function does a few things. First it checks that the
# supplied domain name matches a standard domain name regex. Then it checks if
# the domain already has a trailing . character and appends it if it doesn't
# already have one. Then finally it check's that the domain is terminated by a
# valid TLD.
function domainCheckAndPrep(){
# Validates supplied domain. Thanks ilkkachu for the validation regex!
# https://unix.stackexchange.com/questions/548543/check-valid-subdomain-with-regex-in-bash
valid_dom_regex='^([a-zA-Z0-9](([a-zA-Z0-9-]){0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}\.?'
! [[ $domain =~ $valid_dom_regex ]] &&
echo "Not a valid domain!" &&
exit 1
# If the last character is . then pass otherwise append a . in prep for
# zoneDepth().
[[ "${domain: -1}" = '.' ]] ||
domain="${domain}."
# Accepted List of Top Level Domains (TLDs)
TLDs=( 'com' 'net' 'org' 'info' 'io' 'ac' 'sh' 'me' 'us' 'ws' 'uk' \
'biz' 'mobi' 'tv' 'cc' 'eu' 'ru' 'in' 'it' 'au' )
domainsTLD=$(echo $domain|rev|cut -d . -f 2|rev)
# Loops over TLDs and checks if domainsTLD string matches one of the tld
# strings. If it does then pass, otherwise increment i.
i=0
for tld in ${TLDs[@]}; do
if [[ $domainsTLD = $tld ]]; then
:
else
((i++))
fi
done
# If the value of i equals the number of elements in the TLDs array then
# the domain name must not match any of the tlds and can therefore be
# rejected.
[[ $i -eq ${#TLDs[@]} ]] &&
echo "Not a valid domain!" &&
exit 1
}
############ zoneDepth()
# The zoneDepth() function returns the number of DNS zones deep a particular
# name is. For example, the domain bluesquare23.sh. is two DNS zones deep
# because there are two subdomains under the root domain .
#
# Zone 2 Zone 1
# \ /
# bluesquare23.sh.
# \
# Zone 0 - root domain
#
# This value can be used to determine how many times you have to ask a
# different DNS name server for information before you get to an authoritative
# name server for the given domain.
function zoneDepth(){
# Counts the number of periods in the $domain string.
zone_depth=$(grep -o "\." <<< "$domain"|wc -l)
echo "$zone_depth"
}
############ recursiveNsLookup()
# The recursiveNsLookup() finds the authoritative name servers for a supplied
# domain. It does this by running successive manual name server lookups from
# the root resolver server on down. I'm using a dig command here to keep asking
# for the next NS server in the chain. This function returns / hits its base
# case when it can either find no more authorities for a given domain or it has
# dug to the correct depth in the zone hierarchy.
function recursiveNsLookup(){
ns_serv=$1
# If the zone_depth value is zero return the ns server
[[ $zone_depth = 0 ]] &&
echo "$ns_serv" &&
return
# Uses dig to find an authoritative name server for this level in the
# zone hierarchy.
new_ns_serv=$(dig @"$ns_serv" "$domain" \
| grep -A1 "AUTHORITY SECTION" \
| tail -1 \
| awk '{print $5}')
# If new_ns_serv empty return old ns_serv.
[[ -z $new_ns_serv ]] &&
echo $ns_serv &&
return
# Decrement the zone_depth value after ns lookup.
((zone_depth--))
# Recurse with this level's ns_serv
recursiveNsLookup "$new_ns_serv"
}
############ main()
# The main() function does a number of things; it gets the domain from user
# input, then it calls the zoneDepth() function to determine the zone_depth of
# the supplied domain, then it calls the recursiveNsLookup() function to get
# the auth_ns_serv for the supplied domain, finally it runs an A record lookup
# on auth_ns_serv to see if the supplied domain has an Address record. If the
# domain doesn't have an A record just return the auth_ns_serv name instead.
function main(){
# Get's domain name from user.
read -r -p "Please enter a domain name: " domain
# Validate the supplied user input.
domainCheckAndPrep
# Find's number of DNS zones in domain name (aka number of subdomains)
# via the zoneDepth() function.
zone_depth=$(zoneDepth)
# Get's the authoritative name servers for the domain via the
# recursiveNsLookup() function. Uses first root resolver server,
# a.root-servers.net.
auth_ns_serv=$(recursiveNsLookup a.root-servers.net)
# If there's an answer for A record print it else print auth name serv.
response=$(dig @"$auth_ns_serv" A "$domain"|grep -A1 "ANSWER SECTION")
[[ $? -eq 0 ]] &&
echo "$response" ||
echo "Authoritative Name Server for $domain is $auth_ns_serv" &&
return
}
main