Commit 1f17221bc4

daurnimator <quae@daurnimator.com>
2021-02-27 04:19:38
std: add sendmsg
1 parent 294ee1b
Changed files (5)
lib/std/os/bits/linux/arm64.zig
@@ -400,10 +400,10 @@ pub const msghdr = extern struct {
     msg_namelen: socklen_t,
     msg_iov: [*]iovec,
     msg_iovlen: i32,
-    __pad1: i32,
+    __pad1: i32 = 0,
     msg_control: ?*c_void,
     msg_controllen: socklen_t,
-    __pad2: socklen_t,
+    __pad2: socklen_t = 0,
     msg_flags: i32,
 };
 
@@ -412,10 +412,10 @@ pub const msghdr_const = extern struct {
     msg_namelen: socklen_t,
     msg_iov: [*]iovec_const,
     msg_iovlen: i32,
-    __pad1: i32,
+    __pad1: i32 = 0,
     msg_control: ?*c_void,
     msg_controllen: socklen_t,
-    __pad2: socklen_t,
+    __pad2: socklen_t = 0,
     msg_flags: i32,
 };
 
lib/std/os/bits/linux/x86_64.zig
@@ -495,10 +495,10 @@ pub const msghdr = extern struct {
     msg_namelen: socklen_t,
     msg_iov: [*]iovec,
     msg_iovlen: i32,
-    __pad1: i32,
+    __pad1: i32 = 0,
     msg_control: ?*c_void,
     msg_controllen: socklen_t,
-    __pad2: socklen_t,
+    __pad2: socklen_t = 0,
     msg_flags: i32,
 };
 
@@ -507,10 +507,10 @@ pub const msghdr_const = extern struct {
     msg_namelen: socklen_t,
     msg_iov: [*]iovec_const,
     msg_iovlen: i32,
-    __pad1: i32,
+    __pad1: i32 = 0,
     msg_control: ?*c_void,
     msg_controllen: socklen_t,
-    __pad2: socklen_t,
+    __pad2: socklen_t = 0,
     msg_flags: i32,
 };
 
lib/std/os/linux.zig
@@ -977,7 +977,7 @@ pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noal
     return syscall5(.getsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
 }
 
-pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize {
+pub fn sendmsg(fd: i32, msg: *const msghdr_const, flags: u32) usize {
     if (builtin.arch == .i386) {
         return socketcall(SC_sendmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags });
     }
lib/std/os/windows.zig
@@ -1291,6 +1291,19 @@ pub fn getsockname(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.so
     return ws2_32.getsockname(s, name, @ptrCast(*i32, namelen));
 }
 
+pub fn sendmsg(
+    s: ws2_32.SOCKET,
+    msg: *const ws2_32.WSAMSG,
+    flags: u32,
+) i32 {
+    var bytes_send: DWORD = undefined;
+    if (ws2_32.WSASendMsg(s, msg, flags, &bytes_send, null, null) == ws2_32.SOCKET_ERROR) {
+        return ws2_32.SOCKET_ERROR;
+    } else {
+        return @as(i32, @intCast(u31, bytes_send));
+    }
+}
+
 pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 {
     var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @intToPtr([*]u8, @ptrToInt(buf)) };
     var bytes_send: DWORD = undefined;
lib/std/os.zig
@@ -4840,7 +4840,7 @@ pub const SendError = error{
     NetworkSubsystemFailed,
 } || UnexpectedError;
 
-pub const SendToError = SendError || error{
+pub const SendMsgError = SendError || error{
     /// The passed address didn't have the correct address family in its sa_family field.
     AddressFamilyNotSupported,
 
@@ -4859,6 +4859,79 @@ pub const SendToError = SendError || error{
     AddressNotAvailable,
 };
 
+pub fn sendmsg(
+    /// The file descriptor of the sending socket.
+    sockfd: socket_t,
+    /// Message header and iovecs
+    msg: msghdr_const,
+    flags: u32,
+) SendMsgError!usize {
+    while (true) {
+        const rc = system.sendmsg(sockfd, &msg, flags);
+        if (builtin.os.tag == .windows) {
+            if (rc == windows.ws2_32.SOCKET_ERROR) {
+                switch (windows.ws2_32.WSAGetLastError()) {
+                    .WSAEACCES => return error.AccessDenied,
+                    .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
+                    .WSAECONNRESET => return error.ConnectionResetByPeer,
+                    .WSAEMSGSIZE => return error.MessageTooBig,
+                    .WSAENOBUFS => return error.SystemResources,
+                    .WSAENOTSOCK => return error.FileDescriptorNotASocket,
+                    .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
+                    .WSAEDESTADDRREQ => unreachable, // A destination address is required.
+                    .WSAEFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
+                    .WSAEHOSTUNREACH => return error.NetworkUnreachable,
+                    // TODO: WSAEINPROGRESS, WSAEINTR
+                    .WSAEINVAL => unreachable,
+                    .WSAENETDOWN => return error.NetworkSubsystemFailed,
+                    .WSAENETRESET => return error.ConnectionResetByPeer,
+                    .WSAENETUNREACH => return error.NetworkUnreachable,
+                    .WSAENOTCONN => return error.SocketNotConnected,
+                    .WSAESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
+                    .WSAEWOULDBLOCK => return error.WouldBlock,
+                    .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
+                    else => |err| return windows.unexpectedWSAError(err),
+                }
+            } else {
+                return @intCast(usize, rc);
+            }
+        } else {
+            switch (errno(rc)) {
+                0 => return @intCast(usize, rc),
+
+                EACCES => return error.AccessDenied,
+                EAGAIN => return error.WouldBlock,
+                EALREADY => return error.FastOpenAlreadyInProgress,
+                EBADF => unreachable, // always a race condition
+                ECONNRESET => return error.ConnectionResetByPeer,
+                EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
+                EFAULT => unreachable, // An invalid user space address was specified for an argument.
+                EINTR => continue,
+                EINVAL => unreachable, // Invalid argument passed.
+                EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
+                EMSGSIZE => return error.MessageTooBig,
+                ENOBUFS => return error.SystemResources,
+                ENOMEM => return error.SystemResources,
+                ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+                EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
+                EPIPE => return error.BrokenPipe,
+                EAFNOSUPPORT => return error.AddressFamilyNotSupported,
+                ELOOP => return error.SymLinkLoop,
+                ENAMETOOLONG => return error.NameTooLong,
+                ENOENT => return error.FileNotFound,
+                ENOTDIR => return error.NotDir,
+                EHOSTUNREACH => return error.NetworkUnreachable,
+                ENETUNREACH => return error.NetworkUnreachable,
+                ENOTCONN => return error.SocketNotConnected,
+                ENETDOWN => return error.NetworkSubsystemFailed,
+                else => |err| return unexpectedErrno(err),
+            }
+        }
+    }
+}
+
+pub const SendToError = SendMsgError;
+
 /// Transmit a message to another socket.
 ///
 /// The `sendto` call may be used only when the socket is in a connected state (so that the intended