diff --git a/lib/std/c.zig b/lib/std/c.zig index 845061b2c..684758286 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -120,7 +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 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, diff --git a/lib/std/net.zig b/lib/std/net.zig index 53b75d857..d4e68b2e1 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, @@ -1290,6 +1291,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 @@ -1298,6 +1302,7 @@ pub const StreamServer = struct { return StreamServer{ .sockfd = null, .kernel_backlog = options.kernel_backlog, + .reuse_address = options.reuse_address, .listen_address = undefined, }; } @@ -1320,6 +1325,15 @@ pub const StreamServer = struct { self.sockfd = null; } + if (self.reuse_address) { + try os.setsockopt( + self.sockfd.?, + os.SOL_SOCKET, + os.SO_REUSEADDR, + &mem.toBytes(@as(c_int, 1)), + ); + } + var socklen = address.getOsSockLen(); try os.bind(sockfd, &address.any, socklen); try os.listen(sockfd, self.kernel_backlog); diff --git a/lib/std/os.zig b/lib/std/os.zig index 3d1b3540b..f7426faed 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3250,3 +3250,34 @@ pub fn sched_yield() SchedYieldError!void { else => return error.SystemCannotYield, } } + +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) SetSockOptError!void { + switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) { + 0 => {}, + 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, + ENOMEM => return error.SystemResources, + ENOBUFS => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } +}