Integrate connless messages into packet.parse7()

This commit is contained in:
ChillerDragon 2023-05-09 20:39:13 +02:00
parent 7874d769aa
commit ff4c28fd42
8 changed files with 169 additions and 37 deletions

View file

@ -3,6 +3,8 @@ from twnet_parser.packet import \
from twnet_parser.chunk_header import \
ChunkHeader
from tests.helper import cmp_bytes_as_bin
def test_chunk_header_unpack_vital1() -> None:
parser = ChunkHeaderParser()
header: ChunkHeader = parser.parse_header7(b'\x40\x10\x0a')
@ -13,11 +15,6 @@ def test_chunk_header_unpack_vital1() -> None:
assert header.size == 16
assert header.seq == 10
def cmp_bytes_as_bin(got: bytes, expected: bytes) -> None:
g_bin = [format(int(x), '#010b') for x in got]
e_bin = [format(int(x), '#010b') for x in expected]
assert g_bin == e_bin
def test_chunk_header_pack_vital1() -> None:
header: ChunkHeader = ChunkHeader()

5
tests/helper.py Normal file
View file

@ -0,0 +1,5 @@
def cmp_bytes_as_bin(got: bytes, expected: bytes) -> None:
g_bin = [format(int(x), '#010b') for x in got]
e_bin = [format(int(x), '#010b') for x in expected]
assert g_bin == e_bin

View file

@ -185,14 +185,14 @@ def test_packet_header_repack_all_set() -> None:
header.flags.control = True
header.flags.resend = True
header.flags.compression = True
header.flags.connless = True
header.flags.connless = False
# Note that even if we set everything
# we still end up with two leading zeros
# because those bits are unused
# and we init the flags with 0
assert header.pack() == bytes([ \
0b00111111, \
0b00011111, \
0b11111111, \
0b11111111, \
0b11111111, \
@ -202,7 +202,7 @@ def test_packet_header_repack_all_set() -> None:
])
parser = PacketHeaderParser7()
header = parser.parse_header(b'\xff\xff\xff\xff\xff\xff\xff')
header = parser.parse_header(b'\x1f\xff\xff\xff\xff\xff\xff')
assert header.ack == 1023
assert header.token == b'\xff\xff\xff\xff'
@ -211,19 +211,19 @@ def test_packet_header_repack_all_set() -> None:
assert header.flags.control is True
assert header.flags.resend is True
assert header.flags.compression is True
assert header.flags.connless is True
assert header.flags.connless is False
# Note that is doesn matter wether we parse
#
# b'\xff\xff\xff\xff\xff\xff\xff'
# b'\x1f\xff\xff\xff\xff\xff\xff'
#
# or
#
# b'\x3f\xff\xff\xff\xff\xff\xff'
# b'\xdf\xff\xff\xff\xff\xff\xff'
#
# because the first two bytes are ignored anyways
parser = PacketHeaderParser7()
header = parser.parse_header(b'\x3f\xff\xff\xff\xff\xff\xff')
header = parser.parse_header(b'\xdf\xff\xff\xff\xff\xff\xff')
assert header.ack == 1023
assert header.token == b'\xff\xff\xff\xff'
@ -232,7 +232,7 @@ def test_packet_header_repack_all_set() -> None:
assert header.flags.control is True
assert header.flags.resend is True
assert header.flags.compression is True
assert header.flags.connless is True
assert header.flags.connless is False
def test_packet_header_repack_none_set() -> None:
header: PacketHeader = PacketHeader()

102
tests/packets7/lis2_test.py Normal file
View file

@ -0,0 +1,102 @@
from twnet_parser.packet import parse7
def test_lis2_connless7():
data = b'\x21\xc2\xc7\x28\x7a\xa5' \
b'\xba\x7f\x3b\xff\xff\xff\xff\x6c\x69\x73\x32\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\xff\xff\x05\x4e\x49\x11\x20\x6a\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\xff\xff\x05\x4e\x49\x11\x20\x6c\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x05\x4e\x49\x11\x20' \
b'\x6f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x05\x4e\x49' \
b'\x11\x20\x70\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x05' \
b'\x4e\x49\x11\x20\x71\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff' \
b'\xff\x05\x4e\x49\x11\x20\x72\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\xff\xff\x05\x4e\x49\x11\x20\x74\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\xff\xff\x05\x4e\x49\x11\x20\x75\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\xff\xff\x05\xb5\x7c\x54\x20\x6f\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\xff\xff\x05\xb5\x7c\x54\x20\x70\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x05\xb5\x7c\x54\x20' \
b'\x71\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x05\xb5\x7c' \
b'\x54\x20\x72\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x05' \
b'\xb5\x7c\x54\x20\x73\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff' \
b'\xff\x05\xb5\x7c\x54\x20\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\xff\xff\x17\xed\x1a\x05\x20\x91\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\xff\xff\x17\xed\x1a\x05\x20\x92\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\xff\xff\x1f\x28\xd4\xb3\x20\x0b\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\xff\xff\x1f\x28\xd4\xb3\x20\x6a\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x1f\x28\xd4\xb3\x20' \
b'\x6c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x1f\x28\xd4' \
b'\xb3\x20\x6f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x1f' \
b'\x28\xd4\xb3\x20\x70\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff' \
b'\xff\x1f\x28\xd4\xb3\x20\x71\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\xff\xff\x1f\x28\xd4\xb3\x20\x72\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\xff\xff\x1f\xba\xfa\x2c\x20\x6f\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\xff\xff\x1f\xba\xfa\x2c\x20\x70\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\xff\xff\x1f\xba\xfa\x2c\x20\x71\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x1f\xba\xfa\x2c\x20' \
b'\x72\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x23\xcc\x77' \
b'\xd6\x20\x92\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25' \
b'\xe6\xd2\x54\x20\x6f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff' \
b'\xff\x25\xe6\xd2\x54\x20\x70\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\xff\xff\x25\xe6\xd2\x54\x20\x71\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x0b\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x0c\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x6a\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20' \
b'\x6c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2' \
b'\xe7\x20\x6d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25' \
b'\xe6\xd2\xe7\x20\x6e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff' \
b'\xff\x25\xe6\xd2\xe7\x20\x6f\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\xff\xff\x25\xe6\xd2\xe7\x20\x70\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x72\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x74\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x75\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20' \
b'\x77\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2' \
b'\xe7\x20\x78\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25' \
b'\xe6\xd2\xe7\x20\x79\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff' \
b'\xff\x25\xe6\xd2\xe7\x20\x7a\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\xff\xff\x25\xe6\xd2\xe7\x20\x7c\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x7d\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x7e\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x7f\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20' \
b'\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2' \
b'\xe7\x20\x81\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25' \
b'\xe6\xd2\xe7\x20\x82\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff' \
b'\xff\x25\xe6\xd2\xe7\x20\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\xff\xff\x25\xe6\xd2\xe7\x20\x84\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x86\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x87\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x88\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20' \
b'\x8a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2' \
b'\xe7\x20\x8b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25' \
b'\xe6\xd2\xe7\x20\x8c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff' \
b'\xff\x25\xe6\xd2\xe7\x20\x8d\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\xff\xff\x25\xe6\xd2\xe7\x20\x8e\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x8f\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x90\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x91\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20' \
b'\x92\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2' \
b'\xe7\x20\x93\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25' \
b'\xe6\xd2\xe7\x20\x94\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff' \
b'\xff\x25\xe6\xd2\xe7\x20\x95\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\xff\xff\x25\xe6\xd2\xe7\x20\x96\x00\x00\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x97\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x98\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20\x99\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x25\xe6\xd2\xe7\x20' \
b'\x9a'
packet = parse7(data)
assert packet.version == '0.7'
assert packet.header.token == b'\xc2\xc7\x28\x7a'
assert packet.header.response_token == b'\xa5\xba\x7f\x3b'
assert packet.header.flags.control is False
assert packet.header.flags.compression is False
assert packet.header.flags.resend is False
assert packet.header.flags.connless is True

View file

@ -1,2 +1,3 @@
NET_MAX_SEQUENCE = 1<<10
NET_MAX_PACKETSIZE = 1400
NET_PACKETVERSION = 1 # connless version field

View file

@ -1,15 +1,16 @@
# connless
REQUEST_LIST = [255, 255, 255, 255, 114, 101, 113, 50]
LIST = [255, 255, 255, 255, 108, 105, 115, 50]
REQUEST_COUNT = [255, 255, 255, 255, 99, 111, 117, 50]
COUNT = [255, 255, 255, 255, 115, 105, 122, 50]
REQUEST_INFO = [255, 255, 255, 255, 103, 105, 101, 51]
HEARTBEAT = [255, 255, 255, 255, 98, 101, 97, 50]
FORWARD_CHECK = [255, 255, 255, 255, 102, 119, 63, 63]
FORWARD_RESPONSE = [255, 255, 255, 255, 102, 119, 33, 33]
FORWARD_OK = [255, 255, 255, 255, 102, 119, 111, 107]
FORWARD_ERROR = [255, 255, 255, 255, 102, 119, 101, 114]
HEARTBEAT = b'\xff\xff\xff\xffbea2'
REQUEST_LIST = b'\xff\xff\xff\xffreq2'
LIST = b'\xff\xff\xff\xfflis2'
REQUEST_COUNT = b'\xff\xff\xff\xffcou2'
COUNT = b'\xff\xff\xff\xffsiz2'
REQUEST_INFO = b'\xff\xff\xff\xffgie3'
# INFO = todo not in libtw2 and name conficts with sys.info
FORWARD_CHECK = b'\xff\xff\xff\xfffw??'
FORWARD_RESPONSE = b'\xff\xff\xff\xfffw!!'
FORWARD_OK = b'\xff\xff\xff\xfffwok'
FORWARD_ERROR = b'\xff\xff\xff\xfffwer'
# control

View file

