Fixed Darwin-incompatible socket flags and unavailable system calls
parent
03a7124543
commit
07bee9da42
|
@ -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;
|
||||
|
|
|
@ -358,13 +358,44 @@ pub const Address = extern union {
|
|||
}
|
||||
};
|
||||
|
||||
fn getUnixSocketInitFlags() u16 {
|
||||
comptime {
|
||||
var flags = 0;
|
||||
switch (builtin.os.tag) {
|
||||
.linux, .freebsd, .netbsd, .dragonfly => {
|
||||
flags |= os.SOCK_CLOEXEC;
|
||||
flags |= if (std.io.is_async) os.SOCK_NONBLOCK else 0;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
|
||||
// These are primarily needed for UNIX-based platforms without
|
||||
// SOCK_CLOEXEC and SOCK_NONBLOCK flags when creating sockets
|
||||
// or accepting connections
|
||||
fn setUnixSocketFlags(sock: os.fd_t) os.FcntlError!void {
|
||||
var fdflags = try os.fcntl(sock, os.F_GETFD, 0);
|
||||
fdflags |= os.FD_CLOEXEC;
|
||||
_ = try os.fcntl(sock, os.F_SETFD, fdflags);
|
||||
|
||||
if (std.io.is_async) {
|
||||
var flflags = try os.fcntl(sock, os.F_GETFL, 0);
|
||||
flflags |= os.O_NONBLOCK;
|
||||
_ = try os.fcntl(sock, os.F_SETFL, fdflags);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connectUnixSocket(path: []const u8) !fs.File {
|
||||
const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
|
||||
const sockfd = try os.socket(
|
||||
os.AF_UNIX,
|
||||
os.SOCK_STREAM | os.SOCK_CLOEXEC | opt_non_block,
|
||||
0,
|
||||
);
|
||||
const flags = os.SOCK_STREAM | getUnixSocketInitFlags();
|
||||
const sockfd = try os.socket(os.AF_UNIX, flags, 0);
|
||||
|
||||
if (comptime builtin.os.tag.isDarwin()) {
|
||||
try setUnixSocketFlags(sockfd);
|
||||
}
|
||||
|
||||
errdefer os.close(sockfd);
|
||||
|
||||
var addr = try std.net.Address.initUnix(path);
|
||||
|
@ -406,9 +437,13 @@ pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16)
|
|||
}
|
||||
|
||||
pub fn tcpConnectToAddress(address: Address) !fs.File {
|
||||
const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
|
||||
const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock;
|
||||
const sock_flags = os.SOCK_STREAM | getUnixSocketInitFlags();
|
||||
const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP);
|
||||
|
||||
if (comptime builtin.os.tag.isDarwin()) {
|
||||
try setUnixSocketFlags(sockfd);
|
||||
}
|
||||
|
||||
errdefer os.close(sockfd);
|
||||
try os.connect(sockfd, &address.any, address.getOsSockLen());
|
||||
|
||||
|
@ -1312,11 +1347,14 @@ pub const StreamServer = struct {
|
|||
}
|
||||
|
||||
pub fn listen(self: *StreamServer, address: Address) !void {
|
||||
const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
|
||||
const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock;
|
||||
const flags = os.SOCK_STREAM | getUnixSocketInitFlags();
|
||||
const proto = if (address.any.family == os.AF_UNIX) @as(u32, 0) else os.IPPROTO_TCP;
|
||||
const sockfd = try os.socket(address.any.family, flags, proto);
|
||||
|
||||
if (comptime builtin.os.tag.isDarwin()) {
|
||||
try setUnixSocketFlags(sockfd);
|
||||
}
|
||||
|
||||
const sockfd = try os.socket(address.any.family, sock_flags, proto);
|
||||
self.sockfd = sockfd;
|
||||
errdefer {
|
||||
os.close(sockfd);
|
||||
|
@ -1374,12 +1412,20 @@ pub const StreamServer = struct {
|
|||
};
|
||||
|
||||
/// If this function succeeds, the returned `Connection` is a caller-managed resource.
|
||||
pub fn accept(self: *StreamServer) AcceptError!Connection {
|
||||
const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
|
||||
const accept_flags = nonblock | os.SOCK_CLOEXEC;
|
||||
pub fn accept(self: *StreamServer) !Connection {
|
||||
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| {
|
||||
var _accept: os.AcceptError!os.fd_t = undefined;
|
||||
|
||||
if (comptime builtin.os.tag.isDarwin()) {
|
||||
try setUnixSocketFlags(self.sockfd.?);
|
||||
_accept = os.accept(self.sockfd.?, &accepted_addr.any, &adr_len);
|
||||
} else {
|
||||
const flags = getUnixSocketInitFlags();
|
||||
_accept = os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, flags);
|
||||
}
|
||||
|
||||
if (_accept) |fd| {
|
||||
return Connection{
|
||||
.file = fs.File{ .handle = fd },
|
||||
.address = accepted_addr,
|
||||
|
|
|
@ -2309,12 +2309,53 @@ pub fn accept4(
|
|||
/// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
|
||||
flags: u32,
|
||||
) AcceptError!fd_t {
|
||||
if (comptime builtin.os.tag.isDarwin()) {
|
||||
@compileError("accept4 not available for target Darwin, use accept");
|
||||
}
|
||||
|
||||
return try _accept(sockfd, addr, addr_size, flags);
|
||||
}
|
||||
|
||||
/// 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 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,
|
||||
/// This argument is a pointer to a sockaddr structure. This structure is filled in with the
|
||||
/// address of the peer socket, as known to the communications layer. The exact format of the
|
||||
/// address returned addr is determined by the socket's address family (see `socket` and the
|
||||
/// respective protocol man pages).
|
||||
addr: *sockaddr,
|
||||
/// This argument is a value-result argument: the caller must initialize it to contain the
|
||||
/// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
|
||||
/// of the peer address.
|
||||
///
|
||||
/// 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,
|
||||
) AcceptError!fd_t {
|
||||
return try _accept(sockfd, addr, addr_size, 0);
|
||||
}
|
||||
|
||||
fn _accept(sockfd: fd_t, addr: *sockaddr, addr_size: *socklen_t, flags: u32) AcceptError!fd_t {
|
||||
while (true) {
|
||||
const rc = system.accept4(sockfd, addr, addr_size, flags);
|
||||
const rc = func: {
|
||||
switch (comptime builtin.os.tag) {
|
||||
.linux, .freebsd, .netbsd, .dragonfly =>
|
||||
break :func system.accept4(sockfd, addr, addr_size, flags),
|
||||
.ios, .macosx, .watchos, .tvos => {
|
||||
assert(flags == 0);
|
||||
break :func system.accept(sockfd, addr, addr_size);
|
||||
},
|
||||
else => @compileError("accept not available for target"),
|
||||
}
|
||||
};
|
||||
|
||||
switch (errno(rc)) {
|
||||
0 => return @intCast(fd_t, rc),
|
||||
EINTR => continue,
|
||||
|
||||
EAGAIN => if (std.event.Loop.instance) |loop| {
|
||||
loop.waitUntilFdReadable(sockfd);
|
||||
continue;
|
||||
|
@ -2333,7 +2374,6 @@ pub fn accept4(
|
|||
EOPNOTSUPP => unreachable,
|
||||
EPROTO => return error.ProtocolFailure,
|
||||
EPERM => return error.BlockedByFirewall,
|
||||
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue