Files
Spoofy/modules/syntax.py
Matt Keeley c99233b94a redundant
2024-08-12 15:14:58 -07:00

97 lines
3.0 KiB
Python

# modules/syntax.py
import re
def validate_record_syntax(record, record_type):
"""Validate the syntax of a DNS record (SPF or DMARC)."""
if record_type == "SPF":
mechanism_patterns = {
"all": r"^all$",
"include": r"^include:[\w\.\-]+\.[a-zA-Z]{2,}$",
"a": r"^a(:[\w\.\-]+)?$",
"mx": r"^mx(:[\w\.\-]+)?$",
"ptr": r"^ptr(:[\w\.\-]+)?$",
"ip4": r"^ip4:(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$",
"ip6": r"^ip6:[a-fA-F0-9:]+(\/\d{1,3})?$",
"exists": r"^exists:[\w\.\-]+\.[a-zA-Z]{2,}$",
}
modifier_patterns = {
"redirect": r"^redirect=[\w\.\-]+\.[a-zA-Z]{2,}$",
"exp": r"^exp=[\w\.\-]+\.[a-zA-Z]{2,}$",
}
elements = record.split()
if len(elements) == 0 or elements[0].strip().lower() != "v=spf1":
return False
for element in elements[1:]:
element = element.strip()
if ":" in element:
mechanism, value = element.split(":", 1)
elif "=" in element:
modifier, value = element.split("=", 1)
mechanism = modifier
else:
mechanism = element
value = None
if mechanism in mechanism_patterns:
pattern = mechanism_patterns[mechanism]
if value:
if not re.match(pattern, element):
return False
elif not re.match(r"^" + mechanism + r"$", element):
return False
elif mechanism in modifier_patterns:
pattern = modifier_patterns[mechanism]
if not re.match(pattern, element):
return False
else:
return False
elif record_type == "DMARC":
tag_patterns = {
"v": r"^DMARC1$",
"p": r"^(none|quarantine|reject)$",
"sp": r"^(none|quarantine|reject)$",
"pct": r"^(100|[1-9]?[0-9])$",
"rua": r"^[\w\.\-]+@[\w\.\-]+\.[a-zA-Z]{2,}$",
"ruf": r"^[\w\.\-]+@[\w\.\-]+\.[a-zA-Z]{2,}$",
"rf": r"^(afrf)$",
"fo": r"^(0|1|d|s)$",
"ri": r"^\d+$",
"aspf": r"^(r|s)$",
"adkim": r"^(r|s)$",
}
tags = record.split(";")
if len(tags) < 2 or not tags[0].strip().startswith("v=DMARC1"):
return False
for tag in tags:
tag = tag.strip()
if not tag:
continue
tag_key_value = tag.split("=")
if len(tag_key_value) != 2:
return False
tag_key, tag_value = tag_key_value
tag_key = tag_key.lower().strip()
tag_value = tag_value.strip()
if tag_key in tag_patterns:
if not re.match(tag_patterns[tag_key], tag_value):
return False
else:
return False
else:
return False
return True