xpra icon
Bug tracker and wiki

Ticket #2406: client-bind.patch

File client-bind.patch, 6.3 KB (added by Antoine Martin, 7 months ago)

support client bind

  • xpra/client/mixins/network_state.py

     
    99from collections import deque
    1010
    1111from xpra.os_util import monotonic_time, POSIX
    12 from xpra.util import envint, envbool, csv, typedict
     12from xpra.util import envint, envbool, envfloat, csv, typedict
    1313from xpra.exit_codes import EXIT_TIMEOUT
    1414from xpra.net.packet_encoding import ALL_ENCODERS
    1515from xpra.net.compression import ALL_COMPRESSORS
     16from xpra.net.bytestreams import log_new_connection
     17from xpra.net.socket_util import create_sockets, add_listen_socket, accept_connection
     18from xpra.net.net_util import get_network_caps
     19from xpra.net.protocol import Protocol
    1620from xpra.client.mixins.stub_client_mixin import StubClientMixin
    17 from xpra.scripts.config import parse_with_unit
     21from xpra.scripts.config import parse_with_unit, InitException
    1822from xpra.log import Logger
    1923
    2024log = Logger("network")
     
    2731LOG_INFO_RESPONSE = os.environ.get("XPRA_LOG_INFO_RESPONSE", "")
    2832AUTO_BANDWIDTH_PCT = envint("XPRA_AUTO_BANDWIDTH_PCT", 80)
    2933assert 1<AUTO_BANDWIDTH_PCT<=100, "invalid value for XPRA_AUTO_BANDWIDTH_PCT: %i" % AUTO_BANDWIDTH_PCT
     34BIND_SOCKETS = envbool("XPRA_CLIENT_BIND_SOCKETS", True)
     35SOCKET_TIMEOUT = envfloat("XPRA_CLIENT_SOCKET_TIMEOUT", "0.1")
     36MAX_CONCURRENT_CONNECTIONS = envint("XPRA_MAX_CONCURRENT_CONNECTIONS", 100)
    3037
    3138
    3239"""
     
    6875        self.ping_echo_timers = {}
    6976        self.ping_echo_timeout_timer = None
    7077
     78        #sockets:
     79        self.sockets = {}
     80        self.socket_info = {}
     81        self.socket_options = {}
     82        self.socket_cleanup = []
     83        self._potential_protocols = []
    7184
     85
    7286    def init(self, opts):
    7387        self.pings = opts.pings
    7488        self.bandwidth_limit = parse_with_unit("bandwidth-limit", opts.bandwidth_limit)
    7589        self.bandwidth_detection = opts.bandwidth_detection
    7690        bandwidthlog("init bandwidth_limit=%s", self.bandwidth_limit)
     91        if BIND_SOCKETS:
     92            def err(msg):
     93                raise InitException(msg)
     94            #don't create regular local sockets or udp sockets for now:
     95            opts.bind = ()
     96            opts.bind_udp = ()
     97            self.sockets = create_sockets(opts, err)
     98            log.error("sockets=%s", self.sockets)
    7799
     100    def run(self):
     101        self.start_listen_sockets()
    78102
    79103    def cleanup(self):
    80104        self.cancel_ping_timer()
    81105        self.cancel_ping_echo_timers()
    82106        self.cancel_ping_echo_timeout_timer()
     107        self.cleanup_sockets()
    83108
     109    def cleanup_sockets(self):
     110        for c in self.socket_cleanup:
     111            try:
     112                c()
     113            except Exception:
     114                log.error("Error during socket cleanup", exc_info=True)
    84115
     116    def start_listen_sockets(self):
     117        for sock_def, options in self.sockets.items():
     118            socktype, sock, info, _ = sock_def
     119            log("start_listen_sockets() will add %s socket %s (%s)", socktype, sock, info)
     120            self.socket_info[sock] = info
     121            self.socket_options[sock] = options
     122            self.idle_add(self.add_listen_socket, socktype, sock)
     123
     124    def add_listen_socket(self, socktype, sock):
     125        info = self.socket_info.get(sock)
     126        log("add_listen_socket(%s, %s) info=%s", socktype, sock, info)
     127        cleanup = add_listen_socket(socktype, sock, info, self._new_connection, None)
     128        if cleanup:
     129            self.socket_cleanup.append(cleanup)
     130
     131    def _new_connection(self, socktype, listener, handle=0):
     132        """
     133            Accept the new connection,
     134            verify that there aren't too many,
     135            start a thread to dispatch it to the correct handler.
     136        """
     137        log("_new_connection%s", (listener, socktype, handle))
     138        if self.exit_code is not None:
     139            log("ignoring new connection during shutdown")
     140            return False
     141        try:
     142            self.handle_new_connection(socktype, listener, handle)
     143        except Exception:
     144            log.error("Error handling new connection", exc_info=True)
     145        return self.exit_code is None
     146
     147    def handle_new_connection(self, socktype, listener, handle):
     148        socket_info = self.socket_info.get(listener)
     149        assert socktype, "cannot find socket type for %s" % listener
     150        socket_options = self.socket_options.get(listener, {})
     151        assert socktype!="named-pipe"
     152        conn = accept_connection(socktype, listener, SOCKET_TIMEOUT, socket_options)
     153        if conn is None:
     154            return
     155        #limit number of concurrent network connections:
     156        if len(self._potential_protocols)>=MAX_CONCURRENT_CONNECTIONS:
     157            log.error("Error: too many connections (%i)", len(self._potential_protocols))
     158            log.error(" ignoring new one: %s", conn.endpoint)
     159            conn.close()
     160            return
     161        sock = conn._socket
     162        socktype = conn.socktype
     163        peername = conn.endpoint
     164        sockname = sock.getsockname()
     165        target = peername or sockname
     166        log("handle_new_connection%s sockname=%s, target=%s",
     167               (conn, socket_info, socket_options), sockname, target)
     168        sock.settimeout(SOCKET_TIMEOUT)
     169        log_new_connection(conn, socket_info)
     170
     171        socktype = socktype.lower()
     172        protocol = Protocol(self, conn, self.process_network_packet)
     173        #protocol.large_packets.append(b"info-response")
     174        protocol.socket_type = socktype
     175        self._potential_protocols.append(protocol)
     176        protocol.authenticators = ()
     177        protocol.start()
     178        #self.schedule_verify_connection_accepted(protocol, self._accept_timeout)
     179
     180    def process_network_packet(self, proto, packet):
     181        log.info("packet: %s", packet)
     182        log.info("packet type: %s", packet[0])
     183        if packet[0]==b"hello":
     184            caps = typedict(packet[1])
     185            proto.parse_remote_caps(caps)
     186            proto.enable_compressor_from_caps(caps)
     187            proto.enable_encoder_from_caps(caps)
     188            hello = get_network_caps()
     189            proto.send_now(["hello", hello])
     190            info = self.get_info()
     191            proto.send_now(["info-response", info])
     192            import time
     193            time.sleep(1)
     194        proto.close()
     195
     196
    85197    def get_caps(self) -> dict:
    86198        caps = {
    87199            "network-state" : True,