fix bug + more license automata
This commit is contained in:
44
README.md
44
README.md
@@ -1,6 +1,9 @@
|
|||||||
# RDPY
|
# RDPY
|
||||||
|
|
||||||
Remote Desktop Protocol in Twisted Python
|
Remote Desktop Protocol in Twisted Python.
|
||||||
|
|
||||||
|
RDPY is ful python except the bitmap decompression in RDP client for performance. RDPY has no ambition to be as faster as freerdp, rdesktop or mstsc, is made to play with microsoft protocol. There are some limitations essentially due to price of license (Packet redirection and License extesion in RDP protocol).
|
||||||
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
* python2.7
|
* python2.7
|
||||||
@@ -8,16 +11,45 @@ Remote Desktop Protocol in Twisted Python
|
|||||||
* python-openssl
|
* python-openssl
|
||||||
* python-qt4
|
* python-qt4
|
||||||
* python-qt4reactor
|
* python-qt4reactor
|
||||||
|
|
||||||
## Requirements libs
|
|
||||||
* python-sip-dev
|
* python-sip-dev
|
||||||
* scons
|
* scons
|
||||||
|
|
||||||
|
## Build
|
||||||
```
|
```
|
||||||
$ git clone https://github.com/citronneur/rdpy.git rdpy
|
$ git clone https://github.com/citronneur/rdpy.git rdpy
|
||||||
$ scons -C rdpy/lib install
|
$ scons -C rdpy/lib install
|
||||||
```
|
```
|
||||||
|
|
||||||
## Binaries
|
## Binaries
|
||||||
|
Binaries are uses as examples to use rdpy lib.
|
||||||
|
|
||||||
|
To create an RDP client (this example doesn't need build step of project because it doesn't call bitmap uncompress):
|
||||||
|
```
|
||||||
|
from rdpy.protocol.rdp import rdp
|
||||||
|
class RDPClientQtFactory(rdp.ClientFactory):
|
||||||
|
def buildObserver(self, controller):
|
||||||
|
class MyObserver(rdp.RDPClientObserver)
|
||||||
|
def __init__(self, controller)
|
||||||
|
rdp.RDPClientObserver.__init__(self, controller)
|
||||||
|
def onBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||||
|
#here code handle bitmap
|
||||||
|
pass
|
||||||
|
def onReady(self):
|
||||||
|
#send r key
|
||||||
|
self._controller.sendKeyEventUnicode(ord(unicode("r".toUtf8(), encoding="UTF-8")), True)
|
||||||
|
#mouse and click at pixel 200x200
|
||||||
|
self._controller.sendPointerEvent(200, 200, 1, true)
|
||||||
|
|
||||||
|
return MyObserver(controller)
|
||||||
|
|
||||||
|
def startedConnecting(self, connector):
|
||||||
|
pass
|
||||||
|
def clientConnectionLost(self, connector, reason):
|
||||||
|
pass
|
||||||
|
def clientConnectionFailed(self, connector, reason):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
RDP Client
|
RDP Client
|
||||||
```
|
```
|
||||||
$ rdpy/bin/rdpy-rdpclient XXX.XXX.XXX.XXX 3389
|
$ rdpy/bin/rdpy-rdpclient XXX.XXX.XXX.XXX 3389
|
||||||
@@ -30,15 +62,15 @@ $ rdpy/bin/rdpy-vncclient XXX.XXX.XXX.XXX 5901
|
|||||||
|
|
||||||
RDP Proxy
|
RDP Proxy
|
||||||
```
|
```
|
||||||
$ rdpy/bin/rdpy-vncclient XXX.XXX.XXX.XXX 5901
|
$ rdpy/bin/rdpy-rdpproxy
|
||||||
```
|
```
|
||||||
|
|
||||||
##Must be implemented before first release
|
##Limitations
|
||||||
* CreedSSP
|
* CreedSSP
|
||||||
* Packet redirection
|
* Packet redirection
|
||||||
* License
|
* License
|
||||||
* Most common orders
|
* Most common orders
|
||||||
* FastPath messages
|
|
||||||
* Des VNC (using pyDes)
|
* Des VNC (using pyDes)
|
||||||
|
* VNC server side
|
||||||
|
|
||||||
this project is still in progress.
|
this project is still in progress.
|
||||||
|
|||||||
@@ -443,10 +443,10 @@ class CompositeType(Type):
|
|||||||
readLen += sizeof(self.__dict__[name])
|
readLen += sizeof(self.__dict__[name])
|
||||||
|
|
||||||
def __write__(self, s):
|
def __write__(self, s):
|
||||||
'''
|
"""
|
||||||
call write on each ordered subtype
|
Call write on each ordered sub type
|
||||||
@param s: Stream
|
@param s: Stream
|
||||||
'''
|
"""
|
||||||
for name in self._typeName:
|
for name in self._typeName:
|
||||||
try:
|
try:
|
||||||
s.writeType(self.__dict__[name])
|
s.writeType(self.__dict__[name])
|
||||||
@@ -818,21 +818,20 @@ class Stream(StringIO):
|
|||||||
value.write(self)
|
value.write(self)
|
||||||
|
|
||||||
class ArrayType(Type):
|
class ArrayType(Type):
|
||||||
'''
|
"""
|
||||||
in write mode ArrayType is just list
|
In write mode ArrayType is just list
|
||||||
but in read mode it can be dynamic
|
But in read mode it can be dynamic
|
||||||
readLen may be dynamic
|
readLen may be dynamic
|
||||||
'''
|
"""
|
||||||
def __init__(self, typeFactory, init = None, readLen = UInt8(), conditional = lambda:True, optional = False, constant = False):
|
def __init__(self, typeFactory, init = None, readLen = UInt8(), conditional = lambda:True, optional = False, constant = False):
|
||||||
'''
|
"""
|
||||||
constructor
|
|
||||||
@param typeFactory: class use to init new element on read
|
@param typeFactory: class use to init new element on read
|
||||||
@param init: init array
|
@param init: init array
|
||||||
@param readLen: number of element in sequence
|
@param readLen: number of element in sequence
|
||||||
@param conditional : function call before read or write type
|
@param conditional : function call before read or write type
|
||||||
@param optional: boolean check before read if there is still data in stream
|
@param optional: boolean check before read if there is still data in stream
|
||||||
@param constant: if true check any changes of object during reading
|
@param constant: if true check any changes of object during reading
|
||||||
'''
|
"""
|
||||||
Type.__init__(self, conditional, optional, constant)
|
Type.__init__(self, conditional, optional, constant)
|
||||||
self._typeFactory = typeFactory
|
self._typeFactory = typeFactory
|
||||||
self._readLen = readLen
|
self._readLen = readLen
|
||||||
@@ -841,10 +840,10 @@ class ArrayType(Type):
|
|||||||
self._array = init
|
self._array = init
|
||||||
|
|
||||||
def __read__(self, s):
|
def __read__(self, s):
|
||||||
'''
|
"""
|
||||||
create new object and read it
|
Create new object and read it
|
||||||
@param s: Stream
|
@param s: Stream
|
||||||
'''
|
"""
|
||||||
self._array = []
|
self._array = []
|
||||||
for _ in range(0, self._readLen.value):
|
for _ in range(0, self._readLen.value):
|
||||||
element = self._typeFactory()
|
element = self._typeFactory()
|
||||||
@@ -852,21 +851,21 @@ class ArrayType(Type):
|
|||||||
self._array.append(element)
|
self._array.append(element)
|
||||||
|
|
||||||
def __write__(self, s):
|
def __write__(self, s):
|
||||||
'''
|
"""
|
||||||
just write array
|
Just write array
|
||||||
@param s: Stream
|
@param s: Stream
|
||||||
'''
|
"""
|
||||||
s.writeType(self._array)
|
s.writeType(self._array)
|
||||||
|
|
||||||
def __sizeof__(self):
|
def __sizeof__(self):
|
||||||
'''
|
"""
|
||||||
sizeof inner array
|
Size of inner array
|
||||||
'''
|
"""
|
||||||
return sizeof(self._array)
|
return sizeof(self._array)
|
||||||
|
|
||||||
class FactoryType(Type):
|
class FactoryType(Type):
|
||||||
"""
|
"""
|
||||||
Call factory function on read or write
|
Call factory function on read
|
||||||
"""
|
"""
|
||||||
def __init__(self, factory, conditional = lambda:True, optional = False, constant = False):
|
def __init__(self, factory, conditional = lambda:True, optional = False, constant = False):
|
||||||
"""
|
"""
|
||||||
@@ -880,29 +879,43 @@ class FactoryType(Type):
|
|||||||
if not callable(factory):
|
if not callable(factory):
|
||||||
self._factory = lambda:factory
|
self._factory = lambda:factory
|
||||||
|
|
||||||
self._value = self._factory()
|
self._value = None
|
||||||
|
|
||||||
def __read__(self, s):
|
def __read__(self, s):
|
||||||
'''
|
"""
|
||||||
call factory and read it
|
Call factory and write it
|
||||||
@param s: Stream
|
@param s: Stream
|
||||||
'''
|
"""
|
||||||
self._value = self._factory()
|
self._value = self._factory()
|
||||||
s.readType(self._value)
|
s.readType(self._value)
|
||||||
|
|
||||||
def __write__(self, s):
|
def __write__(self, s):
|
||||||
'''
|
"""
|
||||||
call factory and write elements
|
Call factory and read it
|
||||||
@param s: Stream
|
@param s: Stream
|
||||||
'''
|
"""
|
||||||
self._value = self._factory()
|
self._value = self._factory()
|
||||||
s.writeType(self._value)
|
s.writeType(self._value)
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
"""
|
||||||
|
Magic function to be FactoryType as transparent as possible
|
||||||
|
@return: _value parameter
|
||||||
|
"""
|
||||||
|
return self._value.__getattribute__(name)
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
"""
|
||||||
|
Magic function to be FactoryType as transparent as possible
|
||||||
|
@return: index of _value
|
||||||
|
"""
|
||||||
|
return self._value.__getitem__(item)
|
||||||
|
|
||||||
def __sizeof__(self):
|
def __sizeof__(self):
|
||||||
'''
|
"""
|
||||||
sizeof of object returned by factory
|
Size of of object returned by factory
|
||||||
'''
|
"""
|
||||||
return sizeof(self._factory())
|
return sizeof(self._value)
|
||||||
|
|
||||||
def CheckValueOnRead(cls):
|
def CheckValueOnRead(cls):
|
||||||
'''
|
'''
|
||||||
@@ -923,7 +936,7 @@ def CheckValueOnRead(cls):
|
|||||||
|
|
||||||
def hexDump(src, length=16):
|
def hexDump(src, length=16):
|
||||||
'''
|
'''
|
||||||
print hex representation of sr
|
print hex representation of str
|
||||||
@param src: string
|
@param src: string
|
||||||
'''
|
'''
|
||||||
FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)])
|
FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)])
|
||||||
|
|||||||
@@ -269,9 +269,9 @@ class Capability(CompositeType):
|
|||||||
return String(readLen = UInt16Le(lambda:self.lengthCapability.value - 4))
|
return String(readLen = UInt16Le(lambda:self.lengthCapability.value - 4))
|
||||||
|
|
||||||
if capability is None:
|
if capability is None:
|
||||||
capability = CapabilityFactory
|
capability = FactoryType(CapabilityFactory)
|
||||||
|
|
||||||
self.capability = FactoryType(capability)
|
self.capability = capability
|
||||||
|
|
||||||
class GeneralCapability(CompositeType):
|
class GeneralCapability(CompositeType):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ RDP extended license
|
|||||||
@see: http://msdn.microsoft.com/en-us/library/cc241880.aspx
|
@see: http://msdn.microsoft.com/en-us/library/cc241880.aspx
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof
|
from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType
|
||||||
|
|
||||||
class MessageType(object):
|
class MessageType(object):
|
||||||
"""
|
"""
|
||||||
@@ -41,6 +41,7 @@ class MessageType(object):
|
|||||||
class ErrorCode(object):
|
class ErrorCode(object):
|
||||||
"""
|
"""
|
||||||
License error message code
|
License error message code
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||||
"""
|
"""
|
||||||
ERR_INVALID_SERVER_CERTIFICATE = 0x00000001
|
ERR_INVALID_SERVER_CERTIFICATE = 0x00000001
|
||||||
ERR_NO_LICENSE = 0x00000002
|
ERR_NO_LICENSE = 0x00000002
|
||||||
@@ -55,41 +56,163 @@ class ErrorCode(object):
|
|||||||
class StateTransition(object):
|
class StateTransition(object):
|
||||||
"""
|
"""
|
||||||
Automata state transition
|
Automata state transition
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||||
"""
|
"""
|
||||||
ST_TOTAL_ABORT = 0x00000001
|
ST_TOTAL_ABORT = 0x00000001
|
||||||
ST_NO_TRANSITION = 0x00000002
|
ST_NO_TRANSITION = 0x00000002
|
||||||
ST_RESET_PHASE_TO_START = 0x00000003
|
ST_RESET_PHASE_TO_START = 0x00000003
|
||||||
ST_RESEND_LAST_MESSAGE = 0x00000004
|
ST_RESEND_LAST_MESSAGE = 0x00000004
|
||||||
|
|
||||||
class LicenceBinaryBlob(CompositeType):
|
class BinaryBlobType(object):
|
||||||
"""
|
"""
|
||||||
Blob use by license manager to echange security data
|
Binary blob data type
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
BB_DATA_BLOB = 0x0001
|
||||||
|
BB_RANDOM_BLOB = 0x0002
|
||||||
|
BB_CERTIFICATE_BLOB = 0x0003
|
||||||
|
BB_ERROR_BLOB = 0x0004
|
||||||
|
BB_ENCRYPTED_DATA_BLOB = 0x0009
|
||||||
|
BB_KEY_EXCHG_ALG_BLOB = 0x000D
|
||||||
|
BB_SCOPE_BLOB = 0x000E
|
||||||
|
BB_CLIENT_USER_NAME_BLOB = 0x000F
|
||||||
|
BB_CLIENT_MACHINE_NAME_BLOB = 0x0010
|
||||||
|
|
||||||
|
class LicenseBinaryBlob(CompositeType):
|
||||||
|
"""
|
||||||
|
Blob use by license manager to exchange security data
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||||
|
"""
|
||||||
|
def __init__(self, blobType = 0):
|
||||||
CompositeType.__init__(self)
|
CompositeType.__init__(self)
|
||||||
self.wBlobType = UInt16Le()
|
self.wBlobType = UInt16Le(blobType, constant = True)
|
||||||
self.wBlobLen = UInt16Le(lambda:sizeof(self.blobData))
|
self.wBlobLen = UInt16Le(lambda:sizeof(self.blobData))
|
||||||
self.blobData = String(readLen = self.wBlobLen, conditional = lambda:self.wBlobLen.value > 0)
|
self.blobData = String(readLen = self.wBlobLen)
|
||||||
|
|
||||||
class LicensingErrorMessage(CompositeType):
|
class LicensingErrorMessage(CompositeType):
|
||||||
"""
|
"""
|
||||||
License error message
|
License error message
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||||
"""
|
"""
|
||||||
def __init__(self, conditional = lambda:True):
|
def __init__(self, conditional = lambda:True):
|
||||||
CompositeType.__init__(self, conditional = conditional)
|
CompositeType.__init__(self, conditional = conditional)
|
||||||
self.dwErrorCode = UInt32Le()
|
self.dwErrorCode = UInt32Le()
|
||||||
self.dwStateTransition = UInt32Le()
|
self.dwStateTransition = UInt32Le()
|
||||||
self.blob = LicenceBinaryBlob()
|
self.blob = LicenseBinaryBlob(BinaryBlobType.BB_ERROR_BLOB)
|
||||||
|
|
||||||
|
class ProductInformation(CompositeType):
|
||||||
|
"""
|
||||||
|
License server product information
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc241915.aspx
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
CompositeType.__init__(self)
|
||||||
|
self.dwVersion = UInt32Le()
|
||||||
|
self.cbCompanyName = UInt32Le(lambda:sizeof(self.pbCompanyName))
|
||||||
|
#may contain "Microsoft Corporation" from server microsoft
|
||||||
|
self.pbCompanyName = String(readLen = self.cbCompanyName)
|
||||||
|
self.cbProductId = UInt32Le(lambda:sizeof(self.pbProductId))
|
||||||
|
#may contain "A02" from microsoft license server
|
||||||
|
self.pbProductId = String(readLen = self.cbProductId)
|
||||||
|
|
||||||
|
|
||||||
|
class Scope(CompositeType):
|
||||||
|
"""
|
||||||
|
Use in license nego
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc241917.aspx
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
CompositeType.__init__(self)
|
||||||
|
self.scope = LicenseBinaryBlob(BinaryBlobType.BB_SCOPE_BLOB)
|
||||||
|
|
||||||
|
class ScopeList(CompositeType):
|
||||||
|
"""
|
||||||
|
Use in license nego
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc241916.aspx
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
CompositeType.__init__(self)
|
||||||
|
self.scopeCount = UInt32Le(lambda:sizeof(self.scopeArray))
|
||||||
|
self.scopeArray = ArrayType(Scope, readLen = self.scopeCount)
|
||||||
|
|
||||||
|
class ServerLicenseRequest(CompositeType):
|
||||||
|
"""
|
||||||
|
Send by server to signal license request
|
||||||
|
server -> client
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc241914.aspx
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
CompositeType.__init__(self)
|
||||||
|
self.serverRandom = String(readLen = UInt8(32))
|
||||||
|
self.productInfo = ProductInformation()
|
||||||
|
self.keyExchangeList = LicenseBinaryBlob(BinaryBlobType.BB_KEY_EXCHG_ALG_BLOB)
|
||||||
|
self.serverCertificate = LicenseBinaryBlob(BinaryBlobType.BB_CERTIFICATE_BLOB)
|
||||||
|
self.scopeList = ScopeList()
|
||||||
|
|
||||||
|
class ClientNewLicenseRequest(CompositeType):
|
||||||
|
"""
|
||||||
|
Send by client to ask new license for client.
|
||||||
|
RDPY doesn'support license reuse, need it in futur version
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc241918.aspx
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
CompositeType.__init__(self)
|
||||||
|
#RSA and must be only RSA
|
||||||
|
self.preferredKeyExchangeAlg = UInt32Le(0x00000001, constant = True)
|
||||||
|
#pure microsoft client ;-)
|
||||||
|
#http://msdn.microsoft.com/en-us/library/1040af38-c733-4fb3-acd1-8db8cc979eda#id10
|
||||||
|
self.platformId = UInt32Le(0x04000000 | 0x00020000)
|
||||||
|
self.clientRandom = String("\x00" * 32)
|
||||||
|
self.encryptedPreMasterSecret = LicenseBinaryBlob(BinaryBlobType.BB_RANDOM_BLOB)
|
||||||
|
self.ClientUserName = LicenseBinaryBlob(BinaryBlobType.BB_CLIENT_USER_NAME_BLOB)
|
||||||
|
self.ClientMachineName = LicenseBinaryBlob(BinaryBlobType.BB_CLIENT_MACHINE_NAME_BLOB)
|
||||||
|
|
||||||
class LicPacket(CompositeType):
|
class LicPacket(CompositeType):
|
||||||
"""
|
"""
|
||||||
A license packet
|
A license packet
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self, message = None):
|
||||||
CompositeType.__init__(self)
|
CompositeType.__init__(self)
|
||||||
|
|
||||||
|
def MessageTypeFactory():
|
||||||
|
"""
|
||||||
|
Determine type in accordance of instance of licensingMessage type
|
||||||
|
Use in write mode
|
||||||
|
"""
|
||||||
|
if isinstance(self.licensingMessage, LicensingErrorMessage):
|
||||||
|
return MessageType.ERROR_ALERT
|
||||||
|
elif isinstance(self.licensingMessage, ServerLicenseRequest):
|
||||||
|
return MessageType.LICENSE_REQUEST
|
||||||
|
elif isinstance(self.licensingMessage, ClientNewLicenseRequest):
|
||||||
|
return MessageType.NEW_LICENSE_REQUEST
|
||||||
|
|
||||||
#preambule
|
#preambule
|
||||||
self.bMsgtype = UInt8()
|
self.bMsgtype = UInt8(lambda:(MessageTypeFactory()))
|
||||||
self.flag = UInt8()
|
self.flag = UInt8()
|
||||||
self.wMsgSize = UInt16Le(lambda: sizeof(self))
|
self.wMsgSize = UInt16Le(lambda: sizeof(self))
|
||||||
self.errorMessage = LicensingErrorMessage(conditional = lambda:self.bMsgtype.value == MessageType.ERROR_ALERT)
|
|
||||||
|
|
||||||
|
def LicensingMessageFactory():
|
||||||
|
"""
|
||||||
|
factory for message nego
|
||||||
|
Use in read mode
|
||||||
|
"""
|
||||||
|
if self.bMsgtype.value == MessageType.ERROR_ALERT:
|
||||||
|
return LicensingErrorMessage()
|
||||||
|
elif self.bMsgtype.value == MessageType.LICENSE_REQUEST:
|
||||||
|
return ServerLicenseRequest()
|
||||||
|
elif self.bMsgtype.value == MessageType.NEW_LICENSE_REQUEST:
|
||||||
|
ClientNewLicenseRequest()
|
||||||
|
|
||||||
|
if message is None:
|
||||||
|
message = FactoryType(LicensingMessageFactory)
|
||||||
|
|
||||||
|
self.licensingMessage = message
|
||||||
|
|
||||||
|
def createNewLicenseRequest(serverLicenseRequest):
|
||||||
|
"""
|
||||||
|
Create new license request in response to server license request
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc241989.aspx
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc241918.aspx
|
||||||
|
@todo: need RDP license server
|
||||||
|
"""
|
||||||
|
return LicPacket(message = ClientNewLicenseRequest())
|
||||||
@@ -32,9 +32,24 @@ import gcc, lic, caps, tpkt
|
|||||||
class SecurityFlag(object):
|
class SecurityFlag(object):
|
||||||
"""
|
"""
|
||||||
Microsoft security flags
|
Microsoft security flags
|
||||||
|
@see: http://msdn.microsoft.com/en-us/library/cc240579.aspx
|
||||||
"""
|
"""
|
||||||
|
SEC_EXCHANGE_PKT = 0x0001
|
||||||
|
SEC_TRANSPORT_REQ = 0x0002
|
||||||
|
RDP_SEC_TRANSPORT_RSP = 0x0004
|
||||||
|
SEC_ENCRYPT = 0x0008
|
||||||
|
SEC_RESET_SEQNO = 0x0010
|
||||||
|
SEC_IGNORE_SEQNO = 0x0020
|
||||||
SEC_INFO_PKT = 0x0040
|
SEC_INFO_PKT = 0x0040
|
||||||
SEC_LICENSE_PKT = 0x0080
|
SEC_LICENSE_PKT = 0x0080
|
||||||
|
SEC_LICENSE_ENCRYPT_CS = 0x0200
|
||||||
|
SEC_LICENSE_ENCRYPT_SC = 0x0200
|
||||||
|
SEC_REDIRECTION_PKT = 0x0400
|
||||||
|
SEC_SECURE_CHECKSUM = 0x0800
|
||||||
|
SEC_AUTODETECT_REQ = 0x1000
|
||||||
|
SEC_AUTODETECT_RSP = 0x2000
|
||||||
|
SEC_HEARTBEAT = 0x4000
|
||||||
|
SEC_FLAGSHI_VALID = 0x8000
|
||||||
|
|
||||||
class InfoFlag(object):
|
class InfoFlag(object):
|
||||||
"""
|
"""
|
||||||
@@ -635,9 +650,9 @@ class DataPDU(CompositeType):
|
|||||||
return String()
|
return String()
|
||||||
|
|
||||||
if pduData is None:
|
if pduData is None:
|
||||||
pduData = PDUDataFactory
|
pduData = FactoryType(PDUDataFactory)
|
||||||
|
|
||||||
self.pduData = FactoryType(pduData)
|
self.pduData = pduData
|
||||||
|
|
||||||
class SynchronizeDataPDU(CompositeType):
|
class SynchronizeDataPDU(CompositeType):
|
||||||
"""
|
"""
|
||||||
@@ -737,9 +752,9 @@ class UpdateDataPDU(CompositeType):
|
|||||||
return String()
|
return String()
|
||||||
|
|
||||||
if updateData is None:
|
if updateData is None:
|
||||||
updateData = UpdateDataFactory
|
updateData = FactoryType(UpdateDataFactory, conditional = lambda:(self.updateType.value != UpdateType.UPDATETYPE_SYNCHRONIZE))
|
||||||
|
|
||||||
self.updateData = FactoryType(updateData, conditional = lambda:(self.updateType.value != UpdateType.UPDATETYPE_SYNCHRONIZE))
|
self.updateData = updateData
|
||||||
|
|
||||||
class FastPathUpdatePDU(CompositeType):
|
class FastPathUpdatePDU(CompositeType):
|
||||||
"""
|
"""
|
||||||
@@ -766,9 +781,9 @@ class FastPathUpdatePDU(CompositeType):
|
|||||||
return String()
|
return String()
|
||||||
|
|
||||||
if updateData is None:
|
if updateData is None:
|
||||||
updateData = UpdateDataFactory
|
updateData = FactoryType(UpdateDataFactory)
|
||||||
|
|
||||||
self.updateData = FactoryType(updateData)
|
self.updateData = updateData
|
||||||
|
|
||||||
class SynchronizeUpdatePDU(CompositeType):
|
class SynchronizeUpdatePDU(CompositeType):
|
||||||
"""
|
"""
|
||||||
@@ -876,26 +891,21 @@ class SlowPathInputEvent(CompositeType):
|
|||||||
"""
|
"""
|
||||||
if isinstance(event, PointerEvent):
|
if isinstance(event, PointerEvent):
|
||||||
return InputMessageType.INPUT_EVENT_MOUSE
|
return InputMessageType.INPUT_EVENT_MOUSE
|
||||||
|
|
||||||
elif isinstance(event, ScancodeKeyEvent):
|
elif isinstance(event, ScancodeKeyEvent):
|
||||||
return InputMessageType.INPUT_EVENT_SCANCODE
|
return InputMessageType.INPUT_EVENT_SCANCODE
|
||||||
|
|
||||||
elif isinstance(event, UnicodeKeyEvent):
|
elif isinstance(event, UnicodeKeyEvent):
|
||||||
return InputMessageType.INPUT_EVENT_UNICODE
|
return InputMessageType.INPUT_EVENT_UNICODE
|
||||||
|
|
||||||
else:
|
self.messageType = UInt16Le(lambda:MessageTypeFactory(self.slowPathInputData))
|
||||||
return None
|
|
||||||
|
|
||||||
self.messageType = UInt16Le(lambda:MessageTypeFactory(self.slowPathInputData._value))
|
|
||||||
|
|
||||||
def SlowPathInputDataFactory():
|
def SlowPathInputDataFactory():
|
||||||
if self.messageType.value == InputMessageType.INPUT_EVENT_MOUSE:
|
if self.messageType.value == InputMessageType.INPUT_EVENT_MOUSE:
|
||||||
return PointerEvent()
|
return PointerEvent()
|
||||||
|
|
||||||
if messageData is None:
|
if messageData is None:
|
||||||
messageData = SlowPathInputDataFactory
|
messageData = FactoryType(SlowPathInputDataFactory)
|
||||||
|
|
||||||
self.slowPathInputData = FactoryType(messageData)
|
self.slowPathInputData = messageData
|
||||||
|
|
||||||
class PointerEvent(CompositeType):
|
class PointerEvent(CompositeType):
|
||||||
"""
|
"""
|
||||||
@@ -935,7 +945,13 @@ class PDUClientListener(object):
|
|||||||
"""
|
"""
|
||||||
Interface for PDU client automata listener
|
Interface for PDU client automata listener
|
||||||
"""
|
"""
|
||||||
def recvBitmapUpdateDataPDU(self, rectangles):
|
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
|
call when a bitmap data is received from update PDU
|
||||||
@param rectangles: [pdu.BitmapData] struct
|
@param rectangles: [pdu.BitmapData] struct
|
||||||
@@ -1012,9 +1028,6 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
|||||||
#share id between client and server
|
#share id between client and server
|
||||||
self._shareId = 0
|
self._shareId = 0
|
||||||
|
|
||||||
#determine if layer is connected
|
|
||||||
self._isConnected = False
|
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
"""
|
"""
|
||||||
Connect event in client mode send logon info
|
Connect event in client mode send logon info
|
||||||
@@ -1043,6 +1056,7 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
|||||||
Read license info packet and check if is a valid client info
|
Read license info packet and check if is a valid client info
|
||||||
@param data: Stream
|
@param data: Stream
|
||||||
"""
|
"""
|
||||||
|
#license preambule
|
||||||
securityFlag = UInt16Le()
|
securityFlag = UInt16Le()
|
||||||
securityFlagHi = UInt16Le()
|
securityFlagHi = UInt16Le()
|
||||||
data.readType((securityFlag, securityFlagHi))
|
data.readType((securityFlag, securityFlagHi))
|
||||||
@@ -1053,13 +1067,14 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
|||||||
validClientPdu = lic.LicPacket()
|
validClientPdu = lic.LicPacket()
|
||||||
data.readType(validClientPdu)
|
data.readType(validClientPdu)
|
||||||
|
|
||||||
if not validClientPdu.errorMessage._is_readed:
|
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:
|
||||||
raise InvalidExpectedDataException("Waiting valid client PDU : rdpy doesn't support licensing nego")
|
self.setNextState(self.recvDemandActivePDU)
|
||||||
|
#not tested because i can't buy RDP license server
|
||||||
if not (validClientPdu.errorMessage.dwErrorCode.value == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.errorMessage.dwStateTransition.value == lic.StateTransition.ST_NO_TRANSITION):
|
elif validClientPdu.bMsgtype.value == lic.MessageType.LICENSE_REQUEST:
|
||||||
raise InvalidExpectedDataException("Server refuse licensing negotiation")
|
newLicenseReq = lic.createNewLicenseRequest(validClientPdu.licensingMessage)
|
||||||
|
self._transport.send((UInt16Le(SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), newLicenseReq))
|
||||||
self.setNextState(self.recvDemandActivePDU)
|
else:
|
||||||
|
raise InvalidExpectedDataException("Not a valid license packet")
|
||||||
|
|
||||||
def readDataPDU(self, data):
|
def readDataPDU(self, data):
|
||||||
"""
|
"""
|
||||||
@@ -1073,9 +1088,9 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
|||||||
if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
|
if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
|
||||||
return dataPDU
|
return dataPDU
|
||||||
|
|
||||||
message = "Unknown code %s"%hex(dataPDU.pduData._value.errorInfo.value)
|
message = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value)
|
||||||
if ErrorInfo._MESSAGES_.has_key(dataPDU.pduData._value.errorInfo):
|
if ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo):
|
||||||
message = ErrorInfo._MESSAGES_[dataPDU.pduData._value.errorInfo]
|
message = ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
|
||||||
|
|
||||||
raise ErrorReportedFromPeer("Receive PDU Error info : %s"%message)
|
raise ErrorReportedFromPeer("Receive PDU Error info : %s"%message)
|
||||||
|
|
||||||
@@ -1114,7 +1129,7 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
|||||||
@param data: Stream from transport layer
|
@param data: Stream from transport layer
|
||||||
"""
|
"""
|
||||||
dataPDU = self.readDataPDU(data)
|
dataPDU = self.readDataPDU(data)
|
||||||
if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or dataPDU.pduData._value.action.value != Action.CTRLACTION_COOPERATE:
|
if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or dataPDU.pduData.action.value != Action.CTRLACTION_COOPERATE:
|
||||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU")
|
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU")
|
||||||
self.setNextState(self.recvServerControlGrantedPDU)
|
self.setNextState(self.recvServerControlGrantedPDU)
|
||||||
|
|
||||||
@@ -1124,7 +1139,7 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
|||||||
@param data: Stream from transport layer
|
@param data: Stream from transport layer
|
||||||
"""
|
"""
|
||||||
dataPDU = self.readDataPDU(data)
|
dataPDU = self.readDataPDU(data)
|
||||||
if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or dataPDU.pduData._value.action.value != Action.CTRLACTION_GRANTED_CONTROL:
|
if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or dataPDU.pduData.action.value != Action.CTRLACTION_GRANTED_CONTROL:
|
||||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU")
|
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU")
|
||||||
self.setNextState(self.recvServerFontMapPDU)
|
self.setNextState(self.recvServerFontMapPDU)
|
||||||
|
|
||||||
@@ -1138,7 +1153,7 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
|||||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU")
|
raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU")
|
||||||
|
|
||||||
#here i'm connected
|
#here i'm connected
|
||||||
self._isConnected = True
|
self._clientListener.onReady()
|
||||||
self.setNextState(self.recvDataPDU)
|
self.setNextState(self.recvDataPDU)
|
||||||
|
|
||||||
def recvDataPDU(self, data):
|
def recvDataPDU(self, data):
|
||||||
@@ -1147,8 +1162,8 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
|||||||
@param data: Stream from transport layer
|
@param data: Stream from transport layer
|
||||||
"""
|
"""
|
||||||
dataPDU = self.readDataPDU(data)
|
dataPDU = self.readDataPDU(data)
|
||||||
if dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_UPDATE and dataPDU.pduData._value.updateType.value == UpdateType.UPDATETYPE_BITMAP:
|
if dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_UPDATE and dataPDU.pduData.updateType.value == UpdateType.UPDATETYPE_BITMAP:
|
||||||
self._clientListener.recvBitmapUpdateDataPDU(dataPDU.pduData._value.updateData._value.rectangles._array)
|
self._clientListener.onUpdate(dataPDU.pduData.updateData.rectangles._array)
|
||||||
|
|
||||||
def recvFastPath(self, fastPathData):
|
def recvFastPath(self, fastPathData):
|
||||||
"""
|
"""
|
||||||
@@ -1159,30 +1174,30 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
|||||||
fastPathPDU = FastPathUpdatePDU()
|
fastPathPDU = FastPathUpdatePDU()
|
||||||
fastPathData.readType(fastPathPDU)
|
fastPathData.readType(fastPathPDU)
|
||||||
if fastPathPDU.updateHeader.value == FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
|
if fastPathPDU.updateHeader.value == FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
|
||||||
self._clientListener.recvBitmapUpdateDataPDU(fastPathPDU.updateData._value[1].rectangles._array)
|
self._clientListener.onUpdate(fastPathPDU.updateData[1].rectangles._array)
|
||||||
|
|
||||||
def sendConfirmActivePDU(self):
|
def sendConfirmActivePDU(self):
|
||||||
"""
|
"""
|
||||||
Send all client capabilities
|
Send all client capabilities
|
||||||
"""
|
"""
|
||||||
#init general capability
|
#init general capability
|
||||||
generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability._value
|
generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
|
||||||
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
|
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
|
||||||
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT
|
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
|
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
|
||||||
|
|
||||||
#init bitmap capability
|
#init bitmap capability
|
||||||
bitmapCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability._value
|
bitmapCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability
|
||||||
bitmapCapability.preferredBitsPerPixel = self._transport.getGCCClientSettings().core.highColorDepth
|
bitmapCapability.preferredBitsPerPixel = self._transport.getGCCClientSettings().core.highColorDepth
|
||||||
bitmapCapability.desktopWidth = self._transport.getGCCClientSettings().core.desktopWidth
|
bitmapCapability.desktopWidth = self._transport.getGCCClientSettings().core.desktopWidth
|
||||||
bitmapCapability.desktopHeight = self._transport.getGCCClientSettings().core.desktopHeight
|
bitmapCapability.desktopHeight = self._transport.getGCCClientSettings().core.desktopHeight
|
||||||
|
|
||||||
#init order capability
|
#init order capability
|
||||||
orderCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].capability._value
|
orderCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].capability
|
||||||
orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT
|
orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT
|
||||||
|
|
||||||
#init input capability
|
#init input capability
|
||||||
inputCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_INPUT].capability._value
|
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.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE
|
||||||
inputCapability.keyboardLayout = self._transport.getGCCClientSettings().core.kbdLayout
|
inputCapability.keyboardLayout = self._transport.getGCCClientSettings().core.kbdLayout
|
||||||
inputCapability.keyboardType = self._transport.getGCCClientSettings().core.keyboardType
|
inputCapability.keyboardType = self._transport.getGCCClientSettings().core.keyboardType
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ class RDPClientController(pdu.PDUClientListener):
|
|||||||
#transport packet (protocol layer)
|
#transport packet (protocol layer)
|
||||||
self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer)
|
self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer)
|
||||||
|
|
||||||
|
#is pdu layer is ready to send
|
||||||
|
self._isReady = False
|
||||||
|
|
||||||
def getProtocol(self):
|
def getProtocol(self):
|
||||||
"""
|
"""
|
||||||
@return: return Protocol layer for twisted
|
@return: return Protocol layer for twisted
|
||||||
@@ -92,21 +95,30 @@ class RDPClientController(pdu.PDUClientListener):
|
|||||||
|
|
||||||
def addClientObserver(self, observer):
|
def addClientObserver(self, observer):
|
||||||
"""
|
"""
|
||||||
add observer to RDP protocol
|
Add observer to RDP protocol
|
||||||
@param observer: new observer to add
|
@param observer: new observer to add
|
||||||
"""
|
"""
|
||||||
self._clientObserver.append(observer)
|
self._clientObserver.append(observer)
|
||||||
observer._clientListener = self
|
observer._clientListener = self
|
||||||
|
|
||||||
def recvBitmapUpdateDataPDU(self, rectangles):
|
def onUpdate(self, rectangles):
|
||||||
"""
|
"""
|
||||||
call when a bitmap data is received from update PDU
|
Call when a bitmap data is received from update PDU
|
||||||
@param rectangles: [pdu.BitmapData] struct
|
@param rectangles: [pdu.BitmapData] struct
|
||||||
"""
|
"""
|
||||||
for observer in self._clientObserver:
|
for observer in self._clientObserver:
|
||||||
#for each rectangle in update PDU
|
#for each rectangle in update PDU
|
||||||
for rectangle in rectangles:
|
for rectangle in rectangles:
|
||||||
observer.onBitmapUpdate(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.BitmapFlag.BITMAP_COMPRESSION, rectangle.bitmapDataStream.value)
|
||||||
|
|
||||||
|
def onReady(self):
|
||||||
|
"""
|
||||||
|
Call when PDU layer is connected
|
||||||
|
"""
|
||||||
|
self._isReady = True
|
||||||
|
#signal all listener
|
||||||
|
for observer in self._clientObserver:
|
||||||
|
observer.onReady()
|
||||||
|
|
||||||
def sendPointerEvent(self, x, y, button, isPressed):
|
def sendPointerEvent(self, x, y, button, isPressed):
|
||||||
"""
|
"""
|
||||||
@@ -116,7 +128,7 @@ class RDPClientController(pdu.PDUClientListener):
|
|||||||
@param button: 1 or 2 or 3
|
@param button: 1 or 2 or 3
|
||||||
@param isPressed: true if button is pressed or false if it's released
|
@param isPressed: true if button is pressed or false if it's released
|
||||||
"""
|
"""
|
||||||
if not self._pduLayer._isConnected:
|
if not self._isReady:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -149,7 +161,7 @@ class RDPClientController(pdu.PDUClientListener):
|
|||||||
@param code: scan code
|
@param code: scan code
|
||||||
@param isPressed: True if key is pressed and false if it's released
|
@param isPressed: True if key is pressed and false if it's released
|
||||||
"""
|
"""
|
||||||
if not self._pduLayer._isConnected:
|
if not self._isReady:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -172,7 +184,7 @@ class RDPClientController(pdu.PDUClientListener):
|
|||||||
@param code: unicode
|
@param code: unicode
|
||||||
@param isPressed: True if key is pressed and false if it's released
|
@param isPressed: True if key is pressed and false if it's released
|
||||||
"""
|
"""
|
||||||
if not self._pduLayer._isConnected:
|
if not self._isReady:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -271,8 +283,13 @@ class RDPClientObserver(object):
|
|||||||
self._controller = controller
|
self._controller = controller
|
||||||
self._controller.addClientObserver(self)
|
self._controller.addClientObserver(self)
|
||||||
|
|
||||||
|
def onReady(self):
|
||||||
|
"""
|
||||||
|
Stack is ready and connected
|
||||||
|
"""
|
||||||
|
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPClientObserver"))
|
||||||
|
|
||||||
def onBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||||
"""
|
"""
|
||||||
Notify bitmap update
|
Notify bitmap update
|
||||||
@param destLeft: xmin position
|
@param destLeft: xmin position
|
||||||
@@ -285,4 +302,4 @@ class RDPClientObserver(object):
|
|||||||
@param isCompress: use RLE compression
|
@param isCompress: use RLE compression
|
||||||
@param data: bitmap data
|
@param data: bitmap data
|
||||||
"""
|
"""
|
||||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onBitmapUpdate", "RDPClientObserver"))
|
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "RDPClientObserver"))
|
||||||
@@ -140,8 +140,7 @@ class TPDU(LayerAutomata, StreamSender):
|
|||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
"""
|
"""
|
||||||
connection request
|
Connection request for client send a connection request packet
|
||||||
for client send a connection request packet
|
|
||||||
"""
|
"""
|
||||||
if self._mode == LayerMode.CLIENT:
|
if self._mode == LayerMode.CLIENT:
|
||||||
self.sendConnectionRequest()
|
self.sendConnectionRequest()
|
||||||
@@ -150,9 +149,9 @@ class TPDU(LayerAutomata, StreamSender):
|
|||||||
|
|
||||||
def recvConnectionConfirm(self, data):
|
def recvConnectionConfirm(self, data):
|
||||||
"""
|
"""
|
||||||
receive connection confirm message
|
Receive connection confirm message
|
||||||
next state is recvData
|
Next state is recvData
|
||||||
call connect on presentation layer if all is good
|
Call connect on presentation layer if all is good
|
||||||
@param data: Stream that contain connection confirm
|
@param data: Stream that contain connection confirm
|
||||||
@see: response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx
|
@see: response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx
|
||||||
@see: failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx
|
@see: failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx
|
||||||
@@ -170,14 +169,16 @@ class TPDU(LayerAutomata, StreamSender):
|
|||||||
self._selectedProtocol = message.protocolNeg.selectedProtocol.value
|
self._selectedProtocol = message.protocolNeg.selectedProtocol.value
|
||||||
|
|
||||||
if self._selectedProtocol != Protocols.PROTOCOL_SSL:
|
if self._selectedProtocol != Protocols.PROTOCOL_SSL:
|
||||||
raise InvalidExpectedDataException("only ssl protocol is supported in RDPY version")
|
raise InvalidExpectedDataException("only SSL protocol is supported in RDPY version")
|
||||||
|
|
||||||
#_transport is TPKT and transport is TCP layer of twisted
|
#_transport is TPKT and transport is TCP layer of twisted
|
||||||
self._transport.transport.startTLS(ClientTLSContext())
|
self._transport.transport.startTLS(ClientTLSContext())
|
||||||
|
|
||||||
|
#now i'm ready to receive data
|
||||||
self.setNextState(self.recvData)
|
self.setNextState(self.recvData)
|
||||||
|
|
||||||
#connection is done send to presentation
|
#connection is done send to presentation
|
||||||
self._presentation.connect(self)
|
self._presentation.connect()
|
||||||
|
|
||||||
def recvConnectionRequest(self, data):
|
def recvConnectionRequest(self, data):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
|||||||
"""
|
"""
|
||||||
self._controller.sendKeyEventUnicode(ord(unicode(e.text().toUtf8(), encoding="UTF-8")), isPressed)
|
self._controller.sendKeyEventUnicode(ord(unicode(e.text().toUtf8(), encoding="UTF-8")), isPressed)
|
||||||
|
|
||||||
def onBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||||
"""
|
"""
|
||||||
Notify bitmap update
|
Notify bitmap update
|
||||||
@param destLeft: xmin position
|
@param destLeft: xmin position
|
||||||
@@ -207,8 +207,19 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
|||||||
print "Receive image in bad format"
|
print "Receive image in bad format"
|
||||||
return
|
return
|
||||||
|
|
||||||
|
#if image need to be cut
|
||||||
|
#For bit alignement server may send more than image pixel
|
||||||
|
if width != destRight - destLeft + 1 or height != destBottom - destTop + 1:
|
||||||
|
image = image.copy(0, 0, destRight - destLeft + 1, destBottom - destTop + 1)
|
||||||
self._widget.notifyImage(destLeft, destTop, image)
|
self._widget.notifyImage(destLeft, destTop, image)
|
||||||
|
|
||||||
|
def onReady(self):
|
||||||
|
"""
|
||||||
|
Call when stack is ready
|
||||||
|
"""
|
||||||
|
#do something maybe a loader
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class QRemoteDesktop(QtGui.QWidget):
|
class QRemoteDesktop(QtGui.QWidget):
|
||||||
"""
|
"""
|
||||||
@@ -225,7 +236,7 @@ class QRemoteDesktop(QtGui.QWidget):
|
|||||||
#because we can update image only in paint
|
#because we can update image only in paint
|
||||||
#event function. When protocol receive image
|
#event function. When protocol receive image
|
||||||
#we will stock into refresh list
|
#we will stock into refresh list
|
||||||
#and in paiont event paint list of all refresh images
|
#and in paint event paint list of all refresh images
|
||||||
self._refresh = []
|
self._refresh = []
|
||||||
#bind mouse event
|
#bind mouse event
|
||||||
self.setMouseTracking(True)
|
self.setMouseTracking(True)
|
||||||
|
|||||||
Reference in New Issue
Block a user