programs for edns, svcb, https support measurement

This commit is contained in:
MDK
2024-03-25 17:15:25 +08:00
commit 7115311c42
10 changed files with 517 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.log

12
go.mod Normal file
View File

@@ -0,0 +1,12 @@
module edns_svcb
go 1.20
require github.com/miekg/dns v1.1.58
require (
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/tools v0.17.0 // indirect
)

11
go.sum Normal file
View File

@@ -0,0 +1,11 @@
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=

38
main.go Normal file
View File

@@ -0,0 +1,38 @@
package main
import (
"edns_svcb/method"
"edns_svcb/utils"
"flag"
"log"
"os"
"sync"
"time"
)
func main() {
var input_file string
flag.StringVar(&input_file, "input", "", "input file name")
flag.Parse()
input_pool := make(chan string, 500)
result_pool := make(chan method.CombinedResult)
var test_tasks sync.WaitGroup
var process_tasks sync.WaitGroup
logfilename := "test/" + time.Now().Format("2006-01-02_15-04-05") + ".log"
logfile, err := os.OpenFile(logfilename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatal("Failed to create log file: ", err)
}
defer logfile.Close()
logger := log.New(logfile, "", log.LstdFlags)
go utils.RetrieveLines(input_pool, input_file)
method.CombinedMeasurement(input_pool, result_pool, 500, &test_tasks, logger)
process_tasks.Add(1)
go method.CombinedResultProcess(result_pool, &process_tasks)
test_tasks.Wait()
close(result_pool)
process_tasks.Wait()
time.Sleep(time.Second)
}

123
method/combined.go Normal file
View File

@@ -0,0 +1,123 @@
package method
import (
"edns_svcb/utils"
"fmt"
"log"
"sync"
"github.com/miekg/dns"
)
type CombinedResult struct {
Alive bool
EDNSSupport bool
SVCBSupport bool
HTTPSSupport bool
Err error
}
func CombinedMeasurement(ip_pool chan string, result_pool chan CombinedResult, routine_num int, wg *sync.WaitGroup, logger *log.Logger) {
for i := 0; i < routine_num; i++ {
wg.Add(1)
go CombinedTestRoutine(ip_pool, result_pool, wg, logger)
}
}
func CombinedTestRoutine(ip_pool chan string, result_pool chan CombinedResult, wg *sync.WaitGroup, logger *log.Logger) {
for {
if ip, ok := <-ip_pool; ok {
addr := ip + ":53"
res := CombinedTest(addr)
result_pool <- res
if res.Err != nil {
logger.Printf("%v : failed ( %v )", ip, res.Err)
} else {
logger.Printf("%v : alive %v edns %v svcb %v https %v", ip, res.Alive, res.EDNSSupport, res.SVCBSupport, res.HTTPSSupport)
}
} else {
break
}
}
wg.Done()
}
func CombinedTest(addr string) CombinedResult {
result := CombinedResult{}
_, err := utils.DNSQuery(addr, utils.DNSOptions{Domain: "www.example.com", RD: true})
if err != nil {
return CombinedResult{Err: err}
}
result.Alive = true
//edns support
res, err := utils.DNSQuery(addr, utils.DNSOptions{Domain: "www.example.com", EDNS: true, RD: true})
if err == nil {
if len(res.Extra) > 0 {
if _, ok := res.Extra[0].(*dns.OPT); ok {
result.EDNSSupport = true
}
}
} else {
result.Err = err
return result
}
// svcb support
res, err = utils.DNSQuery(addr, utils.DNSOptions{Domain: "_dns.resolver.arpa", Qtype: dns.TypeSVCB, RD: true})
if err == nil {
if len(res.Answer) > 0 {
if _, ok := res.Answer[0].(*dns.SVCB); ok {
result.SVCBSupport = true
}
}
} else {
result.Err = err
return result
}
// https support
res, err = utils.DNSQuery(addr, utils.DNSOptions{Domain: "blog.cloudflare.com", Qtype: dns.TypeHTTPS, RD: true})
if err == nil {
if len(res.Answer) > 0 {
if _, ok := res.Answer[0].(*dns.HTTPS); ok {
result.HTTPSSupport = true
}
}
} else {
result.Err = err
return result
}
return result
}
func CombinedResultProcess(result_pool chan CombinedResult, wg *sync.WaitGroup) {
alive_cnt := 0
edns_cnt := 0
svcb_cnt := 0
https_cnt := 0
for {
if res, ok := <-result_pool; ok {
if res.Err != nil {
continue
}
if res.Alive {
alive_cnt++
}
if res.EDNSSupport {
edns_cnt++
}
if res.SVCBSupport {
svcb_cnt++
}
if res.HTTPSSupport {
https_cnt++
}
} else {
break
}
}
fmt.Printf("EDNS support Test: alive %v edns %v svcb %v https %v\n", alive_cnt, edns_cnt, svcb_cnt, https_cnt)
wg.Done()
}

