add ERRNO for macOS, improve net error messages (#885)

* lib/std/libc: add ERRNO values for macOS

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/net: improve error messages

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
This commit is contained in:
Pierre Curto
2023-07-26 13:41:35 +02:00
committed by GitHub
parent 59b077223b
commit a376d8e2bf
3 changed files with 213 additions and 119 deletions

View File

@@ -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
/*

View File

@@ -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)

View File

@@ -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()!;
}