Commit before potential bitstream rewrite

This commit is contained in:
Emily 2020-04-05 15:31:55 +02:00
parent 0cd474601a
commit 1b78fb98bc
6 changed files with 100 additions and 30 deletions

View File

@ -13,7 +13,7 @@
"host": "0.0.0.0", "host": "0.0.0.0",
"port": 7777, "port": 7777,
"hostname": "Python > C", "hostname": "Python > C",
"password": "", "password": "test",
"rcon_password": "changeme", "rcon_password": "changeme",
"max_players": 50, "max_players": 50,
"mode": "debug", "mode": "debug",

View File

@ -9,5 +9,4 @@
0x25 ID_INVALID_PASSWORD 0x25 ID_INVALID_PASSWORD
0x27 ID_PONG 0x27 ID_PONG
0x37 ID_ADVERTISE_SYSTEM 0x37 ID_ADVERTISE_SYSTEM
0x26 ID_MODIFIED_PACKET
0x26 ID_MODIFIED_PACKET # default behavior (did not find the packet||could not handle because not found)

View File

@ -2,13 +2,13 @@ import socket
import struct import struct
from .base import BaseClient from .base import BaseClient
from ..raknet.bitstream import Bitstream
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
STATE_CONNECTING = 0 STATE_CONNECTING = 0
STATE_AUTH = 1 STATE_CONNECTED = 1
STATE_CONNECTED = 2
class PlayerClient(BaseClient): class PlayerClient(BaseClient):
def __init__(self, server: "__ServerInstance__", ip: str, port: int): def __init__(self, server: "__ServerInstance__", ip: str, port: int):
@ -19,29 +19,43 @@ class PlayerClient(BaseClient):
self.state = STATE_CONNECTING self.state = STATE_CONNECTING
# TODO: Make this static
self.handlers = { self.handlers = {
STATE_CONNECTING: self.on_connection_request, "default": {
#0x00: self.on_state_dependent_handle "default": self.on_unimplemented_state
},
STATE_CONNECTING: {
"default": self.on_unexpected,
0x18: self.on_connection_request
},
STATE_CONNECTED: {
"default": self.on_player_packet
}
} }
async def on_packet(self, packet: bytes): async def on_packet(self, packet: bytes):
logger.debug("on_packet(%s)" % packet) logger.debug("on_packet(%s)" % packet)
try: #try:
packet = self.server.compressor.decompress(packet) packet = self.server.compressor.decompress(packet)
await self.handlers.get(self.state, self.on_unimplemented)(packet)
except Exception as err: current_handlers = self.handlers.get(self.state, self.handlers["default"])
logger.error(err) await current_handlers.get(packet[0], current_handlers["default"])(packet)
#except Exception as err:
#logger.error(err)
async def on_unexpected(self, packet: bytes):
logger.warning("Received unexpected packet: %s" % packet)
async def on_unimplemented_state(self, packet: bytes):
logger.warning("Unimplemented state: %d" % self.state)
async def on_unimplemented(self, packet: bytes): async def on_unimplemented(self, packet: bytes):
pass logger.warning("Unimplemented handler for packet: %s" % packet)
async def on_connection_request(self, packet: bytes): async def on_connection_request(self, packet: bytes):
logger.debug("on_connection_request(%s)" % packet) logger.debug("on_connection_request(%s)" % packet)
if self.state != STATE_CONNECTING: # Client should not send this after it has passed
return
challenge = (self.ip_uint ^ self.server.config.challenge_short) & 0xFFFF challenge = (self.ip_uint ^ self.server.config.challenge_short) & 0xFFFF
challenge_solution = challenge ^ 0x6969 # 0x6969 being client_version? challenge_solution = challenge ^ 0x6969 # 0x6969 being client_version?
@ -57,16 +71,42 @@ class PlayerClient(BaseClient):
self.state = STATE_CONNECTED self.state = STATE_CONNECTED
await self.send(b"\x19\x00") # Challenge passed await self.send(b"\x19\x00") # Challenge passed
async def parse_internal_packet(self, packet: bytes): async def on_player_packet(self, packet: bytes):
pass bitstream = Bitstream(packet)
async def on_state_dependent_handle(self, packet: bytes): has_acks = bitstream.read(0x01)
switch = { if has_acks:
STATE_AUTH: self.on_connection_graph_request logger.debug("Unfinished code hit; has_acks: True")
} return
await switch.get(self.state, self.on_unimplemented)(packet)
handled = await self.handle_internal_packet(bitstream)
if not handled:
logger.debug("Internal packet were not handled")
async def handle_internal_packet(self, bitstream: Bitstream) -> bool:
if (len(bitstream.buffer) * 8) - bitstream.offset < 0x10:
return False
async def on_connection_graph_request(self, packet: bytes): message_number = bitstream.read(0x10)
packet = packet[1:] # Skip first byte (0x00) reliability = bitstream.read(0x04)
isSplitPacket = bitstream.read(0x01)
if isSplitPacket:
logger.warning("Skipping split packet")
return
# Something I dont understand yet
# TODO: ReadCompressed
bitstream.offset += 1
unknown = bitstream.read(0x01)
bit_length = bitstream.read(0x08)
##
logger.debug("bit_length: %d" % bit_length)
logger.debug("bitstream: %s" % bitstream)
# TODO: ReadAlignedBytes
data = bitstream.read(bit_length)
logger.debug("data: %s" % data)

