From fadf1ff5c4a1aed24b3bbfa52242ccce2862244a Mon Sep 17 00:00:00 2001 From: speyrefitte Date: Tue, 24 Jun 2014 16:01:04 +0200 Subject: [PATCH] code refactoring --- rdpy/display/rle.py | 9 +- rdpy/network/error.py | 10 +++ rdpy/network/layer.py | 145 +++++++++++++++++--------------- rdpy/protocol/rdp/ber.py | 125 +++++++++++++++------------ rdpy/protocol/rdp/mcs.py | 172 ++++++++++++++++++++++---------------- rdpy/protocol/rdp/pdu.py | 10 +-- rdpy/protocol/rdp/rdp.py | 2 +- rdpy/protocol/rdp/tpdu.py | 6 +- 8 files changed, 271 insertions(+), 208 deletions(-) diff --git a/rdpy/display/rle.py b/rdpy/display/rle.py index e9295b8..a321513 100644 --- a/rdpy/display/rle.py +++ b/rdpy/display/rle.py @@ -45,7 +45,9 @@ def decode(src, width, height, colorType): insertMix = False fom_mask = 0 mask = 0 - dst = Stream("\x00" * (width * height * sizeof(colorType()))) + line = 0 + typeSize = sizeof(colorType()) + dst = Stream("\x00" * (width * height * typeSize)) while src.dataLen() > 0: #compute orders @@ -107,7 +109,10 @@ def decode(src, width, height, colorType): raise InvalidExpectedDataException("In RLE decompression height must be greater than 0") x = 0 height -= 1 - #prevline = line + prevline = line + line = width * height * typeSize + + return dst \ No newline at end of file diff --git a/rdpy/network/error.py b/rdpy/network/error.py index 69eacea..46b0080 100644 --- a/rdpy/network/error.py +++ b/rdpy/network/error.py @@ -21,6 +21,16 @@ All exceptions error use in RDPY """ +class CallPureVirtualFuntion(Exception): + """ + Raise when a virtual function is called and not implemented + """ + def __init__(self, message = ""): + """ + @param message: message show when exception is raised + """ + Exception.__init__(self, message) + class InvalidValue(Exception): """ Raise when invalid value type occurred diff --git a/rdpy/network/layer.py b/rdpy/network/layer.py index 5c2d514..dc011dd 100644 --- a/rdpy/network/layer.py +++ b/rdpy/network/layer.py @@ -23,88 +23,94 @@ Join RDPY design with twisted design RDPY use Layer Protocol design (like twisted) """ +from rdpy.network.error import CallPureVirtualFuntion + class LayerMode(object): NONE = 0 SERVER = 1 CLIENT = 2 - + + +class StreamListener(object): + """ + Interface use to inform that we can handle receive stream + """ + def recv(self, s): + """ + Signal that data is available for this layer + call by transport layer + default is to pass data to presentation layer + @param s: raw Stream receive from transport layer + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "StreamListener")) + +class StreamSender(object): + """ + Interface use to show stream sender capability + """ + def send(self, data): + ''' + Send Stream on layer + @param data: Type or tuple element handle by transport layer + ''' + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "send", "StreamSender")) + class Layer(object): - ''' - Network abstraction for protocol - Try as possible to divide user protocol in layer - default implementation is a transparent layer - ''' - def __init__(self, mode = LayerMode.NONE, presentation = None): - ''' - Constructor - @param presentation: Layer which handled connect and recv messages - ''' + """ + A simple double linked list with presentation and transport layer + and a subset of event (connect and close) + """ + def __init__(self, mode, presentation = None): + """ + @param mode: LayerMode use + @param presentation: presentation layer + """ #presentation layer higher layer in model self._presentation = presentation #transport layer under layer in model self._transport = None - #register layer mode + #network layer mode self._mode = mode #auto set transport layer of own presentation layer if not self._presentation is None: self._presentation._transport = self def connect(self): - ''' - call when transport layer is connected + """ + Call when transport layer is connected default is send connect event to presentation layer - ''' + """ if not self._presentation is None: self._presentation.connect() - def recv(self, s): - ''' - signal that data is available for this layer - call by transport layer - default is to pass data to presentation layer - @param s: raw Stream receive from transport layer - ''' - if not self._presentation is None: - self._presentation.recv(s) - - def send(self, data): - ''' - classical use by presentation layer - write data for this layer - default pass data to transport layer - @param data: Type or tuple element handle by transport layer - ''' - if not self._transport is None: - self._transport.send(data) - def close(self): - ''' - close layer and send close signal - to transport layer - ''' + """ + Close layer event + default is sent to transport layer + """ if not self._transport is None: self._transport.close() -class LayerAutomata(Layer): - ''' - layer with automata state - we can set next recv function used - ''' +class LayerAutomata(Layer, StreamListener): + """ + Layer with automata state + we can set next recv function used for Stream packet + """ def __init__(self, mode, presentation = None): - ''' - Constructor + """ + @param mode: LayerMode use @param presentation: presentation Layer - ''' + """ #call parent constructor Layer.__init__(self, mode, presentation) def setNextState(self, callback = None): - ''' - set recv function to next callback or + """ + Set receive function to next callback or current self.recv function if it's None @param callback: a callable object that can receive Layer, Stream parameters - ''' + """ if callback is None: callback = self.__class__.recv @@ -115,16 +121,17 @@ from twisted.internet import protocol #first that handle stream from type import Stream -class RawLayer(protocol.Protocol, LayerAutomata): - ''' +class RawLayer(protocol.Protocol, LayerAutomata, StreamSender): + """ Inherit from protocol twisted class allow this protocol to wait until expected size of packet and use Layer automata to call next automata state - ''' + """ def __init__(self, mode, presentation = None): - ''' - Constructor - ''' + """ + @param mode: LayerMode use + @param presentation: presentation layer in layer list + """ #call parent automata LayerAutomata.__init__(self, mode, presentation) #data buffer received from twisted network layer @@ -133,11 +140,11 @@ class RawLayer(protocol.Protocol, LayerAutomata): self._expectedLen = 0 def dataReceived(self, data): - ''' - inherit from protocol class + """ + Inherit from protocol class main event of received data @param data: string data receive from twisted - ''' + """ #add in buffer self._buffer += data #while buffer have expected size call local callback @@ -150,29 +157,29 @@ class RawLayer(protocol.Protocol, LayerAutomata): self.recv(expectedData) def connectionMade(self): - ''' + """ inherit from twisted protocol - ''' + """ #join two scheme self.connect() def expect(self, expectedLen, callback = None): - ''' - configure layer to change next state with callback only + """ + Configure layer to change next state with callback only when expectLen bytes is received from transport layer - @param expectedLen: in bytes len use to call nextstate - @param callback: callback call when expectedlen bytes is received - ''' + @param expectedLen: in bytes length use to call next state + @param callback: callback call when expected length bytes is received + """ self._expectedLen = expectedLen #default callback is recv from LayerAutomata self.setNextState(callback) def send(self, message): - ''' - send stream on tcp layer + """ + Send Stream on TCP layer format message into raw stream understood by transport layer @param message: (tuple | Type) - ''' + """ s = Stream() s.writeType(message) self.transport.write(s.getvalue()) \ No newline at end of file diff --git a/rdpy/protocol/rdp/ber.py b/rdpy/protocol/rdp/ber.py index 1300cec..5e6eabc 100644 --- a/rdpy/protocol/rdp/ber.py +++ b/rdpy/protocol/rdp/ber.py @@ -1,6 +1,27 @@ -''' -@author: sylvain -''' +# +# Copyright (c) 2014 Sylvain Peyrefitte +# +# This file is part of rdpy. +# +# rdpy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +""" +Basic Encoding Rules use in RDP. +ASN.1 standard +""" + from rdpy.network.type import UInt8, UInt16Be, UInt32Be, String from rdpy.network.const import ConstAttributes, TypeAttributes from rdpy.network.error import InvalidExpectedDataException, InvalidSize @@ -35,24 +56,24 @@ class Tag(object): BER_TAG_SEQUENCE_OF = 0x10 def berPC(pc): - ''' - return BER_CONSTRUCT if true + """ + Return BER_CONSTRUCT if true BER_PRIMITIVE if false @param pc: boolean @return: BerPc value - ''' + """ if pc: return BerPc.BER_CONSTRUCT else: return BerPc.BER_PRIMITIVE def readLength(s): - ''' - read length of ber structure + """ + Read length of BER structure length be on 1 2 or 3 bytes @param s: stream - @return: int or python long - ''' + @return: int or Python long + """ size = None length = UInt8() s.readType(length) @@ -64,50 +85,50 @@ def readLength(s): elif byte == 2: size = UInt16Be() else: - raise InvalidExpectedDataException("ber length may be 1 or 2") + raise InvalidExpectedDataException("BER length may be 1 or 2") s.readType(size) else: size = length return size.value def writeLength(size): - ''' - return strcture length as expected in Ber specification + """ + Return structure length as expected in BER specification @param size: int or python long @return: UInt8 or (UInt8(0x82), UInt16Be) - ''' + """ if size > 0x7f: return (UInt8(0x82), UInt16Be(size)) else: return UInt8(size) def readUniversalTag(s, tag, pc): - ''' - read tag of ber packet + """ + Read tag of BER packet @param tag: Tag class attributes @param pc: boolean @return: true if tag is correctly read - ''' + """ byte = UInt8() s.readType(byte) return byte == ((Class.BER_CLASS_UNIV | berPC(pc)) | (Tag.BER_TAG_MASK & tag)) def writeUniversalTag(tag, pc): - ''' - return universal tag byte + """ + Return universal tag byte @param tag: tag class attributes @param pc: boolean @return: UInt8 - ''' + """ return ((Class.BER_CLASS_UNIV | berPC(pc)) | (Tag.BER_TAG_MASK & tag)) def readApplicationTag(s, tag): - ''' - read application tag + """ + Read application tag @param s: stream @param tag: tag class attributes @return: length of application packet - ''' + """ byte = UInt8() s.readType(byte) if tag > UInt8(30): @@ -123,22 +144,22 @@ def readApplicationTag(s, tag): return readLength(s) def writeApplicationTag(tag, size): - ''' - return struct that represent ber application tag + """ + Return structure that represent BER application tag @param tag: UINt8 @param size: size to rest of packet - ''' + """ if tag > UInt8(30): return (((Class.BER_CLASS_APPL | BerPc.BER_CONSTRUCT) | Tag.BER_TAG_MASK), tag, writeLength(size)) else: return (((Class.BER_CLASS_APPL | BerPc.BER_CONSTRUCT) | (Tag.BER_TAG_MASK & tag)), writeLength(size)) def readBoolean(s): - ''' - return boolean + """ + Return boolean @param s: stream @return: boolean - ''' + """ if not readUniversalTag(s, Tag.BER_TAG_BOOLEAN, False): raise InvalidExpectedDataException("bad boolean tag") size = readLength(s) @@ -149,24 +170,24 @@ def readBoolean(s): return bool(b.value) def writeBoolean(b): - ''' - return structure that represent boolean in ber specification + """ + Return structure that represent boolean in BER specification @param b: boolean - @return: ber boolean structure - ''' + @return: BER boolean structure + """ boolean = UInt8(0) if b: boolean = UInt8(0xff) return (writeUniversalTag(Tag.BER_TAG_BOOLEAN, False), writeLength(1), boolean) def readInteger(s): - ''' - read integer structure from stream + """ + Read integer structure from stream @param s: stream @return: int or long python - ''' + """ if not readUniversalTag(s, Tag.BER_TAG_INTEGER, False): - raise InvalidExpectedDataException("bad integer tag") + raise InvalidExpectedDataException("Bad integer tag") size = readLength(s) @@ -189,14 +210,14 @@ def readInteger(s): s.readType(integer) return integer.value else: - raise InvalidExpectedDataException("wrong integer size") + raise InvalidExpectedDataException("Wrong integer size") def writeInteger(value): - ''' - write integer value + """ + Write integer value @param param: int or python long - @return ber interger structure - ''' + @return: BER integer structure + """ if value <= 0xff: return (writeUniversalTag(Tag.BER_TAG_INTEGER, False), writeLength(1), UInt8(value)) elif value <= 0xffff: @@ -205,30 +226,30 @@ def writeInteger(value): return (writeUniversalTag(Tag.BER_TAG_INTEGER, False), writeLength(4), UInt32Be(value)) def readOctetString(s): - ''' - read ber string structure + """ + Read BER string structure @param s: stream @return: String - ''' + """ if not readUniversalTag(s, Tag.BER_TAG_OCTET_STRING, False): - raise InvalidExpectedDataException("unexpected ber tag") + raise InvalidExpectedDataException("Unexpected BER tag") size = readLength(s) return String(s.read(size.value)) def writeOctetstring(value): - ''' - write string in ber representation + """ + Write string in ber representation @param value: string @return: string ber structure - ''' + """ return (writeUniversalTag(Tag.BER_TAG_OCTET_STRING, False), writeLength(len(value)), String(value)) def readEnumerated(s): - ''' - read enumerated structure + """ + Read enumerated structure @param s: Stream @return: int or long - ''' + """ if not readUniversalTag(s, Tag.BER_TAG_ENUMERATED, False): raise InvalidExpectedDataException("invalid ber tag") if readLength(s) != 1: diff --git a/rdpy/protocol/rdp/mcs.py b/rdpy/protocol/rdp/mcs.py index b0f2a46..37d1afe 100644 --- a/rdpy/protocol/rdp/mcs.py +++ b/rdpy/protocol/rdp/mcs.py @@ -1,9 +1,32 @@ -''' -@author: citronneur -''' +# +# Copyright (c) 2014 Sylvain Peyrefitte +# +# This file is part of rdpy. +# +# rdpy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +""" +Implement Multi Channel Service + +Each channel have a particular role. +The main channel is the graphical channel. +It exist channel for file system order, audio channel, clipboard etc... +""" from rdpy.network.const import ConstAttributes, TypeAttributes -from rdpy.network.layer import LayerAutomata, Layer, LayerMode +from rdpy.network.layer import LayerAutomata, LayerMode, StreamSender from rdpy.network.type import sizeof, Stream, UInt8, UInt16Be from rdpy.network.error import InvalidExpectedDataException, InvalidValue, InvalidSize from rdpy.protocol.rdp.ber import writeLength @@ -13,18 +36,18 @@ import ber, gcc, per @ConstAttributes @TypeAttributes(UInt8) class Message(object): - ''' - message type - ''' + """ + Message type + """ MCS_TYPE_CONNECT_INITIAL = 0x65 MCS_TYPE_CONNECT_RESPONSE = 0x66 @ConstAttributes @TypeAttributes(UInt8) class DomainMCSPDU: - ''' - domain mcs pdu header - ''' + """ + Domain MCS PDU header + """ ERECT_DOMAIN_REQUEST = 1 DISCONNECT_PROVIDER_ULTIMATUM = 8 ATTACH_USER_REQUEST = 10 @@ -37,27 +60,28 @@ class DomainMCSPDU: @ConstAttributes @TypeAttributes(UInt16Be) class Channel: + """ + Channel id of main channels use in RDP + """ MCS_GLOBAL_CHANNEL = 1003 MCS_USERCHANNEL_BASE = 1001 class MCS(LayerAutomata): - ''' + """ Multi Channel Service layer the main layer of RDP protocol is why he can do everything and more! - ''' - - class MCSProxySender(Layer): - ''' - Proxy use to set as trnsport layer for upper channel + """ + class MCSProxySender(StreamSender): + """ + Proxy use to set as transport layer for upper channel use to abstract channel id for presentation layer - ''' + """ def __init__(self, mcs, channelId): - ''' - ctor - @param mcs: mcs layer use as proxy + """ + @param mcs: MCS layer use as proxy @param channelId: channel id for presentation layer - ''' + """ self._mcs = mcs self._channelId = channelId @@ -94,11 +118,10 @@ class MCS(LayerAutomata): def __init__(self, mode, presentation): - ''' - ctor call base class ctor - @param mode: mode of mcs layer + """ + @param mode: mode of MCS layer @param presentation: presentation layer - ''' + """ LayerAutomata.__init__(self, mode, presentation) self._clientSettings = gcc.ClientSettings() self._serverSettings = gcc.ServerSettings() @@ -110,17 +133,17 @@ class MCS(LayerAutomata): self._channelIdsRequest = {} def connect(self): - ''' - connection send for client mode + """ + Connection send for client mode a write connect initial packet - ''' + """ self._clientSettings.core.serverSelectedProtocol = self._transport._selectedProtocol self.sendConnectInitial() def connectNextChannel(self): - ''' - send sendChannelJoinRequest message on next unconnect channel - ''' + """ + Send sendChannelJoinRequest message on next unconnect channel + """ for (channelId, layer) in self._channelIds.iteritems(): #for each unconnect channel send a request if not self._channelIdsRequest.has_key(channelId): @@ -138,9 +161,9 @@ class MCS(LayerAutomata): layer.connect() def sendConnectInitial(self): - ''' - send connect initial packet - ''' + """ + Send connect initial packet + """ ccReq = gcc.writeConferenceCreateRequest(self._clientSettings) ccReqStream = Stream() ccReqStream.writeType(ccReq) @@ -155,28 +178,28 @@ class MCS(LayerAutomata): self.setNextState(self.recvConnectResponse) def sendErectDomainRequest(self): - ''' - send a formated erect domain request for RDP connection - ''' + """ + Send a formated erect domain request for RDP connection + """ self._transport.send((self.writeMCSPDUHeader(DomainMCSPDU.ERECT_DOMAIN_REQUEST), per.writeInteger(0), per.writeInteger(0))) def sendAttachUserRequest(self): - ''' - send a formated attach user request for RDP connection - ''' + """ + Send a formated attach user request for RDP connection + """ self._transport.send(self.writeMCSPDUHeader(DomainMCSPDU.ATTACH_USER_REQUEST)) def sendChannelJoinRequest(self, channelId): - ''' - send a formated Channel join request from client to server - ''' + """ + Send a formated Channel join request from client to server + """ self._transport.send((self.writeMCSPDUHeader(DomainMCSPDU.CHANNEL_JOIN_REQUEST), self._userId, channelId)) def recvConnectResponse(self, data): - ''' - receive mcs connect response from server + """ + receive MCS connect response from server @param data: Stream - ''' + """ ber.readApplicationTag(data, Message.MCS_TYPE_CONNECT_RESPONSE) ber.readEnumerated(data) ber.readInteger(data) @@ -196,10 +219,10 @@ class MCS(LayerAutomata): self.setNextState(self.recvAttachUserConfirm) def recvAttachUserConfirm(self, data): - ''' - receive an attach user confirm + """ + Receive an attach user confirm @param data: Stream - ''' + """ opcode = UInt8() confirm = UInt8() data.readType((opcode, confirm)) @@ -217,10 +240,10 @@ class MCS(LayerAutomata): self.connectNextChannel() def recvChannelJoinConfirm(self, data): - ''' - receive a channel join confirm from server + """ + Receive a channel join confirm from server @param data: Stream - ''' + """ opcode = UInt8() confirm = UInt8() data.readType((opcode, confirm)) @@ -239,10 +262,10 @@ class MCS(LayerAutomata): self.connectNextChannel() def recvData(self, data): - ''' - main receive method + """ + Main receive method @param data: Stream - ''' + """ opcode = UInt8() data.readType(opcode) @@ -278,51 +301,52 @@ class MCS(LayerAutomata): self._channelIds[channelId].recv(data) def send(self, channelId, data): - ''' - specific send function for channelId + """ + Specific send function for channelId + @param channelId: Channel use to send @param data: message to send - ''' + """ self._transport.send((self.writeMCSPDUHeader(DomainMCSPDU.SEND_DATA_REQUEST), self._userId, channelId, UInt8(0x70), UInt16Be(sizeof(data)) | UInt16Be(0x8000), data)) def writeDomainParams(self, maxChannels, maxUsers, maxTokens, maxPduSize): - ''' - write a special domain param structure + """ + Write a special domain parameter structure use in connection sequence - @param maxChannels: number of mcs channel use - @param maxUsers: number of mcs user used (1) + @param maxChannels: number of MCS channel use + @param maxUsers: number of MCS user used (1) @param maxTokens: unknown @param maxPduSize: unknown - @return: domain param structure - ''' + @return: domain parameter structure + """ domainParam = (ber.writeInteger(maxChannels), ber.writeInteger(maxUsers), ber.writeInteger(maxTokens), ber.writeInteger(1), ber.writeInteger(0), ber.writeInteger(1), ber.writeInteger(maxPduSize), ber.writeInteger(2)) return (ber.writeUniversalTag(ber.Tag.BER_TAG_SEQUENCE, True), writeLength(sizeof(domainParam)), domainParam) def writeMCSPDUHeader(self, mcsPdu, options = 0): - ''' - write mcs pdu header - @param mcsPdu: pdu code + """ + Write MCS PDU header + @param mcsPdu: PDU code @param options: option contains in header @return: UInt8 - ''' + """ return (mcsPdu << 2) | options def readMCSPDUHeader(self, opcode, mcsPdu): - ''' - read mcsPdu header and return options parameter + """ + Read mcsPdu header and return options parameter @param opcode: UInt8 opcode @param mcsPdu: mcsPdu will be checked @return: true if opcode is correct - ''' + """ return (opcode >> 2) == mcsPdu def readDomainParams(self, s): - ''' - read domain params structure + """ + Read domain parameters structure @return: (max_channels, max_users, max_tokens, max_pdu_size) - ''' + """ if not ber.readUniversalTag(s, ber.Tag.BER_TAG_SEQUENCE, True): raise InvalidValue("bad BER tags") ber.readLength(s)#length diff --git a/rdpy/protocol/rdp/pdu.py b/rdpy/protocol/rdp/pdu.py index f5a0a8a..60323df 100644 --- a/rdpy/protocol/rdp/pdu.py +++ b/rdpy/protocol/rdp/pdu.py @@ -2,16 +2,12 @@ @author: citronneur ''' -from rdpy.network.layer import LayerAutomata, LayerMode -from rdpy.network.type import CompositeType, UniString, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType,\ - FactoryType +from rdpy.network.layer import LayerAutomata +from rdpy.network.type import CompositeType, UniString, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType from rdpy.network.const import ConstAttributes, TypeAttributes from rdpy.network.error import InvalidExpectedDataException, ErrorReportedFromPeer -import gcc -import lic -import caps -import rdp +import gcc, lic, caps @ConstAttributes @TypeAttributes(UInt16Le) diff --git a/rdpy/protocol/rdp/rdp.py b/rdpy/protocol/rdp/rdp.py index 7d21a2e..4735a26 100644 --- a/rdpy/protocol/rdp/rdp.py +++ b/rdpy/protocol/rdp/rdp.py @@ -2,8 +2,8 @@ @author: sylvain ''' from twisted.internet import protocol -import tpkt, tpdu, mcs, pdu from rdpy.network.layer import LayerMode +import tpkt, tpdu, mcs, pdu class RDPController(object): """ diff --git a/rdpy/protocol/rdp/tpdu.py b/rdpy/protocol/rdp/tpdu.py index f124fcd..31a3658 100644 --- a/rdpy/protocol/rdp/tpdu.py +++ b/rdpy/protocol/rdp/tpdu.py @@ -24,7 +24,7 @@ This layer have main goal to negociate ssl transport RDP basic security is not supported by RDPY (because is not a true security layer...) """ -from rdpy.network.layer import LayerAutomata, LayerMode +from rdpy.network.layer import LayerAutomata, LayerMode, StreamSender from rdpy.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof from rdpy.network.error import InvalidExpectedDataException from rdpy.network.const import ConstAttributes, TypeAttributes @@ -116,7 +116,7 @@ class Negotiation(CompositeType): self.selectedProtocol = UInt32Le(conditional = lambda: self.code == NegociationType.TYPE_RDP_NEG_RSP) self.failureCode = UInt32Le(conditional = lambda: self.code == NegociationType.TYPE_RDP_NEG_FAILURE) -class TPDU(LayerAutomata): +class TPDU(LayerAutomata, StreamSender): """ TPDU layer management there is an connection automata @@ -223,7 +223,7 @@ class TPDU(LayerAutomata): """ header = TPDUDataHeader() data.readType(header) - LayerAutomata.recv(self, data) + self._presentation.recv(data) def sendConnectionRequest(self): """