dns.odin 21 KB

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