diff --git a/lib/std/c.zig b/lib/std/c.zig index e6516fa8ab..9f1114b9c1 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -131,6 +131,7 @@ pub extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint pub extern "c" fn listen(sockfd: fd_t, backlog: c_uint) c_int; pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int; pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int; +pub extern "c" fn accept(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) c_int; pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int; pub extern "c" fn getsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*c_void, optlen: *socklen_t) c_int; pub extern "c" fn setsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*const c_void, optlen: socklen_t) c_int; diff --git a/lib/std/net.zig b/lib/std/net.zig index 1c7355bcb2..b9681bc618 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1379,7 +1379,7 @@ pub const StreamServer = struct { const accept_flags = nonblock | os.SOCK_CLOEXEC; var accepted_addr: Address = undefined; var adr_len: os.socklen_t = @sizeOf(Address); - if (os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| { + if (os.accept(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| { return Connection{ .file = fs.File{ .handle = fd }, .address = accepted_addr, diff --git a/lib/std/os.zig b/lib/std/os.zig index 90c74738b8..ff7089ceb0 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -2159,9 +2159,20 @@ pub const SocketError = error{ } || UnexpectedError; pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t { + const have_sock_flags = comptime !std.Target.current.isDarwin(); + const filtered_sock_type = if (!have_sock_flags) + socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC) + else + socket_type; const rc = system.socket(domain, socket_type, protocol); switch (errno(rc)) { - 0 => return @intCast(fd_t, rc), + 0 => { + const fd = @intCast(fd_t, rc); + if (!have_sock_flags and filtered_sock_type != socket_type) { + try setSockFlags(fd, socket_type); + } + return fd; + }, EACCES => return error.PermissionDenied, EAFNOSUPPORT => return error.AddressFamilyNotSupported, EINVAL => return error.ProtocolFamilyNotAvailable, @@ -2284,7 +2295,7 @@ pub const AcceptError = error{ /// Accept a connection on a socket. /// If the application has a global event loop enabled, EAGAIN is handled /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. -pub fn accept4( +pub fn accept( /// This argument is a socket that has been created with `socket`, bound to a local address /// with `bind`, and is listening for connections after a `listen`. sockfd: fd_t, @@ -2300,8 +2311,7 @@ pub fn accept4( /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size` /// will return a value greater than was supplied to the call. addr_size: *socklen_t, - /// If flags is 0, then `accept4` is the same as `accept`. The following values can be bitwise - /// ORed in flags to obtain different behavior: + /// The following values can be bitwise ORed in flags to obtain different behavior: /// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`) /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve /// the same result. @@ -2309,12 +2319,24 @@ pub fn accept4( /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful. flags: u32, ) AcceptError!fd_t { - while (true) { - const rc = system.accept4(sockfd, addr, addr_size, flags); - switch (errno(rc)) { - 0 => return @intCast(fd_t, rc), - EINTR => continue, + const have_accept4 = comptime !std.Target.current.isDarwin(); + assert(0 == (flags & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC))); // Unsupported flag(s) + while (true) { + const rc = if (have_accept4) + system.accept4(sockfd, addr, addr_size, flags) + else + system.accept(sockfd, addr, addr_size); + + switch (errno(rc)) { + 0 => { + const fd = @intCast(fd_t, rc); + if (!have_accept4 and flags != 0) { + try setSockFlags(fd, flags); + } + return fd; + }, + EINTR => continue, EAGAIN => if (std.event.Loop.instance) |loop| { loop.waitUntilFdReadable(sockfd); continue; @@ -2333,7 +2355,6 @@ pub fn accept4( EOPNOTSUPP => unreachable, EPROTO => return error.ProtocolFailure, EPERM => return error.BlockedByFirewall, - else => |err| return unexpectedErrno(err), } } @@ -3245,6 +3266,35 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize { } } +fn setSockFlags(fd: fd_t, flags: u32) !void { + { + var fd_flags = fcntl(fd, F_GETFD, 0) catch |err| switch (err) { + error.FileBusy => unreachable, + error.Locked => unreachable, + else => |e| return e, + }; + if ((flags & SOCK_NONBLOCK) != 0) fd_flags |= FD_CLOEXEC; + _ = fcntl(fd, F_SETFD, fd_flags) catch |err| switch (err) { + error.FileBusy => unreachable, + error.Locked => unreachable, + else => |e| return e, + }; + } + { + var fl_flags = fcntl(fd, F_GETFL, 0) catch |err| switch (err) { + error.FileBusy => unreachable, + error.Locked => unreachable, + else => |e| return e, + }; + if ((flags & SOCK_CLOEXEC) != 0) fl_flags |= O_NONBLOCK; + _ = fcntl(fd, F_SETFL, fl_flags) catch |err| switch (err) { + error.FileBusy => unreachable, + error.Locked => unreachable, + else => |e| return e, + }; + } +} + pub const FlockError = error{ WouldBlock, diff --git a/lib/std/os/bits/darwin.zig b/lib/std/os/bits/darwin.zig index 2ea9ae2cfa..d116d6157a 100644 --- a/lib/std/os/bits/darwin.zig +++ b/lib/std/os/bits/darwin.zig @@ -764,6 +764,15 @@ pub const SOCK_RDM = 4; pub const SOCK_SEQPACKET = 5; pub const SOCK_MAXADDRLEN = 255; +/// Not actually supported by Darwin, but Zig supplies a shim. +/// This numerical value is not ABI-stable. It need only not conflict +/// with any other "SOCK_" bits. +pub const SOCK_CLOEXEC = 1 << 15; +/// Not actually supported by Darwin, but Zig supplies a shim. +/// This numerical value is not ABI-stable. It need only not conflict +/// with any other "SOCK_" bits. +pub const SOCK_NONBLOCK = 1 << 16; + pub const IPPROTO_ICMP = 1; pub const IPPROTO_ICMPV6 = 58; pub const IPPROTO_TCP = 6;