Skip to content

Instantly share code, notes, and snippets.

Created April 24, 2023 22:14
Show Gist options
  • Save kousu/fcc61e963348784a780f0ba8c9d9da90 to your computer and use it in GitHub Desktop.
Save kousu/fcc61e963348784a780f0ba8c9d9da90 to your computer and use it in GitHub Desktop.
Prototype a workaround for unix domain socket length limit of 107.
#!/usr/bin/env python
# this is a tiny little clone of `nc -U -l path`
# but it creates the socket *after chdir()ing in a subprocess* to avoid ENAMETOOLONG:
# To test the difference, get openbsd-netcat and run:
# mkdir -p /tmp/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz
# nc -v -U -l /tmp/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz/ipc.sock
# This will fail with "nc: File name too long", but
# ./nc-chdir /tmp/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz/ipc.sock
# starts, and can be communicated with by
# (cd /tmp/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz/abcdefhijklmnopqrstuvwxyz/ && nc -v -U ipc.sock)
import sys, os
import socket, select
import traceback
def reliable_bind(socket, path):
This is a wrapper for socket.socket.bind(), with the same API.
But it works for paths of (almost) any length rather than being
limited to about 100:
(this will still fail if len(basename(path)) > 100, or thereabouts; just don't do that)
o = socket.get_inheritable()
socket.set_inheritable(True) # this is a pythonism, to ensure the parent and child can share file descriptors
child = os.fork()
if child == 0:
# child process
# in the subprocess, chdir() to the desired path
# so that we only need to bind() on basename(path);
# so long as the actual filename is not too long,
# this should succeed.
# The reason for using a child process is so that we can call chdir() -- which sets a global variable -- without having to worry about concurrency,
# or undoing the chdir() afterwards -- when the child process dies the chdir() is forgotten, but the socket is left bound correctly.
# On Linux at least(?) this bind() is picked up correctly on the matching file descriptor in the parent.
except OSError as exc:
# assume that most of the errors are OSErrors
# and pass them back via the process's exit code
raise SystemExit(exc.errno) # error
except Exception as exc:
# but in really exceptional circumstances (low memory? corrupt process code?)
# encode that as code 255, which should be greater than any defined errno
raise SystemExit(255)
raise SystemExit(0) # success
# parent process
_, status = os.waitpid(child, 0)
errno = os.waitstatus_to_exitcode(status)
if errno != 0:
if errno == 255:
raise Exception("Unknown error occurred in child")
raise OSError(errno, os.strerror(errno))
#reliable_bind = socket.socket.bind # no subdir
def main():
addr = sys.argv[1]
s = socket.socket(socket.AF_UNIX)
reliable_bind(s, addr)
print("Bound on", s.getsockname())
print("Listening on", s.getsockname())
S, remote = s.accept()
print("Connection received on ", S.getsockname())
# XXX 'remote' is empty; on linux, to find out who is talking to you, you have to use recvmsg() and look at the "acnillary data":
while True:
readable, _, _ =[S, sys.stdin], [], [])
for fd in readable:
if fd == S:
buf = S.recv(512)
if not buf:
# socket disconnected
sys.stdout.buffer.raw.write(buf) # TODO: .buffer.raw?? should I just use os.write(sys.stdout.fileno())??
elif fd == sys.stdin:
buf = # TODO: ditto
if not buf:
except KeyboardInterrupt:
os.unlink(addr) # TODO: is there a 'with:' for unix domain sockets?
if __name__ == '__main__':
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment