#!/usr/bin/env python3
import netifaces
import netaddr
import socket
import subprocess
import sys
import argparse
import urllib.request
import traceback
import logging
import inspect
from lavacontrolslib import getid, getmodel, getserial, sendlog

def fetchpage(ip):
    response = "Error"
    url = 'http://{}'.format(ip)
    req = urllib.request.Request(url)
    try:
        response = urllib.request.urlopen(req).read().decode('utf-8')
    except urllib.error.HTTPError as e:
        if e.code == 404:
            print('{}: 404 Page not found'.format(ip))
        elif e.code == 403:
            print('{}: 403 Access denied'.format(ip))
        elif e.code == 401:
            print('{}: 401 Unauthorized'.format(ip))
        else:
            print('{}: Unknown HTTP error: {}'.format(ip,e.code))
        pass
    except urllib.error.URLError as e:
        print('{}: HTTP Misc Error Occurred {}'.format(ip,e.reason))
        pass
    except UnicodeDecodeError as e:
        print('{}: Decode Error Occurred {}'.format(ip,e.reason))
        pass
    except:
        print("Unexpected error:", sys.exc_info()[0])
        pass
    return response

def fetchhostname(ip):
    response = "Hostname Error"
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(1)
    try:
        s.connect((ip, 4242))
        msg = b'hostname'
        s.send(msg)
        while True:
            part = s.recv(1024)
            received += part
            if not part:
                break
            response = json.loads(received.decode())
    except socket.error as e:
        print(ip, e)
        pass
    finally:
        s.close()
    return response
 
def scansingle(ip,port):
    remoteServerIP  = socket.gethostbyname(str(ip))
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(.1)
    result = sock.connect_ex((remoteServerIP, port))
    sock.close()
    if result == 0:
        return("Port {}:      Open".format(port))
    else:
        return

def main():
    gotthefunc = inspect.stack()[0][3]
    logger = logging.getLogger('main')
    logger.setLevel(logging.DEBUG)
    sock = logging.handlers.SocketHandler('localhost', logging.handlers.DEFAULT_TCP_LOGGING_PORT)
    logger.addHandler(sock)
    consolelog = logging.StreamHandler(sys.stdout)
    logger.addHandler(consolelog)
    msg = 'Log test'
    sendlog(gotthefunc, msg, 10)

    ifaces = netifaces.interfaces()
    myiface = 'eth0'
    addrs = netifaces.ifaddresses(myiface)
    ipinfo = addrs[socket.AF_INET][0]
    address = ipinfo['addr']
    netmask = ipinfo['netmask']
    cidr = netaddr.IPNetwork('%s/%s' % (address, netmask))
    network = cidr.network
    broadcast = cidr.broadcast

    parser = argparse.ArgumentParser(description='Display Engine Discovery Tools')
    parser.add_argument('-amt', action='store_true', help='Search for Intel AMT Engines')
    parser.add_argument('-apc', action='store_true', help='Search for APC PDUs')
    parser.add_argument('-de', action='store_true', help='Search for Display Engines')
    parser.add_argument('-nec', action='store_true', help='Search for NEC Monitors')
    args = parser.parse_args()
    if not any([args.amt, args.apc, args.de, args.nec]):
        parser.print_help()
        quit()
    if args.nec:
        displaylistforpdcommsconfig = []
        displaylistforlavacontrolsconfig = ''
        displaylistno = 1
        print('Scanning {} {} for NEC Displays'.format(cidr.cidr,cidr.netmask))
        for ip in netaddr.IPNetwork(cidr):
            if ip != network and ip != broadcast:
                port1 = scansingle(ip,80)
                port2 = scansingle(ip,7142)
                if port1 and port2:
                    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    s.connect((str(ip), 7142))
                    try:
                        displayid = getid(s)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Unknown Error Detail: {}'.format(err))
                        print(msg)
                    try:
                        displaymodel = getmodel(displayid,s)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Unknown Error Detail: {}'.format(err))
                        print(msg)
                        displaymodel = 'NULL'
                    try:
                        displayserial = getserial(displayid,s)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Unknown Error Detail: {}'.format(err))
                        print(msg)
                        displayserial = 'NULL'

                    print('{}: *** Found NEC Display: Model: {} ID: {} Serial: {} ***'.format(ip,displaymodel,displayid,displayserial))
                    displaylistforlavacontrolsconfig += ('IP%d=%s\n' % (displaylistno,ip))
#                    print('IP%d=%s' % (displaylistno,ip))
                    displaylistforpdcommsconfig.append([displaylistno,ip])
                    displaylistno += 1
                    s.close()
        print('\nFor LAVA Controls Config:')
        print(displaylistforlavacontrolsconfig)
        print('For PD Comms Config:')
        print(displaylistforpdcommsconfig)
        print('\n')
    if args.apc:
        apclist = []
        apclistno = 1
        print('Scanning {} {} for APC Products'.format(cidr.cidr,cidr.netmask))
        for ip in netaddr.IPNetwork(cidr):
            if ip != network and ip != broadcast:
                port1 = scansingle(ip,80)
                port2 = scansingle(ip,23)
                if port1 and port2:
                    if 'apc' in fetchpage(ip):
                        print('{}: *** APC found ***' .format(ip))
                        apclist.append([apclistno,ip])
                        apclistno += 1
    if args.amt:
        amtlist = []
        amtlistno = 1
        print('Scanning {} {} for Intel AMT Engines'.format(cidr.cidr,cidr.netmask))
        for ip in netaddr.IPNetwork(cidr):
            if ip != network and ip != broadcast:
                port1 = scansingle(ip,22)
                port2 = scansingle(ip,16992)
                if port1 and port2:
                    print('{}: *** Intel AMT Engine found ***' .format(ip))
                    amtlistno += 1
    if args.de:
        delist = []
        delistno = 1
        print('Scanning {} {} for Display Engines'.format(cidr.cidr,cidr.netmask))
        for ip in netaddr.IPNetwork(cidr):
            if ip != network and ip != broadcast:
                port1 = scansingle(ip,4242)
                port2 = scansingle(ip,22)
                if port1 and port2:
                    hostname=fetchhostname(ip)
                    print('{0} {1}: *** Display Engine found ***' .format(ip,hostname))
                    delist.append([delistno,ipi,hostname])
                    delistno += 1

if __name__ == '__main__':
   main()
