Example #1
0
def test_hello():
    data = {'pycrypto.version': '2.6', 'bell': True, 'cursor.default_size': 66L, 'platform.release': '3.11.3-201.fc19.x86_64', 'lz4': False, 'encoding.vpx.version': 6, 'sound.receive': True, 'digest': ('hmac', 'xor'), 'aliases': {'suspend': 13, 'encoding': 14, 'desktop_size': 15, 'damage-sequence': 10, 'focus': 16, 'unmap-window': 17, 'connection-lost': 21, 'jpeg-quality': 19, 'min-speed': 20, 'ping_echo': 8, 'keymap-changed': 18, 'shutdown-server': 22, 'speed': 23, 'close-window': 24, 'server-settings': 25, 'set-clipboard-enabled': 7, 'quality': 27, 'ping': 9, 'set-cursors': 11, 'resize-window': 28, 'set_deflate': 29, 'key-repeat': 30, 'layout-changed': 31, 'set-keyboard-sync-enabled': 12, 'sound-control': 32, 'screenshot': 33, 'resume': 34, 'sound-data': 35, 'pointer-position': 36, 'disconnect': 26, 'button-action': 37, 'map-window': 38, 'buffer-refresh': 39, 'info-request': 40, 'set-notify': 5, 'configure-window': 41, 'set-bell': 6, 'min-quality': 42, 'gibberish': 43, 'hello': 44, 'key-action': 45, 'move-window': 46}, 'encodings.with_quality': ['jpeg'], 'change-quality': True, 'window_unmap': True, 'encodings.core': ['rgb24', 'rgb32', 'png', 'png/L', 'png/P', 'jpeg', 'vpx'], 'actual_desktop_size': (2560, 1600), 'encoding': 'png', 'root_window_size': (2560, 1600), 'encoding.PIL.version': '1.1.7', 'platform': 'linux2', 'gid': 1000, 'resize_screen': True, 'chunked_compression': True, 'sound.pygst.version': (0, 10, 22), 'sound.send': True, 'encoding.x264.version': 130L, 'encodings.with_speed': ['png', 'png/L', 'png/P', 'jpeg'], 'auto_refresh_delay': 250, 'client_window_properties': True, 'start_time': 1381225490, 'build.cpu': 'x86_64', 'notifications': False, 'server_type': 'base', 'platform.machine': 'x86_64', 'build.on': 'desktop', 'MARKER': '***********************************************************************************', 'rencode': True, 'window_configure': True, 'gtk.version': (2, 24, 19), 'window.raise': True, 'encodings.lossless': ['rgb24', 'rgb32', 'png', 'png/L', 'png/P', 'webp'], 'platform.name': 'Linux', 'build.revision': '4193', 'modifier_keycodes': {'control': [('Control_L', 'Control_L'), ('Control_R', 'Control_R')], 'mod1': [(64, 'Alt_L'), ('Alt_L', 'Alt_L'), ('Meta_L', 'Meta_L')], 'mod2': [('Num_Lock', 'Num_Lock')], 'mod3': [], 'mod4': [(133, 'Super_L'), ('Super_R', 'Super_R'), ('Super_L', 'Super_L'), ('Hyper_L', 'Hyper_L')], 'mod5': [(92, 'ISO_Level3_Shift'), ('Multi_key', 'ISO_Level3_Shift'), ('Mode_switch', 'Mode_switch')], 'shift': [('Shift_L', 'Shift_L'), ('Shift_R', 'Shift_R')], 'lock': [('Caps_Lock', 'Caps_Lock')]}, 'sound.pulseaudio.server': '', 'build.by': 'root', 'display': ':10', 'python.version': (2, 7, 5), 'uid': 1000, 'desktop_size': (2560, 1600), 'pid': 21794, 'sound_sequence': True, 'change-speed': True, 'sound.pulseaudio.id': '', 'cursors': True, 'encodings': ['png/L', 'vpx', 'jpeg', 'webp', 'rgb', 'png/P', 'png'], 'sound.decoders': ['mp3', 'wavpack', 'wav', 'flac', 'speex'], 'rencode.version': '1.0.2', 'current_time': 1381225493, 'hostname': 'desktop', 'encodings.with_lossless_mode': ['webp'], 'key_repeat': [500, 30], 'elapsed_time': 3, 'version': '0.11.0', 'build.bit': '64bit', 'suspend-resume': True, 'platform.linux_distribution': ('Fedora', '19', 'Schr\xc3\xb6dinger\xe2\x80\x99s Cat'), 'max_desktop_size': (5120, 3200), 'sound.encoders': ['mp3', 'wavpack', 'wav', 'flac', 'speex'], 'clipboard': True, 'encoding.avcodec.version': 'Lavc54.92.100', 'platform.platform': 'Linux-3.11.3-201.fc19.x86_64-x86_64-with-fedora-19-Schr\xc3\xb6dinger\xe2\x80\x99s_Cat', 'notify-startup-complete': True, 'toggle_keyboard_sync': True, 'xsettings-tuple': True, 'change-min-speed': True, 'clipboards': ['CLIPBOARD', 'PRIMARY', 'SECONDARY'], 'pygtk.version': (2, 24, 0), 'change-min-quality': True, 'byteorder': 'little', 'build.date': '2013-10-08', 'python.full_version': '2.7.5 (default, Aug 22 2013, 09:31:58) \n[GCC 4.8.1 20130603 (Red Hat 4.8.1-1)]', 'toggle_cursors_bell_notify': True, 'encoding.opencl.version': '2013.1', 'encoding.webp.version': '0.2.2', 'key_repeat_modifiers': True, 'raw_packets': True, 'build.local_modifications': '1', 'platform.processor': 'x86_64', 'sound.gst.version': (0, 10, 36), 'encoding.swscale.version': 'SwS2.2.100', 'mmap_enabled': False}
    s = rencode_dumps(data)
    #import binascii
    #print("rencode(%s)=%s" % (data, binascii.hexlify(s)))
    l = rencode_loads(s)
    #print("rdecode(%s)=%s" % (binascii.hexlify(s), l))
    #print("original has %s keys, reloaded has %s keys" % (len(data), len(l)))
    errs = compare_dict(data, l)
    return len(errs)==0
Example #2
0
def decode(data, protocol_flags):
    if protocol_flags & FLAGS_RENCODE:
        if not has_rencode:
            raise InvalidPacketEncodingException("rencode is not available")
        if not use_rencode:
            raise InvalidPacketEncodingException("rencode is disabled")
        return list(rencode_loads(data))
    elif protocol_flags & FLAGS_YAML:
        if not has_yaml:
            raise InvalidPacketEncodingException("yaml is not available")
        if not use_yaml:
            raise InvalidPacketEncodingException("yaml is disabled")
        return list(yaml_decode(data))
    else:
        if not has_bencode:
            raise InvalidPacketEncodingException("bencode is not available")
        if not use_bencode:
            raise InvalidPacketEncodingException("bencode is disabled")
        #if sys.version>='3':
        #    data = data.decode("latin1")
        packet, l = bdecode(data)
        assert l == len(data)
        return packet
Example #3
0
    def do_read_parse_thread_loop(self):
        """
            Process the individual network packets placed in _read_queue.
            Concatenate the raw packet data, then try to parse it.
            Extract the individual packets from the potentially large buffer,
            saving the rest of the buffer for later, and optionally decompress this data
            and re-construct the one python-object-packet from potentially multiple packets (see packet_index).
            The 8 bytes packet header gives us information on the packet index, packet size and compression.
            The actual processing of the packet is done via the callback process_packet_cb,
            this will be called from this parsing thread so any calls that need to be made
            from the UI thread will need to use a callback (usually via 'idle_add')
        """
        read_buffer = None
        payload_size = -1
        padding = None
        packet_index = 0
        compression_level = False
        raw_packets = {}
        while not self._closed:
            buf = self._read_queue.get()
            if not buf:
                log("read thread: empty marker, exiting")
                self.idle_add(self.close)
                return
            if read_buffer:
                read_buffer = read_buffer + buf
            else:
                read_buffer = buf
            bl = len(read_buffer)
            while not self._closed:
                bl = len(read_buffer)
                if bl<=0:
                    break
                if payload_size<0:
                    head = read_buffer[:8]
                    if read_buffer[0] not in ("P", ord("P")):
                        self._invalid_header(read_buffer)
                        return
                    if bl<8:
                        break   #packet still too small
                    #packet format: struct.pack('cBBBL', ...) - 8 bytes
                    _, protocol_flags, compression_level, packet_index, data_size = unpack_header(head)

                    #sanity check size (will often fail if not an xpra client):
                    if data_size>self.abs_max_packet_size:
                        self._invalid_header(read_buffer)
                        return

                    bl = len(read_buffer)-8
                    if protocol_flags & Protocol.FLAGS_CIPHER:
                        if self.cipher_in_block_size==0 or not self.cipher_in_name:
                            log.warn("received cipher block but we don't have a cipher to decrypt it with, not an xpra client?")
                            self._invalid_header(read_buffer)
                            return
                        padding = (self.cipher_in_block_size - data_size % self.cipher_in_block_size) * " "
                        payload_size = data_size + len(padding)
                    else:
                        #no cipher, no padding:
                        padding = None
                        payload_size = data_size
                    assert payload_size>0
                    read_buffer = read_buffer[8:]

                    if payload_size>self.max_packet_size:
                        #this packet is seemingly too big, but check again from the main UI thread
                        #this gives 'set_max_packet_size' a chance to run from "hello"
                        def check_packet_size(size_to_check, packet_header):
                            if not self._closed:
                                log("check_packet_size(%s, 0x%s) limit is %s", size_to_check, repr_ellipsized(packet_header), self.max_packet_size)
                                if size_to_check>self.max_packet_size:
                                    self._call_connection_lost("invalid packet: size requested is %s (maximum allowed is %s - packet header: 0x%s), dropping this connection!" %
                                                                  (size_to_check, self.max_packet_size, repr_ellipsized(packet_header)))
                            return False
                        self.timeout_add(1000, check_packet_size, payload_size, read_buffer[:32])

                if bl<payload_size:
                    # incomplete packet, wait for the rest to arrive
                    break

                #chop this packet from the buffer:
                if len(read_buffer)==payload_size:
                    raw_string = read_buffer
                    read_buffer = ''
                else:
                    raw_string = read_buffer[:payload_size]
                    read_buffer = read_buffer[payload_size:]
                #decrypt if needed:
                data = raw_string
                if self.cipher_in and protocol_flags & Protocol.FLAGS_CIPHER:
                    log("received %s encrypted bytes with %s padding", payload_size, len(padding))
                    data = self.cipher_in.decrypt(raw_string)
                    if padding:
                        def debug_str(s):
                            try:
                                return list(bytearray(s))
                            except:
                                return list(str(s))
                        if not data.endswith(padding):
                            log("decryption failed: string does not end with '%s': %s (%s) -> %s (%s)",
                            padding, debug_str(raw_string), type(raw_string), debug_str(data), type(data))
                            self._connection_lost("encryption error (wrong key?)")
                            return
                        data = data[:-len(padding)]
                #uncompress if needed:
                if compression_level>0:
                    try:
                        if compression_level & LZ4_FLAG:
                            assert has_lz4
                            data = LZ4_uncompress(data)
                        else:
                            data = decompress(data)
                    except Exception, e:
                        if self.cipher_in:
                            return self._call_connection_lost("decompression failed (invalid encryption key?): %s" % e)
                        return self._call_connection_lost("decompression failed: %s" % e)

                if self.cipher_in and not (protocol_flags & Protocol.FLAGS_CIPHER):
                    return self._call_connection_lost("unencrypted packet dropped: %s" % repr_ellipsized(data))

                if self._closed:
                    return
                if packet_index>0:
                    #raw packet, store it and continue:
                    raw_packets[packet_index] = data
                    payload_size = -1
                    packet_index = 0
                    if len(raw_packets)>=4:
                        return self._call_connection_lost("too many raw packets: %s" % len(raw_packets))
                    continue
                #final packet (packet_index==0), decode it:
                try:
                    if protocol_flags & Protocol.FLAGS_RENCODE:
                        assert has_rencode, "we don't support rencode mode but the other end sent us a rencoded packet! not an xpra client?"
                        packet = list(rencode_loads(data))
                    else:
                        #if sys.version>='3':
                        #    data = data.decode("latin1")
                        packet, l = bdecode(data)
                        assert l==len(data)
                except ValueError, e:
                    log.error("value error reading packet: %s", e, exc_info=True)
                    if self._closed:
                        return
                    log("failed to parse packet: %s", binascii.hexlify(data))
                    msg = "gibberish received: %s, packet index=%s, packet size=%s, buffer size=%s, error=%s" % (repr_ellipsized(data), packet_index, payload_size, bl, e)
                    self.gibberish(msg, data)
                    return

                if self._closed:
                    return
                payload_size = -1
                padding = None
                #add any raw packets back into it:
                if raw_packets:
                    for index,raw_data in raw_packets.items():
                        #replace placeholder with the raw_data packet data:
                        packet[index] = raw_data
                    raw_packets = {}

                packet_type = packet[0]
                if self.receive_aliases and type(packet_type)==int and packet_type in self.receive_aliases:
                    packet_type = self.receive_aliases.get(packet_type)
                    packet[0] = packet_type
                self.input_stats[packet_type] = self.output_stats.get(packet_type, 0)+1

                self.input_packetcount += 1
                log("processing packet %s", packet_type)
                self._process_packet_cb(self, packet)
Example #4
0
    def do_read_parse_thread_loop(self):
        """
            Process the individual network packets placed in _read_queue.
            Concatenate the raw packet data, then try to parse it.
            Extract the individual packets from the potentially large buffer,
            saving the rest of the buffer for later, and optionally decompress this data
            and re-construct the one python-object-packet from potentially multiple packets (see packet_index).
            The 8 bytes packet header gives us information on the packet index, packet size and compression.
            The actual processing of the packet is done via the callback process_packet_cb,
            this will be called from this parsing thread so any calls that need to be made
            from the UI thread will need to use a callback (usually via 'idle_add')
        """
        read_buffer = None
        payload_size = -1
        padding = None
        packet_index = 0
        compression_level = False
        raw_packets = {}
        while not self._closed:
            buf = self._read_queue.get()
            if not buf:
                log("read thread: empty marker, exiting")
                self.idle_add(self.close)
                return
            if read_buffer:
                read_buffer = read_buffer + buf
            else:
                read_buffer = buf
            bl = len(read_buffer)
            while not self._closed:
                bl = len(read_buffer)
                if bl <= 0:
                    break
                if payload_size < 0:
                    head = read_buffer[:8]
                    if read_buffer[0] not in ("P", ord("P")):
                        self._invalid_header(read_buffer)
                        return
                    if bl < 8:
                        break  #packet still too small
                    #packet format: struct.pack('cBBBL', ...) - 8 bytes
                    _, protocol_flags, compression_level, packet_index, data_size = unpack_header(
                        head)

                    #sanity check size (will often fail if not an xpra client):
                    if data_size > self.abs_max_packet_size:
                        self._invalid_header(read_buffer)
                        return

                    bl = len(read_buffer) - 8
                    if protocol_flags & Protocol.FLAGS_CIPHER:
                        if self.cipher_in_block_size == 0 or not self.cipher_in_name:
                            log.warn(
                                "received cipher block but we don't have a cipher to decrypt it with, not an xpra client?"
                            )
                            self._invalid_header(read_buffer)
                            return
                        padding = (self.cipher_in_block_size -
                                   data_size % self.cipher_in_block_size) * " "
                        payload_size = data_size + len(padding)
                    else:
                        #no cipher, no padding:
                        padding = None
                        payload_size = data_size
                    assert payload_size > 0
                    read_buffer = read_buffer[8:]

                    if payload_size > self.max_packet_size:
                        #this packet is seemingly too big, but check again from the main UI thread
                        #this gives 'set_max_packet_size' a chance to run from "hello"
                        def check_packet_size(size_to_check, packet_header):
                            if not self._closed:
                                log("check_packet_size(%s, 0x%s) limit is %s",
                                    size_to_check,
                                    repr_ellipsized(packet_header),
                                    self.max_packet_size)
                                if size_to_check > self.max_packet_size:
                                    self._call_connection_lost(
                                        "invalid packet: size requested is %s (maximum allowed is %s - packet header: 0x%s), dropping this connection!"
                                        % (size_to_check, self.max_packet_size,
                                           repr_ellipsized(packet_header)))
                            return False

                        self.timeout_add(1000, check_packet_size, payload_size,
                                         read_buffer[:32])

                if bl < payload_size:
                    # incomplete packet, wait for the rest to arrive
                    break

                #chop this packet from the buffer:
                if len(read_buffer) == payload_size:
                    raw_string = read_buffer
                    read_buffer = ''
                else:
                    raw_string = read_buffer[:payload_size]
                    read_buffer = read_buffer[payload_size:]
                #decrypt if needed:
                data = raw_string
                if self.cipher_in and protocol_flags & Protocol.FLAGS_CIPHER:
                    log("received %s encrypted bytes with %s padding",
                        payload_size, len(padding))
                    data = self.cipher_in.decrypt(raw_string)
                    if padding:

                        def debug_str(s):
                            try:
                                return list(bytearray(s))
                            except:
                                return list(str(s))

                        if not data.endswith(padding):
                            log(
                                "decryption failed: string does not end with '%s': %s (%s) -> %s (%s)",
                                padding, debug_str(raw_string),
                                type(raw_string), debug_str(data), type(data))
                            self._connection_lost(
                                "encryption error (wrong key?)")
                            return
                        data = data[:-len(padding)]
                #uncompress if needed:
                if compression_level > 0:
                    try:
                        if compression_level & LZ4_FLAG:
                            assert use_lz4
                            data = LZ4_uncompress(data)
                        else:
                            data = decompress(data)
                    except Exception, e:
                        if self.cipher_in:
                            return self._call_connection_lost(
                                "decompression failed (invalid encryption key?): %s"
                                % e)
                        return self._call_connection_lost(
                            "decompression failed: %s" % e)

                if self.cipher_in and not (protocol_flags
                                           & Protocol.FLAGS_CIPHER):
                    return self._call_connection_lost(
                        "unencrypted packet dropped: %s" %
                        repr_ellipsized(data))

                if self._closed:
                    return
                if packet_index > 0:
                    #raw packet, store it and continue:
                    raw_packets[packet_index] = data
                    payload_size = -1
                    packet_index = 0
                    if len(raw_packets) >= 4:
                        return self._call_connection_lost(
                            "too many raw packets: %s" % len(raw_packets))
                    continue
                #final packet (packet_index==0), decode it:
                try:
                    if protocol_flags & Protocol.FLAGS_RENCODE:
                        assert has_rencode, "we don't support rencode mode but the other end sent us a rencoded packet! not an xpra client?"
                        packet = list(rencode_loads(data))
                    else:
                        #if sys.version>='3':
                        #    data = data.decode("latin1")
                        packet, l = bdecode(data)
                        assert l == len(data)
                except ValueError, e:
                    log.error("value error reading packet: %s",
                              e,
                              exc_info=True)
                    if self._closed:
                        return
                    log("failed to parse packet: %s", binascii.hexlify(data))
                    msg = "gibberish received: %s, packet index=%s, packet size=%s, buffer size=%s, error=%s" % (
                        repr_ellipsized(data), packet_index, payload_size, bl,
                        e)
                    self.gibberish(msg, data)
                    return

                if self._closed:
                    return
                payload_size = -1
                padding = None
                #add any raw packets back into it:
                if raw_packets:
                    for index, raw_data in raw_packets.items():
                        #replace placeholder with the raw_data packet data:
                        packet[index] = raw_data
                    raw_packets = {}

                packet_type = packet[0]
                if self.receive_aliases and type(
                        packet_type
                ) == int and packet_type in self.receive_aliases:
                    packet_type = self.receive_aliases.get(packet_type)
                    packet[0] = packet_type
                self.input_stats[packet_type] = self.output_stats.get(
                    packet_type, 0) + 1

                self.input_packetcount += 1
                log("processing packet %s", packet_type)
                self._process_packet_cb(self, packet)
