code refactoring + proxy code

This commit is contained in:
speyrefitte
2014-07-23 09:39:40 +02:00
parent 3eecf6ace3
commit c71c4f46f4
13 changed files with 1139 additions and 921 deletions

View File

@@ -8,10 +8,46 @@ import sys, os
sys.path.insert(1, os.path.join(sys.path[0], '..'))
from rdpy.protocol.rdp import rdp
from twisted.internet import reactor
class TestServerFactory(rdp.ServerFactory):
class ProxyServer(rdp.RDPServerObserver):
def onReady(self):
reactor.connectTCP("wav-glw-013", 3389, ProxyClientFactory(self))
def clientConnected(self, client):
print "ok"
class ProxyClient(rdp.RDPClientObserver):
def __init__(self, controller, server):
rdp.RDPClientObserver.__init__(self, controller)
self._server = server
def onReady(self):
self._server.clientConnected(self)
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
pass
class ProxyServerFactory(rdp.ServerFactory):
def __init__(self):
rdp.ServerFactory.__init__(self, "/home/sylvain/dev/certificate/rdpy.key", "/home/sylvain/dev/certificate/rdpy.crt")
rdp.ServerFactory.__init__(self, "/home/speyrefitte/dev/certificate/rdpy.key", "/home/speyrefitte/dev/certificate/rdpy.crt")
def buildObserver(self, controller):
return ProxyServer(controller)
def startedConnecting(self, connector):
pass
def clientConnectionLost(self, connector, reason):
pass
def clientConnectionFailed(self, connector, reason):
pass
class ProxyClientFactory(rdp.ClientFactory):
def __init__(self, server):
self._server = server
def buildObserver(self, controller):
return ProxyClient(controller, self._server)
def startedConnecting(self, connector):
pass
@@ -22,6 +58,6 @@ class TestServerFactory(rdp.ServerFactory):
pass
if __name__ == '__main__':
from twisted.internet import reactor
reactor.listenTCP(33389, TestServerFactory())
reactor.listenTCP(33389, ProxyServerFactory())
reactor.run()

View File

@@ -25,12 +25,6 @@ RDPY use Layer Protocol design (like twisted)
from rdpy.base.error import CallPureVirtualFuntion
class LayerMode(object):
NONE = 0
SERVER = 1
CLIENT = 2
class StreamListener(object):
"""
Interface use to inform that we can handle receive stream
@@ -60,17 +54,14 @@ class Layer(object):
A simple double linked list with presentation and transport layer
and a subset of event (connect and close)
"""
def __init__(self, mode, presentation = None):
def __init__(self, presentation = None):
"""
@param mode: LayerMode use
@param presentation: presentation layer
"""
#presentation layer higher layer in model
self._presentation = presentation
#transport layer under layer in model
self._transport = None
#network layer mode
self._mode = mode
#auto set transport layer of own presentation layer
if not self._presentation is None:
self._presentation._transport = self
@@ -96,13 +87,12 @@ class LayerAutomata(Layer, StreamListener):
Layer with automata state
we can set next recv function used for Stream packet
"""
def __init__(self, mode, presentation = None):
def __init__(self, presentation = None):
"""
@param mode: LayerMode use
@param presentation: presentation Layer
"""
#call parent constructor
Layer.__init__(self, mode, presentation)
Layer.__init__(self, presentation)
def setNextState(self, callback = None):
"""
@@ -127,13 +117,12 @@ class RawLayer(protocol.Protocol, LayerAutomata, StreamSender):
allow this protocol to wait until expected size of packet
and use Layer automata to call next automata state
"""
def __init__(self, mode, presentation = None):
def __init__(self, presentation = None):
"""
@param mode: LayerMode use
@param presentation: presentation layer in layer list
"""
#call parent automata
LayerAutomata.__init__(self, mode, presentation)
LayerAutomata.__init__(self, presentation)
#data buffer received from twisted network layer
self._buffer = ""
#len of next packet pass to next state function

View File

@@ -665,19 +665,18 @@ class UInt24Le(SimpleType):
self.value = struct.unpack(self._structFormat, s.read(self._typeSize) + '\x00')[0]
class String(Type, CallableValue):
'''
"""
String network type
'''
"""
def __init__(self, value = "", readLen = None, conditional = lambda:True, optional = False, constant = False, unicode = False):
'''
constructor with new string
"""
@param value: python string use for inner value
@param readLen: length use to read in stream (SimpleType) if 0 read entire stream
@param conditional : function call before read or write type
@param optional: boolean check before read if there is still data in stream
@param constant: if true check any changement of object during reading
@param unicode: Encode and decode value as unicode
'''
"""
Type.__init__(self, conditional = conditional, optional = optional, constant = constant)
CallableValue.__init__(self, value)
#type use to know read length

View File

@@ -24,7 +24,7 @@ Each channel have a particular role.
The main channel is the graphical channel.
It exist channel for file system order, audio channel, clipboard etc...
"""
from rdpy.network.layer import LayerAutomata, StreamSender, Layer, LayerMode
from rdpy.network.layer import LayerAutomata, StreamSender, Layer
from rdpy.network.type import sizeof, Stream, UInt8, UInt16Le
from rdpy.base.error import InvalidExpectedDataException, InvalidValue, InvalidSize
from rdpy.protocol.rdp.ber import writeLength
@@ -59,7 +59,7 @@ class Channel:
MCS_GLOBAL_CHANNEL = 1003
MCS_USERCHANNEL_BASE = 1001
class MCS(LayerAutomata):
class MCSLayer(LayerAutomata):
"""
Multiple Channel Service layer
the main layer of RDP protocol
@@ -116,13 +116,14 @@ class MCS(LayerAutomata):
return self._mcs._serverSettings
def __init__(self, mode, presentation, virtualChannels = []):
def __init__(self, presentation, receiveOpcode, sendOpcode, virtualChannels = []):
"""
@param mode: mode of MCS layer
@param presentation: presentation layer
@param virtualChannels: list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)]
@param receiveOpcode: opcode check when receive data
@param sendOpcode: opcode use when send data
"""
LayerAutomata.__init__(self, mode, presentation)
LayerAutomata.__init__(self, presentation)
self._clientSettings = gcc.clientSettings()
self._serverSettings = gcc.serverSettings()
#default user Id
@@ -131,56 +132,10 @@ class MCS(LayerAutomata):
self._channels = {Channel.MCS_GLOBAL_CHANNEL: presentation}
#virtual channels
self._virtualChannels = virtualChannels
#nb join and confirm channel
self._nbJoinAndConfirmChannel = 0
self._isGlobalChannelRequested = False
self._isUserChannelRequested = False
#use to record already requested channel
self._channelIdsRequested = {}
def connect(self):
"""
Connection send for client mode
a write connect initial packet
"""
if self._mode == LayerMode.CLIENT:
self._clientSettings.getBlock(gcc.MessageType.CS_CORE).serverSelectedProtocol.value = self._transport._selectedProtocol
#ask for virtual channel
self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array = [x for (x, _) in self._virtualChannels]
#send connect initial
self.sendConnectInitial()
#next wait response
self.setNextState(self.recvConnectResponse)
else:
self._serverSettings.getBlock(gcc.MessageType.SC_CORE).clientRequestedProtocol.value = self._transport._requestedProtocol
self.setNextState(self.recvConnectInitial)
def connectNextChannel(self):
"""
Send sendChannelJoinRequest message on next disconnect channel
client automata function
"""
self.setNextState(self.recvChannelJoinConfirm)
#global channel
if not self._isGlobalChannelRequested:
self.sendChannelJoinRequest(Channel.MCS_GLOBAL_CHANNEL)
self._isGlobalChannelRequested = True
return
#user channel
if not self._isUserChannelRequested:
self.sendChannelJoinRequest(self._userId)
self._isUserChannelRequested = True
return
#static virtual channel
if self._nbJoinAndConfirmChannel < self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value:
channelId = self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array[self._nbJoinAndConfirmChannel]
self._nbJoinAndConfirmChannel += 1
self.sendChannelJoinRequest(channelId)
return
self.allChannelConnected()
#send opcode
self._sendOpcode = sendOpcode
#receive opcode
self._receiveOpcode = receiveOpcode
def allChannelConnected(self):
"""
@@ -193,252 +148,21 @@ class MCS(LayerAutomata):
#try connection on all requested channel
for (channelId, layer) in self._channels.iteritems():
#use proxy for each channel
layer._transport = MCS.MCSProxySender(self, channelId)
layer._transport = MCSLayer.MCSProxySender(self, channelId)
layer.connect()
def sendConnectInitial(self):
"""
Send connect initial packet
client automata function
"""
ccReq = gcc.writeConferenceCreateRequest(self._clientSettings)
ccReqStream = Stream()
ccReqStream.writeType(ccReq)
tmp = (ber.writeOctetstring("\x01"), ber.writeOctetstring("\x01"), ber.writeBoolean(True),
self.writeDomainParams(34, 2, 0, 0xffff),
self.writeDomainParams(1, 1, 1, 0x420),
self.writeDomainParams(0xffff, 0xfc17, 0xffff, 0xffff),
ber.writeOctetstring(ccReqStream.getvalue()))
self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_INITIAL, sizeof(tmp)), tmp))
def sendConnectResponse(self):
"""
Send connect response
server automata function
"""
ccReq = gcc.writeConferenceCreateResponse(self._serverSettings)
ccReqStream = Stream()
ccReqStream.writeType(ccReq)
tmp = (ber.writeEnumerated(0), ber.writeInteger(0), self.writeDomainParams(22, 3, 0, 0xfff8),
ber.writeOctetstring(ccReqStream.getvalue()))
self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_RESPONSE, sizeof(tmp)), tmp))
def sendErectDomainRequest(self):
"""
Send a formated erect domain request for RDP connection
client automata function
"""
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ERECT_DOMAIN_REQUEST)),
per.writeInteger(0),
per.writeInteger(0)))
def sendAttachUserRequest(self):
"""
Send a formated attach user request for RDP connection
client automata function
"""
self._transport.send(self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_REQUEST)))
def sendAttachUserConfirm(self):
"""
Send attach user confirm
server automata function
"""
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_CONFIRM), 2),
per.writeEnumerates(0),
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE)))
def sendChannelJoinRequest(self, channelId):
"""
Send a formated Channel join request from client to server
client automata function
@param channelId: id of channel requested
"""
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_REQUEST)),
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
per.writeInteger16(channelId)))
def sendChannelJoinConfirm(self, channelId, confirm):
"""
Send a confirm channel (or not) to client
@param channelId: id of channel
@param confirm: connection state
"""
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_CONFIRM), 2),
per.writeEnumerates(int(confirm)),
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
per.writeInteger16(channelId),
per.writeInteger16(channelId)))
def send(self, channelId, data):
"""
Specific send function for channelId
@param channelId: Channel use to send
@param data: message to send
"""
opcode = DomainMCSPDU.SEND_DATA_REQUEST if self._mode == LayerMode.CLIENT else DomainMCSPDU.SEND_DATA_INDICATION
self._transport.send((self.writeMCSPDUHeader(UInt8(opcode)),
self._transport.send((self.writeMCSPDUHeader(UInt8(self._sendOpcode)),
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
per.writeInteger16(channelId),
UInt8(0x70),
per.writeLength(sizeof(data)), data))
def recvConnectInitial(self, data):
"""
Receive MCS connect initial from client
server automata function
@param data: Stream
"""
ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_INITIAL))
ber.readOctetString(data)
ber.readOctetString(data)
if not ber.readBoolean(data):
raise InvalidExpectedDataException("invalid expected BER boolean tag")
self.readDomainParams(data)
self.readDomainParams(data)
self.readDomainParams(data)
self._clientSettings = gcc.readConferenceCreateRequest(Stream(ber.readOctetString(data)))
self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array = [UInt16Le(x + Channel.MCS_GLOBAL_CHANNEL) for x in range(1, len(self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array) + 1)]
self.sendConnectResponse()
self.setNextState(self.recvErectDomainRequest)
def recvConnectResponse(self, data):
"""
Receive MCS connect response from server
client automata function
@param data: Stream
"""
ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_RESPONSE))
ber.readEnumerated(data)
ber.readInteger(data)
self.readDomainParams(data)
if not ber.readUniversalTag(data, ber.Tag.BER_TAG_OCTET_STRING, False):
raise InvalidExpectedDataException("invalid expected BER tag")
gccRequestLength = ber.readLength(data)
if data.dataLen() != gccRequestLength:
raise InvalidSize("bad size of GCC request")
self._serverSettings = gcc.readConferenceCreateResponse(data)
#send domain request
self.sendErectDomainRequest()
#send attach user request
self.sendAttachUserRequest()
#now wait user confirm from server
self.setNextState(self.recvAttachUserConfirm)
def recvErectDomainRequest(self, data):
"""
Receive erect domain request
server automata function
@param data: Stream
"""
opcode = UInt8()
data.readType(opcode)
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ERECT_DOMAIN_REQUEST):
raise InvalidExpectedDataException("Invalid MCS PDU : ERECT_DOMAIN_REQUEST expected")
per.readInteger(data)
per.readInteger(data)
self.setNextState(self.recvAttachUserRequest)
def recvAttachUserRequest(self, data):
"""
Receive Attach user request
server automata function
@param data: Stream
"""
opcode = UInt8()
data.readType(opcode)
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_REQUEST):
raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_REQUEST expected")
self.sendAttachUserConfirm()
self.setNextState(self.recvChannelJoinRequest)
def recvAttachUserConfirm(self, data):
"""
Receive an attach user confirm
client automata function
@param data: Stream
"""
opcode = UInt8()
data.readType(opcode)
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_CONFIRM):
raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_CONFIRM expected")
if per.readEnumerates(data) != 0:
raise InvalidExpectedDataException("Server reject user")
self._userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
self.connectNextChannel()
def recvChannelJoinRequest(self, data):
"""
Receive for each client channel a request
server automata function
@param data: Stream
"""
opcode = UInt8()
data.readType(opcode)
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_REQUEST):
raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_REQUEST expected")
userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
if self._userId != userId:
raise InvalidExpectedDataException("Invalid MCS User Id")
channelId = per.readInteger16(data)
#TODO check if it's a virtual channel too
#actually algo support virtual channel but not RDPY
self.sendChannelJoinConfirm(channelId, channelId in self._channels.keys() or channelId == self._userId)
self._nbJoinAndConfirmChannel += 1
if self._nbJoinAndConfirmChannel == self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value + 2:
self.allChannelConnected()
def recvChannelJoinConfirm(self, data):
"""
Receive a channel join confirm from server
client automata function
@param data: Stream
"""
opcode = UInt8()
data.readType(opcode)
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_CONFIRM):
raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_CONFIRM expected")
confirm = per.readEnumerates(data)
userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
if self._userId != userId:
raise InvalidExpectedDataException("Invalid MCS User Id")
channelId = per.readInteger16(data)
#must confirm global channel and user channel
if (confirm != 0) and (channelId == Channel.MCS_GLOBAL_CHANNEL or channelId == self._userId):
raise InvalidExpectedDataException("Server must confirm static channel")
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:
self._channels[channelId] = self._virtualChannels[i][1]
self.connectNextChannel()
def recvData(self, data):
"""
Main receive method
@@ -453,11 +177,8 @@ class MCS(LayerAutomata):
return
#client case
elif self._mode == LayerMode.CLIENT and not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.SEND_DATA_INDICATION):
raise InvalidExpectedDataException("Invalid expected MCS opcode for server to client communication")
elif self._mode == LayerMode.SERVER and not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.SEND_DATA_REQUEST):
raise InvalidExpectedDataException("Invalid expected MCS opcode for client to server communication")
elif not self.readMCSPDUHeader(opcode.value, self._receiveOpcode):
raise InvalidExpectedDataException("Invalid expected MCS opcode receive data")
#server user id
per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
@@ -525,3 +246,317 @@ class MCS(LayerAutomata):
ber.readInteger(s)
return (max_channels, max_users, max_tokens, max_pdu_size)
class Client(MCSLayer):
"""
Client automata of multiple channel service layer
"""
def __init__(self, presentation, virtualChannels = []):
"""
@param presentation: presentation layer
@param virtualChannels: list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)]
"""
MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST, virtualChannels)
#use to know state of static channel
self._isGlobalChannelRequested = False
self._isUserChannelRequested = False
#nb channel requested
self._nbChannelRequested = 0
def connect(self):
"""
Connect message in client automata case
Send ConnectInitial
Wait ConnectResponse
"""
self._clientSettings.getBlock(gcc.MessageType.CS_CORE).serverSelectedProtocol.value = self._transport._selectedProtocol
#ask for virtual channel
self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array = [x for (x, _) in self._virtualChannels]
#send connect initial
self.sendConnectInitial()
#next wait response
self.setNextState(self.recvConnectResponse)
def connectNextChannel(self):
"""
Send sendChannelJoinRequest message on next disconnect channel
Send channel request or connect upper layer if all channels are connected
Wait channel confirm
"""
self.setNextState(self.recvChannelJoinConfirm)
#global channel
if not self._isGlobalChannelRequested:
self.sendChannelJoinRequest(Channel.MCS_GLOBAL_CHANNEL)
self._isGlobalChannelRequested = True
return
#user channel
if not self._isUserChannelRequested:
self.sendChannelJoinRequest(self._userId)
self._isUserChannelRequested = True
return
#static virtual channel
if self._nbChannelRequested < self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value:
channelId = self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array[self._nbChannelRequested]
self._nbChannelRequested += 1
self.sendChannelJoinRequest(channelId)
return
self.allChannelConnected()
def recvConnectResponse(self, data):
"""
Receive MCS connect response from server
Send Erect domain Request
Send Attach User Request
Wait Attach User Confirm
@param data: Stream
"""
ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_RESPONSE))
ber.readEnumerated(data)
ber.readInteger(data)
self.readDomainParams(data)
if not ber.readUniversalTag(data, ber.Tag.BER_TAG_OCTET_STRING, False):
raise InvalidExpectedDataException("invalid expected BER tag")
gccRequestLength = ber.readLength(data)
if data.dataLen() != gccRequestLength:
raise InvalidSize("bad size of GCC request")
self._serverSettings = gcc.readConferenceCreateResponse(data)
#send domain request
self.sendErectDomainRequest()
#send attach user request
self.sendAttachUserRequest()
#now wait user confirm from server
self.setNextState(self.recvAttachUserConfirm)
def recvAttachUserConfirm(self, data):
"""
Receive an attach user confirm
Send Connect Channel
@param data: Stream
"""
opcode = UInt8()
data.readType(opcode)
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_CONFIRM):
raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_CONFIRM expected")
if per.readEnumerates(data) != 0:
raise InvalidExpectedDataException("Server reject user")
self._userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
self.connectNextChannel()
def recvChannelJoinConfirm(self, data):
"""
Receive a channel join confirm from server
client automata function
@param data: Stream
"""
opcode = UInt8()
data.readType(opcode)
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_CONFIRM):
raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_CONFIRM expected")
confirm = per.readEnumerates(data)
userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
if self._userId != userId:
raise InvalidExpectedDataException("Invalid MCS User Id")
channelId = per.readInteger16(data)
#must confirm global channel and user channel
if (confirm != 0) and (channelId == Channel.MCS_GLOBAL_CHANNEL or channelId == self._userId):
raise InvalidExpectedDataException("Server must confirm static channel")
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:
self._channels[channelId] = self._virtualChannels[i][1]
self.connectNextChannel()
def sendConnectInitial(self):
"""
Send connect initial packet
client automata function
"""
ccReq = gcc.writeConferenceCreateRequest(self._clientSettings)
ccReqStream = Stream()
ccReqStream.writeType(ccReq)
tmp = (ber.writeOctetstring("\x01"), ber.writeOctetstring("\x01"), ber.writeBoolean(True),
self.writeDomainParams(34, 2, 0, 0xffff),
self.writeDomainParams(1, 1, 1, 0x420),
self.writeDomainParams(0xffff, 0xfc17, 0xffff, 0xffff),
ber.writeOctetstring(ccReqStream.getvalue()))
self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_INITIAL, sizeof(tmp)), tmp))
def sendErectDomainRequest(self):
"""
Send a formated erect domain request for RDP connection
"""
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ERECT_DOMAIN_REQUEST)),
per.writeInteger(0),
per.writeInteger(0)))
def sendAttachUserRequest(self):
"""
Send a formated attach user request for RDP connection
"""
self._transport.send(self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_REQUEST)))
def sendChannelJoinRequest(self, channelId):
"""
Send a formated Channel join request from client to server
client automata function
@param channelId: id of channel requested
"""
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_REQUEST)),
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
per.writeInteger16(channelId)))
class Server(MCSLayer):
"""
Server automata of multiple channel service layer
"""
def __init__(self, presentation, virtualChannels = []):
"""
@param presentation: presentation layer
@param virtualChannels: list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)]
"""
MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION, virtualChannels)
#nb channel requested
self._nbChannelConfirmed = 0
def connect(self):
"""
Connect message for server automata
Wait Connect Initial
"""
self._serverSettings.getBlock(gcc.MessageType.SC_CORE).clientRequestedProtocol.value = self._transport._requestedProtocol
self.setNextState(self.recvConnectInitial)
def recvConnectInitial(self, data):
"""
Receive MCS connect initial from client
Send Connect Response
Wait Erect Domain Request
@param data: Stream
"""
ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_INITIAL))
ber.readOctetString(data)
ber.readOctetString(data)
if not ber.readBoolean(data):
raise InvalidExpectedDataException("invalid expected BER boolean tag")
self.readDomainParams(data)
self.readDomainParams(data)
self.readDomainParams(data)
self._clientSettings = gcc.readConferenceCreateRequest(Stream(ber.readOctetString(data)))
i = 1
for channelDef in self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array:
self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array.append(UInt16Le(i + Channel.MCS_GLOBAL_CHANNEL))
#if channel can be handle by serve add it
for serverChannelDef, layer in self._virtualChannels:
if channelDef.name == serverChannelDef.name:
self._channels[i + Channel.MCS_GLOBAL_CHANNEL] = layer
i += 1
self.sendConnectResponse()
self.setNextState(self.recvErectDomainRequest)
def recvErectDomainRequest(self, data):
"""
Receive erect domain request
Wait Attach User Request
@param data: Stream
"""
opcode = UInt8()
data.readType(opcode)
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ERECT_DOMAIN_REQUEST):
raise InvalidExpectedDataException("Invalid MCS PDU : ERECT_DOMAIN_REQUEST expected")
per.readInteger(data)
per.readInteger(data)
self.setNextState(self.recvAttachUserRequest)
def recvAttachUserRequest(self, data):
"""
Receive Attach user request
Send Attach User Confirm
Wait Channel Join Request
@param data: Stream
"""
opcode = UInt8()
data.readType(opcode)
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_REQUEST):
raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_REQUEST expected")
self.sendAttachUserConfirm()
self.setNextState(self.recvChannelJoinRequest)
def recvChannelJoinRequest(self, data):
"""
Receive for each client channel a request
Send Channel Join Confirm or Connect upper layer when all channel are joined
@param data: Stream
"""
opcode = UInt8()
data.readType(opcode)
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_REQUEST):
raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_REQUEST expected")
userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
if self._userId != userId:
raise InvalidExpectedDataException("Invalid MCS User Id")
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)
self._nbChannelConfirmed += 1
if self._nbChannelConfirmed == self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value + 2:
self.allChannelConnected()
def sendConnectResponse(self):
"""
Send connect response
"""
ccReq = gcc.writeConferenceCreateResponse(self._serverSettings)
ccReqStream = Stream()
ccReqStream.writeType(ccReq)
tmp = (ber.writeEnumerated(0), ber.writeInteger(0), self.writeDomainParams(22, 3, 0, 0xfff8),
ber.writeOctetstring(ccReqStream.getvalue()))
self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_RESPONSE, sizeof(tmp)), tmp))
def sendAttachUserConfirm(self):
"""
Send attach user confirm
"""
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_CONFIRM), 2),
per.writeEnumerates(0),
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE)))
def sendChannelJoinConfirm(self, channelId, confirm):
"""
Send a confirm channel (or not) to client
@param channelId: id of channel
@param confirm: connection state
"""
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_CONFIRM), 2),
per.writeEnumerates(int(confirm)),
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
per.writeInteger16(channelId),
per.writeInteger16(channelId)))

