Skip to content

Commit d06a680

Browse files
committed
Zero-config offline mode, platform-aware binary finder, and bug fixes
- Auto-load bundled local resolvers when no -i flag (fully offline) - Auto-default output to results.json when no -o flag - Auto-generate _ips.txt plain IP list alongside JSON results - Add platformVariants() to accept release binary names without renaming (e.g. dnstt-client-linux, dnstt-client.exe found automatically) - Use IP-based DoH URLs in preflight to bypass DNS blocking - Fix fetch auto-fallback when online download returns empty - Fix exec_unix.go directory exclusion in isExecutable - Fix TUI writing empty _ips.txt when zero resolvers pass - Cap worker channel buffer to prevent OOM with large inputs - Skip resolve step when --domain is set (tunnel domains have no A record) - Update README.md and GUIDE.md with all new features and flags
1 parent 35b1731 commit d06a680

12 files changed

Lines changed: 227 additions & 79 deletions

File tree

GUIDE.md

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ findns tui
1212

1313
<div dir="rtl">
1414

15-
یا با خط فرمان (۳ دستور):
15+
یا با خط فرمان (بدون نیاز به هیچ فایل ورودی):
1616

1717
</div>
1818

1919
```bash
20-
findns fetch -o resolvers.txt --local # 1. دانلود لیست ریزالورها
21-
findns scan -i resolvers.txt -o results.json --domain t.example.com # 2. اسکن
22-
# 3. اولین IP از لیست passed در results.json = بهترین ریزالور شما
20+
findns scan --domain t.example.com # همین! resolverها خودکار بارگذاری می‌شوند
21+
# نتایج در results.json + results_ips.txt ذخیره می‌شود
22+
# اولین IP از لیست passed = بهترین ریزالور شما
2323
```
2424

2525
<div dir="rtl">
@@ -96,13 +96,12 @@ findns همه این‌ها را به صورت خودکار تست می‌کند
9696
# دانلود
9797
curl -LO https://github.com/SamNet-dev/findns/releases/latest/download/dnstt-client-linux
9898
chmod +x dnstt-client-linux
99-
mv dnstt-client-linux dnstt-client
10099

101-
# گذاشتن کنار findns (ساده‌ترین روش):
102-
mv dnstt-client /path/to/findns/
100+
# گذاشتن کنار findns (ساده‌ترین روش — نیازی به تغییر نام نیست):
101+
mv dnstt-client-linux /path/to/findns/
103102