Example #5
0
    def do_read_parse_thread_loop(self):
        """
            Process the individual network packets placed in _read_queue.
            We concatenate them, then try to parse them.
            We extract the individual packets from the potentially large buffer,
            saving the rest of the buffer for later, and optionally decompress this data
            and re-construct the one python-object-packet from potentially multiple packets (see packet_index).
            The 8 bytes packet header gives us information on the packet index, packet size and compression.
            The actual processing of the packet is done in the main thread via gobject.idle_add
        """
        read_buffer = None
        payload_size = -1
        padding = None
        packet_index = 0
        compression_level = False
        raw_packets = {}
        while not self._closed:
            buf = self._read_queue.get()
            if not buf:
                debug("read thread: empty marker, exiting")
                scheduler.idle_add(self.close)
                return
            if read_buffer:
                read_buffer = read_buffer + buf
            else:
                read_buffer = buf
            bl = len(read_buffer)
            while not self._closed:
                bl = len(read_buffer)
                if bl<=0:
                    break
                if payload_size<0:
                    if read_buffer[0] not in ("P", ord("P")):
                        err = "invalid packet header byte: '%s', not an xpra client?" % hex(ord(read_buffer[0]))
                        if len(read_buffer)>1:
                            err += " read buffer=0x%s" % repr_ellipsized(read_buffer)
                            if len(read_buffer)>40:
                                err += "..."
                        self._gibberish(err, read_buffer[:8])
                        return
                    if bl<8:
                        break   #packet still too small
                    #packet format: struct.pack('cBBBL', ...) - 8 bytes
                    try:
                        _, protocol_flags, compression_level, packet_index, data_size = unpack_header(read_buffer[:8])
                    except Exception, e:
                        self._gibberish("failed to parse packet header: 0x%s: %s" % (repr_ellipsized(read_buffer[:8]), e), read_buffer[:8])
                        return
                    read_buffer = read_buffer[8:]
                    bl = len(read_buffer)
                    if protocol_flags & Protocol.FLAGS_CIPHER:
                        assert self.cipher_in_block_size>0, "received cipher block but we don't have a cipher do decrypt it with"
                        padding = (self.cipher_in_block_size - data_size % self.cipher_in_block_size) * " "
                        payload_size = data_size + len(padding)
                    else:
                        #no cipher, no padding:
                        padding = None
                        payload_size = data_size
                    assert payload_size>0

                if payload_size>self.max_packet_size:
                    #this packet is seemingly too big, but check again from the main UI thread
                    #this gives 'set_max_packet_size' a chance to run from "hello"
                    def check_packet_size(size_to_check, packet_header):
                        if not self._closed:
                            debug("check_packet_size(%s, 0x%s) limit is %s", size_to_check, repr_ellipsized(packet_header), self.max_packet_size)
                            if size_to_check>self.max_packet_size:
                                self._call_connection_lost("invalid packet: size requested is %s (maximum allowed is %s - packet header: 0x%s), dropping this connection!" %
                                                              (size_to_check, self.max_packet_size, repr_ellipsized(packet_header)))
                        return False
                    scheduler.timeout_add(1000, check_packet_size, payload_size, read_buffer[:32])

                if bl<payload_size:
                    # incomplete packet, wait for the rest to arrive
                    break

                #chop this packet from the buffer:
                if len(read_buffer)==payload_size:
                    raw_string = read_buffer
                    read_buffer = ''
                else:
                    raw_string = read_buffer[:payload_size]
                    read_buffer = read_buffer[payload_size:]
                #decrypt if needed:
                data = raw_string
                if self.cipher_in and protocol_flags & Protocol.FLAGS_CIPHER:
                    debug("received %s encrypted bytes with %s padding", payload_size, len(padding))
                    data = self.cipher_in.decrypt(raw_string)
                    if padding:
                        def debug_str(s):
                            try:
                                return list(bytearray(s))
                            except:
                                return list(str(s))
                        if not data.endswith(padding):
                            log("decryption failed: string does not end with '%s': %s (%s) -> %s (%s)",
                            padding, debug_str(raw_string), type(raw_string), debug_str(data), type(data))
                            self._connection_lost("encryption error (wrong key?)")
                            return
                        data = data[:-len(padding)]
                #uncompress if needed:
                if compression_level>0:
                    if self.chunked_compression:
                        data = decompress(data)
                    else:
                        data = self._decompressor.decompress(data)

                if self.cipher_in and not (protocol_flags & Protocol.FLAGS_CIPHER):
                    return self._call_connection_lost("unencrypted packet dropped: %s" % repr_ellipsized(data))

                if self._closed:
                    return
                if packet_index>0:
                    #raw packet, store it and continue:
                    raw_packets[packet_index] = data
                    payload_size = -1
                    packet_index = 0
                    if len(raw_packets)>=4:
                        return self._call_connection_lost("too many raw packets: %s" % len(raw_packets))
                    continue
                #final packet (packet_index==0), decode it:
                try:
                    if protocol_flags & Protocol.FLAGS_RENCODE:
                        assert has_rencode, "we don't support rencode mode but the other end sent us a rencoded packet!"
                        packet = list(rencode_loads(data))
                    else:
                        #if sys.version>='3':
                        #    data = data.decode("latin1")
                        packet, l = bdecode(data)
                        assert l==len(data)
                except ValueError, e:
                    log.error("value error reading packet: %s", e, exc_info=True)
                    if self._closed:
                        return
                    msg = "gibberish received: %s, packet index=%s, packet size=%s, buffer size=%s, error=%s" % (repr_ellipsized(data), packet_index, payload_size, bl, e)
                    self._gibberish(msg, data)
                    return

                if self._closed:
                    return
                payload_size = -1
                padding = None
                #add any raw packets back into it:
                if raw_packets:
                    for index,raw_data in raw_packets.items():
                        #replace placeholder with the raw_data packet data:
                        packet[index] = raw_data
                    raw_packets = {}

                self.input_packetcount += 1
                self._process_packet_cb(self, packet)
