aboutsummaryrefslogtreecommitdiff
path: root/core/net/socket_linux.odin
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/socket_linux.odin
parent8e4bdcfb9837d70e94634db02e79a06036a3dde7 (diff)
Implement new sys/unix package
Diffstat (limited to 'core/net/socket_linux.odin')
-rw-r--r--core/net/socket_linux.odin466
1 files changed, 221 insertions, 245 deletions
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
-}