DNS Benchmarking Tool
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bench.go 2.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package main
  2. import (
  3. "fmt"
  4. "math"
  5. "sort"
  6. "strings"
  7. "time"
  8. "github.com/miekg/dns"
  9. "github.com/pkg/errors"
  10. )
  11. type benchResult struct {
  12. DNSSECSupport bool
  13. TimeResults TimeResults
  14. }
  15. type TimeResults struct {
  16. P0Dot5 time.Duration
  17. P5 time.Duration
  18. P25 time.Duration
  19. P50 time.Duration
  20. P75 time.Duration
  21. P95 time.Duration
  22. P99Dot5 time.Duration
  23. Average time.Duration
  24. }
  25. type ProgressCallback func(i, n uint16)
  26. func bench(dnsServer, target string, measurements uint16, cb ProgressCallback) (*benchResult, error) {
  27. if !strings.Contains(dnsServer, ":") {
  28. dnsServer += ":53"
  29. }
  30. c := new(dns.Client)
  31. c.SingleInflight = true
  32. result := &benchResult{
  33. DNSSECSupport: false,
  34. }
  35. fmt.Print("Checking DNSSEC...")
  36. // verify DNSSEC
  37. m := new(dns.Msg)
  38. m.SetEdns0(4096, true) // Set DNSSEC OK
  39. m.SetQuestion("www.dnssec-failed.org.", dns.TypeA)
  40. r, _, err := c.Exchange(m, dnsServer)
  41. if err != nil {
  42. fmt.Println("FAIL")
  43. return nil, errors.Wrap(err, "Could not check DNSSEC")
  44. }
  45. if r.Rcode == dns.RcodeServerFailure {
  46. result.DNSSECSupport = true
  47. }
  48. fmt.Println("OK")
  49. fmt.Println("Running benchmark...")
  50. errTolerance := *errorTolerance
  51. // execute measurements
  52. var ttls = make([]time.Duration, 0)
  53. for i := measurements; i > 0; i-- {
  54. cb(measurements-i, measurements)
  55. time.Sleep(time.Duration(*sleepTimeout) * time.Millisecond)
  56. q := new(dns.Msg)
  57. if *antiCache {
  58. q.SetQuestion(RandStringRunes(4)+"."+target+".", dns.TypeA)
  59. } else {
  60. q.SetQuestion(target+".", dns.TypeA)
  61. }
  62. r, ttl, err := c.Exchange(q, dnsServer)
  63. if err != nil {
  64. if errTolerance <= 0 {
  65. return nil, errors.Wrap(err, "Bench Question failed")
  66. }
  67. fmt.Printf("\nError (tolerance=%d): %s\n", errTolerance, err)
  68. errTolerance--
  69. ttl = time.Second
  70. } else {
  71. if len(r.Answer) != 1 {
  72. return nil, errors.New("DNS has no response Answers")
  73. }
  74. }
  75. ttls = append(ttls, ttl)
  76. }
  77. cb(measurements, measurements)
  78. sort.Slice(ttls, func(i, j int) bool {
  79. return ttls[i].Nanoseconds() < ttls[j].Nanoseconds()
  80. })
  81. var avg int64
  82. for k := range ttls {
  83. avg += ttls[k].Nanoseconds()
  84. }
  85. avg /= int64(len(ttls))
  86. result.TimeResults.Average = time.Duration(avg) * time.Nanosecond
  87. result.TimeResults.P0Dot5 = Px(ttls, 0.005)
  88. result.TimeResults.P5 = Px(ttls, 0.05)
  89. result.TimeResults.P25 = Px(ttls, 0.25)
  90. result.TimeResults.P50 = Px(ttls, 0.50)
  91. result.TimeResults.P75 = Px(ttls, 0.75)
  92. result.TimeResults.P95 = Px(ttls, 0.95)
  93. result.TimeResults.P99Dot5 = Px(ttls, 0.995)
  94. return result, nil
  95. }
  96. func Px(sl []time.Duration, p float64) time.Duration {
  97. var res int64 = 0
  98. numR := int64(
  99. math.Min(
  100. math.Ceil(p*float64(len(sl))),
  101. float64(len(sl)),
  102. ),
  103. )
  104. for i := int64(0); i < numR && i < int64(len(sl)); i++ {
  105. if res < sl[i].Nanoseconds() {
  106. res = sl[i].Nanoseconds()
  107. }
  108. }
  109. return time.Duration(res) * time.Nanosecond
  110. }