This commit is contained in:
Pierre Ossman 2022-12-22 14:15:46 +01:00
commit d54020538d
2 changed files with 76 additions and 32 deletions

View File

@ -11,7 +11,7 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
''' '''
import signal, socket, optparse, time, os, sys, subprocess, logging, errno, ssl import signal, socket, optparse, time, os, sys, subprocess, logging, errno, ssl, stat
from socketserver import ThreadingMixIn from socketserver import ThreadingMixIn
from http.server import HTTPServer from http.server import HTTPServer
@ -112,7 +112,9 @@ Traffic Legend:
self.server.target_host, self.server.target_port, e) self.server.target_host, self.server.target_port, e)
raise self.CClose(1011, "Failed to connect to downstream server") raise self.CClose(1011, "Failed to connect to downstream server")
self.request.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) # Option unavailable when listening to unix socket
if not self.server.unix_listen:
self.request.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
if not self.server.wrap_cmd and not self.server.unix_target: if not self.server.wrap_cmd and not self.server.unix_target:
tsock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) tsock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
@ -495,6 +497,10 @@ def websockify_init():
parser.add_option("--ssl-ciphers", action="store", parser.add_option("--ssl-ciphers", action="store",
help="list of ciphers allowed for connection. For a list of " help="list of ciphers allowed for connection. For a list of "
"supported ciphers run `openssl ciphers`") "supported ciphers run `openssl ciphers`")
parser.add_option("--unix-listen",
help="listen to unix socket", metavar="FILE", default=None)
parser.add_option("--unix-listen-mode", default=None,
help="specify mode for unix socket (defaults to 0600)")
parser.add_option("--unix-target", parser.add_option("--unix-target",
help="connect to unix socket target", metavar="FILE") help="connect to unix socket target", metavar="FILE")
parser.add_option("--inetd", parser.add_option("--inetd",
@ -650,6 +656,16 @@ def websockify_init():
if opts.inetd: if opts.inetd:
opts.listen_fd = sys.stdin.fileno() opts.listen_fd = sys.stdin.fileno()
elif opts.unix_listen:
if opts.unix_listen_mode:
try:
# Parse octal notation (like 750)
opts.unix_listen_mode = int(opts.unix_listen_mode, 8)
except ValueError:
parser.error("Error parsing listen unix socket mode")
else:
# Default to 0600 (Owner Read/Write)
opts.unix_listen_mode = stat.S_IREAD | stat.S_IWRITE
else: else:
if len(args) < 1: if len(args) < 1:
parser.error("Too few arguments") parser.error("Too few arguments")

View File

@ -325,37 +325,40 @@ class WebSockifyServer():
file_only=False, file_only=False,
run_once=False, timeout=0, idle_timeout=0, traffic=False, run_once=False, timeout=0, idle_timeout=0, traffic=False,
tcp_keepalive=True, tcp_keepcnt=None, tcp_keepidle=None, tcp_keepalive=True, tcp_keepcnt=None, tcp_keepidle=None,
tcp_keepintvl=None, ssl_ciphers=None, ssl_options=0): tcp_keepintvl=None, ssl_ciphers=None, ssl_options=0,
unix_listen=None, unix_listen_mode=None):
# settings # settings
self.RequestHandlerClass = RequestHandlerClass self.RequestHandlerClass = RequestHandlerClass
self.verbose = verbose self.verbose = verbose
self.listen_fd = listen_fd self.listen_fd = listen_fd
self.listen_host = listen_host self.unix_listen = unix_listen
self.listen_port = listen_port self.unix_listen_mode = unix_listen_mode
self.prefer_ipv6 = source_is_ipv6 self.listen_host = listen_host
self.ssl_only = ssl_only self.listen_port = listen_port
self.ssl_ciphers = ssl_ciphers self.prefer_ipv6 = source_is_ipv6
self.ssl_options = ssl_options self.ssl_only = ssl_only
self.verify_client = verify_client self.ssl_ciphers = ssl_ciphers
self.daemon = daemon self.ssl_options = ssl_options
self.run_once = run_once self.verify_client = verify_client
self.timeout = timeout self.daemon = daemon
self.idle_timeout = idle_timeout self.run_once = run_once
self.traffic = traffic self.timeout = timeout
self.file_only = file_only self.idle_timeout = idle_timeout
self.web_auth = web_auth self.traffic = traffic
self.file_only = file_only
self.web_auth = web_auth
self.launch_time = time.time() self.launch_time = time.time()
self.ws_connection = False self.ws_connection = False
self.handler_id = 1 self.handler_id = 1
self.terminating = False self.terminating = False
self.logger = self.get_logger() self.logger = self.get_logger()
self.tcp_keepalive = tcp_keepalive self.tcp_keepalive = tcp_keepalive
self.tcp_keepcnt = tcp_keepcnt self.tcp_keepcnt = tcp_keepcnt
self.tcp_keepidle = tcp_keepidle self.tcp_keepidle = tcp_keepidle
self.tcp_keepintvl = tcp_keepintvl self.tcp_keepintvl = tcp_keepintvl
# keyfile path must be None if not specified # keyfile path must be None if not specified
self.key = None self.key = None
@ -387,6 +390,8 @@ class WebSockifyServer():
self.msg("WebSocket server settings:") self.msg("WebSocket server settings:")
if self.listen_fd != None: if self.listen_fd != None:
self.msg(" - Listen for inetd connections") self.msg(" - Listen for inetd connections")
elif self.unix_listen != None:
self.msg(" - Listen on unix socket %s", self.unix_listen)
else: else:
self.msg(" - Listen on %s:%s", self.msg(" - Listen on %s:%s",
self.listen_host, self.listen_port) self.listen_host, self.listen_port)
@ -421,8 +426,9 @@ class WebSockifyServer():
@staticmethod @staticmethod
def socket(host, port=None, connect=False, prefer_ipv6=False, def socket(host, port=None, connect=False, prefer_ipv6=False,
unix_socket=None, use_ssl=False, tcp_keepalive=True, unix_socket=None, unix_socket_mode=None, unix_socket_listen=False,
tcp_keepcnt=None, tcp_keepidle=None, tcp_keepintvl=None): use_ssl=False, tcp_keepalive=True, tcp_keepcnt=None,
tcp_keepidle=None, tcp_keepintvl=None):
""" Resolve a host (and optional port) to an IPv4 or IPv6 """ Resolve a host (and optional port) to an IPv4 or IPv6
address. Create a socket. Bind to it if listen is set, address. Create a socket. Bind to it if listen is set,
otherwise connect to it. Return the socket. otherwise connect to it. Return the socket.
@ -470,8 +476,22 @@ class WebSockifyServer():
sock.bind(addrs[0][4]) sock.bind(addrs[0][4])
sock.listen(100) sock.listen(100)
else: else:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) if unix_socket_listen:
sock.connect(unix_socket) # Make sure the socket does not already exist
try:
os.unlink(unix_socket)
except FileNotFoundError:
pass
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
oldmask = os.umask(0o777 ^ unix_socket_mode)
try:
sock.bind(unix_socket)
finally:
os.umask(oldmask)
sock.listen(100)
else:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(unix_socket)
return sock return sock
@ -700,6 +720,11 @@ class WebSockifyServer():
if self.listen_fd != None: if self.listen_fd != None:
lsock = socket.fromfd(self.listen_fd, socket.AF_INET, socket.SOCK_STREAM) lsock = socket.fromfd(self.listen_fd, socket.AF_INET, socket.SOCK_STREAM)
elif self.unix_listen != None:
lsock = self.socket(host=None,
unix_socket=self.unix_listen,
unix_socket_mode=self.unix_listen_mode,
unix_socket_listen=True)
else: else:
lsock = self.socket(self.listen_host, self.listen_port, False, lsock = self.socket(self.listen_host, self.listen_port, False,
self.prefer_ipv6, self.prefer_ipv6,
@ -766,6 +791,9 @@ class WebSockifyServer():
ready = select.select([lsock], [], [], 1)[0] ready = select.select([lsock], [], [], 1)[0]
if lsock in ready: if lsock in ready:
startsock, address = lsock.accept() startsock, address = lsock.accept()
# Unix Socket will not report address (empty string), but address[0] is logged a bunch
if self.unix_listen != None:
address = [ self.unix_listen ]
else: else:
continue continue
except self.Terminate: except self.Terminate: