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_NAMEorAFFILIATE_INTERNET_NAME→ forward resolution (A/AAAA records)IP_ADDRESSorAFFILIATE_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):
- Emits a
RAW_DNS_RECORDSevent containing the complete zone data - Extracts individual hostnames from A/AAAA records
- Generates
INTERNET_NAMEevents 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:
- A scan module discovers a domain or IP
- The engine emits an event (
INTERNET_NAME,IP_ADDRESS, etc.) sfp_dnsresolvereceives the event and performs resolution- If name servers are identified,
sfp_dnszonexferreceivesPROVIDER_DNSevents - Successful zone transfers generate new
INTERNET_NAMEevents - 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_dnsresolvecallingsflib.pymethods:resolveHost()for IPv4,resolveHost6()for IPv6, andresolveIP()for reverse lookups - Zone transfers are attempted by
sfp_dnszonexferusingdnspython'sdns.query.xfr()anddns.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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →