diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 142c8d4e4a32..24f15ab7633d 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1850,48 +1850,29 @@ pub fn sigismember(set: *const sigset_t, sig: usize) bool { } pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - if (native_arch == .x86) { - return socketcall(SC.getsockname, &[3]usize{ @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @intFromPtr(len) }); - } return syscall3(.getsockname, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @intFromPtr(len)); } pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - if (native_arch == .x86) { - return socketcall(SC.getpeername, &[3]usize{ @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @intFromPtr(len) }); - } return syscall3(.getpeername, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @intFromPtr(len)); } pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { - if (native_arch == .x86) { - return socketcall(SC.socket, &[3]usize{ domain, socket_type, protocol }); - } return syscall3(.socket, domain, socket_type, protocol); } pub fn setsockopt(fd: i32, level: i32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { - if (native_arch == .x86) { - return socketcall(SC.setsockopt, &[5]usize{ @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(@as(isize, level))), optname, @intFromPtr(optval), @as(usize, @intCast(optlen)) }); - } return syscall5(.setsockopt, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(@as(isize, level))), optname, @intFromPtr(optval), @as(usize, @intCast(optlen))); } pub fn getsockopt(fd: i32, level: i32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { - if (native_arch == .x86) { - return socketcall(SC.getsockopt, &[5]usize{ @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(@as(isize, level))), optname, @intFromPtr(optval), @intFromPtr(optlen) }); - } return syscall5(.getsockopt, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(@as(isize, level))), optname, @intFromPtr(optval), @intFromPtr(optlen)); } pub fn sendmsg(fd: i32, msg: *const msghdr_const, flags: u32) usize { const fd_usize = @as(usize, @bitCast(@as(isize, fd))); const msg_usize = @intFromPtr(msg); - if (native_arch == .x86) { - return socketcall(SC.sendmsg, &[3]usize{ fd_usize, msg_usize, flags }); - } else { - return syscall3(.sendmsg, fd_usize, msg_usize, flags); - } + return syscall3(.sendmsg, fd_usize, msg_usize, flags); } pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize { @@ -1938,21 +1919,13 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize pub fn connect(fd: i32, addr: *const anyopaque, len: socklen_t) usize { const fd_usize = @as(usize, @bitCast(@as(isize, fd))); const addr_usize = @intFromPtr(addr); - if (native_arch == .x86) { - return socketcall(SC.connect, &[3]usize{ fd_usize, addr_usize, len }); - } else { - return syscall3(.connect, fd_usize, addr_usize, len); - } + return syscall3(.connect, fd_usize, addr_usize, len); } pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { const fd_usize = @as(usize, @bitCast(@as(isize, fd))); const msg_usize = @intFromPtr(msg); - if (native_arch == .x86) { - return socketcall(SC.recvmsg, &[3]usize{ fd_usize, msg_usize, flags }); - } else { - return syscall3(.recvmsg, fd_usize, msg_usize, flags); - } + return syscall3(.recvmsg, fd_usize, msg_usize, flags); } pub fn recvmmsg(fd: i32, msgvec: ?[*]mmsghdr, vlen: u32, flags: u32, timeout: ?*timespec) usize { @@ -1978,38 +1951,22 @@ pub fn recvfrom( const buf_usize = @intFromPtr(buf); const addr_usize = @intFromPtr(addr); const alen_usize = @intFromPtr(alen); - if (native_arch == .x86) { - return socketcall(SC.recvfrom, &[6]usize{ fd_usize, buf_usize, len, flags, addr_usize, alen_usize }); - } else { - return syscall6(.recvfrom, fd_usize, buf_usize, len, flags, addr_usize, alen_usize); - } + return syscall6(.recvfrom, fd_usize, buf_usize, len, flags, addr_usize, alen_usize); } pub fn shutdown(fd: i32, how: i32) usize { - if (native_arch == .x86) { - return socketcall(SC.shutdown, &[2]usize{ @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(@as(isize, how))) }); - } return syscall2(.shutdown, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(@as(isize, how)))); } pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { - if (native_arch == .x86) { - return socketcall(SC.bind, &[3]usize{ @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @as(usize, @intCast(len)) }); - } return syscall3(.bind, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @as(usize, @intCast(len))); } pub fn listen(fd: i32, backlog: u32) usize { - if (native_arch == .x86) { - return socketcall(SC.listen, &[2]usize{ @as(usize, @bitCast(@as(isize, fd))), backlog }); - } return syscall2(.listen, @as(usize, @bitCast(@as(isize, fd))), backlog); } pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { - if (native_arch == .x86) { - return socketcall(SC.sendto, &[6]usize{ @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(buf), len, flags, @intFromPtr(addr), @as(usize, @intCast(alen)) }); - } return syscall6(.sendto, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(buf), len, flags, @intFromPtr(addr), @as(usize, @intCast(alen))); } @@ -2034,23 +1991,15 @@ pub fn sendfile(outfd: i32, infd: i32, offset: ?*i64, count: usize) usize { } pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: *[2]i32) usize { - if (native_arch == .x86) { - return socketcall(SC.socketpair, &[4]usize{ @as(usize, @intCast(domain)), @as(usize, @intCast(socket_type)), @as(usize, @intCast(protocol)), @intFromPtr(fd) }); - } return syscall4(.socketpair, @as(usize, @intCast(domain)), @as(usize, @intCast(socket_type)), @as(usize, @intCast(protocol)), @intFromPtr(fd)); } pub fn accept(fd: i32, noalias addr: ?*sockaddr, noalias len: ?*socklen_t) usize { - if (native_arch == .x86) { - return socketcall(SC.accept, &[4]usize{ @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @intFromPtr(len), 0 }); - } + // There's no point in using the accept syscall as it just forwards to the accept4 syscall in the kernel. return accept4(fd, addr, len, 0); } pub fn accept4(fd: i32, noalias addr: ?*sockaddr, noalias len: ?*socklen_t, flags: u32) usize { - if (native_arch == .x86) { - return socketcall(SC.accept4, &[4]usize{ @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @intFromPtr(len), flags }); - } return syscall4(.accept4, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @intFromPtr(len), flags); } diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 04702903efd7..cbd5ac035650 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -207,6 +207,388 @@ test "sysinfo" { try expect(info.mem_unit <= std.heap.page_size_max); } +// socket functions not tested: +// - send (these forward to sendto in the kernel, and Zig doesn't provide bindings for them) +// - recv (these forward to recvfrom in the kernel, and Zig doesn't provide bindings for them) +// - accept4 (accept forwards to accept4 in the kernel, and Zig's binding for accept forwards directly to accept4) + +test "inet sockets" { + // socket functions tested: + // - [x] socket + // - [x] bind + // - [x] connect + // - [x] listen + // - [x] accept (and by extension accept4) + // - [x] getsockname + // - [x] getpeername + // - [ ] socketpair + // - [ ] send + // - [ ] recv + // - [x] sendto + // - [x] recvfrom + // - [x] shutdown + // - [x] setsockopt + // - [x] getsockopt + // - [ ] sendmsg + // - [ ] recvmsg + // - [ ] accept4 + // - [ ] recvmmsg + // - [ ] sendmmsg + + const AF = linux.AF; + const SOCK = linux.SOCK; + const SOL = linux.SOL; + const SO = linux.SO; + const E = linux.E; + const fd_t = linux.fd_t; + const sockaddr = linux.sockaddr; + + // create a socket "fd" to be the server + var rc = linux.socket(AF.INET, SOCK.STREAM, 0); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + const fd: fd_t = @intCast(rc); + + const test_sndbuf: c_int = 4096; + rc = linux.setsockopt(fd, SOL.SOCKET, SO.SNDBUF, std.mem.asBytes(&test_sndbuf).ptr, std.mem.asBytes(&test_sndbuf).len); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + + var sndbuf: c_int = undefined; + var sndbuf_len: linux.socklen_t = std.mem.asBytes(&sndbuf).len; + rc = linux.getsockopt(fd, SOL.SOCKET, SO.SNDBUF, std.mem.asBytes(&sndbuf).ptr, &sndbuf_len); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + try std.testing.expectEqual(test_sndbuf * 2, sndbuf); + + var addr: sockaddr.storage = undefined; + var len: u32 = @sizeOf(@TypeOf(addr)); + rc = linux.getsockname(fd, @ptrCast(&addr), &len); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + try std.testing.expectEqual(@sizeOf(sockaddr.in), len); + try std.testing.expectEqual(AF.INET, addr.family); + const addr_in = @as(*sockaddr.in, @ptrCast(&addr)); + try std.testing.expectEqual(0, addr_in.addr); + try std.testing.expectEqual(0, addr_in.port); + + // bind the socket to a random port + const INADDR_LOOPBACK = std.mem.nativeToBig(u32, 0x7F_00_00_01); + addr = undefined; + addr_in.* = .{ + .addr = INADDR_LOOPBACK, + .port = 0, + }; + rc = linux.bind(fd, @ptrCast(&addr), @sizeOf(sockaddr.in)); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + + // test getsockname + addr = undefined; + len = @sizeOf(@TypeOf(addr)); + rc = linux.getsockname(fd, @ptrCast(&addr), &len); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + try std.testing.expectEqual(@sizeOf(sockaddr.in), len); + try std.testing.expectEqual(AF.INET, addr.family); + try std.testing.expectEqual(INADDR_LOOPBACK, addr_in.addr); + const bind_port = addr_in.port; + + // listen with a backlog of one so we can establish a connection and accept on the same thread + rc = linux.listen(fd, 1); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + + // create another socket "other_fd" to be the client + rc = linux.socket(AF.INET, SOCK.STREAM, 0); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + const other_fd: fd_t = @intCast(rc); + + // connect to other_fd from fd + addr = undefined; + addr_in.* = .{ + .addr = INADDR_LOOPBACK, + .port = bind_port, + }; + rc = linux.connect(other_fd, @ptrCast(&addr), @sizeOf(sockaddr.in)); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + + // check connection sock addr + addr = undefined; + len = @sizeOf(@TypeOf(addr)); + rc = linux.getsockname(other_fd, @ptrCast(&addr), &len); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + try std.testing.expectEqual(@sizeOf(sockaddr.in), len); + try std.testing.expectEqual(AF.INET, addr.family); + try std.testing.expectEqual(INADDR_LOOPBACK, addr_in.addr); + const other_port = addr_in.port; + + // check connection peer addr + addr = undefined; + len = @sizeOf(@TypeOf(addr)); + rc = linux.getpeername(other_fd, @ptrCast(&addr), &len); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + try std.testing.expectEqual(@sizeOf(sockaddr.in), len); + try std.testing.expectEqual(AF.INET, addr.family); + try std.testing.expectEqual(INADDR_LOOPBACK, addr_in.addr); + try std.testing.expectEqual(bind_port, addr_in.port); + + // accept the connection to fd from other_fd and check peer addr + addr = undefined; + len = @sizeOf(@TypeOf(addr)); + rc = linux.accept(fd, @ptrCast(&addr), &len); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(AF.INET, addr.family); + try std.testing.expectEqual(INADDR_LOOPBACK, addr_in.addr); + try std.testing.expectEqual(other_port, addr_in.port); + const conn_fd: fd_t = @intCast(rc); + + const data = "foo"; + rc = linux.sendto(conn_fd, data, data.len, 0, null, 0); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(data.len, rc); + + rc = linux.shutdown(fd, linux.SHUT.RDWR); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + + var recv_data: [data.len]u8 = undefined; + rc = linux.recvfrom(other_fd, &recv_data, recv_data.len, 0, null, null); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(recv_data.len, rc); + try std.testing.expectEqualSlices(u8, data, &recv_data); + + rc = linux.close(conn_fd); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + + rc = linux.close(fd); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + + rc = linux.close(other_fd); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); +} + +test "unix sockets sendmsg and recvmsg" { + // socket functions tested: + // - [ ] socket + // - [ ] bind + // - [ ] connect + // - [ ] listen + // - [ ] accept + // - [ ] getsockname + // - [ ] getpeername + // - [x] socketpair + // - [ ] send + // - [ ] recv + // - [ ] sendto + // - [ ] recvfrom + // - [ ] shutdown + // - [ ] setsockopt + // - [ ] getsockopt + // - [x] sendmsg + // - [x] recvmsg + // - [ ] accept4 + // - [ ] recvmmsg + // - [ ] sendmmsg + + const AF = linux.AF; + const SOCK = linux.SOCK; + const E = linux.E; + const iovec = std.posix.iovec; + const iovec_const = std.posix.iovec_const; + + // create a socket "fd" to be the server + var socks: [2]linux.socket_t = undefined; + var rc = linux.socketpair(AF.UNIX, SOCK.SEQPACKET, 0, &socks); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + + // test sendmsg + const data = "foo"; + const send_iov: [1]iovec_const = .{.{ + .base = data, + .len = data.len, + }}; + rc = linux.sendmsg(socks[0], &.{ + .name = null, + .namelen = 0, + .iov = &send_iov, + .iovlen = send_iov.len, + .control = null, + .controllen = 0, + .flags = 0, + }, 0); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(data.len, rc); + + // test recvmsg + var recv_data: [data.len]u8 = undefined; + var recv_iov: [1]iovec = .{.{ + .base = &recv_data, + .len = recv_data.len, + }}; + var hdr: linux.msghdr = .{ + .name = null, + .namelen = 0, + .iov = &recv_iov, + .iovlen = recv_iov.len, + .control = null, + .controllen = 0, + .flags = 0, + }; + rc = linux.recvmsg(socks[1], &hdr, 0); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(data.len, rc); + try std.testing.expectEqual(data.len, recv_iov[0].len); + try std.testing.expectEqualSlices(u8, data, &recv_data); + + rc = linux.close(socks[0]); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + + rc = linux.close(socks[1]); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); +} + +test "unix sockets sendmmsg and recvmmsg" { + // socket functions tested: + // - [ ] socket + // - [ ] bind + // - [ ] connect + // - [ ] listen + // - [ ] accept + // - [ ] getsockname + // - [ ] getpeername + // - [x] socketpair + // - [ ] send + // - [ ] recv + // - [ ] sendto + // - [ ] recvfrom + // - [ ] shutdown + // - [ ] setsockopt + // - [ ] getsockopt + // - [ ] sendmsg + // - [ ] recvmsg + // - [ ] accept4 + // - [x] recvmmsg + // - [x] sendmmsg + + const AF = linux.AF; + const SOCK = linux.SOCK; + const E = linux.E; + const iovec = std.posix.iovec; + const iovec_const = std.posix.iovec_const; + + // create a socket "fd" to be the server + var socks: [2]linux.socket_t = undefined; + var rc = linux.socketpair(AF.UNIX, SOCK.SEQPACKET, 0, &socks); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + + // test sendmmsg + const data: [2][]const u8 = .{ "foo", "bar" }; + const send_iov: [2][1]iovec_const = .{ + .{.{ + .base = data[0].ptr, + .len = data[0].len, + }}, + .{.{ + .base = data[1].ptr, + .len = data[1].len, + }}, + }; + var send_hdr: [2]linux.mmsghdr_const = .{ + .{ + .hdr = .{ + .name = null, + .namelen = 0, + .iov = &send_iov[0], + .iovlen = send_iov[0].len, + .control = null, + .controllen = 0, + .flags = 0, + }, + .len = 0, + }, + .{ + .hdr = .{ + .name = null, + .namelen = 0, + .iov = &send_iov[1], + .iovlen = send_iov[1].len, + .control = null, + .controllen = 0, + .flags = 0, + }, + .len = 0, + }, + }; + rc = linux.sendmmsg(socks[0], &send_hdr, 2, 0); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(data.len, rc); + for (data, send_hdr) |data_el, hdr| { + try std.testing.expectEqual(data_el.len, hdr.len); + } + + // test recvmmsg + var recv_data: struct { [data[0].len]u8, [data[1].len]u8 } = undefined; + var recv_iov: [2][1]iovec = .{ + .{.{ + .base = &recv_data[0], + .len = recv_data[0].len, + }}, + .{.{ + .base = &recv_data[1], + .len = recv_data[1].len, + }}, + }; + var recv_hdr: [2]linux.mmsghdr = .{ + .{ + .hdr = .{ + .name = null, + .namelen = 0, + .iov = &recv_iov[0], + .iovlen = recv_iov[0].len, + .control = null, + .controllen = 0, + .flags = 0, + }, + .len = 0, + }, + .{ + .hdr = .{ + .name = null, + .namelen = 0, + .iov = &recv_iov[1], + .iovlen = recv_iov[1].len, + .control = null, + .controllen = 0, + .flags = 0, + }, + .len = 0, + }, + }; + rc = linux.recvmmsg(socks[1], &recv_hdr, recv_hdr.len, 0, null); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(recv_hdr.len, rc); + inline for (data, recv_iov, &recv_data) |data_el, iov, *recv_data_el| { + try std.testing.expectEqual(data_el.len, iov[0].len); + try std.testing.expectEqualSlices(u8, data_el, recv_data_el); + } + + rc = linux.close(socks[0]); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); + + rc = linux.close(socks[1]); + try std.testing.expectEqual(.SUCCESS, E.init(rc)); + try std.testing.expectEqual(0, rc); +} + test { _ = linux.IoUring; }