Tools to work with the Google DNS over HTTPS API in R
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.

105 lines
4.4 KiB

ipv4_regex <-
S_GET <- safely(httr::GET)
#' Perform DNS over HTTPS queries using Google
#' Traditional DNS queries and responses are sent over UDP or TCP without
#' encryption. This is vulnerable to eavesdropping and spoofing (including
#' DNS-based Internet filtering). Responses from recursive resolvers to clients
#' are the most vulnerable to undesired or malicious changes, while
#' communications between recursive resolvers and authoritative nameservers
#' often incorporate additional protection.\cr
#' \cr
#' To address this problem, Google Public DNS offers DNS resolution over an
#' encrypted HTTPS connection. DNS-over-HTTPS greatly enhances privacy and
#' security between a client and a recursive resolver, and complements DNSSEC
#' to provide end-to-end authenticated DNS lookups.
#' To perform vectorized queries with only answers (and no metadata) use
#' \code{bulk_query()}).
#' @param name item to lookup. Valid characters are numbers, letters, hyphen, and dot. Length
#' must be between 1 and 255. Names with escaped or non-ASCII characters
#' are not supported. Internationalized domain names must use the
#' punycode format (e.g. "\code{xn--qxam}").\cr
#' \cr If an IPv4 string is input, it will be transformed into
#' a proper format for reverse lookups.
#' @param type RR type can be represented as a number in [1, 65535] or canonical
#' string (A, aaaa, etc). More information on RR types can be
#' found \href{}{here}.
#' You can use \code{255} for an \code{ANY} query.
#' @param cd (Checking Disabled) flag. Use `TRUE` to disable DNSSEC validation;
#' Default: `FALSE`.
#' @param do (DNSSEC OK) flag. Use `TRUE` include DNSSEC records (RRSIG, NSEC, NSEC3);
#' Default: `FALSE`.
#' @param random_padding clients concerned about possible side-channel privacy
#' attacks using the packet sizes of HTTPS GET requests can use this to
#' make all requests exactly the same size by padding requests with random data.
#' To prevent misinterpretation of the URL, restrict the padding characters to
#' the unreserved URL characters: upper- and lower-case letters, digits,
#' hyphen, period, underscore and tilde.
#' @param edns_client_subnet The edns0-client-subnet option. Format is an IP
#' address with a subnet mask. Examples: \code{},
#' \code{2001:700:300::/48}.\cr
#' If you are using DNS-over-HTTPS because of privacy concerns, and do
#' not want any part of your IP address to be sent to authoritative
#' nameservers for geographic location accuracy, use
#' \code{edns_client_subnet=}. Google Public DNS normally sends
#' approximate network information (usually replacing the last part of
#' your IPv4 address with zeroes). \code{} is the default.
#' @return a \code{list} with the query result or \code{NULL} if an error occurred
#' @references <>
#' @export
#' @examples
#' query("")
#' dig("", "255") # ANY query
#' query("", "MX")
#' dig("", "TXT")
#' query("")
#' dig("", "PTR")
query <- function(name, type = "1", cd = FALSE, do = FALSE,
edns_client_subnet = "",
random_padding = NULL) {
name <- name[1]
# helper to turn IPv4 addresses in to
if (grepl(ipv4_regex, name)) {
name <- paste0(c(rev(unlist(stringi::stri_split_fixed(name, ".", 4))),
sep="", collapse=".")
res <- S_GET(
url = "",
query = list(
name = name,
type = type,
cd = if (cd) 1 else 0,
do = if (do) 1 else 0,
ct = "application/x-javascript",
edns_client_subnet = edns_client_subnet,
random_padding = random_padding
if (!is.null(res$result)) {
txt <- httr::content(res$result, as="text")
txt <- stringi::stri_enc_toascii(txt)
txt <- stringi::stri_replace_all_regex(txt, "[[:cntrl:][:blank:]\\n ]+", " ")
out <- jsonlite::fromJSON(txt)
class(out) <- c("gdns_response", "list")
} else {
#' @rdname query
#' @export
dig <- query