golang 实现 dns agent

常见DNS记录类型

记录类型 核心功能 应用场景 示例格式
A 域名 → IPv4 地址 网站基础解析、服务器绑定 example.com. IN A 192.0.2.1
AAAA 域名 → IPv6 地址 IPv6 网络支持、物联网设备 example.com. IN AAAA 2001:db8::1
CNAME 域名 → 别名域名 CDN 加速、统一域名管理 www.example.com. IN CNAME cdn.com.
PTR IP 地址 → 域名 反垃圾邮件、日志分析 1.2.0.192.in-addr.arpa. IN PTR mail.example.com.
NS 指定权威 DNS 服务器 域名解析委派 example.com. IN NS ns1.cloud-dns.net.
MX 指定邮件服务器及优先级 企业邮箱配置 example.com. IN MX 10 mail.example.com.
TXT 存储任意文本信息 SPF 反垃圾邮件、域名验证 example.com. IN TXT "v=spf1 include:_spf.google.com ~all"
SRV 指定服务地址及端口 VoIP、LDAP 服务 _sip._tcp.example.com. IN SRV 10 5 5060 sipserver.example.com.
SOA 定义域管理元数据 区域传输控制、主从同步 example.com. IN SOA ns1.example.com. admin.example.com. (2024062501 3600 900 1209600 86400)

go常用dns解析库

Go语言中常用的DNS解析库有以下两个:

  • net/dns包:是Go语言标准库的一部分,提供了DNS解析功能,如解析域名、获取主机信息、查询MX记录等。示例代码:
  • miekg/dns库:是Go语言中比较流行的DNS解析库,支持自定义DNS查询(A记录、CNAME、TXT、MX等)、构建DNS服务器(拦截、解析、转发)、DNS数据包分析(流量监控、恶意检测)、DNSSEC(增强DNS安全)等功能。示例代码:

解析示例

解析配置

import "github.com/miekg/dns"

func loadResolvConf() *dns.ClientConfig {
    config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
    if err != nil {
        log.Fatalf("Failed to load resolv.conf: %v", err)
    }
    return config
}
  • 返回的config包含:
    • servers: nameserver(IP地址)
    • search: search域列表(用于域名补全)
    • options: options参数(如timeout, ndots)

处理 options关键参数

  • attempts: 需在代码中手动实现重试逻辑(遍历 nameserver 列表直到成功或达到重试次数)
  • timeout:超时时间配置
c := new(dns.Client)
c.Timeout = time.Duration(config.Timeout) * time.Second // 默认单位秒
c.DialTimeout = c.Timeout // 连接超时
  • ndots:决定是否自动添加serach域:若启用,需遍历 config.Search 列表,拼接域名后依次查询
func shouldUseSearch(domain string, ndots int) bool {
    dotCount := strings.Count(domain, ".")
    return dotCount < ndots // 域名中 "." 数量 < ndots 时启用 search
}

实战demo

package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/miekg/dns"
)

type DNSProxy struct {
	upstream string
}

func main() {
	addr := "127.0.0.1:5533"
	proxy := &DNSProxy{upstream: "8.8.8.8:53"}
	dns.HandleFunc(".", proxy.handleRequest)

	server := &dns.Server{Addr: addr, Net: "udp"}
	go func() {
		log.Printf("Starting DNS proxy on %s", server.Addr)
		if err := server.ListenAndServe(); err != nil {
			log.Fatalf("Failed to start DNS server: %v", err)
		}
	}()

	time.Sleep(2 * time.Second) // 等待代理就绪
	exampleQueries(addr)
	select {}
}

func (p *DNSProxy) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
	c := new(dns.Client)
	resp, _, err := c.Exchange(r, p.upstream)
	if err != nil {
		log.Printf("Failed to query upstream: %v", err)
		m := new(dns.Msg)
		m.SetRcode(r, dns.RcodeServerFailure)
		w.WriteMsg(m)
		return
	}
	for _, ans := range resp.Answer {
		fmt.Println(ans.String())
	}
	w.WriteMsg(resp)
}

func queryExample(proxyAddr, domain string, qtype uint16) {
	m := new(dns.Msg)
	m.SetQuestion(dns.Fqdn(domain), qtype)
	c := new(dns.Client)
	in, _, err := c.Exchange(m, proxyAddr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Query failed: %v\n", err)
		return
	}
	for _, ans := range in.Answer {
		fmt.Println(ans.String())
	}
	time.Sleep(2 * time.Second) // 限频
}

func exampleQueries(proxyAddr string) {
	domain := "example.com"
	fmt.Println("=====A record:=====")
	queryExample(proxyAddr, domain, dns.TypeA)
	fmt.Println("=====AAAA record:=====")
	queryExample(proxyAddr, domain, dns.TypeAAAA)
	fmt.Println("=====CNAME record:=====")
	cnameDomain := "www.google.com"
	queryExample(proxyAddr, cnameDomain, dns.TypeCNAME)
	fmt.Println("=====PTR record:=====")
	queryExample(proxyAddr, "8.8.8.8.in-addr.arpa", dns.TypePTR)
	fmt.Println("=====NS record:=====")
	queryExample(proxyAddr, domain, dns.TypeNS)
	fmt.Println("=====MX record:=====")
	queryExample(proxyAddr, domain, dns.TypeMX)
	fmt.Println("=====TXT record:=====")
	queryExample(proxyAddr, domain, dns.TypeTXT)
	fmt.Println("=====SRV record:=====")
	srvDomain := "_xmpp-server._tcp.jabber.org"
	queryExample(proxyAddr, srvDomain, dns.TypeSRV)
	fmt.Println("=====SOA record:=====")
	queryExample(proxyAddr, domain, dns.TypeSOA)
}
  • 运行日志如下:
2025/07/03 17:15:20 Starting DNS proxy on 127.0.0.1:5533
=====A record:=====
example.com.    101     IN      A       96.7.128.175
example.com.    101     IN      A       23.215.0.136
example.com.    101     IN      A       23.192.228.84
example.com.    101     IN      A       96.7.128.198
example.com.    101     IN      A       23.192.228.80
example.com.    101     IN      A       23.215.0.138
example.com.    101     IN      A       96.7.128.175
example.com.    101     IN      A       23.215.0.136
example.com.    101     IN      A       23.192.228.84
example.com.    101     IN      A       96.7.128.198
example.com.    101     IN      A       23.192.228.80
example.com.    101     IN      A       23.215.0.138
=====AAAA record:=====
example.com.    75      IN      AAAA    2600:1406:bc00:53::b81e:94ce
example.com.    75      IN      AAAA    2600:1408:ec00:36::1736:7f24
example.com.    75      IN      AAAA    2600:1406:3a00:21::173e:2e66
example.com.    75      IN      AAAA    2600:1406:bc00:53::b81e:94c8
example.com.    75      IN      AAAA    2600:1408:ec00:36::1736:7f31
example.com.    75      IN      AAAA    2600:1406:3a00:21::173e:2e65
example.com.    75      IN      AAAA    2600:1406:bc00:53::b81e:94ce
example.com.    75      IN      AAAA    2600:1408:ec00:36::1736:7f24
example.com.    75      IN      AAAA    2600:1406:3a00:21::173e:2e66
example.com.    75      IN      AAAA    2600:1406:bc00:53::b81e:94c8
example.com.    75      IN      AAAA    2600:1408:ec00:36::1736:7f31
example.com.    75      IN      AAAA    2600:1406:3a00:21::173e:2e65
=====CNAME record:=====
www.google.com. 177     IN      A       31.13.94.49
www.google.com. 177     IN      A       31.13.94.49
=====PTR record:=====
8.8.8.8.in-addr.arpa.   13841   IN      PTR     dns.google.
8.8.8.8.in-addr.arpa.   13841   IN      PTR     dns.google.
=====NS record:=====
example.com.    10887   IN      NS      b.iana-servers.net.
example.com.    10887   IN      NS      a.iana-servers.net.
example.com.    10887   IN      NS      b.iana-servers.net.
example.com.    10887   IN      NS      a.iana-servers.net.
=====MX record:=====
example.com.    5189    IN      MX      0 .
example.com.    5189    IN      MX      0 .
=====TXT record:=====
example.com.    21341   IN      TXT     "_k2n1y4vw3qtb4skdx9e7dxt97qrmmq9"
example.com.    21341   IN      TXT     "v=spf1 -all"
example.com.    21341   IN      TXT     "_k2n1y4vw3qtb4skdx9e7dxt97qrmmq9"
example.com.    21341   IN      TXT     "v=spf1 -all"
=====SRV record:=====
_xmpp-server._tcp.jabber.org.   60      IN      SRV     30 30 5269 scarlet.jabber.org.
_xmpp-server._tcp.jabber.org.   60      IN      SRV     30 30 5269 scarlet.jabber.org.
=====SOA record:=====
example.com.    326     IN      SOA     ns.icann.org. noc.dns.icann.org. 2025011701 7200 3600 1209600 3600
example.com.    326     IN      SOA     ns.icann.org. noc.dns.icann.org. 2025011701 7200 3600 1209600 3600

Ref

https://man7.org/linux/man-pages/man5/resolv.conf.5.html
https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml

posted @ 2025-07-04 16:22  惜阳茕影  阅读(261)  评论(0)    收藏  举报