dns_spf_chain
Trace SPF DNS records to map email authentication chains, identify included domains, IP ranges, and verify RFC 7208 compliance for domain security analysis.
Instructions
Recursively resolve SPF include chain. Shows all included domains, IP ranges, detected services (Google Workspace, Microsoft 365, SendGrid, etc.), and RFC 7208 lookup limit compliance.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| domain | Yes | Domain to trace SPF chain for | |
| max_depth | No | Maximum recursion depth (default: 10) |
Implementation Reference
- src/dns/index.ts:264-328 (handler)The `dnsSpfChain` function resolves the SPF record chain for a given domain, tracking depth, includes, and IP ranges to identify services and detect SPF lookups exceeding the RFC limit.
export async function dnsSpfChain(domain: string, maxDepth = 10): Promise<SpfChainResult> { const visited = new Set<string>(); const chain: SpfChainNode[] = []; const allIpRanges: string[] = []; const services = new Set<string>(); let totalLookups = 0; async function resolveSpf(d: string, depth: number): Promise<void> { if (depth > maxDepth || visited.has(d)) return; visited.add(d); totalLookups++; try { const txts = await dns.resolveTxt(d); const spfRecord = txts.map((t) => t.join("")).find((t) => t.startsWith("v=spf1")); if (!spfRecord) return; const mechanisms = spfRecord.split(/\s+/).filter((m) => m !== "v=spf1"); const includes: string[] = []; const ipRanges: string[] = []; for (const mech of mechanisms) { if (mech.startsWith("include:")) { const target = mech.slice(8); includes.push(target); // Identify known services for (const [pattern, service] of Object.entries(SPF_SERVICE_PATTERNS)) { if (target.includes(pattern)) services.add(service); } } else if (mech.startsWith("ip4:") || mech.startsWith("ip6:")) { const range = mech.slice(4); ipRanges.push(range); allIpRanges.push(range); } else if (mech.startsWith("a:") || mech.startsWith("mx:")) { // Count as DNS lookup totalLookups++; } } chain.push({ domain: d, depth, mechanisms, includes, ipRanges }); // Recurse into includes for (const inc of includes) { await resolveSpf(inc, depth + 1); } } catch { // Domain might not have TXT records } } await resolveSpf(domain, 0); const maxChainDepth = chain.reduce((max, n) => Math.max(max, n.depth), 0); return { domain, chainDepth: maxChainDepth, totalLookups, lookupLimit: 10, // RFC 7208 chain, allIpRanges, services: [...services], exceedsLimit: totalLookups > 10, }; } - src/protocol/tools.ts:57-66 (registration)The `dnsSpfChainTool` definition registers the `dns_spf_chain` tool and connects it to the `dnsSpfChain` handler function.
const dnsSpfChainTool: ToolDef = { name: "dns_spf_chain", description: "Recursively resolve SPF include chain. Shows all included domains, IP ranges, detected services (Google Workspace, Microsoft 365, SendGrid, etc.), and RFC 7208 lookup limit compliance.", schema: { domain: z.string().describe("Domain to trace SPF chain for"), max_depth: z.number().optional().describe("Maximum recursion depth (default: 10)"), }, execute: async (args) => json(await dnsSpfChain(args.domain as string, args.max_depth as number | undefined)), };