def parseConnectionPDU(self, data: bytes, length: int, name: str) -> Tuple[int, int, int, bytes]: """ Parse the provided data to extract common information contained in Connection Request, Connection Confirm and Disconnect Request PDUs. :param data: bytes to parse. :param length: Length of the Connection PDU. :param name: For debugging purposes: the name of the connection PDU (like "Connection Request"). :return: A tuple of the information we find in both connection PDUs: (source, destination, options, payload) """ if length < 6: raise ParsingError( "Invalid X244 %s length indicator: indicator = %d, expected at least 6 bytes" % (name, 6)) destination = Uint16BE.unpack(data[2:4]) source = Uint16BE.unpack(data[4:6]) options = Uint8.unpack(data[6]) payload = data[7:] if len(payload) != length - 6: raise ParsingError( "Invalid X224 %s payload length: expected = %d, length = %d" % (name, length - 6, len(payload))) return source, destination, options, payload
def writeChannelJoinRequest(self, stream: BytesIO, pdu: MCSChannelJoinRequestPDU): """ Encode a Channel Join Request PDU :param stream: The destination stream to write into. :param pdu: the PDU to encode. """ stream.write(Uint16BE.pack(pdu.initiator - MCSChannelID.USERCHANNEL_BASE)) stream.write(Uint16BE.pack(pdu.channelID)) stream.write(pdu.payload)
def writeConnectionPDU(self, stream: BytesIO, header: X224PDUType, destination: int, source: int, options: int): """ Write a connection PDU (connectionRequest/connectionConfirm/disconnectRequest) in the provided byte stream. """ stream.write(Uint8.pack(header)) stream.write(Uint16BE.pack(destination)) stream.write(Uint16BE.pack(source)) stream.write(Uint8.pack(options))
def writeDataPDU(self, stream: BytesIO, pdu: Union[MCSSendDataRequestPDU, MCSSendDataIndicationPDU]): """ Encode a Data PDU :param stream: The destination stream to write into. :param pdu: the PDU to encode. """ stream.write(Uint16BE.pack(pdu.initiator - MCSChannelID.USERCHANNEL_BASE)) stream.write(Uint16BE.pack(pdu.channelID)) stream.write(per.writeEnumeration(pdu.priority)) stream.write(per.writeOctetStream(pdu.payload))
def parseDataPDU(self, stream: BytesIO, PDUClass: type) -> Union[MCSSendDataRequestPDU, MCSSendDataIndicationPDU]: """ Common logic for parsing Send Data Request and Send Data Indication PDUs :param stream: stream containing the data :param PDUClass: the actual PDU class: MCSSendDataRequestPDU or MCSSendDataIndicationPDU """ initiator = Uint16BE.unpack(stream.read(2)) + MCSChannelID.USERCHANNEL_BASE channelID = Uint16BE.unpack(stream.read(2)) priority = per.readEnumeration(stream) payload = per.readOctetStream(stream) return PDUClass(initiator, channelID, priority, payload)
def writeChannelJoinConfirm(self, stream: BytesIO, pdu: MCSChannelJoinConfirmPDU): """ Encode a Channel Join Confirm PDU :param stream: The destination stream to write into. :param pdu: the confirmation PDU. """ stream.write(per.writeEnumeration(pdu.result)) stream.write(Uint16BE.pack(pdu.initiator - MCSChannelID.USERCHANNEL_BASE)) stream.write(Uint16BE.pack(pdu.requested)) if pdu.channelID is not None: stream.write(Uint16BE.pack(pdu.channelID)) stream.write(pdu.payload)
def parseChannelJoinRequest(self, stream: BytesIO) -> MCSChannelJoinRequestPDU: """ Parse a Channel Join Request PDU :param stream: stream containing the data """ data = stream.read() if len(data) < 4: raise ParsingError("Invalid Channel Join Request PDU received") initiator = Uint16BE.unpack(data[0 : 2]) + MCSChannelID.USERCHANNEL_BASE channelID = Uint16BE.unpack(data[2 : 4]) payload = data[4 :] return MCSChannelJoinRequestPDU(initiator, channelID, payload)
def getPDULengthWithSocket(self, socket): """ Same as getPDULength, but using a network socket. :type socket: socket.socket """ data = socket.recv(3) return data, Uint16BE.unpack(data[1:])
def parseConferenceCreateResponse( self, stream: BytesIO) -> GCCConferenceCreateResponsePDU: """ Parse ConferenceCreateResponse data into a GCCPDU :param stream: byte stream containing the PDU data """ nodeID = Uint16BE.unpack(stream.read(2)) + 1001 tag = per.readInteger(stream) result = per.readEnumeration(stream) userDataCount = per.readNumberOfSet(stream) if userDataCount != 1: raise ParsingError("Expected user data count to be 1, got %d" % userDataCount) userDataType = per.readChoice(stream) if userDataType != 0xc0: raise ParsingError( "Expected user data type to be 0xc0 (h221NonStandard), got %d" % userDataType) key = per.readOctetStream(stream, 4) if key != GCCParser.H221_SERVER_KEY: raise ParsingError("Expected user data key to be %s, got %s" % (GCCParser.H221_SERVER_KEY, key)) payload = per.readOctetStream(stream) return GCCConferenceCreateResponsePDU(nodeID, tag, result, payload)
def getPDULength(self, data): """ Get the length of the PDU contained in data. :param data: the PDU data. :type data: bytes :return: int """ return Uint16BE.unpack(data[2:4])
def writeAttachUserConfirm(self, stream: BytesIO, pdu: MCSAttachUserConfirmPDU): """ Encode a Attach User Confirm PDU :param stream: The destination stream to write into. :param pdu: the PDU to encode. """ stream.write(per.writeEnumeration(pdu.result)) if pdu.initiator is not None: stream.write(Uint16BE.pack(pdu.initiator - MCSChannelID.USERCHANNEL_BASE))
def parseChannelJoinConfirm(self, stream: BytesIO) -> MCSChannelJoinConfirmPDU: """ Parse a Channel Join Confirm PDU :param stream: stream containing the data """ result = per.readEnumeration(stream) data = stream.read() if len(data) < 4 or len(data) == 5: raise ParsingError("Invalid Channel Join Confirm PDU received") elif len(data) >= 6: channelID = Uint16BE.unpack(data[4 : 6]) payload = data[6 :] else: channelID = None payload = b"" initiator = Uint16BE.unpack(data[0 : 2]) + MCSChannelID.USERCHANNEL_BASE requested = Uint16BE.unpack(data[2 : 4]) return MCSChannelJoinConfirmPDU(result, initiator, requested, channelID, payload)
def write(self, pdu: TPKTPDU) -> bytes: """ Encode a TPKTPDU into bytes to send on the network. """ stream = BytesIO() stream.write(Uint8.pack(pdu.header)) stream.write(b"\x00") stream.write(Uint16BE.pack(len(pdu.payload) + 4)) stream.write(pdu.payload) return stream.getvalue()
def writeConferenceCreateResponse(self, stream: BytesIO, pdu: GCCConferenceCreateResponsePDU): """ Write a GCCConferenceCreateResponsePDU to a stream. :param stream: byte stream to put the ConferenceCreateResponse data in. :param pdu: the PDU to write. """ stream.write(Uint16BE.pack(GCCParser.NODE_ID - 1001)) stream.write(per.writeInteger(1)) stream.write(per.writeEnumeration(0)) stream.write(per.writeNumberOfSet(1)) stream.write(per.writeChoice(0xc0)) stream.write(per.writeOctetStream(GCCParser.H221_SERVER_KEY, 4)) stream.write(per.writeOctetStream(pdu.payload))
def parse(self, data: bytes) -> TPKTPDU: """ Read the byte stream and return a TPKTPDU """ _version = Uint8.unpack(data[0:1]) _padding = Uint8.unpack(data[1:2]) length = Uint16BE.unpack(data[2:4]) payload = data[4:length] if len(payload) != length - 4: raise ParsingError("Payload is too short for TPKT length field") return TPKTPDU(payload)
def parseAttachUserConfirm(self, stream: BytesIO) -> MCSAttachUserConfirmPDU: """ Parse an Attach User Confirm PDU :param stream: stream containing the data """ result = per.readEnumeration(stream) data = stream.read() initiator = None if len(data) == 2: initiator = Uint16BE.unpack(data) + MCSChannelID.USERCHANNEL_BASE elif len(data) > 2: raise ParsingError("Unexpected payload") return MCSAttachUserConfirmPDU(result, initiator)
def writeConferenceCreateResponse(self, stream, pdu): """ Read a GCCConferenceCreateResponsePDU and put its raw data into stream :param stream: byte stream to put the ConferenceCreateResponse data in :type stream: BytesIO :type pdu: GCCConferenceCreateResponsePDU """ stream.write(Uint16BE.pack(GCCParser.NODE_ID - 1001)) stream.write(per.writeInteger(1)) stream.write(per.writeEnumeration(0)) stream.write(per.writeNumberOfSet(1)) stream.write(per.writeChoice(0xc0)) stream.write(per.writeOctetStream(GCCParser.H221_SERVER_KEY, 4)) stream.write(per.writeOctetStream(pdu.payload))
def parseError(self, data, length): """ Parse a Error PDU from the raw bytes :type data: bytes :param length: The length in bytes of the Error PDU. :return: X224ErrorPDU """ if length < 4: raise ParsingError("Invalid X224 Error PDU length indicator: indicator = %d, expected at least 4 bytes") destination = Uint16BE.unpack(data[2 : 4]) cause = Uint8.unpack(data[4]) payload = data[5 :] if len(payload) != length - 4: raise ParsingError("Invalid X224 Error PDU payload length: expected = %d, length = %d" % (length - 4, len(payload))) return X224ErrorPDU(destination, cause, payload)
def getPDULength(self, data: bytes) -> int: """ Get the length of the PDU contained in data. :param data: the PDU data. """ return Uint16BE.unpack(data[2:4])
def writeLength(self, stream: BytesIO, pdu: FastPathPDU): length = self.calculatePDULength(pdu) Uint16BE.pack(length | 0x8000, stream)
def writeLength(self, stream, pdu): length = self.calculatePDULength(pdu) Uint16BE.pack(length | 0x8000, stream)