diff --git a/lib/std/c.zig b/lib/std/c.zig index 7ce7f2899c..c9d4265c74 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -10779,7 +10779,15 @@ pub extern "c" fn recvfrom( noalias src_addr: ?*sockaddr, noalias addrlen: ?*socklen_t, ) if (native_os == .windows) c_int else isize; -pub extern "c" fn recvmsg(sockfd: fd_t, msg: *msghdr, flags: u32) isize; + +pub const recvmsg = switch (native_os) { + // Windows: Technically, a form of recvmsg() exists for Windows, but the + // user has to install some kind of callback for it. I'm not sure if/how + // we can map this to normal recvmsg() interface use. + // https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nc-mswsock-lpfn_wsarecvmsg + .windows => void, + else => private.recvmsg, +}; pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int; @@ -11424,6 +11432,7 @@ const private = struct { extern "c" fn pipe2(fds: *[2]fd_t, flags: O) c_int; extern "c" fn readdir(dir: *DIR) ?*dirent; extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; + extern "c" fn recvmsg(sockfd: fd_t, msg: *msghdr, flags: u32) isize; extern "c" fn sched_yield() c_int; extern "c" fn sendfile(out_fd: fd_t, in_fd: fd_t, offset: ?*off_t, count: usize) isize; extern "c" fn sigaction(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int; diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 6f7c095870..e0fd68db24 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -6644,6 +6644,9 @@ pub const RecvFromError = error{ /// The socket is not connected (connection-oriented sockets only). SocketNotConnected, + + /// The other end closed the socket unexpectedly or a read is executed on a shut down socket + BrokenPipe, } || UnexpectedError; pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize { @@ -6692,12 +6695,62 @@ pub fn recvfrom( .CONNREFUSED => return error.ConnectionRefused, .CONNRESET => return error.ConnectionResetByPeer, .TIMEDOUT => return error.ConnectionTimedOut, + .PIPE => return error.BrokenPipe, else => |err| return unexpectedErrno(err), } } } } +pub const RecvMsgError = RecvFromError || error{ + /// Reception of SCM_RIGHTS fds via ancillary data in msg.control would + /// exceed some system limit (generally this is retryable by trying to + /// receive fewer fds or closing some existing fds) + SystemFdQuotaExceeded, + + /// Reception of SCM_RIGHTS fds via ancillary data in msg.control would + /// exceed some process limit (generally this is retryable by trying to + /// receive fewer fds, closing some existing fds, or changing the ulimit) + ProcessFdQuotaExceeded, +}; + +/// If `sockfd` is opened in non blocking mode, the function will +/// return error.WouldBlock when EAGAIN is received. +pub fn recvmsg( + /// The file descriptor of the sending socket. + sockfd: socket_t, + /// Message header and iovecs + msg: *msghdr, + flags: u32, +) RecvMsgError!usize { + if (@TypeOf(system.recvmsg) == void) + @compileError("recvmsg() not supported on this OS"); + while (true) { + const rc = system.recvmsg(sockfd, msg, flags); + switch (errno(rc)) { + .SUCCESS => return @intCast(rc), + .AGAIN => return error.WouldBlock, + .BADF => unreachable, // always a race condition + .NFILE => return error.SystemFdQuotaExceeded, + .MFILE => return error.ProcessFdQuotaExceeded, + .INTR => continue, + .FAULT => unreachable, // An invalid user space address was specified for an argument. + .INVAL => unreachable, // Invalid argument passed. + .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .NOTCONN => return error.SocketNotConnected, + .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + .MSGSIZE => return error.MessageTooBig, + .PIPE => return error.BrokenPipe, + .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type. + .CONNRESET => return error.ConnectionResetByPeer, + .NETDOWN => return error.NetworkSubsystemFailed, + else => |err| return unexpectedErrno(err), + } + } +} + pub const DnExpandError = error{InvalidDnsPacket}; pub fn dn_expand(