View File

View File

@@ -22,12 +22,10 @@ Implement the main graphic layer
In this layer are managed all mains bitmap update orders end user inputs
"""
from rdpy.network.layer import LayerAutomata, LayerMode
from rdpy.network.type import CompositeType, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType
from rdpy.base.error import InvalidExpectedDataException, CallPureVirtualFuntion, InvalidType
from rdpy.base.error import InvalidExpectedDataException
import rdpy.base.log as log
import gcc, lic, caps, tpkt
import caps
class SecurityFlag(object):
"""
@@ -809,23 +807,29 @@ class UpdateDataPDU(CompositeType):
"""
_PDUTYPE2_ = PDUType2.PDUTYPE2_UPDATE
def __init__(self, updateType = 0, updateData = None, readLen = None):
def __init__(self, updateData = None, readLen = None):
"""
@param updateType: UpdateType macro
@param updateData: Update data PDU in accordance with updateType (BitmapUpdateDataPDU)
@param readLen: Max length to read
"""
CompositeType.__init__(self, readLen = readLen)
self.updateType = UInt16Le(updateType)
self.updateType = UInt16Le(lambda:updateData.__class__._UPDATE_TYPE_)
def UpdateDataFactory():
if self.updateType.value == UpdateType.UPDATETYPE_BITMAP:
return BitmapUpdateDataPDU()
else:
return String()
"""
Create object in accordance self.updateType value
"""
for c in [BitmapUpdateDataPDU]:
if self.updateType.value == c._UPDATE_TYPE_:
return c()
log.debug("unknown PDU update data type : %s"%hex(self.updateType.value))
return String()
if updateData is None:
updateData = FactoryType(UpdateDataFactory, conditional = lambda:(self.updateType.value != UpdateType.UPDATETYPE_SYNCHRONIZE))
elif not "_UPDATE_TYPE_" in updateData.__class__.__dict__:
raise InvalidExpectedDataException("Try to send an invalid data update PDU")
self.updateData = updateData
@@ -834,9 +838,9 @@ class FastPathUpdatePDU(CompositeType):
Fast path update PDU packet
@see: http://msdn.microsoft.com/en-us/library/cc240622.aspx
"""
def __init__(self, updateType = 0, updateData = None):
def __init__(self, updateData = None):
CompositeType.__init__(self)
self.updateHeader = UInt8(updateType)
self.updateHeader = UInt8(lambda:updateData.__class__._FASTPATH_UPDATE_TYPE_)
self.compressionFlags = UInt8(conditional = lambda:((self.updateHeader.value >> 4) & FastPathOutputCompression.FASTPATH_OUTPUT_COMPRESSION_USED))
self.size = UInt16Le()
@@ -851,6 +855,8 @@ class FastPathUpdatePDU(CompositeType):
if updateData is None:
updateData = FactoryType(UpdateDataFactory)
elif not "_FASTPATH_UPDATE_TYPE_" in updateData.__class__.__dict__:
raise InvalidExpectedDataException("Try to send an invalid fast path data update PDU")
self.updateData = updateData
@@ -859,6 +865,9 @@ class BitmapUpdateDataPDU(CompositeType):
PDU use to send raw bitmap compressed or not
@see: http://msdn.microsoft.com/en-us/library/dd306368.aspx
"""
_UPDATE_TYPE_ = UpdateType.UPDATETYPE_BITMAP
_FASTPATH_UPDATE_TYPE_ = FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP
def __init__(self, readLen = None):
"""
@param readLen: Max size of packet
@@ -984,468 +993,3 @@ class UnicodeKeyEvent(CompositeType):
self.keyboardFlags = UInt16Le()
self.unicode = UInt16Le()
self.pad2Octets = UInt16Le()
class PDUClientListener(object):
"""
Interface for PDU client automata listener
"""
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
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "PDUClientListener"))
def recvDstBltOrder(self, order):
"""
@param order: rectangle order
"""
pass
class PDUServerListener(object):
"""
Interface for PDU server automata listener
"""
pass
class PDULayer(LayerAutomata, tpkt.FastPathListener):
"""
Global channel for MCS that handle session
identification user, licensing management, and capabilities exchange
"""
def __init__(self, listener):
"""
@param listener: listener use to inform orders
"""
mode = None
if isinstance(listener, PDUClientListener):
mode = LayerMode.CLIENT
#set client listener
self._clientListener = listener
elif isinstance(listener, PDUServerListener):
mode = LayerMode.SERVER
else:
raise InvalidType("PDU Layer expect PDU(Client|Server)Listener as listener")
LayerAutomata.__init__(self, mode, None)
#logon info send from client to server
self._info = RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_CORE).rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS))
#server capabilities
self._serverCapabilities = {
caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()),
caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()),
caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()),
caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()),
caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()),
caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()),
caps.CapsType.CAPSTYPE_FONT : caps.Capability(caps.CapsType.CAPSTYPE_FONT, caps.FontCapability()),
caps.CapsType.CAPSTYPE_COLORCACHE : caps.Capability(caps.CapsType.CAPSTYPE_COLORCACHE, caps.ColorCacheCapability()),
caps.CapsType.CAPSTYPE_SHARE : caps.Capability(caps.CapsType.CAPSTYPE_SHARE, caps.ShareCapability())
}
#client capabilities
self._clientCapabilities = {
caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()),
caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()),
caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()),
caps.CapsType.CAPSTYPE_BITMAPCACHE : caps.Capability(caps.CapsType.CAPSTYPE_BITMAPCACHE, caps.BitmapCacheCapability()),
caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()),
caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()),
caps.CapsType.CAPSTYPE_BRUSH : caps.Capability(caps.CapsType.CAPSTYPE_BRUSH, caps.BrushCapability()),
caps.CapsType.CAPSTYPE_GLYPHCACHE : caps.Capability(caps.CapsType.CAPSTYPE_GLYPHCACHE, caps.GlyphCapability()),
caps.CapsType.CAPSTYPE_OFFSCREENCACHE : caps.Capability(caps.CapsType.CAPSTYPE_OFFSCREENCACHE, caps.OffscreenBitmapCacheCapability()),
caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()),
caps.CapsType.CAPSTYPE_SOUND : caps.Capability(caps.CapsType.CAPSTYPE_SOUND, caps.SoundCapability())
}
#share id between client and server
self._shareId = 0x103EA
def connect(self):
"""
Connect event in client mode send logon info
Next state receive license PDU
"""
if self._mode == LayerMode.CLIENT:
self.sendInfoPkt()
#next state is license info PDU
self.setNextState(self.recvLicenceInfo)
else:
self.setNextState(self.recvInfoPkt)
def close(self):
"""
Send PDU close packet and call close method on transport method
"""
self.sendDataPDU(ShutdownRequestPDU())
def recvInfoPkt(self, data):
"""
Receive info packet from client
Client credential
@param data: Stream
"""
securityFlag = UInt16Le()
securityFlagHi = UInt16Le()
data.readType((securityFlag, securityFlagHi))
if not (securityFlag.value & SecurityFlag.SEC_INFO_PKT):
raise InvalidExpectedDataException("Waiting info packet")
data.readType(self._info)
#next state send error license
self.sendLicensingErrorMessage()
self.sendDemandActivePDU()
self.setNextState(self.recvConfirmActivePDU)
def recvLicenceInfo(self, data):
"""
Read license info packet and check if is a valid client info
@param data: Stream
"""
#packet preambule
securityFlag = UInt16Le()
securityFlagHi = UInt16Le()
data.readType((securityFlag, securityFlagHi))
if not (securityFlag.value & SecurityFlag.SEC_LICENSE_PKT):
raise InvalidExpectedDataException("Waiting license packet")
validClientPdu = lic.LicPacket()
data.readType(validClientPdu)
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 recvDemandActivePDU(self, data):
"""
Receive demand active PDU which contains
Server capabilities. In this version of RDPY only
Restricted group of capabilities are used.
Send confirm active PDU
@param data: Stream
"""
pdu = PDU()
data.readType(pdu)
if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DEMANDACTIVEPDU:
raise InvalidExpectedDataException("Expected Demand Active PDU from server")
self._shareId = pdu.pduMessage.shareId.value
for cap in pdu.pduMessage.capabilitySets._array:
self._serverCapabilities[cap.capabilitySetType] = cap
self.sendConfirmActivePDU()
#send synchronize
self.sendClientFinalizeSynchronizePDU()
self.setNextState(self.recvServerSynchronizePDU)
def recvConfirmActivePDU(self, data):
"""
Receive confirm active PDU from client
Capabilities exchange
@param data: Stream
"""
pdu = PDU()
data.readType(pdu)
if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_CONFIRMACTIVEPDU:
raise InvalidExpectedDataException("Expected Confirm Active PDU from client")
for cap in pdu.pduMessage.capabilitySets._array:
self._clientCapabilities[cap.capabilitySetType] = cap
self.setNextState(self.recvClientSynchronizePDU)
def recvServerSynchronizePDU(self, data):
"""
Receive from server
@param data: Stream from transport layer
"""
pdu = PDU()
data.readType(pdu)
if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_SYNCHRONIZE:
raise InvalidExpectedDataException("Error in PDU layer automata : expected synchronizePDU")
self.setNextState(self.recvServerControlCooperatePDU)
def recvServerControlCooperatePDU(self, data):
"""
Receive control cooperate PDU from server
@param data: Stream from transport layer
"""
pdu = PDU()
data.readType(pdu)
if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != Action.CTRLACTION_COOPERATE:
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU")
self.setNextState(self.recvServerControlGrantedPDU)
def recvServerControlGrantedPDU(self, data):
"""
Receive last control PDU the granted control PDU
@param data: Stream from transport layer
"""
pdu = PDU()
data.readType(pdu)
if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != Action.CTRLACTION_GRANTED_CONTROL:
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU")
self.setNextState(self.recvServerFontMapPDU)
def recvServerFontMapPDU(self, data):
"""
Last useless connection packet from server to client
@param data: Stream from transport layer
"""
pdu = PDU()
data.readType(pdu)
if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_FONTMAP:
raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU")
#here i'm connected
self._clientListener.onReady()
self.setNextState(self.recvPDU)
def recvClientSynchronizePDU(self, data):
"""
Receive from client
@param data: Stream from transport layer
"""
pdu = PDU()
data.readType(pdu)
if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_SYNCHRONIZE:
raise InvalidExpectedDataException("Error in PDU layer automata : expected synchronizePDU")
self.setNextState(self.recvClientControlCooperatePDU)
def recvClientControlCooperatePDU(self, data):
"""
Receive control cooperate PDU from client
@param data: Stream from transport layer
"""
pdu = PDU()
data.readType(pdu)
if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != Action.CTRLACTION_COOPERATE:
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU")
self.setNextState(self.recvClientControlRequestPDU)
def recvClientControlRequestPDU(self, data):
"""
Receive last control PDU the request control PDU from client
@param data: Stream from transport layer
"""
pdu = PDU()
data.readType(pdu)
if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != Action.CTRLACTION_REQUEST_CONTROL:
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU")
self.setNextState(self.recvClientFontListPDU)
def recvClientFontListPDU(self, data):
"""
Last synchronize packet from client to server
@param data: Stream from transport layer
"""
pdu = PDU()
data.readType(pdu)
if pdu.shareControlHeader.pduType.value != PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_FONTLIST:
raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU")
#finalize server
self.sendServerFinalizeSynchronizePDU()
self.setNextState(self.recvPDU)
def recvPDU(self, data):
"""
Main receive function after connection sequence
@param data: Stream from transport layer
"""
pdu = PDU()
data.readType(pdu)
if pdu.shareControlHeader.pduType.value == PDUType.PDUTYPE_DATAPDU:
self.readDataPDU(pdu.pduMessage)
elif pdu.shareControlHeader.pduType.value == PDUType.PDUTYPE_DEACTIVATEALLPDU:
#use in deactivation-reactivation sequence
#next state is either a capabilities re exchange or disconnection
#http://msdn.microsoft.com/en-us/library/cc240454.aspx
self.setNextState(self.recvDemandActivePDU)
def recvFastPath(self, fastPathData):
"""
Implement FastPathListener interface
Fast path is needed by RDP 8.0
@param fastPathData: Stream that contain fast path data
"""
fastPathPDU = FastPathUpdatePDU()
fastPathData.readType(fastPathPDU)
if fastPathPDU.updateHeader.value == FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
self._clientListener.onUpdate(fastPathPDU.updateData[1].rectangles._array)
def readDataPDU(self, dataPDU):
"""
read a data PDU object
@param dataPDU: DataPDU object
"""
if dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
message = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value)
if ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo):
message = ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
log.error("INFO PDU : %s"%message)
elif dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
#may be an event to ask to user
self._transport.close()
elif dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_UPDATE:
self.readUpdateDataPDU(dataPDU.pduData)
def readUpdateDataPDU(self, updateDataPDU):
"""
Read an update data PDU message
dispatch update message
@param: UpdateDataPDU object
"""
if updateDataPDU.updateType.value == UpdateType.UPDATETYPE_BITMAP:
self._clientListener.onUpdate(updateDataPDU.updateData.rectangles._array)
def sendInfoPkt(self):
"""
Send a logon info packet
client automata message
"""
self._transport.send((UInt16Le(SecurityFlag.SEC_INFO_PKT), UInt16Le(), self._info))
def sendLicensingErrorMessage(self):
"""
Send a licensing error message
server automata message
"""
self._transport.send((UInt16Le(SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), lic.createValidClientLicensingErrorMessage()))
def sendDemandActivePDU(self):
"""
Send server capabilities
server automata PDU
"""
#init general capability
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
#init bitmap capability
bitmapCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability
bitmapCapability.preferredBitsPerPixel.value = 16
bitmapCapability.desktopWidth.value = 800
bitmapCapability.desktopHeight.value = 600
demandActivePDU = DemandActivePDU()
demandActivePDU.shareId.value = self._shareId
demandActivePDU.capabilitySets._array = self._serverCapabilities.values()
self.sendPDU(demandActivePDU)
def sendPDU(self, pduMessage):
"""
Send a PDU message to transport layer
"""
self._transport.send(PDU(self._transport.getUserId(), pduMessage))
def sendDataPDU(self, pduData):
"""
Send an PDUData to transport layer
"""
self.sendPDU(DataPDU(pduData, self._shareId))
def sendConfirmActivePDU(self):
"""
Send all client capabilities
"""
#init general capability
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
bitmapCapability.preferredBitsPerPixel = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).highColorDepth
bitmapCapability.desktopWidth = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).desktopWidth
bitmapCapability.desktopHeight = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).desktopHeight
#init order capability
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
inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE
inputCapability.keyboardLayout = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).kbdLayout
inputCapability.keyboardType = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardType
inputCapability.keyboardSubType = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardSubType
inputCapability.keyboardrFunctionKey = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardFnKeys
inputCapability.imeFileName = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).imeFileName
#make active PDU packet
confirmActivePDU = ConfirmActivePDU()
confirmActivePDU.shareId.value = self._shareId
confirmActivePDU.capabilitySets._array = self._clientCapabilities.values()
self.sendPDU(confirmActivePDU)
def sendClientFinalizeSynchronizePDU(self):
"""
send a synchronize PDU from client to server
"""
synchronizePDU = SynchronizeDataPDU(self._transport.getChannelId())
self.sendDataPDU(synchronizePDU)
#ask for cooperation
controlCooperatePDU = ControlDataPDU(Action.CTRLACTION_COOPERATE)
self.sendDataPDU(controlCooperatePDU)
#request control
controlRequestPDU = ControlDataPDU(Action.CTRLACTION_REQUEST_CONTROL)
self.sendDataPDU(controlRequestPDU)
#TODO persistent key list http://msdn.microsoft.com/en-us/library/cc240494.aspx
#deprecated font list pdu
fontListPDU = FontListDataPDU()
self.sendDataPDU(fontListPDU)
def sendServerFinalizeSynchronizePDU(self):
"""
Send last synchronize packet from server to client
"""
synchronizePDU = SynchronizeDataPDU(self._transport.getChannelId())
self.sendDataPDU(synchronizePDU)
#ask for cooperation
controlCooperatePDU = ControlDataPDU(Action.CTRLACTION_COOPERATE)
self.sendDataPDU(controlCooperatePDU)
#request control
controlRequestPDU = ControlDataPDU(Action.CTRLACTION_GRANTED_CONTROL)
self.sendDataPDU(controlRequestPDU)
#TODO persistent key list http://msdn.microsoft.com/en-us/library/cc240494.aspx
#deprecated font list pdu
fontMapPDU = FontMapDataPDU()
self.sendDataPDU(fontMapPDU)
def sendInputEvents(self, pointerEvents):
"""
send client input events
@param pointerEvents: list of pointer events
"""
pdu = ClientInputEventPDU()
pdu.slowPathInputEvents._array = [SlowPathInputEvent(x) for x in pointerEvents]
self.sendDataPDU(pdu)