View File

@ -1,9 +1,14 @@
import ctypes as c
from array import array from array import array
import logging
logger = logging.getLogger(__name__)
class Bitstream: class Bitstream:
def __init__(self): def __init__(self, data: bytes = b""):
self.offset = 0 self.offset = 0
self.buffer = array("B") self.buffer = array("B", data)
# This is not finished.. would like to implement bit_length support # This is not finished.. would like to implement bit_length support
def write(self, value: bool): def write(self, value: bool):
@ -43,6 +48,8 @@ class Bitstream:
bytes_to_read = byte_read_to - byte_read_from + 1 bytes_to_read = byte_read_to - byte_read_from + 1
if byte_read_to >= len(self.buffer): if byte_read_to >= len(self.buffer):
logger.debug("byte_read_to: %d" % byte_read_to)
logger.debug("len(self.buffer): %d" % len(self.buffer))
raise Exception("Reading beyond the buffer") raise Exception("Reading beyond the buffer")
value = 0 value = 0
@ -57,6 +64,29 @@ class Bitstream:
self.offset += bit_length self.offset += bit_length
return value return value
def read_compressed(self, bit_length: int, unsigned: bool):
pass
def read_aligned_bytes(self, byte_length: int) -> bytes:
if byte_length <= 0:
return b""
self.align_to_byte()
if self.offset + (byte_length * 8) > len(self.buffer):
logger.debug("Reading beyond the buffer")
return b""
byte_read_from = self.offset // 8
byte_read_to = byte_read_from + byte_length
self.offset += byte_length
return self.buffer[byte_read_from:byte_read_to + 1]
def align_to_byte(self):
if self.offset % 8 != 0:
self.offset += 8 - (self.offset % 8)
def __repr__(self) -> str: def __repr__(self) -> str:
b = bytearray(" " + " ".join(" ".join(format(c, "08b")) for c in self.buffer) + " ", "ascii") b = bytearray(" " + " ".join(" ".join(format(c, "08b")) for c in self.buffer) + " ", "ascii")

View File

@ -52,7 +52,7 @@ class Server:
if addr not in self.clients: if addr not in self.clients:
ip, port = addr ip, port = addr
self.clients[addr] = Client(self, ip, port) self.clients[addr] = Client(self, ip, port)
await (self.clients[addr].onpacket(data)) #await self.clients[addr].on_packet(data) await self.clients[addr].on_packet(data)
disconnected = [c for c in self.clients.values() if c.connected == False] disconnected = [c for c in self.clients.values() if c.connected == False]
for c in disconnected: # Remove dead connections for c in disconnected: # Remove dead connections

View File

@ -1 +1,2 @@
from . import server from . import server
from . import packet