#!/usr/bin/python
# Modified rfcat_server to support Metasploit HWBridge



import re
import os
import sys
import cmd
import time
import json
import base64
import socket
import threading

from http.server import BaseHTTPRequestHandler,HTTPServer
from urllib.parse import parse_qs,urlparse
from rflib import *

# Global Nic used for MSFHandler
nic = None
last_errors = 0
starttime = 0
packets_sent = 0
last_sent = 0
username = None
password = None

class MSFHandler(BaseHTTPRequestHandler):
    def status(self):
        status = {}
        hardware = nic.getBuildInfo()
        hw_version = ""
        fw_version = nic.reprSoftwareConfig().split(':')[1].lstrip()
        try:
            hw_version = hardware.split('r')[1]
        except:
            hw_version = "not supported"
        status["operational"] = 1 # Possibly connect this to ping?
        status["hw_specialty"] = { "rftransceiver": True }
        status["hw_capabilities"] = { "cc11xx": True}
        status["last_10_errors"] = last_errors
        status["api_version"] = "0.0.2"
        status["fw_version"] = fw_version
        status["hw_version"] = hw_version
        status["device_name"] = hardware.split(' ')[0]
        return status

    def statistics(self):
        stats = {}
        stats["uptime"] = int(time.time()) - starttime
        stats["packet_stats"] = packets_sent
        stats["last_request"] = last_sent
        stats["voltage"] = "0.0v"
        return stats

    def datetime(self):
        return { "sytem_datetime": int(time.time()) }

    def timezone(self):
        return { "system_timezone": time.strftime("%Z") }

    def supported_idx(self):
        return { "indexes": [ nic.idx ] }

    def reset(self):
        nic.resetup()
        return { "status": "Resetting" }

    def set_freq(self, args):
        mhz = 24
        if not "freq" in args:
            return self.not_supported()
        if "mhz" in args:
            mhz = int(args["mhz"])
        nic.setFreq(int(args["freq"][0]), mhz)
        return { "success": True }

    def get_modulations(self):
        mods = [ "2FSK", "GFSK", "4FSK", "ASK/OOK", "MSK", "2FSK/Manchester", "GFSK/Manchester", "ASK/OOK/Manchester", "MSK/Manchester" ]
        return mods

    def set_modulation(self, args):
        if not "mod" in args:
            return self.not_supported()
        modvalue = -1
        for modv, modstr in list(MODULATIONS.items()):
            if modstr.split(' ')[0] == args["mod"][0]:
                modvalue = modv
        if modvalue == -1:
            return self.not_supported()
        try:
            nic.setMdmModulation(modvalue)
        except:
            return { "success": False }
        return { "success": True }

    # Fixed Len
    def make_packet_flen(self, args):
        if not "len" in args:
            return self.not_supported()
        try:
            nic.makePktFLEN(int(args["len"][0]))
        except:
            return { "success": False }
        return { "success": True }

    # Variable Len
    def make_packet_vlen(self, args):
        if not "len" in args:
            return self.not_supported()
        try:
            nic.makePktVLEN(int(args["len"][0]))
        except:
            return { "success": False }
        return { "success": True }

    def set_mode(self, args):
        if not "mode" in args:
            return self.not_supported()
        mode = args["mode"][0]
        if mode == "TX" or mode == "tx":
            nic.setModeTX()
        elif mode == "RX" or mode == "rx":
            nic.setModeRX()
        elif mode == "IDLE" or mode == "idle":
            nic.setModeIDLE()
        else:
            return self.not_supported()
        return { "success": True }

    def enablePktCRC(self):
        nic.setEnablePktCRC()
        return { "success": True }

    def enableManchester(self):
        nic.setEnableMdmManchester()
        return { "success": True}

    def set_channel(self, args):
        if not "channel" in args:
            return self.not_supported()
        nic.setChannel(int(args["channel"]))
        return { "success": True }

    def set_channel_bandwidth(self, args):
        mhz = 24
        if not "bw" in args:
            return self.not_supported()
        if "mhz" in args:
            mhz = int(args["mhz"][0])
        nic.setMdmChanBW(int(args["bw"]), mhz)
        return { "success": True }

    def set_channel_spc(self, args):
        chanspc = None
        chanspc_m = None
        chanspc_e = None
        mhz = 24
        if "chanspc" in args:
            chanspc = args["chanspc"]
        if "chanspc_m" in args:
            chanspc_m = args["chanspc_m"]
        if "chanspc_e" in args:
            chanspc_e = args["chanspc_e"]
        try:
            nic.setMdmChanSpc(chanspc, chanspc_m, chanspc_e, mhz)
        except:
            return { "success": False }
        return { "success": True }

    def set_baud_rate(self, args):
        mhz = 24
        if not "rate" in args:
            return self.not_supported()
        if "mhz" in args:
            mhz = int(args["mhz"][0])
        try:
            nic.setMdmDRate(int(args["rate"][0]), mhz)
        except:
            return { "success": False }
        return { "success": True }

    def set_deviation(self, args):
        mhz = 24
        if not "deviat" in args:
            return self.not_supported()
        if "mhz" in args:
            mhz = int(args["mhz"][0])
        try:
            nic.setMdmDeviatn(int(args["deviat"][0]), mhz)
        except:
            return { "success": False }
        return { "success": True }

    def set_sync_word(self, args):
        if not "word" in args:
            return self.not_supported()
        nic.setMdmSyncWord(int(args["word"][0]))
        return { "success": True}

    def set_sync_mode(self, args):
        if not "mode" in args:
            return self.not_supported()
        nic.setMdmSyncMode(int(args["mode"][0]))
        return { "success": True }

    def set_number_preamble(self, args):
        if not "num" in args:
            return self.not_supported()
        nic.setMdmNumPreamble(int(args["num"][0]))
        return { "success": True }

    def set_lowball(self):
        nic.lowball(1)
        return { "success": True }

    def set_maxpower(self):
        nic.setMaxPower()
        return { "success": True }

    def set_power(self, args):
        if not "power" in args:
            return self.not_supported()
        nic.setPower(int(args["power"][0]))
        return { "success": True }

    def rfxmit(self, args):
        repeat = 0
        offset = 0
        if not "data" in args:
            return self.not_supported()
        if "repeat" in args:
            repeat = int(args["repeat"][0])
        if "offset" in args:
            offset = int(args["offset"][0])
        data = base64.urlsafe_b64decode(args["data"][0])
        nic.RFxmit(data, repeat, offset)
        return { "success": True } # Should do some checks here eventually

    def rfrecv(self, args):
        timeout=USB_RX_WAIT
        blocksize=None
        if "timeout" in args:
            timeout = int(args["timeout"][0])
        if "blocksize" in args:
            blocksize = int(args["blocksize"][0])
        try:
            data = nic.RFrecv(timeout, blocksize)
        except:
            return {}
        msg, ts = data
        return { "data": base64.urlsafe_b64encode(msg), "timestamp": ts }
        

    def not_supported(self):
        return { "status": "not supported" }

    def send(self, data, resp=200):
        self.send_response(resp)
        self.send_header('Content-type','application/json')
        self.end_headers()
        self.wfile.write(json.dumps(data))
        return
        
    def do_AUTHHEAD(self):
        self.send_response(401)
        self.send_header('WWW-Authenticate', 'Basic realm=\"RfCat MSF Relay\"')
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        self.wfile.write("Please Authenticate")

    def do_GET(self):
        if not password == None:
            if self.headers.getheader('Authorization') == None:
                print("Did not authenticate")
                self.do_AUTHHEAD()
                return
            if not self.headers.getheader('Authorization') == 'Basic '+base64.b64encode(username + ":" + password):
                print("Bad Authentication")
                self.do_AUTHHEAD()
                return
        url = urlparse(self.path)
        args = parse_qs(url.query)
        if self.path=="/status":
            self.send(self.status())
        elif self.path=="/statistics":
            self.send(self.statistics())
        elif self.path=="/settings/datetime":
            self.send(self.datetime())
        elif self.path=="/settings/timezone":
            self.send(self.timezone())
        elif self.path=="/control/factory_reset":
            self.send(self.reset())
        elif self.path=="/rftransceiver/supported_idx":
            self.send(self.supported_idx())
        elif self.path.startswith("/rftransceiver/"):
            re_idx = re.compile("/rftransceiver/(\d+)/")
            m = re_idx.match(self.path)
            if m:
                idx = m.group(1)
                if self.path.find("/set_freq?") > -1:
                    self.send(self.set_freq(args))
                elif self.path.find("/get_modulations") > -1:
                    self.send(self.get_modulations())
                elif self.path.find("/set_modulation?") > -1:
                    self.send(self.set_modulation(args))
                elif self.path.find("/set_mode?") > -1:
                    self.send(self.set_mode(args))
                elif self.path.find("/make_packet_flen?") > -1:
                    self.send(self.make_packet_flen(args))
                elif self.path.find("/make_packet_vlen?") > -1:
                    self.send(self.make_packet_vlen(args))
                elif self.path.find("/enable_packet_crc") > -1:
                    self.send(self.enablePktCRC())
                elif self.path.find("/enable_manchester") > -1:
                    self.send(self.enableManchester())
                elif self.path.find("/set_channel?") > -1:
                    self.send(self.set_channel(args))
                elif self.path.find("/set_channel_bandwidth?") > -1:
                    self.send(self.set_channel_bandwidth(args))
                elif self.path.find("/set_channel_spc") > -1:
                    self.send(self.set_channel_spc(args))
                elif self.path.find("/set_baud_rate?") > -1:
                    self.send(self.set_baud_rate(args))
                elif self.path.find("/set_deviation?") > -1:
                    self.send(self.set_deviation(args))
                elif self.path.find("/set_sync_word?") > -1:
                    self.send(self.set_sync_word(args))
                elif self.path.find("/set_sync_mode?") > -1:
                    self.send(self.set_sync_mode(args))
                elif self.path.find("/set_number_preamble?") > -1:
                    self.send(self.set_number_preamble(args))
                elif self.path.find("/set_lowball") > -1:
                    self.send(self.set_lowball())
                elif self.path.find("/set_maxpower") > -1:
                    self.send(self.set_maxpower())
                elif self.path.find("/set_power?") > -1:
                    self.send(self.set_power(args))
                elif self.path.find("/rfxmit") > -1:
                    self.send(self.rfxmit(args))
                elif self.path.find("/rfrecv") > -1:
                    self.send(self.rfrecv(args))
                else:
                    self.send(self.not_supported(), 404)
            else:
                self.send(self.not_supported(), 404)
        else:
            self.send(self.not_supported(), 404)
        return

