diff --git a/core/net/errors_darwin.odin b/core/net/errors_darwin.odin new file mode 100644 index 000000000..3ee28bc9b --- /dev/null +++ b/core/net/errors_darwin.odin @@ -0,0 +1,182 @@ +// +build darwin +/* + Copyright 2022 Tetralux + Copyright 2022 Colin Davidson + Copyright 2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + List of contributors: + Tetralux: Initial implementation + Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver + Jeroen van Rijn: Cross platform unification, code style, documentation +*/ + +/* + Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures. + For other protocols and their features, see subdirectories of this package. +*/ +package net + +import "core:c" +import "core:os" + +Create_Socket_Error :: enum c.int { + Family_Not_Supported_For_This_Socket = c.int(os.EAFNOSUPPORT), + No_Socket_Descriptors_Available = c.int(os.EMFILE), + No_Buffer_Space_Available = c.int(os.ENOBUFS), + No_Memory_Available_Available = c.int(os.ENOMEM), + Protocol_Unsupported_By_System = c.int(os.EPROTONOSUPPORT), + Wrong_Protocol_For_Socket = c.int(os.EPROTONOSUPPORT), + Family_And_Socket_Type_Mismatch = c.int(os.EPROTONOSUPPORT), +} + +Dial_Error :: enum c.int { + Port_Required = -1, + + Address_In_Use = c.int(os.EADDRINUSE), + In_Progress = c.int(os.EINPROGRESS), + Cannot_Use_Any_Address = c.int(os.EADDRNOTAVAIL), + Wrong_Family_For_Socket = c.int(os.EAFNOSUPPORT), + Refused = c.int(os.ECONNREFUSED), + Is_Listening_Socket = c.int(os.EACCES), + Already_Connected = c.int(os.EISCONN), + Network_Unreachable = c.int(os.ENETUNREACH), // Device is offline + Host_Unreachable = c.int(os.EHOSTUNREACH), // Remote host cannot be reached + No_Buffer_Space_Available = c.int(os.ENOBUFS), + Not_Socket = c.int(os.ENOTSOCK), + Timeout = c.int(os.ETIMEDOUT), + + // TODO: we may need special handling for this; maybe make a socket a struct with metadata? + Would_Block = c.int(os.EWOULDBLOCK), +} + +Bind_Error :: enum c.int { + Address_In_Use = c.int(os.EADDRINUSE), // Another application is currently bound to this endpoint. + Given_Nonlocal_Address = c.int(os.EADDRNOTAVAIL), // The address is not a local address on this machine. + Broadcast_Disabled = c.int(os.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set. + Address_Family_Mismatch = c.int(os.EFAULT), // The address family of the address does not match that of the socket. + Already_Bound = c.int(os.EINVAL), // The socket is already bound to an address. + No_Ports_Available = c.int(os.ENOBUFS), // There are not enough ephemeral ports available. +} + +Listen_Error :: enum c.int { + Address_In_Use = c.int(os.EADDRINUSE), + Already_Connected = c.int(os.EISCONN), + No_Socket_Descriptors_Available = c.int(os.EMFILE), + No_Buffer_Space_Available = c.int(os.ENOBUFS), + Nonlocal_Address = c.int(os.EADDRNOTAVAIL), + Not_Socket = c.int(os.ENOTSOCK), + Listening_Not_Supported_For_This_Socket = c.int(os.EOPNOTSUPP), +} + +Accept_Error :: enum c.int { + + // TODO(tetra): Is this error actually possible here? Or is like Linux, in which case we can remove it. + Reset = c.int(os.ECONNRESET), + Not_Listening = c.int(os.EINVAL), + No_Socket_Descriptors_Available_For_Client_Socket = c.int(os.EMFILE), + No_Buffer_Space_Available = c.int(os.ENOBUFS), + Not_Socket = c.int(os.ENOTSOCK), + Not_Connection_Oriented_Socket = c.int(os.EOPNOTSUPP), + + // TODO: we may need special handling for this; maybe make a socket a struct with metadata? + Would_Block = c.int(os.EWOULDBLOCK), +} + +TCP_Recv_Error :: enum c.int { + Shutdown = c.int(os.ESHUTDOWN), + Not_Connected = c.int(os.ENOTCONN), + + // TODO(tetra): Is this error actually possible here? + Connection_Broken = c.int(os.ENETRESET), + Not_Socket = c.int(os.ENOTSOCK), + Aborted = c.int(os.ECONNABORTED), + + // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them? + Connection_Closed = c.int(os.ECONNRESET), + Offline = c.int(os.ENETDOWN), + Host_Unreachable = c.int(os.EHOSTUNREACH), + Interrupted = c.int(os.EINTR), + + // NOTE: No, really. Presumably this means something different for nonblocking sockets... + Timeout = c.int(os.EWOULDBLOCK), +} + +UDP_Recv_Error :: enum c.int { + Truncated = c.int(os.EMSGSIZE), // The buffer is too small to fit the entire message, and the message was truncated. + Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket. + Not_Descriptor = c.int(os.EBADF), // The so-called socket is, in fact, not even a valid descriptor. + Bad_Buffer = c.int(os.EFAULT), // The buffer did not point to a valid location in memory. + Interrupted = c.int(os.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(os.EWOULDBLOCK), + Socket_Not_Bound = c.int(os.EINVAL), // The socket must be bound for this operation, but isn't. +} + +// TODO +TCP_Send_Error :: enum c.int { + // TODO: merge with other errors? + Aborted = c.int(os.ECONNABORTED), + Connection_Closed = c.int(os.ECONNRESET), + Not_Connected = c.int(os.ENOTCONN), + Shutdown = c.int(os.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(os.ENOBUFS), + Offline = c.int(os.ENETDOWN), + Host_Unreachable = c.int(os.EHOSTUNREACH), + Interrupted = c.int(os.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(os.EWOULDBLOCK), + Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket. +} + +// TODO +UDP_Send_Error :: enum c.int { + Truncated = c.int(os.EMSGSIZE), // The message is too big. No data was sent. + + // TODO: not sure what the exact circumstances for this is yet + Network_Unreachable = c.int(os.ENETUNREACH), + No_Outbound_Ports_Available = c.int(os.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(os.EWOULDBLOCK), + Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket. + Not_Descriptor = c.int(os.EBADF), // The so-called socket is, in fact, not even a valid descriptor. + Bad_Buffer = c.int(os.EFAULT), // The buffer did not point to a valid location in memory. + Interrupted = c.int(os.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(os.ENOBUFS), + No_Memory_Available = c.int(os.ENOMEM), // No memory was available to properly manage the send queue. +} + +Shutdown_Error :: enum c.int { + Aborted = c.int(os.ECONNABORTED), + Reset = c.int(os.ECONNRESET), + Offline = c.int(os.ENETDOWN), + Not_Connected = c.int(os.ENOTCONN), + Not_Socket = c.int(os.ENOTSOCK), + Invalid_Manner = c.int(os.EINVAL), +} + +Socket_Option_Error :: enum c.int { + Offline = c.int(os.ENETDOWN), + Timeout_When_Keepalive_Set = c.int(os.ENETRESET), + Invalid_Option_For_Socket = c.int(os.ENOPROTOOPT), + Reset_When_Keepalive_Set = c.int(os.ENOTCONN), + Not_Socket = c.int(os.ENOTSOCK), +} diff --git a/core/net/errors_linux.odin b/core/net/errors_linux.odin new file mode 100644 index 000000000..16a5411ea --- /dev/null +++ b/core/net/errors_linux.odin @@ -0,0 +1,175 @@ +// +build linux +/* + Copyright 2022 Tetralux + Copyright 2022 Colin Davidson + Copyright 2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + List of contributors: + Tetralux: Initial implementation + Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver + Jeroen van Rijn: Cross platform unification, code style, documentation +*/ + +/* + Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures. + For other protocols and their features, see subdirectories of this package. +*/ +package net + +import "core:c" +import "core:os" + +Create_Socket_Error :: enum c.int { + Family_Not_Supported_For_This_Socket = c.int(os.EAFNOSUPPORT), + No_Socket_Descriptors_Available = c.int(os.EMFILE), + No_Buffer_Space_Available = c.int(os.ENOBUFS), + No_Memory_Available_Available = c.int(os.ENOMEM), + Protocol_Unsupported_By_System = c.int(os.EPROTONOSUPPORT), + Wrong_Protocol_For_Socket = c.int(os.EPROTONOSUPPORT), + Family_And_Socket_Type_Mismatch = c.int(os.EPROTONOSUPPORT), +} + +Dial_Error :: enum c.int { + Port_Required = -1, + + Address_In_Use = c.int(os.EADDRINUSE), + In_Progress = c.int(os.EINPROGRESS), + Cannot_Use_Any_Address = c.int(os.EADDRNOTAVAIL), + Wrong_Family_For_Socket = c.int(os.EAFNOSUPPORT), + Refused = c.int(os.ECONNREFUSED), + Is_Listening_Socket = c.int(os.EACCES), + Already_Connected = c.int(os.EISCONN), + Network_Unreachable = c.int(os.ENETUNREACH), // Device is offline + Host_Unreachable = c.int(os.EHOSTUNREACH), // Remote host cannot be reached + No_Buffer_Space_Available = c.int(os.ENOBUFS), + Not_Socket = c.int(os.ENOTSOCK), + Timeout = c.int(os.ETIMEDOUT), + + // TODO: we may need special handling for this; maybe make a socket a struct with metadata? + Would_Block = c.int(os.EWOULDBLOCK), +} + +Bind_Error :: enum c.int { + Address_In_Use = c.int(os.EADDRINUSE), // Another application is currently bound to this endpoint. + Given_Nonlocal_Address = c.int(os.EADDRNOTAVAIL), // The address is not a local address on this machine. + Broadcast_Disabled = c.int(os.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set. + Address_Family_Mismatch = c.int(os.EFAULT), // The address family of the address does not match that of the socket. + Already_Bound = c.int(os.EINVAL), // The socket is already bound to an address. + No_Ports_Available = c.int(os.ENOBUFS), // There are not enough ephemeral ports available. +} + +Listen_Error :: enum c.int { + Address_In_Use = c.int(os.EADDRINUSE), + Already_Connected = c.int(os.EISCONN), + No_Socket_Descriptors_Available = c.int(os.EMFILE), + No_Buffer_Space_Available = c.int(os.ENOBUFS), + Nonlocal_Address = c.int(os.EADDRNOTAVAIL), + Not_Socket = c.int(os.ENOTSOCK), + Listening_Not_Supported_For_This_Socket = c.int(os.EOPNOTSUPP), +} + +Accept_Error :: enum c.int { + Not_Listening = c.int(os.EINVAL), + No_Socket_Descriptors_Available_For_Client_Socket = c.int(os.EMFILE), + No_Buffer_Space_Available = c.int(os.ENOBUFS), + Not_Socket = c.int(os.ENOTSOCK), + Not_Connection_Oriented_Socket = c.int(os.EOPNOTSUPP), + + // TODO: we may need special handling for this; maybe make a socket a struct with metadata? + Would_Block = c.int(os.EWOULDBLOCK), +} + +TCP_Recv_Error :: enum c.int { + Shutdown = c.int(os.ESHUTDOWN), + Not_Connected = c.int(os.ENOTCONN), + Connection_Broken = c.int(os.ENETRESET), + Not_Socket = c.int(os.ENOTSOCK), + Aborted = c.int(os.ECONNABORTED), + + // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them? + Connection_Closed = c.int(os.ECONNRESET), + Offline = c.int(os.ENETDOWN), + Host_Unreachable = c.int(os.EHOSTUNREACH), + Interrupted = c.int(os.EINTR), + Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... +} + +UDP_Recv_Error :: enum c.int { + // The buffer is too small to fit the entire message, and the message was truncated. + // When this happens, the rest of message is lost. + Buffer_Too_Small = c.int(os.EMSGSIZE), + Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket. + Not_Descriptor = c.int(os.EBADF), // The so-called socket is, in fact, not even a valid descriptor. + Bad_Buffer = c.int(os.EFAULT), // The buffer did not point to a valid location in memory. + Interrupted = c.int(os.EINTR), // A signal occurred before any data was transmitted. See signal(7). + + // 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(os.EWOULDBLOCK), + Socket_Not_Bound = c.int(os.EINVAL), // The socket must be bound for this operation, but isn't. +} + +// TODO +TCP_Send_Error :: enum c.int { + + // TODO(tetra): merge with other errors? + Aborted = c.int(os.ECONNABORTED), + Connection_Closed = c.int(os.ECONNRESET), + Not_Connected = c.int(os.ENOTCONN), + Shutdown = c.int(os.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(os.ENOBUFS), + Offline = c.int(os.ENETDOWN), + Host_Unreachable = c.int(os.EHOSTUNREACH), // A signal occurred before any data was transmitted. See signal(7). + Interrupted = c.int(os.EINTR), // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout. + Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... + Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket. +} + +// TODO +UDP_Send_Error :: enum c.int { + Message_Too_Long = c.int(os.EMSGSIZE), // The message is too big. No data was sent. + + // TODO: not sure what the exact circumstances for this is yet + Network_Unreachable = c.int(os.ENETUNREACH), + No_Outbound_Ports_Available = c.int(os.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(os.EWOULDBLOCK), + Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket. + Not_Descriptor = c.int(os.EBADF), // The so-called socket is, in fact, not even a valid descriptor. + Bad_Buffer = c.int(os.EFAULT), // The buffer did not point to a valid location in memory. + Interrupted = c.int(os.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(os.ENOBUFS), + No_Memory_Available = c.int(os.ENOMEM), // No memory was available to properly manage the send queue. +} + +Shutdown_Error :: enum c.int { + Aborted = c.int(os.ECONNABORTED), + Reset = c.int(os.ECONNRESET), + Offline = c.int(os.ENETDOWN), + Not_Connected = c.int(os.ENOTCONN), + Not_Socket = c.int(os.ENOTSOCK), + Invalid_Manner = c.int(os.EINVAL), +} + +Socket_Option_Error :: enum c.int { + Offline = c.int(os.ENETDOWN), + Timeout_When_Keepalive_Set = c.int(os.ENETRESET), + Invalid_Option_For_Socket = c.int(os.ENOPROTOOPT), + Reset_When_Keepalive_Set = c.int(os.ENOTCONN), + Not_Socket = c.int(os.ENOTSOCK), +} diff --git a/core/net/errors_openbsd.odin b/core/net/errors_openbsd.odin new file mode 100644 index 000000000..51d9cc7e8 --- /dev/null +++ b/core/net/errors_openbsd.odin @@ -0,0 +1,191 @@ +// +build openbsd +/* + Copyright 2022 Tetralux + Copyright 2022 Colin Davidson + Copyright 2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + List of contributors: + Tetralux: Initial implementation + Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver + Jeroen van Rijn: Cross platform unification, code style, documentation +*/ + +/* + Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures. + For other protocols and their features, see subdirectories of this package. + + + IMPORTANT/TODO: This is a carbon copy of `socket_darwin.odin`. Adjust if necessary. + +*/ +package net + +import "core:c" +import "core:os" + +Create_Socket_Error :: enum c.int { + Family_Not_Supported_For_This_Socket = c.int(os.EAFNOSUPPORT), + No_Socket_Descriptors_Available = c.int(os.EMFILE), + No_Buffer_Space_Available = c.int(os.ENOBUFS), + No_Memory_Available_Available = c.int(os.ENOMEM), + Protocol_Unsupported_By_System = c.int(os.EPROTONOSUPPORT), + Wrong_Protocol_For_Socket = c.int(os.EPROTONOSUPPORT), + Family_And_Socket_Type_Mismatch = c.int(os.EPROTONOSUPPORT), +} + +Dial_Error :: enum c.int { + Port_Required = -1, + Address_In_Use = c.int(os.EADDRINUSE), + In_Progress = c.int(os.EINPROGRESS), + Cannot_Use_Any_Address = c.int(os.EADDRNOTAVAIL), + Wrong_Family_For_Socket = c.int(os.EAFNOSUPPORT), + Refused = c.int(os.ECONNREFUSED), + Is_Listening_Socket = c.int(os.EACCES), + Already_Connected = c.int(os.EISCONN), + Network_Unreachable = c.int(os.ENETUNREACH), // Device is offline + Host_Unreachable = c.int(os.EHOSTUNREACH), // Remote host cannot be reached + No_Buffer_Space_Available = c.int(os.ENOBUFS), + Not_Socket = c.int(os.ENOTSOCK), + Timeout = c.int(os.ETIMEDOUT), + + // TODO: we may need special handling for this; maybe make a socket a struct with metadata? + Would_Block = c.int(os.EWOULDBLOCK), +} + +Bind_Error :: enum c.int { + Address_In_Use = c.int(os.EADDRINUSE), // Another application is currently bound to this endpoint. + Given_Nonlocal_Address = c.int(os.EADDRNOTAVAIL), // The address is not a local address on this machine. + Broadcast_Disabled = c.int(os.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set. + Address_Family_Mismatch = c.int(os.EFAULT), // The address family of the address does not match that of the socket. + Already_Bound = c.int(os.EINVAL), // The socket is already bound to an address. + No_Ports_Available = c.int(os.ENOBUFS), // There are not enough ephemeral ports available. +} + +Listen_Error :: enum c.int { + Address_In_Use = c.int(os.EADDRINUSE), + Already_Connected = c.int(os.EISCONN), + No_Socket_Descriptors_Available = c.int(os.EMFILE), + No_Buffer_Space_Available = c.int(os.ENOBUFS), + Nonlocal_Address = c.int(os.EADDRNOTAVAIL), + Not_Socket = c.int(os.ENOTSOCK), + Listening_Not_Supported_For_This_Socket = c.int(os.EOPNOTSUPP), +} + +Accept_Error :: enum c.int { + + // TODO(tetra): Is this error actually possible here? Or is like Linux, in which case we can remove it. + Reset = c.int(os.ECONNRESET), + Not_Listening = c.int(os.EINVAL), + No_Socket_Descriptors_Available_For_Client_Socket = c.int(os.EMFILE), + No_Buffer_Space_Available = c.int(os.ENOBUFS), + Not_Socket = c.int(os.ENOTSOCK), + Not_Connection_Oriented_Socket = c.int(os.EOPNOTSUPP), + + // TODO: we may need special handling for this; maybe make a socket a struct with metadata? + Would_Block = c.int(os.EWOULDBLOCK), +} + +TCP_Recv_Error :: enum c.int { + Shutdown = c.int(os.ESHUTDOWN), + Not_Connected = c.int(os.ENOTCONN), + + // TODO(tetra): Is this error actually possible here? + Connection_Broken = c.int(os.ENETRESET), + Not_Socket = c.int(os.ENOTSOCK), + Aborted = c.int(os.ECONNABORTED), + + // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them? + Connection_Closed = c.int(os.ECONNRESET), + Offline = c.int(os.ENETDOWN), + Host_Unreachable = c.int(os.EHOSTUNREACH), + Interrupted = c.int(os.EINTR), + Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... +} + +UDP_Recv_Error :: enum c.int { + // The buffer is too small to fit the entire message, and the message was truncated. + Truncated = c.int(os.EMSGSIZE), + // The so-called socket is not an open socket. + Not_Socket = c.int(os.ENOTSOCK), + // The so-called socket is, in fact, not even a valid descriptor. + Not_Descriptor = c.int(os.EBADF), + // The buffer did not point to a valid location in memory. + Bad_Buffer = c.int(os.EFAULT), + // A signal occurred before any data was transmitted. See signal(7). + Interrupted = c.int(os.EINTR), + + // 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(os.EWOULDBLOCK), + Socket_Not_Bound = c.int(os.EINVAL), // The socket must be bound for this operation, but isn't. +} + +// TODO +TCP_Send_Error :: enum c.int { + + // TODO: merge with other errors? + Aborted = c.int(os.ECONNABORTED), + Connection_Closed = c.int(os.ECONNRESET), + Not_Connected = c.int(os.ENOTCONN), + Shutdown = c.int(os.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(os.ENOBUFS), + Offline = c.int(os.ENETDOWN), + Host_Unreachable = c.int(os.EHOSTUNREACH), + Interrupted = c.int(os.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(os.EWOULDBLOCK), +} + +// TODO +UDP_Send_Error :: enum c.int { + Truncated = c.int(os.EMSGSIZE), // The message is too big. No data was sent. + + // TODO: not sure what the exact circumstances for this is yet + Network_Unreachable = c.int(os.ENETUNREACH), + No_Outbound_Ports_Available = c.int(os.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(os.EWOULDBLOCK), + Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket. + Not_Descriptor = c.int(os.EBADF), // The so-called socket is, in fact, not even a valid descriptor. + Bad_Buffer = c.int(os.EFAULT), // The buffer did not point to a valid location in memory. + Interrupted = c.int(os.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(os.ENOBUFS), + No_Memory_Available = c.int(os.ENOMEM), // No memory was available to properly manage the send queue. +} + +Shutdown_Error :: enum c.int { + Aborted = c.int(os.ECONNABORTED), + Reset = c.int(os.ECONNRESET), + Offline = c.int(os.ENETDOWN), + Not_Connected = c.int(os.ENOTCONN), + Not_Socket = c.int(os.ENOTSOCK), + Invalid_Manner = c.int(os.EINVAL), +} + +Socket_Option_Error :: enum c.int { + Offline = c.int(os.ENETDOWN), + Timeout_When_Keepalive_Set = c.int(os.ENETRESET), + Invalid_Option_For_Socket = c.int(os.ENOPROTOOPT), + Reset_When_Keepalive_Set = c.int(os.ENOTCONN), + Not_Socket = c.int(os.ENOTSOCK), +} diff --git a/core/net/errors_windows.odin b/core/net/errors_windows.odin new file mode 100644 index 000000000..9f9589f29 --- /dev/null +++ b/core/net/errors_windows.odin @@ -0,0 +1,249 @@ +// +build windows +/* + Copyright 2022 Tetralux + Copyright 2022 Colin Davidson + Copyright 2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + List of contributors: + Tetralux: Initial implementation + Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver + Jeroen van Rijn: Cross platform unification, code style, documentation +*/ + +/* + Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures. + For other protocols and their features, see subdirectories of this package. +*/ +package net + +import "core:c" +import win "core:sys/windows" + +Create_Socket_Error :: enum c.int { + 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, +} + +Dial_Error :: enum c.int { + 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? +} + +Bind_Error :: enum c.int { + 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. +} + +Listen_Error :: enum c.int { + 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, +} + +Accept_Error :: enum c.int { + 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, +} + +TCP_Recv_Error :: enum c.int { + 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, + + // TODO: not functionally different from Reset; merge? + 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, +} + +UDP_Recv_Error :: enum c.int { + Network_Subsystem_Failure = win.WSAENETDOWN, + + // TODO: not functionally different from Reset; merge? + // UDP packets are limited in size, and the length of the incoming message exceeded it. + Aborted = win.WSAECONNABORTED, + Truncated = win.WSAEMSGSIZE, + 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. +} + +// 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 { + + // TODO: not functionally different from Reset; merge? + 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, + + // 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. +} + +UDP_Send_Error :: enum c.int { + Network_Subsystem_Failure = win.WSAENETDOWN, + + // TODO: not functionally different from Reset; merge? + Aborted = win.WSAECONNABORTED, // UDP packets are limited in size, and len(buf) exceeded it. + Message_Too_Long = win.WSAEMSGSIZE, // The machine at the remote endpoint doesn't have the given port open to receiving UDP data. + Remote_Not_Listening = win.WSAECONNRESET, + 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, +} + +Shutdown_Manner :: enum c.int { + Receive = win.SD_RECEIVE, + Send = win.SD_SEND, + Both = win.SD_BOTH, +} + +Shutdown_Error :: enum c.int { + Aborted = win.WSAECONNABORTED, + Reset = win.WSAECONNRESET, + Offline = win.WSAENETDOWN, + Not_Connected = win.WSAENOTCONN, + Not_Socket = win.WSAENOTSOCK, + Invalid_Manner = win.WSAEINVAL, +} + +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, +} + +Socket_Option_Error :: enum c.int { + 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, +} diff --git a/core/net/socket_darwin.odin b/core/net/socket_darwin.odin index 89ad7b31c..75c0eaf56 100644 --- a/core/net/socket_darwin.odin +++ b/core/net/socket_darwin.odin @@ -23,16 +23,6 @@ import "core:time" Platform_Socket :: os.Socket -Create_Socket_Error :: enum c.int { - Family_Not_Supported_For_This_Socket = c.int(os.EAFNOSUPPORT), - No_Socket_Descriptors_Available = c.int(os.EMFILE), - No_Buffer_Space_Available = c.int(os.ENOBUFS), - No_Memory_Available_Available = c.int(os.ENOMEM), - Protocol_Unsupported_By_System = c.int(os.EPROTONOSUPPORT), - Wrong_Protocol_For_Socket = c.int(os.EPROTONOSUPPORT), - Family_And_Socket_Type_Mismatch = c.int(os.EPROTONOSUPPORT), -} - create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) { c_type, c_protocol, c_family: int @@ -64,25 +54,6 @@ create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (soc } } - -Dial_Error :: enum c.int { - Port_Required = -1, - - Address_In_Use = c.int(os.EADDRINUSE), - In_Progress = c.int(os.EINPROGRESS), - Cannot_Use_Any_Address = c.int(os.EADDRNOTAVAIL), - Wrong_Family_For_Socket = c.int(os.EAFNOSUPPORT), - Refused = c.int(os.ECONNREFUSED), - Is_Listening_Socket = c.int(os.EACCES), - Already_Connected = c.int(os.EISCONN), - Network_Unreachable = c.int(os.ENETUNREACH), // Device is offline - Host_Unreachable = c.int(os.EHOSTUNREACH), // Remote host cannot be reached - No_Buffer_Space_Available = c.int(os.ENOBUFS), - Not_Socket = c.int(os.ENOTSOCK), - Timeout = c.int(os.ETIMEDOUT), - Would_Block = c.int(os.EWOULDBLOCK), // TODO: we may need special handling for this; maybe make a socket a struct with metadata? -} - dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (skt: TCP_Socket, err: Network_Error) { if endpoint.port == 0 { return 0, .Port_Required @@ -107,22 +78,6 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option return } - -Bind_Error :: enum c.int { - // Another application is currently bound to this endpoint. - Address_In_Use = c.int(os.EADDRINUSE), - // The address is not a local address on this machine. - Given_Nonlocal_Address = c.int(os.EADDRNOTAVAIL), - // To bind a UDP socket to the broadcast address, the appropriate socket option must be set. - Broadcast_Disabled = c.int(os.EACCES), - // The address family of the address does not match that of the socket. - Address_Family_Mismatch = c.int(os.EFAULT), - // The socket is already bound to an address. - Already_Bound = c.int(os.EINVAL), - // There are not enough ephemeral ports available. - No_Ports_Available = c.int(os.ENOBUFS), -} - bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) { sockaddr := endpoint_to_sockaddr(ep) s := any_socket_to_socket(skt) @@ -133,7 +88,6 @@ bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) { return } - // This type of socket becomes bound when you try to send data. // This is likely what you want if you want to send data unsolicited. // @@ -156,18 +110,6 @@ make_bound_udp_socket :: proc(bound_address: Address, port: int) -> (skt: UDP_So return } - - -Listen_Error :: enum c.int { - Address_In_Use = c.int(os.EADDRINUSE), - Already_Connected = c.int(os.EISCONN), - No_Socket_Descriptors_Available = c.int(os.EMFILE), - No_Buffer_Space_Available = c.int(os.ENOBUFS), - Nonlocal_Address = c.int(os.EADDRNOTAVAIL), - Not_Socket = c.int(os.ENOTSOCK), - Listening_Not_Supported_For_This_Socket = c.int(os.EOPNOTSUPP), -} - listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_Socket, err: Network_Error) { assert(backlog > 0 && i32(backlog) < max(i32)) @@ -193,18 +135,6 @@ listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_S return } - - -Accept_Error :: enum c.int { - Reset = c.int(os.ECONNRESET), // TODO(tetra): Is this error actually possible here? Or is like Linux, in which case we can remove it. - Not_Listening = c.int(os.EINVAL), - No_Socket_Descriptors_Available_For_Client_Socket = c.int(os.EMFILE), - No_Buffer_Space_Available = c.int(os.ENOBUFS), - Not_Socket = c.int(os.ENOTSOCK), - Not_Connection_Oriented_Socket = c.int(os.EOPNOTSUPP), - Would_Block = c.int(os.EWOULDBLOCK), // TODO: we may need special handling for this; maybe make a socket a struct with metadata? -} - accept_tcp :: proc(sock: TCP_Socket) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) { sockaddr: os.SOCKADDR_STORAGE_LH sockaddrlen := c.int(size_of(sockaddr)) @@ -219,28 +149,11 @@ accept_tcp :: proc(sock: TCP_Socket) -> (client: TCP_Socket, source: Endpoint, e return } - - close :: proc(skt: Any_Socket) { s := any_socket_to_socket(skt) os.close(os.Handle(Platform_Socket(s))) } - - -TCP_Recv_Error :: enum c.int { - Shutdown = c.int(os.ESHUTDOWN), - Not_Connected = c.int(os.ENOTCONN), - Connection_Broken = c.int(os.ENETRESET), // TODO(tetra): Is this error actually possible here? - Not_Socket = c.int(os.ENOTSOCK), - Aborted = c.int(os.ECONNABORTED), - Connection_Closed = c.int(os.ECONNRESET), // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them? - Offline = c.int(os.ENETDOWN), - Host_Unreachable = c.int(os.EHOSTUNREACH), - Interrupted = c.int(os.EINTR), - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... -} - recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) { if len(buf) <= 0 { return @@ -253,25 +166,6 @@ recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network return int(res), nil } -UDP_Recv_Error :: enum c.int { - // The buffer is too small to fit the entire message, and the message was truncated. - Truncated = c.int(os.EMSGSIZE), - // The so-called socket is not an open socket. - Not_Socket = c.int(os.ENOTSOCK), - // The so-called socket is, in fact, not even a valid descriptor. - Not_Descriptor = c.int(os.EBADF), - // The buffer did not point to a valid location in memory. - Bad_Buffer = c.int(os.EFAULT), - // A signal occurred before any data was transmitted. - // See signal(7). - Interrupted = c.int(os.EINTR), - // The send timeout duration passed before all data was sent. - // See Socket_Option.Send_Timeout. - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... - // The socket must be bound for this operation, but isn't. - Socket_Not_Bound = c.int(os.EINVAL), -} - recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) { if len(buf) <= 0 { return @@ -292,32 +186,6 @@ recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpo recv :: proc{recv_tcp, recv_udp} - - -// TODO -TCP_Send_Error :: enum c.int { - Aborted = c.int(os.ECONNABORTED), // TODO: merge with other errors? - Connection_Closed = c.int(os.ECONNRESET), - Not_Connected = c.int(os.ENOTCONN), - Shutdown = c.int(os.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(os.ENOBUFS), - Offline = c.int(os.ENETDOWN), - Host_Unreachable = c.int(os.EHOSTUNREACH), - // A signal occurred before any data was transmitted. - // See signal(7). - Interrupted = c.int(os.EINTR), - // The send timeout duration passed before all data was sent. - // See Socket_Option.Send_Timeout. - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... - // The so-called socket is not an open socket. - Not_Socket = c.int(os.ENOTSOCK), -} - // 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. @@ -335,36 +203,6 @@ send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Netw return } -// TODO -UDP_Send_Error :: enum c.int { - // The message is too big. No data was sent. - Truncated = c.int(os.EMSGSIZE), - // TODO: not sure what the exact circumstances for this is yet - Network_Unreachable = c.int(os.ENETUNREACH), - // There are no more emphemeral outbound ports available to bind the socket to, in order to send. - No_Outbound_Ports_Available = c.int(os.EAGAIN), - // The send timeout duration passed before all data was sent. - // See Socket_Option.Send_Timeout. - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... - // The so-called socket is not an open socket. - Not_Socket = c.int(os.ENOTSOCK), - // The so-called socket is, in fact, not even a valid descriptor. - Not_Descriptor = c.int(os.EBADF), - // The buffer did not point to a valid location in memory. - Bad_Buffer = c.int(os.EFAULT), - // A signal occurred before any data was transmitted. - // See signal(7). - Interrupted = c.int(os.EINTR), - // 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(os.ENOBUFS), - // No memory was available to properly manage the send queue. - No_Memory_Available = c.int(os.ENOMEM), -} - send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) { toaddr := endpoint_to_sockaddr(to) for bytes_written < len(buf) { @@ -382,22 +220,10 @@ send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: send :: proc{send_tcp, send_udp} - - - Shutdown_Manner :: enum c.int { Receive = c.int(os.SHUT_RD), - Send = c.int(os.SHUT_WR), - Both = c.int(os.SHUT_RDWR), -} - -Shutdown_Error :: enum c.int { - Aborted = c.int(os.ECONNABORTED), - Reset = c.int(os.ECONNRESET), - Offline = c.int(os.ENETDOWN), - Not_Connected = c.int(os.ENOTCONN), - Not_Socket = c.int(os.ENOTSOCK), - Invalid_Manner = c.int(os.EINVAL), + Send = c.int(os.SHUT_WR), + Both = c.int(os.SHUT_RDWR), } shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) { @@ -409,29 +235,16 @@ shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Erro return } - - - Socket_Option :: enum c.int { - Reuse_Address = c.int(os.SO_REUSEADDR), - Keep_Alive = c.int(os.SO_KEEPALIVE), + Reuse_Address = c.int(os.SO_REUSEADDR), + Keep_Alive = c.int(os.SO_KEEPALIVE), Out_Of_Bounds_Data_Inline = c.int(os.SO_OOBINLINE), - TCP_Nodelay = c.int(os.TCP_NODELAY), - - Linger = c.int(os.SO_LINGER), - - Receive_Buffer_Size = c.int(os.SO_RCVBUF), - Send_Buffer_Size = c.int(os.SO_SNDBUF), - Receive_Timeout = c.int(os.SO_RCVTIMEO), - Send_Timeout = c.int(os.SO_SNDTIMEO), -} - -Socket_Option_Error :: enum c.int { - Offline = c.int(os.ENETDOWN), - Timeout_When_Keepalive_Set = c.int(os.ENETRESET), - Invalid_Option_For_Socket = c.int(os.ENOPROTOOPT), - Reset_When_Keepalive_Set = c.int(os.ENOTCONN), - Not_Socket = c.int(os.ENOTSOCK), + TCP_Nodelay = c.int(os.TCP_NODELAY), + Linger = c.int(os.SO_LINGER), + Receive_Buffer_Size = c.int(os.SO_RCVBUF), + Send_Buffer_Size = c.int(os.SO_SNDBUF), + Receive_Timeout = c.int(os.SO_RCVTIMEO), + Send_Timeout = c.int(os.SO_SNDTIMEO), } set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error { diff --git a/core/net/socket_linux.odin b/core/net/socket_linux.odin index dcc48d3fa..4b80ef202 100644 --- a/core/net/socket_linux.odin +++ b/core/net/socket_linux.odin @@ -23,16 +23,6 @@ import "core:time" Platform_Socket :: os.Socket -Create_Socket_Error :: enum c.int { - Family_Not_Supported_For_This_Socket = c.int(os.EAFNOSUPPORT), - No_Socket_Descriptors_Available = c.int(os.EMFILE), - No_Buffer_Space_Available = c.int(os.ENOBUFS), - No_Memory_Available_Available = c.int(os.ENOMEM), - Protocol_Unsupported_By_System = c.int(os.EPROTONOSUPPORT), - Wrong_Protocol_For_Socket = c.int(os.EPROTONOSUPPORT), - Family_And_Socket_Type_Mismatch = c.int(os.EPROTONOSUPPORT), -} - create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) { c_type, c_protocol, c_family: int @@ -64,25 +54,6 @@ create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (soc } } - -Dial_Error :: enum c.int { - Port_Required = -1, - - Address_In_Use = c.int(os.EADDRINUSE), - In_Progress = c.int(os.EINPROGRESS), - Cannot_Use_Any_Address = c.int(os.EADDRNOTAVAIL), - Wrong_Family_For_Socket = c.int(os.EAFNOSUPPORT), - Refused = c.int(os.ECONNREFUSED), - Is_Listening_Socket = c.int(os.EACCES), - Already_Connected = c.int(os.EISCONN), - Network_Unreachable = c.int(os.ENETUNREACH), // Device is offline - Host_Unreachable = c.int(os.EHOSTUNREACH), // Remote host cannot be reached - No_Buffer_Space_Available = c.int(os.ENOBUFS), - Not_Socket = c.int(os.ENOTSOCK), - Timeout = c.int(os.ETIMEDOUT), - Would_Block = c.int(os.EWOULDBLOCK), // TODO: we may need special handling for this; maybe make a socket a struct with metadata? -} - dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (skt: TCP_Socket, err: Network_Error) { if endpoint.port == 0 { return 0, .Port_Required @@ -112,21 +83,6 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option } -Bind_Error :: enum c.int { - // Another application is currently bound to this endpoint. - Address_In_Use = c.int(os.EADDRINUSE), - // The address is not a local address on this machine. - Given_Nonlocal_Address = c.int(os.EADDRNOTAVAIL), - // To bind a UDP socket to the broadcast address, the appropriate socket option must be set. - Broadcast_Disabled = c.int(os.EACCES), - // The address family of the address does not match that of the socket. - Address_Family_Mismatch = c.int(os.EFAULT), - // The socket is already bound to an address. - Already_Bound = c.int(os.EINVAL), - // There are not enough ephemeral ports available. - No_Ports_Available = c.int(os.ENOBUFS), -} - bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) { sockaddr := endpoint_to_sockaddr(ep) s := any_socket_to_socket(skt) @@ -161,17 +117,6 @@ make_bound_udp_socket :: proc(bound_address: Address, port: int) -> (skt: UDP_So } - -Listen_Error :: enum c.int { - Address_In_Use = c.int(os.EADDRINUSE), - Already_Connected = c.int(os.EISCONN), - No_Socket_Descriptors_Available = c.int(os.EMFILE), - No_Buffer_Space_Available = c.int(os.ENOBUFS), - Nonlocal_Address = c.int(os.EADDRNOTAVAIL), - Not_Socket = c.int(os.ENOTSOCK), - Listening_Not_Supported_For_This_Socket = c.int(os.EOPNOTSUPP), -} - listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_Socket, err: Network_Error) { assert(backlog > 0 && i32(backlog) < max(i32)) @@ -197,17 +142,6 @@ listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_S return } - - -Accept_Error :: enum c.int { - Not_Listening = c.int(os.EINVAL), - No_Socket_Descriptors_Available_For_Client_Socket = c.int(os.EMFILE), - No_Buffer_Space_Available = c.int(os.ENOBUFS), - Not_Socket = c.int(os.ENOTSOCK), - Not_Connection_Oriented_Socket = c.int(os.EOPNOTSUPP), - Would_Block = c.int(os.EWOULDBLOCK), // TODO: we may need special handling for this; maybe make a socket a struct with metadata? -} - accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) { sockaddr: os.SOCKADDR_STORAGE_LH sockaddrlen := c.int(size_of(sockaddr)) @@ -232,21 +166,6 @@ close :: proc(skt: Any_Socket) { os.close(os.Handle(Platform_Socket(s))) } - - -TCP_Recv_Error :: enum c.int { - Shutdown = c.int(os.ESHUTDOWN), - Not_Connected = c.int(os.ENOTCONN), - Connection_Broken = c.int(os.ENETRESET), - Not_Socket = c.int(os.ENOTSOCK), - Aborted = c.int(os.ECONNABORTED), - Connection_Closed = c.int(os.ECONNRESET), // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them? - Offline = c.int(os.ENETDOWN), - Host_Unreachable = c.int(os.EHOSTUNREACH), - Interrupted = c.int(os.EINTR), - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... -} - recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) { if len(buf) <= 0 { return @@ -259,26 +178,6 @@ recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network return int(res), nil } -UDP_Recv_Error :: enum c.int { - // The buffer is too small to fit the entire message, and the message was truncated. - // When this happens, the rest of message is lost. - Buffer_Too_Small = c.int(os.EMSGSIZE), - // The so-called socket is not an open socket. - Not_Socket = c.int(os.ENOTSOCK), - // The so-called socket is, in fact, not even a valid descriptor. - Not_Descriptor = c.int(os.EBADF), - // The buffer did not point to a valid location in memory. - Bad_Buffer = c.int(os.EFAULT), - // A signal occurred before any data was transmitted. - // See signal(7). - Interrupted = c.int(os.EINTR), - // The send timeout duration passed before all data was received. - // See Socket_Option.Receive_Timeout. - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... - // The socket must be bound for this operation, but isn't. - Socket_Not_Bound = c.int(os.EINVAL), -} - recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) { if len(buf) <= 0 { return @@ -312,31 +211,6 @@ recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpo recv :: proc{recv_tcp, recv_udp} - -// TODO -TCP_Send_Error :: enum c.int { - Aborted = c.int(os.ECONNABORTED), // TODO(tetra): merge with other errors? - Connection_Closed = c.int(os.ECONNRESET), - Not_Connected = c.int(os.ENOTCONN), - Shutdown = c.int(os.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(os.ENOBUFS), - Offline = c.int(os.ENETDOWN), - Host_Unreachable = c.int(os.EHOSTUNREACH), - // A signal occurred before any data was transmitted. - // See signal(7). - Interrupted = c.int(os.EINTR), - // The send timeout duration passed before all data was sent. - // See Socket_Option.Send_Timeout. - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... - // The so-called socket is not an open socket. - Not_Socket = c.int(os.ENOTSOCK), -} - // 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. @@ -354,36 +228,6 @@ send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Netw return } -// TODO -UDP_Send_Error :: enum c.int { - // The message is too big. No data was sent. - Message_Too_Long = c.int(os.EMSGSIZE), - // TODO: not sure what the exact circumstances for this is yet - Network_Unreachable = c.int(os.ENETUNREACH), - // There are no more emphemeral outbound ports available to bind the socket to, in order to send. - No_Outbound_Ports_Available = c.int(os.EAGAIN), - // The send timeout duration passed before all data was sent. - // See Socket_Option.Send_Timeout. - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... - // The so-called socket is not an open socket. - Not_Socket = c.int(os.ENOTSOCK), - // The so-called socket is, in fact, not even a valid descriptor. - Not_Descriptor = c.int(os.EBADF), - // The buffer did not point to a valid location in memory. - Bad_Buffer = c.int(os.EFAULT), - // A signal occurred before any data was transmitted. - // See signal(7). - Interrupted = c.int(os.EINTR), - // 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(os.ENOBUFS), - // No memory was available to properly manage the send queue. - No_Memory_Available = c.int(os.ENOMEM), -} - // Sends a single UDP datagram packet. // // Datagrams are limited in size; attempting to send more than this limit at once will result in a Message_Too_Long error. @@ -401,22 +245,10 @@ send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: send :: proc{send_tcp, send_udp} - - - Shutdown_Manner :: enum c.int { Receive = c.int(os.SHUT_RD), - Send = c.int(os.SHUT_WR), - Both = c.int(os.SHUT_RDWR), -} - -Shutdown_Error :: enum c.int { - Aborted = c.int(os.ECONNABORTED), - Reset = c.int(os.ECONNRESET), - Offline = c.int(os.ENETDOWN), - Not_Connected = c.int(os.ENOTCONN), - Not_Socket = c.int(os.ENOTSOCK), - Invalid_Manner = c.int(os.EINVAL), + Send = c.int(os.SHUT_WR), + Both = c.int(os.SHUT_RDWR), } shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) { @@ -428,29 +260,16 @@ shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Erro return } - - - Socket_Option :: enum c.int { - Reuse_Address = c.int(os.SO_REUSEADDR), - Keep_Alive = c.int(os.SO_KEEPALIVE), + Reuse_Address = c.int(os.SO_REUSEADDR), + Keep_Alive = c.int(os.SO_KEEPALIVE), Out_Of_Bounds_Data_Inline = c.int(os.SO_OOBINLINE), - TCP_Nodelay = c.int(os.TCP_NODELAY), - - Linger = c.int(os.SO_LINGER), - - Receive_Buffer_Size = c.int(os.SO_RCVBUF), - Send_Buffer_Size = c.int(os.SO_SNDBUF), - Receive_Timeout = c.int(os.SO_RCVTIMEO_NEW), - Send_Timeout = c.int(os.SO_SNDTIMEO_NEW), -} - -Socket_Option_Error :: enum c.int { - Offline = c.int(os.ENETDOWN), - Timeout_When_Keepalive_Set = c.int(os.ENETRESET), - Invalid_Option_For_Socket = c.int(os.ENOPROTOOPT), - Reset_When_Keepalive_Set = c.int(os.ENOTCONN), - Not_Socket = c.int(os.ENOTSOCK), + TCP_Nodelay = c.int(os.TCP_NODELAY), + Linger = c.int(os.SO_LINGER), + Receive_Buffer_Size = c.int(os.SO_RCVBUF), + Send_Buffer_Size = c.int(os.SO_SNDBUF), + Receive_Timeout = c.int(os.SO_RCVTIMEO_NEW), + Send_Timeout = c.int(os.SO_SNDTIMEO_NEW), } set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error { @@ -529,4 +348,4 @@ set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #cal } return nil -} \ No newline at end of file +} diff --git a/core/net/socket_openbsd.odin b/core/net/socket_openbsd.odin index 746b886ef..f69a62a21 100644 --- a/core/net/socket_openbsd.odin +++ b/core/net/socket_openbsd.odin @@ -27,16 +27,6 @@ import "core:time" Platform_Socket :: os.Socket -Create_Socket_Error :: enum c.int { - Family_Not_Supported_For_This_Socket = c.int(os.EAFNOSUPPORT), - No_Socket_Descriptors_Available = c.int(os.EMFILE), - No_Buffer_Space_Available = c.int(os.ENOBUFS), - No_Memory_Available_Available = c.int(os.ENOMEM), - Protocol_Unsupported_By_System = c.int(os.EPROTONOSUPPORT), - Wrong_Protocol_For_Socket = c.int(os.EPROTONOSUPPORT), - Family_And_Socket_Type_Mismatch = c.int(os.EPROTONOSUPPORT), -} - create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) { c_type, c_protocol, c_family: int @@ -68,25 +58,6 @@ create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (soc } } - -Dial_Error :: enum c.int { - Port_Required = -1, - - Address_In_Use = c.int(os.EADDRINUSE), - In_Progress = c.int(os.EINPROGRESS), - Cannot_Use_Any_Address = c.int(os.EADDRNOTAVAIL), - Wrong_Family_For_Socket = c.int(os.EAFNOSUPPORT), - Refused = c.int(os.ECONNREFUSED), - Is_Listening_Socket = c.int(os.EACCES), - Already_Connected = c.int(os.EISCONN), - Network_Unreachable = c.int(os.ENETUNREACH), // Device is offline - Host_Unreachable = c.int(os.EHOSTUNREACH), // Remote host cannot be reached - No_Buffer_Space_Available = c.int(os.ENOBUFS), - Not_Socket = c.int(os.ENOTSOCK), - Timeout = c.int(os.ETIMEDOUT), - Would_Block = c.int(os.EWOULDBLOCK), // TODO: we may need special handling for this; maybe make a socket a struct with metadata? -} - dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (skt: TCP_Socket, err: Network_Error) { if endpoint.port == 0 { return 0, .Port_Required @@ -111,22 +82,6 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option return } - -Bind_Error :: enum c.int { - // Another application is currently bound to this endpoint. - Address_In_Use = c.int(os.EADDRINUSE), - // The address is not a local address on this machine. - Given_Nonlocal_Address = c.int(os.EADDRNOTAVAIL), - // To bind a UDP socket to the broadcast address, the appropriate socket option must be set. - Broadcast_Disabled = c.int(os.EACCES), - // The address family of the address does not match that of the socket. - Address_Family_Mismatch = c.int(os.EFAULT), - // The socket is already bound to an address. - Already_Bound = c.int(os.EINVAL), - // There are not enough ephemeral ports available. - No_Ports_Available = c.int(os.ENOBUFS), -} - bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) { sockaddr := endpoint_to_sockaddr(ep) s := any_socket_to_socket(skt) @@ -160,18 +115,6 @@ make_bound_udp_socket :: proc(bound_address: Address, port: int) -> (skt: UDP_So return } - - -Listen_Error :: enum c.int { - Address_In_Use = c.int(os.EADDRINUSE), - Already_Connected = c.int(os.EISCONN), - No_Socket_Descriptors_Available = c.int(os.EMFILE), - No_Buffer_Space_Available = c.int(os.ENOBUFS), - Nonlocal_Address = c.int(os.EADDRNOTAVAIL), - Not_Socket = c.int(os.ENOTSOCK), - Listening_Not_Supported_For_This_Socket = c.int(os.EOPNOTSUPP), -} - listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_Socket, err: Network_Error) { assert(backlog > 0 && i32(backlog) < max(i32)) @@ -197,18 +140,6 @@ listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_S return } - - -Accept_Error :: enum c.int { - Reset = c.int(os.ECONNRESET), // TODO(tetra): Is this error actually possible here? Or is like Linux, in which case we can remove it. - Not_Listening = c.int(os.EINVAL), - No_Socket_Descriptors_Available_For_Client_Socket = c.int(os.EMFILE), - No_Buffer_Space_Available = c.int(os.ENOBUFS), - Not_Socket = c.int(os.ENOTSOCK), - Not_Connection_Oriented_Socket = c.int(os.EOPNOTSUPP), - Would_Block = c.int(os.EWOULDBLOCK), // TODO: we may need special handling for this; maybe make a socket a struct with metadata? -} - accept_tcp :: proc(sock: TCP_Socket) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) { sockaddr: os.SOCKADDR_STORAGE_LH sockaddrlen := c.int(size_of(sockaddr)) @@ -223,28 +154,11 @@ accept_tcp :: proc(sock: TCP_Socket) -> (client: TCP_Socket, source: Endpoint, e return } - - close :: proc(skt: Any_Socket) { s := any_socket_to_socket(skt) os.close(os.Handle(Platform_Socket(s))) } - - -TCP_Recv_Error :: enum c.int { - Shutdown = c.int(os.ESHUTDOWN), - Not_Connected = c.int(os.ENOTCONN), - Connection_Broken = c.int(os.ENETRESET), // TODO(tetra): Is this error actually possible here? - Not_Socket = c.int(os.ENOTSOCK), - Aborted = c.int(os.ECONNABORTED), - Connection_Closed = c.int(os.ECONNRESET), // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them? - Offline = c.int(os.ENETDOWN), - Host_Unreachable = c.int(os.EHOSTUNREACH), - Interrupted = c.int(os.EINTR), - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... -} - recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) { if len(buf) <= 0 { return @@ -257,25 +171,6 @@ recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network return int(res), nil } -UDP_Recv_Error :: enum c.int { - // The buffer is too small to fit the entire message, and the message was truncated. - Truncated = c.int(os.EMSGSIZE), - // The so-called socket is not an open socket. - Not_Socket = c.int(os.ENOTSOCK), - // The so-called socket is, in fact, not even a valid descriptor. - Not_Descriptor = c.int(os.EBADF), - // The buffer did not point to a valid location in memory. - Bad_Buffer = c.int(os.EFAULT), - // A signal occurred before any data was transmitted. - // See signal(7). - Interrupted = c.int(os.EINTR), - // The send timeout duration passed before all data was sent. - // See Socket_Option.Send_Timeout. - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... - // The socket must be bound for this operation, but isn't. - Socket_Not_Bound = c.int(os.EINVAL), -} - recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) { if len(buf) <= 0 { return @@ -296,30 +191,6 @@ recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpo recv :: proc{recv_tcp, recv_udp} - - -// TODO -TCP_Send_Error :: enum c.int { - Aborted = c.int(os.ECONNABORTED), // TODO: merge with other errors? - Connection_Closed = c.int(os.ECONNRESET), - Not_Connected = c.int(os.ENOTCONN), - Shutdown = c.int(os.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(os.ENOBUFS), - Offline = c.int(os.ENETDOWN), - Host_Unreachable = c.int(os.EHOSTUNREACH), - // A signal occurred before any data was transmitted. - // See signal(7). - Interrupted = c.int(os.EINTR), - // The send timeout duration passed before all data was sent. - // See Socket_Option.Send_Timeout. - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... -} - // 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. @@ -337,36 +208,6 @@ send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Netw return } -// TODO -UDP_Send_Error :: enum c.int { - // The message is too big. No data was sent. - Truncated = c.int(os.EMSGSIZE), - // TODO: not sure what the exact circumstances for this is yet - Network_Unreachable = c.int(os.ENETUNREACH), - // There are no more emphemeral outbound ports available to bind the socket to, in order to send. - No_Outbound_Ports_Available = c.int(os.EAGAIN), - // The send timeout duration passed before all data was sent. - // See Socket_Option.Send_Timeout. - Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets... - // The so-called socket is not an open socket. - Not_Socket = c.int(os.ENOTSOCK), - // The so-called socket is, in fact, not even a valid descriptor. - Not_Descriptor = c.int(os.EBADF), - // The buffer did not point to a valid location in memory. - Bad_Buffer = c.int(os.EFAULT), - // A signal occurred before any data was transmitted. - // See signal(7). - Interrupted = c.int(os.EINTR), - // 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(os.ENOBUFS), - // No memory was available to properly manage the send queue. - No_Memory_Available = c.int(os.ENOMEM), -} - send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) { toaddr := endpoint_to_sockaddr(to) for bytes_written < len(buf) { @@ -384,22 +225,10 @@ send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: send :: proc{send_tcp, send_udp} - - - Shutdown_Manner :: enum c.int { Receive = c.int(os.SHUT_RD), - Send = c.int(os.SHUT_WR), - Both = c.int(os.SHUT_RDWR), -} - -Shutdown_Error :: enum c.int { - Aborted = c.int(os.ECONNABORTED), - Reset = c.int(os.ECONNRESET), - Offline = c.int(os.ENETDOWN), - Not_Connected = c.int(os.ENOTCONN), - Not_Socket = c.int(os.ENOTSOCK), - Invalid_Manner = c.int(os.EINVAL), + Send = c.int(os.SHUT_WR), + Both = c.int(os.SHUT_RDWR), } shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) { @@ -411,29 +240,16 @@ shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Erro return } - - - Socket_Option :: enum c.int { - Reuse_Address = c.int(os.SO_REUSEADDR), - Keep_Alive = c.int(os.SO_KEEPALIVE), + Reuse_Address = c.int(os.SO_REUSEADDR), + Keep_Alive = c.int(os.SO_KEEPALIVE), Out_Of_Bounds_Data_Inline = c.int(os.SO_OOBINLINE), - TCP_Nodelay = c.int(os.TCP_NODELAY), - - Linger = c.int(os.SO_LINGER), - - Receive_Buffer_Size = c.int(os.SO_RCVBUF), - Send_Buffer_Size = c.int(os.SO_SNDBUF), - Receive_Timeout = c.int(os.SO_RCVTIMEO), - Send_Timeout = c.int(os.SO_SNDTIMEO), -} - -Socket_Option_Error :: enum c.int { - Offline = c.int(os.ENETDOWN), - Timeout_When_Keepalive_Set = c.int(os.ENETRESET), - Invalid_Option_For_Socket = c.int(os.ENOPROTOOPT), - Reset_When_Keepalive_Set = c.int(os.ENOTCONN), - Not_Socket = c.int(os.ENOTSOCK), + TCP_Nodelay = c.int(os.TCP_NODELAY), + Linger = c.int(os.SO_LINGER), + Receive_Buffer_Size = c.int(os.SO_RCVBUF), + Send_Buffer_Size = c.int(os.SO_SNDBUF), + Receive_Timeout = c.int(os.SO_RCVTIMEO), + Send_Timeout = c.int(os.SO_SNDTIMEO), } set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error { @@ -512,4 +328,4 @@ set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #cal } return nil -} \ No newline at end of file +} diff --git a/core/net/socket_windows.odin b/core/net/socket_windows.odin index a08248d91..ed6342316 100644 --- a/core/net/socket_windows.odin +++ b/core/net/socket_windows.odin @@ -23,16 +23,6 @@ import "core:time" Platform_Socket :: win.SOCKET -Create_Socket_Error :: enum c.int { - 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, -} - @(init, private) ensure_winsock_initialized :: proc() { win.ensure_winsock_initialized() @@ -69,25 +59,6 @@ create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (soc } } - -Dial_Error :: enum c.int { - 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? -} - dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (skt: TCP_Socket, err: Network_Error) { if endpoint.port == 0 { err = .Port_Required @@ -117,21 +88,6 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option return } -Bind_Error :: enum c.int { - // Another application is currently bound to this endpoint. - Address_In_Use = win.WSAEADDRINUSE, - // The address is not a local address on this machine. - Given_Nonlocal_Address = win.WSAEADDRNOTAVAIL, - // To bind a UDP socket to the broadcast address, the appropriate socket option must be set. - Broadcast_Disabled = win.WSAEACCES, - // The address family of the address does not match that of the socket. - Address_Family_Mismatch = win.WSAEFAULT, - // The socket is already bound to an address. - Already_Bound = win.WSAEINVAL, - // There are not enough ephemeral ports available. - No_Ports_Available = win.WSAENOBUFS, -} - bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) { sockaddr := endpoint_to_sockaddr(ep) s := any_socket_to_socket(skt) @@ -165,18 +121,6 @@ make_bound_udp_socket :: proc(bound_address: Address, port: int) -> (skt: UDP_So return } - - -Listen_Error :: enum c.int { - 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, -} - listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_Socket, err: Network_Error) { assert(backlog > 0 && i32(backlog) < max(i32)) @@ -199,17 +143,6 @@ listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_S return } - - -Accept_Error :: enum c.int { - 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, - Would_Block = win.WSAEWOULDBLOCK, // TODO: we may need special handling for this; maybe make a socket a struct with metadata? -} - accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) { for { sockaddr: win.SOCKADDR_STORAGE_LH @@ -236,30 +169,12 @@ accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: } } - - close :: proc(skt: Any_Socket) { if s := any_socket_to_socket(skt); s != {} { win.closesocket(Platform_Socket(s)) } } - - -TCP_Recv_Error :: enum c.int { - 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, // TODO: not functionally different from Reset; merge? - Timeout = win.WSAETIMEDOUT, - Connection_Closed = win.WSAECONNRESET, // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them? - Host_Unreachable = win.WSAEHOSTUNREACH, // TODO: verify can actually happen -} - recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) { if len(buf) <= 0 { return @@ -272,32 +187,6 @@ recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network return int(res), nil } -UDP_Recv_Error :: enum c.int { - Network_Subsystem_Failure = win.WSAENETDOWN, - Aborted = win.WSAECONNABORTED, // TODO: not functionally different from Reset; merge? - // UDP packets are limited in size, and the length of the incoming message exceeded it. - Truncated = win.WSAEMSGSIZE, - // The machine at the remote endpoint doesn't have the given port open to receiving UDP data. - Remote_Not_Listening = win.WSAECONNRESET, - Shutdown = win.WSAESHUTDOWN, - // A broadcast address was specified, but the .Broadcast socket option isn't set. - Broadcast_Disabled = win.WSAEACCES, - Bad_Buffer = win.WSAEFAULT, - No_Buffer_Space_Available = win.WSAENOBUFS, - // The socket is not valid socket handle. - Not_Socket = win.WSAENOTSOCK, - Would_Block = win.WSAEWOULDBLOCK, - // The remote host cannot be reached from this host at this time. - Host_Unreachable = win.WSAEHOSTUNREACH, - // The network cannot be reached from this host at this time. - Offline = win.WSAENETUNREACH, - Timeout = win.WSAETIMEDOUT, - // The socket isn't bound; an unknown flag specified; or MSG_OOB specified with SO_OOBINLINE enabled. - Incorrectly_Configured = win.WSAEINVAL, // TODO: can this actually happen? - // The message took more hops than was allowed (the Time To Live) to reach the remote endpoint. - TTL_Expired = win.WSAENETRESET, -} - recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) { if len(buf) <= 0 { return @@ -318,31 +207,6 @@ recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpo recv :: proc{recv_tcp, recv_udp} - -// -// 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 { - Aborted = win.WSAECONNABORTED, // TODO: not functionally different from Reset; merge? - 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, - Offline = win.WSAENETUNREACH, // TODO: verify possible, as not mentioned in docs - 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? - // The so-called socket is not an open socket. - Not_Socket = win.WSAENOTSOCK, -} - // 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. @@ -360,36 +224,6 @@ send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Netw return } -UDP_Send_Error :: enum c.int { - Network_Subsystem_Failure = win.WSAENETDOWN, - Aborted = win.WSAECONNABORTED, // TODO: not functionally different from Reset; merge? - // UDP packets are limited in size, and len(buf) exceeded it. - Message_Too_Long = win.WSAEMSGSIZE, - // The machine at the remote endpoint doesn't have the given port open to receiving UDP data. - Remote_Not_Listening = win.WSAECONNRESET, - 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. - Keepalive_Failure = win.WSAENETRESET, // TODO: not functionally different from Reset; merge? - No_Buffer_Space_Available = win.WSAENOBUFS, - // The socket is not valid socket handle. - Not_Socket = win.WSAENOTSOCK, - // 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, - // The remote host cannot be reached from this host at this time. - Host_Unreachable = win.WSAEHOSTUNREACH, - // Attempt to send to the Any address. - Cannot_Use_Any_Address = win.WSAEADDRNOTAVAIL, - // The address is of an incorrect address family for this socket. - Family_Not_Supported_For_This_Socket = win.WSAEAFNOSUPPORT, - // The network cannot be reached from this host at this time. - Offline = win.WSAENETUNREACH, - Timeout = win.WSAETIMEDOUT, -} // Sends a single UDP datagram packet. // @@ -413,24 +247,6 @@ send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: send :: proc{send_tcp, send_udp} - - - -Shutdown_Manner :: enum c.int { - Receive = win.SD_RECEIVE, - Send = win.SD_SEND, - Both = win.SD_BOTH, -} - -Shutdown_Error :: enum c.int { - Aborted = win.WSAECONNABORTED, - Reset = win.WSAECONNRESET, - Offline = win.WSAENETDOWN, - Not_Connected = win.WSAENOTCONN, - Not_Socket = win.WSAENOTSOCK, - Invalid_Manner = win.WSAEINVAL, -} - shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) { s := any_socket_to_socket(skt) res := win.shutdown(Platform_Socket(s), c.int(manner)) @@ -440,59 +256,6 @@ shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Erro return } - - - -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, -} - -Socket_Option_Error :: enum c.int { - 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, -} - set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error { level := win.SOL_SOCKET if option != .TCP_Nodelay else win.IPPROTO_TCP