Step 1: Please describe your environment
- EpixNet version: 0.2.7 (source from
main-fc3486d8f9e5196870aa451531cf4712a213ed02)
- Operating system: Ubuntu 24.04 (Python 3.12.3, Gevent 26.4.0)
- Web browser: N/A (crash happens before the UI is opened)
- Tor status: always (
--tor always)
- Opened port: no
- Special configuration: Started via
start-venv.sh with flags --tor always --no-dht. System tray (Trayicon plugin) enabled, X11 session with DISPLAY=:1. pystray and python-xlib installed in the venv.
Step 2: Describe the problem
When starting EpixNet with --tor always, the Trayicon plugin crashes the whole process at startup because the global socket.socket has been replaced with socks.socksocket by src/util/SocksProxy.py:monkeyPatch. pystray's xorg backend imports Xlib, which then tries to open the X server connection through the SOCKS-patched socket. Unix-domain X11 sockets aren't supported by PySocks, and the TCP fallback fails because there's no destination configured for a local X server connection, so EpixNet exits.
Steps to reproduce
- On Linux with an X11 session (e.g.
DISPLAY=:1), set up the venv and install requirements.
- Make sure
pystray and python-xlib are installed (they're pulled in for the system tray).
- Run
./start-venv.sh (which launches python3 epixnet.py --tor always --no-dht).
- EpixNet patches sockets to the Tor SOCKS proxy, then the Trayicon plugin tries to create the tray icon.
Observed Results
EpixNet crashes at startup with the following traceback:
[21:27:52] - Patching sockets to tor socks proxy: 127.0.0.1:9050
[21:27:52] - epixnet 0.2.7 (source from main-fc3486d8f9e5196870aa451531cf4712a213ed02-dirty) on Python 3.12.3 ... Gevent 26.4.0
[21:27:52] - Unhandled exception: Can't connect to display ":1": Invalid destination-connection (host, port) pair
Traceback (most recent call last):
File ".../Xlib/support/unix_connect.py", line 104, in get_socket
s = _get_unix_socket(address)
File ".../Xlib/support/unix_connect.py", line 82, in _get_unix_socket
s.connect(address)
File ".../socks.py", line 742, in connect
raise socket.error("PySocks doesn't support IPv6: %s"
OSError: PySocks doesn't support IPv6: /tmp/.X11-unix/X1
During handling of the above exception, another exception occurred:
File ".../Xlib/support/unix_connect.py", line 76, in _get_tcp_socket
s.connect((host, 6000 + dno))
File ".../socks.py", line 769, in connect
raise GeneralProxyError(...)
socks.GeneralProxyError: Invalid destination-connection (host, port) pair
During handling of the above exception, another exception occurred:
File ".../plugins/Trayicon/TrayiconPlugin.py", line 205, in main
self.icon = pystray.Icon(...)
File ".../pystray/_xorg.py", line 107, in __init__
self._display = Xlib.display.Display()
...
Xlib.error.DisplayConnectionError: Can't connect to display ":1": Invalid destination-connection (host, port) pair
Exception ignored in: <function Icon.__del__ at ...>
AttributeError: 'Icon' object has no attribute '_display'
Root cause: src/util/SocksProxy.py:monkeyPatch replaces socket.socket with socks.socksocket globally. When pystray._xorg later constructs Xlib.display.Display() (both during import and inside Icon.__init__ / Icon.run), Xlib.support.unix_connect calls socket.socket(AF_UNIX, ...) to talk to /tmp/.X11-unix/X*. PySocks rejects the AF_UNIX address (PySocks doesn't support IPv6) and the TCP fallback to localhost:6001 fails too (Invalid destination-connection (host, port) pair).
Expected Results
When --tor always is set on a Linux desktop with a system tray, EpixNet should still start normally and show its tray icon. X11 connections to the local display should never be routed through the Tor SOCKS proxy. If the X display genuinely cannot be opened (truly headless), the Trayicon plugin should log a warning and continue without a tray icon instead of crashing the entire process.
Suggested fix
In plugins/Trayicon/TrayiconPlugin.py, before importing/initializing pystray, restore the original socket class (saved by SocksProxy.monkeyPatch as socket.socket_noproxy) and patch the socket module reference inside Xlib.support.unix_connect, Xlib.support.connect, and Xlib.protocol.display with a shim whose .socket attribute is socket.socket_noproxy. This keeps the global SOCKS patch in place for normal network traffic but ensures Xlib's local X11 connections always use the unproxied socket. Also wrap the pystray initialization in a generic except Exception so a missing/unreachable display degrades gracefully instead of crashing EpixNet.
Suggested patch
Applied to plugins/Trayicon/TrayiconPlugin.py, inside ActionsPlugin.main(), replacing the original try: import pystray ... block:
def main(self):
# SocksProxy.monkeyPatch may have replaced socket.socket with
# socks.socksocket, which doesn't support AF_UNIX. Xlib (used by
# pystray's xorg backend) connects to /tmp/.X11-unix/X* via AF_UNIX,
# so temporarily restore the real socket while pystray initializes.
import socket
_patched_socket = None
if hasattr(socket, "socket_noproxy"):
_patched_socket = socket.socket
socket.socket = socket.socket_noproxy
try:
try:
import pystray
import pystray._base
from PIL import Image
except ImportError as err:
print("Trayicon plugin: pystray or Pillow not installed (%s), skipping tray icon." % err)
super(ActionsPlugin, self).main()
return
except Exception as err:
print("Trayicon plugin: failed to initialize tray icon backend (%s), skipping tray icon." % err)
super(ActionsPlugin, self).main()
return
# pystray's xorg backend opens (and uses) the X display lazily
# inside Icon.__init__ / Icon.run, long after this function
# returns. Patch Xlib's module-level `socket` references with a
# shim that exposes the un-proxied socket class, so X11
# connections never go through PySocks.
if _patched_socket is not None:
try:
import types
shim = types.ModuleType("socket_noproxy_shim")
for _name in dir(socket):
try:
setattr(shim, _name, getattr(socket, _name))
except AttributeError:
pass
shim.socket = socket.socket_noproxy
for _modname in (
"Xlib.support.unix_connect",
"Xlib.support.connect",
"Xlib.protocol.display",
):
_mod = sys.modules.get(_modname)
if _mod is not None and hasattr(_mod, "socket"):
_mod.socket = shim
except Exception as err:
print("Trayicon plugin: failed to patch Xlib socket: %s" % err)
finally:
if _patched_socket is not None:
socket.socket = _patched_socket
What this does:
- Temporarily un-patches
socket.socket while pystray (and transitively Xlib) is imported, so the import-time Xlib.display.Display() call in pystray/_xorg.py succeeds.
- Installs a
socket shim into the loaded Xlib modules (Xlib.support.unix_connect, Xlib.support.connect, Xlib.protocol.display). The shim mirrors the real socket module but exposes socket.socket_noproxy as its .socket class. This is necessary because pystray._xorg.Icon.__init__ opens its own Xlib.display.Display() later, in the trayicon thread — long after main() returns — and that call must also bypass PySocks.
- Restores the global
socket.socket (= socks.socksocket) in finally, so all other EpixNet/Gevent network traffic continues to be routed through the Tor SOCKS proxy as before.
- Adds a generic
except Exception around the pystray import so a truly unavailable display (headless server, Wayland-only, etc.) just logs a warning and continues without a tray icon, instead of taking the whole process down.
No other files are modified, and the change is a no-op when --tor always is not in use (because socket.socket_noproxy only exists after SocksProxy.monkeyPatch has run).
Step 1: Please describe your environment
main-fc3486d8f9e5196870aa451531cf4712a213ed02)--tor always)start-venv.shwith flags--tor always --no-dht. System tray (Trayicon plugin) enabled, X11 session withDISPLAY=:1.pystrayandpython-xlibinstalled in the venv.Step 2: Describe the problem
When starting EpixNet with
--tor always, the Trayicon plugin crashes the whole process at startup because the globalsocket.sockethas been replaced withsocks.socksocketbysrc/util/SocksProxy.py:monkeyPatch.pystray's xorg backend importsXlib, which then tries to open the X server connection through the SOCKS-patched socket. Unix-domain X11 sockets aren't supported by PySocks, and the TCP fallback fails because there's no destination configured for a local X server connection, so EpixNet exits.Steps to reproduce
DISPLAY=:1), set up the venv and install requirements.pystrayandpython-xlibare installed (they're pulled in for the system tray)../start-venv.sh(which launchespython3 epixnet.py --tor always --no-dht).Observed Results
EpixNet crashes at startup with the following traceback:
Root cause:
src/util/SocksProxy.py:monkeyPatchreplacessocket.socketwithsocks.socksocketglobally. Whenpystray._xorglater constructsXlib.display.Display()(both during import and insideIcon.__init__/Icon.run),Xlib.support.unix_connectcallssocket.socket(AF_UNIX, ...)to talk to/tmp/.X11-unix/X*. PySocks rejects the AF_UNIX address (PySocks doesn't support IPv6) and the TCP fallback tolocalhost:6001fails too (Invalid destination-connection (host, port) pair).Expected Results
When
--tor alwaysis set on a Linux desktop with a system tray, EpixNet should still start normally and show its tray icon. X11 connections to the local display should never be routed through the Tor SOCKS proxy. If the X display genuinely cannot be opened (truly headless), the Trayicon plugin should log a warning and continue without a tray icon instead of crashing the entire process.Suggested fix
In
plugins/Trayicon/TrayiconPlugin.py, before importing/initializingpystray, restore the original socket class (saved bySocksProxy.monkeyPatchassocket.socket_noproxy) and patch thesocketmodule reference insideXlib.support.unix_connect,Xlib.support.connect, andXlib.protocol.displaywith a shim whose.socketattribute issocket.socket_noproxy. This keeps the global SOCKS patch in place for normal network traffic but ensures Xlib's local X11 connections always use the unproxied socket. Also wrap the pystray initialization in a genericexcept Exceptionso a missing/unreachable display degrades gracefully instead of crashing EpixNet.Suggested patch
Applied to
plugins/Trayicon/TrayiconPlugin.py, insideActionsPlugin.main(), replacing the originaltry: import pystray ...block:What this does:
socket.socketwhilepystray(and transitivelyXlib) is imported, so the import-timeXlib.display.Display()call inpystray/_xorg.pysucceeds.socketshim into the loaded Xlib modules (Xlib.support.unix_connect,Xlib.support.connect,Xlib.protocol.display). The shim mirrors the realsocketmodule but exposessocket.socket_noproxyas its.socketclass. This is necessary becausepystray._xorg.Icon.__init__opens its ownXlib.display.Display()later, in the trayicon thread — long aftermain()returns — and that call must also bypass PySocks.socket.socket(=socks.socksocket) infinally, so all other EpixNet/Gevent network traffic continues to be routed through the Tor SOCKS proxy as before.except Exceptionaround the pystray import so a truly unavailable display (headless server, Wayland-only, etc.) just logs a warning and continues without a tray icon, instead of taking the whole process down.No other files are modified, and the change is a no-op when
--tor alwaysis not in use (becausesocket.socket_noproxyonly exists afterSocksProxy.monkeyPatchhas run).