fix bug + more license automata
This commit is contained in:
44
README.md
44
README.md
@@ -1,6 +1,9 @@
|
||||
# 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
|
||||
* python2.7
|
||||
@@ -8,16 +11,45 @@ Remote Desktop Protocol in Twisted Python
|
||||
* python-openssl
|
||||
* python-qt4
|
||||
* python-qt4reactor
|
||||
|
||||
## Requirements libs
|
||||
* python-sip-dev
|
||||
* scons
|
||||
|
||||
## Build
|
||||
```
|
||||
$ git clone https://github.com/citronneur/rdpy.git rdpy
|
||||
$ scons -C rdpy/lib install
|
||||
```
|
||||
|
||||
## 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
|
||||
```
|
||||
$ rdpy/bin/rdpy-rdpclient XXX.XXX.XXX.XXX 3389
|
||||
@@ -30,15 +62,15 @@ $ rdpy/bin/rdpy-vncclient XXX.XXX.XXX.XXX 5901
|
||||
|
||||
RDP Proxy
|
||||
```
|
||||
$ rdpy/bin/rdpy-vncclient XXX.XXX.XXX.XXX 5901
|
||||
$ rdpy/bin/rdpy-rdpproxy
|
||||
```
|
||||
|
||||
##Must be implemented before first release
|
||||
##Limitations
|
||||
* CreedSSP
|
||||
* Packet redirection
|
||||
* License
|
||||
* Most common orders
|
||||
* FastPath messages
|
||||
* Des VNC (using pyDes)
|
||||
* VNC server side
|
||||
|
||||
this project is still in progress.
|
||||
|
||||
@@ -443,10 +443,10 @@ class CompositeType(Type):
|
||||
readLen += sizeof(self.__dict__[name])
|
||||
|
||||
def __write__(self, s):
|
||||
'''
|
||||
call write on each ordered subtype
|
||||
"""
|
||||
Call write on each ordered sub type
|
||||
@param s: Stream
|
||||
'''
|
||||
"""
|
||||
for name in self._typeName:
|
||||
try:
|
||||
s.writeType(self.__dict__[name])
|
||||
@@ -818,21 +818,20 @@ class Stream(StringIO):
|
||||
value.write(self)
|
||||
|
||||
class ArrayType(Type):
|
||||
'''
|
||||
in write mode ArrayType is just list
|
||||
but in read mode it can be dynamic
|
||||
"""
|
||||
In write mode ArrayType is just list
|
||||
But in read mode it can be dynamic
|
||||
readLen may be dynamic
|
||||
'''
|
||||
"""
|
||||
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 init: init array
|
||||
@param readLen: number of element in sequence
|
||||
@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._typeFactory = typeFactory
|
||||
self._readLen = readLen
|
||||
@@ -841,10 +840,10 @@ class ArrayType(Type):
|
||||
self._array = init
|
||||
|
||||
def __read__(self, s):
|
||||
'''
|
||||
create new object and read it
|
||||
"""
|
||||
Create new object and read it
|
||||
@param s: Stream
|
||||
'''
|
||||
"""
|
||||
self._array = []
|
||||
for _ in range(0, self._readLen.value):
|
||||
element = self._typeFactory()
|
||||
@@ -852,21 +851,21 @@ class ArrayType(Type):
|
||||
self._array.append(element)
|
||||
|
||||
def __write__(self, s):
|
||||
'''
|
||||
just write array
|
||||
"""
|
||||
Just write array
|
||||
@param s: Stream
|
||||
'''
|
||||
"""
|
||||
s.writeType(self._array)
|
||||
|
||||
def __sizeof__(self):
|
||||
'''
|
||||
sizeof inner array
|
||||
'''
|
||||
"""
|
||||
Size of inner array
|
||||
"""
|
||||
return sizeof(self._array)
|
||||
|
||||
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):
|
||||
"""
|
||||
@@ -880,29 +879,43 @@ class FactoryType(Type):
|
||||
if not callable(factory):
|
||||
self._factory = lambda:factory
|
||||
|
||||
self._value = self._factory()
|
||||
self._value = None
|
||||
|
||||
def __read__(self, s):
|
||||
'''
|
||||
call factory and read it
|
||||
"""
|
||||
Call factory and write it
|
||||
@param s: Stream
|
||||
'''
|
||||
"""
|
||||
self._value = self._factory()
|
||||
s.readType(self._value)
|
||||
|
||||
def __write__(self, s):
|
||||
'''
|
||||
call factory and write elements
|
||||
"""
|
||||
Call factory and read it
|
||||
@param s: Stream
|
||||
'''
|
||||
"""
|
||||
self._value = self._factory()
|
||||
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):
|
||||
'''
|
||||
sizeof of object returned by factory
|
||||
'''
|
||||
return sizeof(self._factory())
|
||||
"""
|
||||
Size of of object returned by factory
|
||||
"""
|
||||
return sizeof(self._value)
|
||||
|
||||
def CheckValueOnRead(cls):
|
||||
'''
|
||||
@@ -923,7 +936,7 @@ def CheckValueOnRead(cls):
|
||||
|
||||
def hexDump(src, length=16):
|
||||
'''
|
||||
print hex representation of sr
|
||||
print hex representation of str
|
||||
@param src: string
|
||||
'''
|
||||
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))
|
||||
|
||||
if capability is None:
|
||||
capability = CapabilityFactory
|
||||
capability = FactoryType(CapabilityFactory)
|
||||
|
||||
self.capability = FactoryType(capability)
|
||||
self.capability = capability
|
||||
|
||||
class GeneralCapability(CompositeType):
|
||||
"""
|
||||
|
||||
@@ -22,7 +22,7 @@ RDP extended license
|
||||
@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):
|
||||
"""
|
||||
@@ -41,6 +41,7 @@ class MessageType(object):
|
||||
class ErrorCode(object):
|
||||
"""
|
||||
License error message code
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
"""
|
||||
ERR_INVALID_SERVER_CERTIFICATE = 0x00000001
|
||||
ERR_NO_LICENSE = 0x00000002
|
||||
@@ -55,41 +56,163 @@ class ErrorCode(object):
|
||||
class StateTransition(object):
|
||||
"""
|
||||
Automata state transition
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
"""
|
||||
ST_TOTAL_ABORT = 0x00000001
|
||||
ST_NO_TRANSITION = 0x00000002
|
||||
ST_RESET_PHASE_TO_START = 0x00000003
|
||||
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)
|
||||
self.wBlobType = UInt16Le()
|
||||
self.wBlobType = UInt16Le(blobType, constant = True)
|
||||
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):
|
||||
"""
|
||||
License error message
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
"""
|
||||
def __init__(self, conditional = lambda:True):
|
||||
CompositeType.__init__(self, conditional = conditional)
|
||||
self.dwErrorCode = 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):
|
||||
"""
|
||||
A license packet
|
||||
"""
|
||||
def __init__(self):
|
||||
def __init__(self, message = None):
|
||||
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
|
||||
self.bMsgtype = UInt8()
|
||||
self.bMsgtype = UInt8(lambda:(MessageTypeFactory()))
|
||||
self.flag = UInt8()
|
||||
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):
|
||||
"""
|
||||
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_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):
|
||||
"""
|
||||
@@ -635,9 +650,9 @@ class DataPDU(CompositeType):
|
||||
return String()
|
||||
|
||||
if pduData is None:
|
||||
pduData = PDUDataFactory
|
||||
pduData = FactoryType(PDUDataFactory)
|
||||
|
||||
self.pduData = FactoryType(pduData)
|
||||
self.pduData = pduData
|
||||
|
||||
class SynchronizeDataPDU(CompositeType):
|
||||
"""
|
||||
@@ -737,9 +752,9 @@ class UpdateDataPDU(CompositeType):
|
||||
return String()
|
||||
|
||||
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):
|
||||
"""
|
||||
@@ -766,9 +781,9 @@ class FastPathUpdatePDU(CompositeType):
|
||||
return String()
|
||||
|
||||
if updateData is None:
|
||||
updateData = UpdateDataFactory
|
||||
updateData = FactoryType(UpdateDataFactory)
|
||||
|
||||
self.updateData = FactoryType(updateData)
|
||||
self.updateData = updateData
|
||||
|
||||
class SynchronizeUpdatePDU(CompositeType):
|
||||
"""
|
||||
@@ -876,26 +891,21 @@ 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
|
||||
|
||||
self.messageType = UInt16Le(lambda:MessageTypeFactory(self.slowPathInputData._value))
|
||||
self.messageType = UInt16Le(lambda:MessageTypeFactory(self.slowPathInputData))
|
||||
|
||||
def SlowPathInputDataFactory():
|
||||
if self.messageType.value == InputMessageType.INPUT_EVENT_MOUSE:
|
||||
return PointerEvent()
|
||||
|
||||
if messageData is None:
|
||||
messageData = SlowPathInputDataFactory
|
||||
messageData = FactoryType(SlowPathInputDataFactory)
|
||||
|
||||
self.slowPathInputData = FactoryType(messageData)
|
||||
self.slowPathInputData = messageData
|
||||
|
||||
class PointerEvent(CompositeType):
|
||||
"""
|
||||
@@ -935,7 +945,13 @@ class PDUClientListener(object):
|
||||
"""
|
||||
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
|
||||
@param rectangles: [pdu.BitmapData] struct
|
||||
@@ -1012,9 +1028,6 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
||||
#share id between client and server
|
||||
self._shareId = 0
|
||||
|
||||
#determine if layer is connected
|
||||
self._isConnected = False
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
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
|
||||
@param data: Stream
|
||||
"""
|
||||
#license preambule
|
||||
securityFlag = UInt16Le()
|
||||
securityFlagHi = UInt16Le()
|
||||
data.readType((securityFlag, securityFlagHi))
|
||||
@@ -1053,13 +1067,14 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
||||
validClientPdu = lic.LicPacket()
|
||||
data.readType(validClientPdu)
|
||||
|
||||
if not validClientPdu.errorMessage._is_readed:
|
||||
raise InvalidExpectedDataException("Waiting valid client PDU : rdpy doesn't support licensing nego")
|
||||
|
||||
if not (validClientPdu.errorMessage.dwErrorCode.value == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.errorMessage.dwStateTransition.value == lic.StateTransition.ST_NO_TRANSITION):
|
||||
raise InvalidExpectedDataException("Server refuse licensing negotiation")
|
||||
|
||||
if validClientPdu.bMsgtype.value == lic.MessageType.ERROR_ALERT and validClientPdu.licensingMessage.dwErrorCode.value == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.licensingMessage.dwStateTransition.value == lic.StateTransition.ST_NO_TRANSITION:
|
||||
self.setNextState(self.recvDemandActivePDU)
|
||||
#not tested because i can't buy RDP license server
|
||||
elif validClientPdu.bMsgtype.value == lic.MessageType.LICENSE_REQUEST:
|
||||
newLicenseReq = lic.createNewLicenseRequest(validClientPdu.licensingMessage)
|
||||
self._transport.send((UInt16Le(SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), newLicenseReq))
|
||||
else:
|
||||
raise InvalidExpectedDataException("Not a valid license packet")
|
||||
|
||||
def readDataPDU(self, data):
|
||||
"""
|
||||
@@ -1073,9 +1088,9 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
||||
if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
|
||||
return dataPDU
|
||||
|
||||
message = "Unknown code %s"%hex(dataPDU.pduData._value.errorInfo.value)
|
||||
if ErrorInfo._MESSAGES_.has_key(dataPDU.pduData._value.errorInfo):
|
||||
message = ErrorInfo._MESSAGES_[dataPDU.pduData._value.errorInfo]
|
||||
message = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value)
|
||||
if ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo):
|
||||
message = ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
|
||||
|
||||
raise ErrorReportedFromPeer("Receive PDU Error info : %s"%message)
|
||||
|
||||
@@ -1114,7 +1129,7 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
||||
@param data: Stream from transport layer
|
||||
"""
|
||||
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")
|
||||
self.setNextState(self.recvServerControlGrantedPDU)
|
||||
|
||||
@@ -1124,7 +1139,7 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
||||
@param data: Stream from transport layer
|
||||
"""
|
||||
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")
|
||||
self.setNextState(self.recvServerFontMapPDU)
|
||||
|
||||
@@ -1138,7 +1153,7 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU")
|
||||
|
||||
#here i'm connected
|
||||
self._isConnected = True
|
||||
self._clientListener.onReady()
|
||||
self.setNextState(self.recvDataPDU)
|
||||
|
||||
def recvDataPDU(self, data):
|
||||
@@ -1147,8 +1162,8 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
||||
@param data: Stream from transport layer
|
||||
"""
|
||||
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)
|
||||
if dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_UPDATE and dataPDU.pduData.updateType.value == UpdateType.UPDATETYPE_BITMAP:
|
||||
self._clientListener.onUpdate(dataPDU.pduData.updateData.rectangles._array)
|
||||
|
||||
def recvFastPath(self, fastPathData):
|
||||
"""
|
||||
@@ -1159,30 +1174,30 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
|
||||
fastPathPDU = FastPathUpdatePDU()
|
||||
fastPathData.readType(fastPathPDU)
|
||||
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):
|
||||
"""
|
||||
Send all client capabilities
|
||||
"""
|
||||
#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.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT
|
||||
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
|
||||
|
||||
#init bitmap capability
|
||||
bitmapCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability._value
|
||||
bitmapCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability
|
||||
bitmapCapability.preferredBitsPerPixel = self._transport.getGCCClientSettings().core.highColorDepth
|
||||
bitmapCapability.desktopWidth = self._transport.getGCCClientSettings().core.desktopWidth
|
||||
bitmapCapability.desktopHeight = self._transport.getGCCClientSettings().core.desktopHeight
|
||||
|
||||
#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
|
||||
|
||||
#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.keyboardLayout = self._transport.getGCCClientSettings().core.kbdLayout
|
||||
inputCapability.keyboardType = self._transport.getGCCClientSettings().core.keyboardType
|
||||
|
||||
@@ -45,6 +45,9 @@ class RDPClientController(pdu.PDUClientListener):
|
||||
#transport packet (protocol layer)
|
||||
self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer)
|
||||
|
||||
#is pdu layer is ready to send
|
||||
self._isReady = False
|
||||
|
||||
def getProtocol(self):
|
||||
"""
|
||||
@return: return Protocol layer for twisted
|
||||
@@ -92,21 +95,30 @@ class RDPClientController(pdu.PDUClientListener):
|
||||
|
||||
def addClientObserver(self, observer):
|
||||
"""
|
||||
add observer to RDP protocol
|
||||
Add observer to RDP protocol
|
||||
@param observer: new observer to add
|
||||
"""
|
||||
self._clientObserver.append(observer)
|
||||
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
|
||||
"""
|
||||
for observer in self._clientObserver:
|
||||
#for each rectangle in update PDU
|
||||
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):
|
||||
"""
|
||||
@@ -116,7 +128,7 @@ class RDPClientController(pdu.PDUClientListener):
|
||||
@param button: 1 or 2 or 3
|
||||
@param isPressed: true if button is pressed or false if it's released
|
||||
"""
|
||||
if not self._pduLayer._isConnected:
|
||||
if not self._isReady:
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -149,7 +161,7 @@ class RDPClientController(pdu.PDUClientListener):
|
||||
@param code: scan code
|
||||
@param isPressed: True if key is pressed and false if it's released
|
||||
"""
|
||||
if not self._pduLayer._isConnected:
|
||||
if not self._isReady:
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -172,7 +184,7 @@ class RDPClientController(pdu.PDUClientListener):
|
||||
@param code: unicode
|
||||
@param isPressed: True if key is pressed and false if it's released
|
||||
"""
|
||||
if not self._pduLayer._isConnected:
|
||||
if not self._isReady:
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -271,8 +283,13 @@ class RDPClientObserver(object):
|
||||
self._controller = controller
|
||||
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
|
||||
@param destLeft: xmin position
|
||||
@@ -285,4 +302,4 @@ class RDPClientObserver(object):
|
||||
@param isCompress: use RLE compression
|
||||
@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):
|
||||
"""
|
||||
connection request
|
||||
for client send a connection request packet
|
||||
Connection request for client send a connection request packet
|
||||
"""
|
||||
if self._mode == LayerMode.CLIENT:
|
||||
self.sendConnectionRequest()
|
||||
@@ -150,9 +149,9 @@ class TPDU(LayerAutomata, StreamSender):
|
||||
|
||||
def recvConnectionConfirm(self, data):
|
||||
"""
|
||||
receive connection confirm message
|
||||
next state is recvData
|
||||
call connect on presentation layer if all is good
|
||||
Receive connection confirm message
|
||||
Next state is recvData
|
||||
Call connect on presentation layer if all is good
|
||||
@param data: Stream that contain connection confirm
|
||||
@see: response -> http://msdn.microsoft.com/en-us/library/cc240506.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
|
||||
|
||||
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
|
||||
self._transport.transport.startTLS(ClientTLSContext())
|
||||
|
||||
#now i'm ready to receive data
|
||||
self.setNextState(self.recvData)
|
||||
|
||||
#connection is done send to presentation
|
||||
self._presentation.connect(self)
|
||||
self._presentation.connect()
|
||||
|
||||
def recvConnectionRequest(self, data):
|
||||
"""
|
||||
|
||||
@@ -162,7 +162,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
"""
|
||||
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
|
||||
@param destLeft: xmin position
|
||||
@@ -207,8 +207,19 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
print "Receive image in bad format"
|
||||
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)
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
Call when stack is ready
|
||||
"""
|
||||
#do something maybe a loader
|
||||
pass
|
||||
|
||||
|
||||
class QRemoteDesktop(QtGui.QWidget):
|
||||
"""
|
||||
@@ -225,7 +236,7 @@ class QRemoteDesktop(QtGui.QWidget):
|
||||
#because we can update image only in paint
|
||||
#event function. When protocol receive image
|
||||
#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 = []
|
||||
#bind mouse event
|
||||
self.setMouseTracking(True)
|
||||
|
||||
Reference in New Issue
Block a user