本文主要是介绍Golang自定义DNS Nameserver,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
某些情况下我们希望程序通过自定义Nameserver
去查询域名,而不希望通过操作系统给定的Nameserver
,本文介绍如何在Golang
中实现自定义Nameserver
。
DNS解析过程
Golang
中一般通过net.Resolver
的LookupHost(ctx context.Context, host string) (addrs []string, err error)
去实现域名解析,解析过程如下:
- 检查本地
hosts
文件是否存在解析记录,存在即返回解析地址 - 不存在即根据
resolv.conf
中读取的nameserver
发起递归查询 nameserver
不断的向上级nameserver
发起迭代查询nameserver
最终返回查询结果给请求者
用户可以通过修改/etc/resolv.conf
来添加特定的nameserver
,但某些场景下我们不希望更改系统配置。比如在kubernetes
中,作为sidecar
服务需要通过service
去访问其他集群内服务,必须更改dnsPolicy
为ClusterFirst
,但这可能会影响其他容器的DNS查询效率。
自定义Nameserver
在Golang
中自定义Nameserver
,需要我们自己实现一个Resolver
,如果是httpClient
需要自定义DialContext()
Resolver
实现如下:
// 默认dialer
dialer := &net.Dialer{Timeout: 1 * time.Second,
}// 定义resolver
resolver := &net.Resolver{Dial: func(ctx context.Context, network, address string) (net.Conn, error) {return dialer.DialContext(ctx, "tcp", nameserver) // 通过tcp请求nameserver解析域名},
}
自定义Dialer
如下:
type Dialer struct {dialer *net.Dialerresolver *net.Resolvernameserver string
}// NewDialer create a Dialer with user's nameserver.
func NewDialer(dialer *net.Dialer, nameserver string) (*Dialer, error) {conn, err := dialer.Dial("tcp", nameserver)if err != nil {return nil, err}defer conn.Close()return &Dialer{dialer: dialer,resolver: &net.Resolver{Dial: func(ctx context.Context, network, address string) (net.Conn, error) {return dialer.DialContext(ctx, "tcp", nameserver)},},nameserver: nameserver, // 用户设置的nameserver}, nil
}// DialContext connects to the address on the named network using
// the provided context.
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {host, port, err := net.SplitHostPort(address)if err != nil {return nil, err}ips, err := d.resolver.LookupHost(ctx, host) // 通过自定义nameserver查询域名for _, ip := range ips {// 创建链接conn, err := d.dialer.DialContext(ctx, network, ip+":"+port)if err == nil {return conn, nil}}return d.dialer.DialContext(ctx, network, address)
}
httpClient
中自定义DialContext()
如下:
ndialer, _ := NewDialer(dialer, nameserver)client := &http.Client{Transport: &http.Transport{DialContext: ndialer.DialContext,TLSHandshakeTimeout: 10 * time.Second,},Timeout: timeout,
}
总结
通过以上实现可解决自定义Nameserver
,也可以在Dailer
中添加缓存,实现DNS缓存。
这篇关于Golang自定义DNS Nameserver的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!