Skip to content

Commit 784b48f

Browse files
authored
Add AF_UNIX named (pathname) stream socket support under NODERAWSOCKETS (emscripten-core#27424)
Named unix-domain sockets let POSIX code compiled to wasm participate in the host's unix-socket IPC as a first-class client or server: connect() to a daemon path (docker.sock, postgres/redis) or bind()+listen()+accept() a path that native tools connect into. This is a straight remap of the existing node:net TCP flow from (host, port) to (path), reusing the same readiness/poll seam so mio/tokio IPC types work unmodified. The socket path lives in the host filesystem (node owns it), so it only stays coherent with the program's own file syscalls - bind's parent directory, getsockname, unlink - when the program's FS is the host FS. The family is therefore gated behind NODERAWFS; requesting AF_UNIX otherwise returns EAFNOSUPPORT rather than half-working against a disjoint MEMFS namespace. - Address plumbing: add AF_UNIX and sockaddr_un to struct info; readSockaddr/ writeSockaddr encode/decode sun_path (pathname, unnamed, and Linux abstract '\0' names), and the socket syscalls pass unix paths verbatim rather than through the IP DNS layer. - createSocket accepts AF_UNIX only under NODERAWFS (datagram -> EPROTONOSUPPORT; the IPPROTO_TCP guard is INET-only, unix uses protocol 0). - Backend: synchronous bind reserving the filesystem entry (bind-time EADDRINUSE), server.listen on the bound handle, onconnection accept queue, connect-by-path through the shared wireConnection, and getsockname/getpeername reporting the tracked path. Bind prefers net.BoundSocket({ path }) when the node build exposes it (detected via 'isPipe' in BoundSocket.prototype), else falls back to the private pipe_wrap binding - mirroring the existing useBoundSocket() -> tcp_wrap seam. - Tests (NODERAWSOCKETS + NODERAWFS): self-contained named accept+echo (bind/ listen/getsockname/accept/getpeername/connect), connect-to-missing-path -> ENOENT, and rebind -> EADDRINUSE. All also build and run natively. socketpair()/UnixStream::pair() remains unimplemented (ENOSYS) for now.
1 parent 9e8fbbd commit 784b48f

12 files changed

Lines changed: 566 additions & 25 deletions

src/lib/libcore.js

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -822,14 +822,38 @@ addToLibrary({
822822
return str;
823823
},
824824

825-
$readSockaddr__deps: ['$inetNtop4', '$inetNtop6', 'ntohs'],
825+
$readSockaddr__deps: ['$inetNtop4', '$inetNtop6', 'ntohs'
826+
#if NODERAWSOCKETS
827+
, '$UTF8ToString'
828+
#endif
829+
],
826830
$readSockaddr: (sa, salen) => {
827831
// family / port offsets are common to both sockaddr_in and sockaddr_in6
828832
var family = {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_family, 'i16') }}};
829833
var port = _ntohs({{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_port, 'u16') }}});
830834
var addr;
831835

