Skip to content

Commit d29ed49

Browse files
feat: add network DNS resolution, reachability, preflight, and WHOIS/RDAP modules
1 parent 77bd086 commit d29ed49

File tree

8 files changed

+1107
-2
lines changed

8 files changed

+1107
-2
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,14 +455,14 @@ down-force complete <config-file> --dry-run
455455
├─ アクセス情報の自動取得
456456
├─ レポートディレクトリ作成(BASE32タイムスタンプ)
457457
458-
2. DNS/IP事前検証(最優先)
458+
2. DNS/IP事前検証(最優先)✅ **実装済み**: `internal/network/dns.go`, `internal/network/reachability.go`
459459
├─ DoH並行クエリ(Quad9/Cloudflare/Google)
460460
├─ システムDNS解決
461461
├─ 結果比較(改ざん検出/応答がいずれかにでもあれば続行)
462462
├─ IPv4/IPv6アドレス取得
463463
└─ DNS解決失敗 → 終了
464464
465-
3. WHOIS/RDAP事前確認(早期終了判定)
465+
3. WHOIS/RDAP事前確認(早期終了判定)✅ **実装済み**: `internal/whois/rdap.go`, `internal/whois/whois.go`
466466
├─ ドメインWHOIS
467467
├─ ドメインRDAP
468468
├─ Domain Status確認

go.mod

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ toolchain go1.24.11
66

77
require (
88
github.com/charmbracelet/huh v0.8.0
9+
github.com/miekg/dns v1.1.59
10+
github.com/openrdap/rdap v0.9.1
911
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
12+
github.com/tantalor93/doh-go v0.3.0
1013
golang.org/x/net v0.47.0
1114
gopkg.in/yaml.v3 v3.0.1
1215
)
1316

