diff options
| author | Colin Davidson <colrdavidson@gmail.com> | 2023-03-01 07:58:30 -0800 |
|---|---|---|
| committer | Colin Davidson <colrdavidson@gmail.com> | 2023-03-01 07:58:30 -0800 |
| commit | 28f7f572473c4e97ccd6133bb4f5fa6f45505530 (patch) | |
| tree | 0bd159c24c617df409f72b0ec75daa086372e94d /core/net/common.odin | |
| parent | 3567c006e6683d989805c078db48a95a901d9e72 (diff) | |
manually start merging core_net
Diffstat (limited to 'core/net/common.odin')
| -rw-r--r-- | core/net/common.odin | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/core/net/common.odin b/core/net/common.odin new file mode 100644 index 000000000..9e980e88e --- /dev/null +++ b/core/net/common.odin @@ -0,0 +1,437 @@ +/* + Copyright 2022 Tetralux <tetraluxonpc@gmail.com> + Copyright 2022 Colin Davidson <colrdavidson@gmail.com> + Copyright 2022 Jeroen van Rijn <nom@duclavier.com>. + 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. + + This file collects structs, enums and settings applicable to the entire package in one handy place. + Platform-specific ones can be found in their respective `*_windows.odin` and similar files. +*/ +package net + +import "core:runtime" + +/* + TUNEABLES +*/ + +/* + Determines the default value for whether dial_tcp() and accept_tcp() will set TCP_NODELAY on the new + socket, and the client socket, respectively. + This can also be set on a per-socket basis using the 'options' optional parameter to those procedures. + + When TCP_NODELAY is set, data will be sent out to the peer as quickly as possible, rather than being + coalesced into fewer network packets. + + This makes the networking layer more eagerly send data when you ask it to, + which can reduce latency by up to 200ms. + + This does mean that a lot of small writes will negatively effect throughput however, + since the Nagle algorithm will be disabled, and each write becomes one + IP packet. This will increase traffic by a factor of 40, with IP and TCP + headers for each payload. + + However, you can avoid this by buffering things up yourself if you wish to send a lot of + short data chunks, when TCP_NODELAY is enabled on that socket. +*/ +ODIN_NET_TCP_NODELAY_DEFAULT :: #config(ODIN_NET_TCP_NODELAY_DEFAULT, true) + +/* + See also top of `dns.odin` for DNS configuration. +*/ + +/* + COMMON DEFINITIONS +*/ + +Maybe :: runtime.Maybe + +General_Error :: enum { + Unable_To_Enumerate_Network_Interfaces = 1, +} + +/* + `Platform_Error` is used to wrap errors returned by the different platforms that defy translation into a common error. +*/ +Platform_Error :: enum u32 {} + +/* + NOTE(tetra): Enums in Network_Error should not have a named zero value. + If you have a proc that returns an enum with an Ok=0 value, using or_return from the callsite, when the caller returns a union, works as expected. + However, if that proc returns the union directly, returning the Ok value will NOT work with the caller's or_return, as it will treat Error{.Ok} as != nil, and early-return with it. + + The approach currently taken to avoid this is: + - Remove the named zero values for the enums + - Use the union everywhere +*/ +Network_Error :: union { + General_Error, + Platform_Error, + Create_Socket_Error, + Dial_Error, + Listen_Error, + Accept_Error, + Bind_Error, + TCP_Send_Error, + UDP_Send_Error, + TCP_Recv_Error, + UDP_Recv_Error, + Shutdown_Error, + Socket_Option_Error, + Parse_Endpoint_Error, + Resolve_Error, + DNS_Error, +} + + +Resolve_Error :: enum { + Unable_To_Resolve = 1, +} + +DNS_Error :: enum { + Invalid_Hostname_Error = 1, + Invalid_Hosts_Config_Error, + Invalid_Resolv_Config_Error, + Connection_Error, + Server_Error, + System_Error, +} + +/* + SOCKET OPTIONS & DEFINITIONS +*/ + +TCP_Options :: struct { + no_delay: bool, +} + +default_tcp_options := TCP_Options { + no_delay = ODIN_NET_TCP_NODELAY_DEFAULT, +} + +/* + To allow freely using `Socket` in your own data structures in a cross-platform manner, + we treat it as a handle large enough to accomodate OS-specific notions of socket handles. + + The platform code will perform the cast so you don't have to. +*/ +Socket :: distinct i64 + +TCP_Socket :: distinct Socket +UDP_Socket :: distinct Socket + +Socket_Protocol :: enum { + TCP, + UDP, +} + +Any_Socket :: union { + TCP_Socket, + UDP_Socket, +} + +/* + ADDRESS DEFINITIONS +*/ + +IP4_Address :: distinct [4]u8 +IP6_Address :: distinct [8]u16be +Address :: union {IP4_Address, IP6_Address} + +IP4_Loopback := IP4_Address{127, 0, 0, 1} +IP6_Loopback := IP6_Address{0, 0, 0, 0, 0, 0, 0, 1} + +IP4_Any := IP4_Address{} +IP6_Any := IP6_Address{} + +Endpoint :: struct { + address: Address, + port: int, +} + +Address_Family :: enum { + IP4, + IP6, +} + +Netmask :: distinct Address + +/* + INTERFACE / LINK STATE +*/ +Network_Interface :: struct { + adapter_name: string, // On Windows this is a GUID that we could parse back into its u128 for more compact storage. + friendly_name: string, + description: string, + dns_suffix: string, + + physical_address: string, // MAC address, etc. + mtu: u32, + + unicast: [dynamic]Lease, + multicast: [dynamic]Address, + anycast: [dynamic]Address, + + gateways: [dynamic]Address, + dhcp_v4: Address, + dhcp_v6: Address, + + tunnel_type: Tunnel_Type, + + link: struct { + state: Link_State, + transmit_speed: u64, + receive_speed: u64, + }, +} + +/* + Empty bit set is unknown state. +*/ +Link_States :: enum u32 { + Up = 1, + Down = 2, + Testing = 3, + Dormant = 4, + Not_Present = 5, + Lower_Layer_Down = 6, + Loopback = 7, +} +Link_State :: bit_set[Link_States; u32] + +Lease :: struct { + address: Address, + netmask: Netmask, + lifetime: struct { + valid: u32, + preferred: u32, + lease: u32, + }, + origin: struct { + prefix: Prefix_Origin, + suffix: Suffix_Origin, + }, + address_duplication: Address_Duplication, +} + +Tunnel_Type :: enum i32 { + None = 0, + Other = 1, + Direct = 2, + IPv4_To_IPv6 = 11, + ISA_TAP = 13, + Teredo = 14, + IP_HTTPS = 15, +} + +Prefix_Origin :: enum i32 { + Other = 0, + Manual = 1, + Well_Known = 2, + DHCP = 3, + Router_Advertisement = 4, + Unchanged = 16, +} + +Suffix_Origin :: enum i32 { + Other = 0, + Manual = 1, + Well_Known = 2, + DHCP = 3, + Link_Layer_Address = 4, + Random = 5, + Unchanged = 16, +} + +Address_Duplication :: enum i32 { + Invalid = 0, + Tentative = 1, + Duplicate = 2, + Deprecated = 3, + Preferred = 4, +} + +/* + DNS DEFINITIONS +*/ + +DNS_Configuration :: struct { + /* + Configuration files. + */ + resolv_conf: string, + hosts_file: string, + + // TODO: Allow loading these up with `reload_configuration()` call or the like so we don't have to do it each call. + name_servers: []Endpoint, + hosts_file_entries: []DNS_Record, +} + +DNS_Record_Type :: enum u16 { + DNS_TYPE_A = 0x1, // IP4 address. + DNS_TYPE_NS = 0x2, // IP6 address. + DNS_TYPE_CNAME = 0x5, // Another host name. + DNS_TYPE_MX = 0xf, // Arbitrary binary data or text. + DNS_TYPE_AAAA = 0x1c, // Address of a name (DNS) server. + DNS_TYPE_TEXT = 0x10, // Address and preference priority of a mail exchange server. + DNS_TYPE_SRV = 0x21, // Address, port, priority, and weight of a host that provides a particular service. + + IP4 = DNS_TYPE_A, + IP6 = DNS_TYPE_AAAA, + CNAME = DNS_TYPE_CNAME, + TXT = DNS_TYPE_TEXT, + NS = DNS_TYPE_NS, + MX = DNS_TYPE_MX, + SRV = DNS_TYPE_SRV, +} + +/* + Base DNS Record. All DNS responses will carry a hostname and TTL (time to live) field. +*/ +DNS_Record_Base :: struct { + record_name: string, + ttl_seconds: u32, // The time in seconds that this service will take to update, after the record is updated. +} + +/* + An IP4 address that the domain name maps to. There can be any number of these. +*/ +DNS_Record_IP4 :: struct { + using base: DNS_Record_Base, + address: IP4_Address, +} + +/* + An IPv6 address that the domain name maps to. There can be any number of these. +*/ +DNS_Record_IP6 :: struct { + using base: DNS_Record_Base, + address: IP6_Address, +} + +/* + Another domain name that the domain name maps to. + Domains can be pointed to another domain instead of directly to an IP address. + `get_dns_records` will recursively follow these if you request this type of record. +*/ +DNS_Record_CNAME :: struct { + using base: DNS_Record_Base, + host_name: string, +} + +/* + Arbitrary string data that is associated with the domain name. + Commonly of the form `key=value` to be parsed, though there is no specific format for them. + These can be used for any purpose. +*/ +DNS_Record_TXT :: struct { + using base: DNS_Record_Base, + value: string, +} + +/* + Domain names of other DNS servers that are associated with the domain name. + TODO(tetra): Expand on what these records are used for, and when you should use pay attention to these. +*/ +DNS_Record_NS :: struct { + using base: DNS_Record_Base, + host_name: string, +} + +// Domain names for email servers that are associated with the domain name. +// These records also have values which ranks them in the order they should be preferred. Lower is more-preferred. +DNS_Record_MX :: struct { + using base: DNS_Record_Base, + host_name: string, + preference: int, +} + +// An endpoint for a service that is available through the domain name. +// This is the way to discover the services that a domain name provides. +// +// Clients MUST attempt to contact the host with the lowest priority that they can reach. +// If two hosts have the same priority, they should be contacted in the order according to their weight. +// Hosts with larger weights should have a proportionally higher chance of being contacted by clients. +// A weight of zero indicates a very low weight, or, when there is no choice (to reduce visual noise). +// +// The host may be "." to indicate that it is "decidedly not available" on this domain. +DNS_Record_SRV :: struct { + // base contains the full name of this record. + // e.g: _sip._tls.example.com + using base: DNS_Record_Base, + + // The hostname or address where this service can be found. + target: string, + // The port on which this service can be found. + port: int, + + service_name: string, // NOTE(tetra): These are substrings of 'record_name' + protocol_name: string, // NOTE(tetra): These are substrings of 'record_name' + + // Lower is higher priority + priority: int, + // Relative weight of this host compared to other of same priority; the chance of using this host should be proporitional to this weight. + // The number of seconds that it will take to update the record. + weight: int, +} + +DNS_Record :: union { + DNS_Record_IP4, + DNS_Record_IP6, + DNS_Record_CNAME, + DNS_Record_TXT, + DNS_Record_NS, + DNS_Record_MX, + DNS_Record_SRV, +} + +DNS_Response_Code :: enum u16be { + No_Error, + Format_Error, + Server_Failure, + Name_Error, + Not_Implemented, + Refused, +} + +DNS_Query :: enum u16be { + Host_Address = 1, + Authoritative_Name_Server = 2, + Mail_Destination = 3, + Mail_Forwarder = 4, + CNAME = 5, + All = 255, +} + +DNS_Header :: struct { + id: u16be, + is_response: bool, + opcode: u16be, + is_authoritative: bool, + is_truncated: bool, + is_recursion_desired: bool, + is_recursion_available: bool, + response_code: DNS_Response_Code, +} + +DNS_Record_Header :: struct #packed { + type: u16be, + class: u16be, + ttl: u32be, + length: u16be, +} + +DNS_Host_Entry :: struct { + name: string, + addr: Address, +}
\ No newline at end of file |