@ -25,7 +25,7 @@ import twnet_parser.messages7.connless.forward_ok as \
import twnet_parser.messages7.connless.forward_error as \
connless_forward_error
def match_connless7(msg_id: list[int], data: bytes) -> ConnlessMessage:
def match_connless7(msg_id: bytes, data: bytes) -> ConnlessMessage:
msg: Optional[ConnlessMessage] = None
if msg_id == twnet_parser.msg7.REQUEST_LIST:
@ -50,7 +50,9 @@ def match_connless7(msg_id: list[int], data: bytes) -> ConnlessMessage:
msg = connless_forward_error.MsgForwardError()
if msg is None:
raise ValueError(f"Error: unknown conless message id={msg_id} data={data[0]}")
raise ValueError(
f"Error: unknown conless message id={msg_id!r} data={data[0]}"
)
msg.unpack(data)
return msg

View file

@ -7,9 +7,11 @@ from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.message_parser import MessageParser
from twnet_parser.net_message import NetMessage
from twnet_parser.ctrl_message import CtrlMessage
from twnet_parser.connless_message import ConnlessMessage
from twnet_parser.chunk_header import ChunkHeader, ChunkFlags
from twnet_parser.msg_matcher.control7 import match_control7
from twnet_parser.constants import NET_MAX_SEQUENCE
from twnet_parser.msg_matcher.connless7 import match_connless7
from twnet_parser.constants import NET_MAX_SEQUENCE, NET_PACKETVERSION
from twnet_parser.external.huffman import huffman
@ -24,6 +26,7 @@ CHUNKFLAG7_VITAL = 1
CHUNKFLAG7_RESEND = 2
PACKET_HEADER7_SIZE = 7
CONNLESS_PACKET_HEADER7_SIZE = 9
class PacketFlags7(PrettyPrint):
def __init__(self) -> None:
@ -60,6 +63,10 @@ class PacketHeader(PrettyPrint):
self.token: bytes = token
self.num_chunks: Optional[int] = num_chunks
# connless only
self.connless_version: int = NET_PACKETVERSION
self.response_token: bytes = b'\xff\xff\xff\xff'
def pack(self) -> bytes:
"""
Generate 7 byte teeworlds 0.7 packet header
@ -103,11 +110,11 @@ class TwPacket(PrettyPrint):
self.payload_raw: bytes = b''
self.payload_decompressed: bytes = b''
self.header: PacketHeader = PacketHeader()
self.messages: list[Union[CtrlMessage, NetMessage]] = []
self.messages: list[Union[CtrlMessage, NetMessage, ConnlessMessage]] = []
def pack(self, we_are_a_client = True) -> bytes:
payload: bytes = b''
msg: Union[CtrlMessage, NetMessage]
msg: Union[CtrlMessage, NetMessage, ConnlessMessage]
is_control: bool = False
for msg in self.messages:
# TODO: this is super ugly
@ -122,7 +129,10 @@ class TwPacket(PrettyPrint):
payload += msg.pack(we_are_a_client)
else:
msg = cast(NetMessage, msg)
msg_payload: bytes = pack_int((msg.message_id<<1)|(int)(msg.system_message))
msg_payload: bytes = pack_int(
(msg.message_id<<1) |
(int)(msg.system_message)
)
msg_payload += msg.pack()
if msg.header.size is None:
msg.header.size = len(msg_payload)
@ -164,12 +174,19 @@ class PacketHeaderParser7():
header = PacketHeader()
# bits 2..5
header.flags = self.parse_flags7(data)
# bits 6..16
header.ack = self.parse_ack(data)
# bits 17..25
header.num_chunks = self.parse_num_chunks(data)
# bits 16..57
header.token = self.parse_token(data)
if header.flags.connless:
# TODO: do not hardcode version field
# but actually read the bits
header.connless_version = NET_PACKETVERSION
header.token = data[1:5]
header.response_token = data[5:9]
else:
# bits 6..16
header.ack = self.parse_ack(data)
# bits 17..25
header.num_chunks = self.parse_num_chunks(data)
# bits 16..57
header.token = self.parse_token(data)
return header
class ChunkHeaderParser:
@ -239,17 +256,24 @@ class PacketParser():
# methods that do not share state seems like a waste of performance
# would this be nicer with class methods?
pck.header = PacketHeaderParser7().parse_header(data)
pck.payload_raw = data[PACKET_HEADER7_SIZE:]
header_size = PACKET_HEADER7_SIZE
if pck.header.flags.connless:
header_size = CONNLESS_PACKET_HEADER7_SIZE
pck.payload_raw = data[header_size:]
pck.payload_decompressed = pck.payload_raw
if pck.header.flags.control:
ctrl_msg: CtrlMessage = match_control7(data[7], data[8:], client)
pck.messages.append(ctrl_msg)
return pck
if pck.header.flags.connless:
connless_msg: ConnlessMessage = match_connless7(data[9:17], data[18:])
pck.messages.append(connless_msg)
return pck
if pck.header.flags.compression:
payload = bytearray(pck.payload_raw)
pck.payload_decompressed = huffman.decompress(payload)
pck.messages = cast(
list[Union[CtrlMessage, NetMessage]],
list[Union[CtrlMessage, NetMessage, ConnlessMessage]],
self.get_messages(pck.payload_decompressed))
return pck