76
method/edns.go Normal file
View File

@@ -0,0 +1,76 @@
package method
import (
"edns_svcb/utils"
"fmt"
"log"
"sync"
"github.com/miekg/dns"
)
type TestResult struct {
Alive bool
Support bool
Err error
}
func EDNSSupportMeasurement(ip_pool chan string, result_pool chan TestResult, routine_num int, wg *sync.WaitGroup, logger *log.Logger) {
for i := 0; i < routine_num; i++ {
wg.Add(1)
go EDNSSupportTestRoutine(ip_pool, result_pool, wg, logger)
}
}
func EDNSSupportTestRoutine(ip_pool chan string, result_pool chan TestResult, wg *sync.WaitGroup, logger *log.Logger) {
for {
if ip, ok := <-ip_pool; ok {
addr := ip + ":53"
res := EDNSSupportTest(addr)
result_pool <- res
if res.Err != nil {
logger.Printf("%v : failed ( %v )", ip, res.Err)
} else {
logger.Printf("%v : alive %v support %v", ip, res.Alive, res.Support)
}
} else {
break
}
}
wg.Done()
}
func EDNSSupportTest(addr string) TestResult {
_, err := utils.DNSQuery(addr, utils.DNSOptions{Domain: "www.example.net", RD: true})
if err != nil {
return TestResult{Err: err}
}
res, err := utils.DNSQuery(addr, utils.DNSOptions{Domain: "www.example.net", EDNS: true, RD: true})
if err == nil {
if len(res.Extra) == 1 {
if _, ok := res.Extra[0].(*dns.OPT); ok {
return TestResult{Alive: true, Support: true}
}
}
}
return TestResult{Alive: true}
}
func EDNSSupportResultProcess(result_pool chan TestResult, wg *sync.WaitGroup) {
alive_cnt := 0
valid_cnt := 0
for {
if res, ok := <-result_pool; ok {
if res.Alive {
alive_cnt++
}
if res.Support {
valid_cnt++
}
} else {
break
}
}
fmt.Printf("EDNS support Test: alive %v support %v\n", alive_cnt, valid_cnt)
wg.Done()
}

63
method/svcb.go Normal file
View File

@@ -0,0 +1,63 @@
package method
import (
"edns_svcb/utils"
"github.com/miekg/dns"
)
func HTTPSSupportTest(addr string) TestResult {
_, err := utils.DNSQuery(addr, utils.DNSOptions{Domain: "www.example.net", RD: true})
if err != nil {
return TestResult{Err: err}
}
res, err := utils.DNSQuery(addr, utils.DNSOptions{Domain: "blog.cloudflare.com", Qtype: dns.TypeHTTPS, RD: true})
if err == nil {
if len(res.Answer) == 1 {
if _, ok := res.Extra[0].(*dns.HTTPS); ok {
return TestResult{Alive: true, Support: true}
}
}
}
return TestResult{Alive: true}
}
func SVCBSupportTest(addr string) TestResult {
_, err := utils.DNSQuery(addr, utils.DNSOptions{Domain: "www.example.net", RD: true})
if err != nil {
return TestResult{Err: err}
}
res, err := utils.DNSQuery(addr, utils.DNSOptions{Domain: "blog.cloudflare.com", Qtype: dns.TypeSVCB, RD: true})
if err == nil {
if len(res.Answer) == 1 {
if _, ok := res.Extra[0].(*dns.SVCB); ok {
return TestResult{Alive: true, Support: true}
}
}
}
return TestResult{Alive: true}
}
func HTTPSRecordTest(addr, domain string) bool {
res, err := utils.DNSQuery(addr, utils.DNSOptions{Domain: domain, Qtype: dns.TypeHTTPS, RD: true})
if err == nil {
if len(res.Answer) > 0 {
if _, ok := res.Answer[0].(*dns.HTTPS); ok {
return true
}
}
}
return false
}
func SVCBRecordTest(addr, domain string) bool {
res, err := utils.DNSQuery(addr, utils.DNSOptions{Domain: domain, Qtype: dns.TypeSVCB, RD: true})
if err == nil {
if len(res.Answer) > 0 {
if _, ok := res.Answer[0].(*dns.SVCB); ok {
return true
}
}
}
return false
}

100
test/test-input Normal file
View File

