From e87ceb76c242cf991f85c9aa6619f5faf4059af3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 17 Oct 2025 00:09:25 -0700 Subject: [PATCH] std.Io.net.Server: refine AcceptError set --- lib/std/Io/Threaded.zig | 76 +++++++++++++++++++++-------------------- lib/std/Io/net.zig | 21 +++++++++++- lib/std/posix.zig | 39 +-------------------- 3 files changed, 60 insertions(+), 76 deletions(-) diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index cdaf06a7c0..1d137ebe85 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -898,7 +898,7 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: .CANCELED => return error.Canceled, .ACCES => return error.AccessDenied, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .PERM => return error.PermissionDenied, .DQUOT => return error.DiskQuota, .EXIST => return error.PathAlreadyExists, @@ -930,7 +930,7 @@ fn dirMakeWasi(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: I .CANCELED => return error.Canceled, .ACCES => return error.AccessDenied, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .PERM => return error.PermissionDenied, .DQUOT => return error.DiskQuota, .EXIST => return error.PathAlreadyExists, @@ -989,7 +989,7 @@ fn dirStatPathLinux( .CANCELED => return error.Canceled, .ACCES => return error.AccessDenied, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .FAULT => |err| return errnoBug(err), .INVAL => |err| return errnoBug(err), .LOOP => return error.SymLinkLoop, @@ -1024,7 +1024,7 @@ fn dirStatPathPosix( .CANCELED => return error.Canceled, .INVAL => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // Always a race condition. + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .NOMEM => return error.SystemResources, .ACCES => return error.AccessDenied, .PERM => return error.PermissionDenied, @@ -1060,7 +1060,7 @@ fn dirStatPathWasi( .CANCELED => return error.Canceled, .INVAL => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // Always a race condition. + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .NOMEM => return error.SystemResources, .ACCES => return error.AccessDenied, .FAULT => |err| return errnoBug(err), @@ -1088,7 +1088,7 @@ fn fileStatPosix(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File .CANCELED => return error.Canceled, .INVAL => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .NOMEM => return error.SystemResources, .ACCES => return error.AccessDenied, else => |err| return posix.unexpectedErrno(err), @@ -1115,7 +1115,7 @@ fn fileStatLinux(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File .CANCELED => return error.Canceled, .ACCES => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .FAULT => |err| return errnoBug(err), .INVAL => |err| return errnoBug(err), .LOOP => |err| return errnoBug(err), @@ -1147,7 +1147,7 @@ fn fileStatWasi(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File. .CANCELED => return error.Canceled, .INVAL => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .NOMEM => return error.SystemResources, .ACCES => return error.AccessDenied, .NOTCAPABLE => return error.AccessDenied, @@ -1220,7 +1220,7 @@ fn dirAccessWasi( .CANCELED => return error.Canceled, .INVAL => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // Always a race condition. + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .NOMEM => return error.SystemResources, .ACCES => return error.AccessDenied, .FAULT => |err| return errnoBug(err), @@ -1305,7 +1305,7 @@ fn dirCreateFilePosix( .FAULT => |err| return errnoBug(err), .INVAL => return error.BadPathName, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .ACCES => return error.AccessDenied, .FBIG => return error.FileTooBig, .OVERFLOW => return error.FileTooBig, @@ -1347,7 +1347,7 @@ fn dirCreateFilePosix( .INTR => continue, .CANCELED => return error.Canceled, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .INVAL => |err| return errnoBug(err), // invalid parameters .NOLCK => return error.SystemResources, .AGAIN => return error.WouldBlock, @@ -1427,7 +1427,7 @@ fn dirCreateFileWasi( .FAULT => |err| return errnoBug(err), // Provides INVAL with a linux host on a bad path name, but NOENT on Windows .INVAL => return error.BadPathName, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .ACCES => return error.AccessDenied, .FBIG => return error.FileTooBig, .OVERFLOW => return error.FileTooBig, @@ -1506,7 +1506,7 @@ fn dirOpenFile( .FAULT => |err| return errnoBug(err), .INVAL => return error.BadPathName, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .ACCES => return error.AccessDenied, .FBIG => return error.FileTooBig, .OVERFLOW => return error.FileTooBig, @@ -1548,7 +1548,7 @@ fn dirOpenFile( .INTR => continue, .CANCELED => return error.Canceled, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .INVAL => |err| return errnoBug(err), // invalid parameters .NOLCK => return error.SystemResources, .AGAIN => return error.WouldBlock, @@ -1653,7 +1653,7 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File .INVAL => |err| return errnoBug(err), .FAULT => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .IO => return error.InputOutput, .ISDIR => return error.IsDir, .NOBUFS => return error.SystemResources, @@ -1678,7 +1678,7 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File .FAULT => |err| return errnoBug(err), .SRCH => return error.ProcessNotFound, .AGAIN => return error.WouldBlock, - .BADF => return error.NotOpenForReading, // can be a race condition + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .IO => return error.InputOutput, .ISDIR => return error.IsDir, .NOBUFS => return error.SystemResources, @@ -1779,7 +1779,7 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset .INVAL => |err| return errnoBug(err), .FAULT => |err| return errnoBug(err), .AGAIN => |err| return errnoBug(err), - .BADF => return error.NotOpenForReading, // can be a race condition + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .IO => return error.InputOutput, .ISDIR => return error.IsDir, .NOBUFS => return error.SystemResources, @@ -1807,7 +1807,7 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset .FAULT => |err| return errnoBug(err), .SRCH => return error.ProcessNotFound, .AGAIN => return error.WouldBlock, - .BADF => return error.NotOpenForReading, // can be a race condition + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .IO => return error.InputOutput, .ISDIR => return error.IsDir, .NOBUFS => return error.SystemResources, @@ -1844,7 +1844,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekErr .INTR => continue, .CANCELED => return error.Canceled, - .BADF => |err| return errnoBug(err), // Always a race condition. + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .INVAL => return error.Unseekable, .OVERFLOW => return error.Unseekable, .SPIPE => return error.Unseekable, @@ -1866,7 +1866,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekErr .INTR => continue, .CANCELED => return error.Canceled, - .BADF => |err| return errnoBug(err), // Always a race condition. + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .INVAL => return error.Unseekable, .OVERFLOW => return error.Unseekable, .SPIPE => return error.Unseekable, @@ -1885,7 +1885,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekErr .INTR => continue, .CANCELED => return error.Canceled, - .BADF => |err| return errnoBug(err), // Always a race condition. + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .INVAL => return error.Unseekable, .OVERFLOW => return error.Unseekable, .SPIPE => return error.Unseekable, @@ -2111,7 +2111,7 @@ fn netListenIpPosix( switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) { .SUCCESS => break, .ADDRINUSE => return error.AddressInUse, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. else => |err| return posix.unexpectedErrno(err), } } @@ -2150,7 +2150,7 @@ fn netListenUnix( switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) { .SUCCESS => break, .ADDRINUSE => return error.AddressInUse, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. else => |err| return posix.unexpectedErrno(err), } } @@ -2178,7 +2178,7 @@ fn posixBindUnix(t: *Threaded, fd: posix.socket_t, addr: *const posix.sockaddr, .ROFS => return error.ReadOnlyFileSystem, .PERM => return error.PermissionDenied, - .BADF => |err| return errnoBug(err), // always a race condition if this error is returned + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .INVAL => |err| return errnoBug(err), // invalid parameters .NOTSOCK => |err| return errnoBug(err), // invalid `sockfd` .FAULT => |err| return errnoBug(err), // invalid `addr` pointer @@ -2197,7 +2197,7 @@ fn posixBind(t: *Threaded, socket_fd: posix.socket_t, addr: *const posix.sockadd .CANCELED => return error.Canceled, .ADDRINUSE => return error.AddressInUse, - .BADF => |err| return errnoBug(err), // always a race condition if this error is returned + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .INVAL => |err| return errnoBug(err), // invalid parameters .NOTSOCK => |err| return errnoBug(err), // invalid `sockfd` .AFNOSUPPORT => return error.AddressFamilyUnsupported, @@ -2221,7 +2221,7 @@ fn posixConnect(t: *Threaded, socket_fd: posix.socket_t, addr: *const posix.sock .AFNOSUPPORT => return error.AddressFamilyUnsupported, .AGAIN, .INPROGRESS => return error.WouldBlock, .ALREADY => return error.ConnectionPending, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .CONNREFUSED => return error.ConnectionRefused, .CONNRESET => return error.ConnectionResetByPeer, .FAULT => |err| return errnoBug(err), @@ -2260,7 +2260,7 @@ fn posixConnectUnix(t: *Threaded, fd: posix.socket_t, addr: *const posix.sockadd .ROFS => return error.ReadOnlyFileSystem, .PERM => return error.PermissionDenied, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .CONNABORTED => |err| return errnoBug(err), .FAULT => |err| return errnoBug(err), .ISCONN => |err| return errnoBug(err), @@ -2279,7 +2279,7 @@ fn posixGetSockName(t: *Threaded, socket_fd: posix.fd_t, addr: *posix.sockaddr, .INTR => continue, .CANCELED => return error.Canceled, - .BADF => |err| return errnoBug(err), // always a race condition + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .FAULT => |err| return errnoBug(err), .INVAL => |err| return errnoBug(err), // invalid parameters .NOTSOCK => |err| return errnoBug(err), // always a race condition @@ -2298,7 +2298,7 @@ fn setSocketOption(t: *Threaded, fd: posix.fd_t, level: i32, opt_name: u32, opti .INTR => continue, .CANCELED => return error.Canceled, - .BADF => |err| return errnoBug(err), // always a race condition + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .NOTSOCK => |err| return errnoBug(err), // always a race condition .INVAL => |err| return errnoBug(err), .FAULT => |err| return errnoBug(err), @@ -2430,6 +2430,7 @@ fn openSocketPosix( } fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Server.AcceptError!net.Stream { + if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); var storage: PosixAddress = undefined; var addr_len: posix.socklen_t = @sizeOf(PosixAddress); @@ -2456,11 +2457,12 @@ fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Serve }, .INTR => continue, .CANCELED => return error.Canceled, + .AGAIN => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // always a race condition + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .CONNABORTED => return error.ConnectionAborted, .FAULT => |err| return errnoBug(err), - .INVAL => return error.SocketNotListening, + .INVAL => |err| return errnoBug(err), .NOTSOCK => |err| return errnoBug(err), .MFILE => return error.ProcessFdQuotaExceeded, .NFILE => return error.SystemFdQuotaExceeded, @@ -2504,7 +2506,7 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net. .INVAL => |err| return errnoBug(err), .FAULT => |err| return errnoBug(err), .AGAIN => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .NOBUFS => return error.SystemResources, .NOMEM => return error.SystemResources, .NOTCONN => return error.SocketUnconnected, @@ -2526,7 +2528,7 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net. .INVAL => |err| return errnoBug(err), .FAULT => |err| return errnoBug(err), .AGAIN => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // Always a race condition. + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .NOBUFS => return error.SystemResources, .NOMEM => return error.SystemResources, .NOTCONN => return error.SocketUnconnected, @@ -2625,7 +2627,7 @@ fn netSendOne( .ACCES => return error.AccessDenied, .ALREADY => return error.FastOpenAlreadyInProgress, - .BADF => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .CONNRESET => return error.ConnectionResetByPeer, .DESTADDRREQ => |err| return errnoBug(err), .FAULT => |err| return errnoBug(err), @@ -2694,7 +2696,7 @@ fn netSendMany( .AGAIN => |err| return errnoBug(err), .ALREADY => return error.FastOpenAlreadyInProgress, - .BADF => |err| return errnoBug(err), // Always a race condition. + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .CONNRESET => return error.ConnectionResetByPeer, .DESTADDRREQ => |err| return errnoBug(err), // The socket is not connection-mode, and no peer address is set. .FAULT => |err| return errnoBug(err), // An invalid user space address was specified for an argument. @@ -2905,7 +2907,7 @@ fn netWritePosix( .ACCES => |err| return errnoBug(err), .AGAIN => |err| return errnoBug(err), .ALREADY => return error.FastOpenAlreadyInProgress, - .BADF => |err| return errnoBug(err), // always a race condition + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .CONNRESET => return error.ConnectionResetByPeer, .DESTADDRREQ => |err| return errnoBug(err), // The socket is not connection-mode, and no peer address is set. .FAULT => |err| return errnoBug(err), // An invalid user space address was specified for an argument. @@ -2979,7 +2981,7 @@ fn netInterfaceNameResolve( .INVAL => |err| return errnoBug(err), // Bad parameters. .NOTTY => |err| return errnoBug(err), .NXIO => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // Always a race condition. + .BADF => |err| return errnoBug(err), // File descriptor used after closed. .FAULT => |err| return errnoBug(err), // Bad pointer parameter. .IO => |err| return errnoBug(err), // sock_fd is not a file descriptor .NODEV => return error.InterfaceNotFound, diff --git a/lib/std/Io/net.zig b/lib/std/Io/net.zig index ca18325e2a..17d82451c5 100644 --- a/lib/std/Io/net.zig +++ b/lib/std/Io/net.zig @@ -1312,7 +1312,26 @@ pub const Server = struct { s.* = undefined; } - pub const AcceptError = std.posix.AcceptError || Io.Cancelable; + pub const AcceptError = error{ + /// The per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, + /// The system-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, + /// Not enough free memory. This often means that the memory allocation is limited + /// by the socket buffer limits, not by the system memory. + SystemResources, + /// The network subsystem has failed. + NetworkDown, + /// No connection is already queued and ready to be accepted, and + /// the socket is configured as non-blocking. + WouldBlock, + /// An incoming connection was indicated, but was subsequently terminated by the + /// remote peer prior to accepting the call. + ConnectionAborted, + /// Firewall rules forbid connection. + BlockedByFirewall, + ProtocolFailure, + } || Io.UnexpectedError || Io.Cancelable; /// Blocks until a client connects to the server. pub fn accept(s: *Server, io: Io) AcceptError!Stream { diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 9d58a4f643..358715a61f 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -3622,44 +3622,7 @@ pub fn listen(sock: socket_t, backlog: u31) ListenError!void { } } -pub const AcceptError = error{ - ConnectionAborted, - - /// The file descriptor sockfd does not refer to a socket. - FileDescriptorNotASocket, - - /// The per-process limit on the number of open file descriptors has been reached. - ProcessFdQuotaExceeded, - - /// The system-wide limit on the total number of open files has been reached. - SystemFdQuotaExceeded, - - /// Not enough free memory. This often means that the memory allocation is limited - /// by the socket buffer limits, not by the system memory. - SystemResources, - - /// Socket is not listening for new connections. - SocketNotListening, - - ProtocolFailure, - - /// Firewall rules forbid connection. - BlockedByFirewall, - - /// This error occurs when no global event loop is configured, - /// and accepting from the socket would block. - WouldBlock, - - /// An incoming connection was indicated, but was subsequently terminated by the - /// remote peer prior to accepting the call. - ConnectionResetByPeer, - - /// The network subsystem has failed. - NetworkDown, - - /// The referenced socket is not a type that supports connection-oriented service. - OperationNotSupported, -} || UnexpectedError; +pub const AcceptError = std.Io.net.Server.AcceptError; /// Accept a connection on a socket. /// If `sockfd` is opened in non blocking mode, the function will