import { ConstantField } from "../fields/ConstantField.js"; import { EnumField } from "../fields/EnumField.js"; import { IntField } from "../fields/IntField.js"; import { Field } from "../fields/Field.js"; import { DmarcUriListField } from "../fields/DmarcUriListField.js"; import { DnsTool } from "./DnsTool.js"; import { ValidationError } from "../ValidationError.js"; export class DmarcTool extends DnsTool { static fields = [ new ConstantField("v", "DMARC1") .required() .atIndex(0), new EnumField("p", ["none", "quarantine", "reject"]) .label("Mail Receiver policy") .desc("How to handle failed validations. The email may be quarantined (usually means sent to spam) or rejected") .options(["None", "Quarantine", "Reject"]) .required() .atIndex(1) .after("v"), new EnumField("adkim", ["r", "s"]) .label("DKIM") .desc("How strictly to handle DKIM validation") .options(["Relaxed", "Strict"]) .default("r"), new EnumField("aspf", ["r", "s"]) .label("SPF") .desc("How strictly to handle SPF validation") .options(["Relaxed", "Strict"]) .default("r"), new EnumField("sp", ["none", "quarantine", "reject"]) .label("Mail Receiver policy (for subdomains)") .desc("Same as Mail Receiver policy, but applies only to subdomains. If not set, Mail Receiver policy applies to both top-level domain and subdomains") .category("advanced") .options(["None", "Quarantine", "Reject"]), new IntField("pct", 0, 100) .label("Percentage") .desc("Percentage of emails to apply DMARC validation on. Useful for split-testing and continuous rollout") .category("advanced") .default(100), new DmarcUriListField("ruf") .label("Send failure reports to") .desc("When DMARC validation fails, reports are sent to this email") .category("failure-reporting"), new EnumField("fo", ["0", "1", "d", "s"]) .label("Failure reporting options") .desc("Define how reports will be generated") .category("failure-reporting") .options([ "Generate DMARC failure report if any fail", "Generate DMARC failure report if all fail", "Generate DKIM failure report", "Generate SPF failure report", ]) .default("0"), new DmarcUriListField("rua") .label("Send aggregate feedback to") .desc("Aggregate reports will be sent to this email, if defined") .category("failure-reporting"), new IntField("ri", 0, 2 ** 32) .label("Aggregate report interval") .desc("Interval (in seconds) between aggregate reports") .category("failure-reporting") .default(86400), new Field("rf").default("afrf"), // Other values not supported ]; static categories = { "advanced": "Advanced", "failure-reporting": "Failure reporting", }; constructor(text) { super(text); } tokenize() { return this.text .replace(/;\s+$/, "") .split(/;\s*/); } getKeyValues() { const result = []; for (const token of this.tokenize()) { const [key, value] = token.split(/\s*=\s*/); if (!value) { throw new ValidationError(`Field "${key}" is missing a value`); } result.push({ key, value }); } return result; } fieldsToString() { let tokens = this.constructor.fields .filter(field => !field.defaultValue || field.getInputValue() !== field.defaultValue) .filter(field => field.getInputValue()) .map(field => field.key + "=" + field.getInputValue()); return tokens.join("; "); } }