# -*- coding: utf-8 -*-
"""Reverse tunnel via localhost.run (raw SSH :22) -> 127.0.0.1:8899.
替代被机房封锁的 cloudflared quick tunnel。URL 写入 /tmp/lr_url.txt。"""
import paramiko, socket, threading, select, time, re, sys

REMOTE_HOST = "ssh.localhost.run"
REMOTE_PORT = 22
LOCAL_HOST, LOCAL_PORT = "127.0.0.1", 8899
USERNAME = "nokey"

def log(msg):
    sys.stderr.write(msg + "\n"); sys.stderr.flush()

def pipe(chan):
    log("pipe: forward channel opened")
    sock = socket.socket()
    try:
        sock.connect((LOCAL_HOST, LOCAL_PORT))
    except Exception as e:
        log("pipe connect fail: %r" % e)
        chan.close(); return
    log("pipe: connected to local %d" % LOCAL_PORT)
    sock.setblocking(False); chan.settimeout(1)
    total_up = total_down = 0
    try:
        while True:
            r, _, _ = select.select([sock, chan], [], [], 5)
            if sock in r:
                try: data = sock.recv(65536)
                except BlockingIOError: data = None
                if not data: break
                chan.sendall(data)
            if chan in r:
                try: data = chan.recv(65536)
                except socket.timeout: data = None
                except Exception: data = None
                if data is None: continue
                if not data: break
                sock.sendall(data)
    finally:
        try: chan.close()
        except Exception: pass
        try: sock.close()
        except Exception: pass

def accept_loop(t):
    while True:
        try:
            chan = t.accept(1000)
        except Exception as e:
            log("accept error: %r" % (e,))
            if not t.is_active(): return
            continue
        if chan is None:
            if not t.is_active(): return
            continue
        threading.Thread(target=pipe, args=(chan,), daemon=True).start()

def try_once():
    tcp = socket.create_connection((REMOTE_HOST, REMOTE_PORT), timeout=15)
    t = paramiko.Transport(tcp)
    t.set_keepalive(25)
    t.banner = "SSH-2.0-OpenSSH_8.9"
    t.local_version = "SSH-2.0-OpenSSH_8.9"
    key = paramiko.RSAKey.generate(2048)
    t.start_client(timeout=20)
    t.auth_publickey(USERNAME, key)
    log("auth ok")
    bound = t.request_port_forward("", 80)
    log("bound remote port: %r" % (bound,))
    threading.Thread(target=accept_loop, args=(t,), daemon=True).start()
    chan = t.open_session()
    try:
        chan.get_pty()
    except Exception:
        pass
    try:
        chan.invoke_shell()
    except Exception:
        pass
    buf, url, deadline = b"", None, time.time() + 60
    while time.time() < deadline and t.is_active():
        try:
            if chan.recv_ready():
                buf += chan.recv(4096)
                txt = buf.decode("utf-8", "replace")
                m = re.search(r"https://[a-zA-Z0-9-]+\.(?:localhost\.run|lhr\.life)", txt)
                if m:
                    url = m.group(0)
                    break
                buf = buf[-8000:]
            else:
                time.sleep(0.3)
        except Exception:
            time.sleep(0.3)
    if url:
        def drain(c=chan):
            while t.is_active() and not c.closed:
                try:
                    if c.recv_ready():
                        d = c.recv(4096)
                        if d: log("shell> %s" % d.decode("utf-8", "replace")[:120])
                        else: break
                    else:
                        time.sleep(1)
                except Exception:
                    time.sleep(1)
        threading.Thread(target=drain, daemon=True).start()
    if not url:
        sys.stdout.write(buf.decode("utf-8", "replace")[-1500:])
        try: t.close()
        except Exception: pass
        return None, None, None
    return url, t, chan

attempt = 0
while True:
    attempt += 1
    log("=== attempt %d ===" % attempt)
    chan = None
    try:
        url, t, chan = try_once()
    except Exception as e:
        log("attempt %d error: %r" % (attempt, e))
        url = None
    if url:
        with open("/tmp/lr_url.txt", "w") as f:
            f.write(url + "\n")
        log("URL=%s" % url)
        while t.is_active() and (chan is None or not chan.closed):
            time.sleep(5)
        log("transport/session died")
    time.sleep(5)
log("all attempts failed")
