dns.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package nettools
  2. import (
  3. "crypto/tls"
  4. "errors"
  5. "fmt"
  6. "github.com/miekg/dns"
  7. "golang.org/x/net/context"
  8. "net"
  9. "strconv"
  10. "strings"
  11. "time"
  12. )
  13. type DnsPingResult struct {
  14. Time int
  15. Err error
  16. IP net.IP
  17. }
  18. func (dnsR *DnsPingResult) Result() int {
  19. return dnsR.Time
  20. }
  21. func (dnsR *DnsPingResult) Error() error {
  22. return dnsR.Err
  23. }
  24. func (dnsR *DnsPingResult) String() string {
  25. if dnsR.Err != nil {
  26. return fmt.Sprintf("%s", dnsR.Err)
  27. } else {
  28. return fmt.Sprintf("%s: time=%d ms", dnsR.IP.String(), dnsR.Time)
  29. }
  30. }
  31. type DnsPing struct {
  32. host string
  33. Port uint16
  34. Timeout time.Duration
  35. // udp, tcp, tcp-tls,默认 udp
  36. Net string
  37. // A, AAAA, NS, ...,默认 NS
  38. Type string
  39. // 查询域名,默认 .
  40. Domain string
  41. // Net 为 tcp-tls 时,是否跳过证书验证
  42. Insecure bool
  43. ip net.IP
  44. }
  45. func (dnsC *DnsPing) SetHost(host string) {
  46. dnsC.host = host
  47. dnsC.ip = net.ParseIP(host)
  48. }
  49. func (dnsC *DnsPing) Host() string {
  50. return dnsC.host
  51. }
  52. func (dnsC *DnsPing) Ping() IPingResult {
  53. return dnsC.PingContext(context.Background())
  54. }
  55. func (dnsC *DnsPing) PingContext(ctx context.Context) IPingResult {
  56. ip := cloneIP(dnsC.ip)
  57. if ip == nil {
  58. var err error
  59. ip, err = LookupFunc(dnsC.host)
  60. if err != nil {
  61. return &DnsPingResult{0, err, nil}
  62. }
  63. }
  64. msg := &dns.Msg{}
  65. qtype, ok := dns.StringToType[dnsC.Type]
  66. if !ok {
  67. return &DnsPingResult{0, errors.New("unknown type"), nil}
  68. }
  69. if !strings.HasSuffix(dnsC.Domain, ".") {
  70. dnsC.Domain += "."
  71. }
  72. msg.SetQuestion(dnsC.Domain, qtype)
  73. msg.MsgHdr.RecursionDesired = true
  74. client := &dns.Client{}
  75. client.Net = dnsC.Net
  76. client.Timeout = dnsC.Timeout
  77. client.TLSConfig = &tls.Config{
  78. ServerName: dnsC.host,
  79. InsecureSkipVerify: dnsC.Insecure,
  80. }
  81. t0 := time.Now()
  82. r, _, err := client.ExchangeContext(ctx, msg, net.JoinHostPort(ip.String(), strconv.Itoa(int(dnsC.Port))))
  83. if err != nil {
  84. return &DnsPingResult{0, err, nil}
  85. }
  86. if r == nil || r.Response == false || r.Opcode != dns.OpcodeQuery {
  87. return &DnsPingResult{0, errors.New("response error"), nil}
  88. }
  89. return &DnsPingResult{int(time.Now().Sub(t0).Milliseconds()), nil, ip}
  90. }
  91. func NewDnsPing(host string, timeout time.Duration) *DnsPing {
  92. return &DnsPing{
  93. host: host,
  94. Port: 53,
  95. Timeout: timeout,
  96. Net: "udp",
  97. Type: "NS",
  98. Domain: ".",
  99. Insecure: false,
  100. ip: net.ParseIP(host),
  101. }
  102. }
  103. var (
  104. _ IPing = (*DnsPing)(nil)
  105. _ IPingResult = (*DnsPingResult)(nil)
  106. )