Browse Source

Make more private.

Jeroen van Rijn 2 years ago
parent
commit
d5ea492ef5

+ 13 - 7
core/net/addr.odin

@@ -1,3 +1,9 @@
+package net
+/*
+	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+	For other protocols and their features, see subdirectories of this package.
+*/
+
 /*
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -7,15 +13,9 @@
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
-		Jeroen van Rijn: Cross platform unification, code style, IPv4 + IPv6 parsers, documentation.
+		Jeroen van Rijn: Cross platform unification, code style, documentation
 */
 
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
-
 import "core:strconv"
 import "core:strings"
 import "core:fmt"
@@ -738,4 +738,10 @@ parse_ip_component :: proc(input: string, max_value := u64(max(u32)), bases := D
 
 	// If we consumed at least 1 digit byte, `value` *should* continue a valid number in an appropriate base in the allowable range.
 	return value, digit_bytes + prefix_bytes, digit_bytes >= 1
+}
+
+// Returns an address for each interface that can be bound to.
+get_network_interfaces :: proc() -> []Address {
+	// TODO
+	return nil
 }

+ 0 - 71
core/net/addr_darwin.odin

@@ -1,71 +0,0 @@
-/*
-	Copyright 2022 Tetralux        <[email protected]>
-	Copyright 2022 Colin Davidson  <[email protected]>
-	Copyright 2022 Jeroen van Rijn <[email protected]>.
-	Made available under Odin's BSD-3 license.
-
-	List of contributors:
-		Tetralux:        Initial implementation
-		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
-		Jeroen van Rijn: Cross platform unification, code style, documentation
-*/
-
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
-
-import "core:os"
-
-// Returns an address for each interface that can be bound to.
-get_network_interfaces :: proc() -> []Address {
-	// TODO
-	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 = u8(os.AF_INET),
-			sin_len = size_of(os.sockaddr_in),
-		}
-		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 = u8(os.AF_INET6),
-			sin6_len = size_of(os.sockaddr_in6),
-		}
-		return
-	}
-	unreachable()
-}
-
-@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
-		port := int(addr.sin_port)
-		ep = Endpoint {
-			address = IP4_Address(transmute([4]byte) addr.sin_addr),
-			port = port,
-		}
-	case u8(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
-}

+ 0 - 92
core/net/addr_linux.odin

@@ -1,92 +0,0 @@
-/*
-	Copyright 2022 Tetralux        <[email protected]>
-	Copyright 2022 Colin Davidson  <[email protected]>
-	Copyright 2022 Jeroen van Rijn <[email protected]>.
-	Made available under Odin's BSD-3 license.
-
-	List of contributors:
-		Tetralux:        Initial implementation
-		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
-		Jeroen van Rijn: Cross platform unification, code style, documentation
-*/
-
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
-
-import "core:os"
-
-// Returns an address for each interface that can be bound to.
-get_network_interfaces :: proc() -> []Address {
-	// TODO
-	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()
-}
-
-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
-}
-
-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
-}
-
-sockaddr_to_endpoint :: proc { sockaddr_basic_to_endpoint, sockaddr_storage_to_endpoint }

+ 0 - 69
core/net/addr_openbsd.odin

@@ -1,69 +0,0 @@
-/*
-	Copyright 2022 Tetralux        <[email protected]>
-	Copyright 2022 Colin Davidson  <[email protected]>
-	Copyright 2022 Jeroen van Rijn <[email protected]>.
-	Made available under Odin's BSD-3 license.
-
-	List of contributors:
-		Tetralux:        Initial implementation
-		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
-		Jeroen van Rijn: Cross platform unification, code style, documentation
-*/
-
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
-
-import "core:os"
-
-// Returns an address for each interface that can be bound to.
-get_network_interfaces :: proc() -> []Address {
-	// TODO
-	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 = u8(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 = u8(os.AF_INET6),
-		}
-		return
-	}
-	unreachable()
-}
-
-@private
-sockaddr_to_endpoint :: proc(native_addr: ^os.SOCKADDR_STORAGE_LH) -> (ep: Endpoint) {
-	switch native_addr.ss_family {
-	case u8(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 u8(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
-}

+ 0 - 69
core/net/addr_windows.odin

@@ -1,69 +0,0 @@
-/*
-	Copyright 2022 Tetralux        <[email protected]>
-	Copyright 2022 Colin Davidson  <[email protected]>
-	Copyright 2022 Jeroen van Rijn <[email protected]>.
-	Made available under Odin's BSD-3 license.
-
-	List of contributors:
-		Tetralux:        Initial implementation
-		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
-		Jeroen van Rijn: Cross platform unification, code style, documentation
-*/
-
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
-
-import win "core:sys/windows"
-
-// Returns an address for each interface that can be bound to.
-get_network_interfaces :: proc() -> []Address {
-	// TODO
-	return nil
-}
-
-@private
-endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: win.SOCKADDR_STORAGE_LH) {
-	switch a in ep.address {
-	case IP4_Address:
-		(^win.sockaddr_in)(&sockaddr)^ = win.sockaddr_in {
-			sin_port = u16be(win.USHORT(ep.port)),
-			sin_addr = transmute(win.in_addr) a,
-			sin_family = u16(win.AF_INET),
-		}
-		return
-	case IP6_Address:
-		(^win.sockaddr_in6)(&sockaddr)^ = win.sockaddr_in6 {
-			sin6_port = u16be(win.USHORT(ep.port)),
-			sin6_addr = transmute(win.in6_addr) a,
-			sin6_family = u16(win.AF_INET6),
-		}
-		return
-	}
-	unreachable()
-}
-
-@private
-sockaddr_to_endpoint :: proc(native_addr: ^win.SOCKADDR_STORAGE_LH) -> (ep: Endpoint) {
-	switch native_addr.ss_family {
-	case u16(win.AF_INET):
-		addr := cast(^win.sockaddr_in) native_addr
-		port := int(addr.sin_port)
-		ep = Endpoint {
-			address = IP4_Address(transmute([4]byte) addr.sin_addr),
-			port = port,
-		}
-	case u16(win.AF_INET6):
-		addr := cast(^win.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
-}

+ 24 - 12
core/net/dns.odin

@@ -113,6 +113,7 @@ resolve :: proc(hostname_and_maybe_port: string) -> (ep4, ep6: Endpoint, err: Ne
 	}
 	unreachable()
 }
+
 resolve_ip4 :: proc(hostname_and_maybe_port: string) -> (ep4: Endpoint, err: Network_Error) {
 	target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
 	switch t in target {
@@ -139,6 +140,7 @@ resolve_ip4 :: proc(hostname_and_maybe_port: string) -> (ep4: Endpoint, err: Net
 	}
 	unreachable()
 }
+
 resolve_ip6 :: proc(hostname_and_maybe_port: string) -> (ep6: Endpoint, err: Network_Error) {
 	target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
 	switch t in target {
@@ -166,13 +168,18 @@ resolve_ip6 :: proc(hostname_and_maybe_port: string) -> (ep6: Endpoint, err: Net
 	unreachable()
 }
 
-// `get_dns_records` uses OS-specific methods to query DNS records.
-when ODIN_OS == .Windows {
-	get_dns_records_from_os :: get_dns_records_windows
-} else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
-	get_dns_records_from_os :: get_dns_records_unix
-} else {
-	#panic("get_dns_records_from_os not implemented on this OS")
+/*
+	Performs a recursive DNS query for records of a particular type for the hostname using the OS.
+
+	NOTE: This procedure instructs the DNS resolver to recursively perform CNAME requests on our behalf,
+	meaning that DNS queries for a hostname will resolve through CNAME records until an
+	IP address is reached.
+
+	IMPORTANT: This procedure allocates memory for each record returned; deleting just the returned slice is not enough!
+	See `destroy_records`.
+*/
+get_dns_records_from_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
+	return _get_dns_records_os(hostname, type, allocator)
 }
 
 /*
@@ -182,6 +189,9 @@ when ODIN_OS == .Windows {
 	NOTE: This procedure instructs the DNS resolver to recursively perform CNAME requests on our behalf,
 	meaning that DNS queries for a hostname will resolve through CNAME records until an
 	IP address is reached.
+
+	IMPORTANT: This procedure allocates memory for each record returned; deleting just the returned slice is not enough!
+	See `destroy_records`.
 */
 get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type, name_servers: []Endpoint, host_overrides: []DNS_Record, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
 	context.allocator = allocator
@@ -771,14 +781,16 @@ parse_record :: proc(packet: []u8, cur_off: ^int, filter: DNS_Record_Type = nil)
 */
 
 parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, ok: bool) {
-	header_size_bytes :: 12
-	if len(response) < header_size_bytes {
+	context.allocator = allocator
+
+	HEADER_SIZE_BYTES :: 12
+	if len(response) < HEADER_SIZE_BYTES {
 		return
 	}
 
 	_records := make([dynamic]DNS_Record, 0)
 
-	dns_hdr_chunks := mem.slice_data_cast([]u16be, response[:header_size_bytes])
+	dns_hdr_chunks := mem.slice_data_cast([]u16be, response[:HEADER_SIZE_BYTES])
 	hdr := unpack_dns_header(dns_hdr_chunks[0], dns_hdr_chunks[1])
 	if !hdr.is_response {
 		return
@@ -792,7 +804,7 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
 	authority_count := int(dns_hdr_chunks[4])
 	additional_count := int(dns_hdr_chunks[5])
 
-	cur_idx := header_size_bytes
+	cur_idx := HEADER_SIZE_BYTES
 
 	for _ in 0..<question_count {
 		if cur_idx == len(response) {
@@ -846,4 +858,4 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
 	}
 
 	return _records[:], true
-}
+}

+ 9 - 9
core/net/dns_unix.odin

@@ -1,4 +1,10 @@
-//+build linux, darwin, freebsd, openbsd, !windows
+//+build linux, darwin
+package net
+/*
+	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+	For other protocols and their features, see subdirectories of this package.
+*/
+
 /*
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -10,16 +16,10 @@
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
 */
-
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
-
 import "core:strings"
 
-get_dns_records_unix :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
+@(private)
+_get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
 	context.allocator = allocator
 
 	if type != .SRV {

+ 9 - 17
core/net/dns_windows.odin

@@ -1,4 +1,11 @@
 //+build windows
+package net
+
+/*
+	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+	For other protocols and their features, see subdirectories of this package.
+*/
+
 /*
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -11,28 +18,13 @@
 		Jeroen van Rijn: Cross platform unification, code style, documentation
 */
 
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
-
 import "core:strings"
 import "core:mem"
 
 import win "core:sys/windows"
 
-/*
-	Performs a recursive DNS query for records of a particular type for the hostname.
-
-	NOTE: This procedure instructs the DNS resolver to recursively perform CNAME requests on our behalf,
-	meaning that DNS queries for a hostname will resolve through CNAME records until an
-	IP address is reached.
-
-	WARNING: This procedure allocates memory for each record returned; deleting just the returned slice is not enough!
-	See `destroy_records`.
-*/
-get_dns_records_windows :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
+@(private)
+_get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
 	context.allocator = allocator
 
 	host_cstr := strings.clone_to_cstring(hostname, context.temp_allocator)

+ 0 - 186
core/net/errors_openbsd.odin

@@ -1,186 +0,0 @@
-// +build openbsd
-/*
-	Copyright 2022 Tetralux        <[email protected]>
-	Copyright 2022 Colin Davidson  <[email protected]>
-	Copyright 2022 Jeroen van Rijn <[email protected]>.
-	Made available under Odin's BSD-3 license.
-
-	List of contributors:
-		Tetralux:        Initial implementation
-		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
-		Jeroen van Rijn: Cross platform unification, code style, documentation
-*/
-
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-
-
-	IMPORTANT/TODO: This is a carbon copy of `socket_darwin.odin`. Adjust if necessary.
-
-*/
-package net
-
-import "core:c"
-import "core:os"
-
-Create_Socket_Error :: enum c.int {
-	Family_Not_Supported_For_This_Socket = c.int(os.EAFNOSUPPORT),
-	No_Socket_Descriptors_Available      = c.int(os.EMFILE),
-	No_Buffer_Space_Available            = c.int(os.ENOBUFS),
-	No_Memory_Available_Available        = c.int(os.ENOMEM),
-	Protocol_Unsupported_By_System       = c.int(os.EPROTONOSUPPORT),
-	Wrong_Protocol_For_Socket            = c.int(os.EPROTONOSUPPORT),
-	Family_And_Socket_Type_Mismatch      = c.int(os.EPROTONOSUPPORT),
-}
-
-Dial_Error :: enum c.int {
-	Port_Required             = -1,
-	Address_In_Use            = c.int(os.EADDRINUSE),
-	In_Progress               = c.int(os.EINPROGRESS),
-	Cannot_Use_Any_Address    = c.int(os.EADDRNOTAVAIL),
-	Wrong_Family_For_Socket   = c.int(os.EAFNOSUPPORT),
-	Refused                   = c.int(os.ECONNREFUSED),
-	Is_Listening_Socket       = c.int(os.EACCES),
-	Already_Connected         = c.int(os.EISCONN),
-	Network_Unreachable       = c.int(os.ENETUNREACH),  // Device is offline
-	Host_Unreachable          = c.int(os.EHOSTUNREACH), // Remote host cannot be reached
-	No_Buffer_Space_Available = c.int(os.ENOBUFS),
-	Not_Socket                = c.int(os.ENOTSOCK),
-	Timeout                   = c.int(os.ETIMEDOUT),
-
-	// TODO: we may need special handling for this; maybe make a socket a struct with metadata?
-	Would_Block               = c.int(os.EWOULDBLOCK), 
-}
-
-Bind_Error :: enum c.int {
-	Address_In_Use          = c.int(os.EADDRINUSE),    // Another application is currently bound to this endpoint.
-	Given_Nonlocal_Address  = c.int(os.EADDRNOTAVAIL), // The address is not a local address on this machine.
-	Broadcast_Disabled      = c.int(os.EACCES),        // To bind a UDP socket to the broadcast address, the appropriate socket option must be set.
-	Address_Family_Mismatch = c.int(os.EFAULT),        // The address family of the address does not match that of the socket.
-	Already_Bound           = c.int(os.EINVAL),        // The socket is already bound to an address.
-	No_Ports_Available      = c.int(os.ENOBUFS),       // There are not enough ephemeral ports available.
-}
-
-Listen_Error :: enum c.int {
-	Address_In_Use                          = c.int(os.EADDRINUSE),
-	Already_Connected                       = c.int(os.EISCONN),
-	No_Socket_Descriptors_Available         = c.int(os.EMFILE),
-	No_Buffer_Space_Available               = c.int(os.ENOBUFS),
-	Nonlocal_Address                        = c.int(os.EADDRNOTAVAIL),
-	Not_Socket                              = c.int(os.ENOTSOCK),
-	Listening_Not_Supported_For_This_Socket = c.int(os.EOPNOTSUPP),
-}
-
-Accept_Error :: enum c.int {
-
-	// TODO(tetra): Is this error actually possible here? Or is like Linux, in which case we can remove it.
-	Reset                                             = c.int(os.ECONNRESET),
-	Not_Listening                                     = c.int(os.EINVAL),
-	No_Socket_Descriptors_Available_For_Client_Socket = c.int(os.EMFILE),
-	No_Buffer_Space_Available                         = c.int(os.ENOBUFS),
-	Not_Socket                                        = c.int(os.ENOTSOCK),
-	Not_Connection_Oriented_Socket                    = c.int(os.EOPNOTSUPP),
-
-	// TODO: we may need special handling for this; maybe make a socket a struct with metadata?
-	Would_Block                                       = c.int(os.EWOULDBLOCK),
-}
-
-TCP_Recv_Error :: enum c.int {
-	Shutdown          = c.int(os.ESHUTDOWN),
-	Not_Connected     = c.int(os.ENOTCONN),
-
-	// TODO(tetra): Is this error actually possible here?
-	Connection_Broken = c.int(os.ENETRESET), 
-	Not_Socket        = c.int(os.ENOTSOCK),
-	Aborted           = c.int(os.ECONNABORTED),
-
-	// TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them?
-	Connection_Closed = c.int(os.ECONNRESET),
-	Offline           = c.int(os.ENETDOWN),
-	Host_Unreachable  = c.int(os.EHOSTUNREACH),
-	Interrupted       = c.int(os.EINTR),
-	Timeout           = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets...
-}
-
-UDP_Recv_Error :: enum c.int {
-	Truncated        = c.int(os.EMSGSIZE), // The buffer is too small to fit the entire message, and the message was truncated.
-	Not_Socket       = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
-	Not_Descriptor   = c.int(os.EBADF),    // The so-called socket is, in fact, not even a valid descriptor.
-	Bad_Buffer       = c.int(os.EFAULT),   // The buffer did not point to a valid location in memory.
-	Interrupted      = c.int(os.EINTR),    // A signal occurred before any data was transmitted. See signal(7).
-
-	// The send timeout duration passed before all data was sent.
-	// See Socket_Option.Send_Timeout.
-	// NOTE: No, really. Presumably this means something different for nonblocking sockets...
-	Timeout          = c.int(os.EWOULDBLOCK), 
-	Socket_Not_Bound = c.int(os.EINVAL),   // The socket must be bound for this operation, but isn't.
-}
-
-// TODO
-TCP_Send_Error :: enum c.int {
-
-	// TODO: merge with other errors?
-	Aborted                   = c.int(os.ECONNABORTED),
-	Connection_Closed         = c.int(os.ECONNRESET),
-	Not_Connected             = c.int(os.ENOTCONN),
-	Shutdown                  = c.int(os.ESHUTDOWN),
-
-	// The send queue was full.
-	// This is usually a transient issue.
-	//
-	// This also shouldn't normally happen on Linux, as data is dropped if it
-	// doesn't fit in the send queue.
-	No_Buffer_Space_Available = c.int(os.ENOBUFS),
-	Offline                   = c.int(os.ENETDOWN),
-	Host_Unreachable          = c.int(os.EHOSTUNREACH),
-	Interrupted               = c.int(os.EINTR), // A signal occurred before any data was transmitted. See signal(7).
-
-	// The send timeout duration passed before all data was sent.
-	// See Socket_Option.Send_Timeout.
-	// NOTE: No, really. Presumably this means something different for nonblocking sockets...
-	Timeout                   = c.int(os.EWOULDBLOCK),
-}
-
-// TODO
-UDP_Send_Error :: enum c.int {
-	Truncated                   = c.int(os.EMSGSIZE), // The message is too big. No data was sent.
-
-	// TODO: not sure what the exact circumstances for this is yet
-	Network_Unreachable         = c.int(os.ENETUNREACH),
-	No_Outbound_Ports_Available = c.int(os.EAGAIN), // There are no more emphemeral outbound ports available to bind the socket to, in order to send.
-
-	// The send timeout duration passed before all data was sent.
-	// See Socket_Option.Send_Timeout.
-	// NOTE: No, really. Presumably this means something different for nonblocking sockets...
-	Timeout                    = c.int(os.EWOULDBLOCK), 
-	Not_Socket                 = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
-	Not_Descriptor             = c.int(os.EBADF),    // The so-called socket is, in fact, not even a valid descriptor.
-	Bad_Buffer                 = c.int(os.EFAULT),   // The buffer did not point to a valid location in memory.
-	Interrupted                = c.int(os.EINTR),    // A signal occurred before any data was transmitted. See signal(7).
-
-	// The send queue was full.
-	// This is usually a transient issue.
-	//
-	// This also shouldn't normally happen on Linux, as data is dropped if it
-	// doesn't fit in the send queue.
-	No_Buffer_Space_Available  = c.int(os.ENOBUFS),
-	No_Memory_Available        = c.int(os.ENOMEM), // No memory was available to properly manage the send queue.
-}
-
-Shutdown_Error :: enum c.int {
-	Aborted        = c.int(os.ECONNABORTED),
-	Reset          = c.int(os.ECONNRESET),
-	Offline        = c.int(os.ENETDOWN),
-	Not_Connected  = c.int(os.ENOTCONN),
-	Not_Socket     = c.int(os.ENOTSOCK),
-	Invalid_Manner = c.int(os.EINVAL),
-}
-
-Socket_Option_Error :: enum c.int {
-	Offline                    = c.int(os.ENETDOWN),
-	Timeout_When_Keepalive_Set = c.int(os.ENETRESET),
-	Invalid_Option_For_Socket  = c.int(os.ENOPROTOOPT),
-	Reset_When_Keepalive_Set   = c.int(os.ENOTCONN),
-	Not_Socket                 = c.int(os.ENOTSOCK),
-}

+ 7 - 7
core/net/interface_darwin.odin

@@ -1,4 +1,11 @@
+package net
 //+build darwin
+
+/*
+	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+	For other protocols and their features, see subdirectories of this package.
+*/
+
 /*
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -9,15 +16,8 @@
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
-*/
 
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
 
-/*
 	TODO: Implement. Can probably use the (current) Linux implementation,
 	which will itself be switched over to talking to the kernel via NETLINK protocol once we have raw sockets.
 */

+ 11 - 13
core/net/interface_linux.odin

@@ -1,4 +1,11 @@
-//+build linux, darwin, openbsd, !windows
+package net
+//+build linux
+
+/*
+	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+	For other protocols and their features, see subdirectories of this package.
+*/
+
 /*
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -9,17 +16,8 @@
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
-*/
 
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
-
-/*
 	This file uses `getifaddrs` libc call to enumerate interfaces.
-
 	TODO: When we have raw sockets, split off into its own file for Linux so we can use the NETLINK protocol and bypass libc.
 */
 
@@ -65,7 +63,7 @@ enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []N
 		if ifaddr.address != nil {
 			switch int(ifaddr.address.sa_family) {
 			case os.AF_INET, os.AF_INET6:
-				address = sockaddr_to_endpoint(ifaddr.address).address
+				address = _sockaddr_basic_to_endpoint(ifaddr.address).address
 
 			case os.AF_PACKET:
 				/*
@@ -87,7 +85,7 @@ enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []N
 		if ifaddr.netmask != nil {
 			switch int(ifaddr.netmask.sa_family) {
 			case os.AF_INET, os.AF_INET6:
-			 	netmask = Netmask(sockaddr_to_endpoint(ifaddr.netmask).address)
+			 	netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
 			case:
 			}
 		}
@@ -95,7 +93,7 @@ enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []N
 		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_to_endpoint(ifaddr.broadcast_or_dest).address
+			 	broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
 			 	append(&iface.multicast, broadcast)
 			case:
 			}

+ 58 - 11
core/net/socket_darwin.odin

@@ -1,4 +1,11 @@
+package net
 // +build darwin
+
+/*
+	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+	For other protocols and their features, see subdirectories of this package.
+*/
+
 /*
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -11,12 +18,6 @@
 		Jeroen van Rijn: Cross platform unification, code style, documentation
 */
 
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
-
 import "core:c"
 import "core:os"
 import "core:time"
@@ -68,7 +69,7 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option
 	// use the same address immediately.
 	_ = set_option(skt, .Reuse_Address, true)
 
-	sockaddr := endpoint_to_sockaddr(endpoint)
+	sockaddr := _endpoint_to_sockaddr(endpoint)
 	res := os.connect(Platform_Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
 	if res != os.ERROR_NONE {
 		err = Dial_Error(res)
@@ -79,7 +80,7 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option
 }
 
 bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
-	sockaddr := endpoint_to_sockaddr(ep)
+	sockaddr := _endpoint_to_sockaddr(ep)
 	s := any_socket_to_socket(skt)
 	res := os.bind(Platform_Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
 	if res != os.ERROR_NONE {
@@ -145,7 +146,7 @@ accept_tcp :: proc(sock: TCP_Socket) -> (client: TCP_Socket, source: Endpoint, e
 		return
 	}
 	client = TCP_Socket(client_sock)
-	source = sockaddr_to_endpoint(&sockaddr)
+	source = _sockaddr_to_endpoint(&sockaddr)
 	return
 }
 
@@ -180,7 +181,7 @@ recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpo
 	}
 
 	bytes_read = int(res)
-	remote_endpoint = sockaddr_to_endpoint(&from)
+	remote_endpoint = _sockaddr_to_endpoint(&from)
 	return
 }
 
@@ -204,7 +205,7 @@ send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Netw
 }
 
 send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
-	toaddr := endpoint_to_sockaddr(to)
+	toaddr := _endpoint_to_sockaddr(to)
 	for bytes_written < len(buf) {
 		limit := min(1<<31, len(buf) - bytes_written)
 		remaining := buf[bytes_written:][:limit]
@@ -324,3 +325,49 @@ set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #cal
 
 	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 = u8(os.AF_INET),
+			sin_len = size_of(os.sockaddr_in),
+		}
+		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 = u8(os.AF_INET6),
+			sin6_len = size_of(os.sockaddr_in6),
+		}
+		return
+	}
+	unreachable()
+}
+
+@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
+		port := int(addr.sin_port)
+		ep = Endpoint {
+			address = IP4_Address(transmute([4]byte) addr.sin_addr),
+			port = port,
+		}
+	case u8(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
+}

+ 79 - 11
core/net/socket_linux.odin

@@ -1,4 +1,11 @@
+package net
 // +build linux
+
+/*
+	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+	For other protocols and their features, see subdirectories of this package.
+*/
+
 /*
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -11,12 +18,6 @@
 		Jeroen van Rijn: Cross platform unification, code style, documentation
 */
 
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
-
 import "core:c"
 import "core:os"
 import "core:time"
@@ -68,7 +69,7 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option
 	// use the same address immediately.
 	_ = set_option(skt, .Reuse_Address, true)
 
-	sockaddr := endpoint_to_sockaddr(endpoint)
+	sockaddr := _endpoint_to_sockaddr(endpoint)
 	res := os.connect(Platform_Socket(skt), (^os.SOCKADDR)(&sockaddr), size_of(sockaddr))
 	if res != os.ERROR_NONE {
 		err = Dial_Error(res)
@@ -84,7 +85,7 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option
 
 
 bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
-	sockaddr := endpoint_to_sockaddr(ep)
+	sockaddr := _endpoint_to_sockaddr(ep)
 	s := any_socket_to_socket(skt)
 	res := os.bind(Platform_Socket(s), (^os.SOCKADDR)(&sockaddr), size_of(sockaddr))
 	if res != os.ERROR_NONE {
@@ -152,7 +153,7 @@ accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client:
 		return
 	}
 	client = TCP_Socket(client_sock)
-	source = sockaddr_to_endpoint(&sockaddr)
+	source = _sockaddr_storage_to_endpoint(&sockaddr)
 	if options.no_delay {
 		_ = set_option(client, .TCP_Nodelay, true) // NOTE(tetra): Not vital to succeed; error ignored
 	}
@@ -197,7 +198,7 @@ recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpo
 	}
 
 	bytes_read = int(res)
-	remote_endpoint = sockaddr_to_endpoint(&from)
+	remote_endpoint = _sockaddr_storage_to_endpoint(&from)
 
 	if bytes_read > len(buf) {
 		// NOTE(tetra): The buffer has been filled, with a partial message.
@@ -233,7 +234,7 @@ send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Netw
 // Datagrams are limited in size; attempting to send more than this limit at once will result in a Message_Too_Long error.
 // UDP packets are not guarenteed to be received in order.
 send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
-	toaddr := endpoint_to_sockaddr(to)
+	toaddr := _endpoint_to_sockaddr(to)
 	res, os_err := os.sendto(Platform_Socket(skt), buf, 0, cast(^os.SOCKADDR) &toaddr, size_of(toaddr))
 	if os_err != os.ERROR_NONE {
 		err = UDP_Send_Error(os_err)
@@ -349,3 +350,70 @@ set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #cal
 
 	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
+}

+ 0 - 331
core/net/socket_openbsd.odin

@@ -1,331 +0,0 @@
-// +build openbsd
-/*
-	Copyright 2022 Tetralux        <[email protected]>
-	Copyright 2022 Colin Davidson  <[email protected]>
-	Copyright 2022 Jeroen van Rijn <[email protected]>.
-	Made available under Odin's BSD-3 license.
-
-	List of contributors:
-		Tetralux:        Initial implementation
-		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
-		Jeroen van Rijn: Cross platform unification, code style, documentation
-*/
-
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-
-
-	IMPORTANT/TODO: This is a carbon copy of `socket_darwin.odin`. Adjust if necessary.
-
-*/
-package net
-
-import "core:c"
-import "core:os"
-import "core:time"
-
-Platform_Socket :: os.Socket
-
-create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
-	c_type, c_protocol, c_family: int
-
-	switch family {
-	case .IP4:  c_family = os.AF_INET
-	case .IP6:  c_family = os.AF_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:
-		unreachable()
-	}
-
-	sock, ok := os.socket(c_family, c_type, c_protocol)
-	if ok != os.ERROR_NONE {
-		err = Create_Socket_Error(ok)
-		return
-	}
-
-	switch protocol {
-	case .TCP:  return TCP_Socket(sock), nil
-	case .UDP:  return UDP_Socket(sock), nil
-	case:
-		unreachable()
-	}
-}
-
-dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (skt: TCP_Socket, err: Network_Error) {
-	if endpoint.port == 0 {
-		return 0, .Port_Required
-	}
-
-	family := family_from_endpoint(endpoint)
-	sock := create_socket(family, .TCP) or_return
-	skt = sock.(TCP_Socket)
-
-	// 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(Platform_Socket(skt), (^os.SOCKADDR)(&sockaddr), size_of(sockaddr))
-	if res != os.ERROR_NONE {
-		err = Dial_Error(res)
-		return
-	}
-
-	return
-}
-
-bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
-	sockaddr := endpoint_to_sockaddr(ep)
-	s := any_socket_to_socket(skt)
-	res := os.bind(Platform_Socket(s), (^os.SOCKADDR)(&sockaddr), size_of(sockaddr))
-	if res != os.ERROR_NONE {
-		err = Bind_Error(res)
-	}
-	return
-}
-
-
-// This type of socket becomes bound when you try to send data.
-// This is likely what you want if you want to send data unsolicited.
-//
-// This is like a client TCP socket, except that it can send data to any remote endpoint without needing to establish a connection first.
-make_unbound_udp_socket :: proc(family: Address_Family) -> (skt: UDP_Socket, err: Network_Error) {
-	sock := create_socket(family, .UDP) or_return
-	skt = sock.(UDP_Socket)
-	return
-}
-
-// This type of socket is bound immediately, which enables it to receive data on the port.
-// Since it's UDP, it's also able to send data without receiving any first.
-//
-// This is like a listening TCP socket, except that data packets can be sent and received without needing to establish a connection first.
-//
-// The bound_address is the address of the network interface that you want to use, or a loopback address if you don't care which to use.
-make_bound_udp_socket :: proc(bound_address: Address, port: int) -> (skt: UDP_Socket, err: Network_Error) {
-	skt = make_unbound_udp_socket(family_from_address(bound_address)) or_return
-	bind(skt, {bound_address, port}) or_return
-	return
-}
-
-listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_Socket, err: Network_Error) {
-	assert(backlog > 0 && i32(backlog) < max(i32))
-
-	family := family_from_endpoint(interface_endpoint)
-	sock := create_socket(family, .TCP) or_return
-	skt = sock.(TCP_Socket)
-
-	// 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(Platform_Socket(skt), backlog)
-	if res != os.ERROR_NONE {
-		err = Listen_Error(res)
-		return
-	}
-
-	return
-}
-
-accept_tcp :: proc(sock: TCP_Socket) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
-	sockaddr: os.SOCKADDR_STORAGE_LH
-	sockaddrlen := c.int(size_of(sockaddr))
-
-	client_sock, ok := os.accept(Platform_Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
-	if ok != os.ERROR_NONE {
-		err = Accept_Error(ok)
-		return
-	}
-	client = TCP_Socket(client_sock)
-	source = sockaddr_to_endpoint(&sockaddr)
-	return
-}
-
-close :: proc(skt: Any_Socket) {
-	s := any_socket_to_socket(skt)
-	os.close(os.Handle(Platform_Socket(s)))
-}
-
-recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
-	if len(buf) <= 0 {
-		return
-	}
-	res, ok := os.recv(Platform_Socket(skt), buf, 0)
-	if ok != os.ERROR_NONE {
-		err = TCP_Recv_Error(ok)
-		return
-	}
-	return int(res), nil
-}
-
-recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
-	if len(buf) <= 0 {
-		return
-	}
-
-	from: os.SOCKADDR_STORAGE_LH
-	fromsize := c.int(size_of(from))
-	res, ok := os.recvfrom(Platform_Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
-	if ok != os.ERROR_NONE {
-		err = UDP_Recv_Error(ok)
-		return
-	}
-
-	bytes_read = int(res)
-	remote_endpoint = sockaddr_to_endpoint(&from)
-	return
-}
-
-recv :: proc{recv_tcp, recv_udp}
-
-// Repeatedly sends data until the entire buffer is sent.
-// If a send fails before all data is sent, returns the amount
-// sent up to that point.
-send_tcp :: proc(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(Platform_Socket(skt), remaining, 0)
-		if ok != os.ERROR_NONE {
-			err = TCP_Send_Error(ok)
-			return
-		}
-		bytes_written += int(res)
-	}
-	return
-}
-
-send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
-	toaddr := endpoint_to_sockaddr(to)
-	for bytes_written < len(buf) {
-		limit := min(1<<31, len(buf) - bytes_written)
-		remaining := buf[bytes_written:][:limit]
-		res, ok := os.sendto(Platform_Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, size_of(toaddr))
-		if ok != os.ERROR_NONE {
-			err = UDP_Send_Error(ok)
-			return
-		}
-		bytes_written += int(res)
-	}
-	return
-}
-
-send :: proc{send_tcp, send_udp}
-
-Shutdown_Manner :: enum c.int {
-	Receive = c.int(os.SHUT_RD),
-	Send    = c.int(os.SHUT_WR),
-	Both    = c.int(os.SHUT_RDWR),
-}
-
-shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
-	s := any_socket_to_socket(skt)
-	res := os.shutdown(Platform_Socket(s), int(manner))
-	if res != os.ERROR_NONE {
-		return Shutdown_Error(res)
-	}
-	return
-}
-
-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),
-	Send_Timeout              = c.int(os.SO_SNDTIMEO),
-}
-
-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
-
-	// 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
-
-	ptr: rawptr
-	len: os.socklen_t
-
-	switch option {
-	case
-		.Reuse_Address,
-		.Keep_Alive,
-		.Out_Of_Bounds_Data_Inline,
-		.TCP_Nodelay:
-		// TODO: verify whether these are options or not on Linux
-		// .Broadcast,
-		// .Conditional_Accept,
-		// .Dont_Linger:
-			switch x in value {
-			case bool, b8:
-				x2 := x
-				bool_value = b32((^bool)(&x2)^)
-			case b16:
-				bool_value = b32(x)
-			case b32:
-				bool_value = b32(x)
-			case b64:
-				bool_value = b32(x)
-			case:
-				panic("set_option() value must be a boolean here", loc)
-			}
-			ptr = &bool_value
-			len = size_of(bool_value)
-	case
-		.Linger,
-		.Send_Timeout,
-		.Receive_Timeout:
-			t, ok := value.(time.Duration)
-			if !ok do panic("set_option() value must be a time.Duration here", loc)
-
-			nanos := time.duration_nanoseconds(t)
-			timeval_value.nanoseconds = int(nanos % 1e9)
-			timeval_value.seconds = (nanos - i64(timeval_value.nanoseconds)) / 1e9
-
-			ptr = &timeval_value
-			len = size_of(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:
-				panic("set_option() value must be an integer here", loc)
-			}
-			ptr = &int_value
-			len = size_of(int_value)
-	}
-
-	skt := any_socket_to_socket(s)
-	res := os.setsockopt(Platform_Socket(skt), int(level), int(option), ptr, len)
-	if res != os.ERROR_NONE {
-		return Socket_Option_Error(res)
-	}
-
-	return nil
-}

