def do_connect_builtin(self): self.set_sensitive(False) self.set_info_text("Connecting.") #cooked vars used by connect_or_fail params = {"type": xpra_opts.mode} if xpra_opts.mode == "ssh": remote_xpra = xpra_opts.remote_xpra.split() if xpra_opts.sockdir: remote_xpra.append("--socket-dir=%s" % xpra_opts.sockdir) params["remote_xpra"] = remote_xpra if xpra_opts.port: params["display"] = ":%s" % xpra_opts.port params["display_as_args"] = [params["display"]] else: params["display"] = "auto" params["display_as_args"] = [] full_ssh = shlex.split(xpra_opts.ssh) password = xpra_opts.password username = xpra_opts.username host = xpra_opts.host upos = host.find("@") if upos >= 0: #found at sign: username@host username = host[:upos] host = host[upos + 1:] ppos = username.find(":") if ppos >= 0: #found separator: username:password@host password = username[ppos + 1:] username = username[:ppos] if username: params["username"] = username full_ssh += ["-l", username] full_ssh += ["-T", host] params["full_ssh"] = full_ssh params["password"] = password params["display_name"] = "ssh:%s:%s" % (xpra_opts.host, xpra_opts.port) elif xpra_opts.mode == "unix-domain": params["display"] = ":%s" % xpra_opts.port params["display_name"] = "unix-domain:%s" % xpra_opts.port else: #tcp: params["host"] = xpra_opts.host params["port"] = int(xpra_opts.port) params["display_name"] = "tcp:%s:%s" % (xpra_opts.host, xpra_opts.port) #print("connect_or_fail(%s)" % params) self.set_info_text("Connecting...") try: conn = connect_to(params, self.set_info_text) except Exception, e: self.set_sensitive(True) self.set_info_color(True) self.set_info_text(str(e)) return
def do_connect_builtin(self, params): self.exit_code = None self.set_info_text("Connecting.") self.set_sensitive(False) try: conn = connect_to(params, self.set_info_text, ssh_fail_cb=self.ssh_failed) except Exception, e: log.error("failed to connect", exc_info=True) self.handle_exception(e) return
def start_proxy(self, client_proto, c, auth_caps): assert client_proto.authenticator is not None #find the target server session: def disconnect(msg): self.send_disconnect(client_proto, msg) sessions = client_proto.authenticator.get_sessions() if sessions is None: disconnect("no sessions found") return debug("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions) uid, gid, displays, env_options, session_options = sessions #debug("unused options: %s, %s", env_options, session_options) if len(displays) == 0: disconnect("no displays found") return display = c.strget("display") proxy_virtual_display = os.environ["DISPLAY"] #ensure we don't loop back to the proxy: if proxy_virtual_display in displays: displays.remove(proxy_virtual_display) if display == proxy_virtual_display: disconnect("invalid display") return if display: if display not in displays: disconnect("display not found") return else: if len(displays) != 1: disconnect( "please specify a display (more than one available)") return display = displays[0] debug("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display) def parse_error(*args): disconnect("invalid display string") log.warn("parse error on %s: %s", display, args) raise Exception("parse error on %s: %s" % (display, args)) opts = make_defaults_struct() opts.username = c.strget("username") disp_desc = parse_display_name(parse_error, opts, display) debug("display description(%s) = %s", display, disp_desc) try: server_conn = connect_to(disp_desc) except Exception, e: log.error("cannot start proxy connection to %s: %s", disp_desc, e) disconnect("failed to connect to display") return
def do_connect_builtin(self): self.set_sensitive(False) self.set_info_text("Connecting.") #cooked vars used by connect_or_fail params = {"type" : xpra_opts.mode} if xpra_opts.mode=="ssh": remote_xpra = xpra_opts.remote_xpra.split() if xpra_opts.sockdir: remote_xpra.append("--socket-dir=%s" % xpra_opts.sockdir) params["remote_xpra"] = remote_xpra if xpra_opts.port: params["display"] = ":%s" % xpra_opts.port params["display_as_args"] = [params["display"]] else: params["display"] = "auto" params["display_as_args"] = [] full_ssh = shlex.split(xpra_opts.ssh) password = xpra_opts.password username = xpra_opts.username host = xpra_opts.host upos = host.find("@") if upos>=0: #found at sign: username@host username = host[:upos] host = host[upos+1:] ppos = username.find(":") if ppos>=0: #found separator: username:password@host password = username[ppos+1:] username = username[:ppos] if username: params["username"] = username full_ssh += ["-l", username] full_ssh += ["-T", host] params["full_ssh"] = full_ssh params["password"] = password params["display_name"] = "ssh:%s:%s" % (xpra_opts.host, xpra_opts.port) elif xpra_opts.mode=="unix-domain": params["display"] = ":%s" % xpra_opts.port params["display_name"] = "unix-domain:%s" % xpra_opts.port else: #tcp: params["host"] = xpra_opts.host params["port"] = int(xpra_opts.port) params["display_name"] = "tcp:%s:%s" % (xpra_opts.host, xpra_opts.port) #print("connect_or_fail(%s)" % params) self.set_info_text("Connecting...") try: conn = connect_to(params, self.set_info_text) except Exception, e: self.set_sensitive(True) self.set_info_color(True) self.set_info_text(str(e)) return
def do_connect_builtin(self, params): self.exit_code = None self.set_info_text("Connecting.") self.set_sensitive(False) try: conn = connect_to(params, self.set_info_text, ssh_fail_cb=self.ssh_failed) except Exception as e: log.error("failed to connect", exc_info=True) self.handle_exception(e) return glib.idle_add(self.window.hide) glib.idle_add(self.start_XpraClient, conn)
def do_connect_builtin(self, params): self.exit_code = None self.set_info_text("Connecting.") self.set_sensitive(False) try: conn = connect_to(params, self.set_info_text, ssh_fail_cb=self.ssh_failed) except Exception, e: log.error("failed to connect", exc_info=True) self.set_sensitive(True) self.set_info_color(True) self.set_info_text(str(e)) gobject.idle_add(self.window.show) return
def do_connect_builtin(self, display_desc): log("do_connect_builtin(%s)", display_desc) self.exit_code = None self.current_error = None self.set_info_text("Connecting.") self.set_sensitive(False) try: conn = connect_to(display_desc, opts=self.config, debug_cb=self.set_info_text, ssh_fail_cb=self.ssh_failed) except Exception as e: log.error("failed to connect", exc_info=True) self.handle_exception(e) return glib.idle_add(self.window.hide) glib.idle_add(self.start_XpraClient, conn, display_desc)
def do_connect_builtin(self, display_desc): log("do_connect_builtin(%s)", display_desc) self.exit_code = None self.current_error = None self.set_info_text("Connecting.") self.set_sensitive(False) try: log("calling %s%s", connect_to, (display_desc, repr_ellipsized(str(self.config)), self.set_info_text, self.ssh_failed)) conn = connect_to(display_desc, opts=self.config, debug_cb=self.set_info_text, ssh_fail_cb=self.ssh_failed) except Exception as e: log("do_connect_builtin(%s) failed to connect", display_desc, exc_info=True) self.handle_exception(e) return log("connect_to(..)=%s, hiding launcher window, starting client", conn) GLib.idle_add(self.start_XpraClient, conn, display_desc)
def start_proxy(self, client_proto, c, auth_caps): assert client_proto.authenticator is not None #find the target server session: def disconnect(msg): self.send_disconnect(client_proto, msg) sessions = client_proto.authenticator.get_sessions() if sessions is None: disconnect("no sessions found") return debug("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions) uid, gid, displays, env_options, session_options = sessions #debug("unused options: %s, %s", env_options, session_options) if len(displays)==0: disconnect("no displays found") return display = c.strget("display") proxy_virtual_display = os.environ["DISPLAY"] #ensure we don't loop back to the proxy: if proxy_virtual_display in displays: displays.remove(proxy_virtual_display) if display==proxy_virtual_display: disconnect("invalid display") return if display: if display not in displays: disconnect("display not found") return else: if len(displays)!=1: disconnect("please specify a display (more than one available)") return display = displays[0] debug("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display) def parse_error(*args): disconnect("invalid display string") log.warn("parse error on %s: %s", display, args) raise Exception("parse error on %s: %s" % (display, args)) opts = make_defaults_struct() opts.username = c.strget("username") disp_desc = parse_display_name(parse_error, opts, display) debug("display description(%s) = %s", display, disp_desc) try: server_conn = connect_to(disp_desc) except Exception, e: log.error("cannot start proxy connection to %s: %s", disp_desc, e) disconnect("failed to connect to display") return
def fd(d): opts = AdHocStruct() try: #silence errors since we're expecting them: from xpra.scripts import main as xpra_main try: saved_timeout = xpra_main.CONNECT_TIMEOUT xpra_main.CONNECT_TIMEOUT = 5 saved_werr = xpra_main.werr xpra_main.werr = noop conn = connect_to(d, opts) finally: xpra_main.werr = saved_werr xpra_main.CONNECT_TIMEOUT = saved_timeout except Exception: #from xpra.util import get_util_logger #get_util_logger().error("connect_to(%s, %s)", d, opts, exc_info=True) pass else: try: conn.close() except Exception: pass raise Exception("connect_to(%s) should have failed" % (d, ))
def proxy_session(self, client_proto, c, auth_caps, sessions): def disconnect(reason, *extras): log("disconnect(%s, %s)", reason, extras) self.send_disconnect(client_proto, reason, *extras) uid, gid, displays, env_options, session_options = sessions if POSIX: if getuid()==0: if uid==0 or gid==0: log.error("Error: proxy instances cannot run as root") log.error(" use a different uid and gid (ie: nobody)") disconnect(AUTHENTICATION_ERROR, "cannot run proxy instances as root") return else: uid = getuid() gid = getgid() username = get_username_for_uid(uid) groups = get_groups(username) log("username(%i)=%s, groups=%s", uid, username, groups) else: #the auth module recorded the username we authenticate against assert client_proto.authenticators for authenticator in client_proto.authenticators: username = getattr(authenticator, "username", "") if username: break #ensure we don't loop back to the proxy: proxy_virtual_display = os.environ.get("DISPLAY") if proxy_virtual_display in displays: displays.remove(proxy_virtual_display) #remove proxy instance virtual displays: displays = [x for x in displays if not x.startswith(":proxy-")] #log("unused options: %s, %s", env_options, session_options) proc = None socket_path = None display = None sns = c.dictget("start-new-session") authlog("proxy_session: displays=%s, start_sessions=%s, start-new-session=%s", displays, self._start_sessions, sns) if len(displays)==0 or sns: if not self._start_sessions: disconnect(SESSION_NOT_FOUND, "no displays found") return try: proc, socket_path, display = self.start_new_session(username, uid, gid, sns, displays) log("start_new_session%s=%s", (username, uid, gid, sns, displays), (proc, socket_path, display)) except Exception as e: log("start_server_subprocess failed", exc_info=True) log.error("Error: failed to start server subprocess:") log.error(" %s", e) disconnect(SERVER_ERROR, "failed to start a new session") return if display is None: display = c.strget("display") authlog("proxy_session: proxy-virtual-display=%s (ignored), user specified display=%s, found displays=%s", proxy_virtual_display, display, displays) if display==proxy_virtual_display: disconnect(SESSION_NOT_FOUND, "invalid display") return if display: if display not in displays: disconnect(SESSION_NOT_FOUND, "display '%s' not found" % display) return else: if len(displays)!=1: disconnect(SESSION_NOT_FOUND, "please specify a display, more than one is available: %s" % csv(displays)) return display = displays[0] connect = c.boolget("connect", True) #ConnectTestXpraClient doesn't want to connect to the real session either: ctr = c.strget("connect_test_request") log("connect=%s, connect_test_request=%s", connect, ctr) if not connect or ctr: log("proxy_session: not connecting to the session") hello = {"display" : display} if socket_path: hello["socket-path"] = socket_path #echo mode if present: mode = sns.get("mode") if mode: hello["mode"] = mode client_proto.send_now(("hello", hello)) return def stop_server_subprocess(): log("stop_server_subprocess() proc=%s", proc) if proc and proc.poll() is None: proc.terminate() log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display) def parse_error(*args): stop_server_subprocess() disconnect(SESSION_NOT_FOUND, "invalid display string") log.warn("Error: parsing failed for display string '%s':", display) for arg in args: log.warn(" %s", arg) raise Exception("parse error on %s: %s" % (display, args)) opts = make_defaults_struct(username=username, uid=uid, gid=gid) opts.username = username disp_desc = parse_display_name(parse_error, opts, display) if uid or gid: disp_desc["uid"] = uid disp_desc["gid"] = gid log("display description(%s) = %s", display, disp_desc) try: server_conn = connect_to(disp_desc, opts) except Exception as e: log("cannot connect", exc_info=True) log.error("Error: cannot start proxy connection:") for x in str(e).splitlines(): log.error(" %s", x) log.error(" connection definition:") print_nested_dict(disp_desc, prefix=" ", lchar="*", pad=20, print_fn=log.error) disconnect(SESSION_NOT_FOUND, "failed to connect to display") stop_server_subprocess() return log("server connection=%s", server_conn) #no other packets should be arriving until the proxy instance responds to the initial hello packet def unexpected_packet(packet): if packet: log.warn("Warning: received an unexpected packet on the proxy connection %s:", client_proto) log.warn(" %s", repr_ellipsized(packet)) client_conn = client_proto.steal_connection(unexpected_packet) client_state = client_proto.save_state() cipher = None encryption_key = None if auth_caps: cipher = auth_caps.get("cipher") if cipher: encryption_key = self.get_encryption_key(client_proto.authenticators, client_proto.keyfile) log("start_proxy(..) client connection=%s", client_conn) log("start_proxy(..) client state=%s", client_state) #this may block, so run it in a thread: def do_start_proxy(): log("do_start_proxy()") message_queue = MQueue() try: ioe = client_proto.wait_for_io_threads_exit(5+self._socket_timeout) if not ioe: log.error("Error: some network IO threads have failed to terminate") return client_conn.set_active(True) process = ProxyInstanceProcess(uid, gid, env_options, session_options, self._socket_dir, self.video_encoders, self.csc_modules, client_conn, disp_desc, client_state, cipher, encryption_key, server_conn, c, message_queue) log("starting %s from pid=%s", process, os.getpid()) self.processes[process] = (display, message_queue) process.start() log("process started") popen = process._popen assert popen #when this process dies, run reap to update our list of proxy processes: self.child_reaper.add_process(popen, "xpra-proxy-%s" % display, "xpra-proxy-instance", True, True, self.reap) finally: #now we can close our handle on the connection: client_conn.close() server_conn.close() message_queue.put("socket-handover-complete") start_thread(do_start_proxy, "start_proxy(%s)" % client_conn)
def start_proxy(self, client_proto, c, auth_caps): assert client_proto.authenticator is not None #find the target server session: def disconnect(reason, *extras): self.send_disconnect(client_proto, reason, *extras) try: sessions = client_proto.authenticator.get_sessions() except Exception as e: log.error("failed to get the list of sessions: %s", e) disconnect(AUTHENTICATION_ERROR) return if sessions is None: disconnect(SESSION_NOT_FOUND, "no sessions found") return log("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions) uid, gid, displays, env_options, session_options = sessions #log("unused options: %s, %s", env_options, session_options) if len(displays)==0: disconnect(SESSION_NOT_FOUND, "no displays found") return display = c.strget("display") proxy_virtual_display = os.environ.get("DISPLAY") #ensure we don't loop back to the proxy: if proxy_virtual_display in displays: displays.remove(proxy_virtual_display) if display==proxy_virtual_display: disconnect(SESSION_NOT_FOUND, "invalid display") return if display: if display not in displays: disconnect(SESSION_NOT_FOUND, "display not found") return else: if len(displays)!=1: disconnect(SESSION_NOT_FOUND, "please specify a display (more than one available)") return display = displays[0] log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display) def parse_error(*args): disconnect(SESSION_NOT_FOUND, "invalid display string") log.warn("parse error on %s: %s", display, args) raise Exception("parse error on %s: %s" % (display, args)) opts = make_defaults_struct() opts.username = client_proto.authenticator.username disp_desc = parse_display_name(parse_error, opts, display) log("display description(%s) = %s", display, disp_desc) try: server_conn = connect_to(disp_desc) except Exception as e: log.error("cannot start proxy connection to %s: %s", disp_desc, e, exc_info=True) disconnect(SESSION_NOT_FOUND, "failed to connect to display") return log("server connection=%s", server_conn) #no other packets should be arriving until the proxy instance responds to the initial hello packet def unexpected_packet(packet): if packet: log.warn("received an unexpected packet on the proxy connection: %s", repr_ellipsized(packet)) client_conn = client_proto.steal_connection(unexpected_packet) client_state = client_proto.save_state() cipher = None encryption_key = None if auth_caps: cipher = auth_caps.get("cipher") if cipher: encryption_key = self.get_encryption_key(client_proto.authenticator, client_proto.keyfile) log("start_proxy(..) client connection=%s", client_conn) log("start_proxy(..) client state=%s", client_state) #this may block, so run it in a thread: def do_start_proxy(): log("do_start_proxy()") message_queue = MQueue() try: ioe = client_proto.wait_for_io_threads_exit(5+self._socket_timeout) if not ioe: log.error("some network IO threads have failed to terminate!") return client_conn.set_active(True) assert uid!=0 and gid!=0 process = ProxyInstanceProcess(uid, gid, env_options, session_options, self._socket_dir, self.video_encoders, self.csc_modules, client_conn, client_state, cipher, encryption_key, server_conn, c, message_queue) log("starting %s from pid=%s", process, os.getpid()) self.processes[process] = (display, message_queue) process.start() log("process started") finally: #now we can close our handle on the connection: client_conn.close() server_conn.close() message_queue.put("socket-handover-complete") #FIXME: remove processes that have terminated make_thread(do_start_proxy, "start_proxy(%s)" % client_conn).start()
def start_proxy(self, client_proto, c, auth_caps): def disconnect(reason, *extras): log("disconnect(%s, %s)", reason, extras) self.send_disconnect(client_proto, reason, *extras) #find the target server session: if not client_proto.authenticator: log.error( "Error: the proxy server requires an authentication mode,") try: log.error(" client connection '%s' does not specify one", client_proto._conn.socktype) except: pass log.error(" use 'none' to disable authentication") disconnect(SESSION_NOT_FOUND, "no sessions found") return try: sessions = client_proto.authenticator.get_sessions() except Exception as e: authlog("failed to get the list of sessions", exc_info=True) authlog.error( "Error: failed to get the list of sessions using '%s' authenticator", client_proto.authenticator) authlog.error(" %s", e) disconnect(AUTHENTICATION_ERROR) return if sessions is None: disconnect(SESSION_NOT_FOUND, "no sessions found") return authlog("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions) uid, gid, displays, env_options, session_options = sessions #log("unused options: %s, %s", env_options, session_options) if len(displays) == 0: disconnect(SESSION_NOT_FOUND, "no displays found") return display = c.strget("display") proxy_virtual_display = os.environ.get("DISPLAY") #ensure we don't loop back to the proxy: if proxy_virtual_display in displays: displays.remove(proxy_virtual_display) authlog( "start_proxy: proxy-virtual-display=%s (ignored), user specified display=%s, found displays=%s", proxy_virtual_display, display, displays) if display == proxy_virtual_display: disconnect(SESSION_NOT_FOUND, "invalid display") return if display: if display not in displays: disconnect(SESSION_NOT_FOUND, "display '%s' not found" % display) return else: if len(displays) != 1: disconnect( SESSION_NOT_FOUND, "please specify a display, more than one is available: %s" % csv(displays)) return display = displays[0] log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display) def parse_error(*args): disconnect(SESSION_NOT_FOUND, "invalid display string") log.warn("Error: parsing failed for display string '%s':", display) for arg in args: log.warn(" %s", arg) raise Exception("parse error on %s: %s" % (display, args)) opts = make_defaults_struct() opts.username = client_proto.authenticator.username disp_desc = parse_display_name(parse_error, opts, display) log("display description(%s) = %s", display, disp_desc) try: server_conn = connect_to(disp_desc, opts) except Exception as e: log("cannot connect", exc_info=True) log.error("Error: cannot start proxy connection:") log.error(" %s", e) log.error(" connection definition:") print_nested_dict(disp_desc, prefix=" ", lchar="*", pad=20, print_fn=log.error) disconnect(SESSION_NOT_FOUND, "failed to connect to display") return log("server connection=%s", server_conn) #no other packets should be arriving until the proxy instance responds to the initial hello packet def unexpected_packet(packet): if packet: log.warn( "Warning: received an unexpected packet on the proxy connection %s:", client_proto) log.warn(" %s", repr_ellipsized(packet)) client_conn = client_proto.steal_connection(unexpected_packet) client_state = client_proto.save_state() cipher = None encryption_key = None if auth_caps: cipher = auth_caps.get("cipher") if cipher: encryption_key = self.get_encryption_key( client_proto.authenticator, client_proto.keyfile) log("start_proxy(..) client connection=%s", client_conn) log("start_proxy(..) client state=%s", client_state) #this may block, so run it in a thread: def do_start_proxy(): log("do_start_proxy()") message_queue = MQueue() try: ioe = client_proto.wait_for_io_threads_exit( 5 + self._socket_timeout) if not ioe: log.error( "Error: some network IO threads have failed to terminate" ) return client_conn.set_active(True) assert uid != 0 and gid != 0 process = ProxyInstanceProcess( uid, gid, env_options, session_options, self._socket_dir, self.video_encoders, self.csc_modules, client_conn, client_state, cipher, encryption_key, server_conn, c, message_queue) log("starting %s from pid=%s", process, os.getpid()) self.processes[process] = (display, message_queue) process.start() log("process started") popen = process._popen assert popen #when this process dies, run reap to update our list of proxy processes: self.child_reaper.add_process(popen, "xpra-proxy-%s" % display, "xpra-proxy-instance", True, True, self.reap) finally: #now we can close our handle on the connection: client_conn.close() server_conn.close() message_queue.put("socket-handover-complete") #FIXME: remove processes that have terminated start_thread(do_start_proxy, "start_proxy(%s)" % client_conn)
class ProxyServer(ServerCore): """ This is the proxy server you can launch with "xpra proxy", once authenticated, it will dispatch the connection to the session found using the authenticator's get_sessions() function. """ def __init__(self): log("ProxyServer.__init__()") ServerCore.__init__(self) self._max_connections = MAX_CONCURRENT_CONNECTIONS self.main_loop = None #keep track of the proxy process instances #the display they're on and the message queue we can # use to communicate with them self.processes = {} self.idle_add = gobject.idle_add self.timeout_add = gobject.timeout_add self.source_remove = gobject.source_remove self._socket_timeout = PROXY_SOCKET_TIMEOUT self._socket_dir = None self.control_commands = ["hello", "stop"] #ensure we cache the platform info before intercepting SIGCHLD #as this will cause a fork and SIGCHLD to be emitted: from xpra.version_util import get_platform_info get_platform_info() signal.signal(signal.SIGCHLD, self.sigchld) def init(self, opts): log("ProxyServer.init(%s)", opts) if not opts.auth: raise Exception( "The proxy server requires an authentication mode (use 'none' to disable authentication)" ) self._socket_dir = opts.socket_dir self.video_encoders = opts.video_encoders self.csc_modules = opts.csc_modules ServerCore.init(self, opts) def get_server_mode(self): return "proxy" def init_aliases(self): pass def do_run(self): self.main_loop = gobject.MainLoop() self.main_loop.run() def do_handle_command_request(self, proto, command, args): if command in ("help", "hello"): return ServerCore.do_handle_command_request( self, proto, command, args) assert command == "stop" if len(args) != 1: return ServerCore.control_command_response( self, proto, command, 4, "invalid number of arguments, usage: 'xpra control stop DISPLAY'" ) display = args[0] log("stop command: will try to find proxy process for display %s", display) for process, v in list(self.processes.items()): disp, mq = v if disp == display: pid = process.pid log.info( "stop command: found process %s with pid %s for display %s, sending it 'stop' request", process, pid, display) mq.put("stop") return self.control_command_response( proto, command, 0, "stopped proxy process with pid %s" % pid) return self.control_command_response( proto, command, 14, "no proxy found for display %s" % display) def stop_all_proxies(self): processes = self.processes self.processes = {} log("stop_all_proxies() will stop proxy processes: %s", processes) for process, v in processes.items(): if not process.is_alive(): continue disp, mq = v log("stop_all_proxies() stopping process %s for display %s", process, disp) mq.put("stop") log("stop_all_proxies() done") def cleanup(self): self.stop_all_proxies() ServerCore.cleanup(self) def do_quit(self): self.main_loop.quit() log.info("Proxy Server process ended") def add_listen_socket(self, socktype, sock): sock.listen(5) gobject.io_add_watch(sock, gobject.IO_IN, self._new_connection, sock) self.socket_types[sock] = socktype def verify_connection_accepted(self, protocol): #if we start a proxy, the protocol will be closed #(a new one is created in the proxy process) if not protocol._closed: self.send_disconnect(protocol, "connection timeout") def hello_oked(self, proto, packet, c, auth_caps): if c.boolget("stop_request"): self.clean_quit() return self.accept_client(proto, c) self.start_proxy(proto, c, auth_caps) def start_proxy(self, client_proto, c, auth_caps): assert client_proto.authenticator is not None #find the target server session: def disconnect(msg): self.send_disconnect(client_proto, msg) try: sessions = client_proto.authenticator.get_sessions() except Exception, e: log.error("failed to get the list of sessions: %s", e) disconnect("authentication error") return if sessions is None: disconnect("no sessions found") return log("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions) uid, gid, displays, env_options, session_options = sessions #log("unused options: %s, %s", env_options, session_options) if len(displays) == 0: disconnect("no displays found") return display = c.strget("display") proxy_virtual_display = os.environ.get("DISPLAY") #ensure we don't loop back to the proxy: if proxy_virtual_display in displays: displays.remove(proxy_virtual_display) if display == proxy_virtual_display: disconnect("invalid display") return if display: if display not in displays: disconnect("display not found") return else: if len(displays) != 1: disconnect( "please specify a display (more than one available)") return display = displays[0] log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display) def parse_error(*args): disconnect("invalid display string") log.warn("parse error on %s: %s", display, args) raise Exception("parse error on %s: %s" % (display, args)) opts = make_defaults_struct() opts.username = client_proto.authenticator.username disp_desc = parse_display_name(parse_error, opts, display) log("display description(%s) = %s", display, disp_desc) try: server_conn = connect_to(disp_desc) except Exception, e: log.error("cannot start proxy connection to %s: %s", disp_desc, e, exc_info=True) disconnect("failed to connect to display") return
def start_proxy(self, client_proto, c, auth_caps): def disconnect(reason, *extras): log("disconnect(%s, %s)", reason, extras) self.send_disconnect(client_proto, reason, *extras) #find the target server session: if not client_proto.authenticator: log.error( "Error: the proxy server requires an authentication mode,") try: log.error(" client connection '%s' does not specify one", client_proto._conn.socktype) except: pass log.error(" use 'none' to disable authentication") disconnect(SESSION_NOT_FOUND, "no sessions found") return try: sessions = client_proto.authenticator.get_sessions() except Exception as e: authlog("failed to get the list of sessions", exc_info=True) authlog.error( "Error: failed to get the list of sessions using '%s' authenticator", client_proto.authenticator) authlog.error(" %s", e) disconnect(AUTHENTICATION_ERROR, "cannot access sessions") return authlog("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions) if sessions is None: disconnect(SESSION_NOT_FOUND, "no sessions found") return uid, gid, displays, env_options, session_options = sessions if os.name == "posix": if uid == 0 or gid == 0: log.error("Error: proxy instances should not run as root") log.error(" use a different uid and gid (ie: nobody)") disconnect(AUTHENTICATION_ERROR, "cannot run proxy instances as root") return username = get_username_for_uid(uid) groups = get_groups(username) if "xpra" not in groups: log("user '%s' (uid=%i) is not in the xpra group", username, uid) log(" it belongs to: %s", csv(groups) or None) #ensure we don't loop back to the proxy: proxy_virtual_display = os.environ.get("DISPLAY") if proxy_virtual_display in displays: displays.remove(proxy_virtual_display) #remove proxy instance virtual displays: displays = [x for x in displays if not x.startswith(":proxy-")] #log("unused options: %s, %s", env_options, session_options) opts = make_defaults_struct() display = None proc = None sns = c.dictget("start-new-session") authlog("start_proxy: displays=%s, start-new-session=%s", displays, bool(sns)) if len(displays) == 0 or sns: if self._start_sessions: #start a new session mode = sns.get("mode", "start") assert mode in ( "start", "start-desktop", "shadow"), "invalid start-new-session mode '%s'" % mode sns = typedict(sns) display = sns.get("display") args = [] if display: args = [display] start = sns.strlistget("start") start_child = sns.strlistget("start-child") exit_with_children = sns.boolget("exit-with-children") exit_with_client = sns.boolget("exit-with-client") log( "starting new server subprocess: mode=%s, socket-dir=%s, socket-dirs=%s, start=%s, start-child=%s, exit-with-children=%s, exit-with-client=%s, uid=%s, gid=%s", mode, opts.socket_dir, opts.socket_dirs, start, start_child, exit_with_children, exit_with_client, uid, gid) try: proc, socket_path = start_server_subprocess( sys.argv[0], args, mode, opts, opts.socket_dir, opts.socket_dirs, start, start_child, exit_with_children, exit_with_client, uid=uid, gid=gid) display = "socket:%s" % socket_path except Exception as e: log("start_server_subprocess failed", exc_info=True) log.error("Error: failed to start server subprocess:") log.error(" %s", e) disconnect(SERVER_ERROR, "failed to start a new session") return if proc: self.child_reaper.add_process(proc, "server-%s" % display, "xpra start", True, True) else: disconnect(SESSION_NOT_FOUND, "no displays found") return if display is None: display = c.strget("display") authlog( "start_proxy: proxy-virtual-display=%s (ignored), user specified display=%s, found displays=%s", proxy_virtual_display, display, displays) if display == proxy_virtual_display: disconnect(SESSION_NOT_FOUND, "invalid display") return if display: if display not in displays: disconnect(SESSION_NOT_FOUND, "display '%s' not found" % display) return else: if len(displays) != 1: disconnect( SESSION_NOT_FOUND, "please specify a display, more than one is available: %s" % csv(displays)) return display = displays[0] def stop_server_subprocess(): if proc: proc.terminate() log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display) def parse_error(*args): stop_server_subprocess() disconnect(SESSION_NOT_FOUND, "invalid display string") log.warn("Error: parsing failed for display string '%s':", display) for arg in args: log.warn(" %s", arg) raise Exception("parse error on %s: %s" % (display, args)) opts.username = client_proto.authenticator.username disp_desc = parse_display_name(parse_error, opts, display) if uid or gid: disp_desc["uid"] = uid disp_desc["gid"] = gid log("display description(%s) = %s", display, disp_desc) try: server_conn = connect_to(disp_desc, opts) except Exception as e: log("cannot connect", exc_info=True) log.error("Error: cannot start proxy connection:") log.error(" %s", e) log.error(" connection definition:") print_nested_dict(disp_desc, prefix=" ", lchar="*", pad=20, print_fn=log.error) disconnect(SESSION_NOT_FOUND, "failed to connect to display") stop_server_subprocess() return log("server connection=%s", server_conn) #no other packets should be arriving until the proxy instance responds to the initial hello packet def unexpected_packet(packet): if packet: log.warn( "Warning: received an unexpected packet on the proxy connection %s:", client_proto) log.warn(" %s", repr_ellipsized(packet)) client_conn = client_proto.steal_connection(unexpected_packet) client_state = client_proto.save_state() cipher = None encryption_key = None if auth_caps: cipher = auth_caps.get("cipher") if cipher: encryption_key = self.get_encryption_key( client_proto.authenticator, client_proto.keyfile) log("start_proxy(..) client connection=%s", client_conn) log("start_proxy(..) client state=%s", client_state) #this may block, so run it in a thread: def do_start_proxy(): log("do_start_proxy()") message_queue = MQueue() try: ioe = client_proto.wait_for_io_threads_exit( 5 + self._socket_timeout) if not ioe: log.error( "Error: some network IO threads have failed to terminate" ) return client_conn.set_active(True) process = ProxyInstanceProcess( uid, gid, env_options, session_options, self._socket_dir, self.video_encoders, self.csc_modules, client_conn, client_state, cipher, encryption_key, server_conn, c, message_queue) log("starting %s from pid=%s", process, os.getpid()) self.processes[process] = (display, message_queue) process.start() log("process started") popen = process._popen assert popen #when this process dies, run reap to update our list of proxy processes: self.child_reaper.add_process(popen, "xpra-proxy-%s" % display, "xpra-proxy-instance", True, True, self.reap) finally: #now we can close our handle on the connection: client_conn.close() server_conn.close() message_queue.put("socket-handover-complete") start_thread(do_start_proxy, "start_proxy(%s)" % client_conn)
def start_proxy(self, client_proto, c, auth_caps): def disconnect(reason, *extras): log("disconnect(%s, %s)", reason, extras) self.send_disconnect(client_proto, reason, *extras) #find the target server session: if not client_proto.authenticator: log.error("Error: the proxy server requires an authentication mode,") try: log.error(" client connection '%s' does not specify one", client_proto._conn.socktype) except: pass log.error(" use 'none' to disable authentication") disconnect(SESSION_NOT_FOUND, "no sessions found") return try: sessions = client_proto.authenticator.get_sessions() except Exception as e: authlog("failed to get the list of sessions", exc_info=True) authlog.error("Error: failed to get the list of sessions using '%s' authenticator", client_proto.authenticator) authlog.error(" %s", e) disconnect(AUTHENTICATION_ERROR, "cannot access sessions") return authlog("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions) if sessions is None: disconnect(SESSION_NOT_FOUND, "no sessions found") return uid, gid, displays, env_options, session_options = sessions if os.name=="posix": if uid==0 or gid==0: log.error("Error: proxy instances should not run as root") log.error(" use a different uid and gid (ie: nobody)") disconnect(AUTHENTICATION_ERROR, "cannot run proxy instances as root") return username = get_username_for_uid(uid) groups = get_groups(username) if "xpra" not in groups: log.error("Error: user '%s' (uid=%i) is not in the xpra group", username, uid) log.error(" it belongs to: %s", csv(groups) or None) disconnect(PERMISSION_ERROR, "user missing 'xpra' group membership") return #ensure we don't loop back to the proxy: proxy_virtual_display = os.environ.get("DISPLAY") if proxy_virtual_display in displays: displays.remove(proxy_virtual_display) #remove proxy instance virtual displays: displays = [x for x in displays if not x.startswith(":proxy-")] #log("unused options: %s, %s", env_options, session_options) opts = make_defaults_struct() display = None proc = None sns = c.dictget("start-new-session") authlog("start_proxy: displays=%s, start-new-session=%s", displays, bool(sns)) if len(displays)==0 or sns: if self._start_sessions: #start a new session mode = sns.get("mode", "start") assert mode in ("start", "start-desktop", "shadow"), "invalid start-new-session mode '%s'" % mode sns = typedict(sns) display = sns.get("display") args = [] if display: args = [display] start = sns.strlistget("start") start_child = sns.strlistget("start-child") exit_with_children = sns.boolget("exit-with-children") exit_with_client = sns.boolget("exit-with-client") log("starting new server subprocess: mode=%s, socket-dir=%s, socket-dirs=%s, start=%s, start-child=%s, exit-with-children=%s, exit-with-client=%s, uid=%s, gid=%s", mode, opts.socket_dir, opts.socket_dirs, start, start_child, exit_with_children, exit_with_client, uid, gid) try: proc, socket_path = start_server_subprocess(sys.argv[0], args, mode, opts, opts.socket_dir, opts.socket_dirs, start, start_child, exit_with_children, exit_with_client, uid=uid, gid=gid) display = "socket:%s" % socket_path except Exception as e: log("start_server_subprocess failed", exc_info=True) log.error("Error: failed to start server subprocess:") log.error(" %s", e) disconnect(SERVER_ERROR, "failed to start a new session") return if proc: self.child_reaper.add_process(proc, "server-%s" % display, "xpra start", True, True) else: disconnect(SESSION_NOT_FOUND, "no displays found") return if display is None: display = c.strget("display") authlog("start_proxy: proxy-virtual-display=%s (ignored), user specified display=%s, found displays=%s", proxy_virtual_display, display, displays) if display==proxy_virtual_display: disconnect(SESSION_NOT_FOUND, "invalid display") return if display: if display not in displays: disconnect(SESSION_NOT_FOUND, "display '%s' not found" % display) return else: if len(displays)!=1: disconnect(SESSION_NOT_FOUND, "please specify a display, more than one is available: %s" % csv(displays)) return display = displays[0] def stop_server_subprocess(): if proc: proc.terminate() log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display) def parse_error(*args): stop_server_subprocess() disconnect(SESSION_NOT_FOUND, "invalid display string") log.warn("Error: parsing failed for display string '%s':", display) for arg in args: log.warn(" %s", arg) raise Exception("parse error on %s: %s" % (display, args)) opts.username = client_proto.authenticator.username disp_desc = parse_display_name(parse_error, opts, display) if uid or gid: disp_desc["uid"] = uid disp_desc["gid"] = gid log("display description(%s) = %s", display, disp_desc) try: server_conn = connect_to(disp_desc, opts) except Exception as e: log("cannot connect", exc_info=True) log.error("Error: cannot start proxy connection:") log.error(" %s", e) log.error(" connection definition:") print_nested_dict(disp_desc, prefix=" ", lchar="*", pad=20, print_fn=log.error) disconnect(SESSION_NOT_FOUND, "failed to connect to display") stop_server_subprocess() return log("server connection=%s", server_conn) #no other packets should be arriving until the proxy instance responds to the initial hello packet def unexpected_packet(packet): if packet: log.warn("Warning: received an unexpected packet on the proxy connection %s:", client_proto) log.warn(" %s", repr_ellipsized(packet)) client_conn = client_proto.steal_connection(unexpected_packet) client_state = client_proto.save_state() cipher = None encryption_key = None if auth_caps: cipher = auth_caps.get("cipher") if cipher: encryption_key = self.get_encryption_key(client_proto.authenticator, client_proto.keyfile) log("start_proxy(..) client connection=%s", client_conn) log("start_proxy(..) client state=%s", client_state) #this may block, so run it in a thread: def do_start_proxy(): log("do_start_proxy()") message_queue = MQueue() try: ioe = client_proto.wait_for_io_threads_exit(5+self._socket_timeout) if not ioe: log.error("Error: some network IO threads have failed to terminate") return client_conn.set_active(True) process = ProxyInstanceProcess(uid, gid, env_options, session_options, self._socket_dir, self.video_encoders, self.csc_modules, client_conn, client_state, cipher, encryption_key, server_conn, c, message_queue) log("starting %s from pid=%s", process, os.getpid()) self.processes[process] = (display, message_queue) process.start() log("process started") popen = process._popen assert popen #when this process dies, run reap to update our list of proxy processes: self.child_reaper.add_process(popen, "xpra-proxy-%s" % display, "xpra-proxy-instance", True, True, self.reap) finally: #now we can close our handle on the connection: client_conn.close() server_conn.close() message_queue.put("socket-handover-complete") start_thread(do_start_proxy, "start_proxy(%s)" % client_conn)
def start_proxy(self, client_proto, c, auth_caps): assert client_proto.authenticator is not None #find the target server session: def disconnect(reason, *extras): self.send_disconnect(client_proto, reason, *extras) try: sessions = client_proto.authenticator.get_sessions() except Exception as e: log.error("failed to get the list of sessions: %s", e) disconnect(AUTHENTICATION_ERROR) return if sessions is None: disconnect(SESSION_NOT_FOUND, "no sessions found") return log("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions) uid, gid, displays, env_options, session_options = sessions #log("unused options: %s, %s", env_options, session_options) if len(displays)==0: disconnect(SESSION_NOT_FOUND, "no displays found") return display = c.strget("display") proxy_virtual_display = os.environ.get("DISPLAY") #ensure we don't loop back to the proxy: if proxy_virtual_display in displays: displays.remove(proxy_virtual_display) if display==proxy_virtual_display: disconnect(SESSION_NOT_FOUND, "invalid display") return if display: if display not in displays: disconnect(SESSION_NOT_FOUND, "display not found") return else: if len(displays)!=1: disconnect(SESSION_NOT_FOUND, "please specify a display (more than one available)") return display = displays[0] log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display) def parse_error(*args): disconnect(SESSION_NOT_FOUND, "invalid display string") log.warn("parse error on %s: %s", display, args) raise Exception("parse error on %s: %s" % (display, args)) opts = make_defaults_struct() opts.username = client_proto.authenticator.username disp_desc = parse_display_name(parse_error, opts, display) log("display description(%s) = %s", display, disp_desc) try: server_conn = connect_to(disp_desc) except Exception as e: log.error("cannot start proxy connection to %s: %s", disp_desc, e, exc_info=True) disconnect(SESSION_NOT_FOUND, "failed to connect to display") return log("server connection=%s", server_conn) #no other packets should be arriving until the proxy instance responds to the initial hello packet def unexpected_packet(packet): if packet: log.warn("received an unexpected packet on the proxy connection: %s", repr_ellipsized(packet)) client_conn = client_proto.steal_connection(unexpected_packet) client_state = client_proto.save_state() cipher = None encryption_key = None if auth_caps: cipher = auth_caps.get("cipher") if cipher: encryption_key = self.get_encryption_key(client_proto.authenticator, client_proto.keyfile) log("start_proxy(..) client connection=%s", client_conn) log("start_proxy(..) client state=%s", client_state) #this may block, so run it in a thread: def do_start_proxy(): log("do_start_proxy()") message_queue = MQueue() try: ioe = client_proto.wait_for_io_threads_exit(0.5+self._socket_timeout) if not ioe: log.error("some network IO threads have failed to terminate!") return client_conn.set_active(True) assert uid!=0 and gid!=0 process = ProxyInstanceProcess(uid, gid, env_options, session_options, self._socket_dir, self.video_encoders, self.csc_modules, client_conn, client_state, cipher, encryption_key, server_conn, c, message_queue) log("starting %s from pid=%s", process, os.getpid()) self.processes[process] = (display, message_queue) process.start() log("process started") finally: #now we can close our handle on the connection: client_conn.close() server_conn.close() message_queue.put("socket-handover-complete") #FIXME: remove processes that have terminated make_thread(do_start_proxy, "start_proxy(%s)" % client_conn).start()
def test_connect_to(self): def f(**kwargs): fd(kwargs) def fd(d): opts = AdHocStruct() try: #silence errors since we're expecting them: from xpra.scripts import main as xpra_main try: saved_timeout = xpra_main.CONNECT_TIMEOUT xpra_main.CONNECT_TIMEOUT = 5 saved_werr = xpra_main.werr xpra_main.werr = main.noop conn = connect_to(d, opts) finally: xpra_main.werr = saved_werr xpra_main.CONNECT_TIMEOUT = saved_timeout except Exception: #from xpra.util import get_util_logger #get_util_logger().error("connect_to(%s, %s)", d, opts, exc_info=True) pass else: try: conn.close() except Exception: pass raise Exception("connect_to(%s) should have failed" % (d, )) #without extra arguments to specify the endpoint, #all connections should fail, even if they have a valid type: f(type="invalid", display_name="test") f(type="vsock", display_name="test", vsock=(10, 1000)) fd({ "type": "named-pipe", "display_name": "test", "named-pipe": "TEST-INVALID" }) f(type="unix-domain", display_name=":100000", display="100000") for socktype in ( "tcp", "ssl", "ws", "wss", ): f(type=socktype, display_name="test", host="localhost", port=100000) for paramiko in (True, False): f(type="ssh", display_name="test", host="localhost", port=100000, is_paramiko=paramiko) fd({ "type": "ssl", "display_name": "test", "host": "localhost", "port": 100000, "strict-host-check": False, }) #udp never fails when opening the connection: conn = connect_to( { "type": "udp", "host": "localhost", "port": 20000, "display_name": ":200" }, AdHocStruct()) conn.close()