code refactoring + proxy code
This commit is contained in:
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)))
|
||||
0
rdpy/protocol/rdp/pdu/__init__.py
Normal file
0
rdpy/protocol/rdp/pdu/__init__.py
Normal 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)
|
||||
554
rdpy/protocol/rdp/pdu/layer.py
Normal file
554
rdpy/protocol/rdp/pdu/layer.py
Normal 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)
|
||||
@@ -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"))
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user