#!/usr/bin/env python
import sys
import threading 
import socket
import subprocess
from collections import OrderedDict
import re
from datetime import datetime

PY2 = sys.version_info[0] == 2
if PY2:
    raise Exception("Please use finally Python3.x version :)")

'''==================== DESCRIPTION ===================='''
# Script helps you to find all available IP adresses (IPv4) from given range
'''==================== DESCRIPTION-END =================='''    


'''==================== USAGE ===================='''
# main method
'''====================  USAGE-END =================='''

'''==================== GLOBAL SETTINGS ===================='''
NUMBER_OF_ADDRESSES_PER_THREAD = 20
NUMBER_OF_IPs_TO_BE_CHECKED= 50
DEFAULT_PORT= 80
DEFAULT_IP_ADDR= "192.168.1.0"
'''==================== GLOBAL SETTINGS-END =================='''

class IPaddress():
    
    def __init__(self,ipAddr):
        
        self._ip= [0]*4
        reg= re.match(r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)',ipAddr)
        if not reg:
            raise ValueError("Incorrect IP format")
        self._ip[0]=int(reg.group(4))
        self._ip[1]=int(reg.group(3))
        self._ip[2]=int(reg.group(2))
        self._ip[3]=int(reg.group(1))
           
    def __str__(self):
        return "%d.%d.%d.%d" % (self._ip[3], self._ip[2], self._ip[1], self._ip[0])
        
    def inc(self):
        self._ip[0] +=1
        if(not self._ip[0] %256):
            self._ip[0] =0
            self._ip[1]+=1
            if(not self._ip[1] %256):
                self._ip[1] =0
                self._ip[2]+=1
                if(not self._ip[2] %256):
                    self._ip[2] =0
                    self._ip[3]+=1
                    if(not self._ip[3] %256):
                        self._ip[0]=0
                        self._ip[1]=0
                        self._ip[2]=0
                        self._ip[3]=0
        return self

        
class IPScannerThread(threading.Thread):
    
    def __init__(self, startIPaddr, endIPaddr,resultCallback=None):
        threading.Thread.__init__(self)
        self._startIPaddr=startIPaddr
        self._endIPaddr=endIPaddr
        self._conn= TCPconnector()
        self._resCb= resultCallback
    def run(self):
        currentIP= self._startIPaddr
        for _ in range(NUMBER_OF_ADDRESSES_PER_THREAD):    
            if(self._conn.scan(str(currentIP))):
                if(self._resCb):
                    self._resCb(str(currentIP))                   
            currentIP.inc()
        

class TCPconnector():
    
    def __init__(self):
        self._port= DEFAULT_PORT
        self._ip=IPaddress(DEFAULT_IP_ADDR)
        socket.setdefaulttimeout(1)
       
    def scan(self,addr=DEFAULT_IP_ADDR,port=DEFAULT_PORT):
        ''' if connection succeeded, res ==0 '''
        try:
            self._socket= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            res= self._socket.connect_ex((addr,port))
        except:
            pass
        finally:
            self._socket.close()
        return not res 


        
''' MAIN APP '''
results=OrderedDict()
def result(ip):
    results[ip]=1
    print("ADDR: "+ip+ " is alive")


def printResults():
    for ip,res in results.items():
        print("IP: %s  VAL: %d"%(ip,res))
        
if __name__ == '__main__':
    t1= datetime.now()
    try:
        ipAddr= IPaddress(DEFAULT_IP_ADDR)
        numOfIPs= NUMBER_OF_IPs_TO_BE_CHECKED
        threads=[]
        for _ in range(0,numOfIPs,NUMBER_OF_ADDRESSES_PER_THREAD):
            startIpAddr= IPaddress(str(ipAddr))
            for _ in range(NUMBER_OF_ADDRESSES_PER_THREAD):
                results[str(ipAddr)]=0
                ipAddr.inc()                               
            #print("START: %s | END: %s |IDs: %s, %s"%(startIpAddr,ipAddr,hex(id(startIpAddr)),hex(id(ipAddr))))
            threads.append(IPScannerThread(startIpAddr,ipAddr,result))    
        try:
            for t in threads:
                t.start()
            print("Number of active threads:", threading.activeCount())
            for t in threads:
                t.join()
        except:
            print("Error: Cannot start the thread")       
        print("Exiting Main Thread")
        print("Scanning time: %s"%(datetime.now()-t1))
        printResults()        
    except Exception as e:
        print(e)
        print("--failed--")
''' MAIN APP- END '''