authorgravatar for cato@cato.ninjaCato <cato@cato.ninja> 2020-05-02 15:14:28+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-02 16:37:39-04:00
log07bee9da42977420916ca0f1044b47fab4a694dc
tree88cab3ee6234abf5a7022da6f12bca6a497cf007
parent03a7124543a52f7904090516e572b71b893c7ba2

Fixed Darwin-incompatible socket flags and unavailable system calls


3 files changed, 105 insertions(+), 18 deletions(-)

lib/std/c.zig+1
...@@ -131,6 +131,7 @@ pub extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint...@@ -131,6 +131,7 @@ pub extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint
131pub extern "c" fn listen(sockfd: fd_t, backlog: c_uint) c_int;131pub extern "c" fn listen(sockfd: fd_t, backlog: c_uint) c_int;
132pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int;132pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int;
133pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int;133pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int;
134pub extern "c" fn accept(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) c_int;
134pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int;135pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int;
135pub extern "c" fn getsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*c_void, optlen: *socklen_t) c_int;136pub extern "c" fn getsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*c_void, optlen: *socklen_t) c_int;
136pub extern "c" fn setsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*const c_void, optlen: socklen_t) c_int;137pub extern "c" fn setsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*const c_void, optlen: socklen_t) c_int;
lib/std/net.zig+61-15
...@@ -358,13 +358,44 @@ pub const Address = extern union {...@@ -358,13 +358,44 @@ pub const Address = extern union {
358 }358 }
359};359};
360360
361fn getUnixSocketInitFlags() u16 {
362 comptime {
363 var flags = 0;
364 switch (builtin.os.tag) {
365 .linux, .freebsd, .netbsd, .dragonfly => {
366 flags |= os.SOCK_CLOEXEC;
367 flags |= if (std.io.is_async) os.SOCK_NONBLOCK else 0;
368 },
369 else => {},
370 }
371
372 return flags;
373 }
374}
375
376// These are primarily needed for UNIX-based platforms without
377// SOCK_CLOEXEC and SOCK_NONBLOCK flags when creating sockets
378// or accepting connections
379fn setUnixSocketFlags(sock: os.fd_t) os.FcntlError!void {
380 var fdflags = try os.fcntl(sock, os.F_GETFD, 0);
381 fdflags |= os.FD_CLOEXEC;
382 _ = try os.fcntl(sock, os.F_SETFD, fdflags);
383
384 if (std.io.is_async) {
385 var flflags = try os.fcntl(sock, os.F_GETFL, 0);
386 flflags |= os.O_NONBLOCK;
387 _ = try os.fcntl(sock, os.F_SETFL, fdflags);
388 }
389}
390
361pub fn connectUnixSocket(path: []const u8) !fs.File {391pub fn connectUnixSocket(path: []const u8) !fs.File {
362 const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;392 const flags = os.SOCK_STREAM | getUnixSocketInitFlags();
363 const sockfd = try os.socket(393 const sockfd = try os.socket(os.AF_UNIX, flags, 0);
364 os.AF_UNIX,394
365 os.SOCK_STREAM | os.SOCK_CLOEXEC | opt_non_block,395 if (comptime builtin.os.tag.isDarwin()) {
366 0,396 try setUnixSocketFlags(sockfd);
367 );397 }
398
368 errdefer os.close(sockfd);399 errdefer os.close(sockfd);
369400
370 var addr = try std.net.Address.initUnix(path);401 var addr = try std.net.Address.initUnix(path);
...@@ -406,9 +437,13 @@ pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16)...@@ -406,9 +437,13 @@ pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16)
406}437}
407438
408pub fn tcpConnectToAddress(address: Address) !fs.File {439pub fn tcpConnectToAddress(address: Address) !fs.File {
409 const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;440 const sock_flags = os.SOCK_STREAM | getUnixSocketInitFlags();
410 const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock;
411 const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP);441 const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP);
442
443 if (comptime builtin.os.tag.isDarwin()) {
444 try setUnixSocketFlags(sockfd);
445 }
446
412 errdefer os.close(sockfd);447 errdefer os.close(sockfd);
413 try os.connect(sockfd, &address.any, address.getOsSockLen());448 try os.connect(sockfd, &address.any, address.getOsSockLen());
414449
...@@ -1312,11 +1347,14 @@ pub const StreamServer = struct {...@@ -1312,11 +1347,14 @@ pub const StreamServer = struct {
1312 }1347 }
13131348
1314 pub fn listen(self: *StreamServer, address: Address) !void {1349 pub fn listen(self: *StreamServer, address: Address) !void {
1315 const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;1350 const flags = os.SOCK_STREAM | getUnixSocketInitFlags();
1316 const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock;
1317 const proto = if (address.any.family == os.AF_UNIX) @as(u32, 0) else os.IPPROTO_TCP;1351 const proto = if (address.any.family == os.AF_UNIX) @as(u32, 0) else os.IPPROTO_TCP;
1352 const sockfd = try os.socket(address.any.family, flags, proto);
1353
1354 if (comptime builtin.os.tag.isDarwin()) {
1355 try setUnixSocketFlags(sockfd);
1356 }
13181357
1319 const sockfd = try os.socket(address.any.family, sock_flags, proto);
1320 self.sockfd = sockfd;1358 self.sockfd = sockfd;
1321 errdefer {1359 errdefer {
1322 os.close(sockfd);1360 os.close(sockfd);
...@@ -1374,12 +1412,20 @@ pub const StreamServer = struct {...@@ -1374,12 +1412,20 @@ pub const StreamServer = struct {
1374 };1412 };
13751413
1376 /// If this function succeeds, the returned `Connection` is a caller-managed resource.1414 /// If this function succeeds, the returned `Connection` is a caller-managed resource.
1377 pub fn accept(self: *StreamServer) AcceptError!Connection {1415 pub fn accept(self: *StreamServer) !Connection {
1378 const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
1379 const accept_flags = nonblock | os.SOCK_CLOEXEC;
1380 var accepted_addr: Address = undefined;1416 var accepted_addr: Address = undefined;
1381 var adr_len: os.socklen_t = @sizeOf(Address);1417 var adr_len: os.socklen_t = @sizeOf(Address);
1382 if (os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| {1418 var _accept: os.AcceptError!os.fd_t = undefined;
1419
1420 if (comptime builtin.os.tag.isDarwin()) {
1421 try setUnixSocketFlags(self.sockfd.?);
1422 _accept = os.accept(self.sockfd.?, &accepted_addr.any, &adr_len);
1423 } else {
1424 const flags = getUnixSocketInitFlags();
1425 _accept = os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, flags);
1426 }
1427
1428 if (_accept) |fd| {
1383 return Connection{1429 return Connection{
1384 .file = fs.File{ .handle = fd },1430 .file = fs.File{ .handle = fd },
1385 .address = accepted_addr,1431 .address = accepted_addr,
lib/std/os.zig+43-3
...@@ -2309,12 +2309,53 @@ pub fn accept4(...@@ -2309,12 +2309,53 @@ pub fn accept4(
2309 /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.2309 /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
2310 flags: u32,2310 flags: u32,
2311) AcceptError!fd_t {2311) AcceptError!fd_t {
2312 if (comptime builtin.os.tag.isDarwin()) {
2313 @compileError("accept4 not available for target Darwin, use accept");
2314 }
2315
2316 return try _accept(sockfd, addr, addr_size, flags);
2317}
2318
2319/// Accept a connection on a socket.
2320/// If the application has a global event loop enabled, EAGAIN is handled
2321/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
2322pub fn accept(
2323 /// This argument is a socket that has been created with `socket`, bound to a local address
2324 /// with `bind`, and is listening for connections after a `listen`.
2325 sockfd: fd_t,
2326 /// This argument is a pointer to a sockaddr structure. This structure is filled in with the
2327 /// address of the peer socket, as known to the communications layer. The exact format of the
2328 /// address returned addr is determined by the socket's address family (see `socket` and the
2329 /// respective protocol man pages).
2330 addr: *sockaddr,
2331 /// This argument is a value-result argument: the caller must initialize it to contain the
2332 /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
2333 /// of the peer address.
2334 ///
2335 /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size`
2336 /// will return a value greater than was supplied to the call.
2337 addr_size: *socklen_t,
2338) AcceptError!fd_t {
2339 return try _accept(sockfd, addr, addr_size, 0);
2340}
2341
2342fn _accept(sockfd: fd_t, addr: *sockaddr, addr_size: *socklen_t, flags: u32) AcceptError!fd_t {
2312 while (true) {2343 while (true) {
2313 const rc = system.accept4(sockfd, addr, addr_size, flags);2344 const rc = func: {
2345 switch (comptime builtin.os.tag) {
2346 .linux, .freebsd, .netbsd, .dragonfly =>
2347 break :func system.accept4(sockfd, addr, addr_size, flags),
2348 .ios, .macosx, .watchos, .tvos => {
2349 assert(flags == 0);
2350 break :func system.accept(sockfd, addr, addr_size);
2351 },
2352 else => @compileError("accept not available for target"),
2353 }
2354 };
2355
2314 switch (errno(rc)) {2356 switch (errno(rc)) {
2315 0 => return @intCast(fd_t, rc),2357 0 => return @intCast(fd_t, rc),
2316 EINTR => continue,2358 EINTR => continue,
2317
2318 EAGAIN => if (std.event.Loop.instance) |loop| {2359 EAGAIN => if (std.event.Loop.instance) |loop| {
2319 loop.waitUntilFdReadable(sockfd);2360 loop.waitUntilFdReadable(sockfd);
2320 continue;2361 continue;
...@@ -2333,7 +2374,6 @@ pub fn accept4(...@@ -2333,7 +2374,6 @@ pub fn accept4(
2333 EOPNOTSUPP => unreachable,2374 EOPNOTSUPP => unreachable,
2334 EPROTO => return error.ProtocolFailure,2375 EPROTO => return error.ProtocolFailure,
2335 EPERM => return error.BlockedByFirewall,2376 EPERM => return error.BlockedByFirewall,
2336
2337 else => |err| return unexpectedErrno(err),2377 else => |err| return unexpectedErrno(err),
2338 }2378 }
2339 }2379 }