mirror of
https://github.com/MattKeeley/Spoofy.git
synced 2026-02-03 13:33:24 +00:00
97 lines
3.0 KiB
Python
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
|