+ 50 - 5
core/net/socket_windows.odin

@@ -74,7 +74,7 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option
 	// use the same address immediately.
 	_ = set_option(skt, .Reuse_Address, true)
 
-	sockaddr := endpoint_to_sockaddr(endpoint)
+	sockaddr := _endpoint_to_sockaddr(endpoint)
 	res := win.connect(Platform_Socket(skt), &sockaddr, size_of(sockaddr))
 	if res < 0 {
 		err = Dial_Error(win.WSAGetLastError())
@@ -89,7 +89,7 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option
 }
 
 bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
-	sockaddr := endpoint_to_sockaddr(ep)
+	sockaddr := _endpoint_to_sockaddr(ep)
 	s := any_socket_to_socket(skt)
 	res := win.bind(Platform_Socket(s), &sockaddr, size_of(sockaddr))
 	if res < 0 {
@@ -161,7 +161,7 @@ accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client:
 			return
 		}
 		client = TCP_Socket(client_sock)
-		source = sockaddr_to_endpoint(&sockaddr)
+		source = _sockaddr_to_endpoint(&sockaddr)
 		if options.no_delay {
 			_ = set_option(client, .TCP_Nodelay, true) // NOTE(tetra): Not vital to succeed; error ignored
 		}
@@ -201,7 +201,7 @@ recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpo
 	}
 
 	bytes_read = int(res)
