summaryrefslogtreecommitdiff
path: root/example/gethostbyaddr
blob: 513a3302c081108ec43898028df9f534c24042b6 (plain)
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
#!/bin/sh
# -*- tcl -*-
# The next line is executed by /bin/sh, but not tcl \
exec tclsh "$0" ${1+"$@"}

package require Expect


#
# gethostbyaddr a.b.c.d - translate an internet address to a FQDN,
#			  guessing (a lot) if necessary.
# Author: Don Libes, NIST
# Version 4.0
# Written: January 11, 1991
# Last revised: March 21, 1996

# By default, return a FQDN (fully qualified domain name) or descriptive
# string (if FQDN is not easily determinable).  This is tagged with a brief
# explanation of how it was determined.
#
# If the host part of the FQDN cannot be determined, the original IP address
# is used.  
#
# Optional arguments act as toggles:					Default
# -t	tag names with a description of how derived.			true
# -v	verbose.							false
# -r	reverse names to see if they resolve back to orig IP address.	true
# -n	query nic for a descriptive string if it begins to look like	true
#	the FQDN may be hard to derive.
# -d	turn on debugging to expose underlying dialogue			false
#
# These options and others (see below) may be set in a ~/.gethostbyaddr file
# To set options from that file, use the same syntax as below.
set timeout 120		;# timeout query after this many seconds
set tag 1		;# same as -t
set reverse 1		;# same as -r
set verbose 0		;# same as -v
set nic 1		;# same as -n
set debug 0		;# same as -d
log_user 0

proc usage {} {
	send_user "usage: gethostbyaddr \[options\] a.b.c.d\n"
	send_user "options meaning (all options act as toggles)      default\n"
	send_user "  -t    tag with derivation description           true\n"
	send_user "  -v    verbose                                   false\n"
	send_user "  -r    reverse back to IP addr for verification  true\n"
	send_user "  -n    query nic                                 true\n"
	send_user "  -d    produce debugging output                  false\n"
	send_user "options must be separate.\n"
	exit
}

if {[file readable ~/.gethostbyaddr]} {source ~/.gethostbyaddr}

while {[llength $argv]>0} {
	set flag [lindex $argv 0]
	switch -- $flag \
	"-v" {
		set verbose [expr !$verbose]
		set argv [lrange $argv 1 end]
	} "-r" {
		set reverse [expr !$reverse]
		set argv [lrange $argv 1 end]
	} "-n" {
		set nic [expr !$nic]
		set argv [lrange $argv 1 end]
	} "-t" {
		set tag [expr !$tag]
		set argv [lrange $argv 1 end]
	} "-d" {
		set debug [expr !$debug]
		set argv [lrange $argv 1 end]
		debug $debug
	} default {
		break
	}
}

set IPaddress $argv

if {[llength $argv]!=1} usage
if {4!=[scan $IPaddress "%d.%d.%d.%d" a b c d]} usage

proc vprint {s} {
	global verbose

	if {!$verbose} return
	send_user $s\n
}

# dn==1 if domain name, 0 if text (from nic)
proc printhost {name how dn} {
	global reverse tag IPaddress

	if {$dn && $reverse} {
		set verified [verify $name $IPaddress]
	} else {set verified 0}

	if {$verified || !$reverse || !$dn} {
		if {$tag} {
			send_user "$name ($how)\n"
		} else {
			send_user "$name\n"
		}
		
		if {$verified || !$reverse} {
			close
			wait
			exit
		}
	}
}

# return 1 if name resolves to IP address
proc verify {name IPaddress} {
	vprint "verifying $name is $IPaddress"
	set rc 0
	spawn nslookup
	expect ">*"
	send $name\r

	expect {
	 	-re "\\*\\*\\* (\[^\r]*)\r" {
			vprint $expect_out(1,string)
		} timeout {
			vprint "timed out"
		} -re "Address:.*Address:  (\[^\r]*)\r" {
			set addr2 $expect_out(1,string)
			if {[string match $IPaddress $addr2]} {
				vprint "verified"
				set rc 1
			} else {
				vprint "not verified - $name is $addr2"
			}
		}
	}
	close
	wait
	return $rc
}

set bad_telnet_responses "(telnet:|: unknown).*"

proc telnet_error {s} {
	regexp ": (.*)\r" $s dontcare msg
	vprint $msg
}

proc guessHost {guess} {
	global guessHost
	if {[info exists guessHost]} return
	set guessHost $guess
}

proc guessDomain {guess} {
	global guessDomain
	if {[info exists guessDomain]} return
	set guessDomain $guess
}

