Skip to content

Commit 57e86bd

Browse files
committed
Add --query-size flag to cap dnstt-client upstream DNS query size
Fixes e2e failures on filtered networks that block large DNS queries. Adds Query Size field to TUI config screen and CLI flags parser.
1 parent 4eeb92d commit 57e86bd

7 files changed

Lines changed: 47 additions & 6 deletions

File tree

cmd/scan.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ func init() {
6666
scanCmd.Flags().Bool("skip-nxdomain", false, "skip NXDOMAIN hijack check")
6767
scanCmd.Flags().Bool("edns", false, "include EDNS payload size check (filters resolvers that don't support EDNS)")
6868
scanCmd.Flags().Int("edns-size", 1232, "EDNS0 UDP payload size in bytes (default 1232, lower if fragmented)")
69+
scanCmd.Flags().Int("query-size", 0, "cap dnstt-client upstream query size in bytes (0 = max, try 50-80 if e2e fails)")
6970
scanCmd.Flags().StringSlice("cidr", nil, "CIDR range(s) to scan (e.g. --cidr 5.52.0.0/16)")
7071
scanCmd.Flags().String("output-ips", "", "write plain IP list (one per line) to this file")
7172
scanCmd.Flags().Int("top", 10, "number of top results to display")
@@ -86,8 +87,14 @@ func runScan(cmd *cobra.Command, args []string) error {
8687
outputIPs, _ := cmd.Flags().GetString("output-ips")
8788

8889
ednsSize, _ := cmd.Flags().GetInt("edns-size")
90+
querySize, _ := cmd.Flags().GetInt("query-size")
8991
cidrRanges, _ := cmd.Flags().GetStringSlice("cidr")
9092

93+
// Apply query size (dnstt-client MTU)
94+
if querySize > 0 {
95+
scanner.DnsttMTU = querySize
96+
}
97+
9198
// Apply EDNS buffer size
9299
if ednsSize > 0 && ednsSize <= 65535 {
93100
scanner.EDNSBufSize = uint16(ednsSize)

internal/scanner/doh.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io"
99
"net/http"
10+
"strconv"
1011
"strings"
1112
"sync/atomic"
1213
"time"
@@ -202,11 +203,15 @@ func dohDnsttCheck(bin, domain, pubkey, testURL, proxyAuth string, ports chan in
202203
start := time.Now()
203204

204205
var stderrBuf bytes.Buffer
205-
cmd := execCommandContext(ctx, bin,
206+
args := []string{
206207
"-doh", url,
207208
"-pubkey", pubkey,
208-
domain,
209-
fmt.Sprintf("127.0.0.1:%d", port))
209+
}
210+
if DnsttMTU > 0 {
211+
args = append(args, "-mtu", strconv.Itoa(DnsttMTU))
212+
}
213+
args = append(args, domain, fmt.Sprintf("127.0.0.1:%d", port))
214+
cmd := execCommandContext(ctx, bin, args...)
210215
cmd.Stdout = io.Discard
211216
cmd.Stderr = &stderrBuf
212217
if err := cmd.Start(); err != nil {

internal/scanner/e2e.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import (
1717

1818
const defaultTestURL = "http://httpbin.org/ip"
1919

20+
// DnsttMTU caps the upstream DNS query payload size (dnstt-client -mtu flag).
21+
// 0 means use dnstt-client's default (maximum capacity).
22+
var DnsttMTU int
23+
2024
func PortPool(base, count int) chan int {
2125
ch := make(chan int, count)
2226
for i := 0; i < count; i++ {
@@ -93,11 +97,15 @@ func dnsttCheck(bin, domain, pubkey, testURL, proxyAuth string, ports chan int)
9397
start := time.Now()
9498

9599
var stderrBuf bytes.Buffer
96-
cmd := execCommandContext(ctx, bin,
100+
args := []string{
97101
"-udp", net.JoinHostPort(ip, "53"),
98102
"-pubkey", pubkey,
99-
domain,
100-
fmt.Sprintf("127.0.0.1:%d", port))
103+
}
104+
if DnsttMTU > 0 {
105+
args = append(args, "-mtu", strconv.Itoa(DnsttMTU))
106+
}
107+
args = append(args, domain, fmt.Sprintf("127.0.0.1:%d", port))
108+
cmd := execCommandContext(ctx, bin, args...)
101109
cmd.Stdout = io.Discard
102110
cmd.Stderr = &stderrBuf
103111
if err := cmd.Start(); err != nil {

internal/tui/screen_config.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323
txtTimeout
2424
txtCount
2525
txtEDNSSize
26+
txtQuerySize
2627
txtE2ETimeout
2728
numTextInputs
2829
)
@@ -40,6 +41,7 @@ const (
4041
fSkipNXD
4142
fEDNS
4243
fEDNSSize
44+
fQuerySize
4345
fE2E // toggle: enables/disables e2e section
4446
fPubkey // e2e fields below
4547
fCert
@@ -69,6 +71,7 @@ var allFields = []fieldDef{
6971
{fSkipNXD, "Skip NXDOMAIN", "", "Skip NXDOMAIN hijack detection. Checks if resolver fakes responses.", -1},
7072
{fEDNS, "EDNS Check", "", "Test EDNS0 payload size support. Important for DNS tunneling throughput.", -1},
7173
{fEDNSSize, "EDNS Size", "", "EDNS0 UDP payload size in bytes. Larger = better throughput, lower if fragmented.", txtEDNSSize},
74+
{fQuerySize, "Query Size", "", "Cap upstream DNS query payload size (dnstt -mtu). 0 = max. Try 50-80 if e2e fails on filtered networks.", txtQuerySize},
7275
{fE2E, "E2E Testing", "E2E (end-to-end tunnel test)", "Enable end-to-end tunnel tests. Requires tunnel client binaries.", -1},
7376
{fPubkey, "Pubkey", "", "Hex public key for dnstt. Requires dnstt-client in PATH.", txtPubkey},
7477
{fCert, "Cert", "", "Path to slipstream TLS cert. Requires slipstream-client in PATH.", txtCert},
@@ -143,6 +146,11 @@ func initConfigInputs() []textinput.Model {
143146
inputs[txtEDNSSize].SetValue("1232")
144147
inputs[txtEDNSSize].CharLimit = 4
145148

149+
inputs[txtQuerySize] = textinput.New()
150+
inputs[txtQuerySize].Placeholder = "0 (max)"
151+
inputs[txtQuerySize].SetValue("0")
152+
inputs[txtQuerySize].CharLimit = 4
153+
146154
inputs[txtE2ETimeout] = textinput.New()
147155
inputs[txtE2ETimeout].Placeholder = "15"
148156
inputs[txtE2ETimeout].SetValue("15")
@@ -286,6 +294,9 @@ func applyConfig(m Model) (Model, tea.Cmd) {
286294
if v, err := strconv.Atoi(m.configInputs[txtEDNSSize].Value()); err == nil && v > 0 {
287295
m.config.EDNSSize = v
288296
}
297+
if v, err := strconv.Atoi(m.configInputs[txtQuerySize].Value()); err == nil && v >= 0 {
298+
m.config.QuerySize = v
299+
}
289300
if v, err := strconv.Atoi(m.configInputs[txtE2ETimeout].Value()); err == nil && v > 0 {
290301
m.config.E2ETimeout = v
291302
}

internal/tui/screen_running.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ func launchScan(ctx context.Context, ips []string, cfg ScanConfig, steps []scann
138138
if cfg.EDNSSize > 0 {
139139
scanner.EDNSBufSize = uint16(cfg.EDNSSize)
140140
}
141+
// Apply query size cap (dnstt-client MTU)
142+
scanner.DnsttMTU = cfg.QuerySize
141143

142144
if len(steps) == 0 {
143145
doneCh <- scanDoneMsg{err: fmt.Errorf("no scan steps configured")}

internal/tui/screen_welcome.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ func parseCLIFlags(m *Model, raw string) {
151151
m.configInputs[txtEDNSSize].SetValue(next)
152152
i++
153153
}
154+
case "--query-size":
155+
if next != "" {
156+
fmt.Sscanf(next, "%d", &m.config.QuerySize)
157+
m.configInputs[txtQuerySize].SetValue(next)
158+
i++
159+
}
154160
case "--e2e":
155161
m.config.E2E = true
156162
case "--doh":

internal/tui/tui.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type ScanConfig struct {
3131
Count int
3232
E2ETimeout int
3333
EDNSSize int
34+
QuerySize int
3435
SkipPing bool
3536
SkipNXDomain bool
3637
EDNS bool
@@ -125,6 +126,7 @@ func NewModelWithConfig(cfg ScanConfig) Model {
125126
inputs[txtTimeout].SetValue(fmt.Sprintf("%d", cfg.Timeout))
126127
inputs[txtCount].SetValue(fmt.Sprintf("%d", cfg.Count))
127128
inputs[txtEDNSSize].SetValue(fmt.Sprintf("%d", cfg.EDNSSize))
129+
inputs[txtQuerySize].SetValue(fmt.Sprintf("%d", cfg.QuerySize))
128130
inputs[txtE2ETimeout].SetValue(fmt.Sprintf("%d", cfg.E2ETimeout))
129131

130132
return Model{

0 commit comments

Comments
 (0)