Golang中DNS解析:TTL和GetServers如何选择
Golang中DNS解析:TTL和GetServers如何选择
![挥手] 你好,我一直在寻找一种方法来获取 DNS TXT 记录的 ttl 值。看起来 golang 标准库不支持记录的 ttl,所以我一直在研究 miekg/dns。它工作得很好,但为了让它开箱即用,我需要获取系统的 DNS 服务器。由于系统设置是针对每个网络接口的,我原以为 net.Interfaces() 会有一个 Interface.DNS 属性,或者有一个可用的 net.DNSServers() 函数。
你知道有什么其他库可以帮助解决这个问题吗?
你认为这应该是一个 golang 标准库的问题吗?
你认为支持 DNS 记录的 TTL 应该是 golang 标准库的一个功能吗?
这应该是像 miekd/dns 这样的第三方库的功能吗?
注意: 从在 Node.js 中使用的情况来看,似乎应该可以以跨平台的方式实现它。可能是因为 Node 在后台使用了 c-ares?
$ node -p "(new dns.Resolver).getServers()"
更多关于Golang中DNS解析:TTL和GetServers如何选择的实战教程也可以访问 https://www.itying.com/category-94-b0.html
确实,Windows可能会给跨平台解决方案增加复杂性……
更多关于Golang中DNS解析:TTL和GetServers如何选择的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你好 @martinheidegger,欢迎来到论坛。
关于获取系统的 DNS 服务器列表,这个 Stack Overflow 回答建议像 Ruby 那样解析 /etc/resolv.conf 文件。
我无法评论获取 TTL 数据是否应该成为标准库或第三方包的功能。这取决于 Go 团队或任何已经编写或计划编写 DNS 包的人的决定。但我发现 retryabledns 这个包似乎包含了 TTL 数据,作为 TXT 响应的一部分,这可能符合你的需求。
retryabledns 是一个死胡同,因为它在底层使用了 miekg/dns 😓。
Stack Overflow 上的回答指出了正确的实现方向,即使它包含一个失效的链接。以下是我找到的实现片段:
when /\Andots:(\d+)\z/
ndots = $1.to_i
end
}
end
}
}
return { :nameserver => nameserver, :search => search, :ndots => ndots }
end
def Config.default_config_hash(filename="/etc/resolv.conf")
if File.exist? filename
config_hash = Config.parse_resolv_conf(filename)
else
if /mswin|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
require 'win32/resolv'
search, nameserver = Win32::Resolv.get_resolv_info
config_hash = {}
config_hash[:nameserver] = nameserver if nameserver
config_hash[:search] = [search].flatten if search
end
end
实际上,它会使用 win32/resolv 单独处理 Windows 的情况——所以相当复杂。尝试支持它看起来远不止是一个“5分钟搞定”的快速项目 😅,我在想是否有人(可能比我更感兴趣)愿意实现它。
在Golang中获取DNS记录的TTL值确实需要依赖第三方库,标准库的net.LookupTXT等函数不返回TTL信息。以下是具体实现方案:
使用miekg/dns获取TTL和系统DNS服务器
1. 获取系统DNS服务器(跨平台方案)
package main
import (
"fmt"
"github.com/miekg/dns"
"net"
)
// 获取系统DNS服务器列表
func getSystemDNSServers() ([]string, error) {
config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
if err != nil {
// Windows系统回退
return []string{"8.8.8.8:53", "8.8.4.4:53"}, nil
}
servers := make([]string, 0, len(config.Servers))
for _, server := range config.Servers {
servers = append(servers, net.JoinHostPort(server, config.Port))
}
return servers, nil
}
2. 获取TXT记录及其TTL
func getTXTWithTTL(domain string) ([]string, []uint32, error) {
servers, err := getSystemDNSServers()
if err != nil {
return nil, nil, err
}
c := new(dns.Client)
m := new(dns.Msg)
m.SetQuestion(dns.Fqdn(domain), dns.TypeTXT)
var txtRecords []string
var ttls []uint32
for _, server := range servers {
r, _, err := c.Exchange(m, server)
if err != nil {
continue // 尝试下一个DNS服务器
}
if r.Rcode != dns.RcodeSuccess {
continue
}
for _, ans := range r.Answer {
if txt, ok := ans.(*dns.TXT); ok {
txtRecords = append(txtRecords, txt.Txt...)
ttls = append(ttls, txt.Hdr.Ttl)
}
}
break // 成功获取后退出
}
return txtRecords, ttls, nil
}
func main() {
txts, ttls, err := getTXTWithTTL("example.com")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
for i, txt := range txts {
fmt.Printf("TXT: %s, TTL: %d seconds\n", txt, ttls[i])
}
}
3. 针对不同操作系统的优化处理
// 增强版系统DNS获取
func getSystemDNSServersEnhanced() []string {
// Linux/macOS
if servers, err := dns.ClientConfigFromFile("/etc/resolv.conf"); err == nil {
result := make([]string, len(servers.Servers))
for i, s := range servers.Servers {
result[i] = net.JoinHostPort(s, servers.Port)
}
return result
}
// Windows备用方案
return getWindowsDNSServers()
}
func getWindowsDNSServers() []string {
// Windows系统可以通过注册表或WMI获取DNS服务器
// 这里提供回退方案
return []string{
"8.8.8.8:53", // Google DNS
"1.1.1.1:53", // Cloudflare DNS
"208.67.222.222:53", // OpenDNS
}
}
关于标准库支持的问题
-
TTL支持必要性:DNS记录的TTL对于缓存策略至关重要,标准库应该提供此信息。当前
net.LookupTXT等函数的设计确实存在不足。 -
系统DNS服务器获取:标准库缺少
net.GetDNSServers()这样的函数是个明显的缺口。不同平台的实现差异应该由标准库封装。 -
第三方库现状:
miekg/dns是目前最成熟的解决方案,它提供了完整的DNS协议实现。对于需要TTL或高级DNS功能的场景,必须使用此类第三方库。 -
Node.js对比:Node.js的
dns模块确实更完善,它底层使用c-ares或系统解析器,提供了TTL和服务器配置访问。Go标准库在这方面需要跟进。
生产环境建议
// 带缓存的DNS解析器
type DNSCache struct {
cache map[string]cacheEntry
mu sync.RWMutex
}
type cacheEntry struct {
records []string
ttls []uint32
expires time.Time
}
func (c *DNSCache) GetTXT(domain string) ([]string, error) {
c.mu.RLock()
if entry, found := c.cache[domain]; found && time.Now().Before(entry.expires) {
c.mu.RUnlock()
return entry.records, nil
}
c.mu.RUnlock()
// 缓存未命中,重新解析
txts, ttls, err := getTXTWithTTL(domain)
if err != nil {
return nil, err
}
// 使用最小TTL设置缓存过期
minTTL := findMinTTL(ttls)
c.mu.Lock()
c.cache[domain] = cacheEntry{
records: txts,
ttls: ttls,
expires: time.Now().Add(time.Duration(minTTL) * time.Second),
}
c.mu.Unlock()
return txts, nil
}
func findMinTTL(ttls []uint32) uint32 {
if len(ttls) == 0 {
return 300 // 默认5分钟
}
min := ttls[0]
for _, ttl := range ttls[1:] {
if ttl < min {
min = ttl
}
}
return min
}
当前情况下,使用miekg/dns是获取DNS记录TTL的唯一可靠方法。标准库需要增加对TTL和系统DNS配置的访问支持,这属于合理的功能需求。

