dns.odin 21 KB

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