dns.odin 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871
  1. #+build windows, linux, darwin, freebsd
  2. package net
  3. /*
  4. Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
  5. For other protocols and their features, see subdirectories of this package.
  6. */
  7. /*
  8. Copyright 2022 Tetralux <[email protected]>
  9. Copyright 2022 Colin Davidson <[email protected]>
  10. Copyright 2022 Jeroen van Rijn <[email protected]>.
  11. Copyright 2024 Feoramund <[email protected]>.
  12. Copyright 2025 Christiano Haesbaert <[email protected]>.
  13. Made available under Odin's BSD-3 license.
  14. List of contributors:
  15. Tetralux: Initial implementation
  16. Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
  17. Jeroen van Rijn: Cross platform unification, code style, documentation
  18. Feoramund: FreeBSD platform code
  19. Haesbaert: Security fixes
  20. */
  21. @(require) import "base:runtime"
  22. import "core:mem"
  23. import "core:strings"
  24. import "core:time"
  25. import "core:os"
  26. import "core:math/rand"
  27. @(require) import "core:sync"
  28. dns_config_initialized: sync.Once
  29. when ODIN_OS == .Windows {
  30. dns_configuration := DNS_Configuration{
  31. resolv_conf = "",
  32. hosts_file = "%WINDIR%\\system32\\drivers\\etc\\hosts",
  33. }
  34. } else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
  35. dns_configuration := DNS_Configuration{
  36. resolv_conf = "/etc/resolv.conf",
  37. hosts_file = "/etc/hosts",
  38. }
  39. } else {
  40. #panic("Please add a configuration for this OS.")
  41. }
  42. /*
  43. Replaces environment placeholders in `dns_configuration`. Only necessary on Windows.
  44. Is automatically called, once, by `get_dns_records_*`.
  45. */
  46. @(private)
  47. init_dns_configuration :: proc() {
  48. when ODIN_OS == .Windows {
  49. runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
  50. val := os.replace_environment_placeholders(dns_configuration.hosts_file, context.temp_allocator)
  51. copy(dns_configuration.hosts_file_buf[:], val)
  52. dns_configuration.hosts_file = string(dns_configuration.hosts_file_buf[:len(val)])
  53. }
  54. }
  55. /*
  56. Resolves a hostname to exactly one IP4 and IP6 endpoint.
  57. It's then up to you which one you use.
  58. Note that which address you use to open a socket, determines the type of the socket you get.
  59. Returns `ok=false` if the host name could not be resolved to any endpoints.
  60. Returned endpoints have the same port as provided in the string, or 0 if absent.
  61. If you want to use a specific port, just modify the field after the call to this procedure.
  62. If the hostname part of the endpoint is actually a string representation of an IP address, DNS resolution will be skipped.
  63. This allows you to pass both strings like "example.com:9000" and "1.2.3.4:9000" to this function end reliably get
  64. back an endpoint in both cases.
  65. */
  66. resolve :: proc(hostname_and_maybe_port: string) -> (ep4, ep6: Endpoint, err: Network_Error) {
  67. target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
  68. switch t in target {
  69. case Endpoint:
  70. // NOTE(tetra): The hostname was actually an IP address; nothing to resolve, so just return it.
  71. switch _ in t.address {
  72. case IP4_Address: ep4 = t
  73. case IP6_Address: ep6 = t
  74. case: unreachable()
  75. }
  76. return
  77. case Host:
  78. err4, err6: Network_Error = ---, ---
  79. ep4, err4 = resolve_ip4(t.hostname)
  80. ep6, err6 = resolve_ip6(t.hostname)
  81. ep4.port = t.port if err4 == nil else 0
  82. ep6.port = t.port if err6 == nil else 0
  83. if err4 != nil && err6 != nil {
  84. err = err4
  85. }
  86. return
  87. }
  88. unreachable()
  89. }
  90. resolve_ip4 :: proc(hostname_and_maybe_port: string) -> (ep4: Endpoint, err: Network_Error) {
  91. target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
  92. switch t in target {
  93. case Endpoint:
  94. // NOTE(tetra): The hostname was actually an IP address; nothing to resolve, so just return it.
  95. switch _ in t.address {
  96. case IP4_Address:
  97. return t, nil
  98. case IP6_Address:
  99. err = .Unable_To_Resolve
  100. return
  101. }
  102. case Host:
  103. recs: []DNS_Record
  104. if ODIN_OS != .Windows && strings.has_suffix(t.hostname, ".local") {
  105. recs, _ = get_dns_records_from_nameservers(t.hostname, .IP4, {IP4_mDNS_Broadcast}, nil, context.temp_allocator)
  106. } else {
  107. recs, _ = get_dns_records_from_os(t.hostname, .IP4, context.temp_allocator)
  108. }
  109. if len(recs) == 0 {
  110. err = .Unable_To_Resolve
  111. return
  112. }
  113. ep4 = {
  114. address = recs[0].(DNS_Record_IP4).address,
  115. port = t.port,
  116. }
  117. return
  118. }
  119. unreachable()
  120. }
  121. resolve_ip6 :: proc(hostname_and_maybe_port: string) -> (ep6: Endpoint, err: Network_Error) {
  122. target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
  123. switch t in target {
  124. case Endpoint:
  125. // NOTE(tetra): The hostname was actually an IP address; nothing to resolve, so just return it.
  126. switch _ in t.address {
  127. case IP4_Address:
  128. err = .Unable_To_Resolve
  129. return
  130. case IP6_Address:
  131. return t, nil
  132. }
  133. case Host:
  134. recs: []DNS_Record
  135. if ODIN_OS != .Windows && strings.has_suffix(t.hostname, ".local") {
  136. recs, _ = get_dns_records_from_nameservers(t.hostname, .IP6, {IP6_mDNS_Broadcast}, nil, context.temp_allocator)
  137. } else {
  138. recs, _ = get_dns_records_from_os(t.hostname, .IP6, context.temp_allocator)
  139. }
  140. if len(recs) == 0 {
  141. err = .Unable_To_Resolve
  142. return
  143. }
  144. ep6 = {
  145. address = recs[0].(DNS_Record_IP6).address,
  146. port = t.port,
  147. }
  148. return
  149. }
  150. unreachable()
  151. }
  152. /*
  153. Performs a recursive DNS query for records of a particular type for the hostname using the OS.
  154. NOTE: This procedure instructs the DNS resolver to recursively perform CNAME requests on our behalf,
  155. meaning that DNS queries for a hostname will resolve through CNAME records until an
  156. IP address is reached.
  157. IMPORTANT: This procedure allocates memory for each record returned; deleting just the returned slice is not enough!
  158. See `destroy_records`.
  159. */
  160. get_dns_records_from_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
  161. when ODIN_OS == .Windows {
  162. sync.once_do(&dns_config_initialized, init_dns_configuration)
  163. }
  164. return _get_dns_records_os(hostname, type, allocator)
  165. }
  166. /*
  167. A generic DNS client usable on any platform.
  168. Performs a recursive DNS query for records of a particular type for the hostname.
  169. NOTE: This procedure instructs the DNS resolver to recursively perform CNAME requests on our behalf,
  170. meaning that DNS queries for a hostname will resolve through CNAME records until an
  171. IP address is reached.
  172. IMPORTANT: This procedure allocates memory for each record returned; deleting just the returned slice is not enough!
  173. See `destroy_records`.
  174. */
  175. 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) {
  176. when ODIN_OS == .Windows {
  177. sync.once_do(&dns_config_initialized, init_dns_configuration)
  178. }
  179. context.allocator = allocator
  180. if type != .SRV {
  181. // NOTE(tetra): 'hostname' can contain underscores when querying SRV records
  182. ok := validate_hostname(hostname)
  183. if !ok {
  184. return nil, .Invalid_Hostname_Error
  185. }
  186. }
  187. hdr := DNS_Header{
  188. id = u16be(rand.uint32()),
  189. is_response = false,
  190. opcode = 0,
  191. is_authoritative = false,
  192. is_truncated = false,
  193. is_recursion_desired = true,
  194. is_recursion_available = false,
  195. response_code = DNS_Response_Code.No_Error,
  196. }
  197. id, bits := pack_dns_header(hdr)
  198. dns_hdr := [6]u16be{}
  199. dns_hdr[0] = id
  200. dns_hdr[1] = bits
  201. dns_hdr[2] = 1
  202. dns_query := [2]u16be{ u16be(type), 1 }
  203. output := [(size_of(u16be) * 6) + NAME_MAX + (size_of(u16be) * 2)]u8{}
  204. b := strings.builder_from_slice(output[:])
  205. strings.write_bytes(&b, mem.slice_data_cast([]u8, dns_hdr[:]))
  206. ok := encode_hostname(&b, hostname)
  207. if !ok {
  208. return nil, .Invalid_Hostname_Error
  209. }
  210. strings.write_bytes(&b, mem.slice_data_cast([]u8, dns_query[:]))
  211. dns_packet := output[:strings.builder_len(b)]
  212. dns_response_buf := [4096]u8{}
  213. dns_response: []u8
  214. for name_server in name_servers {
  215. conn, sock_err := make_unbound_udp_socket(family_from_endpoint(name_server))
  216. if sock_err != nil {
  217. return nil, .Connection_Error
  218. }
  219. defer close(conn)
  220. _ = send(conn, dns_packet[:], name_server) or_continue
  221. if set_option(conn, .Receive_Timeout, time.Second * 1) != nil {
  222. return nil, .Connection_Error
  223. }
  224. recv_sz, src := recv_udp(conn, dns_response_buf[:]) or_continue
  225. if recv_sz == 0 {
  226. continue
  227. }
  228. if src != name_server {
  229. continue
  230. }
  231. dns_response = dns_response_buf[:recv_sz]
  232. rsp, xid, _ok := parse_response(dns_response, type)
  233. if !_ok {
  234. return nil, .Server_Error
  235. }
  236. if id != xid {
  237. continue
  238. }
  239. if len(rsp) == 0 {
  240. continue
  241. }
  242. return rsp[:], nil
  243. }
  244. return
  245. }
  246. // `records` slice is also destroyed.
  247. destroy_dns_records :: proc(records: []DNS_Record, allocator := context.allocator) {
  248. context.allocator = allocator
  249. for rec in records {
  250. switch r in rec {
  251. case DNS_Record_IP4:
  252. delete(r.base.record_name)
  253. case DNS_Record_IP6:
  254. delete(r.base.record_name)
  255. case DNS_Record_CNAME:
  256. delete(r.base.record_name)
  257. delete(r.host_name)
  258. case DNS_Record_TXT:
  259. delete(r.base.record_name)
  260. delete(r.value)
  261. case DNS_Record_NS:
  262. delete(r.base.record_name)
  263. delete(r.host_name)
  264. case DNS_Record_MX:
  265. delete(r.base.record_name)
  266. delete(r.host_name)
  267. case DNS_Record_SRV:
  268. delete(r.record_name)
  269. delete(r.target)
  270. }
  271. }
  272. delete(records, allocator)
  273. }
  274. /*
  275. TODO(cloin): Does the DNS Resolver need to recursively hop through CNAMEs to get the IP
  276. or is that what recursion desired does? Do we need to handle recursion unavailable?
  277. How do we deal with is_authoritative / is_truncated?
  278. */
  279. NAME_MAX :: 255
  280. LABEL_MAX :: 63
  281. pack_dns_header :: proc(hdr: DNS_Header) -> (id: u16be, bits: u16be) {
  282. id = hdr.id
  283. bits = hdr.opcode << 1 | u16be(hdr.response_code)
  284. if hdr.is_response {
  285. bits |= 1 << 15
  286. }
  287. if hdr.is_authoritative {
  288. bits |= 1 << 10
  289. }
  290. if hdr.is_truncated {
  291. bits |= 1 << 9
  292. }
  293. if hdr.is_recursion_desired {
  294. bits |= 1 << 8
  295. }
  296. if hdr.is_recursion_available {
  297. bits |= 1 << 7
  298. }
  299. return id, bits
  300. }
  301. unpack_dns_header :: proc(id: u16be, bits: u16be) -> (hdr: DNS_Header) {
  302. hdr.id = id
  303. hdr.is_response = (bits & (1 << 15)) != 0
  304. hdr.opcode = (bits >> 11) & 0xF
  305. hdr.is_authoritative = (bits & (1 << 10)) != 0
  306. hdr.is_truncated = (bits & (1 << 9)) != 0
  307. hdr.is_recursion_desired = (bits & (1 << 8)) != 0
  308. hdr.is_recursion_available = (bits & (1 << 7)) != 0
  309. hdr.response_code = DNS_Response_Code(bits & 0xF)
  310. return hdr
  311. }
  312. load_resolv_conf :: proc(resolv_conf_path: string, allocator := context.allocator) -> (name_servers: []Endpoint, ok: bool) {
  313. context.allocator = allocator
  314. res := os.read_entire_file_from_filename(resolv_conf_path) or_return
  315. defer delete(res)
  316. resolv_str := string(res)
  317. id_str := "nameserver"
  318. id_len := len(id_str)
  319. _name_servers := make([dynamic]Endpoint, 0, allocator)
  320. for line in strings.split_lines_iterator(&resolv_str) {
  321. if len(line) == 0 || line[0] == '#' {
  322. continue
  323. }
  324. if len(line) < id_len || strings.compare(line[:id_len], id_str) != 0 {
  325. continue
  326. }
  327. server_ip_str := strings.trim_left_space(line[id_len:])
  328. if len(server_ip_str) == 0 {
  329. continue
  330. }
  331. addr := parse_address(server_ip_str)
  332. if addr == nil {
  333. continue
  334. }
  335. endpoint := Endpoint{
  336. addr,
  337. 53,
  338. }
  339. append(&_name_servers, endpoint)
  340. }
  341. return _name_servers[:], true
  342. }
  343. load_hosts :: proc(hosts_file_path: string, allocator := context.allocator) -> (hosts: []DNS_Host_Entry, ok: bool) {
  344. context.allocator = allocator
  345. res := os.read_entire_file_from_filename(hosts_file_path, allocator) or_return
  346. defer delete(res)
  347. _hosts := make([dynamic]DNS_Host_Entry, 0, allocator)
  348. hosts_str := string(res)
  349. for line in strings.split_lines_iterator(&hosts_str) {
  350. if len(line) == 0 || line[0] == '#' {
  351. continue
  352. }
  353. splits := strings.fields(line)
  354. defer delete(splits)
  355. (len(splits) >= 2) or_continue
  356. ip_str := splits[0]
  357. addr := parse_address(ip_str)
  358. if addr == nil {
  359. continue
  360. }
  361. for hostname in splits[1:] {
  362. if len(hostname) != 0 {
  363. append(&_hosts, DNS_Host_Entry{hostname, addr})
  364. }
  365. }
  366. }
  367. return _hosts[:], true
  368. }
  369. // www.google.com -> 3www6google3com0
  370. encode_hostname :: proc(b: ^strings.Builder, hostname: string) -> (ok: bool) {
  371. _hostname := hostname
  372. for section in strings.split_iterator(&_hostname, ".") {
  373. if len(section) > LABEL_MAX {
  374. return
  375. }
  376. strings.write_byte(b, u8(len(section)))
  377. strings.write_string(b, section)
  378. }
  379. strings.write_byte(b, 0)
  380. return true
  381. }
  382. skip_hostname :: proc(packet: []u8, start_idx: int) -> (encode_size: int, ok: bool) {
  383. out_size := 0
  384. cur_idx := start_idx
  385. iteration_max := 0
  386. top: for cur_idx < len(packet) {
  387. if packet[cur_idx] == 0 {
  388. out_size += 1
  389. break
  390. }
  391. if iteration_max > 255 {
  392. return
  393. }
  394. if packet[cur_idx] > 63 && packet[cur_idx] != 0xC0 {
  395. return
  396. }
  397. switch packet[cur_idx] {
  398. case 0xC0:
  399. out_size += 2
  400. break top
  401. case:
  402. label_size := int(packet[cur_idx]) + 1
  403. idx2 := cur_idx + label_size
  404. if idx2 < cur_idx + 1 || idx2 > len(packet) {
  405. return
  406. }
  407. out_size += label_size
  408. cur_idx = idx2
  409. }
  410. iteration_max += 1
  411. }
  412. if start_idx + out_size > len(packet) {
  413. return
  414. }
  415. return out_size, true
  416. }
  417. decode_hostname :: proc(packet: []u8, start_idx: int, allocator := context.allocator) -> (hostname: string, encode_size: int, ok: bool) {
  418. output := [NAME_MAX]u8{}
  419. b := strings.builder_from_slice(output[:])
  420. // If you're on level 0, update out_bytes, everything through a pointer
  421. // doesn't count towards this hostname's packet length
  422. // Evaluate tokens to generate the hostname
  423. out_size := 0
  424. level := 0
  425. print_size := 0
  426. cur_idx := start_idx
  427. iteration_max := 0
  428. labels_added := 0
  429. for cur_idx < len(packet) {
  430. if packet[cur_idx] == 0 {
  431. if (level == 0) {
  432. out_size += 1
  433. }
  434. break
  435. }
  436. if iteration_max > 255 {
  437. return
  438. }
  439. switch {
  440. // A pointer is when the two higher bits are set.
  441. case packet[cur_idx] & 0xC0 == 0xC0:
  442. if len(packet[cur_idx:]) < 2 {
  443. return
  444. }
  445. pkt := packet[cur_idx:cur_idx+2]
  446. val := (^u16be)(raw_data(pkt))^
  447. offset := int(val & 0x3FFF)
  448. // RFC 9267 a ptr should only point backwards, enough to avoid infinity.
  449. // "The offset at which this octet is located must be smaller than the offset
  450. // at which the compression pointer is located". Still keep iteration_max to
  451. // avoid tiny jumps.
  452. if offset > len(packet) || offset >= cur_idx {
  453. return
  454. }
  455. cur_idx = offset
  456. if (level == 0) {
  457. out_size += 2
  458. level += 1
  459. }
  460. // Validate label len
  461. case packet[cur_idx] > LABEL_MAX:
  462. return
  463. // This is a label, insert it into the hostname
  464. case:
  465. label_size := int(packet[cur_idx])
  466. idx2 := cur_idx + label_size + 1
  467. if idx2 < cur_idx + 1 || idx2 > len(packet) {
  468. return
  469. }
  470. if print_size + label_size + 1 > NAME_MAX {
  471. return
  472. }
  473. if labels_added > 0 {
  474. strings.write_byte(&b, '.')
  475. }
  476. strings.write_bytes(&b, packet[cur_idx+1:idx2])
  477. print_size += label_size + 1
  478. labels_added += 1
  479. cur_idx = idx2
  480. if (level == 0) {
  481. out_size += label_size + 1
  482. }
  483. }
  484. iteration_max += 1
  485. }
  486. if start_idx + out_size > len(packet) {
  487. return
  488. }
  489. return strings.clone(strings.to_string(b), allocator), out_size, true
  490. }
  491. // Uses RFC 952 & RFC 1123
  492. validate_hostname :: proc(hostname: string) -> (ok: bool) {
  493. if len(hostname) > 255 || len(hostname) == 0 {
  494. return
  495. }
  496. if hostname[0] == '-' {
  497. return
  498. }
  499. _hostname := hostname
  500. for label in strings.split_iterator(&_hostname, ".") {
  501. if len(label) > 63 || len(label) == 0 {
  502. return
  503. }
  504. for ch in label {
  505. switch ch {
  506. case:
  507. return
  508. case 'a'..='z', 'A'..='Z', '0'..='9', '-':
  509. continue
  510. }
  511. }
  512. }
  513. return true
  514. }
  515. parse_record :: proc(packet: []u8, cur_off: ^int, filter: DNS_Record_Type = nil) -> (record: DNS_Record, ok: bool) {
  516. record_buf := packet[cur_off^:]
  517. srv_record_name, hn_sz := decode_hostname(packet, cur_off^, context.temp_allocator) or_return
  518. // TODO(tetra): Not sure what we should call this.
  519. // Is it really only used in SRVs?
  520. // Maybe some refactoring is required?
  521. ahdr_sz := size_of(DNS_Record_Header)
  522. if len(record_buf) - hn_sz < ahdr_sz {
  523. return
  524. }
  525. record_hdr_bytes := record_buf[hn_sz:hn_sz+ahdr_sz]
  526. record_hdr := cast(^DNS_Record_Header)raw_data(record_hdr_bytes)
  527. data_sz := record_hdr.length
  528. data_off := cur_off^ + int(hn_sz) + int(ahdr_sz)
  529. data := packet[data_off:data_off+int(data_sz)]
  530. cur_off^ += int(hn_sz) + int(ahdr_sz) + int(data_sz)
  531. if u16be(filter) != record_hdr.type {
  532. return nil, true
  533. }
  534. _record: DNS_Record
  535. #partial switch DNS_Record_Type(record_hdr.type) {
  536. case .IP4:
  537. if len(data) != 4 {
  538. return
  539. }
  540. addr := (^IP4_Address)(raw_data(data))^
  541. _record = DNS_Record_IP4{
  542. base = DNS_Record_Base{
  543. record_name = strings.clone(srv_record_name),
  544. ttl_seconds = u32(record_hdr.ttl),
  545. },
  546. address = addr,
  547. }
  548. case .IP6:
  549. if len(data) != 16 {
  550. return
  551. }
  552. addr := (^IP6_Address)(raw_data(data))^
  553. _record = DNS_Record_IP6{
  554. base = DNS_Record_Base{
  555. record_name = strings.clone(srv_record_name),
  556. ttl_seconds = u32(record_hdr.ttl),
  557. },
  558. address = addr,
  559. }
  560. case .CNAME:
  561. hostname, _ := decode_hostname(packet, data_off) or_return
  562. _record = DNS_Record_CNAME{
  563. base = DNS_Record_Base{
  564. record_name = strings.clone(srv_record_name),
  565. ttl_seconds = u32(record_hdr.ttl),
  566. },
  567. host_name = hostname,
  568. }
  569. case .TXT:
  570. _record = DNS_Record_TXT{
  571. base = DNS_Record_Base{
  572. record_name = strings.clone(srv_record_name),
  573. ttl_seconds = u32(record_hdr.ttl),
  574. },
  575. value = strings.clone(string(data)),
  576. }
  577. case .NS:
  578. name, _ := decode_hostname(packet, data_off) or_return
  579. _record = DNS_Record_NS{
  580. base = DNS_Record_Base{
  581. record_name = strings.clone(srv_record_name),
  582. ttl_seconds = u32(record_hdr.ttl),
  583. },
  584. host_name = name,
  585. }
  586. case .SRV:
  587. if len(data) <= 6 {
  588. return
  589. }
  590. _data := mem.slice_data_cast([]u16be, data)
  591. priority, weight, port := _data[0], _data[1], _data[2]
  592. target, _ := decode_hostname(packet, data_off + (size_of(u16be) * 3)) or_return
  593. // NOTE(tetra): Srv record name should be of the form '_servicename._protocol.hostname'
  594. // The record name is the name of the record.
  595. // Not to be confused with the _target_ of the record, which is--in combination with the port--what we're looking up
  596. // by making this request in the first place.
  597. // NOTE(Jeroen): Service Name and Protocol Name can probably just be string slices into the record name.
  598. // It's already cloned, after all. I wouldn't put them on the temp allocator like this.
  599. parts := strings.split_n(srv_record_name, ".", 3, context.temp_allocator)
  600. if len(parts) != 3 {
  601. return
  602. }
  603. service_name, protocol_name := parts[0], parts[1]
  604. _record = DNS_Record_SRV{
  605. base = DNS_Record_Base{
  606. record_name = strings.clone(srv_record_name),
  607. ttl_seconds = u32(record_hdr.ttl),
  608. },
  609. target = target,
  610. service_name = service_name,
  611. protocol_name = protocol_name,
  612. priority = int(priority),
  613. weight = int(weight),
  614. port = int(port),
  615. }
  616. case .MX:
  617. if len(data) <= 2 {
  618. return
  619. }
  620. preference: u16be = mem.slice_data_cast([]u16be, data)[0]
  621. hostname, _ := decode_hostname(packet, data_off + size_of(u16be)) or_return
  622. _record = DNS_Record_MX{
  623. base = DNS_Record_Base{
  624. record_name = strings.clone(srv_record_name),
  625. ttl_seconds = u32(record_hdr.ttl),
  626. },
  627. host_name = hostname,
  628. preference = int(preference),
  629. }
  630. case:
  631. return
  632. }
  633. return _record, true
  634. }
  635. /*
  636. DNS Query Response Format:
  637. - DNS_Header (packed)
  638. - Query Count
  639. - Answer Count
  640. - Authority Count
  641. - Additional Count
  642. - Query[]
  643. - Hostname -- encoded
  644. - Type
  645. - Class
  646. - Answer[]
  647. - DNS Record Data
  648. - Authority[]
  649. - DNS Record Data
  650. - Additional[]
  651. - DNS Record Data
  652. DNS Record Data:
  653. - DNS_Record_Header
  654. - Data[]
  655. */
  656. parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, xid: u16be, ok: bool) {
  657. context.allocator = allocator
  658. HEADER_SIZE_BYTES :: 12
  659. if len(response) < HEADER_SIZE_BYTES {
  660. return
  661. }
  662. _records := make([dynamic]DNS_Record, 0)
  663. dns_hdr_chunks := mem.slice_data_cast([]u16be, response[:HEADER_SIZE_BYTES])
  664. hdr := unpack_dns_header(dns_hdr_chunks[0], dns_hdr_chunks[1])
  665. if !hdr.is_response {
  666. delete(_records)
  667. return
  668. }
  669. question_count := int(dns_hdr_chunks[2])
  670. if question_count != 1 {
  671. delete(_records)
  672. return
  673. }
  674. answer_count := int(dns_hdr_chunks[3])
  675. authority_count := int(dns_hdr_chunks[4])
  676. additional_count := int(dns_hdr_chunks[5])
  677. cur_idx := HEADER_SIZE_BYTES
  678. for _ in 0..<question_count {
  679. if cur_idx == len(response) {
  680. continue
  681. }
  682. dq_sz :: 4
  683. hn_sz := skip_hostname(response, cur_idx) or_return
  684. cur_idx += hn_sz + dq_sz
  685. }
  686. for _ in 0..<answer_count {
  687. if cur_idx == len(response) {
  688. continue
  689. }
  690. rec := parse_record(response, &cur_idx, filter) or_return
  691. if rec != nil {
  692. append(&_records, rec)
  693. }
  694. }
  695. for _ in 0..<authority_count {
  696. if cur_idx == len(response) {
  697. continue
  698. }
  699. rec := parse_record(response, &cur_idx, filter) or_return
  700. if rec != nil {
  701. append(&_records, rec)
  702. }
  703. }
  704. for _ in 0..<additional_count {
  705. if cur_idx == len(response) {
  706. continue
  707. }
  708. rec := parse_record(response, &cur_idx, filter) or_return
  709. if rec != nil {
  710. append(&_records, rec)
  711. }
  712. }
  713. xid = hdr.id
  714. return _records[:], xid, true
  715. }