golang实现dns域名解析(一)
本文将详细讲解如何用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 main import (
"bytes"
"encoding/binary"
"fmt"
"net"
"strings"
"time"
) type dnsHeader struct {
Id uint16
Bits uint16
Qdcount, 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<< + OperationCode<< + AuthoritativeAnswer<< + Truncation<< + RecursionDesired<< + RecursionAvailable<< + ResponseCode
} type dnsQuery struct {
QuestionType uint16
QuestionClass uint16
} func ParseDomainName(domain string) []byte {
var (
buffer bytes.Buffer
segments []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: ,
Ancount: ,
Nscount: ,
Arcount: ,
}
requestHeader.SetFlag(, , , , , , ) requestQuery := dnsQuery{
QuestionType: ,
QuestionClass: ,
} var (
conn net.Conn
err error
buffer bytes.Buffer
) if conn, err = net.Dial("udp", dnsServer); err != nil {
fmt.Println(err.Error())
return make([]byte, ), ,
}
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, )
t1 := time.Now()
if _, err := conn.Write(buffer.Bytes()); err != nil {
fmt.Println(err.Error())
return make([]byte, ), ,
}
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。下一节我们在来讨论下如何处理服务器端响应的内容。
golang实现dns域名解析(一)的更多相关文章
- golang实现dns域名解析(三):响应报文分析
前面说了构造请求发送报文,接下来我们好好研究下如何解析服务器端发回来的应答信息. 首先还是用前面的程序代码发一个请求,用抓包工具看看应答的内容有哪些: 截图的第一部分是返回信息的统计,表明这个返回的包 ...
- golang实现dns域名解析(二)
上一节已经讲了如何构造dns请求包的情况,这一节接着上一节的情况,谈谈dns查询报文中的问题部分.问题部分中每个问题的格式如下: 查询名是要查找的名字,它是一个或者多个标识符的序列.每个标识符以首字母 ...
- C++实现DNS域名解析
一.概述 现在来搞定DNS域名解析,其实这是前面一篇文章C++实现Ping里面的遗留问题,要干的活是ping的过程中画红线的部分: cmd下域名解析的命令是nslookup,比如“nslookup w ...
- DNS域名解析过程
图1-10是DNS域名解析的主要请求过程实例图. 如图1-10所示,当一个用户在浏览器中输入www.abc.com时,DNS解析将会有将近10个步骤,这个过程大体描述如下.当用户在浏览器中输入域名并按 ...
- 配置DNS域名解析服务器
bind这个DNS域名解析服务器解析好后,执行下面的语句实现开启服务 named -c named.conf & -c指配置脚本named.conf的文件地址 named.conf主要有下面几 ...
- 域名下Web项目重定向出现DNS域名解析错误问题
问题: 项目使用的是阿里云的ESC,前几天为IP地址添加了域名 发现发送正常请求时跳转没问题,但发送重定向请求时,页面就会出现DNS域名解析错误的情况 1.我在Tomcat的server.xml中配置 ...
- 每天进步一点点——负载均衡之DNS域名解析
转载请说明出处:http://blog.csdn.net/cywosp/article/details/38017027 在上一篇文章(http://blog.csdn.net/cywosp/ ...
- 一张图看懂DNS域名解析全过程
DNS域名解析是互联网上非常重要的一项服务,上网冲浪(还有人在用这个词吗?)伴随着大量DNS服务来支撑,而对于网站运营来说,DNS域名解析的稳定可靠,意味着更多用户的喜欢,更好的SEO效果和更大的访问 ...
- DNS域名解析过程,域名的认识
DNS域名解析过程 参考知乎:https://www.zhihu.com/question/23042131 当你通过浏览器输入url访问资源时,会请求DNS解析域名成对应的IP地址,由IP地址在去与 ...
随机推荐
- 2018 Multi-University Training Contest 1 Balanced Sequence(贪心)
题意: t组测试数据,每组数据有 n 个只由 '(' 和 ')' 构成的括号串. 要求把这 n 个串排序然后组成一个大的括号串,使得能够匹配的括号数最多. 如()()答案能够匹配的括号数是 4,(() ...
- 实现hadoop自动安装包
最近研究hadoop,需要安装多个dadanode,想从重复劳动解脱出来,只能自己实现自动安装包,开始考虑使用shell.python等实现,感觉比较费时间,用installshield又有点牛刀小试 ...
- provider:命名管道提供程序,error:40 - 无法打开到SQL Server的连接 (Microsoft
最近一直在配置服务器, 这当中最头疼的就是配置数据库 我们用的是SQL Server 数据库 2008 版本,数据库配置完之后从另一台电脑访问数据库死活连接不上,提示信息如下 " 无法连接到 ...
- dubbo rpc filter实现剖析(二)
2.6.3版本,之前读的是2.4.9版本 本篇主要阐述dubbo rpc的filter的实现,包括作用,用法,原理,与Spring Cloud在这些能力的对比. 整个filter列表的获取过程在 co ...
- python week08 并发编程之多线程--实践部分
一. threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍 官网链接:https://docs.pytho ...
- 设计模式之工厂模式 Factory实现
simpleFactory //car接口 public interface Car { void run(); } //两个实现类 public class Audi implements Car{ ...
- verilog 实现中值滤波
图像信号在形成.传输和记录的过程中,由于成像系统.传输介质.工作环境和记录设备等的固有缺陷,不可避免地产生各种类型的噪声,降低了图像的质量,进而影响后续处理(如边缘检测.图像分割.特征提取.模式识别等 ...
- EOJ Monthly 2018.1
985月赛,当时鸽了,现在想补一补 A. 石头剪刀布的套路 Time limit per test: 1.0 seconds Memory limit: 256 megabytes 现在有一种石头剪刀 ...
- rabbitmq exchange type
This is the fourth installment to the series: RabbitMQ for Windows. In thelast installment, we revi ...
- 九度oj 题目1457:非常可乐
题目描述: 大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为.因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样 ...