authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-08 20:47:54-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-09-08 20:47:54-07:00
log3071ba4272c2a89e892c6a48090496ec0ba46d8c
treed6e2b78d0f9bfeb0797c9188206e8284f0705d88
parent1872c85ac25146036119387d1f376e2cba3ff7be
parentc4c6c01cc5b89a2647702b6849979572dcb74eab
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #25190 from blblack/netcalls

Add missing posix wrappers for socketpair() and recvmsg()

2 files changed, 110 insertions(+), 2 deletions(-)

lib/std/c.zig+17-2
......@@ -10599,6 +10599,12 @@ pub const socket = switch (native_os) {
1059910599 else => private.socket,
1060010600};
1060110601
10602pub const socketpair = switch (native_os) {
10603 // https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/#unsupported\unavailable:
10604 .windows => void,
10605 else => private.socketpair,
10606};
10607
1060210608pub const stat = switch (native_os) {
1060310609 .macos => switch (native_arch) {
1060410610 .x86_64 => private.@"stat$INODE64",
......@@ -10740,7 +10746,6 @@ pub extern "c" fn uname(buf: *utsname) c_int;
1074010746pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int;
1074110747pub extern "c" fn shutdown(socket: fd_t, how: c_int) c_int;
1074210748pub extern "c" fn bind(socket: fd_t, address: ?*const sockaddr, address_len: socklen_t) c_int;
10743pub extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint, sv: *[2]fd_t) c_int;
1074410749pub extern "c" fn listen(sockfd: fd_t, backlog: c_uint) c_int;
1074510750pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int;
1074610751pub extern "c" fn getpeername(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int;
......@@ -10774,7 +10779,15 @@ pub extern "c" fn recvfrom(
1077410779 noalias src_addr: ?*sockaddr,
1077510780 noalias addrlen: ?*socklen_t,
1077610781) if (native_os == .windows) c_int else isize;
10777pub extern "c" fn recvmsg(sockfd: fd_t, msg: *msghdr, flags: u32) isize;
10782
10783pub const recvmsg = switch (native_os) {
10784 // Windows: Technically, a form of recvmsg() exists for Windows, but the
10785 // user has to install some kind of callback for it. I'm not sure if/how
10786 // we can map this to normal recvmsg() interface use.
10787 // https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nc-mswsock-lpfn_wsarecvmsg
10788 .windows => void,
10789 else => private.recvmsg,
10790};
1077810791
1077910792pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;
1078010793
......@@ -11419,6 +11432,7 @@ const private = struct {
1141911432 extern "c" fn pipe2(fds: *[2]fd_t, flags: O) c_int;
1142011433 extern "c" fn readdir(dir: *DIR) ?*dirent;
1142111434 extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8;
11435 extern "c" fn recvmsg(sockfd: fd_t, msg: *msghdr, flags: u32) isize;
1142211436 extern "c" fn sched_yield() c_int;
1142311437 extern "c" fn sendfile(out_fd: fd_t, in_fd: fd_t, offset: ?*off_t, count: usize) isize;
1142411438 extern "c" fn sigaction(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int;
......@@ -11429,6 +11443,7 @@ const private = struct {
1142911443 extern "c" fn sigismember(set: ?*const sigset_t, signo: c_int) c_int;
1143011444 extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;
1143111445 extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;
11446 extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint, sv: *[2]fd_t) c_int;
1143211447 extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int;
1143311448 extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
1143411449 extern "c" fn sysconf(sc: c_int) c_long;
lib/std/posix.zig+93
......@@ -3671,6 +3671,46 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t
36713671 }
36723672}
36733673
3674pub fn socketpair(domain: u32, socket_type: u32, protocol: u32) SocketError![2]socket_t {
3675 // Note to the future: we could provide a shim here for e.g. windows which
3676 // creates a listening socket, then creates a second socket and connects it
3677 // to the listening socket, and then returns the two.
3678 if (@TypeOf(system.socketpair) == void)
3679 @compileError("socketpair() not supported by this OS");
3680
3681 // I'm not really sure if haiku supports flags here. I'm following the
3682 // existing filter here from pipe2(), because it sure seems like it
3683 // supports flags there too, but haiku can be hard to understand.
3684 const have_sock_flags = !builtin.target.os.tag.isDarwin() and native_os != .haiku;
3685 const filtered_sock_type = if (!have_sock_flags)
3686 socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC)
3687 else
3688 socket_type;
3689 var socks: [2]socket_t = undefined;
3690 const rc = system.socketpair(domain, filtered_sock_type, protocol, &socks);
3691 switch (errno(rc)) {
3692 .SUCCESS => {
3693 errdefer close(socks[0]);
3694 errdefer close(socks[1]);
3695 if (!have_sock_flags) {
3696 try setSockFlags(socks[0], socket_type);
3697 try setSockFlags(socks[1], socket_type);
3698 }
3699 return socks;
3700 },
3701 .ACCES => return error.AccessDenied,
3702 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
3703 .INVAL => return error.ProtocolFamilyNotAvailable,
3704 .MFILE => return error.ProcessFdQuotaExceeded,
3705 .NFILE => return error.SystemFdQuotaExceeded,
3706 .NOBUFS => return error.SystemResources,
3707 .NOMEM => return error.SystemResources,
3708 .PROTONOSUPPORT => return error.ProtocolNotSupported,
3709 .PROTOTYPE => return error.SocketTypeNotSupported,
3710 else => |err| return unexpectedErrno(err),
3711 }
3712}
3713
36743714pub const ShutdownError = error{
36753715 ConnectionAborted,
36763716
......@@ -6604,6 +6644,9 @@ pub const RecvFromError = error{
66046644
66056645 /// The socket is not connected (connection-oriented sockets only).
66066646 SocketNotConnected,
6647
6648 /// The other end closed the socket unexpectedly or a read is executed on a shut down socket
6649 BrokenPipe,
66076650} || UnexpectedError;
66086651
66096652pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize {
......@@ -6652,12 +6695,62 @@ pub fn recvfrom(
66526695 .CONNREFUSED => return error.ConnectionRefused,
66536696 .CONNRESET => return error.ConnectionResetByPeer,
66546697 .TIMEDOUT => return error.ConnectionTimedOut,
6698 .PIPE => return error.BrokenPipe,
66556699 else => |err| return unexpectedErrno(err),
66566700 }
66576701 }
66586702 }
66596703}
66606704
6705pub const RecvMsgError = RecvFromError || error{
6706 /// Reception of SCM_RIGHTS fds via ancillary data in msg.control would
6707 /// exceed some system limit (generally this is retryable by trying to
6708 /// receive fewer fds or closing some existing fds)
6709 SystemFdQuotaExceeded,
6710
6711 /// Reception of SCM_RIGHTS fds via ancillary data in msg.control would
6712 /// exceed some process limit (generally this is retryable by trying to
6713 /// receive fewer fds, closing some existing fds, or changing the ulimit)
6714 ProcessFdQuotaExceeded,
6715};
6716
6717/// If `sockfd` is opened in non blocking mode, the function will
6718/// return error.WouldBlock when EAGAIN is received.
6719pub fn recvmsg(
6720 /// The file descriptor of the sending socket.
6721 sockfd: socket_t,
6722 /// Message header and iovecs
6723 msg: *msghdr,
6724 flags: u32,
6725) RecvMsgError!usize {
6726 if (@TypeOf(system.recvmsg) == void)
6727 @compileError("recvmsg() not supported on this OS");
6728 while (true) {
6729 const rc = system.recvmsg(sockfd, msg, flags);
6730 switch (errno(rc)) {
6731 .SUCCESS => return @intCast(rc),
6732 .AGAIN => return error.WouldBlock,
6733 .BADF => unreachable, // always a race condition
6734 .NFILE => return error.SystemFdQuotaExceeded,
6735 .MFILE => return error.ProcessFdQuotaExceeded,
6736 .INTR => continue,
6737 .FAULT => unreachable, // An invalid user space address was specified for an argument.
6738 .INVAL => unreachable, // Invalid argument passed.
6739 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
6740 .NOBUFS => return error.SystemResources,
6741 .NOMEM => return error.SystemResources,
6742 .NOTCONN => return error.SocketNotConnected,
6743 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
6744 .MSGSIZE => return error.MessageTooBig,
6745 .PIPE => return error.BrokenPipe,
6746 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
6747 .CONNRESET => return error.ConnectionResetByPeer,
6748 .NETDOWN => return error.NetworkSubsystemFailed,
6749 else => |err| return unexpectedErrno(err),
6750 }
6751 }
6752}
6753
66616754pub const DnExpandError = error{InvalidDnsPacket};
66626755
66636756pub fn dn_expand(