Skip to content

Add more control over custom DNS domains#1288

Open
Vasco-jofra wants to merge 9 commits intoprojectdiscovery:devfrom
Vasco-jofra:VF/dns-better-custom-records
Open

Add more control over custom DNS domains#1288
Vasco-jofra wants to merge 9 commits intoprojectdiscovery:devfrom
Vasco-jofra:VF/dns-better-custom-records

Conversation

@Vasco-jofra
Copy link

@Vasco-jofra Vasco-jofra commented Dec 1, 2025

This PR adds more control over custom DNS records that can be served with interatsch.

It keeps support of the legacy format, as explained in the example YAML file.

Summary by CodeRabbit

  • New Features
    • DNS server now supports custom DNS records for multiple record types (A, AAAA, CNAME, MX, TXT, NS).
    • Added per-subdomain DNS record configuration with structured format support.
    • Custom records now take priority over default records.
    • Maintained backward compatibility with legacy DNS record format.

✏️ Tip: You can customize this high-level summary in your review settings.

ehsandeep and others added 7 commits January 11, 2025 18:33
…overy#1069)

Bumps [golang.org/x/net](https://github.com/golang/net) from 0.29.0 to 0.33.0.
- [Commits](golang/net@v0.29.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
interact.sh is no longer in use so updated docs to use one of the current domains
…y#1160)

ZAP left OWASP over a year ago. Also there was a weird dangling image tag that seems to now 404, so I trimmed it.
@coderabbitai
Copy link

coderabbitai bot commented Dec 1, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This change restructures custom DNS records from a flat string map to a type-aware, structured format with support for multiple record types (A, AAAA, CNAME, MX, TXT, NS, ANY). The DNS server now prioritizes custom records over defaults, reads from both YAML and legacy formats, and applies domain-aware filtering during query resolution.

Changes

Cohort / File(s) Change Summary
DNS Server Custom Records Refactoring
pkg/server/dns_server.go
Introduces CustomRecordConfig struct and DNSRecordsConfig map for structured, type-aware custom DNS records. Adds checkCustomResponse() method for domain and record-type filtering. Modifies newCustomDNSRecordsServer() constructor to accept domains parameter. Implements dual-format record loading (structured YAML and legacy map). Updates query handlers (A, AAAA, CNAME, MX, TXT, NS, ANY) to consult custom records first. Adds addCustomRecordToMessage() helper for constructing DNS resource records with TTL and priority support. Converts default records to CustomRecordConfig entries for uniform handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Areas requiring extra attention:

  • Constructor signature change (domains parameter addition) and verification of all call sites
  • Custom record prioritization logic across all query type handlers (A, AAAA, CNAME, MX, NS, TXT, ANY)
  • Dual-format parsing logic for YAML vs. legacy map conversion and subdomain normalization
  • Type-aware record selection and domain matching logic in checkCustomResponse()
  • MX priority handling and TTL override logic in addCustomRecordToMessage()
  • Edge cases around empty custom record lists and fallback to default behavior

Poem

🐰 Custom records, now with flair!
Types aligned, each with care,
YAML and legacy, side by side,
DNS zones with nowhere to hide!
Prioritized, filtered, TTL'd clean—
The finest DNS I've ever seen!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add more control over custom DNS domains' accurately reflects the main change: enhancing custom DNS record handling with type-aware features and per-subdomain configuration.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
pkg/server/dns_server.go (1)

569-578: MX priority 0 is conflated with "unset".

Using 0 as the sentinel for "use default priority" means users cannot explicitly set priority 0 (though this is rare in practice). If this becomes a requirement, consider using a pointer type *uint16 for Priority in the config struct to distinguish between unset and zero.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0eb8a06 and ac8d42e.

⛔ Files ignored due to path filters (1)
  • cmd/interactsh-server/example-custom-records.yaml is excluded by !**/*.yaml
📒 Files selected for processing (1)
  • pkg/server/dns_server.go (8 hunks)
🔇 Additional comments (5)
pkg/server/dns_server.go (5)

56-56: LGTM!

Constructor call correctly passes options.Domains to enable domain-aware subdomain extraction.


386-401: LGTM!

The new type definitions are well-structured with appropriate YAML tags and optional fields marked with omitempty.


411-428: LGTM!

Initialization correctly handles the new domains parameter and converts default records to the structured format. Error handling for file reading is appropriate (log and continue with defaults).


430-472: LGTM!

The dual-format parsing logic correctly prioritizes the structured format while maintaining backward compatibility with legacy configs. Required field validation ensures configuration errors are caught early.


474-542: LGTM!

The zone matching and subdomain extraction logic is correct. Type filtering properly handles all supported record types including ANY queries.

Comment on lines 165 to 170
if len(customRecords) > 0 {
for _, record := range customRecords {
h.addCustomRecordToMessage(record, zone, m)
}
return
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Error return from addCustomRecordToMessage is ignored.

If an unsupported record type is encountered or if IP parsing fails, the error will be silently discarded. Consider logging or handling the error.

 	if len(customRecords) > 0 {
 		for _, record := range customRecords {
-			h.addCustomRecordToMessage(record, zone, m)
+			if err := h.addCustomRecordToMessage(record, zone, m); err != nil {
+				gologger.Warning().Msgf("Could not add custom DNS record: %s", err)
+			}
 		}
 		return
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if len(customRecords) > 0 {
for _, record := range customRecords {
h.addCustomRecordToMessage(record, zone, m)
}
return
}
if len(customRecords) > 0 {
for _, record := range customRecords {
if err := h.addCustomRecordToMessage(record, zone, m); err != nil {
gologger.Warning().Msgf("Could not add custom DNS record: %s", err)
}
}
return
}
🤖 Prompt for AI Agents
In pkg/server/dns_server.go around lines 165–170, the loop calling
addCustomRecordToMessage ignores its error return; capture the error and handle
it instead of discarding it. Change the loop to check err :=
h.addCustomRecordToMessage(...); if err != nil { log the error with context
(record, zone, and the error) and continue to the next record, or return the
error upstream if that fits the function flow — ensure IP parse and
unsupported-type errors are surfaced via the log or returned so they are not
silently dropped.

Comment on lines 194 to 199
if len(customRecords) > 0 {
for _, record := range customRecords {
h.addCustomRecordToMessage(record, zone, m)
}
return
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same ignored error issue applies here.

For consistency, handle or log errors from addCustomRecordToMessage.

🤖 Prompt for AI Agents
In pkg/server/dns_server.go around lines 194 to 199, the loop calling
h.addCustomRecordToMessage ignores any error returned; update the loop to
capture the error from addCustomRecordToMessage and handle it consistently
(either return the error up the call chain or log it using the server/logger
with contextual details such as the record and zone). Ensure you include the
error in the log message and decide whether to continue processing remaining
records or abort by returning the error according to the function's
error-handling convention.

Comment on lines 215 to 220
if len(customRecords) > 0 {
for _, record := range customRecords {
h.addCustomRecordToMessage(record, zone, m)
}
return
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same ignored error issue applies here.

For consistency, handle or log errors from addCustomRecordToMessage.

🤖 Prompt for AI Agents
In pkg/server/dns_server.go around lines 215 to 220, the loop calling
h.addCustomRecordToMessage(record, zone, m) ignores its returned error; update
the loop to capture the error for each call and either log it (with context:
record and zone) or handle it appropriately (e.g., continue on non-fatal errors,
or return on fatal ones). Ensure the chosen logging uses the existing logger and
includes the error value so failures in addCustomRecordToMessage are not
silently ignored.

Comment on lines 251 to 256
if len(customRecords) > 0 {
for _, record := range customRecords {
h.addCustomRecordToMessage(record, zone, m)
}
return
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same ignored error issue applies here.

For consistency, handle or log errors from addCustomRecordToMessage.

🤖 Prompt for AI Agents
In pkg/server/dns_server.go around lines 251 to 256, the loop calling
h.addCustomRecordToMessage ignores returned errors; update the loop to capture
the error from addCustomRecordToMessage for each record and handle it
consistently (either log the error with context including the record and zone,
and continue, or return the error upstream if appropriate). Use the same
logging/error-handling pattern used elsewhere in this file (include zone and
record info) so errors aren't silently dropped.

Comment on lines 554 to 563
case "A":
m.Answer = append(m.Answer, &dns.A{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl},
A: net.ParseIP(record.Value),
})
case "AAAA":
m.Answer = append(m.Answer, &dns.AAAA{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl},
AAAA: net.ParseIP(record.Value),
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing IP address validation for A and AAAA records.

net.ParseIP returns nil for invalid IP strings, which would create malformed DNS responses with nil addresses. Validate the parsed IP before adding the record.

 	case "A":
+		ip := net.ParseIP(record.Value)
+		if ip == nil || ip.To4() == nil {
+			return fmt.Errorf("invalid IPv4 address for A record: %s", record.Value)
+		}
 		m.Answer = append(m.Answer, &dns.A{
 			Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl},
-			A:   net.ParseIP(record.Value),
+			A:   ip.To4(),
 		})
 	case "AAAA":
+		ip := net.ParseIP(record.Value)
+		if ip == nil || ip.To16() == nil {
+			return fmt.Errorf("invalid IPv6 address for AAAA record: %s", record.Value)
+		}
 		m.Answer = append(m.Answer, &dns.AAAA{
 			Hdr:  dns.RR_Header{Name: zone, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl},
-			AAAA: net.ParseIP(record.Value),
+			AAAA: ip.To16(),
 		})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case "A":
m.Answer = append(m.Answer, &dns.A{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl},
A: net.ParseIP(record.Value),
})
case "AAAA":
m.Answer = append(m.Answer, &dns.AAAA{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl},
AAAA: net.ParseIP(record.Value),
})
case "A":
ip := net.ParseIP(record.Value)
if ip == nil || ip.To4() == nil {
return fmt.Errorf("invalid IPv4 address for A record: %s", record.Value)
}
m.Answer = append(m.Answer, &dns.A{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl},
A: ip.To4(),
})
case "AAAA":
ip := net.ParseIP(record.Value)
if ip == nil || ip.To16() == nil {
return fmt.Errorf("invalid IPv6 address for AAAA record: %s", record.Value)
}
m.Answer = append(m.Answer, &dns.AAAA{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl},
AAAA: ip.To16(),
})
🤖 Prompt for AI Agents
In pkg/server/dns_server.go around lines 554 to 563, the code appends A/AAAA
records using net.ParseIP(record.Value) without checking for nil; this can
produce malformed responses for invalid IPs. Parse the IP into a variable, check
if it's non-nil (and for A vs AAAA ensure correct 4/16-byte family if desired),
and only append the dns.A or dns.AAAA RR when validation passes; if parsing
fails, skip the record and log or return an error as appropriate to avoid adding
nil addresses to the response.

@Mzack9999 Mzack9999 changed the base branch from main to dev December 4, 2025 08:49
Copy link
Member

@Mzack9999 Mzack9999 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • merge conflict

@dogancanbakir
Copy link
Member

@coderabbitai create an issue for this

@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the PR details including description and all comments
gh pr view 1288 --json title,body,comments --repo projectdiscovery/interactsh | jq '.'

Length of output: 12239


🏁 Script executed:

#!/bin/bash
# Fetch review comments on the PR to understand context better
gh pr view 1288 --json reviews --repo projectdiscovery/interactsh | jq '.reviews[] | {author: .author.login, state: .state, body: .body}'

# Also check for review comments on specific files
gh api "/repos/projectdiscovery/interactsh/pulls/1288/comments" | jq '.[] | {author: .user.login, body: .body, path: .path, line: .line}'

Length of output: 10569


@dogancanbakir, I'll create an issue to track the unresolved review comments from this PR.


🏁 Script executed:

#!/bin/bash
# Create an issue for the unresolved review comments on PR `#1288`
gh issue create \
  --repo projectdiscovery/interactsh \
  --title "Address review feedback for custom DNS records feature" \
  --body "## Context
This issue tracks unresolved review feedback from PR `#1288` that adds more control over custom DNS domains.

**Related PR**: https://github.com/projectdiscovery/interactsh/pull/1288
**Related Comment**: https://github.com/projectdiscovery/interactsh/pull/1288#issuecomment-3950834765
**Requested by**: `@dogancanbakir`

## Items to Address

### 1. Error Handling - Multiple Locations (Minor)
The error return from \`addCustomRecordToMessage\` is ignored in multiple places:
- Line 170
- Line 199  
- Line 220
- Line 256

**Recommendation**: Capture and log errors with context (record, zone, error message) using gologger.

### 2. IP Address Validation (Major)
Missing validation for A and AAAA records around lines 554-563. \`net.ParseIP\` can return nil for invalid IP strings, which would create malformed DNS responses.

**Recommendation**: Validate parsed IPs before adding them to DNS records:
- For A records: check \`ip.To4() != nil\`
- For AAAA records: check \`ip.To16() != nil\`
- Return appropriate error if validation fails

### 3. MX Priority Sentinel Value (Nitpick)
Using \`0\` as sentinel for \"use default priority\" means users cannot explicitly set priority 0. Consider using a pointer type \`*uint16\` if this becomes a requirement.

## Additional Notes
There may also be a merge conflict that needs resolution." \
  --assignee dogancanbakir

Length of output: 58


@dogancanbakir, I've created issue #1336 to track the unresolved review feedback from this PR.

Issue link: #1336

The issue documents:

  • Error handling improvements needed in 4 locations (ignored return values from addCustomRecordToMessage)
  • Missing IP address validation for A and AAAA records (major issue)
  • MX priority sentinel value consideration (minor)
  • Note about potential merge conflict

The issue has been assigned to you as requested.

@dogancanbakir
Copy link
Member

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain
🏁 Script executed:

#!/bin/bash
# Fetch the PR details including description and all comments
gh pr view 1288 --json title,body,comments --repo projectdiscovery/interactsh | jq '.'

Length of output: 12239

🏁 Script executed:

#!/bin/bash
# Fetch review comments on the PR to understand context better
gh pr view 1288 --json reviews --repo projectdiscovery/interactsh | jq '.reviews[] | {author: .author.login, state: .state, body: .body}'

# Also check for review comments on specific files
gh api "/repos/projectdiscovery/interactsh/pulls/1288/comments" | jq '.[] | {author: .user.login, body: .body, path: .path, line: .line}'

Length of output: 10569

@dogancanbakir, I'll create an issue to track the unresolved review comments from this PR.

🏁 Script executed:

#!/bin/bash
# Create an issue for the unresolved review comments on PR `#1288`
gh issue create \
  --repo projectdiscovery/interactsh \
  --title "Address review feedback for custom DNS records feature" \
  --body "## Context
This issue tracks unresolved review feedback from PR `#1288` that adds more control over custom DNS domains.

**Related PR**: https://github.com/projectdiscovery/interactsh/pull/1288
**Related Comment**: https://github.com/projectdiscovery/interactsh/pull/1288#issuecomment-3950834765
**Requested by**: `@dogancanbakir`

## Items to Address

### 1. Error Handling - Multiple Locations (Minor)
The error return from \`addCustomRecordToMessage\` is ignored in multiple places:
- Line 170
- Line 199  
- Line 220
- Line 256

**Recommendation**: Capture and log errors with context (record, zone, error message) using gologger.

### 2. IP Address Validation (Major)
Missing validation for A and AAAA records around lines 554-563. \`net.ParseIP\` can return nil for invalid IP strings, which would create malformed DNS responses.

**Recommendation**: Validate parsed IPs before adding them to DNS records:
- For A records: check \`ip.To4() != nil\`
- For AAAA records: check \`ip.To16() != nil\`
- Return appropriate error if validation fails

### 3. MX Priority Sentinel Value (Nitpick)
Using \`0\` as sentinel for \"use default priority\" means users cannot explicitly set priority 0. Consider using a pointer type \`*uint16\` if this becomes a requirement.

## Additional Notes
There may also be a merge conflict that needs resolution." \
  --assignee dogancanbakir

Length of output: 58

@dogancanbakir, I've created issue #1336 to track the unresolved review feedback from this PR.

Issue link: #1336

The issue documents:

  • Error handling improvements needed in 4 locations (ignored return values from addCustomRecordToMessage)
  • Missing IP address validation for A and AAAA records (major issue)
  • MX priority sentinel value consideration (minor)
  • Note about potential merge conflict

The issue has been assigned to you as requested.

@coderabbitai the issue was supposed to be for the PR not for unrsolved reviews. update the issue accordingly

@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@dogancanbakir, I'll update issue #1336 to track the PR feature instead of review feedback.


🏁 Script executed:

#!/bin/bash
# Update issue `#1336` to be about the PR feature
gh issue edit 1336 \
  --repo projectdiscovery/interactsh \
  --title "Add more control over custom DNS domains" \
  --body "## Overview
This issue tracks the implementation of enhanced custom DNS records support in interactsh.

**Related PR**: https://github.com/projectdiscovery/interactsh/pull/1288
**Requested by**: `@dogancanbakir`

## Feature Description

This feature adds more control over custom DNS records that can be served with interactsh, introducing:

- **Structured per-subdomain custom DNS records** with a new \`CustomRecordConfig\` type
- **Type-aware DNS record handling** supporting A, AAAA, CNAME, MX, TXT, NS, and ANY record types
- **Dual-format support** for configuration:
  - New structured YAML format mapping subdomains to lists of \`CustomRecordConfig\`
  - Legacy simple map format (subdomain to value, assumed as A record) for backward compatibility
- **Custom TTL and priority settings** for individual records
- **Domain-aware filtering** of custom records for specific zones and query types

## Key Changes

- New \`CustomRecordConfig\` struct with fields: Type, Value, TTL, Priority
- New \`DNSRecordsConfig\` type as \`map[string][]CustomRecordConfig\`
- Enhanced DNS query handling to prioritize custom records before falling back to defaults
- Helper method \`addCustomRecordToMessage\` to construct appropriate DNS resource records
- Subdomain normalization to lowercase and domain matching validation

## Benefits

- Greater flexibility in defining custom DNS responses
- Support for multiple record types per subdomain
- Maintains backward compatibility with existing configurations
- Enables more complex DNS testing scenarios"

Length of output: 58


@dogancanbakir, I've updated issue #1336 to track the PR feature for custom DNS domains control instead of review feedback.

Updated issue: #1336

The issue now describes:

  • The feature overview and purpose
  • Key changes introduced (CustomRecordConfig, DNSRecordsConfig, etc.)
  • Support for multiple DNS record types (A, AAAA, CNAME, MX, TXT, NS)
  • Dual-format configuration support (structured YAML + legacy format)
  • Benefits of the enhanced functionality

@neo-by-projectdiscovery-dev
Copy link

neo-by-projectdiscovery-dev bot commented Feb 24, 2026

Neo - PR Security Review

No security issues found

Highlights

  • Adds support for multiple DNS record types (A, AAAA, CNAME, MX, TXT, NS) with structured YAML configuration
  • Maintains backward compatibility with legacy simple key-value format
  • Implements proper input validation for IP addresses and domain names
Hardening Notes
  • Consider adding a maximum record count limit per subdomain in readRecordsFromFile (pkg/server/dns_server.go:521-534) to prevent potential DoS via extremely large YAML config files
  • Consider validating CNAME/MX/NS target domains against a maximum length in addCustomRecordToMessage (pkg/server/dns_server.go:652-676) to prevent potential DNS amplification abuse

Comment @neo help for available commands. · Open in Neo

@dogancanbakir dogancanbakir linked an issue Feb 24, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add more control over custom DNS domains

7 participants