Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.fa.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
- 🧪 **تست Burst** - سرورهایی که فقط به یک کوئری جواب می‌دهند اما زیر بار واقعی fail می‌شوند را فیلتر می‌کند (مثل 1.1.1.1 که 0% موفقیت دارد)
- 🛡️ **تشخیص DNS Hijacking** - سرورهایی که IP خصوصی برمی‌گردانند را شناسایی و هشدار می‌دهد
- ⚡ **مرتب‌سازی بر اساس QPS** - نتایج بر اساس سرعت (queries per second) مرتب می‌شوند
- 🎨 **رنگ‌بندی** - سبز برای ≥85% موفقیت، زرد برای 70-84%
- 🎨 **رنگ‌بندی** - سبز برای ≥threshold+15%، زرد برای threshold تا threshold+15%

## کاربرد

Expand Down Expand Up @@ -62,6 +62,7 @@ go build -o dnscan .
| `--progress` | true | نمایش نوار پیشرفت |
| `--verify` | - | مسیر باینری slipstream-client |
| `--json` | false | خروجی به فرمت JSON |
| `--threshold` | 70 | حداقل نرخ موفقیت برای بنچمارک (0-100) |

## حالت‌های اسکن

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Find working DNS servers for DNS tunnels during internet blackouts. Scans countr
- 🧪 **Burst Testing** - Filters servers that respond to single queries but fail under real load (e.g., 1.1.1.1 shows 0% success)
- 🛡️ **DNS Hijacking Detection** - Detects and warns when servers return private IPs
- ⚡ **QPS Sorting** - Results sorted by throughput (queries per second)
- 🎨 **Color Coding** - Green for ≥85% success, yellow for 70-84%
- 🎨 **Color Coding** - Green for ≥threshold+15%, yellow for threshold to threshold+15%

## Use Case

Expand Down Expand Up @@ -63,6 +63,7 @@ go build -o dnscan .
| `--progress` | true | Show progress bar |
| `--verify` | - | Path to slipstream-client binary |
| `--json` | false | Output results as JSON |
| `--threshold` | 70 | Minimum success rate for benchmark (0-100) |

## Scan Modes

Expand Down
18 changes: 14 additions & 4 deletions benchmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,32 @@ type Benchmarker struct {
domain string
port int
timeout time.Duration
threshold int
output io.Writer
showProgress bool
}

func NewBenchmarker(domain string, port int, timeout time.Duration, output io.Writer, showProgress bool) *Benchmarker {
func NewBenchmarker(domain string, port int, timeout time.Duration, threshold int, output io.Writer, showProgress bool) *Benchmarker {
if port == 0 {
port = 53
}
if threshold <= 0 || threshold > 100 {
threshold = BenchmarkThreshold
}
return &Benchmarker{
domain: domain,
port: port,
timeout: timeout,
threshold: threshold,
output: output,
showProgress: showProgress,
}
}

func (b *Benchmarker) passed(r *BenchmarkResult) bool {
return r.SuccessRate() >= float64(b.threshold)
}

func (b *Benchmarker) Benchmark(ctx context.Context, ips []string) []*BenchmarkResult {
if len(ips) == 0 {
return nil
Expand Down Expand Up @@ -137,9 +146,10 @@ func (b *Benchmarker) summary(results []*BenchmarkResult, total int) {
}
fmt.Fprintf(b.output, "\r\033[1;32mBenchmark: %d/%d | Passed: %d | sorted by QPS\033[0m \n", total, total, len(results))
fmt.Fprintln(b.output, "---")
greenThreshold := float64(b.threshold + 15)
for _, r := range results {
color := "\033[33m"
if r.SuccessRate() >= 85 {
if r.SuccessRate() >= greenThreshold {
color = "\033[32m"
}
fmt.Fprintf(b.output, "%s%-15s %.0f%% (%.1f qps, p50=%v)\033[0m\n",
Expand All @@ -157,7 +167,7 @@ func (b *Benchmarker) sequential(ctx context.Context, ips []string, prog *Progre
}
result := b.benchmark(ctx, ip)
prog.Increment()
if result.Passed() {
if b.passed(result) {
prog.Success()
results = append(results, result)
}
Expand Down Expand Up @@ -214,7 +224,7 @@ func (b *Benchmarker) parallel(ctx context.Context, ips []string, prog *Progress
var results []*BenchmarkResult
for result := range resultChan {
prog.Increment()
if result.Passed() {
if b.passed(result) {
prog.Success()
results = append(results, result)
}
Expand Down
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Config struct {
JSONOutput bool
Progress bool
ShowVersion bool
Threshold int
}

func ParseFlags() *Config {
Expand All @@ -37,6 +38,7 @@ func ParseFlags() *Config {
flag.StringVar(&c.VerifyBinary, "verify", "", "Path to slipstream-client binary")
flag.BoolVar(&c.ShowVersion, "version", false, "Show version")
flag.BoolVar(&c.JSONOutput, "json", false, "Output results as JSON")
flag.IntVar(&c.Threshold, "threshold", 70, "Minimum success rate for benchmark (0-100)")

flag.Parse()

Expand Down
10 changes: 5 additions & 5 deletions e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func TestE2EBenchmark(t *testing.T) {
}
defer mock.Close()

benchmarker := NewBenchmarker("test.example.com", mock.port, 2*time.Second, nil, false)
benchmarker := NewBenchmarker("test.example.com", mock.port, 2*time.Second, 70, nil, false)
results := benchmarker.Benchmark(context.Background(), []string{mock.ip})

if len(results) != 1 {
Expand Down Expand Up @@ -217,7 +217,7 @@ func TestE2EBenchmarkQPS(t *testing.T) {
}
defer mock.Close()

benchmarker := NewBenchmarker("test.example.com", mock.port, 2*time.Second, nil, false)
benchmarker := NewBenchmarker("test.example.com", mock.port, 2*time.Second, 70, nil, false)
results := benchmarker.Benchmark(context.Background(), []string{mock.ip})

if len(results) != 1 {
Expand Down Expand Up @@ -253,7 +253,7 @@ func TestE2EBenchmarkMultiple(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

benchmarker := NewBenchmarker("test.example.com", mocks[0].port, 2*time.Second, nil, false)
benchmarker := NewBenchmarker("test.example.com", mocks[0].port, 2*time.Second, 70, nil, false)
results := benchmarker.Benchmark(ctx, ips)

if len(results) != 3 {
Expand All @@ -271,7 +271,7 @@ func TestE2EBenchmarkContextCancellation(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()

benchmarker := NewBenchmarker("test.example.com", mock.port, 2*time.Second, nil, false)
benchmarker := NewBenchmarker("test.example.com", mock.port, 2*time.Second, 70, nil, false)
results := benchmarker.Benchmark(ctx, []string{mock.ip})

if len(results) > 0 && results[0].Successful == BenchmarkQueries {
Expand Down Expand Up @@ -305,7 +305,7 @@ func TestE2EBenchmarkResultMetrics(t *testing.T) {
}
defer mock.Close()

benchmarker := NewBenchmarker("test.example.com", mock.port, 2*time.Second, nil, false)
benchmarker := NewBenchmarker("test.example.com", mock.port, 2*time.Second, 70, nil, false)
results := benchmarker.Benchmark(context.Background(), []string{mock.ip})

if len(results) != 1 {
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func main() {

var benchResults []*BenchmarkResult
if cfg.Domain != "" && len(workingDNS) > 0 {
benchmarker := NewBenchmarker(cfg.Domain, 53, cfg.Timeout, os.Stderr, cfg.Progress)
benchmarker := NewBenchmarker(cfg.Domain, 53, cfg.Timeout, cfg.Threshold, os.Stderr, cfg.Progress)
benchResults = benchmarker.Benchmark(ctx, workingDNS)

workingDNS = nil
Expand Down