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 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -7,15 +13,9 @@
 	List of contributors:
 	List of contributors:
 		Tetralux:        Initial implementation
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		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:strconv"
 import "core:strings"
 import "core:strings"
 import "core:fmt"
 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.
 	// 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
 	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()
 	unreachable()
 }
 }
+
 resolve_ip4 :: proc(hostname_and_maybe_port: string) -> (ep4: Endpoint, err: Network_Error) {
 resolve_ip4 :: proc(hostname_and_maybe_port: string) -> (ep4: Endpoint, err: Network_Error) {
 	target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
 	target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
 	switch t in target {
 	switch t in target {
@@ -139,6 +140,7 @@ resolve_ip4 :: proc(hostname_and_maybe_port: string) -> (ep4: Endpoint, err: Net
 	}
 	}
 	unreachable()
 	unreachable()
 }
 }
+
 resolve_ip6 :: proc(hostname_and_maybe_port: string) -> (ep6: Endpoint, err: Network_Error) {
 resolve_ip6 :: proc(hostname_and_maybe_port: string) -> (ep6: Endpoint, err: Network_Error) {
 	target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
 	target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
 	switch t in target {
 	switch t in target {
@@ -166,13 +168,18 @@ resolve_ip6 :: proc(hostname_and_maybe_port: string) -> (ep6: Endpoint, err: Net
 	unreachable()
 	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,
 	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
 	meaning that DNS queries for a hostname will resolve through CNAME records until an
 	IP address is reached.
 	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) {
 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
 	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) {
 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
 		return
 	}
 	}
 
 
 	_records := make([dynamic]DNS_Record, 0)
 	_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])
 	hdr := unpack_dns_header(dns_hdr_chunks[0], dns_hdr_chunks[1])
 	if !hdr.is_response {
 	if !hdr.is_response {
 		return
 		return
@@ -792,7 +804,7 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
 	authority_count := int(dns_hdr_chunks[4])
 	authority_count := int(dns_hdr_chunks[4])
 	additional_count := int(dns_hdr_chunks[5])
 	additional_count := int(dns_hdr_chunks[5])
 
 
-	cur_idx := header_size_bytes
+	cur_idx := HEADER_SIZE_BYTES
 
 
 	for _ in 0..<question_count {
 	for _ in 0..<question_count {
 		if cur_idx == len(response) {
 		if cur_idx == len(response) {
@@ -846,4 +858,4 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
 	}
 	}
 
 
 	return _records[:], true
 	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 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -10,16 +16,10 @@
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, 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:strings"
 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
 	context.allocator = allocator
 
 
 	if type != .SRV {
 	if type != .SRV {

+ 9 - 17
core/net/dns_windows.odin

@@ -1,4 +1,11 @@
 //+build windows
 //+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 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -11,28 +18,13 @@
 		Jeroen van Rijn: Cross platform unification, code style, 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:strings"
 import "core:strings"
 import "core:mem"
 import "core:mem"
 
 
 import win "core:sys/windows"
 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
 	context.allocator = allocator
 
 
 	host_cstr := strings.clone_to_cstring(hostname, context.temp_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
 //+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 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -9,15 +16,8 @@
 		Tetralux:        Initial implementation
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, 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
 
 
-/*
 	TODO: Implement. Can probably use the (current) Linux implementation,
 	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.
 	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 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -9,17 +16,8 @@
 		Tetralux:        Initial implementation
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, 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
-
-/*
 	This file uses `getifaddrs` libc call to enumerate interfaces.
 	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.
 	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 {
 		if ifaddr.address != nil {
 			switch int(ifaddr.address.sa_family) {
 			switch int(ifaddr.address.sa_family) {
 			case os.AF_INET, os.AF_INET6:
 			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:
 			case os.AF_PACKET:
 				/*
 				/*
@@ -87,7 +85,7 @@ enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []N
 		if ifaddr.netmask != nil {
 		if ifaddr.netmask != nil {
 			switch int(ifaddr.netmask.sa_family) {
 			switch int(ifaddr.netmask.sa_family) {
 			case os.AF_INET, os.AF_INET6:
 			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:
 			case:
 			}
 			}
 		}
 		}
@@ -95,7 +93,7 @@ enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []N
 		if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
 		if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
 			switch int(ifaddr.broadcast_or_dest.sa_family) {
 			switch int(ifaddr.broadcast_or_dest.sa_family) {
 			case os.AF_INET, os.AF_INET6:
 			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)
 			 	append(&iface.multicast, broadcast)
 			case:
 			case:
 			}
 			}

+ 58 - 11
core/net/socket_darwin.odin

@@ -1,4 +1,11 @@
+package net
 // +build darwin
 // +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 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -11,12 +18,6 @@
 		Jeroen van Rijn: Cross platform unification, code style, 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:c"
 import "core:c"
 import "core:os"
 import "core:os"
 import "core:time"
 import "core:time"
@@ -68,7 +69,7 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option
 	// use the same address immediately.
 	// use the same address immediately.
 	_ = set_option(skt, .Reuse_Address, true)
 	_ = 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))
 	res := os.connect(Platform_Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
 	if res != os.ERROR_NONE {
 	if res != os.ERROR_NONE {
 		err = Dial_Error(res)
 		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) {
 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)
 	s := any_socket_to_socket(skt)
 	res := os.bind(Platform_Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
 	res := os.bind(Platform_Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
 	if res != os.ERROR_NONE {
 	if res != os.ERROR_NONE {
@@ -145,7 +146,7 @@ accept_tcp :: proc(sock: TCP_Socket) -> (client: TCP_Socket, source: Endpoint, e
 		return
 		return
 	}
 	}
 	client = TCP_Socket(client_sock)
 	client = TCP_Socket(client_sock)
-	source = sockaddr_to_endpoint(&sockaddr)
+	source = _sockaddr_to_endpoint(&sockaddr)
 	return
 	return
 }
 }
 
 
@@ -180,7 +181,7 @@ recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpo
 	}
 	}
 
 
 	bytes_read = int(res)
 	bytes_read = int(res)
-	remote_endpoint = sockaddr_to_endpoint(&from)
+	remote_endpoint = _sockaddr_to_endpoint(&from)
 	return
 	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) {
 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) {
 	for bytes_written < len(buf) {
 		limit := min(1<<31, len(buf) - bytes_written)
 		limit := min(1<<31, len(buf) - bytes_written)
 		remaining := buf[bytes_written:][:limit]
 		remaining := buf[bytes_written:][:limit]
@@ -324,3 +325,49 @@ set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #cal
 
 
 	return nil
 	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
 // +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 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -11,12 +18,6 @@
 		Jeroen van Rijn: Cross platform unification, code style, 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:c"
 import "core:c"
 import "core:os"
 import "core:os"
 import "core:time"
 import "core:time"
@@ -68,7 +69,7 @@ dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_option
 	// use the same address immediately.
 	// use the same address immediately.
 	_ = set_option(skt, .Reuse_Address, true)
 	_ = 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))
 	res := os.connect(Platform_Socket(skt), (^os.SOCKADDR)(&sockaddr), size_of(sockaddr))
 	if res != os.ERROR_NONE {
 	if res != os.ERROR_NONE {
 		err = Dial_Error(res)
 		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) {
 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)
 	s := any_socket_to_socket(skt)
 	res := os.bind(Platform_Socket(s), (^os.SOCKADDR)(&sockaddr), size_of(sockaddr))
 	res := os.bind(Platform_Socket(s), (^os.SOCKADDR)(&sockaddr), size_of(sockaddr))
 	if res != os.ERROR_NONE {
 	if res != os.ERROR_NONE {
@@ -152,7 +153,7 @@ accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client:
 		return
 		return
 	}
 	}
 	client = TCP_Socket(client_sock)
 	client = TCP_Socket(client_sock)
-	source = sockaddr_to_endpoint(&sockaddr)
+	source = _sockaddr_storage_to_endpoint(&sockaddr)
 	if options.no_delay {
 	if options.no_delay {
 		_ = set_option(client, .TCP_Nodelay, true) // NOTE(tetra): Not vital to succeed; error ignored
 		_ = 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)
 	bytes_read = int(res)
-	remote_endpoint = sockaddr_to_endpoint(&from)
+	remote_endpoint = _sockaddr_storage_to_endpoint(&from)
 
 
 	if bytes_read > len(buf) {
 	if bytes_read > len(buf) {
 		// NOTE(tetra): The buffer has been filled, with a partial message.
 		// 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.
 // 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.
 // 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) {
 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))
 	res, os_err := os.sendto(Platform_Socket(skt), buf, 0, cast(^os.SOCKADDR) &toaddr, size_of(toaddr))
 	if os_err != os.ERROR_NONE {
 	if os_err != os.ERROR_NONE {
 		err = UDP_Send_Error(os_err)
 		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
 	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.
 	// use the same address immediately.
 	_ = set_option(skt, .Reuse_Address, true)
 	_ = 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))
 	res := win.connect(Platform_Socket(skt), &sockaddr, size_of(sockaddr))
 	if res < 0 {
 	if res < 0 {
 		err = Dial_Error(win.WSAGetLastError())
 		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) {
 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)
 	s := any_socket_to_socket(skt)
 	res := win.bind(Platform_Socket(s), &sockaddr, size_of(sockaddr))
 	res := win.bind(Platform_Socket(s), &sockaddr, size_of(sockaddr))
 	if res < 0 {
 	if res < 0 {
@@ -161,7 +161,7 @@ accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client:
 			return
 			return
 		}
 		}
 		client = TCP_Socket(client_sock)
 		client = TCP_Socket(client_sock)
-		source = sockaddr_to_endpoint(&sockaddr)
+		source = _sockaddr_to_endpoint(&sockaddr)
 		if options.no_delay {
 		if options.no_delay {
 			_ = set_option(client, .TCP_Nodelay, true) // NOTE(tetra): Not vital to succeed; error ignored
 			_ = 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)
 	bytes_read = int(res)
-	remote_endpoint = sockaddr_to_endpoint(&from)
+	remote_endpoint = _sockaddr_to_endpoint(&from)
 	return
 	return
 }
 }
 
 
@@ -235,7 +235,7 @@ send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written:
 		err = .Message_Too_Long
 		err = .Message_Too_Long
 		return
 		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))
 	res := win.sendto(Platform_Socket(skt), raw_data(buf), c.int(len(buf)), 0, &toaddr, size_of(toaddr))
 	if res < 0 {
 	if res < 0 {
 		err = UDP_Send_Error(win.WSAGetLastError())
 		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
 	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 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
@@ -10,12 +16,6 @@
 		Jeroen van Rijn: Cross platform unification, code style, 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:strings"
 import "core:strings"
 import "core:strconv"
 import "core:strconv"
 import "core:unicode/utf8"
 import "core:unicode/utf8"