aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/net/common.odin14
-rw-r--r--core/net/errors.odin275
-rw-r--r--core/net/errors_darwin.odin360
-rw-r--r--core/net/errors_freebsd.odin407
-rw-r--r--core/net/errors_linux.odin384
-rw-r--r--core/net/errors_others.odin20
-rw-r--r--core/net/errors_windows.odin428
-rw-r--r--core/net/interface.odin2
-rw-r--r--core/net/interface_darwin.odin4
-rw-r--r--core/net/interface_freebsd.odin4
-rw-r--r--core/net/interface_linux.odin4
-rw-r--r--core/net/interface_windows.odin13
-rw-r--r--core/net/socket.odin57
-rw-r--r--core/net/socket_darwin.odin83
-rw-r--r--core/net/socket_freebsd.odin91
-rw-r--r--core/net/socket_linux.odin85
-rw-r--r--core/net/socket_windows.odin125
-rw-r--r--core/sys/windows/ws2_32.odin1
-rw-r--r--tests/core/net/test_core_net_freebsd.odin23
19 files changed, 1462 insertions, 918 deletions
diff --git a/core/net/common.odin b/core/net/common.odin
index 12add8225..b38291a2c 100644
--- a/core/net/common.odin
+++ b/core/net/common.odin
@@ -53,8 +53,6 @@ ODIN_NET_TCP_NODELAY_DEFAULT :: #config(ODIN_NET_TCP_NODELAY_DEFAULT, true)
Maybe :: runtime.Maybe
Network_Error :: union #shared_nil {
- General_Error,
- Platform_Error,
Create_Socket_Error,
Dial_Error,
Listen_Error,
@@ -65,6 +63,7 @@ Network_Error :: union #shared_nil {
TCP_Recv_Error,
UDP_Recv_Error,
Shutdown_Error,
+ Interfaces_Error,
Socket_Option_Error,
Set_Blocking_Error,
Parse_Endpoint_Error,
@@ -74,14 +73,13 @@ Network_Error :: union #shared_nil {
#assert(size_of(Network_Error) == 8)
-General_Error :: enum u32 {
- None = 0,
- Unable_To_Enumerate_Network_Interfaces = 1,
+Interfaces_Error :: enum u32 {
+ None,
+ Unable_To_Enumerate_Network_Interfaces,
+ Allocation_Failure,
+ Unknown,
}
-// `Platform_Error` is used to wrap errors returned by the different platforms that don't fit a common error.
-Platform_Error :: enum u32 {}
-
Parse_Endpoint_Error :: enum u32 {
None = 0,
Bad_Port = 1,
diff --git a/core/net/errors.odin b/core/net/errors.odin
new file mode 100644
index 000000000..53c936a66
--- /dev/null
+++ b/core/net/errors.odin
@@ -0,0 +1,275 @@
+package net
+
+/*
+Retrieve a platform specific error code, for when the categorized cross-platform errors are not enough.
+
+Platforms specific returns:
+- Darwin: `posix.Errno` (`core:sys/posix`)
+- Linux: `linux.Errno` (`core:sys/linux`)
+- FreeBSD: `freebsd.Errno` (`core:sys/freebsd`)
+- Windows: `windows.System_Error` (`core:sys/windows`)
+*/
+@(require_results)
+last_platform_error :: proc() -> i32 {
+ return _last_platform_error()
+}
+
+/*
+Retrieve a stringified version of the last platform error.
+*/
+@(require_results)
+last_platform_error_string :: proc() -> string {
+ return _last_platform_error_string()
+}
+
+set_last_platform_error :: proc(err: i32) {
+ _set_last_platform_error(err)
+}
+
+Create_Socket_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid/unsupported family or protocol.
+ Invalid_Argument,
+ // The user has no permission to create a socket of this type and/or protocol.
+ Insufficient_Permissions,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Dial_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid endpoint and/or options.
+ Invalid_Argument,
+ // An attempt was made to connect to a broadcast socket on a socket that doesn't support it.
+ Broadcast_Not_Supported,
+ // The socket is already connected.
+ Already_Connected,
+ // The socket is already in the progress of making a connection.
+ Already_Connecting,
+ // The address is already in use.
+ Address_In_Use,
+ // Could not reach the remote host.
+ Host_Unreachable,
+ // The remote host refused the connection or isn't listening.
+ Refused,
+ // The connection was reset by the remote host.
+ Reset,
+ // Timed out before making a connection.
+ Timeout,
+ // Non-blocking socket that would need to block waiting to connect.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+ // Endpoint given without a port, which is required.
+ Port_Required,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Bind_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket or endpoint, or invalid combination of the two.
+ Invalid_Argument,
+ // The socket is already bound to an address.
+ Already_Bound,
+ // The address is protected and the current user has insufficient permissions to access it.
+ Insufficient_Permissions_For_Address,
+ // The address is already in use.
+ Address_In_Use,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Listen_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // The socket or backlog is invalid.
+ Invalid_Argument,
+ // The socket is valid, but does not support listening.
+ Unsupported_Socket,
+ // The socket is already connected.
+ Already_Connected,
+ // The address is already in use.
+ Address_In_Use,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Accept_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket, or options.
+ Invalid_Argument,
+ // The given socket does not support accepting connections.
+ Unsupported_Socket,
+ // accept called on a socket which is not listening.
+ Not_Listening,
+ // A connection arrived but was closed while in the listen queue.
+ Aborted,
+ // Timed out before being able to accept a connection.
+ Timeout,
+ // Non-blocking socket that would need to block waiting for a connection.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+TCP_Recv_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket or buffer given.
+ Invalid_Argument,
+ // The socket is not connected.
+ Not_Connected,
+ // Connection was closed/broken/shutdown while receiving data.
+ Connection_Closed,
+ // Timed out before being able to receive any data.
+ Timeout,
+ // Non-blocking socket that would need to block waiting on data.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+UDP_Recv_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket or buffer given.
+ Invalid_Argument,
+ // "Connection" was refused by remote, or closed/broken/shutdown while receiving data.
+ Connection_Refused,
+ // Timed out before being able to receive any data.
+ Timeout,
+ // Non-blocking socket that would need to block waiting on data.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+ // Linux and UDP only: indicates the buffer was too small to receive all data, and the excess is truncated and discarded.
+ Excess_Truncated,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+TCP_Send_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket or buffer given.
+ Invalid_Argument,
+ // Connection was closed/broken/shutdown while receiving data.
+ Connection_Closed,
+ // The socket is not connected.
+ Not_Connected,
+ // Could not reach the remote host.
+ Host_Unreachable,
+ // Timed out before being able to send any data.
+ Timeout,
+ // Non-blocking socket that would need to block waiting on the remote to be able to receive the data.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+UDP_Send_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket or buffer given.
+ Invalid_Argument,
+ // Could not reach the remote host.
+ Host_Unreachable,
+ // "Connection" was refused by remote, or closed/broken/shutdown while sending data.
+ Connection_Refused,
+ // Timed out before being able to send any data.
+ Timeout,
+ // Non-blocking socket that would need to block waiting on the remote to be able to receive the data.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Shutdown_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Socket is invalid or not connected, or the manner given is invalid.
+ Invalid_Argument,
+ // Connection was closed/aborted/shutdown.
+ Connection_Closed,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Socket_Option_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Socket is invalid, not connected, or the connection has been closed/reset/shutdown.
+ Invalid_Socket,
+ // Unknown or unsupported option for the socket.
+ Invalid_Option,
+ // Invalid level or value.
+ Invalid_Value,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Set_Blocking_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Socket is invalid.
+ Invalid_Argument,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
diff --git a/core/net/errors_darwin.odin b/core/net/errors_darwin.odin
index ccf1e0f7f..7aaa220e9 100644
--- a/core/net/errors_darwin.odin
+++ b/core/net/errors_darwin.odin
@@ -20,192 +20,232 @@ package net
Feoramund: FreeBSD platform code
*/
-import "core:c"
+import "core:reflect"
import "core:sys/posix"
-@(private)
-ESHUTDOWN :: 58
-
-Create_Socket_Error :: enum c.int {
- None = 0,
- Family_Not_Supported_For_This_Socket = c.int(posix.EAFNOSUPPORT),
- No_Socket_Descriptors_Available = c.int(posix.EMFILE),
- No_Buffer_Space_Available = c.int(posix.ENOBUFS),
- No_Memory_Available = c.int(posix.ENOMEM),
- Protocol_Unsupported_By_System = c.int(posix.EPROTONOSUPPORT),
- Wrong_Protocol_For_Socket = c.int(posix.EPROTONOSUPPORT),
- Family_And_Socket_Type_Mismatch = c.int(posix.EPROTONOSUPPORT),
+_last_platform_error :: proc() -> i32 {
+ return i32(posix.errno())
}
-Dial_Error :: enum c.int {
- None = 0,
- Port_Required = -1, // Attempted to dial an endpointing without a port being set.
-
- Address_In_Use = c.int(posix.EADDRINUSE),
- In_Progress = c.int(posix.EINPROGRESS),
- Cannot_Use_Any_Address = c.int(posix.EADDRNOTAVAIL),
- Wrong_Family_For_Socket = c.int(posix.EAFNOSUPPORT),
- Refused = c.int(posix.ECONNREFUSED),
- Is_Listening_Socket = c.int(posix.EACCES),
- Already_Connected = c.int(posix.EISCONN),
- Network_Unreachable = c.int(posix.ENETUNREACH), // Device is offline
- Host_Unreachable = c.int(posix.EHOSTUNREACH), // Remote host cannot be reached
- No_Buffer_Space_Available = c.int(posix.ENOBUFS),
- Not_Socket = c.int(posix.ENOTSOCK),
- Timeout = c.int(posix.ETIMEDOUT),
-
- // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
- Would_Block = c.int(posix.EWOULDBLOCK),
+_last_platform_error_string :: proc() -> string {
+ description, _ := reflect.enum_name_from_value(posix.errno())
+ return description
}
-Bind_Error :: enum c.int {
- None = 0,
- Privileged_Port_Without_Root = -1, // Attempted to bind to a port less than 1024 without root access.
-
- Address_In_Use = c.int(posix.EADDRINUSE), // Another application is currently bound to this endpoint.
- Given_Nonlocal_Address = c.int(posix.EADDRNOTAVAIL), // The address is not a local address on this machine.
- Broadcast_Disabled = c.int(posix.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set.
- Address_Family_Mismatch = c.int(posix.EFAULT), // The address family of the address does not match that of the socket.
- Already_Bound = c.int(posix.EINVAL), // The socket is already bound to an address.
- No_Ports_Available = c.int(posix.ENOBUFS), // There are not enough ephemeral ports available.
+_set_last_platform_error :: proc(err: i32) {
+ posix.errno(posix.Errno(err))
}
-Listen_Error :: enum c.int {
- None = 0,
- Address_In_Use = c.int(posix.EADDRINUSE),
- Already_Connected = c.int(posix.EISCONN),
- No_Socket_Descriptors_Available = c.int(posix.EMFILE),
- No_Buffer_Space_Available = c.int(posix.ENOBUFS),
- Nonlocal_Address = c.int(posix.EADDRNOTAVAIL),
- Not_Socket = c.int(posix.ENOTSOCK),
- Listening_Not_Supported_For_This_Socket = c.int(posix.EOPNOTSUPP),
+_create_socket_error :: proc() -> Create_Socket_Error {
+ #partial switch posix.errno() {
+ case .EMFILE, .ENOBUFS, .ENOMEM, .EPROTONOSUPPORT, .EISCONN, .ENFILE:
+ return .Insufficient_Resources
+ case .EAFNOSUPPORT, .EPROTOTYPE:
+ return .Invalid_Argument
+ case .EACCES:
+ return .Insufficient_Permissions
+ case:
+ return .Unknown
+ }
}
-Accept_Error :: enum c.int {
- None = 0,
- // TODO(tetra): Is this error actually possible here? Or is like Linux, in which case we can remove it.
- Reset = c.int(posix.ECONNRESET),
- Not_Listening = c.int(posix.EINVAL),
- No_Socket_Descriptors_Available_For_Client_Socket = c.int(posix.EMFILE),
- No_Buffer_Space_Available = c.int(posix.ENOBUFS),
- Not_Socket = c.int(posix.ENOTSOCK),
- Not_Connection_Oriented_Socket = c.int(posix.EOPNOTSUPP),
-
- // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
- Would_Block = c.int(posix.EWOULDBLOCK),
+_dial_error :: proc() -> Dial_Error {
+ #partial switch posix.errno() {
+ case .ENOBUFS:
+ return .Insufficient_Resources
+ case .EAFNOSUPPORT, .EBADF, .EFAULT, .EINVAL, .ENOTSOCK, .EPROTOTYPE, .EADDRNOTAVAIL:
+ return .Invalid_Argument
+ case .EISCONN:
+ return .Already_Connected
+ case .EALREADY:
+ return .Already_Connecting
+ case .EADDRINUSE:
+ return .Address_In_Use
+ case .ENETDOWN:
+ 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 = ESHUTDOWN,
- Not_Connected = c.int(posix.ENOTCONN),
-
- // TODO(tetra): Is this error actually possible here?
- Connection_Broken = c.int(posix.ENETRESET),
- Not_Socket = c.int(posix.ENOTSOCK),
- Aborted = c.int(posix.ECONNABORTED),
-
- // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them?
- Connection_Closed = c.int(posix.ECONNRESET),
- Offline = c.int(posix.ENETDOWN),
- Host_Unreachable = c.int(posix.EHOSTUNREACH),
- Interrupted = c.int(posix.EINTR),
+_bind_error :: proc() -> Bind_Error {
+ #partial switch posix.errno() {
+ case .EADDRNOTAVAIL, .EAFNOSUPPORT, .EBADF, .EDESTADDRREQ, .EFAULT, .ENOTSOCK, .EOPNOTSUPP:
+ return .Invalid_Argument
+ case .EINVAL:
+ return .Already_Bound
+ case .EACCES:
+ return .Insufficient_Permissions_For_Address
+ case .EADDRINUSE:
+ return .Address_In_Use
+ case:
+ return .Unknown
+ }
+}
- // NOTE: No, really. Presumably this means something different for nonblocking sockets...
- Timeout = c.int(posix.EWOULDBLOCK),
+_listen_error :: proc() -> Listen_Error {
+ #partial switch posix.errno() {
+ case .EBADF, .ENOTSOCK:
+ return .Invalid_Argument
+ case .EDESTADDRREQ, .EOPNOTSUPP:
+ return .Unsupported_Socket
+ case .EINVAL:
+ return .Already_Connected
+ case:
+ return .Unknown
+ }
}
-UDP_Recv_Error :: enum c.int {
- None = 0,
- Buffer_Too_Small = c.int(posix.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(posix.ENOTSOCK), // The so-called socket is not an open socket.
- Not_Descriptor = c.int(posix.EBADF), // The so-called socket is, in fact, not even a valid descriptor.
- Bad_Buffer = c.int(posix.EFAULT), // The buffer did not point to a valid location in memory.
- Interrupted = c.int(posix.EINTR), // A signal occurred before any data was transmitted. See signal(7).
-
- // 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(posix.EWOULDBLOCK),
- Socket_Not_Bound = c.int(posix.EINVAL), // The socket must be bound for this operation, but isn't.
+_accept_error :: proc() -> Accept_Error {
+ #partial switch posix.errno() {
+ case .EMFILE, .ENFILE, .ENOMEM:
+ return .Insufficient_Resources
+ case .EBADF, .ENOTSOCK, .EFAULT:
+ return .Invalid_Argument
+ case .EOPNOTSUPP:
+ return .Unsupported_Socket
+ case .ECONNABORTED:
+ return .Aborted
+ case .EWOULDBLOCK:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-TCP_Send_Error :: enum c.int {
- None = 0,
-
- Aborted = c.int(posix.ECONNABORTED),
- Connection_Closed = c.int(posix.ECONNRESET),
- Not_Connected = c.int(posix.ENOTCONN),
- Shutdown = 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(posix.ENOBUFS),
- Offline = c.int(posix.ENETDOWN),
- Host_Unreachable = c.int(posix.EHOSTUNREACH),
- Interrupted = c.int(posix.EINTR), // A signal occurred before any data was transmitted. See signal(7).
-
- // NOTE: No, really. Presumably this means something different for nonblocking sockets...
- // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
- Timeout = c.int(posix.EWOULDBLOCK),
- Not_Socket = c.int(posix.ENOTSOCK), // The so-called socket is not an open socket.
+_tcp_recv_error :: proc() -> TCP_Recv_Error {
+ #partial switch posix.errno() {
+ case .EBADF, .EFAULT, .EINVAL, .ENOTSOCK, .EOPNOTSUPP:
+ return .Invalid_Argument
+ case .ENOBUFS:
+ return .Insufficient_Resources
+ case .ENOTCONN:
+ return .Not_Connected
+ case .ECONNRESET:
+ return .Connection_Closed
+ 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(posix.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(posix.ENETUNREACH),
- No_Outbound_Ports_Available = c.int(posix.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(posix.EWOULDBLOCK),
- Not_Socket = c.int(posix.ENOTSOCK), // The so-called socket is not an open socket.
- Not_Descriptor = c.int(posix.EBADF), // The so-called socket is, in fact, not even a valid descriptor.
- Bad_Buffer = c.int(posix.EFAULT), // The buffer did not point to a valid location in memory.
- Interrupted = c.int(posix.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(posix.ENOBUFS),
- No_Memory_Available = c.int(posix.ENOMEM), // No memory was available to properly manage the send queue.
+_udp_recv_error :: proc() -> UDP_Recv_Error {
+ #partial switch posix.errno() {
+ case .EBADF, .EFAULT, .EINVAL, .ENOTSOCK, .EOPNOTSUPP, .EMSGSIZE:
+ return .Invalid_Argument
+ case .ENOBUFS, .ENOMEM:
+ return .Insufficient_Resources
+ case .ECONNRESET, .ENOTCONN:
+ return .Connection_Refused
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Shutdown_Manner :: enum c.int {
- Receive = c.int(posix.SHUT_RD),
- Send = c.int(posix.SHUT_WR),
- Both = c.int(posix.SHUT_RDWR),
+_tcp_send_error :: proc() -> TCP_Send_Error {
+ #partial switch posix.errno() {
+ case .EACCES, .EBADF, .EFAULT, .EMSGSIZE, .ENOTSOCK, .EOPNOTSUPP:
+ return .Invalid_Argument
+ case .ENOBUFS:
+ return .Insufficient_Resources
+ case .ECONNRESET, .EPIPE:
+ return .Connection_Closed
+ case .ENOTCONN:
+ return .Not_Connected
+ case .EHOSTUNREACH:
+ return .Host_Unreachable
+ case .ENETDOWN, .ENETUNREACH:
+ return .Network_Unreachable
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Shutdown_Error :: enum c.int {
- None = 0,
- Aborted = c.int(posix.ECONNABORTED),
- Reset = c.int(posix.ECONNRESET),
- Offline = c.int(posix.ENETDOWN),
- Not_Connected = c.int(posix.ENOTCONN),
- Not_Socket = c.int(posix.ENOTSOCK),
- Invalid_Manner = c.int(posix.EINVAL),
+_udp_send_error :: proc() -> UDP_Send_Error {
+ #partial switch posix.errno() {
+ case .EACCES, .EBADF, .EFAULT, .EMSGSIZE, .ENOTSOCK, .EOPNOTSUPP, .EAFNOSUPPORT, .EDESTADDRREQ:
+ return .Invalid_Argument
+ case .ENOBUFS, .ENOMEM:
+ return .Insufficient_Resources
+ case .ECONNRESET, .EPIPE:
+ return .Connection_Refused
+ case .EHOSTUNREACH:
+ return .Host_Unreachable
+ case .ENETDOWN, .ENETUNREACH:
+ return .Network_Unreachable
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Socket_Option_Error :: enum c.int {
- None = 0,
- Offline = c.int(posix.ENETDOWN),
- Timeout_When_Keepalive_Set = c.int(posix.ENETRESET),
- Invalid_Option_For_Socket = c.int(posix.ENOPROTOOPT),
- Reset_When_Keepalive_Set = c.int(posix.ENOTCONN),
- Not_Socket = c.int(posix.ENOTSOCK),
+_shutdown_error :: proc() -> Shutdown_Error {
+ #partial switch posix.errno() {
+ case .EBADF, .EINVAL, .ENOTSOCK, .ENOTCONN:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
}
-Set_Blocking_Error :: enum c.int {
- None = 0,
+_socket_option_error :: proc() -> Socket_Option_Error {
+ #partial switch posix.errno() {
+ case .ENOBUFS:
+ return .Insufficient_Resources
+ case .EBADF, .ENOTSOCK, .EISCONN:
+ return .Invalid_Socket
+ case .EINVAL, .ENOPROTOOPT:
+ return .Invalid_Option
+ case .EFAULT, .EDOM:
+ return .Invalid_Value
+ case:
+ return .Unknown
+ }
+}
- // TODO: Add errors for `set_blocking`
+_set_blocking_error :: proc() -> Set_Blocking_Error {
+ #partial switch posix.errno() {
+ case .EBADF:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
}
diff --git a/core/net/errors_freebsd.odin b/core/net/errors_freebsd.odin
index 486732a95..707ffd0dd 100644
--- a/core/net/errors_freebsd.odin
+++ b/core/net/errors_freebsd.odin
@@ -20,198 +20,267 @@ package net
Feoramund: FreeBSD platform code
*/
-import "core:c"
+import "core:reflect"
import "core:sys/freebsd"
-Create_Socket_Error :: enum c.int {
- None = 0,
- Access_Denied = cast(c.int)freebsd.Errno.EACCES,
- Family_Not_Supported_For_This_Socket = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
- Full_Per_Process_Descriptor_Table = cast(c.int)freebsd.Errno.EMFILE,
- Full_System_File_Table = cast(c.int)freebsd.Errno.ENFILE,
- No_Buffer_Space_Available = cast(c.int)freebsd.Errno.ENOBUFS,
- Insufficient_Permission = cast(c.int)freebsd.Errno.EPERM,
- Protocol_Unsupported_In_Family = cast(c.int)freebsd.Errno.EPROTONOSUPPORT,
- Socket_Type_Unsupported_By_Protocol = cast(c.int)freebsd.Errno.EPROTOTYPE,
+@(private="file", thread_local)
+_last_error: freebsd.Errno
+
+_last_platform_error :: proc() -> i32 {
+ return i32(_last_error)
+}
+
+_last_platform_error_string :: proc() -> string {
+ description, _ := reflect.enum_name_from_value(_last_error)
+ return description
+}
+
+_set_last_platform_error :: proc(err: i32) {
+ _last_error = freebsd.Errno(err)
}
-Dial_Error :: enum c.int {
- None = 0,
- Port_Required = -1,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Invalid_Namelen = cast(c.int)freebsd.Errno.EINVAL,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Address_Unavailable = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
- Wrong_Family_For_Socket = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
- Already_Connected = cast(c.int)freebsd.Errno.EISCONN,
- Timeout = cast(c.int)freebsd.Errno.ETIMEDOUT,
- Refused_By_Remote_Host = cast(c.int)freebsd.Errno.ECONNREFUSED,
- // `Refused` alias for `core:net` tests.
- // The above default name `Refused_By_Remote_Host` is more explicit.
- Refused = Refused_By_Remote_Host,
- Reset_By_Remote_Host = cast(c.int)freebsd.Errno.ECONNRESET,
- Network_Unreachable = cast(c.int)freebsd.Errno.ENETUNREACH,
- Host_Unreachable = cast(c.int)freebsd.Errno.EHOSTUNREACH,
- Address_In_Use = cast(c.int)freebsd.Errno.EADDRINUSE,
- Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
- In_Progress = cast(c.int)freebsd.Errno.EINPROGRESS,
- Interrupted_By_Signal = cast(c.int)freebsd.Errno.EINTR,
- Previous_Attempt_Incomplete = cast(c.int)freebsd.Errno.EALREADY,
- Broadcast_Unavailable = cast(c.int)freebsd.Errno.EACCES,
- Auto_Port_Unavailable = cast(c.int)freebsd.Errno.EAGAIN,
-
- // NOTE: There are additional connect() error possibilities, but they are
- // strictly for addresses in the UNIX domain.
+_create_socket_error :: proc(errno: freebsd.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
+ }
}
-Bind_Error :: enum c.int {
- None = 0,
- Kernel_Resources_Unavailable = cast(c.int)freebsd.Errno.EAGAIN,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
-
- // NOTE: bind() can also return EINVAL if the underlying `addrlen` is an
- // invalid length for the address family. This shouldn't happen for the net
- // package, but it's worth noting.
- Already_Bound = cast(c.int)freebsd.Errno.EINVAL,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Given_Nonlocal_Address = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
- Address_In_Use = cast(c.int)freebsd.Errno.EADDRINUSE,
- Address_Family_Mismatch = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
- Protected_Address = cast(c.int)freebsd.Errno.EACCES,
- Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
-
- // NOTE: There are additional bind() error possibilities, but they are
- // strictly for addresses in the UNIX domain.
+_dial_error :: proc(errno: freebsd.Errno) -> Dial_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .EINVAL, .ENOTSOCK, .EADDRNOTAVAIL, .EAFNOSUPPORT, .EFAULT, .EAGAIN:
+ 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
+ }
}
-Listen_Error :: enum c.int {
- None = 0,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Socket_Not_Bound = cast(c.int)freebsd.Errno.EDESTADDRREQ,
- Already_Connected = cast(c.int)freebsd.Errno.EINVAL,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Listening_Not_Supported_For_This_Socket = cast(c.int)freebsd.Errno.EOPNOTSUPP,
+_bind_error :: proc(errno: freebsd.Errno) -> Bind_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EAGAIN, .ENOTSOCK, .EADDRNOTAVAIL, .EAFNOSUPPORT, .EFAULT:
+ return .Insufficient_Resources
+ case .EBADF:
+ return .Invalid_Argument
+ case .EINVAL:
+ return .Already_Bound
+ case .EACCES:
+ return .Insufficient_Permissions_For_Address
+ case .EADDRINUSE:
+ return .Address_In_Use
+ case:
+ return .Unknown
+ }
}
-Accept_Error :: enum c.int {
- None = 0,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Interrupted = cast(c.int)freebsd.Errno.EINTR,
- Full_Per_Process_Descriptor_Table = cast(c.int)freebsd.Errno.EMFILE,
- Full_System_File_Table = cast(c.int)freebsd.Errno.ENFILE,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Listen_Not_Called_On_Socket_Yet = cast(c.int)freebsd.Errno.EINVAL,
- Address_Not_Writable = cast(c.int)freebsd.Errno.EFAULT,
-
- // NOTE: This is the same as EWOULDBLOCK.
- No_Connections_Available = cast(c.int)freebsd.Errno.EAGAIN,
- // `Would_Block` alias for `core:net` tests.
- Would_Block = cast(c.int)freebsd.Errno.EAGAIN,
-
- New_Connection_Aborted = cast(c.int)freebsd.Errno.ECONNABORTED,
+_listen_error :: proc(errno: freebsd.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
+ }
}
-TCP_Recv_Error :: enum c.int {
- None = 0,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Connection_Closed = cast(c.int)freebsd.Errno.ECONNRESET,
- Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
-
- // NOTE(Feoramund): The next two errors are only relevant for recvmsg(),
- // but I'm including them for completeness's sake.
- Full_Table_And_Pending_Data = cast(c.int)freebsd.Errno.EMFILE,
- Invalid_Message_Size = cast(c.int)freebsd.Errno.EMSGSIZE,
-
- Timeout = cast(c.int)freebsd.Errno.EAGAIN,
- Interrupted_By_Signal = cast(c.int)freebsd.Errno.EINTR,
- Buffer_Pointer_Outside_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
+_accept_error :: proc(errno: freebsd.Errno) -> Accept_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EMFILE, .ENFILE:
+ 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
+ }
}
-UDP_Recv_Error :: enum c.int {
- None = 0,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Connection_Closed = cast(c.int)freebsd.Errno.ECONNRESET,
- Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
-
- // NOTE(Feoramund): The next two errors are only relevant for recvmsg(),
- // but I'm including them for completeness's sake.
- Full_Table_And_Data_Discarded = cast(c.int)freebsd.Errno.EMFILE,
- Invalid_Message_Size = cast(c.int)freebsd.Errno.EMSGSIZE,
-
- Timeout = cast(c.int)freebsd.Errno.EAGAIN,
- Interrupted_By_Signal = cast(c.int)freebsd.Errno.EINTR,
- Buffer_Pointer_Outside_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
+_tcp_recv_error :: proc(errno: freebsd.Errno) -> TCP_Recv_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .ENOTSOCK, .EFAULT:
+ return .Invalid_Argument
+ case .ENOTCONN:
+ return .Not_Connected
+ case .ECONNRESET:
+ 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,
- Connection_Closed = cast(c.int)freebsd.Errno.ECONNRESET,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Broadcast_Status_Mismatch = cast(c.int)freebsd.Errno.EACCES,
- Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Argument_In_Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
-
- Message_Size_Breaks_Atomicity = cast(c.int)freebsd.Errno.EMSGSIZE,
-
- /* The socket is marked non-blocking, or MSG_DONTWAIT is
- specified, and the requested operation would block. */
- Would_Block = cast(c.int)freebsd.Errno.EAGAIN,
-
- /* NOTE: This error arises for two distinct reasons:
-
- 1. The system was unable to allocate an internal buffer.
- The operation may succeed when buffers become available.
-
- 2. The output queue for a network interface was full.
- This generally indicates that the interface has stopped
- sending, but may be caused by transient congestion.
- */
- No_Buffer_Space_Available = cast(c.int)freebsd.Errno.ENOBUFS,
-
- Host_Unreachable = cast(c.int)freebsd.Errno.EHOSTUNREACH,
- Already_Connected = cast(c.int)freebsd.Errno.EISCONN,
- ICMP_Unreachable = cast(c.int)freebsd.Errno.ECONNREFUSED,
- Host_Down = cast(c.int)freebsd.Errno.EHOSTDOWN,
- Network_Down = cast(c.int)freebsd.Errno.ENETDOWN,
- Jailed_Socket_Tried_To_Escape = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
- Cannot_Send_More_Data = cast(c.int)freebsd.Errno.EPIPE,
+_udp_recv_error :: proc(errno: freebsd.Errno) -> UDP_Recv_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .ENOTSOCK, .EFAULT:
+ return .Invalid_Argument
+ case .ECONNRESET, .ENOTCONN:
+ return .Connection_Refused
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-// NOTE(Feoramund): The same as TCP errors go, as far as I'm aware.
-UDP_Send_Error :: distinct TCP_Send_Error
+_tcp_send_error :: proc(errno: freebsd.Errno) -> TCP_Send_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .EACCES, .ENOTSOCK, .EFAULT, .EMSGSIZE:
+ return .Invalid_Argument
+ case .ENOBUFS:
+ 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
+ }
+}
-Shutdown_Manner :: enum c.int {
- Receive = cast(c.int)freebsd.Shutdown_Method.RD,
- Send = cast(c.int)freebsd.Shutdown_Method.WR,
- Both = cast(c.int)freebsd.Shutdown_Method.RDWR,
+_udp_send_error :: proc(errno: freebsd.Errno) -> UDP_Send_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .EACCES, .ENOTSOCK, .EFAULT, .EMSGSIZE:
+ return .Invalid_Argument
+ case .ENOBUFS:
+ 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,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Invalid_Manner = cast(c.int)freebsd.Errno.EINVAL,
- Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
+_shutdown_error :: proc(errno: freebsd.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,
- Value_Out_Of_Range = -1,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Unknown_Option_For_Level = cast(c.int)freebsd.Errno.ENOPROTOOPT,
- Argument_In_Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
- // This error can arise for many different reasons.
- Invalid_Value = cast(c.int)freebsd.Errno.EINVAL,
- System_Memory_Allocation_Failed = cast(c.int)freebsd.Errno.ENOMEM,
- Insufficient_System_Resources = cast(c.int)freebsd.Errno.ENOBUFS,
+_socket_option_error :: proc(errno: freebsd.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:
+ return .Invalid_Option
+ case .EINVAL, .EFAULT:
+ return .Invalid_Value
+ case:
+ return .Unknown
+ }
}
-Set_Blocking_Error :: enum c.int {
- None = 0,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Wrong_Descriptor = cast(c.int)freebsd.Errno.ENOTTY,
+_set_blocking_error :: proc(errno: freebsd.Errno) -> Set_Blocking_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .ENOTTY:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
}
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
+ }
+}
diff --git a/core/net/errors_others.odin b/core/net/errors_others.odin
new file mode 100644
index 000000000..bda0fd28f
--- /dev/null
+++ b/core/net/errors_others.odin
@@ -0,0 +1,20 @@
+#+build !darwin
+#+build !linux
+#+build !freebsd
+#+build !windows
+package net
+
+@(private="file", thread_local)
+_last_error: i32
+
+_last_platform_error :: proc() -> i32 {
+ return _last_error
+}
+
+_last_platform_error_string :: proc() -> string {
+ return ""
+}
+
+_set_last_platform_error :: proc(err: i32) {
+ _last_error = err
+}
diff --git a/core/net/errors_windows.odin b/core/net/errors_windows.odin
index f41bcf888..b30046a17 100644
--- a/core/net/errors_windows.odin
+++ b/core/net/errors_windows.odin
@@ -20,250 +20,242 @@ package net
Feoramund: FreeBSD platform code
*/
-import "core:c"
+import "core:reflect"
import win "core:sys/windows"
-Create_Socket_Error :: enum c.int {
- None = 0,
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Family_Not_Supported_For_This_Socket = win.WSAEAFNOSUPPORT,
- No_Socket_Descriptors_Available = win.WSAEMFILE,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Protocol_Unsupported_By_System = win.WSAEPROTONOSUPPORT,
- Wrong_Protocol_For_Socket = win.WSAEPROTOTYPE,
- Family_And_Socket_Type_Mismatch = win.WSAESOCKTNOSUPPORT,
+_last_platform_error :: proc() -> i32 {
+ return i32(win.WSAGetLastError())
}
-Dial_Error :: enum c.int {
- None = 0,
- Port_Required = -1,
- Address_In_Use = win.WSAEADDRINUSE,
- In_Progress = win.WSAEALREADY,
- Cannot_Use_Any_Address = win.WSAEADDRNOTAVAIL,
- Wrong_Family_For_Socket = win.WSAEAFNOSUPPORT,
- Refused = win.WSAECONNREFUSED,
- Is_Listening_Socket = win.WSAEINVAL,
- Already_Connected = win.WSAEISCONN,
- Network_Unreachable = win.WSAENETUNREACH, // Device is offline
- Host_Unreachable = win.WSAEHOSTUNREACH, // Remote host cannot be reached
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Not_Socket = win.WSAENOTSOCK,
- Timeout = win.WSAETIMEDOUT,
- Would_Block = win.WSAEWOULDBLOCK, // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
+_last_platform_error_string :: proc() -> string {
+ description, _ := reflect.enum_name_from_value(win.System_Error(win.WSAGetLastError()))
+ return description
}
-Bind_Error :: enum c.int {
- None = 0,
- Address_In_Use = win.WSAEADDRINUSE, // Another application is currently bound to this endpoint.
- Given_Nonlocal_Address = win.WSAEADDRNOTAVAIL, // The address is not a local address on this machine.
- Broadcast_Disabled = win.WSAEACCES, // To bind a UDP socket to the broadcast address, the appropriate socket option must be set.
- Address_Family_Mismatch = win.WSAEFAULT, // The address family of the address does not match that of the socket.
- Already_Bound = win.WSAEINVAL, // The socket is already bound to an address.
- No_Ports_Available = win.WSAENOBUFS, // There are not enough ephemeral ports available.
+_set_last_platform_error :: proc(err: i32) {
+ win.WSASetLastError(err)
}
-Listen_Error :: enum c.int {
- None = 0,
- Address_In_Use = win.WSAEADDRINUSE,
- Already_Connected = win.WSAEISCONN,
- No_Socket_Descriptors_Available = win.WSAEMFILE,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Nonlocal_Address = win.WSAEADDRNOTAVAIL,
- Not_Socket = win.WSAENOTSOCK,
- Listening_Not_Supported_For_This_Socket = win.WSAEOPNOTSUPP,
+_create_socket_error :: proc() -> Create_Socket_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN, .WSAEINVALIDPROVIDER, .WSAEINVALIDPROCTABLE, .WSAEPROVIDERFAILEDINIT:
+ return .Network_Unreachable
+ case .WSAEAFNOSUPPORT, .WSAEINPROGRESS, .WSAEINVAL, .WSAEPROTOTYPE, .WSAESOCKTNOSUPPORT:
+ return .Invalid_Argument
+ case .WSAEMFILE, .WSAENOBUFS, .WSAEPROTONOSUPPORT:
+ return .Insufficient_Resources
+ case:
+ return .Unknown
+ }
}
-Accept_Error :: enum c.int {
- None = 0,
- Not_Listening = win.WSAEINVAL,
- No_Socket_Descriptors_Available_For_Client_Socket = win.WSAEMFILE,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Not_Socket = win.WSAENOTSOCK,
- Not_Connection_Oriented_Socket = win.WSAEOPNOTSUPP,
-
- // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
- Would_Block = win.WSAEWOULDBLOCK,
+_dial_error :: proc() -> Dial_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEADDRINUSE:
+ return .Address_In_Use
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAEALREADY:
+ return .Already_Connecting
+ case .WSAEADDRNOTAVAIL, .WSAEAFNOSUPPORT, .WSAEFAULT, .WSAENOTSOCK, .WSAEINPROGRESS, .WSAEINVAL:
+ return .Invalid_Argument
+ case .WSAECONNREFUSED:
+ return .Refused
+ case .WSAEISCONN:
+ return .Already_Connected
+ case .WSAEHOSTUNREACH:
+ return .Host_Unreachable
+ case .WSAENOBUFS:
+ return .Insufficient_Resources
+ case .WSAETIMEDOUT:
+ return .Timeout
+ case .WSAEACCES:
+ return .Broadcast_Not_Supported
+ case:
+ return .Unknown
+ }
}
-TCP_Recv_Error :: enum c.int {
- None = 0,
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Not_Connected = win.WSAENOTCONN,
- Bad_Buffer = win.WSAEFAULT,
- Keepalive_Failure = win.WSAENETRESET,
- Not_Socket = win.WSAENOTSOCK,
- Shutdown = win.WSAESHUTDOWN,
- Would_Block = win.WSAEWOULDBLOCK,
- Aborted = win.WSAECONNABORTED,
- Timeout = win.WSAETIMEDOUT,
-
- // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them?
- Connection_Closed = win.WSAECONNRESET,
-
- // TODO: verify can actually happen
- Host_Unreachable = win.WSAEHOSTUNREACH,
+_bind_error :: proc() -> Bind_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEADDRINUSE:
+ return .Address_In_Use
+ case .WSAEADDRNOTAVAIL, .WSAEFAULT, .WSAEINPROGRESS, .WSAEACCES, .WSAEINVAL, .WSAENOTSOCK:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
}
-UDP_Recv_Error :: enum c.int {
- None = 0,
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Aborted = win.WSAECONNABORTED,
- Buffer_Too_Small = win.WSAEMSGSIZE, // The buffer is too small to fit the entire message, and the message was truncated. When this happens, the rest of message is lost.
- Remote_Not_Listening = win.WSAECONNRESET, // The machine at the remote endpoint doesn't have the given port open to receiving UDP data.
- Shutdown = win.WSAESHUTDOWN,
- Broadcast_Disabled = win.WSAEACCES, // A broadcast address was specified, but the .Broadcast socket option isn't set.
- Bad_Buffer = win.WSAEFAULT,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Not_Socket = win.WSAENOTSOCK, // The socket is not valid socket handle.
- Would_Block = win.WSAEWOULDBLOCK,
- Host_Unreachable = win.WSAEHOSTUNREACH, // The remote host cannot be reached from this host at this time.
- Offline = win.WSAENETUNREACH, // The network cannot be reached from this host at this time.
- Timeout = win.WSAETIMEDOUT,
-
- // TODO: can this actually happen? The socket isn't bound; an unknown flag specified; or MSG_OOB specified with SO_OOBINLINE enabled.
- Incorrectly_Configured = win.WSAEINVAL,
- TTL_Expired = win.WSAENETRESET, // The message took more hops than was allowed (the Time To Live) to reach the remote endpoint.
+_listen_error :: proc() -> Listen_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEMFILE, .WSAENOBUFS:
+ return .Insufficient_Resources
+ case .WSAEADDRINUSE:
+ return .Address_In_Use
+ case .WSAEINPROGRESS, .WSAENOTSOCK:
+ return .Invalid_Argument
+ case .WSAEISCONN:
+ return .Already_Connected
+ case .WSAEOPNOTSUPP, .WSAEINVAL:
+ return .Unsupported_Socket
+ case:
+ return .Unknown
+ }
}
-// TODO: consider merging some errors to make handling them easier
-// TODO: verify once more what errors to actually expose
-TCP_Send_Error :: enum c.int {
- None = 0,
-
- Aborted = win.WSAECONNABORTED,
- Not_Connected = win.WSAENOTCONN,
- Shutdown = win.WSAESHUTDOWN,
- Connection_Closed = win.WSAECONNRESET,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Host_Unreachable = win.WSAEHOSTUNREACH,
- Would_Block = win.WSAEWOULDBLOCK,
-
- // TODO: verify possible, as not mentioned in docs
- Offline = win.WSAENETUNREACH,
- Timeout = win.WSAETIMEDOUT,
-
- // A broadcast address was specified, but the .Broadcast socket option isn't set.
- Broadcast_Disabled = win.WSAEACCES,
- Bad_Buffer = win.WSAEFAULT,
-
- // Connection is broken due to keepalive activity detecting a failure during the operation.
- Keepalive_Failure = win.WSAENETRESET, // TODO: not functionally different from Reset; merge?
- Not_Socket = win.WSAENOTSOCK, // The so-called socket is not an open socket.
+_accept_error :: proc() -> Accept_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEMFILE, .WSAENOBUFS:
+ return .Insufficient_Resources
+ case .WSAECONNRESET:
+ return .Aborted
+ case .WSAEFAULT, .WSAEINPROGRESS, .WSAENOTSOCK:
+ return .Invalid_Argument
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAEINVAL:
+ return .Not_Listening
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAEOPNOTSUPP:
+ return .Unsupported_Socket
+ case:
+ return .Unknown
+ }
}
-UDP_Send_Error :: enum c.int {
- None = 0,
- Network_Subsystem_Failure = win.WSAENETDOWN,
-
- Aborted = win.WSAECONNABORTED,
- Message_Too_Long = win.WSAEMSGSIZE, // The message is larger than the maximum UDP packet size.
- Remote_Not_Listening = win.WSAECONNRESET, // The machine at the remote endpoint doesn't have the given port open to receiving UDP data.
- Shutdown = win.WSAESHUTDOWN, // A broadcast address was specified, but the .Broadcast socket option isn't set.
- Broadcast_Disabled = win.WSAEACCES,
- Bad_Buffer = win.WSAEFAULT, // Connection is broken due to keepalive activity detecting a failure during the operation.
-
- // TODO: not functionally different from Reset; merge?
- Keepalive_Failure = win.WSAENETRESET,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Not_Socket = win.WSAENOTSOCK, // The socket is not valid socket handle.
-
- // This socket is unidirectional and cannot be used to send any data.
- // TODO: verify possible; decide whether to keep if not
- Receive_Only = win.WSAEOPNOTSUPP,
- Would_Block = win.WSAEWOULDBLOCK,
- Host_Unreachable = win.WSAEHOSTUNREACH, // The remote host cannot be reached from this host at this time.
- Cannot_Use_Any_Address = win.WSAEADDRNOTAVAIL, // Attempt to send to the Any address.
- Family_Not_Supported_For_This_Socket = win.WSAEAFNOSUPPORT, // The address is of an incorrect address family for this socket.
- Offline = win.WSAENETUNREACH, // The network cannot be reached from this host at this time.
- Timeout = win.WSAETIMEDOUT,
+_tcp_recv_error :: proc() -> TCP_Recv_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEFAULT, .WSAEINPROGRESS, .WSAENOTSOCK, .WSAEMSGSIZE, .WSAEINVAL, .WSAEOPNOTSUPP:
+ return .Invalid_Argument
+ case .WSAENOTCONN:
+ return .Not_Connected
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAENETRESET, .WSAESHUTDOWN, .WSAECONNABORTED, .WSAECONNRESET:
+ return .Connection_Closed
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAETIMEDOUT:
+ return .Timeout
+ case:
+ return .Unknown
+ }
}
-Shutdown_Manner :: enum c.int {
- Receive = win.SD_RECEIVE,
- Send = win.SD_SEND,
- Both = win.SD_BOTH,
+_udp_recv_error :: proc() -> UDP_Recv_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEFAULT, .WSAEINPROGRESS, .WSAEINVAL, .WSAEISCONN, .WSAENOTSOCK, .WSAEOPNOTSUPP, .WSAEMSGSIZE:
+ return .Invalid_Argument
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAENETRESET, .WSAESHUTDOWN, .WSAECONNRESET:
+ return .Connection_Refused
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAETIMEDOUT:
+ return .Timeout
+ case:
+ return .Unknown
+ }
}
-Shutdown_Error :: enum c.int {
- None = 0,
- Aborted = win.WSAECONNABORTED,
- Reset = win.WSAECONNRESET,
- Offline = win.WSAENETDOWN,
- Not_Connected = win.WSAENOTCONN,
- Not_Socket = win.WSAENOTSOCK,
- Invalid_Manner = win.WSAEINVAL,
+_tcp_send_error :: proc() -> TCP_Send_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAENOBUFS:
+ return .Insufficient_Resources
+ case .WSAEACCES, .WSAEINPROGRESS, .WSAEFAULT, .WSAENOTSOCK, .WSAEOPNOTSUPP, .WSAEMSGSIZE, .WSAEINVAL:
+ return .Invalid_Argument
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAENETRESET, .WSAESHUTDOWN, .WSAECONNABORTED, .WSAECONNRESET:
+ return .Connection_Closed
+ case .WSAENOTCONN:
+ return .Not_Connected
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAETIMEDOUT:
+ return .Timeout
+ case .WSAEHOSTUNREACH:
+ return .Host_Unreachable
+ case:
+ return .Unknown
+ }
}
-Socket_Option :: enum c.int {
- // bool: Whether the address that this socket is bound to can be reused by other sockets.
- // This allows you to bypass the cooldown period if a program dies while the socket is bound.
- Reuse_Address = win.SO_REUSEADDR,
-
- // bool: Whether other programs will be inhibited from binding the same endpoint as this socket.
- Exclusive_Addr_Use = win.SO_EXCLUSIVEADDRUSE,
-
- // bool: When true, keepalive packets will be automatically be sent for this connection. TODO: verify this understanding
- Keep_Alive = win.SO_KEEPALIVE,
-
- // bool: When true, client connections will immediately be sent a TCP/IP RST response, rather than being accepted.
- Conditional_Accept = win.SO_CONDITIONAL_ACCEPT,
-
- // bool: If true, when the socket is closed, but data is still waiting to be sent, discard that data.
- Dont_Linger = win.SO_DONTLINGER,
-
- // bool: When true, 'out-of-band' data sent over the socket will be read by a normal net.recv() call, the same as normal 'in-band' data.
- Out_Of_Bounds_Data_Inline = win.SO_OOBINLINE,
-
- // bool: When true, disables send-coalescing, therefore reducing latency.
- TCP_Nodelay = win.TCP_NODELAY,
-
- // win.LINGER: Customizes how long (if at all) the socket will remain open when there
- // is some remaining data waiting to be sent, and net.close() is called.
- Linger = win.SO_LINGER,
-
- // win.DWORD: The size, in bytes, of the OS-managed receive-buffer for this socket.
- Receive_Buffer_Size = win.SO_RCVBUF,
-
- // win.DWORD: The size, in bytes, of the OS-managed send-buffer for this socket.
- Send_Buffer_Size = win.SO_SNDBUF,
-
- // win.DWORD: For blocking sockets, the time in milliseconds to wait for incoming data to be received, before giving up and returning .Timeout.
- // For non-blocking sockets, ignored.
- // Use a value of zero to potentially wait forever.
- Receive_Timeout = win.SO_RCVTIMEO,
-
- // win.DWORD: For blocking sockets, the time in milliseconds to wait for outgoing data to be sent, before giving up and returning .Timeout.
- // For non-blocking sockets, ignored.
- // Use a value of zero to potentially wait forever.
- Send_Timeout = win.SO_SNDTIMEO,
-
- // bool: Allow sending to, receiving from, and binding to, a broadcast address.
- Broadcast = win.SO_BROADCAST,
+_udp_send_error :: proc() -> UDP_Send_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN, .WSAENETUNREACH:
+ return .Network_Unreachable
+ case .WSAENOBUFS:
+ return .Insufficient_Resources
+ case .WSAEACCES, .WSAEINVAL, .WSAEINPROGRESS, .WSAEFAULT, .WSAENOTCONN, .WSAENOTSOCK, .WSAEOPNOTSUPP, .WSAEADDRNOTAVAIL, .WSAEAFNOSUPPORT, .WSAEDESTADDRREQ:
+ return .Invalid_Argument
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAENETRESET, .WSAESHUTDOWN, .WSAECONNRESET:
+ return .Connection_Refused
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAETIMEDOUT:
+ return .Timeout
+ case:
+ return .Unknown
+ }
}
-Socket_Option_Error :: enum c.int {
- None = 0,
- Linger_Only_Supports_Whole_Seconds = 1,
-
- // The given value is too big or small to be given to the OS.
- Value_Out_Of_Range,
-
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Timeout_When_Keepalive_Set = win.WSAENETRESET,
- Invalid_Option_For_Socket = win.WSAENOPROTOOPT,
- Reset_When_Keepalive_Set = win.WSAENOTCONN,
- Not_Socket = win.WSAENOTSOCK,
+_shutdown_error :: proc() -> Shutdown_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSAENETDOWN, .WSANOTINITIALISED:
+ return .Network_Unreachable
+ case .WSAECONNABORTED, .WSAECONNRESET:
+ return .Connection_Closed
+ case .WSAEINPROGRESS, .WSAEINVAL, .WSAENOTCONN, .WSAENOTSOCK:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
}
-Set_Blocking_Error :: enum c.int {
- None = 0,
-
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Blocking_Call_In_Progress = win.WSAEINPROGRESS,
- Not_Socket = win.WSAENOTSOCK,
+_socket_option_error :: proc() -> Socket_Option_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSAENETDOWN, .WSANOTINITIALISED:
+ return .Network_Unreachable
+ case .WSAEFAULT, .WSAEINVAL:
+ return .Invalid_Value
+ case .WSAENETRESET, .WSAENOTCONN, .WSAENOTSOCK:
+ return .Invalid_Socket
+ case .WSAENOPROTOOPT:
+ return .Invalid_Option
+ case:
+ return .Unknown
+ }
+}
- // TODO: are those errors possible?
- Network_Subsystem_Not_Initialized = win.WSAENOTINITIALISED,
- Invalid_Argument_Pointer = win.WSAEFAULT,
-} \ No newline at end of file
+_set_blocking_error :: proc() -> Set_Blocking_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSAENETDOWN, .WSANOTINITIALISED:
+ return .Network_Unreachable
+ case .WSAEINPROGRESS, .WSAENOTSOCK, .WSAEFAULT:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
+}
diff --git a/core/net/interface.odin b/core/net/interface.odin
index 775a812f3..4d499a008 100644
--- a/core/net/interface.odin
+++ b/core/net/interface.odin
@@ -27,7 +27,7 @@ MAX_INTERFACE_ENUMERATION_TRIES :: 3
/*
`enumerate_interfaces` retrieves a list of network interfaces with their associated properties.
*/
-enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) {
return _enumerate_interfaces(allocator)
}
diff --git a/core/net/interface_darwin.odin b/core/net/interface_darwin.odin
index 9aa6cbd1a..f189e5844 100644
--- a/core/net/interface_darwin.odin
+++ b/core/net/interface_darwin.odin
@@ -26,7 +26,7 @@ import "core:sys/posix"
foreign import lib "system:System.framework"
@(private)
-_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) {
context.allocator = allocator
head: ^ifaddrs
@@ -47,7 +47,7 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
iface.adapter_name = key_ptr^
}
if mem_err != nil {
- return {}, .Unable_To_Enumerate_Network_Interfaces
+ return {}, .Allocation_Failure
}
address: Address
diff --git a/core/net/interface_freebsd.odin b/core/net/interface_freebsd.odin
index 50e2d1a96..90a538a04 100644
--- a/core/net/interface_freebsd.odin
+++ b/core/net/interface_freebsd.odin
@@ -25,7 +25,7 @@ import "core:strings"
import "core:sys/freebsd"
@(private)
-_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) {
// This is a simplified implementation of `getifaddrs` from the FreeBSD
// libc using only Odin and syscalls.
context.allocator = allocator
@@ -50,7 +50,7 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
// Allocate and get the entries.
buf, alloc_err := make([]byte, needed)
if alloc_err != nil {
- return nil, .Unable_To_Enumerate_Network_Interfaces
+ return nil, .Allocation_Failure
}
defer delete(buf)
diff --git a/core/net/interface_linux.odin b/core/net/interface_linux.odin
index 28724735b..e329803c5 100644
--- a/core/net/interface_linux.odin
+++ b/core/net/interface_linux.odin
@@ -30,7 +30,7 @@ package net
// NOTE(flysand): https://man7.org/linux/man-pages/man7/netlink.7.html
// apparently musl libc uses this to enumerate network interfaces
@(private)
-_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) {
context.allocator = allocator
// head: ^os.ifaddrs
@@ -143,4 +143,4 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
// }
// return _interfaces[:], {}
return nil, {}
-} \ No newline at end of file
+}
diff --git a/core/net/interface_windows.odin b/core/net/interface_windows.odin
index a6eb72846..571fb322f 100644
--- a/core/net/interface_windows.odin
+++ b/core/net/interface_windows.odin
@@ -23,7 +23,7 @@ package net
import sys "core:sys/windows"
import strings "core:strings"
-_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) {
context.allocator = allocator
buf: []u8
@@ -52,7 +52,8 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
case 0:
break gaa
case:
- return {}, Platform_Error(res)
+ set_last_platform_error(i32(res))
+ return {}, .Unknown
}
}
@@ -63,13 +64,13 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
_interfaces := make([dynamic]Network_Interface, 0, allocator)
for adapter := (^sys.IP_Adapter_Addresses)(raw_data(buf)); adapter != nil; adapter = adapter.Next {
friendly_name, err1 := sys.wstring_to_utf8(sys.wstring(adapter.FriendlyName), 256, allocator)
- if err1 != nil { return {}, Platform_Error(err1) }
+ if err1 != nil { return {}, .Allocation_Failure }
description, err2 := sys.wstring_to_utf8(sys.wstring(adapter.Description), 256, allocator)
- if err2 != nil { return {}, Platform_Error(err2) }
+ if err2 != nil { return {}, .Allocation_Failure }
dns_suffix, err3 := sys.wstring_to_utf8(sys.wstring(adapter.DnsSuffix), 256, allocator)
- if err3 != nil { return {}, Platform_Error(err3) }
+ if err3 != nil { return {}, .Allocation_Failure }
interface := Network_Interface{
adapter_name = strings.clone(string(adapter.AdapterName)),
@@ -176,4 +177,4 @@ parse_socket_address :: proc(addr_in: sys.SOCKET_ADDRESS) -> (addr: Endpoint) {
case: return // Empty or invalid address type
}
unreachable()
-} \ No newline at end of file
+}
diff --git a/core/net/socket.odin b/core/net/socket.odin
index c5ea11e11..11655cf4f 100644
--- a/core/net/socket.odin
+++ b/core/net/socket.odin
@@ -35,6 +35,8 @@ any_socket_to_socket :: proc "contextless" (socket: Any_Socket) -> Socket {
`a.host.name:9999`, or as `1.2.3.4:9999`, or IP6 equivalent.
Calls `parse_hostname_or_endpoint` and `dial_tcp_from_host_or_endpoint`.
+
+ Errors that can be returned: `Parse_Endpoint_Error`, `Resolve_Error`, `DNS_Error`, `Create_Socket_Error`, or `Dial_Error`
*/
dial_tcp_from_hostname_and_port_string :: proc(hostname_and_port: string, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
target := parse_hostname_or_endpoint(hostname_and_port) or_return
@@ -47,6 +49,8 @@ dial_tcp_from_hostname_and_port_string :: proc(hostname_and_port: string, option
`parse_hostname_or_endpoint` is called and the `hostname` will be resolved into an IP.
If a `hostname` of form `a.host.name:9999` is given, the port will be ignored in favor of the explicit `port` param.
+
+ Errors that can be returned: `Parse_Endpoint_Error`, `Resolve_Error`, `DNS_Error`, `Create_Socket_Error`, or `Dial_Error`
*/
dial_tcp_from_hostname_with_port_override :: proc(hostname: string, port: int, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
target := parse_hostname_or_endpoint(hostname) or_return
@@ -62,6 +66,8 @@ dial_tcp_from_hostname_with_port_override :: proc(hostname: string, port: int, o
/*
Expects the `host` as Host.
+
+ Errors that can be returned: `Resolve_Error`, `DNS_Error`, `Create_Socket_Error`, or `Dial_Error`
*/
dial_tcp_from_host :: proc(host: Host, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
if host.port == 0 {
@@ -76,6 +82,8 @@ dial_tcp_from_host :: proc(host: Host, options := default_tcp_options) -> (socke
/*
Expects the `target` as a Host_OrEndpoint.
Unwraps the underlying type and calls `dial_tcp_from_host` or `dial_tcp_from_endpoint`.
+
+ Errors that can be returned: `Parse_Endpoint_Error`, `Resolve_Error`, `DNS_Error`, `Create_Socket_Error`, or `Dial_Error`
*/
dial_tcp_from_host_or_endpoint :: proc(target: Host_Or_Endpoint, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
switch t in target {
@@ -87,11 +95,20 @@ dial_tcp_from_host_or_endpoint :: proc(target: Host_Or_Endpoint, options := defa
unreachable()
}
-// Dial from an Address
+/*
+ Dial from an Address.
+
+ Errors that can be returned: `Create_Socket_Error`, or `Dial_Error`
+*/
dial_tcp_from_address_and_port :: proc(address: Address, port: int, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
return dial_tcp_from_endpoint({address, port}, options)
}
+/*
+ Dial from an Endpoint.
+
+ Errors that can be returned: `Create_Socket_Error`, or `Dial_Error`
+*/
dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
return _dial_tcp_from_endpoint(endpoint, options)
}
@@ -105,11 +122,11 @@ dial_tcp :: proc{
dial_tcp_from_host_or_endpoint,
}
-create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Create_Socket_Error) {
return _create_socket(family, protocol)
}
-bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Bind_Error) {
return _bind(socket, ep)
}
@@ -119,7 +136,7 @@ bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
This is like a client TCP socket, except that it can send data to any remote endpoint without needing to establish a connection first.
*/
-make_unbound_udp_socket :: proc(family: Address_Family) -> (socket: UDP_Socket, err: Network_Error) {
+make_unbound_udp_socket :: proc(family: Address_Family) -> (socket: UDP_Socket, err: Create_Socket_Error) {
sock := create_socket(family, .UDP) or_return
socket = sock.(UDP_Socket)
return
@@ -131,6 +148,8 @@ make_unbound_udp_socket :: proc(family: Address_Family) -> (socket: UDP_Socket,
This is like a listening TCP socket, except that data packets can be sent and received without needing to establish a connection first.
The `bound_address` is the address of the network interface that you want to use, or a loopback address if you don't care which to use.
+
+ Errors that can be returned: `Parse_Endpoint_Error`, `Create_Socket_Error`, or `Bind_Error`
*/
make_bound_udp_socket :: proc(bound_address: Address, port: int) -> (socket: UDP_Socket, err: Network_Error) {
if bound_address == nil {
@@ -141,6 +160,11 @@ make_bound_udp_socket :: proc(bound_address: Address, port: int) -> (socket: UDP
return
}
+/*
+ Creates a TCP socket and starts listening on the given endpoint.
+
+ Errors that can be returned: `Create_Socket_Error`, `Bind_Error`, or `Listen_Error`
+*/
listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket, err: Network_Error) {
assert(backlog > 0 && backlog < int(max(i32)))
@@ -150,11 +174,11 @@ listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: TC
/*
Returns the endpoint that the given socket is listening / bound on.
*/
-bound_endpoint :: proc(socket: Any_Socket) -> (endpoint: Endpoint, err: Network_Error) {
+bound_endpoint :: proc(socket: Any_Socket) -> (endpoint: Endpoint, err: Listen_Error) {
return _bound_endpoint(socket)
}
-accept_tcp :: proc(socket: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+accept_tcp :: proc(socket: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Accept_Error) {
return _accept_tcp(socket, options)
}
@@ -162,11 +186,11 @@ close :: proc(socket: Any_Socket) {
_close(socket)
}
-recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) {
return _recv_tcp(socket, buf)
}
-recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) {
return _recv_udp(socket, buf)
}
@@ -175,6 +199,8 @@ recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_en
Note: `remote_endpoint` parameter is non-nil only if the socket type is UDP. On TCP sockets it
will always return `nil`.
+
+ Errors that can be returned: `TCP_Recv_Error`, or `UDP_Recv_Error`
*/
recv_any :: proc(socket: Any_Socket, buf: []byte) -> (
bytes_read: int,
@@ -197,7 +223,7 @@ recv :: proc{recv_tcp, recv_udp, recv_any}
Repeatedly sends data until the entire buffer is sent.
If a send fails before all data is sent, returns the amount sent up to that point.
*/
-send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: TCP_Send_Error) {
return _send_tcp(socket, buf)
}
@@ -207,10 +233,15 @@ send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: N
Datagrams are limited in size; attempting to send more than this limit at once will result in a Message_Too_Long error.
UDP packets are not guarenteed to be received in order.
*/
-send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
+send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: UDP_Send_Error) {
return _send_udp(socket, buf, to)
}
+/*
+ Sends data over the socket.
+
+ Errors that can be returned: `TCP_Send_Error`, or `UDP_Send_Error`
+*/
send_any :: proc(socket: Any_Socket, buf: []byte, to: Maybe(Endpoint) = nil) -> (
bytes_written: int,
err: Network_Error,
@@ -226,14 +257,14 @@ send_any :: proc(socket: Any_Socket, buf: []byte, to: Maybe(Endpoint) = nil) ->
send :: proc{send_tcp, send_udp, send_any}
-shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Shutdown_Error) {
return _shutdown(socket, manner)
}
-set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Socket_Option_Error {
return _set_option(socket, option, value, loc)
}
-set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Set_Blocking_Error) {
return _set_blocking(socket, should_block)
}
diff --git a/core/net/socket_darwin.odin b/core/net/socket_darwin.odin
index a132a6a95..1e3fd8f4e 100644
--- a/core/net/socket_darwin.odin
+++ b/core/net/socket_darwin.odin
@@ -37,8 +37,14 @@ Socket_Option :: enum c.int {
Send_Timeout = c.int(posix.Sock_Option.SNDTIMEO),
}
+Shutdown_Manner :: enum c.int {
+ Receive = c.int(posix.SHUT_RD),
+ Send = c.int(posix.SHUT_WR),
+ Both = c.int(posix.SHUT_RDWR),
+}
+
@(private)
-_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Create_Socket_Error) {
c_type: posix.Sock
c_protocol: posix.Protocol
c_family: posix.AF
@@ -59,7 +65,7 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
sock := posix.socket(c_family, c_type, c_protocol)
if sock < 0 {
- err = Create_Socket_Error(posix.errno())
+ err = _create_socket_error()
return
}
@@ -88,28 +94,19 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint)
if posix.connect(posix.FD(skt), (^posix.sockaddr)(&sockaddr), posix.socklen_t(sockaddr.ss_len)) != .OK {
- errno := posix.errno()
+ err = _dial_error()
close(skt)
- return {}, Dial_Error(errno)
}
return
}
-// On Darwin, any port below 1024 is 'privileged' - which means that you need root access in order to use it.
-MAX_PRIVILEGED_PORT :: 1023
-
@(private)
-_bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+_bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Bind_Error) {
sockaddr := _endpoint_to_sockaddr(ep)
s := any_socket_to_socket(skt)
if posix.bind(posix.FD(s), (^posix.sockaddr)(&sockaddr), posix.socklen_t(sockaddr.ss_len)) != .OK {
- errno := posix.errno()
- if errno == .EACCES && ep.port <= MAX_PRIVILEGED_PORT {
- err = .Privileged_Port_Without_Root
- } else {
- err = Bind_Error(errno)
- }
+ err = _bind_error()
}
return
@@ -128,25 +125,23 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
// bypass the cooldown period, and allow the next run of the program to
// use the same address immediately.
//
- // TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address!
- set_option(sock, .Reuse_Address, true) or_return
+ _ = set_option(sock, .Reuse_Address, true)
bind(sock, interface_endpoint) or_return
if posix.listen(posix.FD(skt), i32(backlog)) != .OK {
- err = Listen_Error(posix.errno())
- return
+ err = _listen_error()
}
return
}
@(private)
-_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error) {
+_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
addr: posix.sockaddr_storage
addr_len := posix.socklen_t(size_of(addr))
if posix.getsockname(posix.FD(any_socket_to_socket(sock)), (^posix.sockaddr)(&addr), &addr_len) != .OK {
- err = Listen_Error(posix.errno())
+ err = _listen_error()
return
}
@@ -155,12 +150,12 @@ _bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error)
}
@(private)
-_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Accept_Error) {
addr: posix.sockaddr_storage
addr_len := posix.socklen_t(size_of(addr))
client_sock := posix.accept(posix.FD(sock), (^posix.sockaddr)(&addr), &addr_len)
if client_sock < 0 {
- err = Accept_Error(posix.errno())
+ err = _accept_error()
return
}
@@ -176,14 +171,14 @@ _close :: proc(skt: Any_Socket) {
}
@(private)
-_recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+_recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) {
if len(buf) <= 0 {
return
}
res := posix.recv(posix.FD(skt), raw_data(buf), len(buf), {})
if res < 0 {
- err = TCP_Recv_Error(posix.errno())
+ err = _tcp_recv_error()
return
}
@@ -191,7 +186,7 @@ _recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Networ
}
@(private)
-_recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+_recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) {
if len(buf) <= 0 {
return
}
@@ -200,7 +195,7 @@ _recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endp
fromsize := posix.socklen_t(size_of(from))
res := posix.recvfrom(posix.FD(skt), raw_data(buf), len(buf), {}, (^posix.sockaddr)(&from), &fromsize)
if res < 0 {
- err = UDP_Recv_Error(posix.errno())
+ err = _udp_recv_error()
return
}
@@ -210,20 +205,13 @@ _recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endp
}
@(private)
-_send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+_send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: TCP_Send_Error) {
for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit]
res := posix.send(posix.FD(skt), raw_data(remaining), len(remaining), {.NOSIGNAL})
if res < 0 {
- errno := posix.errno()
- if errno == .EPIPE {
- // EPIPE arises if the socket has been closed remotely.
- err = TCP_Send_Error.Connection_Closed
- return
- }
-
- err = TCP_Send_Error(errno)
+ err = _tcp_send_error()
return
}
@@ -233,21 +221,14 @@ _send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Net
}
@(private)
-_send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
+_send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: UDP_Send_Error) {
toaddr := _endpoint_to_sockaddr(to)
for bytes_written < len(buf) {
limit := min(1<<31, len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit]
res := posix.sendto(posix.FD(skt), raw_data(remaining), len(remaining), {.NOSIGNAL}, (^posix.sockaddr)(&toaddr), posix.socklen_t(toaddr.ss_len))
if res < 0 {
- errno := posix.errno()
- if errno == .EPIPE {
- // EPIPE arises if the socket has been closed remotely.
- err = UDP_Send_Error.Not_Socket
- return
- }
-
- err = UDP_Send_Error(errno)
+ err = _udp_send_error()
return
}
@@ -257,16 +238,16 @@ _send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written:
}
@(private)
-_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Shutdown_Error) {
s := any_socket_to_socket(skt)
if posix.shutdown(posix.FD(s), posix.Shut(manner)) != .OK {
- err = Shutdown_Error(posix.errno())
+ err = _shutdown_error()
}
return
}
@(private)
-_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Socket_Option_Error {
level := posix.SOL_SOCKET if option != .TCP_Nodelay else posix.IPPROTO_TCP
// NOTE(tetra, 2022-02-15): On Linux, you cannot merely give a single byte for a bool;
@@ -337,19 +318,19 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
skt := any_socket_to_socket(s)
if posix.setsockopt(posix.FD(skt), i32(level), posix.Sock_Option(option), ptr, len) != .OK {
- return Socket_Option_Error(posix.errno())
+ return _socket_option_error()
}
return nil
}
@(private)
-_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Set_Blocking_Error) {
socket := any_socket_to_socket(socket)
flags_ := posix.fcntl(posix.FD(socket), .GETFL, 0)
if flags_ < 0 {
- return Set_Blocking_Error(posix.errno())
+ return _set_blocking_error()
}
flags := transmute(posix.O_Flags)flags_
@@ -360,7 +341,7 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
}
if posix.fcntl(posix.FD(socket), .SETFL, flags) < 0 {
- return Set_Blocking_Error(posix.errno())
+ return _set_blocking_error()
}
return nil
diff --git a/core/net/socket_freebsd.odin b/core/net/socket_freebsd.odin
index 3a3774007..4824b63dd 100644
--- a/core/net/socket_freebsd.odin
+++ b/core/net/socket_freebsd.odin
@@ -54,8 +54,14 @@ Socket_Option :: enum c.int {
Receive_Timeout = cast(c.int)freebsd.Socket_Option.RCVTIMEO,
}
+Shutdown_Manner :: enum c.int {
+ Receive = cast(c.int)freebsd.Shutdown_Method.RD,
+ Send = cast(c.int)freebsd.Shutdown_Method.WR,
+ Both = cast(c.int)freebsd.Shutdown_Method.RDWR,
+}
+
@(private)
-_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Create_Socket_Error) {
sys_family: freebsd.Protocol_Family = ---
sys_protocol: freebsd.Protocol = ---
sys_socket_type: freebsd.Socket_Type = ---
@@ -72,24 +78,7 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
new_socket, errno := freebsd.socket(sys_family, sys_socket_type, sys_protocol)
if errno != nil {
- err = cast(Create_Socket_Error)errno
- return
- }
-
- // NOTE(Feoramund): By default, FreeBSD will generate SIGPIPE if an EPIPE
- // error is raised during the writing of a socket that may be closed.
- // This behavior is unlikely to be expected by general users.
- //
- // There are two workarounds. One is to apply the .NOSIGNAL flag when using
- // the `sendto` syscall. However, that would prevent users of this library
- // from re-enabling the SIGPIPE-raising functionality, if they really
- // wanted it.
- //
- // So I have disabled it here with this socket option for all sockets.
- truth: b32 = true
- errno = freebsd.setsockopt(new_socket, .SOCKET, .NOSIGPIPE, &truth, size_of(truth))
- if errno != nil {
- err = cast(Socket_Option_Error)errno
+ err = _create_socket_error(errno)
return
}
@@ -115,19 +104,19 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
errno := freebsd.connect(cast(Fd)socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
if errno != nil {
close(socket)
- return {}, cast(Dial_Error)errno
+ return {}, _dial_error(errno)
}
return
}
@(private)
-_bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+_bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Bind_Error) {
sockaddr := _endpoint_to_sockaddr(ep)
real_socket := any_socket_to_socket(socket)
errno := freebsd.bind(cast(Fd)real_socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
if errno != nil {
- err = cast(Bind_Error)errno
+ err = _bind_error(errno)
}
return
}
@@ -143,7 +132,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
errno := freebsd.listen(cast(Fd)socket, backlog)
if errno != nil {
- err = cast(Listen_Error)errno
+ err = _listen_error(errno)
return
}
@@ -151,12 +140,12 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
}
@(private)
-_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error) {
+_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
sockaddr: freebsd.Socket_Address_Storage
errno := freebsd.getsockname(cast(Fd)any_socket_to_socket(sock), &sockaddr)
if errno != nil {
- err = cast(Listen_Error)errno
+ err = _listen_error(errno)
return
}
@@ -165,12 +154,12 @@ _bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error)
}
@(private)
-_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Accept_Error) {
sockaddr: freebsd.Socket_Address_Storage
result, errno := freebsd.accept(cast(Fd)sock, &sockaddr)
if errno != nil {
- err = cast(Accept_Error)errno
+ err = _accept_error(errno)
return
}
@@ -187,20 +176,20 @@ _close :: proc(socket: Any_Socket) {
}
@(private)
-_recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+_recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) {
if len(buf) == 0 {
return
}
result, errno := freebsd.recv(cast(Fd)socket, buf, .NONE)
if errno != nil {
- err = cast(TCP_Recv_Error)errno
+ err = _tcp_recv_error(errno)
return
}
return result, nil
}
@(private)
-_recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+_recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) {
if len(buf) == 0 {
return
}
@@ -208,21 +197,21 @@ _recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_e
result, errno := freebsd.recvfrom(cast(Fd)socket, buf, .NONE, &from)
if errno != nil {
- err = cast(UDP_Recv_Error)errno
+ err = _udp_recv_error(errno)
return
}
return result, _sockaddr_to_endpoint(&from), nil
}
@(private)
-_send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+_send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: TCP_Send_Error) {
for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit]
- result, errno := freebsd.send(cast(Fd)socket, remaining, .NONE)
+ result, errno := freebsd.send(cast(Fd)socket, remaining, .NOSIGNAL)
if errno != nil {
- err = cast(TCP_Send_Error)errno
+ err = _tcp_send_error(errno)
return
}
bytes_written += result
@@ -231,15 +220,15 @@ _send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err:
}
@(private)
-_send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
+_send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: UDP_Send_Error) {
toaddr := _endpoint_to_sockaddr(to)
for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit]
- result, errno := freebsd.sendto(cast(Fd)socket, remaining, .NONE, &toaddr)
+ result, errno := freebsd.sendto(cast(Fd)socket, remaining, .NOSIGNAL, &toaddr)
if errno != nil {
- err = cast(UDP_Send_Error)errno
+ err = _udp_send_error(errno)
return
}
bytes_written += result
@@ -248,17 +237,17 @@ _send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_writt
}
@(private)
-_shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+_shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Shutdown_Error) {
real_socket := cast(Fd)any_socket_to_socket(socket)
errno := freebsd.shutdown(real_socket, cast(freebsd.Shutdown_Method)manner)
if errno != nil {
- return cast(Shutdown_Error)errno
+ return _shutdown_error(errno)
}
return
}
@(private)
-_set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+_set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Socket_Option_Error {
// NOTE(Feoramund): I found that FreeBSD, like Linux, requires at least 32
// bits for a boolean socket option value. Nothing less will work.
bool_value: b32
@@ -315,25 +304,25 @@ _set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc :
case u16: int_value = cast(i32)real
case i32: int_value = real
case u32:
- if real > u32(max(i32)) { return .Value_Out_Of_Range }
+ if real > u32(max(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case i64:
- if real > i64(max(i32)) || real < i64(min(i32)) { return .Value_Out_Of_Range }
+ if real > i64(max(i32)) || real < i64(min(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case u64:
- if real > u64(max(i32)) { return .Value_Out_Of_Range }
+ if real > u64(max(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case i128:
- if real > i128(max(i32)) || real < i128(min(i32)) { return .Value_Out_Of_Range }
+ if real > i128(max(i32)) || real < i128(min(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case u128:
- if real > u128(max(i32)) { return .Value_Out_Of_Range }
+ if real > u128(max(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case int:
- if real > int(max(i32)) || real < int(min(i32)) { return .Value_Out_Of_Range }
+ if real > int(max(i32)) || real < int(min(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case uint:
- if real > uint(max(i32)) { return .Value_Out_Of_Range }
+ if real > uint(max(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case:
panic("set_option() value must be an integer here", loc)
@@ -347,19 +336,19 @@ _set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc :
real_socket := any_socket_to_socket(socket)
errno := freebsd.setsockopt(cast(Fd)real_socket, .SOCKET, cast(freebsd.Socket_Option)option, ptr, len)
if errno != nil {
- return cast(Socket_Option_Error)errno
+ return _socket_option_error(errno)
}
return nil
}
@(private)
-_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Set_Blocking_Error) {
real_socket := any_socket_to_socket(socket)
flags, errno := freebsd.fcntl_getfl(cast(freebsd.Fd)real_socket)
if errno != nil {
- return cast(Set_Blocking_Error)errno
+ return _set_blocking_error(errno)
}
if should_block {
@@ -370,7 +359,7 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
errno = freebsd.fcntl_setfl(cast(freebsd.Fd)real_socket, flags)
if errno != nil {
- return cast(Set_Blocking_Error)errno
+ return _set_blocking_error(errno)
}
return
diff --git a/core/net/socket_linux.odin b/core/net/socket_linux.odin
index cafec747d..7dd1ccc9c 100644
--- a/core/net/socket_linux.odin
+++ b/core/net/socket_linux.odin
@@ -38,15 +38,21 @@ Socket_Option :: enum c.int {
Broadcast = c.int(linux.Socket_Option.BROADCAST),
}
+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),
+}
+
// Wrappers and unwrappers for system-native types
@(private="file")
-_unwrap_os_socket :: proc "contextless" (sock: Any_Socket)->linux.Fd {
+_unwrap_os_socket :: proc "contextless" (sock: Any_Socket) -> linux.Fd {
return linux.Fd(any_socket_to_socket(sock))
}
@(private="file")
-_wrap_os_socket :: proc "contextless" (sock: linux.Fd, protocol: Socket_Protocol)->Any_Socket {
+_wrap_os_socket :: proc "contextless" (sock: linux.Fd, protocol: Socket_Protocol) -> Any_Socket {
switch protocol {
case .TCP: return TCP_Socket(Socket(sock))
case .UDP: return UDP_Socket(Socket(sock))
@@ -56,7 +62,7 @@ _wrap_os_socket :: proc "contextless" (sock: linux.Fd, protocol: Socket_Protocol
}
@(private="file")
-_unwrap_os_family :: proc "contextless" (family: Address_Family)->linux.Address_Family {
+_unwrap_os_family :: proc "contextless" (family: Address_Family) -> linux.Address_Family {
switch family {
case .IP4: return .INET
case .IP6: return .INET6
@@ -66,7 +72,7 @@ _unwrap_os_family :: proc "contextless" (family: Address_Family)->linux.Address_
}
@(private="file")
-_unwrap_os_proto_socktype :: proc "contextless" (protocol: Socket_Protocol)->(linux.Protocol, linux.Socket_Type) {
+_unwrap_os_proto_socktype :: proc "contextless" (protocol: Socket_Protocol) -> (linux.Protocol, linux.Socket_Type) {
switch protocol {
case .TCP: return .TCP, .STREAM
case .UDP: return .UDP, .DGRAM
@@ -76,7 +82,7 @@ _unwrap_os_proto_socktype :: proc "contextless" (protocol: Socket_Protocol)->(li
}
@(private="file")
-_unwrap_os_addr :: proc "contextless" (endpoint: Endpoint)->(linux.Sock_Addr_Any) {
+_unwrap_os_addr :: proc "contextless" (endpoint: Endpoint) -> linux.Sock_Addr_Any {
switch address in endpoint.address {
case IP4_Address:
return {
@@ -100,7 +106,7 @@ _unwrap_os_addr :: proc "contextless" (endpoint: Endpoint)->(linux.Sock_Addr_Any
}
@(private="file")
-_wrap_os_addr :: proc "contextless" (addr: linux.Sock_Addr_Any)->(Endpoint) {
+_wrap_os_addr :: proc "contextless" (addr: linux.Sock_Addr_Any) -> Endpoint {
#partial switch addr.family {
case .INET:
return {
@@ -117,12 +123,12 @@ _wrap_os_addr :: proc "contextless" (addr: linux.Sock_Addr_Any)->(Endpoint) {
}
}
-_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (Any_Socket, Network_Error) {
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (Any_Socket, Create_Socket_Error) {
family := _unwrap_os_family(family)
proto, socktype := _unwrap_os_proto_socktype(protocol)
sock, errno := linux.socket(family, socktype, {.CLOEXEC}, proto)
if errno != .NONE {
- return {}, Create_Socket_Error(errno)
+ return {}, _create_socket_error(errno)
}
return _wrap_os_socket(sock, protocol), nil
}
@@ -138,7 +144,7 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
os_sock, errno = linux.socket(_unwrap_os_family(family_from_endpoint(endpoint)), .STREAM, {.CLOEXEC}, .TCP)
if errno != .NONE {
// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
- return {}, Create_Socket_Error(errno)
+ return {}, _create_socket_error(errno)
}
// NOTE(tetra): This is so that if we crash while the socket is open, we can
// bypass the cooldown period, and allow the next run of the program to
@@ -149,7 +155,7 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
errno = linux.connect(linux.Fd(os_sock), &addr)
if errno != .NONE {
close(cast(TCP_Socket) os_sock)
- return {}, Dial_Error(errno)
+ return {}, _dial_error(errno)
}
// NOTE(tetra): Not vital to succeed; error ignored
no_delay: b32 = cast(b32) options.no_delay
@@ -158,11 +164,11 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
}
@(private)
-_bind :: proc(sock: Any_Socket, endpoint: Endpoint) -> (Network_Error) {
+_bind :: proc(sock: Any_Socket, endpoint: Endpoint) -> (Bind_Error) {
addr := _unwrap_os_addr(endpoint)
errno := linux.bind(_unwrap_os_socket(sock), &addr)
if errno != .NONE {
- return Bind_Error(errno)
+ return _bind_error(errno)
}
return nil
}
@@ -180,7 +186,7 @@ _listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket,
os_sock: linux.Fd
os_sock, errno = linux.socket(ep_family, .STREAM, {.CLOEXEC}, .TCP)
if errno != .NONE {
- err = Create_Socket_Error(errno)
+ err = _create_socket_error(errno)
return
}
socket = cast(TCP_Socket)os_sock
@@ -193,31 +199,30 @@ _listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket,
// TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address!
do_reuse_addr: b32 = true
if errno = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &do_reuse_addr); errno != .NONE {
- err = Listen_Error(errno)
+ err = _listen_error(errno)
return
}
// Bind the socket to endpoint address
if errno = linux.bind(os_sock, &ep_address); errno != .NONE {
- err = Bind_Error(errno)
+ err = _bind_error(errno)
return
}
// Listen on bound socket
if errno = linux.listen(os_sock, cast(i32) backlog); errno != .NONE {
- err = Listen_Error(errno)
- return
+ err = _listen_error(errno)
}
return
}
@(private)
-_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error) {
+_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
addr: linux.Sock_Addr_Any
errno := linux.getsockname(_unwrap_os_socket(sock), &addr)
if errno != .NONE {
- err = Listen_Error(errno)
+ err = _listen_error(errno)
return
}
@@ -226,11 +231,11 @@ _bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error)
}
@(private)
-_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (tcp_client: TCP_Socket, endpoint: Endpoint, err: Network_Error) {
+_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (tcp_client: TCP_Socket, endpoint: Endpoint, err: Accept_Error) {
addr: linux.Sock_Addr_Any
client_sock, errno := linux.accept(linux.Fd(sock), &addr)
if errno != .NONE {
- return {}, {}, Accept_Error(errno)
+ return {}, {}, _accept_error(errno)
}
// NOTE(tetra): Not vital to succeed; error ignored
val: b32 = cast(b32) options.no_delay
@@ -244,19 +249,19 @@ _close :: proc(sock: Any_Socket) {
}
@(private)
-_recv_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, Network_Error) {
+_recv_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, TCP_Recv_Error) {
if len(buf) <= 0 {
return 0, nil
}
bytes_read, errno := linux.recv(linux.Fd(tcp_sock), buf, {})
if errno != .NONE {
- return 0, TCP_Recv_Error(errno)
+ return 0, _tcp_recv_error(errno)
}
return int(bytes_read), nil
}
@(private)
-_recv_udp :: proc(udp_sock: UDP_Socket, buf: []byte) -> (int, Endpoint, Network_Error) {
+_recv_udp :: proc(udp_sock: UDP_Socket, buf: []byte) -> (int, Endpoint, UDP_Recv_Error) {
if len(buf) <= 0 {
// NOTE(flysand): It was returning no error, I didn't change anything
return 0, {}, {}
@@ -268,28 +273,24 @@ _recv_udp :: proc(udp_sock: UDP_Socket, buf: []byte) -> (int, Endpoint, Network_
from_addr: linux.Sock_Addr_Any
bytes_read, errno := linux.recvfrom(linux.Fd(udp_sock), buf, {.TRUNC}, &from_addr)
if errno != .NONE {
- return 0, {}, UDP_Recv_Error(errno)
+ return 0, {}, _udp_recv_error(errno)
}
if bytes_read > len(buf) {
// NOTE(tetra): The buffer has been filled, with a partial message.
- return len(buf), {}, .Buffer_Too_Small
+ return len(buf), {}, .Excess_Truncated
}
return bytes_read, _wrap_os_addr(from_addr), nil
}
@(private)
-_send_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, Network_Error) {
+_send_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, TCP_Send_Error) {
total_written := 0
for total_written < len(buf) {
limit := min(int(max(i32)), len(buf) - total_written)
remaining := buf[total_written:][:limit]
res, errno := linux.send(linux.Fd(tcp_sock), remaining, {.NOSIGNAL})
- if errno == .EPIPE {
- // If the peer is disconnected when we are trying to send we will get an `EPIPE` error,
- // so we turn that into a clearer error
- return total_written, TCP_Send_Error.Connection_Closed
- } else if errno != .NONE {
- return total_written, TCP_Send_Error(errno)
+ if errno != .NONE {
+ return total_written, _tcp_send_error(errno)
}
total_written += int(res)
}
@@ -297,28 +298,28 @@ _send_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, Network_Error) {
}
@(private)
-_send_udp :: proc(udp_sock: UDP_Socket, buf: []byte, to: Endpoint) -> (int, Network_Error) {
+_send_udp :: proc(udp_sock: UDP_Socket, buf: []byte, to: Endpoint) -> (int, UDP_Send_Error) {
to_addr := _unwrap_os_addr(to)
bytes_written, errno := linux.sendto(linux.Fd(udp_sock), buf, {}, &to_addr)
if errno != .NONE {
- return bytes_written, UDP_Send_Error(errno)
+ return bytes_written, _udp_send_error(errno)
}
return int(bytes_written), nil
}
@(private)
-_shutdown :: proc(sock: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+_shutdown :: proc(sock: Any_Socket, manner: Shutdown_Manner) -> (err: Shutdown_Error) {
os_sock := _unwrap_os_socket(sock)
errno := linux.shutdown(os_sock, cast(linux.Shutdown_How) manner)
if errno != .NONE {
- return Shutdown_Error(errno)
+ return _shutdown_error(errno)
}
return nil
}
// TODO(flysand): Figure out what we want to do with this on core:sys/ level.
@(private)
-_set_option :: proc(sock: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+_set_option :: proc(sock: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Socket_Option_Error {
level: int
if option == .TCP_Nodelay {
level = int(linux.SOL_TCP)
@@ -388,19 +389,19 @@ _set_option :: proc(sock: Any_Socket, option: Socket_Option, value: any, loc :=
errno = linux.setsockopt(os_sock, level, int(option), &int_value)
}
if errno != .NONE {
- return Socket_Option_Error(errno)
+ return _socket_option_error(errno)
}
return nil
}
@(private)
-_set_blocking :: proc(sock: Any_Socket, should_block: bool) -> (err: Network_Error) {
+_set_blocking :: proc(sock: Any_Socket, should_block: bool) -> (err: Set_Blocking_Error) {
errno: linux.Errno
flags: linux.Open_Flags
os_sock := _unwrap_os_socket(sock)
flags, errno = linux.fcntl(os_sock, linux.F_GETFL)
if errno != .NONE {
- return Set_Blocking_Error(errno)
+ return _set_blocking_error(errno)
}
if should_block {
flags -= {.NONBLOCK}
@@ -409,7 +410,7 @@ _set_blocking :: proc(sock: Any_Socket, should_block: bool) -> (err: Network_Err
}
errno = linux.fcntl(os_sock, linux.F_SETFL, flags)
if errno != .NONE {
- return Set_Blocking_Error(errno)
+ return _set_blocking_error(errno)
}
return nil
}
diff --git a/core/net/socket_windows.odin b/core/net/socket_windows.odin
index f19be536a..64bacf6b5 100644
--- a/core/net/socket_windows.odin
+++ b/core/net/socket_windows.odin
@@ -24,13 +24,67 @@ import "core:c"
import win "core:sys/windows"
import "core:time"
+Socket_Option :: enum c.int {
+ // bool: Whether the address that this socket is bound to can be reused by other sockets.
+ // This allows you to bypass the cooldown period if a program dies while the socket is bound.
+ Reuse_Address = win.SO_REUSEADDR,
+
+ // bool: Whether other programs will be inhibited from binding the same endpoint as this socket.
+ Exclusive_Addr_Use = win.SO_EXCLUSIVEADDRUSE,
+
+ // bool: When true, keepalive packets will be automatically be sent for this connection. TODO: verify this understanding
+ Keep_Alive = win.SO_KEEPALIVE,
+
+ // bool: When true, client connections will immediately be sent a TCP/IP RST response, rather than being accepted.
+ Conditional_Accept = win.SO_CONDITIONAL_ACCEPT,
+
+ // bool: If true, when the socket is closed, but data is still waiting to be sent, discard that data.
+ Dont_Linger = win.SO_DONTLINGER,
+
+ // bool: When true, 'out-of-band' data sent over the socket will be read by a normal net.recv() call, the same as normal 'in-band' data.
+ Out_Of_Bounds_Data_Inline = win.SO_OOBINLINE,
+
+ // bool: When true, disables send-coalescing, therefore reducing latency.
+ TCP_Nodelay = win.TCP_NODELAY,
+
+ // win.LINGER: Customizes how long (if at all) the socket will remain open when there
+ // is some remaining data waiting to be sent, and net.close() is called.
+ Linger = win.SO_LINGER,
+
+ // win.DWORD: The size, in bytes, of the OS-managed receive-buffer for this socket.
+ Receive_Buffer_Size = win.SO_RCVBUF,
+
+ // win.DWORD: The size, in bytes, of the OS-managed send-buffer for this socket.
+ Send_Buffer_Size = win.SO_SNDBUF,
+
+ // win.DWORD: For blocking sockets, the time in milliseconds to wait for incoming data to be received, before giving up and returning .Timeout.
+ // For non-blocking sockets, ignored.
+ // Use a value of zero to potentially wait forever.
+ Receive_Timeout = win.SO_RCVTIMEO,
+
+ // win.DWORD: For blocking sockets, the time in milliseconds to wait for outgoing data to be sent, before giving up and returning .Timeout.
+ // For non-blocking sockets, ignored.
+ // Use a value of zero to potentially wait forever.
+ Send_Timeout = win.SO_SNDTIMEO,
+
+ // bool: Allow sending to, receiving from, and binding to, a broadcast address.
+ Broadcast = win.SO_BROADCAST,
+}
+
+
+Shutdown_Manner :: enum c.int {
+ Receive = win.SD_RECEIVE,
+ Send = win.SD_SEND,
+ Both = win.SD_BOTH,
+}
+
@(init, private)
ensure_winsock_initialized :: proc() {
win.ensure_winsock_initialized()
}
@(private)
-_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Create_Socket_Error) {
c_type, c_protocol, c_family: c.int
switch family {
@@ -49,7 +103,7 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
sock := win.socket(c_family, c_type, c_protocol)
if sock == win.INVALID_SOCKET {
- err = Create_Socket_Error(win.WSAGetLastError())
+ err = _create_socket_error()
return
}
@@ -80,7 +134,7 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint)
res := win.connect(win.SOCKET(socket), &sockaddr, size_of(sockaddr))
if res < 0 {
- err = Dial_Error(win.WSAGetLastError())
+ err = _dial_error()
close(socket)
return {}, err
}
@@ -93,12 +147,12 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
}
@(private)
-_bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+_bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Bind_Error) {
sockaddr := _endpoint_to_sockaddr(ep)
sock := any_socket_to_socket(socket)
res := win.bind(win.SOCKET(sock), &sockaddr, size_of(sockaddr))
if res < 0 {
- err = Bind_Error(win.WSAGetLastError())
+ err = _bind_error()
}
return
}
@@ -117,17 +171,17 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
bind(sock, interface_endpoint) or_return
if res := win.listen(win.SOCKET(socket), i32(backlog)); res == win.SOCKET_ERROR {
- err = Listen_Error(win.WSAGetLastError())
+ err = _listen_error()
}
return
}
@(private)
-_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error) {
+_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
sockaddr: win.SOCKADDR_STORAGE_LH
sockaddrlen := c.int(size_of(sockaddr))
if win.getsockname(win.SOCKET(any_socket_to_socket(sock)), &sockaddr, &sockaddrlen) == win.SOCKET_ERROR {
- err = Listen_Error(win.WSAGetLastError())
+ err = _listen_error()
return
}
@@ -136,7 +190,7 @@ _bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error)
}
@(private)
-_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Accept_Error) {
for {
sockaddr: win.SOCKADDR_STORAGE_LH
sockaddrlen := c.int(size_of(sockaddr))
@@ -150,7 +204,7 @@ _accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client
// can do this to match the behaviour.
continue
}
- err = Accept_Error(e)
+ err = _accept_error()
return
}
client = TCP_Socket(client_sock)
@@ -170,20 +224,20 @@ _close :: proc(socket: Any_Socket) {
}
@(private)
-_recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+_recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) {
if len(buf) <= 0 {
return
}
res := win.recv(win.SOCKET(socket), raw_data(buf), c.int(len(buf)), 0)
if res < 0 {
- err = TCP_Recv_Error(win.WSAGetLastError())
+ err = _tcp_recv_error()
return
}
return int(res), nil
}
@(private)
-_recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+_recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) {
if len(buf) <= 0 {
return
}
@@ -192,7 +246,7 @@ _recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_e
fromsize := c.int(size_of(from))
res := win.recvfrom(win.SOCKET(socket), raw_data(buf), c.int(len(buf)), 0, &from, &fromsize)
if res < 0 {
- err = UDP_Recv_Error(win.WSAGetLastError())
+ err = _udp_recv_error()
return
}
@@ -202,13 +256,13 @@ _recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_e
}
@(private)
-_send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+_send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: TCP_Send_Error) {
for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:]
res := win.send(win.SOCKET(socket), raw_data(remaining), c.int(limit), 0)
if res < 0 {
- err = TCP_Send_Error(win.WSAGetLastError())
+ err = _tcp_send_error()
return
}
bytes_written += int(res)
@@ -217,34 +271,34 @@ _send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err:
}
@(private)
-_send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
- if len(buf) > int(max(c.int)) {
- // NOTE(tetra): If we don't guard this, we'll return (0, nil) instead, which is misleading.
- err = .Message_Too_Long
- return
- }
+_send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: UDP_Send_Error) {
toaddr := _endpoint_to_sockaddr(to)
- res := win.sendto(win.SOCKET(socket), raw_data(buf), c.int(len(buf)), 0, &toaddr, size_of(toaddr))
- if res < 0 {
- err = UDP_Send_Error(win.WSAGetLastError())
- return
+ for bytes_written < len(buf) {
+ limit := min(int(max(i32)), len(buf) - bytes_written)
+ remaining := buf[bytes_written:]
+ res := win.sendto(win.SOCKET(socket), raw_data(remaining), c.int(limit), 0, &toaddr, size_of(toaddr))
+ if res < 0 {
+ err = _udp_send_error()
+ return
+ }
+
+ bytes_written += int(res)
}
- bytes_written = int(res)
return
}
@(private)
-_shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+_shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Shutdown_Error) {
s := any_socket_to_socket(socket)
res := win.shutdown(win.SOCKET(s), c.int(manner))
if res < 0 {
- return Shutdown_Error(win.WSAGetLastError())
+ return _shutdown_error()
}
return
}
@(private)
-_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Socket_Option_Error {
level := win.SOL_SOCKET if option != .TCP_Nodelay else win.IPPROTO_TCP
bool_value: b32
@@ -283,11 +337,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
num_secs := i64(time.duration_seconds(t))
- if time.Duration(num_secs * 1e9) != t {
- return .Linger_Only_Supports_Whole_Seconds
- }
if num_secs > i64(max(u16)) {
- return .Value_Out_Of_Range
+ return .Invalid_Value
}
linger_value.l_onoff = 1
linger_value.l_linger = c.ushort(num_secs)
@@ -323,19 +374,19 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
socket := any_socket_to_socket(s)
res := win.setsockopt(win.SOCKET(socket), c.int(level), c.int(option), ptr, len)
if res < 0 {
- return Socket_Option_Error(win.WSAGetLastError())
+ return _socket_option_error()
}
return nil
}
@(private)
-_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Set_Blocking_Error) {
socket := any_socket_to_socket(socket)
arg: win.DWORD = 0 if should_block else 1
res := win.ioctlsocket(win.SOCKET(socket), transmute(win.c_long)win.FIONBIO, &arg)
if res == win.SOCKET_ERROR {
- return Set_Blocking_Error(win.WSAGetLastError())
+ return _set_blocking_error()
}
return nil
diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin
index 5b2952495..d808bcd09 100644
--- a/core/sys/windows/ws2_32.odin
+++ b/core/sys/windows/ws2_32.odin
@@ -91,6 +91,7 @@ foreign ws2_32 {
WSACleanup :: proc() -> c_int ---
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsagetlasterror)
WSAGetLastError :: proc() -> c_int ---
+ WSASetLastError :: proc(err: c_int) ---
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll)
WSAPoll :: proc(fdArray: ^WSA_POLLFD, fds: c_ulong, timeout: c_int) -> c_int ---
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaduplicatesocketw)
diff --git a/tests/core/net/test_core_net_freebsd.odin b/tests/core/net/test_core_net_freebsd.odin
index 39e364e80..590db7de0 100644
--- a/tests/core/net/test_core_net_freebsd.odin
+++ b/tests/core/net/test_core_net_freebsd.odin
@@ -17,9 +17,6 @@ import "core:net"
import "core:time"
import "core:testing"
-ENDPOINT_DUPLICATE_BINDING := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 11000}
-ENDPOINT_EPIPE_TEST := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 11001}
-
@test
test_duplicate_binding :: proc(t: ^testing.T) {
// FreeBSD has the capacity to permit multiple processes and sockets to
@@ -35,11 +32,16 @@ test_duplicate_binding :: proc(t: ^testing.T) {
if !testing.expect_value(t, err_set1, nil) {
return
}
- err_bind1 := net.bind(tcp_socket1, ENDPOINT_DUPLICATE_BINDING)
+ err_bind1 := net.bind(tcp_socket1, {net.IP4_Loopback, 0})
if !testing.expect_value(t, err_bind1, nil) {
return
}
+ ep, err_bound := net.bound_endpoint(tcp_socket1)
+ if !testing.expect_value(t, err_bound, nil) {
+ return
+ }
+
raw_socket2, err_create2 := net.create_socket(.IP4, .TCP)
if !testing.expect_value(t, err_create2, nil) {
return
@@ -50,7 +52,7 @@ test_duplicate_binding :: proc(t: ^testing.T) {
if !testing.expect_value(t, err_set2, nil) {
return
}
- err_bind2 := net.bind(tcp_socket2, ENDPOINT_DUPLICATE_BINDING)
+ err_bind2 := net.bind(tcp_socket2, ep)
if !testing.expect_value(t, err_bind2, nil) {
return
}
@@ -60,13 +62,18 @@ test_duplicate_binding :: proc(t: ^testing.T) {
test_sigpipe_bypass :: proc(t: ^testing.T) {
// If the internals aren't working as expected, this test will fail by raising SIGPIPE.
- server_socket, listen_err := net.listen_tcp(ENDPOINT_EPIPE_TEST)
+ server_socket, listen_err := net.listen_tcp({net.IP4_Loopback, 0})
if !testing.expect_value(t, listen_err, nil) {
return
}
defer net.close(server_socket)
- client_socket, dial_err := net.dial_tcp(ENDPOINT_EPIPE_TEST)
+ ep, bound_err := net.bound_endpoint(server_socket)
+ if !testing.expect_value(t, bound_err, nil) {
+ return
+ }
+
+ client_socket, dial_err := net.dial_tcp(ep)
if !testing.expect_value(t, dial_err, nil) {
return
}
@@ -80,7 +87,7 @@ test_sigpipe_bypass :: proc(t: ^testing.T) {
data := "Hellope!"
bytes_written, err_send := net.send(client_socket, transmute([]u8)data)
- if !testing.expect_value(t, err_send, net.TCP_Send_Error.Cannot_Send_More_Data) {
+ if !testing.expect_value(t, err_send, net.TCP_Send_Error.Connection_Closed) {
return
}
if !testing.expect_value(t, bytes_written, 0) {