-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathdnsbench.go
130 lines (116 loc) · 3.18 KB
/
dnsbench.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
package main
import (
"flag"
"fmt"
"math/rand"
"strings"
"time"
"github.com/miekg/dns"
)
type Query struct {
Name string
TypeStr string
Type uint16
}
func (query Query) Exec(client *dns.Client, server string) (time.Duration, error) {
msg := new(dns.Msg)
msg.SetQuestion(dns.Fqdn(query.Name), query.Type)
_, rtt, err := client.Exchange(msg, server)
return rtt, err
}
type Queries []*Query
func (queries *Queries) String() string {
str := ""
for _, query := range *queries {
if len(str) > 0 {
str += ","
}
str += fmt.Sprintf("%s:%s", query.Name, query.TypeStr)
}
return str
}
func (queries *Queries) Set(values string) error {
for _, value := range strings.Split(values, ",") {
parts := strings.SplitN(value, ":", 2)
if len(parts) == 1 {
*queries = append(*queries, &Query{value, "A", dns.TypeA})
} else {
dnsTypeStr := strings.ToLower(parts[1])
var dnsType uint16
if dnsTypeStr == "a" {
dnsType = dns.TypeA
} else if dnsTypeStr == "mx" {
dnsType = dns.TypeMX
} else if dnsTypeStr == "aaaa" {
dnsType = dns.TypeAAAA
} else if dnsTypeStr == "ns" {
dnsType = dns.TypeNS
} else if dnsTypeStr == "txt" {
dnsType = dns.TypeTXT
} else if dnsTypeStr == "cname" {
dnsType = dns.TypeCNAME
} else {
return fmt.Errorf("invalid DNS type %s", dnsTypeStr)
}
*queries = append(*queries, &Query{parts[0], strings.ToUpper(dnsTypeStr), dnsType})
}
}
return nil
}
func main() {
// process flags
var flagQueries Queries
flag.Var(&flagQueries, "query", "one or more queries to execute")
flagServer := flag.String("server", "127.0.0.1:53", "hostname or IP address and port of DNS server to benchmark")
flagThreads := flag.Int("threads", 8, "number of threads")
flagSeconds := flag.Int("seconds", 5, "seconds to run benchmark for")
flag.Parse()
if len(flagQueries) == 0 {
fmt.Println("dnsbench: no queries specified; use -h for help")
fmt.Println("example: dnsbench -query example.com:A -query example.com:MX -server 22.231.113.64:53")
return
}
seconds := time.Duration(*flagSeconds) * time.Second
endTime := time.Now().Add(seconds)
// set up rtt/iterations reporting
type report struct {
rtt float64
iterations int
}
reportChan := make(chan report)
// launch threads
for i := 0; i < *flagThreads; i++ {
go func(i int) {
var rttSum time.Duration
var iterations int
var errors int
client := new(dns.Client)
for time.Now().Before(endTime) {
randIndex := rand.Intn(len(flagQueries))
rtt, err := flagQueries[randIndex].Exec(client, *flagServer)
if err != nil {
fmt.Printf("query error: %s\n", err)
errors++
if errors > iterations * 5 + 5 {
fmt.Printf("thread %d/%d: too many errors, quitting\n", i, *flagThreads)
break
} else {
continue
}
}
rttSum += rtt
iterations++
}
reportChan <- report{float64(rttSum) / float64(iterations), iterations}
}(i)
}
// collect reports
var rttAvg float64
var iterations int
for i := 0; i < *flagThreads; i++ {
r := <- reportChan
rttAvg += r.rtt / float64(*flagThreads)
iterations += r.iterations
}
fmt.Printf("iterations: %d; average rtt: %.2f ms\n", iterations, float64(rttAvg) / float64(time.Millisecond))
}