xpra icon
Bug tracker and wiki

Ticket #639: udp.patch

File udp.patch, 31.1 KB (added by Antoine Martin, 2 years ago)

udp and dtls work in progress: client can send hello wrapped in a UDP frame

  • 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 UDPProtocol
     238            self._protocol = UDPProtocol(self.get_scheduler(), conn, self.process_packet, self.next_packet)
     239        else:
     240            self._protocol = Protocol(self.get_scheduler(), conn, self.process_packet, self.next_packet)
    237241        self._protocol.large_packets.append("keymap-changed")
    238242        self._protocol.large_packets.append("server-settings")
    239243        self._protocol.large_packets.append("logging")
  • xpra/net/bytestreams.py

     
    277277            i = s
    278278        log("%s.close() for socket=%s", self, i)
    279279        Connection.close(self)
    280         s.settimeout(0)
     280        try:
     281            s.settimeout(0)
     282        except:
     283            pass
    281284        #this is more proper but would break the proxy server:
    282285        #s.shutdown(socket.SHUT_RDWR)
    283286        s.close()
     
    306309        s = self._socket
    307310        if not s:
    308311            return None
    309         return {
    310                 #"class"         : str(type(s)),
    311                 "fileno"        : s.fileno(),
     312        info = {
    312313                "timeout"       : int(1000*(s.gettimeout() or 0)),
    313314                "family"        : FAMILY_STR.get(s.family, s.family),
    314315                "proto"         : s.proto,
    315316                "type"          : PROTOCOL_STR.get(s.type, s.type),
    316317                }
     318        if hasattr(s, "fileno"):
     319            info["fileno"] = s.fileno()
     320        return info
    317321
    318 
    319322try:
    320323    #this wrapper class allows us to override the normal ssl.Socket
    321324    #class so that we can fake peek() support by actually reading from the socket
  • xpra/net/protocol.py

     
    344344        self._write_queue.put(items)
    345345        self.output_packetcount += 1
    346346
     347
    347348    def start_write_thread(self):
    348349        self._write_thread = start_thread(self._write_thread_loop, "write", daemon=True)
    349350
     
    579580                    if not self._closed:
    580581                        log.error("Error on write start callback %s", start_cb, exc_info=True)
    581582            while buf and not self._closed:
    582                 written = con.write(buf)
     583                written = self.con_write(con, buf)
    583584                #example test code, for sending small chunks very slowly:
    584585                #written = con.write(buf[:1024])
    585586                #import time
     
    595596                        log.error("Error on write end callback %s", end_cb, exc_info=True)
    596597        return True
    597598
     599    def con_write(self, con, data):
     600        return con.write(data)
     601
     602
    598603    def _read_thread_loop(self):
    599604        self._io_thread_loop("read", self._read)
    600605    def _read(self):
  • 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 struct
     7
     8from xpra.log import Logger
     9log = Logger("network", "protocol")
     10
     11from xpra.os_util import Queue, memoryview_to_bytes
     12from xpra.util import envint
     13from xpra.make_thread import make_thread
     14from xpra.net.protocol import Protocol, JOIN_TYPES
     15
     16READ_BUFFER_SIZE = envint("XPRA_READ_BUFFER_SIZE", 65536)
     17
     18
     19#UUID, seqno, packetsize, dataoffset, datalen
     20_header_struct = struct.Struct('!QQLLL')
     21_header_size = _header_struct.size
     22
     23
     24class UDPListener(object):
     25
     26    def __init__(self, scheduler, sock, socktype, process_packet_cb):
     27        assert scheduler is not None
     28        assert sock is not None
     29        self.timeout_add = scheduler.timeout_add
     30        self.idle_add = scheduler.idle_add
     31        self._closed = False
     32        self._socket = sock
     33        self._socktype = socktype
     34        self._process_packet_cb =  process_packet_cb
     35        self._read_queue = Queue(20)
     36        self._read_thread = make_thread(self._read_thread_loop, "read", daemon=True)
     37        self._read_parser_thread = None         #started when needed
     38        self._read_queue_put = self.read_queue_put
     39
     40    def __repr__(self):
     41        return "UDPListener(%s)" % self._socket
     42
     43    def start(self):
     44        def start_network_read_thread():
     45            if not self._closed:
     46                self._read_thread.start()
     47        self.idle_add(start_network_read_thread)
     48
     49
     50    def _io_thread_loop(self, name, callback):
     51        try:
     52            log("io_thread_loop(%s, %s) loop starting", name, callback)
     53            while not self._closed and callback():
     54                pass
     55            log("io_thread_loop(%s, %s) loop ended, closed=%s", name, callback, self._closed)
     56        except Exception as e:
     57            #can happen during close(), in which case we just ignore:
     58            if not self._closed:
     59                log.error("Error: %s on %s failed: %s", name, self._socket, type(e), exc_info=True)
     60                self.close()
     61
     62    def _read_thread_loop(self):
     63        self._io_thread_loop("read", self._read)
     64    def _read(self):
     65        buf, bfrom = self._socket.recvfrom(READ_BUFFER_SIZE)
     66        #log("read thread: got data of size %s: %s", len(buf), repr_ellipsized(buf))
     67        #add to the read queue (or whatever takes its place - see steal_connection)
     68        self._read_queue_put((buf, bfrom))
     69        if not buf:
     70            log("read thread: eof")
     71            #give time to the parse thread to call close itself
     72            #so it has time to parse and process the last packet received
     73            self.timeout_add(1000, self.close)
     74            return False
     75        return True
     76
     77    def read_queue_put(self, data):
     78        #start the parse thread if needed:
     79        if not self._read_parser_thread and not self._closed:
     80            if data is None:
     81                log("empty marker in read queue, exiting")
     82                self.idle_add(self.close)
     83                return
     84            self._read_parser_thread = make_thread(self._read_parse_thread_loop, "parse", daemon=True)
     85            self._read_parser_thread.start()
     86        self._read_queue.put(data)
     87        #from now on, take shortcut:
     88        if self._read_queue_put==self.read_queue_put:
     89            self._read_queue_put = self._read_queue.put
     90
     91    def _read_parse_thread_loop(self):
     92        log("read_parse_thread_loop starting")
     93        try:
     94            self.do_read_parse_thread_loop()
     95        except Exception:
     96            if self._closed:
     97                return
     98            log.error("Error: network packet reading/parsing", exc_info=True)
     99
     100    def do_read_parse_thread_loop(self):
     101        while not self._closed:
     102            buf, bfrom = self._read_queue.get()
     103            if not buf:
     104                log("parse thread: empty marker, exiting")
     105                self.idle_add(self.close)
     106                return
     107            values = list(_header_struct.unpack_from(buf[:_header_size]))
     108            values.append(buf[_header_size:])
     109            values.append(bfrom)
     110            self._process_packet_cb(self, *values)
     111           
     112
     113    def close(self):
     114        log("UDPListener.close() closed=%s, socket=%s", self._closed, self._socket)
     115        if self._closed:
     116            return
     117        self._closed = True
     118        s = self._socket
     119        if s:
     120            try:
     121                log("Protocol.close() calling %s", s.close)
     122                s.close()
     123            except:
     124                log.error("error closing %s", self._socket, exc_info=True)
     125            self._socket = None
     126        self.terminate_queue_threads()
     127        self.idle_add(self.clean)
     128        log("UDPListener.close() done")
     129
     130    def clean(self):
     131        #clear all references to ensure we can get garbage collected quickly:
     132        self._read_thread = None
     133        self._read_parser_thread = None
     134
     135    def terminate_queue_threads(self):
     136        log("terminate_queue_threads()")
     137        exit_queue = Queue()
     138        for _ in range(10):     #just 2 should be enough!
     139            exit_queue.put(None)
     140        try:
     141            orq = self._read_queue
     142            self._read_queue = exit_queue
     143            #discard all elements in the old queue and push the None marker:
     144            try:
     145                while orq.qsize()>0:
     146                    orq.read(False)
     147            except:
     148                pass
     149            orq.put_nowait(None)
     150        except:
     151            pass
     152
     153class UDPProtocol(Protocol):
     154
     155    def con_write(self, con, data):
     156        log.info("UDP.con_write(%s, %s)", con, len(data))
     157        if type(data) not in JOIN_TYPES:
     158            data = memoryview_to_bytes(data)
     159        uuid = 0 #todo!
     160        seqno = self.output_raw_packetcount
     161        packetsize = len(data)
     162        header_and_data = _header_struct.pack(uuid, seqno, packetsize, 0, packetsize) + data
     163        return con.write(header_and_data)
     164
     165    def _read_thread_loop(self):
     166        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
  • 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")
     
    589591            cpaths = csv("'%s'" % x for x in (opts.ssl_cert, opts.ssl_key) if x)
    590592            raise InitException("cannot create SSL socket, check your certificate paths (%s): %s" % (cpaths, e))
    591593
     594    from xpra.server.socket_util import setup_tcp_socket, setup_udp_socket, setup_vsock_socket, setup_local_sockets
    592595    def add_mdns(socktype, host, port):
    593596        recs = mdns_recs.setdefault(socktype.lower(), [])
    594597        rec = (host, port)
     
    598601        socket = setup_tcp_socket(host, iport, socktype)
    599602        sockets.append(socket)
    600603        add_mdns(socktype, host, iport)
    601 
     604    def add_udp_socket(socktype, host, iport):
     605        socket = setup_udp_socket(host, iport, socktype)
     606        if socktype=="dtls":
     607            from dtls import do_patch
     608            do_patch()
     609        sockets.append(socket)
     610        add_mdns(socktype, host, iport)
    602611    # Initialize the TCP sockets before the display,
    603612    # That way, errors won't make us kill the Xvfb
    604613    # (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
    606614    netlog("setting up SSL sockets: %s", bind_ssl)
    607615    for host, iport in bind_ssl:
    608616        add_tcp_socket("SSL", host, iport)
     
    615623        add_tcp_socket("tcp", host, iport)
    616624        if tcp_ssl:
    617625            add_mdns("ssl", host, iport)
     626    netlog("setting up UDP sockets: %s", bind_udp)
     627    for host, iport in bind_udp:
     628        add_udp_socket("udp", host, iport)
     629    netlog("setting up UDP+DTLS sockets: %s", bind_dtls)
     630    for host, iport in bind_dtls:
     631        add_udp_socket("dtls", host, iport)
    618632    netlog("setting up http / ws (websockets): %s", bind_ws)
    619633    for host, iport in bind_ws:
    620634        add_tcp_socket("ws", host, iport)
  • xpra/server/server_core.py

     
    146146        self.ws_auth_class = None
    147147        self.wss_auth_class = None
    148148        self.ssl_auth_class = None
     149        self.udp_auth_class = None
     150        self.dtls_auth_class = None
    149151        self.rfb_auth_class = None
    150152        self.vsock_auth_class = None
    151153        self._when_ready = []
     
    157159        #networking bits:
    158160        self._socket_info = []
    159161        self._potential_protocols = []
     162        self._udp_listeners = []
     163        self._udp_protocols = {}
    160164        self._tcp_proxy_clients = []
    161165        self._tcp_proxy = ""
    162166        self._ssl_wrap_socket = None
     
    538542        self.do_cleanup()
    539543        self.cleanup_protocols(protocols, reason, True)
    540544        self._potential_protocols = []
     545        self.cleanup_udp_listeners()
    541546
    542547    def do_cleanup(self):
    543548        #allow just a bit of time for the protocol packet flush
     
    544549        sleep(0.1)
    545550
    546551
     552    def cleanup_udp_listeners(self):
     553        for udpl in self._udp_listeners:
     554            udpl.close()
     555        self._udp_listeners = []
     556
    547557    def cleanup_all_protocols(self, reason):
    548558        protocols = self.get_all_protocols()
    549559        self.cleanup_protocols(protocols, reason)
     
    569579            #named pipe listener uses a thread:
    570580            sock.new_connection_cb = self._new_connection
    571581            sock.start()
     582        elif socktype in ("udp", "dtls"):
     583            #socket_info = self.socket_info.get(sock)
     584            from xpra.net.udp_protocol import UDPListener
     585            udpl = UDPListener(self, sock, socktype, self.process_udp_packet)
     586            udpl.start()
     587            self._udp_listeners.append(udpl)
    572588        else:
    573589            from xpra.gtk_common.gobject_compat import import_glib
    574590            glib = import_glib()
     
    729745            self.handle_rfb_connection(conn)
    730746            return
    731747
    732         elif socktype=="tcp" and peek_data and (self._html or self._tcp_proxy or self._ssl_wrap_socket):
     748        elif peek_data and (
     749            (socktype=="tcp" and  (self._html or self._tcp_proxy or self._ssl_wrap_socket)) or
     750            (socktype=="udp" and self._ssl_wrap_socket)
     751            ):
    733752            #see if the packet data is actually xpra or something else
    734753            #that we need to handle via a tcp proxy, ssl wrapper or the websockify adapter:
    735754            try:
     
    763782        netlog("make_protocol(%s, %s)", socktype, conn)
    764783        socktype = socktype.lower()
    765784        protocol = protocol_class(conn)
     785        protocol.socket_type = socktype
    766786        self._potential_protocols.append(protocol)
    767787        protocol.challenge_sent = False
    768788        protocol.authenticator = None
    769789        protocol.encryption = None
    770790        protocol.keyfile = None
     791        protocol.auth_class = {
     792            "tcp"           : self.tcp_auth_class,
     793            "ssl"           : self.ssl_auth_class,
     794            "udp"           : self.udp_auth_class,
     795            "dtls"          : self.dtls_auth_class,
     796            "ws"            : self.ws_auth_class,
     797            "wss"           : self.wss_auth_class,
     798            "rfb"           : self.rfb_auth_class,
     799            "vsock"         : self.vsock_auth_class,
     800            "unix-domain"   : self.auth_class,
     801            "named-pipe"    : self.auth_class,
     802            }[socktype]
    771803        if socktype=="tcp":
    772             protocol.auth_class = self.tcp_auth_class
     804            #special case for legacy encryption code:
    773805            protocol.encryption = self.tcp_encryption
    774806            protocol.keyfile = self.tcp_encryption_keyfile
    775807            if protocol.encryption and ENCRYPT_FIRST_PACKET:
     
    776808                authlog("encryption=%s, keyfile=%s", protocol.encryption, protocol.keyfile)
    777809                password = self.get_encryption_key(None, protocol.keyfile)
    778810                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
    792811        protocol.invalid_header = self.invalid_header
    793812        authlog("socktype=%s, auth class=%s, encryption=%s, keyfile=%s", socktype, protocol.auth_class, protocol.encryption, protocol.keyfile)
    794813        protocol.start()
     
    812831            return True, conn, peek_data
    813832        frominfo = pretty_socket(conn.remote)
    814833        if self._ssl_wrap_socket and peek_data[0] in (chr(0x16), 0x16):
    815             socktype = "SSL"
     834            socktype = {
     835                "udp"   : "dtls",
     836                "tcp"   : "SSL",
     837                }[socktype]
    816838            sock, sockname, address, target = conn._socket, conn.local, conn.remote, conn.target
    817839            sock = self._ssl_wrap_socket(sock)
    818840            if sock is None:
     
    14441466        for socktype, auth_class in {
    14451467                                     "tcp"          : self.tcp_auth_class,
    14461468                                     "ssl"          : self.ssl_auth_class,
     1469                                     "ws"           : self.ws_auth_class,
     1470                                     "wss"          : self.wss_auth_class,
     1471                                     "udp"          : self.udp_auth_class,
     1472                                     "dtls"         : self.dtls_auth_class,
     1473                                     "rfb"          : self.rfb_auth_class,
    14471474                                     "unix-domain"  : self.auth_class,
    14481475                                     "vsock"        : self.vsock_auth_class,
    14491476                                     }.items():
     
    14741501    def handle_rfb_connection(self, conn):
    14751502        log.error("Error: RFB protocol is not supported by this server")
    14761503        conn.close()
     1504
     1505    def process_udp_packet(self, udp_listener, uuid, seqno, packetsize, dataoffset, datalen, data, bfrom):
     1506        log.info("process_udp_packet%s", (udp_listener, uuid, seqno, packetsize, dataoffset, datalen, len(data), bfrom))
     1507        protocol = self._udp_protocols.get(uuid)
     1508        if not protocol:
     1509            def udp_protocol_class(conn):
     1510                from xpra.net.udp_protocol import UDPProtocol
     1511                protocol = UDPProtocol(self, conn, self.process_packet)
     1512                protocol.large_packets.append("info-response")
     1513                protocol.receive_aliases.update(self._aliases)
     1514                return protocol
     1515            socktype = udp_listener._socktype
     1516            #TODO: IPv6
     1517            if False:
     1518                assert socket.has_ipv6, "no IPv6 support"
     1519                family = socket.AF_INET6
     1520            else:
     1521                family = socket.AF_INET
     1522            host, port = bfrom
     1523            try:
     1524                addrinfo = socket.getaddrinfo(host, port, family)
     1525            except Exception as e:
     1526                raise InitException("cannot get %s address of %s: %s" % ({
     1527                    socket.AF_INET6 : "IPv6",
     1528                    socket.AF_INET  : "IPv4",
     1529                    }.get(family, family), (host, port), e))
     1530            sockaddr = addrinfo[0][-1]
     1531            sock = socket.socket(family, socket.SOCK_DGRAM)
     1532            conn = _socket_connect(sock, sockaddr, "client", socktype)
     1533            if socktype=="dtls":
     1534                from dtls import do_patch   #@UnresolvedImport
     1535                do_patch()
     1536                wrap_socket = self._ssl_wrap_socket(server_side=False)
     1537                sock = wrap_socket(sock)
     1538                assert sock, "failed to wrap socket %s" % sock
     1539                conn._socket = sock
     1540            conn.timeout = SOCKET_TIMEOUT
     1541            protocol = self.do_make_protocol(socktype, conn, udp_protocol_class)
     1542            protocol = self.make_protocol(conn.socktype, conn)
     1543            self._udp_protocols[uuid] = protocol
     1544        assert packetsize==datalen
     1545        assert len(data)==datalen
     1546        protocol._read_queue_put(data)
  • xpra/server/socket_util.py

     
    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