diff --git a/std/c/freebsd.zig b/std/c/freebsd.zig index 2f2f4c0a1b75..b6cf15bf3150 100644 --- a/std/c/freebsd.zig +++ b/std/c/freebsd.zig @@ -42,14 +42,36 @@ pub const pthread_attr_t = extern struct { }; pub const msghdr = extern struct { - msg_name: *u8, + /// optional address + msg_name: ?*sockaddr, + /// size of address msg_namelen: socklen_t, - msg_iov: *iovec, + /// scatter/gather array + msg_iov: [*]iovec, + /// # elements in msg_iov msg_iovlen: i32, - __pad1: i32, - msg_control: *u8, + /// ancillary data + msg_control: ?*c_void, + /// ancillary data buffer len msg_controllen: socklen_t, - __pad2: socklen_t, + /// flags on received message + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + /// optional address + msg_name: ?*const sockaddr, + /// size of address + msg_namelen: socklen_t, + /// scatter/gather array + msg_iov: [*]iovec_const, + /// # elements in msg_iov + msg_iovlen: i32, + /// ancillary data + msg_control: ?*c_void, + /// ancillary data buffer len + msg_controllen: socklen_t, + /// flags on received message msg_flags: i32, }; diff --git a/std/c/netbsd.zig b/std/c/netbsd.zig index 796d45dafccb..2c07ee296bfd 100644 --- a/std/c/netbsd.zig +++ b/std/c/netbsd.zig @@ -42,14 +42,36 @@ pub const pthread_attr_t = extern struct { }; pub const msghdr = extern struct { - msg_name: *u8, + /// optional address + msg_name: ?*sockaddr, + /// size of address msg_namelen: socklen_t, - msg_iov: *iovec, + /// scatter/gather array + msg_iov: [*]iovec, + /// # elements in msg_iov msg_iovlen: i32, - __pad1: i32, - msg_control: *u8, + /// ancillary data + msg_control: ?*c_void, + /// ancillary data buffer len msg_controllen: socklen_t, - __pad2: socklen_t, + /// flags on received message + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + /// optional address + msg_name: ?*const sockaddr, + /// size of address + msg_namelen: socklen_t, + /// scatter/gather array + msg_iov: [*]iovec_const, + /// # elements in msg_iov + msg_iovlen: i32, + /// ancillary data + msg_control: ?*c_void, + /// ancillary data buffer len + msg_controllen: socklen_t, + /// flags on received message msg_flags: i32, }; diff --git a/std/os/linux.zig b/std/os/linux.zig index e7f822185bc4..425df3a55d8c 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -12,6 +12,7 @@ pub use switch (builtin.arch) { pub use @import("linux/errno.zig"); pub const PATH_MAX = 4096; +pub const IOV_MAX = 1024; pub const STDIN_FILENO = 0; pub const STDOUT_FILENO = 1; @@ -1193,6 +1194,16 @@ pub const iovec_const = extern struct { iov_len: usize, }; +pub const mmsghdr = extern struct { + msg_hdr: msghdr, + msg_len: u32, +}; + +pub const mmsghdr_const = extern struct { + msg_hdr: msghdr_const, + msg_len: u32, +}; + pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); } @@ -1213,10 +1224,50 @@ pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noal return syscall5(SYS_getsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); } -pub fn sendmsg(fd: i32, msg: *const msghdr, flags: u32) usize { +pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize { return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); } +pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize { + if (@typeInfo(usize).Int.bits > @typeInfo(@typeOf(mmsghdr(undefined).msg_len)).Int.bits) { + // workaround kernel brokenness: + // if adding up all iov_len overflows a i32 then split into multiple calls + // see https://www.openwall.com/lists/musl/2014/06/07/5 + const kvlen = if (vlen > IOV_MAX) IOV_MAX else vlen; // matches kernel + var next_unsent: usize = 0; + for (msgvec[0..kvlen]) |*msg, i| { + var size: i32 = 0; + const msg_iovlen = @intCast(usize, msg.msg_hdr.msg_iovlen); // kernel side this is treated as unsigned + for (msg.msg_hdr.msg_iov[0..msg_iovlen]) |iov, j| { + if (iov.iov_len > std.math.maxInt(i32) or @addWithOverflow(i32, size, @intCast(i32, iov.iov_len), &size)) { + // batch-send all messages up to the current message + if (next_unsent < i) { + const batch_size = i - next_unsent; + const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags); + if (getErrno(r) != 0) return next_unsent; + if (r < batch_size) return next_unsent + r; + } + // send current message as own packet + const r = sendmsg(fd, &msg.msg_hdr, flags); + if (getErrno(r) != 0) return r; + // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe. + msg.msg_len = @intCast(u32, r); + next_unsent = i + 1; + break; + } + } + } + if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG_EOR) + const batch_size = kvlen - next_unsent; + const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags); + if (getErrno(r) != 0) return r; + return next_unsent + r; + } + return kvlen; + } + return syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(msgvec), vlen, flags); +} + pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize { return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len); } diff --git a/std/os/linux/arm64.zig b/std/os/linux/arm64.zig index ac2c18ee5f2a..4ae55765fb1c 100644 --- a/std/os/linux/arm64.zig +++ b/std/os/linux/arm64.zig @@ -2,6 +2,7 @@ const std = @import("../../std.zig"); const linux = std.os.linux; const socklen_t = linux.socklen_t; const iovec = linux.iovec; +const iovec_const = linux.iovec_const; pub const SYS_io_setup = 0; pub const SYS_io_destroy = 1; @@ -415,12 +416,24 @@ pub fn syscall6( pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub const msghdr = extern struct { - msg_name: *u8, + msg_name: ?*sockaddr, msg_namelen: socklen_t, - msg_iov: *iovec, + msg_iov: [*]iovec, msg_iovlen: i32, __pad1: i32, - msg_control: *u8, + msg_control: ?*c_void, + msg_controllen: socklen_t, + __pad2: socklen_t, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: i32, + __pad1: i32, + msg_control: ?*c_void, msg_controllen: socklen_t, __pad2: socklen_t, msg_flags: i32, diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index d194cd4003cf..dc7fe601289e 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -1,7 +1,9 @@ const std = @import("../../std.zig"); const linux = std.os.linux; +const sockaddr = linux.sockaddr; const socklen_t = linux.socklen_t; const iovec = linux.iovec; +const iovec_const = linux.iovec_const; pub const SYS_read = 0; pub const SYS_write = 1; @@ -483,12 +485,24 @@ pub nakedcc fn restore_rt() void { } pub const msghdr = extern struct { - msg_name: *u8, + msg_name: ?*sockaddr, msg_namelen: socklen_t, - msg_iov: *iovec, + msg_iov: [*]iovec, msg_iovlen: i32, __pad1: i32, - msg_control: *u8, + msg_control: ?*c_void, + msg_controllen: socklen_t, + __pad2: socklen_t, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: i32, + __pad1: i32, + msg_control: ?*c_void, msg_controllen: socklen_t, __pad2: socklen_t, msg_flags: i32,