1417
require (
18+
github.com/alecthomas/kingpin/v2 v2.3.2 // indirect
19+
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
1520
github.com/atotto/clipboard v0.1.4 // indirect
1621
github.com/catppuccin/go v0.3.0 // indirect
1722
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect
@@ -20,10 +25,15 @@ require (
2025
github.com/dustin/go-humanize v1.0.1 // indirect
2126
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
2227
github.com/mattn/go-localereader v0.0.1 // indirect
28+
github.com/mitchellh/go-homedir v1.1.0 // indirect
2329
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
2430
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
2531
github.com/muesli/cancelreader v0.2.2 // indirect
32+
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
33+
golang.org/x/crypto v0.44.0 // indirect
34+
golang.org/x/mod v0.29.0 // indirect
2635
golang.org/x/sync v0.18.0 // indirect
36+
golang.org/x/tools v0.38.0 // indirect
2737
)
2838

2939
require (

go.sum

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
22
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
3+
github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU=
4+
github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
5+
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
6+
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
37
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
48
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
59
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
@@ -41,6 +45,7 @@ github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJn
4145
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
4246
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
4347
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
48+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4449
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4550
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4651
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
@@ -53,8 +58,12 @@ github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi
5358
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
5459
github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA=
5560
github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg=
61+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
62+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
5663
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
5764
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
65+
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
66+
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
5867
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
5968
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
6069
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -63,6 +72,10 @@ github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2J
6372
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
6473
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
6574
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
75+
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
76+
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
77+
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
78+
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
6679
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
6780
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
6881
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
@@ -71,6 +84,8 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
7184
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
7285
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
7386
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
87+
github.com/openrdap/rdap v0.9.1 h1:Rv6YbanbiVPsKRvOLdUmlU1AL5+2OFuEFLjFN+mQsCM=
88+
github.com/openrdap/rdap v0.9.1/go.mod h1:vKSiotbsENrjM/vaHXLddXbW8iQkBfa+ldEuYEjyLTQ=
7489
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
7590
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7691
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -83,8 +98,14 @@ github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
8398
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
8499
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
85100
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
101+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
102+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
86103
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
87104
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
105+
github.com/tantalor93/doh-go v0.3.0 h1:Hy7CRfrpUeqhAt/XGSWr3L4Wro+lmbvNH7476Lx2rDA=
106+
github.com/tantalor93/doh-go v0.3.0/go.mod h1:1uDDy9iGTVHKEofhXUz9pTvCo7QAPRNys7pxkdLbFuM=
107+
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
108+
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
88109
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
89110
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
90111
github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ=
@@ -102,11 +123,15 @@ github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3R
102123
github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU=
103124
github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
104125
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
126+
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
127+
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
105128
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
106129
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
107130
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
108131
golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ=
109132
golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc=
133+
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
134+
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
110135
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
111136
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
112137
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
@@ -118,7 +143,10 @@ golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
118143
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
119144
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
120145
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
146+
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
147+
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
121148
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
122149
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
150+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
123151
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
124152
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/network/dns.go

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
Copyright © 2025 canaria-computer
3+
*/
4+
package network
5+
6+
import (
7+
"context"
8+
"net"
9+
"sync"
10+
"time"
11+
12+
"github.com/miekg/dns"
13+
"github.com/tantalor93/doh-go/doh"
14+
)
15+
16+
// DNSResult represents the result of DNS resolution
17+
type DNSResult struct {
18+
Domain string `json:"domain"`
19+
IPv4 []string `json:"ipv4"`
20+
IPv6 []string `json:"ipv6"`
21+
Providers map[string]ProviderResult `json:"providers"`
22+
Tampering bool `json:"tampering"`
23+
Success bool `json:"success"`
24+
}
25+
26+
// ProviderResult represents the result from a single DNS provider
27+
type ProviderResult struct {
28+
IPv4 []string `json:"ipv4"`
29+
IPv6 []string `json:"ipv6"`
30+
Error string `json:"error,omitempty"`
31+
}
32+
33+
// DoHProvider represents a DNS over HTTPS provider
34+
type DoHProvider struct {
35+
Name string
36+
URL string
37+
}
38+
39+
// DefaultDoHProviders returns the default list of DoH providers
40+
var DefaultDoHProviders = []DoHProvider{
41+
{Name: "quad9", URL: "https://dns.quad9.net/dns-query"},
42+
{Name: "cloudflare", URL: "https://cloudflare-dns.com/dns-query"},
43+
{Name: "google", URL: "https://dns.google/dns-query"},
44+
}
45+
46+
// ResolveAll performs DNS resolution using DoH providers and system DNS in parallel
47+
func ResolveAll(domain string) (*DNSResult, error) {
48+
return ResolveAllWithTimeout(domain, 10*time.Second)
49+
}
50+
51+
// ResolveAllWithTimeout performs DNS resolution with a custom timeout
52+
func ResolveAllWithTimeout(domain string, timeout time.Duration) (*DNSResult, error) {
53+
ctx, cancel := context.WithTimeout(context.Background(), timeout)
54+
defer cancel()
55+
56+
result := &DNSResult{
57+
Domain: domain,
58+
Providers: make(map[string]ProviderResult),
59+
}
60+
61+
var wg sync.WaitGroup
62+
var mu sync.Mutex
63+
64+
// Query system DNS
65+
wg.Add(1)
66+
go func() {
67+
defer wg.Done()
68+
pr := resolveSystem(ctx, domain)
69+
mu.Lock()
70+
result.Providers["system"] = pr
71+
mu.Unlock()
72+
}()
73+
74+
// Query DoH providers
75+
for _, provider := range DefaultDoHProviders {
76+
wg.Add(1)
77+
go func(p DoHProvider) {
78+
defer wg.Done()
79+
pr := resolveDoH(ctx, domain, p)
80+
mu.Lock()
81+
result.Providers[p.Name] = pr
82+
mu.Unlock()
83+
}(provider)
84+
}
85+
86+
wg.Wait()
87+
88+
// Aggregate results
89+
allIPv4 := make(map[string]bool)
90+
allIPv6 := make(map[string]bool)
91+
hasSuccess := false
92+
93+
for _, pr := range result.Providers {
94+
if pr.Error == "" {
95+
hasSuccess = true
96+
for _, ip := range pr.IPv4 {
97+
allIPv4[ip] = true
98+
}
99+
for _, ip := range pr.IPv6 {
100+
allIPv6[ip] = true
101+
}
102+
}
103+
}
104+
105+
// Convert to slices
106+
for ip := range allIPv4 {
107+
result.IPv4 = append(result.IPv4, ip)
108+
}
109+
for ip := range allIPv6 {
110+
result.IPv6 = append(result.IPv6, ip)
111+
}
112+
113+
result.Success = hasSuccess
114+
result.Tampering = detectTampering(result.Providers)
115+
116+
return result, nil
117+
}
118+
119+
// resolveSystem resolves using the system DNS resolver
120+
func resolveSystem(ctx context.Context, domain string) ProviderResult {
121+
pr := ProviderResult{}
122+
123+
resolver := net.Resolver{}
124+
125+
// Resolve A records (IPv4)
126+
ips, err := resolver.LookupIPAddr(ctx, domain)
127+
if err != nil {
128+
pr.Error = err.Error()
129+
return pr
130+
}
131+
132+
for _, ip := range ips {
133+
if ip.IP.To4() != nil {
134+
pr.IPv4 = append(pr.IPv4, ip.IP.String())
135+
} else {
136+
pr.IPv6 = append(pr.IPv6, ip.IP.String())
137+
}
138+
}
139+
140+
return pr
141+
}
142+
143+
// resolveDoH resolves using DNS over HTTPS
144+
func resolveDoH(ctx context.Context, domain string, provider DoHProvider) ProviderResult {
145+
pr := ProviderResult{}
146+
147+
// Create DoH client
148+
client := doh.NewClient(provider.URL)
149+
150+
// Query A records (IPv4)
151+
msgA := new(dns.Msg)
152+
msgA.SetQuestion(dns.Fqdn(domain), dns.TypeA)
153+
154+
respA, err := client.SendViaPost(ctx, msgA)
155+
if err != nil {
156+
pr.Error = err.Error()
157+
return pr
158+
}
159+
160+
for _, ans := range respA.Answer {
161+
if a, ok := ans.(*dns.A); ok {
162+
pr.IPv4 = append(pr.IPv4, a.A.String())
163+
}
164+
}
165+
166+
// Query AAAA records (IPv6)
167+
msgAAAA := new(dns.Msg)
168+
msgAAAA.SetQuestion(dns.Fqdn(domain), dns.TypeAAAA)
169+
170+
respAAAA, err := client.SendViaPost(ctx, msgAAAA)
171+
if err == nil {
172+
for _, ans := range respAAAA.Answer {
173+
if aaaa, ok := ans.(*dns.AAAA); ok {
174+
pr.IPv6 = append(pr.IPv6, aaaa.AAAA.String())
175+
}
176+
}
177+
}
178+
179+
return pr
180+
}
181+
182+
// detectTampering checks if results differ significantly between providers
183+
func detectTampering(providers map[string]ProviderResult) bool {
184+
var ipv4Sets []map[string]bool
185+
186+
for _, pr := range providers {
187+
if pr.Error != "" {
188+
continue
189+
}
190+
191+
ipSet := make(map[string]bool)
192+
for _, ip := range pr.IPv4 {
193+
ipSet[ip] = true
194+
}
195+
196+
if len(ipSet) > 0 {
197+
ipv4Sets = append(ipv4Sets, ipSet)
198+
}
199+
}
200+
201+
// If we have less than 2 successful results, can't detect tampering
202+
if len(ipv4Sets) < 2 {
203+
return false
204+
}
205+
206+
// Check if all sets have at least one common IP
207+
firstSet := ipv4Sets[0]
208+
for i := 1; i < len(ipv4Sets); i++ {
209+
hasCommon := false
210+
for ip := range firstSet {
211+
if ipv4Sets[i][ip] {
212+
hasCommon = true
213+
break
214+
}
215+
}
216+
if !hasCommon {
217+
return true // Potential tampering detected
218+
}
219+
}
220+
221+
return false
222+
}

0 commit comments

Comments
 (0)