@portable/dmarc
Functions
Dmarc.check
Checks a message against the DMARC policy of its From domain (RFC 9989, which replaces RFC
7489). It finds the policy record with a DNS tree walk and checks whether a passing SPF result or
DKIM signature is aligned with the From domain. DNS failures give temperror, and a From domain
that isn't a domain name gives permerror. The promise rejects only for a resolver without the
Resolver methods, or with signal.reason when signal aborts.
async function check(
input: check.Input,
options: check.Options,
): Promise<Result>;| Option | Type | Default | Description |
|---|---|---|---|
resolver | Resolver | Looks up DNS records. Pass Dns.create() or node:dns/promises. | |
organizationalDomain | ((domain: string) => string | null | undefined) | Returns the organizational domain of a domain from a public suffix list, as tldts' getDomain does, and switches from RFC 9989's DNS tree walk to RFC 7489's lookups. A missing result means the domain is its own organizational domain. | |
signal | AbortSignal | Stops the check, such as AbortSignal.timeout(10_000). The promise rejects with signal.reason. |
import * as Dmarc from '@portable/dmarc';
import * as Dns from '@portable/dns';
import * as Spf from '@portable/spf';
const resolver = Dns.create();
const spf = await Spf.check({ ip, sender, helo }, { resolver });
const dmarc = await Dmarc.check(
{
from: 'example.com',
spf,
dkim: [{ result: 'pass', domain: 'mail.example.com' }],
},
{ resolver },
);
dmarc.result; // 'pass', 'fail', 'none', 'temperror' or 'permerror'
dmarc.disposition; // 'none', 'quarantine' or 'reject'Types
Dmarc.Alignment
An authenticated identifier and whether it aligns with the From domain. Only a pass result can
align.
type Alignment = {
domain: string;
aligned: boolean;
};Dmarc.Resolver
The DNS lookups DMARC needs: a Dns.Resolver from @portable/dns, node:dns/promises, or a
plain object with these methods. check treats a rejection with code ENOTFOUND, ENODATA
or EBADNAME as no records, and any other rejection as a temporary DNS error. resolve4 and
resolveCname are called only to find out whether a subdomain exists, for a record with an
np tag.
type Resolver = Pick<Dns.Resolver, | | >;Dmarc.Result
The outcome of a DMARC check.
type Result = {
result: Result.Code;
domain: string;
policy?: Policy | undefined;
disposition: Policy;
policyDomain?: string | undefined;
record?: Record | undefined;
spf?: Alignment | undefined;
dkim: readonly Alignment[];
reason?: string | undefined;
cause?: unknown;
};Dmarc.Policy
A Domain Owner Assessment Policy (RFC 9989 section 4.7): what the domain owner asks a receiver to do with mail that fails DMARC.
type Policy = | | ;Dmarc.Record
A DMARC Policy Record, with the defaults of RFC 9989 section 4.7 filled in. Invalid tag values
are replaced by their defaults, and unknown and historic tags such as pct and ri are left
out.
type Record = {
text: string;
p: Policy;
sp?: Policy | undefined;
np?: Policy | undefined;
adkim: | ;
aspf: | ;
t: | ;
psd: | | ;
rua: readonly string[];
ruf: readonly string[];
fo: string;
};