104103
# یا گذاشتن در PATH:
105-
sudo mv dnstt-client /usr/local/bin/
104+
sudo mv dnstt-client-linux /usr/local/bin/
106105
```
107106

108107
<div dir="rtl">
@@ -335,7 +334,9 @@ go build -o findns.exe ./cmd
335334

336335
## 3. دریافت لیست Resolverها (fetch)
337336

338-
قبل از اسکن، باید لیست resolver داشته باشید. دستور fetch به صورت خودکار از منابع عمومی دانلود می‌کند.
337+
دستور fetch به صورت خودکار از منابع عمومی دانلود می‌کند. اگر دانلود شکست بخورد (مثلاً GitHub فیلتر باشد)، به صورت خودکار از resolverهای داخلی استفاده می‌کند.
338+
339+
> **نکته:** اگر فقط می‌خواهید اسکن کنید، دیگر نیازی به `fetch` نیست! `findns scan --domain t.example.com` بدون `-i` خودکار resolverهای ایرانی داخلی را بارگذاری می‌کند.
339340
340341
### دانلود resolverهای UDP جهانی
341342

@@ -717,19 +718,23 @@ findns scan -i resolvers.txt -o results.json
717718

718719
این حالت بررسی می‌کند resolver زنده، فعال و بدون هایجک است. (برای رد کردن nxdomain از `--skip-nxdomain` استفاده کنید)
719720

721+
> **نکته:** `-i` و `-o` اختیاری هستند. بدون `-i` از 7,800+ resolver ایرانی داخلی استفاده می‌شود. بدون `-o` نتایج در `results.json` ذخیره می‌شود. فایل `results_ips.txt` هم خودکار ساخته می‌شود.
722+
720723
### اسکن کامل با دامنه تانل (پیشنهادی)
721724

722725
</div>
723726

724727
```bash
725-
findns scan -i resolvers.txt -o results.json --domain t.example.com
728+
findns scan --domain t.example.com
726729
```
727730

728731
<div dir="rtl">
729732

730-
مراحل: `ping -> resolve -> nxdomain -> resolve/tunnel`
733+
مراحل: `ping -> nxdomain -> resolve/tunnel`
734+
735+
> **نکته مهم:** وقتی `--domain` تنظیم شود، مرحله `resolve` ساده (رکورد A برای google.com) رد می‌شود — دامنه‌های تانل رکورد A ندارند. findns مستقیم به `resolve/tunnel` (بررسی NS delegation) می‌رود.
731736
732-
> **نکته:** برای اضافه کردن تست EDNS payload size از فلگ `--edns` استفاده کنید. با این فلگ: `ping -> resolve -> nxdomain -> edns -> resolve/tunnel`
737+
> برای اضافه کردن تست EDNS payload size از فلگ `--edns` استفاده کنید. با این فلگ: `ping -> nxdomain -> edns -> resolve/tunnel`
733738
734739
### توضیح هر مرحله
735740

@@ -759,9 +764,9 @@ findns scan -i resolvers.txt -o results.json \
759764

760765
<div dir="rtl">
761766

762-
مراحل: `ping -> resolve -> nxdomain -> resolve/tunnel -> e2e/dnstt`
767+
مراحل: `ping -> nxdomain -> resolve/tunnel -> e2e/dnstt`
763768

764-
> با `--edns`: `ping -> resolve -> nxdomain -> edns -> resolve/tunnel -> e2e/dnstt`
769+
> با `--edns`: `ping -> nxdomain -> edns -> resolve/tunnel -> e2e/dnstt`
765770
766771
نیازمند: `dnstt-client` و `curl` در PATH. این مرحله واقعاً dnstt-client را اجرا می‌کند، یک تانل SOCKS می‌سازد و با curl از طریق آن تانل یک صفحه وب را باز می‌کند.
767772
- متریک: `e2e_ms` (کل زمان از شروع تا اتصال موفق)
@@ -789,7 +794,9 @@ findns scan -i doh-resolvers.txt -o results.json --domain t.example.com --doh
789794

790795
<div dir="rtl">
791796

792-
مراحل: `doh/resolve -> doh/resolve/tunnel`
797+
مراحل: `doh/resolve/tunnel`
798+
799+
> وقتی `--domain` تنظیم شود، مرحله `doh/resolve` ساده رد می‌شود.
793800
794801
اسکن DoH با تست e2e:
795802

@@ -802,7 +809,7 @@ findns scan -i doh-resolvers.txt -o results.json \
802809

803810
<div dir="rtl">
804811

805-
مراحل: `doh/resolve -> doh/resolve/tunnel -> doh/e2e`
812+
مراحل: `doh/resolve/tunnel -> doh/e2e`
806813

807814
### فلگ‌های دستور scan
808815

@@ -818,6 +825,7 @@ findns scan -i doh-resolvers.txt -o results.json \
818825
| `--edns` | فعال‌سازی تست سایز EDNS payload (اختیاری) | `false` |
819826
| `--skip-nxdomain` | رد کردن بررسی هایجک | `false` |
820827
| `--top` | تعداد نتایج برتر در خروجی ترمینال | `10` |
828+
| `--output-ips` | خروجی لیست آی‌پی ساده کنار فایل JSON | خودکار |
821829

822830
---
823831

@@ -1192,7 +1200,11 @@ findns scan -i resolvers.txt -o results.json \
11921200
- pubkey باید دقیقاً همان کلیدی باشد که سرور DNSTT با آن اجرا شده
11931201
- اگر pubkey اشتباه باشد، dnstt-client بدون پیام خطا فیل می‌شود
11941202

1195-
**۶. تست دستی:**
1203+
**۶. preflight e2e و DoH fallback:**
1204+
1205+
findns قبل از شروع تست e2e، یک "preflight" انجام می‌دهد — با یک resolver عمومی (Google, Cloudflare و ...) تست می‌کند تانل کار می‌کند. اگر DNS معمولی (UDP) مسدود باشد، به صورت خودکار از DoH fallback (آدرس‌های IP-based مثل `https://8.8.8.8/dns-query`) استفاده می‌کند. این یعنی حتی اگر تمام DNS مسدود باشد، preflight e2e همچنان کار می‌کند.
1206+
1207+
**۷. تست دستی:**
11961208

