From a376d8e2bf7fb6d1cb74e60ce31677f07e5d29f0 Mon Sep 17 00:00:00 2001 From: Pierre Curto Date: Wed, 26 Jul 2023 13:41:35 +0200 Subject: [PATCH] add ERRNO for macOS, improve net error messages (#885) * lib/std/libc: add ERRNO values for macOS Signed-off-by: Pierre Curto * lib/std/net: improve error messages Signed-off-by: Pierre Curto --------- Signed-off-by: Pierre Curto --- lib/std/libc/libc.c3 | 124 ++++++++++++++++++++------- lib/std/net/net.c3 | 18 +++- lib/std/net/socket.c3 | 190 +++++++++++++++++++++++------------------- 3 files changed, 213 insertions(+), 119 deletions(-) diff --git a/lib/std/libc/libc.c3 b/lib/std/libc/libc.c3 index 69e365a46..056e26ea4 100644 --- a/lib/std/libc/libc.c3 +++ b/lib/std/libc/libc.c3 @@ -460,22 +460,75 @@ const Errno EPIPE = 32; // Broken pipe const Errno EDOM = 33; // Math argument out of domain of func const Errno ERANGE = 34; // Math result not representable +// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/intro.2.html module libc::errno @if(env::DARWIN); -const Errno EDEADLK = 11; // Resource deadlock would occur MacOS -const Errno ENAMETOOLONG = 63; // File name too long MacOS -const Errno ELOOP = 62; // Too many symbolic links encountered -const Errno EOVERFLOW = 84; // Value too large for defined data type Macos -const Errno ECONNRESET = 54; // Connection reset by peer Macos -const Errno ENETDOWN = 50; // Network is down MacOS -const Errno ENETUNREACH = 51; // Network is unreachable MacOS -const Errno ENETRESET = 52; // Network dropped connection because of reset MacOS -const Errno EOPNOTSUPP = 45; // Operation not supported on transport endpoint -const Errno ENOTEMPTY = 66; // Directory not empty -const Errno ETIMEDOUT = 60; // Connection timed out -const Errno EINPROGRESS = 36; // Operation now in progress MacOS -const Errno EALREADY = 37; // Operation already in progress MacOS -const Errno EDQUOT = 69; // Quota exceeded, MacOS -const Errno EWOULDBLOCK = 35; // Operation would block +const Errno EWOULDBLOCK = EAGAIN; // Operation would block +const Errno EDEADLK = 11; // Resource deadlock would occur +const Errno EINPROGRESS = 36; // Operation now in progress +const Errno EALREADY = 37; // Operation already in progress +const Errno ENOTSOCK = 38; // Socket operation on non-socket +const Errno EDESTADDRREQ = 39; // Destination address required +const Errno EMSGSIZE = 40; // Message too long +const Errno EPROTOTYPE = 41; // Protocol wrong type for socket +const Errno ENOPROTOOPT = 42; // Protocol not available +const Errno EPROTONOSUPPORT = 43; // Protocol not supported +const Errno ESOCKTNOSUPPORT = 44; // Socket type not supported +const Errno ENOTSUP = 45; // Not supported +const Errno EPFNOSUPPORT = 46; // Protocol family not supported +const Errno EAFNOSUPPORT = 47; // Address family not supported by protocol family +const Errno EADDRINUSE = 48; // Address already in use +const Errno EADDRNOTAVAIL = 49; // Cannot assign requested address +const Errno ENETDOWN = 50; // Network is down +const Errno ENETUNREACH = 51; // Network is unreachable +const Errno ENETRESET = 52; // Network dropped connection on reset +const Errno ECONNABORTED = 53; // Software caused connection abort +const Errno ECONNRESET = 54; // Connection reset by peer +const Errno ENOBUFS = 55; // No buffer space available +const Errno EISCONN = 56; // Socket is already connected +const Errno ENOTCONN = 57; // Socket is not connected +const Errno ESHUTDOWN = 58; // Cannot send after socket shutdown +const Errno ETIMEDOUT = 60; // Operation timed out +const Errno ECONNREFUSED = 61; // Connection refused +const Errno ELOOP = 62; // Too many levels of symbolic links +const Errno ENAMETOOLONG = 63; // File name too long +const Errno EHOSTDOWN = 64; // Host is down +const Errno EHOSTUNREACH = 65; // No route to host +const Errno ENOTEMPTY = 66; // Directory not empty +const Errno EPROCLIM = 67; // Too many processes +const Errno EUSERS = 68; // Too many users +const Errno EDQUOT = 69; // Disc quota exceeded +const Errno ESTALE = 70; // Stale NFS file handle +const Errno EBADRPC = 72; // RPC struct is bad +const Errno ERPCMISMATCH = 73; // RPC version wrong +const Errno EPROGUNAVAIL = 74; // RPC prog. not avail +const Errno EPROGMISMATCH = 75; // Program version wrong +const Errno EPROCUNAVAIL = 76; // Bad procedure for program +const Errno ENOLCK = 77; // No locks available +const Errno ENOSYS = 78; // Function not implemented +const Errno EFTYPE = 79; // Inappropriate file type or format +const Errno EAUTH = 80; // Authentication error +const Errno ENEEDAUTH = 81; // Need authenticator +const Errno EPWROFF = 82; // Device power is off +const Errno EDEVERR = 83; // Device error +const Errno EOVERFLOW = 84; // Value too large to be stored in data type +const Errno EBADEXEC = 85; // Bad executable (or shared library) +const Errno EBADARCH = 86; // Bad CPU type in executable +const Errno ESHLIBVERS = 87; // Shared library version mismatch +const Errno EBADMACHO = 88; // Malformed Mach-o file +const Errno ECANCELED = 89; // Operation canceled +const Errno EIDRM = 90; // Identifier removed +const Errno ENOMSG = 91; // No message of desired type +const Errno EILSEQ = 92; // Illegal byte sequence +const Errno ENOATTR = 93; // Attribute not found +const Errno EBADMSG = 94; // Bad message +const Errno EMULTIHOP = 95; // Reserved +const Errno ENODATA = 96; // No message available +const Errno ENOLINK = 97; // Reserved +const Errno ENOSR = 98; // No STREAM resources +const Errno ENOSTR = 99; // Not a STREAM +const Errno EPROTO = 100; // Protocol error +const Errno ETIME = 101; // STREAM ioctl() timeout +const Errno EOPNOTSUPP = 102; // Operation not supported on socket module libc::errno @if(env::WIN32); const Errno EDEADLK = 36; // Resource deadlock would occur Win32 @@ -493,23 +546,34 @@ const Errno EALREADY = 103; // Operation already in progress const Errno EINPROGRESS = 112; // Operation now in progress Win32 const Errno EDQUOT = -122; // Quota exceeded, not in Win32 const Errno EWOULDBLOCK = 140; // Operation would block +// https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 +const Errno ENOTSOCK = 10038; // Network is unreachable +//const Errno EOPNOTSUPP = 10045; // Operation not supported on transport endpoint +const Errno EADDRINUSE = 10048; // Address already in use +const Errno EISCONN = 10056; // Socket is already connected +const Errno ECONNREFUSED = 10061; // Connection refused +//const Errno ENETUNREACH = 10065; // Network is unreachable module libc::errno @if(!env::WIN32 && !env::DARWIN); -const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?) -const Errno ENAMETOOLONG = 36; // File name too long Linux (others?) -const Errno ELOOP = 40; // Too many symbolic links encountered -const Errno EOVERFLOW = 75; // Value too large for defined data type -const Errno ENETDOWN = 100; // Network is down -const Errno ECONNRESET = 104; // Connection reset by peer -const Errno ENETUNREACH = 101; // Network is unreachable -const Errno ENETRESET = 102; // Network dropped connection because of reset -const Errno EOPNOTSUPP = 95; // Operation not supported on transport endpoint -const Errno ENOTEMPTY = 39; // Directory not empty -const Errno ETIMEDOUT = 110; // Connection timed out -const Errno EALREADY = 114; // Operation already in progress -const Errno EINPROGRESS = 115; // Operation now in progress -const Errno EDQUOT = 122; // Quota exceeded -const Errno EWOULDBLOCK = 41; // Operation would block +const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?) +const Errno ENAMETOOLONG = 36; // File name too long Linux (others?) +const Errno ENOTEMPTY = 39; // Directory not empty +const Errno ELOOP = 40; // Too many symbolic links encountered +const Errno EWOULDBLOCK = EAGAIN; // Operation would block +const Errno EOVERFLOW = 75; // Value too large for defined data type +const Errno ENOTSOCK = 88; // Socket operation on non-socket +const Errno EOPNOTSUPP = 95; // Operation not supported on transport endpoint +const Errno EADDRINUSE = 98; // Address already in use +const Errno ENETDOWN = 100; // Network is down +const Errno ENETUNREACH = 101; // Network is unreachable +const Errno ENETRESET = 102; // Network dropped connection because of reset +const Errno ECONNRESET = 104; // Connection reset by peer +const Errno EISCONN = 106; // Socket is already connected +const Errno ETIMEDOUT = 110; // Connection timed out +const Errno ECONNREFUSED = 111; // Connection refused +const Errno EALREADY = 114; // Operation already in progress +const Errno EINPROGRESS = 115; // Operation now in progress +const Errno EDQUOT = 122; // Quota exceeded /* diff --git a/lib/std/net/net.c3 b/lib/std/net/net.c3 index 34586f904..f9b149433 100644 --- a/lib/std/net/net.c3 +++ b/lib/std/net/net.c3 @@ -8,13 +8,23 @@ fault NetError INVALID_SOCKET, GENERAL_ERROR, INVALID_IP_STRING, - ADDRINFO_FAILED, + ADDRINFO_FAILED, CONNECT_FAILED, LISTEN_FAILED, ACCEPT_FAILED, - WRITE_FAILED, - READ_FAILED, - SOCKOPT_FAILED, + WRITE_FAILED, + READ_FAILED, + SOCKOPT_FAILED, + + BAD_SOCKET_DESCRIPTOR, + NOT_A_SOCKET, + CONNECTION_REFUSED, + CONNECTION_TIMED_OUT, + ADDRESS_IN_USE, + CONNECTION_ALREADY_IN_PROGRESS, + ALREADY_CONNECTED, + NETWORK_UNREACHABLE, + OPERATION_NOT_SUPPORTED_ON_SOCKET, } fn uint! ipv4toint(String s) diff --git a/lib/std/net/socket.c3 b/lib/std/net/socket.c3 index 5c49a0f9a..ddd3737e7 100644 --- a/lib/std/net/socket.c3 +++ b/lib/std/net/socket.c3 @@ -4,167 +4,187 @@ import libc; enum Network : char (int domain, int type) { - TCP (os::AF_INET, SOCK_STREAM), - TCP6 (os::AF_INET6, SOCK_STREAM), - UDP (os::AF_INET, SOCK_DGRAM), - UDP6 (os::AF_INET6, SOCK_DGRAM), + TCP (os::AF_INET, SOCK_STREAM), + TCP6 (os::AF_INET6, SOCK_STREAM), + UDP (os::AF_INET, SOCK_DGRAM), + UDP6 (os::AF_INET6, SOCK_DGRAM), } fn Socket! Network.connect(&self, String host, String port, SocketOption... options) { - @network_loop_over_ai(self, host, port; NativeSocket sockfd, AddrInfo* ai) - { - apply_sockoptions(sockfd, options)!; - int errcode = os::connect(sockfd, ai.ai_addr, ai.ai_addrlen); - // Keep the first successful connection. - if (errcode == 0) return network_socket(sockfd, ai); - }!; - return NetError.CONNECT_FAILED?; + @network_loop_over_ai(self, host, port; NativeSocket sockfd, AddrInfo* ai) + { + apply_sockoptions(sockfd, options)!; + int errcode = os::connect(sockfd, ai.ai_addr, ai.ai_addrlen); + // Keep the first successful connection. + if (errcode == 0) return network_socket(sockfd, ai); + }!; + switch (libc::errno()) + { + case errno::EACCES: return IoError.NO_PERMISSION?; + case errno::EADDRINUSE: return NetError.ADDRESS_IN_USE?; + case errno::EALREADY: return NetError.CONNECTION_ALREADY_IN_PROGRESS?; + case errno::EBADF: return NetError.BAD_SOCKET_DESCRIPTOR?; + case errno::ECONNREFUSED: return NetError.CONNECTION_REFUSED?; + case errno::EISCONN: return NetError.ALREADY_CONNECTED?; + case errno::ENETUNREACH: return NetError.NETWORK_UNREACHABLE?; + case errno::ENOTSOCK: return NetError.NOT_A_SOCKET?; + case errno::EOPNOTSUPP: return NetError.OPERATION_NOT_SUPPORTED_ON_SOCKET?; + case errno::ETIMEDOUT: return NetError.CONNECTION_TIMED_OUT?; + } +return NetError.CONNECT_FAILED?; } fn Socket! Network.listen(&self, String host, String port, int backlog, SocketOption... options) { - @network_loop_over_ai(self, host, port; NativeSocket sockfd, AddrInfo* ai) - { - apply_sockoptions(sockfd, options)!; - int errcode = os::bind(sockfd, ai.ai_addr, ai.ai_addrlen); - if (errcode == 0) - { - errcode = os::listen(sockfd, backlog); - // Keep the first successful connection. - if (errcode == 0) return network_socket(sockfd, ai); - } - }!; - return NetError.LISTEN_FAILED?; + @network_loop_over_ai(self, host, port; NativeSocket sockfd, AddrInfo* ai) + { + apply_sockoptions(sockfd, options)!; + int errcode = os::bind(sockfd, ai.ai_addr, ai.ai_addrlen); + if (errcode == 0) + { + errcode = os::listen(sockfd, backlog); + // Keep the first successful connection. + if (errcode == 0) return network_socket(sockfd, ai); + } + }!; + switch (libc::errno()) + { + case errno::EADDRINUSE: return NetError.ADDRESS_IN_USE?; + case errno::EBADF: return NetError.BAD_SOCKET_DESCRIPTOR?; + case errno::ENOTSOCK: return NetError.NOT_A_SOCKET?; + case errno::EOPNOTSUPP: return NetError.OPERATION_NOT_SUPPORTED_ON_SOCKET?; + } + return NetError.LISTEN_FAILED?; } fn AddrInfo*! Network.addrinfo(&self, String host, String port) @private { - ZString zhost = host.zstr_tcopy(); - ZString zport = port.zstr_tcopy(); - AddrInfo hints = { .ai_family = self.domain, .ai_socktype = self.type }; - AddrInfo* ai; - int errcode = os::getaddrinfo(zhost, zport, &hints, &ai); - if (errcode != 0) return NetError.ADDRINFO_FAILED?; - return ai; + ZString zhost = host.zstr_tcopy(); + ZString zport = port.zstr_tcopy(); + AddrInfo hints = { .ai_family = self.domain, .ai_socktype = self.type }; + AddrInfo* ai; + int errcode = os::getaddrinfo(zhost, zport, &hints, &ai); + if (errcode != 0) return NetError.ADDRINFO_FAILED?; + return ai; } macro apply_sockoptions(sockfd, options) @private { - Socket sock = { .sock = sockfd }; - foreach (o : options) sock.set_option(o)!; + Socket sock = { .sock = sockfd }; + foreach (o : options) sock.set_option(o)!; } macro @network_loop_over_ai(network, host, port; @body(fd, ai)) @private { - AddrInfo* ai = network.addrinfo(host, port)!; - AddrInfo* first = ai; - defer os::freeaddrinfo(first); - while (ai) - { - NativeSocket sockfd = os::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol); - if (sockfd > 0) - { - @body(sockfd, ai); - } - ai = ai.ai_next; - } + AddrInfo* ai = network.addrinfo(host, port)!; + AddrInfo* first = ai; + defer os::freeaddrinfo(first); + while (ai) + { + NativeSocket sockfd = os::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol); + if (sockfd > 0) + { + @body(sockfd, ai); + } + ai = ai.ai_next; + } } macro Socket network_socket(fd, ai) @private { - Socket sock = { .sock = fd, .ai_addrlen = ai.ai_addrlen }; - assert(sock.ai_addr_storage.len >= ai.ai_addrlen, "storage %d < addrlen %d", sock.ai_addr_storage.len, ai.ai_addrlen); - mem::copy(&sock.ai_addr_storage, (void*)ai.ai_addr, ai.ai_addrlen); - return sock; + Socket sock = { .sock = fd, .ai_addrlen = ai.ai_addrlen }; + assert(sock.ai_addr_storage.len >= ai.ai_addrlen, "storage %d < addrlen %d", sock.ai_addr_storage.len, ai.ai_addrlen); + mem::copy(&sock.ai_addr_storage, (void*)ai.ai_addr, ai.ai_addrlen); + return sock; } enum SocketOption : char (CInt value) @if(!env::WIN32) { - REUSEADDR (os::SO_REUSEADDR), - REUSEPORT (os::SO_REUSEPORT), - KEEPALIVE (os::SO_KEEPALIVE), - BROADCAST (os::SO_BROADCAST), + REUSEADDR (os::SO_REUSEADDR), + REUSEPORT (os::SO_REUSEPORT), + KEEPALIVE (os::SO_KEEPALIVE), + BROADCAST (os::SO_BROADCAST), } enum SocketOption : char (CInt value) @if(env::WIN32) { - REUSEADDR (os::SO_REUSEADDR), - KEEPALIVE (os::SO_KEEPALIVE), - BROADCAST (os::SO_BROADCAST), + REUSEADDR (os::SO_REUSEADDR), + KEEPALIVE (os::SO_KEEPALIVE), + BROADCAST (os::SO_BROADCAST), } struct Socket { - NativeSocket sock; - Socklen_t ai_addrlen; - // TODO proper way to get the size of sockaddr_storage - // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms740504(v=vs.85) - char[128] ai_addr_storage; + NativeSocket sock; + Socklen_t ai_addrlen; + // TODO proper way to get the size of sockaddr_storage + // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms740504(v=vs.85) + char[128] ai_addr_storage; } fn Stream Socket.as_stream(&self) { - return { .fns = &socketstream_interface, .data = self }; + return { .fns = &socketstream_interface, .data = self }; } StreamInterface socketstream_interface = { - .read_fn = fn(s, char[] bytes) => ((Socket*)s.data).read(bytes) @inline, - .write_fn = fn(s, char[] bytes) => ((Socket*)s.data).write(bytes) @inline, - .close_fn = fn(s) => ((Socket*)s.data).close(), + .read_fn = fn(s, char[] bytes) => ((Socket*)s.data).read(bytes) @inline, + .write_fn = fn(s, char[] bytes) => ((Socket*)s.data).write(bytes) @inline, + .close_fn = fn(s) => ((Socket*)s.data).close(), }; fn void! Socket.set_option(&self, SocketOption option) { - CInt flag = 1; - int errcode = os::setsockopt(self.sock, os::SOL_SOCKET, option.value, &flag, CInt.sizeof); - if (errcode != 0) return NetError.SOCKOPT_FAILED?; + CInt flag = 1; + int errcode = os::setsockopt(self.sock, os::SOL_SOCKET, option.value, &flag, CInt.sizeof); + if (errcode != 0) return NetError.SOCKOPT_FAILED?; } fn void! Socket.unset_option(&self, SocketOption option) { - CInt flag = 0; - int errcode = os::setsockopt(self.sock, os::SOL_SOCKET, option.value, &flag, CInt.sizeof); - if (errcode != 0) return NetError.SOCKOPT_FAILED?; + CInt flag = 0; + int errcode = os::setsockopt(self.sock, os::SOL_SOCKET, option.value, &flag, CInt.sizeof); + if (errcode != 0) return NetError.SOCKOPT_FAILED?; } fn usz! Socket.read(&self, char[] bytes) { - isz n = libc::read((Fd)self.sock, bytes.ptr, bytes.len); - if (n < 0) return NetError.READ_FAILED?; - return (usz)n; + isz n = libc::read((Fd)self.sock, bytes.ptr, bytes.len); + if (n < 0) return NetError.READ_FAILED?; + return (usz)n; } fn usz! Socket.write(&self, char[] bytes) { - isz n = libc::write((Fd)self.sock, bytes.ptr, bytes.len); - if (n < 0) return NetError.WRITE_FAILED?; - return (usz)n; + isz n = libc::write((Fd)self.sock, bytes.ptr, bytes.len); + if (n < 0) return NetError.WRITE_FAILED?; + return (usz)n; } fn void! Socket.close(&self) @inline { - self.sock.close()!; + self.sock.close()!; } struct Listener { - Socket socket; + Socket socket; } fn void! Listener.init(&self, Network network, String host, String port, int backlog = 10, SocketOption... options) { - *self = { .socket = network.listen(host, port, backlog, ...options)! }; + *self = { .socket = network.listen(host, port, backlog, ...options)! }; } fn Socket! Listener.accept(&self) { - Socket sock = self.socket; - sock.sock = os::accept(sock.sock, (SockAddrPtr)&sock.ai_addr_storage, &sock.ai_addrlen); - if (sock.sock < 0) return NetError.ACCEPT_FAILED?; - return sock; + Socket sock = self.socket; + sock.sock = os::accept(sock.sock, (SockAddrPtr)&sock.ai_addr_storage, &sock.ai_addrlen); + if (sock.sock < 0) return NetError.ACCEPT_FAILED?; + return sock; } fn void! Listener.close(&self) @inline { - self.socket.close()!; + self.socket.close()!; } \ No newline at end of file