From 2072f5245db273f01463a5b81615e1f87ea03333 Mon Sep 17 00:00:00 2001 From: Bob Rudis Date: Sun, 10 Apr 2016 22:35:25 -0400 Subject: [PATCH] moar spf --- DESCRIPTION | 8 +- NAMESPACE | 16 +++ R/gdns-package.r | 8 +- R/gdns.r | 37 +++++-- R/spf.r | 108 ++++++++++++++++++++ README.Rmd | 27 ++++- README.md | 245 ++++++++++++++++++++++++++++++++++++++++++--- man/bulk_query.Rd | 9 +- man/gdns.Rd | 4 +- man/has_spf.Rd | 15 +++ man/is_soft_fail.Rd | 21 ++++ man/query.Rd | 15 ++- man/spf_ipv4s.Rd | 24 +++++ man/split_spf.Rd | 18 ++++ tests/testthat/test-gdns.R | 2 +- 15 files changed, 523 insertions(+), 34 deletions(-) create mode 100644 R/spf.r create mode 100644 man/has_spf.Rd create mode 100644 man/is_soft_fail.Rd create mode 100644 man/spf_ipv4s.Rd create mode 100644 man/split_spf.Rd diff --git a/DESCRIPTION b/DESCRIPTION index d070b5d..213c67f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: gdns Title: Tools to Work with Google DNS Over HTTPS API -Version: 0.1.0.9000 +Version: 0.2.0.9000 Authors@R: c(person("Bob", "Rudis", email = "bob@rudis.net", role = c("aut", "cre"))) Maintainer: Bob Rudis Description: To address the problem of insecurity of UDP-based DNS requests, @@ -9,7 +9,8 @@ Description: To address the problem of insecurity of UDP-based DNS requests, between a client and a recursive resolver, and complements DNSSEC to provide end-to-end authenticated DNS lookups. Functions that enable querying individual requests that bulk requests that return detailed - responses and bulk requests are both provided. + responses and bulk requests are both provided. Support for reverse + lookups is also provided. License: AGPL + file LICENSE LazyData: true Encoding: UTF-8 @@ -20,5 +21,6 @@ Suggests: Imports: httr, jsonlite, - purrr + purrr, + stringi RoxygenNote: 5.0.1 diff --git a/NAMESPACE b/NAMESPACE index 3a1715d..752abb9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,9 +1,25 @@ # Generated by roxygen2: do not edit by hand export(bulk_query) +export(has_spf) +export(is_hard_fail) +export(is_soft_fail) +export(passes_all) export(query) +export(spf_exists) +export(spf_includes) +export(spf_ipv4s) +export(spf_ipv6s) +export(spf_ptrs) +export(split_spf) import(httr) importFrom(jsonlite,fromJSON) +importFrom(purrr,"%||%") importFrom(purrr,map) importFrom(purrr,map_df) importFrom(purrr,safely) +importFrom(stringi,stri_enc_toutf8) +importFrom(stringi,stri_replace_all_regex) +importFrom(stringi,stri_split_fixed) +importFrom(stringi,stri_split_regex) +importFrom(stringi,stri_trim) diff --git a/R/gdns-package.r b/R/gdns-package.r index 509e25d..e0f63ff 100644 --- a/R/gdns-package.r +++ b/R/gdns-package.r @@ -10,12 +10,16 @@ #' 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 provide end-to-end authenticated DNS lookups.\cr +#' \cr +#' Support for reverse lookups is also provided. #' #' @name gdns #' @docType package #' @author Bob Rudis (@@hrbrmstr) #' @import httr +#' @importFrom stringi stri_split_fixed stri_split_regex stri_trim +#' stri_replace_all_regex stri_enc_toutf8 #' @importFrom jsonlite fromJSON -#' @importFrom purrr safely map map_df +#' @importFrom purrr safely map map_df %||% NULL diff --git a/R/gdns.r b/R/gdns.r index 4285ce6..b2c7942 100644 --- a/R/gdns.r +++ b/R/gdns.r @@ -1,3 +1,6 @@ +ipv4_regex <- + "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" + S_GET <- purrr::safely(GET) #' Perform DNS over HTTPS queries using Google @@ -20,9 +23,12 @@ S_GET <- purrr::safely(GET) #' @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}"). +#' 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.) +#' string (A, aaaa, etc). More information on RR types can be +#' found \href{http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4}{here}. #' @param edns_client_subnet The edns0-client-subnet option. Format is an IP #' address with a subnet mask. Examples: \code{1.2.3.4/24}, #' \code{2001:700:300::/48}.\cr @@ -31,13 +37,23 @@ S_GET <- purrr::safely(GET) #' nameservers for geographic location accuracy, use #' \code{edns_client_subnet=0.0.0.0/0}. Google Public DNS normally sends #' approximate network information (usually replacing the last part of -#' your IPv4 address with zeroes). +#' your IPv4 address with zeroes). \code{0.0.0.0/0} is the default. #' @return a \code{list} with the query result or \code{NULL} if an error occurred #' @references \url{https://developers.google.com/speed/public-dns/docs/dns-over-https} #' @export #' @examples #' query("rud.is") -query <- function(name, type="1", edns_client_subnet=NULL) { +#' query("microsoft.com", "MX") +#' query("google-public-dns-a.google.com", "TXT") +#' query("apple.com") +#' query("17.142.160.59", "PTR") +query <- function(name, type="1", edns_client_subnet="0.0.0.0/0") { + + if (grepl(ipv4_regex, name)) { + name <- paste0(c(rev(unlist(stringi::stri_split_fixed(name, ".", 4))), + "in-addr.arpa."), + sep="", collapse=".") + } res <- S_GET("https://dns.google.com/resolve", query=list(name=name, @@ -46,7 +62,10 @@ query <- function(name, type="1", edns_client_subnet=NULL) { if (!is.null(res$result)) { stop_for_status(res$result) - jsonlite::fromJSON(httr::content(res$result, as="text")) + txt <- httr::content(res$result, as="text") + txt <- stringi::stri_enc_toascii(txt) + txt <- stringi::stri_replace_all_regex(txt, "[[:cntrl:][:blank:]\\n ]+", " ") + jsonlite::fromJSON(txt) } else { NULL } @@ -56,13 +75,17 @@ query <- function(name, type="1", edns_client_subnet=NULL) { #' Vectorized query, returning only answers in a data frame #' #' @param hosts character vector of hosts to query +#' @param type RR type (see \code{type} in \code{query()}). #' @return \code{data.frame} of only answers (use \code{query()} for detailed responses) #' @references \url{https://developers.google.com/speed/public-dns/docs/dns-over-https} #' @export +#' @note this is a fairly naive function. It expects \code{Answer} to be one of the +#' return value list slots. The intent for it was to make it easier +#' to do bulk forward queries. It will get smarter in future versions. #' @examples #' hosts <- c("rud.is", "dds.ec", "r-project.org", "rstudio.com", "apple.com") #' gdns::bulk_query(hosts) -bulk_query <- function(hosts) { - results <- map(hosts, gdns::query) +bulk_query <- function(hosts, type=1) { + results <- map(hosts, gdns::query, type=type) map_df(results, "Answer") } diff --git a/R/spf.r b/R/spf.r new file mode 100644 index 0000000..e513479 --- /dev/null +++ b/R/spf.r @@ -0,0 +1,108 @@ +#' Split out all SPF records in a domain's TXT record +#' +#' Given a vector of TXT records, this function will return a list of vectors +#' of all the SPF records for each. If the given TXT record is not an SPF +#' record, \code{NULL} is returned (which makes it easy to skip with \code{purrr} +#' functions). +#' +#' @param spf_rec a character vector of DNS TXT records +#' @export +split_spf <- function(spf_rec) { + purrr::map(spf_rec, .split_spf) +} + +.split_spf <- function(spf_rec) { + + if (has_spf(spf_rec)) { + spf_rec <- stringi::stri_trim(stringi::stri_replace_all_regex(spf_rec, '"', "")) + recs <- stri_trim(unlist(stringi::stri_split_regex(spf_rec, "\ +"))) + grep("v=spf1", recs, invert=TRUE, value=TRUE) + } else { + NA_character_ + } + +} + +#' Test for whether a DNS TXT record is an SPF record +#' +#' @param spf_rec a character vector of DNS TXT records +#' @export +has_spf <- function(spf_rec) { + grepl("v=spf1", spf_rec) +} + +#' SPF "all" type test +#' +#' @param spf_rec a character vector of DNS TXT records +#' @export +is_soft_fail <- function(spf_rec) { + ret <- vector("logical", length(spf_rec)) + spf_rec <- stringi::stri_trim(stringi::stri_replace_all_regex(spf_rec, '"', "")) + SPF <- which(has_spf(spf_rec)) + ret[SPF] <- grepl("~all$", stringi::stri_trim(spf_rec[SPF])) + ret[!SPF] <- NA + ret +} + +#' @rdname is_soft_fail +#' @export +is_hard_fail <- function(spf_rec) { + ret <- vector("logical", length(spf_rec)) + spf_rec <- stringi::stri_trim(stringi::stri_replace_all_regex(spf_rec, '"', "")) + SPF <- which(has_spf(spf_rec)) + ret[SPF] <- grepl("\\-all$", stringi::stri_trim(spf_rec[SPF])) + ret[!SPF] <- NA + ret +} + +#' @rdname is_soft_fail +#' @export +passes_all <- function(spf_rec) { + ret <- vector("logical", length(spf_rec)) + spf_rec <- stringi::stri_trim(stringi::stri_replace_all_regex(spf_rec, '"', "")) + SPF <- which(has_spf(spf_rec)) + ret[SPF] <- grepl("\\+all$", stringi::stri_trim(spf_rec[SPF])) + ret[!SPF] <- NA + ret +} + +#' SPF field extraction functions +#' +#' @export +spf_ipv4s <- function(spf_rec) { + purrr::map(split_spf(spf_rec), function(x) { + stringi::stri_replace_all_regex(grep("ip4", x, value=TRUE), "^ip4:", "") + }) +} + +#' @rdname spf_ipv4s +#' @export +spf_ipv6s <- function(spf_rec) { + purrr::map(split_spf(spf_rec), function(x) { + stringi::stri_replace_all_regex(grep("ip6", x, value=TRUE), "^ip6:", "") + }) +} + +#' @rdname spf_ipv4s +#' @export +spf_includes <- function(spf_rec) { + purrr::map(split_spf(spf_rec), function(x) { + stringi::stri_replace_all_regex(grep("include", x, value=TRUE), "^include:", "") + }) +} + +#' @rdname spf_ipv4s +#' @export +spf_ptrs <- function(spf_rec) { + purrr::map(split_spf(spf_rec), function(x) { + stringi::stri_replace_all_regex(grep("ptr", x, value=TRUE), "^ptr[:]", "") + }) +} + +#' @rdname spf_ipv4s +#' @export +spf_exists <- function(spf_rec) { + purrr::map(split_spf(spf_rec), function(x) { + stringi::stri_replace_all_regex(grep("exists", x, value=TRUE), "^exists:", "") + }) +} diff --git a/README.Rmd b/README.Rmd index a5a645e..d76dcac 100644 --- a/README.Rmd +++ b/README.Rmd @@ -23,11 +23,22 @@ More info at [1] '0.1.0.9000' +#> [1] '0.2.0.9000' ``` ### Test Results @@ -42,26 +53,236 @@ library(gdns) library(testthat) date() -#> [1] "Sat Apr 9 21:17:02 2016" +#> [1] "Sun Apr 10 14:51:46 2016" test_dir("tests/") #> testthat results ======================================================================================================== #> OK: 2 SKIPPED: 0 FAILED: 0 -hosts <- c("rud.is", "dds.ec", "r-project.org", "rstudio.com", "apple.com") +query("rud.is") +#> $Status +#> [1] 0 +#> +#> $TC +#> [1] FALSE +#> +#> $RD +#> [1] TRUE +#> +#> $RA +#> [1] TRUE +#> +#> $AD +#> [1] FALSE +#> +#> $CD +#> [1] FALSE +#> +#> $Question +#> name type +#> 1 rud.is. 1 +#> +#> $Answer +#> name type TTL data +#> 1 rud.is. 1 3599 104.236.112.222 +#> +#> $Additional +#> list() +#> +#> $edns_client_subnet +#> [1] "0.0.0.0/0" +#> +#> $Comment +#> [1] "Response from dns.mwebdns.net.(84.246.121.10)" +query("microsoft.com", "MX") +#> $Status +#> [1] 0 +#> +#> $TC +#> [1] FALSE +#> +#> $RD +#> [1] TRUE +#> +#> $RA +#> [1] TRUE +#> +#> $AD +#> [1] FALSE +#> +#> $CD +#> [1] FALSE +#> +#> $Question +#> name type +#> 1 microsoft.com. 15 +#> +#> $Answer +#> name type TTL data +#> 1 microsoft.com. 15 2054 10 microsoft-com.mail.protection.outlook.com. +#> +#> $Additional +#> list() +#> +#> $edns_client_subnet +#> [1] "0.0.0.0/0" + +query("google-public-dns-a.google.com", "TXT") +#> $Status +#> [1] 0 +#> +#> $TC +#> [1] FALSE +#> +#> $RD +#> [1] TRUE +#> +#> $RA +#> [1] TRUE +#> +#> $AD +#> [1] FALSE +#> +#> $CD +#> [1] FALSE +#> +#> $Question +#> name type +#> 1 google-public-dns-a.google.com. 16 +#> +#> $Answer +#> name type TTL data +#> 1 google-public-dns-a.google.com. 16 21599 "http://xkcd.com/1361/" +#> +#> $Additional +#> list() +#> +#> $edns_client_subnet +#> [1] "0.0.0.0/0" +#> +#> $Comment +#> [1] "Response from 216.239.38.10" + +query("apple.com") +#> $Status +#> [1] 0 +#> +#> $TC +#> [1] FALSE +#> +#> $RD +#> [1] TRUE +#> +#> $RA +#> [1] TRUE +#> +#> $AD +#> [1] FALSE +#> +#> $CD +#> [1] FALSE +#> +#> $Question +#> name type +#> 1 apple.com. 1 +#> +#> $Answer +#> name type TTL data +#> 1 apple.com. 1 1119 17.172.224.47 +#> 2 apple.com. 1 1119 17.178.96.59 +#> 3 apple.com. 1 1119 17.142.160.59 +#> +#> $Additional +#> list() +#> +#> $edns_client_subnet +#> [1] "0.0.0.0/0" + +query("17.142.160.59", "PTR") +#> $Status +#> [1] 0 +#> +#> $TC +#> [1] FALSE +#> +#> $RD +#> [1] TRUE +#> +#> $RA +#> [1] TRUE +#> +#> $AD +#> [1] FALSE +#> +#> $CD +#> [1] FALSE +#> +#> $Question +#> name type +#> 1 59.160.142.17.in-addr.arpa. 12 +#> +#> $Answer +#> name type TTL data +#> 1 59.160.142.17.in-addr.arpa. 12 1134 alchemysynth.com. +#> 2 59.160.142.17.in-addr.arpa. 12 1134 openni.org. +#> 3 59.160.142.17.in-addr.arpa. 12 1134 swell.am. +#> 4 59.160.142.17.in-addr.arpa. 12 1134 appleweb.net. +#> 5 59.160.142.17.in-addr.arpa. 12 1134 apple.com. +#> 6 59.160.142.17.in-addr.arpa. 12 1134 pv-apple-com.apple.com. +#> 7 59.160.142.17.in-addr.arpa. 12 1134 apple.by. +#> 8 59.160.142.17.in-addr.arpa. 12 1134 airtunes.info. +#> 9 59.160.142.17.in-addr.arpa. 12 1134 applecentre.info. +#> 10 59.160.142.17.in-addr.arpa. 12 1134 applecomputerinc.info. +#> 11 59.160.142.17.in-addr.arpa. 12 1134 appleexpo.info. +#> 12 59.160.142.17.in-addr.arpa. 12 1134 applemasters.info. +#> 13 59.160.142.17.in-addr.arpa. 12 1134 applepay.info. +#> 14 59.160.142.17.in-addr.arpa. 12 1134 applepaymerchantsupplies.info. +#> 15 59.160.142.17.in-addr.arpa. 12 1134 applepaysupplies.info. +#> 16 59.160.142.17.in-addr.arpa. 12 1134 applescript.info. +#> 17 59.160.142.17.in-addr.arpa. 12 1134 appleshare.info. +#> 18 59.160.142.17.in-addr.arpa. 12 1134 macosx.info. +#> 19 59.160.142.17.in-addr.arpa. 12 1134 powerbook.info. +#> 20 59.160.142.17.in-addr.arpa. 12 1134 powermac.info. +#> 21 59.160.142.17.in-addr.arpa. 12 1134 quicktimelive.info. +#> 22 59.160.142.17.in-addr.arpa. 12 1134 quicktimetv.info. +#> 23 59.160.142.17.in-addr.arpa. 12 1134 sherlock.info. +#> 24 59.160.142.17.in-addr.arpa. 12 1134 shopdifferent.info. +#> 25 59.160.142.17.in-addr.arpa. 12 1134 skyvines.info. +#> 26 59.160.142.17.in-addr.arpa. 12 1134 ubnw.info. +#> 27 59.160.142.17.in-addr.arpa. 12 1134 webobjects.info. +#> 28 59.160.142.17.in-addr.arpa. 12 1134 yessql.info. +#> 29 59.160.142.17.in-addr.arpa. 12 1134 ripmixburn.com. +#> 30 59.160.142.17.in-addr.arpa. 12 1134 apples-msk.ru. +#> 31 59.160.142.17.in-addr.arpa. 12 1134 icloud.se. +#> 32 59.160.142.17.in-addr.arpa. 12 1134 icloud.es. +#> 33 59.160.142.17.in-addr.arpa. 12 1134 icloud.om. +#> 34 59.160.142.17.in-addr.arpa. 12 1134 icloudo.com. +#> 35 59.160.142.17.in-addr.arpa. 12 1134 icloud.ch. +#> 36 59.160.142.17.in-addr.arpa. 12 1134 icloud.fr. +#> 37 59.160.142.17.in-addr.arpa. 12 1134 icloude.com. +#> 38 59.160.142.17.in-addr.arpa. 12 1134 camelspaceeffect.com. +#> 39 59.160.142.17.in-addr.arpa. 12 1134 camelphat.com. +#> +#> $Additional +#> list() +#> +#> $edns_client_subnet +#> [1] "0.0.0.0/0" + +hosts <- c("rud.is", "dds.ec", "r-project.org", "rstudio.com", "apple.com") gdns::bulk_query(hosts) #> Source: local data frame [7 x 4] #> #> name type TTL data #> (chr) (int) (int) (chr) -#> 1 rud.is. 1 710 104.236.112.222 -#> 2 dds.ec. 1 280 162.243.111.4 -#> 3 r-project.org. 1 6410 137.208.57.37 -#> 4 rstudio.com. 1 71 45.79.156.36 -#> 5 apple.com. 1 3290 17.172.224.47 -#> 6 apple.com. 1 3290 17.178.96.59 -#> 7 apple.com. 1 3290 17.142.160.59 +#> 1 rud.is. 1 3598 104.236.112.222 +#> 2 dds.ec. 1 299 162.243.111.4 +#> 3 r-project.org. 1 7177 137.208.57.37 +#> 4 rstudio.com. 1 3599 45.79.156.36 +#> 5 apple.com. 1 1102 17.172.224.47 +#> 6 apple.com. 1 1102 17.178.96.59 +#> 7 apple.com. 1 1102 17.142.160.59 ``` ### Code of Conduct diff --git a/man/bulk_query.Rd b/man/bulk_query.Rd index 1b202c2..1cff95e 100644 --- a/man/bulk_query.Rd +++ b/man/bulk_query.Rd @@ -4,10 +4,12 @@ \alias{bulk_query} \title{Vectorized query, returning only answers in a data frame} \usage{ -bulk_query(hosts) +bulk_query(hosts, type = 1) } \arguments{ \item{hosts}{character vector of hosts to query} + +\item{type}{RR type (see \code{type} in \code{query()}).} } \value{ \code{data.frame} of only answers (use \code{query()} for detailed responses) @@ -15,6 +17,11 @@ bulk_query(hosts) \description{ Vectorized query, returning only answers in a data frame } +\note{ +this is a fairly naive function. It expects \code{Answer} to be one of the + return value list slots. The intent for it was to make it easier + to do bulk forward queries. It will get smarter in future versions. +} \examples{ hosts <- c("rud.is", "dds.ec", "r-project.org", "rstudio.com", "apple.com") gdns::bulk_query(hosts) diff --git a/man/gdns.Rd b/man/gdns.Rd index c934204..088bb78 100644 --- a/man/gdns.Rd +++ b/man/gdns.Rd @@ -16,7 +16,9 @@ often incorporate additional protection.\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 provide end-to-end authenticated DNS lookups.\cr +\cr +Support for reverse lookups is also provided. } \author{ Bob Rudis (@hrbrmstr) diff --git a/man/has_spf.Rd b/man/has_spf.Rd new file mode 100644 index 0000000..40afa79 --- /dev/null +++ b/man/has_spf.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spf.r +\name{has_spf} +\alias{has_spf} +\title{Test for whether a DNS TXT record is an SPF record} +\usage{ +has_spf(spf_rec) +} +\arguments{ +\item{spf_rec}{a character vector of DNS TXT records} +} +\description{ +Test for whether a DNS TXT record is an SPF record +} + diff --git a/man/is_soft_fail.Rd b/man/is_soft_fail.Rd new file mode 100644 index 0000000..529a546 --- /dev/null +++ b/man/is_soft_fail.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spf.r +\name{is_soft_fail} +\alias{is_hard_fail} +\alias{is_soft_fail} +\alias{passes_all} +\title{SPF "all" type test} +\usage{ +is_soft_fail(spf_rec) + +is_hard_fail(spf_rec) + +passes_all(spf_rec) +} +\arguments{ +\item{spf_rec}{a character vector of DNS TXT records} +} +\description{ +SPF "all" type test +} + diff --git a/man/query.Rd b/man/query.Rd index b88311f..fb074bb 100644 --- a/man/query.Rd +++ b/man/query.Rd @@ -4,16 +4,19 @@ \alias{query} \title{Perform DNS over HTTPS queries using Google} \usage{ -query(name, type = "1", edns_client_subnet = NULL) +query(name, type = "1", edns_client_subnet = "0.0.0.0/0") } \arguments{ \item{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}").} +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.} \item{type}{RR type can be represented as a number in [1, 65535] or canonical -string (A, aaaa, etc.)} +string (A, aaaa, etc). More information on RR types can be +found \href{http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4}{here}.} \item{edns_client_subnet}{The edns0-client-subnet option. Format is an IP address with a subnet mask. Examples: \code{1.2.3.4/24}, @@ -23,7 +26,7 @@ not want any part of your IP address to be sent to authoritative nameservers for geographic location accuracy, use \code{edns_client_subnet=0.0.0.0/0}. Google Public DNS normally sends approximate network information (usually replacing the last part of -your IPv4 address with zeroes).} +your IPv4 address with zeroes). \code{0.0.0.0/0} is the default.} } \value{ a \code{list} with the query result or \code{NULL} if an error occurred @@ -47,6 +50,10 @@ To perform vectorized queries with only answers (and no metadata) use } \examples{ query("rud.is") +query("microsoft.com", "MX") +query("google-public-dns-a.google.com", "TXT") +query("apple.com") +query("17.142.160.59", "PTR") } \references{ \url{https://developers.google.com/speed/public-dns/docs/dns-over-https} diff --git a/man/spf_ipv4s.Rd b/man/spf_ipv4s.Rd new file mode 100644 index 0000000..e056411 --- /dev/null +++ b/man/spf_ipv4s.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spf.r +\name{spf_ipv4s} +\alias{spf_exists} +\alias{spf_includes} +\alias{spf_ipv4s} +\alias{spf_ipv6s} +\alias{spf_ptrs} +\title{SPF field extraction functions} +\usage{ +spf_ipv4s(spf_rec) + +spf_ipv6s(spf_rec) + +spf_includes(spf_rec) + +spf_ptrs(spf_rec) + +spf_exists(spf_rec) +} +\description{ +SPF field extraction functions +} + diff --git a/man/split_spf.Rd b/man/split_spf.Rd new file mode 100644 index 0000000..b94c70c --- /dev/null +++ b/man/split_spf.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spf.r +\name{split_spf} +\alias{split_spf} +\title{Split out all SPF records in a domain's TXT record} +\usage{ +split_spf(spf_rec) +} +\arguments{ +\item{spf_rec}{a character vector of DNS TXT records} +} +\description{ +Given a vector of TXT records, this function will return a list of vectors +of all the SPF records for each. If the given TXT record is not an SPF +record, \code{NULL} is returned (which makes it easy to skip with \code{purrr} +functions). +} + diff --git a/tests/testthat/test-gdns.R b/tests/testthat/test-gdns.R index e2f3b2d..858135a 100644 --- a/tests/testthat/test-gdns.R +++ b/tests/testthat/test-gdns.R @@ -1,7 +1,7 @@ context("basic functionality") test_that("we can do something", { - expect_that(length(gdns::query("example.com")), equals(8)) + expect_that(length(gdns::query("example.com")), equals(10)) doms <- c("example.com", "example.org", "example.net") qry <- gdns::bulk_query(doms)