code refactoring + proxy code

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

View File

@@ -8,10 +8,46 @@ import sys, os
sys.path.insert(1, os.path.join(sys.path[0], '..')) sys.path.insert(1, os.path.join(sys.path[0], '..'))
from rdpy.protocol.rdp import rdp 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): 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): def startedConnecting(self, connector):
pass pass
@@ -22,6 +58,6 @@ class TestServerFactory(rdp.ServerFactory):
pass pass
if __name__ == '__main__': if __name__ == '__main__':
from twisted.internet import reactor
reactor.listenTCP(33389, TestServerFactory()) reactor.listenTCP(33389, ProxyServerFactory())
reactor.run() reactor.run()

View File

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

View File

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

View File

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

View File

View File

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

View File

@@ -0,0 +1,554 @@
#
# Copyright (c) 2014 Sylvain Peyrefitte
#
# This file is part of rdpy.
#
# rdpy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Implement the main graphic layer
In this layer are managed all mains bitmap update orders end user inputs
"""
from rdpy.network.layer import LayerAutomata
from rdpy.base.error import InvalidExpectedDataException, CallPureVirtualFuntion
from rdpy.network.type import UInt16Le
import rdpy.base.log as log
import rdpy.protocol.rdp.gcc as gcc
import rdpy.protocol.rdp.tpkt as tpkt
import lic, data, caps
class PDUClientListener(object):
"""
Interface for PDU client automata listener
"""
def onReady(self):
"""
Event call when PDU layer is ready to send events
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUClientListener"))
def onUpdate(self, rectangles):
"""
call when a bitmap data is received from update PDU
@param rectangles: [pdu.BitmapData] struct
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "PDUClientListener"))
def recvDstBltOrder(self, order):
"""
@param order: rectangle order
"""
pass
class PDUServerListener(object):
"""
Interface for PDU server automata listener
"""
def onReady(self):
"""
Event call when PDU layer is ready to send update
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUServerListener"))
class PDULayer(LayerAutomata):
"""
Global channel for MCS that handle session
identification user, licensing management, and capabilities exchange
"""
def __init__(self):
LayerAutomata.__init__(self, None)
#logon info send from client to server
self._info = data.RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_CORE).rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS))
#server capabilities
self._serverCapabilities = {
caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()),
caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()),
caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()),
caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()),
caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()),
caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()),
caps.CapsType.CAPSTYPE_FONT : caps.Capability(caps.CapsType.CAPSTYPE_FONT, caps.FontCapability()),
caps.CapsType.CAPSTYPE_COLORCACHE : caps.Capability(caps.CapsType.CAPSTYPE_COLORCACHE, caps.ColorCacheCapability()),
caps.CapsType.CAPSTYPE_SHARE : caps.Capability(caps.CapsType.CAPSTYPE_SHARE, caps.ShareCapability())
}
#client capabilities
self._clientCapabilities = {
caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()),
caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()),
caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()),
caps.CapsType.CAPSTYPE_BITMAPCACHE : caps.Capability(caps.CapsType.CAPSTYPE_BITMAPCACHE, caps.BitmapCacheCapability()),
caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()),
caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()),
caps.CapsType.CAPSTYPE_BRUSH : caps.Capability(caps.CapsType.CAPSTYPE_BRUSH, caps.BrushCapability()),
caps.CapsType.CAPSTYPE_GLYPHCACHE : caps.Capability(caps.CapsType.CAPSTYPE_GLYPHCACHE, caps.GlyphCapability()),
caps.CapsType.CAPSTYPE_OFFSCREENCACHE : caps.Capability(caps.CapsType.CAPSTYPE_OFFSCREENCACHE, caps.OffscreenBitmapCacheCapability()),
caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()),
caps.CapsType.CAPSTYPE_SOUND : caps.Capability(caps.CapsType.CAPSTYPE_SOUND, caps.SoundCapability())
}
#share id between client and server
self._shareId = 0x103EA
def sendPDU(self, pduMessage):
"""
Send a PDU data to transport layer
"""
self._transport.send(data.PDU(self._transport.getUserId(), pduMessage))
def sendDataPDU(self, pduData):
"""
Send an PDUData to transport layer
"""
self.sendPDU(data.DataPDU(pduData, self._shareId))
class Client(PDULayer, tpkt.FastPathListener):
"""
Client automata of PDU layer
"""
def __init__(self, listener):
"""
@param listener: PDUClientListener
"""
PDULayer.__init__(self)
self._listener = listener
def connect(self):
"""
Connect message in client automata
Send INfo packet (credentials)
Wait License info
"""
self.sendInfoPkt()
#next state is license info PDU
self.setNextState(self.recvLicenceInfo)
def close(self):
"""
Send PDU close packet and call close method on transport method
"""
self.sendDataPDU(data.ShutdownRequestPDU())
def recvLicenceInfo(self, s):
"""
Read license info packet and check if is a valid client info
Wait Demand Active PDU
@param s: Stream
"""
#packet preambule
securityFlag = UInt16Le()
securityFlagHi = UInt16Le()
s.readType((securityFlag, securityFlagHi))
if not (securityFlag.value & data.SecurityFlag.SEC_LICENSE_PKT):
raise InvalidExpectedDataException("Waiting license packet")
validClientPdu = lic.LicPacket()
s.readType(validClientPdu)
if validClientPdu.bMsgtype.value == lic.MessageType.ERROR_ALERT and validClientPdu.licensingMessage.dwErrorCode.value == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.licensingMessage.dwStateTransition.value == lic.StateTransition.ST_NO_TRANSITION:
self.setNextState(self.recvDemandActivePDU)
#not tested because i can't buy RDP license server
elif validClientPdu.bMsgtype.value == lic.MessageType.LICENSE_REQUEST:
newLicenseReq = lic.createNewLicenseRequest(validClientPdu.licensingMessage)
self._transport.send((UInt16Le(data.SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), newLicenseReq))
else:
raise InvalidExpectedDataException("Not a valid license packet")
def recvDemandActivePDU(self, s):
"""
Receive demand active PDU which contains
Server capabilities. In this version of RDPY only
Restricted group of capabilities are used.
Send Confirm Active PDU
Send Finalize PDU
Wait Server Synchronize PDU
@param s: Stream
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DEMANDACTIVEPDU:
raise InvalidExpectedDataException("Expected Demand Active PDU from server")
self._shareId = pdu.pduMessage.shareId.value
for cap in pdu.pduMessage.capabilitySets._array:
self._serverCapabilities[cap.capabilitySetType] = cap
self.sendConfirmActivePDU()
#send synchronize
self.sendClientFinalizeSynchronizePDU()
self.setNextState(self.recvServerSynchronizePDU)
def recvServerSynchronizePDU(self, s):
"""
Receive from server
Wait Control Cooperate PDU
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_SYNCHRONIZE:
raise InvalidExpectedDataException("Error in PDU layer automata : expected synchronizePDU")
self.setNextState(self.recvServerControlCooperatePDU)
def recvServerControlCooperatePDU(self, s):
"""
Receive control cooperate PDU from server
Wait Control Granted PDU
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_COOPERATE:
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU")
self.setNextState(self.recvServerControlGrantedPDU)
def recvServerControlGrantedPDU(self, s):
"""
Receive last control PDU the granted control PDU
Wait Font map PDU
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_GRANTED_CONTROL:
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU")
self.setNextState(self.recvServerFontMapPDU)
def recvServerFontMapPDU(self, s):
"""
Last useless connection packet from server to client
Wait any PDU
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_FONTMAP:
raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU")
self.setNextState(self.recvPDU)
#here i'm connected
self._listener.onReady()
def recvPDU(self, s):
"""
Main receive function after connection sequence
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU:
self.readDataPDU(pdu.pduMessage)
elif pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DEACTIVATEALLPDU:
#use in deactivation-reactivation sequence
#next state is either a capabilities re exchange or disconnection
#http://msdn.microsoft.com/en-us/library/cc240454.aspx
self.setNextState(self.recvDemandActivePDU)
def recvFastPath(self, fastPathS):
"""
Implement FastPathListener interface
Fast path is needed by RDP 8.0
@param fastPathS: Stream that contain fast path data
"""
fastPathPDU = data.FastPathUpdatePDU()
fastPathS.readType(fastPathPDU)
if fastPathPDU.updateHeader.value == data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
self._listener.onUpdate(fastPathPDU.updateData[1].rectangles._array)
def readDataPDU(self, dataPDU):
"""
read a data PDU object
@param dataPDU: DataPDU object
"""
if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
errorMessage = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value)
if data.ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo):
errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
log.error("INFO PDU : %s"%errorMessage)
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
#may be an event to ask to user
self._transport.close()
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_UPDATE:
self.readUpdateDataPDU(dataPDU.pduData)
def readUpdateDataPDU(self, updateDataPDU):
"""
Read an update data PDU data
dispatch update data
@param: UpdateDataPDU object
"""
if updateDataPDU.updateType.value == data.UpdateType.UPDATETYPE_BITMAP:
self._listener.onUpdate(updateDataPDU.updateData.rectangles._array)
def sendInfoPkt(self):
"""
Send a logon info packet
client automata data
"""
self._transport.send((UInt16Le(data.SecurityFlag.SEC_INFO_PKT), UInt16Le(), self._info))
def sendConfirmActivePDU(self):
"""
Send all client capabilities
"""
#init general capability
generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
#init bitmap capability
bitmapCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability
bitmapCapability.preferredBitsPerPixel = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).highColorDepth
bitmapCapability.desktopWidth = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).desktopWidth
bitmapCapability.desktopHeight = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).desktopHeight
#init order capability
orderCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].capability
orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT
#init input capability
inputCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_INPUT].capability
inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE
inputCapability.keyboardLayout = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).kbdLayout
inputCapability.keyboardType = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardType
inputCapability.keyboardSubType = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardSubType
inputCapability.keyboardrFunctionKey = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).keyboardFnKeys
inputCapability.imeFileName = self._transport.getGCCClientSettings().getBlock(gcc.MessageType.CS_CORE).imeFileName
#make active PDU packet
confirmActivePDU = data.ConfirmActivePDU()
confirmActivePDU.shareId.value = self._shareId
confirmActivePDU.capabilitySets._array = self._clientCapabilities.values()
self.sendPDU(confirmActivePDU)
def sendClientFinalizeSynchronizePDU(self):
"""
send a synchronize PDU from client to server
"""
synchronizePDU = data.SynchronizeDataPDU(self._transport.getChannelId())
self.sendDataPDU(synchronizePDU)
#ask for cooperation
controlCooperatePDU = data.ControlDataPDU(data.Action.CTRLACTION_COOPERATE)
self.sendDataPDU(controlCooperatePDU)
#request control
controlRequestPDU = data.ControlDataPDU(data.Action.CTRLACTION_REQUEST_CONTROL)
self.sendDataPDU(controlRequestPDU)
#TODO persistent key list http://msdn.microsoft.com/en-us/library/cc240494.aspx
#deprecated font list pdu
fontListPDU = data.FontListDataPDU()
self.sendDataPDU(fontListPDU)
def sendInputEvents(self, pointerEvents):
"""
send client input events
@param pointerEvents: list of pointer events
"""
pdu = data.ClientInputEventPDU()
pdu.slowPathInputEvents._array = [data.SlowPathInputEvent(x) for x in pointerEvents]
self.sendDataPDU(pdu)
class Server(PDULayer):
"""
Server Automata of PDU layer
"""
def __init__(self, listener):
"""
@param listener: PDUServerListener
"""
PDULayer.__init__(self)
self._listener = listener
def connect(self):
"""
Connect message for server automata
Wait Info Packet
"""
self.setNextState(self.recvInfoPkt)
def recvInfoPkt(self, s):
"""
Receive info packet from client
Client credentials
Send License valid error message
Send Demand Active PDU
Wait Confirm Active PDU
@param s: Stream
"""
securityFlag = UInt16Le()
securityFlagHi = UInt16Le()
s.readType((securityFlag, securityFlagHi))
if not (securityFlag.value & data.SecurityFlag.SEC_INFO_PKT):
raise InvalidExpectedDataException("Waiting info packet")
s.readType(self._info)
#next state send error license
self.sendLicensingErrorMessage()
self.sendDemandActivePDU()
self.setNextState(self.recvConfirmActivePDU)
def recvConfirmActivePDU(self, s):
"""
Receive confirm active PDU from client
Capabilities exchange
Wait Client Synchronize PDU
@param s: Stream
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_CONFIRMACTIVEPDU:
raise InvalidExpectedDataException("Expected Confirm Active PDU from client")
for cap in pdu.pduMessage.capabilitySets._array:
self._clientCapabilities[cap.capabilitySetType] = cap
self.setNextState(self.recvClientSynchronizePDU)
def recvClientSynchronizePDU(self, s):
"""
Receive from client
Wait Control Cooperate PDU
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_SYNCHRONIZE:
raise InvalidExpectedDataException("Error in PDU layer automata : expected synchronizePDU")
self.setNextState(self.recvClientControlCooperatePDU)
def recvClientControlCooperatePDU(self, s):
"""
Receive control cooperate PDU from client
Wait Control Request PDU
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_COOPERATE:
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU")
self.setNextState(self.recvClientControlRequestPDU)
def recvClientControlRequestPDU(self, s):
"""
Receive last control PDU the request control PDU from client
Wait Font List PDU
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_REQUEST_CONTROL:
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU")
self.setNextState(self.recvClientFontListPDU)
def recvClientFontListPDU(self, s):
"""
Last synchronize packet from client to server
Send Server Finalize PDUs
Wait any PDU
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_FONTLIST:
raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU")
#finalize server
self.sendServerFinalizeSynchronizePDU()
self.setNextState(self.recvPDU)
#now i'm ready
self._listener.onReady()
def recvPDU(self, s):
"""
Main receive function after connection sequence
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU:
self.readDataPDU(pdu.pduMessage)
def readDataPDU(self, dataPDU):
"""
read a data PDU object
@param dataPDU: DataPDU object
"""
if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
errorMessage = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value)
if data.ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo):
errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
log.error("INFO PDU : %s"%errorMessage)
def sendLicensingErrorMessage(self):
"""
Send a licensing error data
"""
self._transport.send((UInt16Le(data.SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), lic.createValidClientLicensingErrorMessage()))
def sendDemandActivePDU(self):
"""
Send server capabilities
server automata PDU
"""
#init general capability
generalCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
#init bitmap capability
bitmapCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability
bitmapCapability.preferredBitsPerPixel.value = 16
bitmapCapability.desktopWidth.value = 800
bitmapCapability.desktopHeight.value = 600
demandActivePDU = data.DemandActivePDU()
demandActivePDU.shareId.value = self._shareId
demandActivePDU.capabilitySets._array = self._serverCapabilities.values()
self.sendPDU(demandActivePDU)
def sendServerFinalizeSynchronizePDU(self):
"""
Send last synchronize packet from server to client
"""
synchronizePDU = data.SynchronizeDataPDU(self._transport.getChannelId())
self.sendDataPDU(synchronizePDU)
#ask for cooperation
controlCooperatePDU = data.ControlDataPDU(data.Action.CTRLACTION_COOPERATE)
self.sendDataPDU(controlCooperatePDU)
#request control
controlRequestPDU = data.ControlDataPDU(data.Action.CTRLACTION_GRANTED_CONTROL)
self.sendDataPDU(controlRequestPDU)
#TODO persistent key list http://msdn.microsoft.com/en-us/library/cc240494.aspx
#deprecated font list pdu
fontMapPDU = data.FontMapDataPDU()
self.sendDataPDU(fontMapPDU)

