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/dns_windows.odin | |
| parent | 3567c006e6683d989805c078db48a95a901d9e72 (diff) | |
manually start merging core_net
Diffstat (limited to 'core/net/dns_windows.odin')
| -rw-r--r-- | core/net/dns_windows.odin | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/core/net/dns_windows.odin b/core/net/dns_windows.odin new file mode 100644 index 000000000..ae38ca05f --- /dev/null +++ b/core/net/dns_windows.odin @@ -0,0 +1,166 @@ +//+build windows +/* + 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. +*/ +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) { + context.allocator = allocator + + host_cstr := strings.clone_to_cstring(hostname, context.temp_allocator) + rec: ^win.DNS_RECORD + res := win.DnsQuery_UTF8(host_cstr, u16(type), 0, nil, &rec, nil) + + switch u32(res) { + case 0: + // okay + case win.ERROR_INVALID_NAME: + return nil, .Invalid_Hostname_Error + case win.DNS_INFO_NO_RECORDS: + return + case: + return nil, .System_Error + } + defer win.DnsRecordListFree(rec, 1) // 1 means that we're freeing a list... because the proc name isn't enough. + + count := 0 + for r := rec; r != nil; r = r.pNext { + if r.wType != u16(type) do continue // NOTE(tetra): Should never happen, but... + count += 1 + } + + + recs := make([dynamic]DNS_Record, 0, count) + if recs == nil do return nil, .System_Error // return no results if OOM. + + for r := rec; r != nil; r = r.pNext { + if r.wType != u16(type) do continue // NOTE(tetra): Should never happen, but... + + base_record := DNS_Record_Base{ + record_name = strings.clone(string(r.pName)), + ttl_seconds = r.dwTtl, + } + + switch DNS_Record_Type(r.wType) { + case .IP4: + addr := IP4_Address(transmute([4]u8)r.Data.A) + record := DNS_Record_IP4{ + base = base_record, + address = addr, + } + append(&recs, record) + + case .IP6: + addr := IP6_Address(transmute([8]u16be) r.Data.AAAA) + record := DNS_Record_IP6{ + base = base_record, + address = addr, + } + append(&recs, record) + + case .CNAME: + + hostname := strings.clone(string(r.Data.CNAME)) + record := DNS_Record_CNAME{ + base = base_record, + host_name = hostname, + } + append(&recs, record) + + case .TXT: + n := r.Data.TXT.dwStringCount + ptr := &r.Data.TXT.pStringArray + c_strs := mem.slice_ptr(ptr, int(n)) + + for cstr in c_strs { + record := DNS_Record_TXT{ + base = base_record, + value = strings.clone(string(cstr)), + } + append(&recs, record) + } + + case .NS: + hostname := strings.clone(string(r.Data.NS)) + record := DNS_Record_NS{ + base = base_record, + host_name = hostname, + } + append(&recs, record) + + case .MX: + /* + TODO(tetra): Order by preference priority? (Prefer hosts with lower preference values.) + Or maybe not because you're supposed to just use the first one that works + and which order they're in changes every few calls. + */ + + record := DNS_Record_MX{ + base = base_record, + host_name = strings.clone(string(r.Data.MX.pNameExchange)), + preference = int(r.Data.MX.wPreference), + } + append(&recs, record) + + case .SRV: + target := strings.clone(string(r.Data.SRV.pNameTarget)) // The target hostname/address that the service can be found on + priority := int(r.Data.SRV.wPriority) + weight := int(r.Data.SRV.wWeight) + port := int(r.Data.SRV.wPort) + + // NOTE(tetra): Srv record name should be of the form '_servicename._protocol.hostname' + // The record name is the name of the record. + // Not to be confused with the _target_ of the record, which is--in combination with the port--what we're looking up + // by making this request in the first place. + + // NOTE(Jeroen): Service Name and Protocol Name can probably just be string slices into the record name. + // It's already cloned, after all. I wouldn't put them on the temp allocator like this. + + parts := strings.split_n(base_record.record_name, ".", 3, context.temp_allocator) + if len(parts) != 3 { + continue + } + service_name, protocol_name := parts[0], parts[1] + + append(&recs, DNS_Record_SRV { + base = base_record, + target = target, + port = port, + service_name = service_name, + protocol_name = protocol_name, + priority = priority, + weight = weight, + + }) + } + } + + records = recs[:] + return +} |