#!../expect --
#
# gethostbyaddr a.b.c.d - translate an internet address to a FQDN,
#			  guessing (a lot) if necessary.
# Author: Don Libes, NIST
# Version 3.5
# January 11, 1991

# 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 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 original 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
set guess ""		;# default, if host basename can't be determined.  If
			;# empty, use address.

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 address 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 address $argv

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

# return string between prefix and suffix
proc extract {buf prefix suffix} {
	# avoid the array ref via braces
	regexp ${prefix}(.*)$suffix $buf dontcare str
	return $str
}

proc vprint {s} {
	global verbose

	if !$verbose return
	send_user $s\n
}

proc vprint_extract {s prefix suffix} {
	global verbose

	if !$verbose return
	send_user [extract $s $prefix $suffix]\n
}

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

	if {$dn && $reverse} {
		set verified [verify $name $address]
	} 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 address
proc verify {name address} {
	vprint "verifying $name is $address"
	set rc 0
	spawn nslookup
	expect ">*"
	send $name\r

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

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

		# split out hostname from FQDN as guess for later
		set s [split $host ""]
		set i [string first . $s]
		set guess [join [lrange $s 0 $i] ""]
	}

exit

close
wait

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

vprint "talking smtp to $address"
spawn telnet $address smtp
expect	-re $bad_telnet_responses {vprint_extract $expect_out(buffer) ": " "\r"} \
	timeout {vprint "timed out"} \
	-re "\n220 ((\[^\\.]*).\[^ ]*) "
		set host $expect_out(1,string)
		printhost $host smtp 1
		if {0 == [llength [split $guess ""]]} {
			set guess $expect_out(2,string)	;# for later
		}
	}
close
wait

if {$nic || ($d == 0)} {
 vprint "talking to nic"
 spawn telnet nic.ddn.mil
 expect	-re $bad_telnet_responses {vprint_extract $expect_out(buffer) ": " "\r"} \
	timeout {vprint "timed out"} \
	"@" {
		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"
			vprint "getting info on network $a.$b"
			send "net $a.$b\r"
			expect	"No match*" {
					vprint "no info"
				} "net*\r*\r*" {
					printhost [extract $expect_out(buffer) "\n" "\r"] nic 0
				}
			} "net*\r*\r*" {
				printhost [extract $expect_out(buffer) "\n" "\r"] nic 0
			}
	}
 close
 wait
 if {$d == 0} exit
}

if 0==[llength [split $guess ""]] {set guess $address}

for {set i [expr $d-1]} {$i>0} {set i [expr $i-1]} {
	vprint "talking smtp to $a.$b.$c.$i"
	spawn telnet $a.$b.$c.$i smtp
	expect	-re $bad_telnet_responses {vprint_extract $expect_out(buffer) ": " "\r"} \
		timeout {vprint "timed out"} \
		-re "\n220 .* .*} {
			set host [extract $expect_out(buffer) "\n220 " " "]
			if [string first . $host]!=-1 {
				printhost [format %s.%s $guess [extract $host "\\." ""]] "smtp - $a.$b.$c.$i is $host" 1
			}
		}
	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 {vprint_extract $expect_out(buffer) ": " "\r"} \
		timeout {vprint "timed out"} \
		-re "\n220 .* .*} {
			set host [extract $expect_out(buffer) "\n220 " " "]
			if [string first . $host]!=-1 {
				printhost [format %s.%s $guess [extract $host "\\." ""]] "smtp - $a.$b.$c.$i is $host" 1
			}
		}
	close
	wait
}

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