View File

@@ -0,0 +1,554 @@
#
# 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/>.
#
"""
Implement the main graphic layer
In this layer are managed all mains bitmap update orders end user inputs
"""
from rdpy.network.layer import LayerAutomata
from rdpy.base.error import InvalidExpectedDataException, CallPureVirtualFuntion
from rdpy.network.type import UInt16Le
import rdpy.base.log as log
import rdpy.protocol.rdp.gcc as gcc
import rdpy.protocol.rdp.tpkt as tpkt
import lic, data, caps
class PDUClientListener(object):
"""
Interface for PDU client automata listener
"""
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
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "PDUClientListener"))
def recvDstBltOrder(self, order):
"""
@param order: rectangle order
"""
pass
class PDUServerListener(object):
"""
Interface for PDU server automata listener
"""
def onReady(self):
"""
Event call when PDU layer is ready to send update
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUServerListener"))
class PDULayer(LayerAutomata):
"""
Global channel for MCS that handle session
identification user, licensing management, and capabilities exchange
"""
def __init__(self):
LayerAutomata.__init__(self, None)
#logon info send from client to server
self._info = data.RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_CORE).rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS))
#server capabilities
self._serverCapabilities = {
caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()),
caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()),
caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()),
caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()),
caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()),
caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()),
caps.CapsType.CAPSTYPE_FONT : caps.Capability(caps.CapsType.CAPSTYPE_FONT, caps.FontCapability()),
caps.CapsType.CAPSTYPE_COLORCACHE : caps.Capability(caps.CapsType.CAPSTYPE_COLORCACHE, caps.ColorCacheCapability()),
caps.CapsType.CAPSTYPE_SHARE : caps.Capability(caps.CapsType.CAPSTYPE_SHARE, caps.ShareCapability())
}
#client capabilities
self._clientCapabilities = {
caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()),
caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()),
caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()),
caps.CapsType.CAPSTYPE_BITMAPCACHE : caps.Capability(caps.CapsType.CAPSTYPE_BITMAPCACHE, caps.BitmapCacheCapability()),
caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()),
caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()),
caps.CapsType.CAPSTYPE_BRUSH : caps.Capability(caps.CapsType.CAPSTYPE_BRUSH, caps.BrushCapability()),
caps.CapsType.CAPSTYPE_GLYPHCACHE : caps.Capability(caps.CapsType.CAPSTYPE_GLYPHCACHE, caps.GlyphCapability()),
caps.CapsType.CAPSTYPE_OFFSCREENCACHE : caps.Capability(caps.CapsType.CAPSTYPE_OFFSCREENCACHE, caps.OffscreenBitmapCacheCapability()),
caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()),
caps.CapsType.CAPSTYPE_SOUND : caps.Capability(caps.CapsType.CAPSTYPE_SOUND, caps.SoundCapability())
}
#share id between client and server
self._shareId = 0x103EA
def sendPDU(self, pduMessage):
"""
Send a PDU data to transport layer
"""
self._transport.send(data.PDU(self._transport.getUserId(), pduMessage))
def sendDataPDU(self, pduData):
"""
Send an PDUData to transport layer
"""
self.sendPDU(data.DataPDU(pduData, self._shareId))
class Client(PDULayer, tpkt.FastPathListener):
"""
Client automata of PDU layer
"""
def __init__(self, listener):
"""
@param listener: PDUClientListener
"""
PDULayer.__init__(self)
self._listener = listener
def connect(self):
"""
Connect message in client automata
Send INfo packet (credentials)
Wait License info
"""
self.sendInfoPkt()
#next state is license info PDU
self.setNextState(self.recvLicenceInfo)
def close(self):
"""
Send PDU close packet and call close method on transport method
"""
self.sendDataPDU(data.ShutdownRequestPDU())
def recvLicenceInfo(self, s):
"""
Read license info packet and check if is a valid client info
Wait Demand Active PDU
@param s: Stream
"""
#packet preambule
securityFlag = UInt16Le()
securityFlagHi = UInt16Le()
s.readType((securityFlag, securityFlagHi))
if not (securityFlag.value & data.SecurityFlag.SEC_LICENSE_PKT):
raise InvalidExpectedDataException("Waiting license packet")
validClientPdu = lic.LicPacket()
s.readType(validClientPdu)
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(data.SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), newLicenseReq))
else:
raise InvalidExpectedDataException("Not a valid license packet")
def recvDemandActivePDU(self, s):
"""
Receive demand active PDU which contains
Server capabilities. In this version of RDPY only
Restricted group of capabilities are used.
Send Confirm Active PDU
Send Finalize PDU
Wait Server Synchronize PDU
@param s: Stream
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DEMANDACTIVEPDU:
raise InvalidExpectedDataException("Expected Demand Active PDU from server")
self._shareId = pdu.pduMessage.shareId.value
for cap in pdu.pduMessage.capabilitySets._array:
self._serverCapabilities[cap.capabilitySetType] = cap
self.sendConfirmActivePDU()
#send synchronize
self.sendClientFinalizeSynchronizePDU()
self.setNextState(self.recvServerSynchronizePDU)
def recvServerSynchronizePDU(self, s):
"""
Receive from server
Wait Control Cooperate PDU
@param s: Stream from transport layer
"""
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")
self.setNextState(self.recvServerControlCooperatePDU)
def recvServerControlCooperatePDU(self, s):
"""
Receive control cooperate PDU from server
Wait Control Granted PDU
@param s: Stream from transport layer
"""
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")
self.setNextState(self.recvServerControlGrantedPDU)
def recvServerControlGrantedPDU(self, s):
"""
Receive last control PDU the granted control PDU
Wait Font map PDU
@param s: Stream from transport layer
"""
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")
self.setNextState(self.recvServerFontMapPDU)
def recvServerFontMapPDU(self, s):
"""
Last useless connection packet from server to client
Wait any PDU
@param s: Stream from transport layer
"""
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")
self.setNextState(self.recvPDU)
#here i'm connected
self._listener.onReady()
def recvPDU(self, s):
"""
Main receive function after connection sequence
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU:
self.readDataPDU(pdu.pduMessage)
elif pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DEACTIVATEALLPDU:
#use in deactivation-reactivation sequence
#next state is either a capabilities re exchange or disconnection
#http://msdn.microsoft.com/en-us/library/cc240454.aspx
self.setNextState(self.recvDemandActivePDU)
def recvFastPath(self, fastPathS):
"""
Implement FastPathListener interface
Fast path is needed by RDP 8.0
@param fastPathS: Stream that contain fast path data
"""
fastPathPDU = data.FastPathUpdatePDU()
fastPathS.readType(fastPathPDU)
if fastPathPDU.updateHeader.value == data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
self._listener.onUpdate(fastPathPDU.updateData[1].rectangles._array)
def readDataPDU(self, dataPDU):
"""
read a data PDU object
@param dataPDU: DataPDU object
"""
if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
errorMessage = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value)
if data.ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo):
errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
log.error("INFO PDU : %s"%errorMessage)
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
#may be an event to ask to user
self._transport.close()
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_UPDATE:
self.readUpdateDataPDU(dataPDU.pduData)
def readUpdateDataPDU(self, updateDataPDU):
"""
Read an update data PDU data
dispatch update data
@param: UpdateDataPDU object
"""
if updateDataPDU.updateType.value == data.UpdateType.UPDATETYPE_BITMAP:
self._listener.onUpdate(updateDataPDU.updateData.rectangles._array)
def sendInfoPkt(self):
"""
Send a logon info packet
client automata data
"""
self._transport.send((UInt16Le(data.SecurityFlag.SEC_INFO_PKT), UInt16Le(), self._info))
def sendConfirmActivePDU(self):
"""
Send all client capabilities
"""
#init general capability
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
bitmapCapability.preferredBitsPerPixel = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).highColorDepth
bitmapCapability.desktopWidth = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).desktopWidth
bitmapCapability.desktopHeight = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).desktopHeight
#init order capability
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
inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE
inputCapability.keyboardLayout = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).kbdLayout
inputCapability.keyboardType = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardType
inputCapability.keyboardSubType = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardSubType
inputCapability.keyboardrFunctionKey = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardFnKeys
inputCapability.imeFileName = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).imeFileName
#make active PDU packet
confirmActivePDU = data.ConfirmActivePDU()
confirmActivePDU.shareId.value = self._shareId
confirmActivePDU.capabilitySets._array = self._clientCapabilities.values()
self.sendPDU(confirmActivePDU)
def sendClientFinalizeSynchronizePDU(self):
"""
send a synchronize PDU from client to server
"""
synchronizePDU = data.SynchronizeDataPDU(self._transport.getChannelId())
self.sendDataPDU(synchronizePDU)
#ask for cooperation
controlCooperatePDU = data.ControlDataPDU(data.Action.CTRLACTION_COOPERATE)
self.sendDataPDU(controlCooperatePDU)
#request control
controlRequestPDU = data.ControlDataPDU(data.Action.CTRLACTION_REQUEST_CONTROL)
self.sendDataPDU(controlRequestPDU)
#TODO persistent key list http://msdn.microsoft.com/en-us/library/cc240494.aspx
#deprecated font list pdu
fontListPDU = data.FontListDataPDU()
self.sendDataPDU(fontListPDU)
def sendInputEvents(self, pointerEvents):
"""
send client input events
@param pointerEvents: list of pointer events
"""
pdu = data.ClientInputEventPDU()
pdu.slowPathInputEvents._array = [data.SlowPathInputEvent(x) for x in pointerEvents]
self.sendDataPDU(pdu)
class Server(PDULayer):
"""
Server Automata of PDU layer
"""
def __init__(self, listener):
"""
@param listener: PDUServerListener
"""
PDULayer.__init__(self)
self._listener = listener
def connect(self):
"""
Connect message for server automata
Wait Info Packet
"""
self.setNextState(self.recvInfoPkt)
def recvInfoPkt(self, s):
"""
Receive info packet from client
Client credentials
Send License valid error message
Send Demand Active PDU
Wait Confirm Active PDU
@param s: Stream
"""
securityFlag = UInt16Le()
securityFlagHi = UInt16Le()
s.readType((securityFlag, securityFlagHi))
if not (securityFlag.value & data.SecurityFlag.SEC_INFO_PKT):
raise InvalidExpectedDataException("Waiting info packet")
s.readType(self._info)
#next state send error license
self.sendLicensingErrorMessage()
self.sendDemandActivePDU()
self.setNextState(self.recvConfirmActivePDU)
def recvConfirmActivePDU(self, s):
"""
Receive confirm active PDU from client
Capabilities exchange
Wait Client Synchronize PDU
@param s: Stream
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_CONFIRMACTIVEPDU:
raise InvalidExpectedDataException("Expected Confirm Active PDU from client")
for cap in pdu.pduMessage.capabilitySets._array:
self._clientCapabilities[cap.capabilitySetType] = cap
self.setNextState(self.recvClientSynchronizePDU)
def recvClientSynchronizePDU(self, s):
"""
Receive from client
Wait Control Cooperate PDU
@param s: Stream from transport layer
"""
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")
self.setNextState(self.recvClientControlCooperatePDU)
def recvClientControlCooperatePDU(self, s):
"""
Receive control cooperate PDU from client
Wait Control Request PDU
@param s: Stream from transport layer
"""
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")
self.setNextState(self.recvClientControlRequestPDU)
def recvClientControlRequestPDU(self, s):
"""
Receive last control PDU the request control PDU from client
Wait Font List PDU
@param s: Stream from transport layer
"""
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")
self.setNextState(self.recvClientFontListPDU)
def recvClientFontListPDU(self, s):
"""
Last synchronize packet from client to server
Send Server Finalize PDUs
Wait any PDU
@param s: Stream from transport layer
"""
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")
#finalize server
self.sendServerFinalizeSynchronizePDU()
self.setNextState(self.recvPDU)
#now i'm ready
self._listener.onReady()
def recvPDU(self, s):
"""
Main receive function after connection sequence
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU:
self.readDataPDU(pdu.pduMessage)
def readDataPDU(self, dataPDU):
"""
read a data PDU object
@param dataPDU: DataPDU object
"""
if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
errorMessage = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value)
if data.ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo):
errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
log.error("INFO PDU : %s"%errorMessage)
def sendLicensingErrorMessage(self):
"""
Send a licensing error data
"""
self._transport.send((UInt16Le(data.SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), lic.createValidClientLicensingErrorMessage()))
def sendDemandActivePDU(self):
"""
Send server capabilities
server automata PDU
"""
#init general capability
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
#init bitmap capability
bitmapCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability
bitmapCapability.preferredBitsPerPixel.value = 16
bitmapCapability.desktopWidth.value = 800
bitmapCapability.desktopHeight.value = 600
demandActivePDU = data.DemandActivePDU()
demandActivePDU.shareId.value = self._shareId
demandActivePDU.capabilitySets._array = self._serverCapabilities.values()
self.sendPDU(demandActivePDU)
def sendServerFinalizeSynchronizePDU(self):
"""
Send last synchronize packet from server to client
"""
synchronizePDU = data.SynchronizeDataPDU(self._transport.getChannelId())
self.sendDataPDU(synchronizePDU)
#ask for cooperation
controlCooperatePDU = data.ControlDataPDU(data.Action.CTRLACTION_COOPERATE)
self.sendDataPDU(controlCooperatePDU)
#request control
controlRequestPDU = data.ControlDataPDU(data.Action.CTRLACTION_GRANTED_CONTROL)
self.sendDataPDU(controlRequestPDU)
#TODO persistent key list http://msdn.microsoft.com/en-us/library/cc240494.aspx
#deprecated font list pdu
fontMapPDU = data.FontMapDataPDU()
self.sendDataPDU(fontMapPDU)

View File

@@ -23,29 +23,26 @@ Use to manage RDP stack in twisted
from twisted.internet import protocol
from rdpy.base.error import CallPureVirtualFuntion, InvalidValue
from rdpy.network.layer import LayerMode
import pdu.layer
import pdu.data
import rdpy.base.log as log
import tpkt, tpdu, mcs, pdu, gcc
import tpkt, tpdu, mcs, gcc
class RDPClientController(pdu.PDUClientListener):
class RDPClientController(pdu.layer.PDUClientListener):
"""
Manage RDP stack as client
"""
def __init__(self):
"""
@param observer: observer
"""
#list of observer
self._clientObserver = []
#PDU layer
self._pduLayer = pdu.PDULayer(self)
self._pduLayer = pdu.layer.Client(self)
#multi channel service
self._mcsLayer = mcs.MCS(LayerMode.CLIENT, self._pduLayer)
self._mcsLayer = mcs.Client(self._pduLayer)
#transport pdu layer
self._tpduLayer = tpdu.createClient(self._mcsLayer)
self._tpduLayer = tpdu.Client(self._mcsLayer)
#transport packet (protocol layer)
self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer)
#is pdu layer is ready to send
self._isReady = False
@@ -60,7 +57,7 @@ class RDPClientController(pdu.PDUClientListener):
"""
Set particular flag in RDP stack to avoid wall-paper, theme, menu animation etc...
"""
self._pduLayer._info.extendedInfo.performanceFlags.value = pdu.PerfFlag.PERF_DISABLE_WALLPAPER | pdu.PerfFlag.PERF_DISABLE_MENUANIMATIONS | pdu.PerfFlag.PERF_DISABLE_CURSOR_SHADOW | pdu.PerfFlag.PERF_DISABLE_THEMING
self._pduLayer._info.extendedInfo.performanceFlags.value = pdu.data.PerfFlag.PERF_DISABLE_WALLPAPER | pdu.data.PerfFlag.PERF_DISABLE_MENUANIMATIONS | pdu.data.PerfFlag.PERF_DISABLE_CURSOR_SHADOW | pdu.data.PerfFlag.PERF_DISABLE_THEMING | pdu.data.PerfFlag.PERF_DISABLE_FULLWINDOWDRAG
def setScreen(self, width, height):
"""
@@ -100,7 +97,6 @@ class RDPClientController(pdu.PDUClientListener):
@param observer: new observer to add
"""
self._clientObserver.append(observer)
observer._clientListener = self
def onUpdate(self, rectangles):
"""
@@ -110,7 +106,7 @@ class RDPClientController(pdu.PDUClientListener):
for observer in self._clientObserver:
#for each rectangle in update PDU
for rectangle in rectangles:
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)
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.data.BitmapFlag.BITMAP_COMPRESSION, rectangle.bitmapDataStream.value)
def onReady(self):
"""
@@ -133,18 +129,18 @@ class RDPClientController(pdu.PDUClientListener):
return
try:
event = pdu.PointerEvent()
event = pdu.data.PointerEvent()
if isPressed:
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_DOWN
event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN
if button == 1:
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON1
event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON1
elif button == 2:
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON2
event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON2
elif button == 3:
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON3
event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON3
else:
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_MOVE
event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE
#position
event.xPos.value = x
@@ -166,12 +162,12 @@ class RDPClientController(pdu.PDUClientListener):
return
try:
event = pdu.ScancodeKeyEvent()
event = pdu.data.ScancodeKeyEvent()
event.keyCode.value = code
if isPressed:
event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_DOWN
event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_DOWN
else:
event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_RELEASE
event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE
#send event
self._pduLayer.sendInputEvents([event])
@@ -189,10 +185,10 @@ class RDPClientController(pdu.PDUClientListener):
return
try:
event = pdu.UnicodeKeyEvent()
event = pdu.data.UnicodeKeyEvent()
event.unicode.value = code
if not isPressed:
event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_RELEASE
event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE
#send event
self._pduLayer.sendInputEvents([event])
@@ -206,7 +202,7 @@ class RDPClientController(pdu.PDUClientListener):
"""
self._pduLayer.close()
class RDPServerController(pdu.PDUServerListener):
class RDPServerController(pdu.layer.PDUServerListener):
"""
Controller use in server side mode
"""
@@ -215,20 +211,84 @@ class RDPServerController(pdu.PDUServerListener):
@param privateKeyFileName: file contain server private key
@param certficiateFileName: file that contain public key
"""
self._pduLayer = pdu.PDULayer(self)
#list of observer
self._serverObserver = []
#build RDP protocol stack
self._pduLayer = pdu.layer.Server(self)
#multi channel service
self._mcsLayer = mcs.MCS(LayerMode.SERVER, self._pduLayer)
self._mcsLayer = mcs.Server(self._pduLayer)
#transport pdu layer
self._tpduLayer = tpdu.createServer(self._mcsLayer, privateKeyFileName, certificateFileName)
self._tpduLayer = tpdu.Server(self._mcsLayer, privateKeyFileName, certificateFileName)
#transport packet (protocol layer)
self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer)
self._isReady = False
def getProtocol(self):
"""
@return: the twisted protocol layer
in RDP case is TPKT layer
"""
return self._tpktLayer;
return self._tpktLayer
def getUsername(self):
"""
Must be call after on ready event else always empty string
@return: username send by client may be an empty string
"""
return self._pduLayer._info.userName.value
def getPassword(self):
"""
Must be call after on ready event else always empty string
@return: password send by client may be an empty string
"""
return self._pduLayer._info.password.value
def getDomain(self):
"""
Must be call after on ready event else always empty string
@return: domain send by client may be an empty string
"""
return self._pduLayer._info.domain.value
def getCredentials(self):
"""
Must be call after on ready event else always empty string
@return: tuple(domain, username, password)
"""
return (self.getDomain(), self.getUsername(), self.getPassword())
def addServerObserver(self, observer):
"""
Add observer to RDP protocol
@param observer: new observer to add
"""
self._serverObserver.append(observer)
def onReady(self):
"""
RDP stack is now ready
"""
self._isReady = True
for observer in self._serverObserver:
observer.onReady()
def sendUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
"""
send bitmap update
@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
"""
if not self._isReady:
return
class ClientFactory(protocol.Factory):
"""
@@ -268,9 +328,8 @@ class ServerFactory(protocol.Factory):
@param addr: destination address
"""
controller = RDPServerController(self._privateKeyFileName, self._certificateFileName)
self.buildObserver(controller)
return controller.getProtocol()
#pduLayer = pdu.PDU(pdu.PDUServerListener())
#return tpkt.TPKT(tpdu.createServer(mcs.createServer(pduLayer), self._privateKeyFileName, self._certificateFileName));
def buildObserver(self, controller):
"""
@@ -310,3 +369,20 @@ class RDPClientObserver(object):
@param data: bitmap data
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "RDPClientObserver"))
class RDPServerObserver(object):
"""
Class use to inform all RDP event handle by RDPY
"""
def __init__(self, controller):
"""
@param controller: RDP controller use to interact with protocol
"""
self._controller = controller
self._controller.addServerObserver(self)
def onReady(self):
"""
Stack is ready and connected
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPServerObserver"))

View File

@@ -24,7 +24,7 @@ This layer have main goal to negociate SSL transport
RDP basic security is not supported by RDPY (because is not a true security layer...)
"""
from rdpy.network.layer import LayerAutomata, LayerMode, StreamSender
from rdpy.network.layer import LayerAutomata, StreamSender
from rdpy.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof
from rdpy.base.error import InvalidExpectedDataException
@@ -107,17 +107,16 @@ class Negotiation(CompositeType):
self.selectedProtocol = UInt32Le(conditional = lambda: (self.code.value != NegociationType.TYPE_RDP_NEG_FAILURE))
self.failureCode = UInt32Le(conditional = lambda: (self.code.value == NegociationType.TYPE_RDP_NEG_FAILURE))
class TPDU(LayerAutomata, StreamSender):
class TPDULayer(LayerAutomata, StreamSender):
"""
TPDU layer management
there is an connection automata
"""
def __init__(self, mode, presentation):
def __init__(self, presentation):
"""
@param mode: automata mode (client or server)
@param presentation: upper layer, MCS layer in RDP case
"""
LayerAutomata.__init__(self, mode, presentation)
LayerAutomata.__init__(self, presentation)
#default selectedProtocol is SSl because is the only supported
#in this version of RDPY
#client requested selectedProtocol
@@ -125,27 +124,51 @@ class TPDU(LayerAutomata, StreamSender):
#server selected selectedProtocol
self._selectedProtocol = Protocols.PROTOCOL_SSL
#Server mode informations for TLS connection
self._serverPrivateKeyFileName = None
self._serverCertificateFileName = None
def recvData(self, data):
"""
Read data header from packet
And pass to presentation layer
@param data: Stream
"""
header = TPDUDataHeader()
data.readType(header)
self._presentation.recv(data)
def initTLSServerInfos(self, privateKeyFileName, certificateFileName):
def send(self, message):
"""
Initialize informations for SSL server connection
@param privateKeyFileName: file contain server private key
@param certficiateFileName: file that contain public key
Write message packet for TPDU layer
Add TPDU header
@param message: network.Type message
"""
self._serverPrivateKeyFileName = privateKeyFileName
self._serverCertificateFileName = certificateFileName
self._transport.send((TPDUDataHeader(), message))
class Client(TPDULayer):
"""
Client automata of TPDU layer
"""
def __init__(self, presentation):
"""
@param presentation: upper layer, MCS layer in RDP case
"""
TPDULayer.__init__(self, presentation)
def connect(self):
"""
Connection request for client send a connection request packet
"""
if self._mode == LayerMode.CLIENT:
self.sendConnectionRequest()
else:
self.setNextState(self.recvConnectionRequest)
self.sendConnectionRequest()
def sendConnectionRequest(self):
"""
Write connection request message
Next state is recvConnectionConfirm
@see: http://msdn.microsoft.com/en-us/library/cc240500.aspx
"""
message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_REQUEST)
message.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_REQ
message.protocolNeg.selectedProtocol.value = self._requestedProtocol
self._transport.send(message)
self.setNextState(self.recvConnectionConfirm)
def recvConnectionConfirm(self, data):
"""
@@ -180,6 +203,27 @@ class TPDU(LayerAutomata, StreamSender):
#connection is done send to presentation
self._presentation.connect()
class Server(TPDULayer):
"""
Server automata of TPDU layer
"""
def __init__(self, presentation, privateKeyFileName, certificateFileName):
"""
@param presentation: upper layer, MCS layer in RDP case
@param privateKeyFileName: file contain server private key
@param certficiateFileName: file that contain public key
"""
TPDULayer.__init__(self, presentation)
#Server mode informations for TLS connection
self._serverPrivateKeyFileName = privateKeyFileName
self._serverCertificateFileName = certificateFileName
def connect(self):
"""
Connection request for server wait connection request packet from client
"""
self.setNextState(self.recvConnectionRequest)
def recvConnectionRequest(self, data):
"""
Read connection confirm packet
@@ -207,28 +251,6 @@ class TPDU(LayerAutomata, StreamSender):
self._selectedProtocol = Protocols.PROTOCOL_SSL
self.sendConnectionConfirm()
def recvData(self, data):
"""
Read data header from packet
And pass to presentation layer
@param data: Stream
"""
header = TPDUDataHeader()
data.readType(header)
self._presentation.recv(data)
def sendConnectionRequest(self):
"""
Write connection request message
Next state is recvConnectionConfirm
@see: http://msdn.microsoft.com/en-us/library/cc240500.aspx
"""
message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_REQUEST)
message.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_REQ
message.protocolNeg.selectedProtocol.value = self._requestedProtocol
self._transport.send(message)
self.setNextState(self.recvConnectionConfirm)
def sendConnectionConfirm(self):
"""
Write connection confirm message
@@ -246,32 +268,6 @@ class TPDU(LayerAutomata, StreamSender):
self.setNextState(self.recvData)
self._presentation.connect()
def send(self, message):
"""
Write message packet for TPDU layer
Add TPDU header
@param message: network.Type message
"""
self._transport.send((TPDUDataHeader(), message))
def createClient(mcsLayer):
"""
Factory for client TPDU automata
@param mcsLayer: presentation layer of TPDU
"""
return TPDU(LayerMode.CLIENT, mcsLayer)
def createServer(mcsLayer, privateKeyFileName, certificateFileName):
"""
Factory for server TPDU automata
@param mcsLayer: presentation layer of TPDU
@param privateKeyFileName: file contain server private key
@param certficiateFileName: file that contain public key
"""
tpduLayer = TPDU(LayerMode.SERVER, mcsLayer)
tpduLayer.initTLSServerInfos(privateKeyFileName, certificateFileName)
return tpduLayer
#open ssl needed
from twisted.internet import ssl
from OpenSSL import SSL

View File

@@ -22,7 +22,7 @@ Transport packet layer implementation
Use to build correct size packet and handle slow path and fast path mode
"""
from rdpy.network.layer import RawLayer, LayerMode
from rdpy.network.layer import RawLayer
from rdpy.network.type import UInt8, UInt16Be, sizeof
from rdpy.base.error import CallPureVirtualFuntion
@@ -31,10 +31,10 @@ class FastPathListener(object):
Fast path packet listener
Usually PDU layer
"""
def recvFastPath(self, fastPathData):
def recvFastPath(self, fastPathS):
"""
Call when fast path packet is received
@param fastPathData: Stream
@param fastPathS: Stream
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "StreamListener"))
@@ -50,7 +50,7 @@ class TPKT(RawLayer):
"""
@param presentation: presentation layer, in RDP case is TPDU layer
"""
RawLayer.__init__(self, LayerMode.NONE, presentation)
RawLayer.__init__(self, presentation)
#last packet version read from header
self._lastPacketVersion = UInt8()
#length may be coded on more than 1 bytes

