xpra icon
Bug tracker and wiki

Ticket #2139: protocol-noappend-v1.patch

File protocol-noappend-v1.patch, 10.1 KB (added by Antoine Martin, 9 months ago)

poc

  • xpra/net/protocol.py

     
    793793            this will be called from this parsing thread so any calls that need to be made
    794794            from the UI thread will need to use a callback (usually via 'idle_add')
    795795        """
    796         read_buffer = None
    797         packet_size = 0
     796        header = None
     797        read_buffers = []
    798798        payload_size = -1
    799799        padding_size = 0
    800800        packet_index = 0
    801         compression_level = False
    802         packet = None
     801        compression_level = 0
    803802        raw_packets = {}
    804803        while not self._closed:
    805804            buf = self._read_queue.get()
     805            log.info("parse thread: got %i bytes", len(buf))
    806806            if not buf:
    807807                log("parse thread: empty marker, exiting")
    808808                self.idle_add(self.close)
    809809                return
    810             if read_buffer:
    811                 read_buffer = read_buffer + buf
    812             else:
    813                 read_buffer = buf
    814             while not self._closed:
    815                 packet = None
    816                 bl = len(read_buffer)
    817                 if bl<=0:
    818                     break
     810
     811            read_buffers.append(buf)
     812            while read_buffers:
     813                #have we read the header yet?
    819814                if payload_size<0:
    820                     if read_buffer[0] not in ("P", ord("P")):
    821                         self._invalid_header(read_buffer, "invalid packet header byte %s" % read_buffer[0])
    822                         return
    823                     if bl<HEADER_SIZE:
    824                         break   #packet still too small
    825                     #packet format: struct.pack(b'cBBBL', ...) - HEADER_SIZE bytes
    826                     _, protocol_flags, compression_level, packet_index, data_size = unpack_header(read_buffer[:HEADER_SIZE])
    827 
     815                    buf = read_buffers.pop(0)
     816                    log("parse thread: parsing header, %i already, %i new bytes, %i read_buffers", len(header or b""), len(buf), len(read_buffers))
     817                    if header is None:
     818                        if buf[0] not in ("P", ord("P")):
     819                            self._invalid_header(buf, "invalid packet header byte %s" % buf)
     820                            return
     821                        read = min(len(buf), HEADER_SIZE)
     822                        header = buf[:read]
     823                    else:
     824                        read = HEADER_SIZE-len(header)
     825                        header += buf[:read]
     826                    if len(header)<HEADER_SIZE:
     827                        log("not enough for a full header: only %i bytes", len(header))
     828                        break
     829                    elif len(buf)>read:
     830                        #add the rest of the packet:
     831                        log("left over added to read_buffers: %i bytes", len(buf)-read)
     832                        read_buffers.append(buf[read:])
     833                    #parse the header:
     834                    # format: struct.pack(b'cBBBL', ...) - HEADER_SIZE bytes
     835                    _, protocol_flags, compression_level, packet_index, data_size = unpack_header(header)
     836                    log("parsed header: %i data size, %i read_buffers: %s", data_size, len(read_buffers), csv(len(v) for v in read_buffers))
     837   
    828838                    #sanity check size (will often fail if not an xpra client):
    829839                    if data_size>self.abs_max_packet_size:
    830                         self._invalid_header(read_buffer, "invalid size in packet header: %s" % data_size)
     840                        self._invalid_header(header, "invalid size in packet header: %s" % data_size)
    831841                        return
    832 
    833                     bl = len(read_buffer)-HEADER_SIZE
     842   
    834843                    if protocol_flags & FLAGS_CIPHER:
    835844                        if self.cipher_in_block_size==0 or not self.cipher_in_name:
    836845                            cryptolog.warn("received cipher block but we don't have a cipher to decrypt it with, not an xpra client?")
    837                             self._invalid_header(read_buffer, "invalid encryption packet flag (no cipher configured)")
     846                            self._invalid_header(header, "invalid encryption packet flag (no cipher configured)")
    838847                            return
    839848                        padding_size = self.cipher_in_block_size - (data_size % self.cipher_in_block_size)
    840849                        payload_size = data_size + padding_size
     
    843852                        padding_size = 0
    844853                        payload_size = data_size
    845854                    assert payload_size>0, "invalid payload size: %i" % payload_size
    846                     read_buffer = read_buffer[HEADER_SIZE:]
    847 
     855   
    848856                    if payload_size>self.max_packet_size:
    849857                        #this packet is seemingly too big, but check again from the main UI thread
    850858                        #this gives 'set_max_packet_size' a chance to run from "hello"
     
    857865                                              (size_to_check, self.max_packet_size)
    858866                                self.invalid(msg, packet_header)
    859867                            return False
    860                         self.timeout_add(1000, check_packet_size, payload_size, read_buffer[:32])
    861 
     868                        self.timeout_add(1000, check_packet_size, payload_size, header)
     869   
     870                #how much data do we have?
     871                bl = sum(len(buf) for buf in read_buffers)
     872                log("got %i bytes from %i buffers: %s", bl, len(read_buffers), csv(len(x) for x in read_buffers))
    862873                if bl<payload_size:
    863874                    # incomplete packet, wait for the rest to arrive
    864875                    break
    865 
    866                 #chop this packet from the buffer:
    867                 if len(read_buffer)==payload_size:
    868                     raw_string = read_buffer
    869                     read_buffer = ''
     876   
     877                if len(read_buffers[0])==payload_size:
     878                    data = read_buffers.pop(0)
     879                    log("popped first buffer, left: %s", csv(len(x) for x in read_buffers))
     880                elif len(read_buffers[0])>payload_size:
     881                    data = read_buffers[0][:payload_size]
     882                    read_buffers[0] = read_buffers[0][payload_size:]
     883                    log("sliced first buffer, left: %s", csv(len(x) for x in read_buffers))
    870884                else:
    871                     raw_string = read_buffer[:payload_size]
    872                     read_buffer = read_buffer[payload_size:]
    873                 packet_size += HEADER_SIZE + payload_size
     885                    data = b"".join(read_buffers)
     886                    if bl==payload_size:
     887                        #nothing left:
     888                        read_buffers = []
     889                        log("consumed all buffers")
     890                    else:
     891                        #keep the left over:
     892                        read_buffers = [data[payload_size:]]
     893                        data = data[:payload_size]
     894                        log("aggregated all buffers, left: %s", csv(len(x) for x in read_buffers))
     895   
    874896                #decrypt if needed:
    875                 data = raw_string
    876                 if self.cipher_in and protocol_flags & FLAGS_CIPHER:
     897                if self.cipher_in:
     898                    if not (protocol_flags & FLAGS_CIPHER):
     899                        self.invalid("unencrypted packet dropped", data)
     900                        return
    877901                    cryptolog("received %i %s encrypted bytes with %s padding", payload_size, self.cipher_in_name, padding_size)
    878                     data = self.cipher_in.decrypt(raw_string)
     902                    data = self.cipher_in.decrypt(data)
    879903                    if padding_size > 0:
    880904                        def debug_str(s):
    881905                            try:
     
    914938                            msg += " %s" % e
    915939                        del e
    916940                        return self.gibberish(msg, data)
    917 
    918                 if self.cipher_in and not (protocol_flags & FLAGS_CIPHER):
    919                     self.invalid("unencrypted packet dropped", data)
    920                     return
    921 
     941   
    922942                if self._closed:
    923943                    return
     944   
     945                #we're processing this packet,
     946                #make sure we get a new header next time
     947                header = None
    924948                if packet_index>0:
    925949                    #raw packet, store it and continue:
    926950                    raw_packets[packet_index] = data
    927951                    payload_size = -1
    928                     packet_index = 0
    929952                    if len(raw_packets)>=4:
    930953                        self.invalid("too many raw packets: %s" % len(raw_packets), data)
    931954                        return
     
    948971                    log(" packet index=%i, packet size=%i, buffer size=%s", packet_index, payload_size, bl)
    949972                    self.gibberish("failed to parse %s packet" % etype, data)
    950973                    return
    951 
     974   
    952975                if self._closed:
    953976                    return
    954977                payload_size = -1
    955                 padding_size = 0
    956978                #add any raw packets back into it:
    957979                if raw_packets:
    958980                    for index,raw_data in raw_packets.items():
     
    959981                        #replace placeholder with the raw_data packet data:
    960982                        packet[index] = raw_data
    961983                    raw_packets = {}
    962 
     984   
    963985                packet_type = packet[0]
    964986                if self.receive_aliases and type(packet_type)==int and packet_type in self.receive_aliases:
    965987                    packet_type = self.receive_aliases.get(packet_type)
     
    966988                    packet[0] = packet_type
    967989                self.input_stats[packet_type] = self.output_stats.get(packet_type, 0)+1
    968990                if LOG_RAW_PACKET_SIZE:
    969                     log("%s: %i bytes", packet_type, packet_size)
    970                 packet_size = 0
    971 
     991                    log("%s: %i bytes", packet_type, HEADER_SIZE + payload_size)
     992   
    972993                self.input_packetcount += 1
    973994                log("processing packet %s", bytestostr(packet_type))
    974995                self._process_packet_cb(self, packet)