From 631eb6783d1dbc64f031426436ab685008c31ca8 Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 28 Nov 2019 22:19:42 -0300 Subject: [PATCH 01/11] add StreamServer.Options.reuse_address this uses a bad direct interface with std.os.linux, this should add setsockopt to std.os. --- lib/std/net.zig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/std/net.zig b/lib/std/net.zig index 53b75d857..cd456c46c 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1290,6 +1290,9 @@ pub const StreamServer = struct { /// If more than this many connections pool in the kernel, clients will start /// seeing "Connection refused". kernel_backlog: u32 = 128, + + /// Enable SO_REUSEADDR on the socket. + reuse_address: bool = false, }; /// After this call succeeds, resources have been acquired and must @@ -1320,6 +1323,19 @@ pub const StreamServer = struct { self.sockfd = null; } + // TODO proper interface with errors in std.os + var optval: c_int = 1; + + if (self.options.reuse_address) { + _ = os.linux.setsockopt( + server.sockfd.?, + os.linux.SOL_SOCKET, + os.linux.SO_REUSEADDR, + @ptrCast([*]const u8, &optval), + @sizeOf(c_int), + ); + } + var socklen = address.getOsSockLen(); try os.bind(sockfd, &address.any, socklen); try os.listen(sockfd, self.kernel_backlog); From 4a4d2c0d80443a00945beeff4e3acaa9e7ea59cb Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 29 Nov 2019 17:17:09 -0300 Subject: [PATCH 02/11] os: add setsockopt - net: use os.setsockopt() --- lib/std/net.zig | 12 +++++------- lib/std/os.zig | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index cd456c46c..effe8e74c 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1323,14 +1323,12 @@ pub const StreamServer = struct { self.sockfd = null; } - // TODO proper interface with errors in std.os - var optval: c_int = 1; - if (self.options.reuse_address) { - _ = os.linux.setsockopt( - server.sockfd.?, - os.linux.SOL_SOCKET, - os.linux.SO_REUSEADDR, + var optval: c_int = 1; + try os.setsockopt( + self.sockfd.?, + os.SOL_SOCKET, + os.SO_REUSEADDR, @ptrCast([*]const u8, &optval), @sizeOf(c_int), ); diff --git a/lib/std/os.zig b/lib/std/os.zig index 3d1b3540b..27327c101 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3250,3 +3250,23 @@ pub fn sched_yield() SchedYieldError!void { else => return error.SystemCannotYield, } } + +/// Set a socket's options. +pub fn setsockopt(fd: fd_t, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) SetSockOptError!void { + const rc = system.setsockopt(); + + switch (errno(system.setsockopt(fd, level, optname, optval, optlen))) { + 0 => {}, + EBADF => unreachable, + EINVAL => unreachable, + EDOM => return error.TimeoutTooBig, + EISCONN => return error.AlreadyConnected, + ENOPROTOOOPT => return error.InvalidProtocolOption, + ENOTSOCK => return error.NotSocket, + + ENOMEM => return error.OutOfMemory, + ENOBUFS => return error.SystemResources, + + else => |err| return std.os.unexpectedErrno(err), + } +} From 0e67568bcac0c4aea795969e2c8b733326b87bec Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 29 Nov 2019 17:36:05 -0300 Subject: [PATCH 03/11] net: fix Options - os: fix typos on setsockopt --- lib/std/net.zig | 4 +++- lib/std/os.zig | 6 ++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index effe8e74c..531900cf9 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1279,6 +1279,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) pub const StreamServer = struct { /// Copied from `Options` on `init`. kernel_backlog: u32, + reuse_address: bool, /// `undefined` until `listen` returns successfully. listen_address: Address, @@ -1301,6 +1302,7 @@ pub const StreamServer = struct { return StreamServer{ .sockfd = null, .kernel_backlog = options.kernel_backlog, + .reuse_address = options.reuse_address, .listen_address = undefined, }; } @@ -1323,7 +1325,7 @@ pub const StreamServer = struct { self.sockfd = null; } - if (self.options.reuse_address) { + if (self.reuse_address) { var optval: c_int = 1; try os.setsockopt( self.sockfd.?, diff --git a/lib/std/os.zig b/lib/std/os.zig index 27327c101..7d20802ba 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3252,16 +3252,14 @@ pub fn sched_yield() SchedYieldError!void { } /// Set a socket's options. -pub fn setsockopt(fd: fd_t, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) SetSockOptError!void { - const rc = system.setsockopt(); - +pub fn setsockopt(fd: fd_t, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) !void { switch (errno(system.setsockopt(fd, level, optname, optval, optlen))) { 0 => {}, EBADF => unreachable, EINVAL => unreachable, EDOM => return error.TimeoutTooBig, EISCONN => return error.AlreadyConnected, - ENOPROTOOOPT => return error.InvalidProtocolOption, + ENOPROTOOPT => return error.InvalidProtocolOption, ENOTSOCK => return error.NotSocket, ENOMEM => return error.OutOfMemory, From d423bb3808a96deb8595bb164c5f248750edfe84 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 1 Dec 2019 11:59:10 -0300 Subject: [PATCH 04/11] std.c: add setsockopt --- lib/std/c.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/c.zig b/lib/std/c.zig index 845061b2c..cf31100a7 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -121,6 +121,7 @@ pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias add pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const 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: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int; +pub extern "c" fn setsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: socklen_t) c_int; pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize; pub extern "c" fn sendto( sockfd: fd_t, From 3ccbf3cfc2e816a5c88b3021a86ee7da79be1876 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 1 Dec 2019 11:59:51 -0300 Subject: [PATCH 05/11] move parameters on sockopt functions to c_uint this makes them consistent with the linux syscalls --- lib/std/c.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index cf31100a7..4e4c824cf 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -120,8 +120,8 @@ 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 accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int; -pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int; -pub extern "c" fn setsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: socklen_t) c_int; +pub extern "c" fn getsockopt(sockfd: fd_t, level: c_uint, optname: c_uint, optval: *c_void, optlen: *socklen_t) c_int; +pub extern "c" fn setsockopt(sockfd: fd_t, level: c_uint, optname: c_uint, optval: *c_void, optlen: socklen_t) c_int; pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize; pub extern "c" fn sendto( sockfd: fd_t, From ea8f496970f95a11417a9c8f5a671e3676681d1e Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 1 Dec 2019 13:11:13 -0300 Subject: [PATCH 06/11] std.c: c_uint -> u32 for sockopt functions --- lib/std/c.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 4e4c824cf..684758286 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -120,8 +120,8 @@ 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 accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int; -pub extern "c" fn getsockopt(sockfd: fd_t, level: c_uint, optname: c_uint, optval: *c_void, optlen: *socklen_t) c_int; -pub extern "c" fn setsockopt(sockfd: fd_t, level: c_uint, optname: c_uint, optval: *c_void, optlen: socklen_t) 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: *c_void, optlen: socklen_t) c_int; pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize; pub extern "c" fn sendto( sockfd: fd_t, From 5efc0ea89e76a270a0232fdb87ca4c0448ae65be Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 1 Dec 2019 13:12:23 -0300 Subject: [PATCH 07/11] std.os: make setsockopt receive a slice as option --- lib/std/os.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 7d20802ba..855a61d95 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3252,8 +3252,8 @@ pub fn sched_yield() SchedYieldError!void { } /// Set a socket's options. -pub fn setsockopt(fd: fd_t, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) !void { - switch (errno(system.setsockopt(fd, level, optname, optval, optlen))) { +pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) !void { + switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) { 0 => {}, EBADF => unreachable, EINVAL => unreachable, @@ -3261,7 +3261,6 @@ pub fn setsockopt(fd: fd_t, level: u32, optname: u32, optval: [*]const u8, optle EISCONN => return error.AlreadyConnected, ENOPROTOOPT => return error.InvalidProtocolOption, ENOTSOCK => return error.NotSocket, - ENOMEM => return error.OutOfMemory, ENOBUFS => return error.SystemResources, From 50e6a27c297bf73d97b1b4bb46c5529f5495ab8f Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 1 Dec 2019 13:17:52 -0300 Subject: [PATCH 08/11] std.net: fix setsockopt call --- lib/std/net.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 531900cf9..a7c6797de 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1326,13 +1326,12 @@ pub const StreamServer = struct { } if (self.reuse_address) { - var optval: c_int = 1; + var opt = [_]u8{1} ** @sizeOf(c_int); try os.setsockopt( self.sockfd.?, os.SOL_SOCKET, os.SO_REUSEADDR, - @ptrCast([*]const u8, &optval), - @sizeOf(c_int), + &opt, ); } From 0d852effe342595e36ed1fd5a5d4fdacf0f57f00 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 1 Dec 2019 13:23:43 -0300 Subject: [PATCH 09/11] std.net: use mem.toBytes --- lib/std/net.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index a7c6797de..d4e68b2e1 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1326,12 +1326,11 @@ pub const StreamServer = struct { } if (self.reuse_address) { - var opt = [_]u8{1} ** @sizeOf(c_int); try os.setsockopt( self.sockfd.?, os.SOL_SOCKET, os.SO_REUSEADDR, - &opt, + &mem.toBytes(@as(c_int, 1)), ); } From 22f6297157d713ba7a511773b6dc2b095c722346 Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 30 Dec 2019 12:41:23 -0300 Subject: [PATCH 10/11] std.os: update error set for setsockopt --- lib/std/os.zig | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 855a61d95..965decb10 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3257,13 +3257,10 @@ pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) !void { 0 => {}, EBADF => unreachable, EINVAL => unreachable, - EDOM => return error.TimeoutTooBig, + EFAULT => unreachable, EISCONN => return error.AlreadyConnected, ENOPROTOOPT => return error.InvalidProtocolOption, - ENOTSOCK => return error.NotSocket, - ENOMEM => return error.OutOfMemory, - ENOBUFS => return error.SystemResources, - - else => |err| return std.os.unexpectedErrno(err), + ENOTSOCK => unreachable, + else => |err| return unexpectedErrno(err), } } From 99f6f8ead90d36eab9b50fc1f2792a2bdfab8867 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Dec 2019 19:35:05 -0500 Subject: [PATCH 11/11] update setsockopt error set according to POSIX In the code review I accidentally encouraged Luna to remove some handling of errors that are possible according to POSIX, but I think how Luna had it before was better, so I fixed it, and now the branch should be good to merge. --- lib/std/os.zig | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 965decb10..f7426faed 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3251,16 +3251,33 @@ pub fn sched_yield() SchedYieldError!void { } } +pub const SetSockOptError = error{ + /// The socket is already connected, and a specified option cannot be set while the socket is connected. + AlreadyConnected, + + /// The option is not supported by the protocol. + InvalidProtocolOption, + + /// The send and receive timeout values are too big to fit into the timeout fields in the socket structure. + TimeoutTooBig, + + /// Insufficient resources are available in the system to complete the call. + SystemResources, +} || UnexpectedError; + /// Set a socket's options. -pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) !void { +pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void { switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) { 0 => {}, - EBADF => unreachable, + EBADF => unreachable, // always a race condition + ENOTSOCK => unreachable, // always a race condition EINVAL => unreachable, EFAULT => unreachable, + EDOM => return error.TimeoutTooBig, EISCONN => return error.AlreadyConnected, ENOPROTOOPT => return error.InvalidProtocolOption, - ENOTSOCK => unreachable, + ENOMEM => return error.SystemResources, + ENOBUFS => return error.SystemResources, else => |err| return unexpectedErrno(err), } }