diff --git a/rdpy/protocol/network/error.py b/rdpy/protocol/network/error.py index 6371486..a6ad224 100644 --- a/rdpy/protocol/network/error.py +++ b/rdpy/protocol/network/error.py @@ -46,3 +46,13 @@ class InvalidType(Exception): ''' Exception.__init__(self, message) +class InvalidSize(Exception): + ''' + raise when invalid size is present in packet type occured + ''' + def __init__(self, message = ""): + ''' + constructor with message + @param message: message show when exception is raised + ''' + Exception.__init__(self, message) diff --git a/rdpy/protocol/network/type.py b/rdpy/protocol/network/type.py index 4c59691..a2ea388 100644 --- a/rdpy/protocol/network/type.py +++ b/rdpy/protocol/network/type.py @@ -3,6 +3,7 @@ ''' import struct +from copy import deepcopy from StringIO import StringIO from error import InvalidValue, InvalidType @@ -240,6 +241,28 @@ class CompositeType(Type): size += sizeof(self.__dict__[name]) return size + def __eq__(self, other): + ''' + compare each properties which are Type inheritance + if one is different then not equal + @param other: CompositeType + @return: True if each subtype are equals + ''' + if self._typeName != other._typeName: + return False + for name in self._typeName: + if self.__dict__[name] != other.__dict__[name]: + return False + return True + + def __ne__(self, other): + ''' + return not equal result operator + @param other: CompositeType + @return: False if each subtype are equals + ''' + return not self.__eq__(other) + class UInt8(SimpleType): ''' unsigned byte @@ -460,15 +483,29 @@ class Stream(StringIO): self.writeType(element) return value.write(self) - - def write_unistr(self, value): - for c in value: - self.write_uint8(ord(c)) - self.write_uint8(0) - self.write_uint8(0) - self.write_uint8(0) + +def CheckValueOnRead(cls): + ''' + wrap read method of class + to check value on read + if new value is different from old value + raise InvalidValue + @param cls: class that inherit from Type + ''' + oldRead = cls.read + def read(self, s): + old = deepcopy(self) + oldRead(self, s) + if self != old: + raise InvalidValue("CheckValueOnRead %s != %s"%(self, old)) + cls.read = read + return cls def hexDump(src, length=16): + ''' + print hex representation of sr + @param src: string + ''' FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)]) for c in xrange(0, len(src), length): chars = src[c:c+length] diff --git a/rdpy/protocol/rdp/ber.py b/rdpy/protocol/rdp/ber.py index d0f4791..bf1d016 100644 --- a/rdpy/protocol/rdp/ber.py +++ b/rdpy/protocol/rdp/ber.py @@ -3,7 +3,8 @@ ''' from rdpy.protocol.network.type import UInt8, UInt16Be, UInt32Be, String from rdpy.utils.const import ConstAttributes -from rdpy.protocol.network.error import InvalidExpectedDataException +from rdpy.protocol.network.error import InvalidExpectedDataException,\ + InvalidSize @ConstAttributes class BerPc(object): @@ -228,9 +229,8 @@ def readEnumerated(s): ''' if not readUniversalTag(s, Tag.BER_TAG_ENUMERATED, False): raise InvalidExpectedDataException("invalid ber tag") - size = readLength(s) - if size != UInt32Be(1): - raise InvalidExpectedDataException("enumerate size is wrong") + if readLength(s) != 1: + raise InvalidSize("enumerate size is wrong") enumer = UInt8() s.readType(enumer) return enumer.value diff --git a/rdpy/protocol/rdp/mcs.py b/rdpy/protocol/rdp/mcs.py index a259ea2..ba57e47 100644 --- a/rdpy/protocol/rdp/mcs.py +++ b/rdpy/protocol/rdp/mcs.py @@ -2,28 +2,30 @@ @author: sylvain ''' -from rdpy.utils.const import ConstAttributes +from rdpy.utils.const import ConstAttributes, TypeAttributes from rdpy.protocol.network.layer import LayerAutomata from rdpy.protocol.network.type import sizeof, Stream, UInt8 from rdpy.protocol.rdp.ber import writeLength +from rdpy.protocol.network.error import InvalidExpectedDataException, InvalidValue, InvalidSize import ber, gcc @ConstAttributes +@TypeAttributes(UInt8) class Message(object): ''' message type ''' - MCS_TYPE_CONNECT_INITIAL = UInt8(0x65) - MCS_TYPE_CONNECT_RESPONSE = UInt8(0x66) - MCS_EDRQ = UInt8(1) - MCS_DPUM = UInt8(8) - MCS_AURQ = UInt8(10) - MCS_AUCF = UInt8(11) - MCS_CJRQ = UInt8(14) - MCS_CJCF = UInt8(15) - MCS_SDRQ = UInt8(25) - MCS_SDIN = UInt8(26) + MCS_TYPE_CONNECT_INITIAL = 0x65 + MCS_TYPE_CONNECT_RESPONSE = 0x66 + MCS_EDRQ = 1 + MCS_DPUM = 8 + MCS_AURQ = 10 + MCS_AUCF = 11 + MCS_CJRQ = 14 + MCS_CJCF = 15 + MCS_SDRQ = 25 + MCS_SDIN = 26 class Channel: MCS_GLOBAL_CHANNEL = 1003 @@ -66,6 +68,8 @@ class MCS(LayerAutomata): self.writeDomainParams(0xffff, 0xfc17, 0xffff, 0xffff), ber.writeOctetstring(ccReqStream.getvalue())) self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_INITIAL, sizeof(tmp)), tmp)) + #we must receive a connect response + self.setNextState(self.recvConnectResponse) def writeDomainParams(self, maxChannels, maxUsers, maxTokens, maxPduSize): ''' @@ -81,5 +85,34 @@ class MCS(LayerAutomata): 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 readDomainParams(self, s): + ''' + read domain params structure + ''' + if not ber.readUniversalTag(s, ber.Tag.BER_TAG_SEQUENCE, True): + raise InvalidValue("bad BER tags") + length = ber.readLength(s) + max_channels = ber.readInteger(s) + max_users = ber.readInteger(s) + max_tokens = ber.readInteger(s) + ber.readInteger(s) + ber.readInteger(s) + ber.readInteger(s) + max_pdu_size = ber.readInteger(s) + ber.readInteger(s) + + def recvConnectResponse(self, data): + ber.readApplicationTag(data, 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 tag") + gccRequestLength = ber.readLength(data) + if data.dataLen() != gccRequestLength: + raise InvalidSize("gcc request have ") + from rdpy.protocol.network.type import hexDump + hexDump(data.getvalue()) \ No newline at end of file diff --git a/rdpy/protocol/rdp/per.py b/rdpy/protocol/rdp/per.py index b142b20..e140497 100644 --- a/rdpy/protocol/rdp/per.py +++ b/rdpy/protocol/rdp/per.py @@ -2,7 +2,7 @@ @author: sylvain ''' -from rdpy.protocol.network.type import UInt8, UInt16Be, UInt32Be, String, Stream +from rdpy.protocol.network.type import UInt8, UInt16Be, UInt32Be, String from rdpy.protocol.network.error import InvalidValue, InvalidExpectedDataException def readLength(s): diff --git a/rdpy/protocol/rdp/tpdu.py b/rdpy/protocol/rdp/tpdu.py index cae6673..9fe03f1 100644 --- a/rdpy/protocol/rdp/tpdu.py +++ b/rdpy/protocol/rdp/tpdu.py @@ -4,37 +4,40 @@ from rdpy.protocol.network.layer import LayerAutomata from rdpy.protocol.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof from rdpy.protocol.network.error import InvalidExpectedDataException, NegotiationFailure -from rdpy.utils.const import ConstAttributes +from rdpy.utils.const import ConstAttributes, TypeAttributes @ConstAttributes +@TypeAttributes(UInt8) class MessageType(object): ''' message type ''' - X224_TPDU_CONNECTION_REQUEST = UInt8(0xE0) - X224_TPDU_CONNECTION_CONFIRM = UInt8(0xD0) - X224_TPDU_DISCONNECT_REQUEST = UInt8(0x80) - X224_TPDU_DATA = UInt8(0xF0) - X224_TPDU_ERROR = UInt8(0x70) + X224_TPDU_CONNECTION_REQUEST = 0xE0 + X224_TPDU_CONNECTION_CONFIRM = 0xD0 + X224_TPDU_DISCONNECT_REQUEST = 0x80 + X224_TPDU_DATA = 0xF0 + X224_TPDU_ERROR = 0x70 @ConstAttributes +@TypeAttributes(UInt8) class NegociationType(object): ''' negotiation header ''' - TYPE_RDP_NEG_REQ = UInt8(0x01) - TYPE_RDP_NEG_RSP = UInt8(0x02) - TYPE_RDP_NEG_FAILURE = UInt8(0x03) + TYPE_RDP_NEG_REQ = 0x01 + TYPE_RDP_NEG_RSP = 0x02 + TYPE_RDP_NEG_FAILURE = 0x03 @ConstAttributes +@TypeAttributes(UInt32Le) class Protocols(object): ''' protocols available for TPDU layer ''' - PROTOCOL_RDP = UInt32Le(0x00000000) - PROTOCOL_SSL = UInt32Le(0x00000001) - PROTOCOL_HYBRID = UInt32Le(0x00000002) - PROTOCOL_HYBRID_EX = UInt32Le(0x00000008) + PROTOCOL_RDP = 0x00000000 + PROTOCOL_SSL = 0x00000001 + PROTOCOL_HYBRID = 0x00000002 + PROTOCOL_HYBRID_EX = 0x00000008 class TPDUConnectHeader(CompositeType): ''' @@ -46,6 +49,15 @@ class TPDUConnectHeader(CompositeType): self.code = code self.padding = (UInt16Be(), UInt16Be(), UInt8()) +class TPDUDataHeader(CompositeType): + ''' + header send when tpdu exchange application data + ''' + def __init__(self): + CompositeType.__init__(self) + self.header = UInt8(2) + self.messageType = MessageType.X224_TPDU_DATA + self.separator = UInt8(0x80) class Negotiation(CompositeType): ''' @@ -69,7 +81,6 @@ class TPDU(LayerAutomata): @param presentation: MCS layer ''' LayerAutomata.__init__(self, presentation) - #default protocol is SSl because is the only supported #in this version of RDPY self._protocol = Protocols.PROTOCOL_SSL @@ -103,9 +114,19 @@ class TPDU(LayerAutomata): LayerAutomata.connect(self) def recvData(self, data): - print "TPDU data" - from rdpy.protocol.network.type import hexDump - hexDump(data.getvalue()) + ''' + read data header from packet + and pass to presentation layer + @param data: stream + ''' + header = TPDUDataHeader() + data.readType(header) + if header.messageType == MessageType.X224_TPDU_DATA: + LayerAutomata.recv(self, data) + elif header.messageType == MessageType.X224_TPDU_ERROR: + raise Exception("receive error from tpdu layer") + else: + raise InvalidExpectedDataException("unknow tpdu code %s"%header.messageType) def sendConnectionRequest(self): ''' @@ -121,7 +142,7 @@ class TPDU(LayerAutomata): write message packet for TPDU layer add TPDU header ''' - self._transport.send((UInt8(2), MessageType.X224_TPDU_DATA, UInt8(0x80), message)) + self._transport.send((TPDUDataHeader(), message)) def readNeg(self, data): ''' diff --git a/rdpy/protocol/rfb/message.py b/rdpy/protocol/rfb/message.py index 3a3df65..5b01072 100644 --- a/rdpy/protocol/rfb/message.py +++ b/rdpy/protocol/rfb/message.py @@ -3,58 +3,63 @@ ''' from rdpy.protocol.network.type import UInt8, UInt16Be, UInt32Be, SInt32Be, String, CompositeType -from rdpy.utils.const import ConstAttributes +from rdpy.utils.const import ConstAttributes, TypeAttributes @ConstAttributes +@TypeAttributes(String) class ProtocolVersion(object): ''' different ptotocol version ''' - UNKNOWN = String() - RFB003003 = String("RFB 003.003\n") - RFB003007 = String("RFB 003.007\n") - RFB003008 = String("RFB 003.008\n") + UNKNOWN = "" + RFB003003 = "RFB 003.003\n" + RFB003007 = "RFB 003.007\n" + RFB003008 = "RFB 003.008\n" @ConstAttributes +@TypeAttributes(UInt8) class SecurityType(object): ''' security type supported (or will be supported) by rdpy ''' - INVALID = UInt8(0) - NONE = UInt8(1) - VNC = UInt8(2) + INVALID = 0 + NONE = 1 + VNC = 2 @ConstAttributes +@TypeAttributes(UInt32Be) class Pointer(object): ''' mouse event code (which button) actually in RFB specification only$ three buttons are supported ''' - BUTTON1 = UInt32Be(0x1) - BUTTON2 = UInt32Be(0x2) - BUTTON3 = UInt32Be(0x4) + BUTTON1 = 0x1 + BUTTON2 = 0x2 + BUTTON3 = 0x4 -@ConstAttributes +@ConstAttributes +@TypeAttributes(SInt32Be) class Encoding(object): ''' encoding types ''' - RAW = SInt32Be(0) + RAW = 0 @ConstAttributes +@TypeAttributes(UInt8) class ClientToServerMessages(object): ''' messages types ''' - PIXEL_FORMAT = UInt8(0) - ENCODING = UInt8(2) - FRAME_BUFFER_UPDATE_REQUEST = UInt8(3) - KEY_EVENT = UInt8(4) - POINTER_EVENT = UInt8(5) - CUT_TEXT = UInt8(6) + PIXEL_FORMAT = 0 + ENCODING = 2 + FRAME_BUFFER_UPDATE_REQUEST = 3 + KEY_EVENT = 4 + POINTER_EVENT = 5 + CUT_TEXT = 6 class PixelFormat(CompositeType): ''' diff --git a/rdpy/utils/const.py b/rdpy/utils/const.py index 6c1b012..fbd3dd8 100644 --- a/rdpy/utils/const.py +++ b/rdpy/utils/const.py @@ -33,16 +33,27 @@ class Constant(object): delete is forbidden on constant ''' raise Exception("can't delete constant") - + +def TypeAttributes(typeClass): + ''' + call typeClass ctor on each attributes + to uniform atributes type on class + @param typeClass: class use to construct each class attributes + @return: class decorator + ''' + def wrapper(cls): + for c_name, c_value in cls.__dict__.iteritems(): + if c_name[0] != '_' and not callable(c_value): + setattr(cls, c_name, typeClass(c_value)) + return cls + return wrapper def ConstAttributes(cls): ''' + copy on read attributes transform all attributes of class in constant attribute only attributes which are not begining with '_' char and are not callable ''' - for c_name, c_value in cls.__dict__.iteritems(): - if c_name[0] != '_' and not callable(c_value): - setattr(cls, c_name, Constant(c_value)) - return cls + return TypeAttributes(Constant)(cls) \ No newline at end of file