@@ -0,0 +1,100 @@
1.0.0.1
1.0.0.2
1.0.0.3
1.0.0.19
1.0.66.192
1.0.72.68
1.0.77.169
1.0.79.90
1.0.81.135
1.0.84.117
1.0.91.59
1.0.91.149
1.0.97.194
1.0.103.132
1.0.106.125
1.0.123.53
1.0.141.2
1.0.141.7
1.0.141.23
1.0.141.31
1.0.141.32
1.0.141.36
1.0.141.46
1.0.141.49
1.0.141.54
1.0.141.55
1.0.141.59
1.0.141.62
1.0.141.64
1.0.141.67
1.0.141.69
1.0.141.72
1.0.141.75
1.0.141.76
1.0.141.77
1.0.141.79
1.0.141.85
1.0.141.86
1.0.141.92
1.0.141.101
1.0.141.103
1.0.141.110
1.0.141.114
1.0.141.117
1.0.141.126
1.0.141.135
1.0.141.143
1.0.141.145
1.0.141.152
1.0.141.164
1.0.141.167
1.0.141.174
1.0.141.177
1.0.141.179
1.0.141.180
1.0.141.191
1.0.141.203
1.0.141.204
1.0.141.211
1.0.141.223
1.0.141.238
1.0.141.242
1.0.141.244
1.0.141.247
1.0.141.250
1.0.141.252
1.0.141.253
1.0.144.8
1.0.144.27
1.0.144.34
1.0.144.36
1.0.144.38
1.0.144.47
1.0.144.48
1.0.144.50
1.0.144.51
1.0.144.52
1.0.144.55
1.0.144.73
1.0.144.74
1.0.144.78
1.0.144.99
1.0.144.112
1.0.144.127
1.0.144.130
1.0.144.150
1.0.144.164
1.0.144.167
1.0.144.172
1.0.144.177
1.0.144.178
1.0.144.181
1.0.144.182
1.0.144.183
1.0.144.186
1.0.144.193
1.0.144.197
1.0.144.206
1.0.144.207
1.0.144.224

62
utils/dns_utils.go Normal file
View File

@@ -0,0 +1,62 @@
// dns utils
package utils
import (
"github.com/miekg/dns"
)
type DNSOptions struct {
Domain string
RD bool
Qclass uint16
Qtype uint16
EDNS bool
}
// build the question section of a dns packet
func questionMaker(domain string, qclass uint16, qtype uint16) *dns.Question {
return &dns.Question{Name: dns.Fqdn(domain), Qtype: qtype, Qclass: qclass}
}
// build a specific query message
func queryMaker(domain string, rd bool, qclass uint16, qtype uint16, edns bool) *dns.Msg {
msg := new(dns.Msg)
msg.Id = dns.Id()
msg.RecursionDesired = rd
msg.Question = make([]dns.Question, 1)
msg.Question[0] = *questionMaker(domain, qclass, qtype)
if edns {
msg = msg.SetEdns0(1232, false)
}
return msg
}
// query and receive the response
// addr must contain dest port
// func DNSQuery(addr string, domain string, rd bool, qclass uint16, qtype uint16, edns bool) (*dns.Msg, error) {
func DNSQuery(addr string, opt DNSOptions) (*dns.Msg, error) {
if opt.Qclass == 0 {
opt.Qclass = 1
}
if opt.Qtype == 0 {
opt.Qtype = 1
}
msg := queryMaker(opt.Domain, opt.RD, opt.Qclass, opt.Qtype, opt.EDNS)
res, err := dns.Exchange(msg, addr)
return res, err
}
func AsyncDNSQuery(addr string, domain string, rd bool, qclass uint16, qtype uint16, edns bool) error {
msg := queryMaker(domain, rd, qclass, qtype, edns)
client := dns.Client{Net: "udp"}
conn, err := client.Dial(addr)
if err != nil {
return err
}
defer conn.Close()
err = conn.WriteMsg(msg)
if err != nil {
return err
}
return nil
}

31
utils/other_utils.go Normal file
View File

@@ -0,0 +1,31 @@
package utils
import (
"bufio"
_ "encoding/json"
_ "fmt"
"io"
_ "net"
_ "net/http"
"os"
_ "strconv"
"strings"
)
func RetrieveLines(pool chan string, filename string) {
f, err := os.Open(filename)
if err != nil {
panic(err)
}
//fmt.Println("reading file ...")
reader := bufio.NewReader(f)
for {
s, err := reader.ReadString('\n')
if err == io.EOF {
break
}
s = strings.Trim(s, "\n")
pool <- s
}
close(pool)
}