import random
import selectors
import socket
import struct
import time

GS10 = "00:13:43:02:E9:EF"
LOCAL_MAC = bytes.fromhex("b827ebf49190")
LOCAL_IP = bytes([192, 168, 253, 1])
REMOTE_IP = bytes([192, 168, 253, 2])
LISTEN = ("127.0.0.1", 8253)
REMOTE_PORT = 80
MSS = 1400
FIN, SYN, RST, PSH, ACK = 0x01, 0x02, 0x04, 0x08, 0x10
BNEP_OFFSETS = {0: 13, 2: 1, 3: 7, 4: 7}
CONTROL_REPLIES = {0x01: 0x02, 0x03: 0x04, 0x05: 0x06}


def log(*a):
    print(*a, flush=True)


def fold(s):
    return s if s <= 0xFFFF else fold((s & 0xFFFF) + (s >> 16))


def checksum(b):
    padded = b + b"\0" * (len(b) % 2)
    return struct.pack("!H", ~fold(sum(struct.unpack(f"!{len(padded) // 2}H", padded))) & 0xFFFF)


def ipv4(proto, payload):
    head = struct.pack("!BBHHHBB", 0x45, 0, 20 + len(payload), 0, 0x4000, 64, proto)
    addrs = LOCAL_IP + REMOTE_IP
    return head + checksum(head + b"\0\0" + addrs) + addrs + payload


