diff --git a/core/net/errors_darwin.odin b/core/net/errors_darwin.odin index 2905b44bc..ccf1e0f7f 100644 --- a/core/net/errors_darwin.odin +++ b/core/net/errors_darwin.odin @@ -21,188 +21,191 @@ package net */ import "core:c" -import "core:os" +import "core:sys/posix" + +@(private) +ESHUTDOWN :: 58 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(posix.EAFNOSUPPORT), + No_Socket_Descriptors_Available = c.int(posix.EMFILE), + No_Buffer_Space_Available = c.int(posix.ENOBUFS), + No_Memory_Available = c.int(posix.ENOMEM), + Protocol_Unsupported_By_System = c.int(posix.EPROTONOSUPPORT), + Wrong_Protocol_For_Socket = c.int(posix.EPROTONOSUPPORT), + Family_And_Socket_Type_Mismatch = c.int(posix.EPROTONOSUPPORT), } Dial_Error :: enum c.int { None = 0, Port_Required = -1, // Attempted to dial an endpointing without a port being set. - Address_In_Use = c.int(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(posix.EADDRINUSE), + In_Progress = c.int(posix.EINPROGRESS), + Cannot_Use_Any_Address = c.int(posix.EADDRNOTAVAIL), + Wrong_Family_For_Socket = c.int(posix.EAFNOSUPPORT), + Refused = c.int(posix.ECONNREFUSED), + Is_Listening_Socket = c.int(posix.EACCES), + Already_Connected = c.int(posix.EISCONN), + Network_Unreachable = c.int(posix.ENETUNREACH), // Device is offline + Host_Unreachable = c.int(posix.EHOSTUNREACH), // Remote host cannot be reached + No_Buffer_Space_Available = c.int(posix.ENOBUFS), + Not_Socket = c.int(posix.ENOTSOCK), + Timeout = c.int(posix.ETIMEDOUT), // TODO: we may need special handling for this; maybe make a socket a struct with metadata? - Would_Block = c.int(os.EWOULDBLOCK), + Would_Block = c.int(posix.EWOULDBLOCK), } Bind_Error :: enum c.int { None = 0, Privileged_Port_Without_Root = -1, // Attempted to bind to a port less than 1024 without root access. - Address_In_Use = c.int(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(posix.EADDRINUSE), // Another application is currently bound to this endpoint. + Given_Nonlocal_Address = c.int(posix.EADDRNOTAVAIL), // The address is not a local address on this machine. + Broadcast_Disabled = c.int(posix.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set. + Address_Family_Mismatch = c.int(posix.EFAULT), // The address family of the address does not match that of the socket. + Already_Bound = c.int(posix.EINVAL), // The socket is already bound to an address. + No_Ports_Available = c.int(posix.ENOBUFS), // There are not enough ephemeral ports available. } 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(posix.EADDRINUSE), + Already_Connected = c.int(posix.EISCONN), + No_Socket_Descriptors_Available = c.int(posix.EMFILE), + No_Buffer_Space_Available = c.int(posix.ENOBUFS), + Nonlocal_Address = c.int(posix.EADDRNOTAVAIL), + Not_Socket = c.int(posix.ENOTSOCK), + Listening_Not_Supported_For_This_Socket = c.int(posix.EOPNOTSUPP), } Accept_Error :: enum c.int { None = 0, // TODO(tetra): Is this error actually possible here? Or is like Linux, in which case we can remove it. - Reset = c.int(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), + Reset = c.int(posix.ECONNRESET), + Not_Listening = c.int(posix.EINVAL), + No_Socket_Descriptors_Available_For_Client_Socket = c.int(posix.EMFILE), + No_Buffer_Space_Available = c.int(posix.ENOBUFS), + Not_Socket = c.int(posix.ENOTSOCK), + Not_Connection_Oriented_Socket = c.int(posix.EOPNOTSUPP), // TODO: we may need special handling for this; maybe make a socket a struct with metadata? - Would_Block = c.int(os.EWOULDBLOCK), + Would_Block = c.int(posix.EWOULDBLOCK), } TCP_Recv_Error :: enum c.int { None = 0, - Shutdown = c.int(os.ESHUTDOWN), - Not_Connected = c.int(os.ENOTCONN), + Shutdown = ESHUTDOWN, + Not_Connected = c.int(posix.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), + Connection_Broken = c.int(posix.ENETRESET), + Not_Socket = c.int(posix.ENOTSOCK), + Aborted = c.int(posix.ECONNABORTED), // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them? - Connection_Closed = c.int(os.ECONNRESET), - Offline = c.int(os.ENETDOWN), - Host_Unreachable = c.int(os.EHOSTUNREACH), - Interrupted = c.int(os.EINTR), + Connection_Closed = c.int(posix.ECONNRESET), + Offline = c.int(posix.ENETDOWN), + Host_Unreachable = c.int(posix.EHOSTUNREACH), + Interrupted = c.int(posix.EINTR), // NOTE: No, really. Presumably this means something different for nonblocking sockets... - Timeout = c.int(os.EWOULDBLOCK), + Timeout = c.int(posix.EWOULDBLOCK), } 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(posix.EMSGSIZE), // The buffer is too small to fit the entire message, and the message was truncated. When this happens, the rest of message is lost. + Not_Socket = c.int(posix.ENOTSOCK), // The so-called socket is not an open socket. + Not_Descriptor = c.int(posix.EBADF), // The so-called socket is, in fact, not even a valid descriptor. + Bad_Buffer = c.int(posix.EFAULT), // The buffer did not point to a valid location in memory. + Interrupted = c.int(posix.EINTR), // A signal occurred before any data was transmitted. See signal(7). // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout. // NOTE: No, really. Presumably this means something different for nonblocking sockets... - Timeout = c.int(os.EWOULDBLOCK), - Socket_Not_Bound = c.int(os.EINVAL), // The socket must be bound for this operation, but isn't. + Timeout = c.int(posix.EWOULDBLOCK), + Socket_Not_Bound = c.int(posix.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(posix.ECONNABORTED), + Connection_Closed = c.int(posix.ECONNRESET), + Not_Connected = c.int(posix.ENOTCONN), + Shutdown = ESHUTDOWN, // The send queue was full. // This is usually a transient issue. // // This also shouldn't normally happen on Linux, as data is dropped if it // doesn't fit in the send queue. - No_Buffer_Space_Available = c.int(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). + No_Buffer_Space_Available = c.int(posix.ENOBUFS), + Offline = c.int(posix.ENETDOWN), + Host_Unreachable = c.int(posix.EHOSTUNREACH), + Interrupted = c.int(posix.EINTR), // A signal occurred before any data was transmitted. See signal(7). // NOTE: No, really. Presumably this means something different for nonblocking sockets... // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout. - Timeout = c.int(os.EWOULDBLOCK), - Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket. + Timeout = c.int(posix.EWOULDBLOCK), + Not_Socket = c.int(posix.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(posix.EMSGSIZE), // The message is larger than the maximum UDP packet size. No data was sent. // TODO: not sure what the exact circumstances for this is yet - Network_Unreachable = c.int(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(posix.ENETUNREACH), + No_Outbound_Ports_Available = c.int(posix.EAGAIN), // There are no more emphemeral outbound ports available to bind the socket to, in order to send. // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout. // NOTE: No, really. Presumably this means something different for nonblocking sockets... - Timeout = c.int(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(posix.EWOULDBLOCK), + Not_Socket = c.int(posix.ENOTSOCK), // The so-called socket is not an open socket. + Not_Descriptor = c.int(posix.EBADF), // The so-called socket is, in fact, not even a valid descriptor. + Bad_Buffer = c.int(posix.EFAULT), // The buffer did not point to a valid location in memory. + Interrupted = c.int(posix.EINTR), // A signal occurred before any data was transmitted. See signal(7). // The send queue was full. // This is usually a transient issue. // // This also shouldn't normally happen on Linux, as data is dropped if it // doesn't fit in the send queue. - No_Buffer_Space_Available = c.int(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(posix.ENOBUFS), + No_Memory_Available = c.int(posix.ENOMEM), // No memory was available to properly manage the send queue. } 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(posix.SHUT_RD), + Send = c.int(posix.SHUT_WR), + Both = c.int(posix.SHUT_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(posix.ECONNABORTED), + Reset = c.int(posix.ECONNRESET), + Offline = c.int(posix.ENETDOWN), + Not_Connected = c.int(posix.ENOTCONN), + Not_Socket = c.int(posix.ENOTSOCK), + Invalid_Manner = c.int(posix.EINVAL), } 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(posix.ENETDOWN), + Timeout_When_Keepalive_Set = c.int(posix.ENETRESET), + Invalid_Option_For_Socket = c.int(posix.ENOPROTOOPT), + Reset_When_Keepalive_Set = c.int(posix.ENOTCONN), + Not_Socket = c.int(posix.ENOTSOCK), } Set_Blocking_Error :: enum c.int { None = 0, // TODO: Add errors for `set_blocking` -} \ No newline at end of file +} diff --git a/core/net/interface_darwin.odin b/core/net/interface_darwin.odin index 4921bc3fe..9aa6cbd1a 100644 --- a/core/net/interface_darwin.odin +++ b/core/net/interface_darwin.odin @@ -20,60 +20,57 @@ package net Feoramund: FreeBSD platform code */ -import "core:os" import "core:strings" +import "core:sys/posix" + +foreign import lib "system:System.framework" @(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 { + head: ^ifaddrs + if getifaddrs(&head) != .OK { return {}, .Unable_To_Enumerate_Network_Interfaces } + defer freeifaddrs(head) - /* - 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 + 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) + key_ptr, iface, inserted, mem_err := map_entry(&ifaces, adapter_name) + if mem_err == nil && inserted { + key_ptr^, mem_err = strings.clone(adapter_name) + iface.adapter_name = key_ptr^ + } + if mem_err != nil { + return {}, .Unable_To_Enumerate_Network_Interfaces } - iface := ifaces[adapter_name] address: Address netmask: Netmask - if ifaddr.address != nil { - switch int(ifaddr.address.family) { - case os.AF_INET, os.AF_INET6: - address = _sockaddr_basic_to_endpoint(ifaddr.address).address + if ifaddr.addr != nil { + #partial switch ifaddr.addr.sa_family { + case .INET, .INET6: + address = _sockaddr_basic_to_endpoint(ifaddr.addr).address } } if ifaddr.netmask != nil { - switch int(ifaddr.netmask.family) { - case os.AF_INET, os.AF_INET6: + #partial switch ifaddr.netmask.sa_family { + case .INET, .INET6: netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address) } } - if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags { - switch int(ifaddr.broadcast_or_dest.family) { - case os.AF_INET, os.AF_INET6: - broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address + if ifaddr.dstaddr != nil && .BROADCAST in ifaddr.flags { + #partial switch ifaddr.dstaddr.sa_family { + case .INET, .INET6: + broadcast := _sockaddr_basic_to_endpoint(ifaddr.dstaddr).address append(&iface.multicast, broadcast) } } @@ -105,18 +102,51 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: [] 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) + interfaces = make([]Network_Interface, len(ifaces)) + i: int for _, iface in ifaces { - append(&_interfaces, iface^) - free(iface) + interfaces[i] = iface + i += 1 } - return _interfaces[:], {} + return interfaces, nil +} + +@(private) +IF_Flag :: enum u32 { + UP, + BROADCAST, + DEBUG, + LOOPBACK, + POINTTOPOINT, + NOTRAILERS, + RUNNING, + NOARP, + PROMISC, + ALLMULTI, + OACTIVE, + SIMPLEX, + LINK0, + LINK1, + LINK2, + MULTICAST, +} + +@(private) +IF_Flags :: bit_set[IF_Flag; u32] + +@(private) +ifaddrs :: struct { + next: ^ifaddrs, + name: cstring, + flags: IF_Flags, + addr: ^posix.sockaddr, + netmask: ^posix.sockaddr, + dstaddr: ^posix.sockaddr, + data: rawptr, +} + +@(private) +foreign lib { + getifaddrs :: proc(ifap: ^^ifaddrs) -> posix.result --- + freeifaddrs :: proc(ifp: ^ifaddrs) --- } diff --git a/core/net/socket_darwin.odin b/core/net/socket_darwin.odin index 27927e973..a132a6a95 100644 --- a/core/net/socket_darwin.odin +++ b/core/net/socket_darwin.odin @@ -21,44 +21,45 @@ package net */ import "core:c" -import "core:os" import "core:sys/posix" import "core:time" Socket_Option :: enum c.int { - Broadcast = c.int(os.SO_BROADCAST), - 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), + Broadcast = c.int(posix.Sock_Option.BROADCAST), + Reuse_Address = c.int(posix.Sock_Option.REUSEADDR), + Keep_Alive = c.int(posix.Sock_Option.KEEPALIVE), + Out_Of_Bounds_Data_Inline = c.int(posix.Sock_Option.OOBINLINE), + TCP_Nodelay = c.int(posix.TCP_NODELAY), + Linger = c.int(posix.Sock_Option.LINGER), + Receive_Buffer_Size = c.int(posix.Sock_Option.RCVBUF), + Send_Buffer_Size = c.int(posix.Sock_Option.SNDBUF), + Receive_Timeout = c.int(posix.Sock_Option.RCVTIMEO), + Send_Timeout = c.int(posix.Sock_Option.SNDTIMEO), } @(private) _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) { - c_type, c_protocol, c_family: int + c_type: posix.Sock + c_protocol: posix.Protocol + c_family: posix.AF switch family { - case .IP4: c_family = os.AF_INET - case .IP6: c_family = os.AF_INET6 + case .IP4: c_family = .INET + case .IP6: c_family = .INET6 case: unreachable() } 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: c_type = .STREAM; c_protocol = .TCP + case .UDP: c_type = .DGRAM; c_protocol = .UDP case: unreachable() } - sock, sock_err := os.socket(c_family, c_type, c_protocol) - if sock_err != nil { - err = Create_Socket_Error(os.is_platform_error(sock_err) or_else -1) + sock := posix.socket(c_family, c_type, c_protocol) + if sock < 0 { + err = Create_Socket_Error(posix.errno()) return } @@ -86,10 +87,10 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio _ = set_option(skt, .Reuse_Address, true) sockaddr := _endpoint_to_sockaddr(endpoint) - res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len)) - if res != nil { + if posix.connect(posix.FD(skt), (^posix.sockaddr)(&sockaddr), posix.socklen_t(sockaddr.ss_len)) != .OK { + errno := posix.errno() close(skt) - return {}, Dial_Error(os.is_platform_error(res) or_else -1) + return {}, Dial_Error(errno) } return @@ -102,14 +103,15 @@ MAX_PRIVILEGED_PORT :: 1023 _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), i32(sockaddr.len)) - if res != nil { - if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT { + if posix.bind(posix.FD(s), (^posix.sockaddr)(&sockaddr), posix.socklen_t(sockaddr.ss_len)) != .OK { + errno := posix.errno() + if errno == .EACCES && ep.port <= MAX_PRIVILEGED_PORT { err = .Privileged_Port_Without_Root } else { - err = Bind_Error(os.is_platform_error(res) or_else -1) + err = Bind_Error(errno) } } + return } @@ -131,9 +133,8 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_ bind(sock, interface_endpoint) or_return - res := os.listen(os.Socket(skt), backlog) - if res != nil { - err = Listen_Error(os.is_platform_error(res) or_else -1) + if posix.listen(posix.FD(skt), i32(backlog)) != .OK { + err = Listen_Error(posix.errno()) return } @@ -144,34 +145,34 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_ _bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error) { addr: posix.sockaddr_storage addr_len := posix.socklen_t(size_of(addr)) - res := posix.getsockname(posix.FD(any_socket_to_socket(sock)), (^posix.sockaddr)(&addr), &addr_len) - if res != .OK { + if posix.getsockname(posix.FD(any_socket_to_socket(sock)), (^posix.sockaddr)(&addr), &addr_len) != .OK { err = Listen_Error(posix.errno()) return } - ep = _sockaddr_to_endpoint((^os.SOCKADDR_STORAGE_LH)(&addr)) + + ep = _sockaddr_to_endpoint(&addr) return } @(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, client_sock_err := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen) - if client_sock_err != nil { - err = Accept_Error(os.is_platform_error(client_sock_err) or_else -1) + addr: posix.sockaddr_storage + addr_len := posix.socklen_t(size_of(addr)) + client_sock := posix.accept(posix.FD(sock), (^posix.sockaddr)(&addr), &addr_len) + if client_sock < 0 { + err = Accept_Error(posix.errno()) return } + client = TCP_Socket(client_sock) - source = _sockaddr_to_endpoint(&sockaddr) + source = _sockaddr_to_endpoint(&addr) return } @(private) _close :: proc(skt: Any_Socket) { s := any_socket_to_socket(skt) - os.close(os.Handle(os.Socket(s))) + posix.close(posix.FD(s)) } @(private) @@ -179,11 +180,13 @@ _recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Networ if len(buf) <= 0 { return } - res, res_err := os.recv(os.Socket(skt), buf, 0) - if res_err != nil { - err = TCP_Recv_Error(os.is_platform_error(res_err) or_else -1) + + res := posix.recv(posix.FD(skt), raw_data(buf), len(buf), {}) + if res < 0 { + err = TCP_Recv_Error(posix.errno()) return } + return int(res), nil } @@ -193,11 +196,11 @@ _recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endp return } - from: os.SOCKADDR_STORAGE_LH - fromsize := c.int(size_of(from)) - res, res_err := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize) - if res_err != nil { - err = UDP_Recv_Error(os.is_platform_error(res_err) or_else -1) + from: posix.sockaddr_storage + fromsize := posix.socklen_t(size_of(from)) + res := posix.recvfrom(posix.FD(skt), raw_data(buf), len(buf), {}, (^posix.sockaddr)(&from), &fromsize) + if res < 0 { + err = UDP_Recv_Error(posix.errno()) return } @@ -211,15 +214,19 @@ _send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Net for bytes_written < len(buf) { limit := min(int(max(i32)), len(buf) - bytes_written) remaining := buf[bytes_written:][:limit] - res, res_err := os.send(os.Socket(skt), remaining, os.MSG_NOSIGNAL) - if res_err == os.EPIPE { - // EPIPE arises if the socket has been closed remotely. - err = TCP_Send_Error.Connection_Closed - return - } else if res_err != nil { - err = TCP_Send_Error(os.is_platform_error(res_err) or_else -1) + res := posix.send(posix.FD(skt), raw_data(remaining), len(remaining), {.NOSIGNAL}) + if res < 0 { + errno := posix.errno() + if errno == .EPIPE { + // EPIPE arises if the socket has been closed remotely. + err = TCP_Send_Error.Connection_Closed + return + } + + err = TCP_Send_Error(errno) return } + bytes_written += int(res) } return @@ -231,15 +238,19 @@ _send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: for bytes_written < len(buf) { limit := min(1<<31, len(buf) - bytes_written) remaining := buf[bytes_written:][:limit] - res, res_err := os.sendto(os.Socket(skt), remaining, os.MSG_NOSIGNAL, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len)) - if res_err == os.EPIPE { - // EPIPE arises if the socket has been closed remotely. - err = UDP_Send_Error.Not_Socket - return - } else if res_err != nil { - err = UDP_Send_Error(os.is_platform_error(res_err) or_else -1) + res := posix.sendto(posix.FD(skt), raw_data(remaining), len(remaining), {.NOSIGNAL}, (^posix.sockaddr)(&toaddr), posix.socklen_t(toaddr.ss_len)) + if res < 0 { + errno := posix.errno() + if errno == .EPIPE { + // EPIPE arises if the socket has been closed remotely. + err = UDP_Send_Error.Not_Socket + return + } + + err = UDP_Send_Error(errno) return } + bytes_written += int(res) } return @@ -248,26 +259,25 @@ _send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: @(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 != nil { - return Shutdown_Error(os.is_platform_error(res) or_else -1) + if posix.shutdown(posix.FD(s), posix.Shut(manner)) != .OK { + err = Shutdown_Error(posix.errno()) } return } @(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 + level := posix.SOL_SOCKET if option != .TCP_Nodelay else posix.IPPROTO_TCP // NOTE(tetra, 2022-02-15): On Linux, you cannot merely give a single byte for a bool; // it _has_ to be a b32. // I haven't tested if you can give more than that. bool_value: b32 - int_value: i32 - timeval_value: os.Timeval + int_value: posix.socklen_t + timeval_value: posix.timeval ptr: rawptr - len: os.socklen_t + len: posix.socklen_t switch option { case @@ -302,8 +312,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc) micros := i64(time.duration_microseconds(t)) - timeval_value.microseconds = int(micros % 1e6) - timeval_value.seconds = (micros - i64(timeval_value.microseconds)) / 1e6 + timeval_value.tv_usec = posix.suseconds_t(micros % 1e6) + timeval_value.tv_sec = posix.time_t(micros - i64(timeval_value.tv_usec)) / 1e6 ptr = &timeval_value len = size_of(timeval_value) @@ -312,12 +322,12 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca .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 = posix.socklen_t((^u8)(&i2)^) + case i16, u16: i2 := i; int_value = posix.socklen_t((^u16)(&i2)^) + case i32, u32: i2 := i; int_value = posix.socklen_t((^u32)(&i2)^) + case i64, u64: i2 := i; int_value = posix.socklen_t((^u64)(&i2)^) + case i128, u128: i2 := i; int_value = posix.socklen_t((^u128)(&i2)^) + case int, uint: i2 := i; int_value = posix.socklen_t((^uint)(&i2)^) case: panic("set_option() value must be an integer here", loc) } @@ -326,9 +336,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca } skt := any_socket_to_socket(s) - res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len) - if res != nil { - return Socket_Option_Error(os.is_platform_error(res) or_else -1) + if posix.setsockopt(posix.FD(skt), i32(level), posix.Sock_Option(option), ptr, len) != .OK { + return Socket_Option_Error(posix.errno()) } return nil @@ -338,42 +347,42 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca _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 != nil { - return Set_Blocking_Error(os.is_platform_error(getfl_err) or_else -1) + flags_ := posix.fcntl(posix.FD(socket), .GETFL, 0) + if flags_ < 0 { + return Set_Blocking_Error(posix.errno()) } + flags := transmute(posix.O_Flags)flags_ 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 != nil { - return Set_Blocking_Error(os.is_platform_error(setfl_err) or_else -1) + if posix.fcntl(posix.FD(socket), .SETFL, flags) < 0 { + return Set_Blocking_Error(posix.errno()) } return nil } @private -_endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: os.SOCKADDR_STORAGE_LH) { +_endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: posix.sockaddr_storage) { switch a in ep.address { case IP4_Address: - (^os.sockaddr_in)(&sockaddr)^ = os.sockaddr_in { + (^posix.sockaddr_in)(&sockaddr)^ = posix.sockaddr_in { sin_port = u16be(ep.port), - sin_addr = transmute(os.in_addr) a, - sin_family = u8(os.AF_INET), - sin_len = size_of(os.sockaddr_in), + sin_addr = transmute(posix.in_addr)a, + sin_family = .INET, + sin_len = size_of(posix.sockaddr_in), } return case IP6_Address: - (^os.sockaddr_in6)(&sockaddr)^ = os.sockaddr_in6 { + (^posix.sockaddr_in6)(&sockaddr)^ = posix.sockaddr_in6 { sin6_port = u16be(ep.port), - sin6_addr = transmute(os.in6_addr) a, - sin6_family = u8(os.AF_INET6), - sin6_len = size_of(os.sockaddr_in6), + sin6_addr = transmute(posix.in6_addr)a, + sin6_family = .INET6, + sin6_len = size_of(posix.sockaddr_in6), } return } @@ -381,21 +390,21 @@ _endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: os.SOCKADDR_STORAGE_LH } @private -_sockaddr_to_endpoint :: proc(native_addr: ^os.SOCKADDR_STORAGE_LH) -> (ep: Endpoint) { - switch native_addr.family { - case u8(os.AF_INET): - addr := cast(^os.sockaddr_in) native_addr +_sockaddr_to_endpoint :: proc(native_addr: ^posix.sockaddr_storage) -> (ep: Endpoint) { + #partial switch native_addr.ss_family { + case .INET: + addr := cast(^posix.sockaddr_in)native_addr port := int(addr.sin_port) ep = Endpoint { - address = IP4_Address(transmute([4]byte) addr.sin_addr), - port = port, + address = IP4_Address(transmute([4]byte)addr.sin_addr), + port = port, } - case u8(os.AF_INET6): - addr := cast(^os.sockaddr_in6) native_addr + case .INET6: + addr := cast(^posix.sockaddr_in6)native_addr port := int(addr.sin6_port) ep = Endpoint { - address = IP6_Address(transmute([8]u16be) addr.sin6_addr), - port = port, + address = IP6_Address(transmute([8]u16be)addr.sin6_addr), + port = port, } case: panic("native_addr is neither IP4 or IP6 address") @@ -404,21 +413,21 @@ _sockaddr_to_endpoint :: proc(native_addr: ^os.SOCKADDR_STORAGE_LH) -> (ep: Endp } @(private) -_sockaddr_basic_to_endpoint :: proc(native_addr: ^os.SOCKADDR) -> (ep: Endpoint) { - switch u16(native_addr.family) { - case u16(os.AF_INET): - addr := cast(^os.sockaddr_in) native_addr +_sockaddr_basic_to_endpoint :: proc(native_addr: ^posix.sockaddr) -> (ep: Endpoint) { + #partial switch native_addr.sa_family { + case .INET: + addr := cast(^posix.sockaddr_in)native_addr port := int(addr.sin_port) ep = Endpoint { - address = IP4_Address(transmute([4]byte) addr.sin_addr), - port = port, + address = IP4_Address(transmute([4]byte)addr.sin_addr), + port = port, } - case u16(os.AF_INET6): - addr := cast(^os.sockaddr_in6) native_addr + case .INET6: + addr := cast(^posix.sockaddr_in6)native_addr port := int(addr.sin6_port) ep = Endpoint { - address = IP6_Address(transmute([8]u16be) addr.sin6_addr), - port = port, + address = IP6_Address(transmute([8]u16be)addr.sin6_addr), + port = port, } case: panic("native_addr is neither IP4 or IP6 address")