本文将详细讲解如何用go语言一步一步实现dns域名解析的过程,并简单介绍点dns有关的知识,直接开始正题吧。

  首先我们要了解dns解析的过程,没有了解的请看这里DNS入门(转)很详细。扫盲结束后,我们需要了解下dns报文格式,知道了报文的格式是怎样的,才可以写代码构造dns请求包:

  dns请求和应答都是用相同的报文格式,分成5个段(有的报文段在不同的情况下可能为空),如下:

  

  Header段是报文的头部,它定义了报文是请求还是应答,也定义了其他段是否需要存在,以及是标准查询还是其他。

  Header包含如下字段:

  

  各字段分别解释如下:

  ID:请求客户端设置的16位标示,服务器给出应答的时候会带相同的标示字段回来,这样请求客户端就可以区分不同的请求应答了。

  QR:1个比特位用来区分是请求(0)还是应答(1)。

  OPCODE:4个比特位用来设置查询的种类,应答的时候会带相同值,可用的值如下: 0 标准查询 (QUERY) 1 反向查询 (IQUERY) 2 服务器状态查询 (STATUS) 3-15保留值,暂时未使用

  AA:授权应答(Authoritative Answer) - 这个比特位在应答的时候才有意义,指出给出应答的服务器是查询域名的授权解析服务器。注意因为别名的存在,应答可能存在多个主域名,这个AA位对应请求名,或者应答中的第一个主域名。

  TC:截断(TrunCation) - 用来指出报文比允许的长度还要长,导致被截断。

  RD:期望递归(Recursion Desired) - 这个比特位被请求设置,应答的时候使用的相同的值返回。如果设置了RD,就建议域名服务器进行递归解析,递归查询的支持是可选的。

  RA:支持递归(Recursion Available) - 这个比特位在应答中设置或取消,用来代表服务器是否支持递归查询。

  Z:保留值,暂时未使用。在所有的请求和应答报文中必须置为0。

  RCODE:应答码(Response code) - 这4个比特位在应答报文中设置,代表的含义如下:

    0 没有错误。

    1 报文格式错误(Format error) - 服务器不能理解请求的报文。

    2 服务器失败(Server failure) - 因为服务器的原因导致没办法处理这个请求。

    3 名字错误(Name Error) - 只有对授权域名解析服务器有意义,指出解析的域名不存在。

    4 没有实现(Not Implemented) - 域名服务器不支持查询类型。

    5 拒绝(Refused) - 服务器由于设置的策略拒绝给出应答。比如,服务器不希望对某些请求者给出应答,或者服务器不希望进行某些操作(比如区域传送zone transfer)。

    6-15 保留值,暂时未使用。

  QDCOUNT 无符号16位整数表示报文请求段中的问题记录数。

  ANCOUNT 无符号16位整数表示报文回答段中的回答记录数。

  NSCOUNT 无符号16位整数表示报文授权段中的授权记录数。

  ARCOUNT 无符号16位整数表示报文附加段中的附加记录数。

  根据这些,dns头部的数据结构可以定义如下:

  type dnsHeader struct {

   Id                                 uint16

    Bits                               uint16

     Qdcount, Ancount, Nscount, Arcount uint16

  }

  构造头部信息我们主要处理Bits,可以直接根据需求对相应位置值,也可以定义好每一个字段,通过移位的方式然后相加构造请求的头部各个字段。推荐后一种方法,这样就有:

  header.Bits = QR<<15 + OperationCode<<11 + AuthoritativeAnswer<<10 + Truncation<<9 + RecursionDesired<<8 + RecursionAvailable<<7 + ResponseCode

其他的头部信息就比较简单了:

  requestHeader := dnsHeader{

Id:      0x0010,

Qdcount: 1,

Ancount: 0,

Nscount: 0,

Arcount: 0,

  }

  报文头搞定后,接下来就是查询问题Question:

  Question段描述了查询的问题,包括查询类型(QTYPE),查询类(QCLASS),以及查询的域名(QNAME)。字段含义如下   QNAME:域名被编码为一些labels序列,每个labels包含一个字节表示后续字符串长度,以及这个字符串,以0长度和空字符串来表示域名结束。注意这个字段可能为奇数字节,不需要进行边界填充对齐。   QTYPE:2个字节表示查询类型,.取值可以为任何可用的类型值,以及通配码来表示所有的资源记录。   QCLASS:2个字节表示查询的协议类,比如,IN代表Internet。所以我们直接定义查询的结构体如下:

  type dnsQuery struct {

  QuestionType  uint16

  QuestionClass uint16

  }

