Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251socket_pair = self.wakeup_socket_pair252if socket_pair is not None:253socket_pair[0].send("CloseWakeup".encode())254logger.debug("Waking up forwarding thread")255except OSError as e:256logger.warning(f"Error waking up forwarding thread: {e}")257pass260# Wrapper for thread.join(), which may throw for daemon threads (in late stages of finalization).261# Return True if the thread is no longer active (join succeeded), False otherwise262# See PR #34863 for more details on using daemon threads.263def try_join_daemon_thread(thread, timeout=0) -> bool:264try:265thread.join(timeout=timeout)266return not thread.is_alive()267except Exception as e:268logger.debug(f"Exception in thread.join, {e}")269return True271class Socks5Server():272def __init__(self, conf):273self.conf = conf274self.s = socket.socket(conf.af)275self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)276# When using dynamic port allocation (port=0), ensure we don't get a277# port that conflicts with the test framework's static port range.278if conf.addr[1] == 0:279set_ephemeral_port_range(self.s)280self.s.bind(conf.addr)281# When port=0, the OS assigns an available port. Update conf.addr282# to reflect the actual bound address so callers can use it.283self.conf.addr = self.s.getsockname()284self.s.listen(5)285# Set to False when stop is initiated286self._running = False287self._running_lock = threading.Lock()288self.thread = None289self.queue = queue.Queue() # report connections and exceptions to client290self.keep_alive = conf.keep_alive291# Store the background handlers, needed for clean shutdown292# Append-only array, completed handlers are set to None293self._handlers = []294self._handlers_lock = threading.Lock()296def is_running(self) -> bool:297with self._running_lock:298return self._running300def set_running(self, new_value: bool):301with self._running_lock:302self._running = new_value304def run(self):305while self.is_running():306(sockconn, _) = self.s.accept()307if self.is_running():308conn = Socks5Connection(self, sockconn)309# Use "daemon" threads, see PR #34863 for more discussion.310thread = threading.Thread(None, conn.handle, daemon=True)311with self._handlers_lock:312conn.handler_index = len(self._handlers)313self._handlers.append((thread, conn))314assert(conn.handler_index < len(self._handlers))315thread.start()317def remove_handler(self, handler_index):318with self._handlers_lock:319if handler_index < len(self._handlers):320if self._handlers[handler_index] is not None:321self._handlers[handler_index] = None322logger.debug(f"Handler {handler_index} removed")324def start(self):325assert not self.is_running()326self.set_running(True)327self.thread = threading.Thread(None, self.run, daemon=True)328self.thread.start()330def stop(self):331self.set_running(False)332# connect to self to end run loop333s = socket.socket(self.conf.af)334s.connect(self.conf.addr)335s.close()336self.thread.join()337# if there are active handlers, close them338with self._handlers_lock:339items = list(self._handlers)340for i, item in enumerate(items):341if item is None:342continue343thread, conn = item344# check if thread is still active345if not try_join_daemon_thread(thread, timeout=0):346conn.wakeup()347if try_join_daemon_thread(thread, timeout=2):348logger.debug(f"Stop(): Handler {i} thread joined")349else:350logger.warning(f"Stop(): Handler thread {i} didn't finish after force close")352def start_socks5_server(destinations_factory):353config = Socks5Configuration()354config.addr = ("127.0.0.1", 0) # Use port=0 to let the OS pick one. The actual port is later in server.conf.addr[1].355config.unauth = True356config.auth = True357config.destinations_factory = destinations_factory359server = Socks5Server(config)360server.start()362return server
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.