Perform Secure-by-default or Woefully Insecure ‘DNS’ Queries
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

273 lines
6.5 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. #' Create a gdns DNS over TLS context and populate it with a resolver
  2. #' for use in resolution functions
  3. #'
  4. #' @note [DNS Privacy](https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers#DNSPrivacyTestServers-DoTservers)
  5. #' maintains a list of DNS over TLS servers.
  6. #' @param resolvers character vector of valid DNS over TLS resolvers;
  7. #' Defaults to Quad9 (`9.9.9.9`).
  8. #' @export
  9. #' @family query functions
  10. #' @examples
  11. #' x <- gdns_context()
  12. #' x <- gdns_context("1.1.1.1")
  13. gdns_context <- function(resolvers = "9.9.9.9") {
  14. int_gdns_context(resolvers)
  15. }
  16. #' Changes the list of resolvers in an already created context for use in resolution functions
  17. #'
  18. #' @note [DNS Privacy](https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers#DNSPrivacyTestServers-DoTservers)
  19. #' maintains a list of DNS over TLS servers.
  20. #' @param gctx gdns resolver context created with [gdns_resolver()]
  21. #' @param resolvers character vector of valid DNS over TLS resolvers
  22. #' @family context functions
  23. #' @export
  24. #' @examples
  25. #' x <- gdns_context()
  26. #' x <- gdns_update_resolvers(x, "1.1.1.1")
  27. gdns_update_resolvers<- function(gctx, resolvers) {
  28. int_gdns_update_resolvers(gctx, resolvers)
  29. }
  30. #' Initialized the context's local names namespace with values from the given hosts file.
  31. #'
  32. #' @param gctx gdns resolver context created with [gdns_resolver()]
  33. #' @param hosts_file path to a valid `hosts` file (e.g. "`/etc/hosts`). This value
  34. #' will be [path.expand()]ed.
  35. #' @export
  36. #' @family context functions
  37. #' @examples
  38. #' x <- gdns_context()
  39. #' gdns_set_hosts(x, "/etc/hosts")
  40. gdns_set_hosts<- function(gctx, hosts_file) {
  41. hosts_file <- path.expand(hosts_file[1])
  42. stopifnot(file.exists(hosts_file))
  43. int_gdns_set_hosts(gctx, hosts_file)
  44. }
  45. #' Retrieve the list of addresses in use for looking up top-level domains in use by the context.
  46. #'
  47. #' @param gctx gdns resolver context created with [gdns_resolver()]
  48. #' @export
  49. #' @family context functions
  50. # x <- gdns_context()
  51. # gdns_get_root_servers(x)
  52. gdns_get_root_servers <- function(gctx) {
  53. x <- int_gdns_get_root_servers(gctx);
  54. if (length(x)) jsonlite::fromJSON(x) else NULL
  55. }
  56. #' Arbitrary DNS queries
  57. #'
  58. #' Perform any valid resource record inquiry for a given name. See `Details`.
  59. #'
  60. #' This returns a fairly complex result object but that is the nature
  61. #' of DNS queries. You're likely going to want what is in `$replies_tree$answer`
  62. #' but the rest of the structure contains lovely metadata about the query and
  63. #' remote query environment. There will eventually be "as data frame"-ish helpers
  64. #' for this object.
  65. #'
  66. #' Valid values for `rr_type`:
  67. #' - `a`
  68. #' - `a6`
  69. #' - `aaaa`
  70. #' - `afsdb`
  71. #' - `any`
  72. #' - `apl`
  73. #' - `atma`
  74. #' - `avc`
  75. #' - `axfr`
  76. #' - `caa`
  77. #' - `cdnskey`
  78. #' - `cds`
  79. #' - `cert`
  80. #' - `cname`
  81. #' - `csync`
  82. #' - `dhcid`
  83. #' - `dlv`
  84. #' - `dname`
  85. #' - `dnskey`
  86. #' - `doa`
  87. #' - `ds`
  88. #' - `eid`
  89. #' - `eui48`
  90. #' - `eui64`
  91. #' - `gid`
  92. #' - `gpos`
  93. #' - `hinfo`
  94. #' - `hip`
  95. #' - `ipseckey`
  96. #' - `isdn`
  97. #' - `ixfr`
  98. #' - `key`
  99. #' - `kx`
  100. #' - `l32`
  101. #' - `l64`
  102. #' - `loc`
  103. #' - `lp`
  104. #' - `maila`
  105. #' - `mailb`
  106. #' - `mb`
  107. #' - `md`
  108. #' - `mf`
  109. #' - `mg`
  110. #' - `minfo`
  111. #' - `mr`
  112. #' - `mx`
  113. #' - `naptr`
  114. #' - `nid`
  115. #' - `nimloc`
  116. #' - `ninfo`
  117. #' - `ns`
  118. #' - `nsap`
  119. #' - `nsap_ptr`
  120. #' - `nsec`
  121. #' - `nsec3`
  122. #' - `nsec3param`
  123. #' - `null`
  124. #' - `nxt`
  125. #' - `openpgpkey`
  126. #' - `opt`
  127. #' - `ptr`
  128. #' - `px`
  129. #' - `rkey`
  130. #' - `rp`
  131. #' - `rrsig`
  132. #' - `rt`
  133. #' - `sig`
  134. #' - `sink`
  135. #' - `smimea`
  136. #' - `soa`
  137. #' - `spf`
  138. #' - `srv`
  139. #' - `sshfp`
  140. #' - `ta`
  141. #' - `talink`
  142. #' - `tkey`
  143. #' - `tlsa`
  144. #' - `tsig`
  145. #' - `txt`
  146. #' - `uid`
  147. #' - `uinfo`
  148. #' - `unspec`
  149. #' - `uri`
  150. #' - `wks`
  151. #' - `x25`
  152. #' - `zonemd`
  153. #'
  154. #' @note Local hosts files are ignored when using this `getdns` API endpoint
  155. #' @param gctx gdns resolver context created with [gdns_resolver()]. If `NULL` a
  156. #' temporary context will be created but is not ideal since there is overhead
  157. #' associated with context creation and garbage collection.
  158. #' @param name an entity to query for
  159. #' @param rr_type what resource record type do you want to queyr for? See `Details`.
  160. #' @param include_reporting if `TRUE` include debugging information for queries
  161. #' such as the length of time it takes for each query. Default: `FALSE`
  162. #' @references <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml>
  163. #' @family query functions
  164. #' @export
  165. #' @examples
  166. #' x <- gdns_resolver()
  167. #' gdns_query(x, "example.com")
  168. gdns_query <- function(gctx = NULL, name, rr_type = "txt", rr_class = 1L,
  169. include_reporting = FALSE) {
  170. if (is.null(gctx)) gctx <- gdns_context()
  171. stopifnot(is_gctx(gctx))
  172. rr_class <- rr_class[1]
  173. if (!rr_class %in% c(1, 3, 4, 254, 255)) rr_class <- 1
  174. rr_type <- match.arg(trimws(tolower(rr_type[1])), names(rr_types))
  175. res <- int_gdns_query(gctx, name, unname(as.integer(rr_types[rr_type])),
  176. as.logical(include_reporting))
  177. if (length(res)) {
  178. out <- jsonlite::fromJSON(res)
  179. class(out) <- c("gdns_response", "list")
  180. out
  181. } else {
  182. NULL
  183. }
  184. }
  185. #' Printer for gdns_response objects
  186. #'
  187. #' @param x a `gdns_response` object
  188. #' @param ... ignored
  189. #' @keywords internal
  190. #' @export
  191. print.gdns_response <- function(x, ...) {
  192. cat(
  193. "Query: ",
  194. x$replies_tree$question$qname[1], " ",
  195. toupper(rr_types_rev[x$replies_tree$question$qtype[1]]),
  196. "\n", sep=""
  197. )
  198. qtype <- as.character(x$replies_tree$question$qtype[[1]])
  199. ans <- x$replies_tree$answer[[1]]
  200. ans$rdata$rdata_raw <- NULL
  201. switch(
  202. qtype,
  203. "1" = {
  204. cat(
  205. "Answer: ",
  206. paste0(unlist(x$just_address_answers$address_data), collapse="\n"),
  207. "\n", sep=""
  208. )
  209. },
  210. "2" ={
  211. cat(
  212. "Answer: ",
  213. paste0(ans$rdata$nsdname, collapse=", "),
  214. "\n", sep=""
  215. )
  216. },
  217. "15" = {
  218. cat(
  219. "Answer: \n",
  220. paste0(glue::glue_data(ans$rdata, "{preference} {exchange}"), collapse="\n"),
  221. "\n", sep=""
  222. )
  223. },
  224. "16" = {
  225. rd <- ans$rdata
  226. typs <- ans$type
  227. typs <- which(typs == 16)
  228. if (length(typs)) {
  229. cat(
  230. "Answer: ",
  231. paste0(unlist(rd$txt_strings[typs]), collapse="\n"),
  232. "\n", sep=""
  233. )
  234. }
  235. },
  236. "28" = {
  237. cat(
  238. "Answer: ",
  239. paste0(unlist(x$just_address_answers$address_data), collapse="\n"),
  240. "\n", sep=""
  241. )
  242. },
  243. "257" = {
  244. cat(
  245. "Answer: \n",
  246. paste0(glue::glue_data(ans$rdata, "{flags} {tag} {value}"), collapse="\n"),
  247. "\n", sep=""
  248. )
  249. },
  250. {
  251. print(str(ans$rdata, give.attr = FALSE))
  252. }
  253. )
  254. }