boB Rudis
5 년 전
21개의 변경된 파일과 657개의 추가작업 그리고 47개의 파일을 삭제
@ -0,0 +1,83 @@ |
|||
R6Class( |
|||
|
|||
classname = "DNSHeader", |
|||
|
|||
public = list( |
|||
|
|||
q = NULL, |
|||
a = NULL, |
|||
id = NULL, |
|||
auth = NULL, |
|||
bitmap = 0L, |
|||
qr = NULL, |
|||
opcode = NULL, |
|||
aa = NULL, |
|||
tc = NULL, |
|||
rd = NULL, |
|||
ra = NULL, |
|||
z = NULL, |
|||
ad = NULL, |
|||
ar = NULL, |
|||
cd = NULL, |
|||
rcode = NULL, |
|||
|
|||
set_rd = function(val) { |
|||
|
|||
bits(self$bitmap, 8) <- as.integer(val) |
|||
|
|||
}, |
|||
|
|||
initialize = function(id = NULL, bitmap = NULL, q = 0L, |
|||
a = 0L, auth = 0L, ar = 0L, ...) { |
|||
|
|||
self$id <- id %||% sample(0:65535, 1) |
|||
|
|||
self$bitmap <- bitmap %||% 0L |
|||
if (is.null(bitmap)) self$set_rd(1L) |
|||
|
|||
self$q <- q |
|||
self$a <- a |
|||
self$auth <- auth |
|||
self$ar <- ar |
|||
|
|||
valid <- c("qr", "opcode", "aa", "tc", "rd", "ra", "z", "ad", "cd", "rcode") |
|||
|
|||
args <- list(...) |
|||
args <- setNames(args, tolower(colnames(args))) |
|||
args <- Filter(Negate(is.null), args[valid]) |
|||
|
|||
for (n in names(args)) self[[n]] <- args[[n]] |
|||
|
|||
}, |
|||
|
|||
parse = function(buf, buf_pos = 1L) { |
|||
|
|||
self$id <- readBin(buf[buf_pos:(buf_pos+1)], "int", size = 2, endian = "big") |
|||
self$bitmap <- readBin(buf[(buf_pos+2):(buf_pos+3)], "int", size = 2, endian = "big") |
|||
self$q <- readBin(buf[(buf_pos+4):(buf_pos+5)], "int", size = 2, endian = "big") |
|||
self$a <- readBin(buf[(buf_pos+6):(buf_pos+7)], "int", size = 2, endian = "big") |
|||
self$auth <- readBin(buf[(buf_pos+8):(buf_pos+9)], "int", size = 2, endian = "big") |
|||
self$ar <- readBin(buf[(buf_pos+10):(buf_pos+11)], "int", size = 2, endian = "big") |
|||
|
|||
attr(self, "buflen") <- 12L |
|||
|
|||
self |
|||
|
|||
}, |
|||
|
|||
pack = function() { |
|||
|
|||
c( |
|||
writeBin(as.integer(self$id), raw(), size = 2, endian = "big"), |
|||
writeBin(as.integer(self$bitmap), raw(), size = 2, endian = "big"), |
|||
writeBin(as.integer(self$q), raw(), size = 2, endian = "big"), |
|||
writeBin(as.integer(self$a), raw(), size = 2, endian = "big"), |
|||
writeBin(as.integer(self$auth), raw(), size = 2, endian = "big"), |
|||
writeBin(as.integer(self$ar), raw(), size = 2, endian = "big") |
|||
) |
|||
|
|||
} |
|||
|
|||
) |
|||
|
|||
) -> DNSHeader |
@ -0,0 +1,43 @@ |
|||
R6Class( |
|||
|
|||
classname = "DNSLabel", |
|||
|
|||
public = list( |
|||
|
|||
label = NULL, |
|||
|
|||
initialize = function(label) { |
|||
|
|||
if (inherits(label, "DNSLabel")) { |
|||
|
|||
self$label <- label$label |
|||
|
|||
} else if (length(label) > 1) { |
|||
|
|||
self$label <- as.character(label) |
|||
|
|||
} else if (is.character(label)) { |
|||
label <- sub("\\.$", "", label) |
|||
self$label <- unlist(strsplit(label, "\\."), use.names = FALSE) |
|||
} |
|||
|
|||
}, |
|||
|
|||
encode = function() { |
|||
|
|||
sz <- nchar(self$label) |
|||
out <- raw() |
|||
for (i in seq_along(self$label)) { |
|||
out <- c(out, as.raw(sz[[i]]), charToRaw(self$label[[i]])) |
|||
} |
|||
c(out, as.raw(0x00)) |
|||
|
|||
}, |
|||
|
|||
parse = function(buf, buf_pos) { |
|||
|
|||
} |
|||
|
|||
) |
|||
|
|||
) -> DNSLabel |
@ -0,0 +1,51 @@ |
|||
R6Class( |
|||
|
|||
classname = "DNSQuestion", |
|||
|
|||
public = list( |
|||
|
|||
qname = NULL, |
|||
qtype = NULL, |
|||
qclass = NULL, |
|||
|
|||
initialize = function(qname = NULL, qtype = 1L, qclass = 1L) { |
|||
|
|||
if (inherits(qname, "DNSLabel")) { |
|||
self$qname <- qname |
|||
} else { |
|||
if (length(qname)) self$qname <- DNSLabel$new(qname) |
|||
} |
|||
|
|||
self$qtype <- qtype |
|||
self$qclass <- qclass |
|||
|
|||
}, |
|||
|
|||
pack = function() { |
|||
c( |
|||
self$qname$encode(), |
|||
writeBin(as.integer(self$qtype), raw(), size = 2, endian = "big"), |
|||
writeBin(as.integer(self$qclass), raw(), size = 2, endian = "big") |
|||
) |
|||
}, |
|||
|
|||
parse = function(buf, buf_pos = 1L) { |
|||
|
|||
ret <- DNSLabel$new()$parse(buf, buf_pos) |
|||
|
|||
self$qname <- ret |
|||
|
|||
buf_pos <- buf_pos + attr(ret, "buflen") |
|||
|
|||
self$qtype <- readBin(buf[buf_pos:(buf_pos+1)], "int", size = 2, endian = "big") |
|||
self$qclass <- readBin(buf[(buf_pos+2):(buf_pos+3)], "int", size = 2, endian = "big") |
|||
|
|||
attr(self, "buflen") <- attr(ret, "buflen") + 4L |
|||
|
|||
self |
|||
|
|||
} |
|||
|
|||
) |
|||
|
|||
) -> DNSQuestion |
@ -0,0 +1,90 @@ |
|||
R6Class( |
|||
|
|||
classname = "DNSRecord", |
|||
|
|||
public = list( |
|||
|
|||
header = NULL, |
|||
questions = list(), |
|||
rr = list(), |
|||
auth = list(), |
|||
ar = list(), |
|||
|
|||
initialize = function(header = NULL, questions = NULL, |
|||
rr = NULL, q = NULL, a = NULL, |
|||
auth = NULL, ar = NULL) { |
|||
|
|||
self$header <- header %||% DNSHeader$new() |
|||
self$questions <- questions %||% list() |
|||
self$rr <- rr %||% list() |
|||
self$auth <- auth %||% list() |
|||
self$ar <- ar %||% list() |
|||
|
|||
if (length(q)) self$questions <- append(self$questions, q) |
|||
if (length(a)) self$rr <- append(self$rr, a) |
|||
|
|||
self$set_header_qa() |
|||
|
|||
}, |
|||
|
|||
question = function(qname, qtype = "A", qclass = "IN") { |
|||
DNSRecord$new( |
|||
q = DNSQuestion$new( |
|||
qname = qname, |
|||
qtype = .qtype[toupper(qtype)], |
|||
qclass = .class[toupper(qclass)] |
|||
) |
|||
) |
|||
}, |
|||
|
|||
parse = function(buf, buf_pos = 1L) { |
|||
|
|||
#self$header <- NULL |
|||
self$questions <- list() |
|||
self$rr <- list() |
|||
self$auth <- list() |
|||
self$ar <- list() |
|||
|
|||
buf_pos <- 1L |
|||
|
|||
ret <- DNSHeader$new()$parse(buf) |
|||
|
|||
self$header <- ret |
|||
|
|||
buf_pos <- attr(ret, "buflen") + 1L |
|||
|
|||
message(buf_pos) |
|||
|
|||
self$questions <- lapply(1:self$header$q, function(.idx) { |
|||
|
|||
ret <- DNSQuestion$new()$parse(buf, buf_pos) |
|||
buf_pos <<- buf_pos + attr(ret, "buflen") |
|||
ret |
|||
|
|||
}) |
|||
|
|||
self |
|||
|
|||
}, |
|||
|
|||
# Reset header q/a/auth/ar counts to match numver of records (normally done transparently) |
|||
|
|||
set_header_qa = function() { |
|||
self$header$q <- length(self$questions) |
|||
self$header$a <- length(self$rr) |
|||
self$header$auth <- length(self$auth) |
|||
self$header$ar <- length(self$ar) |
|||
}, |
|||
|
|||
pack = function() { |
|||
out <- self$header$pack() |
|||
for (q in self$questions) out <- c(out, q$pack()) |
|||
out |
|||
} |
|||
|
|||
), |
|||
|
|||
private = list( |
|||
) |
|||
|
|||
) -> DNSRecord |
@ -0,0 +1,46 @@ |
|||
#' Make a DoH Request (POST/wireformat) |
|||
#' |
|||
#' Issue a `POST` wireformat 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 |
|||
#' @examples |
|||
#' doh_post("rud.is", "A") |
|||
doh_post <- function(name, type = "A", server_path = "https://dns.quad9.net/dns-query") { |
|||
|
|||
DNSRecord$new()$question( |
|||
qname = tolower(name[1]), |
|||
qtype = toupper(type[1]), |
|||
qclass = "IN" |
|||
) -> q |
|||
|
|||
# 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 = q$pack() |
|||
) -> res |
|||
|
|||
httr::stop_for_status(res) |
|||
|
|||
res <- int_dns_wire_to_list(httr::content(res, as = "raw")) |
|||
|
|||
if (length(res)) { |
|||
out <- jsonlite::fromJSON(res) |
|||
# class(out) <- c("gdns_response", "list") |
|||
out |
|||
} else { |
|||
NULL |
|||
} |
|||
|
|||
} |
@ -0,0 +1,118 @@ |
|||
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 `GET`/`POST` requests and |
|||
#' the `extra_params` element has any needed query parameters |
|||
#' for `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> |
|||
#' - `cleanbrowsing_security`: <https://doh.cleanbrowsing.org/doh/security-filter/> |
|||
#' - `cleanbrowsing_family`: <https://doh.cleanbrowsing.org/doh/family-filter/> |
|||
#' - `cleanbrowsing_adult`: <https://doh.cleanbrowsing.org/doh/adult-filter/> |
|||
#' - `power_dns`: <https://doh.powerdns.org> |
|||
#' - `appliedprivacy`: <https://doh.appliedprivacy.net/query> |
|||
#' |
|||
#' @docType data |
|||
#' @export |
|||
list( |
|||
google = list( |
|||
url = "https://dns.google.com/experimental", |
|||
extra_params = list() |
|||
), |
|||
cloudflare = list( |
|||
url = "https://cloudflare-dns.com/dns-query", |
|||
extra_params = list( |
|||
cd = "false", |
|||
do = "true", |
|||
ct = "application/dns-json" |
|||
) |
|||
), |
|||
quad9 = list( |
|||
url = "https://dns.quad9.net/dns-query", |
|||
extra_params = list() |
|||
), |
|||
securedns_eu = list( |
|||
url = "https://doh.securedns.eu/dns-query", |
|||
extra_params = list( |
|||
edns_client_subnet = NULL |
|||
) |
|||
), |
|||
dnswarden_adblock = list( |
|||
url = "https://doh.dnswarden.com/adblock", |
|||
extra_params = 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 |
@ -0,0 +1,4 @@ |
|||
`%l0%` <- function(x, y) if (length(x) == 0) y else x |
|||
`%||%` <- function(x, y) if (is.null(x)) y else x |
|||
`%@%` <- function(x, name) attr(x, name, exact = TRUE) |
|||
`%nin%` <- function(x, table) match(x, table, nomatch = 0) == 0 |
@ -0,0 +1,9 @@ |
|||
"bits" <- function(object, bit) { |
|||
(object %/% (2^bit)) %% 2 |
|||
} |
|||
|
|||
"bits<-" <- function(object, bit, value) { |
|||
mask <- 2^bit |
|||
object <- object+(value - ((object %/% mask) %% 2))*mask |
|||
object |
|||
} |
@ -0,0 +1,29 @@ |
|||
% Generated by roxygen2: do not edit by hand |
|||
% Please edit documentation in R/doh-query.R |
|||
\name{doh_post} |
|||
\alias{doh_post} |
|||
\title{Make a DoH Request (POST/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 a \code{POST} wireformat query of type \code{type} for \code{name} to |
|||
the DoH endpoint specified at \code{server_path}. |
|||
} |
|||
\examples{ |
|||
doh_post("rud.is", "A") |
|||
} |
|||
\references{ |
|||
\url{https://tools.ietf.org/id/draft-ietf-doh-dns-over-https-05.html} |
|||
} |
@ -0,0 +1,32 @@ |
|||
% 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 11.} |
|||
\usage{ |
|||
doh_servers |
|||
} |
|||
\description{ |
|||
The \code{url} element has the URL for \code{GET}/\code{POST} requests and |
|||
the \code{extra_params} element has any needed query parameters |
|||
for \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} |
|||
\item \code{cleanbrowsing_security}: \url{https://doh.cleanbrowsing.org/doh/security-filter/} |
|||
\item \code{cleanbrowsing_family}: \url{https://doh.cleanbrowsing.org/doh/family-filter/} |
|||
\item \code{cleanbrowsing_adult}: \url{https://doh.cleanbrowsing.org/doh/adult-filter/} |
|||
\item \code{power_dns}: \url{https://doh.powerdns.org} |
|||
\item \code{appliedprivacy}: \url{https://doh.appliedprivacy.net/query} |
|||
} |
|||
} |
|||
\keyword{datasets} |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,45 @@ |
|||
#include <Rcpp.h> |
|||
|
|||
#include <getdns/getdns.h> |
|||
#include <getdns/getdns_extra.h> |
|||
|
|||
#include <arpa/inet.h> |
|||
|
|||
using namespace Rcpp; |
|||
|
|||
// [[Rcpp::export]]
|
|||
CharacterVector int_dns_wire_to_list(SEXP buf) { |
|||
|
|||
getdns_return_t r; |
|||
bool ok = true; |
|||
|
|||
getdns_dict *resp; |
|||
std::string out; |
|||
|
|||
r = getdns_wire2msg_dict(RAW(buf), LENGTH(buf), &resp); |
|||
|
|||
if (r == GETDNS_RETURN_GOOD) { |
|||
|
|||
char *charout = getdns_print_json_dict(resp, 0); |
|||
|
|||
if (charout) { |
|||
out = std::string(charout); |
|||
free(charout); |
|||
} else { |
|||
ok = false; |
|||
} |
|||
|
|||
} else { |
|||
ok = false; |
|||
} |
|||
|
|||
if (resp) getdns_dict_destroy(resp); |
|||
|
|||
if (ok) return(wrap(out)); else return(CharacterVector()); |
|||
|
|||
} |
|||
|
|||
// getdns_wire2rr_dict ( const uint8_t * wire,
|
|||
// size_t wire_sz,
|
|||
// getdns_dict ** rr_dict
|
|||
// )
|
불러오는 중...
Reference in new issue