DNS Query Execution¶
This page documents the DNS query execution mechanism in Amass, specifically the PerformQuery function and its supporting infrastructure. This covers the process of submitting DNS queries to the resolver pool, handling retries, validating responses, and detecting wildcards. For information about the resolver pool infrastructure and selection strategies, see DNS Resolver Infrastructure. For details on wildcard detection algorithms, see Wildcard Detection.
Sources:
Query Execution Architecture¶
The DNS query execution system consists of four primary components working together to perform reliable DNS lookups with automatic retry and validation.
flowchart TD
PerformQuery["PerformQuery(name, qtype)"]
BuildMsg["Build DNS Message"]
TypeCheck{"qtype == PTR?"}
QueryMsg["utils.QueryMsg(name, qtype)"]
ReverseMsg["utils.ReverseMsg(name)"]
DnsQuery["dnsQuery(msg, trusted)"]
WildcardCheck["wildcardDetected(resp, detector)"]
ValidateAnswers["Validate Answer Records"]
ExtractRR["Extract RRs by Type"]
PerformQuery --> BuildMsg
BuildMsg --> TypeCheck
TypeCheck -->|No| QueryMsg
TypeCheck -->|Yes| ReverseMsg
QueryMsg --> DnsQuery
ReverseMsg --> DnsQuery
DnsQuery --> WildcardCheck
WildcardCheck -->|Not Wildcard| ValidateAnswers
WildcardCheck -->|Wildcard| ErrorReturn["Return wildcard error"]
ValidateAnswers --> ExtractRR
ExtractRR -->|RRs Found| Success["Return []dns.RR"]
ExtractRR -->|No RRs| ErrorReturn
subgraph "Retry Loop (up to 10 attempts)"
BuildMsg
TypeCheck
QueryMsg
ReverseMsg
DnsQuery
WildcardCheck
ValidateAnswers
ExtractRR
end
Sources:
The PerformQuery Function¶
The PerformQuery function is the primary entry point for DNS resolution in Amass plugins. It implements a robust retry mechanism with wildcard detection and response validation.
Function Signature¶
Retry Logic¶
The function attempts up to 10 query iterations before giving up. This aggressive retry strategy ensures resilience against temporary network issues, rate limiting, and resolver failures.
| Attempt | Action | Failure Handling |
|---|---|---|
| 1-9 | Execute DNS query via dnsQuery |
Continue to next attempt |
| 10 | Final attempt | Return error "no valid answers" |
On each iteration:
1. Construct a DNS message using utils.QueryMsg or utils.ReverseMsg for PTR records
2. Submit query to the trusted resolver pool via dnsQuery
3. Check for wildcard responses using wildcardDetected
4. Validate that answers exist and match the requested type
5. Return immediately on success, or continue to next attempt on failure
Sources:
Low-Level Query Execution¶
The dnsQuery function handles the actual network communication with DNS resolvers through the pool abstraction.
flowchart LR
Input["dns.Msg"]
Exchange["pool.Exchange(ctx, msg)"]
CheckRcode{"Check Rcode"}
NameError["RcodeNameError"]
Success["RcodeSuccess"]
CheckAnswers{"len(Answer) > 0?"}
Input --> Exchange
Exchange --> CheckRcode
CheckRcode -->|NameError| NameError
CheckRcode -->|Success| Success
CheckRcode -->|Other| OtherError["Return unexpected response error"]
NameError --> ErrorNameNotExist["Error: name does not exist"]
Success --> CheckAnswers
CheckAnswers -->|Yes| ReturnResp["Return dns.Msg"]
CheckAnswers -->|No| ErrorNoRecord["Error: no record of this type"]
Response Code Handling¶
| Rcode | Value | Action | Error Message |
|---|---|---|---|
RcodeNameError |
3 | Return error | "name does not exist" |
RcodeSuccess |
0 | Validate answers exist | "no record of this type" if empty |
| Other | Various | Return error | "unexpected response" |
Sources:
Response Validation Flow¶
DNS responses undergo two validation stages before being returned to callers.
flowchart TD
Response["DNS Response"]
ExtractName["Extract Question Name"]
GetETLD1["publicsuffix.EffectiveTLDPlusOne(name)"]
CallDetector["detector.WildcardDetected(ctx, resp, dom)"]
WildcardResult{"Is Wildcard?"}
CheckAnswers["Check len(resp.Answer) > 0"]
FilterByType["utils.AnswersByType(resp, qtype)"]
ValidateRRs{"len(rr) > 0?"}
Response --> ExtractName
ExtractName --> GetETLD1
GetETLD1 --> CallDetector
CallDetector --> WildcardResult
WildcardResult -->|Yes| RejectWildcard["Reject: wildcard detected"]
WildcardResult -->|No| CheckAnswers
CheckAnswers --> FilterByType
FilterByType --> ValidateRRs
ValidateRRs -->|Yes| Accept["Accept Response"]
ValidateRRs -->|No| RejectNoRR["Reject: no matching RRs"]
Stage 1: Wildcard Detection¶
Before processing answers, the system checks for DNS wildcard responses:
- Extract Domain: The question name is converted to its EffectiveTLD+1 (e.g., "sub.example.com" → "example.com")
- Invoke Detector: The
detector.WildcardDetectedmethod analyzes the response against known wildcard patterns for the domain - Reject on Match: If a wildcard is detected, the response is rejected with error "wildcard detected"
Sources:
Stage 2: Answer Type Validation¶
After wildcard detection, the system validates that:
- The response contains at least one answer record (len(resp.Answer) > 0)
- At least one answer matches the requested type via utils.AnswersByType(resp, qtype)
Only answers matching the exact query type are extracted and returned.
Sources:
Resolver Pool Integration¶
Query execution relies on the trusted resolver pool, a pre-configured pool.Pool instance that manages connections to baseline resolvers.
flowchart LR
subgraph "Query Execution Layer"
PQ["PerformQuery"]
DQ["dnsQuery"]
end
subgraph "Pool Layer"
Pool["pool.Pool"]
Exchange["Exchange(ctx, msg)"]
end
subgraph "Selection Layer"
Selector["selectors.Random"]
ChooseResolver["Select Nameserver"]
end
subgraph "Connection Layer"
Conns["conn.Connections"]
TCPConn["TCP/UDP Socket"]
end
subgraph "Nameserver Layer"
NS1["servers.Nameserver
8.8.8.8"]
NS2["servers.Nameserver
1.1.1.1"]
NSN["servers.Nameserver
...78 total"]
end
PQ --> DQ
DQ -->|"trusted pool"| Exchange
Exchange --> Pool
Pool --> Selector
Selector --> ChooseResolver
ChooseResolver --> Conns
Conns --> TCPConn
ChooseResolver -.->|"Random selection"| NS1
ChooseResolver -.->|"Random selection"| NS2
ChooseResolver -.->|"Random selection"| NSN
Trusted Pool Initialization¶
The trustedResolvers function creates the pool during system startup:
| Component | Type | Configuration |
|---|---|---|
| Timeout | time.Duration |
2 seconds per query |
| Worker Threads | int |
runtime.NumCPU() concurrent workers |
| Nameservers | []types.Nameserver |
78 baseline resolvers from baselineResolvers |
| Selector | selectors.Random |
Random selection among available resolvers |
| Connection Pool | conn.Connections |
CPU-count connection handlers |
| Rate Limiting | pool.Pool |
No explicit limit (pool-level management) |
Sources:
Special Handling: PTR Queries¶
Reverse DNS (PTR) queries require special message construction because they use a different question format (in-addr.arpa or ip6.arpa).
flowchart LR
Input["IP Address String"]
TypeCheck{"qtype == dns.TypePTR?"}
QueryMsg["utils.QueryMsg(name, qtype)"]
ReverseMsg["utils.ReverseMsg(name)
Constructs *.in-addr.arpa"]
Result["dns.Msg"]
Input --> TypeCheck
TypeCheck -->|No| QueryMsg
TypeCheck -->|Yes| ReverseMsg
QueryMsg --> Result
ReverseMsg --> Result
The distinction is made at :
- Standard queries use utils.QueryMsg(name, qtype)
- PTR queries use utils.ReverseMsg(name) which converts IPs to reverse lookup format
Sources:
Error Conditions¶
The query execution system returns specific errors to help callers distinguish between different failure modes.
| Error Message | Source | Meaning | Retry Strategy |
|---|---|---|---|
"wildcard detected" |
PerformQuery |
Response matched wildcard pattern | Immediate failure, no retry |
"no valid answers" |
PerformQuery |
All 10 attempts exhausted | Fatal after 10 attempts |
"name does not exist" |
dnsQuery |
DNS returned NXDOMAIN | Propagated up, triggers retry |
"no record of this type" |
dnsQuery |
Success but no answers | Propagated up, triggers retry |
"unexpected response" |
dnsQuery |
Non-standard Rcode | Propagated up, triggers retry |
| Network errors | pool.Exchange |
TCP/UDP failure | Propagated up, triggers retry |
Sources:
Wildcard Detection Integration¶
Query execution integrates with the wildcard detection system through a dedicated wildcards.Detector instance configured to use Google's 8.8.4.4 resolver.
Detector Configuration¶
flowchart TD
TrustedResolvers["trustedResolvers()"]
CreateDetectorNS["servers.NewNameserver(8.8.4.4)"]
CreateConns["conn.New(cpus, single_selector)"]
CreateDetector["wildcards.NewDetector(serv, wconns, nil)"]
TrustedResolvers --> CreateDetectorNS
CreateDetectorNS --> CreateConns
CreateConns --> CreateDetector
CreateDetector --> DetectorInstance["detector (global var)"]
The detector is initialized once during pool creation:
1. A dedicated servers.Nameserver is created for 8.8.4.4 (Google DNS Secondary)
2. A separate connection pool is created with selectors.NewSingle (always uses 8.8.4.4)
3. The detector is instantiated with these dedicated resources
4. The detector is stored in the global detector variable for use by all queries
This separation ensures wildcard detection doesn't interfere with normal resolver pool operations and always uses a consistent, reliable resolver.
Sources:
Thread Safety and Concurrency¶
All query execution components are designed for concurrent use by multiple goroutines:
PerformQuery: Stateless function, safe to call concurrentlypool.Pool: Thread-safe, manages internal connection poolingwildcards.Detector: Thread-safe with internal caching and state management- Global Variables:
trustedanddetectorare initialized once and read-only afterward
Plugins can call PerformQuery from multiple event handlers without synchronization.