One of the first things that pique my curiosity when I find myself in a new network environment is "what's around me?". To me it feels a bit like waking up from a dream and not remembering where I am or how I got there, so I want to look around a bit. I wrote a little script for this, and I can't say that it was terribly effective. It was based on using ping
to send packets to every possible host on the current network (ie. the one I'm connected to presently). The scan was sequential, so it would ping 10.0.0.1
, then ping 10.0.0.2
and so on. Most of these addresses had no hosts bound to them, so the scan would take forever for the ping to time out and move on to the next host. It would actually take so long (10min+) that in a wireless network, clients would come and go between the start and the end of the scan.
I didn't use ping
because it was such a great choice for this problem, just that it was the first thing that occurred to me. I did get the script to run a bit faster by parallelling the ping
s, but this is a very silly thing to do, because with a Class C network, there are now 254 instances of ping
running on the system. This would often drown out the packets from the hosts which were connected and the script would fail to report any hosts at all. I'm not sure why that is, but I improved the situation a bit by pausing for one second before starting every new thread.
Just the other day I stumbled upon a mention of using nmap
to do this same thing. Sure enough, nmap
was *designed* for this, so it should be the obvious choice. Somehow that never occurred to me. :lala: So I rewrote my little script to use nmap
in place of ping
. nmap
does essentially the same thing as my script did, it ping
s hosts in parallell, but it does so without forking itself 254 times and it has some clever algorithms that monitor the state of the network to get best throughput at least congestion. To put that in plain English, here's a little comparison for a scan across 254 IP addresses:
- parallell nmap: 0m 5.868s
- parallell ping: 4m 15.912s
In other words, the ping method is absolutely rubbish. But, while I always have nmap
available on my laptop, it's not an application that is installed by default on every system (unlike ping
), so perhaps it would be handy to be able to fall back on the ping
method, as lame as it is, if that's all we have.
Another small refinement is checking ifconfig
for network info, so the user doesn't have to supply this manually. Again, this could fail (no priviliges, no ifconfig
), so it's made to be an option, not a requirement.
#!/usr/bin/env python
#
# Author: Martin Matusiak <numerodix@gmail.com>
# Licensed under the GNU Public License, version 2.
#
# revision 2 - add hostname lookup
import os, string, re, sys, time, thread
def main():
network = None
try:
netinfo = check_network()
(ip, mask) = netinfo
network = ip + "/" + mask
except:
print "Warning: No network connection found, scan may fail."
if len(sys.argv) > 1:
network = sys.argv[1]
if not network:
print "Error: No network range given."
print "Usage:\t" + sys.argv[0] + " 10.0.0.0/24"
sys.exit(1)
if cmd_exists("nmap"):
nmap_scan(network)
else:
print "Warning: nmap not found, falling back on failsafe ping scan method."
ping_scan(network)
def nmap_scan(network):
try:
print "Using network: " + network
cmd = 'nmap -n -sP -T4 ' + network + ' 2>&1'
res = invoke(cmd)
lines = res.split('\n')
for i in lines:
m = find('Host\s+\(?([0-9\.]+)\)?\s+appears to be up.', i)
if m:
print m, "\t", nslookup(m)
except: pass
def ping_scan(network):
iprange = find('(\w+\.\w+\.\w+)', network)
print "Using network: " + iprange + ".0/24"
for i in range(1,254):
host = iprange + '.' + str(i)
thread.start_new_thread(ping, (host, None))
time.sleep(1)
def ping(host, dummy):
try:
cmd = 'ping -c3 -n -w300 ' + host + ' 2>&1'
res = invoke(cmd)
if "bytes from" in res: print host, "\t", nslookup(host)
except: pass
def nslookup(ip):
if cmd_exists("host"):
cmd = 'host ' + ip + ' 2>&1'
res = invoke(cmd)
if "domain name pointer" in res:
return res.split(" ")[4][:-2]
return ""
def check_network():
cmd = "/sbin/ifconfig"
res = invoke(cmd)
iface, ip, mask = None, None, None
lines = res.split('\n')
for i in lines:
# find interface
m = find('^(\w+)\s+', i)
if m: iface = m
# ignore loopback interface
if iface and iface != "lo":
# find ip address
m = find('inet addr:([0-9\.]+)\s+', i)
if m: ip = m
# find net mask
m = find('Mask:([0-9\.]+)$', i)
if m: mask = m
if ip and mask:
mask = mask_numerical(mask)
return (ip, mask)
def mask_numerical(mask):
segs = find('(\w+)\.(\w+)\.(\w+)\.(\w+)', mask)
mask = 0
adds = (0, 128, 192, 224, 240, 248, 252, 254, 255)
for i in segs:
for j in range(0, len(adds)):
if int(i) == adds[j]:
mask += j
return str( mask )
def find(needle, haystack):
try:
match = re.search(needle, haystack)
if len(match.groups()) > 1:
return match.groups()
else:
return match.groups()[0]
except: pass
def invoke(cmd):
(sin, sout) = os.popen2(cmd)
return sout.read()
def cmd_exists(cmd):
if invoke("which " + cmd + " 2>&1").find("no " + cmd) == -1:
return True
return False
if __name__ == "__main__":
main()
The output looks like this:
Using network: 192.168.2.119/24 192.168.2.1 192.168.2.119 james.home.lan
The first host listed, whose address ends in a 1, is often a router. Then there's the host transmitting the scan, that is localhost. At the time of the scan there were no other hosts connected on the network. Of course, beyond finding hosts, there's a lot more one can find out about them using.. *drumroll*.. nmap
.
Update: I added a name lookup feature so that if there is a nameserver on the network, you not only get ip addresses, but hostnames as well. :)
Or you could just ping the network adress 0.0.0.0 or the broadcast 255.255.255.255 and all hosts on the network would answer, if they respond to ping. Only works in linux (or gnu ping).
[...] Once you’ve established that the connection is working, and you want to know more about the network, you can go further with something like netscan. [...]