查询的域名不定义在查询的结构体中,由函数接收参数的方式接收。

  剩下的3个段包含相同的格式:一系列可能为空的资源记录(RRs)。Answer段包含回答问题的RRs;授权段包含授权域名服务器的RRs;附加段包含和请求相关的,但是不是必须回答的RRs。而在发送请求的时候,我们是发起请求方,所以这些字段放空就好。

完整代码:

// 002 project main.go
package mainimport ("bytes""encoding/binary""fmt""net""strings""time"
)type dnsHeader struct {Id                                 uint16Bits                               uint16Qdcount, Ancount, Nscount, Arcount uint16
}func (header *dnsHeader) SetFlag(QR uint16, OperationCode uint16, AuthoritativeAnswer uint16, Truncation uint16, RecursionDesired uint16, RecursionAvailable uint16, ResponseCode uint16) {header.Bits = QR<<15 + OperationCode<<11 + AuthoritativeAnswer<<10 + Truncation<<9 + RecursionDesired<<8 + RecursionAvailable<<7 + ResponseCode
}type dnsQuery struct {QuestionType  uint16QuestionClass uint16
}func ParseDomainName(domain string) []byte {var (buffer   bytes.Buffersegments []string = strings.Split(domain, "."))for _, seg := range segments {binary.Write(&buffer, binary.BigEndian, byte(len(seg)))binary.Write(&buffer, binary.BigEndian, []byte(seg))}binary.Write(&buffer, binary.BigEndian, byte(0x00))return buffer.Bytes()
}
func Send(dnsServer, domain string) ([]byte, int, time.Duration) {requestHeader := dnsHeader{Id:      0x0010,Qdcount: 1,Ancount: 0,Nscount: 0,Arcount: 0,}requestHeader.SetFlag(0, 0, 0, 0, 1, 0, 0)requestQuery := dnsQuery{QuestionType:  1,QuestionClass: 1,}var (conn   net.Connerr    errorbuffer bytes.Buffer)if conn, err = net.Dial("udp", dnsServer); err != nil {fmt.Println(err.Error())return make([]byte, 0), 0, 0}defer conn.Close()binary.Write(&buffer, binary.BigEndian, requestHeader)binary.Write(&buffer, binary.BigEndian, ParseDomainName(domain))binary.Write(&buffer, binary.BigEndian, requestQuery)buf := make([]byte, 1024)t1 := time.Now()if _, err := conn.Write(buffer.Bytes()); err != nil {fmt.Println(err.Error())return make([]byte, 0), 0, 0}length, err := conn.Read(buf)t := time.Now().Sub(t1)return buf, length, t
}
func main() {remsg, n, _ := Send("114.114.114.114:53", "www.baidu.com")fmt.Println(remsg, n)
}

抓个包看看:

这是发出去的,看看详细的Questions信息:

我们设置的请求类型是1,class是1,意味着是请求A记录,class IN。下一节我们在来讨论下如何处理服务器端响应的内容。

转载于:https://www.cnblogs.com/chase-wind/p/6814053.html

