2016-08-28 14:43:00 +03:00
|
|
|
# This file is based on TLSharp
|
|
|
|
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/Network/TcpMessage.cs
|
2016-09-04 13:42:11 +03:00
|
|
|
from utils import BinaryWriter, BinaryReader
|
2016-09-04 22:07:09 +03:00
|
|
|
from binascii import crc32
|
2016-09-05 19:35:12 +03:00
|
|
|
from errors import *
|
2016-08-26 13:58:53 +03:00
|
|
|
|
|
|
|
|
|
|
|
class TcpMessage:
|
|
|
|
def __init__(self, seq_number, body):
|
|
|
|
"""
|
|
|
|
:param seq_number: Sequence number
|
|
|
|
:param body: Message body byte array
|
|
|
|
"""
|
|
|
|
if body is None:
|
2016-09-05 19:35:12 +03:00
|
|
|
raise InvalidParameterError('body cannot be None')
|
2016-08-26 13:58:53 +03:00
|
|
|
|
|
|
|
self.sequence_number = seq_number
|
|
|
|
self.body = body
|
|
|
|
|
|
|
|
def encode(self):
|
2016-08-28 14:43:00 +03:00
|
|
|
"""Returns the bytes of the this message encoded, following Telegram's guidelines"""
|
2016-08-26 13:58:53 +03:00
|
|
|
with BinaryWriter() as writer:
|
|
|
|
''' https://core.telegram.org/mtproto#tcp-transport
|
|
|
|
|
|
|
|
4 length bytes are added at the front
|
|
|
|
(to include the length, the sequence number, and CRC32; always divisible by 4)
|
|
|
|
and 4 bytes with the packet sequence number within this TCP connection
|
|
|
|
(the first packet sent is numbered 0, the next one 1, etc.),
|
|
|
|
and 4 CRC32 bytes at the end (length, sequence number, and payload together).
|
|
|
|
'''
|
|
|
|
writer.write_int(len(self.body) + 12)
|
|
|
|
writer.write_int(self.sequence_number)
|
|
|
|
writer.write(self.body)
|
|
|
|
|
2016-09-04 22:07:09 +03:00
|
|
|
crc = crc32(writer.get_bytes())
|
2016-08-26 13:58:53 +03:00
|
|
|
writer.write_int(crc, signed=False)
|
|
|
|
|
|
|
|
return writer.get_bytes()
|
|
|
|
|
2016-08-28 14:43:00 +03:00
|
|
|
@staticmethod
|
|
|
|
def decode(body):
|
|
|
|
"""Returns a TcpMessage from the given encoded bytes, decoding them previously"""
|
2016-08-26 13:58:53 +03:00
|
|
|
if body is None:
|
2016-09-05 19:35:12 +03:00
|
|
|
raise InvalidParameterError('body cannot be None')
|
2016-08-26 13:58:53 +03:00
|
|
|
|
|
|
|
if len(body) < 12:
|
2016-09-05 19:35:12 +03:00
|
|
|
raise InvalidParameterError('Wrong size of input packet')
|
2016-08-26 13:58:53 +03:00
|
|
|
|
|
|
|
with BinaryReader(body) as reader:
|
2016-09-03 11:54:58 +03:00
|
|
|
packet_len = int.from_bytes(reader.read(4), byteorder='little')
|
2016-08-26 13:58:53 +03:00
|
|
|
if packet_len < 12:
|
2016-09-05 19:35:12 +03:00
|
|
|
raise InvalidParameterError('Invalid packet length in body: {}'.format(packet_len))
|
2016-08-26 13:58:53 +03:00
|
|
|
|
|
|
|
seq = reader.read_int()
|
|
|
|
packet = reader.read(packet_len - 12)
|
2016-09-04 22:07:09 +03:00
|
|
|
checksum = reader.read_int(signed=False)
|
2016-08-26 13:58:53 +03:00
|
|
|
|
2016-09-04 22:07:09 +03:00
|
|
|
valid_checksum = crc32(body[:packet_len - 4])
|
2016-08-26 13:58:53 +03:00
|
|
|
if checksum != valid_checksum:
|
2016-09-05 19:35:12 +03:00
|
|
|
raise InvalidChecksumError(checksum, valid_checksum)
|
2016-08-26 13:58:53 +03:00
|
|
|
|
|
|
|
return TcpMessage(seq, packet)
|