proc guessFQDN {} {
	global guessHost guessDomain
	return $guessHost.$guessDomain
}

######################################################################
# first do a simple reverse nslookup
######################################################################

vprint "using nslookup"
spawn nslookup
expect ">*"
send "set query=ptr\r"
expect ">*"
send "$d.$c.$b.$a.in-addr.arpa\r"
expect {
	timeout {
		vprint "timed out"
	} -re "\\*\\*\\* (\[^\r]*)\r" {
		vprint $expect_out(1,string)
	} -re "name = (\[^\r]*)\r" {
		set host $expect_out(1,string)
		printhost $host nslookup 1

		# split out hostname from FQDN as guess for later
		guessHost [lindex [split $host "."] 0]
	}
}

close
wait

######################################################################
# next telnet to host and ask it what its name is
######################################################################

vprint "talking smtp to $IPaddress"
spawn telnet $IPaddress smtp
expect	{
	-re $bad_telnet_responses {
		telnet_error $expect_out(buffer)
	} timeout {
		vprint "timed out"
	} -re "\n220 (\[^\\. ]*).?(\[^ ]*)" {
		set host $expect_out(1,string)
		set domain $expect_out(2,string)
		printhost $host.$domain smtp 1

		# if not valid FQDN, it's likely either host or domain
		if {[string length $domain]} {
		    guessDomain $host.$domain
		} else {
		    guessHost $host
		}
	}
}
catch close
wait

######################################################################
# ask NIC for any info about this host
######################################################################

if {$nic || ($d == 0)} {
 vprint "talking to nic"
 spawn telnet internic.net
 expect	{
	-re $bad_telnet_responses {
		telnet_error $expect_out(buffer)
	} timeout {
		vprint "timed out"
	} "InterNIC >" {
		send "whois\r"
		expect "Whois: "
		vprint "getting info on network $a.$b.$c"
		send "net $a.$b.$c\r"
		expect {
		    "No match*" {
			vprint "no info"
			expect "Whois: "
			vprint "getting info on network $a.$b"
			send "net $a.$b\r"
			expect {
			    "No match*" {
				vprint "no info"
			    } -re "net\r\n(\[^\r]*)\r" {
				printhost $expect_out(1,string) nic 0
			    } timeout {
				vprint "timed out"
			    }
			}
		    } -re "net\r\n(\[^\r]*)\r" {
			printhost $expect_out(1,string) nic 0
		    } timeout {
			vprint "timed out"
		    }
		}
	}
 }
 catch close
 wait
 if {$d == 0} exit
}

######################################################################
# ask other hosts in the same class C what their name is
# so that we can at least get the likely domain
#
# do this in two loops - first from current IP address down to 0
# and then next from current IP address up to 255
######################################################################

# give up guessing host name
guessHost "unknown"

for {set i [expr $d-1]} {$i>0} {incr i -1} {
	vprint "talking smtp to $a.$b.$c.$i"
	spawn telnet $a.$b.$c.$i smtp
	expect {
		-re $bad_telnet_responses {
			telnet_error $expect_out(buffer)
		} timeout {
			vprint "timed out"
		} -re "\n220 (\[^\\. ]*).?(\[^ ]*)" {
			set host $expect_out(1,string)
			set domain $expect_out(2,string)
			printhost $guessHost.$domain "smtp - $a.$b.$c.$i is $host.$domain" 1

			# if not valid FQDN, it's likely either host or domain
			# don't bother recording host since it can't be for
			# original addr.
			if {[string length $domain]} {
			    guessDomain $host.$domain
			}
		}
	}
	catch close
	wait
}

for {set i [expr $d+1]} {$i<255} {incr i} {
	vprint "talking smtp to $a.$b.$c.$i"
	spawn telnet $a.$b.$c.$i smtp
	expect {
		-re $bad_telnet_responses {
			telnet_error $expect_out(buffer)
		} timeout {
			vprint "timed out"
		} -re "\n220 (\[^ ]*.(\[^ ])) " {
			set host $expect_out(1,string)
			set domain $expect_out(2,string)
			printhost $guessHost.$domain "smtp - $a.$b.$c.$i is $host.$domain" 1

			# if not valid FQDN, it's likely either host or domain
			# don't bother recording host since it can't be for
			# original addr.
			if {[string length $domain]} {
			    guessDomain $host.$domain
			}
		}
	}
	catch close
	wait
}

######################################################################
# print our best guess as to the name
######################################################################


# How pathetic.  Print something, anything!
if {!$verbose && !$tag} {send_user [guessFQDN]}