From 15206ff4a198ed5fb2a37646aa2e66d09cf6d75a Mon Sep 17 00:00:00 2001 From: boB Rudis Date: Sun, 26 May 2019 13:32:06 -0400 Subject: [PATCH] GET only; done-ish --- DESCRIPTION | 5 ++-- NAMESPACE | 4 +-- R/aaa.R | 7 +++++ R/doh-get.R | 51 +++++++++++++++++++++++++++++++++ R/doh-post.R | 70 --------------------------------------------- R/globals.R | 82 ++++++++++++++++++++++++++++++++++++++++------------- R/playdoh-package.R | 2 +- R/zzz.R | 29 ------------------- README.Rmd | 4 +-- README.md | 51 +++++++++++++++++++-------------- man/doh_get.Rd | 32 +++++++++++++++++++++ man/doh_post.Rd | 25 ---------------- man/doh_servers.Rd | 27 ++++++++++++++++++ 13 files changed, 215 insertions(+), 174 deletions(-) create mode 100644 R/aaa.R create mode 100644 R/doh-get.R delete mode 100644 R/doh-post.R delete mode 100644 R/zzz.R create mode 100644 man/doh_get.Rd delete mode 100644 man/doh_post.Rd create mode 100644 man/doh_servers.Rd diff --git a/DESCRIPTION b/DESCRIPTION index b739c87..9812fcd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -8,7 +8,7 @@ Authors@R: c( comment = c(ORCID = "0000-0001-5670-2640")) ) Maintainer: Bob Rudis -Description: Make 'DNS over HTTPS' () queries. +Description: Make 'DNS over HTTPS' () queries via 'DoH' 'GET'/'REST API'. URL: https://gitlab.com/hrbrmstr/playdoh BugReports: https://gitlab.com/hrbrmstr/playdoh/issues Encoding: UTF-8 @@ -20,7 +20,6 @@ Depends: R (>= 3.2.0) Imports: httr, - jsonlite, - reticulate + jsonlite Roxygen: list(markdown = TRUE) RoxygenNote: 6.1.1 diff --git a/NAMESPACE b/NAMESPACE index c4ab996..8b363c6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,6 @@ # Generated by roxygen2: do not edit by hand -export(doh_post) +export(doh_get) +export(doh_servers) import(httr) -import(reticulate) importFrom(jsonlite,fromJSON) diff --git a/R/aaa.R b/R/aaa.R new file mode 100644 index 0000000..6bed453 --- /dev/null +++ b/R/aaa.R @@ -0,0 +1,7 @@ +httr::user_agent( + sprintf( + "playdoh package v%s: (<%s>)", + utils::packageVersion("playdoh"), + utils::packageDescription("playdoh")$URL + ) +) -> .PLAYDOH_UA diff --git a/R/doh-get.R b/R/doh-get.R new file mode 100644 index 0000000..f3b1da9 --- /dev/null +++ b/R/doh-get.R @@ -0,0 +1,51 @@ +#' Make a DoH Request (GET/REST) +#' +#' Issue a `GET` REST API query of type `type` for `name` to the +#' DoH endpoint specified at `server_path`. +#' +#' @param name name to query for +#' @param type DNS query type (defaults to "`A`") +#' @param server_path full URL path to the DoH server quer endpoint (defaults to Quad9). +#' @param extra_params any special `GET` query parameter needed for a given server API endpoint. +#' this should be a named `list`. +#' @return `NULL` (if the query failed) or a `data.frame` (tibble) +#' @references +#' @export +#' @examples +#' doh_get("rud.is", "A") +doh_get <- function(name, type = "a", extra_params = list(), service_path = "https://9.9.9.9/dns-query") { + + stopifnot(is.list(extra_params)) + + extra_params[["name"]] <- tolower(name) + extra_params[["type"]] <- tolower(type[1]) + + httr::GET( + url = service_path, + query = extra_params, + .PLAYDOH_UA + ) -> res + + httr::stop_for_status(res) + + out <- httr::content(res, as = "text", encoding = "UTF-8") + out <- jsonlite::fromJSON(out) + + # will be NULL or a data frame + ret <- out[["Answer"]] + + # cleanup column names and add query metadata if query was OK + if (length(ret)) { + + colnames(ret) <- tolower(colnames(ret)) + + attr(ret, "question") <- out[["Question"]] + attr(ret, "flags") <- out[c("Status", "TC", "RD", "RA", "AD", "CD")] + attr(ret, "edns_client_subnet") <- out[["edns_client_subnet"]] + attr(ret, "comment") <- out[["comment"]] + + } + + ret + +} diff --git a/R/doh-post.R b/R/doh-post.R deleted file mode 100644 index 82bd663..0000000 --- a/R/doh-post.R +++ /dev/null @@ -1,70 +0,0 @@ -#' Make a POST DoH Request (wireformat) -#' -#' Issue the query of type `type` for `name` to the DoH endpoint specified at `server_path`. -#' -#' @param name name to query for -#' @param type DNS query type (defaults to "`A`") -#' @param server_path full URL path to the DoH server quer endpoint (defaults to Quad9). -#' @return `NULL` (if the query failed) or a `data.frame` (tibble) -#' @references -#' @export -doh_post <- function(name, type = "A", server_path = "https://dns.quad9.net/dns-query") { - - # for now, use python's {dnslib} as a crutch to - # encode/decode wireformat DNS questions and answers - - .dns$DNSRecord$question( - qname = tolower(name[1]), - qtype = toupper(type[1]), - qclass = "IN" - ) -> q - - qpak <- q$pack() - - # now, send it off to the server - - httr::POST( - url = server_path[1], - httr::add_headers( - `Content-Type` = "application/dns-message", - `Accept` = "application/dns-message" - ), - encode = "raw", - body = qpak - ) -> res - - httr::warn_for_status(res) - - # if the response is OK, make it a data frame - - if (httr::status_code(res) == 200) { - - r <- .dns$DNSRecord$parse(httr::content(res)) - - q <- r$get_q() - - do.call( - rbind.data.frame, - lapply(r$rr, function(.x) { - data.frame( - query = py_str(q$qname), - qtype = q$qtype, - rname = py_str(.x$rname), - rtype = .x$rtype, - rdata = py_str(.x$rdata), - ttl = .x$ttl, - stringsAsFactors = FALSE - ) - }) - - ) -> xdf - - class(xdf) <- c("tbl_df", "tbl", "data.frame") - - xdf - - } else { - NULL - } - -} diff --git a/R/globals.R b/R/globals.R index de32a9e..5b786cf 100644 --- a/R/globals.R +++ b/R/globals.R @@ -1,3 +1,65 @@ + +c( + 'A' = 1L, 'NS' = 2L, 'CNAME' = 5L, 'SOA' = 6L, 'PTR' = 12L, 'HINFO' = 13L, + 'MX' = 15L, 'TXT' = 16L, 'RP' = 17L, 'AFSDB' = 18L, 'SIG' = 24L, + 'KEY' = 25L, 'AAAA' = 28L, 'LOC' = 29, 'SRV' = 33L, 'NAPTR' = 35L, + 'KX' = 36L, 'CERT' = 37L, 'A6' = 38L, 'DNAME' = 39L, 'OPT' = 41, + 'APL' = 42L, 'DS' = 43L, 'SSHFP' = 44L, 'IPSECKEY' = 45L, 'RRSIG' = 46L, + 'NSEC' = 47L, 'DNSKEY' = 48L, 'DHCID' = 49L, 'NSEC3' = 50L, + 'NSEC3PARAM' = 51L, 'TLSA' = 52L, 'HIP' = 55L, 'CDS' = 59L, + 'CDNSKEY' = 60L, 'OPENPGPKEY' = 61L, 'SPF' = 99L, 'TKEY' = 249L, + 'TSIG' = 250L, 'IXFR' = 251L, 'AXFR' = 252L, 'ANY' = 255L, + 'URI' = 256L, 'CAA' = 257L, 'TA' = 32768L, 'DLV' = 32769L +) -> .qtype + +c( + 'IN' = 1L, + 'CS' = 2L, + 'CH' = 3L, + 'Hesiod' = 4L, + 'None' = 254L, + '*' = 255L +) -> .class + +.qr <- c('QUERY' = 0, 'RESPONSE' = 1) + +c( + 'NOERROR' = 0L, + 'FORMERR' = 1L, + 'SERVFAIL' = 2L, + 'NXDOMAIN' = 3L, + 'NOTIMP' = 4L, + 'REFUSED' = 5L, + 'YXDOMAIN' = 6L, + 'YXRRSET' = 7L, + 'NXRRSET' = 8L, + 'NOTAUTH' = 9L, + 'NOTZONE' = 10L +) -> .rcode + +c( + 'QUERY' = 0L, + 'IQUERY' = 1L, + 'STATUS' = 2L, + 'UPDATE' = 5L +) -> .opcode + +#' Built-in list of DoH Servers +#' +#' The `url` element has the URL for the `GET` requests and +#' the `extra_params` element has any needed query parameters +#' for the `GET` requests. +#' +#' The list so far. +#' - `google`: +#' - `cloudflare`: +#' - `quad9`: +#' - `securedns_eu`: +#' - `dnswarden_adblock`: +#' - `dnswarden_uncensored`: +#' +#' @docType data +#' @export list( google = list( url = "https://dns.google.com/experimental", @@ -28,25 +90,5 @@ list( dnswarden_uncensored = list( url = "https://doh.dnswarden.com/uncensored", extra_params = list() - ), - cleanbrowsing_security = list( - url = "https://doh.cleanbrowsing.org/doh/security-filter/", - extra_params = list(cd = "false") - ), - cleanbrowsing_family = list( - url = "https://doh.cleanbrowsing.org/doh/family-filter/", - extra_params = list() - ), - cleanbrowsing_adult = list( - url = "https://doh.cleanbrowsing.org/doh/adult-filter/", - extra_params = list() - ), - power_dns = list( - url = "https://doh.powerdns.org", - extra_params = list() - ), - appliedprivacy = list( - url = "https://doh.appliedprivacy.net/query", - extra_params = list() ) ) -> doh_servers \ No newline at end of file diff --git a/R/playdoh-package.R b/R/playdoh-package.R index 1a5fc23..7f86dcf 100644 --- a/R/playdoh-package.R +++ b/R/playdoh-package.R @@ -9,6 +9,6 @@ #' @docType package #' @keywords internal #' @author Bob Rudis (bob@@rud.is) -#' @import httr reticulate +#' @import httr #' @importFrom jsonlite fromJSON NULL diff --git a/R/zzz.R b/R/zzz.R deleted file mode 100644 index 0ed52e1..0000000 --- a/R/zzz.R +++ /dev/null @@ -1,29 +0,0 @@ -py_c <- reticulate::py_config() - -.dns <- NULL - -.onLoad <- function(libname, pkgname) { - - if (utils::compareVersion(py_c$version, "3.5") < 0) { - stop( - paste0( - c( - "Python 3.5+ is required. If this is installed please set RETICULATE_PYTHON ", - "to the path to the Python 3 binary on your system and try re-installing/", - "re-loading the package." - ), - collapse = "" - ) - ) - return() - } - - if (!reticulate::py_module_available("dnslib")) { - packageStartupMessage( - "The 'dnslib' Python module must be installed." - ) - } else { - .dns <<- reticulate::import("dnslib", delay_load = TRUE) - } - -} \ No newline at end of file diff --git a/README.Rmd b/README.Rmd index 486f518..1d89eeb 100644 --- a/README.Rmd +++ b/README.Rmd @@ -52,9 +52,9 @@ packageVersion("playdoh") ### Basic functionality ```{r} -doh_post("rud.is") +doh_get("rud.is") -doh_post("lenovo.com", "txt") +doh_get("lenovo.com", "txt") ``` ## playdoh Metrics diff --git a/README.md b/README.md index 8ed9504..4e6b318 100644 --- a/README.md +++ b/README.md @@ -46,34 +46,41 @@ packageVersion("playdoh") ### Basic functionality ``` r -doh_post("rud.is") -## # A tibble: 1 x 6 -## query qtype rname rtype rdata ttl -## -## 1 rud.is. 1 rud.is. 1 172.93.49.183 3600 - -doh_post("lenovo.com", "txt") -## # A tibble: 10 x 6 -## query qtype rname rtype rdata ttl -## -## 1 lenovo.co… 16 lenovo.co… 16 "\"qh7hdmqm4lzs85p704d6wsybgrpsly0j\"" 1 -## 2 lenovo.co… 16 lenovo.co… 16 "\"ece42d7743c84d6889abda7011fe6f53\"" 1 -## 3 lenovo.co… 16 lenovo.co… 16 "\"a82c74b37aa84e7c8580f0e32f4d795d\"" 1 -## 4 lenovo.co… 16 lenovo.co… 16 "\"google-site-verification=VxW_e6r_Ka7A518qfX2MmIMHGnkpGbnACsjSxKFCBw0\"" 1 -## 5 lenovo.co… 16 lenovo.co… 16 "\"google-site-verification=sHIlSlj0U6UnCDkfHp1AolWgVEvDjWvc0TR4KaysD2c\"" 1 -## 6 lenovo.co… 16 lenovo.co… 16 "\"Visit www.lenovo.com/think for information about Lenovo products and serv… 1 -## 7 lenovo.co… 16 lenovo.co… 16 "\"google-site-verification=nGgukcp60rC-gFxMOJw1NHH0B4VnSchRrlfWV-He_tE\"" 1 -## 8 lenovo.co… 16 lenovo.co… 16 "\"iHzQJvsKnyGP2Nm2qBgL3fyBJ0CC9z4GkY/flfk4EzLP8lPxWHDDPKqZWm1TkeF5kEIL+NotY… 1 -## 9 lenovo.co… 16 lenovo.co… 16 "\"v=spf1 include:spf.messagelabs.com include:_netblocks.eloqua.com ~all\"" 1 -## 10 lenovo.co… 16 lenovo.co… 16 "\"facebook-domain-verification=1r2am7c2bhzrxpqyt0mda0djoquqsi\"" 1 +doh_get("rud.is") +## name type ttl expires data +## 1 rud.is. 1 2300 Sun, 26 May 2019 18:08:58 UTC 172.93.49.183 + +doh_get("lenovo.com", "txt") +## name type ttl expires +## 1 lenovo.com. 16 7200 Sun, 26 May 2019 19:30:38 UTC +## 2 lenovo.com. 16 7200 Sun, 26 May 2019 19:30:38 UTC +## 3 lenovo.com. 16 7200 Sun, 26 May 2019 19:30:38 UTC +## 4 lenovo.com. 16 7200 Sun, 26 May 2019 19:30:38 UTC +## 5 lenovo.com. 16 7200 Sun, 26 May 2019 19:30:38 UTC +## 6 lenovo.com. 16 7200 Sun, 26 May 2019 19:30:38 UTC +## 7 lenovo.com. 16 7200 Sun, 26 May 2019 19:30:38 UTC +## 8 lenovo.com. 16 7200 Sun, 26 May 2019 19:30:38 UTC +## 9 lenovo.com. 16 7200 Sun, 26 May 2019 19:30:38 UTC +## 10 lenovo.com. 16 7200 Sun, 26 May 2019 19:30:38 UTC +## data +## 1 "facebook-domain-verification=1r2am7c2bhzrxpqyt0mda0djoquqsi" +## 2 "google-site-verification=sHIlSlj0U6UnCDkfHp1AolWgVEvDjWvc0TR4KaysD2c" +## 3 "google-site-verification=nGgukcp60rC-gFxMOJw1NHH0B4VnSchRrlfWV-He_tE" +## 4 "a82c74b37aa84e7c8580f0e32f4d795d" +## 5 "Visit www.lenovo.com/think for information about Lenovo products and services" +## 6 "qh7hdmqm4lzs85p704d6wsybgrpsly0j" +## 7 "google-site-verification=VxW_e6r_Ka7A518qfX2MmIMHGnkpGbnACsjSxKFCBw0" +## 8 "ece42d7743c84d6889abda7011fe6f53" +## 9 "iHzQJvsKnyGP2Nm2qBgL3fyBJ0CC9z4GkY/flfk4EzLP8lPxWHDDPKqZWm1TkeF5kEIL+NotYOF1wo7JtUDXXw==" +## 10 "v=spf1 include:spf.messagelabs.com include:_netblocks.eloqua.com ~all" ``` ## playdoh Metrics | Lang | \# Files | (%) | LoC | (%) | Blank lines | (%) | \# Lines | (%) | | :--- | -------: | ---: | --: | ---: | ----------: | ---: | -------: | ---: | -| R | 6 | 0.86 | 122 | 0.92 | 23 | 0.51 | 26 | 0.42 | -| Rmd | 1 | 0.14 | 11 | 0.08 | 22 | 0.49 | 36 | 0.58 | +| R | 6 | 0.86 | 107 | 0.91 | 20 | 0.48 | 47 | 0.57 | +| Rmd | 1 | 0.14 | 11 | 0.09 | 22 | 0.52 | 36 | 0.43 | ## Code of Conduct diff --git a/man/doh_get.Rd b/man/doh_get.Rd new file mode 100644 index 0000000..236e0f2 --- /dev/null +++ b/man/doh_get.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/doh-get.R +\name{doh_get} +\alias{doh_get} +\title{Make a DoH Request (GET/REST)} +\usage{ +doh_get(name, type = "a", extra_params = list(), + service_path = "https://9.9.9.9/dns-query") +} +\arguments{ +\item{name}{name to query for} + +\item{type}{DNS query type (defaults to "\code{A}")} + +\item{extra_params}{any special \code{GET} query parameter needed for a given server API endpoint. +this should be a named \code{list}.} + +\item{server_path}{full URL path to the DoH server quer endpoint (defaults to Quad9).} +} +\value{ +\code{NULL} (if the query failed) or a \code{data.frame} (tibble) +} +\description{ +Issue a \code{GET} REST API query of type \code{type} for \code{name} to the +DoH endpoint specified at \code{server_path}. +} +\examples{ +doh_get("rud.is", "A") +} +\references{ +\url{https://tools.ietf.org/id/draft-ietf-doh-dns-over-https-05.html} +} diff --git a/man/doh_post.Rd b/man/doh_post.Rd deleted file mode 100644 index 7302fb9..0000000 --- a/man/doh_post.Rd +++ /dev/null @@ -1,25 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/doh-post.R -\name{doh_post} -\alias{doh_post} -\title{Make a POST DoH Request (wireformat)} -\usage{ -doh_post(name, type = "A", - server_path = "https://dns.quad9.net/dns-query") -} -\arguments{ -\item{name}{name to query for} - -\item{type}{DNS query type (defaults to "\code{A}")} - -\item{server_path}{full URL path to the DoH server quer endpoint (defaults to Quad9).} -} -\value{ -\code{NULL} (if the query failed) or a \code{data.frame} (tibble) -} -\description{ -Issue the query of type \code{type} for \code{name} to the DoH endpoint specified at \code{server_path}. -} -\references{ -\url{https://tools.ietf.org/id/draft-ietf-doh-dns-over-https-05.html} -} diff --git a/man/doh_servers.Rd b/man/doh_servers.Rd new file mode 100644 index 0000000..fdfea90 --- /dev/null +++ b/man/doh_servers.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/globals.R +\docType{data} +\name{doh_servers} +\alias{doh_servers} +\title{Built-in list of DoH Servers} +\format{An object of class \code{list} of length 6.} +\usage{ +doh_servers +} +\description{ +The \code{url} element has the URL for the \code{GET} requests and +the \code{extra_params} element has any needed query parameters +for the \code{GET} requests. +} +\details{ +The list so far. +\itemize{ +\item \code{google}: \url{https://dns.google.com/experimental} +\item \code{cloudflare}: \url{https://cloudflare-dns.com/dns-query} +\item \code{quad9}: \url{https://dns.quad9.net/dns-query} +\item \code{securedns_eu}: \url{https://doh.securedns.eu/dns-query} +\item \code{dnswarden_adblock}: \url{https://doh.dnswarden.com/adblock} +\item \code{dnswarden_uncensored}: \url{https://doh.dnswarden.com/uncensored} +} +} +\keyword{datasets}