aboutsummaryrefslogtreecommitdiff
path: root/core/net
diff options
context:
space:
mode:
authorflysand7 <yyakut.ac@gmail.com>2023-10-18 01:57:26 +1100
committerflysand7 <yyakut.ac@gmail.com>2023-10-27 10:51:21 +1100
commit4d65b1ab9cb86bcbbfb0e5b26e3552f6f3582004 (patch)
treeb61fb2dbcfe8fbd8574cbda546c27ed91e49d44a /core/net
parent8e4bdcfb9837d70e94634db02e79a06036a3dde7 (diff)
Implement new sys/unix package
Diffstat (limited to 'core/net')
-rw-r--r--core/net/errors_linux.odin188
-rw-r--r--core/net/interface_linux.odin226
-rw-r--r--core/net/socket.odin2
-rw-r--r--core/net/socket_linux.odin466
4 files changed, 432 insertions, 450 deletions
diff --git a/core/net/errors_linux.odin b/core/net/errors_linux.odin
index d76132ad5..2370dd0d8 100644
--- a/core/net/errors_linux.odin
+++ b/core/net/errors_linux.odin
@@ -16,182 +16,184 @@ package net
Tetralux: Initial implementation
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
Jeroen van Rijn: Cross platform unification, code style, documentation
+ flysand: Move dependency from core:linux.Errno to core:sys/linux
*/
import "core:c"
-import "core:os"
+import "core:sys/linux"
Create_Socket_Error :: enum c.int {
None = 0,
- 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),
+ 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),
}
Dial_Error :: enum c.int {
None = 0,
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),
+ 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(os.EWOULDBLOCK),
+ Would_Block = c.int(linux.Errno.EWOULDBLOCK),
}
Bind_Error :: enum c.int {
None = 0,
- 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.
+ 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.
}
Listen_Error :: enum c.int {
None = 0,
- 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),
+ 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),
}
Accept_Error :: enum c.int {
None = 0,
- 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),
+ 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),
// TODO: we may need special handling for this; maybe make a socket a struct with metadata?
- Would_Block = c.int(os.EWOULDBLOCK),
+ Would_Block = c.int(linux.Errno.EWOULDBLOCK),
}
TCP_Recv_Error :: enum c.int {
None = 0,
- 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),
+ 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(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...
+ 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...
}
UDP_Recv_Error :: enum c.int {
None = 0,
- Buffer_Too_Small = c.int(os.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(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).
+ 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).
// 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.
+ 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.
}
TCP_Send_Error :: enum c.int {
None = 0,
- Aborted = c.int(os.ECONNABORTED),
- Connection_Closed = c.int(os.ECONNRESET),
- Not_Connected = c.int(os.ENOTCONN),
- Shutdown = c.int(os.ESHUTDOWN),
+ 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(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).
- Timeout = c.int(os.EWOULDBLOCK), // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
- Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
+ 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.
}
// TODO
UDP_Send_Error :: enum c.int {
None = 0,
- Message_Too_Long = c.int(os.EMSGSIZE), // The message is larger than the maximum UDP packet size. No data was sent.
+ 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(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.
+ 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(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).
+ 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(os.ENOBUFS),
- No_Memory_Available = c.int(os.ENOMEM), // No memory was available to properly manage 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.
}
+// TODO(flysand): slight regression
Shutdown_Manner :: enum c.int {
- Receive = c.int(os.SHUT_RD),
- Send = c.int(os.SHUT_WR),
- Both = c.int(os.SHUT_RDWR),
+ Receive = c.int(linux.Shutdown_How.RD),
+ Send = c.int(linux.Shutdown_How.WR),
+ Both = c.int(linux.Shutdown_How.RDWR),
}
Shutdown_Error :: enum c.int {
None = 0,
- 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),
+ 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),
}
Socket_Option_Error :: enum c.int {
None = 0,
- 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),
+ 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),
}
Set_Blocking_Error :: enum c.int {
None = 0,
// TODO: add errors occuring on followig calls:
- // flags, _ := os.fcntl(sd, os.F_GETFL, 0)
- // os.fcntl(sd, os.F_SETFL, flags | int(os.O_NONBLOCK))
+ // 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
diff --git a/core/net/interface_linux.odin b/core/net/interface_linux.odin
index e889cfa97..7c99cf23b 100644
--- a/core/net/interface_linux.odin
+++ b/core/net/interface_linux.odin
@@ -21,120 +21,124 @@ package net
TODO: When we have raw sockets, split off into its own file for Linux so we can use the NETLINK protocol and bypass libc.
*/
-import "core:os"
-import "core:strings"
+//import "core:strings"
+
+// TODO(flysand): regression
+// 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) {
context.allocator = allocator
- head: ^os.ifaddrs
-
- if res := os._getifaddrs(&head); res < 0 {
- return {}, .Unable_To_Enumerate_Network_Interfaces
- }
-
- /*
- Unlike Windows, *nix regrettably doesn't return all it knows about an interface in one big struct.
- We're going to have to iterate over a list and coalesce information as we go.
- */
- ifaces: map[string]^Network_Interface
- defer delete(ifaces)
-
- for ifaddr := head; ifaddr != nil; ifaddr = ifaddr.next {
- adapter_name := string(ifaddr.name)
-
- /*
- Check if we have seen this interface name before so we can reuse the `Network_Interface`.
- Else, create a new one.
- */
- if adapter_name not_in ifaces {
- ifaces[adapter_name] = new(Network_Interface)
- ifaces[adapter_name].adapter_name = strings.clone(adapter_name)
- }
- iface := ifaces[adapter_name]
-
- address: Address
- netmask: Netmask
-
- if ifaddr.address != nil {
- switch int(ifaddr.address.sa_family) {
- case os.AF_INET, os.AF_INET6:
- address = _sockaddr_basic_to_endpoint(ifaddr.address).address
-
- case os.AF_PACKET:
- /*
- For some obscure reason the 64-bit `getifaddrs` call returns a pointer to a
- 32-bit `RTNL_LINK_STATS` structure, which of course means that tx/rx byte count
- is truncated beyond usefulness.
-
- We're not going to retrieve stats now. Instead this serves as a reminder to use
- the NETLINK protocol for this purpose.
-
- But in case you were curious:
- stats := transmute(^os.rtnl_link_stats)ifaddr.data
- fmt.println(stats)
- */
- case:
- }
- }
-
- if ifaddr.netmask != nil {
- switch int(ifaddr.netmask.sa_family) {
- case os.AF_INET, os.AF_INET6:
- netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
- case:
- }
- }
-
- if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
- switch int(ifaddr.broadcast_or_dest.sa_family) {
- case os.AF_INET, os.AF_INET6:
- broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
- append(&iface.multicast, broadcast)
- case:
- }
- }
-
- if address != nil {
- lease := Lease{
- address = address,
- netmask = netmask,
- }
- append(&iface.unicast, lease)
- }
-
- /*
- TODO: Refine this based on the type of adapter.
- */
- state := Link_State{}
-
- if .UP in ifaddr.flags {
- state |= {.Up}
- }
-
- if .DORMANT in ifaddr.flags {
- state |= {.Dormant}
- }
-
- if .LOOPBACK in ifaddr.flags {
- state |= {.Loopback}
- }
- iface.link.state = state
- }
-
- /*
- Free the OS structures.
- */
- os._freeifaddrs(head)
-
- /*
- Turn the map into a slice to return.
- */
- _interfaces := make([dynamic]Network_Interface, 0, allocator)
- for _, iface in ifaces {
- append(&_interfaces, iface^)
- free(iface)
- }
- return _interfaces[:], {}
+ // head: ^os.ifaddrs
+
+ // if res := os._getifaddrs(&head); res < 0 {
+ // return {}, .Unable_To_Enumerate_Network_Interfaces
+ // }
+
+ // /*
+ // Unlike Windows, *nix regrettably doesn't return all it knows about an interface in one big struct.
+ // We're going to have to iterate over a list and coalesce information as we go.
+ // */
+ // ifaces: map[string]^Network_Interface
+ // defer delete(ifaces)
+
+ // for ifaddr := head; ifaddr != nil; ifaddr = ifaddr.next {
+ // adapter_name := string(ifaddr.name)
+
+ // /*
+ // Check if we have seen this interface name before so we can reuse the `Network_Interface`.
+ // Else, create a new one.
+ // */
+ // if adapter_name not_in ifaces {
+ // ifaces[adapter_name] = new(Network_Interface)
+ // ifaces[adapter_name].adapter_name = strings.clone(adapter_name)
+ // }
+ // iface := ifaces[adapter_name]
+
+ // address: Address
+ // netmask: Netmask
+
+ // if ifaddr.address != nil {
+ // switch int(ifaddr.address.sa_family) {
+ // case os.AF_INET, os.AF_INET6:
+ // address = _sockaddr_basic_to_endpoint(ifaddr.address).address
+
+ // case os.AF_PACKET:
+ // /*
+ // For some obscure reason the 64-bit `getifaddrs` call returns a pointer to a
+ // 32-bit `RTNL_LINK_STATS` structure, which of course means that tx/rx byte count
+ // is truncated beyond usefulness.
+
+ // We're not going to retrieve stats now. Instead this serves as a reminder to use
+ // the NETLINK protocol for this purpose.
+
+ // But in case you were curious:
+ // stats := transmute(^os.rtnl_link_stats)ifaddr.data
+ // fmt.println(stats)
+ // */
+ // case:
+ // }
+ // }
+
+ // if ifaddr.netmask != nil {
+ // switch int(ifaddr.netmask.sa_family) {
+ // case os.AF_INET, os.AF_INET6:
+ // netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
+ // case:
+ // }
+ // }
+
+ // if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
+ // switch int(ifaddr.broadcast_or_dest.sa_family) {
+ // case os.AF_INET, os.AF_INET6:
+ // broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
+ // append(&iface.multicast, broadcast)
+ // case:
+ // }
+ // }
+
+ // if address != nil {
+ // lease := Lease{
+ // address = address,
+ // netmask = netmask,
+ // }
+ // append(&iface.unicast, lease)
+ // }
+
+ // /*
+ // TODO: Refine this based on the type of adapter.
+ // */
+ // state := Link_State{}
+
+ // if .UP in ifaddr.flags {
+ // state |= {.Up}
+ // }
+
+ // if .DORMANT in ifaddr.flags {
+ // state |= {.Dormant}
+ // }
+
+ // if .LOOPBACK in ifaddr.flags {
+ // state |= {.Loopback}
+ // }
+ // iface.link.state = state
+ // }
+
+ // /*
+ // Free the OS structures.
+ // */
+ // os._freeifaddrs(head)
+
+ // /*
+ // Turn the map into a slice to return.
+ // */
+ // _interfaces := make([dynamic]Network_Interface, 0, allocator)
+ // for _, iface in ifaces {
+ // append(&_interfaces, iface^)
+ // free(iface)
+ // }
+ // return _interfaces[:], {}
+ return nil, {}
} \ No newline at end of file
diff --git a/core/net/socket.odin b/core/net/socket.odin
index 8cdf7cceb..40fa6ab56 100644
--- a/core/net/socket.odin
+++ b/core/net/socket.odin
@@ -18,7 +18,7 @@ package net
Jeroen van Rijn: Cross platform unification, code style, documentation
*/
-any_socket_to_socket :: proc(socket: Any_Socket) -> Socket {
+any_socket_to_socket :: proc "contextless" (socket: Any_Socket) -> Socket {
switch s in socket {
case TCP_Socket: return Socket(s)
case UDP_Socket: return Socket(s)
diff --git a/core/net/socket_linux.odin b/core/net/socket_linux.odin
index b7141e8ba..590946dff 100644
--- a/core/net/socket_linux.odin
+++ b/core/net/socket_linux.odin
@@ -16,241 +16,294 @@ package net
Tetralux: Initial implementation
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
Jeroen van Rijn: Cross platform unification, code style, documentation
+ flysand: Move dependency from core:os to core:sys/linux
*/
import "core:c"
-import "core:os"
import "core:time"
+import "core:sys/linux"
Socket_Option :: enum c.int {
- 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),
+ Reuse_Address = c.int(linux.Socket_Option.REUSEADDR),
+ Keep_Alive = c.int(linux.Socket_Option.KEEPALIVE),
+ Out_Of_Bounds_Data_Inline = c.int(linux.Socket_Option.OOBINLINE),
+ TCP_Nodelay = c.int(linux.Socket_TCP_Option.NODELAY),
+ Linger = c.int(linux.Socket_Option.LINGER),
+ Receive_Buffer_Size = c.int(linux.Socket_Option.RCVBUF),
+ Send_Buffer_Size = c.int(linux.Socket_Option.SNDBUF),
+ Receive_Timeout = c.int(linux.Socket_Option.RCVTIMEO_NEW),
+ Send_Timeout = c.int(linux.Socket_Option.SNDTIMEO_NEW),
}
-@(private)
-_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
- c_type, c_protocol, c_family: int
+// Wrappers and unwrappers for system-native types
+
+@(private="file")
+_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 {
+ switch protocol {
+ case .TCP: return TCP_Socket(Socket(sock))
+ case .UDP: return UDP_Socket(Socket(sock))
+ case:
+ unreachable()
+ }
+}
+
+@(private="file")
+_unwrap_os_family :: proc "contextless" (family: Address_Family)->linux.Address_Family {
switch family {
- case .IP4: c_family = os.AF_INET
- case .IP6: c_family = os.AF_INET6
+ case .IP4: return .INET
+ case .IP6: return .INET6
case:
unreachable()
}
+}
+@(private="file")
+_unwrap_os_proto_socktype :: proc "contextless" (protocol: Socket_Protocol)->(linux.Protocol, linux.Socket_Type) {
switch protocol {
- case .TCP: c_type = os.SOCK_STREAM; c_protocol = os.IPPROTO_TCP
- case .UDP: c_type = os.SOCK_DGRAM; c_protocol = os.IPPROTO_UDP
+ case .TCP: return .TCP, .STREAM
+ case .UDP: return .UDP, .DGRAM
case:
unreachable()
}
+}
- sock, ok := os.socket(c_family, c_type, c_protocol)
- if ok != os.ERROR_NONE {
- err = Create_Socket_Error(ok)
- return
+@(private="file")
+_unwrap_os_addr :: proc "contextless" (endpoint: Endpoint)->(linux.Sock_Addr_Any) {
+ switch address in endpoint.address {
+ case IP4_Address:
+ return {
+ ipv4 = {
+ sin_family = .INET,
+ sin_port = u16be(endpoint.port),
+ sin_addr = transmute([4]u8) endpoint.address.(IP4_Address),
+ },
+ }
+ case IP6_Address:
+ return {
+ ipv6 = {
+ sin6_port = u16be(endpoint.port),
+ sin6_addr = transmute([16]u8) endpoint.address.(IP6_Address),
+ sin6_family = .INET6,
+ },
+ }
+ case:
+ unreachable()
}
+}
- switch protocol {
- case .TCP: return TCP_Socket(sock), nil
- case .UDP: return UDP_Socket(sock), nil
+@(private="file")
+_wrap_os_addr :: proc "contextless" (addr: linux.Sock_Addr_Any)->(Endpoint) {
+ #partial switch addr.family {
+ case .INET:
+ return {
+ address = cast(IP4_Address) addr.sin_addr,
+ port = cast(int) addr.sin_port,
+ }
+ case .INET6:
+ return {
+ port = cast(int) addr.sin6_port,
+ address = transmute(IP6_Address) addr.sin6_addr,
+ }
case:
unreachable()
}
}
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (Any_Socket, Network_Error) {
+ family := _unwrap_os_family(family)
+ proto, socktype := _unwrap_os_proto_socktype(protocol)
+ sock, errno := linux.socket(family, socktype, {}, proto)
+ if errno != .NONE {
+ return {}, Create_Socket_Error(errno)
+ }
+ return _wrap_os_socket(sock, protocol), nil
+}
+
@(private)
-_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (skt: TCP_Socket, err: Network_Error) {
+_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (tcp_sock: TCP_Socket, err: Network_Error) {
+ errno: linux.Errno
if endpoint.port == 0 {
return 0, .Port_Required
}
-
- family := family_from_endpoint(endpoint)
- sock := create_socket(family, .TCP) or_return
- skt = sock.(TCP_Socket)
-
+ // Create new TCP socket
+ os_sock: linux.Fd
+ os_sock, errno = linux.socket(_unwrap_os_family(family_from_endpoint(endpoint)), .STREAM, {}, .TCP)
+ if errno != .NONE {
+ // TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
+ 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
// use the same address immediately.
- _ = set_option(skt, .Reuse_Address, true)
-
- sockaddr := _endpoint_to_sockaddr(endpoint)
- res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), size_of(sockaddr))
- if res != os.ERROR_NONE {
- err = Dial_Error(res)
- return
+ reuse_addr: b32 = true
+ _ = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &reuse_addr)
+ addr := _unwrap_os_addr(endpoint)
+ errno = linux.connect(linux.Fd(tcp_sock), &addr)
+ if errno != .NONE {
+ return cast(TCP_Socket) os_sock, Dial_Error(errno)
}
-
- if options.no_delay {
- _ = _set_option(sock, .TCP_Nodelay, true) // NOTE(tetra): Not vital to succeed; error ignored
- }
-
- return
+ // NOTE(tetra): Not vital to succeed; error ignored
+ no_delay: b32 = cast(b32) options.no_delay
+ _ = linux.setsockopt(os_sock, linux.SOL_TCP, linux.Socket_TCP_Option.NODELAY, &no_delay)
+ return cast(TCP_Socket) os_sock, nil
}
@(private)
-_bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
- sockaddr := _endpoint_to_sockaddr(ep)
- s := any_socket_to_socket(skt)
- res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), size_of(sockaddr))
- if res != os.ERROR_NONE {
- err = Bind_Error(res)
+_bind :: proc(sock: Any_Socket, endpoint: Endpoint) -> (Network_Error) {
+ addr := _unwrap_os_addr(endpoint)
+ errno := linux.bind(_unwrap_os_socket(sock), &addr)
+ if errno != .NONE {
+ return Bind_Error(errno)
}
- return
+ return nil
}
@(private)
-_listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_Socket, err: Network_Error) {
+_listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (TCP_Socket, Network_Error) {
+ errno: linux.Errno
assert(backlog > 0 && i32(backlog) < max(i32))
-
- family := family_from_endpoint(interface_endpoint)
- sock := create_socket(family, .TCP) or_return
- skt = sock.(TCP_Socket)
-
+ // Figure out the address family and address of the endpoint
+ ep_family := _unwrap_os_family(family_from_endpoint(endpoint))
+ ep_address := _unwrap_os_addr(endpoint)
+ // Create TCP socket
+ os_sock: linux.Fd
+ os_sock, errno = linux.socket(ep_family, .STREAM, {}, .TCP)
+ if errno != .NONE {
+ // TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
+ 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
// 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
-
- bind(sock, interface_endpoint) or_return
-
- res := os.listen(os.Socket(skt), backlog)
- if res != os.ERROR_NONE {
- err = Listen_Error(res)
- return
+ do_reuse_addr: b32 = true
+ errno = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &do_reuse_addr)
+ if errno != .NONE {
+ return cast(TCP_Socket) os_sock, Listen_Error(errno)
}
-
- return
+ // Bind the socket to endpoint address
+ errno = linux.bind(os_sock, &ep_address)
+ if errno != .NONE {
+ return cast(TCP_Socket) os_sock, Bind_Error(errno)
+ }
+ // Listen on bound socket
+ errno = linux.listen(os_sock, cast(i32) backlog)
+ if errno != .NONE {
+ return cast(TCP_Socket) os_sock, Listen_Error(errno)
+ }
+ return cast(TCP_Socket) os_sock, nil
}
@(private)
-_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))
-
- client_sock, ok := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
- if ok != os.ERROR_NONE {
- err = Accept_Error(ok)
- return
+_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (tcp_client: TCP_Socket, endpoint: Endpoint, err: Network_Error) {
+ addr: linux.Sock_Addr_Any
+ client_sock, errno := linux.accept(linux.Fd(sock), &addr)
+ if errno != .NONE {
+ return {}, {}, Accept_Error(errno)
}
- client = TCP_Socket(client_sock)
- source = _sockaddr_storage_to_endpoint(&sockaddr)
- if options.no_delay {
- _ = _set_option(client, .TCP_Nodelay, true) // NOTE(tetra): Not vital to succeed; error ignored
- }
- return
+ // NOTE(tetra): Not vital to succeed; error ignored
+ val: b32 = cast(b32) options.no_delay
+ _ = linux.setsockopt(client_sock, linux.SOL_TCP, linux.Socket_TCP_Option.NODELAY, &val)
+ return TCP_Socket(client_sock), _wrap_os_addr(addr), nil
}
@(private)
-_close :: proc(skt: Any_Socket) {
- s := any_socket_to_socket(skt)
- os.close(os.Handle(os.Socket(s)))
+_close :: proc(sock: Any_Socket) {
+ linux.close(_unwrap_os_socket(sock))
}
@(private)
-_recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+_recv_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, Network_Error) {
if len(buf) <= 0 {
- return
+ return 0, nil
}
- res, ok := os.recv(os.Socket(skt), buf, 0)
- if ok != os.ERROR_NONE {
- err = TCP_Recv_Error(ok)
- return
+ bytes_read, errno := linux.recv(linux.Fd(tcp_sock), buf, {})
+ if errno != .NONE {
+ return 0, TCP_Recv_Error(errno)
}
- return int(res), nil
+ return int(bytes_read), nil
}
@(private)
-_recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+_recv_udp :: proc(udp_sock: UDP_Socket, buf: []byte) -> (int, Endpoint, Network_Error) {
if len(buf) <= 0 {
- return
+ // NOTE(flysand): It was returning no error, I didn't change anything
+ return 0, {}, {}
}
-
- from: os.SOCKADDR_STORAGE_LH = ---
- fromsize := c.int(size_of(from))
-
// NOTE(tetra): On Linux, if the buffer is too small to fit the entire datagram payload, the rest is silently discarded,
// and no error is returned.
// However, if you pass MSG_TRUNC here, 'res' will be the size of the incoming message, rather than how much was read.
// We can use this fact to detect this condition and return .Buffer_Too_Small.
- res, ok := os.recvfrom(os.Socket(skt), buf, os.MSG_TRUNC, cast(^os.SOCKADDR) &from, &fromsize)
- if ok != os.ERROR_NONE {
- err = UDP_Recv_Error(ok)
- return
+ 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)
}
-
- bytes_read = int(res)
- remote_endpoint = _sockaddr_storage_to_endpoint(&from)
-
if bytes_read > len(buf) {
// NOTE(tetra): The buffer has been filled, with a partial message.
- bytes_read = len(buf)
- err = .Buffer_Too_Small
+ return len(buf), {}, .Buffer_Too_Small
}
-
- return
+ return bytes_read, _wrap_os_addr(from_addr), nil
}
@(private)
-_send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
- for bytes_written < len(buf) {
- limit := min(int(max(i32)), len(buf) - bytes_written)
- remaining := buf[bytes_written:][:limit]
- res, ok := os.send(os.Socket(skt), remaining, 0)
- if ok != os.ERROR_NONE {
- err = TCP_Send_Error(ok)
- return
+_send_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, Network_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, {})
+ if errno != .NONE {
+ return total_written, TCP_Send_Error(errno)
}
- bytes_written += int(res)
+ total_written += int(res)
}
- return
+ return total_written, nil
}
@(private)
-_send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
- toaddr := _endpoint_to_sockaddr(to)
- res, os_err := os.sendto(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &toaddr, size_of(toaddr))
- if os_err != os.ERROR_NONE {
- err = UDP_Send_Error(os_err)
- return
+_send_udp :: proc(udp_sock: UDP_Socket, buf: []byte, to: Endpoint) -> (int, Network_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)
}
- bytes_written = int(res)
- return
+ return int(bytes_written), nil
}
@(private)
-_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
- s := any_socket_to_socket(skt)
- res := os.shutdown(os.Socket(s), int(manner))
- if res != os.ERROR_NONE {
- return Shutdown_Error(res)
+_shutdown :: proc(sock: Any_Socket, manner: Shutdown_Manner) -> (err: Network_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
+ return nil
}
+// TODO(flysand): Figure out what we want to do with this on core:sys/ level.
@(private)
-_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
- level := os.SOL_SOCKET if option != .TCP_Nodelay else os.IPPROTO_TCP
-
+_set_option :: proc(sock: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+ level: int
+ if option == .TCP_Nodelay {
+ level = int(linux.SOL_TCP)
+ } else {
+ level = int(linux.SOL_SOCKET)
+ }
+ os_sock := _unwrap_os_socket(sock)
// NOTE(tetra, 2022-02-15): On Linux, you cannot merely give a single byte for a bool;
// it _has_ to be a b32.
- // I haven't tested if you can give more than that.
+ // I haven't tested if you can give more than that. <-- (flysand) probably not, posix explicitly specifies an int
bool_value: b32
int_value: i32
- timeval_value: os.Timeval
-
- ptr: rawptr
- len: os.socklen_t
-
+ timeval_value: linux.Time_Val
+ errno: linux.Errno
switch option {
case
.Reuse_Address,
@@ -258,7 +311,7 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
.Out_Of_Bounds_Data_Inline,
.TCP_Nodelay:
// TODO: verify whether these are options or not on Linux
- // .Broadcast,
+ // .Broadcast, <-- yes
// .Conditional_Accept,
// .Dont_Linger:
switch x in value {
@@ -274,8 +327,7 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
case:
panic("set_option() value must be a boolean here", loc)
}
- ptr = &bool_value
- len = size_of(bool_value)
+ errno = linux.setsockopt(os_sock, level, int(option), &bool_value)
case
.Linger,
.Send_Timeout,
@@ -283,125 +335,49 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
t, ok := value.(time.Duration)
if !ok do panic("set_option() value must be a time.Duration here", loc)
- micros := i64(time.duration_microseconds(t))
- timeval_value.microseconds = int(micros % 1e6)
- timeval_value.seconds = (micros - i64(timeval_value.microseconds)) / 1e6
-
- ptr = &timeval_value
- len = size_of(timeval_value)
+ micros := cast(i64) (time.duration_microseconds(t))
+ timeval_value.microseconds = cast(int) (micros % 1e6)
+ timeval_value.seconds = cast(int) ((micros - i64(timeval_value.microseconds)) / 1e6)
+ errno = linux.setsockopt(os_sock, level, int(option), &timeval_value)
case
.Receive_Buffer_Size,
.Send_Buffer_Size:
// TODO: check for out of range values and return .Value_Out_Of_Range?
switch i in value {
- case i8, u8: i2 := i; int_value = os.socklen_t((^u8)(&i2)^)
- case i16, u16: i2 := i; int_value = os.socklen_t((^u16)(&i2)^)
- case i32, u32: i2 := i; int_value = os.socklen_t((^u32)(&i2)^)
- case i64, u64: i2 := i; int_value = os.socklen_t((^u64)(&i2)^)
- case i128, u128: i2 := i; int_value = os.socklen_t((^u128)(&i2)^)
- case int, uint: i2 := i; int_value = os.socklen_t((^uint)(&i2)^)
+ case i8, u8: i2 := i; int_value = i32((^u8)(&i2)^)
+ case i16, u16: i2 := i; int_value = i32((^u16)(&i2)^)
+ case i32, u32: i2 := i; int_value = i32((^u32)(&i2)^)
+ case i64, u64: i2 := i; int_value = i32((^u64)(&i2)^)
+ case i128, u128: i2 := i; int_value = i32((^u128)(&i2)^)
+ case int, uint: i2 := i; int_value = i32((^uint)(&i2)^)
case:
panic("set_option() value must be an integer here", loc)
}
- ptr = &int_value
- len = size_of(int_value)
+ errno = linux.setsockopt(os_sock, level, int(option), &int_value)
}
-
- skt := any_socket_to_socket(s)
- res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
- if res != os.ERROR_NONE {
- return Socket_Option_Error(res)
+ if errno != .NONE {
+ return Socket_Option_Error(errno)
}
-
return nil
}
@(private)
-_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
- socket := any_socket_to_socket(socket)
-
- flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
- if getfl_err != os.ERROR_NONE {
- return Set_Blocking_Error(getfl_err)
+_set_blocking :: proc(sock: Any_Socket, should_block: bool) -> (err: Network_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)
}
-
if should_block {
- flags &= ~int(os.O_NONBLOCK)
+ flags &= ~{.NONBLOCK}
} else {
- flags |= int(os.O_NONBLOCK)
+ flags |= {.NONBLOCK}
}
-
- _, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags)
- if setfl_err != os.ERROR_NONE {
- return Set_Blocking_Error(setfl_err)
+ errno = linux.fcntl(os_sock, linux.F_SETFL, flags)
+ if errno != .NONE {
+ return Set_Blocking_Error(errno)
}
-
return nil
}
-
-@(private)
-_endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: os.SOCKADDR_STORAGE_LH) {
- switch a in ep.address {
- case IP4_Address:
- (^os.sockaddr_in)(&sockaddr)^ = os.sockaddr_in {
- sin_port = u16be(ep.port),
- sin_addr = transmute(os.in_addr) a,
- sin_family = u16(os.AF_INET),
- }
- return
- case IP6_Address:
- (^os.sockaddr_in6)(&sockaddr)^ = os.sockaddr_in6 {
- sin6_port = u16be(ep.port),
- sin6_addr = transmute(os.in6_addr) a,
- sin6_family = u16(os.AF_INET6),
- }
- return
- }
- unreachable()
-}
-
-@(private)
-_sockaddr_storage_to_endpoint :: proc(native_addr: ^os.SOCKADDR_STORAGE_LH) -> (ep: Endpoint) {
- switch native_addr.ss_family {
- case u16(os.AF_INET):
- addr := cast(^os.sockaddr_in) native_addr
- port := int(addr.sin_port)
- ep = Endpoint {
- address = IP4_Address(transmute([4]byte) addr.sin_addr),
- port = port,
- }
- case u16(os.AF_INET6):
- addr := cast(^os.sockaddr_in6) native_addr
- port := int(addr.sin6_port)
- ep = Endpoint {
- address = IP6_Address(transmute([8]u16be) addr.sin6_addr),
- port = port,
- }
- case:
- panic("native_addr is neither IP4 or IP6 address")
- }
- return
-}
-
-@(private)
-_sockaddr_basic_to_endpoint :: proc(native_addr: ^os.SOCKADDR) -> (ep: Endpoint) {
- switch native_addr.sa_family {
- case u16(os.AF_INET):
- addr := cast(^os.sockaddr_in) native_addr
- port := int(addr.sin_port)
- ep = Endpoint {
- address = IP4_Address(transmute([4]byte) addr.sin_addr),
- port = port,
- }
- case u16(os.AF_INET6):
- addr := cast(^os.sockaddr_in6) native_addr
- port := int(addr.sin6_port)
- ep = Endpoint {
- address = IP6_Address(transmute([8]u16be) addr.sin6_addr),
- port = port,
- }
- case:
- panic("native_addr is neither IP4 or IP6 address")
- }
- return
-}