How SpiderFoot Performs DNS Resolution and Zone Transfers: A Technical Deep Dive

SpiderFoot uses two dedicated plugins—sfp_dnsresolve for forward/reverse lookups and sfp_dnszonexfer for AXFR zone transfers—delegating actual DNS operations to helper methods in its core sflib.py library.

The open-source reconnaissance tool SpiderFoot automates DNS discovery through an event-driven plugin architecture. This article examines exactly how DNS resolution and zone transfers work in the smicallef/spiderfoot codebase, referencing actual source files and method implementations.

DNS Resolution in SpiderFoot

The sfp_dnsresolve Plugin Architecture

The sfp_dnsresolve plugin handles DNS resolution for hostnames, IP addresses, netblocks, and raw content discovered during a scan. It receives events from the SpiderFoot engine, processes them based on type, and emits new events for downstream consumption.

In modules/sfp_dnsresolve.py, the handleEvent method (lines 71-84) routes incoming events to appropriate resolution paths:

  • INTERNET_NAME or AFFILIATE_INTERNET_NAME → forward resolution (A/AAAA records)
  • IP_ADDRESS or AFFILIATE_IPADDR → reverse resolution (PTR records)
  • NETBLOCK_OWNER → enumerate all hosts in a netblock

The plugin delegates actual DNS operations to three core helper methods in sflib.py.

IPv4 Forward Resolution: resolveHost()

The resolveHost(host) method in sflib.py (lines 27-44) performs IPv4 forward lookups using Python's standard library:


# sflib.py – simplified implementation

def resolveHost(self, host):
    try:
        # socket.gethostbyname_ex returns (hostname, aliaslist, ipaddrlist)

        addrs = socket.gethostbyname_ex(host)
        return list(set(addrs[2]))  # deduplicated IPv4 addresses

    except socket.gaierror:
        return []

This method normalizes results, removes duplicates, and returns a clean list of IPv4 addresses.

IPv6 Forward Resolution: resolveHost6()

For IPv6 support, resolveHost6(hostname) (lines 85-104) uses socket.getaddrinfo with AF_INET6:


# sflib.py – IPv6 resolution

def resolveHost6(self, host):
    try:
        res = socket.getaddrinfo(host, None, family=socket.AF_INET6)
        return list(set([x[4][0] for x in res]))
    except Exception:
        return []

SpiderFoot automatically combines IPv4 and IPv6 results when processing hostnames.

Reverse DNS Resolution: resolveIP()

The resolveIP(ipaddr) method (lines 55-73) performs PTR lookups:


# sflib.py – reverse DNS lookup

def resolveIP(self, ipaddr):
    try:
        # socket.gethostbyaddr returns (hostname, aliaslist, ipaddrlist)

        hostname = socket.gethostbyaddr(ipaddr)[0]
        return [hostname]
    except socket.herror:
        return []

SpiderFoot optionally validates reverse-resolved names through the validatereverse configuration option, ensuring the hostname resolves back to the original IP.

Resolution Flow Example

The resolveTargets method in sfp_dnsresolve.py (lines 95-124) orchestrates these calls based on event type:


# From sfp_dnsresolve.py – simplified resolution dispatch

def resolveTargets(self, target, event):
    if target.targetType == "INTERNET_NAME":
        addrs = self.sf.resolveHost(target.targetValue) + \
                self.sf.resolveHost6(target.targetValue)
        # Emit IP_ADDRESS events for each resolved address...

    elif target.targetType == "IP_ADDRESS":
        hostnames = self.sf.resolveIP(target.targetValue)
        # Emit INTERNET_NAME events for each PTR result...

DNS Zone Transfers in SpiderFoot

The sfp_dnszonexfer Plugin

The sfp_dnszonexfer plugin attempts AXFR (full zone transfer) against authoritative name servers discovered during reconnaissance. It depends on the dnspython library for RFC-compliant zone transfer operations.

Zone Transfer Execution

When sfp_dnszonexfer.handleEvent (lines 55-68) receives a PROVIDER_DNS event containing a name server, it executes the transfer attempt (lines 99-104):


# sfp_dnszonexfer.py – AXFR zone transfer

