dns.odin 21 KB

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