dns.odin 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  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 || ODIN_OS == .NetBSD {
  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(conn, dns_packet[:], name_server) or_continue
  216. if set_option(conn, .Receive_Timeout, time.Second * 1) != nil {
  217. return nil, .Connection_Error
  218. }
  219. // recv_sz, _, recv_err := recv_udp(conn, dns_response_buf[:])
  220. // if recv_err == UDP_Recv_Error.Timeout {
  221. // continue
  222. // } else if recv_err != nil {
  223. // continue
  224. // }
  225. recv_sz, _ := recv_udp(conn, dns_response_buf[:]) or_continue
  226. if recv_sz == 0 {
  227. continue
  228. }
  229. dns_response = dns_response_buf[:recv_sz]
  230. rsp, _ok := parse_response(dns_response, type)
  231. if !_ok {
  232. return nil, .Server_Error
  233. }
  234. if len(rsp) == 0 {
  235. continue
  236. }
  237. return rsp[:], nil
  238. }
  239. return
  240. }
  241. // `records` slice is also destroyed.
  242. destroy_dns_records :: proc(records: []DNS_Record, allocator := context.allocator) {
  243. context.allocator = allocator
  244. for rec in records {
  245. switch r in rec {
  246. case DNS_Record_IP4:
  247. delete(r.base.record_name)
  248. case DNS_Record_IP6:
  249. delete(r.base.record_name)
  250. case DNS_Record_CNAME:
  251. delete(r.base.record_name)
  252. delete(r.host_name)
  253. case DNS_Record_TXT:
  254. delete(r.base.record_name)
  255. delete(r.value)
  256. case DNS_Record_NS:
  257. delete(r.base.record_name)
  258. delete(r.host_name)
  259. case DNS_Record_MX:
  260. delete(r.base.record_name)
  261. delete(r.host_name)
  262. case DNS_Record_SRV:
  263. delete(r.record_name)
  264. delete(r.target)
  265. }
  266. }
  267. delete(records, allocator)
  268. }
  269. /*
  270. TODO(cloin): Does the DNS Resolver need to recursively hop through CNAMEs to get the IP
  271. or is that what recursion desired does? Do we need to handle recursion unavailable?
  272. How do we deal with is_authoritative / is_truncated?
  273. */
  274. NAME_MAX :: 255
  275. LABEL_MAX :: 63
  276. pack_dns_header :: proc(hdr: DNS_Header) -> (id: u16be, bits: u16be) {
  277. id = hdr.id
  278. bits = hdr.opcode << 1 | u16be(hdr.response_code)
  279. if hdr.is_response {
  280. bits |= 1 << 15
  281. }
  282. if hdr.is_authoritative {
  283. bits |= 1 << 10
  284. }
  285. if hdr.is_truncated {
  286. bits |= 1 << 9
  287. }
  288. if hdr.is_recursion_desired {
  289. bits |= 1 << 8
  290. }
  291. if hdr.is_recursion_available {
  292. bits |= 1 << 7
  293. }
  294. return id, bits
  295. }
  296. unpack_dns_header :: proc(id: u16be, bits: u16be) -> (hdr: DNS_Header) {
  297. hdr.id = id
  298. hdr.is_response = (bits & (1 << 15)) != 0
  299. hdr.opcode = (bits >> 11) & 0xF
  300. hdr.is_authoritative = (bits & (1 << 10)) != 0
  301. hdr.is_truncated = (bits & (1 << 9)) != 0
  302. hdr.is_recursion_desired = (bits & (1 << 8)) != 0
  303. hdr.is_recursion_available = (bits & (1 << 7)) != 0
  304. hdr.response_code = DNS_Response_Code(bits & 0xF)
  305. return hdr
  306. }
  307. load_resolv_conf :: proc(resolv_conf_path: string, allocator := context.allocator) -> (name_servers: []Endpoint, ok: bool) {
  308. context.allocator = allocator
  309. res := os.read_entire_file_from_filename(resolv_conf_path) or_return
  310. defer delete(res)
  311. resolv_str := string(res)
  312. id_str := "nameserver"
  313. id_len := len(id_str)
  314. _name_servers := make([dynamic]Endpoint, 0, allocator)
  315. for line in strings.split_lines_iterator(&resolv_str) {
  316. if len(line) == 0 || line[0] == '#' {
  317. continue
  318. }
  319. if len(line) < id_len || strings.compare(line[:id_len], id_str) != 0 {
  320. continue
  321. }
  322. server_ip_str := strings.trim_left_space(line[id_len:])
  323. if len(server_ip_str) == 0 {
  324. continue
  325. }
  326. addr := parse_address(server_ip_str)
  327. if addr == nil {
  328. continue
  329. }
  330. endpoint := Endpoint{
  331. addr,
  332. 53,
  333. }
  334. append(&_name_servers, endpoint)
  335. }
  336. return _name_servers[:], true
  337. }
  338. load_hosts :: proc(hosts_file_path: string, allocator := context.allocator) -> (hosts: []DNS_Host_Entry, ok: bool) {
  339. context.allocator = allocator
  340. res := os.read_entire_file_from_filename(hosts_file_path, allocator) or_return
  341. defer delete(res)
  342. _hosts := make([dynamic]DNS_Host_Entry, 0, allocator)
  343. hosts_str := string(res)
  344. for line in strings.split_lines_iterator(&hosts_str) {
  345. if len(line) == 0 || line[0] == '#' {
  346. continue
  347. }
  348. splits := strings.fields(line)
  349. defer delete(splits)
  350. ip_str := splits[0]
  351. addr := parse_address(ip_str)
  352. if addr == nil {
  353. continue
  354. }
  355. for hostname in splits[1:] {
  356. if len(hostname) != 0 {
  357. append(&_hosts, DNS_Host_Entry{hostname, addr})
  358. }
  359. }
  360. }
  361. return _hosts[:], true
  362. }
  363. // www.google.com -> 3www6google3com0
  364. encode_hostname :: proc(b: ^strings.Builder, hostname: string) -> (ok: bool) {
  365. _hostname := hostname
  366. for section in strings.split_iterator(&_hostname, ".") {
  367. if len(section) > LABEL_MAX {
  368. return
  369. }
  370. strings.write_byte(b, u8(len(section)))
  371. strings.write_string(b, section)
  372. }
  373. strings.write_byte(b, 0)
  374. return true
  375. }
  376. skip_hostname :: proc(packet: []u8, start_idx: int) -> (encode_size: int, ok: bool) {
  377. out_size := 0
  378. cur_idx := start_idx
  379. iteration_max := 0
  380. top: for cur_idx < len(packet) {
  381. if packet[cur_idx] == 0 {
  382. out_size += 1
  383. break
  384. }
  385. if iteration_max > 255 {
  386. return
  387. }
  388. if packet[cur_idx] > 63 && packet[cur_idx] != 0xC0 {
  389. return
  390. }
  391. switch packet[cur_idx] {
  392. case 0xC0:
  393. out_size += 2
  394. break top
  395. case:
  396. label_size := int(packet[cur_idx]) + 1
  397. idx2 := cur_idx + label_size
  398. if idx2 < cur_idx + 1 || idx2 > len(packet) {
  399. return
  400. }
  401. out_size += label_size
  402. cur_idx = idx2
  403. }
  404. iteration_max += 1
  405. }
  406. if start_idx + out_size > len(packet) {
  407. return
  408. }
  409. return out_size, true
  410. }
  411. decode_hostname :: proc(packet: []u8, start_idx: int, allocator := context.allocator) -> (hostname: string, encode_size: int, ok: bool) {
  412. output := [NAME_MAX]u8{}
  413. b := strings.builder_from_slice(output[:])
  414. // If you're on level 0, update out_bytes, everything through a pointer
  415. // doesn't count towards this hostname's packet length
  416. // Evaluate tokens to generate the hostname
  417. out_size := 0
  418. level := 0
  419. print_size := 0
  420. cur_idx := start_idx
  421. iteration_max := 0
  422. labels_added := 0
  423. for cur_idx < len(packet) {
  424. if packet[cur_idx] == 0 {
  425. if (level == 0) {
  426. out_size += 1
  427. }
  428. break
  429. }
  430. if iteration_max > 255 {
  431. return
  432. }
  433. if packet[cur_idx] > 63 && packet[cur_idx] != 0xC0 {
  434. return
  435. }
  436. switch packet[cur_idx] {
  437. // This is a offset to more data in the packet, jump to it
  438. case 0xC0:
  439. pkt := packet[cur_idx:cur_idx+2]
  440. val := (^u16be)(raw_data(pkt))^
  441. offset := int(val & 0x3FFF)
  442. if offset > len(packet) {
  443. return
  444. }
  445. cur_idx = offset
  446. if (level == 0) {
  447. out_size += 2
  448. level += 1
  449. }
  450. // This is a label, insert it into the hostname
  451. case:
  452. label_size := int(packet[cur_idx])
  453. idx2 := cur_idx + label_size + 1
  454. if idx2 < cur_idx + 1 || idx2 > len(packet) {
  455. return
  456. }
  457. if print_size + label_size + 1 > NAME_MAX {
  458. return
  459. }
  460. if labels_added > 0 {
  461. strings.write_byte(&b, '.')
  462. }
  463. strings.write_bytes(&b, packet[cur_idx+1:idx2])
  464. print_size += label_size + 1
  465. labels_added += 1
  466. cur_idx = idx2
  467. if (level == 0) {
  468. out_size += label_size + 1
  469. }
  470. }
  471. iteration_max += 1
  472. }
  473. if start_idx + out_size > len(packet) {
  474. return
  475. }
  476. return strings.clone(strings.to_string(b), allocator), out_size, true
  477. }
  478. // Uses RFC 952 & RFC 1123
  479. validate_hostname :: proc(hostname: string) -> (ok: bool) {
  480. if len(hostname) > 255 || len(hostname) == 0 {
  481. return
  482. }
  483. if hostname[0] == '-' {
  484. return
  485. }
  486. _hostname := hostname
  487. for label in strings.split_iterator(&_hostname, ".") {
  488. if len(label) > 63 || len(label) == 0 {
  489. return
  490. }
  491. for ch in label {
  492. switch ch {
  493. case:
  494. return
  495. case 'a'..='z', 'A'..='Z', '0'..='9', '-':
  496. continue
  497. }
  498. }
  499. }
  500. return true
  501. }
  502. parse_record :: proc(packet: []u8, cur_off: ^int, filter: DNS_Record_Type = nil) -> (record: DNS_Record, ok: bool) {
  503. record_buf := packet[cur_off^:]
  504. srv_record_name, hn_sz := decode_hostname(packet, cur_off^, context.temp_allocator) or_return
  505. // TODO(tetra): Not sure what we should call this.
  506. // Is it really only used in SRVs?
  507. // Maybe some refactoring is required?
  508. ahdr_sz := size_of(DNS_Record_Header)
  509. if len(record_buf) - hn_sz < ahdr_sz {
  510. return
  511. }
  512. record_hdr_bytes := record_buf[hn_sz:hn_sz+ahdr_sz]
  513. record_hdr := cast(^DNS_Record_Header)raw_data(record_hdr_bytes)
  514. data_sz := record_hdr.length
  515. data_off := cur_off^ + int(hn_sz) + int(ahdr_sz)
  516. data := packet[data_off:data_off+int(data_sz)]
  517. cur_off^ += int(hn_sz) + int(ahdr_sz) + int(data_sz)
  518. if u16be(filter) != record_hdr.type {
  519. return nil, true
  520. }
  521. _record: DNS_Record
  522. #partial switch DNS_Record_Type(record_hdr.type) {
  523. case .IP4:
  524. if len(data) != 4 {
  525. return
  526. }
  527. addr := (^IP4_Address)(raw_data(data))^
  528. _record = DNS_Record_IP4{
  529. base = DNS_Record_Base{
  530. record_name = strings.clone(srv_record_name),
  531. ttl_seconds = u32(record_hdr.ttl),
  532. },
  533. address = addr,
  534. }
  535. case .IP6:
  536. if len(data) != 16 {
  537. return
  538. }
  539. addr := (^IP6_Address)(raw_data(data))^
  540. _record = DNS_Record_IP6{
  541. base = DNS_Record_Base{
  542. record_name = strings.clone(srv_record_name),
  543. ttl_seconds = u32(record_hdr.ttl),
  544. },
  545. address = addr,
  546. }
  547. case .CNAME:
  548. hostname, _ := decode_hostname(packet, data_off) or_return
  549. _record = DNS_Record_CNAME{
  550. base = DNS_Record_Base{
  551. record_name = strings.clone(srv_record_name),
  552. ttl_seconds = u32(record_hdr.ttl),
  553. },
  554. host_name = hostname,
  555. }
  556. case .TXT:
  557. _record = DNS_Record_TXT{
  558. base = DNS_Record_Base{
  559. record_name = strings.clone(srv_record_name),
  560. ttl_seconds = u32(record_hdr.ttl),
  561. },
  562. value = strings.clone(string(data)),
  563. }
  564. case .NS:
  565. name, _ := decode_hostname(packet, data_off) or_return
  566. _record = DNS_Record_NS{
  567. base = DNS_Record_Base{
  568. record_name = strings.clone(srv_record_name),
  569. ttl_seconds = u32(record_hdr.ttl),
  570. },
  571. host_name = name,
  572. }
  573. case .SRV:
  574. if len(data) <= 6 {
  575. return
  576. }
  577. _data := mem.slice_data_cast([]u16be, data)
  578. priority, weight, port := _data[0], _data[1], _data[2]
  579. target, _ := decode_hostname(packet, data_off + (size_of(u16be) * 3)) or_return
  580. // NOTE(tetra): Srv record name should be of the form '_servicename._protocol.hostname'
  581. // The record name is the name of the record.
  582. // Not to be confused with the _target_ of the record, which is--in combination with the port--what we're looking up
  583. // by making this request in the first place.
  584. // NOTE(Jeroen): Service Name and Protocol Name can probably just be string slices into the record name.
  585. // It's already cloned, after all. I wouldn't put them on the temp allocator like this.
  586. parts := strings.split_n(srv_record_name, ".", 3, context.temp_allocator)
  587. if len(parts) != 3 {
  588. return
  589. }
  590. service_name, protocol_name := parts[0], parts[1]
  591. _record = DNS_Record_SRV{
  592. base = DNS_Record_Base{
  593. record_name = strings.clone(srv_record_name),
  594. ttl_seconds = u32(record_hdr.ttl),
  595. },
  596. target = target,
  597. service_name = service_name,
  598. protocol_name = protocol_name,
  599. priority = int(priority),
  600. weight = int(weight),
  601. port = int(port),
  602. }
  603. case .MX:
  604. if len(data) <= 2 {
  605. return
  606. }
  607. preference: u16be = mem.slice_data_cast([]u16be, data)[0]
  608. hostname, _ := decode_hostname(packet, data_off + size_of(u16be)) or_return
  609. _record = DNS_Record_MX{
  610. base = DNS_Record_Base{
  611. record_name = strings.clone(srv_record_name),
  612. ttl_seconds = u32(record_hdr.ttl),
  613. },
  614. host_name = hostname,
  615. preference = int(preference),
  616. }
  617. case:
  618. return
  619. }
  620. return _record, true
  621. }
  622. /*
  623. DNS Query Response Format:
  624. - DNS_Header (packed)
  625. - Query Count
  626. - Answer Count
  627. - Authority Count
  628. - Additional Count
  629. - Query[]
  630. - Hostname -- encoded
  631. - Type
  632. - Class
  633. - Answer[]
  634. - DNS Record Data
  635. - Authority[]
  636. - DNS Record Data
  637. - Additional[]
  638. - DNS Record Data
  639. DNS Record Data:
  640. - DNS_Record_Header
  641. - Data[]
  642. */
  643. parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, ok: bool) {
  644. context.allocator = allocator
  645. HEADER_SIZE_BYTES :: 12
  646. if len(response) < HEADER_SIZE_BYTES {
  647. return
  648. }
  649. _records := make([dynamic]DNS_Record, 0)
  650. dns_hdr_chunks := mem.slice_data_cast([]u16be, response[:HEADER_SIZE_BYTES])
  651. hdr := unpack_dns_header(dns_hdr_chunks[0], dns_hdr_chunks[1])
  652. if !hdr.is_response {
  653. return
  654. }
  655. question_count := int(dns_hdr_chunks[2])
  656. if question_count != 1 {
  657. return
  658. }
  659. answer_count := int(dns_hdr_chunks[3])
  660. authority_count := int(dns_hdr_chunks[4])
  661. additional_count := int(dns_hdr_chunks[5])
  662. cur_idx := HEADER_SIZE_BYTES
  663. for _ in 0..<question_count {
  664. if cur_idx == len(response) {
  665. continue
  666. }
  667. dq_sz :: 4
  668. hn_sz := skip_hostname(response, cur_idx) or_return
  669. cur_idx += hn_sz + dq_sz
  670. }
  671. for _ in 0..<answer_count {
  672. if cur_idx == len(response) {
  673. continue
  674. }
  675. rec := parse_record(response, &cur_idx, filter) or_return
  676. if rec != nil {
  677. append(&_records, rec)
  678. }
  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. append(&_records, rec)
  687. }
  688. }
  689. for _ in 0..<additional_count {
  690. if cur_idx == len(response) {
  691. continue
  692. }
  693. rec := parse_record(response, &cur_idx, filter) or_return
  694. if rec != nil {
  695. append(&_records, rec)
  696. }
  697. }
  698. return _records[:], true
  699. }