简单DNS客户端编写

目标:

  • 可使用参数来设置解析的域名类型,拨测数量,期望结果
  • 执行DNS解析并获取结果

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import org.xbill.DNS.Lookup;
import org.xbill.DNS.Type;
import org.xbill.DNS.Record;

public class DNSClient {
public static void main(String[] args) {
// 参数解析
if (args.length < 4) {
System.out.println("Usage: java DNSClient <domain> <recordType> <attempts> <expectedResult>");
System.out.println("Example: java DNSClient example.com A 3 93.184.216.34");
return;
}

String domain = args[0];
String recordTypeStr = args[1].toUpperCase();
int attempts = Integer.parseInt(args[2]);
String expectedResult = args[3];

// 获取记录类型代码
int recordType = getRecordType(recordTypeStr);
if (recordType == -1) {
System.out.println("Unsupported record type: " + recordTypeStr);
return;
}

// 执行拨测
boolean success = false;
for (int i = 1; i <= attempts; i++) {
System.out.println("Attempt " + i + ": Querying " + domain + " for " + recordTypeStr + "... ");
try {
Lookup lookup = new Lookup(domain, recordType);
Record[] records = lookup.run();

if (records == null || records.length == 0) {
System.out.println("No records found.");
continue;
}

// 检查结果是否包含期望值
boolean found = false;
for (Record record : records) {
String result = record.rdataToString();
System.out.println(result);
if (result.contains(expectedResult)) {
found = true;
break;
}
}

if (found) {
System.out.println("✅ Expected result found: " + expectedResult);
success = true;
break;
} else {
System.out.println("❌ No expected result found.");
}

} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}

if (!success) {
System.out.println("⚠️ All attempts failed. Expected result not found: " + expectedResult);
}
}

// 将记录类型字符串转换为DNSJava的记录类型代码
private static int getRecordType(String type) {
switch (type) {
case "A":
return Type.A;
case "AAAA":
return Type.AAAA;
case "CNAME":
return Type.CNAME;
case "MX":
return Type.MX;
case "TXT":
return Type.TXT;
case "NS":
return Type.NS;
case "SOA":
return Type.SOA;
default:
return -1;
}
}
}

1
2
java -cp .:DNSClient-1.0-SNAPSHOT.jar DNSClient www.aliyun.com A 1 93.184.216.34

Go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package main

import (
"flag"
"fmt"
"time"

"github.com/miekg/dns"
)

var (
msg = new(dns.Msg)
dnsServer = "223.5.5.5:53"
client = new(dns.Client)
)

func ResolveRecord(domain string, recordType string, count int, expected string) error {
dnsType := RecordType2DnsType(recordType)
// 创建 DNS 消息
msg.SetQuestion(dns.Fqdn(domain), dnsType)

for i := range count {
fmt.Printf("Querying DNS for %s (%d/%d)\n", domain, i+1, count)
// 发送 DNS 查询
response, _, err := client.Exchange(msg, dnsServer)
if err != nil {
fmt.Printf("Failed to query DNS for %s: %v\n", domain, err)
return err
}

// 处理响应
if response.Rcode != dns.RcodeSuccess {
fmt.Printf("DNS query failed with Rcode %d\n", response.Rcode)
return fmt.Errorf("DNS query failed with Rcode %d", response.Rcode)
}

// 检查记录里面有没有预期的值,判断所有的记录类型

if expected != "" {
found := false
for _, answer := range response.Answer {
switch recordType {
case "A":
if record, ok := answer.(*dns.A); ok && record.A.String() == expected {
found = true
}
case "AAAA":
if record, ok := answer.(*dns.AAAA); ok && record.AAAA.String() == expected {
found = true
}
case "CNAME":
if record, ok := answer.(*dns.CNAME); ok && record.Target == expected {
found = true
}
case "MX":
if record, ok := answer.(*dns.MX); ok && record.Mx == expected {
found = true
}
case "NS":
if record, ok := answer.(*dns.NS); ok && record.Ns == expected {
found = true
}
case "TXT":
if record, ok := answer.(*dns.TXT); ok {
for _, txt := range record.Txt {
if txt == expected {
found = true
}
}
}
default:
fmt.Printf("Unknown record type: %d\n", answer.Header().Rrtype)
}
}
if !found {
fmt.Printf("Expected value %s not found in DNS response\n", expected)
} else {
fmt.Printf("Expected value %s found in DNS response\n", expected)
}
}
// 打印响应
for _, answer := range response.Answer {
switch recordType {
case "A":
if record, ok := answer.(*dns.A); ok {
fmt.Printf("A record: %s\n", record.A.String())
}
case "AAAA":
if record, ok := answer.(*dns.AAAA); ok {
fmt.Printf("AAAA record: %s\n", record.AAAA.String())
}
case "CNAME":
if record, ok := answer.(*dns.CNAME); ok {
fmt.Printf("CNAME record: %s\n", record.Target)
}
case "MX":
if record, ok := answer.(*dns.MX); ok {
fmt.Printf("MX record: %s\n", record.Mx)
}
case "NS":
if record, ok := answer.(*dns.NS); ok {
fmt.Printf("NS record: %s\n", record.Ns)
}
case "TXT":
if record, ok := answer.(*dns.TXT); ok {
fmt.Printf("TXT record: %s\n", record.Txt)
}
default:
fmt.Printf("Unknown record type: %d\n", answer.Header().Rrtype)
}
}
// 休眠 1 秒
time.Sleep(1 * time.Second)
}
return nil
}

// RecordType2DnsType 将 DNS 记录类型转换为 dns.Type
func RecordType2DnsType(recordType string) uint16 {
switch recordType {
case "A":
return dns.TypeA
case "AAAA":
return dns.TypeAAAA
case "CNAME":
return dns.TypeCNAME
case "MX":
return dns.TypeMX
case "NS":
return dns.TypeNS
case "TXT":
return dns.TypeTXT
default:
return 0
}
}

func main() {
domain := flag.String("domain", "example.com", "The domain name to query.")
recordType := flag.String("type", "A", "The type of DNS record (A, AAAA, etc.)")
count := flag.Int("count", 3, "Number of DNS queries to perform.")
expected := flag.String("expected", "", "Expected result from the DNS query.")
flag.Parse()

err := ResolveRecord(*domain, *recordType, *count, *expected)
if err != nil {
fmt.Printf("Error resolving domain %s: %v\n", *domain, err)
} else {
fmt.Printf("Successfully resolved domain %s\n", *domain)
}
}

// 解析 DNS 记录
// 该代码使用 miekg/dns 库来查询 DNS 记录
// 你需要安装该库:go get github.com/miekg/dns
// 运行代码前,请确保你的 Go 环境已正确设置
// 运行代码:go run main.go -domain=www.aliyun.com -type=A -count=3 -expected=93.184.216.34

1
go run main.go -domain=www.aliyun.com  -type=A -count=3 -expected=93.184.216.34