文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

使用golang怎么实现一个DNS服务器

2023-06-14 22:51

关注

使用golang怎么实现一个DNS服务器?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

golang适合做什么

golang可以做服务器端开发,但golang很适合做日志处理、数据打包、虚拟机处理、数据库代理等工作。在网络编程方面,它还广泛应用于web应用、API应用等领域。

简单的DNS服务器

提供一个简单的可以查询域名和反向查询的DNS服务器。

dig命令主要用来从 DNS 域名服务器查询主机地址信息。

查找www.baidu.com的ip (A记录):

命令:dig @127.0.0.1 www.baidu.com

使用golang怎么实现一个DNS服务器

根据ip查找对应域名 (PTR记录):

命令:dig @127.0.0.1 -x 220.181.38.150

使用golang怎么实现一个DNS服务器

源码 :

package mainimport ("fmt""net""golang.org/x/net/dns/dnsmessage")func main() {conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 53})if err != nil {panic(err)}defer conn.Close()fmt.Println("Listing ...")for {buf := make([]byte, 512)_, addr, _ := conn.ReadFromUDP(buf)var msg dnsmessage.Messageif err := msg.Unpack(buf); err != nil {fmt.Println(err)continue}go ServerDNS(addr, conn, msg)}}// address booksvar (addressBookOfA = map[string][4]byte{"www.baidu.com.": [4]byte{220, 181, 38, 150},}addressBookOfPTR = map[string]string{"150.38.181.220.in-addr.arpa.": "www.baidu.com.",})// ServerDNS servefunc ServerDNS(addr *net.UDPAddr, conn *net.UDPConn, msg dnsmessage.Message) {// query infoif len(msg.Questions) < 1 {return}question := msg.Questions[0]var (queryTypeStr = question.Type.String()queryNameStr = question.Name.String()queryType    = question.TypequeryName, _ = dnsmessage.NewName(queryNameStr))fmt.Printf("[%s] queryName: [%s]\n", queryTypeStr, queryNameStr)// find recordvar resource dnsmessage.Resourceswitch queryType {case dnsmessage.TypeA:if rst, ok := addressBookOfA[queryNameStr]; ok {resource = NewAResource(queryName, rst)} else {fmt.Printf("not fount A record queryName: [%s] \n", queryNameStr)Response(addr, conn, msg)return}case dnsmessage.TypePTR:if rst, ok := addressBookOfPTR[queryName.String()]; ok {resource = NewPTRResource(queryName, rst)} else {fmt.Printf("not fount PTR record queryName: [%s] \n", queryNameStr)Response(addr, conn, msg)return}default:fmt.Printf("not support dns queryType: [%s] \n", queryTypeStr)return}// send responsemsg.Response = truemsg.Answers = append(msg.Answers, resource)Response(addr, conn, msg)}// Response returnfunc Response(addr *net.UDPAddr, conn *net.UDPConn, msg dnsmessage.Message) {packed, err := msg.Pack()if err != nil {fmt.Println(err)return}if _, err := conn.WriteToUDP(packed, addr); err != nil {fmt.Println(err)}}// NewAResource A recordfunc NewAResource(query dnsmessage.Name, a [4]byte) dnsmessage.Resource {return dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name:  query,Class: dnsmessage.ClassINET,TTL:   600,},Body: &dnsmessage.AResource{A: a,},}}// NewPTRResource PTR recordfunc NewPTRResource(query dnsmessage.Name, ptr string) dnsmessage.Resource {name, _ := dnsmessage.NewName(ptr)return dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name:  query,Class: dnsmessage.ClassINET,},Body: &dnsmessage.PTRResource{PTR: name,},}}

补充: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实现如下:

// 默认dialerdialer := &net.Dialer{  Timeout: 1 * time.Second,}// 定义resolverresolver := &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.Dialer resolver   *net.Resolver nameserver 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,}

关于使用golang怎么实现一个DNS服务器问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网行业资讯频道了解更多相关知识。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-服务器
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