import dns.query
import dns.zone

def tryZoneTransfer(self, ns, domain):
    try:
        # Attempt AXFR from the name server

        z = dns.zone.from_xfr(dns.query.xfr(ns, domain, timeout=30))
        return z
    except (dns.exception.DNSException, socket.error):
        return None

The dns.query.xfr() function establishes a TCP connection to the name server and requests zone transfer, while dns.zone.from_xfr() parses the response into a zone object.

Processing Zone Transfer Results

On successful transfer, the plugin (lines 106-124):

  1. Emits a RAW_DNS_RECORDS event containing the complete zone data
  2. Extracts individual hostnames from A/AAAA records
  3. Generates INTERNET_NAME events for each discovered host

# From sfp_dnszonexfer.py – result processing

if z is not None:
    # Emit raw zone data for other modules

    e = SpiderFootEvent("RAW_DNS_RECORDS", str(z), self.__name__, event)
    self.notifyListeners(e)
    
    # Extract hostnames from zone records

    for name, node in z.nodes.items():
        for rdataset in node.rdatasets:
            if rdataset.rdtype in [dns.rdatatype.A, dns.rdatatype.AAAA]:
                hostname = str(name) + "." + domain
                # Emit INTERNET_NAME event...

Event-Driven Architecture

SpiderFoot's DNS capabilities operate within a pipeline of event-driven plugins. The flow works as follows:

  1. A scan module discovers a domain or IP
  2. The engine emits an event (INTERNET_NAME, IP_ADDRESS, etc.)
  3. sfp_dnsresolve receives the event and performs resolution
  4. If name servers are identified, sfp_dnszonexfer receives PROVIDER_DNS events
  5. Successful zone transfers generate new INTERNET_NAME events
  6. The cycle continues, expanding the attack surface

This architecture allows DNS data to propagate through the system automatically, with each plugin specialized to its specific technique.

Key Source Files

File Purpose
modules/sfp_dnsresolve.py DNS forward/reverse resolution plugin
modules/sfp_dnszonexfer.py AXFR zone transfer implementation
sflib.py Core DNS helper methods (resolveHost, resolveHost6, resolveIP)
spiderfoot.py Event engine and SpiderFoot base class

Summary

  • DNS resolution in SpiderFoot uses sfp_dnsresolve calling sflib.py methods: resolveHost() for IPv4, resolveHost6() for IPv6, and resolveIP() for reverse lookups
  • Zone transfers are attempted by sfp_dnszonexfer using dnspython's dns.query.xfr() and dns.zone.from_xfr() against discovered name servers
  • The event-driven architecture allows DNS discoveries to automatically trigger additional reconnaissance modules
  • Results are validated, deduplicated, and cached to avoid redundant queries

Frequently Asked Questions

How does SpiderFoot handle both IPv4 and IPv6 DNS resolution?

SpiderFoot resolves both address families through separate methods in sflib.py. The resolveHost() method uses socket.gethostbyname_ex for IPv4, while resolveHost6() uses socket.getaddrinfo with AF_INET6 for IPv6. The sfp_dnsresolve plugin automatically combines results from both methods when processing hostname events.

What library does SpiderFoot use for DNS zone transfers?

SpiderFoot uses the dnspython library for zone transfers. Specifically, sfp_dnszonexfer.py calls dns.query.xfr() to initiate the AXFR request and dns.zone.from_xfr() to parse the response. This approach provides RFC-compliant DNS zone transfer capabilities with proper timeout and error handling.

Can SpiderFoot validate that reverse DNS results are accurate?

Yes. The sfp_dnsresolve plugin supports reverse validation through the validatereverse option. When enabled, SpiderFoot verifies that a hostname obtained from resolveIP() still resolves back to the original IP address using resolveHost() or resolveHost6(), filtering out inconsistent PTR records.

What happens when a SpiderFoot DNS zone transfer succeeds?

Upon successful AXFR, sfp_dnszonexfer emits two types of events: RAW_DNS_RECORDS containing the complete zone data as a string, and individual INTERNET_NAME events for each hostname discovered in A and AAAA records. These events feed back into the reconnaissance pipeline, potentially triggering additional modules.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →