xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Ticket #639: udp-v7.patch

File udp-v7.patch, 145.7 KB (added by Antoine Martin, 4 years ago)

add some authentication refactoring

  • xpra/client/client_base.py

     
    232232        raise NotImplementedError()
    233233
    234234    def setup_connection(self, conn):
    235         netlog("setup_connection(%s) timeout=%s", conn, conn.timeout)
    236         self._protocol = Protocol(self.get_scheduler(), conn, self.process_packet, self.next_packet)
     235        netlog.info("setup_connection(%s) timeout=%s, socktype=%s", conn, conn.timeout, conn.socktype)
     236        if conn.socktype in ("udp", "dtls"):
     237            from xpra.net.udp_protocol import UDPClientProtocol
     238            self._protocol = UDPClientProtocol(self.get_scheduler(), conn, self.process_packet, self.next_packet)
     239            self.set_packet_handlers(self._packet_handlers, {
     240                "udp-control"   : self._process_udp_control,
     241                })
     242        else:
     243            self._protocol = Protocol(self.get_scheduler(), conn, self.process_packet, self.next_packet)
    237244        self._protocol.large_packets.append("keymap-changed")
    238245        self._protocol.large_packets.append("server-settings")
    239246        self._protocol.large_packets.append("logging")
     
    254261            getChildReaper().add_process(proc, name, command, ignore=True, forget=False)
    255262        netlog("setup_connection(%s) protocol=%s", conn, self._protocol)
    256263
     264    def _process_udp_control(self, packet):
     265        self._protocol.process_control(*packet[1:])
    257266
     267
    258268    def remove_packet_handlers(self, *keys):
    259269        for k in keys:
    260270            for d in (self._packet_handlers, self._ui_packet_handlers):
     
    440450            p.source_has_more()
    441451
    442452    def next_packet(self):
     453        netlog("next_packet() packets in queues: priority=%i, ordinary=%i, mouse=%s", len(self._priority_packets), len(self._ordinary_packets), bool(self._mouse_position))
    443454        if self._priority_packets:
    444455            packet = self._priority_packets.pop(0)
    445456        elif self._ordinary_packets:
     
    452463        has_more = packet is not None and \
    453464                (bool(self._priority_packets) or bool(self._ordinary_packets) \
    454465                 or self._mouse_position is not None)
    455         return packet, None, None, has_more
     466        return packet, None, None, None, has_more
    456467
    457468
    458469    def cleanup(self):
  • xpra/client/gtk_base/gtk_client_window_base.py

     
    550550        def do_set_shape():
    551551            xid = get_xid(self.get_window())
    552552            x_off, y_off = shape.get("x", 0), shape.get("y", 0)
    553             for kind, name in SHAPE_KIND.items():
     553            for kind, name in SHAPE_KIND.items():       #@UndefinedVariable
    554554                rectangles = shape.get("%s.rectangles" % name)      #ie: Bounding.rectangles = [(0, 0, 150, 100)]
    555555                if rectangles:
    556556                    #adjust for scaling:
  • xpra/client/ui_client_base.py

     
    3939webcamlog = Logger("webcam")
    4040notifylog = Logger("notify")
    4141cursorlog = Logger("cursor")
     42netlog = Logger("network")
    4243
    4344
    4445from xpra.gtk_common.gobject_util import no_arg_signal
     
    16971698        if FAKE_BROKEN_CONNECTION>0:
    16981699            self._server_ok = (int(monotonic_time()) % FAKE_BROKEN_CONNECTION) <= (FAKE_BROKEN_CONNECTION//2)
    16991700        else:
    1700             self._server_ok = not FAKE_BROKEN_CONNECTION and self.last_ping_echoed_time>=ping_sent_time
    1701         log("check_server_echo(%s) last=%s, server_ok=%s", ping_sent_time, last, self._server_ok)
     1701            self._server_ok = self.last_ping_echoed_time>=ping_sent_time
     1702        log("check_server_echo(%s) last=%s, server_ok=%s (last_ping_echoed_time=%s)", ping_sent_time, last, self._server_ok, self.last_ping_echoed_time)
    17021703        if last!=self._server_ok and not self._server_ok:
    17031704            log.info("server is not responding, drawing spinners over the windows")
    17041705            def timer_redraw():
     
    17241725                w.spinner(ok)
    17251726
    17261727    def check_echo_timeout(self, ping_time):
    1727         log("check_echo_timeout(%s) last_ping_echoed_time=%s", ping_time, self.last_ping_echoed_time)
     1728        netlog("check_echo_timeout(%s) last_ping_echoed_time=%s", ping_time, self.last_ping_echoed_time)
    17281729        if self.last_ping_echoed_time<ping_time:
    17291730            #no point trying to use disconnect_and_quit() to tell the server here..
    17301731            self.warn_and_quit(EXIT_TIMEOUT, "server ping timeout - waited %s seconds without a response" % PING_TIMEOUT)
     
    17381739            l = [x for _,x in list(self.server_ping_latency)]
    17391740            avg = sum(l) / len(l)
    17401741            wait = min(5, 1.0+avg*2.0)
    1741             log("average server latency=%.1f, using max wait %.2fs", 1000.0*avg, wait)
     1742            netlog("send_ping() timestamp=%s, average server latency=%.1f, using max wait %.2fs", now_ms, 1000.0*avg, wait)
    17421743        self.timeout_add(int(1000.0*wait), self.check_server_echo, now_ms)
    17431744        return True
    17441745
     
    17511752        self.server_load = l1, l2, l3
    17521753        if cl>=0:
    17531754            self.client_ping_latency.append((monotonic_time(), cl/1000.0))
    1754         log("ping echo server load=%s, measured client latency=%sms", self.server_load, cl)
     1755        netlog("ping echo server load=%s, measured client latency=%sms", self.server_load, cl)
    17551756
    17561757    def _process_ping(self, packet):
    17571758        echotime = packet[1]
  • xpra/net/bytestreams.py

     
    9898            return f(*a, **kw)
    9999        except Exception as e:
    100100            retry = can_retry(e)
     101            log("untilConcludes", exc_info=True)
    101102            log("untilConcludes(%s, %s, %s, %s, %s) %s, retry=%s", is_active_cb, can_retry, f, a, kw, e, retry)
    102103            if retry:
    103104                if wait>0:
     
    277278            i = s
    278279        log("%s.close() for socket=%s", self, i)
    279280        Connection.close(self)
    280         s.settimeout(0)
     281        try:
     282            s.settimeout(0)
     283        except:
     284            pass
    281285        #this is more proper but would break the proxy server:
    282286        #s.shutdown(socket.SHUT_RDWR)
    283287        s.close()
     
    306310        s = self._socket
    307311        if not s:
    308312            return None
    309         return {
    310                 #"class"         : str(type(s)),
    311                 "fileno"        : s.fileno(),
    312                 "timeout"       : int(1000*(s.gettimeout() or 0)),
     313        info = {
    313314                "family"        : FAMILY_STR.get(s.family, s.family),
    314315                "proto"         : s.proto,
    315316                "type"          : PROTOCOL_STR.get(s.type, s.type),
    316317                }
     318        try:
     319            info["timeout"] = int(1000*(s.gettimeout() or 0))
     320        except:
     321            pass
     322        if hasattr(s, "fileno"):
     323            info["fileno"] = s.fileno()
     324        return info
    317325
    318 
    319326try:
    320327    #this wrapper class allows us to override the normal ssl.Socket
    321328    #class so that we can fake peek() support by actually reading from the socket
  • xpra/net/protocol.py

     
    5959    packet_encoding_sanity_checks()
    6060
    6161
     62def exit_queue():
     63    queue = Queue()
     64    for _ in range(10):     #just 2 should be enough!
     65        queue.put(None)
     66    return queue
     67
     68def force_flush_queue(q):
     69    try:
     70        #discard all elements in the old queue and push the None marker:
     71        try:
     72            while q.qsize()>0:
     73                q.read(False)
     74        except:
     75            pass
     76        q.put_nowait(None)
     77    except:
     78        pass
     79
    6280class Protocol(object):
     81    """
     82        This class handles sending and receiving packets,
     83        it will encode and compress them before sending,
     84        and decompress and decode when receiving.
     85    """
     86
    6387    CONNECTION_LOST = "connection-lost"
    6488    GIBBERISH = "gibberish"
    6589    INVALID = "invalid"
     
    7296        assert conn is not None
    7397        self.timeout_add = scheduler.timeout_add
    7498        self.idle_add = scheduler.idle_add
     99        self.source_remove = scheduler.source_remove
    75100        self._conn = conn
    76101        if FAKE_JITTER>0:
    77102            from xpra.net.fake_jitter import FakeJitter
     
    81106            self._process_packet_cb = process_packet_cb
    82107        self._write_queue = Queue(1)
    83108        self._read_queue = Queue(20)
     109        self._process_read = self.read_queue_put
    84110        self._read_queue_put = self.read_queue_put
    85111        # Invariant: if .source is None, then _source_has_more == False
    86112        self._get_packet_cb = get_packet_cb
     
    239265        if SEND_INVALID_PACKET:
    240266            self.timeout_add(SEND_INVALID_PACKET*1000, self.raw_write, SEND_INVALID_PACKET_DATA)
    241267
     268
     269    def send_disconnect(self, reason, *extra):
     270        self.send_now(["disconnect", reason]+list(extra))
     271
    242272    def send_now(self, packet):
    243273        if self._closed:
    244274            log("send_now(%s ...) connection is closed already, not sending", packet[0])
     
    279309                return
    280310            self._internal_error("error in network packet write/format", e, exc_info=True)
    281311
    282     def _add_packet_to_queue(self, packet, start_send_cb=None, end_send_cb=None, has_more=False):
     312    def _add_packet_to_queue(self, packet, start_send_cb=None, end_send_cb=None, fail_cb=None, has_more=False):
    283313        if has_more:
    284314            self._source_has_more.set()
    285315        if packet is None:
     
    290320            if self._closed:
    291321                return
    292322            try:
    293                 self._add_chunks_to_queue(chunks, start_send_cb, end_send_cb)
     323                self._add_chunks_to_queue(chunks, start_send_cb, end_send_cb, fail_cb)
    294324            except:
    295325                log.error("Error: failed to queue '%s' packet", packet[0])
    296                 log("add_chunks_to_queue%s", (chunks, start_send_cb, end_send_cb), exc_info=True)
     326                log("add_chunks_to_queue%s", (chunks, start_send_cb, end_send_cb, fail_cb), exc_info=True)
    297327                raise
    298328
    299     def _add_chunks_to_queue(self, chunks, start_send_cb=None, end_send_cb=None):
     329    def _add_chunks_to_queue(self, chunks, start_send_cb=None, end_send_cb=None, fail_cb=None, synchronous=True):
    300330        """ the write_lock must be held when calling this function """
    301331        counter = 0
    302332        items = []
    303333        for proto_flags,index,level,data in chunks:
    304             scb, ecb = None, None
    305334            #fire the start_send_callback just before the first packet is processed:
    306             if counter==0:
    307                 scb = start_send_cb
    308335            #fire the end_send callback when the last packet (index==0) makes it out:
    309             if index==0:
    310                 ecb = end_send_cb
    311336            payload_size = len(data)
    312337            actual_size = payload_size
    313338            if self.cipher_out:
     
    328353                assert not self.cipher_out
    329354                #for plain/text packets (ie: gibberish response)
    330355                log("sending %s bytes without header", payload_size)
    331                 items.append((data, scb, ecb))
     356                items.append(data)
    332357            elif actual_size<PACKET_JOIN_SIZE:
    333                 if type(data) not in JOIN_TYPES:
     358                if not isinstance(data, JOIN_TYPES):
    334359                    data = memoryview_to_bytes(data)
    335360                header_and_data = pack_header(proto_flags, level, index, payload_size) + data
    336                 items.append((header_and_data, scb, ecb))
     361                items.append(header_and_data)
    337362            else:
    338363                header = pack_header(proto_flags, level, index, payload_size)
    339                 items.append((header, scb, None))
    340                 items.append((data, None, ecb))
     364                items.append(header)
     365                items.append(data)
    341366            counter += 1
    342367        if self._write_thread is None:
    343368            self.start_write_thread()
    344         self._write_queue.put(items)
    345         self.output_packetcount += 1
     369        self._write_queue.put((items, start_send_cb, end_send_cb, fail_cb, synchronous))
    346370
     371
    347372    def start_write_thread(self):
    348373        self._write_thread = start_thread(self._write_thread_loop, "write", daemon=True)
    349374
    350     def raw_write(self, contents, start_cb=None, end_cb=None):
     375    def raw_write(self, contents, start_cb=None, end_cb=None, fail_cb=None, synchronous=True):
    351376        """ Warning: this bypasses the compression and packet encoder! """
    352377        if self._write_thread is None:
    353378            self.start_write_thread()
    354         self._write_queue.put(((contents, start_cb, end_cb), ))
     379        self._write_queue.put((contents, start_cb, end_cb, fail_cb, synchronous))
    355380
    356381    def verify_packet(self, packet):
    357382        """ look for None values which may have caused the packet to fail encoding """
     
    539564        assert level>=0 and level<=10, "invalid compression level: %s (must be between 0 and 10" % level
    540565        self.compression_level = level
    541566
     567
    542568    def _io_thread_loop(self, name, callback):
    543569        try:
    544570            log("io_thread_loop(%s, %s) loop starting", name, callback)
     
    559585                log.error("Error: %s on %s failed: %s", name, self._conn, type(e), exc_info=True)
    560586                self.close()
    561587
     588
    562589    def _write_thread_loop(self):
    563590        self._io_thread_loop("write", self._write)
    564591    def _write(self):
     
    568595            log("write thread: empty marker, exiting")
    569596            self.close()
    570597            return False
    571         for buf, start_cb, end_cb in items:
    572             con = self._conn
    573             if not con:
    574                 return False
    575             if start_cb:
    576                 try:
    577                     start_cb(con.output_bytecount)
    578                 except:
    579                     if not self._closed:
    580                         log.error("Error on write start callback %s", start_cb, exc_info=True)
     598        return self.write_items(*items)
     599   
     600    def write_items(self, buf_data, start_cb=None, end_cb=None, fail_cb=None, synchronous=True):
     601        con = self._conn
     602        if not con:
     603            return False
     604        if start_cb:
     605            try:
     606                start_cb(con.output_bytecount)
     607            except:
     608                if not self._closed:
     609                    log.error("Error on write start callback %s", start_cb, exc_info=True)
     610        self.write_buffers(buf_data, fail_cb, synchronous)
     611        if end_cb:
     612            try:
     613                end_cb(self._conn.output_bytecount)
     614            except:
     615                if not self._closed:
     616                    log.error("Error on write end callback %s", end_cb, exc_info=True)
     617        return True
     618
     619    def write_buffers(self, buf_data, _fail_cb, _synchronous):
     620        con = self._conn
     621        if not con:
     622            return 0
     623        for buf in buf_data:
    581624            while buf and not self._closed:
    582625                written = con.write(buf)
    583626                #example test code, for sending small chunks very slowly:
     
    587630                if written:
    588631                    buf = buf[written:]
    589632                    self.output_raw_packetcount += 1
    590             if end_cb:
    591                 try:
    592                     end_cb(self._conn.output_bytecount)
    593                 except:
    594                     if not self._closed:
    595                         log.error("Error on write end callback %s", end_cb, exc_info=True)
    596         return True
     633        self.output_packetcount += 1
    597634
     635
    598636    def _read_thread_loop(self):
    599637        self._io_thread_loop("read", self._read)
    600638    def _read(self):
     
    601639        buf = self._conn.read(READ_BUFFER_SIZE)
    602640        #log("read thread: got data of size %s: %s", len(buf), repr_ellipsized(buf))
    603641        #add to the read queue (or whatever takes its place - see steal_connection)
    604         self._read_queue_put(buf)
     642        self._process_read(buf)
    605643        if not buf:
    606644            log("read thread: eof")
    607645            #give time to the parse thread to call close itself
     
    646684    def _invalid_header(self, data, msg=""):
    647685        self.invalid_header(self, data, msg)
    648686
    649     def invalid_header(self, proto, data, msg="invalid packet header"):
     687    def invalid_header(self, _proto, data, msg="invalid packet header"):
    650688        err = "%s: '%s'" % (msg, binascii.hexlify(data[:8]))
    651689        if len(data)>1:
    652690            err += " read buffer=%s (%i bytes)" % (repr_ellipsized(data), len(data))
     
    653691        self.gibberish(err, data)
    654692
    655693
     694    def process_read(self, data):
     695        self._read_queue_put(data)
     696
    656697    def read_queue_put(self, data):
    657698        #start the parse thread if needed:
    658699        if not self._read_parser_thread and not self._closed:
     
    660701                log("empty marker in read queue, exiting")
    661702                self.idle_add(self.close)
    662703                return
    663             self._read_parser_thread = make_thread(self._read_parse_thread_loop, "parse", daemon=True)
    664             self._read_parser_thread.start()
     704            self.start_read_parser_thread()
    665705        self._read_queue.put(data)
    666706        #from now on, take shortcut:
    667707        if self._read_queue_put==self.read_queue_put:
    668708            self._read_queue_put = self._read_queue.put
    669709
     710    def start_read_parser_thread(self):
     711        self._read_parser_thread = start_thread(self._read_parse_thread_loop, "parse", daemon=True)
     712
    670713    def _read_parse_thread_loop(self):
    671714        log("read_parse_thread_loop starting")
    672715        try:
     
    911954                        close_and_release()
    912955                        return False
    913956                    return not self._closed     #run until we manage to close (here or via the timeout)
    914                 def packet_queued(*args):
     957                def packet_queued(*_args):
    915958                    #if we're here, we have the lock and the packet is in the write queue
    916959                    log("flush_then_close: packet_queued() closed=%s", self._closed)
    917960                    if wait_for_packet_sent():
     
    952995        self.idle_add(self._process_packet_cb, self, [Protocol.CONNECTION_LOST])
    953996        c = self._conn
    954997        if c:
     998            self._conn = None
    955999            try:
    9561000                log("Protocol.close() calling %s", c.close)
    9571001                c.close()
    958                 if self._log_stats is None and self._conn.input_bytecount==0 and self._conn.output_bytecount==0:
     1002                if self._log_stats is None and c.input_bytecount==0 and c.output_bytecount==0:
    9591003                    #no data sent or received, skip logging of stats:
    9601004                    self._log_stats = False
    9611005                if self._log_stats:
    9621006                    from xpra.simple_stats import std_unit, std_unit_dec
    9631007                    log.info("connection closed after %s packets received (%s bytes) and %s packets sent (%s bytes)",
    964                          std_unit(self.input_packetcount), std_unit_dec(self._conn.input_bytecount),
    965                          std_unit(self.output_packetcount), std_unit_dec(self._conn.output_bytecount)
     1008                         std_unit(self.input_packetcount), std_unit_dec(c.input_bytecount),
     1009                         std_unit(self.output_packetcount), std_unit_dec(c.output_bytecount)
    9661010                         )
    9671011            except:
    968                 log.error("error closing %s", self._conn, exc_info=True)
    969             self._conn = None
     1012                log.error("error closing %s", c, exc_info=True)
    9701013        self.terminate_queue_threads()
    9711014        self.idle_add(self.clean)
    9721015        log("Protocol.close() done")
     
    10071050        self._get_packet_cb = None
    10081051        self._source_has_more.set()
    10091052        #make all the queue based threads exit by adding the empty marker:
    1010         exit_queue = Queue()
    1011         for _ in range(10):     #just 2 should be enough!
    1012             exit_queue.put(None)
    1013         try:
    1014             owq = self._write_queue
    1015             self._write_queue = exit_queue
    1016             #discard all elements in the old queue and push the None marker:
    1017             try:
    1018                 while owq.qsize()>0:
    1019                     owq.read(False)
    1020             except:
    1021                 pass
    1022             owq.put_nowait(None)
    1023         except:
    1024             pass
    1025         try:
    1026             orq = self._read_queue
    1027             self._read_queue = exit_queue
    1028             #discard all elements in the old queue and push the None marker:
    1029             try:
    1030                 while orq.qsize()>0:
    1031                     orq.read(False)
    1032             except:
    1033                 pass
    1034             orq.put_nowait(None)
    1035         except:
    1036             pass
     1053        #write queue:
     1054        owq = self._write_queue
     1055        self._write_queue = exit_queue()
     1056        force_flush_queue(owq)
     1057        #read queue:
     1058        orq = self._read_queue
     1059        self._read_queue = exit_queue()
     1060        force_flush_queue(orq)
    10371061        #just in case the read thread is waiting again:
    10381062        self._source_has_more.set()
  • xpra/net/rfb.py

     
    1 # This file is part of Xpra.
    2 # Copyright (C) 2017 Antoine Martin <antoine@devloop.org.uk>
    3 # Xpra is released under the terms of the GNU GPL v2, or, at your option, any
    4 # later version. See the file COPYING for details.
    5 
    6 import struct
    7 from socket import error as socket_error
    8 import binascii
    9 from threading import Lock
    10 
    11 
    12 from xpra.log import Logger
    13 log = Logger("network", "protocol", "rfb")
    14 
    15 from xpra.os_util import Queue
    16 from xpra.util import repr_ellipsized, envint
    17 from xpra.make_thread import make_thread, start_thread
    18 from xpra.net.common import ConnectionClosedException          #@UndefinedVariable (pydev false positive)
    19 from xpra.net.bytestreams import ABORT
    20 
    21 READ_BUFFER_SIZE = envint("XPRA_READ_BUFFER_SIZE", 65536)
    22 #merge header and packet if packet is smaller than:
    23 PIXEL_FORMAT = "BBBBHHHBBBBBB"
    24 
    25 RFB_SETPIXELFORMAT = 0
    26 RFB_SETENCODINGS = 2
    27 RFB_FRAMEBUFFERUPDATEREQUEST = 3
    28 RFB_KEYEVENT = 4
    29 RFB_POINTEREVENT = 5
    30 RFB_CLIENTCUTTEXT = 6
    31 PACKET_TYPE = {
    32     RFB_SETPIXELFORMAT              : "SetPixelFormat",
    33     RFB_SETENCODINGS                : "SetEncodings",
    34     RFB_FRAMEBUFFERUPDATEREQUEST    : "FramebufferUpdateRequest",
    35     RFB_KEYEVENT                    : "KeyEvent",
    36     RFB_POINTEREVENT                : "PointerEvent",
    37     RFB_CLIENTCUTTEXT               : "ClientCutText",
    38     }
    39 PACKET_FMT = {
    40     RFB_SETPIXELFORMAT              : "!BBBB"+PIXEL_FORMAT,
    41     RFB_SETENCODINGS                : "!BBH",
    42     RFB_FRAMEBUFFERUPDATEREQUEST    : "!BBHHHH",
    43     RFB_KEYEVENT                    : "!BBBBi",
    44     RFB_POINTEREVENT                : "!BBHH",
    45     RFB_CLIENTCUTTEXT               : "!BBBBi",
    46     }
    47 PACKET_STRUCT = {}
    48 for ptype, fmt in PACKET_FMT.items():
    49     PACKET_STRUCT[ptype] = struct.Struct(fmt)
    50 
    51 
    52 class RFBProtocol(object):
    53     CONNECTION_LOST = "connection-lost"
    54     INVALID = "invalid"
    55 
    56     def __init__(self, scheduler, conn, process_packet_cb, get_rfb_pixelformat, session_name="Xpra"):
    57         """
    58             You must call this constructor and source_has_more() from the main thread.
    59         """
    60         assert scheduler is not None
    61         assert conn is not None
    62         self.timeout_add = scheduler.timeout_add
    63         self.idle_add = scheduler.idle_add
    64         self._conn = conn
    65         self._process_packet_cb = process_packet_cb
    66         self._get_rfb_pixelformat = get_rfb_pixelformat
    67         self.session_name = session_name
    68         self._write_queue = Queue(1)
    69         self._buffer = b""
    70         #counters:
    71         self.input_packetcount = 0
    72         self.input_raw_packetcount = 0
    73         self.output_packetcount = 0
    74         self.output_raw_packetcount = 0
    75         self._protocol_version = ()
    76         self._closed = False
    77         self._packet_parser = self._parse_protocol_handshake
    78         self._write_lock = Lock()
    79         self._write_thread = None
    80         self._read_thread = make_thread(self._read_thread_loop, "read", daemon=True)
    81 
    82 
    83     def send_protocol_handshake(self):
    84         self.raw_write(b"RFB 003.008\n")
    85 
    86     def _parse_invalid(self, packet):
    87         return len(packet)
    88 
    89     def _parse_protocol_handshake(self, packet):
    90         if len(packet)<12:
    91             return 0
    92         if not packet.startswith(b'RFB '):
    93             self._invalid_header(packet, "invalid RFB protocol handshake packet header")
    94             return 0
    95         #ie: packet==b'RFB 003.008\n'
    96         self._protocol_version = tuple(int(x) for x in packet[4:11].split("."))
    97         log.info("RFB version %s", b".".join(str(x) for x in self._protocol_version))
    98         #reply with Security Handshake:
    99         self._packet_parser = self._parse_security_handshake
    100         self.send(struct.pack("BB", 1, 1))
    101         return 12
    102 
    103     def _parse_security_handshake(self, packet):
    104         if packet!=b"\1":
    105             self._invalid_header(packet, "invalid security handshake response")
    106             return 0
    107         #Security Handshake, send SecurityResult Handshake
    108         self._packet_parser = self._parse_security_result
    109         self.send(struct.pack("BBBB", 0, 0, 0, 0))
    110         return 1
    111 
    112     def _parse_security_result(self, packet):
    113         if packet!=b"\0":
    114             self._invalid_header(packet, "invalid security result")
    115             return 0
    116         #send ClientInit
    117         self._packet_parser = self._parse_rfb
    118         w, h, bpp, depth, bigendian, truecolor, rmax, gmax, bmax, rshift, bshift, gshift = self._get_rfb_pixelformat()
    119         packet =  struct.pack("!HH"+PIXEL_FORMAT+"I", w, h, bpp, depth, bigendian, truecolor, rmax, gmax, bmax, rshift, bshift, gshift, 0, 0, 0, len(self.session_name))+self.session_name
    120         self.send(packet)
    121         self._process_packet_cb(self, [b"authenticated"])
    122         return 1
    123 
    124     def _parse_rfb(self, packet):
    125         try:
    126             ptype = ord(packet[0])
    127         except:
    128             ptype = packet[0]
    129         packet_type = PACKET_TYPE.get(ptype)
    130         if not packet_type:
    131             self.invalid("unknown RFB packet type: %#x" % ptype, packet)
    132             return 0
    133         s = PACKET_STRUCT[ptype]        #ie: Struct("!BBBB")
    134         if len(packet)<s.size:
    135             return 0
    136         size = s.size
    137         values = list(s.unpack(packet[:size]))
    138         values[0] = packet_type
    139         #some packets require parsing extra data:
    140         if ptype==RFB_SETENCODINGS:
    141             N = values[2]
    142             estruct = struct.Struct("!"+"i"*N)
    143             size += estruct.size
    144             if len(packet)<size:
    145                 return 0
    146             encodings = estruct.unpack(packet[s.size:size])
    147             values.append(encodings)
    148         elif ptype==RFB_CLIENTCUTTEXT:
    149             l = values[4]
    150             size += l
    151             if len(packet)<size:
    152                 return 0
    153             text = packet[s.size:size]
    154             values.append(text)
    155         self.input_packetcount += 1
    156         #log("RFB packet: %s", values)
    157         #now trigger the callback:
    158         self._process_packet_cb(self, values)
    159         #return part of packet not consumed:
    160         return size
    161 
    162 
    163     def wait_for_io_threads_exit(self, timeout=None):
    164         for t in (self._read_thread, self._write_thread):
    165             if t and t.isAlive():
    166                 t.join(timeout)
    167         exited = True
    168         cinfo = self._conn or "cleared connection"
    169         for t in (self._read_thread, self._write_thread):
    170             if t and t.isAlive():
    171                 log.warn("Warning: %s thread of %s is still alive (timeout=%s)", t.name, cinfo, timeout)
    172                 exited = False
    173         return exited
    174 
    175     def __repr__(self):
    176         return "RFBProtocol(%s)" % self._conn
    177 
    178     def get_threads(self):
    179         return  [x for x in [self._write_thread, self._read_thread] if x is not None]
    180 
    181 
    182     def get_info(self, *_args):
    183         info = {"protocol" : self._protocol_version}
    184         for t in (self._write_thread, self._read_thread):
    185             if t:
    186                 info.setdefault("thread", {})[t.name] = t.is_alive()
    187         return info
    188 
    189 
    190     def start(self):
    191         def start_network_read_thread():
    192             if not self._closed:
    193                 self._read_thread.start()
    194         self.idle_add(start_network_read_thread)
    195 
    196     def send(self, packet):
    197         if self._closed:
    198             log("send(%s ...) connection is closed already, not sending", packet[0])
    199             return
    200         log("send(%s ...)", packet[0])
    201         with self._write_lock:
    202             if self._closed:
    203                 return
    204             self.raw_write(packet)
    205 
    206     def start_write_thread(self):
    207         self._write_thread = start_thread(self._write_thread_loop, "write", daemon=True)
    208 
    209     def raw_write(self, contents):
    210         """ Warning: this bypasses the compression and packet encoder! """
    211         if self._write_thread is None:
    212             self.start_write_thread()
    213         self._write_queue.put(contents)
    214 
    215     def _io_thread_loop(self, name, callback):
    216         try:
    217             log("io_thread_loop(%s, %s) loop starting", name, callback)
    218             while not self._closed and callback():
    219                 pass
    220             log("io_thread_loop(%s, %s) loop ended, closed=%s", name, callback, self._closed)
    221         except ConnectionClosedException as e:
    222             log("%s closed", self._conn, exc_info=True)
    223             if not self._closed:
    224                 #ConnectionClosedException means the warning has been logged already
    225                 self._connection_lost("%s connection %s closed" % (name, self._conn))
    226         except (OSError, IOError, socket_error) as e:
    227             if not self._closed:
    228                 self._internal_error("%s connection %s reset" % (name, self._conn), e, exc_info=e.args[0] not in ABORT)
    229         except Exception as e:
    230             #can happen during close(), in which case we just ignore:
    231             if not self._closed:
    232                 log.error("Error: %s on %s failed: %s", name, self._conn, type(e), exc_info=True)
    233                 self.close()
    234 
    235     def _write_thread_loop(self):
    236         self._io_thread_loop("write", self._write)
    237     def _write(self):
    238         buf = self._write_queue.get()
    239         # Used to signal that we should exit:
    240         if buf is None:
    241             log("write thread: empty marker, exiting")
    242             self.close()
    243             return False
    244         con = self._conn
    245         if not con:
    246             return False
    247         while buf and not self._closed:
    248             written = con.write(buf)
    249             if written:
    250                 buf = buf[written:]
    251                 self.output_raw_packetcount += 1
    252         self.output_packetcount += 1
    253         return True
    254 
    255     def _read_thread_loop(self):
    256         self._io_thread_loop("read", self._read)
    257     def _read(self):
    258         buf = self._conn.read(READ_BUFFER_SIZE)
    259         #log("read()=%s", repr_ellipsized(buf))
    260         if not buf:
    261             log("read thread: eof")
    262             #give time to the parse thread to call close itself
    263             #so it has time to parse and process the last packet received
    264             self.timeout_add(1000, self.close)
    265             return False
    266         self.input_raw_packetcount += 1
    267         self._buffer += buf
    268         #log("calling %s(%s)", self._packet_parser, repr_ellipsized(self._buffer))
    269         while self._buffer:
    270             consumed = self._packet_parser(self._buffer)
    271             if consumed==0:
    272                 break
    273             self._buffer = self._buffer[consumed:]
    274         return True
    275 
    276     def _internal_error(self, message="", exc=None, exc_info=False):
    277         #log exception info with last log message
    278         if self._closed:
    279             return
    280         ei = exc_info
    281         if exc:
    282             ei = None   #log it separately below
    283         log.error("Error: %s", message, exc_info=ei)
    284         if exc:
    285             log.error(" %s", exc, exc_info=exc_info)
    286         self.idle_add(self._connection_lost, message)
    287 
    288     def _connection_lost(self, message="", exc_info=False):
    289         log("connection lost: %s", message, exc_info=exc_info)
    290         self.close()
    291         return False
    292 
    293 
    294     def invalid(self, msg, data):
    295         self._packet_parser = self._parse_invalid
    296         self.idle_add(self._process_packet_cb, self, [RFBProtocol.INVALID, msg, data])
    297         # Then hang up:
    298         self.timeout_add(1000, self._connection_lost, msg)
    299 
    300 
    301     #delegates to invalid_header()
    302     #(so this can more easily be intercepted and overriden
    303     # see tcp-proxy)
    304     def _invalid_header(self, data, msg=""):
    305         self.invalid_header(self, data, msg)
    306 
    307     def invalid_header(self, _proto, data, msg="invalid packet header"):
    308         self._packet_parser = self._parse_invalid
    309         err = "%s: '%s'" % (msg, binascii.hexlify(data[:8]))
    310         if len(data)>1:
    311             err += " read buffer=%s (%i bytes)" % (repr_ellipsized(data), len(data))
    312         self.invalid(err, data)
    313 
    314 
    315     def flush_then_close(self, _last_packet, done_callback=None):
    316         """ Note: this is best effort only
    317             the packet may not get sent.
    318 
    319             We try to get the write lock,
    320             we try to wait for the write queue to flush
    321             we queue our last packet,
    322             we wait again for the queue to flush,
    323             then no matter what, we close the connection and stop the threads.
    324         """
    325         log("flush_then_close(%s) closed=%s", done_callback, self._closed)
    326         def done():
    327             log("flush_then_close: done, callback=%s", done_callback)
    328             if done_callback:
    329                 done_callback()
    330         if self._closed:
    331             log("flush_then_close: already closed")
    332             return done()
    333         def wait_for_queue(timeout=10):
    334             #IMPORTANT: if we are here, we have the write lock held!
    335             if not self._write_queue.empty():
    336                 #write queue still has stuff in it..
    337                 if timeout<=0:
    338                     log("flush_then_close: queue still busy, closing without sending the last packet")
    339                     self._write_lock.release()
    340                     self.close()
    341                     done()
    342                 else:
    343                     log("flush_then_close: still waiting for queue to flush")
    344                     self.timeout_add(100, wait_for_queue, timeout-1)
    345             else:
    346                 log("flush_then_close: queue is now empty, sending the last packet and closing")
    347                 def close_and_release():
    348                     log("flush_then_close: wait_for_packet_sent() close_and_release()")
    349                     self.close()
    350                     try:
    351                         self._write_lock.release()
    352                     except:
    353                         pass
    354                     done()
    355                 def wait_for_packet_sent():
    356                     log("flush_then_close: wait_for_packet_sent() queue.empty()=%s, closed=%s", self._write_queue.empty(), self._closed)
    357                     if self._write_queue.empty() or self._closed:
    358                         #it got sent, we're done!
    359                         close_and_release()
    360                         return False
    361                     return not self._closed     #run until we manage to close (here or via the timeout)
    362                 self.timeout_add(100, wait_for_packet_sent)
    363                 #just in case wait_for_packet_sent never fires:
    364                 self.timeout_add(5*1000, close_and_release)
    365 
    366         def wait_for_write_lock(timeout=100):
    367             if not self._write_lock.acquire(False):
    368                 if timeout<=0:
    369                     log("flush_then_close: timeout waiting for the write lock")
    370                     self.close()
    371                     done()
    372                 else:
    373                     log("flush_then_close: write lock is busy, will retry %s more times", timeout)
    374                     self.timeout_add(10, wait_for_write_lock, timeout-1)
    375             else:
    376                 log("flush_then_close: acquired the write lock")
    377                 #we have the write lock - we MUST free it!
    378                 wait_for_queue()
    379         #normal codepath:
    380         # -> wait_for_write_lock
    381         # -> wait_for_queue
    382         # -> wait_for_packet_sent
    383         # -> close_and_release
    384         log("flush_then_close: wait_for_write_lock()")
    385         wait_for_write_lock()
    386 
    387 
    388     def close(self):
    389         log("Protocol.close() closed=%s, connection=%s", self._closed, self._conn)
    390         if self._closed:
    391             return
    392         self._closed = True
    393         #self.idle_add(self._process_packet_cb, self, [Protocol.CONNECTION_LOST])
    394         c = self._conn
    395         if c:
    396             try:
    397                 log("Protocol.close() calling %s", c.close)
    398                 c.close()
    399             except:
    400                 log.error("error closing %s", self._conn, exc_info=True)
    401             self._conn = None
    402         self.terminate_queue_threads()
    403         self.idle_add(self.clean)
    404         log("Protocol.close() done")
    405 
    406     def clean(self):
    407         #clear all references to ensure we can get garbage collected quickly:
    408         self._write_thread = None
    409         self._read_thread = None
    410         self._process_packet_cb = None
    411 
    412     def terminate_queue_threads(self):
    413         log("terminate_queue_threads()")
    414         #make all the queue based threads exit by adding the empty marker:
    415         exit_queue = Queue()
    416         for _ in range(10):     #just 2 should be enough!
    417             exit_queue.put(None)
    418         try:
    419             owq = self._write_queue
    420             self._write_queue = exit_queue
    421             #discard all elements in the old queue and push the None marker:
    422             try:
    423                 while owq.qsize()>0:
    424                     owq.read(False)
    425             except:
    426                 pass
    427             owq.put_nowait(None)
    428         except:
    429             pass
    430         try:
    431             orq = self._read_queue
    432             self._read_queue = exit_queue
    433             #discard all elements in the old queue and push the None marker:
    434             try:
    435                 while orq.qsize()>0:
    436                     orq.read(False)
    437             except:
    438                 pass
    439             orq.put_nowait(None)
    440         except:
    441             pass
  • xpra/net/udp_protocol.py

     
     1# This file is part of Xpra.
     2# Copyright (C) 2017 Antoine Martin <antoine@devloop.org.uk>
     3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     4# later version. See the file COPYING for details.
     5
     6import socket
     7import struct
     8import random
     9try:
     10    import errno
     11    EMSGSIZE = errno.EMSGSIZE
     12except ImportError as e:
     13    EMSGSIZE = None
     14
     15from xpra.log import Logger
     16log = Logger("network", "protocol")
     17
     18from xpra.os_util import LINUX, monotonic_time
     19from xpra.util import envint, repr_ellipsized
     20from xpra.make_thread import start_thread
     21from xpra.net.protocol import Protocol
     22from xpra.net.bytestreams import SocketConnection
     23
     24READ_BUFFER_SIZE = envint("XPRA_READ_BUFFER_SIZE", 65536)
     25DROP_PCT = envint("XPRA_UDP_DROP_PCT", 0)
     26
     27
     28#UUID, seqno, chunk, chunks
     29_header_struct = struct.Struct('!QQHHH')
     30_header_size = _header_struct.size
     31
     32
     33class IncompletePacket(object):
     34    def __init__(self, seqno, start_time, chunks=None):
     35        self.seqno = seqno
     36        self.start_time = start_time
     37        self.last_time = start_time
     38        #todo: use numpy array of bytes
     39        self.chunks = chunks
     40    def __repr__(self):
     41        return ("IncompletePacket(%i: %s chunks)" % (self.seqno, len(self.chunks or [])))
     42
     43
     44class UDPListener(object):
     45    """
     46        This class is used by servers to receive UDP packets,
     47        it parses the header and then exposes the data received via process_packet_cb.
     48    """
     49
     50    def __init__(self, sock, process_packet_cb):
     51        assert sock is not None
     52        self._closed = False
     53        self._socket = sock
     54        self._process_packet_cb =  process_packet_cb
     55        self._read_thread = start_thread(self._read_thread_loop, "read", daemon=True)
     56
     57    def __repr__(self):
     58        return "UDPListener(%s)" % self._socket
     59
     60    def _read_thread_loop(self):
     61        log.info("udp read thread loop starting")
     62        try:
     63            while not self._closed:
     64                buf, bfrom = self._socket.recvfrom(READ_BUFFER_SIZE)
     65                if not buf:
     66                    log("read thread: eof")
     67                    break
     68                values = list(_header_struct.unpack_from(buf[:_header_size])) + [buf[_header_size:], bfrom]
     69                try:
     70                    self._process_packet_cb(self, *values)
     71                except Exception as e:
     72                    log("_read_thread_loop() buffer=%s, from=%s", repr_ellipsized(buf), bfrom, exc_info=True)
     73                    if not self._closed:
     74                        log.error("Error: UDP packet processing error:")
     75                        log.error(" %s", e)
     76        except Exception as e:
     77            #can happen during close(), in which case we just ignore:
     78            if not self._closed:
     79                log.error("Error: read on %s failed: %s", self._socket, type(e), exc_info=True)
     80        log("udp read thread loop ended")
     81        self.close()
     82
     83    def close(self):
     84        s = self._socket
     85        log("UDPListener.close() closed=%s, socket=%s", self._closed, s)
     86        if self._closed:
     87            return
     88        self._closed = True
     89        if s:
     90            try:
     91                log("Protocol.close() calling %s", s.close)
     92                s.close()
     93            except:
     94                log.error("error closing %s", s, exc_info=True)
     95            self._socket = None
     96        log("UDPListener.close() done")
     97
     98
     99class UDPProtocol(Protocol):
     100    """
     101        This class extends the Protocol class with UDP encapsulation.
     102        A single packet may end up being fragmented into multiple UDP frames
     103        to fit in the MTU.
     104        We keep track of the function which can be used to handle send failures
     105        (or the packet data if no function is supplied).
     106        "udp-control" packets are used to synchronize both ends.
     107    """
     108
     109    def __init__(self, *args):
     110        Protocol.__init__(self, *args)
     111        self.mtu = 0
     112        self.last_sequence = -1     #the most recent packet sequence we processed in full
     113        self.highest_sequence = -1
     114        self.jitter = 20            #20ms
     115        self.fail_cb = {}
     116        self.incomplete_packets = {}
     117        self.can_skip = set()       #processed already, or cancelled
     118        self.cancel = set()         #tell the other end to forget those
     119        self.control_timer = None
     120        self.control_timer_due = 0
     121        self._process_read = self.process_read
     122
     123    def close(self):
     124        Protocol.close(self)
     125        self.cancel_control_timer()
     126
     127
     128    def schedule_control(self, delay=1000):
     129        due = monotonic_time()+delay/1000.0
     130        if self.control_timer_due<=due:
     131            #due already
     132            return
     133        ct = self.control_timer
     134        if ct:
     135            self.source_remove(ct)
     136        self.control_timer = self.timeout_add(delay, self.send_control)
     137        self.control_timer_due = due
     138
     139    def cancel_control_timer(self):
     140        ct = self.control_timer
     141        if ct:
     142            self.control_timer = None
     143            self.source_remove(ct)
     144
     145    def send_control(self):
     146        self.control_timer = None
     147        self.control_timer_due = 0
     148        if self._closed:
     149            return False
     150        missing = self._get_missing()
     151        packet = ("udp-control", self.mtu, self.last_sequence, self.highest_sequence, missing, tuple(self.cancel))
     152        log("send_control() packet(%s)=%s", self.incomplete_packets, packet)
     153        self.send_async(packet)
     154        self.cancel = set()
     155        self.schedule_control()
     156        return False
     157
     158    def _get_missing(self):
     159        """ the packets and chunks we are missing """
     160        if not self.incomplete_packets:
     161            return {}
     162        now = monotonic_time()
     163        max_start_time = now-self.jitter/1000.0
     164        late_start_time = now-2
     165        not_recent = now-0.5
     166        missing = {}
     167        for seqno, ip in self.incomplete_packets.items():
     168            st = ip.start_time
     169            if st>=max_start_time:
     170                continue        #too recent, may still arrive
     171            if st<late_start_time or ip.last_time<not_recent:
     172                if ip.chunks is None:
     173                    missing[seqno] = []
     174                else:
     175                    #TODO: use bitmap instead?
     176                    missing_chunks = [i for i,x in enumerate(ip.chunks) if x is None]
     177                    if missing_chunks:
     178                        missing[seqno] = missing_chunks
     179        return missing
     180
     181    def process_control(self, mtu, last_seq, high_seq, missing, cancel):
     182        log("process_control(%i, %i, %i, %s, %s)", mtu, last_seq, high_seq, missing, cancel)
     183        con = self._conn
     184        if not con:
     185            return
     186        if mtu and self.mtu==0:
     187            self.mtu = mtu
     188        #first, we can free all the packets that have been processed by the other end:
     189        if last_seq>=0:
     190            done = [x for x in self.fail_cb.keys() if x<=last_seq]
     191            for x in done:
     192                try:
     193                    del self.fail_cb[x]
     194                except:
     195                    pass
     196        #next we can forget about sequence numbers that have been cancelled:
     197        if cancel:
     198            for seqno in cancel:
     199                if seqno>self.last_sequence:
     200                    self.can_skip.add(seqno)
     201                try:
     202                    del self.incomplete_packets[seqno]
     203                except:
     204                    pass
     205            #we may now be able to move forward a bit:
     206            if self.incomplete_packets and (self.last_sequence+1) in self.can_skip:
     207                self.process_incomplete()
     208        #re-send the missing ones:
     209        for seqno, missing_chunks in missing.items():
     210            fail_cb_seq = self.fail_cb.get(seqno)
     211            if fail_cb_seq is None:
     212                log.error("Error: cannot resend packet sequence %i", seqno)
     213                #hope for the best, and tell the other end to stop asking:
     214                self.cancel.add(seqno)
     215                continue
     216            log("fail_cb[%i]=%s", seqno, repr_ellipsized(str(fail_cb_seq)))
     217            if callable(fail_cb_seq):
     218                self.cancel.add(seqno)
     219                fail_cb_seq()
     220                continue
     221            if len(missing_chunks)==0:
     222                #the other end only knows it is missing the seqno,
     223                #not how many chunks are missing, so send them all
     224                missing_chunks = fail_cb_seq.keys()
     225            for c in missing_chunks:
     226                data = fail_cb_seq.get(c)
     227                log("resend data[%i][%i]=%s", seqno, c, repr_ellipsized(str(data)))
     228                if data is None:
     229                    log.error("Error: cannot resend chunk %i of packet sequence %i", c, seqno)
     230                    log.error(" data missing from packet resend cache")
     231                    continue
     232                #send it again:
     233                #TODO: if the mtu is now lower, we should re-send the whole packet,
     234                # with the new chunk size..
     235                con.write(data)
     236
     237
     238    def send_async(self, packet):
     239        chunks = self.encode(packet)
     240        if len(chunks)>1:
     241            return Protocol.send_now(packet)
     242        proto_flags,index,level,data = chunks[0]
     243        from xpra.net.header import pack_header
     244        payload_size = len(data)
     245        header_and_data = pack_header(proto_flags, level, index, payload_size) + data
     246        with self._write_lock:
     247            if self._write_thread is None:
     248                self.start_write_thread()
     249            self._write_queue.put((header_and_data, None, None, None, False))
     250
     251    def process_udp_data(self, seqno, synchronous, chunk, chunks, data, bfrom):
     252        log("process_udp_data%s %i bytes", (seqno, synchronous, chunk, chunks, repr_ellipsized(data), bfrom), len(data))
     253        if DROP_PCT>0:
     254            if random.randint(0, 100) <= DROP_PCT:
     255                log.warn("Warning: dropping udp packet %i.%i", seqno, chunk)
     256                return
     257        if seqno<=self.last_sequence:
     258            #must be a duplicate, we've already processed it!
     259            return
     260        self.highest_sequence = max(self.highest_sequence, seqno)
     261        if self.incomplete_packets or (synchronous and seqno!=self.last_sequence+1) or chunk!=0 or chunks!=1:
     262            assert chunk>=0 and chunks>0 and chunk<chunks, "invalid chunk: %i/%i" % (chunk, chunks)
     263            #slow path: add chunk to incomplete packet
     264            now = monotonic_time()
     265            ip = self.incomplete_packets.get(seqno)
     266            if not ip or not ip.chunks:
     267                chunks_array = [None for _ in range(chunks)]
     268                ip = IncompletePacket(seqno, now, chunks_array)
     269                self.incomplete_packets[seqno] = ip
     270            else:
     271                ip.last_time = now
     272            ip.chunks[chunk] = data
     273            if seqno!=self.last_sequence+1:
     274                #we're waiting for a packet and this is not it,
     275                #make sure any gaps are marked as incomplete:
     276                for i in range(self.last_sequence+1, seqno):
     277                    if i not in self.incomplete_packets and i not in self.can_skip:
     278                        self.incomplete_packets[i] = IncompletePacket(i, now)
     279                #make sure we request the missing packets:
     280                self.schedule_control(self.jitter)
     281                if synchronous:
     282                    #we have to wait for the missing chunks / packets
     283                    log("process_udp_data: we're waiting for %i, not %i", self.last_sequence+1, seqno)
     284                    return
     285            if any(x is None for x in ip.chunks):
     286                #one of the chunks is still missing
     287                log("process_udp_data: sequence %i is still missing some chunks: %s", seqno, [i for i,x in enumerate(ip.chunks) if x is None])
     288                self.schedule_control(self.jitter)
     289                return
     290            #all the data is here!
     291            del self.incomplete_packets[seqno]
     292            data = b"".join(ip.chunks)
     293        #log("process_udp_data: adding packet sequence %i to read queue", seqno)
     294        if seqno==self.last_sequence+1:
     295            self.last_sequence = seqno
     296        else:
     297            assert not synchronous
     298            self.can_skip.add(seqno)
     299        self._read_queue_put(data)
     300        if self.incomplete_packets:
     301            self.process_incomplete()
     302
     303    def process_incomplete(self):
     304        #maybe we can send the next one(s) now?
     305        seqno = self.last_sequence
     306        log("process_incomplete() last_sequence=%i, can skip=%s", seqno, self.can_skip)
     307        while True:
     308            seqno += 1
     309            if seqno in self.can_skip:
     310                try:
     311                    del self.incomplete_packets[seqno]
     312                except KeyError:
     313                    pass
     314                self.can_skip.remove(seqno)
     315                self.last_sequence = seqno
     316                continue
     317            ip = self.incomplete_packets.get(seqno)
     318            if not ip or not ip.chunks:
     319                #it's missing, we just don't know how many chunks
     320                return
     321            if any(x is None for x in ip.chunks):
     322                #one of the chunks is still missing
     323                return
     324            #all the data is here!
     325            del self.incomplete_packets[seqno]
     326            data = b"".join(ip.chunks)
     327            log("process_incomplete: adding packet sequence %i to read queue", seqno)
     328            self.last_sequence = seqno
     329            self._read_queue_put(data)
     330
     331
     332    def write_buffers(self, buf_data, fail_cb, synchronous):
     333        buf = b"".join(buf_data)
     334        #if not isinstance(buf, JOIN_TYPES):
     335        #    buf = memoryview_to_bytes(buf)
     336        while True:
     337            try:
     338                seqno = self.output_packetcount
     339                return self.send_buf(seqno, buf, fail_cb, synchronous)
     340            except MTUExceeded as e:
     341                log.warn("%s: %s", e, self.mtu)
     342                if self.mtu>576:
     343                    self.mtu //= 2
     344                raise
     345
     346    def send_buf(self, seqno, data, fail_cb, synchronous):
     347        con = self._conn
     348        if not con:
     349            return 0
     350        #TODO: bump to 1280 for IPv6
     351        #mtu = max(576, self.mtu)
     352        mtu = max(1280, self.mtu)
     353        uuid = 0 #todo!
     354        l = len(data)
     355        maxpayload = mtu-_header_size
     356        chunks = l // maxpayload
     357        if l % maxpayload > 0:
     358            chunks += 1
     359        log("UDP.send_buf(%s, %i bytes, %s, %s) seq=%i, mtu=%s, maxpayload=%i, chunks=%i, data=%s", con, l, fail_cb, synchronous, seqno, mtu, maxpayload, chunks, repr_ellipsized(data))
     360        chunk = 0
     361        offset = 0
     362        chunk_resend_cache = None
     363        if fail_cb:
     364            self.fail_cb[seqno] = fail_cb
     365        else:
     366            chunk_resend_cache = {}
     367            self.fail_cb[seqno] = chunk_resend_cache
     368        while offset<l:
     369            assert chunk<chunks
     370            pl = min(maxpayload, l-offset)
     371            data_chunk = data[offset:offset+pl]
     372            udp_data = _header_struct.pack(uuid, seqno, synchronous, chunk, chunks) + data_chunk
     373            assert len(udp_data)<=mtu, "invalid payload size: %i greater than mtu %i" % (len(udp_data), mtu)
     374            con.write(udp_data)
     375            self.output_raw_packetcount += 1
     376            offset += pl
     377            if chunk_resend_cache is not None:
     378                chunk_resend_cache[chunk] = udp_data
     379            chunk += 1
     380        assert chunk==chunks, "wrote %i chunks but expected %i" % (chunk, chunks)
     381        self.output_packetcount += 1
     382        if not self.control_timer:
     383            self.control_timer = self.timeout_add(1000, self.send_control)
     384        return offset
     385
     386
     387    def get_info(self, alias_info=True):
     388        i = Protocol.get_info(self, alias_info)
     389        i["mtu"] = self.mtu
     390        return i
     391
     392
     393class UDPServerProtocol(UDPProtocol):
     394
     395    def _read_thread_loop(self):
     396        #server protocol is not used to read,
     397        #we rely on the listener to dispatch packets instead
     398        pass
     399
     400class UDPClientProtocol(UDPProtocol):
     401
     402    def con_write(self, data, fail_cb):
     403        """ After successfully writing some data, update the mtu value """
     404        r = UDPProtocol.con_write(self, data, fail_cb)
     405        if r>0 and LINUX:
     406            IP_MTU = 14
     407            con = self._conn
     408            if con:
     409                try:
     410                    self.mtu = min(32767, con._socket.getsockopt(socket.IPPROTO_IP, IP_MTU))
     411                    #log("mtu=%s", self.mtu)
     412                except IOError as e:
     413                    pass
     414        return r
     415
     416    def process_read(self, buf):
     417        """ Splits and parses the UDP frame header from the packet """
     418        #log.info("UDPClientProtocol.read_queue_put(%s)", repr_ellipsized(buf))
     419        _, seqno, synchronous, chunk, chunks = _header_struct.unpack_from(buf[:_header_size])
     420        data = buf[_header_size:]
     421        bfrom = None        #not available here..
     422        self.process_udp_data(seqno, synchronous, chunk, chunks, data, bfrom)
     423
     424
     425class UDPSocketConnection(SocketConnection):
     426    """
     427        This class extends SocketConnection to use socket.sendto
     428        to send data to the correct destination.
     429        (servers use a single socket to talk to multiple clients,
     430        they do not call connect() and so we have to specify the remote target every time)
     431    """
     432
     433    def __init__(self, *args):
     434        SocketConnection.__init__(self, *args)
     435
     436    def write(self, buf):
     437        #log("UDPSocketConnection: sending %i bytes to %s", len(buf), self.remote)
     438        try:
     439            return self._socket.sendto(buf, self.remote)
     440        except IOError as e:
     441            if e.errno==EMSGSIZE:
     442                raise MTUExceeded("invalid UDP payload size, cannot send %i bytes: %s" % (len(buf), e))
     443            raise
     444
     445    def close(self):
     446        """
     447            don't close the socket, we're don't own it
     448        """
     449        pass
     450
     451class MTUExceeded(IOError):
     452    pass
  • xpra/scripts/config.py

     
    437437                    "auth"              : str,
    438438                    "vsock-auth"        : str,
    439439                    "tcp-auth"          : str,
     440                    "udp-auth"          : str,
     441                    "dtls-auth"         : str,
    440442                    "ws-auth"           : str,
    441443                    "wss-auth"          : str,
    442444                    "ssl-auth"          : str,
     
    594596                    "bind"              : list,
    595597                    "bind-vsock"        : list,
    596598                    "bind-tcp"          : list,
     599                    "bind-udp"          : list,
     600                    "bind-dtls"         : list,
    597601                    "bind-ws"           : list,
    598602                    "bind-wss"          : list,
    599603                    "bind-ssl"          : list,
     
    615619    "start-after-connect", "start-child-after-connect",
    616620    "start-on-connect", "start-child-on-connect",
    617621    ]
    618 BIND_OPTIONS = ["bind", "bind-tcp", "bind-ssl", "bind-ws", "bind-wss", "bind-vsock"]
     622BIND_OPTIONS = ["bind", "bind-tcp", "bind-udp", "bind-ssl", "bind-ws", "bind-wss", "bind-vsock"]
    619623
    620624#keep track of the options added since v1,
    621625#so we can generate command lines that work with older supported versions:
     
    679683    "av-sync", "global-menus",
    680684    "printing", "file-transfer", "open-command", "open-files", "start-new-commands",
    681685    "mmap", "mmap-group", "mdns",
    682     "auth", "vsock-auth", "tcp-auth", "ws-auth", "wss-auth", "ssl-auth", "rfb-auth",
    683     "bind", "bind-vsock", "bind-tcp", "bind-ssl", "bind-ws", "bind-wss", "bind-rfb",
     686    "auth", "vsock-auth", "tcp-auth", "udp-auth", "dtls-auth", "ws-auth", "wss-auth", "ssl-auth", "rfb-auth",
     687    "bind", "bind-vsock", "bind-tcp", "bind-udp", "bind-dtls", "bind-ssl", "bind-ws", "bind-wss", "bind-rfb",
    684688    "start", "start-child",
    685689    "start-after-connect", "start-child-after-connect",
    686690    "start-on-connect", "start-child-on-connect",
     
    799803                    "auth"              : "",
    800804                    "vsock-auth"        : "",
    801805                    "tcp-auth"          : "",
     806                    "udp-auth"          : "",
     807                    "dtls-auth"         : "",
    802808                    "ws-auth"           : "",
    803809                    "wss-auth"          : "",
    804810                    "ssl-auth"          : "",
     
    946952                    "bind"              : bind_dirs,
    947953                    "bind-vsock"        : [],
    948954                    "bind-tcp"          : [],
     955                    "bind-udp"          : [],
     956                    "bind-dtls"         : [],
    949957                    "bind-ws"           : [],
    950958                    "bind-wss"          : [],
    951959                    "bind-ssl"          : [],
  • xpra/scripts/main.py

     
    534534                          metavar="[HOST]:[PORT]",
    535535                          help="Listen for connections over TCP (use --tcp-auth to secure it)."
    536536                            + " You may specify this option multiple times with different host and port combinations")
     537        group.add_option("--bind-udp", action="append",
     538                          dest="bind_udp", default=list(defaults.bind_udp or []),
     539                          metavar="[HOST]:[PORT]",
     540                          help="Listen for connections over UDP (use --udp-auth to secure it)."
     541                            + " You may specify this option multiple times with different host and port combinations")
     542        group.add_option("--bind-dtls", action="append",
     543                          dest="bind_dtls", default=list(defaults.bind_dtls or []),
     544                          metavar="[HOST]:[PORT]",
     545                          help="Listen for connections over UDP + DTLS (use --dtls-auth to secure it)."
     546                            + " You may specify this option multiple times with different host and port combinations")
    537547        group.add_option("--bind-ws", action="append",
    538548                          dest="bind_ws", default=list(defaults.bind_ws or []),
    539549                          metavar="[HOST]:[PORT]",
     
    558568        ignore({
    559569            "bind"      : defaults.bind,
    560570            "bind-tcp"  : defaults.bind_tcp,
     571            "bind-udp"  : defaults.bind_udp,
    561572            "bind-ws"   : defaults.bind_ws,
    562573            "bind-wss"  : defaults.bind_wss,
    563574            "bind-ssl"  : defaults.bind_ssl,
     
    974985    group.add_option("--tcp-auth", action="store",
    975986                      dest="tcp_auth", default=defaults.tcp_auth,
    976987                      help="The authentication module to use for TCP sockets (default: '%default')")
     988    group.add_option("--udp-auth", action="store",
     989                      dest="udp_auth", default=defaults.udp_auth,
     990                      help="The authentication module to use for UDP sockets (default: '%default')")
    977991    group.add_option("--ws-auth", action="store",
    978992                      dest="ws_auth", default=defaults.ws_auth,
    979993                      help="The authentication module to use for Websockets (default: '%default')")
     
    16861700        if opts.socket_dir:
    16871701            desc["socket_dir"] = opts.socket_dir
    16881702        return desc
    1689     elif display_name.startswith("tcp:") or display_name.startswith("tcp/") or \
    1690             display_name.startswith("ssl:") or display_name.startswith("ssl/"):
    1691         ctype = display_name[:3]        #ie: "ssl" or "tcp"
    1692         separator = display_name[3]     # ":" or "/"
     1703    elif (
     1704        display_name.startswith("tcp:") or display_name.startswith("tcp/") or \
     1705        display_name.startswith("ssl:") or display_name.startswith("ssl/") or \
     1706        display_name.startswith("udp:") or display_name.startswith("udp/") or \
     1707        display_name.startswith("dtls:") or display_name.startswith("dtls/")
     1708        ):
     1709        ctype = display_name[:4].rstrip(":/")   #ie: "ssl" or "tcp"
     1710        separator = display_name[len(ctype)]     # ":" or "/"
    16931711        desc.update({
    16941712                     "type"     : ctype,
    16951713                     })
     
    20732091        from xpra.net.bytestreams import SocketConnection
    20742092        return SocketConnection(sock, "local", "host", (CID_TYPES.get(cid, cid), iport), dtype)
    20752093
    2076     elif dtype in ("tcp", "ssl", "ws", "wss"):
     2094    elif dtype in ("tcp", "ssl", "ws", "wss", "udp", "dtls"):
    20772095        if display_desc.get("ipv6"):
    20782096            assert socket.has_ipv6, "no IPv6 support"
    20792097            family = socket.AF_INET6
     
    20892107                socket.AF_INET  : "IPv4",
    20902108                }.get(family, family), (host, port), e))
    20912109        sockaddr = addrinfo[0][-1]
    2092         sock = socket.socket(family, socket.SOCK_STREAM)
    2093         sock.settimeout(SOCKET_TIMEOUT)
    2094         sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, TCP_NODELAY)
     2110        if dtype in ("udp", "dtls"):
     2111            sock = socket.socket(family, socket.SOCK_DGRAM)
     2112        else:
     2113            sock = socket.socket(family, socket.SOCK_STREAM)
     2114            sock.settimeout(SOCKET_TIMEOUT)
     2115            sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, TCP_NODELAY)
    20952116        strict_host_check = display_desc.get("strict-host-check")
    20962117        if strict_host_check is False:
    20972118            opts.ssl_server_verify_mode = "none"
    20982119        conn = _socket_connect(sock, sockaddr, display_name, dtype)
    2099         if dtype in ("ssl", "wss"):
     2120        if dtype in ("ssl", "wss", "dtls"):
     2121            if dtype=="dtls":
     2122                from dtls import do_patch   #@UnresolvedImport
     2123                do_patch()
    21002124            wrap_socket = ssl_wrap_socket_fn(opts, server_side=False)
    21012125            sock = wrap_socket(sock)
    21022126            assert sock, "failed to wrap socket %s" % sock
     
    22712295        raise InitException("cannot check hostname with verify mode %s" % verify_mode)
    22722296    wrap_socket = context.wrap_socket
    22732297    del opts
    2274     def do_wrap_socket(tcp_socket):
     2298    def do_wrap_socket(tcp_socket, handshake=None):
    22752299        from xpra.log import Logger
    22762300        try:
    22772301            ssl_sock = wrap_socket(tcp_socket, **kwargs)
     
    22812305            if SSLEOFError and isinstance(e, SSLEOFError):
    22822306                return None
    22832307            raise InitExit(EXIT_SSL_FAILURE, "Cannot wrap socket %s: %s" (tcp_socket, e))
    2284         if not server_side:
     2308        if not server_side and handshake is not False:
    22852309            try:
    22862310                ssl_sock.do_handshake(True)
    22872311            except Exception as e:
  • xpra/scripts/server.py

     
    357357    if opts.encoding=="help" or "help" in opts.encodings:
    358358        return show_encoding_help(opts)
    359359
    360     from xpra.server.socket_util import parse_bind_tcp, parse_bind_vsock
    361     bind_tcp = parse_bind_tcp(opts.bind_tcp)
    362     bind_ssl = parse_bind_tcp(opts.bind_ssl)
    363     bind_ws = parse_bind_tcp(opts.bind_ws)
    364     bind_wss = parse_bind_tcp(opts.bind_wss)
    365     bind_rfb = parse_bind_tcp(opts.bind_rfb)
     360    from xpra.server.socket_util import parse_bind_ip, parse_bind_vsock
     361    bind_tcp = parse_bind_ip(opts.bind_tcp)
     362    bind_udp = parse_bind_ip(opts.bind_udp)
     363    bind_dtls= parse_bind_ip(opts.bind_dtls)
     364    bind_ssl = parse_bind_ip(opts.bind_ssl)
     365    bind_ws  = parse_bind_ip(opts.bind_ws)
     366    bind_wss = parse_bind_ip(opts.bind_wss)
     367    bind_rfb = parse_bind_ip(opts.bind_rfb)
    366368    bind_vsock = parse_bind_vsock(opts.bind_vsock)
    367369
    368370    assert mode in ("start", "start-desktop", "upgrade", "shadow", "proxy")
     
    567569    sockets = []
    568570
    569571    #SSL sockets:
    570     wrap_socket_fn = None
     572    wrap_server_socket_fn = None
     573    wrap_client_socket_fn = None
    571574    need_ssl = False
    572575    ssl_opt = opts.ssl.lower()
    573576    if ssl_opt in TRUE_OPTIONS or bind_ssl or bind_wss:
     
    582585    if need_ssl:
    583586        from xpra.scripts.main import ssl_wrap_socket_fn
    584587        try:
    585             wrap_socket_fn = ssl_wrap_socket_fn(opts, server_side=True)
    586             netlog("wrap_socket_fn=%s", wrap_socket_fn)
     588            wrap_server_socket_fn = ssl_wrap_socket_fn(opts, server_side=True)
     589            wrap_client_socket_fn = ssl_wrap_socket_fn(opts, server_side=False)
     590            netlog("wrap socket functions: %s, %s", wrap_server_socket_fn, wrap_client_socket_fn)
    587591        except Exception as e:
    588592            netlog("SSL error", exc_info=True)
    589593            cpaths = csv("'%s'" % x for x in (opts.ssl_cert, opts.ssl_key) if x)
    590594            raise InitException("cannot create SSL socket, check your certificate paths (%s): %s" % (cpaths, e))
    591595
     596    from xpra.server.socket_util import setup_tcp_socket, setup_udp_socket, setup_vsock_socket, setup_local_sockets
    592597    def add_mdns(socktype, host, port):
    593598        recs = mdns_recs.setdefault(socktype.lower(), [])
    594599        rec = (host, port)
     
    598603        socket = setup_tcp_socket(host, iport, socktype)
    599604        sockets.append(socket)
    600605        add_mdns(socktype, host, iport)
    601 
     606    def add_udp_socket(socktype, host, iport):
     607        socket = setup_udp_socket(host, iport, socktype)
     608        if socktype=="dtls":
     609            from dtls import do_patch   #@UnresolvedImport
     610            do_patch()
     611        sockets.append(socket)
     612        add_mdns(socktype, host, iport)
    602613    # Initialize the TCP sockets before the display,
    603614    # That way, errors won't make us kill the Xvfb
    604615    # (which may not be ours to kill at that point)
    605     from xpra.server.socket_util import setup_tcp_socket, setup_vsock_socket, setup_local_sockets
    606616    netlog("setting up SSL sockets: %s", bind_ssl)
    607617    for host, iport in bind_ssl:
    608618        add_tcp_socket("SSL", host, iport)
     
    615625        add_tcp_socket("tcp", host, iport)
    616626        if tcp_ssl:
    617627            add_mdns("ssl", host, iport)
     628    netlog("setting up UDP sockets: %s", bind_udp)
     629    for host, iport in bind_udp:
     630        add_udp_socket("udp", host, iport)
     631    netlog("setting up UDP+DTLS sockets: %s", bind_dtls)
     632    for host, iport in bind_dtls:
     633        add_udp_socket("dtls", host, iport)
    618634    netlog("setting up http / ws (websockets): %s", bind_ws)
    619635    for host, iport in bind_ws:
    620636        add_tcp_socket("ws", host, iport)
     
    908924            mdns_publish(display_name, mode, listen_on, mdns_info)
    909925
    910926    try:
    911         app._ssl_wrap_socket = wrap_socket_fn
     927        app._ssl_wrap_server_socket = wrap_server_socket_fn
     928        app._ssl_wrap_client_socket = wrap_client_socket_fn
    912929        app.original_desktop_display = desktop_display
    913930        app.exec_cwd = opts.chdir or cwd
    914931        app.init(opts)
  • xpra/server/rfb/__init__.py

     
     1# This file is part of Xpra.
     2# Copyright (C) 2017 Antoine Martin <antoine@devloop.org.uk>
     3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     4# later version. See the file COPYING for details.
  • xpra/server/rfb/d3des.py

     
     1#!/usr/bin/env python
     2##
     3##  d3des.py - DES implementation
     4##
     5##  Copyright (c) 2009 by Yusuke Shinyama
     6##
     7
     8# This is a Python rewrite of d3des.c by Richard Outerbridge.
     9#
     10# I referred to the original VNC viewer code for the changes that
     11# is necessary to maintain the exact behavior of the VNC protocol.
     12# Two constants and two functions were added to the original d3des
     13# code.  These added parts were written in Python and marked
     14# below.  I believe that the added parts do not make this program
     15# a "derivative work" of the VNC viewer (which is GPL'ed and
     16# written in C), but if there's any problem, let me know.
     17#
     18# Yusuke Shinyama (yusuke at cs dot nyu dot edu)
     19
     20
     21#  D3DES (V5.09) -
     22#
     23#  A portable, public domain, version of the Data Encryption Standard.
     24#
     25#  Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
     26#  Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
     27#  code;  Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
     28#  Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
     29#  for humouring me on.
     30#
     31#  Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
     32#  (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
     33#
     34
     35from struct import pack, unpack
     36
     37
     38###################################################
     39###
     40###  start: changes made for VNC.
     41###
     42
     43# This constant was taken from vncviewer/rfb/vncauth.c:
     44vnckey = [ 23,82,107,6,35,78,88,7 ]
     45
     46# This is a departure from the original code.
     47#bytebit = [ 0200, 0100, 040, 020, 010, 04, 02, 01 ] # original
     48bytebit = [ 01, 02, 04, 010, 020, 040, 0100, 0200 ] # VNC version
     49
     50# two password functions for VNC protocol.
     51def decrypt_passwd(data):
     52    dk = deskey(pack('8B', *vnckey), True)
     53    return desfunc(data, dk)
     54
     55def generate_response(passwd, challange):
     56    ek = deskey((passwd+'\x00'*8)[:8], False)
     57    return desfunc(challange[:8], ek) + desfunc(challange[8:], ek)
     58
     59###
     60###  end: changes made for VNC.
     61###
     62###################################################
     63
     64
     65bigbyte = [
     66  0x800000L,    0x400000L,      0x200000L,      0x100000L,
     67  0x80000L,     0x40000L,       0x20000L,       0x10000L,
     68  0x8000L,      0x4000L,        0x2000L,        0x1000L,
     69  0x800L,       0x400L,         0x200L,         0x100L,
     70  0x80L,        0x40L,          0x20L,          0x10L,
     71  0x8L,         0x4L,           0x2L,           0x1L
     72  ]
     73
     74# Use the key schedule specified in the Standard (ANSI X3.92-1981).
     75
     76pc1 = [
     77  56, 48, 40, 32, 24, 16,  8,    0, 57, 49, 41, 33, 25, 17,
     78   9,  1, 58, 50, 42, 34, 26,   18, 10,  2, 59, 51, 43, 35,
     79  62, 54, 46, 38, 30, 22, 14,    6, 61, 53, 45, 37, 29, 21,
     80  13,  5, 60, 52, 44, 36, 28,   20, 12,  4, 27, 19, 11,  3
     81  ]
     82
     83totrot = [ 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 ]
     84
     85pc2 = [
     86  13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
     87  22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
     88  40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
     89  43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
     90  ]
     91
     92def deskey(key, decrypt):      # Thanks to James Gillogly & Phil Karn!
     93    key = unpack('8B', key)
     94
     95    pc1m = [0]*56
     96    pcr = [0]*56
     97    kn = [0L]*32
     98
     99    for j in range(56):
     100        l = pc1[j]
     101        m = l & 07
     102        if key[l >> 3] & bytebit[m]:
     103            pc1m[j] = 1
     104        else:
     105            pc1m[j] = 0
     106
     107    for i in range(16):
     108        if decrypt:
     109            m = (15 - i) << 1
     110        else:
     111            m = i << 1
     112        n = m + 1
     113        kn[m] = kn[n] = 0L
     114        for j in range(28):
     115            l = j + totrot[i]
     116            if l < 28:
     117                pcr[j] = pc1m[l]
     118            else:
     119                pcr[j] = pc1m[l - 28]
     120        for j in range(28, 56):
     121            l = j + totrot[i]
     122            if l < 56:
     123                pcr[j] = pc1m[l]
     124            else:
     125                pcr[j] = pc1m[l - 28]
     126        for j in range(24):
     127            if pcr[pc2[j]]:
     128                kn[m] |= bigbyte[j]
     129            if pcr[pc2[j+24]]:
     130                kn[n] |= bigbyte[j]
     131
     132    return cookey(kn)
     133
     134def cookey(raw):
     135    key = []
     136    for i in range(0, 32, 2):
     137        (raw0, raw1) = (raw[i], raw[i+1])
     138        k  = (raw0 & 0x00fc0000L) << 6
     139        k |= (raw0 & 0x00000fc0L) << 10
     140        k |= (raw1 & 0x00fc0000L) >> 10
     141        k |= (raw1 & 0x00000fc0L) >> 6
     142        key.append(k)
     143        k  = (raw0 & 0x0003f000L) << 12
     144        k |= (raw0 & 0x0000003fL) << 16
     145        k |= (raw1 & 0x0003f000L) >> 4
     146        k |= (raw1 & 0x0000003fL)
     147        key.append(k)
     148    return key
     149
     150SP1 = [
     151  0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
     152  0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
     153  0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
     154  0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
     155  0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
     156  0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
     157  0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
     158  0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
     159  0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
     160  0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
     161  0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
     162  0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
     163  0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
     164  0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
     165  0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
     166  0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L
     167  ]
     168
     169SP2 = [
     170  0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
     171  0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
     172  0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
     173  0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
     174  0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
     175  0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
     176  0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
     177  0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
     178  0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
     179  0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
     180  0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
     181  0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
     182  0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
     183  0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
     184  0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
     185  0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L
     186  ]
     187
     188SP3 = [
     189  0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
     190  0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
     191  0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
     192  0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
     193  0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
     194  0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
     195  0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
     196  0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
     197  0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
     198  0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
     199  0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
     200  0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
     201  0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
     202  0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
     203  0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
     204  0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L
     205  ]
     206
     207SP4 = [
     208  0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
     209  0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
     210  0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
     211  0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
     212  0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
     213  0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
     214  0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
     215  0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
     216  0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
     217  0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
     218  0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
     219  0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
     220  0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
     221  0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
     222  0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
     223  0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L
     224  ]
     225
     226SP5 = [
     227  0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
     228  0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
     229  0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
     230  0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
     231  0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
     232  0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
     233  0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
     234  0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
     235  0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
     236  0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
     237  0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
     238  0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
     239  0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
     240  0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
     241  0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
     242  0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L
     243  ]
     244
     245SP6 = [
     246  0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
     247  0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
     248  0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
     249  0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
     250  0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
     251  0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
     252  0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
     253  0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
     254  0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
     255  0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
     256  0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
     257  0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
     258  0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
     259  0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
     260  0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
     261  0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L
     262  ]
     263
     264SP7 = [
     265  0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
     266  0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
     267  0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
     268  0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
     269  0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
     270  0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
     271  0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
     272  0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
     273  0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
     274  0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
     275  0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
     276  0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
     277  0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
     278  0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
     279  0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
     280  0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L
     281  ]
     282
     283SP8 = [
     284  0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
     285  0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
     286  0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
     287  0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
     288  0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
     289  0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
     290  0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
     291  0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
     292  0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
     293  0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
     294  0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
     295  0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
     296  0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
     297  0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
     298  0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
     299  0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L
     300  ]
     301
     302def desfunc(block, keys):
     303    (leftt, right) = unpack('>II', block)
     304
     305    work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL
     306    right ^= work
     307    leftt ^= (work << 4)
     308    work = ((leftt >> 16) ^ right) & 0x0000ffffL
     309    right ^= work
     310    leftt ^= (work << 16)
     311    work = ((right >> 2) ^ leftt) & 0x33333333L
     312    leftt ^= work
     313    right ^= (work << 2)
     314    work = ((right >> 8) ^ leftt) & 0x00ff00ffL
     315    leftt ^= work
     316    right ^= (work << 8)
     317    right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL
     318    work = (leftt ^ right) & 0xaaaaaaaaL
     319    leftt ^= work
     320    right ^= work
     321    leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL
     322
     323    for i in range(0, 32, 4):
     324        work  = (right << 28) | (right >> 4)
     325        work ^= keys[i]
     326        fval  = SP7[ work            & 0x3fL]
     327        fval |= SP5[(work >>  8) & 0x3fL]
     328        fval |= SP3[(work >> 16) & 0x3fL]
     329        fval |= SP1[(work >> 24) & 0x3fL]
     330        work  = right ^ keys[i+1]
     331        fval |= SP8[ work            & 0x3fL]
     332        fval |= SP6[(work >>  8) & 0x3fL]
     333        fval |= SP4[(work >> 16) & 0x3fL]
     334        fval |= SP2[(work >> 24) & 0x3fL]
     335        leftt ^= fval
     336        work  = (leftt << 28) | (leftt >> 4)
     337        work ^= keys[i+2]
     338        fval  = SP7[ work            & 0x3fL]
     339        fval |= SP5[(work >>  8) & 0x3fL]
     340        fval |= SP3[(work >> 16) & 0x3fL]
     341        fval |= SP1[(work >> 24) & 0x3fL]
     342        work  = leftt ^ keys[i+3]
     343        fval |= SP8[ work            & 0x3fL]
     344        fval |= SP6[(work >>  8) & 0x3fL]
     345        fval |= SP4[(work >> 16) & 0x3fL]
     346        fval |= SP2[(work >> 24) & 0x3fL]
     347        right ^= fval
     348
     349    right = (right << 31) | (right >> 1)
     350    work = (leftt ^ right) & 0xaaaaaaaaL
     351    leftt ^= work
     352    right ^= work
     353    leftt = (leftt << 31) | (leftt >> 1)
     354    work = ((leftt >> 8) ^ right) & 0x00ff00ffL
     355    right ^= work
     356    leftt ^= (work << 8)
     357    work = ((leftt >> 2) ^ right) & 0x33333333L
     358    right ^= work
     359    leftt ^= (work << 2)
     360    work = ((right >> 16) ^ leftt) & 0x0000ffffL
     361    leftt ^= work
     362    right ^= (work << 16)
     363    work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL
     364    leftt ^= work
     365    right ^= (work << 4)
     366
     367    leftt &= 0xffffffffL
     368    right &= 0xffffffffL
     369    return pack('>II', right, leftt)
     370
     371
     372# test
     373if __name__ == '__main__':
     374    key = '0123456789abcdef'.decode('hex')
     375    plain = '0123456789abcdef'.decode('hex')
     376    cipher = '6e09a37726dd560c'.decode('hex')
     377    ek = deskey(key, False)
     378    dk = deskey(key, True)
     379    assert desfunc(plain, ek) == cipher
     380    assert desfunc(desfunc(plain, ek), dk) == plain
     381    assert desfunc(desfunc(plain, dk), ek) == plain
     382    print 'test succeeded.'
  • xpra/server/rfb/rfb_const.py

     
     1# This file is part of Xpra.
     2# Copyright (C) 2017 Antoine Martin <antoine@devloop.org.uk>
     3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     4# later version. See the file COPYING for details.
     5
     6import struct
     7
     8#merge header and packet if packet is smaller than:
     9PIXEL_FORMAT = "BBBBHHHBBBBBB"
     10
     11
     12class RFBClientMessage(object):
     13    """ client to server messages """
     14    SETPIXELFORMAT = 0
     15    SETENCODINGS = 2
     16    FRAMEBUFFERUPDATEREQUEST = 3
     17    KEYEVENT = 4
     18    POINTEREVENT = 5
     19    CLIENTCUTTEXT = 6
     20    #optional:
     21    FILETRANSFER = 7
     22    SETSCALE = 8
     23    SETSERVERINPUT = 9
     24    SETSW = 10
     25    TEXTCHAT = 11
     26    KEYFRAMEREQUEST = 12
     27    KEEPALIVE = 13
     28    SETSCALEFACTOR = 15
     29    REQUESTSESSION = 20
     30    SETSESSION = 21
     31    NOTIFYPLUGINSTREAMING = 80
     32    VMWARE = 127
     33    CARCONNECTIVITY = 128
     34    ENABLECONTINUOUSUPDATES = 150
     35    CLIENTFENCE = 248
     36    OLIVECALLCONTROL = 249
     37    XVPCLIENTMESSAGE = 250
     38    SETDESKTOPSIZE = 251
     39    TIGHT = 252
     40    GIICLIENTMESSAGE = 253
     41    VMWARE = 254
     42    QEMUCLIENTMESSAGE = 255
     43
     44    PACKET_TYPE_STR = {
     45        SETPIXELFORMAT               : "SetPixelFormat",
     46        SETENCODINGS                 : "SetEncodings",
     47        FRAMEBUFFERUPDATEREQUEST     : "FramebufferUpdateRequest",
     48        KEYEVENT                     : "KeyEvent",
     49        POINTEREVENT                 : "PointerEvent",
     50        CLIENTCUTTEXT                : "ClientCutText",
     51        #optional:
     52        FILETRANSFER                 : "FileTransfer",
     53        SETSCALE                     : "SetScale",
     54        SETSERVERINPUT               : "SetServerInput",
     55        SETSW                        : "SetSW",
     56        TEXTCHAT                     : "TextChat",
     57        KEYFRAMEREQUEST              : "KeyFrameRequest",
     58        KEEPALIVE                    : "KeepAlive",
     59        SETSCALEFACTOR               : "SetScaleFactor",
     60        REQUESTSESSION               : "RequestSession",
     61        SETSESSION                   : "SetSession",
     62        NOTIFYPLUGINSTREAMING        : "NotifiyPluginStreaming",
     63        VMWARE                       : "VMWare",
     64        CARCONNECTIVITY              : "CarConnectivity",
     65        ENABLECONTINUOUSUPDATES      : "EnableContiniousUpdates",
     66        CLIENTFENCE                  : "ClientFence",
     67        OLIVECALLCONTROL             : "OliveCallControl",
     68        XVPCLIENTMESSAGE             : "XvpClientMessage",
     69        SETDESKTOPSIZE               : "SetDesktopSize",
     70        TIGHT                        : "Tight",
     71        GIICLIENTMESSAGE             : "GIIClientMessage",
     72        VMWARE                       : "VMWare",
     73        QEMUCLIENTMESSAGE            : "QEMUClientMessage",
     74    }
     75    PACKET_FMT = {
     76        SETPIXELFORMAT               : "!BBBB"+PIXEL_FORMAT,
     77        SETENCODINGS                 : "!BBH",
     78        FRAMEBUFFERUPDATEREQUEST     : "!BBHHHH",
     79        KEYEVENT                     : "!BBBBi",
     80        POINTEREVENT                 : "!BBHH",
     81        CLIENTCUTTEXT                : "!BBBBi",
     82        }
     83    PACKET_STRUCT = {}
     84    for ptype, fmt in PACKET_FMT.items():
     85        PACKET_STRUCT[ptype] = struct.Struct(fmt)
     86
     87
     88class RFBServerMessage(object):
     89    #server to client messages:
     90    FRAMEBUFFERUPDATE = 0
     91    SETCOLORMAPENTRIES = 1
     92    BELL = 2
     93    SERVERCUTTEXT = 3
     94    #optional:
     95    RESIZEFRAMEBUFFER1 = 4
     96    KEYFRAMEUPDATE = 4
     97    FILETRANSFER = 7
     98    TEXTCHAT = 11
     99    KEEPALIVE = 13
     100    RESIZEFRAMEBUFFER2 = 15
     101    VMWARE1 = 127
     102    CARCONNECTIVITY = 128
     103    ENDOFCONTINOUSUPDATES = 150
     104    SERVERSTATE = 173
     105    SERVERFENCE = 248
     106    OLIVECALLCONTROL = 249
     107    XVPSERVERMESSAGE = 250
     108    TIGHT = 252
     109    GIISERVERMESSAGE = 253
     110    VMWARE2 = 254
     111    QEMUSERVERMESSAGE = 255
     112
     113    PACKET_TYPE_STR = {
     114        FRAMEBUFFERUPDATE        : "FramebufferUpdate",
     115        SETCOLORMAPENTRIES       : "SetColorMapEntries",
     116        BELL                     : "Bell",
     117        SERVERCUTTEXT            : "ServerCutText",
     118        #optional:
     119        RESIZEFRAMEBUFFER1       : "ResizeFrameBuffer1",
     120        KEYFRAMEUPDATE           : "KeyFrameUpdate",
     121        FILETRANSFER             : "FileTransfer",
     122        TEXTCHAT                 : "TextChat",
     123        KEEPALIVE                : "KeepAlive",
     124        RESIZEFRAMEBUFFER2       : "ResizeFrameBuffer2",
     125        VMWARE1                  : "VMWare1",
     126        CARCONNECTIVITY          : "CarConnectivity",
     127        ENDOFCONTINOUSUPDATES    : "EndOfContinousUpdates",
     128        SERVERSTATE              : "ServerState",
     129        SERVERFENCE              : "ServerFence",
     130        OLIVECALLCONTROL         : "OliveCallControl",
     131        XVPSERVERMESSAGE         : "XvpServerMessage",
     132        TIGHT                    : "Tight",
     133        GIISERVERMESSAGE         : "GIIServerMessage",
     134        VMWARE2                  : "VMWare2",
     135        QEMUSERVERMESSAGE        : "QEMUServerMessage",
     136        }
     137
     138class RFBEncoding(object):
     139    RAW = 0
     140    COPYRECT = 1
     141    RRE = 2
     142    CORRE = 4
     143    HEXTILE = 5
     144    ZLIB = 6
     145    TIGHT = 7
     146    ZLIBHEX = 8
     147    TRLE = 15
     148    ZRLE = 16
     149    H264 = 20
     150    JPEG = 21
     151    JRLE = 22
     152    HITACHI_ZYWRLE = 17
     153    DESKTOPSIZE = -223
     154    LASTRECT = -224
     155    CURSOR = -239
     156    XCURSOR = -240
     157    QEMU_POINTER = -257
     158    QEMU_KEY = -258
     159    QEMU_AUDIO = -259
     160    GII = -305
     161    DESKTOPNAME = -307
     162    EXTENDEDDESKTOPSIZE = -308
     163    XVP = -309
     164    FENCE = -312
     165    CONTINUOUSUPDATES = -313
     166    CURSORWITHALPHA = -314
     167    VA_H264 = 0x48323634
     168
     169    #-23 to -32    JPEG Quality Level Pseudo-encoding
     170    #-247 to -256    Compression Level Pseudo-encoding
     171    #-412 to -512    JPEG Fine-Grained Quality Level Pseudo-encoding
     172    #-763 to -768    JPEG Subsampling Level Pseudo-encoding
     173   
     174    ENCODING_STR = {
     175        RAW                 : "Raw",
     176        COPYRECT            : "CopyRect",
     177        RRE                 : "RRE",
     178        CORRE               : "CoRRE",
     179        HEXTILE             : "Hextile",
     180        ZLIB                : "Zlib",
     181        TIGHT               : "Tight",
     182        ZLIBHEX             : "ZlibHex",
     183        TRLE                : "TRLE",
     184        ZRLE                : "ZRLE",
     185        H264                : "H264",
     186        JPEG                : "JPEG",
     187        JRLE                : "JRLE",
     188        HITACHI_ZYWRLE      : "HITACHI_ZYWRLE",
     189        DESKTOPSIZE         : "DesktopSize",
     190        LASTRECT            : "LastRect",
     191        CURSOR              : "Cursor",
     192        XCURSOR             : "XCursor",
     193        QEMU_POINTER        : "QEMU Pointer",
     194        QEMU_KEY            : "QEMU Key",
     195        QEMU_AUDIO          : "QEMU Audio",
     196        GII                 : "GII",
     197        DESKTOPNAME         : "DesktopName",
     198        EXTENDEDDESKTOPSIZE : "ExtendedDesktopSize",
     199        XVP                 : "Xvp",
     200        FENCE               : "Fence",
     201        CONTINUOUSUPDATES   : "ContinuousUpdates",
     202        CURSORWITHALPHA     : "CursorWithAlpha",
     203        VA_H264             : "VA_H264",
     204        }
     205
     206
     207class RFBAuth(object):
     208    INVALID = 0
     209    NONE = 1
     210    VNC = 2
     211    TIGHT = 16
     212    AUTH_STR = {
     213        INVALID    : "Invalid",
     214        NONE       : "None",
     215        VNC        : "VNC",
     216        TIGHT      : "Tight",
     217        5                   : "RA2",
     218        6                   : "RA2ne",
     219        17                  : "Ultra",
     220        18                  : "TLS",
     221        19                  : "VeNCrypt",
     222        20                  : "SASL",
     223        21                  : "MD5",
     224        22                  : "xvp",
     225        }
     226    for i in (3, 4):
     227        AUTH_STR[i] = "RealVNC"
     228    for i in range(7, 16):
     229        AUTH_STR[i] = "RealVNC"
     230    for i in range(128, 255):
     231        AUTH_STR[i] = "RealVNC"
     232    for i in range(30, 35):
     233        AUTH_STR[i] = "Apple"
     234
     235
     236RFB_KEYNAMES = {
     237    0xff08      : "BackSpace",
     238    0xff09      : "Tab",
     239    0xff0d      : "Return",
     240    0xff1b      : "Escape",
     241    0xff63      : "Insert",
     242    0xffff      : "Delete",
     243    0xff50      : "Home",
     244    0xff57      : "End",
     245    0xff55      : "PageUp",
     246    0xff56      : "PageDown",
     247    0xff51      : "Left",
     248    0xff52      : "Up",
     249    0xff53      : "Right",
     250    0xff54      : "Down",
     251    0xffe1      : "Shift_L",
     252    0xffe2      : "Shift_R",
     253    0xffe3      : "Control_L",
     254    0xffe4      : "Control_R",
     255    0xffe7      : "Meta_L",
     256    0xffe8      : "Meta_R",
     257    0xffe9      : "Alt_L",
     258    0xffea      : "Alt_R",
     259    }
     260for i in range(1, 13):
     261    RFB_KEYNAMES[0xffbe+(i-1)] = "F%i" % i
  • xpra/server/rfb/rfb_protocol.py

     
    33# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
    44# later version. See the file COPYING for details.
    55
     6import os
    67import struct
    78from socket import error as socket_error
    89import binascii
     
    1314log = Logger("network", "protocol", "rfb")
    1415
    1516from xpra.os_util import Queue
    16 from xpra.util import repr_ellipsized, envint
     17from xpra.util import repr_ellipsized, envint, nonl
    1718from xpra.make_thread import make_thread, start_thread
     19from xpra.net.protocol import force_flush_queue, exit_queue
    1820from xpra.net.common import ConnectionClosedException          #@UndefinedVariable (pydev false positive)
    1921from xpra.net.bytestreams import ABORT
     22from xpra.server.rfb.rfb_const import RFBClientMessage, RFBAuth, PIXEL_FORMAT
    2023
    2124READ_BUFFER_SIZE = envint("XPRA_READ_BUFFER_SIZE", 65536)
    2225#merge header and packet if packet is smaller than:
    23 PIXEL_FORMAT = "BBBBHHHBBBBBB"
    2426
    25 RFB_SETPIXELFORMAT = 0
    26 RFB_SETENCODINGS = 2
    27 RFB_FRAMEBUFFERUPDATEREQUEST = 3
    28 RFB_KEYEVENT = 4
    29 RFB_POINTEREVENT = 5
    30 RFB_CLIENTCUTTEXT = 6
    31 PACKET_TYPE = {
    32     RFB_SETPIXELFORMAT              : "SetPixelFormat",
    33     RFB_SETENCODINGS                : "SetEncodings",
    34     RFB_FRAMEBUFFERUPDATEREQUEST    : "FramebufferUpdateRequest",
    35     RFB_KEYEVENT                    : "KeyEvent",
    36     RFB_POINTEREVENT                : "PointerEvent",
    37     RFB_CLIENTCUTTEXT               : "ClientCutText",
    38     }
    39 PACKET_FMT = {
    40     RFB_SETPIXELFORMAT              : "!BBBB"+PIXEL_FORMAT,
    41     RFB_SETENCODINGS                : "!BBH",
    42     RFB_FRAMEBUFFERUPDATEREQUEST    : "!BBHHHH",
    43     RFB_KEYEVENT                    : "!BBBBi",
    44     RFB_POINTEREVENT                : "!BBHH",
    45     RFB_CLIENTCUTTEXT               : "!BBBBi",
    46     }
    47 PACKET_STRUCT = {}
    48 for ptype, fmt in PACKET_FMT.items():
    49     PACKET_STRUCT[ptype] = struct.Struct(fmt)
    5027
    51 
    5228class RFBProtocol(object):
    5329    CONNECTION_LOST = "connection-lost"
    5430    INVALID = "invalid"
    5531
    56     def __init__(self, scheduler, conn, process_packet_cb, get_rfb_pixelformat, session_name="Xpra"):
     32    def __init__(self, scheduler, conn, auth, process_packet_cb, get_rfb_pixelformat, session_name="Xpra"):
    5733        """
    5834            You must call this constructor and source_has_more() from the main thread.
    5935        """
     
    6238        self.timeout_add = scheduler.timeout_add
    6339        self.idle_add = scheduler.idle_add
    6440        self._conn = conn
     41        self._authenticator = auth
    6542        self._process_packet_cb = process_packet_cb
    6643        self._get_rfb_pixelformat = get_rfb_pixelformat
    6744        self.session_name = session_name
    6845        self._write_queue = Queue(1)
    6946        self._buffer = b""
     47        self._challenge = None
    7048        #counters:
    7149        self.input_packetcount = 0
    7250        self.input_raw_packetcount = 0
     
    8765        return len(packet)
    8866
    8967    def _parse_protocol_handshake(self, packet):
     68        log("parse_protocol_handshake(%s)", nonl(packet))
    9069        if len(packet)<12:
    9170            return 0
    9271        if not packet.startswith(b'RFB '):
     
    9473            return 0
    9574        #ie: packet==b'RFB 003.008\n'
    9675        self._protocol_version = tuple(int(x) for x in packet[4:11].split("."))
    97         log.info("RFB version %s", b".".join(str(x) for x in self._protocol_version))
     76        log.info("RFB version %s connection from %s", b".".join(str(x) for x in self._protocol_version), self._conn.target)
     77        if self._protocol_version!=(3, 8):
     78            msg = "unsupported protocol version"
     79            log.error("Error: %s", msg)
     80            self.send(struct.pack("!BI", 0, len(msg))+msg)
     81            self.invalid(msg, packet)
     82            return 0
    9883        #reply with Security Handshake:
    9984        self._packet_parser = self._parse_security_handshake
    100         self.send(struct.pack("BB", 1, 1))
     85        if self._authenticator and self._authenticator.requires_challenge():
     86            security_types = [RFBAuth.VNC]
     87        else:
     88            security_types = [RFBAuth.NONE]
     89        packet = struct.pack("B", len(security_types))
     90        for x in security_types:
     91            packet += struct.pack("B", x)
     92        self.send(packet)
    10193        return 12
    10294
    10395    def _parse_security_handshake(self, packet):
    104         if packet!=b"\1":
    105             self._invalid_header(packet, "invalid security handshake response")
     96        log("parse_security_handshake(%s)", binascii.hexlify(packet))
     97        try:
     98            auth = struct.unpack("B", packet)[0]
     99        except:
     100            self._internal_error(packet, "cannot parse security handshake response '%s'" % binascii.hexlify(packet))
    106101            return 0
     102        auth_str = RFBAuth.AUTH_STR.get(auth, auth)
     103        if auth==RFBAuth.VNC:
     104            #send challenge:
     105            self._packet_parser = self._parse_challenge
     106            assert self._authenticator
     107            self._challenge = os.urandom(16)
     108            log.info("RFB challenge: %s", binascii.hexlify(self._challenge))
     109            self.send(self._challenge)
     110            return 1
     111        if self._authenticator and self._authenticator.requires_challenge():
     112            self._invalid_header(packet, "invalid security handshake response, authentication is required")
     113            return 0
     114        log("parse_security_handshake: auth=%s, sending SecurityResult", auth_str)
    107115        #Security Handshake, send SecurityResult Handshake
    108116        self._packet_parser = self._parse_security_result
    109         self.send(struct.pack("BBBB", 0, 0, 0, 0))
     117        self.send(struct.pack("!I", 0))
    110118        return 1
    111119
     120    def _parse_challenge(self, response):
     121        assert self._authenticator
     122        log("parse_challenge(%s)", binascii.hexlify(response))
     123        try:
     124            assert len(response)==16
     125            from xpra.server.rfb.d3des import generate_response
     126            password = self._authenticator.get_password()
     127            assert password, "no password available from %s authentication module" % self._authenticator
     128            password = password.ljust(8, "\x00")[:8]
     129            #log("padded password=%s", password)
     130            r = generate_response(password, self._challenge)
     131            if r==response:
     132                log("challenge authentication succeeded")
     133                self.send(struct.pack("!I", 0))
     134                self._packet_parser = self._parse_security_result
     135                return 16
     136            log("expected %s but got %s", binascii.hexlify(r), binascii.hexlify(response))
     137            log.warn("Warning: authentication challenge response failure")
     138            log.warn(" password does not match")
     139        except Exception as e:
     140            log("parse_challenge(%s)", binascii.hexlify(response), exc_info=True)
     141            log.error("Error: authentication challenge failure:")
     142            log.error(" %s", e)
     143        def fail_challenge():
     144            self.send(struct.pack("!I", 1))
     145            self.close()
     146        self.timeout_add(1000, fail_challenge)
     147        return len(response)
     148
    112149    def _parse_security_result(self, packet):
    113         if packet!=b"\0":
    114             self._invalid_header(packet, "invalid security result")
    115             return 0
     150        log("parse_security_result(%s)", binascii.hexlify(packet))
     151        sharing  = packet != b"\0"
     152        log("_parse_security_result: sharing=%s, sending ClientInit", sharing)
    116153        #send ClientInit
    117154        self._packet_parser = self._parse_rfb
    118155        w, h, bpp, depth, bigendian, truecolor, rmax, gmax, bmax, rshift, bshift, gshift = self._get_rfb_pixelformat()
     
    126163            ptype = ord(packet[0])
    127164        except:
    128165            ptype = packet[0]
    129         packet_type = PACKET_TYPE.get(ptype)
     166        packet_type = RFBClientMessage.PACKET_TYPE_STR.get(ptype)
    130167        if not packet_type:
    131168            self.invalid("unknown RFB packet type: %#x" % ptype, packet)
    132169            return 0
    133         s = PACKET_STRUCT[ptype]        #ie: Struct("!BBBB")
     170        s = RFBClientMessage.PACKET_STRUCT.get(ptype)     #ie: Struct("!BBBB")
     171        if not s:
     172            self.invalid("RFB packet type '%s' is not supported" % packet_type, packet)
     173            return 0
    134174        if len(packet)<s.size:
    135175            return 0
    136176        size = s.size
     
    137177        values = list(s.unpack(packet[:size]))
    138178        values[0] = packet_type
    139179        #some packets require parsing extra data:
    140         if ptype==RFB_SETENCODINGS:
     180        if ptype==RFBClientMessage.SETENCODINGS:
    141181            N = values[2]
    142182            estruct = struct.Struct("!"+"i"*N)
    143183            size += estruct.size
     
    145185                return 0
    146186            encodings = estruct.unpack(packet[s.size:size])
    147187            values.append(encodings)
    148         elif ptype==RFB_CLIENTCUTTEXT:
     188        elif ptype==RFBClientMessage.CLIENTCUTTEXT:
    149189            l = values[4]
    150190            size += l
    151191            if len(packet)<size:
     
    153193            text = packet[s.size:size]
    154194            values.append(text)
    155195        self.input_packetcount += 1
    156         #log("RFB packet: %s", values)
     196        log("RFB packet: %s: %s", packet_type, values[1:])
    157197        #now trigger the callback:
    158198        self._process_packet_cb(self, values)
    159199        #return part of packet not consumed:
     
    193233                self._read_thread.start()
    194234        self.idle_add(start_network_read_thread)
    195235
     236
     237    def gibberish(self, msg, data):
     238        GIBBERISH = "gibberish"
     239        self.idle_add(self._process_packet_cb, self, [GIBBERISH, msg, data])
     240        # Then hang up:
     241        self.timeout_add(1000, self._connection_lost, msg)
     242
     243
     244    def send_disconnect(self, _reason, *_extra):
     245        #no such packet in RFB, just close
     246        self.close()
     247
     248
    196249    def send(self, packet):
    197250        if self._closed:
    198             log("send(%s ...) connection is closed already, not sending", packet[0])
     251            log("connection is closed already, not sending packet")
    199252            return
    200         log("send(%s ...)", packet[0])
     253        log("send(%i bytes: %s..)", len(packet), binascii.hexlify(packet[:16]))
    201254        with self._write_lock:
    202255            if self._closed:
    203256                return
     
    255308    def _read_thread_loop(self):
    256309        self._io_thread_loop("read", self._read)
    257310    def _read(self):
    258         buf = self._conn.read(READ_BUFFER_SIZE)
     311        c = self._conn
     312        if not c:
     313            return None
     314        buf = c.read(READ_BUFFER_SIZE)
    259315        #log("read()=%s", repr_ellipsized(buf))
    260316        if not buf:
    261317            log("read thread: eof")
     
    386442
    387443
    388444    def close(self):
    389         log("Protocol.close() closed=%s, connection=%s", self._closed, self._conn)
     445        log("RFBProtocol.close() closed=%s, connection=%s", self._closed, self._conn)
    390446        if self._closed:
    391447            return
    392448        self._closed = True
     
    394450        c = self._conn
    395451        if c:
    396452            try:
    397                 log("Protocol.close() calling %s", c.close)
     453                log("RFBProtocol.close() calling %s", c.close)
    398454                c.close()
    399455            except:
    400456                log.error("error closing %s", self._conn, exc_info=True)
     
    401457            self._conn = None
    402458        self.terminate_queue_threads()
    403459        self.idle_add(self.clean)
    404         log("Protocol.close() done")
     460        log("RFBProtocol.close() done")
    405461
    406462    def clean(self):
    407463        #clear all references to ensure we can get garbage collected quickly:
     
    412468    def terminate_queue_threads(self):
    413469        log("terminate_queue_threads()")
    414470        #make all the queue based threads exit by adding the empty marker:
    415         exit_queue = Queue()
    416         for _ in range(10):     #just 2 should be enough!
    417             exit_queue.put(None)
    418         try:
    419             owq = self._write_queue
    420             self._write_queue = exit_queue
    421             #discard all elements in the old queue and push the None marker:
    422             try:
    423                 while owq.qsize()>0:
    424                     owq.read(False)
    425             except:
    426                 pass
    427             owq.put_nowait(None)
    428         except:
    429             pass
    430         try:
    431             orq = self._read_queue
    432             self._read_queue = exit_queue
    433             #discard all elements in the old queue and push the None marker:
    434             try:
    435                 while orq.qsize()>0:
    436                     orq.read(False)
    437             except:
    438                 pass
    439             orq.put_nowait(None)
    440         except:
    441             pass
     471        owq = self._write_queue
     472        self._write_queue = exit_queue()
     473        force_flush_queue(owq)
  • xpra/server/rfb/rfb_server.py

     
     1# coding=utf8
     2# This file is part of Xpra.
     3# Copyright (C) 2017 Antoine Martin <antoine@devloop.org.uk>
     4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     5# later version. See the file COPYING for details.
     6
     7
     8from xpra.util import nonl, csv
     9from xpra.os_util import POSIX
     10from xpra.server.rfb.rfb_const import RFBEncoding, RFB_KEYNAMES
     11from xpra.server.rfb.rfb_protocol import RFBProtocol
     12from xpra.server.rfb.rfb_source import RFBSource
     13 
     14from xpra.log import Logger
     15log = Logger("rfb")
     16
     17
     18"""
     19    Adds RFB packet handler to a server.
     20"""
     21class RFBServer(object):
     22
     23    def init(self):
     24        self.rfb_buttons = 0
     25        self.x11_keycodes_for_keysym = {}
     26        if POSIX:
     27            from xpra.x11.bindings.keyboard_bindings import X11KeyboardBindings #@UnresolvedImport
     28            X11Keyboard = X11KeyboardBindings()
     29            x11_keycodes = X11Keyboard.get_keycode_mappings()
     30            for keycode, keysyms in x11_keycodes.items():
     31                for keysym in keysyms:
     32                    self.x11_keycodes_for_keysym.setdefault(keysym, []).append(keycode)
     33            log("x11_keycodes_for_keysym=%s", self.x11_keycodes_for_keysym)
     34
     35
     36    def _get_rfb_desktop_model(self):
     37        models = self._window_to_id.keys()
     38        if len(models)!=1:
     39            log.error("RFB can only handle a single desktop window, found %i", len(self._window_to_id))
     40            return None
     41        return models[0]
     42
     43    def _get_rfb_desktop_wid(self):
     44        ids = self._window_to_id.values()
     45        if len(ids)!=1:
     46            log.error("RFB can only handle a single desktop window, found %i", len(self._window_to_id))
     47            return None
     48        return ids[0]
     49
     50
     51    def handle_rfb_connection(self, conn):
     52        model = self._get_rfb_desktop_model()
     53        if not model:
     54            conn.close()
     55            return
     56        def rfb_protocol_class(conn):
     57            auth = self.make_authenticator("rfb", "rfb", conn)
     58            return RFBProtocol(self, conn, auth, self.process_rfb_packet, self.get_rfb_pixelformat, self.session_name or "Xpra Server")
     59        p = self.do_make_protocol("rfb", conn, rfb_protocol_class)
     60        p.send_protocol_handshake()
     61
     62    def process_rfb_packet(self, proto, packet):
     63        #log("RFB packet: '%s'", nonl(packet))
     64        fn_name = "_process_rfb_%s" % packet[0]
     65        fn = getattr(self, fn_name, None)
     66        if not fn:
     67            log.warn("Warning: no RFB handler for %s", fn_name)
     68            return
     69        self.idle_add(fn, proto, packet)
     70
     71
     72    def get_rfb_pixelformat(self):
     73        model = self._get_rfb_desktop_model()
     74        w, h = model.get_dimensions()
     75        #w, h, bpp, depth, bigendian, truecolor, rmax, gmax, bmax, rshift, bshift, gshift
     76        return w, h, 32, 32, False, True, 255, 255, 255, 16, 8, 0
     77
     78    def _process_rfb_invalid(self, proto, packet):
     79        self.disconnect_protocol(proto, "invalid packet: %s" % (packet[1:]))
     80
     81    def _process_rfb_gibberish(self, proto, packet):
     82        self.disconnect_protocol(proto, "invalid packet: %s" % (packet[1:]))
     83
     84    def _process_rfb_authenticated(self, proto, _packet):
     85        model = self._get_rfb_desktop_model()
     86        if not model:
     87            proto.close()
     88            return
     89        self.accept_protocol(proto)
     90        #use blocking sockets from now on:
     91        from xpra.net.bytestreams import set_socket_timeout
     92        set_socket_timeout(proto._conn, None)
     93        source = RFBSource(proto, self._window_to_id.keys()[0])
     94        self._server_sources[proto] = source
     95        w, h = model.get_dimensions()
     96        source.damage(self._window_to_id[model], model, 0, 0, w, h)
     97
     98    def _process_rfb_PointerEvent(self, _proto, packet):
     99        buttons, x, y = packet[1:4]
     100        wid = self._get_rfb_desktop_wid()
     101        self._move_pointer(wid, (x, y))
     102        if buttons!=self.rfb_buttons:
     103            #figure out which buttons have changed:
     104            for button in range(8):
     105                mask = 2**button
     106                if buttons & mask != self.rfb_buttons & mask:
     107                    pressed = bool(buttons & mask)
     108                    self.button_action(1+button, pressed, -1)
     109            self.rfb_buttons = buttons
     110
     111    def _process_rfb_KeyEvent(self, _proto, packet):
     112        pressed, _, _, key = packet[1:5]
     113        wid = self._get_rfb_desktop_wid()
     114        keyval = 0
     115        name = RFB_KEYNAMES.get(key) or chr(key)
     116        keycode = 0
     117        keycodes = self.x11_keycodes_for_keysym.get(name, 0)
     118        log("keycodes(%s)=%s", name, keycodes)
     119        if keycodes:
     120            keycode = keycodes[0]
     121            modifiers = []
     122            self._handle_key(wid, bool(pressed), name, keyval, keycode, modifiers)
     123
     124    def _process_rfb_SetEncodings(self, _proto, packet):
     125        n, encodings = packet[2:4]
     126        known_encodings = [RFBEncoding.ENCODING_STR.get(x) for x in encodings if x in RFBEncoding.ENCODING_STR]
     127        log("%i encodings: %s", n, csv(known_encodings))
     128        unknown_encodings = [x for x in encodings if x not in RFBEncoding.ENCODING_STR]
     129        if unknown_encodings:
     130            log("%i unknown encodings: %s", n, csv(unknown_encodings))
     131
     132    def _process_rfb_SetPixelFormat(self, _proto, packet):
     133        log("RFB: SetPixelFormat %s", packet)
     134        #w, h, bpp, depth, bigendian, truecolor, rmax, gmax, bmax, rshift, bshift, gshift = packet
     135
     136    def _process_rfb_FramebufferUpdateRequest(self, _proto, packet):
     137        #pressed, _, _, keycode = packet[1:5]
     138        inc, x, y, w, h = packet[1:6]
     139        if not inc:
     140            model = self._get_rfb_desktop_model()
     141            self._damage(model, x, y, w, h)
     142
     143    def _process_rfb_ClientCutText(self, _proto, packet):
     144        #l = packet[4]
     145        text = packet[5]
     146        log("got rfb clipboard text: %s", nonl(text))
  • xpra/server/rfb/rfb_source.py

     
     1# coding=utf8
     2# This file is part of Xpra.
     3# Copyright (C) 2017 Antoine Martin <antoine@devloop.org.uk>
     4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     5# later version. See the file COPYING for details.
     6
     7import struct
     8from threading import Event
     9
     10from xpra.os_util import memoryview_to_bytes
     11from xpra.log import Logger
     12log = Logger("rfb")
     13
     14
     15class RFBSource(object):
     16
     17    def __init__(self, protocol, desktop):
     18        self.protocol = protocol
     19        self.desktop = desktop
     20        self.close_event = Event()
     21        self.log_disconnect = True
     22        self.ui_client = True
     23        self.counter = 0
     24        self.uuid = "todo: use protocol?"
     25
     26    def is_closed(self):
     27        return self.close_event.isSet()
     28
     29    def close(self):
     30        pass
     31
     32    def ping(self):
     33        pass
     34
     35    def keys_changed(self):
     36        pass
     37
     38
     39    def send_server_event(self, *_args):
     40        pass
     41
     42    def send_cursor(self):
     43        pass
     44
     45
     46    def damage(self, _wid, window, x, y, w, h, _options=None):
     47        from xpra.net.protocol import PACKET_JOIN_SIZE
     48        img = window.get_image(x, y, w, h)
     49        log("damage: %s", img)
     50        fbupdate = struct.pack("!BBH", 0, 0, 1)
     51        encoding = 0    #Raw
     52        rect = struct.pack("!HHHHi", x, y, w, h, encoding)
     53        if img.get_rowstride()!=w*4:
     54            img.restride(w*4)
     55        pixels = img.get_pixels()
     56        assert len(pixels)>=4*w*h
     57        pixels = pixels[:4*w*h]
     58        if len(pixels)<=PACKET_JOIN_SIZE:
     59            self.send(fbupdate+rect+memoryview_to_bytes(pixels))
     60        else:
     61            self.send(fbupdate+rect)
     62            self.send(pixels)
     63
     64    def send_clipboard(self, text):
     65        nocr = text.replace("\r", "")
     66        msg = struct.pack("!BBBBI", 3, 0, 0, 0, len(nocr))+nocr
     67        self.send(msg)
     68
     69    def bell(self, *_args):
     70        msg = struct.pack("!B", 2)
     71        self.send(msg)
     72
     73    def send(self, msg):
     74        p = self.protocol
     75        if p:
     76            p.send(msg)
  • xpra/server/server_core.py

     
    141141    def __init__(self):
    142142        log("ServerCore.__init__()")
    143143        self.start_time = monotonic_time()
    144         self.auth_class = None
    145         self.tcp_auth_class = None
    146         self.ws_auth_class = None
    147         self.wss_auth_class = None
    148         self.ssl_auth_class = None
    149         self.rfb_auth_class = None
    150         self.vsock_auth_class = None
     144        self.auth_classes = {}
    151145        self._when_ready = []
    152146        self.child_reaper = None
    153147        self.original_desktop_display = None
     
    157151        #networking bits:
    158152        self._socket_info = []
    159153        self._potential_protocols = []
     154        self._udp_listeners = []
     155        self._udp_protocols = {}
    160156        self._tcp_proxy_clients = []
    161157        self._tcp_proxy = ""
    162         self._ssl_wrap_socket = None
     158        self._ssl_wrap_server_socket = None
     159        self._ssl_wrap_client_socket = None
    163160        self._accept_timeout = SOCKET_TIMEOUT + 1
    164161        self.ssl_mode = None
    165162        self._html = False
     
    286283            self._tcp_proxy = False
    287284
    288285    def init_auth(self, opts):
    289         self.auth_class = self.get_auth_module("unix-domain", opts.auth, opts)
    290         self.tcp_auth_class = self.get_auth_module("tcp", opts.tcp_auth or opts.auth, opts)
    291         self.ws_auth_class = self.get_auth_module("ws", opts.ws_auth, opts)
    292         self.wss_auth_class = self.get_auth_module("wss", opts.wss_auth, opts)
    293         self.ssl_auth_class = self.get_auth_module("ssl", opts.ssl_auth or opts.tcp_auth or opts.auth, opts)
    294         self.rfb_auth_class = self.get_auth_module("rfb", opts.rfb_auth, opts)
    295         self.vsock_auth_class = self.get_auth_module("vsock", opts.vsock_auth, opts)
    296         authlog("init_auth(..) auth=%s, tcp auth=%s, ws auth=%s, wss auth=%s, ssl auth=%s, vsock auth=%s",
    297                 self.auth_class, self.tcp_auth_class, self.ws_auth_class, self.wss_auth_class, self.ssl_auth_class, self.vsock_auth_class)
     286        auth = self.get_auth_module("local-auth", opts.auth, opts)
     287        if WIN32:
     288            self.auth_classes["named-pipes"] = auth
     289        else:
     290            self.auth_classes["unix-domain"] = auth
     291        for x in ("tcp", "ws", "wss", "ssl", "rfb", "vsock"):
     292            opts_value = getattr(opts, "%s_auth" % x)
     293            self.auth_classes[x] = self.get_auth_module(x, opts_value, opts)
     294        authlog("init_auth(..) auth=%s", self.auth_classes)
    298295
    299296    def get_auth_module(self, socket_type, auth_str, opts):
    300297        authlog("get_auth_module(%s, %s, {..})", socket_type, auth_str)
     
    383380        self._default_packet_handlers = {
    384381            "hello":                                self._process_hello,
    385382            "disconnect":                           self._process_disconnect,
     383            "udp-control":                          self._process_udp_control,
    386384            Protocol.CONNECTION_LOST:               self._process_connection_lost,
    387385            Protocol.GIBBERISH:                     self._process_gibberish,
    388386            Protocol.INVALID:                       self._process_invalid,
     
    538536        self.do_cleanup()
    539537        self.cleanup_protocols(protocols, reason, True)
    540538        self._potential_protocols = []
     539        self.cleanup_udp_listeners()
    541540
    542541    def do_cleanup(self):
    543542        #allow just a bit of time for the protocol packet flush
     
    544543        sleep(0.1)
    545544
    546545
     546    def cleanup_udp_listeners(self):
     547        for udpl in self._udp_listeners:
     548            udpl.close()
     549        self._udp_listeners = []
     550
    547551    def cleanup_all_protocols(self, reason):
    548552        protocols = self.get_all_protocols()
    549553        self.cleanup_protocols(protocols, reason)
     
    569573            #named pipe listener uses a thread:
    570574            sock.new_connection_cb = self._new_connection
    571575            sock.start()
     576        elif socktype in ("udp", "dtls"):
     577            #socket_info = self.socket_info.get(sock)
     578            from xpra.net.udp_protocol import UDPListener
     579            udpl = UDPListener(sock, self.process_udp_packet)
     580            self._udp_listeners.append(udpl)
    572581        else:
    573582            from xpra.gtk_common.gobject_compat import import_glib
    574583            glib = import_glib()
     
    673682        peek_data, line1 = self.peek_connection(conn)
    674683
    675684        def ssl_wrap():
    676             ssl_sock = self._ssl_wrap_socket(sock)
     685            ssl_sock = self._ssl_wrap_server_socket(sock)
    677686            netlog("ssl wrapped socket(%s)=%s", sock, ssl_sock)
    678687            if ssl_sock is None:
    679688                #None means EOF! (we don't want to import ssl bits here)
     
    729738            self.handle_rfb_connection(conn)
    730739            return
    731740
    732         elif socktype=="tcp" and peek_data and (self._html or self._tcp_proxy or self._ssl_wrap_socket):
     741        elif peek_data and (
     742            (socktype=="tcp" and  (self._html or self._tcp_proxy or self._ssl_wrap_server_socket)) or
     743            (socktype=="udp" and self._ssl_wrap_server_socket and self._ssl_wrap_client_socket)
     744            ):
    733745            #see if the packet data is actually xpra or something else
    734746            #that we need to handle via a tcp proxy, ssl wrapper or the websockify adapter:
    735747            try:
     
    763775        netlog("make_protocol(%s, %s)", socktype, conn)
    764776        socktype = socktype.lower()
    765777        protocol = protocol_class(conn)
     778        protocol.socket_type = socktype
    766779        self._potential_protocols.append(protocol)
    767780        protocol.challenge_sent = False
    768781        protocol.authenticator = None
     
    769782        protocol.encryption = None
    770783        protocol.keyfile = None
    771784        if socktype=="tcp":
    772             protocol.auth_class = self.tcp_auth_class
     785            #special case for legacy encryption code:
    773786            protocol.encryption = self.tcp_encryption
    774787            protocol.keyfile = self.tcp_encryption_keyfile
    775788            if protocol.encryption and ENCRYPT_FIRST_PACKET:
     
    776789                authlog("encryption=%s, keyfile=%s", protocol.encryption, protocol.keyfile)
    777790                password = self.get_encryption_key(None, protocol.keyfile)
    778791                protocol.set_cipher_in(protocol.encryption, DEFAULT_IV, password, DEFAULT_SALT, DEFAULT_ITERATIONS, INITIAL_PADDING)
    779         elif socktype=="ssl":
    780             protocol.auth_class = self.ssl_auth_class
    781         elif socktype=="ws":
    782             protocol.auth_class = self.ws_auth_class
    783         elif socktype=="wss":
    784             protocol.auth_class = self.wss_auth_class
    785         elif socktype=="rfb":
    786             protocol.auth_class = self.rfb_auth_class
    787         elif socktype=="vsock":
    788             protocol.auth_class = self.vsock_auth_class
    789         else:
    790             protocol.auth_class = self.auth_class
    791         protocol.socket_type = socktype
    792792        protocol.invalid_header = self.invalid_header
    793         authlog("socktype=%s, auth class=%s, encryption=%s, keyfile=%s", socktype, protocol.auth_class, protocol.encryption, protocol.keyfile)
     793        authlog("socktype=%s, encryption=%s, keyfile=%s", socktype, protocol.encryption, protocol.keyfile)
    794794        protocol.start()
    795795        self.timeout_add(self._accept_timeout*1000, self.verify_connection_accepted, protocol)
    796796        return protocol
     
    811811            #xpra packet header, no need to wrap this connection
    812812            return True, conn, peek_data
    813813        frominfo = pretty_socket(conn.remote)
    814         if self._ssl_wrap_socket and peek_data[0] in (chr(0x16), 0x16):
    815             socktype = "SSL"
     814        if self._ssl_wrap_server_socket and peek_data[0] in (chr(0x16), 0x16):
     815            socktype = {
     816                "udp"   : "dtls",
     817                "tcp"   : "SSL",
     818                }[socktype]
    816819            sock, sockname, address, target = conn._socket, conn.local, conn.remote, conn.target
    817             sock = self._ssl_wrap_socket(sock)
     820            sock = self._ssl_wrap_server_socket(sock)
    818821            if sock is None:
    819822                #None means EOF! (we don't want to import ssl bits here)
    820823                netlog("ignoring SSL EOF error")
     
    850853        return True, conn, peek_data
    851854
    852855    def invalid_header(self, proto, data, msg=""):
    853         netlog("invalid_header(%s, %s bytes: '%s', %s) input_packetcount=%s, tcp_proxy=%s, html=%s, ssl=%s", proto, len(data or ""), msg, repr_ellipsized(data), proto.input_packetcount, self._tcp_proxy, self._html, bool(self._ssl_wrap_socket))
     856        netlog("invalid_header(%s, %s bytes: '%s', %s) input_packetcount=%s, tcp_proxy=%s, html=%s, ssl=%s",
     857               proto, len(data or ""), msg, repr_ellipsized(data), proto.input_packetcount, self._tcp_proxy, self._html, bool(self._ssl_wrap_server_socket))
    854858        err = "invalid packet format, %s" % self.guess_header_protocol(data)
    855859        proto.gibberish(err, data)
    856860
     
    10031007        netlog("send_disconnect(%s, %s, %s)", proto, reason, extra)
    10041008        if proto._closed:
    10051009            return
    1006         proto.send_now(["disconnect", reason]+list(extra))
     1010        proto.send_disconnect(reason, *extra)
    10071011        self.timeout_add(1000, self.force_disconnect, proto)
    10081012
    10091013    def force_disconnect(self, proto):
     
    11131117                self.disconnect_client(proto, SERVER_ERROR, "error accepting new connection")
    11141118
    11151119
     1120    def make_authenticator(self, socktype, username, conn):
     1121        authlog("make_authenticator%s", (socktype, username, conn))
     1122        authenticator = None
     1123        auth_class = self.auth_classes[socktype]
     1124        if auth_class:
     1125            authlog("creating authenticator %s for %s, with username=%s, connection=%s", auth_class, socktype, username, conn)
     1126            auth, aclass, options = auth_class
     1127            opts = dict(options)
     1128            opts["connection"] = conn
     1129            authenticator = aclass(username, **opts)
     1130            authlog("authenticator: %s(%s, %s)=%s", auth, username, opts, authenticator)
     1131        return authenticator
     1132
    11161133    def verify_hello(self, proto, c):
    11171134        remote_version = c.strget("version")
    11181135        verr = version_compat_check(remote_version)
     
    11191136        if verr is not None:
    11201137            self.disconnect_client(proto, VERSION_ERROR, "incompatible version: %s" % verr)
    11211138            proto.close()
    1122             return  False
     1139            return False
    11231140
    11241141        def auth_failed(msg):
    11251142            authlog.error("Error: authentication failed")
     
    11271144            self.timeout_add(1000, self.disconnect_client, proto, msg)
    11281145
    11291146        #authenticator:
    1130         username = c.strget("username")
    1131         if proto.authenticator is None and proto.auth_class:
    1132             authlog("creating authenticator %s with username=%s", proto.auth_class, username)
     1147        if not proto.authenticator:
     1148            username = c.strget("username")
    11331149            try:
    1134                 auth, aclass, options = proto.auth_class
    1135                 opts = dict(options)
    1136                 opts["connection"] = proto._conn
    1137                 ainstance = aclass(username, **opts)
    1138                 proto.authenticator = ainstance
    1139                 authlog("authenticator: %s(%s, %s)=%s", auth, username, opts, ainstance)
     1150                proto.authenticator = self.make_authenticator(proto.socktype, username, proto._conn)
    11401151            except Exception as e:
    1141                 authlog("instantiating authenticator for %s", proto, exc_info=True)
     1152                authlog("instantiating authenticator %s", proto.auth_class, exc_info=True)
    11421153                authlog.error("Error instantiating %s:", proto.auth_class)
    11431154                authlog.error(" %s", e)
    11441155                auth_failed("authentication failed")
     
    14411452        for socktype, _, info in self._socket_info:
    14421453            if info:
    14431454                si.setdefault(socktype, {}).setdefault("listeners", []).append(info)
    1444         for socktype, auth_class in {
    1445                                      "tcp"          : self.tcp_auth_class,
    1446                                      "ssl"          : self.ssl_auth_class,
    1447                                      "unix-domain"  : self.auth_class,
    1448                                      "vsock"        : self.vsock_auth_class,
    1449                                      }.items():
     1455        for socktype, auth_class in self.auth_classes.items():
    14501456            if auth_class:
    14511457                si.setdefault(socktype, {})["authenticator"] = auth_class[0], auth_class[2]
    14521458        return si
     
    14711477        except:
    14721478            netlog.error("Unhandled error while processing a '%s' packet from peer using %s", packet_type, handler, exc_info=True)
    14731479
     1480
    14741481    def handle_rfb_connection(self, conn):
    14751482        log.error("Error: RFB protocol is not supported by this server")
    14761483        conn.close()
     1484
     1485
     1486    def _process_udp_control(self, proto, packet):
     1487        proto.process_control(*packet[1:])
     1488
     1489    def process_udp_packet(self, udp_listener, uuid, seqno, synchronous, chunk, chunks, data, bfrom):
     1490        #log.info("process_udp_packet%s", (udp_listener, uuid, seqno, synchronous, chunk, chunks, len(data), bfrom))
     1491        protocol = self._udp_protocols.get(uuid)
     1492        if not protocol:
     1493            from xpra.net.udp_protocol import UDPServerProtocol, UDPSocketConnection
     1494            def udp_protocol_class(conn):
     1495                protocol = UDPServerProtocol(self, conn, self.process_packet)
     1496                protocol.large_packets.append("info-response")
     1497                protocol.receive_aliases.update(self._aliases)
     1498                return protocol
     1499            socktype = "udp"        #breaks dtls...
     1500            host, port = bfrom
     1501            sock = udp_listener._socket
     1502            if socktype=="dtls":
     1503                from dtls import do_patch   #@UnresolvedImport
     1504                do_patch()
     1505                if False:
     1506                    family = socket.AF_INET6
     1507                else:
     1508                    family = socket.AF_INET
     1509                #sock = socket.socket(family, socket.SOCK_DGRAM)
     1510                #sock.bind(("0.0.0.0", 10003))
     1511                sock.connect(bfrom)
     1512                sock  = self._ssl_wrap_client_socket(sock, handshake=False)
     1513                assert sock, "failed to wrap socket %s" % sock
     1514                sockname = sock.getsockname()
     1515                conn = SocketConnection(sock, sockname, (host, port), (host, port), socktype)
     1516            else:
     1517                sockname = sock.getsockname()
     1518                conn = UDPSocketConnection(sock, sockname, (host, port), (host, port), socktype)
     1519            conn.timeout = SOCKET_TIMEOUT
     1520            protocol = self.do_make_protocol(socktype, conn, udp_protocol_class)
     1521            self._udp_protocols[uuid] = protocol
     1522        #assert packetsize==datalen, "expected datalen=packetsize, but got %i!=%i" % (datalen, packetsize)
     1523        #assert len(data)==datalen, "expected %i bytes but got %i" % (datalen, len(data))
     1524        protocol.process_udp_data(seqno, synchronous, chunk, chunks, data, bfrom)
  • xpra/server/shadow/shadow_server_base.py

     
    1212from xpra.net.compression import Compressed
    1313from xpra.server.window.batch_config import DamageBatchConfig
    1414from xpra.server.shadow.root_window_model import RootWindowModel
     15from xpra.server.rfb.rfb_server import RFBServer
    1516from xpra.util import envint, DONE
    1617
    1718REFRESH_DELAY = envint("XPRA_SHADOW_REFRESH_DELAY", 50)
    1819
    1920
    20 class ShadowServerBase(object):
     21class ShadowServerBase(RFBServer):
    2122
    2223    def __init__(self, root_window):
    2324        self.root = root_window
     
    2930        self.timer = None
    3031        DamageBatchConfig.ALWAYS = True             #always batch
    3132        DamageBatchConfig.MIN_DELAY = 50            #never lower than 50ms
     33        RFBServer.init(self)
    3234
    3335    def cleanup(self):
    3436        self.stop_refresh()
     
    5254        else:
    5355            log.info(" on display of size %ix%i", w, h)
    5456
    55     def make_hello(self, source):
     57    def make_hello(self, _source):
    5658        return {"shadow" : True}
    5759
    58     def get_info(self, proto=None):
     60    def get_info(self, _proto=None):
    5961        if self.root_window_model:
    6062            return {"root-window" : self.root_window_model.get_info()}
    6163        return {}
    6264
    6365
    64     def get_window_position(self, window):
     66    def get_window_position(self, _window):
    6567        #we export the whole desktop as a window:
    6668        return 0, 0
    6769
     
    113115
    114116    ############################################################################
    115117
    116     def sanity_checks(self, proto, c):
     118    def sanity_checks(self, _proto, c):
    117119        server_uuid = c.strget("server_uuid")
    118120        if server_uuid:
    119121            if server_uuid==self.uuid:
     
    144146        """ don't override the existing desktop """
    145147        pass
    146148
    147     def set_keymap(self, server_source, force=False):
     149    def set_keymap(self, server_source, _force=False):
    148150        log.info("shadow server: setting default keymap translation")
    149151        self.keyboard_config = server_source.set_default_keymap()
    150152
  • xpra/server/socket_util.py

     
    108108        log("create_tcp_socket%s", (host, iport), exc_info=True)
    109109        raise InitException("failed to setup %s socket on %s:%s %s" % (socktype, host, iport, e))
    110110    def cleanup_tcp_socket():
    111         log.info("closing %s socket %s:%s", socktype, host, iport)
     111        log.info("closing %s socket %s:%s", socktype.lower(), host, iport)
    112112        try:
    113113            tcp_socket.close()
    114114        except:
     
    117117    log("%s: %s:%s : %s", socktype, host, iport, socket)
    118118    return socktype, tcp_socket, (host, iport)
    119119
     120def create_udp_socket(host, iport):
     121    if host.find(":")<0:
     122        listener = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     123        sockaddr = (host, iport)
     124    else:
     125        assert socket.has_ipv6, "specified an IPv6 address but this is not supported"
     126        res = socket.getaddrinfo(host, iport, socket.AF_INET6, socket.SOCK_DGRAM)
     127        listener = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
     128        sockaddr = res[0][-1]
     129    listener.bind(sockaddr)
     130    return listener
    120131
    121 def parse_bind_tcp(bind_tcp):
    122     tcp_sockets = set()
    123     if bind_tcp:
    124         for spec in bind_tcp:
     132def setup_udp_socket(host, iport, socktype="udp"):
     133    from xpra.log import Logger
     134    log = Logger("network")
     135    try:
     136        udp_socket = create_udp_socket(host, iport)
     137    except Exception as e:
     138        log("create_udp_socket%s", (host, iport), exc_info=True)
     139        raise InitException("failed to setup %s socket on %s:%s %s" % (socktype, host, iport, e))
     140    def cleanup_udp_socket():
     141        log.info("closing %s socket %s:%s", socktype, host, iport)
     142        try:
     143            udp_socket.close()
     144        except:
     145            pass
     146    add_cleanup(cleanup_udp_socket)
     147    log("%s: %s:%s : %s", socktype, host, iport, socket)
     148    return socktype, udp_socket, (host, iport)
     149
     150
     151def parse_bind_ip(bind_ip):
     152    ip_sockets = set()
     153    if bind_ip:
     154        for spec in bind_ip:
    125155            if ":" not in spec:
    126                 raise InitException("TCP port must be specified as [HOST]:PORT")
     156                raise InitException("port must be specified as [HOST]:PORT")
    127157            host, port = spec.rsplit(":", 1)
    128158            if host == "":
    129159                host = "127.0.0.1"
     
    135165                    assert iport>0 and iport<2**16
    136166                except:
    137167                    raise InitException("invalid port number: %s" % port)
    138             tcp_sockets.add((host, iport))
    139     return tcp_sockets
     168            ip_sockets.add((host, iport))
     169    return ip_sockets
    140170
    141171def setup_vsock_socket(cid, iport):
    142172    from xpra.log import Logger
  • xpra/server/source.py

     
    14021402#
    14031403    def next_packet(self):
    14041404        """ Called by protocol.py when it is ready to send the next packet """
    1405         packet, start_send_cb, end_send_cb, have_more = None, None, None, False
     1405        packet, start_send_cb, end_send_cb, fail_cb, have_more = None, None, None, None, False
    14061406        if not self.is_closed():
    14071407            if len(self.ordinary_packets)>0:
    14081408                packet = self.ordinary_packets.pop(0)
    14091409            elif len(self.packet_queue)>0:
    1410                 packet, _, _, start_send_cb, end_send_cb = self.packet_queue.popleft()
     1410                packet, _, _, start_send_cb, end_send_cb, fail_cb = self.packet_queue.popleft()
    14111411            have_more = packet is not None and (len(self.ordinary_packets)>0 or len(self.packet_queue)>0)
    1412         return packet, start_send_cb, end_send_cb, have_more
     1412        return packet, start_send_cb, end_send_cb, fail_cb, have_more
    14131413
    14141414    def send(self, *parts):
    14151415        """ This method queues non-damage packets (higher priority) """
     
    22942294        self.statistics.compression_work_qsizes.append((monotonic_time(), self.encode_work_queue.qsize()))
    22952295        self.encode_work_queue.put(fn_and_args)
    22962296
    2297     def queue_packet(self, packet, wid=0, pixels=0, start_send_cb=None, end_send_cb=None):
     2297    def queue_packet(self, packet, wid=0, pixels=0, start_send_cb=None, end_send_cb=None, fail_cb=None):
    22982298        """
    22992299            Add a new 'draw' packet to the 'packet_queue'.
    23002300            Note: this code runs in the non-ui thread
     
    23032303        self.statistics.packet_qsizes.append((now, len(self.packet_queue)))
    23042304        if wid>0:
    23052305            self.statistics.damage_packet_qpixels.append((now, wid, sum(x[2] for x in list(self.packet_queue) if x[1]==wid)))
    2306         self.packet_queue.append((packet, wid, pixels, start_send_cb, end_send_cb))
     2306        self.packet_queue.append((packet, wid, pixels, start_send_cb, end_send_cb, fail_cb))
    23072307        p = self.protocol
    23082308        if p:
    23092309            p.source_has_more()
  • xpra/server/window/window_source.py

     
    17261726            now = monotonic_time()
    17271727            damage_in_latency = now-process_damage_time
    17281728            self.statistics.damage_in_latency.append((now, width*height, actual_batch_delay, damage_in_latency))
    1729         self.queue_packet(packet, self.wid, width*height, start_send, damage_packet_sent)
     1729        fail_cb = self.get_fail_cb(packet)
     1730        #log.info("queuing %s packet with fail_cb=%s", coding, fail_cb)
     1731        self.queue_packet(packet, self.wid, width*height, start_send, damage_packet_sent, fail_cb)
    17301732
     1733    def get_fail_cb(self, packet):
     1734        def resend():
     1735            log.warn("paint packet failure, resending")
     1736            x,y,width,height = packet[2:6]
     1737            damage_packet_sequence = packet[8]
     1738            self.damage_packet_acked(damage_packet_sequence, width, height, 0, "")
     1739            self.idle_add(self.damage, x, y, width, height)
     1740        return resend
     1741
    17311742    def damage_packet_acked(self, damage_packet_sequence, width, height, decode_time, message):
    17321743        """
    17331744            The client is acknowledging a damage packet,
  • xpra/server/window/window_video_source.py

     
    14261426        return {}
    14271427
    14281428
     1429    def get_fail_cb(self, packet):
     1430        coding = packet[6]
     1431        if coding in self.common_video_encodings:
     1432            return None
     1433        return WindowSource.get_fail_cb(self, packet)
     1434
     1435
    14291436    def make_draw_packet(self, x, y, w, h, coding, data, outstride, client_options={}, options={}):
    14301437        #overriden so we can invalidate the scroll data:
    14311438        #log.error("make_draw_packet%s", (x, y, w, h, coding, "..", outstride, client_options)
  • xpra/x11/desktop_server.py

     
    88import gtk.gdk
    99import gobject
    1010import socket
    11 import struct
    12 from threading import Event
    1311
    14 from xpra.util import updict, log_screen_sizes, envbool, nonl
    15 from xpra.os_util import get_generic_os_name, memoryview_to_bytes
     12from xpra.util import updict, log_screen_sizes, envbool
     13from xpra.os_util import get_generic_os_name
    1614from xpra.platform.paths import get_icon
    1715from xpra.platform.gui import get_wm_name
     16from xpra.server.rfb.rfb_server import RFBServer
    1817from xpra.gtk_common.gobject_util import one_arg_signal, no_arg_signal
    1918from xpra.gtk_common.gobject_compat import import_glib
    2019from xpra.gtk_common.error import xswallow
     
    4746metadatalog = Logger("x11", "metadata")
    4847screenlog = Logger("screen")
    4948iconlog = Logger("icon")
    50 rfblog = Logger("rfb")
    5149
    5250glib = import_glib()
    5351
     
    274272    A server class for RFB / VNC-like desktop displays,
    275273    used with the "start-desktop" subcommand.
    276274"""
    277 class XpraDesktopServer(gobject.GObject, X11ServerBase):
     275class XpraDesktopServer(gobject.GObject, RFBServer, X11ServerBase):
    278276    __gsignals__ = {
    279277        "xpra-xkb-event"        : one_arg_signal,
    280278        "xpra-cursor-event"     : one_arg_signal,
     
    285283        gobject.GObject.__init__(self)
    286284        X11ServerBase.__init__(self)
    287285
     286    def init(self, opts):
     287        X11ServerBase.init(self, opts)
     288        RFBServer.init(self)
     289
    288290    def x11_init(self):
    289291        X11ServerBase.x11_init(self)
    290292        assert init_x11_filter() is True
     
    567569            offset_y += 0
    568570        return self.make_screenshot_packet_from_regions(regions)
    569571
    570 
    571     def _get_rfb_desktop_model(self):
    572         models = self._window_to_id.keys()
    573         if len(models)!=1:
    574             rfblog.error("RFB can only handle a single desktop window, found %i", len(self._window_to_id))
    575             return None
    576         return models[0]
    577 
    578     def _get_rfb_desktop_wid(self):
    579         ids = self._window_to_id.values()
    580         if len(ids)!=1:
    581             rfblog.error("RFB can only handle a single desktop window, found %i", len(self._window_to_id))
    582             return None
    583         return ids[0]
    584 
    585     def handle_rfb_connection(self, conn):
    586         model = self._get_rfb_desktop_model()
    587         if not model:
    588             conn.close()
    589             return
    590         from xpra.net.rfb import RFBProtocol
    591         def rfb_protocol_class(conn):
    592             return RFBProtocol(self, conn, self.process_rfb_packet, self.get_rfb_pixelformat, self.session_name or "Xpra Server")
    593         p = self.do_make_protocol("rfb", conn, rfb_protocol_class)
    594         p.send_protocol_handshake()
    595 
    596     def get_rfb_pixelformat(self):
    597         model = self._get_rfb_desktop_model()
    598         w, h = model.get_dimensions()
    599         return w, h, 32, 32, False, True, 255, 255, 255, 16, 8, 0
    600 
    601     def process_rfb_packet(self, proto, packet):
    602         rfblog("RFB packet: '%s'", nonl(packet))
    603         fn_name = "_process_rfb_%s" % packet[0]
    604         fn = getattr(self, fn_name, None)
    605         if not fn:
    606             rfblog.warn("Warning: no RFB handler for %s", fn_name)
    607             return
    608         self.idle_add(fn, proto, packet)
    609 
    610     def _process_rfb_invalid(self, proto, packet):
    611         self.disconnect_protocol(proto, "invalid packet: %s" % (packet[1:]))
    612 
    613     def _process_rfb_gibberish(self, proto, packet):
    614         self.disconnect_protocol(proto, "invalid packet: %s" % (packet[1:]))
    615 
    616     def _process_rfb_authenticated(self, proto, _packet):
    617         model = self._get_rfb_desktop_model()
    618         if not model:
    619             proto.close()
    620             return
    621         self.rfb_init()
    622         self.accept_protocol(proto)
    623         #use blocking sockets from now on:
    624         from xpra.net.bytestreams import set_socket_timeout
    625         set_socket_timeout(proto._conn, None)
    626         source = RFBSource(proto, self._window_to_id.keys()[0])
    627         self._server_sources[proto] = source
    628         w, h = model.get_dimensions()
    629         source.damage(self._window_to_id[model], model, 0, 0, w, h)
    630 
    631     def rfb_init(self):
    632         self.rfb_buttons = 0
    633         self.x11_keycodes_for_keysym = {}
    634         x11_keycodes = X11Keyboard.get_keycode_mappings()
    635         for keycode, keysyms in x11_keycodes.items():
    636             for keysym in keysyms:
    637                 self.x11_keycodes_for_keysym.setdefault(keysym, []).append(keycode)
    638         rfblog("x11_keycodes_for_keysym=%s", self.x11_keycodes_for_keysym)
    639 
    640     def _process_rfb_PointerEvent(self, _proto, packet):
    641         buttons, x, y = packet[1:4]
    642         wid = self._get_rfb_desktop_wid()
    643         self._move_pointer(wid, (x, y))
    644         if buttons!=self.rfb_buttons:
    645             #figure out which buttons have changed:
    646             for button in range(8):
    647                 mask = 2**button
    648                 if buttons & mask != self.rfb_buttons & mask:
    649                     pressed = bool(buttons & mask)
    650                     self.button_action(1+button, pressed, -1)
    651             self.rfb_buttons = buttons
    652 
    653     def _process_rfb_KeyEvent(self, _proto, packet):
    654         pressed, _, _, key = packet[1:5]
    655         wid = self._get_rfb_desktop_wid()
    656         keyval = 0
    657         name = RFB_KEYNAMES.get(key) or chr(key)
    658         keycode = 0
    659         keycodes = self.x11_keycodes_for_keysym.get(name, 0)
    660         rfblog("keycodes(%s)=%s", name, keycodes)
    661         if keycodes:
    662             keycode = keycodes[0]
    663             modifiers = []
    664             self._handle_key(wid, bool(pressed), name, keyval, keycode, modifiers)
    665 
    666     def _process_rfb_FramebufferUpdateRequest(self, _proto, packet):
    667         #pressed, _, _, keycode = packet[1:5]
    668         inc, x, y, w, h = packet[1:6]
    669         if not inc:
    670             model = self._get_rfb_desktop_model()
    671             self._damage(model, x, y, w, h)
    672 
    673     def _process_rfb_ClientCutText(self, _proto, packet):
    674         #l = packet[4]
    675         text = packet[5]
    676         rfblog("got rfb clipboard text: %s", nonl(text))
    677 
    678 
    679 RFB_KEYNAMES = {
    680     0xff08      : "BackSpace",
    681     0xff09      : "Tab",
    682     0xff0d      : "Return",
    683     0xff1b      : "Escape",
    684     0xff63      : "Insert",
    685     0xffff      : "Delete",
    686     0xff50      : "Home",
    687     0xff57      : "End",
    688     0xff55      : "PageUp",
    689     0xff56      : "PageDown",
    690     0xff51      : "Left",
    691     0xff52      : "Up",
    692     0xff53      : "Right",
    693     0xff54      : "Down",
    694     0xffe1      : "Shift_L",
    695     0xffe2      : "Shift_R",
    696     0xffe3      : "Control_L",
    697     0xffe4      : "Control_R",
    698     0xffe7      : "Meta_L",
    699     0xffe8      : "Meta_R",
    700     0xffe9      : "Alt_L",
    701     0xffea      : "Alt_R",
    702     }
    703 for i in range(1, 13):
    704     RFB_KEYNAMES[0xffbe+(i-1)] = "F%i" % i
    705 
    706 
    707 class RFBSource(object):
    708 
    709     def __init__(self, protocol, desktop):
    710         self.protocol = protocol
    711         self.desktop = desktop
    712         self.close_event = Event()
    713         self.log_disconnect = True
    714         self.ui_client = True
    715         self.counter = 0
    716         self.uuid = "todo: use protocol?"
    717 
    718     def is_closed(self):
    719         return self.close_event.isSet()
    720 
    721     def close(self):
    722         pass
    723 
    724     def ping(self):
    725         pass
    726 
    727     def keys_changed(self):
    728         pass
    729 
    730     def send_server_event(self, *_args):
    731         pass
    732 
    733     def send_cursor(self):
    734         pass
    735 
    736 
    737     def damage(self, _wid, window, x, y, w, h, _options=None):
    738         img = window.get_image(x, y, w, h)
    739         rfblog("damage: %s", img)
    740         fbupdate = struct.pack("!BBH", 0, 0, 1)
    741         encoding = 0    #Raw
    742         rect = struct.pack("!HHHHi", x, y, w, h, encoding)
    743         if img.get_rowstride()!=w*4:
    744             img.restride(w*4)
    745         pixels = img.get_pixels()
    746         assert len(pixels)>=4*w*h
    747         pixels = pixels[:4*w*h]
    748         if len(pixels)<=4096:
    749             self.send(fbupdate+rect+memoryview_to_bytes(pixels))
    750         else:
    751             self.send(fbupdate+rect)
    752             self.send(pixels)
    753 
    754     def send_clipboard(self, text):
    755         nocr = text.replace("\r", "")
    756         msg = struct.pack("!BBBBI", 3, 0, 0, 0, len(nocr))+nocr
    757         self.send(msg)
    758 
    759     def bell(self, *_args):
    760         msg = struct.pack("!B", 2)
    761         self.send(msg)
    762 
    763     def send(self, msg):
    764         p = self.protocol
    765         if p:
    766             p.send(msg)
    767 
    768 
    769572gobject.type_register(XpraDesktopServer)
  • xpra/x11/x11_server_core.py

     
    720720            self.do_process_mouse_common(proto, wid, pointer)
    721721            self.pointer_device.wheel_motion(button, distance/1000.0)
    722722
    723     def _move_pointer(self, wid, pos, deviceid, *args):
     723    def _move_pointer(self, wid, pos, deviceid=-1, *args):
    724724        #(this is called within an xswallow context)
    725725        screen_no = self.get_screen_number(wid)
    726726        device = self.pointer_device