1#!../expect --
2#
3# gethostbyaddr a.b.c.d - translate an internet address to a FQDN,
4#			  guessing (a lot) if necessary.
5# Author: Don Libes, NIST
6# Version 4.0
7# Written: January 11, 1991
8# Last revised: March 21, 1996
9
10# By default, return a FQDN (fully qualified domain name) or descriptive
11# string (if FQDN is not easily determinable).  This is tagged with a brief
12# explanation of how it was determined.
13#
14# If the host part of the FQDN cannot be determined, the original IP address
15# is used.  
16#
17# Optional arguments act as toggles:					Default
18# -t	tag names with a description of how derived.			true
19# -v	verbose.							false
20# -r	reverse names to see if they resolve back to orig IP address.	true
21# -n	query nic for a descriptive string if it begins to look like	true
22#	the FQDN may be hard to derive.
23# -d	turn on debugging to expose underlying dialogue			false
24#
25# These options and others (see below) may be set in a ~/.gethostbyaddr file
26# To set options from that file, use the same syntax as below.
27set timeout 120		;# timeout query after this many seconds
28set tag 1		;# same as -t
29set reverse 1		;# same as -r
30set verbose 0		;# same as -v
31set nic 1		;# same as -n
32set debug 0		;# same as -d
33log_user 0
34
35proc usage {} {
36	send_user "usage: gethostbyaddr \[options\] a.b.c.d\n"
37	send_user "options meaning (all options act as toggles)      default\n"
38	send_user "  -t    tag with derivation description           true\n"
39	send_user "  -v    verbose                                   false\n"
40	send_user "  -r    reverse back to IP addr for verification  true\n"
41	send_user "  -n    query nic                                 true\n"
42	send_user "  -d    produce debugging output                  false\n"
43	send_user "options must be separate.\n"
44	exit
45}
46
47if {[file readable ~/.gethostbyaddr]} {source ~/.gethostbyaddr}
48
49while {[llength $argv]>0} {
50	set flag [lindex $argv 0]
51	switch -- $flag \
52	"-v" {
53		set verbose [expr !$verbose]
54		set argv [lrange $argv 1 end]
55	} "-r" {
56		set reverse [expr !$reverse]
57		set argv [lrange $argv 1 end]
58	} "-n" {
59		set nic [expr !$nic]
60		set argv [lrange $argv 1 end]
61	} "-t" {
62		set tag [expr !$tag]
63		set argv [lrange $argv 1 end]
64	} "-d" {
65		set debug [expr !$debug]
66		set argv [lrange $argv 1 end]
67		debug $debug
68	} default {
69		break
70	}
71}
72
73set IPaddress $argv
74
75if {[llength $argv]!=1} usage
76if {4!=[scan $IPaddress "%d.%d.%d.%d" a b c d]} usage
77
78proc vprint {s} {
79	global verbose
80
81	if {!$verbose} return
82	send_user $s\n
83}
84
85# dn==1 if domain name, 0 if text (from nic)
86proc printhost {name how dn} {
87	global reverse tag IPaddress
88
89	if {$dn && $reverse} {
90		set verified [verify $name $IPaddress]
91	} else {set verified 0}
92
93	if {$verified || !$reverse || !$dn} {
94		if {$tag} {
95			send_user "$name ($how)\n"
96		} else {
97			send_user "$name\n"
98		}
99		
100		if {$verified || !$reverse} {
101			close
102			wait
103			exit
104		}
105	}
106}
107
108# return 1 if name resolves to IP address
109proc verify {name IPaddress} {
110	vprint "verifying $name is $IPaddress"
111	set rc 0
112	spawn nslookup
113	expect ">*"
114	send $name\r
115
116	expect {
117	 	-re "\\*\\*\\* (\[^\r]*)\r" {
118			vprint $expect_out(1,string)
119		} timeout {
120			vprint "timed out"
121		} -re "Address:.*Address:  (\[^\r]*)\r" {
122			set addr2 $expect_out(1,string)
123			if {[string match $IPaddress $addr2]} {
124				vprint "verified"
125				set rc 1
126			} else {
127				vprint "not verified - $name is $addr2"
128			}
129		}
130	}
131	close
132	wait
133	return $rc
134}
135
136set bad_telnet_responses "(telnet:|: unknown).*"
137
138proc telnet_error {s} {
139	regexp ": (.*)\r" $s dontcare msg
140	vprint $msg
141}
142
143proc guessHost {guess} {
144	global guessHost
145	if {[info exists guessHost]} return
146	set guessHost $guess
147}
148
149proc guessDomain {guess} {
150	global guessDomain
151	if {[info exists guessDomain]} return
152	set guessDomain $guess
153}
154
155proc guessFQDN {} {
156	global guessHost guessDomain
157	return $guessHost.$guessDomain
158}
159
160######################################################################
161# first do a simple reverse nslookup
162######################################################################
163
164vprint "using nslookup"
165spawn nslookup
166expect ">*"
167send "set query=ptr\r"
168expect ">*"
169send "$d.$c.$b.$a.in-addr.arpa\r"
170expect {
171	timeout {
172		vprint "timed out"
173	} -re "\\*\\*\\* (\[^\r]*)\r" {
174		vprint $expect_out(1,string)
175	} -re "name = (\[^\r]*)\r" {
176		set host $expect_out(1,string)
177		printhost $host nslookup 1
178
179		# split out hostname from FQDN as guess for later
180		guessHost [lindex [split $host "."] 0]
181	}
182}
183
184close
185wait
186
187######################################################################
188# next telnet to host and ask it what its name is
189######################################################################
190
191vprint "talking smtp to $IPaddress"
192spawn telnet $IPaddress smtp
193expect	{
194	-re $bad_telnet_responses {
195		telnet_error $expect_out(buffer)
196	} timeout {
197		vprint "timed out"
198	} -re "\n220 (\[^\\. ]*).?(\[^ ]*)" {
199		set host $expect_out(1,string)
200		set domain $expect_out(2,string)
201		printhost $host.$domain smtp 1
202
203		# if not valid FQDN, it's likely either host or domain
204		if {[string length $domain]} {
205		    guessDomain $host.$domain
206		} else {
207		    guessHost $host
208		}
209	}
210}
211catch close
212wait
213
214######################################################################
215# ask NIC for any info about this host
216######################################################################
217
218if {$nic || ($d == 0)} {
219 vprint "talking to nic"
220 spawn telnet internic.net
221 expect	{
222	-re $bad_telnet_responses {
223		telnet_error $expect_out(buffer)
224	} timeout {
225		vprint "timed out"
226	} "InterNIC >" {
227		send "whois\r"
228		expect "Whois: "
229		vprint "getting info on network $a.$b.$c"
230		send "net $a.$b.$c\r"
231		expect {
232		    "No match*" {
233			vprint "no info"
234			expect "Whois: "
235			vprint "getting info on network $a.$b"
236			send "net $a.$b\r"
237			expect {
238			    "No match*" {
239				vprint "no info"
240			    } -re "net\r\n(\[^\r]*)\r" {
241				printhost $expect_out(1,string) nic 0
242			    } timeout {
243				vprint "timed out"
244			    }
245			}
246		    } -re "net\r\n(\[^\r]*)\r" {
247			printhost $expect_out(1,string) nic 0
248		    } timeout {
249			vprint "timed out"
250		    }
251		}
252	}
253 }
254 catch close
255 wait
256 if {$d == 0} exit
257}
258
259######################################################################
260# ask other hosts in the same class C what their name is
261# so that we can at least get the likely domain
262#
263# do this in two loops - first from current IP address down to 0
264# and then next from current IP address up to 255
265######################################################################
266
267# give up guessing host name
268guessHost "unknown"
269
270for {set i [expr $d-1]} {$i>0} {incr i -1} {
271	vprint "talking smtp to $a.$b.$c.$i"
272	spawn telnet $a.$b.$c.$i smtp
273	expect {
274		-re $bad_telnet_responses {
275			telnet_error $expect_out(buffer)
276		} timeout {
277			vprint "timed out"
278		} -re "\n220 (\[^\\. ]*).?(\[^ ]*)" {
279			set host $expect_out(1,string)
280			set domain $expect_out(2,string)
281			printhost $guessHost.$domain "smtp - $a.$b.$c.$i is $host.$domain" 1
282
283			# if not valid FQDN, it's likely either host or domain
284			# don't bother recording host since it can't be for
285			# original addr.
286			if {[string length $domain]} {
287			    guessDomain $host.$domain
288			}
289		}
290	}
291	catch close
292	wait
293}
294
295for {set i [expr $d+1]} {$i<255} {incr i} {
296	vprint "talking smtp to $a.$b.$c.$i"
297	spawn telnet $a.$b.$c.$i smtp
298	expect {
299		-re $bad_telnet_responses {
300			telnet_error $expect_out(buffer)
301		} timeout {
302			vprint "timed out"
303		} -re "\n220 (\[^ ]*.(\[^ ])) " {
304			set host $expect_out(1,string)
305			set domain $expect_out(2,string)
306			printhost $guessHost.$domain "smtp - $a.$b.$c.$i is $host.$domain" 1
307
308			# if not valid FQDN, it's likely either host or domain
309			# don't bother recording host since it can't be for
310			# original addr.
311			if {[string length $domain]} {
312			    guessDomain $host.$domain
313			}
314		}
315	}
316	catch close
317	wait
318}
319
320######################################################################
321# print our best guess as to the name
322######################################################################
323
324
325# How pathetic.  Print something, anything!
326if {!$verbose && !$tag} {send_user [guessFQDN]}
327