Browse Source

GET only; done-ish

pure-r
boB Rudis 10 months ago
parent
commit
15206ff4a1
No known key found for this signature in database GPG Key ID: 1D7529BE14E2BBA9
13 changed files with 215 additions and 174 deletions
  1. +2
    -3
      DESCRIPTION
  2. +2
    -2
      NAMESPACE
  3. +7
    -0
      R/aaa.R
  4. +51
    -0
      R/doh-get.R
  5. +0
    -70
      R/doh-post.R
  6. +62
    -20
      R/globals.R
  7. +1
    -1
      R/playdoh-package.R
  8. +0
    -29
      R/zzz.R
  9. +2
    -2
      README.Rmd
  10. +29
    -22
      README.md
  11. +32
    -0
      man/doh_get.Rd
  12. +0
    -25
      man/doh_post.Rd
  13. +27
    -0
      man/doh_servers.Rd

+ 2
- 3
DESCRIPTION View File

@@ -8,7 +8,7 @@ Authors@R: c(
comment = c(ORCID = "0000-0001-5670-2640"))
)
Maintainer: Bob Rudis <bob@rud.is>
Description: Make 'DNS over HTTPS' (<https://tools.ietf.org/id/draft-ietf-doh-dns-over-https-05.html>) queries.
Description: Make 'DNS over HTTPS' (<https://tools.ietf.org/id/draft-ietf-doh-dns-over-https-05.html>) 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

+ 2
- 2
NAMESPACE View File

@@ -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)

+ 7
- 0
R/aaa.R View File

@@ -0,0 +1,7 @@
httr::user_agent(
sprintf(
"playdoh package v%s: (<%s>)",
utils::packageVersion("playdoh"),
utils::packageDescription("playdoh")$URL
)
) -> .PLAYDOH_UA

+ 51
- 0
R/doh-get.R View File

@@ -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 <https://tools.ietf.org/id/draft-ietf-doh-dns-over-https-05.html>
#' @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

}

+ 0
- 70
R/doh-post.R View File

@@ -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 <https://tools.ietf.org/id/draft-ietf-doh-dns-over-https-05.html>
#' @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
}

}

+ 62
- 20
R/globals.R View File

@@ -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`: <https://dns.google.com/experimental>
#' - `cloudflare`: <https://cloudflare-dns.com/dns-query>
#' - `quad9`: <https://dns.quad9.net/dns-query>
#' - `securedns_eu`: <https://doh.securedns.eu/dns-query>
#' - `dnswarden_adblock`: <https://doh.dnswarden.com/adblock>
#' - `dnswarden_uncensored`: <https://doh.dnswarden.com/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

+ 1
- 1
R/playdoh-package.R View File

@@ -9,6 +9,6 @@
#' @docType package
#' @keywords internal
#' @author Bob Rudis (bob@@rud.is)
#' @import httr reticulate
#' @import httr
#' @importFrom jsonlite fromJSON
NULL

+ 0
- 29
R/zzz.R View File

@@ -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)
}

}

+ 2
- 2
README.Rmd View File

@@ -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


+ 29
- 22
README.md View File

@@ -46,34 +46,41 @@ packageVersion("playdoh")
### Basic functionality

``` r
doh_post("rud.is")
## # A tibble: 1 x 6
## query qtype rname rtype rdata ttl
## <chr> <int> <chr> <int> <chr> <int>
## 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
## <chr> <int> <chr> <int> <chr> <int>
## 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



+ 32
- 0
man/doh_get.Rd View File

@@ -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}
}

+ 0
- 25
man/doh_post.Rd View File

@@ -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}
}

+ 27
- 0
man/doh_servers.Rd View File

@@ -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}

Loading…
Cancel
Save