class CC1111NIC_MSFRelay(cmd.Cmd):
    intro = """
       cc1111usb Metasploit Relay
"""

    def __init__(self, nicidx=0, ip='0.0.0.0', nicport=8080):
        cmd.Cmd.__init__(self)
        self.printable = True

        #nic = FHSSNIC(nicidx)
        global nic
        nic = RfCat(nicidx)
        self._ip = ip
        self._nicport = nicport
        self._nicsock = None
        self._pause = False

        self.start()

    def start(self):
        self._go = True
        while self._go:
            # serve the NIC port
            try:
                buf = ''
                self._nicsock = HTTPServer((self._ip, self._nicport), MSFHandler)
                starttime = int(time.time())
                print("RfCat MSFRelay running.")
                self._nicsock.serve_forever()
            except KeyboardInterrupt:
                self._nicsock.socket.close()
                nic.cleanup()
                self._go = False
            except:
                sys.excepthook(*sys.exc_info())



if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('-i', '--index', default=0, type=int)
    parser.add_argument('-u', '--user', default="msf_relay", help='HTTP Username', type=str)
    parser.add_argument('-p', '--password', default="rfcat_relaypass", help='HTTP Password', type=str)
    parser.add_argument('-P', '--Port', default=8080, type=int)
    parser.add_argument('--noauth', default=False, action="store_true", help='Do not require authentication')
    parser.add_argument('--localonly', default=False, action="store_true", help='Listen on localhost only')

    ifo = parser.parse_args()

    username = ifo.user
    password = ifo.password
    ip = "0.0.0.0"
    nicport = ifo.Port
    if ifo.noauth:
         username = None
         password = None
    if ifo.localonly:
         host = "127.0.0.1"

    dongleserver = CC1111NIC_MSFRelay(ifo.index, ip, nicport)
    
import atexit
atexit.register(cleanupInteractiveAtExit)
