From c71c4f46f4edb823976d16acd241c4860d34e963 Mon Sep 17 00:00:00 2001 From: speyrefitte Date: Wed, 23 Jul 2014 09:39:40 +0200 Subject: [PATCH] code refactoring + proxy code --- bin/rdpy-rdpproxy | 44 +- rdpy/network/layer.py | 21 +- rdpy/network/type.py | 9 +- rdpy/protocol/rdp/mcs.py | 623 ++++++++++++---------- rdpy/protocol/rdp/pdu/__init__.py | 0 rdpy/protocol/rdp/{ => pdu}/caps.py | 0 rdpy/protocol/rdp/{pdu.py => pdu/data.py} | 502 +---------------- rdpy/protocol/rdp/pdu/layer.py | 554 +++++++++++++++++++ rdpy/protocol/rdp/{ => pdu}/lic.py | 0 rdpy/protocol/rdp/rdp.py | 140 +++-- rdpy/protocol/rdp/tpdu.py | 134 +++-- rdpy/protocol/rdp/tpkt.py | 8 +- rdpy/protocol/rfb/rfb.py | 25 +- 13 files changed, 1139 insertions(+), 921 deletions(-) create mode 100644 rdpy/protocol/rdp/pdu/__init__.py rename rdpy/protocol/rdp/{ => pdu}/caps.py (100%) rename rdpy/protocol/rdp/{pdu.py => pdu/data.py} (68%) create mode 100644 rdpy/protocol/rdp/pdu/layer.py rename rdpy/protocol/rdp/{ => pdu}/lic.py (100%) diff --git a/bin/rdpy-rdpproxy b/bin/rdpy-rdpproxy index c5fb3e2..c324b8c 100755 --- a/bin/rdpy-rdpproxy +++ b/bin/rdpy-rdpproxy @@ -8,10 +8,46 @@ import sys, os sys.path.insert(1, os.path.join(sys.path[0], '..')) from rdpy.protocol.rdp import rdp +from twisted.internet import reactor -class TestServerFactory(rdp.ServerFactory): +class ProxyServer(rdp.RDPServerObserver): + def onReady(self): + reactor.connectTCP("wav-glw-013", 3389, ProxyClientFactory(self)) + def clientConnected(self, client): + print "ok" + +class ProxyClient(rdp.RDPClientObserver): + def __init__(self, controller, server): + rdp.RDPClientObserver.__init__(self, controller) + self._server = server + def onReady(self): + self._server.clientConnected(self) + def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): + pass + +class ProxyServerFactory(rdp.ServerFactory): def __init__(self): - rdp.ServerFactory.__init__(self, "/home/sylvain/dev/certificate/rdpy.key", "/home/sylvain/dev/certificate/rdpy.crt") + rdp.ServerFactory.__init__(self, "/home/speyrefitte/dev/certificate/rdpy.key", "/home/speyrefitte/dev/certificate/rdpy.crt") + + def buildObserver(self, controller): + return ProxyServer(controller) + + def startedConnecting(self, connector): + pass + + def clientConnectionLost(self, connector, reason): + pass + + def clientConnectionFailed(self, connector, reason): + pass + +class ProxyClientFactory(rdp.ClientFactory): + def __init__(self, server): + self._server = server + + def buildObserver(self, controller): + return ProxyClient(controller, self._server) + def startedConnecting(self, connector): pass @@ -22,6 +58,6 @@ class TestServerFactory(rdp.ServerFactory): pass if __name__ == '__main__': - from twisted.internet import reactor - reactor.listenTCP(33389, TestServerFactory()) + + reactor.listenTCP(33389, ProxyServerFactory()) reactor.run() \ No newline at end of file diff --git a/rdpy/network/layer.py b/rdpy/network/layer.py index 396743c..1109fe2 100644 --- a/rdpy/network/layer.py +++ b/rdpy/network/layer.py @@ -25,12 +25,6 @@ RDPY use Layer Protocol design (like twisted) from rdpy.base.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 @@ -60,17 +54,14 @@ class Layer(object): A simple double linked list with presentation and transport layer and a subset of event (connect and close) """ - def __init__(self, mode, presentation = None): + def __init__(self, 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 - #network layer mode - self._mode = mode #auto set transport layer of own presentation layer if not self._presentation is None: self._presentation._transport = self @@ -96,13 +87,12 @@ class LayerAutomata(Layer, StreamListener): Layer with automata state we can set next recv function used for Stream packet """ - def __init__(self, mode, presentation = None): + def __init__(self, presentation = None): """ - @param mode: LayerMode use @param presentation: presentation Layer """ #call parent constructor - Layer.__init__(self, mode, presentation) + Layer.__init__(self, presentation) def setNextState(self, callback = None): """ @@ -127,13 +117,12 @@ class RawLayer(protocol.Protocol, LayerAutomata, StreamSender): 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): + def __init__(self, presentation = None): """ - @param mode: LayerMode use @param presentation: presentation layer in layer list """ #call parent automata - LayerAutomata.__init__(self, mode, presentation) + LayerAutomata.__init__(self, presentation) #data buffer received from twisted network layer self._buffer = "" #len of next packet pass to next state function diff --git a/rdpy/network/type.py b/rdpy/network/type.py index 259f531..8e6d27f 100644 --- a/rdpy/network/type.py +++ b/rdpy/network/type.py @@ -665,19 +665,18 @@ class UInt24Le(SimpleType): self.value = struct.unpack(self._structFormat, s.read(self._typeSize) + '\x00')[0] class String(Type, CallableValue): - ''' + """ String network type - ''' + """ def __init__(self, value = "", readLen = None, conditional = lambda:True, optional = False, constant = False, unicode = False): - ''' - constructor with new string + """ @param value: python string use for inner value @param readLen: length use to read in stream (SimpleType) if 0 read entire stream @param conditional : function call before read or write type @param optional: boolean check before read if there is still data in stream @param constant: if true check any changement of object during reading @param unicode: Encode and decode value as unicode - ''' + """ Type.__init__(self, conditional = conditional, optional = optional, constant = constant) CallableValue.__init__(self, value) #type use to know read length diff --git a/rdpy/protocol/rdp/mcs.py b/rdpy/protocol/rdp/mcs.py index 0eb3f34..94cc3fa 100644 --- a/rdpy/protocol/rdp/mcs.py +++ b/rdpy/protocol/rdp/mcs.py @@ -24,7 +24,7 @@ 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.layer import LayerAutomata, StreamSender, Layer, LayerMode +from rdpy.network.layer import LayerAutomata, StreamSender, Layer from rdpy.network.type import sizeof, Stream, UInt8, UInt16Le from rdpy.base.error import InvalidExpectedDataException, InvalidValue, InvalidSize from rdpy.protocol.rdp.ber import writeLength @@ -59,7 +59,7 @@ class Channel: MCS_GLOBAL_CHANNEL = 1003 MCS_USERCHANNEL_BASE = 1001 -class MCS(LayerAutomata): +class MCSLayer(LayerAutomata): """ Multiple Channel Service layer the main layer of RDP protocol @@ -116,13 +116,14 @@ class MCS(LayerAutomata): return self._mcs._serverSettings - def __init__(self, mode, presentation, virtualChannels = []): + def __init__(self, presentation, receiveOpcode, sendOpcode, virtualChannels = []): """ - @param mode: mode of MCS layer @param presentation: presentation layer @param virtualChannels: list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)] + @param receiveOpcode: opcode check when receive data + @param sendOpcode: opcode use when send data """ - LayerAutomata.__init__(self, mode, presentation) + LayerAutomata.__init__(self, presentation) self._clientSettings = gcc.clientSettings() self._serverSettings = gcc.serverSettings() #default user Id @@ -131,56 +132,10 @@ class MCS(LayerAutomata): self._channels = {Channel.MCS_GLOBAL_CHANNEL: presentation} #virtual channels self._virtualChannels = virtualChannels - #nb join and confirm channel - self._nbJoinAndConfirmChannel = 0 - self._isGlobalChannelRequested = False - self._isUserChannelRequested = False - #use to record already requested channel - self._channelIdsRequested = {} - - def connect(self): - """ - Connection send for client mode - a write connect initial packet - """ - if self._mode == LayerMode.CLIENT: - self._clientSettings.getBlock(gcc.MessageType.CS_CORE).serverSelectedProtocol.value = self._transport._selectedProtocol - #ask for virtual channel - self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array = [x for (x, _) in self._virtualChannels] - #send connect initial - self.sendConnectInitial() - #next wait response - self.setNextState(self.recvConnectResponse) - else: - self._serverSettings.getBlock(gcc.MessageType.SC_CORE).clientRequestedProtocol.value = self._transport._requestedProtocol - self.setNextState(self.recvConnectInitial) - - def connectNextChannel(self): - """ - Send sendChannelJoinRequest message on next disconnect channel - client automata function - """ - self.setNextState(self.recvChannelJoinConfirm) - #global channel - if not self._isGlobalChannelRequested: - self.sendChannelJoinRequest(Channel.MCS_GLOBAL_CHANNEL) - self._isGlobalChannelRequested = True - return - - #user channel - if not self._isUserChannelRequested: - self.sendChannelJoinRequest(self._userId) - self._isUserChannelRequested = True - return - - #static virtual channel - if self._nbJoinAndConfirmChannel < self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value: - channelId = self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array[self._nbJoinAndConfirmChannel] - self._nbJoinAndConfirmChannel += 1 - self.sendChannelJoinRequest(channelId) - return - - self.allChannelConnected() + #send opcode + self._sendOpcode = sendOpcode + #receive opcode + self._receiveOpcode = receiveOpcode def allChannelConnected(self): """ @@ -193,84 +148,8 @@ class MCS(LayerAutomata): #try connection on all requested channel for (channelId, layer) in self._channels.iteritems(): #use proxy for each channel - layer._transport = MCS.MCSProxySender(self, channelId) + layer._transport = MCSLayer.MCSProxySender(self, channelId) layer.connect() - - def sendConnectInitial(self): - """ - Send connect initial packet - client automata function - """ - ccReq = gcc.writeConferenceCreateRequest(self._clientSettings) - ccReqStream = Stream() - ccReqStream.writeType(ccReq) - - tmp = (ber.writeOctetstring("\x01"), ber.writeOctetstring("\x01"), ber.writeBoolean(True), - self.writeDomainParams(34, 2, 0, 0xffff), - self.writeDomainParams(1, 1, 1, 0x420), - self.writeDomainParams(0xffff, 0xfc17, 0xffff, 0xffff), - ber.writeOctetstring(ccReqStream.getvalue())) - self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_INITIAL, sizeof(tmp)), tmp)) - - def sendConnectResponse(self): - """ - Send connect response - server automata function - """ - ccReq = gcc.writeConferenceCreateResponse(self._serverSettings) - ccReqStream = Stream() - ccReqStream.writeType(ccReq) - - tmp = (ber.writeEnumerated(0), ber.writeInteger(0), self.writeDomainParams(22, 3, 0, 0xfff8), - ber.writeOctetstring(ccReqStream.getvalue())) - self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_RESPONSE, sizeof(tmp)), tmp)) - - def sendErectDomainRequest(self): - """ - Send a formated erect domain request for RDP connection - client automata function - """ - self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ERECT_DOMAIN_REQUEST)), - per.writeInteger(0), - per.writeInteger(0))) - - def sendAttachUserRequest(self): - """ - Send a formated attach user request for RDP connection - client automata function - """ - self._transport.send(self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_REQUEST))) - - def sendAttachUserConfirm(self): - """ - Send attach user confirm - server automata function - """ - self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_CONFIRM), 2), - per.writeEnumerates(0), - per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE))) - - def sendChannelJoinRequest(self, channelId): - """ - Send a formated Channel join request from client to server - client automata function - @param channelId: id of channel requested - """ - self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_REQUEST)), - per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE), - per.writeInteger16(channelId))) - - def sendChannelJoinConfirm(self, channelId, confirm): - """ - Send a confirm channel (or not) to client - @param channelId: id of channel - @param confirm: connection state - """ - self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_CONFIRM), 2), - per.writeEnumerates(int(confirm)), - per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE), - per.writeInteger16(channelId), - per.writeInteger16(channelId))) def send(self, channelId, data): """ @@ -278,166 +157,11 @@ class MCS(LayerAutomata): @param channelId: Channel use to send @param data: message to send """ - opcode = DomainMCSPDU.SEND_DATA_REQUEST if self._mode == LayerMode.CLIENT else DomainMCSPDU.SEND_DATA_INDICATION - self._transport.send((self.writeMCSPDUHeader(UInt8(opcode)), + self._transport.send((self.writeMCSPDUHeader(UInt8(self._sendOpcode)), per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE), per.writeInteger16(channelId), UInt8(0x70), per.writeLength(sizeof(data)), data)) - - def recvConnectInitial(self, data): - """ - Receive MCS connect initial from client - server automata function - @param data: Stream - """ - ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_INITIAL)) - ber.readOctetString(data) - ber.readOctetString(data) - - if not ber.readBoolean(data): - raise InvalidExpectedDataException("invalid expected BER boolean tag") - - self.readDomainParams(data) - self.readDomainParams(data) - self.readDomainParams(data) - self._clientSettings = gcc.readConferenceCreateRequest(Stream(ber.readOctetString(data))) - - self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array = [UInt16Le(x + Channel.MCS_GLOBAL_CHANNEL) for x in range(1, len(self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array) + 1)] - - self.sendConnectResponse() - self.setNextState(self.recvErectDomainRequest) - - def recvConnectResponse(self, data): - """ - Receive MCS connect response from server - client automata function - @param data: Stream - """ - ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_RESPONSE)) - ber.readEnumerated(data) - ber.readInteger(data) - self.readDomainParams(data) - if not ber.readUniversalTag(data, ber.Tag.BER_TAG_OCTET_STRING, False): - raise InvalidExpectedDataException("invalid expected BER tag") - gccRequestLength = ber.readLength(data) - if data.dataLen() != gccRequestLength: - raise InvalidSize("bad size of GCC request") - self._serverSettings = gcc.readConferenceCreateResponse(data) - - #send domain request - self.sendErectDomainRequest() - #send attach user request - self.sendAttachUserRequest() - #now wait user confirm from server - self.setNextState(self.recvAttachUserConfirm) - - def recvErectDomainRequest(self, data): - """ - Receive erect domain request - server automata function - @param data: Stream - """ - opcode = UInt8() - data.readType(opcode) - - if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ERECT_DOMAIN_REQUEST): - raise InvalidExpectedDataException("Invalid MCS PDU : ERECT_DOMAIN_REQUEST expected") - - per.readInteger(data) - per.readInteger(data) - - self.setNextState(self.recvAttachUserRequest) - - def recvAttachUserRequest(self, data): - """ - Receive Attach user request - server automata function - @param data: Stream - """ - opcode = UInt8() - data.readType(opcode) - - if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_REQUEST): - raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_REQUEST expected") - - self.sendAttachUserConfirm() - self.setNextState(self.recvChannelJoinRequest) - - def recvAttachUserConfirm(self, data): - """ - Receive an attach user confirm - client automata function - @param data: Stream - """ - opcode = UInt8() - data.readType(opcode) - - if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_CONFIRM): - raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_CONFIRM expected") - - if per.readEnumerates(data) != 0: - raise InvalidExpectedDataException("Server reject user") - - self._userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE) - - self.connectNextChannel() - - def recvChannelJoinRequest(self, data): - """ - Receive for each client channel a request - server automata function - @param data: Stream - - """ - opcode = UInt8() - data.readType(opcode) - - if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_REQUEST): - raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_REQUEST expected") - - userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE) - if self._userId != userId: - raise InvalidExpectedDataException("Invalid MCS User Id") - - channelId = per.readInteger16(data) - #TODO check if it's a virtual channel too - #actually algo support virtual channel but not RDPY - self.sendChannelJoinConfirm(channelId, channelId in self._channels.keys() or channelId == self._userId) - self._nbJoinAndConfirmChannel += 1 - if self._nbJoinAndConfirmChannel == self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value + 2: - self.allChannelConnected() - - def recvChannelJoinConfirm(self, data): - """ - Receive a channel join confirm from server - client automata function - @param data: Stream - """ - opcode = UInt8() - data.readType(opcode) - - if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_CONFIRM): - raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_CONFIRM expected") - - confirm = per.readEnumerates(data) - - userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE) - if self._userId != userId: - raise InvalidExpectedDataException("Invalid MCS User Id") - - channelId = per.readInteger16(data) - #must confirm global channel and user channel - if (confirm != 0) and (channelId == Channel.MCS_GLOBAL_CHANNEL or channelId == self._userId): - raise InvalidExpectedDataException("Server must confirm static channel") - - if confirm ==0: - serverNet = self._serverSettings.getBlock(gcc.MessageType.SC_NET) - for i in range(0, serverNet.channelCount.value): - if channelId == serverNet.channelIdArray._array[i].value: - self._channels[channelId] = self._virtualChannels[i][1] - - self.connectNextChannel() def recvData(self, data): """ @@ -453,11 +177,8 @@ class MCS(LayerAutomata): return #client case - elif self._mode == LayerMode.CLIENT and not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.SEND_DATA_INDICATION): - raise InvalidExpectedDataException("Invalid expected MCS opcode for server to client communication") - - elif self._mode == LayerMode.SERVER and not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.SEND_DATA_REQUEST): - raise InvalidExpectedDataException("Invalid expected MCS opcode for client to server communication") + elif not self.readMCSPDUHeader(opcode.value, self._receiveOpcode): + raise InvalidExpectedDataException("Invalid expected MCS opcode receive data") #server user id per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE) @@ -524,4 +245,318 @@ class MCS(LayerAutomata): max_pdu_size = ber.readInteger(s) ber.readInteger(s) return (max_channels, max_users, max_tokens, max_pdu_size) - \ No newline at end of file + +class Client(MCSLayer): + """ + Client automata of multiple channel service layer + """ + def __init__(self, presentation, virtualChannels = []): + """ + @param presentation: presentation layer + @param virtualChannels: list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)] + """ + MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST, virtualChannels) + #use to know state of static channel + self._isGlobalChannelRequested = False + self._isUserChannelRequested = False + #nb channel requested + self._nbChannelRequested = 0 + + def connect(self): + """ + Connect message in client automata case + Send ConnectInitial + Wait ConnectResponse + """ + self._clientSettings.getBlock(gcc.MessageType.CS_CORE).serverSelectedProtocol.value = self._transport._selectedProtocol + #ask for virtual channel + self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array = [x for (x, _) in self._virtualChannels] + #send connect initial + self.sendConnectInitial() + #next wait response + self.setNextState(self.recvConnectResponse) + + def connectNextChannel(self): + """ + Send sendChannelJoinRequest message on next disconnect channel + Send channel request or connect upper layer if all channels are connected + Wait channel confirm + """ + self.setNextState(self.recvChannelJoinConfirm) + #global channel + if not self._isGlobalChannelRequested: + self.sendChannelJoinRequest(Channel.MCS_GLOBAL_CHANNEL) + self._isGlobalChannelRequested = True + return + + #user channel + if not self._isUserChannelRequested: + self.sendChannelJoinRequest(self._userId) + self._isUserChannelRequested = True + return + + #static virtual channel + if self._nbChannelRequested < self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value: + channelId = self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array[self._nbChannelRequested] + self._nbChannelRequested += 1 + self.sendChannelJoinRequest(channelId) + return + + self.allChannelConnected() + + def recvConnectResponse(self, data): + """ + Receive MCS connect response from server + Send Erect domain Request + Send Attach User Request + Wait Attach User Confirm + @param data: Stream + """ + ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_RESPONSE)) + ber.readEnumerated(data) + ber.readInteger(data) + self.readDomainParams(data) + if not ber.readUniversalTag(data, ber.Tag.BER_TAG_OCTET_STRING, False): + raise InvalidExpectedDataException("invalid expected BER tag") + gccRequestLength = ber.readLength(data) + if data.dataLen() != gccRequestLength: + raise InvalidSize("bad size of GCC request") + self._serverSettings = gcc.readConferenceCreateResponse(data) + + #send domain request + self.sendErectDomainRequest() + #send attach user request + self.sendAttachUserRequest() + #now wait user confirm from server + self.setNextState(self.recvAttachUserConfirm) + + def recvAttachUserConfirm(self, data): + """ + Receive an attach user confirm + Send Connect Channel + @param data: Stream + """ + opcode = UInt8() + data.readType(opcode) + + if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_CONFIRM): + raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_CONFIRM expected") + + if per.readEnumerates(data) != 0: + raise InvalidExpectedDataException("Server reject user") + + self._userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE) + + self.connectNextChannel() + + def recvChannelJoinConfirm(self, data): + """ + Receive a channel join confirm from server + client automata function + @param data: Stream + """ + opcode = UInt8() + data.readType(opcode) + + if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_CONFIRM): + raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_CONFIRM expected") + + confirm = per.readEnumerates(data) + + userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE) + if self._userId != userId: + raise InvalidExpectedDataException("Invalid MCS User Id") + + channelId = per.readInteger16(data) + #must confirm global channel and user channel + if (confirm != 0) and (channelId == Channel.MCS_GLOBAL_CHANNEL or channelId == self._userId): + raise InvalidExpectedDataException("Server must confirm static channel") + + if confirm ==0: + serverNet = self._serverSettings.getBlock(gcc.MessageType.SC_NET) + for i in range(0, serverNet.channelCount.value): + if channelId == serverNet.channelIdArray._array[i].value: + self._channels[channelId] = self._virtualChannels[i][1] + + self.connectNextChannel() + + def sendConnectInitial(self): + """ + Send connect initial packet + client automata function + """ + ccReq = gcc.writeConferenceCreateRequest(self._clientSettings) + ccReqStream = Stream() + ccReqStream.writeType(ccReq) + + tmp = (ber.writeOctetstring("\x01"), ber.writeOctetstring("\x01"), ber.writeBoolean(True), + self.writeDomainParams(34, 2, 0, 0xffff), + self.writeDomainParams(1, 1, 1, 0x420), + self.writeDomainParams(0xffff, 0xfc17, 0xffff, 0xffff), + ber.writeOctetstring(ccReqStream.getvalue())) + self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_INITIAL, sizeof(tmp)), tmp)) + + def sendErectDomainRequest(self): + """ + Send a formated erect domain request for RDP connection + """ + self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ERECT_DOMAIN_REQUEST)), + per.writeInteger(0), + per.writeInteger(0))) + + def sendAttachUserRequest(self): + """ + Send a formated attach user request for RDP connection + """ + self._transport.send(self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_REQUEST))) + + def sendChannelJoinRequest(self, channelId): + """ + Send a formated Channel join request from client to server + client automata function + @param channelId: id of channel requested + """ + self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_REQUEST)), + per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE), + per.writeInteger16(channelId))) + +class Server(MCSLayer): + """ + Server automata of multiple channel service layer + """ + def __init__(self, presentation, virtualChannels = []): + """ + @param presentation: presentation layer + @param virtualChannels: list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)] + """ + MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION, virtualChannels) + #nb channel requested + self._nbChannelConfirmed = 0 + + def connect(self): + """ + Connect message for server automata + Wait Connect Initial + """ + self._serverSettings.getBlock(gcc.MessageType.SC_CORE).clientRequestedProtocol.value = self._transport._requestedProtocol + self.setNextState(self.recvConnectInitial) + + def recvConnectInitial(self, data): + """ + Receive MCS connect initial from client + Send Connect Response + Wait Erect Domain Request + @param data: Stream + """ + ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_INITIAL)) + ber.readOctetString(data) + ber.readOctetString(data) + + if not ber.readBoolean(data): + raise InvalidExpectedDataException("invalid expected BER boolean tag") + + self.readDomainParams(data) + self.readDomainParams(data) + self.readDomainParams(data) + self._clientSettings = gcc.readConferenceCreateRequest(Stream(ber.readOctetString(data))) + + i = 1 + for channelDef in self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array: + self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array.append(UInt16Le(i + Channel.MCS_GLOBAL_CHANNEL)) + #if channel can be handle by serve add it + for serverChannelDef, layer in self._virtualChannels: + if channelDef.name == serverChannelDef.name: + self._channels[i + Channel.MCS_GLOBAL_CHANNEL] = layer + i += 1 + + self.sendConnectResponse() + self.setNextState(self.recvErectDomainRequest) + + def recvErectDomainRequest(self, data): + """ + Receive erect domain request + Wait Attach User Request + @param data: Stream + """ + opcode = UInt8() + data.readType(opcode) + + if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ERECT_DOMAIN_REQUEST): + raise InvalidExpectedDataException("Invalid MCS PDU : ERECT_DOMAIN_REQUEST expected") + + per.readInteger(data) + per.readInteger(data) + + self.setNextState(self.recvAttachUserRequest) + + def recvAttachUserRequest(self, data): + """ + Receive Attach user request + Send Attach User Confirm + Wait Channel Join Request + @param data: Stream + """ + opcode = UInt8() + data.readType(opcode) + + if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_REQUEST): + raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_REQUEST expected") + + self.sendAttachUserConfirm() + self.setNextState(self.recvChannelJoinRequest) + + def recvChannelJoinRequest(self, data): + """ + Receive for each client channel a request + Send Channel Join Confirm or Connect upper layer when all channel are joined + @param data: Stream + + """ + opcode = UInt8() + data.readType(opcode) + + if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_REQUEST): + raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_REQUEST expected") + + userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE) + if self._userId != userId: + raise InvalidExpectedDataException("Invalid MCS User Id") + + channelId = per.readInteger16(data) + #actually algo support virtual channel but RDPY have no virtual channel + self.sendChannelJoinConfirm(channelId, channelId in self._channels.keys() or channelId == self._userId) + self._nbChannelConfirmed += 1 + if self._nbChannelConfirmed == self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value + 2: + self.allChannelConnected() + + def sendConnectResponse(self): + """ + Send connect response + """ + ccReq = gcc.writeConferenceCreateResponse(self._serverSettings) + ccReqStream = Stream() + ccReqStream.writeType(ccReq) + + tmp = (ber.writeEnumerated(0), ber.writeInteger(0), self.writeDomainParams(22, 3, 0, 0xfff8), + ber.writeOctetstring(ccReqStream.getvalue())) + self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_RESPONSE, sizeof(tmp)), tmp)) + + def sendAttachUserConfirm(self): + """ + Send attach user confirm + """ + self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_CONFIRM), 2), + per.writeEnumerates(0), + per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE))) + + def sendChannelJoinConfirm(self, channelId, confirm): + """ + Send a confirm channel (or not) to client + @param channelId: id of channel + @param confirm: connection state + """ + self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_CONFIRM), 2), + per.writeEnumerates(int(confirm)), + per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE), + per.writeInteger16(channelId), + per.writeInteger16(channelId))) \ No newline at end of file diff --git a/rdpy/protocol/rdp/pdu/__init__.py b/rdpy/protocol/rdp/pdu/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdpy/protocol/rdp/caps.py b/rdpy/protocol/rdp/pdu/caps.py similarity index 100% rename from rdpy/protocol/rdp/caps.py rename to rdpy/protocol/rdp/pdu/caps.py diff --git a/rdpy/protocol/rdp/pdu.py b/rdpy/protocol/rdp/pdu/data.py similarity index 68% rename from rdpy/protocol/rdp/pdu.py rename to rdpy/protocol/rdp/pdu/data.py index 1830f00..cdbf50b 100644 --- a/rdpy/protocol/rdp/pdu.py +++ b/rdpy/protocol/rdp/pdu/data.py @@ -22,12 +22,10 @@ Implement the main graphic layer In this layer are managed all mains bitmap update orders end user inputs """ - -from rdpy.network.layer import LayerAutomata, LayerMode from rdpy.network.type import CompositeType, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType -from rdpy.base.error import InvalidExpectedDataException, CallPureVirtualFuntion, InvalidType +from rdpy.base.error import InvalidExpectedDataException import rdpy.base.log as log -import gcc, lic, caps, tpkt +import caps class SecurityFlag(object): """ @@ -809,23 +807,29 @@ class UpdateDataPDU(CompositeType): """ _PDUTYPE2_ = PDUType2.PDUTYPE2_UPDATE - def __init__(self, updateType = 0, updateData = None, readLen = None): + def __init__(self, updateData = None, readLen = None): """ @param updateType: UpdateType macro @param updateData: Update data PDU in accordance with updateType (BitmapUpdateDataPDU) @param readLen: Max length to read """ CompositeType.__init__(self, readLen = readLen) - self.updateType = UInt16Le(updateType) + self.updateType = UInt16Le(lambda:updateData.__class__._UPDATE_TYPE_) def UpdateDataFactory(): - if self.updateType.value == UpdateType.UPDATETYPE_BITMAP: - return BitmapUpdateDataPDU() - else: - return String() - + """ + Create object in accordance self.updateType value + """ + for c in [BitmapUpdateDataPDU]: + if self.updateType.value == c._UPDATE_TYPE_: + return c() + log.debug("unknown PDU update data type : %s"%hex(self.updateType.value)) + return String() + if updateData is None: updateData = FactoryType(UpdateDataFactory, conditional = lambda:(self.updateType.value != UpdateType.UPDATETYPE_SYNCHRONIZE)) + elif not "_UPDATE_TYPE_" in updateData.__class__.__dict__: + raise InvalidExpectedDataException("Try to send an invalid data update PDU") self.updateData = updateData @@ -834,9 +838,9 @@ class FastPathUpdatePDU(CompositeType): Fast path update PDU packet @see: http://msdn.microsoft.com/en-us/library/cc240622.aspx """ - def __init__(self, updateType = 0, updateData = None): + def __init__(self, updateData = None): CompositeType.__init__(self) - self.updateHeader = UInt8(updateType) + self.updateHeader = UInt8(lambda:updateData.__class__._FASTPATH_UPDATE_TYPE_) self.compressionFlags = UInt8(conditional = lambda:((self.updateHeader.value >> 4) & FastPathOutputCompression.FASTPATH_OUTPUT_COMPRESSION_USED)) self.size = UInt16Le() @@ -851,6 +855,8 @@ class FastPathUpdatePDU(CompositeType): if updateData is None: updateData = FactoryType(UpdateDataFactory) + elif not "_FASTPATH_UPDATE_TYPE_" in updateData.__class__.__dict__: + raise InvalidExpectedDataException("Try to send an invalid fast path data update PDU") self.updateData = updateData @@ -859,6 +865,9 @@ class BitmapUpdateDataPDU(CompositeType): PDU use to send raw bitmap compressed or not @see: http://msdn.microsoft.com/en-us/library/dd306368.aspx """ + _UPDATE_TYPE_ = UpdateType.UPDATETYPE_BITMAP + _FASTPATH_UPDATE_TYPE_ = FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP + def __init__(self, readLen = None): """ @param readLen: Max size of packet @@ -983,469 +992,4 @@ class UnicodeKeyEvent(CompositeType): CompositeType.__init__(self) self.keyboardFlags = UInt16Le() self.unicode = UInt16Le() - self.pad2Octets = UInt16Le() - - -class PDUClientListener(object): - """ - Interface for PDU client automata listener - """ - def onReady(self): - """ - Event call when PDU layer is ready to send events - """ - raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUClientListener")) - - def onUpdate(self, rectangles): - """ - call when a bitmap data is received from update PDU - @param rectangles: [pdu.BitmapData] struct - """ - raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "PDUClientListener")) - - def recvDstBltOrder(self, order): - """ - @param order: rectangle order - """ - pass - -class PDUServerListener(object): - """ - Interface for PDU server automata listener - """ - pass - -class PDULayer(LayerAutomata, tpkt.FastPathListener): - """ - Global channel for MCS that handle session - identification user, licensing management, and capabilities exchange - """ - def __init__(self, listener): - """ - @param listener: listener use to inform orders - """ - mode = None - if isinstance(listener, PDUClientListener): - mode = LayerMode.CLIENT - #set client listener - self._clientListener = listener - elif isinstance(listener, PDUServerListener): - mode = LayerMode.SERVER - else: - raise InvalidType("PDU Layer expect PDU(Client|Server)Listener as listener") - - LayerAutomata.__init__(self, mode, None) - - #logon info send from client to server - self._info = RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_CORE).rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS)) - #server capabilities - self._serverCapabilities = { - caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()), - caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()), - caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()), - caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()), - caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()), - caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()), - caps.CapsType.CAPSTYPE_FONT : caps.Capability(caps.CapsType.CAPSTYPE_FONT, caps.FontCapability()), - caps.CapsType.CAPSTYPE_COLORCACHE : caps.Capability(caps.CapsType.CAPSTYPE_COLORCACHE, caps.ColorCacheCapability()), - caps.CapsType.CAPSTYPE_SHARE : caps.Capability(caps.CapsType.CAPSTYPE_SHARE, caps.ShareCapability()) - } - #client capabilities - self._clientCapabilities = { - caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()), - caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()), - caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()), - caps.CapsType.CAPSTYPE_BITMAPCACHE : caps.Capability(caps.CapsType.CAPSTYPE_BITMAPCACHE, caps.BitmapCacheCapability()), - caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()), - caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()), - caps.CapsType.CAPSTYPE_BRUSH : caps.Capability(caps.CapsType.CAPSTYPE_BRUSH, caps.BrushCapability()), - caps.CapsType.CAPSTYPE_GLYPHCACHE : caps.Capability(caps.CapsType.CAPSTYPE_GLYPHCACHE, caps.GlyphCapability()), - caps.CapsType.CAPSTYPE_OFFSCREENCACHE : caps.Capability(caps.CapsType.CAPSTYPE_OFFSCREENCACHE, caps.OffscreenBitmapCacheCapability()), - caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()), - caps.CapsType.CAPSTYPE_SOUND : caps.Capability(caps.CapsType.CAPSTYPE_SOUND, caps.SoundCapability()) - } - #share id between client and server - self._shareId = 0x103EA - - def connect(self): - """ - Connect event in client mode send logon info - Next state receive license PDU - """ - if self._mode == LayerMode.CLIENT: - self.sendInfoPkt() - #next state is license info PDU - self.setNextState(self.recvLicenceInfo) - else: - self.setNextState(self.recvInfoPkt) - - def close(self): - """ - Send PDU close packet and call close method on transport method - """ - self.sendDataPDU(ShutdownRequestPDU()) - - def recvInfoPkt(self, data): - """ - Receive info packet from client - Client credential - @param data: Stream - """ - securityFlag = UInt16Le() - securityFlagHi = UInt16Le() - data.readType((securityFlag, securityFlagHi)) - - if not (securityFlag.value & SecurityFlag.SEC_INFO_PKT): - raise InvalidExpectedDataException("Waiting info packet") - - data.readType(self._info) - #next state send error license - self.sendLicensingErrorMessage() - self.sendDemandActivePDU() - self.setNextState(self.recvConfirmActivePDU) - - def recvLicenceInfo(self, data): - """ - Read license info packet and check if is a valid client info - @param data: Stream - """ - #packet preambule - securityFlag = UInt16Le() - securityFlagHi = UInt16Le() - data.readType((securityFlag, securityFlagHi)) - - if not (securityFlag.value & SecurityFlag.SEC_LICENSE_PKT): - raise InvalidExpectedDataException("Waiting license packet") - - validClientPdu = lic.LicPacket() - data.readType(validClientPdu) - - if validClientPdu.bMsgtype.value == lic.MessageType.ERROR_ALERT and validClientPdu.licensingMessage.dwErrorCode.value == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.licensingMessage.dwStateTransition.value == lic.StateTransition.ST_NO_TRANSITION: - self.setNextState(self.recvDemandActivePDU) - #not tested because i can't buy RDP license server - elif validClientPdu.bMsgtype.value == lic.MessageType.LICENSE_REQUEST: - newLicenseReq = lic.createNewLicenseRequest(validClientPdu.licensingMessage) - self._transport.send((UInt16Le(SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), newLicenseReq)) - else: - raise InvalidExpectedDataException("Not a valid license packet") - - def recvDemandActivePDU(self, data): - """ - Receive demand active PDU which contains - Server capabilities. In this version of RDPY only - Restricted group of capabilities are used. - Send confirm active PDU - @param data: Stream - """ - pdu = PDU() - data.readType(pdu) - - if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DEMANDACTIVEPDU: - raise InvalidExpectedDataException("Expected Demand Active PDU from server") - - self._shareId = pdu.pduMessage.shareId.value - - for cap in pdu.pduMessage.capabilitySets._array: - self._serverCapabilities[cap.capabilitySetType] = cap - - self.sendConfirmActivePDU() - #send synchronize - self.sendClientFinalizeSynchronizePDU() - self.setNextState(self.recvServerSynchronizePDU) - - def recvConfirmActivePDU(self, data): - """ - Receive confirm active PDU from client - Capabilities exchange - @param data: Stream - """ - pdu = PDU() - data.readType(pdu) - - if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_CONFIRMACTIVEPDU: - raise InvalidExpectedDataException("Expected Confirm Active PDU from client") - - for cap in pdu.pduMessage.capabilitySets._array: - self._clientCapabilities[cap.capabilitySetType] = cap - - self.setNextState(self.recvClientSynchronizePDU) - - def recvServerSynchronizePDU(self, data): - """ - Receive from server - @param data: Stream from transport layer - """ - pdu = PDU() - data.readType(pdu) - if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_SYNCHRONIZE: - raise InvalidExpectedDataException("Error in PDU layer automata : expected synchronizePDU") - self.setNextState(self.recvServerControlCooperatePDU) - - def recvServerControlCooperatePDU(self, data): - """ - Receive control cooperate PDU from server - @param data: Stream from transport layer - """ - pdu = PDU() - data.readType(pdu) - if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != Action.CTRLACTION_COOPERATE: - raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU") - self.setNextState(self.recvServerControlGrantedPDU) - - def recvServerControlGrantedPDU(self, data): - """ - Receive last control PDU the granted control PDU - @param data: Stream from transport layer - """ - pdu = PDU() - data.readType(pdu) - if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != Action.CTRLACTION_GRANTED_CONTROL: - raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU") - self.setNextState(self.recvServerFontMapPDU) - - def recvServerFontMapPDU(self, data): - """ - Last useless connection packet from server to client - @param data: Stream from transport layer - """ - pdu = PDU() - data.readType(pdu) - if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_FONTMAP: - raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU") - - #here i'm connected - self._clientListener.onReady() - self.setNextState(self.recvPDU) - - def recvClientSynchronizePDU(self, data): - """ - Receive from client - @param data: Stream from transport layer - """ - pdu = PDU() - data.readType(pdu) - if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_SYNCHRONIZE: - raise InvalidExpectedDataException("Error in PDU layer automata : expected synchronizePDU") - self.setNextState(self.recvClientControlCooperatePDU) - - def recvClientControlCooperatePDU(self, data): - """ - Receive control cooperate PDU from client - @param data: Stream from transport layer - """ - pdu = PDU() - data.readType(pdu) - if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != Action.CTRLACTION_COOPERATE: - raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU") - self.setNextState(self.recvClientControlRequestPDU) - - def recvClientControlRequestPDU(self, data): - """ - Receive last control PDU the request control PDU from client - @param data: Stream from transport layer - """ - pdu = PDU() - data.readType(pdu) - if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != Action.CTRLACTION_REQUEST_CONTROL: - raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU") - self.setNextState(self.recvClientFontListPDU) - - def recvClientFontListPDU(self, data): - """ - Last synchronize packet from client to server - @param data: Stream from transport layer - """ - pdu = PDU() - data.readType(pdu) - if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_FONTLIST: - raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU") - - #finalize server - self.sendServerFinalizeSynchronizePDU() - self.setNextState(self.recvPDU) - - def recvPDU(self, data): - """ - Main receive function after connection sequence - @param data: Stream from transport layer - """ - pdu = PDU() - data.readType(pdu) - if pdu.shareControlHeader.pduType.value == PDUType.PDUTYPE_DATAPDU: - self.readDataPDU(pdu.pduMessage) - elif pdu.shareControlHeader.pduType.value == PDUType.PDUTYPE_DEACTIVATEALLPDU: - #use in deactivation-reactivation sequence - #next state is either a capabilities re exchange or disconnection - #http://msdn.microsoft.com/en-us/library/cc240454.aspx - self.setNextState(self.recvDemandActivePDU) - - def recvFastPath(self, fastPathData): - """ - Implement FastPathListener interface - Fast path is needed by RDP 8.0 - @param fastPathData: Stream that contain fast path data - """ - fastPathPDU = FastPathUpdatePDU() - fastPathData.readType(fastPathPDU) - if fastPathPDU.updateHeader.value == FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP: - self._clientListener.onUpdate(fastPathPDU.updateData[1].rectangles._array) - - def readDataPDU(self, dataPDU): - """ - read a data PDU object - @param dataPDU: DataPDU object - """ - if dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU: - message = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value) - if ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo): - message = ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo] - - log.error("INFO PDU : %s"%message) - elif dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_SHUTDOWN_DENIED: - #may be an event to ask to user - self._transport.close() - elif dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_UPDATE: - self.readUpdateDataPDU(dataPDU.pduData) - - def readUpdateDataPDU(self, updateDataPDU): - """ - Read an update data PDU message - dispatch update message - @param: UpdateDataPDU object - """ - if updateDataPDU.updateType.value == UpdateType.UPDATETYPE_BITMAP: - self._clientListener.onUpdate(updateDataPDU.updateData.rectangles._array) - - def sendInfoPkt(self): - """ - Send a logon info packet - client automata message - """ - self._transport.send((UInt16Le(SecurityFlag.SEC_INFO_PKT), UInt16Le(), self._info)) - - def sendLicensingErrorMessage(self): - """ - Send a licensing error message - server automata message - """ - self._transport.send((UInt16Le(SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), lic.createValidClientLicensingErrorMessage())) - - def sendDemandActivePDU(self): - """ - Send server capabilities - server automata PDU - """ - #init general capability - generalCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability - generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS - generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT - generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED - - #init bitmap capability - bitmapCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability - bitmapCapability.preferredBitsPerPixel.value = 16 - bitmapCapability.desktopWidth.value = 800 - bitmapCapability.desktopHeight.value = 600 - - demandActivePDU = DemandActivePDU() - demandActivePDU.shareId.value = self._shareId - demandActivePDU.capabilitySets._array = self._serverCapabilities.values() - self.sendPDU(demandActivePDU) - - def sendPDU(self, pduMessage): - """ - Send a PDU message to transport layer - """ - self._transport.send(PDU(self._transport.getUserId(), pduMessage)) - - def sendDataPDU(self, pduData): - """ - Send an PDUData to transport layer - """ - self.sendPDU(DataPDU(pduData, self._shareId)) - - def sendConfirmActivePDU(self): - """ - Send all client capabilities - """ - #init general capability - generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability - generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS - generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT - generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED - - #init bitmap capability - bitmapCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability - bitmapCapability.preferredBitsPerPixel = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).highColorDepth - bitmapCapability.desktopWidth = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).desktopWidth - bitmapCapability.desktopHeight = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).desktopHeight - - #init order capability - orderCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].capability - orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT - - #init input capability - inputCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_INPUT].capability - inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE - inputCapability.keyboardLayout = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).kbdLayout - inputCapability.keyboardType = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardType - inputCapability.keyboardSubType = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardSubType - inputCapability.keyboardrFunctionKey = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardFnKeys - inputCapability.imeFileName = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).imeFileName - - #make active PDU packet - confirmActivePDU = ConfirmActivePDU() - confirmActivePDU.shareId.value = self._shareId - confirmActivePDU.capabilitySets._array = self._clientCapabilities.values() - self.sendPDU(confirmActivePDU) - - def sendClientFinalizeSynchronizePDU(self): - """ - send a synchronize PDU from client to server - """ - synchronizePDU = SynchronizeDataPDU(self._transport.getChannelId()) - self.sendDataPDU(synchronizePDU) - - #ask for cooperation - controlCooperatePDU = ControlDataPDU(Action.CTRLACTION_COOPERATE) - self.sendDataPDU(controlCooperatePDU) - - #request control - controlRequestPDU = ControlDataPDU(Action.CTRLACTION_REQUEST_CONTROL) - self.sendDataPDU(controlRequestPDU) - - #TODO persistent key list http://msdn.microsoft.com/en-us/library/cc240494.aspx - - #deprecated font list pdu - fontListPDU = FontListDataPDU() - self.sendDataPDU(fontListPDU) - - def sendServerFinalizeSynchronizePDU(self): - """ - Send last synchronize packet from server to client - """ - synchronizePDU = SynchronizeDataPDU(self._transport.getChannelId()) - self.sendDataPDU(synchronizePDU) - - #ask for cooperation - controlCooperatePDU = ControlDataPDU(Action.CTRLACTION_COOPERATE) - self.sendDataPDU(controlCooperatePDU) - - #request control - controlRequestPDU = ControlDataPDU(Action.CTRLACTION_GRANTED_CONTROL) - self.sendDataPDU(controlRequestPDU) - - #TODO persistent key list http://msdn.microsoft.com/en-us/library/cc240494.aspx - - #deprecated font list pdu - fontMapPDU = FontMapDataPDU() - self.sendDataPDU(fontMapPDU) - - def sendInputEvents(self, pointerEvents): - """ - send client input events - @param pointerEvents: list of pointer events - """ - pdu = ClientInputEventPDU() - pdu.slowPathInputEvents._array = [SlowPathInputEvent(x) for x in pointerEvents] - self.sendDataPDU(pdu) \ No newline at end of file + self.pad2Octets = UInt16Le() \ No newline at end of file diff --git a/rdpy/protocol/rdp/pdu/layer.py b/rdpy/protocol/rdp/pdu/layer.py new file mode 100644 index 0000000..b3177f7 --- /dev/null +++ b/rdpy/protocol/rdp/pdu/layer.py @@ -0,0 +1,554 @@ +# +# 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 the main graphic layer + +In this layer are managed all mains bitmap update orders end user inputs +""" + +from rdpy.network.layer import LayerAutomata +from rdpy.base.error import InvalidExpectedDataException, CallPureVirtualFuntion +from rdpy.network.type import UInt16Le +import rdpy.base.log as log +import rdpy.protocol.rdp.gcc as gcc +import rdpy.protocol.rdp.tpkt as tpkt +import lic, data, caps + +class PDUClientListener(object): + """ + Interface for PDU client automata listener + """ + def onReady(self): + """ + Event call when PDU layer is ready to send events + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUClientListener")) + + def onUpdate(self, rectangles): + """ + call when a bitmap data is received from update PDU + @param rectangles: [pdu.BitmapData] struct + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "PDUClientListener")) + + def recvDstBltOrder(self, order): + """ + @param order: rectangle order + """ + pass + +class PDUServerListener(object): + """ + Interface for PDU server automata listener + """ + def onReady(self): + """ + Event call when PDU layer is ready to send update + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUServerListener")) + + +class PDULayer(LayerAutomata): + """ + Global channel for MCS that handle session + identification user, licensing management, and capabilities exchange + """ + def __init__(self): + LayerAutomata.__init__(self, None) + + #logon info send from client to server + self._info = data.RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_CORE).rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS)) + #server capabilities + self._serverCapabilities = { + caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()), + caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()), + caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()), + caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()), + caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()), + caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()), + caps.CapsType.CAPSTYPE_FONT : caps.Capability(caps.CapsType.CAPSTYPE_FONT, caps.FontCapability()), + caps.CapsType.CAPSTYPE_COLORCACHE : caps.Capability(caps.CapsType.CAPSTYPE_COLORCACHE, caps.ColorCacheCapability()), + caps.CapsType.CAPSTYPE_SHARE : caps.Capability(caps.CapsType.CAPSTYPE_SHARE, caps.ShareCapability()) + } + #client capabilities + self._clientCapabilities = { + caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()), + caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()), + caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()), + caps.CapsType.CAPSTYPE_BITMAPCACHE : caps.Capability(caps.CapsType.CAPSTYPE_BITMAPCACHE, caps.BitmapCacheCapability()), + caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()), + caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()), + caps.CapsType.CAPSTYPE_BRUSH : caps.Capability(caps.CapsType.CAPSTYPE_BRUSH, caps.BrushCapability()), + caps.CapsType.CAPSTYPE_GLYPHCACHE : caps.Capability(caps.CapsType.CAPSTYPE_GLYPHCACHE, caps.GlyphCapability()), + caps.CapsType.CAPSTYPE_OFFSCREENCACHE : caps.Capability(caps.CapsType.CAPSTYPE_OFFSCREENCACHE, caps.OffscreenBitmapCacheCapability()), + caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()), + caps.CapsType.CAPSTYPE_SOUND : caps.Capability(caps.CapsType.CAPSTYPE_SOUND, caps.SoundCapability()) + } + #share id between client and server + self._shareId = 0x103EA + + def sendPDU(self, pduMessage): + """ + Send a PDU data to transport layer + """ + self._transport.send(data.PDU(self._transport.getUserId(), pduMessage)) + + def sendDataPDU(self, pduData): + """ + Send an PDUData to transport layer + """ + self.sendPDU(data.DataPDU(pduData, self._shareId)) + +class Client(PDULayer, tpkt.FastPathListener): + """ + Client automata of PDU layer + """ + def __init__(self, listener): + """ + @param listener: PDUClientListener + """ + PDULayer.__init__(self) + self._listener = listener + + def connect(self): + """ + Connect message in client automata + Send INfo packet (credentials) + Wait License info + """ + self.sendInfoPkt() + #next state is license info PDU + self.setNextState(self.recvLicenceInfo) + + def close(self): + """ + Send PDU close packet and call close method on transport method + """ + self.sendDataPDU(data.ShutdownRequestPDU()) + + def recvLicenceInfo(self, s): + """ + Read license info packet and check if is a valid client info + Wait Demand Active PDU + @param s: Stream + """ + #packet preambule + securityFlag = UInt16Le() + securityFlagHi = UInt16Le() + s.readType((securityFlag, securityFlagHi)) + + if not (securityFlag.value & data.SecurityFlag.SEC_LICENSE_PKT): + raise InvalidExpectedDataException("Waiting license packet") + + validClientPdu = lic.LicPacket() + s.readType(validClientPdu) + + if validClientPdu.bMsgtype.value == lic.MessageType.ERROR_ALERT and validClientPdu.licensingMessage.dwErrorCode.value == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.licensingMessage.dwStateTransition.value == lic.StateTransition.ST_NO_TRANSITION: + self.setNextState(self.recvDemandActivePDU) + #not tested because i can't buy RDP license server + elif validClientPdu.bMsgtype.value == lic.MessageType.LICENSE_REQUEST: + newLicenseReq = lic.createNewLicenseRequest(validClientPdu.licensingMessage) + self._transport.send((UInt16Le(data.SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), newLicenseReq)) + else: + raise InvalidExpectedDataException("Not a valid license packet") + + def recvDemandActivePDU(self, s): + """ + Receive demand active PDU which contains + Server capabilities. In this version of RDPY only + Restricted group of capabilities are used. + Send Confirm Active PDU + Send Finalize PDU + Wait Server Synchronize PDU + @param s: Stream + """ + pdu = data.PDU() + s.readType(pdu) + + if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DEMANDACTIVEPDU: + raise InvalidExpectedDataException("Expected Demand Active PDU from server") + + self._shareId = pdu.pduMessage.shareId.value + + for cap in pdu.pduMessage.capabilitySets._array: + self._serverCapabilities[cap.capabilitySetType] = cap + + self.sendConfirmActivePDU() + #send synchronize + self.sendClientFinalizeSynchronizePDU() + self.setNextState(self.recvServerSynchronizePDU) + + def recvServerSynchronizePDU(self, s): + """ + Receive from server + Wait Control Cooperate PDU + @param s: Stream from transport layer + """ + pdu = data.PDU() + s.readType(pdu) + if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_SYNCHRONIZE: + raise InvalidExpectedDataException("Error in PDU layer automata : expected synchronizePDU") + self.setNextState(self.recvServerControlCooperatePDU) + + def recvServerControlCooperatePDU(self, s): + """ + Receive control cooperate PDU from server + Wait Control Granted PDU + @param s: Stream from transport layer + """ + pdu = data.PDU() + s.readType(pdu) + if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_COOPERATE: + raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU") + self.setNextState(self.recvServerControlGrantedPDU) + + def recvServerControlGrantedPDU(self, s): + """ + Receive last control PDU the granted control PDU + Wait Font map PDU + @param s: Stream from transport layer + """ + pdu = data.PDU() + s.readType(pdu) + if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_GRANTED_CONTROL: + raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU") + self.setNextState(self.recvServerFontMapPDU) + + def recvServerFontMapPDU(self, s): + """ + Last useless connection packet from server to client + Wait any PDU + @param s: Stream from transport layer + """ + pdu = data.PDU() + s.readType(pdu) + if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_FONTMAP: + raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU") + + self.setNextState(self.recvPDU) + #here i'm connected + self._listener.onReady() + + def recvPDU(self, s): + """ + Main receive function after connection sequence + @param s: Stream from transport layer + """ + pdu = data.PDU() + s.readType(pdu) + if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU: + self.readDataPDU(pdu.pduMessage) + elif pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DEACTIVATEALLPDU: + #use in deactivation-reactivation sequence + #next state is either a capabilities re exchange or disconnection + #http://msdn.microsoft.com/en-us/library/cc240454.aspx + self.setNextState(self.recvDemandActivePDU) + + def recvFastPath(self, fastPathS): + """ + Implement FastPathListener interface + Fast path is needed by RDP 8.0 + @param fastPathS: Stream that contain fast path data + """ + fastPathPDU = data.FastPathUpdatePDU() + fastPathS.readType(fastPathPDU) + if fastPathPDU.updateHeader.value == data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP: + self._listener.onUpdate(fastPathPDU.updateData[1].rectangles._array) + + def readDataPDU(self, dataPDU): + """ + read a data PDU object + @param dataPDU: DataPDU object + """ + if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU: + errorMessage = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value) + if data.ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo): + errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo] + + log.error("INFO PDU : %s"%errorMessage) + elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED: + #may be an event to ask to user + self._transport.close() + elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_UPDATE: + self.readUpdateDataPDU(dataPDU.pduData) + + def readUpdateDataPDU(self, updateDataPDU): + """ + Read an update data PDU data + dispatch update data + @param: UpdateDataPDU object + """ + if updateDataPDU.updateType.value == data.UpdateType.UPDATETYPE_BITMAP: + self._listener.onUpdate(updateDataPDU.updateData.rectangles._array) + + def sendInfoPkt(self): + """ + Send a logon info packet + client automata data + """ + self._transport.send((UInt16Le(data.SecurityFlag.SEC_INFO_PKT), UInt16Le(), self._info)) + + def sendConfirmActivePDU(self): + """ + Send all client capabilities + """ + #init general capability + generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability + generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS + generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT + generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED + + #init bitmap capability + bitmapCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability + bitmapCapability.preferredBitsPerPixel = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).highColorDepth + bitmapCapability.desktopWidth = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).desktopWidth + bitmapCapability.desktopHeight = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).desktopHeight + + #init order capability + orderCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].capability + orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT + + #init input capability + inputCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_INPUT].capability + inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE + inputCapability.keyboardLayout = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).kbdLayout + inputCapability.keyboardType = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardType + inputCapability.keyboardSubType = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardSubType + inputCapability.keyboardrFunctionKey = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardFnKeys + inputCapability.imeFileName = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).imeFileName + + #make active PDU packet + confirmActivePDU = data.ConfirmActivePDU() + confirmActivePDU.shareId.value = self._shareId + confirmActivePDU.capabilitySets._array = self._clientCapabilities.values() + self.sendPDU(confirmActivePDU) + + def sendClientFinalizeSynchronizePDU(self): + """ + send a synchronize PDU from client to server + """ + synchronizePDU = data.SynchronizeDataPDU(self._transport.getChannelId()) + self.sendDataPDU(synchronizePDU) + + #ask for cooperation + controlCooperatePDU = data.ControlDataPDU(data.Action.CTRLACTION_COOPERATE) + self.sendDataPDU(controlCooperatePDU) + + #request control + controlRequestPDU = data.ControlDataPDU(data.Action.CTRLACTION_REQUEST_CONTROL) + self.sendDataPDU(controlRequestPDU) + + #TODO persistent key list http://msdn.microsoft.com/en-us/library/cc240494.aspx + + #deprecated font list pdu + fontListPDU = data.FontListDataPDU() + self.sendDataPDU(fontListPDU) + + def sendInputEvents(self, pointerEvents): + """ + send client input events + @param pointerEvents: list of pointer events + """ + pdu = data.ClientInputEventPDU() + pdu.slowPathInputEvents._array = [data.SlowPathInputEvent(x) for x in pointerEvents] + self.sendDataPDU(pdu) + +class Server(PDULayer): + """ + Server Automata of PDU layer + """ + def __init__(self, listener): + """ + @param listener: PDUServerListener + """ + PDULayer.__init__(self) + self._listener = listener + + def connect(self): + """ + Connect message for server automata + Wait Info Packet + """ + self.setNextState(self.recvInfoPkt) + + def recvInfoPkt(self, s): + """ + Receive info packet from client + Client credentials + Send License valid error message + Send Demand Active PDU + Wait Confirm Active PDU + @param s: Stream + """ + securityFlag = UInt16Le() + securityFlagHi = UInt16Le() + s.readType((securityFlag, securityFlagHi)) + + if not (securityFlag.value & data.SecurityFlag.SEC_INFO_PKT): + raise InvalidExpectedDataException("Waiting info packet") + + s.readType(self._info) + #next state send error license + self.sendLicensingErrorMessage() + self.sendDemandActivePDU() + self.setNextState(self.recvConfirmActivePDU) + + def recvConfirmActivePDU(self, s): + """ + Receive confirm active PDU from client + Capabilities exchange + Wait Client Synchronize PDU + @param s: Stream + """ + pdu = data.PDU() + s.readType(pdu) + + if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_CONFIRMACTIVEPDU: + raise InvalidExpectedDataException("Expected Confirm Active PDU from client") + + for cap in pdu.pduMessage.capabilitySets._array: + self._clientCapabilities[cap.capabilitySetType] = cap + + self.setNextState(self.recvClientSynchronizePDU) + + def recvClientSynchronizePDU(self, s): + """ + Receive from client + Wait Control Cooperate PDU + @param s: Stream from transport layer + """ + pdu = data.PDU() + s.readType(pdu) + if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_SYNCHRONIZE: + raise InvalidExpectedDataException("Error in PDU layer automata : expected synchronizePDU") + self.setNextState(self.recvClientControlCooperatePDU) + + def recvClientControlCooperatePDU(self, s): + """ + Receive control cooperate PDU from client + Wait Control Request PDU + @param s: Stream from transport layer + """ + pdu = data.PDU() + s.readType(pdu) + if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_COOPERATE: + raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU") + self.setNextState(self.recvClientControlRequestPDU) + + def recvClientControlRequestPDU(self, s): + """ + Receive last control PDU the request control PDU from client + Wait Font List PDU + @param s: Stream from transport layer + """ + pdu = data.PDU() + s.readType(pdu) + if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_REQUEST_CONTROL: + raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU") + self.setNextState(self.recvClientFontListPDU) + + def recvClientFontListPDU(self, s): + """ + Last synchronize packet from client to server + Send Server Finalize PDUs + Wait any PDU + @param s: Stream from transport layer + """ + pdu = data.PDU() + s.readType(pdu) + if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_FONTLIST: + raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU") + + #finalize server + self.sendServerFinalizeSynchronizePDU() + self.setNextState(self.recvPDU) + #now i'm ready + self._listener.onReady() + + def recvPDU(self, s): + """ + Main receive function after connection sequence + @param s: Stream from transport layer + """ + pdu = data.PDU() + s.readType(pdu) + if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU: + self.readDataPDU(pdu.pduMessage) + + def readDataPDU(self, dataPDU): + """ + read a data PDU object + @param dataPDU: DataPDU object + """ + if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU: + errorMessage = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value) + if data.ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo): + errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo] + + log.error("INFO PDU : %s"%errorMessage) + + def sendLicensingErrorMessage(self): + """ + Send a licensing error data + """ + self._transport.send((UInt16Le(data.SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), lic.createValidClientLicensingErrorMessage())) + + def sendDemandActivePDU(self): + """ + Send server capabilities + server automata PDU + """ + #init general capability + generalCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability + generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS + generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT + generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED + + #init bitmap capability + bitmapCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability + bitmapCapability.preferredBitsPerPixel.value = 16 + bitmapCapability.desktopWidth.value = 800 + bitmapCapability.desktopHeight.value = 600 + + demandActivePDU = data.DemandActivePDU() + demandActivePDU.shareId.value = self._shareId + demandActivePDU.capabilitySets._array = self._serverCapabilities.values() + self.sendPDU(demandActivePDU) + + def sendServerFinalizeSynchronizePDU(self): + """ + Send last synchronize packet from server to client + """ + synchronizePDU = data.SynchronizeDataPDU(self._transport.getChannelId()) + self.sendDataPDU(synchronizePDU) + + #ask for cooperation + controlCooperatePDU = data.ControlDataPDU(data.Action.CTRLACTION_COOPERATE) + self.sendDataPDU(controlCooperatePDU) + + #request control + controlRequestPDU = data.ControlDataPDU(data.Action.CTRLACTION_GRANTED_CONTROL) + self.sendDataPDU(controlRequestPDU) + + #TODO persistent key list http://msdn.microsoft.com/en-us/library/cc240494.aspx + + #deprecated font list pdu + fontMapPDU = data.FontMapDataPDU() + self.sendDataPDU(fontMapPDU) \ No newline at end of file diff --git a/rdpy/protocol/rdp/lic.py b/rdpy/protocol/rdp/pdu/lic.py similarity index 100% rename from rdpy/protocol/rdp/lic.py rename to rdpy/protocol/rdp/pdu/lic.py diff --git a/rdpy/protocol/rdp/rdp.py b/rdpy/protocol/rdp/rdp.py index 7f3057b..af05e7c 100644 --- a/rdpy/protocol/rdp/rdp.py +++ b/rdpy/protocol/rdp/rdp.py @@ -23,29 +23,26 @@ Use to manage RDP stack in twisted from twisted.internet import protocol from rdpy.base.error import CallPureVirtualFuntion, InvalidValue -from rdpy.network.layer import LayerMode +import pdu.layer +import pdu.data import rdpy.base.log as log -import tpkt, tpdu, mcs, pdu, gcc +import tpkt, tpdu, mcs, gcc -class RDPClientController(pdu.PDUClientListener): +class RDPClientController(pdu.layer.PDUClientListener): """ Manage RDP stack as client """ def __init__(self): - """ - @param observer: observer - """ #list of observer self._clientObserver = [] #PDU layer - self._pduLayer = pdu.PDULayer(self) + self._pduLayer = pdu.layer.Client(self) #multi channel service - self._mcsLayer = mcs.MCS(LayerMode.CLIENT, self._pduLayer) + self._mcsLayer = mcs.Client(self._pduLayer) #transport pdu layer - self._tpduLayer = tpdu.createClient(self._mcsLayer) + self._tpduLayer = tpdu.Client(self._mcsLayer) #transport packet (protocol layer) self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer) - #is pdu layer is ready to send self._isReady = False @@ -60,7 +57,7 @@ class RDPClientController(pdu.PDUClientListener): """ Set particular flag in RDP stack to avoid wall-paper, theme, menu animation etc... """ - self._pduLayer._info.extendedInfo.performanceFlags.value = pdu.PerfFlag.PERF_DISABLE_WALLPAPER | pdu.PerfFlag.PERF_DISABLE_MENUANIMATIONS | pdu.PerfFlag.PERF_DISABLE_CURSOR_SHADOW | pdu.PerfFlag.PERF_DISABLE_THEMING + self._pduLayer._info.extendedInfo.performanceFlags.value = pdu.data.PerfFlag.PERF_DISABLE_WALLPAPER | pdu.data.PerfFlag.PERF_DISABLE_MENUANIMATIONS | pdu.data.PerfFlag.PERF_DISABLE_CURSOR_SHADOW | pdu.data.PerfFlag.PERF_DISABLE_THEMING | pdu.data.PerfFlag.PERF_DISABLE_FULLWINDOWDRAG def setScreen(self, width, height): """ @@ -100,7 +97,6 @@ class RDPClientController(pdu.PDUClientListener): @param observer: new observer to add """ self._clientObserver.append(observer) - observer._clientListener = self def onUpdate(self, rectangles): """ @@ -110,7 +106,7 @@ class RDPClientController(pdu.PDUClientListener): for observer in self._clientObserver: #for each rectangle in update PDU for rectangle in rectangles: - observer.onUpdate(rectangle.destLeft.value, rectangle.destTop.value, rectangle.destRight.value, rectangle.destBottom.value, rectangle.width.value, rectangle.height.value, rectangle.bitsPerPixel.value, rectangle.flags.value & pdu.BitmapFlag.BITMAP_COMPRESSION, rectangle.bitmapDataStream.value) + observer.onUpdate(rectangle.destLeft.value, rectangle.destTop.value, rectangle.destRight.value, rectangle.destBottom.value, rectangle.width.value, rectangle.height.value, rectangle.bitsPerPixel.value, rectangle.flags.value & pdu.data.BitmapFlag.BITMAP_COMPRESSION, rectangle.bitmapDataStream.value) def onReady(self): """ @@ -133,18 +129,18 @@ class RDPClientController(pdu.PDUClientListener): return try: - event = pdu.PointerEvent() + event = pdu.data.PointerEvent() if isPressed: - event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_DOWN + event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN if button == 1: - event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON1 + event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON1 elif button == 2: - event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON2 + event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON2 elif button == 3: - event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON3 + event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON3 else: - event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_MOVE + event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE #position event.xPos.value = x @@ -166,12 +162,12 @@ class RDPClientController(pdu.PDUClientListener): return try: - event = pdu.ScancodeKeyEvent() + event = pdu.data.ScancodeKeyEvent() event.keyCode.value = code if isPressed: - event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_DOWN + event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_DOWN else: - event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_RELEASE + event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE #send event self._pduLayer.sendInputEvents([event]) @@ -189,10 +185,10 @@ class RDPClientController(pdu.PDUClientListener): return try: - event = pdu.UnicodeKeyEvent() + event = pdu.data.UnicodeKeyEvent() event.unicode.value = code if not isPressed: - event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_RELEASE + event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE #send event self._pduLayer.sendInputEvents([event]) @@ -206,7 +202,7 @@ class RDPClientController(pdu.PDUClientListener): """ self._pduLayer.close() -class RDPServerController(pdu.PDUServerListener): +class RDPServerController(pdu.layer.PDUServerListener): """ Controller use in server side mode """ @@ -215,20 +211,84 @@ class RDPServerController(pdu.PDUServerListener): @param privateKeyFileName: file contain server private key @param certficiateFileName: file that contain public key """ - self._pduLayer = pdu.PDULayer(self) + #list of observer + self._serverObserver = [] + #build RDP protocol stack + self._pduLayer = pdu.layer.Server(self) #multi channel service - self._mcsLayer = mcs.MCS(LayerMode.SERVER, self._pduLayer) + self._mcsLayer = mcs.Server(self._pduLayer) #transport pdu layer - self._tpduLayer = tpdu.createServer(self._mcsLayer, privateKeyFileName, certificateFileName) + self._tpduLayer = tpdu.Server(self._mcsLayer, privateKeyFileName, certificateFileName) #transport packet (protocol layer) self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer) + self._isReady = False + def getProtocol(self): """ @return: the twisted protocol layer in RDP case is TPKT layer """ - return self._tpktLayer; + return self._tpktLayer + + def getUsername(self): + """ + Must be call after on ready event else always empty string + @return: username send by client may be an empty string + """ + return self._pduLayer._info.userName.value + + def getPassword(self): + """ + Must be call after on ready event else always empty string + @return: password send by client may be an empty string + """ + return self._pduLayer._info.password.value + + def getDomain(self): + """ + Must be call after on ready event else always empty string + @return: domain send by client may be an empty string + """ + return self._pduLayer._info.domain.value + + def getCredentials(self): + """ + Must be call after on ready event else always empty string + @return: tuple(domain, username, password) + """ + return (self.getDomain(), self.getUsername(), self.getPassword()) + + def addServerObserver(self, observer): + """ + Add observer to RDP protocol + @param observer: new observer to add + """ + self._serverObserver.append(observer) + + def onReady(self): + """ + RDP stack is now ready + """ + self._isReady = True + for observer in self._serverObserver: + observer.onReady() + + def sendUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): + """ + send bitmap update + @param destLeft: xmin position + @param destTop: ymin position + @param destRight: xmax position because RDP can send bitmap with padding + @param destBottom: ymax position because RDP can send bitmap with padding + @param width: width of bitmap + @param height: height of bitmap + @param bitsPerPixel: number of bit per pixel + @param isCompress: use RLE compression + @param data: bitmap data + """ + if not self._isReady: + return class ClientFactory(protocol.Factory): """ @@ -268,9 +328,8 @@ class ServerFactory(protocol.Factory): @param addr: destination address """ controller = RDPServerController(self._privateKeyFileName, self._certificateFileName) + self.buildObserver(controller) return controller.getProtocol() - #pduLayer = pdu.PDU(pdu.PDUServerListener()) - #return tpkt.TPKT(tpdu.createServer(mcs.createServer(pduLayer), self._privateKeyFileName, self._certificateFileName)); def buildObserver(self, controller): """ @@ -309,4 +368,21 @@ class RDPClientObserver(object): @param isCompress: use RLE compression @param data: bitmap data """ - raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "RDPClientObserver")) \ No newline at end of file + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "RDPClientObserver")) + +class RDPServerObserver(object): + """ + Class use to inform all RDP event handle by RDPY + """ + def __init__(self, controller): + """ + @param controller: RDP controller use to interact with protocol + """ + self._controller = controller + self._controller.addServerObserver(self) + + def onReady(self): + """ + Stack is ready and connected + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPServerObserver")) \ No newline at end of file diff --git a/rdpy/protocol/rdp/tpdu.py b/rdpy/protocol/rdp/tpdu.py index 7ffd9eb..b5bde7b 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, StreamSender +from rdpy.network.layer import LayerAutomata, StreamSender from rdpy.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof from rdpy.base.error import InvalidExpectedDataException @@ -107,46 +107,69 @@ class Negotiation(CompositeType): self.selectedProtocol = UInt32Le(conditional = lambda: (self.code.value != NegociationType.TYPE_RDP_NEG_FAILURE)) self.failureCode = UInt32Le(conditional = lambda: (self.code.value == NegociationType.TYPE_RDP_NEG_FAILURE)) -class TPDU(LayerAutomata, StreamSender): +class TPDULayer(LayerAutomata, StreamSender): """ TPDU layer management there is an connection automata """ - def __init__(self, mode, presentation): + def __init__(self, presentation): """ - @param mode: automata mode (client or server) @param presentation: upper layer, MCS layer in RDP case """ - LayerAutomata.__init__(self, mode, presentation) + LayerAutomata.__init__(self, presentation) #default selectedProtocol is SSl because is the only supported #in this version of RDPY #client requested selectedProtocol self._requestedProtocol = Protocols.PROTOCOL_SSL #server selected selectedProtocol self._selectedProtocol = Protocols.PROTOCOL_SSL + + def recvData(self, data): + """ + Read data header from packet + And pass to presentation layer + @param data: Stream + """ + header = TPDUDataHeader() + data.readType(header) + self._presentation.recv(data) - #Server mode informations for TLS connection - self._serverPrivateKeyFileName = None - self._serverCertificateFileName = None - - def initTLSServerInfos(self, privateKeyFileName, certificateFileName): + def send(self, message): """ - Initialize informations for SSL server connection - @param privateKeyFileName: file contain server private key - @param certficiateFileName: file that contain public key + Write message packet for TPDU layer + Add TPDU header + @param message: network.Type message """ - self._serverPrivateKeyFileName = privateKeyFileName - self._serverCertificateFileName = certificateFileName - + self._transport.send((TPDUDataHeader(), message)) + +class Client(TPDULayer): + """ + Client automata of TPDU layer + """ + def __init__(self, presentation): + """ + @param presentation: upper layer, MCS layer in RDP case + """ + TPDULayer.__init__(self, presentation) + def connect(self): """ Connection request for client send a connection request packet """ - if self._mode == LayerMode.CLIENT: - self.sendConnectionRequest() - else: - self.setNextState(self.recvConnectionRequest) - + self.sendConnectionRequest() + + def sendConnectionRequest(self): + """ + Write connection request message + Next state is recvConnectionConfirm + @see: http://msdn.microsoft.com/en-us/library/cc240500.aspx + """ + message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_REQUEST) + message.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_REQ + message.protocolNeg.selectedProtocol.value = self._requestedProtocol + self._transport.send(message) + self.setNextState(self.recvConnectionConfirm) + def recvConnectionConfirm(self, data): """ Receive connection confirm message @@ -179,6 +202,27 @@ class TPDU(LayerAutomata, StreamSender): #connection is done send to presentation self._presentation.connect() + +class Server(TPDULayer): + """ + Server automata of TPDU layer + """ + def __init__(self, presentation, privateKeyFileName, certificateFileName): + """ + @param presentation: upper layer, MCS layer in RDP case + @param privateKeyFileName: file contain server private key + @param certficiateFileName: file that contain public key + """ + TPDULayer.__init__(self, presentation) + #Server mode informations for TLS connection + self._serverPrivateKeyFileName = privateKeyFileName + self._serverCertificateFileName = certificateFileName + + def connect(self): + """ + Connection request for server wait connection request packet from client + """ + self.setNextState(self.recvConnectionRequest) def recvConnectionRequest(self, data): """ @@ -206,28 +250,6 @@ class TPDU(LayerAutomata, StreamSender): self._selectedProtocol = Protocols.PROTOCOL_SSL self.sendConnectionConfirm() - - def recvData(self, data): - """ - Read data header from packet - And pass to presentation layer - @param data: Stream - """ - header = TPDUDataHeader() - data.readType(header) - self._presentation.recv(data) - - def sendConnectionRequest(self): - """ - Write connection request message - Next state is recvConnectionConfirm - @see: http://msdn.microsoft.com/en-us/library/cc240500.aspx - """ - message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_REQUEST) - message.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_REQ - message.protocolNeg.selectedProtocol.value = self._requestedProtocol - self._transport.send(message) - self.setNextState(self.recvConnectionConfirm) def sendConnectionConfirm(self): """ @@ -245,32 +267,6 @@ class TPDU(LayerAutomata, StreamSender): #connection is done send to presentation self.setNextState(self.recvData) self._presentation.connect() - - def send(self, message): - """ - Write message packet for TPDU layer - Add TPDU header - @param message: network.Type message - """ - self._transport.send((TPDUDataHeader(), message)) - -def createClient(mcsLayer): - """ - Factory for client TPDU automata - @param mcsLayer: presentation layer of TPDU - """ - return TPDU(LayerMode.CLIENT, mcsLayer) - -def createServer(mcsLayer, privateKeyFileName, certificateFileName): - """ - Factory for server TPDU automata - @param mcsLayer: presentation layer of TPDU - @param privateKeyFileName: file contain server private key - @param certficiateFileName: file that contain public key - """ - tpduLayer = TPDU(LayerMode.SERVER, mcsLayer) - tpduLayer.initTLSServerInfos(privateKeyFileName, certificateFileName) - return tpduLayer #open ssl needed from twisted.internet import ssl diff --git a/rdpy/protocol/rdp/tpkt.py b/rdpy/protocol/rdp/tpkt.py index 12ca336..2e0fcb5 100644 --- a/rdpy/protocol/rdp/tpkt.py +++ b/rdpy/protocol/rdp/tpkt.py @@ -22,7 +22,7 @@ Transport packet layer implementation Use to build correct size packet and handle slow path and fast path mode """ -from rdpy.network.layer import RawLayer, LayerMode +from rdpy.network.layer import RawLayer from rdpy.network.type import UInt8, UInt16Be, sizeof from rdpy.base.error import CallPureVirtualFuntion @@ -31,10 +31,10 @@ class FastPathListener(object): Fast path packet listener Usually PDU layer """ - def recvFastPath(self, fastPathData): + def recvFastPath(self, fastPathS): """ Call when fast path packet is received - @param fastPathData: Stream + @param fastPathS: Stream """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "StreamListener")) @@ -50,7 +50,7 @@ class TPKT(RawLayer): """ @param presentation: presentation layer, in RDP case is TPDU layer """ - RawLayer.__init__(self, LayerMode.NONE, presentation) + RawLayer.__init__(self, presentation) #last packet version read from header self._lastPacketVersion = UInt8() #length may be coded on more than 1 bytes diff --git a/rdpy/protocol/rfb/rfb.py b/rdpy/protocol/rfb/rfb.py index faddffc..d809a2b 100644 --- a/rdpy/protocol/rfb/rfb.py +++ b/rdpy/protocol/rfb/rfb.py @@ -27,9 +27,9 @@ Implement Remote FrameBuffer protocol use in VNC client and server """ from twisted.internet import protocol -from rdpy.network.layer import RawLayer, LayerMode +from rdpy.network.layer import RawLayer from rdpy.network.type import UInt8, UInt16Be, UInt32Be, SInt32Be, String, CompositeType -from rdpy.base.error import InvalidValue, CallPureVirtualFuntion, InvalidType +from rdpy.base.error import InvalidValue, CallPureVirtualFuntion class ProtocolVersion(object): """ @@ -170,18 +170,11 @@ class RFB(RawLayer): """ def __init__(self, listener): """ - @param mode: LayerMode client or server @param listener: listener use to inform new orders """ - mode = None - if isinstance(listener, RFBClientListener): - mode = LayerMode.CLIENT - #set client listener - self._clientListener = listener - else: - raise InvalidType("RFB Layer expect RFBClientListener as listener") - - RawLayer.__init__(self, mode) + RawLayer.__init__(self) + #set client listener + self._clientListener = listener #useful for RFB protocol self._callbackBody = None #protocol version negotiated @@ -239,12 +232,8 @@ class RFB(RawLayer): """ Call when transport layer connection is made in Client mode -> wait protocol version - in Server mode -> send protocol version """ - if self._mode == LayerMode.CLIENT: - self.expect(12, self.recvProtocolVersion) - else: - self.send(self._version) + self.expect(12, self.recvProtocolVersion) def readProtocolVersion(self, data): """ @@ -550,7 +539,7 @@ class ClientFactory(protocol.Factory): Function call by twisted on connection @param addr: address where client try to connect """ - controller = RFBController(LayerMode.CLIENT) + controller = RFBController() self.buildObserver(controller) return controller.getRFBLayer()