#!/usr/bin/env python
import socket
import sys
import argparse
import signal
import logging
import re
import multiprocessing
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from datetime import datetime
from scapy.all import *
from multiprocessing import Pool

def scan(arg):
	target_range, ip, timeout = arg
	ip = target_range + str(ip)
	icmp_packet = IP(dst=ip)/ICMP()
	resp = sr1(icmp_packet, timeout=timeout, verbose=0)
	if resp:
		return ip, True
	return ip, False

def ip_is_valid(string):
	if not re.match(r'[0-9]{1,3}(.[0-9]{1,3}){3}-[0-9]{1,3}', string):
		raise argparse.ArgumentTypeError("IP range must be of the form: XX.XX.XX.XX-XX")
	return string

if __name__ == '__main__':
	s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
	s.connect(('8.8.8.8', 0))
	local_ip = s.getsockname()[0]
	s.close()
	local_range = local_ip[:local_ip.rfind('.') + 1]
	local_range = local_range + '1-254'

	parser = argparse.ArgumentParser()
	parser.add_argument('-v', '--verbose', help="Verbose mode.", action='store_true')
	parser.add_argument('-r', help="Specify a IP range. Scan all the local network by default. Usage: XX.XX.XX.XX:XX-XX", type=ip_is_valid, default=local_range)
	parser.add_argument('-t', help="Timeout for the ICMP packet. Higher value is slower, but it can find more devices. 2 by default.", type=int, default=2)
	parser.add_argument('-p', help="Number of processes.", type=int, default=multiprocessing.cpu_count())
	args = parser.parse_args()

	target_range = args.r
	target_minip = target_range[:target_range.rfind('-')]
	target_minip = int(target_minip[target_range.rfind('.') + 1:])
	target_maxip = int(target_range[target_range.rfind('-') + 1:])
	target_range = target_range[:target_range.rfind('.')] + '.'

	if target_maxip < target_minip:
		tmp = target_maxip
		target_maxip = target_minip
		target_minip = tmp

	try:
		pool = Pool(processes=args.p)
		for dest, status in pool.imap(scan, [(target_range, ip, args.t) for ip in range(target_minip, target_maxip + 1)]):
			if status:
				print dest if not args.verbose else dest + " is UP"
			elif args.verbose:
				print dest + " is DOWN"

	except KeyboardInterrupt:
		pool.terminate()
		pool.join()
		sys.exit("\n\nTerminated by user. Bye !")

	except Exception, e:
		sys.exit("Error {0}".format(e))