-	remote_endpoint = sockaddr_to_endpoint(&from)
+	remote_endpoint = _sockaddr_to_endpoint(&from)
 	return
 }
 
@@ -235,7 +235,7 @@ send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written:
 		err = .Message_Too_Long
 		return
 	}
-	toaddr := endpoint_to_sockaddr(to)
+	toaddr := _endpoint_to_sockaddr(to)
 	res := win.sendto(Platform_Socket(skt), raw_data(buf), c.int(len(buf)), 0, &toaddr, size_of(toaddr))
 	if res < 0 {
 		err = UDP_Send_Error(win.WSAGetLastError())
@@ -338,3 +338,48 @@ set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #cal
 
 	return nil
 }
+
+
+@(private)
+_endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: win.SOCKADDR_STORAGE_LH) {
+	switch a in ep.address {
+	case IP4_Address:
+		(^win.sockaddr_in)(&sockaddr)^ = win.sockaddr_in {
+			sin_port = u16be(win.USHORT(ep.port)),
+			sin_addr = transmute(win.in_addr) a,
+			sin_family = u16(win.AF_INET),
+		}
+		return
+	case IP6_Address:
+		(^win.sockaddr_in6)(&sockaddr)^ = win.sockaddr_in6 {
+			sin6_port = u16be(win.USHORT(ep.port)),
+			sin6_addr = transmute(win.in6_addr) a,
+			sin6_family = u16(win.AF_INET6),
+		}
+		return
+	}
+	unreachable()
+}
+
+@(private)
+_sockaddr_to_endpoint :: proc(native_addr: ^win.SOCKADDR_STORAGE_LH) -> (ep: Endpoint) {
+	switch native_addr.ss_family {
+	case u16(win.AF_INET):
+		addr := cast(^win.sockaddr_in) native_addr
+		port := int(addr.sin_port)
+		ep = Endpoint {
+			address = IP4_Address(transmute([4]byte) addr.sin_addr),
+			port = port,
+		}
+	case u16(win.AF_INET6):
+		addr := cast(^win.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
+}

+ 6 - 6
core/net/url.odin

@@ -1,3 +1,9 @@
+package net
+/*
+	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+	For other protocols and their features, see subdirectories of this package.
+*/
+
 /*
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -10,12 +16,6 @@
 		Jeroen van Rijn: Cross platform unification, code style, documentation
 */
 
-/*
-	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
-	For other protocols and their features, see subdirectories of this package.
-*/
-package net
-
 import "core:strings"
 import "core:strconv"
 import "core:unicode/utf8"