xpra icon
Bug tracker and wiki

Ticket #1504: ssl-late-wrap-v2.patch

File ssl-late-wrap-v2.patch, 39.3 KB (added by Antoine Martin, 2 years ago)

better patch - works in almost all cases (just not wss connect to upgradable tcp)

  • xpra/net/bytestreams.py

     
    2727#raises an IOError but we should continue if the error code is EINTR
    2828#this wrapper takes care of it.
    2929#EWOULDBLOCK can also be hit with the proxy server when we handover the socket
    30 CONTINUE = {
     30CONTINUE_ERRNO = {
    3131            errno.EINTR         : "EINTR",
    3232            errno.EWOULDBLOCK   : "EWOULDBLOCK"
    3333            }
     
    4444OS_WRITE = os.write
    4545TTY_READ = os.read
    4646TTY_WRITE = os.write
     47if WIN32 and PYTHON2:
     48    #win32 has problems writing more than 32767 characters to stdout!
     49    #see: http://bugs.python.org/issue11395
     50    #(this is fixed in python 3.2 and we don't care about 3.0 or 3.1)
     51    def win32ttywrite(fd, buf):
     52        #this awful limitation only applies to tty devices:
     53        if len(buf)>32767:
     54            buf = buf[:32767]
     55        return os.write(fd, buf)
     56    TTY_WRITE = win32ttywrite
    4757
    4858
    4959PROTOCOL_STR = {}
     
    6070        pass
    6171
    6272
    63 if WIN32:
    64     #on win32, we have to deal with a few more odd error codes:
    65     CONTINUE[errno.WSAEWOULDBLOCK] = "WSAEWOULDBLOCK"       #@UndefinedVariable
    6673
    67     #some of these may be redundant or impossible to hit? (does not hurt I think)
    68     for x in ("WSAENETDOWN", "WSAENETUNREACH", "WSAECONNABORTED", "WSAECONNRESET",
    69               "WSAENOTCONN", "WSAESHUTDOWN", "WSAETIMEDOUT", "WSAETIMEDOUT",
    70               "WSAEHOSTUNREACH", "WSAEDISCON"):
    71         ABORT[getattr(errno, x)] = x
    72     #duplicated from winerror module:
    73     ERROR_BROKEN_PIPE = 109
    74     ERROR_PIPE_NOT_CONNECTED = 233
    75     ABORT[ERROR_BROKEN_PIPE] = "BROKENPIPE"
    76     ABORT[ERROR_PIPE_NOT_CONNECTED] = "PIPE_NOT_CONNECTED"
    77     if PYTHON2:
    78         #win32 has problems writing more than 32767 characters to stdout!
    79         #see: http://bugs.python.org/issue11395
    80         #(this is fixed in python 3.2 and we don't care about 3.0 or 3.1)
    81         def win32ttywrite(fd, buf):
    82             #this awful limitation only applies to tty devices:
    83             if len(buf)>32767:
    84                 buf = buf[:32767]
    85             return os.write(fd, buf)
    86         TTY_WRITE = win32ttywrite
    87 
    8874def set_continue_wait(v):
    8975    global continue_wait
    9076    continue_wait = v
     
    9581                       socket.timeout   : "socket.timeout",
    9682                       }
    9783
    98 def init_ssl():
    99     import ssl
    100     assert ssl
    101     global CONTINUE_EXCEPTIONS
    102     CONTINUE_EXCEPTIONS[ssl.SSLError] = "SSLError"
    103     CONTINUE_EXCEPTIONS[ssl.SSLWantReadError] = "SSLWantReadError"
    104     CONTINUE_EXCEPTIONS[ssl.SSLWantWriteError] = "SSLWantWriteError"
    105     return ssl
    10684
    107 
    10885def can_retry(e):
    10986    continue_exception = CONTINUE_EXCEPTIONS.get(type(e))
    11087    if continue_exception:
    11188        return continue_exception
    11289    if isinstance(e, (IOError, OSError)):
    113         global CONTINUE
     90        global CONTINUE_ERRNO
    11491        code = e.args[0]
    115         can_continue = CONTINUE.get(code)
     92        can_continue = CONTINUE_ERRNO.get(code)
    11693        if can_continue:
    11794            return can_continue
    11895
    11996        abort = ABORT.get(code, code)
    12097        if abort is not None:
    121             log("untilConcludes: %s, args=%s, code=%s, abort=%s", type(e), e.args, code, abort)
     98            log("can_retry: %s, args=%s, code=%s, abort=%s", type(e), e.args, code, abort)
    12299            raise ConnectionClosedException(e)
    123100    return False
    124101
    125 def untilConcludes(is_active_cb, f, *a, **kw):
     102def untilConcludes(is_active_cb, can_retry, f, *a, **kw):
    126103    global continue_wait
    127104    wait = 0
    128105    while is_active_cb():
     
    130107            return f(*a, **kw)
    131108        except Exception as e:
    132109            retry = can_retry(e)
    133             log("untilConcludes(%s, %s, %s, %s) %s, retry=%s", is_active_cb, f, a, kw, e, retry)
     110            log("untilConcludes(%s, %s, %s, %s, %s) %s, retry=%s", is_active_cb, can_retry, f, a, kw, e, retry)
    134111            if retry:
    135112                if wait>0:
    136113                    time.sleep(wait/1000.0)     #wait is in milliseconds, sleep takes seconds
     
    173150    def close(self):
    174151        self.set_active(False)
    175152
     153    def can_retry(self, e):
     154        return can_retry(e)
     155
    176156    def untilConcludes(self, *args):
    177         return untilConcludes(self.is_active, *args)
     157        return untilConcludes(self.is_active, self.can_retry, *args)
    178158
    179159    def peek(self, n):
    180160        #not implemented
     
    345325                }
    346326
    347327
     328class SSLSocketConnection(SocketConnection):
    348329
     330    def can_retry(self, e):
     331        if getattr(e, "library", None)=="SSL":
     332            reason = getattr(e, "reason", None)
     333            if reason in ("WRONG_VERSION_NUMBER", "UNEXPECTED_RECORD"):
     334                return False
     335            #log.info("can_retry(%s) %s", e, type(e))
     336            #for x in ('args', 'errno', 'filename', 'library', 'message', 'reason', 'strerror'):
     337            #    log.info("%s=%s", x, getattr(e, x, None))
     338        return SocketConnection.can_retry(self, e)
     339
     340    def get_info(self):
     341        i = SocketConnection.get_info(self)
     342        i["ssl"] = True
     343        for k,fn in {
     344                     "compression"      : "compression",
     345                     "alpn-protocol"    : "selected_alpn_protocol",
     346                     "npn-protocol"     : "selected_npn_protocol",
     347                     "version"          : "version",
     348                     }.items():
     349            sfn = getattr(self._socket, fn, None)
     350            if sfn:
     351                v = sfn()
     352                if v is not None:
     353                    i[k] = v
     354        cipher_fn = getattr(self._socket, "cipher", None)
     355        if cipher_fn:
     356            cipher = cipher_fn()
     357            if cipher:
     358                i["cipher"] = {
     359                               "name"       : cipher[0],
     360                               "protocol"   : cipher[1],
     361                               "bits"       : cipher[2],
     362                               }
     363        return i
     364
     365
    349366def set_socket_timeout(conn, timeout=None):
    350367    #FIXME: this is ugly, but less intrusive than the alternative?
    351368    log("set_socket_timeout(%s, %s)", conn, timeout)
     
    353370        conn._socket.settimeout(timeout)
    354371
    355372
    356 def inject_ssl_socket_info(conn):
    357     """
    358         If the socket is an SSLSocket,
    359         we patch the Connection's get_info method
    360         to return additional ssl data.
    361         This method does not load the 'ssl' module.
    362     """
    363     sock = conn._socket
    364     ssl = sys.modules.get("ssl")
    365     log("ssl=%s, socket class=%s", ssl, type(sock))
    366     if ssl and isinstance(sock, ssl.SSLSocket):
    367         #inject extra ssl info into the socket class:
    368         def get_ssl_socket_info(sock):
    369             d = sock.do_get_socket_info()
    370             d["ssl"] = True
    371             s = sock._socket
    372             if not s:
    373                 return d
    374             for k,fn in {
    375                          "compression"      : "compression",
    376                          "alpn-protocol"    : "selected_alpn_protocol",
    377                          "npn-protocol"     : "selected_npn_protocol",
    378                          "version"          : "version",
    379                          }.items():
    380                 sfn = getattr(s, fn, None)
    381                 if sfn:
    382                     v = sfn()
    383                     if v is not None:
    384                         d[k] = v
    385             cipher_fn = getattr(s, "cipher", None)
    386             if cipher_fn:
    387                 cipher = cipher_fn()
    388                 if cipher:
    389                     d["cipher"] = {
    390                                    "name"       : cipher[0],
    391                                    "protocol"   : cipher[1],
    392                                    "bits"       : cipher[2],
    393                                    }
    394             return d
    395         conn.get_socket_info = types.MethodType(get_ssl_socket_info, conn)
    396 
    397 def log_new_connection(conn):
     373def log_new_connection(conn, socket_info=""):
    398374    """ logs the new connection message """
    399375    sock = conn._socket
    400376    address = conn.remote
     
    404380    except:
    405381        peername = str(address)
    406382    sockname = sock.getsockname()
    407     log("log_new_connection(%s) sock=%s, sockname=%s, address=%s, peername=%s", conn, sock, sockname, address, peername)
     383    log("log_new_connection(%s) type=%s, sock=%s, sockname=%s, address=%s, peername=%s", conn, type(conn), sock, sockname, address, peername)
    408384    if peername:
    409385        frominfo = pretty_socket(peername)
    410386        info_msg = "New %s connection received from %s" % (socktype, frominfo)
     387        if socket_info:
     388            info_msg += " on %s" % (socket_info,)
    411389    elif socktype=="unix-domain":
    412390        frominfo = sockname
    413391        info_msg = "New %s connection received on %s" % (socktype, frominfo)
    414392    else:
    415         frominfo = ""
    416393        info_msg = "New %s connection received"
     394        if socket_info:
     395            info_msg += " on %s" % (socket_info,)
    417396    log.info(info_msg)
  • xpra/platform/win32/namedpipes/common.py

     
    2121INFINITE = 65535
    2222INVALID_HANDLE_VALUE = -1
    2323
     24ERROR_BROKEN_PIPE = 109
    2425ERROR_PIPE_NOT_CONNECTED = 233
    2526ERROR_MORE_DATA = 234
    2627ERROR_BROKEN_PIPE = 109
  • xpra/platform/win32/namedpipes/connection.py

     
    66
    77#@PydevCodeAnalysisIgnore
    88
     9import errno
    910from ctypes import WinDLL, addressof, byref, c_ulong, c_char_p, c_char, c_void_p, cast, string_at
    1011
    1112from xpra.net.bytestreams import Connection
     13from xpra.net.common import ConnectionClosedException
    1214from xpra.platform.win32.namedpipes.common import OVERLAPPED, WAIT_STR, INVALID_HANDLE_VALUE, ERROR_PIPE_BUSY, ERROR_PIPE_NOT_CONNECTED, INFINITE, ERROR_STR, ERROR_BROKEN_PIPE, ERROR_IO_PENDING
    1315from xpra.platform.win32.constants import FILE_FLAG_OVERLAPPED, GENERIC_READ, GENERIC_WRITE, OPEN_EXISTING, PIPE_READMODE_BYTE
    1416
     
    3133
    3234BUFSIZE = 65536
    3335
     36CONNECTION_CLOSED_ERRORS = {
     37    ERROR_BROKEN_PIPE           : "BROKENPIPE",
     38    ERROR_PIPE_NOT_CONNECTED    : "PIPE_NOT_CONNECTED",
     39    }
     40#some of these may be redundant or impossible to hit? (does not hurt I think)
     41for x in ("WSAENETDOWN", "WSAENETUNREACH", "WSAECONNABORTED", "WSAECONNRESET",
     42          "WSAENOTCONN", "WSAESHUTDOWN", "WSAETIMEDOUT", "WSAETIMEDOUT",
     43          "WSAEHOSTUNREACH", "WSAEDISCON"):
     44    CONNECTION_CLOSED_ERRORS[getattr(errno, x)] = x
    3445
     46
    3547class NamedPipeConnection(Connection):
    3648    def __init__(self, name, pipe_handle):
    3749        log("NamedPipeConnection(%s, %i)", name, pipe_handle)
     
    5264        self.write_overlapped.InternalHigh = None
    5365        self.write_overlapped.union.Pointer = None
    5466
     67    def can_retry(self, e):
     68        code = e.args[0]
     69        if code==errno.WSAEWOULDBLOCK:      #@UndefinedVariable
     70            return "WSAEWOULDBLOCK"
     71        #convert those to a connection closed:
     72        closed = CONNECTION_CLOSED_ERRORS.get(code)
     73        if closed:
     74            raise ConnectionClosedException(e)
     75        return False
     76       
     77
    5578    def untilConcludes(self, fn, *args, **kwargs):
    5679        try:
    57             return Connection.untilConcludes(self, fn, *args, **kwargs)
     80            return Connection.untilConcludes(self, self.can_retry, fn, *args, **kwargs)
    5881        except Exception as e:
    5982            code = GetLastError()
    6083            log("untilConcludes(%s, ) exception: %s, error code=%s", fn, e, code, exc_info=True)
  • xpra/scripts/config.py

     
    437437                    "auth"              : str,
    438438                    "vsock-auth"        : str,
    439439                    "tcp-auth"          : str,
     440                    "ws-auth"           : str,
     441                    "wss-auth"          : str,
    440442                    "ssl-auth"          : str,
    441443                    "wm-name"           : str,
    442444                    "session-name"      : str,
     
    590592                    "bind"              : list,
    591593                    "bind-vsock"        : list,
    592594                    "bind-tcp"          : list,
     595                    "bind-ws"           : list,
     596                    "bind-wss"          : list,
    593597                    "bind-ssl"          : list,
    594598                    "start-env"         : list,
    595599                    "env"               : list,
     
    608612    "start-after-connect", "start-child-after-connect",
    609613    "start-on-connect", "start-child-on-connect",
    610614    ]
    611 BIND_OPTIONS = ["bind", "bind-tcp", "bind-ssl", "bind-vsock"]
     615BIND_OPTIONS = ["bind", "bind-tcp", "bind-ssl", "bind-ws", "bind-wss", "bind-vsock"]
    612616
    613617#keep track of the options added since v1,
    614618#so we can generate command lines that work with older supported versions:
     
    672676    "av-sync", "global-menus",
    673677    "printing", "file-transfer", "open-command", "open-files", "start-new-commands",
    674678    "mmap", "mmap-group", "mdns",
    675     "auth", "vsock-auth", "tcp-auth", "ssl-auth",
    676     "bind", "bind-vsock", "bind-tcp", "bind-ssl",
     679    "auth", "vsock-auth", "tcp-auth", "ws-auth", "wss-auth", "ssl-auth",
     680    "bind", "bind-vsock", "bind-tcp", "bind-ssl", "bind-ws", "bind-wss",
    677681    "start", "start-child",
    678682    "start-after-connect", "start-child-after-connect",
    679683    "start-on-connect", "start-child-on-connect",
     
    792796                    "auth"              : "",
    793797                    "vsock-auth"        : "",
    794798                    "tcp-auth"          : "",
     799                    "ws-auth"           : "",
     800                    "wss-auth"          : "",
    795801                    "ssl-auth"          : "",
    796802                    "wm-name"           : DEFAULT_NET_WM_NAME,
    797803                    "session-name"      : "",
     
    935941                    "bind"              : bind_dirs,
    936942                    "bind-vsock"        : [],
    937943                    "bind-tcp"          : [],
     944                    "bind-ws"           : [],
     945                    "bind-wss"          : [],
    938946                    "bind-ssl"          : [],
    939947                    "start"             : [],
    940948                    "start-child"       : [],
  • xpra/scripts/main.py

     
    527527                          metavar="[HOST]:[PORT]",
    528528                          help="Listen for connections over TCP (use --tcp-auth to secure it)."
    529529                            + " You may specify this option multiple times with different host and port combinations")
     530        group.add_option("--bind-ws", action="append",
     531                          dest="bind_ws", default=list(defaults.bind_ws or []),
     532                          metavar="[HOST]:[PORT]",
     533                          help="Listen for connections over Websocket (use --ws-auth to secure it)."
     534                            + " You may specify this option multiple times with different host and port combinations")
     535        group.add_option("--bind-wss", action="append",
     536                          dest="bind_wss", default=list(defaults.bind_wss or []),
     537                          metavar="[HOST]:[PORT]",
     538                          help="Listen for connections over HTTPS / wss (secure Websocket). Use --wss-auth to secure it."
     539                            + " You may specify this option multiple times with different host and port combinations")
    530540        group.add_option("--bind-ssl", action="append",
    531541                          dest="bind_ssl", default=list(defaults.bind_ssl or []),
    532542                          metavar="[HOST]:PORT",
    533                           help="Listen for connections over SSL (use --ssl-auth to secure it)."
     543                          help="Listen for connections over SSL. Use --ssl-auth to secure it."
    534544                            + " You may specify this option multiple times with different host and port combinations")
    535545    else:
    536         ignore({"bind"      : defaults.bind})
    537         ignore({"bind-tcp"  : defaults.bind_tcp})
    538         ignore({"bind-ssl"  : defaults.bind_ssl})
     546        ignore({
     547            "bind"      : defaults.bind,
     548            "bind-tcp"  : defaults.bind_tcp,
     549            "bind-ws"   : defaults.bind_ws,
     550            "bind-wss"  : defaults.bind_wss,
     551            "bind-ssl"  : defaults.bind_ssl,
     552            })
    539553    try:
    540554        from xpra.net import vsock
    541555    except:
     
    947961    group.add_option("--tcp-auth", action="store",
    948962                      dest="tcp_auth", default=defaults.tcp_auth,
    949963                      help="The authentication module to use for TCP sockets (default: '%default')")
     964    group.add_option("--ws-auth", action="store",
     965                      dest="ws_auth", default=defaults.ws_auth,
     966                      help="The authentication module to use for Websockets (default: '%default')")
     967    group.add_option("--wss-auth", action="store",
     968                      dest="wss_auth", default=defaults.wss_auth,
     969                      help="The authentication module to use for Secure Websockets (default: '%default')")
    950970    group.add_option("--ssl-auth", action="store",
    951971                      dest="ssl_auth", default=defaults.ssl_auth,
    952972                      help="The authentication module to use for SSL sockets (default: '%default')")
     
    22512271                if SSLEOFError and isinstance(e, SSLEOFError):
    22522272                    return None
    22532273                raise InitExit(EXIT_SSL_FAILURE, "SSL handshake failed: %s" % e)
    2254         #ensure we handle ssl exceptions as we should from now on:
    2255         from xpra.net.bytestreams import init_ssl
    2256         init_ssl()
    22572274        return ssl_sock
    22582275    return do_wrap_socket
    22592276
  • xpra/scripts/server.py

     
    361361    from xpra.server.socket_util import parse_bind_tcp, parse_bind_vsock
    362362    bind_tcp = parse_bind_tcp(opts.bind_tcp)
    363363    bind_ssl = parse_bind_tcp(opts.bind_ssl)
     364    bind_ws = parse_bind_tcp(opts.bind_ws)
     365    bind_wss = parse_bind_tcp(opts.bind_wss)
    364366    bind_vsock = parse_bind_vsock(opts.bind_vsock)
    365367
    366368    assert mode in ("start", "start-desktop", "upgrade", "shadow", "proxy")
     
    534536    log = Logger("server")
    535537    netlog = Logger("network")
    536538
    537     mdns_recs = []
     539    mdns_recs = {}
    538540    sockets = []
    539541
    540542    #SSL sockets:
     
    541543    wrap_socket_fn = None
    542544    need_ssl = False
    543545    ssl_opt = opts.ssl.lower()
    544     if ssl_opt in TRUE_OPTIONS or bind_ssl:
     546    if ssl_opt in TRUE_OPTIONS or bind_ssl or bind_wss:
    545547        need_ssl = True
    546548    if opts.bind_tcp:
    547549        if ssl_opt=="auto" and opts.ssl_cert:
     
    560562            cpaths = csv("'%s'" % x for x in (opts.ssl_cert, opts.ssl_key) if x)
    561563            raise InitException("cannot create SSL socket, check your certificate paths (%s): %s" % (cpaths, e))
    562564
    563     from xpra.server.socket_util import setup_tcp_socket, setup_vsock_socket, setup_local_sockets
    564     netlog("setting up SSL sockets: %s", bind_ssl)
    565     for host, iport in bind_ssl:
    566         _, tcp_socket, host_port = setup_tcp_socket(host, iport, "SSL")
    567         socket = ("SSL", wrap_socket_fn(tcp_socket), host_port)
     565    def add_mdns(socktype, host, port):
     566        recs = mdns_recs.setdefault(socktype, [])
     567        rec = (host, port)
     568        if rec not in recs:
     569            recs.append(rec)
     570    def add_tcp_socket(socktype, host, iport):
     571        socket = setup_tcp_socket(host, iport, socktype)
    568572        sockets.append(socket)
    569         rec = "ssl", [(host, iport)]
    570         netlog("%s : %s", rec, socket)
    571         mdns_recs.append(rec)
     573        add_mdns(socktype, host, iport)
    572574
    573575    # Initialize the TCP sockets before the display,
    574576    # That way, errors won't make us kill the Xvfb
    575577    # (which may not be ours to kill at that point)
     578    from xpra.server.socket_util import setup_tcp_socket, setup_vsock_socket, setup_local_sockets
     579    netlog("setting up SSL sockets: %s", bind_ssl)
     580    for host, iport in bind_ssl:
     581        add_tcp_socket("SSL", host, iport)
     582    netlog("setting up https / wss (secure websockets): %s", bind_wss)
     583    for host, iport in bind_wss:
     584        add_tcp_socket("wss", host, iport)
    576585    tcp_ssl = ssl_opt in TRUE_OPTIONS or (ssl_opt=="auto" and opts.ssl_cert)
    577     def add_tcp_mdns_rec(host, iport):
    578         rec = "tcp", [(host, iport)]
    579         netlog("%s : %s", rec, socket)
    580         mdns_recs.append(rec)
    581         if tcp_ssl:
    582             #SSL is also available on this TCP socket:
    583             rec = "ssl", [(host, iport)]
    584             netlog("%s : %s", rec, socket)
    585             mdns_recs.append(rec)
    586586    netlog("setting up TCP sockets: %s", bind_tcp)
    587587    for host, iport in bind_tcp:
    588         socket = setup_tcp_socket(host, iport)
    589         sockets.append(socket)
    590         add_tcp_mdns_rec(host, iport)
    591 
    592     # VSOCK:
     588        add_tcp_socket("tcp", host, iport)
     589        if tcp_ssl:
     590            add_mdns("ssl", host, iport)
     591    netlog("setting up http / ws (websockets): %s", bind_ws)
     592    for host, iport in bind_ws:
     593        add_tcp_socket("ws", host, iport)
     594        if tcp_ssl:
     595            add_mdns("wss", host, iport)
    593596    netlog("setting up vsock sockets: %s", bind_vsock)
    594597    for cid, iport in bind_vsock:
    595598        socket = setup_vsock_socket(cid, iport)
    596599        sockets.append(socket)
    597         rec = "vsock", [("", iport)]
    598         netlog("%s : %s", rec, socket)
    599         mdns_recs.append(rec)
     600        add_mdns("vsock", host, iport)
    600601
    601602    # systemd socket activation:
    602603    try:
     
    611612            netlog("%s : %s", (stype, [addr]), socket)
    612613            if stype=="tcp":
    613614                host, iport = addr
    614                 add_tcp_mdns_rec(host, iport)
     615                add_mdns("tcp", host, iport)
    615616
    616617    sanitize_env()
    617618    if POSIX:
     
    740741        socktype, socket, sockpath = rec
    741742        #ie: ("unix-domain", sock, sockpath), cleanup_socket
    742743        sockets.append(rec)
    743         netlog("%s : %s", (socktype, [sockpath]), socket)
     744        netlog("%s %s : %s", socktype, sockpath, socket)
    744745        add_cleanup(cleanup_socket)
    745746        if opts.mdns:
    746747            ssh_port = get_ssh_port()
    747             rec = "ssh", [("", ssh_port)]
    748             netlog("%s : %s", rec, socket)
    749             if ssh_port and rec not in mdns_recs:
    750                 mdns_recs.append(rec)
     748            netlog("ssh %s:%s : %s", "", ssh_port, socket)
     749            if ssh_port:
     750                add_mdns("ssh", "", ssh_port)
    751751
    752752    kill_dbus = None
    753753    if shadowing:
     
    851851                     }
    852852        if opts.session_name:
    853853            mdns_info["session"] = opts.session_name
    854         for mode, listen_on in mdns_recs:
     854        for mode, listen_on in mdns_recs.items():
    855855            mdns_publish(display_name, mode, listen_on, mdns_info)
    856856
    857857    try:
  • xpra/server/server_core.py

     
    2929from xpra.scripts.main import _socket_connect, full_version_str
    3030from xpra.scripts.server import deadly_signal
    3131from xpra.scripts.config import InitException, parse_bool, python_platform
    32 from xpra.net.bytestreams import SocketConnection, log_new_connection, inject_ssl_socket_info, pretty_socket, SOCKET_TIMEOUT
     32from xpra.net.bytestreams import SocketConnection, SSLSocketConnection, log_new_connection, pretty_socket, SOCKET_TIMEOUT
    3333from xpra.net.net_util import get_network_caps, get_info as get_net_info
    3434from xpra.platform import set_name
    3535from xpra.os_util import load_binary_file, get_machine_id, get_user_uuid, platform_name, bytestostr, get_hex_uuid, monotonic_time, get_peercred, SIGNAMES, WIN32, OSX, POSIX
     
    143143        self.start_time = monotonic_time()
    144144        self.auth_class = None
    145145        self.tcp_auth_class = None
     146        self.ws_auth_class = None
     147        self.wss_auth_class = None
    146148        self.ssl_auth_class = None
    147149        self.vsock_auth_class = None
    148150        self._when_ready = []
     
    164166        self._aliases = {}
    165167        self._reverse_aliases = {}
    166168        self.socket_types = {}
     169        self.socket_info = {}
    167170        self._max_connections = MAX_CONCURRENT_CONNECTIONS
    168171        self._socket_timeout = SERVER_SOCKET_TIMEOUT
    169172        self._ws_timeout = 5
     
    284287    def init_auth(self, opts):
    285288        self.auth_class = self.get_auth_module("unix-domain", opts.auth, opts)
    286289        self.tcp_auth_class = self.get_auth_module("tcp", opts.tcp_auth or opts.auth, opts)
     290        self.ws_auth_class = self.get_auth_module("ws", opts.ws_auth, opts)
     291        self.wss_auth_class = self.get_auth_module("wss", opts.wss_auth, opts)
    287292        self.ssl_auth_class = self.get_auth_module("ssl", opts.ssl_auth or opts.tcp_auth or opts.auth, opts)
    288293        self.vsock_auth_class = self.get_auth_module("vsock", opts.vsock_auth, opts)
    289         authlog("init_auth(..) auth class=%s, tcp auth class=%s, ssl auth class=%s, vsock auth class=%s", self.auth_class, self.tcp_auth_class, self.ssl_auth_class, self.vsock_auth_class)
     294        authlog("init_auth(..) auth=%s, tcp auth=%s, ws auth=%s, wss auth=%s, ssl auth=%s, vsock auth=%s",
     295                self.auth_class, self.tcp_auth_class, self.ws_auth_class, self.wss_auth_class, self.ssl_auth_class, self.vsock_auth_class)
    290296
    291297    def get_auth_module(self, socket_type, auth_str, opts):
    292298        authlog("get_auth_module(%s, %s, {..})", socket_type, auth_str)
     
    356362        ### All right, we're ready to accept customers:
    357363        for socktype, sock, info in sockets:
    358364            netlog("init_sockets(%s) will add %s socket %s (%s)", sockets, socktype, sock, info)
     365            self.socket_info[sock] = info
    359366            self.idle_add(self.add_listen_socket, socktype, sock)
    360367            if socktype=="unix-domain" and info:
    361368                try:
     
    577584            netlog.warn("ignoring new connection during shutdown")
    578585            return False
    579586        socktype = self.socket_types.get(listener)
     587        socket_info = self.socket_info.get(listener)
    580588        assert socktype, "cannot find socket type for %s" % listener
    581589        #TODO: just like add_listen_socket above, this needs refactoring
    582590        if socktype=="named-pipe":
     
    584592            from xpra.platform.win32.namedpipes.connection import NamedPipeConnection
    585593            conn = NamedPipeConnection(listener.pipe_name, pipe_handle)
    586594            netlog.info("New %s connection received on %s", socktype, conn.target)
    587             return self.make_protocol(socktype, conn, frominfo=conn.target)
     595            return self.make_protocol(socktype, conn)
    588596
    589597        try:
    590598            sock, address = listener.accept()
     
    598606            peername = sock.getpeername()
    599607        except:
    600608            peername = str(address)
    601         sockname = sock.getsockname()
    602         target = peername or sockname
    603         sock.settimeout(self._socket_timeout)
    604609        #limit number of concurrent network connections:
    605         if socktype not in ("unix-domain", "named-pipe") and len(self._potential_protocols)>=self._max_connections:
     610        if socktype not in ("unix-domain", ) and len(self._potential_protocols)>=self._max_connections:
    606611            netlog.error("Error: too many connections (%i)", len(self._potential_protocols))
    607             netlog.error(" ignoring new one: %s", target)
     612            netlog.error(" ignoring new one: %s", peername)
    608613            sock.close()
    609614            return True
    610         netlog("new_connection(%s) sock=%s, timeout=%s, sockname=%s, address=%s, peername=%s. timeout=%s", args, sock, self._socket_timeout, sockname, address, peername, self._socket_timeout)
     615        sockname = sock.getsockname()
     616        target = peername or sockname
     617        sock.settimeout(self._socket_timeout)
     618        netlog("new_connection(%s) sock=%s, socket_info=%s, timeout=%s, address=%s, peername=%s. timeout=%s", args, sock, socket_info, self._socket_timeout, address, peername, self._socket_timeout)
    611619        conn = SocketConnection(sock, sockname, address, target, socktype)
    612620
    613621        #from here on, we run in a thread, so we can poll (peek does)
    614         start_thread(self.handle_new_connection, "new-%s-connection" % socktype, True, args=(conn, sock, socktype))
     622        start_thread(self.handle_new_connection, "new-%s-connection" % socktype, True, args=(conn, sock, address, socktype, peername))
    615623        return True
    616624
    617     def handle_new_connection(self, conn, sock, socktype):
     625    def handle_new_connection(self, conn, sock, address, socktype, peername):
    618626        """
    619627            Use peek to decide what sort of connection this is,
    620628            and start the appropriate handler for it.
     
    631639                conn.close()
    632640            except Exception as e:
    633641                netlog("error sending '%s': %s", nonl(msg), e)
     642        sockname = sock.getsockname()
     643        target = peername or sockname
     644        sock.settimeout(self._socket_timeout)
     645
     646        netlog("handle_new_connection%s sockname=%s, target=%s", (conn, sock, address, socktype, peername), sockname, target)
    634647        #peek so we can detect invalid clients early,
    635648        #or handle non-xpra traffic:
    636649        PEEK_SIZE = 8192
    637650        try:
    638             v = conn.peek(PEEK_SIZE)
     651            peek_data = conn.peek(PEEK_SIZE)
    639652        except:
    640             v = None
    641         if socktype=="tcp" and (self._html or self._tcp_proxy or self._ssl_wrap_socket):
     653            peek_data = ""
     654        netlog("socket peek=%s", repr_ellipsized(peek_data, limit=512))
     655        netlog("socket peek hex=%s", binascii.hexlify(peek_data))
     656        line1 = peek_data.splitlines()[0]
     657        netlog("socket peek line1=%s", repr_ellipsized(line1))
     658
     659        def ssl_wrap():
     660            ssl_sock = self._ssl_wrap_socket(sock)
     661            netlog("ssl wrapped socket(%s)=%s", sock, ssl_sock)
     662            if ssl_sock is None:
     663                #None means EOF! (we don't want to import ssl bits here)
     664                netlog("ignoring SSL EOF error")
     665                return None
     666            ssl_conn = SSLSocketConnection(ssl_sock, sockname, address, target, socktype)
     667            netlog("ssl_wrap()=%s", ssl_conn)
     668            return ssl_conn
     669
     670        if socktype=="SSL" or socktype=="wss":
     671            #verify that this isn't plain HTTP / xpra:
     672            if peek_data:
     673                packet_type = None
     674                if peek_data[0] in ("P", ord("P")):
     675                    packet_type = "xpra"
     676                elif line1.find("HTTP/")>0:
     677                    packet_type = "HTTP"
     678                if packet_type:
     679                    netlog.warn("Warning: invalid SSL connection header:")
     680                    netlog.warn(" packet looks like plain %s packet", packet_type)
     681                    netlog.warn(" connection dropped")
     682                    sock.close()
     683                    return
     684            #always start by wrapping with SSL:
     685            ssl_conn = ssl_wrap()
     686            log_new_connection(conn)
     687            handle_as_https = False
     688            if socktype=="wss" or self.ssl_mode=="www":
     689                handle_as_https = True
     690            elif self.ssl_mode=="auto":
     691                #look for HTTPS request to handle:
     692                if line1.find("HTTP/")>0 or peek_data.find("\x08http/1.1")>0:
     693                    handle_as_https = True
     694            if handle_as_https:
     695                self.start_http_socket(ssl_conn, True, peek_data)
     696            else:
     697                self.make_protocol(socktype, ssl_conn)
     698            return
     699
     700        elif socktype=="ws":
     701            if self.ssl_mode in ("auto", ) and peek_data and peek_data[0] in ("\x16", 0x16):
     702                netlog("ws socket receiving ssl")
     703                conn = ssl_wrap()
     704            log_new_connection(conn)
     705            self.start_http_socket(conn, False, peek_data)
     706            return
     707
     708        if socktype=="tcp" and peek_data and (self._html or self._tcp_proxy or self._ssl_wrap_socket):
    642709            #see if the packet data is actually xpra or something else
    643710            #that we need to handle via a tcp proxy, ssl wrapper or the websockify adapter:
    644711            try:
    645                 cont, conn, v = self.may_wrap_socket(conn, socktype, v)
     712                cont, conn, peek_data = self.may_wrap_socket(conn, socktype, peek_data)
    646713                if not cont:
    647714                    return
    648715            except IOError as e:
     
    649716                netlog("socket wrapping failed", exc_info=True)
    650717                conn_err(str(e))
    651718                return
    652         if v and v[0] not in ("P", ord("P")):
    653             msg = self.guess_header_protocol(v)
     719        if peek_data and peek_data[0] not in ("P", ord("P")):
     720            msg = self.guess_header_protocol(peek_data)
    654721            conn_err("invalid packet header, %s" % msg)
    655722            return True
     723
    656724        sock.settimeout(self._socket_timeout)
    657         inject_ssl_socket_info(conn)
    658         log_new_connection(conn)
     725        socket_info = self.socket_info.get(sock)
     726        log_new_connection(conn, socket_info)
    659727        self.make_protocol(socktype, conn)
    660728
    661 
    662     def make_protocol(self, socktype, conn, frominfo=""):
     729    def make_protocol(self, socktype, conn):
     730        netlog("make_protocol(%s, %s)", socktype, conn)
     731        socktype = socktype.lower()
    663732        protocol = Protocol(self, conn, self.process_packet)
    664733        self._potential_protocols.append(protocol)
    665734        protocol.large_packets.append("info-response")
     
    671740            protocol.auth_class = self.tcp_auth_class
    672741            protocol.encryption = self.tcp_encryption
    673742            protocol.keyfile = self.tcp_encryption_keyfile
    674         elif socktype=="SSL":
     743        elif socktype=="ssl":
    675744            protocol.auth_class = self.ssl_auth_class
     745        elif socktype=="ws":
     746            protocol.auth_class = self.ws_auth_class
     747        elif socktype=="wss":
     748            protocol.auth_class = self.wss_auth_class
    676749        elif socktype=="vsock":
    677750            protocol.auth_class = self.vsock_auth_class
    678751        else:
     
    688761            protocol.set_cipher_in(protocol.encryption, DEFAULT_IV, password, DEFAULT_SALT, DEFAULT_ITERATIONS, INITIAL_PADDING)
    689762        protocol.start()
    690763        self.timeout_add(self._accept_timeout*1000, self.verify_connection_accepted, protocol)
     764        return protocol
    691765
    692766    def may_wrap_socket(self, conn, socktype, peek_data=""):
    693767        """
     
    715789                #None means EOF! (we don't want to import ssl bits here)
    716790                netlog("ignoring SSL EOF error")
    717791                return False, None, None
    718             conn = SocketConnection(sock, sockname, address, target, socktype)
     792            conn = SSLSocketConnection(sock, sockname, address, target, socktype)
    719793            #we cannot peek on SSL sockets, just clear the unencrypted data:
    720794            netlog("may_wrap_socket SSL: %s", conn)
    721795            v = None
    722796        is_ssl = socktype=="SSL"
    723797        if self._html and self.ssl_mode!="tcp":
    724             httplog("peek_data=%s", nonl(peek_data))
     798            httplog("peek_data=%s", repr_ellipsized(peek_data))
    725799            line1 = peek_data.splitlines()[0]
    726800            httplog("line 1=%s", repr_ellipsized(line1))
    727801            if line1.find("HTTP/")>0 or (is_ssl and (self.ssl_mode=="www" or (self.ssl_mode=="auto" and peek_data.find("\x08http/1.1")>0))):
    728                 http_proto = "http"+["","s"][int(is_ssl)]
    729                 if line1.startswith("GET ") or line1.startswith("POST "):
    730                     parts = line1.split(" ")
    731                     httplog("New %s %s request received from %s for '%s'", http_proto, parts[0], frominfo, parts[1])
    732                     tname = "%s-request" % parts[0]
    733                     req_info = "%s %s" % (http_proto, parts[0])
    734                 else:
    735                     httplog("New %s connection received from %s", http_proto, frominfo)
    736                     req_info = "ws"+["","s"][int(is_ssl)]
    737                     tname = "%s-proxy" % req_info
    738                 start_thread(self.start_websockify, "%s-for-%s" % (tname, frominfo), daemon=True, args=(conn, req_info, conn.remote))
     802                self.start_http_socket(conn, is_ssl, peek_data)
    739803                return False, conn, None
    740804        if self._tcp_proxy:
    741805            netlog.info("New tcp proxy connection received from %s", frominfo)
     
    767831            "/Info"         : self.http_info_request,
    768832            }
    769833
    770     def start_websockify(self, conn, req_info, frominfo):
    771         wslog("start_websockify(%s, %s, %s) www dir=%s", conn, req_info, frominfo, self._www_dir)
     834    def start_http_socket(self, conn, is_ssl=False, peek_data=""):
     835        frominfo = pretty_socket(conn.remote)
     836        line1 = peek_data.splitlines()[0]
     837        http_proto = "http"+["","s"][int(is_ssl)]
     838        if line1.startswith("GET ") or line1.startswith("POST "):
     839            parts = line1.split(" ")
     840            httplog("New %s %s request received from %s for '%s'", http_proto, parts[0], frominfo, parts[1])
     841            tname = "%s-request" % parts[0]
     842            req_info = "%s %s" % (http_proto, parts[0])
     843        else:
     844            httplog("New %s connection received from %s", http_proto, frominfo)
     845            req_info = "ws"+["","s"][int(is_ssl)]
     846            tname = "%s-proxy" % req_info
     847        #we start a new thread,
     848        #only so that the websocket handler thread is named correctly:
     849        start_thread(self.start_websockify, "%s-for-%s" % (tname, frominfo), daemon=True, args=(conn, is_ssl, req_info, conn.remote))
     850
     851    def start_websockify(self, conn, is_ssl, req_info, frominfo):
     852        wslog("start_websockify(%s, %s, %s, %s) www dir=%s", conn, is_ssl, req_info, frominfo, self._www_dir)
    772853        from xpra.net.websocket import WebSocketConnection, WSRequestHandler
    773854        try:
    774855            sock = conn._socket
     
    782863                saved_send = sock.send
    783864                #now we can have a "is_active" that belongs to the real connection object:
    784865                def recv(*args):
    785                     return untilConcludes(wsc.is_active, saved_recv, *args)
     866                    return untilConcludes(wsc.is_active, wsc.can_retry, saved_recv, *args)
    786867                def send(*args):
    787                     return untilConcludes(wsc.is_active, saved_send, *args)
     868                    return untilConcludes(wsc.is_active, wsc.can_retry, saved_send, *args)
    788869                sock.recv = recv
    789870                sock.send = send
    790                 self.make_protocol("tcp", wsc, frominfo)
     871                socktype = "ws%s" % ["","s"][int(is_ssl)]
     872                self.make_protocol(socktype, wsc)
    791873            scripts = self.get_http_scripts()
    792874            WSRequestHandler(sock, frominfo, new_websocket_client, self._www_dir, scripts)
    793875            return
  • xpra/server/socket_util.py

     
    9999    listener.bind(sockaddr)
    100100    return listener
    101101
    102 def setup_tcp_socket(host, iport, socktype="TCP"):
     102def setup_tcp_socket(host, iport, socktype="tcp"):
    103103    from xpra.log import Logger
    104104    log = Logger("network")
    105105    try:
     
    114114        except:
    115115            pass
    116116    add_cleanup(cleanup_tcp_socket)
    117     return "tcp", tcp_socket, (host, iport)
     117    log("%s: %s:%s : %s", socktype, host, iport, socket)
     118    return socktype, tcp_socket, (host, iport)
    118119
    119120
    120121def parse_bind_tcp(bind_tcp):