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.
126 lines
3.4 KiB
126 lines
3.4 KiB
validate_mailto_list <- function(x) {
|
|
all(stri_detect_regex(unlist(strsplit(x[1], ",")), "mailto:[^@]+@[[:print:]]+$"))
|
|
}
|
|
|
|
validate_fo <- function(x) {
|
|
|
|
x <- x[1]
|
|
stri_detect_regex(x, "[01ds]{1}") |
|
|
(all(stri_detect_regex(unlist(strsplit(x, ":")), "[01ds]{1}")))
|
|
|
|
}
|
|
|
|
#' Validate a DMARC Record
|
|
#'
|
|
#' @param record The DMARC TXT record value
|
|
#' @param fqdn The FQDN used (e.g. `_dmarc.example.com`)
|
|
#' @param nameserver character vector of nameservers
|
|
#' @export
|
|
validate_dmarc_record <- function(record, fqdn, nameservers = checkdmarc_default_nameservers) {
|
|
|
|
fqdn <- fqdn[1]
|
|
|
|
notes <- c()
|
|
|
|
if ((!length(record)) || (is.na(record[1]))) {
|
|
|
|
notes <- c(notes, "No DMARC record found.")
|
|
|
|
data.frame(
|
|
fqdn = fqdn,
|
|
key = NA_character_,
|
|
value = NA_character_,
|
|
valid = NA_character_,
|
|
stringsAsFactors = FALSE
|
|
) -> out
|
|
|
|
attr(out, "valid") <- FALSE
|
|
|
|
} else {
|
|
|
|
record <- stri_trim_both(record)
|
|
|
|
if (stri_detect_regex(record, "^v=DMARC1")) {
|
|
|
|
record <- stri_replace_first_regex(record, "^v=DMARC1;[[:space:]]*", "")
|
|
|
|
split_rec <- stri_replace_last_regex(record, ";$", "")
|
|
split_rec <- unlist(stri_split_regex(split_rec, ";[[:space:]]*"))
|
|
|
|
do.call(
|
|
rbind.data.frame,
|
|
lapply(split_rec, function(field) {
|
|
vals <- stri_trim_both(unlist(stri_split_fixed(field, "=", 2)))
|
|
if (length(vals) != 2) {
|
|
notes <<- c(notes, sprintf("Malformed key-value pair found in DMARC record: %s.", paste0(vals, "||")))
|
|
data.frame(
|
|
key = character(0),
|
|
value = character(0),
|
|
stringsAsFactors = FALSE
|
|
)
|
|
} else {
|
|
tdf <- set_names(as.list(vals), c("key", "value"))
|
|
as.data.frame(tdf, stringsAsFactors = FALSE)
|
|
}
|
|
})
|
|
) -> out
|
|
|
|
out$fqdn <- fqdn
|
|
out$value <- out[["value"]]
|
|
|
|
if (!all(out[["key"]] %in% c("p", "sp", "rua", "ruf", "adkim", "aspf", "ri", "fo", "rf", "pct"))) {
|
|
notes <- c(notes, "DMARC record contains one or more non-standard keys.")
|
|
}
|
|
|
|
if (any(table(out[["key"]]) > 1)) {
|
|
notes <- c(notes, "DMARC record has duplicate keys.")
|
|
}
|
|
|
|
out$valid <- sapply(out[["key"]], function(key) {
|
|
|
|
val <- out[out$key == key, "value", drop=TRUE]
|
|
|
|
switch(
|
|
key,
|
|
p = (val %in% c("none", "quarantine", "reject")),
|
|
sp = (val %in% c("none", "quarantine", "reject")),
|
|
rua = validate_mailto_list(val),
|
|
ruf = validate_mailto_list(val),
|
|
adkim = (val %in% c("r", "s", "strict", "relaxed")),
|
|
aspf = (val %in% c("r", "s", "strict", "relaxed")),
|
|
ri = stri_detect_regex(val, "^[[:digit:]]+$"),
|
|
fo = validate_fo(val),
|
|
rf = (val == "afrf"),
|
|
pct = stri_detect_regex(val, "^[[:digit:]]+$"),
|
|
FALSE
|
|
)
|
|
|
|
})
|
|
|
|
attr(out, "valid") <- ((sum(out$valid) == nrow(out))) && (length(notes) == 0)
|
|
|
|
} else { # invalid
|
|
|
|
notes <- c(notes, "Non-DMARC TXT record found in DMARC record.")
|
|
|
|
data.frame(
|
|
fqdn = fqdn,
|
|
key = NA_character_,
|
|
value = NA_character_,
|
|
valid = NA_character_,
|
|
stringsAsFactors = FALSE
|
|
) -> out
|
|
|
|
attr(out, "valid") <- FALSE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
attr(out, "notes") <- notes
|
|
|
|
class(out) <- c("tbl_df", "tbl", "data.frame")
|
|
|
|
out[,c("fqdn", "key", "value", "valid")]
|
|
|
|
}
|