Example #6
0
def test_hello():
    data = {
        'pycrypto.version':
        '2.6',
        'bell':
        True,
        'cursor.default_size':
        66L,
        'platform.release':
        '3.11.3-201.fc19.x86_64',
        'lz4':
        False,
        'encoding.vpx.version':
        6,
        'sound.receive':
        True,
        'digest': ('hmac', 'xor'),
        'aliases': {
            'suspend': 13,
            'encoding': 14,
            'desktop_size': 15,
            'damage-sequence': 10,
            'focus': 16,
            'unmap-window': 17,
            'connection-lost': 21,
            'jpeg-quality': 19,
            'min-speed': 20,
            'ping_echo': 8,
            'keymap-changed': 18,
            'shutdown-server': 22,
            'speed': 23,
            'close-window': 24,
            'server-settings': 25,
            'set-clipboard-enabled': 7,
            'quality': 27,
            'ping': 9,
            'set-cursors': 11,
            'resize-window': 28,
            'set_deflate': 29,
            'key-repeat': 30,
            'layout-changed': 31,
            'set-keyboard-sync-enabled': 12,
            'sound-control': 32,
            'screenshot': 33,
            'resume': 34,
            'sound-data': 35,
            'pointer-position': 36,
            'disconnect': 26,
            'button-action': 37,
            'map-window': 38,
            'buffer-refresh': 39,
            'info-request': 40,
            'set-notify': 5,
            'configure-window': 41,
            'set-bell': 6,
            'min-quality': 42,
            'gibberish': 43,
            'hello': 44,
            'key-action': 45,
            'move-window': 46
        },
        'encodings.with_quality': ['jpeg', 'webp'],
        'change-quality':
        True,
        'window_unmap':
        True,
        'encodings.core':
        ['rgb24', 'rgb32', 'png', 'png/L', 'png/P', 'jpeg', 'webp', 'vpx'],
        'actual_desktop_size': (2560, 1600),
        'encoding':
        'png',
        'root_window_size': (2560, 1600),
        'encoding.PIL.version':
        '1.1.7',
        'platform':
        'linux2',
        'gid':
        1000,
        'resize_screen':
        True,
        'chunked_compression':
        True,
        'sound.pygst.version': (0, 10, 22),
        'sound.send':
        True,
        'encoding.x264.version':
        130L,
        'encodings.with_speed': ['png', 'png/L', 'png/P', 'jpeg'],
        'auto_refresh_delay':
        250,
        'client_window_properties':
        True,
        'start_time':
        1381225490,
        'build.cpu':
        'x86_64',
        'notifications':
        False,
        'server_type':
        'base',
        'platform.machine':
        'x86_64',
        'build.on':
        'desktop',
        'MARKER':
        '***********************************************************************************',
        'rencode':
        True,
        'window_configure':
        True,
        'gtk.version': (2, 24, 19),
        'window.raise':
        True,
        'encodings.lossless':
        ['rgb24', 'rgb32', 'png', 'png/L', 'png/P', 'webp'],
        'platform.name':
        'Linux',
        'build.revision':
        '4193',
        'modifier_keycodes': {
            'control': [('Control_L', 'Control_L'),
                        ('Control_R', 'Control_R')],
            'mod1': [(64, 'Alt_L'), ('Alt_L', 'Alt_L'), ('Meta_L', 'Meta_L')],
            'mod2': [('Num_Lock', 'Num_Lock')],
            'mod3': [],
            'mod4': [(133, 'Super_L'), ('Super_R', 'Super_R'),
                     ('Super_L', 'Super_L'), ('Hyper_L', 'Hyper_L')],
            'mod5': [(92, 'ISO_Level3_Shift'),
                     ('Multi_key', 'ISO_Level3_Shift'),
                     ('Mode_switch', 'Mode_switch')],
            'shift': [('Shift_L', 'Shift_L'), ('Shift_R', 'Shift_R')],
            'lock': [('Caps_Lock', 'Caps_Lock')]
        },
        'sound.pulseaudio.server':
        '',
        'build.by':
        'root',
        'display':
        ':10',
        'python.version': (2, 7, 5),
        'uid':
        1000,
        'desktop_size': (2560, 1600),
        'pid':
        21794,
        'sound_sequence':
        True,
        'change-speed':
        True,
        'sound.pulseaudio.id':
        '',
        'cursors':
        True,
        'encodings': ['png/L', 'vpx', 'jpeg', 'webp', 'rgb', 'png/P', 'png'],
        'sound.decoders': ['mp3', 'wavpack', 'wav', 'flac', 'speex'],
        'rencode.version':
        '1.0.2',
        'current_time':
        1381225493,
        'hostname':
        'desktop',
        'encodings.with_lossless_mode': ['webp'],
        'key_repeat': [500, 30],
        'elapsed_time':
        3,
        'version':
        '0.11.0',
        'build.bit':
        '64bit',
        'suspend-resume':
        True,
        'platform.linux_distribution':
        ('Fedora', '19', 'Schr\xc3\xb6dinger\xe2\x80\x99s Cat'),
        'max_desktop_size': (5120, 3200),
        'sound.encoders': ['mp3', 'wavpack', 'wav', 'flac', 'speex'],
        'clipboard':
        True,
        'encoding.avcodec.version':
        'Lavc54.92.100',
        'platform.platform':
        'Linux-3.11.3-201.fc19.x86_64-x86_64-with-fedora-19-Schr\xc3\xb6dinger\xe2\x80\x99s_Cat',
        'notify-startup-complete':
        True,
        'toggle_keyboard_sync':
        True,
        'xsettings-tuple':
        True,
        'change-min-speed':
        True,
        'clipboards': ['CLIPBOARD', 'PRIMARY', 'SECONDARY'],
        'pygtk.version': (2, 24, 0),
        'change-min-quality':
        True,
        'byteorder':
        'little',
        'build.date':
        '2013-10-08',
        'python.full_version':
        '2.7.5 (default, Aug 22 2013, 09:31:58) \n[GCC 4.8.1 20130603 (Red Hat 4.8.1-1)]',
        'toggle_cursors_bell_notify':
        True,
        'encoding.opencl.version':
        '2013.1',
        'encoding.webp.version':
        '0.2.2',
        'key_repeat_modifiers':
        True,
        'raw_packets':
        True,
        'build.local_modifications':
        '1',
        'platform.processor':
        'x86_64',
        'sound.gst.version': (0, 10, 36),
        'encoding.swscale.version':
        'SwS2.2.100',
        'mmap_enabled':
        False
    }
    s = rencode_dumps(data)
    #import binascii
    #print("rencode(%s)=%s" % (data, binascii.hexlify(s)))
    l = rencode_loads(s)
    #print("rdecode(%s)=%s" % (binascii.hexlify(s), l))
    #print("original has %s keys, reloaded has %s keys" % (len(data), len(l)))
    errs = compare_dict(data, l)
    return len(errs) == 0
