Proxy works only in one direction + bug fix in deactive-reactive sequence input are available
This commit is contained in:
@@ -1,7 +1,29 @@
|
||||
#!/usr/bin/python
|
||||
'''
|
||||
@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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
RDP proxy recorder and spyer
|
||||
Proxy RDP protocol
|
||||
With admin account you can watch all currently session
|
||||
For special session you can record
|
||||
"""
|
||||
|
||||
import sys, os
|
||||
# Change path so we find rdpy
|
||||
@@ -11,23 +33,71 @@ from rdpy.protocol.rdp import rdp
|
||||
from twisted.internet import reactor
|
||||
|
||||
class ProxyServer(rdp.RDPServerObserver):
|
||||
"""
|
||||
Server side of proxy
|
||||
"""
|
||||
def __init__(self, controller):
|
||||
"""
|
||||
@param controller: RDPServerController
|
||||
"""
|
||||
rdp.RDPServerObserver.__init__(self, controller)
|
||||
self._client = None
|
||||
|
||||
def onReady(self):
|
||||
reactor.connectTCP("wav-glw-013", 3389, ProxyClientFactory(self))
|
||||
"""
|
||||
Event use to inform state of server stack
|
||||
Use to connect client
|
||||
"""
|
||||
width, height = self._controller.getScreen()
|
||||
reactor.connectTCP("wav-glw-013", 3389, ProxyClientFactory(self, width, height))
|
||||
|
||||
def clientConnected(self, client):
|
||||
print "ok"
|
||||
"""
|
||||
Event throw by client when it's ready
|
||||
@param client: ProxyClient
|
||||
"""
|
||||
self._client = client
|
||||
self._controller.setColorDepth(self._client._controller.getColorDepth())
|
||||
|
||||
class ProxyClient(rdp.RDPClientObserver):
|
||||
"""
|
||||
Client side of proxy
|
||||
"""
|
||||
def __init__(self, controller, server):
|
||||
"""
|
||||
@param controller: RDPClientObserver
|
||||
@param server: ProxyServer
|
||||
"""
|
||||
rdp.RDPClientObserver.__init__(self, controller)
|
||||
self._server = server
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
Event use to signal that RDP stack is ready
|
||||
Inform proxy server that i'm connected
|
||||
implement RDPClientObserver
|
||||
"""
|
||||
self._server.clientConnected(self)
|
||||
|
||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
pass
|
||||
"""
|
||||
Event use to inform bitmap update
|
||||
implement RDPClientObserver
|
||||
@param destLeft: xmin position
|
||||
@param destTop: ymin position
|
||||
@param destRight: xmax position because RDP can send bitmap with padding
|
||||
@param destBottom: ymax position because RDP can send bitmap with padding
|
||||
@param width: width of bitmap
|
||||
@param height: height of bitmap
|
||||
@param bitsPerPixel: number of bit per pixel
|
||||
@param isCompress: use RLE compression
|
||||
@param data: bitmap data
|
||||
"""
|
||||
self._server._controller.sendUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data)
|
||||
|
||||
class ProxyServerFactory(rdp.ServerFactory):
|
||||
def __init__(self):
|
||||
rdp.ServerFactory.__init__(self, "/home/speyrefitte/dev/certificate/rdpy.key", "/home/speyrefitte/dev/certificate/rdpy.crt")
|
||||
rdp.ServerFactory.__init__(self, "/home/speyrefitte/dev/certificate/rdpy.key", "/home/speyrefitte/dev/certificate/rdpy.crt", 16)
|
||||
|
||||
def buildObserver(self, controller):
|
||||
return ProxyServer(controller)
|
||||
@@ -42,10 +112,13 @@ class ProxyServerFactory(rdp.ServerFactory):
|
||||
pass
|
||||
|
||||
class ProxyClientFactory(rdp.ClientFactory):
|
||||
def __init__(self, server):
|
||||
def __init__(self, server, width, height):
|
||||
self._server = server
|
||||
self._width = width
|
||||
self._height = height
|
||||
|
||||
def buildObserver(self, controller):
|
||||
controller.setScreen(self._width, self._height)
|
||||
return ProxyClient(controller, self._server)
|
||||
|
||||
def startedConnecting(self, connector):
|
||||
|
||||
@@ -240,8 +240,8 @@ class ClientCoreData(CompositeType):
|
||||
self.postBeta2ColorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP)
|
||||
self.clientProductId = UInt16Le(1)
|
||||
self.serialNumber = UInt32Le(0)
|
||||
self.highColorDepth = UInt16Le(HighColor.HIGH_COLOR_24BPP)
|
||||
self.supportedColorDepths = UInt16Le(Support.RNS_UD_32BPP_SUPPORT | Support.RNS_UD_24BPP_SUPPORT | Support.RNS_UD_16BPP_SUPPORT)
|
||||
self.highColorDepth = UInt16Le(HighColor.HIGH_COLOR_15BPP)
|
||||
self.supportedColorDepths = UInt16Le(Support.RNS_UD_15BPP_SUPPORT)
|
||||
self.earlyCapabilityFlags = UInt16Le(CapabilityFlags.RNS_UD_CS_SUPPORT_ERRINFO_PDU)
|
||||
self.clientDigProductId = String("\x00"*64, readLen = UInt8(64))
|
||||
self.connectionType = UInt8()
|
||||
|
||||
@@ -372,7 +372,7 @@ class Client(MCSLayer):
|
||||
if (confirm != 0) and (channelId == Channel.MCS_GLOBAL_CHANNEL or channelId == self._userId):
|
||||
raise InvalidExpectedDataException("Server must confirm static channel")
|
||||
|
||||
if confirm ==0:
|
||||
if confirm == 0:
|
||||
serverNet = self._serverSettings.getBlock(gcc.MessageType.SC_NET)
|
||||
for i in range(0, serverNet.channelCount.value):
|
||||
if channelId == serverNet.channelIdArray._array[i].value:
|
||||
@@ -524,7 +524,8 @@ class Server(MCSLayer):
|
||||
|
||||
channelId = per.readInteger16(data)
|
||||
#actually algo support virtual channel but RDPY have no virtual channel
|
||||
self.sendChannelJoinConfirm(channelId, channelId in self._channels.keys() or channelId == self._userId)
|
||||
confirm = 0 if channelId in self._channels.keys() or channelId == self._userId else 1
|
||||
self.sendChannelJoinConfirm(channelId, confirm)
|
||||
self._nbChannelConfirmed += 1
|
||||
if self._nbChannelConfirmed == self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value + 2:
|
||||
self.allChannelConnected()
|
||||
|
||||
@@ -621,7 +621,7 @@ class DeactiveAllPDU(CompositeType):
|
||||
CompositeType.__init__(self)
|
||||
self.shareId = UInt32Le()
|
||||
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
|
||||
self.sourceDescriptor = String(readLen = self.lengthSourceDescriptor)
|
||||
self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor)
|
||||
|
||||
class DataPDU(CompositeType):
|
||||
"""
|
||||
@@ -849,8 +849,9 @@ class FastPathUpdatePDU(CompositeType):
|
||||
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))
|
||||
return (UInt16Le(FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP, constant = True), BitmapUpdateDataPDU(readLen = self.size - 2))
|
||||
else:
|
||||
log.debug("unknown Fast Path PDU update data type : %s"%hex(self.updateHeader.value & 0xf))
|
||||
return String()
|
||||
|
||||
if updateData is None:
|
||||
@@ -904,7 +905,12 @@ class BitmapCompressedDataHeader(CompositeType):
|
||||
Compressed header of bitmap
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240644.aspx
|
||||
"""
|
||||
def __init__(self, conditional = lambda:True):
|
||||
def __init__(self, bodySize = 0, scanWidth = 0, uncompressedSize = 0, conditional = lambda:True):
|
||||
"""
|
||||
@param bodySize: size of image body
|
||||
@param scanWidth: width of image
|
||||
@param uncompressedSize: size of uncompressed image
|
||||
"""
|
||||
CompositeType.__init__(self, conditional = conditional)
|
||||
self.cbCompFirstRowSize = UInt16Le(0x0000, constant = True)
|
||||
#compressed data size
|
||||
@@ -917,19 +923,29 @@ class BitmapData(CompositeType):
|
||||
"""
|
||||
Bitmap data here the screen capture
|
||||
"""
|
||||
def __init__(self):
|
||||
def __init__(self, destLeft = 0, destTop = 0, destRight = 0, destBottom = 0, width = 0, height = 0, bitsPerPixel = 0, bitmapDataStream = ""):
|
||||
"""
|
||||
@param destLeft: destination left coordinate
|
||||
@param destTop: destination top coordinate
|
||||
@param destRight: destination right coordinate
|
||||
@param destBottom: destination bottom coordinate
|
||||
@param width: width of image
|
||||
@param height: height of image
|
||||
@param bitsPerPixel: color depth
|
||||
@param bitmapDataStream: data
|
||||
"""
|
||||
CompositeType.__init__(self)
|
||||
self.destLeft = UInt16Le()
|
||||
self.destTop = UInt16Le()
|
||||
self.destRight = UInt16Le()
|
||||
self.destBottom = UInt16Le()
|
||||
self.width = UInt16Le()
|
||||
self.height = UInt16Le()
|
||||
self.bitsPerPixel = UInt16Le()
|
||||
self.destLeft = UInt16Le(destLeft)
|
||||
self.destTop = UInt16Le(destTop)
|
||||
self.destRight = UInt16Le(destRight)
|
||||
self.destBottom = UInt16Le(destBottom)
|
||||
self.width = UInt16Le(width)
|
||||
self.height = UInt16Le(height)
|
||||
self.bitsPerPixel = UInt16Le(bitsPerPixel)
|
||||
self.flags = UInt16Le()
|
||||
self.bitmapLength = UInt16Le()
|
||||
self.bitmapComprHdr = BitmapCompressedDataHeader(conditional = lambda:(not (self.flags.value | BitmapFlag.NO_BITMAP_COMPRESSION_HDR)))
|
||||
self.bitmapDataStream = String(readLen = UInt16Le(lambda:(self.bitmapLength.value if (self.flags.value | BitmapFlag.NO_BITMAP_COMPRESSION_HDR) else self.bitmapComprHdr.cbCompMainBodySize.value)))
|
||||
self.bitmapLength = UInt16Le(lambda:(sizeof(self.bitmapComprHdr) + sizeof(self.bitmapDataStream)))
|
||||
self.bitmapComprHdr = BitmapCompressedDataHeader(bodySize = lambda:sizeof(self.bitmapDataStream), scanWidth = lambda:self.width.value, uncompressedSize = lambda:(self.width.value * self.height.value * self.bitsPerPixel.value), conditional = lambda:((self.flags.value | BitmapFlag.BITMAP_COMPRESSION) and not (self.flags.value | BitmapFlag.NO_BITMAP_COMPRESSION_HDR)))
|
||||
self.bitmapDataStream = String(bitmapDataStream, readLen = UInt16Le(lambda:(self.bitmapLength.value if (self.flags.value | BitmapFlag.NO_BITMAP_COMPRESSION_HDR) else self.bitmapComprHdr.cbCompMainBodySize.value)))
|
||||
|
||||
class SlowPathInputEvent(CompositeType):
|
||||
"""
|
||||
|
||||
@@ -107,12 +107,14 @@ class PDULayer(LayerAutomata):
|
||||
def sendPDU(self, pduMessage):
|
||||
"""
|
||||
Send a PDU data to transport layer
|
||||
@param pduMessage: PDU message
|
||||
"""
|
||||
self._transport.send(data.PDU(self._transport.getUserId(), pduMessage))
|
||||
|
||||
def sendDataPDU(self, pduData):
|
||||
"""
|
||||
Send an PDUData to transport layer
|
||||
@param pduData: PDU data message
|
||||
"""
|
||||
self.sendPDU(data.DataPDU(pduData, self._shareId))
|
||||
|
||||
@@ -183,7 +185,10 @@ class Client(PDULayer, tpkt.FastPathListener):
|
||||
s.readType(pdu)
|
||||
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DEMANDACTIVEPDU:
|
||||
raise InvalidExpectedDataException("Expected Demand Active PDU from server")
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
log.debug("Ignore message type %s during connection sequence"%hex(pdu.shareControlHeader.pduType.value))
|
||||
return
|
||||
|
||||
self._shareId = pdu.pduMessage.shareId.value
|
||||
|
||||
@@ -204,7 +209,11 @@ class Client(PDULayer, tpkt.FastPathListener):
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_SYNCHRONIZE:
|
||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected synchronizePDU")
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
log.debug("Ignore message type %s during connection sequence"%hex(pdu.shareControlHeader.pduType.value))
|
||||
return
|
||||
|
||||
self.setNextState(self.recvServerControlCooperatePDU)
|
||||
|
||||
def recvServerControlCooperatePDU(self, s):
|
||||
@@ -216,7 +225,11 @@ class Client(PDULayer, tpkt.FastPathListener):
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_COOPERATE:
|
||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU")
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
log.debug("Ignore message type %s during connection sequence"%hex(pdu.shareControlHeader.pduType.value))
|
||||
return
|
||||
|
||||
self.setNextState(self.recvServerControlGrantedPDU)
|
||||
|
||||
def recvServerControlGrantedPDU(self, s):
|
||||
@@ -228,7 +241,11 @@ class Client(PDULayer, tpkt.FastPathListener):
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_GRANTED_CONTROL:
|
||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU")
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
log.debug("Ignore message type %s during connection sequence"%hex(pdu.shareControlHeader.pduType.value))
|
||||
return
|
||||
|
||||
self.setNextState(self.recvServerFontMapPDU)
|
||||
|
||||
def recvServerFontMapPDU(self, s):
|
||||
@@ -240,7 +257,10 @@ class Client(PDULayer, tpkt.FastPathListener):
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_FONTMAP:
|
||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU")
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
log.debug("Ignore message type %s during connection sequence"%hex(pdu.shareControlHeader.pduType.value))
|
||||
return
|
||||
|
||||
self.setNextState(self.recvPDU)
|
||||
#here i'm connected
|
||||
@@ -421,7 +441,10 @@ class Server(PDULayer):
|
||||
s.readType(pdu)
|
||||
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_CONFIRMACTIVEPDU:
|
||||
raise InvalidExpectedDataException("Expected Confirm Active PDU from client")
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
log.debug("Ignore message type %s during connection sequence"%hex(pdu.shareControlHeader.pduType.value))
|
||||
return
|
||||
|
||||
for cap in pdu.pduMessage.capabilitySets._array:
|
||||
self._clientCapabilities[cap.capabilitySetType] = cap
|
||||
@@ -437,7 +460,10 @@ class Server(PDULayer):
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_SYNCHRONIZE:
|
||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected synchronizePDU")
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
log.debug("Ignore message type %s during connection sequence"%hex(pdu.shareControlHeader.pduType.value))
|
||||
return
|
||||
self.setNextState(self.recvClientControlCooperatePDU)
|
||||
|
||||
def recvClientControlCooperatePDU(self, s):
|
||||
@@ -449,7 +475,10 @@ class Server(PDULayer):
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_COOPERATE:
|
||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU")
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
log.debug("Ignore message type %s during connection sequence"%hex(pdu.shareControlHeader.pduType.value))
|
||||
return
|
||||
self.setNextState(self.recvClientControlRequestPDU)
|
||||
|
||||
def recvClientControlRequestPDU(self, s):
|
||||
@@ -461,7 +490,10 @@ class Server(PDULayer):
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_REQUEST_CONTROL:
|
||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU")
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
log.debug("Ignore message type %s during connection sequence"%hex(pdu.shareControlHeader.pduType.value))
|
||||
return
|
||||
self.setNextState(self.recvClientFontListPDU)
|
||||
|
||||
def recvClientFontListPDU(self, s):
|
||||
@@ -474,7 +506,10 @@ class Server(PDULayer):
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_FONTLIST:
|
||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU")
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send but ignored
|
||||
log.debug("Ignore message type %s during connection sequence"%hex(pdu.shareControlHeader.pduType.value))
|
||||
return
|
||||
|
||||
#finalize server
|
||||
self.sendServerFinalizeSynchronizePDU()
|
||||
@@ -519,13 +554,10 @@ class Server(PDULayer):
|
||||
generalCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
|
||||
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
|
||||
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT
|
||||
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
|
||||
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR
|
||||
|
||||
#init bitmap capability
|
||||
bitmapCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability
|
||||
bitmapCapability.preferredBitsPerPixel.value = 16
|
||||
bitmapCapability.desktopWidth.value = 800
|
||||
bitmapCapability.desktopHeight.value = 600
|
||||
inputCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_INPUT].capability
|
||||
inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX
|
||||
|
||||
demandActivePDU = data.DemandActivePDU()
|
||||
demandActivePDU.shareId.value = self._shareId
|
||||
@@ -551,4 +583,28 @@ class Server(PDULayer):
|
||||
|
||||
#deprecated font list pdu
|
||||
fontMapPDU = data.FontMapDataPDU()
|
||||
self.sendDataPDU(fontMapPDU)
|
||||
self.sendDataPDU(fontMapPDU)
|
||||
|
||||
def sendPDU(self, pduMessage):
|
||||
"""
|
||||
Send a PDU data to transport layer
|
||||
@param pduMessage: PDU message
|
||||
"""
|
||||
PDULayer.sendPDU(self, pduMessage)
|
||||
#restart capabilities exchange in case of deactive reactive sequence
|
||||
if isinstance(pduMessage, data.DeactiveAllPDU):
|
||||
self.sendDemandActivePDU()
|
||||
self.setNextState(self.recvConfirmActivePDU)
|
||||
|
||||
def sendBitmapUpdatePDU(self, bitmapDatas):
|
||||
"""
|
||||
Send bitmap update data
|
||||
@param bitmapDatas: List of data.BitmapData
|
||||
"""
|
||||
#check bitmap header for client that want it (very old client)
|
||||
if self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability.extraFlags.value & caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR:
|
||||
for bitmapData in bitmapDatas:
|
||||
bitmapData.flags.value |= data.BitmapFlag.NO_BITMAP_COMPRESSION_HDR
|
||||
updateDataPDU = data.BitmapUpdateDataPDU()
|
||||
updateDataPDU.rectangles._array = bitmapDatas
|
||||
self.sendDataPDU(data.UpdateDataPDU(updateDataPDU))
|
||||
@@ -25,6 +25,7 @@ from twisted.internet import protocol
|
||||
from rdpy.base.error import CallPureVirtualFuntion, InvalidValue
|
||||
import pdu.layer
|
||||
import pdu.data
|
||||
import pdu.caps
|
||||
import rdpy.base.log as log
|
||||
import tpkt, tpdu, mcs, gcc
|
||||
|
||||
@@ -45,6 +46,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer)
|
||||
#is pdu layer is ready to send
|
||||
self._isReady = False
|
||||
self._sendReady = False
|
||||
|
||||
def getProtocol(self):
|
||||
"""
|
||||
@@ -52,6 +54,12 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
In case of RDP TPKT is the Raw layer
|
||||
"""
|
||||
return self._tpktLayer
|
||||
|
||||
def getColorDepth(self):
|
||||
"""
|
||||
@return: color depth set by the server (15, 16, 24)
|
||||
"""
|
||||
return self._pduLayer._serverCapabilities[pdu.caps.CapsType.CAPSTYPE_BITMAP].capability.preferredBitsPerPixel.value
|
||||
|
||||
def setPerformanceSession(self):
|
||||
"""
|
||||
@@ -113,6 +121,9 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
Call when PDU layer is connected
|
||||
"""
|
||||
self._isReady = True
|
||||
if self._sendReady:
|
||||
return
|
||||
self._sendReady = False
|
||||
#signal all listener
|
||||
for observer in self._clientObserver:
|
||||
observer.onReady()
|
||||
@@ -206,11 +217,14 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
"""
|
||||
Controller use in server side mode
|
||||
"""
|
||||
def __init__(self, privateKeyFileName, certificateFileName):
|
||||
def __init__(self, privateKeyFileName, certificateFileName, colorDepth):
|
||||
"""
|
||||
@param privateKeyFileName: file contain server private key
|
||||
@param certficiateFileName: file that contain public key
|
||||
@param colorDepth: 15, 16, 24
|
||||
"""
|
||||
self._isReady = False
|
||||
self._sendReady = False
|
||||
#list of observer
|
||||
self._serverObserver = []
|
||||
#build RDP protocol stack
|
||||
@@ -221,8 +235,8 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
self._tpduLayer = tpdu.Server(self._mcsLayer, privateKeyFileName, certificateFileName)
|
||||
#transport packet (protocol layer)
|
||||
self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer)
|
||||
|
||||
self._isReady = False
|
||||
#set color depth of session
|
||||
self.setColorDepth(colorDepth)
|
||||
|
||||
def getProtocol(self):
|
||||
"""
|
||||
@@ -259,18 +273,40 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
"""
|
||||
return (self.getDomain(), self.getUsername(), self.getPassword())
|
||||
|
||||
def getScreen(self):
|
||||
"""
|
||||
@return: tuple(width, height) of client asked screen
|
||||
"""
|
||||
bitmapCap = self._pduLayer._clientCapabilities[pdu.caps.CapsType.CAPSTYPE_BITMAP].capability
|
||||
return (bitmapCap.desktopWidth.value, bitmapCap.desktopHeight.value)
|
||||
|
||||
def addServerObserver(self, observer):
|
||||
"""
|
||||
Add observer to RDP protocol
|
||||
@param observer: new observer to add
|
||||
"""
|
||||
self._serverObserver.append(observer)
|
||||
|
||||
def setColorDepth(self, colorDepth):
|
||||
"""
|
||||
Set color depth of session
|
||||
if PDU stack is already connected send a deactive-reactive sequence
|
||||
@param colorDepth: depth of session (15, 16, 24)
|
||||
"""
|
||||
self._pduLayer._serverCapabilities[pdu.caps.CapsType.CAPSTYPE_BITMAP].capability.preferredBitsPerPixel.value = colorDepth
|
||||
if self._isReady:
|
||||
#restart connection sequence
|
||||
self._isReady = False
|
||||
self._pduLayer.sendPDU(pdu.data.DeactiveAllPDU())
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
RDP stack is now ready
|
||||
"""
|
||||
self._isReady = True
|
||||
if self._sendReady:
|
||||
return
|
||||
self._sendReady = True
|
||||
for observer in self._serverObserver:
|
||||
observer.onReady()
|
||||
|
||||
@@ -289,6 +325,11 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
"""
|
||||
if not self._isReady:
|
||||
return
|
||||
bitmapData = pdu.data.BitmapData(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, data)
|
||||
if isCompress:
|
||||
bitmapData.flags.value = pdu.data.BitmapFlag.BITMAP_COMPRESSION
|
||||
|
||||
self._pduLayer.sendBitmapUpdatePDU([bitmapData])
|
||||
|
||||
class ClientFactory(protocol.Factory):
|
||||
"""
|
||||
@@ -314,20 +355,22 @@ class ServerFactory(protocol.Factory):
|
||||
"""
|
||||
Factory of Server RDP protocol
|
||||
"""
|
||||
def __init__(self, privateKeyFileName, certificateFileName):
|
||||
def __init__(self, privateKeyFileName, certificateFileName, colorDepth):
|
||||
"""
|
||||
@param privateKeyFileName: file contain server private key
|
||||
@param certficiateFileName: file that contain publi key
|
||||
@param certficiateFileName: file that contain public key
|
||||
@param colorDepth: color depth of session
|
||||
"""
|
||||
self._privateKeyFileName = privateKeyFileName
|
||||
self._certificateFileName = certificateFileName
|
||||
self._colorDepth = colorDepth
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
"""
|
||||
Function call from twisted and build rdp protocol stack
|
||||
@param addr: destination address
|
||||
"""
|
||||
controller = RDPServerController(self._privateKeyFileName, self._certificateFileName)
|
||||
controller = RDPServerController(self._privateKeyFileName, self._certificateFileName, self._colorDepth)
|
||||
self.buildObserver(controller)
|
||||
return controller.getProtocol()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user