View File

@@ -27,9 +27,9 @@ Implement Remote FrameBuffer protocol use in VNC client and server
"""
from twisted.internet import protocol
from rdpy.network.layer import RawLayer, LayerMode
from rdpy.network.layer import RawLayer
from rdpy.network.type import UInt8, UInt16Be, UInt32Be, SInt32Be, String, CompositeType
from rdpy.base.error import InvalidValue, CallPureVirtualFuntion, InvalidType
from rdpy.base.error import InvalidValue, CallPureVirtualFuntion
class ProtocolVersion(object):
"""
@@ -170,18 +170,11 @@ class RFB(RawLayer):
"""
def __init__(self, listener):
"""
@param mode: LayerMode client or server
@param listener: listener use to inform new orders
"""
mode = None
if isinstance(listener, RFBClientListener):
mode = LayerMode.CLIENT
#set client listener
self._clientListener = listener
else:
raise InvalidType("RFB Layer expect RFBClientListener as listener")
RawLayer.__init__(self, mode)
RawLayer.__init__(self)
#set client listener
self._clientListener = listener
#useful for RFB protocol
self._callbackBody = None
#protocol version negotiated
@@ -239,12 +232,8 @@ class RFB(RawLayer):
"""
Call when transport layer connection is made
in Client mode -> wait protocol version
in Server mode -> send protocol version
"""
if self._mode == LayerMode.CLIENT:
self.expect(12, self.recvProtocolVersion)
else:
self.send(self._version)
self.expect(12, self.recvProtocolVersion)
def readProtocolVersion(self, data):
"""
@@ -550,7 +539,7 @@ class ClientFactory(protocol.Factory):
Function call by twisted on connection
@param addr: address where client try to connect
"""
controller = RFBController(LayerMode.CLIENT)
controller = RFBController()
self.buildObserver(controller)
return controller.getRFBLayer()