def tcp(sport, seq, ack, flags, data=b""):
    opts = struct.pack("!BBH", 2, 4, MSS) if flags & SYN else b""
    head = struct.pack("!HHIIBBH", sport, REMOTE_PORT, seq, ack, ((20 + len(opts)) // 4) << 4, flags, 65535)
    pseudo = LOCAL_IP + REMOTE_IP + struct.pack("!BBH", 0, 6, 20 + len(opts) + len(data))
    return head + checksum(pseudo + head + b"\0\0\0\0" + opts + data) + b"\0\0" + opts + data


def skip_extensions(frame, i):
    end = i + 2 + frame[i + 1]
    return skip_extensions(frame, end) if frame[i] & 0x80 else end


class Conn:
    def __init__(self, bridge, client, port):
        self.bridge, self.client, self.port = bridge, client, port
        self.snd_nxt = random.getrandbits(32)
        self.rcv_nxt = 0
        self.established = False
        self.pending = b""
        self.wants_fin = False
        self.fin_sent = False

    def emit(self, flags, data=b""):
        self.bridge.frame(0x0800, ipv4(6, tcp(self.port, self.snd_nxt, self.rcv_nxt, flags, data)))
        self.snd_nxt = (self.snd_nxt + len(data) + (1 if flags & (SYN | FIN) else 0)) & 0xFFFFFFFF

    def write(self, data):
        if not self.established:
            self.pending += data
            return
        list(map(lambda i: self.emit(PSH | ACK, data[i:i + MSS]), range(0, len(data), MSS)))

    def finish(self):
        if not self.established:
            self.wants_fin = True
        elif not self.fin_sent:
            self.fin_sent = True
            self.emit(FIN | ACK)

    def drop(self):
        self.bridge.conns.pop(self.port, None)
        if self.client in self.bridge.registered():
            self.bridge.sel.unregister(self.client)
        self.client.close()

    def deliver(self, data):
        try:
            self.client.sendall(data)
        except OSError:
            self.emit(RST | ACK)
            self.drop()

    def segment(self, t):
        flags = t[13]
        seq = struct.unpack("!I", t[4:8])[0]
        data = t[(t[12] >> 4) * 4:]
        if flags & RST:
            return self.drop()
        if not self.established and flags & (SYN | ACK) == SYN | ACK:
            self.rcv_nxt = (seq + 1) & 0xFFFFFFFF
            self.established = True
            self.emit(ACK)
            self.write(self.pending)
            self.pending = b""
            return self.finish() if self.wants_fin else None
        in_order = seq == self.rcv_nxt
        if in_order and data:
            self.rcv_nxt = (self.rcv_nxt + len(data)) & 0xFFFFFFFF
            self.deliver(data)
        fin = bool(flags & FIN) and in_order
        if fin:
            self.rcv_nxt = (self.rcv_nxt + 1) & 0xFFFFFFFF
        if data or flags & FIN:
            self.emit(ACK)
        if fin:
            self.finish()
            self.drop()


class Bridge:
    def __init__(self):
        self.sel = selectors.DefaultSelector()
        self.conns = {}
        self.next_port = random.randint(40000, 50000)
        self.bt = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP)
        self.bt.setsockopt(6, 1, struct.pack("HHHBBBH", 1691, 1691, 0xFFFF, 0, 1, 0, 0) + b"\0")
        self.bt.connect((GS10, 15))
        self.sel.register(self.bt, selectors.EVENT_READ, self.on_bt)
        self.server = socket.create_server(LISTEN, reuse_port=True)
        self.bt.send(bytes([0x01, 0x01, 0x02, 0x11, 0x17, 0x11, 0x15]))

    def registered(self):
        return [k.fileobj for k in self.sel.get_map().values()]

    def frame(self, ethertype, payload):
        self.bt.send(bytes([0x02]) + struct.pack("!H", ethertype) + payload)

    def on_bt(self, _):
        frame = self.bt.recv(2048)
        if not frame:
            raise ConnectionError("BNEP channel closed")
        kind = frame[0] & 0x7F
        if kind == 0x01:
            return self.control(frame[1:])
        at = BNEP_OFFSETS.get(kind)
        if at is None:
            return log("bnep type", kind)
        start = skip_extensions(frame, at + 2) if frame[0] & 0x80 else at + 2
        handlers = {0x0806: self.arp, 0x0800: self.ip}
        handlers.get(struct.unpack("!H", frame[at:at + 2])[0], lambda _: None)(frame[start:])

    def control(self, c):
        if c[0] == 0x02:
            code = struct.unpack("!H", c[1:3])[0]
            log("BNEP setup response", code)
            if code != 0:
                raise ConnectionError(f"BNEP setup refused: {code}")
            return self.ready()
        reply = CONTROL_REPLIES.get(c[0])
        return self.bt.send(bytes([0x01, reply, 0, 0])) if reply else log("bnep control", c.hex())

    def ready(self):
        self.sel.register(self.server, selectors.EVENT_READ, self.on_accept)
        log(f"ready: {LISTEN[0]}:{LISTEN[1]} -> 192.168.253.2:{REMOTE_PORT}")

    def arp(self, a):
        if struct.unpack("!H", a[6:8])[0] == 1 and a[24:28] == LOCAL_IP:
            self.frame(0x0806, bytes([0, 1, 8, 0, 6, 4, 0, 2]) + LOCAL_MAC + LOCAL_IP + a[8:18])

    def ip(self, p):
        if p[9] != 6:
            return
        t = p[(p[0] & 0x0F) * 4:struct.unpack("!H", p[2:4])[0]]
        conn = self.conns.get(struct.unpack("!H", t[2:4])[0])
        return conn.segment(t) if conn else None

    def on_accept(self, _):
        client, _ = self.server.accept()
        self.next_port = 40000 + (self.next_port - 39999) % 20000
        conn = Conn(self, client, self.next_port)
        self.conns[conn.port] = conn
        self.sel.register(client, selectors.EVENT_READ, lambda _: self.on_client(conn))
        conn.emit(SYN)

    def on_client(self, conn):
        data = conn.client.recv(65536)
        if data:
            return conn.write(data)
        self.sel.unregister(conn.client)
        conn.finish()

    def run(self):
        while True:
            list(map(lambda ev: ev[0].data(ev[0].fileobj), self.sel.select()))


if __name__ == "__main__":
    while True:
        try:
            Bridge().run()
        except OSError as e:
            log("bridge down:", e)
        time.sleep(5)
