diff options
Diffstat (limited to 'core/net/errors_linux.odin')
| -rw-r--r-- | core/net/errors_linux.odin | 384 |
1 files changed, 236 insertions, 148 deletions
diff --git a/core/net/errors_linux.odin b/core/net/errors_linux.odin index 3cd51e6fd..da9811d71 100644 --- a/core/net/errors_linux.odin +++ b/core/net/errors_linux.odin @@ -21,181 +21,269 @@ package net Feoramund: FreeBSD platform code */ -import "core:c" +import "core:reflect" import "core:sys/linux" -Create_Socket_Error :: enum c.int { - None = 0, - Family_Not_Supported_For_This_Socket = c.int(linux.Errno.EAFNOSUPPORT), - No_Socket_Descriptors_Available = c.int(linux.Errno.EMFILE), - No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS), - No_Memory_Available_Available = c.int(linux.Errno.ENOMEM), - Protocol_Unsupported_By_System = c.int(linux.Errno.EPROTONOSUPPORT), - Wrong_Protocol_For_Socket = c.int(linux.Errno.EPROTONOSUPPORT), - Family_And_Socket_Type_Mismatch = c.int(linux.Errno.EPROTONOSUPPORT), +@(private="file", thread_local) +_last_error: linux.Errno + +_last_platform_error :: proc() -> i32 { + return i32(_last_error) } -Dial_Error :: enum c.int { - None = 0, - Port_Required = -1, - - Address_In_Use = c.int(linux.Errno.EADDRINUSE), - In_Progress = c.int(linux.Errno.EINPROGRESS), - Cannot_Use_Any_Address = c.int(linux.Errno.EADDRNOTAVAIL), - Wrong_Family_For_Socket = c.int(linux.Errno.EAFNOSUPPORT), - Refused = c.int(linux.Errno.ECONNREFUSED), - Is_Listening_Socket = c.int(linux.Errno.EACCES), - Already_Connected = c.int(linux.Errno.EISCONN), - Network_Unreachable = c.int(linux.Errno.ENETUNREACH), // Device is offline - Host_Unreachable = c.int(linux.Errno.EHOSTUNREACH), // Remote host cannot be reached - No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS), - Not_Socket = c.int(linux.Errno.ENOTSOCK), - Timeout = c.int(linux.Errno.ETIMEDOUT), - - // TODO: we may need special handling for this; maybe make a socket a struct with metadata? - Would_Block = c.int(linux.Errno.EWOULDBLOCK), +_last_platform_error_string :: proc() -> string { + description, _ := reflect.enum_name_from_value(_last_error) + return description } -Bind_Error :: enum c.int { - None = 0, - Address_In_Use = c.int(linux.Errno.EADDRINUSE), // Another application is currently bound to this endpoint. - Given_Nonlocal_Address = c.int(linux.Errno.EADDRNOTAVAIL), // The address is not a local address on this machine. - Broadcast_Disabled = c.int(linux.Errno.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set. - Address_Family_Mismatch = c.int(linux.Errno.EFAULT), // The address family of the address does not match that of the socket. - Already_Bound = c.int(linux.Errno.EINVAL), // The socket is already bound to an address. - No_Ports_Available = c.int(linux.Errno.ENOBUFS), // There are not enough ephemeral ports available. +_set_last_platform_error :: proc(err: i32) { + _last_error = linux.Errno(err) } -Listen_Error :: enum c.int { - None = 0, - Address_In_Use = c.int(linux.Errno.EADDRINUSE), - Already_Connected = c.int(linux.Errno.EISCONN), - No_Socket_Descriptors_Available = c.int(linux.Errno.EMFILE), - No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS), - Nonlocal_Address = c.int(linux.Errno.EADDRNOTAVAIL), - Not_Socket = c.int(linux.Errno.ENOTSOCK), - Listening_Not_Supported_For_This_Socket = c.int(linux.Errno.EOPNOTSUPP), +_create_socket_error :: proc(errno: linux.Errno) -> Create_Socket_Error { + assert(errno != nil) + _last_error = errno + + #partial switch errno { + case .EMFILE, .ENFILE, .ENOBUFS, .EPROTONOSUPPORT: + return .Insufficient_Resources + case .EAFNOSUPPORT, .EPROTOTYPE: + return .Invalid_Argument + case .EACCES, .EPERM: + return .Insufficient_Permissions + case: + return .Unknown + } } -Accept_Error :: enum c.int { - None = 0, - Not_Listening = c.int(linux.Errno.EINVAL), - No_Socket_Descriptors_Available_For_Client_Socket = c.int(linux.Errno.EMFILE), - No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS), - Not_Socket = c.int(linux.Errno.ENOTSOCK), - Not_Connection_Oriented_Socket = c.int(linux.Errno.EOPNOTSUPP), +_dial_error :: proc(errno: linux.Errno) -> Dial_Error { + assert(errno != nil) + _last_error = errno - // TODO: we may need special handling for this; maybe make a socket a struct with metadata? - Would_Block = c.int(linux.Errno.EWOULDBLOCK), + #partial switch errno { + case .EAGAIN: + return .Insufficient_Resources + case .EBADF, .EINVAL, .ENOTSOCK, .EADDRNOTAVAIL, .EAFNOSUPPORT, .EFAULT: + return .Invalid_Argument + case .EISCONN: + return .Already_Connected + case .EALREADY: + return .Already_Connecting + case .EADDRINUSE: + return .Address_In_Use + case .ENETUNREACH: + return .Network_Unreachable + case .EHOSTUNREACH: + return .Host_Unreachable + case .ECONNREFUSED: + return .Refused + case .ECONNRESET: + return .Reset + case .ETIMEDOUT: + return .Timeout + case .EINPROGRESS: + return .Would_Block + case .EINTR: + return .Interrupted + case .EACCES: + return .Broadcast_Not_Supported + case: + return .Unknown + } } -TCP_Recv_Error :: enum c.int { - None = 0, - Shutdown = c.int(linux.Errno.ESHUTDOWN), - Not_Connected = c.int(linux.Errno.ENOTCONN), - Connection_Broken = c.int(linux.Errno.ENETRESET), - Not_Socket = c.int(linux.Errno.ENOTSOCK), - Aborted = c.int(linux.Errno.ECONNABORTED), - - // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them? - Connection_Closed = c.int(linux.Errno.ECONNRESET), - Offline = c.int(linux.Errno.ENETDOWN), - Host_Unreachable = c.int(linux.Errno.EHOSTUNREACH), - Interrupted = c.int(linux.Errno.EINTR), - Timeout = c.int(linux.Errno.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... +_bind_error :: proc(errno: linux.Errno) -> Bind_Error { + assert(errno != nil) + _last_error = errno + + #partial switch errno { + case .EAGAIN, .ENOTSOCK, .EADDRNOTAVAIL, .EAFNOSUPPORT, .EFAULT: + return .Insufficient_Resources + case .EINVAL: + return .Already_Bound + case .EBADF: + return .Invalid_Argument + case .EACCES: + return .Insufficient_Permissions_For_Address + case .EADDRINUSE: + return .Address_In_Use + case: + return .Unknown + } } -UDP_Recv_Error :: enum c.int { - None = 0, +_listen_error :: proc(errno: linux.Errno) -> Listen_Error { + assert(errno != nil) + _last_error = errno + + #partial switch errno { + case .EBADF, .ENOTSOCK: + return .Invalid_Argument + case .EDESTADDRREQ, .EOPNOTSUPP: + return .Unsupported_Socket + case .EINVAL: + return .Already_Connected + case: + return .Unknown + } +} + +_accept_error :: proc(errno: linux.Errno) -> Accept_Error { + assert(errno != nil) + _last_error = errno + + #partial switch errno { + case .EMFILE, .ENFILE, .ENOBUFS, .ENOMEM: + return .Insufficient_Resources + case .EBADF, .ENOTSOCK, .EFAULT: + return .Invalid_Argument + case .EINVAL: + return .Not_Listening + case .ECONNABORTED: + return .Aborted + case .EWOULDBLOCK: + return .Would_Block + case .EINTR: + return .Interrupted + case: + return .Unknown + } +} - Buffer_Too_Small = c.int(linux.Errno.EMSGSIZE), // The buffer is too small to fit the entire message, and the message was truncated. When this happens, the rest of message is lost. - Not_Socket = c.int(linux.Errno.ENOTSOCK), // The so-called socket is not an open socket. - Not_Descriptor = c.int(linux.Errno.EBADF), // The so-called socket is, in fact, not even a valid descriptor. - Bad_Buffer = c.int(linux.Errno.EFAULT), // The buffer did not point to a valid location in memory. - Interrupted = c.int(linux.Errno.EINTR), // A signal occurred before any data was transmitted. See signal(7). +_tcp_recv_error :: proc(errno: linux.Errno) -> TCP_Recv_Error { + assert(errno != nil) + _last_error = errno - // The send timeout duration passed before all data was received. See Socket_Option.Receive_Timeout. - // NOTE: No, really. Presumably this means something different for nonblocking sockets... - Timeout = c.int(linux.Errno.EWOULDBLOCK), - Socket_Not_Bound = c.int(linux.Errno.EINVAL), // The socket must be bound for this operation, but isn't. + #partial switch errno { + case .EBADF, .ENOTSOCK, .EFAULT: + return .Invalid_Argument + case .ENOTCONN: + return .Not_Connected + case .ECONNREFUSED: + return .Connection_Closed + case .ETIMEDOUT: + return .Timeout + case .EAGAIN: + return .Would_Block + case .EINTR: + return .Interrupted + case: + return .Unknown + } } -TCP_Send_Error :: enum c.int { - None = 0, - Aborted = c.int(linux.Errno.ECONNABORTED), - Connection_Closed = c.int(linux.Errno.ECONNRESET), - Not_Connected = c.int(linux.Errno.ENOTCONN), - Shutdown = c.int(linux.Errno.ESHUTDOWN), - - // The send queue was full. - // This is usually a transient issue. - // - // This also shouldn't normally happen on Linux, as data is dropped if it - // doesn't fit in the send queue. - No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS), - Offline = c.int(linux.Errno.ENETDOWN), - Host_Unreachable = c.int(linux.Errno.EHOSTUNREACH), - Interrupted = c.int(linux.Errno.EINTR), // A signal occurred before any data was transmitted. See signal(7). - Timeout = c.int(linux.Errno.EWOULDBLOCK), // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout. - Not_Socket = c.int(linux.Errno.ENOTSOCK), // The so-called socket is not an open socket. +_udp_recv_error :: proc(errno: linux.Errno) -> UDP_Recv_Error { + assert(errno != nil) + _last_error = errno + + #partial switch errno { + case .EBADF, .ENOTSOCK, .EFAULT: + return .Invalid_Argument + case .ECONNREFUSED, .ENOTCONN: + return .Connection_Refused + case .ETIMEDOUT: + return .Timeout + case .EAGAIN: + return .Would_Block + case .EINTR: + return .Interrupted + case: + return .Unknown + } } -// TODO -UDP_Send_Error :: enum c.int { - None = 0, - Message_Too_Long = c.int(linux.Errno.EMSGSIZE), // The message is larger than the maximum UDP packet size. No data was sent. - - // TODO: not sure what the exact circumstances for this is yet - Network_Unreachable = c.int(linux.Errno.ENETUNREACH), - No_Outbound_Ports_Available = c.int(linux.Errno.EAGAIN), // There are no more emphemeral outbound ports available to bind the socket to, in order to send. - - // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout. - // NOTE: No, really. Presumably this means something different for nonblocking sockets... - Timeout = c.int(linux.Errno.EWOULDBLOCK), - Not_Socket = c.int(linux.Errno.ENOTSOCK), // The so-called socket is not an open socket. - Not_Descriptor = c.int(linux.Errno.EBADF), // The so-called socket is, in fact, not even a valid descriptor. - Bad_Buffer = c.int(linux.Errno.EFAULT), // The buffer did not point to a valid location in memory. - Interrupted = c.int(linux.Errno.EINTR), // A signal occurred before any data was transmitted. See signal(7). - - // The send queue was full. - // This is usually a transient issue. - // - // This also shouldn't normally happen on Linux, as data is dropped if it - // doesn't fit in the send queue. - No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS), - No_Memory_Available = c.int(linux.Errno.ENOMEM), // No memory was available to properly manage the send queue. +_tcp_send_error :: proc(errno: linux.Errno) -> TCP_Send_Error { + assert(errno != nil) + _last_error = errno + + #partial switch errno { + case .EBADF, .EACCES, .ENOTSOCK, .EFAULT, .EMSGSIZE, .EDESTADDRREQ, .EINVAL, .EISCONN, .EOPNOTSUPP: + return .Invalid_Argument + case .ENOBUFS, .ENOMEM: + return .Insufficient_Resources + case .ECONNRESET, .EPIPE: + return .Connection_Closed + case .ENOTCONN: + return .Not_Connected + case .EHOSTUNREACH: + return .Host_Unreachable + case .EHOSTDOWN: + return .Host_Unreachable + case .ENETDOWN: + return .Network_Unreachable + case .EAGAIN: + return .Would_Block + case .EINTR: + return .Interrupted + case: + return .Unknown + } } -// TODO(flysand): slight regression -Shutdown_Manner :: enum c.int { - Receive = c.int(linux.Shutdown_How.RD), - Send = c.int(linux.Shutdown_How.WR), - Both = c.int(linux.Shutdown_How.RDWR), +_udp_send_error :: proc(errno: linux.Errno) -> UDP_Send_Error { + assert(errno != nil) + _last_error = errno + + #partial switch errno { + case .EBADF, .EACCES, .ENOTSOCK, .EFAULT, .EMSGSIZE, .EDESTADDRREQ, .EINVAL, .EISCONN, .EOPNOTSUPP: + return .Invalid_Argument + case .ENOBUFS, .ENOMEM: + return .Insufficient_Resources + case .ECONNRESET, .EPIPE: + return .Connection_Refused + case .EHOSTUNREACH: + return .Host_Unreachable + case .EHOSTDOWN: + return .Host_Unreachable + case .ENETDOWN: + return .Network_Unreachable + case .EAGAIN: + return .Would_Block + case .EINTR: + return .Interrupted + case: + return .Unknown + } } -Shutdown_Error :: enum c.int { - None = 0, - Aborted = c.int(linux.Errno.ECONNABORTED), - Reset = c.int(linux.Errno.ECONNRESET), - Offline = c.int(linux.Errno.ENETDOWN), - Not_Connected = c.int(linux.Errno.ENOTCONN), - Not_Socket = c.int(linux.Errno.ENOTSOCK), - Invalid_Manner = c.int(linux.Errno.EINVAL), +_shutdown_error :: proc(errno: linux.Errno) -> Shutdown_Error { + assert(errno != nil) + _last_error = errno + + #partial switch errno { + case .EBADF, .EINVAL, .ENOTSOCK, .ENOTCONN: + return .Invalid_Argument + case: + return .Unknown + } } -Socket_Option_Error :: enum c.int { - None = 0, - Offline = c.int(linux.Errno.ENETDOWN), - Timeout_When_Keepalive_Set = c.int(linux.Errno.ENETRESET), - Invalid_Option_For_Socket = c.int(linux.Errno.ENOPROTOOPT), - Reset_When_Keepalive_Set = c.int(linux.Errno.ENOTCONN), - Not_Socket = c.int(linux.Errno.ENOTSOCK), +_socket_option_error :: proc(errno: linux.Errno) -> Socket_Option_Error { + assert(errno != nil) + _last_error = errno + + #partial switch errno { + case .ENOMEM, .ENOBUFS: + return .Insufficient_Resources + case .EBADF, .ENOTSOCK: + return .Invalid_Socket + case .ENOPROTOOPT, .EINVAL: + return .Invalid_Option + case .EFAULT, .EDOM: + return .Invalid_Value + case: + return .Unknown + } } -Set_Blocking_Error :: enum c.int { - None = 0, +_set_blocking_error :: proc(errno: linux.Errno) -> Set_Blocking_Error { + assert(errno != nil) + _last_error = errno - // TODO: add errors occuring on followig calls: - // flags, _ := linux.Errno.fcntl(sd, linux.Errno.F_GETFL, 0) - // linux.Errno.fcntl(sd, linux.Errno.F_SETFL, flags | int(linux.Errno.O_NONBLOCK)) -}
\ No newline at end of file + #partial switch errno { + case .EBADF: + return .Invalid_Argument + case: + return .Unknown + } +} |