add fastpath to be compatible with rdp 8.0
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -354,17 +360,3 @@ class MCS(LayerAutomata):
|
||||
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())
|
||||
|
||||
@@ -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
|
||||
@@ -899,28 +1015,6 @@ 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:
|
||||
"""
|
||||
@@ -1055,6 +1150,17 @@ class PDU(LayerAutomata):
|
||||
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):
|
||||
"""
|
||||
Send all client capabilities
|
||||
@@ -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
|
||||
@@ -1126,5 +1232,3 @@ class PDU(LayerAutomata):
|
||||
pdu = ClientInputEventPDU(self._transport.getUserId(), self._shareId)
|
||||
pdu.slowPathInputEvents._array = [SlowPathInputEvent(x) for x in pointerEvents]
|
||||
self._transport.send(pdu)
|
||||
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
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"))
|
||||
@@ -251,23 +251,6 @@ class TPDU(LayerAutomata, StreamSender):
|
||||
"""
|
||||
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
|
||||
from OpenSSL import SSL
|
||||
|
||||
@@ -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))
|
||||
@@ -176,7 +176,14 @@ 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)
|
||||
|
||||
Reference in New Issue
Block a user