Skip to content

Commit

Permalink
#2874 add module websocket headers
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@27604 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Oct 5, 2020
1 parent b79fc23 commit c42615d
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1735,7 +1735,7 @@ def osx_pkgconfig(*pkgs_options, **ekw):

toggle_packages(dbus_ENABLED, "xpra.dbus")
toggle_packages(mdns_ENABLED, "xpra.net.mdns")
toggle_packages(websockets_ENABLED, "xpra.net.websockets")
toggle_packages(websockets_ENABLED, "xpra.net.websockets", "xpra.net.websockets.headers")
toggle_packages(server_ENABLED or proxy_ENABLED, "xpra.server", "xpra.server.auth")
toggle_packages(rfb_ENABLED, "xpra.server.rfb")
toggle_packages(proxy_ENABLED, "xpra.server.proxy")
Expand Down
33 changes: 23 additions & 10 deletions src/xpra/net/websockets/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os
import uuid
from hashlib import sha1
from base64 import b64encode
Expand All @@ -17,26 +18,38 @@
MAX_READ_TIME = 5
READ_CHUNK_SIZE = 4096

HEADERS = {
b"Connection" : b"Upgrade",
b"Upgrade" : b"websocket",
b"Sec-WebSocket-Version" : b"13",
b"Sec-WebSocket-Protocol" : b"binary",
}
HEADERS_MODULES = os.environ.get("XPRA_WEBSOCKET_HEADERS_MODULES", "default").split(",")


def make_websocket_accept_hash(key):
GUID = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
accept = sha1(strtobytes(key) + GUID).digest()
return b64encode(accept)

def get_headers(host, port):
headers = {}
for mod_name in HEADERS_MODULES:
try:
header_module = __import__("xpra.net.websockets.headers.%s" % mod_name, {}, {}, ["get_headers"])
v = header_module.get_headers(host, port)
log("%s.get_headers(%s, %s)=%s", mod_name, host, port, v)
headers.update(v)
except ImportError as e:
log("import %s", mod_name, exc_info=True)
log.error("Error: websocket header module %s not available", mod_name)
log.error(" %s", e)
except Exception as e:
log("get_headers %s", mod_name, exc_info=True)
log.error("Error: cannot get headers from '%s'", mod_name)
log.error(" %s", e)
return headers


def client_upgrade(read, write, host, port):
lines = [b"GET / HTTP/1.1"]
key = b64encode(uuid.uuid4().bytes)
headers = HEADERS.copy()
headers = get_headers(host, port)
headers[b"Sec-WebSocket-Key"] = key
if host:
headers[b"Host"] = strtobytes("%s:%s" % (host, port))
for k,v in headers.items():
lines.append(b"%s: %s" % (k, v))
lines.append(b"")
Expand All @@ -50,7 +63,7 @@ def client_upgrade(read, write, host, port):

now = monotonic_time()
response = b""
while ("Sec-WebSocket-Protocol".lower() not in response.decode("utf-8").lower()) and monotonic_time()-now<MAX_READ_TIME:
while ("sec-websocket-protocol" not in response.decode("utf-8").lower()) and monotonic_time()-now<MAX_READ_TIME:
response += read(READ_CHUNK_SIZE)
headers = parse_response_header(response)
verify_response_headers(headers, key)
Expand Down
4 changes: 4 additions & 0 deletions src/xpra/net/websockets/headers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This file is part of Xpra.
# Copyright (C) 2020 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.
25 changes: 25 additions & 0 deletions src/xpra/net/websockets/headers/browser_cookie.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This file is part of Xpra.
# Copyright (C) 2020 mjharkin
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import browser_cookie3

from xpra.os_util import strtobytes


def get_headers(host, port): #pylint: disable=unused-argument
headers = {}
cookie_domain = host
cookie_string = ''
# get cookies for domain and all parent domains except tld
while cookie_domain.count('.', 2):
cj = browser_cookie3.load(domain_name=cookie_domain)
for c in cj:
cookie = c.name + "=" + c.value + "; "
# add if cookie doesn't already exist from subdomain
if not c.name + "=" in cookie_string:
cookie_string += cookie
cookie_domain = cookie_domain.split('.', 1)[1]
headers[b"Cookie"] = strtobytes(cookie_string)
return headers
21 changes: 21 additions & 0 deletions src/xpra/net/websockets/headers/default.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This file is part of Xpra.
# Copyright (C) 2019-2020 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.


from xpra.os_util import strtobytes

HEADERS = {
b"Connection" : b"Upgrade",
b"Upgrade" : b"websocket",
b"Sec-WebSocket-Version" : b"13",
b"Sec-WebSocket-Protocol" : b"binary",
}


def get_headers(host, port):
headers = HEADERS.copy()
if host:
headers[b"Host"] = strtobytes("%s:%s" % (host, port))
return headers
16 changes: 16 additions & 0 deletions src/xpra/net/websockets/headers/env_cookie.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This file is part of Xpra.
# Copyright (C) 2020 Antoine Martin <[email protected]>
# Copyright (C) 2020 mjharkin
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os

from xpra.os_util import strtobytes


def get_headers(host, port): #pylint: disable=unused-argument
headers = {}
if "XPRA_WS_COOKIE" in os.environ:
headers[b"Cookie"] = strtobytes(os.environ['XPRA_WS_COOKIE'])
return headers

0 comments on commit c42615d

Please sign in to comment.