coolstuff2 / services /mc_tunnel.py
SamuelLance73's picture
Automated deployment update from ML build
a93b3b5 verified
Raw
History Blame Contribute Delete
6.71 kB
_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 A<len(buf):
D=buf[A];A+=1;C|=(D&127)<<B
if not D&128:return C,A
B+=7
if B>35: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)<B:A._fill()
F=A._buf[:B];A.consumed+=F;D=A._buf[C:B];A._buf=A._buf[B:];G,H=read_varint_from_buf(D,0);return G,D[H:]
def extract_tunnel_from_response(payload):
B=payload;E,A=read_varint_from_buf(B,0)
if A>=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