import sys
import getopt
import socket, struct
from netaddr import *
from datetime import date
import json, csv
import subprocess, platform
import logging


today= date.today()
today_dot = today.strftime('%d.%m.%Y')

logging.basicConfig(filename='logs/log_%s' %today,
                    filemode='w+',
                    format='%(asctime)s %(levelname)s %(message)s',           
                    datefmt='%Y-%m-%d %H:%M:%S',
                    level=logging.DEBUG)

version = '1.0'
verbose = False
ip_range=[]
ip_ranges=[]
alive_devices=[]
hostname={}

def help():
    
    print('''
    
    Scans the specified network to find and list alive nodes.

    Syntax:  /usr/bin/python netscan.py [-n Network -p Port -f FilePath ] ...
    
    example: /usr/bin/python netscan.py -n 192.168.1.0/24 -p 22 -f /etc/output.json
             /usr/bin/python netscan.py -n 192.168.1.16/30,192.168.2.2/32 -p 22 -f /etc/output.json
    
    args:
    -h --help           Help.
    -v --version        Shows version.        
    -n --network        Network(S) to scan.
    -f --file-path      File path containing file name to save the output.
                        You can write the output in .csv, .txt or .json file formats.

    © 2023 Hamid Sadeghian
    
    ''')
    

def portscan(ips, port):
    for ip in ips:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(1)
        result = sock.connect_ex((ip, int(port)))
        if result == 0:
            alive_devices.append(ip) 
            print(ip, ' port {} is open.'.format(port))
            logging.info('{} port {} is open.'.format(ip, port))
        sock.close()
    return alive_devices

def pingscan(ips):
    ping_str = '-n 2' if  platform.system().lower()=='windows' else '-c 2'
    need_sh = False if  platform.system().lower()=='windows' else True
    for ip in ips:
        args = 'ping ' + ' ' + ping_str + ' ' + ip
        # Ping
        if subprocess.call(args, shell=need_sh) == 0:
            alive_devices.append(ip)   
    return alive_devices

def ips(start, end):
    start = struct.unpack('>I', socket.inet_aton(start))[0]
    end = struct.unpack('>I', socket.inet_aton(end))[0]
    return [socket.inet_ntoa(struct.pack('>I', i)) for i in range(start, end)]

def nslook(alive_devices):
    hostname={}
    for i in alive_devices:
        try:
            p=(socket.getnameinfo((i, 0), socket.NI_NAMEREQD))
            hostname[i]= p[0]
        except:
            hostname[i]= 'no dns record'
            pass
    return hostname

def main(argv):
    file = None
    networks=[]
    network=''

    logging.info(' START')
    opts, args = getopt.getopt(argv[1:],'n:f:p:hv',['help','version','network=','file-path=','port='])

    for opt, arg in opts:
        if opt in ('-h', '--help'):
            help()
            sys.exit()
        elif opt in ('-v', '--version'):
            print('Version', version)
            sys.exit()
        elif opt in ('-n', '--network'):
            network = arg
            networks = network.split(',')
        elif opt in ('-f', '--file-path'):
            file = arg 
        elif opt in ('-p', '--port'):
            port = arg
    
    if any((not network, not file, not port)):
        help()
        print('\nERROR: Mandatory parameter is missing.\n')
        logging.error('Mandatory parameters are missing.')
        sys.exit()

    # find out ip ranges
    for n in networks:
        ip_ranges = IPNetwork(str(n))
        for ip in ip_ranges:
            ip_range.append(str(ip))

    print(networks, 'will be scaned.')
    logging.info('%s will be scaned.' %networks)
    

    #input('Press Enter to Continue...')
    alive_devices = portscan(ip_range, port)
    print(len(ip_range),'IPs scaned.')
    logging.info('%s IPs scaned.'%len(ip_range))

    hosts = nslook(alive_devices)
    
    header = ['IP-Address','Hostname']

    try:
        with open(file, 'w', encoding='UTF8', newline='') as f:
            if '.json' in file:
                json.dump(hosts, f, indent=2)
            elif '.csv' in file:
                writer = csv.DictWriter(f, fieldnames=header)
                writer.writeheader()
                for key in hosts.keys():
                    f.write('%s,%s\n'%(key,hosts[key]))   
            elif '.txt' in file:
                for key in hosts.keys():
                    f.write('%s     %s\n'%(key,hosts[key]))
            else:
                print('Unsupported file extension!')
                logging.error('Unsupported file extension!')
        logging.info('List of hosts inclusive DNS-Name saved in {}'.format(file))

    except IOError:
        print('Error occurred while exporting to file: {0}'.format(file))
        logging.error('Error occurred while exporting to file: {0}'.format(file))

    print('\n',len(hosts),'IP(s) with open port 22 found:',hosts,'\n')
    logging.info('{} IP(s) with open port 22 found: {}'.format(len(hosts), hosts))
    

if __name__ == '__main__':
   main(sys.argv)