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

> Explore how SpiderFoot performs DNS resolution and zone transfers using dedicated plugins and core library methods. Uncover the technical details of its DNS operations.

- Repository: [Steve Micallef/spiderfoot](https://github.com/smicallef/spiderfoot)
- Tags: deep-dive
- Published: 2026-08-15

---

**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`](https://github.com/smicallef/spiderfoot/blob/main/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`](https://github.com/smicallef/spiderfoot/blob/main/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`](https://github.com/smicallef/spiderfoot/blob/main/sflib.py).

### IPv4 Forward Resolution: `resolveHost()`

The **`resolveHost(host)`** method in [`sflib.py`](https://github.com/smicallef/spiderfoot/blob/main/sflib.py) (lines 27-44) performs IPv4 forward lookups using Python's standard library:

```python

# 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`:

```python

# 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:

```python

# 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`](https://github.com/smicallef/spiderfoot/blob/main/sfp_dnsresolve.py) (lines 95-124) orchestrates these calls based on event type:

```python

# 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):

```python

# 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

```python

# 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`](https://github.com/smicallef/spiderfoot/blob/main/modules/sfp_dnsresolve.py) | DNS forward/reverse resolution plugin |
| [`modules/sfp_dnszonexfer.py`](https://github.com/smicallef/spiderfoot/blob/main/modules/sfp_dnszonexfer.py) | AXFR zone transfer implementation |
| [`sflib.py`](https://github.com/smicallef/spiderfoot/blob/main/sflib.py) | Core DNS helper methods (`resolveHost`, `resolveHost6`, `resolveIP`) |
| [`spiderfoot.py`](https://github.com/smicallef/spiderfoot/blob/main/spiderfoot.py) | Event engine and `SpiderFoot` base class |

## Summary

- **DNS resolution** in SpiderFoot uses `sfp_dnsresolve` calling [`sflib.py`](https://github.com/smicallef/spiderfoot/blob/main/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`](https://github.com/smicallef/spiderfoot/blob/main/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`](https://github.com/smicallef/spiderfoot/blob/main/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.