From c09e466f48d20d6e27c1911fd824e75ead2de539 Mon Sep 17 00:00:00 2001 From: speyrefitte Date: Fri, 11 Jul 2014 18:18:16 +0200 Subject: [PATCH] add fastpath to be compatible with rdp 8.0 --- bin/rdpy-rdpclient | 9 +- rdpy/network/layer.py | 2 +- rdpy/network/type.py | 73 ++++++------- rdpy/protocol/rdp/mcs.py | 48 ++++----- rdpy/protocol/rdp/pdu.py | 216 ++++++++++++++++++++++++++++---------- rdpy/protocol/rdp/rdp.py | 127 +++++++++++++++------- rdpy/protocol/rdp/tpdu.py | 17 --- rdpy/protocol/rdp/tpkt.py | 42 +++++--- rdpy/ui/qt4.py | 11 +- 9 files changed, 348 insertions(+), 197 deletions(-) diff --git a/bin/rdpy-rdpclient b/bin/rdpy-rdpclient index 19de389..b90a9cf 100755 --- a/bin/rdpy-rdpclient +++ b/bin/rdpy-rdpclient @@ -40,7 +40,8 @@ class RDPClientQtFactory(rdp.ClientFactory): """ init client with correct definition """ - rdp.ClientFactory.__init__(self, width, height) + self._width = width + self._height = height self._w = None def buildObserver(self, controller): @@ -56,8 +57,10 @@ class RDPClientQtFactory(rdp.ClientFactory): self._w.setWindowTitle('rdpy-rdpclient') self._w.show() - #enable perf - controller.enablePerformanceSession() + #resize session + controller.setScreen(self._width, self._height) + controller.setPerformanceSession() + return client def startedConnecting(self, connector): diff --git a/rdpy/network/layer.py b/rdpy/network/layer.py index dc011dd..bd9f5a7 100644 --- a/rdpy/network/layer.py +++ b/rdpy/network/layer.py @@ -116,7 +116,7 @@ class LayerAutomata(Layer, StreamListener): self.recv = callback -#twitsed layer concept +#twisted layer concept from twisted.internet import protocol #first that handle stream from type import Stream diff --git a/rdpy/network/type.py b/rdpy/network/type.py index 4949d5b..b9545d2 100644 --- a/rdpy/network/type.py +++ b/rdpy/network/type.py @@ -372,66 +372,68 @@ class SimpleType(Type, CallableValue): return self.__class__(self.value.__rshift__(other.value)) def __hash__(self): - ''' - hash function to treat simple type in hash collection + """ + Hash function to treat simple type in hash collection @return: hash of inner value - ''' + """ return hash(self.value) def __nonzero__(self): - ''' - boolean conversion + """ + Boolean conversion @return: bool of inner value - ''' + """ return bool(self.value) class CompositeType(Type): - ''' - keep ordering declaration of simple type - in list and transparent for other type - @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 readLen: max length to read - ''' + """ + Type node of other sub type + """ def __init__(self, conditional = lambda:True, optional = False, constant = False, readLen = None): - ''' - init list of simple value - ''' + """ + Keep ordering declaration of simple type + in list and transparent for other type + @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 changing of object during reading + @param readLen: max length to read + """ Type.__init__(self, conditional = conditional, optional = optional, constant = constant) #list of ordoned type self._typeName = [] self._readLen = readLen def __setattr__(self, name, value): - ''' - magic function to update type list + """ + Magic function to update type list @param name: name of new attribute @param value: value of new attribute - ''' + """ if name[0] != '_' and (isinstance(value, Type) or isinstance(value, tuple)) and not name in self._typeName: self._typeName.append(name) self.__dict__[name] = value def __read__(self, s): - ''' - call read on each ordered subtype + """ + Call read on each ordered sub-type @param s: Stream - ''' + """ readLen = 0 - for name in self._typeName: - if not self._readLen is None and readLen + sizeof(self.__dict__[name]) > self._readLen.value: - #optional maybe be unread - if self.__dict__[name]._optional: - continue - else: - raise InvalidSize("Impossible to read type %s::%s : read size is too small"%(self.__class__, name)) + for name in self._typeName: try: s.readType(self.__dict__[name]) + + #read is ok but read out of bound + if not self._readLen is None and readLen > self._readLen.value: + #roll back + s.pos -= sizeof(self.__dict__[name]) + #and notify + raise InvalidSize("Impossible to read type %s : read length is too small"%(self.__class__)) + except Exception as e: print "Error during read %s::%s"%(self.__class__, name) - #rollback already readed + #roll back already read for tmpName in self._typeName: if tmpName == name: break @@ -863,17 +865,16 @@ class ArrayType(Type): return sizeof(self._array) class FactoryType(Type): - ''' + """ Call factory function on read or write - ''' + """ def __init__(self, factory, conditional = lambda:True, optional = False, constant = False): - ''' - ctor of factory type + """ @param factory: factory @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 changes of object during reading - ''' + """ Type.__init__(self, conditional, optional, constant) self._factory = factory if not callable(factory): diff --git a/rdpy/protocol/rdp/mcs.py b/rdpy/protocol/rdp/mcs.py index 8064e14..83e57b6 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, LayerMode, StreamSender +from rdpy.network.layer import LayerAutomata, StreamSender, Layer from rdpy.network.type import sizeof, Stream, UInt8, UInt16Be from rdpy.network.error import InvalidExpectedDataException, InvalidValue, InvalidSize from rdpy.protocol.rdp.ber import writeLength @@ -64,7 +64,7 @@ class MCS(LayerAutomata): the main layer of RDP protocol is why he can do everything and more! """ - class MCSProxySender(StreamSender): + class MCSProxySender(Layer, StreamSender): """ Proxy use to set as transport layer for upper channel use to abstract channel id for presentation layer @@ -78,34 +78,40 @@ class MCS(LayerAutomata): self._channelId = channelId def send(self, data): - ''' - a send proxy function, use channel id and specific - send function of mcs layer - ''' + """ + A send proxy function, use channel id and specific + send function of MCS layer + """ self._mcs.send(self._channelId, data) + def close(self): + """ + Close wrapped layer + """ + self._mcs.close() + def getUserId(self): - ''' + """ @return: mcs user id - ''' + """ return self._mcs._userId def getChannelId(self): - ''' + """ @return: return channel id of proxy - ''' + """ return self._channelId def getGCCClientSettings(self): - ''' + """ @return: mcs layer gcc client settings - ''' + """ return self._mcs._clientSettings def getGCCServerSettings(self): - ''' + """ @return: mcs layer gcc server settings - ''' + """ return self._mcs._serverSettings @@ -353,18 +359,4 @@ class MCS(LayerAutomata): max_pdu_size = ber.readInteger(s) ber.readInteger(s) return (max_channels, max_users, max_tokens, max_pdu_size) - -def createClient(controller): - """ - @param controller: RDP controller which initialized all channel layer - @return: MCS layer in client mode - """ - return MCS(LayerMode.CLIENT, controller.getPDULayer()) - -def createServer(controller): - """ - @param controller: RDP controller which initialized all channel layer - @return: MCS layer in server mode - """ - return MCS(LayerMode.SERVER, controller.getPDULayer()) \ No newline at end of file diff --git a/rdpy/protocol/rdp/pdu.py b/rdpy/protocol/rdp/pdu.py index bc01325..8d41e0d 100644 --- a/rdpy/protocol/rdp/pdu.py +++ b/rdpy/protocol/rdp/pdu.py @@ -27,7 +27,7 @@ from rdpy.network.layer import LayerAutomata, LayerMode from rdpy.network.type import CompositeType, UniString, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType from rdpy.network.error import InvalidExpectedDataException, ErrorReportedFromPeer, CallPureVirtualFuntion, InvalidType -import gcc, lic, caps +import gcc, lic, caps, tpkt class SecurityFlag(object): """ @@ -223,6 +223,30 @@ class KeyboardFlag(object): KBDFLAGS_DOWN = 0x4000 KBDFLAGS_RELEASE = 0x8000 +class FastPathUpdateType(object): + """ + Use in Fast Path update packet + @see: http://msdn.microsoft.com/en-us/library/cc240622.aspx + """ + FASTPATH_UPDATETYPE_ORDERS = 0x0 + FASTPATH_UPDATETYPE_BITMAP = 0x1 + FASTPATH_UPDATETYPE_PALETTE = 0x2 + FASTPATH_UPDATETYPE_SYNCHRONIZE = 0x3 + FASTPATH_UPDATETYPE_SURFCMDS = 0x4 + FASTPATH_UPDATETYPE_PTR_NULL = 0x5 + FASTPATH_UPDATETYPE_PTR_DEFAULT = 0x6 + FASTPATH_UPDATETYPE_PTR_POSITION = 0x8 + FASTPATH_UPDATETYPE_COLOR = 0x9 + FASTPATH_UPDATETYPE_CACHED = 0xA + FASTPATH_UPDATETYPE_POINTER = 0xB + +class FastPathOutputCompression(object): + """ + Flag for compression + @see: http://msdn.microsoft.com/en-us/library/cc240622.aspx + """ + FASTPATH_OUTPUT_COMPRESSION_USED = 0x2 + class ErrorInfo(object): """ Error code use in Error info PDU @@ -586,18 +610,26 @@ class DataPDU(CompositeType): self.shareDataHeader = ShareDataHeader(lambda:sizeof(self), pduType, userId, shareId) def PDUDataFactory(): + """ + Create object in accordance self.shareDataHeader.pduType2 value + """ if self.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_UPDATE: - return UpdateDataPDU() + return UpdateDataPDU(readLen = self.shareDataHeader.uncompressedLength) + elif self.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_SYNCHRONIZE: - return SynchronizeDataPDU() + return SynchronizeDataPDU(readLen = self.shareDataHeader.uncompressedLength) + elif self.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_CONTROL: - return ControlDataPDU() + return ControlDataPDU(readLen = self.shareDataHeader.uncompressedLength) + elif self.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU: - return ErrorInfoDataPDU() + return ErrorInfoDataPDU(readLen = self.shareDataHeader.uncompressedLength) + elif self.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_FONTLIST: - return FontListDataPDU() + return FontListDataPDU(readLen = self.shareDataHeader.uncompressedLength) + elif self.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_FONTMAP: - return FontMapDataPDU() + return FontMapDataPDU(readLen = self.shareDataHeader.uncompressedLength) else: #read all value return String() @@ -611,17 +643,24 @@ class SynchronizeDataPDU(CompositeType): """ @see http://msdn.microsoft.com/en-us/library/cc240490.aspx """ - def __init__(self, targetUser = UInt16Le()): - CompositeType.__init__(self) + def __init__(self, targetUser = 0, readLen = None): + """ + @param targetUser: MCS Channel ID + """ + CompositeType.__init__(self, readLen = readLen) self.messageType = UInt16Le(1, constant = True) - self.targetUser = targetUser + self.targetUser = UInt16Le(targetUser) class ControlDataPDU(CompositeType): """ @see http://msdn.microsoft.com/en-us/library/cc240492.aspx """ - def __init__(self, action = None): - CompositeType.__init__(self) + def __init__(self, action = None, readLen = None): + """ + @param action: Action macro + @param readLen: Max length to read + """ + CompositeType.__init__(self, readLen = readLen) self.action = UInt16Le(action, constant = True) if not action is None else UInt16Le() self.grantId = UInt16Le() self.controlId = UInt32Le() @@ -631,10 +670,14 @@ class ErrorInfoDataPDU(CompositeType): Use to inform error in PDU layer @see: http://msdn.microsoft.com/en-us/library/cc240544.aspx """ - def __init__(self, errorInfo = UInt32Le()): - CompositeType.__init__(self) - #use to collect error info pdu - self.errorInfo = errorInfo + def __init__(self, errorInfo = 0, readLen = None): + """ + @param errorInfo: ErrorInfo macro + @param readLen: Max length to read + """ + CompositeType.__init__(self, readLen = readLen) + #use to collect error info PDU + self.errorInfo = UInt32Le(errorInfo) class FontListDataPDU(CompositeType): """ @@ -642,8 +685,11 @@ class FontListDataPDU(CompositeType): client -> server @see: http://msdn.microsoft.com/en-us/library/cc240498.aspx """ - def __init__(self): - CompositeType.__init__(self) + def __init__(self, readLen = None): + """ + @param readLen: Max read length + """ + CompositeType.__init__(self, readLen = readLen) self.numberFonts = UInt16Le() self.totalNumFonts = UInt16Le() self.listFlags = UInt16Le(0x0003) @@ -655,8 +701,11 @@ class FontMapDataPDU(CompositeType): server -> client @see: http://msdn.microsoft.com/en-us/library/cc240498.aspx """ - def __init__(self): - CompositeType.__init__(self) + def __init__(self, readLen = None): + """ + @param readLen: Max read length + """ + CompositeType.__init__(self, readLen = readLen) self.numberEntries = UInt16Le() self.totalNumEntries = UInt16Le() self.mapFlags = UInt16Le(0x0003) @@ -664,32 +713,84 @@ class FontMapDataPDU(CompositeType): class UpdateDataPDU(CompositeType): """ - Update data PDU use by server to inform update img or palette + Update data PDU use by server to inform update image or palet for example @see: http://msdn.microsoft.com/en-us/library/cc240608.aspx """ - def __init__(self, updateType = 0, updateData = None): - CompositeType.__init__(self) + def __init__(self, updateType = 0, 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) def UpdateDataFactory(): if self.updateType.value == UpdateType.UPDATETYPE_BITMAP: return BitmapUpdateDataPDU() + + elif self.updateType.value == UpdateType.UPDATETYPE_SYNCHRONIZE: + return SynchronizeUpdatePDU() + else: - String() + return String() if updateData is None: updateData = UpdateDataFactory self.updateData = FactoryType(updateData, conditional = lambda:(self.updateType.value != UpdateType.UPDATETYPE_SYNCHRONIZE)) + +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): + CompositeType.__init__(self) + self.updateHeader = UInt8(updateType) + self.compressionFlags = UInt8(conditional = lambda:(self.updateHeader.value & FastPathOutputCompression.FASTPATH_OUTPUT_COMPRESSION_USED)) + self.size = UInt16Le() + def UpdateDataFactory(): + """ + Create correct object in accordance to self.updateHeader field + """ + if (self.updateHeader.value & 0xf) == FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP: + return (UInt16Le(FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP, constant = True), BitmapUpdateDataPDU(readLen = self.size)) + + elif (self.updateHeader.value & 0xf) == FastPathUpdateType.FASTPATH_UPDATETYPE_SYNCHRONIZE: + return SynchronizeUpdatePDU(readLen = self.size) + + else: + return String() + + if updateData is None: + updateData = UpdateDataFactory + + self.updateData = FactoryType(updateData) + +class SynchronizeUpdatePDU(CompositeType): + """ + PDU is ignored, artefact of T.125 + """ + def __init__(self, readLen = None): + """ + @param readLen: Max size of packet + """ + CompositeType.__init__(self, readLen = readLen) + self.pad2Octets = UInt16Le() + class BitmapUpdateDataPDU(CompositeType): """ PDU use to send raw bitmap compressed or not @see: http://msdn.microsoft.com/en-us/library/dd306368.aspx """ - def __init__(self): - CompositeType.__init__(self) + def __init__(self, readLen = None): + """ + @param readLen: Max size of packet + """ + CompositeType.__init__(self, readLen = readLen) self.numberRectangles = UInt16Le(lambda:len(self.rectangles._array)) self.rectangles = ArrayType(BitmapData, readLen = self.numberRectangles) @@ -775,10 +876,13 @@ class SlowPathInputEvent(CompositeType): """ if isinstance(event, PointerEvent): return InputMessageType.INPUT_EVENT_MOUSE + elif isinstance(event, ScancodeKeyEvent): return InputMessageType.INPUT_EVENT_SCANCODE + elif isinstance(event, UnicodeKeyEvent): return InputMessageType.INPUT_EVENT_UNICODE + else: return None @@ -851,7 +955,7 @@ class PDUServerListener(object): """ pass -class PDU(LayerAutomata): +class PDU(LayerAutomata, tpkt.FastPathListener): """ Global channel for MCS that handle session identification user, licensing management, and capabilities exchange @@ -860,6 +964,18 @@ class PDU(LayerAutomata): """ @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().core.rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS)) #server capabilities @@ -898,29 +1014,7 @@ class PDU(LayerAutomata): #determine if layer is connected self._isConnected = False - - mode = None - if isinstance(listener, PDUClientListener): - mode = LayerMode.CLIENT - #set client listener - self._clientListener = listener - self.initClientOrder() - elif isinstance(listener, PDUServerListener): - mode = LayerMode.SERVER - else: - raise InvalidType("PDU Layer expect PDU(Client|Server)Listener as listener") - - LayerAutomata.__init__(self, mode, None) - - def initClientOrder(self): - """ - Enable order in accordance of override function of _clientListener - """ - #enable rectangle order - orderCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].capability._value - if id(PDUClientListener.recvDstBltOrder.im_func) != id(self._clientListener.recvDstBltOrder.im_func): - orderCapability.orderSupport._array[caps.Order.TS_NEG_DSTBLT_INDEX].value = 1 - + def connect(self): """ Connect event in client mode send logon info @@ -935,6 +1029,7 @@ class PDU(LayerAutomata): Send PDU close packet and call close method on transport method """ self._transport.send(ShareDataHeader(PDUType2.PDUTYPE2_SHUTDOWN_REQUEST, self._transport.getUserId(), self._shareId)) + self._transport.close() def sendInfoPkt(self): """ @@ -968,7 +1063,7 @@ class PDU(LayerAutomata): def readDataPDU(self, data): """ - Read a DataPdu struct. If is an error pdu log and close layer + Read a DataPdu struct. If is an error PDU log and close layer @param data: Stream from transport layer @return: """ @@ -1054,6 +1149,17 @@ class PDU(LayerAutomata): dataPDU = self.readDataPDU(data) if dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_UPDATE and dataPDU.pduData._value.updateType.value == UpdateType.UPDATETYPE_BITMAP: self._clientListener.recvBitmapUpdateDataPDU(dataPDU.pduData._value.updateData._value.rectangles._array) + + 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.recvBitmapUpdateDataPDU(fastPathPDU.updateData._value[1].rectangles._array) def sendConfirmActivePDU(self): """ @@ -1063,7 +1169,7 @@ class PDU(LayerAutomata): generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability._value 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 + 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._value @@ -1096,7 +1202,7 @@ class PDU(LayerAutomata): """ send a synchronize PDU from client to server """ - synchronizePDU = DataPDU(PDUType2.PDUTYPE2_SYNCHRONIZE, SynchronizeDataPDU(UInt16Le(self._transport.getChannelId())), self._transport.getUserId(), self._shareId) + synchronizePDU = DataPDU(PDUType2.PDUTYPE2_SYNCHRONIZE, SynchronizeDataPDU(self._transport.getChannelId()), self._transport.getUserId(), self._shareId) self._transport.send(synchronizePDU) #ask for cooperation @@ -1125,6 +1231,4 @@ class PDU(LayerAutomata): """ pdu = ClientInputEventPDU(self._transport.getUserId(), self._shareId) pdu.slowPathInputEvents._array = [SlowPathInputEvent(x) for x in pointerEvents] - self._transport.send(pdu) - - \ No newline at end of file + self._transport.send(pdu) \ No newline at end of file diff --git a/rdpy/protocol/rdp/rdp.py b/rdpy/protocol/rdp/rdp.py index 2ad3e05..a876b7a 100644 --- a/rdpy/protocol/rdp/rdp.py +++ b/rdpy/protocol/rdp/rdp.py @@ -1,13 +1,34 @@ -''' -@author: sylvain -''' +# +# Copyright (c) 2014 Sylvain Peyrefitte +# +# This file is part of rdpy. +# +# rdpy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +""" +Use to manage RDP stack in twisted +""" + from twisted.internet import protocol from rdpy.network.error import CallPureVirtualFuntion, InvalidValue +from rdpy.network.layer import LayerMode import tpkt, tpdu, mcs, pdu class RDPClientController(pdu.PDUClientListener): """ - use to decode and dispatch to observer PDU messages and orders + Manage RDP stack as client """ def __init__(self): """ @@ -17,19 +38,58 @@ class RDPClientController(pdu.PDUClientListener): self._clientObserver = [] #transport layer self._pduLayer = pdu.PDU(self) + #multi channel service + self._mcsLayer = mcs.MCS(LayerMode.CLIENT, self._pduLayer) + #transport pdu layer + self._tpduLayer = tpdu.TPDU(LayerMode.CLIENT, self._mcsLayer) + #transport packet (protocol layer) + self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer) - def getPDULayer(self): + def getProtocol(self): """ - @return: PDU layer use by controller + @return: return Protocol layer for twisted + In case of RDP TPKT is the Raw layer """ - return self._pduLayer + return self._tpktLayer - def enablePerformanceSession(self): + def setPerformanceSession(self): """ - Set particular flag in RDP stack to avoid wallpaper, theming, menu animation etc... + 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 + def setScreen(self, width, height): + """ + Set screen dim of session + @param width: width in pixel of screen + @param height: height in pixel of screen + """ + #set screen definition in MCS layer + self._mcsLayer._clientSettings.core.desktopHeight.value = height + self._mcsLayer._clientSettings.core.desktopWidth.value = width + + def setUsername(self, username): + """ + Set the username for session + @param username: username of session + """ + #username in PDU info packet + self._pduLayer._info.userName.value = username + + def setPassword(self, password): + """ + Set password for session + @param password: password of session + """ + self._pduLayer._info.password.value = password + + def setDomain(self, domain): + """ + Set the windows domain of session + @param domain: domain of session + """ + self._pduLayer._info.domain.value = domain + def addClientObserver(self, observer): """ add observer to RDP protocol @@ -132,28 +192,15 @@ class ClientFactory(protocol.Factory): """ Factory of Client RDP protocol """ - def __init__(self, width = 1024, height = 800): - """ - @param width: width of screen - @param height: height of screen - """ - self._width = width - self._height = height - def buildProtocol(self, addr): - ''' + """ Function call from twisted and build rdp protocol stack @param addr: destination address - ''' + """ controller = RDPClientController() self.buildObserver(controller) - mcsLayer = mcs.createClient(controller) - #set screen definition in MCS layer - mcsLayer._clientSettings.core.desktopHeight.value = self._height - mcsLayer._clientSettings.core.desktopWidth.value = self._width - - return tpkt.TPKT(tpdu.createClient(mcsLayer)); + return controller.getProtocol(); def buildObserver(self, controller): ''' @@ -162,9 +209,9 @@ class ClientFactory(protocol.Factory): raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildObserver", "ClientFactory")) class ServerFactory(protocol.Factory): - ''' - Factory of Serrve RDP protocol - ''' + """ + Factory of Server RDP protocol + """ def __init__(self, privateKeyFileName, certificateFileName): """ @param privateKeyFileName: file contain server private key @@ -178,30 +225,30 @@ class ServerFactory(protocol.Factory): Function call from twisted and build rdp protocol stack @param addr: destination address """ - pduLayer = pdu.PDU(pdu.PDUServerListener()) - return tpkt.TPKT(tpdu.createServer(mcs.createServer(pduLayer), self._privateKeyFileName, self._certificateFileName)); + #pduLayer = pdu.PDU(pdu.PDUServerListener()) + #return tpkt.TPKT(tpdu.createServer(mcs.createServer(pduLayer), self._privateKeyFileName, self._certificateFileName)); def buildObserver(self): - ''' - build observer use for connection - ''' + """ + Build observer use for connection + """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildObserver", "ServerFactory")) class RDPClientObserver(object): - ''' - class use to inform all RDP event handle by RDPY - ''' + """ + Class use to inform all RDP event handle by RDPY + """ def __init__(self, controller): """ - @param listener: RDP listener use to interact with protocol + @param controller: RDP controller use to interact with protocol """ self._controller = controller self._controller.addClientObserver(self) def onBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): - ''' - notify bitmap update + """ + Notify bitmap update @param destLeft: xmin position @param destTop: ymin position @param destRight: xmax position because RDP can send bitmap with padding @@ -211,5 +258,5 @@ class RDPClientObserver(object): @param bitsPerPixel: number of bit per pixel @param isCompress: use RLE compression @param data: bitmap data - ''' + """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onBitmapUpdate", "RDPClientObserver")) \ No newline at end of file diff --git a/rdpy/protocol/rdp/tpdu.py b/rdpy/protocol/rdp/tpdu.py index 324f371..f217628 100644 --- a/rdpy/protocol/rdp/tpdu.py +++ b/rdpy/protocol/rdp/tpdu.py @@ -250,23 +250,6 @@ class TPDU(LayerAutomata, StreamSender): @param message: network.Type message """ self._transport.send((TPDUDataHeader(), message)) - -def createClient(presentation): - """ - Factory of TPDU layer in Client mode - @param presentation: presentation layer, in RDP mode is MCS layer - """ - return TPDU(LayerMode.CLIENT, presentation) - -def createServer(presentation, privateKeyFileName, certificateFileName): - """ - Factory of TPDU layer in Server mode - @param privateKeyFileName: file contain server private key - @param certficiateFileName: file that contain publi key - """ - tpdu = TPDU(LayerMode.SERVER, presentation) - tpdu.initTLSServerInfos(privateKeyFileName, certificateFileName) - return tpdu #open ssl needed from twisted.internet import ssl diff --git a/rdpy/protocol/rdp/tpkt.py b/rdpy/protocol/rdp/tpkt.py index 1a3271c..0cb0648 100644 --- a/rdpy/protocol/rdp/tpkt.py +++ b/rdpy/protocol/rdp/tpkt.py @@ -24,19 +24,30 @@ Use to build correct size packet and handle slow path and fast path mode """ from rdpy.network.layer import RawLayer, LayerMode from rdpy.network.type import UInt8, UInt16Be, sizeof +from rdpy.network.error import CallPureVirtualFuntion + +class FastPathListener(object): + """ + Fast path packet listener + Usually PDU layer + """ + def recvFastPath(self, fastPathData): + """ + Call when fast path packet is received + @param fastPathData: Stream + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "StreamListener")) class TPKT(RawLayer): """ TPKT layer in RDP protocol stack - this layer only handle size of packet - and determine if is a fast path packet + This layer only handle size of packet and determine if is a fast path packet """ #first byte of classic tpkt header TPKT_PACKET = 3 - def __init__(self, presentation): + def __init__(self, presentation, fastPathListener): """ - Constructor @param presentation: presentation layer, in RDP case is TPDU layer """ RawLayer.__init__(self, LayerMode.NONE, presentation) @@ -44,10 +55,12 @@ class TPKT(RawLayer): self._lastPacketVersion = UInt8() #length may be coded on more than 1 bytes self._lastShortLength = UInt8() + #fast path listener + self._fastPathListener = fastPathListener def connect(self): """ - call when transport layer connection + Call when transport layer connection is made (inherit from RawLayer) """ #header is on two bytes @@ -58,7 +71,7 @@ class TPKT(RawLayer): def readHeader(self, data): """ - read header of TPKT packet + Read header of TPKT packet @param data: Stream received from twisted layer """ #first read packet version @@ -81,7 +94,7 @@ class TPKT(RawLayer): def readExtendedHeader(self, data): """ - header may be on 4 bytes + Header may be on 4 bytes @param data: Stream from twisted layer """ #next state is read data @@ -91,26 +104,27 @@ class TPKT(RawLayer): def readExtendedFastPathHeader(self, data): """ - fast path header may be on 1 byte more + Fast path header may be on 1 byte more @param data: Stream from twisted layer """ leftPart = UInt8() data.readType(leftPart) self._lastShortLength.value &= ~0x80 - self._lastShortLength.value = (self._lastShortLength.value << 8) + leftPart.value + packetSize = (self._lastShortLength.value << 8) + leftPart.value #next state is fast patn data - self.expect(self._lastShortLength.value - 3, self.readFastPath) + self.expect(packetSize - 3, self.readFastPath) def readFastPath(self, data): """ - fast path data + Fast path data @param data: Stream from twisted layer """ - pass + self._fastPathListener.recvFastPath(data) + self.expect(2, self.readHeader) def readData(self, data): """ - read classic TPKT packet, last state in tpkt automata + Read classic TPKT packet, last state in tpkt automata @param data: Stream with correct size """ #next state is pass to @@ -119,7 +133,7 @@ class TPKT(RawLayer): def send(self, message): """ - send encompassed data + Send encompassed data @param message: network.Type message to send """ RawLayer.send(self, (UInt8(TPKT.TPKT_PACKET), UInt8(0), UInt16Be(sizeof(message) + 4), message)) \ No newline at end of file diff --git a/rdpy/ui/qt4.py b/rdpy/ui/qt4.py index 6048654..301731e 100644 --- a/rdpy/ui/qt4.py +++ b/rdpy/ui/qt4.py @@ -176,13 +176,20 @@ class RDPClientQt(RDPClientObserver, QAdaptor): @param data: bitmap data """ image = None - if bitsPerPixel == 16: + if bitsPerPixel == 15: + if isCompress: + image = QtGui.QImage(width, height, QtGui.QImage.Format_RGB555) + data = rle.bitmap_decompress(image.bits(), width, height, data, len(data), 2) + else: + image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB555) + + elif bitsPerPixel == 16: if isCompress: image = QtGui.QImage(width, height, QtGui.QImage.Format_RGB16) data = rle.bitmap_decompress(image.bits(), width, height, data, len(data), 2) else: image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB16) - + elif bitsPerPixel == 24: if isCompress: image = QtGui.QImage(width, height, QtGui.QImage.Format_RGB24)