code refactoring

This commit is contained in:
speyrefitte
2014-06-24 16:01:04 +02:00
parent aa8c3dee11
commit fadf1ff5c4
8 changed files with 271 additions and 208 deletions

View File

@@ -45,7 +45,9 @@ def decode(src, width, height, colorType):
insertMix = False
fom_mask = 0
mask = 0
dst = Stream("\x00" * (width * height * sizeof(colorType())))
line = 0
typeSize = sizeof(colorType())
dst = Stream("\x00" * (width * height * typeSize))
while src.dataLen() > 0:
#compute orders
@@ -107,7 +109,10 @@ def decode(src, width, height, colorType):
raise InvalidExpectedDataException("In RLE decompression height must be greater than 0")
x = 0
height -= 1
#prevline = line
prevline = line
line = width * height * typeSize
return dst

View File

@@ -21,6 +21,16 @@
All exceptions error use in RDPY
"""
class CallPureVirtualFuntion(Exception):
"""
Raise when a virtual function is called and not implemented
"""
def __init__(self, message = ""):
"""
@param message: message show when exception is raised
"""
Exception.__init__(self, message)
class InvalidValue(Exception):
"""
Raise when invalid value type occurred

View File

@@ -23,88 +23,94 @@ Join RDPY design with twisted design
RDPY use Layer Protocol design (like twisted)
"""
from rdpy.network.error import CallPureVirtualFuntion
class LayerMode(object):
NONE = 0
SERVER = 1
CLIENT = 2
class StreamListener(object):
"""
Interface use to inform that we can handle receive stream
"""
def recv(self, s):
"""
Signal that data is available for this layer
call by transport layer
default is to pass data to presentation layer
@param s: raw Stream receive from transport layer
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "StreamListener"))
class StreamSender(object):
"""
Interface use to show stream sender capability
"""
def send(self, data):
'''
Send Stream on layer
@param data: Type or tuple element handle by transport layer
'''
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "send", "StreamSender"))
class Layer(object):
'''
Network abstraction for protocol
Try as possible to divide user protocol in layer
default implementation is a transparent layer
'''
def __init__(self, mode = LayerMode.NONE, presentation = None):
'''
Constructor
@param presentation: Layer which handled connect and recv messages
'''
"""
A simple double linked list with presentation and transport layer
and a subset of event (connect and close)
"""
def __init__(self, mode, presentation = None):
"""
@param mode: LayerMode use
@param presentation: presentation layer
"""
#presentation layer higher layer in model
self._presentation = presentation
#transport layer under layer in model
self._transport = None
#register layer mode
#network layer mode
self._mode = mode
#auto set transport layer of own presentation layer
if not self._presentation is None:
self._presentation._transport = self
def connect(self):
'''
call when transport layer is connected
"""
Call when transport layer is connected
default is send connect event to presentation layer
'''
"""
if not self._presentation is None:
self._presentation.connect()
def recv(self, s):
'''
signal that data is available for this layer
call by transport layer
default is to pass data to presentation layer
@param s: raw Stream receive from transport layer
'''
if not self._presentation is None:
self._presentation.recv(s)
def send(self, data):
'''
classical use by presentation layer
write data for this layer
default pass data to transport layer
@param data: Type or tuple element handle by transport layer
'''
if not self._transport is None:
self._transport.send(data)
def close(self):
'''
close layer and send close signal
to transport layer
'''
"""
Close layer event
default is sent to transport layer
"""
if not self._transport is None:
self._transport.close()
class LayerAutomata(Layer):
'''
layer with automata state
we can set next recv function used
'''
class LayerAutomata(Layer, StreamListener):
"""
Layer with automata state
we can set next recv function used for Stream packet
"""
def __init__(self, mode, presentation = None):
'''
Constructor
"""
@param mode: LayerMode use
@param presentation: presentation Layer
'''
"""
#call parent constructor
Layer.__init__(self, mode, presentation)
def setNextState(self, callback = None):
'''
set recv function to next callback or
"""
Set receive function to next callback or
current self.recv function if it's None
@param callback: a callable object that can
receive Layer, Stream parameters
'''
"""
if callback is None:
callback = self.__class__.recv
@@ -115,16 +121,17 @@ from twisted.internet import protocol
#first that handle stream
from type import Stream
class RawLayer(protocol.Protocol, LayerAutomata):
'''
class RawLayer(protocol.Protocol, LayerAutomata, StreamSender):
"""
Inherit from protocol twisted class
allow this protocol to wait until expected size of packet
and use Layer automata to call next automata state
'''
"""
def __init__(self, mode, presentation = None):
'''
Constructor
'''
"""
@param mode: LayerMode use
@param presentation: presentation layer in layer list
"""
#call parent automata
LayerAutomata.__init__(self, mode, presentation)
#data buffer received from twisted network layer
@@ -133,11 +140,11 @@ class RawLayer(protocol.Protocol, LayerAutomata):
self._expectedLen = 0
def dataReceived(self, data):
'''
inherit from protocol class
"""
Inherit from protocol class
main event of received data
@param data: string data receive from twisted
'''
"""
#add in buffer
self._buffer += data
#while buffer have expected size call local callback
@@ -150,29 +157,29 @@ class RawLayer(protocol.Protocol, LayerAutomata):
self.recv(expectedData)
def connectionMade(self):
'''
"""
inherit from twisted protocol
'''
"""
#join two scheme
self.connect()
def expect(self, expectedLen, callback = None):
'''
configure layer to change next state with callback only
"""
Configure layer to change next state with callback only
when expectLen bytes is received from transport layer
@param expectedLen: in bytes len use to call nextstate
@param callback: callback call when expectedlen bytes is received
'''
@param expectedLen: in bytes length use to call next state
@param callback: callback call when expected length bytes is received
"""
self._expectedLen = expectedLen
#default callback is recv from LayerAutomata
self.setNextState(callback)
def send(self, message):
'''
send stream on tcp layer
"""
Send Stream on TCP layer
format message into raw stream understood by transport layer
@param message: (tuple | Type)
'''
"""
s = Stream()
s.writeType(message)
self.transport.write(s.getvalue())

View File

@@ -1,6 +1,27 @@
'''
@author: sylvain
'''
#
# Copyright (c) 2014 Sylvain Peyrefitte
#
# This file is part of rdpy.
#
# rdpy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Basic Encoding Rules use in RDP.
ASN.1 standard
"""
from rdpy.network.type import UInt8, UInt16Be, UInt32Be, String
from rdpy.network.const import ConstAttributes, TypeAttributes
from rdpy.network.error import InvalidExpectedDataException, InvalidSize
@@ -35,24 +56,24 @@ class Tag(object):
BER_TAG_SEQUENCE_OF = 0x10
def berPC(pc):
'''
return BER_CONSTRUCT if true
"""
Return BER_CONSTRUCT if true
BER_PRIMITIVE if false
@param pc: boolean
@return: BerPc value
'''
"""
if pc:
return BerPc.BER_CONSTRUCT
else:
return BerPc.BER_PRIMITIVE
def readLength(s):
'''
read length of ber structure
"""
Read length of BER structure
length be on 1 2 or 3 bytes
@param s: stream
@return: int or python long
'''
@return: int or Python long
"""
size = None
length = UInt8()
s.readType(length)
@@ -64,50 +85,50 @@ def readLength(s):
elif byte == 2:
size = UInt16Be()
else:
raise InvalidExpectedDataException("ber length may be 1 or 2")
raise InvalidExpectedDataException("BER length may be 1 or 2")
s.readType(size)
else:
size = length
return size.value
def writeLength(size):
'''
return strcture length as expected in Ber specification
"""
Return structure length as expected in BER specification
@param size: int or python long
@return: UInt8 or (UInt8(0x82), UInt16Be)
'''
"""
if size > 0x7f:
return (UInt8(0x82), UInt16Be(size))
else:
return UInt8(size)
def readUniversalTag(s, tag, pc):
'''
read tag of ber packet
"""
Read tag of BER packet
@param tag: Tag class attributes
@param pc: boolean
@return: true if tag is correctly read
'''
"""
byte = UInt8()
s.readType(byte)
return byte == ((Class.BER_CLASS_UNIV | berPC(pc)) | (Tag.BER_TAG_MASK & tag))
def writeUniversalTag(tag, pc):
'''
return universal tag byte
"""
Return universal tag byte
@param tag: tag class attributes
@param pc: boolean
@return: UInt8
'''
"""
return ((Class.BER_CLASS_UNIV | berPC(pc)) | (Tag.BER_TAG_MASK & tag))
def readApplicationTag(s, tag):
'''
read application tag
"""
Read application tag
@param s: stream
@param tag: tag class attributes
@return: length of application packet
'''
"""
byte = UInt8()
s.readType(byte)
if tag > UInt8(30):
@@ -123,22 +144,22 @@ def readApplicationTag(s, tag):
return readLength(s)
def writeApplicationTag(tag, size):
'''
return struct that represent ber application tag
"""
Return structure that represent BER application tag
@param tag: UINt8
@param size: size to rest of packet
'''
"""
if tag > UInt8(30):
return (((Class.BER_CLASS_APPL | BerPc.BER_CONSTRUCT) | Tag.BER_TAG_MASK), tag, writeLength(size))
else:
return (((Class.BER_CLASS_APPL | BerPc.BER_CONSTRUCT) | (Tag.BER_TAG_MASK & tag)), writeLength(size))
def readBoolean(s):
'''
return boolean
"""
Return boolean
@param s: stream
@return: boolean
'''
"""
if not readUniversalTag(s, Tag.BER_TAG_BOOLEAN, False):
raise InvalidExpectedDataException("bad boolean tag")
size = readLength(s)
@@ -149,24 +170,24 @@ def readBoolean(s):
return bool(b.value)
def writeBoolean(b):
'''
return structure that represent boolean in ber specification
"""
Return structure that represent boolean in BER specification
@param b: boolean
@return: ber boolean structure
'''
@return: BER boolean structure
"""
boolean = UInt8(0)
if b:
boolean = UInt8(0xff)
return (writeUniversalTag(Tag.BER_TAG_BOOLEAN, False), writeLength(1), boolean)
def readInteger(s):
'''
read integer structure from stream
"""
Read integer structure from stream
@param s: stream
@return: int or long python
'''
"""
if not readUniversalTag(s, Tag.BER_TAG_INTEGER, False):
raise InvalidExpectedDataException("bad integer tag")
raise InvalidExpectedDataException("Bad integer tag")
size = readLength(s)
@@ -189,14 +210,14 @@ def readInteger(s):
s.readType(integer)
return integer.value
else:
raise InvalidExpectedDataException("wrong integer size")
raise InvalidExpectedDataException("Wrong integer size")
def writeInteger(value):
'''
write integer value
"""
Write integer value
@param param: int or python long
@return ber interger structure
'''
@return: BER integer structure
"""
if value <= 0xff:
return (writeUniversalTag(Tag.BER_TAG_INTEGER, False), writeLength(1), UInt8(value))
elif value <= 0xffff:
@@ -205,30 +226,30 @@ def writeInteger(value):
return (writeUniversalTag(Tag.BER_TAG_INTEGER, False), writeLength(4), UInt32Be(value))
def readOctetString(s):
'''
read ber string structure
"""
Read BER string structure
@param s: stream
@return: String
'''
"""
if not readUniversalTag(s, Tag.BER_TAG_OCTET_STRING, False):
raise InvalidExpectedDataException("unexpected ber tag")
raise InvalidExpectedDataException("Unexpected BER tag")
size = readLength(s)
return String(s.read(size.value))
def writeOctetstring(value):
'''
write string in ber representation
"""
Write string in ber representation
@param value: string
@return: string ber structure
'''
"""
return (writeUniversalTag(Tag.BER_TAG_OCTET_STRING, False), writeLength(len(value)), String(value))
def readEnumerated(s):
'''
read enumerated structure
"""
Read enumerated structure
@param s: Stream
@return: int or long
'''
"""
if not readUniversalTag(s, Tag.BER_TAG_ENUMERATED, False):
raise InvalidExpectedDataException("invalid ber tag")
if readLength(s) != 1:

View File

@@ -1,9 +1,32 @@
'''
@author: citronneur
'''
#
# 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 Multi Channel Service
Each channel have a particular role.
The main channel is the graphical channel.
It exist channel for file system order, audio channel, clipboard etc...
"""
from rdpy.network.const import ConstAttributes, TypeAttributes
from rdpy.network.layer import LayerAutomata, Layer, LayerMode
from rdpy.network.layer import LayerAutomata, LayerMode, StreamSender
from rdpy.network.type import sizeof, Stream, UInt8, UInt16Be
from rdpy.network.error import InvalidExpectedDataException, InvalidValue, InvalidSize
from rdpy.protocol.rdp.ber import writeLength
@@ -13,18 +36,18 @@ import ber, gcc, per
@ConstAttributes
@TypeAttributes(UInt8)
class Message(object):
'''
message type
'''
"""
Message type
"""
MCS_TYPE_CONNECT_INITIAL = 0x65
MCS_TYPE_CONNECT_RESPONSE = 0x66
@ConstAttributes
@TypeAttributes(UInt8)
class DomainMCSPDU:
'''
domain mcs pdu header
'''
"""
Domain MCS PDU header
"""
ERECT_DOMAIN_REQUEST = 1
DISCONNECT_PROVIDER_ULTIMATUM = 8
ATTACH_USER_REQUEST = 10
@@ -37,27 +60,28 @@ class DomainMCSPDU:
@ConstAttributes
@TypeAttributes(UInt16Be)
class Channel:
"""
Channel id of main channels use in RDP
"""
MCS_GLOBAL_CHANNEL = 1003
MCS_USERCHANNEL_BASE = 1001
class MCS(LayerAutomata):
'''
"""
Multi Channel Service layer
the main layer of RDP protocol
is why he can do everything and more!
'''
class MCSProxySender(Layer):
'''
Proxy use to set as trnsport layer for upper channel
"""
class MCSProxySender(StreamSender):
"""
Proxy use to set as transport layer for upper channel
use to abstract channel id for presentation layer
'''
"""
def __init__(self, mcs, channelId):
'''
ctor
@param mcs: mcs layer use as proxy
"""
@param mcs: MCS layer use as proxy
@param channelId: channel id for presentation layer
'''
"""
self._mcs = mcs
self._channelId = channelId
@@ -94,11 +118,10 @@ class MCS(LayerAutomata):
def __init__(self, mode, presentation):
'''
ctor call base class ctor
@param mode: mode of mcs layer
"""
@param mode: mode of MCS layer
@param presentation: presentation layer
'''
"""
LayerAutomata.__init__(self, mode, presentation)
self._clientSettings = gcc.ClientSettings()
self._serverSettings = gcc.ServerSettings()
@@ -110,17 +133,17 @@ class MCS(LayerAutomata):
self._channelIdsRequest = {}
def connect(self):
'''
connection send for client mode
"""
Connection send for client mode
a write connect initial packet
'''
"""
self._clientSettings.core.serverSelectedProtocol = self._transport._selectedProtocol
self.sendConnectInitial()
def connectNextChannel(self):
'''
send sendChannelJoinRequest message on next unconnect channel
'''
"""
Send sendChannelJoinRequest message on next unconnect channel
"""
for (channelId, layer) in self._channelIds.iteritems():
#for each unconnect channel send a request
if not self._channelIdsRequest.has_key(channelId):
@@ -138,9 +161,9 @@ class MCS(LayerAutomata):
layer.connect()
def sendConnectInitial(self):
'''
send connect initial packet
'''
"""
Send connect initial packet
"""
ccReq = gcc.writeConferenceCreateRequest(self._clientSettings)
ccReqStream = Stream()
ccReqStream.writeType(ccReq)
@@ -155,28 +178,28 @@ class MCS(LayerAutomata):
self.setNextState(self.recvConnectResponse)
def sendErectDomainRequest(self):
'''
send a formated erect domain request for RDP connection
'''
"""
Send a formated erect domain request for RDP connection
"""
self._transport.send((self.writeMCSPDUHeader(DomainMCSPDU.ERECT_DOMAIN_REQUEST), per.writeInteger(0), per.writeInteger(0)))
def sendAttachUserRequest(self):
'''
send a formated attach user request for RDP connection
'''
"""
Send a formated attach user request for RDP connection
"""
self._transport.send(self.writeMCSPDUHeader(DomainMCSPDU.ATTACH_USER_REQUEST))
def sendChannelJoinRequest(self, channelId):
'''
send a formated Channel join request from client to server
'''
"""
Send a formated Channel join request from client to server
"""
self._transport.send((self.writeMCSPDUHeader(DomainMCSPDU.CHANNEL_JOIN_REQUEST), self._userId, channelId))
def recvConnectResponse(self, data):
'''
receive mcs connect response from server
"""
receive MCS connect response from server
@param data: Stream
'''
"""
ber.readApplicationTag(data, Message.MCS_TYPE_CONNECT_RESPONSE)
ber.readEnumerated(data)
ber.readInteger(data)
@@ -196,10 +219,10 @@ class MCS(LayerAutomata):
self.setNextState(self.recvAttachUserConfirm)
def recvAttachUserConfirm(self, data):
'''
receive an attach user confirm
"""
Receive an attach user confirm
@param data: Stream
'''
"""
opcode = UInt8()
confirm = UInt8()
data.readType((opcode, confirm))
@@ -217,10 +240,10 @@ class MCS(LayerAutomata):
self.connectNextChannel()
def recvChannelJoinConfirm(self, data):
'''
receive a channel join confirm from server
"""
Receive a channel join confirm from server
@param data: Stream
'''
"""
opcode = UInt8()
confirm = UInt8()
data.readType((opcode, confirm))
@@ -239,10 +262,10 @@ class MCS(LayerAutomata):
self.connectNextChannel()
def recvData(self, data):
'''
main receive method
"""
Main receive method
@param data: Stream
'''
"""
opcode = UInt8()
data.readType(opcode)
@@ -278,51 +301,52 @@ class MCS(LayerAutomata):
self._channelIds[channelId].recv(data)
def send(self, channelId, data):
'''
specific send function for channelId
"""
Specific send function for channelId
@param channelId: Channel use to send
@param data: message to send
'''
"""
self._transport.send((self.writeMCSPDUHeader(DomainMCSPDU.SEND_DATA_REQUEST), self._userId, channelId, UInt8(0x70), UInt16Be(sizeof(data)) | UInt16Be(0x8000), data))
def writeDomainParams(self, maxChannels, maxUsers, maxTokens, maxPduSize):
'''
write a special domain param structure
"""
Write a special domain parameter structure
use in connection sequence
@param maxChannels: number of mcs channel use
@param maxUsers: number of mcs user used (1)
@param maxChannels: number of MCS channel use
@param maxUsers: number of MCS user used (1)
@param maxTokens: unknown
@param maxPduSize: unknown
@return: domain param structure
'''
@return: domain parameter structure
"""
domainParam = (ber.writeInteger(maxChannels), ber.writeInteger(maxUsers), ber.writeInteger(maxTokens),
ber.writeInteger(1), ber.writeInteger(0), ber.writeInteger(1),
ber.writeInteger(maxPduSize), ber.writeInteger(2))
return (ber.writeUniversalTag(ber.Tag.BER_TAG_SEQUENCE, True), writeLength(sizeof(domainParam)), domainParam)
def writeMCSPDUHeader(self, mcsPdu, options = 0):
'''
write mcs pdu header
@param mcsPdu: pdu code
"""
Write MCS PDU header
@param mcsPdu: PDU code
@param options: option contains in header
@return: UInt8
'''
"""
return (mcsPdu << 2) | options
def readMCSPDUHeader(self, opcode, mcsPdu):
'''
read mcsPdu header and return options parameter
"""
Read mcsPdu header and return options parameter
@param opcode: UInt8 opcode
@param mcsPdu: mcsPdu will be checked
@return: true if opcode is correct
'''
"""
return (opcode >> 2) == mcsPdu
def readDomainParams(self, s):
'''
read domain params structure
"""
Read domain parameters structure
@return: (max_channels, max_users, max_tokens, max_pdu_size)
'''
"""
if not ber.readUniversalTag(s, ber.Tag.BER_TAG_SEQUENCE, True):
raise InvalidValue("bad BER tags")
ber.readLength(s)#length

View File

@@ -2,16 +2,12 @@
@author: citronneur
'''
from rdpy.network.layer import LayerAutomata, LayerMode
from rdpy.network.type import CompositeType, UniString, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType,\
FactoryType
from rdpy.network.layer import LayerAutomata
from rdpy.network.type import CompositeType, UniString, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType
from rdpy.network.const import ConstAttributes, TypeAttributes
from rdpy.network.error import InvalidExpectedDataException, ErrorReportedFromPeer
import gcc
import lic
import caps
import rdp
import gcc, lic, caps
@ConstAttributes
@TypeAttributes(UInt16Le)

View File

@@ -2,8 +2,8 @@
@author: sylvain
'''
from twisted.internet import protocol
import tpkt, tpdu, mcs, pdu
from rdpy.network.layer import LayerMode
import tpkt, tpdu, mcs, pdu
class RDPController(object):
"""

View File

@@ -24,7 +24,7 @@ This layer have main goal to negociate ssl transport
RDP basic security is not supported by RDPY (because is not a true security layer...)
"""
from rdpy.network.layer import LayerAutomata, LayerMode
from rdpy.network.layer import LayerAutomata, LayerMode, StreamSender
from rdpy.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof
from rdpy.network.error import InvalidExpectedDataException
from rdpy.network.const import ConstAttributes, TypeAttributes
@@ -116,7 +116,7 @@ class Negotiation(CompositeType):
self.selectedProtocol = UInt32Le(conditional = lambda: self.code == NegociationType.TYPE_RDP_NEG_RSP)
self.failureCode = UInt32Le(conditional = lambda: self.code == NegociationType.TYPE_RDP_NEG_FAILURE)
class TPDU(LayerAutomata):
class TPDU(LayerAutomata, StreamSender):
"""
TPDU layer management
there is an connection automata
@@ -223,7 +223,7 @@ class TPDU(LayerAutomata):
"""
header = TPDUDataHeader()
data.readType(header)
LayerAutomata.recv(self, data)
self._presentation.recv(data)
def sendConnectionRequest(self):
"""