golang实现dns域名解析(一)相关推荐

  1. C++如何实现DNS域名解析转

    C++如何实现DNS域名解析 这片文章介绍了C++如何实现DNS域名解析,还有对相关技术的介绍,代码很详细,需要的朋友可以参考下 一.概述 现在来搞定DNS域名解析,其实这是前面一篇文章C++实现Pi ...

  2. Linux系统安装DNS域名解析服务

    在linux系统下安装DNS域名解析服务 步骤如下: 1>查看挂在镜像位置 2>查看光盘是否挂载 3>查看需要挂在的位置 4>进行挂载 [root@localhost mnt] ...

  3. 搭建DNS域名解析服务器和本地配置HOST文件有什么区别?

    1.DNS服务器可以供其他人一起使用,hosts 文件只能是一个人用.这样修改个记录还得每个人都改一次 hosts, 而 dns 只需要改一次,大家都生效. 2.hosts速度快,不依赖网络,不用担心 ...

  4. 【鬼网络】之DNS域名解析服务

    序言 域名解析 序言 一.BIND域名服务基础 1.DNS系统的作用及类型 2.DNS系统类型 BIND的安装和配置文件 使用BIND构建域名服务器 构建缓存域名服务器 构建主.从域名服务器 总结 一 ...

  5. DNS域名解析服务(正向解析、反向解析、主从服务器)

    介 绍 Internet发展至今,在网的服务器和个人主机数量庞大,每个用户通过记忆IP地址访问网络资源并不现实了. 目前大家访问互联网进行上网浏览信息时,正常是通过域名进行访问(例如:www.baid ...

  6. DNS域名解析服务(正向解析,反向解析,主从解析)

    文章目录 DNS域名解析 DNS系统的作用及类型 DNS系统的作用 DNS类型 BIND的安装文件 正向解析 反向解析 主从复制 主服务器 从服务器 DNS域名解析 [域名]解析是把域名指向网站空间I ...

  7. Linux网络编程小知识(字节序、IP格式、函数、子网掩码、DNS域名解析代码实现)

    参考:网络编程前的一些小知识–Linux笔记 作者:一只青木呀 发布时间: 2021-04-12 23:19:10 网址:https://blog.csdn.net/weixin_45309916/a ...

  8. 在linux下dns绑定域名,在Linux系统中,使用Bind搭建DNS域名解析服务

    DNS域名解析服务(DomainNameSystem)是用于解析域名与IP地址对应关系的服务作用为维护着一个地址数据库,记录着各种主机域名与IP地址的对应关系,以便为客户提供正向或反向的地址查询服务, ...

  9. C#DNS域名解析工具(DnsLookup)

    C#DNS域名解析工具(DnsLookup) DNS域名解析工具:DnsLookup 输入域名后点击Resolve按钮即可. 主要实现代码如下: private void btnResolve_Cli ...

最新文章

  1. Asynctask源码分析
  2. linux 内核调整相关参数
  3. Netty入门之Netty的基本介绍和IO模型
  4. 阿里云rds升级mysql8_为更强大而生的开源关系型数据库来了!阿里云RDS for MySQL 8.0 正式上线!...
  5. TransactionScope oracle不能用的问题(转载)
  6. typora html代码无效,Typora优化-适合不懂CSS代码的小白
  7. 两堆草前饿死的驴——选择永远是件痛苦的事
  8. ARM开发7.3.2 基础实训( 2 ) 单个按键的输入系统设计( 2)--LPC21XX
  9. DTD中的实体Entity
  10. 单片机--串口通信---11
  11. 第七次前端培训(JavaScript)
  12. python爬虫爬取王者荣耀官网全部英雄头像(源码分享)
  13. 云原生架构下的微服务选型和演进
  14. 用友试水NC租用服务
  15. JavaScript的16进制转10进制
  16. c语言虚数变量,c语言虚数
  17. 2007年8月22日Internet应用讲义以及作业
  18. 像素射击服务器维护公告图片,像素射击怎样导入自定义头像 - 历史资讯网
  19. 基于MATLAB的条形码二维码识别系统
  20. 思享工具箱,各种工具汇总

热门文章

  1. maven pom resources标签的使用示例
  2. 使用KubeKey 离线环境部署 KubeSphere v3.0.0
  3. linux deepin/ubuntu 搭nginx文件服务器配置
  4. Python Django 重写delete方法实现单个对象的删除
  5. 【软考-软件设计师】计算机存储系统
  6. SQL语句--INSERT INTO SELECT 语句用法示例
  7. 【软考-软件设计师】CPU的功能与组成
  8. 创建springboot多模块项目
  9. linux cpu 实际进程,linux – 找出高CPU占用率的apache进程实际上在做什么?
  10. java 静态 二维数组 转化hashmap_将一个二维数组转换为 hashmap 哈希表