View File

@@ -23,29 +23,26 @@ Use to manage RDP stack in twisted
from twisted.internet import protocol from twisted.internet import protocol
from rdpy.base.error import CallPureVirtualFuntion, InvalidValue 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 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 Manage RDP stack as client
""" """
def __init__(self): def __init__(self):
"""
@param observer: observer
"""
#list of observer #list of observer
self._clientObserver = [] self._clientObserver = []
#PDU layer #PDU layer
self._pduLayer = pdu.PDULayer(self) self._pduLayer = pdu.layer.Client(self)
#multi channel service #multi channel service
self._mcsLayer = mcs.MCS(LayerMode.CLIENT, self._pduLayer) self._mcsLayer = mcs.Client(self._pduLayer)
#transport pdu layer #transport pdu layer
self._tpduLayer = tpdu.createClient(self._mcsLayer) self._tpduLayer = tpdu.Client(self._mcsLayer)
#transport packet (protocol layer) #transport packet (protocol layer)
self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer) self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer)
#is pdu layer is ready to send #is pdu layer is ready to send
self._isReady = False 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... 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): def setScreen(self, width, height):
""" """
@@ -100,7 +97,6 @@ class RDPClientController(pdu.PDUClientListener):
@param observer: new observer to add @param observer: new observer to add
""" """
self._clientObserver.append(observer) self._clientObserver.append(observer)
observer._clientListener = self
def onUpdate(self, rectangles): def onUpdate(self, rectangles):
""" """
@@ -110,7 +106,7 @@ class RDPClientController(pdu.PDUClientListener):
for observer in self._clientObserver: for observer in self._clientObserver:
#for each rectangle in update PDU #for each rectangle in update PDU
for rectangle in rectangles: for rectangle in rectangles:
observer.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): def onReady(self):
""" """
@@ -133,18 +129,18 @@ class RDPClientController(pdu.PDUClientListener):
return return
try: try:
event = pdu.PointerEvent() event = pdu.data.PointerEvent()
if isPressed: if isPressed:
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_DOWN event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN
if button == 1: if button == 1:
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON1 event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON1
elif button == 2: elif button == 2:
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON2 event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON2
elif button == 3: elif button == 3:
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON3 event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON3
else: else:
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_MOVE event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE
#position #position
event.xPos.value = x event.xPos.value = x
@@ -166,12 +162,12 @@ class RDPClientController(pdu.PDUClientListener):
return return
try: try:
event = pdu.ScancodeKeyEvent() event = pdu.data.ScancodeKeyEvent()
event.keyCode.value = code event.keyCode.value = code
if isPressed: if isPressed:
event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_DOWN event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_DOWN
else: else:
event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_RELEASE event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE
#send event #send event
self._pduLayer.sendInputEvents([event]) self._pduLayer.sendInputEvents([event])
@@ -189,10 +185,10 @@ class RDPClientController(pdu.PDUClientListener):
return return
try: try:
event = pdu.UnicodeKeyEvent() event = pdu.data.UnicodeKeyEvent()
event.unicode.value = code event.unicode.value = code
if not isPressed: if not isPressed:
event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_RELEASE event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE
#send event #send event
self._pduLayer.sendInputEvents([event]) self._pduLayer.sendInputEvents([event])
@@ -206,7 +202,7 @@ class RDPClientController(pdu.PDUClientListener):
""" """
self._pduLayer.close() self._pduLayer.close()
class RDPServerController(pdu.PDUServerListener): class RDPServerController(pdu.layer.PDUServerListener):
""" """
Controller use in server side mode Controller use in server side mode
""" """
@@ -215,20 +211,84 @@ class RDPServerController(pdu.PDUServerListener):
@param privateKeyFileName: file contain server private key @param privateKeyFileName: file contain server private key
@param certficiateFileName: file that contain public 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 #multi channel service
self._mcsLayer = mcs.MCS(LayerMode.SERVER, self._pduLayer) self._mcsLayer = mcs.Server(self._pduLayer)
#transport pdu layer #transport pdu layer
self._tpduLayer = tpdu.createServer(self._mcsLayer, privateKeyFileName, certificateFileName) self._tpduLayer = tpdu.Server(self._mcsLayer, privateKeyFileName, certificateFileName)
#transport packet (protocol layer) #transport packet (protocol layer)
self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer) self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer)
self._isReady = False
def getProtocol(self): def getProtocol(self):
""" """
@return: the twisted protocol layer @return: the twisted protocol layer
in RDP case is TPKT 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): class ClientFactory(protocol.Factory):
""" """
@@ -268,9 +328,8 @@ class ServerFactory(protocol.Factory):
@param addr: destination address @param addr: destination address
""" """
controller = RDPServerController(self._privateKeyFileName, self._certificateFileName) controller = RDPServerController(self._privateKeyFileName, self._certificateFileName)
self.buildObserver(controller)
return controller.getProtocol() return controller.getProtocol()
#pduLayer = pdu.PDU(pdu.PDUServerListener())
#return tpkt.TPKT(tpdu.createServer(mcs.createServer(pduLayer), self._privateKeyFileName, self._certificateFileName));
def buildObserver(self, controller): def buildObserver(self, controller):
""" """
@@ -310,3 +369,20 @@ class RDPClientObserver(object):
@param data: bitmap data @param data: bitmap data
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "RDPClientObserver")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "RDPClientObserver"))
class RDPServerObserver(object):
"""
Class use to inform all RDP event handle by RDPY
"""
def __init__(self, controller):
"""
@param controller: RDP controller use to interact with protocol
"""
self._controller = controller
self._controller.addServerObserver(self)
def onReady(self):
"""
Stack is ready and connected
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPServerObserver"))

View File

@@ -24,7 +24,7 @@ This layer have main goal to negociate SSL transport
RDP basic security is not supported by RDPY (because is not a true security layer...) 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.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof
from rdpy.base.error import InvalidExpectedDataException 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.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)) self.failureCode = UInt32Le(conditional = lambda: (self.code.value == NegociationType.TYPE_RDP_NEG_FAILURE))
class TPDU(LayerAutomata, StreamSender): class TPDULayer(LayerAutomata, StreamSender):
""" """
TPDU layer management TPDU layer management
there is an connection automata 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 @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 #default selectedProtocol is SSl because is the only supported
#in this version of RDPY #in this version of RDPY
#client requested selectedProtocol #client requested selectedProtocol
@@ -125,27 +124,51 @@ class TPDU(LayerAutomata, StreamSender):
#server selected selectedProtocol #server selected selectedProtocol
self._selectedProtocol = Protocols.PROTOCOL_SSL self._selectedProtocol = Protocols.PROTOCOL_SSL
#Server mode informations for TLS connection def recvData(self, data):
self._serverPrivateKeyFileName = None """
self._serverCertificateFileName = None 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 Write message packet for TPDU layer
@param privateKeyFileName: file contain server private key Add TPDU header
@param certficiateFileName: file that contain public key @param message: network.Type message
""" """
self._serverPrivateKeyFileName = privateKeyFileName self._transport.send((TPDUDataHeader(), message))
self._serverCertificateFileName = certificateFileName
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): def connect(self):
""" """
Connection request for client send a connection request packet Connection request for client send a connection request packet
""" """
if self._mode == LayerMode.CLIENT: self.sendConnectionRequest()
self.sendConnectionRequest()
else: def sendConnectionRequest(self):
self.setNextState(self.recvConnectionRequest) """
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): def recvConnectionConfirm(self, data):
""" """
@@ -180,6 +203,27 @@ class TPDU(LayerAutomata, StreamSender):
#connection is done send to presentation #connection is done send to presentation
self._presentation.connect() 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): def recvConnectionRequest(self, data):
""" """
Read connection confirm packet Read connection confirm packet
@@ -207,28 +251,6 @@ class TPDU(LayerAutomata, StreamSender):
self._selectedProtocol = Protocols.PROTOCOL_SSL self._selectedProtocol = Protocols.PROTOCOL_SSL
self.sendConnectionConfirm() 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): def sendConnectionConfirm(self):
""" """
Write connection confirm message Write connection confirm message
@@ -246,32 +268,6 @@ class TPDU(LayerAutomata, StreamSender):
self.setNextState(self.recvData) self.setNextState(self.recvData)
self._presentation.connect() 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 #open ssl needed
from twisted.internet import ssl from twisted.internet import ssl
from OpenSSL import SSL from OpenSSL import SSL

View File

@@ -22,7 +22,7 @@ Transport packet layer implementation
Use to build correct size packet and handle slow path and fast path mode 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.network.type import UInt8, UInt16Be, sizeof
from rdpy.base.error import CallPureVirtualFuntion from rdpy.base.error import CallPureVirtualFuntion
@@ -31,10 +31,10 @@ class FastPathListener(object):
Fast path packet listener Fast path packet listener
Usually PDU layer Usually PDU layer
""" """
def recvFastPath(self, fastPathData): def recvFastPath(self, fastPathS):
""" """
Call when fast path packet is received 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")) 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 @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 #last packet version read from header
self._lastPacketVersion = UInt8() self._lastPacketVersion = UInt8()
#length may be coded on more than 1 bytes #length may be coded on more than 1 bytes

View File

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