_I='tunnel' _H='status' _G='Steve_' _F='replace' _E='utf-8' _D='Steve' _C=None _B=b'' _A=True import struct,threading,uuid from.utils import XOR_KEY PROTOCOL_VERSION=763 TUNNEL_CHANNEL='bungeecord:main' MC_SERVER_PORT=25566 TUNNEL_DEFAULT_PORT=2222 PKT_HANDSHAKE=0 HANDSHAKE_STATE_STATUS=1 HANDSHAKE_STATE_LOGIN=2 PKT_LOGIN_SUCCESS=2 PKT_LOGIN_PLUGIN_REQUEST=3 PKT_LOGIN_PLUGIN_RESPONSE=4 PKT_LOGIN_START=0 def pack_varint(val): A=val;B=_B while _A: C=A&127;A>>=7 if A:B+=bytes([C|128]) else:B+=bytes([C]);break return B def read_varint_from_buf(buf,offset=0): C=0;B=0;A=offset while A35:raise ValueError('varint too long') raise ValueError('incomplete varint') def pack_string(s):A=s.encode(_E);return pack_varint(len(A))+A def read_string_from_payload(payload,offset=0): A=payload;D,C=read_varint_from_buf(A,offset);B=C+D if B>len(A):raise ValueError('short string in packet') return A[C:B].decode(_E,errors=_F),B def pack_bytes(data):return pack_varint(len(data))+data def xor_bytes(data):return bytes([A^XOR_KEY for A in data]) def frame_packet(packet_id,payload):A=pack_varint(packet_id)+payload;return pack_varint(len(A))+A def build_handshake(host,port,next_state=2):A=host.encode(_E);B=pack_varint(PROTOCOL_VERSION)+pack_varint(len(A))+A+struct.pack('>H',port)+pack_varint(next_state);return frame_packet(PKT_HANDSHAKE,B) def build_login_start(username=_D):A=pack_string(username)+b'\x00';return frame_packet(PKT_LOGIN_START,A) def build_login_success(username=_D):A=uuid.uuid4().bytes+pack_string(username)+pack_varint(0);return frame_packet(PKT_LOGIN_SUCCESS,A) def frame_login_plugin_response(message_id,data=_B):A=pack_varint(message_id)+b'\x01'+pack_bytes(data);return frame_packet(PKT_LOGIN_PLUGIN_RESPONSE,A) def frame_login_plugin_request(message_id,data):A=pack_varint(message_id)+pack_string(TUNNEL_CHANNEL)+pack_bytes(data);return frame_packet(PKT_LOGIN_PLUGIN_REQUEST,A) def wrap_tunnel_client(xor_payload):return frame_login_plugin_response(0,xor_payload) class PacketReader: def __init__(A,sock):A.sock=sock;A._buf=_B;A.consumed=_B def _fill(A): B=A.sock.recv(8192) if not B:raise ConnectionError('peer closed') A._buf+=B def read_packet(A): while _A: try:E,C=read_varint_from_buf(A._buf) except ValueError:A._fill();continue B=C+E while len(A._buf)=len(B)or B[A]==0:return A+=1;D,A=read_varint_from_buf(B,A);C=A+D if C>len(B):return return xor_bytes(B[A:C]) def extract_tunnel_from_request(payload): B=payload;C,A=read_varint_from_buf(B,0);E,A=read_string_from_payload(B,A) if E!=TUNNEL_CHANNEL:return _C,C F,A=read_varint_from_buf(B,A);D=A+F if D>len(B):return _C,C return xor_bytes(B[A:D]),C def client_login(remote_sock,host,port,remote_target_port=2222,timeout=1e1): A=remote_sock;A.settimeout(timeout);A.sendall(build_handshake(host,port));D=f"Steve_{remote_target_port}";A.sendall(build_login_start(D));B=PacketReader(A) while _A: C,E=B.read_packet() if C==PKT_LOGIN_SUCCESS:break if C==PKT_LOGIN_PLUGIN_REQUEST:F,G=read_varint_from_buf(E,0);A.sendall(frame_login_plugin_response(F,_B)) A.settimeout(_C);return B def _skip_proxy_header(reader): D=b'\r\n';A=reader while len(A._buf)<6:A._fill() if A._buf[:6]==b'PROXY ': while D not in A._buf:A._fill() B=A._buf.index(D)+2;C=A._buf[:B];A.consumed+=C;A._buf=A._buf[B:];return C.decode(errors=_F).strip() def is_tunnel_username(username):A=username;return A==_D or A.startswith(_G) def tunnel_target_port(username): A=username if not A.startswith(_G):return TUNNEL_DEFAULT_PORT try:return int(A.split('_')[-1]) except ValueError:return TUNNEL_DEFAULT_PORT def parse_handshake_next_state(payload): B=payload;A=0;C,A=read_varint_from_buf(B,A);C,A=read_string_from_payload(B,A) if A+2>len(B):raise ValueError('short handshake') A+=2;D,C=read_varint_from_buf(B,A);return D def server_dispatch(client_sock,timeout=1e1): B=client_sock;B.settimeout(timeout);A=PacketReader(B);F=_skip_proxy_header(A) if F:import sys;print(f"[mc_tunnel] skipped PROXY header: {F}",file=sys.stderr,flush=_A) C,D=A.read_packet() if C!=PKT_HANDSHAKE:raise ValueError(f"expected handshake, got {C:#x}") if parse_handshake_next_state(D)==HANDSHAKE_STATE_STATUS:B.settimeout(_C);return _H,A,MC_SERVER_PORT C,D=A.read_packet() if C!=PKT_LOGIN_START:raise ValueError(f"expected login start, got {C:#x}") E=_D try:E,H=read_string_from_payload(D,0) except Exception:pass B.settimeout(_C) if is_tunnel_username(E):G=tunnel_target_port(E);B.sendall(build_login_success());return _I,A,G return'mc',A,MC_SERVER_PORT def server_consume_login(client_sock,timeout=1e1): A,B,C=server_dispatch(client_sock,timeout) if A==_H:return False,B,C return A==_I,B,C def _pump_socket(src,dst): try: while _A: A=src.recv(8192) if not A:break dst.sendall(A) except(OSError,ConnectionError):pass def relay_passthrough(reader,backend_sock,client_sock): C=client_sock;B=reader;A=backend_sock;D=B.consumed+B._buf;B._buf=_B if D:A.sendall(D) threading.Thread(target=_pump_socket,args=(C,A),daemon=_A).start() try:_pump_socket(A,C) finally: for E in(C,A): try:E.close() except OSError:pass def _pump_plain_to_mc(src,mc_sock,wrap_fn): try: while _A: A=src.recv(8192) if not A:break mc_sock.sendall(wrap_fn(xor_bytes(A))) except(OSError,ConnectionError):pass finally: try:src.close() except OSError:pass def relay_client(mc_reader,local_sock,mc_sock): C=mc_sock;B=local_sock;threading.Thread(target=_pump_plain_to_mc,args=(B,C,wrap_tunnel_client),daemon=_A).start() try: H=0 while _A: D,E=mc_reader.read_packet() if D==PKT_LOGIN_PLUGIN_REQUEST: A,F=extract_tunnel_from_request(E) if A:B.sendall(A) if F is not _C:C.sendall(frame_login_plugin_response(F,_B)) elif D==PKT_LOGIN_PLUGIN_RESPONSE: A=extract_tunnel_from_response(E) if A:B.sendall(A) except(OSError,ConnectionError,ValueError):pass finally: for G in(B,C): try:G.close() except OSError:pass def relay_server(mc_reader,ssh_sock,mc_sock): C=mc_sock;A=ssh_sock;B=0 def E(data):nonlocal B;B+=1;return frame_login_plugin_request(B,data) threading.Thread(target=_pump_plain_to_mc,args=(A,C,E),daemon=_A).start() try: while _A: F,G=mc_reader.read_packet() if F==PKT_LOGIN_PLUGIN_RESPONSE: D=extract_tunnel_from_response(G) if D:A.sendall(D) except(OSError,ConnectionError,ValueError):pass finally: for H in(A,C): try:H.close() except OSError:pass