Example #7
0
    def do_read_parse_thread_loop(self):
        """
            Process the individual network packets placed in _read_queue.
            We concatenate them, then try to parse them.
            We extract the individual packets from the potentially large buffer,
            saving the rest of the buffer for later, and optionally decompress this data
            and re-construct the one python-object-packet from potentially multiple packets (see packet_index).
            The 8 bytes packet header gives us information on the packet index, packet size and compression.
            The actual processing of the packet is done in the main thread via gobject.idle_add
        """
        read_buffer = None
        payload_size = -1
        padding = None
        packet_index = 0
        compression_level = False
        raw_packets = {}
        while not self._closed:
            buf = self._read_queue.get()
            if not buf:
                debug("read thread: empty marker, exiting")
                scheduler.idle_add(self.close)
                return
            if read_buffer:
                read_buffer = read_buffer + buf
            else:
                read_buffer = buf
            bl = len(read_buffer)
            while not self._closed:
                bl = len(read_buffer)
                if bl <= 0:
                    break
                if payload_size < 0:
                    if read_buffer[0] not in ("P", ord("P")):
                        err = "invalid packet header byte: '%s', not an xpra client?" % hex(
                            ord(read_buffer[0]))
                        if len(read_buffer) > 1:
                            err += " read buffer=0x%s" % repr_ellipsized(
                                read_buffer)
                            if len(read_buffer) > 40:
                                err += "..."
                        self._gibberish(err, read_buffer[:8])
                        return
                    if bl < 8:
                        break  #packet still too small
                    #packet format: struct.pack('cBBBL', ...) - 8 bytes
                    try:
                        _, protocol_flags, compression_level, packet_index, data_size = unpack_header(
                            read_buffer[:8])
                    except Exception, e:
                        self._gibberish(
                            "failed to parse packet header: 0x%s: %s" %
                            (repr_ellipsized(read_buffer[:8]), e),
                            read_buffer[:8])
                        return
                    read_buffer = read_buffer[8:]
                    bl = len(read_buffer)
                    if protocol_flags & Protocol.FLAGS_CIPHER:
                        assert self.cipher_in_block_size > 0, "received cipher block but we don't have a cipher do decrypt it with"
                        padding = (self.cipher_in_block_size -
                                   data_size % self.cipher_in_block_size) * " "
                        payload_size = data_size + len(padding)
                    else:
                        #no cipher, no padding:
                        padding = None
                        payload_size = data_size
                    assert payload_size > 0

                if payload_size > self.max_packet_size:
                    #this packet is seemingly too big, but check again from the main UI thread
                    #this gives 'set_max_packet_size' a chance to run from "hello"
                    def check_packet_size(size_to_check, packet_header):
                        if not self._closed:
                            debug("check_packet_size(%s, 0x%s) limit is %s",
                                  size_to_check,
                                  repr_ellipsized(packet_header),
                                  self.max_packet_size)
                            if size_to_check > self.max_packet_size:
                                self._call_connection_lost(
                                    "invalid packet: size requested is %s (maximum allowed is %s - packet header: 0x%s), dropping this connection!"
                                    % (size_to_check, self.max_packet_size,
                                       repr_ellipsized(packet_header)))
                        return False

                    scheduler.timeout_add(1000, check_packet_size,
                                          payload_size, read_buffer[:32])

                if bl < payload_size:
                    # incomplete packet, wait for the rest to arrive
                    break

                #chop this packet from the buffer:
                if len(read_buffer) == payload_size:
                    raw_string = read_buffer
                    read_buffer = ''
                else:
                    raw_string = read_buffer[:payload_size]
                    read_buffer = read_buffer[payload_size:]
                #decrypt if needed:
                data = raw_string
                if self.cipher_in and protocol_flags & Protocol.FLAGS_CIPHER:
                    debug("received %s encrypted bytes with %s padding",
                          payload_size, len(padding))
                    data = self.cipher_in.decrypt(raw_string)
                    if padding:

                        def debug_str(s):
                            try:
                                return list(bytearray(s))
                            except:
                                return list(str(s))

                        if not data.endswith(padding):
                            log(
                                "decryption failed: string does not end with '%s': %s (%s) -> %s (%s)",
                                padding, debug_str(raw_string),
                                type(raw_string), debug_str(data), type(data))
                            self._connection_lost(
                                "encryption error (wrong key?)")
                            return
                        data = data[:-len(padding)]
                #uncompress if needed:
                if compression_level > 0:
                    if self.chunked_compression:
                        data = decompress(data)
                    else:
                        data = self._decompressor.decompress(data)

                if self.cipher_in and not (protocol_flags
                                           & Protocol.FLAGS_CIPHER):
                    return self._call_connection_lost(
                        "unencrypted packet dropped: %s" %
                        repr_ellipsized(data))

                if self._closed:
                    return
                if packet_index > 0:
                    #raw packet, store it and continue:
                    raw_packets[packet_index] = data
                    payload_size = -1
                    packet_index = 0
                    if len(raw_packets) >= 4:
                        return self._call_connection_lost(
                            "too many raw packets: %s" % len(raw_packets))
                    continue
                #final packet (packet_index==0), decode it:
                try:
                    if protocol_flags & Protocol.FLAGS_RENCODE:
                        assert has_rencode, "we don't support rencode mode but the other end sent us a rencoded packet!"
                        packet = list(rencode_loads(data))
                    else:
                        #if sys.version>='3':
                        #    data = data.decode("latin1")
                        packet, l = bdecode(data)
                        assert l == len(data)
                except ValueError, e:
                    log.error("value error reading packet: %s",
                              e,
                              exc_info=True)
                    if self._closed:
                        return
                    msg = "gibberish received: %s, packet index=%s, packet size=%s, buffer size=%s, error=%s" % (
                        repr_ellipsized(data), packet_index, payload_size, bl,
                        e)
                    self._gibberish(msg, data)
                    return

                if self._closed:
                    return
                payload_size = -1
                padding = None
                #add any raw packets back into it:
                if raw_packets:
                    for index, raw_data in raw_packets.items():
                        #replace placeholder with the raw_data packet data:
                        packet[index] = raw_data
                    raw_packets = {}

                self.input_packetcount += 1
                self._process_packet_cb(self, packet)