832836
switch (family) {
837+
#if NODERAWSOCKETS
838+
case {{{ cDefs.AF_UNIX }}}: {
839+
// sun_path runs from offsetof(sun_path) to salen. An address of only the
840+
// family (salen <= offset) is the unnamed/autobind case -> empty path. A
841+
// leading NUL marks the Linux abstract namespace; keep the NUL so the
842+
// path round-trips and never collides with a filesystem path.
843+
var pathStart = sa + {{{ C_STRUCTS.sockaddr_un.sun_path }}};
844+
var pathLen = salen - {{{ C_STRUCTS.sockaddr_un.sun_path }}};
845+
var path = '';
846+
if (pathLen > 0) {
847+
if (HEAPU8[pathStart] === 0) {
848+
path = '\0' + UTF8ToString(pathStart + 1, pathLen - 1, /*ignoreNul=*/true);
849+
} else {
850+
// A pathname address is NUL-terminated; stop at the first NUL.
851+
path = UTF8ToString(pathStart, pathLen);
852+
}
853+
}
854+
return { family, addr: path, port: 0 };
855+
}
856+
#endif
833857
case {{{ cDefs.AF_INET }}}:
834858
if (salen !== {{{ C_STRUCTS.sockaddr_in.__size__ }}}) {
835859
return { errno: {{{ cDefs.EINVAL }}} };
@@ -856,11 +880,44 @@ addToLibrary({
856880
return { family: family, addr: addr, port: port };
857881
},
858882
$writeSockaddr__docs: '/** @param {number=} addrlen */',
859-
$writeSockaddr__deps: ['$inetPton4', '$inetPton6', '$zeroMemory', 'htons'],
883+
$writeSockaddr__deps: ['$inetPton4', '$inetPton6', '$zeroMemory', '$DNS', 'htons'
884+
#if NODERAWSOCKETS
885+
, '$lengthBytesUTF8', '$stringToUTF8'
886+
#endif
887+
],
860888
$writeSockaddr: (sa, family, addr, port, addrlen) => {
861889
switch (family) {
890+
#if NODERAWSOCKETS
891+
case {{{ cDefs.AF_UNIX }}}: {
892+
// addr is the JS path string produced by readSockaddr: a leading '\0'
893+
// marks the abstract namespace (its bytes are written verbatim, no NUL
894+
// terminator), any other path is written NUL-terminated. An empty path
895+
// is the unnamed address (family only).
896+
addr ||= '';
897+
var abstract = addr.charCodeAt(0) === 0;
898+
// Pathname addresses include the trailing NUL in the reported length;
899+
// abstract addresses do not; an empty address is family-only (unnamed).
900+
var bytes = addr ? lengthBytesUTF8(addr) + (abstract ? 0 : 1) : 0;
901+
var total = {{{ C_STRUCTS.sockaddr_un.sun_path }}} + bytes;
902+
zeroMemory(sa, total);
903+
{{{ makeSetValue('sa', C_STRUCTS.sockaddr_un.sun_family, 'family', 'i16') }}};
904+
if (addr) {
905+
// stringToUTF8 NUL-terminates and needs room for it, so hand it a
906+
// budget one past the path bytes; the terminating NUL lands on the
907+
// zeroed byte just past the reported length and is harmless.
908+
stringToUTF8(addr, sa + {{{ C_STRUCTS.sockaddr_un.sun_path }}}, bytes + 1);
909+
}
910+
if (addrlen) {
911+
{{{ makeSetValue('addrlen', 0, 'total', 'i32') }}};
912+
}
913+
break;
914+
}
915+
#endif
862916
case {{{ cDefs.AF_INET }}}:
863-
addr = inetPton4(addr);
917+
// The address may still be an unresolved hostname (e.g. a peer name
918+
// recorded at connect time); map it to its (possibly fake) IP here so
919+
// callers can pass names and IPs alike.
920+
addr = inetPton4(DNS.lookup_name(addr));
864921
zeroMemory(sa, {{{ C_STRUCTS.sockaddr_in.__size__ }}});
865922
if (addrlen) {
866923
{{{ makeSetValue('addrlen', 0, C_STRUCTS.sockaddr_in.__size__, 'i32') }}};
@@ -870,7 +927,7 @@ addToLibrary({
870927
{{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_port, '_htons(port)', 'i16') }}};
871928
break;
872929
case {{{ cDefs.AF_INET6 }}}:
873-
addr = inetPton6(addr);
930+
addr = inetPton6(DNS.lookup_name(addr));
874931
zeroMemory(sa, {{{ C_STRUCTS.sockaddr_in6.__size__ }}});
875932
if (addrlen) {
876933
{{{ makeSetValue('addrlen', 0, C_STRUCTS.sockaddr_in6.__size__, 'i32') }}};

src/lib/libsockfs.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ addToLibrary({
6464
#if NODERAWSOCKETS
6565
// The node:net backend supports IPv6; other backends are IPv4 only.
6666
&& family != {{{ cDefs.AF_INET6 }}}
67+
#if NODERAWFS
68+
// AF_UNIX stream sockets are backed by node's named pipes, and their
69+
// paths live in the host filesystem. That only stays coherent with the
70+
// program's own file syscalls (bind's parent dir, getsockname, unlink)
71+
// under NODERAWFS, so the family is gated behind it - MEMFS paths are a
72+
// disjoint namespace and would fail confusingly.
73+
&& family != {{{ cDefs.AF_UNIX }}}
74+
#endif
6775
#endif
6876
) {
6977
throw new FS.ErrnoError({{{ cDefs.EAFNOSUPPORT }}});
@@ -73,8 +81,20 @@ addToLibrary({
7381
if (type != {{{ cDefs.SOCK_STREAM }}} && type != {{{ cDefs.SOCK_DGRAM }}}) {
7482
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
7583
}
84+
#if NODERAWSOCKETS && NODERAWFS
85+
// node has no AF_UNIX datagram primitive; only stream unix sockets exist.
86+
if (family == {{{ cDefs.AF_UNIX }}} && type != {{{ cDefs.SOCK_STREAM }}}) {
87+
throw new FS.ErrnoError({{{ cDefs.EPROTONOSUPPORT }}});
88+
}
89+
#endif
7690
var streaming = type == {{{ cDefs.SOCK_STREAM }}};
77-
if (streaming && protocol && protocol != {{{ cDefs.IPPROTO_TCP }}}) {
91+
// The IPPROTO_TCP protocol guard only applies to INET stream sockets; unix
92+
// stream sockets use protocol 0.
93+
if (streaming && protocol && protocol != {{{ cDefs.IPPROTO_TCP }}}
94+
#if NODERAWSOCKETS && NODERAWFS
95+
&& family != {{{ cDefs.AF_UNIX }}}
96+
#endif
97+
) {
7898
throw new FS.ErrnoError({{{ cDefs.EPROTONOSUPPORT }}}); // if SOCK_STREAM, must be tcp or 0.
7999
}
80100

src/lib/libsockfs_node.js

Lines changed: 98 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,54 @@ var NodeSockFSLibrary = {
156156
try { handle.close(); } catch (e) {}
157157
throw new FS.ErrnoError(nodeSockHelpers.codeToErrno(code));
158158
},
159+
// AF_UNIX stream sockets bind through net.BoundSocket when node offers a
160+
// path-capable one, else the private pipe_wrap binding - the same
161+
// public-then-private choice bindHandle() makes for TCP (BoundSocket else
162+
// tcp_wrap). The capability signal is the presence of the `isPipe` accessor
163+
// on the prototype (a { path } bind reports isPipe true). Chosen once, like
164+
// useBoundSocket().
165+
useBoundPipe() {
166+
var BoundSocket = nodeSockHelpers.getNet().BoundSocket;
167+
return nodeSockHelpers.boundPipeOk ??= !!(BoundSocket && 'isPipe' in BoundSocket.prototype);
168+
},
169+
getPipe() {
170+
if (!nodeSockHelpers.pipeModule) {
171+
try {
172+
nodeSockHelpers.pipeModule = process.binding('pipe_wrap');
173+
} catch (e) {
174+
throw new FS.ErrnoError({{{ cDefs.EOPNOTSUPP }}});
175+
}
176+
}
177+
return nodeSockHelpers.pipeModule;
178+
},
179+
// Synchronously bind an AF_UNIX stream socket to a filesystem path,
180+
// reserving the entry (EADDRINUSE if already in use) exactly when POSIX
181+
// bind() would, and record the path. sock.bound is the resulting bound
182+
// handle - a net.BoundSocket or a raw pipe_wrap Pipe - adopted as-is by
183+
// listen() (server.listen). node reports no name back for a pipe, so the
184+
// path we store here is authoritative for getsockname().
185+
bindPipe(sock, path) {
186+
if (nodeSockHelpers.useBoundPipe()) {
187+
// The constructor binds synchronously and throws a conflict
188+
// (EADDRINUSE etc.) right here, matching POSIX bind().
189+
try {
190+
var bh = new (nodeSockHelpers.getNet().BoundSocket)({ path });
191+
}
192+
catch (e) { throw new FS.ErrnoError(nodeSockHelpers.nodeErrToErrno(e)); }
193+
sock.bound = bh;
194+
sock.saddr = path;
195+
return;
196+
}
197+
var pipe = nodeSockHelpers.getPipe();
198+
var handle = new pipe.Pipe(pipe.constants.SERVER);
199+
var code = handle.bind(path);
200+
if (code) {
201+
try { handle.close(); } catch (e) {}
202+
throw new FS.ErrnoError(nodeSockHelpers.codeToErrno(code));
203+
}
204+
sock.bound = handle;
205+
sock.saddr = path;
206+
},
159207
// The peer address is already a numeric IP (emscripten resolves names in
160208
// its own DNS layer), so skip node's async DNS lookup. The family follows
161209
// the literal: a colon means IPv6.
@@ -432,6 +480,13 @@ var NodeSockFSLibrary = {
432480
if (sock.saddr !== undefined || sock.sport !== undefined) {
433481
throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); // already bound
434482
}
483+
if (sock.family === {{{ cDefs.AF_UNIX }}}) {
484+
// addr is a filesystem path (or an abstract '\0...' name). Bind
485+
// synchronously so EADDRINUSE surfaces here and getsockname() works.
486+
nodeSockHelpers.bindPipe(sock, addr);
487+
sock.state = {{{ SOCK_STATE_BOUND }}};
488+
return;
489+
}
435490
if (sock.type === {{{ cDefs.SOCK_DGRAM }}}) {
436491
var udp = nodeSockHelpers.ensureUdpHandle(sock);
437492
if (sock.udpPublic) {
@@ -462,6 +517,29 @@ var NodeSockFSLibrary = {
462517
sock.state = {{{ SOCK_STATE_BOUND }}};
463518
},
464519
connect(sock, addr, port) {
520+
if (sock.family === {{{ cDefs.AF_UNIX }}}) {
521+
if (sock.server) throw new FS.ErrnoError({{{ cDefs.EOPNOTSUPP }}});
522+
if (sock.connection) {
523+
throw new FS.ErrnoError(sock.state === {{{ SOCK_STATE_CONNECTING }}} ? {{{ cDefs.EALREADY }}} : {{{ cDefs.EISCONN }}});
524+
}
525+
// addr is the peer path. node reports no name back, so record it as the
526+
// peer name ourselves; the local end is unnamed unless bind() named it.
527+
sock.daddr = addr;
528+
sock.state = {{{ SOCK_STATE_CONNECTING }}};
529+
var uconn = new (nodeSockHelpers.getNet().Socket)({ allowHalfOpen: true });
530+
uconn.once('connect', () => {
531+
sock.state = {{{ SOCK_STATE_CONNECTED }}};
532+
sock.saddr ??= '';
533+
try { uconn.resume(); } catch (e) {}
534+
SOCKFS.emit('open', sock.stream.fd);
535+
});
536+
// A missing path surfaces as ENOENT and a non-socket path as
537+
// ECONNREFUSED, both through wireConnection's 'error' handler and the
538+
// same SO_ERROR/poll seam as TCP.
539+
nodeSockHelpers.wireConnection(sock, uconn);
540+
uconn.connect({ path: addr });
541+
return;
542+
}
465543
if (sock.type === {{{ cDefs.SOCK_DGRAM }}}) {
466544
sock.daddr = addr;
467545
sock.dport = port;
@@ -521,10 +599,15 @@ var NodeSockFSLibrary = {
521599
if (sock.type !== {{{ cDefs.SOCK_STREAM }}}) throw new FS.ErrnoError({{{ cDefs.EOPNOTSUPP }}}); // not a stream socket
522600
if (sock.server) throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); // already listening
523601
if (sock.connection) throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); // a connected socket cannot listen
524-
// POSIX listen without a prior bind auto-binds an ephemeral port. The bind
525-
// is eager and synchronous (bindHandle), so the assigned port is known and
526-
// any conflict surfaces before we listen.
527-
if (!sock.bound) {
602+
// AF_UNIX has no autobind for listen(): the socket must have been named by
603+
// a prior bind() (which produced the bound Pipe handle we listen on).
604+
var isUnix = sock.family === {{{ cDefs.AF_UNIX }}};
605+
if (isUnix) {
606+
if (!sock.bound) throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
607+
} else if (!sock.bound) {
608+
// POSIX listen without a prior bind auto-binds an ephemeral port. The
609+
// bind is eager and synchronous (bindHandle), so the assigned port is
610+
// known and any conflict surfaces before we listen.
528611
nodeSockHelpers.bindHandle(sock, '0.0.0.0', 0);
529612
sock.state = {{{ SOCK_STATE_BOUND }}};
530613
}
@@ -534,10 +617,17 @@ var NodeSockFSLibrary = {
534617
server.on('connection', (conn) => {
535618
var newsock = SOCKFS.createSocket(sock.family, sock.type, sock.protocol);
536619
newsock.state = {{{ SOCK_STATE_CONNECTED }}};
537-
newsock.saddr = conn.localAddress;
538-
newsock.sport = conn.localPort;
539-
newsock.daddr = conn.remoteAddress;
540-
newsock.dport = conn.remotePort;
620+
if (isUnix) {
621+
// node reports no name for a pipe: the accepted socket's local name is
622+
// the listener's path, the peer is unnamed (the client rarely binds).
623+
newsock.saddr = sock.saddr;
624+
newsock.daddr = '';
625+
} else {
626+
newsock.saddr = conn.localAddress;
627+
newsock.sport = conn.localPort;
628+
newsock.daddr = conn.remoteAddress;
629+
newsock.dport = conn.remotePort;
630+
}
541631
nodeSockHelpers.wireConnection(newsock, conn);
542632
try { conn.resume(); } catch (e) {} // paused by pauseOnConnect
543633
sock.pending.push(newsock);

src/lib/libsyscall.js

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,10 @@ var SyscallsLibrary = {
344344
$getSocketAddress: (addrp, addrlen) => {
345345
var info = readSockaddr(addrp, addrlen);
346346
if (info.errno) throw new FS.ErrnoError(info.errno);
347+
#if NODERAWSOCKETS
348+
// AF_UNIX addresses are filesystem paths, not IP names; pass them verbatim.
349+
if (info.family != {{{ cDefs.AF_UNIX }}})
350+
#endif
347351
info.addr = DNS.lookup_addr(info.addr) || info.addr;
348352
#if SYSCALL_DEBUG
349353
dbg(` (socketaddress: "${[info.addr, info.port]}")`);
@@ -355,23 +359,36 @@ var SyscallsLibrary = {
355359
var sock = SOCKFS.createSocket(domain, type, protocol);
356360
return sock.stream.fd;
357361
},
358-
__syscall_getsockname__deps: ['$getSocketFromFD', '$writeSockaddr', '$DNS'],
362+
__syscall_getsockname__deps: ['$getSocketFromFD', '$writeSockaddr'],
359363
__syscall_getsockname: (fd, addr, len, u1, u2, u3) => {
360364
var sock = getSocketFromFD(fd);
365+
#if NODERAWSOCKETS
366+
// An unbound AF_UNIX socket is unnamed (family-only address), not a
367+
// wildcard IP.
368+
var defaultAddr = sock.family == {{{ cDefs.AF_UNIX }}} ? '' : '0.0.0.0';
369+
#else
370+
var defaultAddr = '0.0.0.0';
371+
#endif
361372
// TODO: sock.saddr should never be undefined, see TODO in websocket_sock_ops.getname
362-
var errno = writeSockaddr(addr, sock.family, DNS.lookup_name(sock.saddr || '0.0.0.0'), sock.sport, len);
373+
var errno = writeSockaddr(addr, sock.family, sock.saddr || defaultAddr, sock.sport, len);
363374
#if ASSERTIONS
364375
assert(!errno);
365376
#endif
366377
return 0;
367378
},
368-
__syscall_getpeername__deps: ['$getSocketFromFD', '$writeSockaddr', '$DNS'],
379+
__syscall_getpeername__deps: ['$getSocketFromFD', '$writeSockaddr'],
369380
__syscall_getpeername: (fd, addr, len, u1, u2, u3) => {
370381
var sock = getSocketFromFD(fd);
382+
#if NODERAWSOCKETS
383+
// '' is a valid connected-but-unnamed AF_UNIX peer (e.g. an accepted
384+
// connection's client), so only undefined means not connected there.
385+
if (sock.family == {{{ cDefs.AF_UNIX }}} ? sock.daddr === undefined : !sock.daddr) {
386+
#else
371387
if (!sock.daddr) {
388+
#endif
372389
return -{{{ cDefs.ENOTCONN }}}; // The socket is not connected.
373390
}
374-
var errno = writeSockaddr(addr, sock.family, DNS.lookup_name(sock.daddr), sock.dport, len);
391+
var errno = writeSockaddr(addr, sock.family, sock.daddr, sock.dport, len);
375392
#if ASSERTIONS
376393
assert(!errno);
377394
#endif
@@ -393,12 +410,12 @@ var SyscallsLibrary = {
393410
return -{{{ cDefs.ENOSYS }}}; // unsupported feature
394411
#endif
395412
},
396-
__syscall_accept4__deps: ['$getSocketFromFD', '$writeSockaddr', '$DNS'],
413+
__syscall_accept4__deps: ['$getSocketFromFD', '$writeSockaddr'],
397414
__syscall_accept4: (fd, addr, len, flags, u1, u2) => {
398415
var sock = getSocketFromFD(fd);
399416
var newsock = sock.sock_ops.accept(sock);
400417
if (addr) {
401-
var errno = writeSockaddr(addr, newsock.family, DNS.lookup_name(newsock.daddr), newsock.dport, len);
418+
var errno = writeSockaddr(addr, newsock.family, newsock.daddr, newsock.dport, len);
402419
#if ASSERTIONS
403420
assert(!errno);
404421
#endif
@@ -418,13 +435,13 @@ var SyscallsLibrary = {
418435
sock.sock_ops.listen(sock, backlog);
419436
return 0;
420437
},
421-
__syscall_recvfrom__deps: ['$getSocketFromFD', '$writeSockaddr', '$DNS'],
438+
__syscall_recvfrom__deps: ['$getSocketFromFD', '$writeSockaddr'],
422439
__syscall_recvfrom: (fd, buf, len, flags, addr, alen) => {
423440
var sock = getSocketFromFD(fd);
424441
var msg = sock.sock_ops.recvmsg(sock, len, flags);
425442
if (!msg) return 0; // socket is closed
426443
if (addr) {
427-
var errno = writeSockaddr(addr, sock.family, DNS.lookup_name(msg.addr), msg.port, alen);
444+
var errno = writeSockaddr(addr, sock.family, msg.addr, msg.port, alen);
428445
#if ASSERTIONS
429446
assert(!errno);
430447
#endif
@@ -507,7 +524,7 @@ var SyscallsLibrary = {
507524
// write the buffer
508525
return sock.sock_ops.sendmsg(sock, view, 0, total, addr, port);
509526
},
510-
__syscall_recvmsg__deps: ['$getSocketFromFD', '$writeSockaddr', '$DNS'],
527+
__syscall_recvmsg__deps: ['$getSocketFromFD', '$writeSockaddr'],
511528
__syscall_recvmsg: (fd, message, flags, u1, u2, u3) => {
512529
var sock = getSocketFromFD(fd);
513530
var iov = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_iov, '*') }}};
@@ -531,7 +548,7 @@ var SyscallsLibrary = {
531548
var name = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_name, '*') }}};
532549
if (name) {
533550
var namelen = message + {{{ C_STRUCTS.msghdr.msg_namelen }}};
534-
var errno = writeSockaddr(name, sock.family, DNS.lookup_name(msg.addr), msg.port, namelen);
551+
var errno = writeSockaddr(name, sock.family, msg.addr, msg.port, namelen);
535552
#if ASSERTIONS
536553
assert(!errno);
537554
#endif

src/struct_info.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,21 @@
300300
"AF_INET",
301301
"AF_UNSPEC",
302302
"AF_INET6",
303+
"AF_UNIX",
303304
"SOL_SOCKET",
304305
"SO_ERROR",
305306
"SO_REUSEPORT"
306307
]
307308
},
309+
{
310+
"file": "sys/un.h",
311+
"structs": {
312+
"sockaddr_un": [
313+
"sun_family",
314+
"sun_path"
315+
]
316+
}
317+
},
308318
{
309319
"file": "bits/ioctl.h",
310320
"defines": [

0 commit comments

Comments
 (0)