11971209
</div>
11981210

@@ -1210,7 +1222,7 @@ kill %1
12101222

12111223
<div dir="rtl">
12121224

1213-
**۷. پورت‌ها در تداخل:**
1225+
**۸. پورت‌ها در تداخل:**
12141226
- findns از پورت‌های 30000 به بالا برای تست استفاده می‌کند
12151227
- اگر سرویس دیگری این پورت‌ها را گرفته، تست فیل می‌شود
12161228
- با `--port-base` پورت شروع را تغییر دهید (فقط در chain)
@@ -1309,8 +1321,9 @@ https://dns.quad9.net/dns-query
13091321

13101322
| فلگ | مخفف | توضیح | پیش‌فرض |
13111323
|-----|------|-------|---------|
1312-
| `--input` | `-i` | فایل ورودی (متنی یا JSON) | الزامی |
1313-
| `--output` | `-o` | فایل خروجی JSON | الزامی |
1324+
| `--input` | `-i` | فایل ورودی (متنی یا JSON). اگر داده نشود، از 7,800+ resolver ایرانی داخلی استفاده می‌شود | لیست داخلی |
1325+
| `--output` | `-o` | فایل خروجی JSON | `results.json` |
1326+
| `--output-ips` || خروجی لیست آی‌پی ساده (هر خط یک آی‌پی) کنار JSON | خودکار |
13141327
| `--timeout` | `-t` | تایم‌اوت هر تلاش (ثانیه) | `3` |
13151328
| `--count` | `-c` | تعداد تلاش برای هر IP | `3` |
13161329
| `--workers` || تعداد workerهای موازی | `50` |
@@ -1420,6 +1433,8 @@ findns resolve -i step1.json -o step2.json --domain google.com
14201433
- **passed:** لیست resolverهای موفق با متریک‌ها (مرتب شده بر اساس عملکرد)
14211434
- **failed:** لیست resolverهای ناموفق
14221435

1436+
**فایل `_ips.txt`:** کنار فایل JSON، یک فایل `_ips.txt` هم خودکار ساخته می‌شود (مثلاً `results_ips.txt`). این فایل فقط شامل آی‌پی‌های موفق است (هر خط یک آی‌پی) — برای استفاده مستقیم در اسکریپت‌ها و ابزارهای دیگر.
1437+
14231438
---
14241439

14251440
## 9. سناریوهای عملی
@@ -1651,4 +1666,14 @@ ping نیاز به دسترسی خاص دارد: `sudo findns scan ...` یا ا
16511666

16521667
> پیشنهاد: اول UDP امتحان کنید. اگر کار نکرد، DoH بزنید.
16531668
1669+
**نکته 11: حالت آفلاین (بدون اینترنت)**
1670+
findns به صورت کامل آفلاین کار می‌کند:
1671+
- بدون `-i`: از 7,800+ resolver ایرانی داخلی استفاده می‌شود
1672+
- بدون `-o`: نتایج در `results.json` ذخیره می‌شود
1673+
- فایل `_ips.txt` خودکار ساخته می‌شود
1674+
- `fetch` اگر دانلود شکست بخورد، خودکار از لیست داخلی استفاده می‌کند
1675+
- تست e2e preflight از DoH fallback (IP-based) استفاده می‌کند — حتی اگر DNS مسدود باشد
1676+
1677+
ساده‌ترین دستور ممکن: `findns scan --domain t.example.com`
1678+
16541679
</div>

README.md

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Supports both **UDP** and **DoH (DNS-over-HTTPS)** resolvers with end-to-end tun
2525
| 📋 **JSON Pipeline** | Output from one scan feeds into the next for multi-stage filtering |
2626
| 🌐 **CIDR Input** | Accept IP ranges like `185.51.200.0/24` — auto-expanded to individual hosts |
2727
| 🖥️ **Interactive TUI** | Full terminal UI with guided setup — no flags to remember |
28+
| 🔌 **Fully Offline** | Zero-config: auto-loads bundled resolvers, no `-i` or `-o` needed |
29+
| 🛡️ **DoH Preflight** | E2E preflight uses DoH fallback (IP-based URLs) to bypass UDP DNS blocking |
2830

2931
---
3032

@@ -165,6 +167,20 @@ findns tui
165167

166168
Launches a full terminal UI that guides you through mode selection, resolver input, and scan configuration. No flags needed — just follow the prompts.
167169

170+
### 0️⃣ Zero-Config Offline Scan (Easiest)
171+
172+
No flags needed — findns auto-loads 7,800+ bundled Iranian resolvers and saves to `results.json`:
173+
174+
```bash
175+
# Scan with just a domain — everything else is automatic
176+
findns scan --domain t.example.com
177+
178+
# With e2e verification
179+
findns scan --domain t.example.com --pubkey <hex-pubkey>
180+
```
181+
182+
> No `-i` flag? Uses bundled resolvers. No `-o` flag? Saves to `results.json`. Also auto-generates `results_ips.txt` with a plain IP list.
183+
168184
### 1️⃣ Get Resolver Lists
169185

170186
```bash
@@ -178,6 +194,8 @@ findns fetch -o resolvers.txt --local
178194
findns fetch -o doh-resolvers.txt --doh
179195
```
180196

197+
> If the online download fails (e.g. GitHub is blocked), `fetch` automatically falls back to bundled resolvers — no internet needed.
198+
181199
### 2️⃣ Run Full Scan
182200

183201
```bash
@@ -199,7 +217,7 @@ findns scan -i doh-resolvers.txt -o results.json \
199217

200218
### 3️⃣ Check Results
201219

202-
Results are saved as JSON. The `passed` array contains resolvers that survived all steps, sorted by performance:
220+
Results are saved as JSON with an auto-generated `_ips.txt` companion file. The `passed` array contains resolvers that survived all steps, sorted by performance:
203221

204222
```json
205223
{
@@ -237,11 +255,15 @@ A guided terminal interface for the full scan workflow. No flags or files needed
237255
Automatically chains the right scan steps based on your flags. This is the **recommended** way to use the scanner.
238256

239257
```bash
240-
findns scan -i resolvers.txt -o results.json --domain t.example.com
258+
findns scan --domain t.example.com
241259
```
242260

243-
**UDP mode pipeline:** `ping → resolve → nxdomain → tunnel → e2e` (add `--edns` for EDNS payload check)
244-
**DoH mode pipeline:** `doh/resolve → doh/tunnel → doh/e2e`
261+
> `-i` and `-o` are optional. Without `-i`, bundled Iranian resolvers are used. Without `-o`, results save to `results.json`.
262+
263+
**UDP mode pipeline:** `ping → nxdomain → resolve/tunnel → e2e` (add `--edns` for EDNS payload check)
264+
**DoH mode pipeline:** `doh/resolve/tunnel → doh/e2e`
265+
266+
> When `--domain` is set, the basic `resolve` step (A record for google.com) is skipped — tunnel domains have no A record, so findns goes straight to `resolve/tunnel` (NS delegation check).
245267
246268
| Flag | Description | Default |
247269
|------|-------------|---------|
@@ -255,6 +277,7 @@ findns scan -i resolvers.txt -o results.json --domain t.example.com
255277
| `--skip-ping` | Skip ICMP ping step | `false` |
256278
| `--skip-nxdomain` | Skip NXDOMAIN hijack check | `false` |
257279
| `--top` | Number of top results to display | `10` |
280+
| `--output-ips` | Write plain IP list alongside JSON | auto |
258281

259282
---
260283

@@ -476,8 +499,9 @@ Step format: `type:key=val,key=val`. Optional params: `count`, `timeout`.
476499

477500
| Flag | Short | Description | Default |
478501
|------|-------|-------------|---------|
479-
| `--input` | `-i` | Input file (text or JSON) | required |
480-
| `--output` | `-o` | Output JSON file | required |
502+
| `--input` | `-i` | Input file (text or JSON). If omitted, uses 7,800+ bundled Iranian resolvers | bundled list |
503+
| `--output` | `-o` | Output JSON file | `results.json` |
504+
| `--output-ips` | | Also write a plain IP list (one per line) | auto when `-o` is set |
481505
| `--timeout` | `-t` | Timeout per attempt (seconds) | 3 |
482506
| `--count` | `-c` | Attempts per IP/URL | 3 |
483507
| `--workers` | | Concurrent workers | 50 |
@@ -610,6 +634,8 @@ MIT
610634
| 📋 **خروجی JSON** | خروجی هر اسکن ورودی اسکن بعدی می‌شود |
611635
| 🌐 **ورودی CIDR** | رنج آی‌پی مثل `185.51.200.0/24` را می‌خواند و به صورت خودکار باز می‌کند |
612636
| 🖥️ **رابط کاربری ترمینال (TUI)** | رابط تعاملی کامل — بدون نیاز به حفظ فلگ‌ها |
637+
| 🔌 **کاملاً آفلاین** | بدون تنظیم: resolverهای داخلی خودکار بارگذاری می‌شوند، نیازی به `-i` یا `-o` نیست |
638+
| 🛡️ **DoH Preflight** | تست e2e از DoH fallback (آدرس‌های IP-based) برای دور زدن مسدودسازی DNS استفاده می‌کند |
613639

614640
---
615641

@@ -782,6 +808,24 @@ findns tui
782808

783809
یک رابط کاربری ترمینال کامل باز می‌شود که شما را قدم به قدم راهنمایی می‌کند: انتخاب حالت (UDP/DoH)، انتخاب لیست ریزالور، تنظیمات اسکن، و مشاهده نتایج. نیازی به فلگ نیست — فقط دنبال کنید.
784810

811+
### 0️⃣ اسکن آفلاین بدون تنظیم (ساده‌ترین)
812+
813+
بدون نیاز به هیچ فلگ اضافه — findns به صورت خودکار 7,800+ resolver ایرانی داخلی را بارگذاری می‌کند و نتایج را در `results.json` ذخیره می‌کند:
814+
815+
</div>
816+
817+
```bash
818+
# فقط با دامنه اسکن کنید — بقیه خودکار است
819+
findns scan --domain t.example.com
820+
821+
# با تست e2e
822+
findns scan --domain t.example.com --pubkey <hex-pubkey>
823+
```
824+
825+
<div dir="rtl">
826+
827+
> بدون `-i`؟ از resolverهای داخلی استفاده می‌شود. بدون `-o`؟ در `results.json` ذخیره می‌شود. فایل `results_ips.txt` هم خودکار ساخته می‌شود.
828+
785829
### 1️⃣ دریافت لیست Resolverها
786830

787831
</div>
@@ -799,6 +843,8 @@ findns fetch -o doh-resolvers.txt --doh
799843

800844
<div dir="rtl">
801845

846+
> اگر دانلود آنلاین شکست بخورد (مثلاً GitHub فیلتر باشد)، `fetch` به صورت خودکار از resolverهای داخلی استفاده می‌کند — نیازی به اینترنت نیست.
847+
802848
### 2️⃣ اجرای اسکن کامل
803849

804850
</div>
@@ -824,7 +870,7 @@ findns scan -i doh-resolvers.txt -o results.json \
824870

825871
### 3️⃣ بررسی نتایج
826872

827-
نتایج به صورت JSON ذخیره می‌شوند. آرایه `passed` شامل resolverهایی است که تمام مراحل را با موفقیت گذرانده‌اند:
873+
نتایج به صورت JSON ذخیره می‌شوند (+ فایل `_ips.txt` خودکار). آرایه `passed` شامل resolverهایی است که تمام مراحل را با موفقیت گذرانده‌اند:
828874

829875
</div>
830876

@@ -868,8 +914,10 @@ findns tui
868914

869915
به صورت خودکار مراحل مناسب را بر اساس فلگ‌ها ترتیب می‌دهد.
870916

871-
**حالت UDP:** `ping → resolve → nxdomain → tunnel → e2e` (با `--edns` مرحله EDNS اضافه می‌شود)
872-
**حالت DoH:** `doh/resolve → doh/tunnel → doh/e2e`
917+
**حالت UDP:** `ping → nxdomain → resolve/tunnel → e2e` (با `--edns` مرحله EDNS اضافه می‌شود)
918+
**حالت DoH:** `doh/resolve/tunnel → doh/e2e`
919+
920+
> وقتی `--domain` تنظیم شود، مرحله `resolve` ساده (رکورد A برای google.com) رد می‌شود — دامنه‌های تانل رکورد A ندارند، بنابراین findns مستقیم به `resolve/tunnel` (بررسی NS delegation) می‌رود.
873921
874922
| فلگ | توضیح | پیش‌فرض |
875923
|-----|-------|---------|
@@ -883,6 +931,7 @@ findns tui
883931
| `--skip-ping` | رد کردن مرحله ping | `false` |
884932
| `--skip-nxdomain` | رد کردن بررسی هایجک | `false` |
885933
| `--top` | تعداد نتایج برتر برای نمایش | `10` |
934+
| `--output-ips` | خروجی لیست آی‌پی ساده کنار JSON | خودکار |
886935

887936
---
888937

@@ -1159,8 +1208,9 @@ findns chain -i doh-resolvers.txt -o result.json \
11591208

11601209
| فلگ | مخفف | توضیح | پیش‌فرض |
11611210
|-----|------|-------|---------|
1162-
| `--input` | `-i` | فایل ورودی (متن یا JSON) | الزامی |
1163-
| `--output` | `-o` | فایل خروجی JSON | الزامی |
1211+
| `--input` | `-i` | فایل ورودی (متن یا JSON). اگر داده نشود، از 7,800+ resolver ایرانی داخلی استفاده می‌شود | لیست داخلی |
1212+
| `--output` | `-o` | فایل خروجی JSON | `results.json` |
1213+
| `--output-ips` | | خروجی لیست آی‌پی ساده (هر خط یک آی‌پی) | خودکار وقتی `-o` تنظیم شود |
11641214
| `--timeout` | `-t` | تایم‌اوت هر تلاش (ثانیه) | 3 |
11651215
| `--count` | `-c` | تعداد تلاش برای هر IP/URL | 3 |
11661216
| `--workers` | | تعداد workerهای موازی | 50 |

cmd/fetch.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ func runFetch(cmd *cobra.Command, args []string) error {
119119
fmt.Fprintf(os.Stderr, " +%d resolvers\n", added)
120120
}
121121

122+
// Auto-fallback: if no resolvers loaded (online failed or returned empty), use bundled list
123+
if !localMode && len(entries) == 0 {
124+
fmt.Fprintf(os.Stderr, "\n online fetch failed — falling back to bundled Iranian resolvers (no internet needed)\n")
125+
localMode = true
126+
}
127+
122128
// Add known Iranian resolvers from bundled list
123129
if localMode {
124130
localIPs, err := data.IRResolvers()

cmd/root.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"time"
77

88
"github.com/SamNet-dev/findns/internal/binutil"
9+
"github.com/SamNet-dev/findns/internal/data"
910
"github.com/SamNet-dev/findns/internal/scanner"
1011
"github.com/SamNet-dev/findns/internal/tui"
1112
"github.com/spf13/cobra"
@@ -57,7 +58,16 @@ func Execute() {
5758

5859
func loadInput() ([]string, error) {
5960
if inputFile == "" {
60-
return nil, fmt.Errorf("--input / -i flag is required")
61+
// No input file — auto-load bundled Iranian resolvers (fully offline)
62+
ips, err := data.IRResolvers()
63+
if err != nil {
64+
return nil, fmt.Errorf("no -i flag and bundled resolvers failed: %w", err)
65+
}
66+
if len(ips) == 0 {
67+
return nil, fmt.Errorf("--input / -i flag is required (bundled resolver list is empty)")
68+
}
69+
fmt.Fprintf(os.Stderr, " no -i flag — using %d bundled Iranian resolvers (offline)\n", len(ips))
70+
return ips, nil
6171
}
6272
ips, err := scanner.LoadInput(inputFile, includeFailed)
6373
if err != nil {

0 commit comments

Comments
 (0)