概要

DNS协议属于比较简单的网络协议,最近用golang实现了对于dns协议的解包和打包,暂时只实现了一个查询问题与一个回答问题,代码如下。
本文代码地址
https://github.com/changjixiong/goNotes/tree/master/dnsnotes

DNS报文解包与打包

package dnsKitimport ("bytes""encoding/binary""net""strings"
)/*
DNS报文格式,不论是请求报文,还是DNS服务器返回的应答报文,都使用统一的格式。
2个字节(16bit),标识字段,客户端会解析服务器返回的DNS应答报文,获取ID值与请求报文设置的ID值做比较
如果相同,则认为是同一个DNS会话。
QR  标示该消息是请求消息(该位为0)还是应答消息(该位为1)
QR这一段位 Flag 16bit长度
header0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                      ID                       |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR|  opcode   |AA|TC|RD|RA|   Z    |   RCODE   |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    QDCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ANCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    NSCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ARCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+question0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                     ...                       |
|                    QNAME                      |
|                     ...                       |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    QTYPE                      |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    QCLASS                     |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/// DNSHeader 头
type DNSHeader struct {ID uint16//golang 没有bit类型,只能这样处理QR                  uint16 //1bitOperationCode       uint16 //4bitAuthoritativeAnswer uint16 //1bitTruncation          uint16 //1bitRecursionDesired    uint16 //1bitRecursionAvailable  uint16 //1bitZero                uint16 //3bitResponseCode        uint16 //4bitQuestionCount uint16AnswerRRs     uint16AuthorityRRs  uint16AdditionalRRs uint16
}func (dh *DNSHeader) ToBytes() []byte {var buffer bytes.Bufferbinary.Write(&buffer, binary.BigEndian, dh.ID)bits := dh.QR<<15 + dh.OperationCode<<11 + dh.AuthoritativeAnswer<<10 + dh.Truncation<<9bits += dh.RecursionDesired<<8 + dh.RecursionAvailable<<7 + dh.ResponseCodebinary.Write(&buffer, binary.BigEndian, bits)binary.Write(&buffer, binary.BigEndian, dh.QuestionCount)binary.Write(&buffer, binary.BigEndian, dh.AnswerRRs)binary.Write(&buffer, binary.BigEndian, dh.AuthorityRRs)binary.Write(&buffer, binary.BigEndian, dh.AdditionalRRs)return buffer.Bytes()
}func NewDNSHeader(buffer *bytes.Buffer) *DNSHeader {// 读12byteid := binary.BigEndian.Uint16(buffer.Next(2))flag := binary.BigEndian.Uint16(buffer.Next(2))return &DNSHeader{ID: id,QR:                  flag >> 15,OperationCode:       (flag >> 11) % (1 << 4),AuthoritativeAnswer: (flag >> 10) % (1 << 1),Truncation:          (flag >> 9) % (1 << 1),RecursionDesired:    (flag >> 8) % (1 << 1),RecursionAvailable:  (flag >> 7) % (1 << 1),Zero:                (flag >> 4) % (1 << 3),ResponseCode:        flag % (1 << 4),QuestionCount: binary.BigEndian.Uint16(buffer.Next(2)),AnswerRRs:     binary.BigEndian.Uint16(buffer.Next(2)),AuthorityRRs:  binary.BigEndian.Uint16(buffer.Next(2)),AdditionalRRs: binary.BigEndian.Uint16(buffer.Next(2)),}
}//DNSQuestion 请求
type DNSQuestion struct {QuestionName  stringQuestionType  uint16QuestionClass uint16
}func (dq *DNSQuestion) ToBytes() []byte {var buffer bytes.Buffersegments := strings.Split(dq.QuestionName, ".")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))binary.Write(&buffer, binary.BigEndian, dq.QuestionType)binary.Write(&buffer, binary.BigEndian, dq.QuestionClass)return buffer.Bytes()
}func NewDNSQuestion(buffer *bytes.Buffer) *DNSQuestion {//8bit标记每一级域名的长度// buf := bytes.NewBuffer(data)length := uint8(0)binary.Read(buffer, binary.BigEndian, &length)segments := []string{}for length > 0 {seg := make([]byte, length)binary.Read(buffer, binary.BigEndian, &seg)segments = append(segments, string(seg))binary.Read(buffer, binary.BigEndian, &length)// fmt.Println(length)}question := &DNSQuestion{QuestionName: strings.Join(segments, "."),}question.QuestionType = binary.BigEndian.Uint16(buffer.Next(2))question.QuestionClass = binary.BigEndian.Uint16(buffer.Next(2))return question
}//DNSResourceRecode 回答字段,授权字段,附加字段
type DNSResourceRecode struct {Name     stringNamePos  uint16RRType   uint16Class    uint16TTL      uint32RDLength uint16RData    string
}func (drr *DNSResourceRecode) ToBytes() []byte {var buffer bytes.Bufferif drr.NamePos > 0 {binary.Write(&buffer, binary.BigEndian, (0x01<<15)|(0x01<<14)|drr.NamePos)} else {segments := strings.Split(drr.Name, ".")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))}binary.Write(&buffer, binary.BigEndian, drr.RRType)binary.Write(&buffer, binary.BigEndian, drr.Class)binary.Write(&buffer, binary.BigEndian, drr.TTL)binary.Write(&buffer, binary.BigEndian, drr.RDLength)if drr.Class == 1 {binary.Write(&buffer, binary.BigEndian, []byte(net.ParseIP(drr.RData).To4()))} else if drr.Class == 5 {}return buffer.Bytes()
}func NewDNSResourceRecode(buffer *bytes.Buffer) *DNSResourceRecode {tag := buffer.Next(1)[0] >> 6buffer.UnreadByte()drr := &DNSResourceRecode{}if tag == 3 {//最高两位11,右移后是3drr.NamePos = (binary.BigEndian.Uint16(buffer.Next(2)) << 2) >> 2} else {}drr.RRType = binary.BigEndian.Uint16(buffer.Next(2))drr.Class = binary.BigEndian.Uint16(buffer.Next(2))drr.TTL = binary.BigEndian.Uint32(buffer.Next(4))drr.RDLength = binary.BigEndian.Uint16(buffer.Next(2))if drr.RRType == 1 && drr.RDLength == 4 {drr.RData = net.IPv4(buffer.Next(1)[0], buffer.Next(1)[0], buffer.Next(1)[0], buffer.Next(1)[0]).String()} else {//FIXME://域名处理}return drr
}// DNSMessage 消息体
type DNSMessage struct {Header          *DNSHeaderQuestions       []*DNSQuestionResourceRecodes []*DNSResourceRecode
}func (dm *DNSMessage) ToBytes() []byte {result := []byte{}result = append(result, dm.Header.ToBytes()...)for i := uint16(0); i < dm.Header.QuestionCount; i++ {result = append(result, dm.Questions[i].ToBytes()...)}for _, rr := range dm.ResourceRecodes {result = append(result, rr.ToBytes()...)}return result
}func NewDNSMessage(buffer *bytes.Buffer) *DNSMessage {//用bytes.Buffer类型来逐个字节读取后处理的优点就是不需要自己计算读取偏移值//这个对于Question和Answer这种第一段长度不固定的内容处理非常方便dnsMsg := &DNSMessage{Header: NewDNSHeader(buffer),}//FIXME: deal more then 1 QuestionCountdnsMsg.Questions = append(dnsMsg.Questions, NewDNSQuestion(buffer))//FIXME: deal more then 1 ResourceRecodeif buffer.Len() > 0 {dnsMsg.ResourceRecodes = append(dnsMsg.ResourceRecodes, NewDNSResourceRecode(buffer))}return dnsMsg
}

客户端

package mainimport ("bytes""fmt". "goNotes/dnsnotes/dnsKit""net"
)func main() {dnsMsg1 := DNSMessage{Header: &DNSHeader{ID:                  0x0010,QR:                  0,OperationCode:       0,AuthoritativeAnswer: 0,Truncation:          0,RecursionDesired:    1,RecursionAvailable:  0,Zero:                0,ResponseCode:        0,QuestionCount:       1,AnswerRRs:           0,AuthorityRRs:        0,AdditionalRRs:       0,},Questions: []*DNSQuestion{&DNSQuestion{QuestionName:  "www.test1.com",QuestionType:  1,QuestionClass: 1,},},}dnsMsg2 := DNSMessage{Header: &DNSHeader{ID:                  0x0010,QR:                  0,OperationCode:       0,AuthoritativeAnswer: 0,Truncation:          0,RecursionDesired:    1,RecursionAvailable:  0,Zero:                0,ResponseCode:        0,QuestionCount:       1,AnswerRRs:           0,AuthorityRRs:        0,AdditionalRRs:       0,},Questions: []*DNSQuestion{&DNSQuestion{QuestionName:  "www.test2.com",QuestionType:  1,QuestionClass: 1,},},}dnsServer := "127.0.0.1:11153"var conn net.Connvar err errorif conn, err = net.Dial("udp", dnsServer); err != nil {fmt.Println(err.Error())return}defer conn.Close()if _, err := conn.Write(dnsMsg1.ToBytes()); err != nil {fmt.Println(err.Error())return}buf := make([]byte, 1024)if length, err := conn.Read(buf); err == nil {result := NewDNSMessage(bytes.NewBuffer(buf[0:length]))fmt.Println("query:", result.Questions[0].QuestionName, ", get ip:", result.ResourceRecodes[0].RData)} else {fmt.Println(err.Error())}if _, err = conn.Write(dnsMsg2.ToBytes()); err != nil {fmt.Println(err.Error())return}buf = make([]byte, 1024)if length, err := conn.Read(buf); err == nil {result := NewDNSMessage(bytes.NewBuffer(buf[0:length]))fmt.Println("query:", result.Questions[0].QuestionName, ", get ip:", result.ResourceRecodes[0].RData)} else {fmt.Println(err.Error())}}

服务端

对域名返回的ip从10.0.0.1开始,新域名返回的ip加1

package mainimport ("bytes""encoding/binary""fmt". "goNotes/dnsnotes/dnsKit""net""sync"
)const RECV_BUF_LEN = 1024var addr2IP Address2IPtype Address2IP struct {lastIP uint32 //167772160 -> 10.0.0.0sync.RWMutexaddress2ip map[string]uint32
}func (a *Address2IP) getIP(address string) string {a.RLock()ip := uint32(0)ok := falseif ip, ok = a.address2ip[address]; ok {a.RUnlock()} else {a.RUnlock()a.Lock()a.lastIP++a.address2ip[address] = a.lastIPip = a.lastIPa.Unlock()}ipByte := []byte{0, 0, 0, 0}binary.BigEndian.PutUint32(ipByte, ip)return net.IPv4(ipByte[0], ipByte[1], ipByte[2], ipByte[3]).String()
}func main() {addr2IP = Address2IP{lastIP:     167772160,address2ip: map[string]uint32{},}udpAddr, err := net.ResolveUDPAddr("udp", ":11153")if err != nil {panic("error listening:" + err.Error())}fmt.Println("Starting the server")conn, err := net.ListenUDP("udp", udpAddr)defer conn.Close()for {buf := make([]byte, RECV_BUF_LEN)n, raddr, err := conn.ReadFromUDP(buf)if err != nil {panic("Error accept:" + err.Error())}fmt.Println("Accepted the Connection :", conn.RemoteAddr())go echoServer(conn, raddr, buf[0:n])}
}func echoServer(conn *net.UDPConn, raddr *net.UDPAddr, data []byte) {dnsMsg := NewDNSMessage(bytes.NewBuffer(data))dnsMsg.Header.QR = 1dnsMsg.Header.RecursionAvailable = 1dnsMsg.Header.AnswerRRs = 1dnsMsg.ResourceRecodes = append(dnsMsg.ResourceRecodes,&DNSResourceRecode{Name:     "",NamePos:  12,RRType:   1,Class:    1,TTL:      384,RDLength: 4,RData:    addr2IP.getIP(dnsMsg.Questions[0].QuestionName),})fmt.Println("query:", dnsMsg.Questions[0].QuestionName, ", ip:", dnsMsg.ResourceRecodes[0].RData)conn.WriteToUDP(dnsMsg.ToBytes(), raddr)}

更多代码

golang通过反射使用json字符串调用struct的指定方法及返回json结果
golang根据配置的时间和时区计算定时任务是否到了刷新时间
golang利用模板生成数据库表对应的模型及操作函数
golang实时消息平台NSQ的使用
golang使用服务发现系统consul
golang实现基于redis和consul的可水平扩展的排行榜服务范例
DNS协议解析与DNS模拟服务器-基于golang实现

DNS协议解析与DNS模拟服务器-基于golang实现相关推荐

  1. 868-超详细 DNS 协议解析

    0. 前言 为了保证网址的正常访问,域名解析协议(DNS)其实在背后做出了很多努力,本文将透彻讲解 DNS 协议的原理,了解我们每天都在接触的网址到底是怎么工作的. 1. 什么是 DNS 协议 在学习 ...

  2. 实验详解——DNS反向解析、DNS主服务器和从服务器的配置

    实验详解--DNS反向解析.DNS主服务器和从服务器的配置 实验一:DNS反向解析 1.安装bind 2.查找配置文件路径 3.配置/etc/named.conf主配置文件 4.修改/etc/name ...

  3. 超详细的 DNS 协议解析

    文章目录 0. 前言 1. 什么是 DNS 协议 2. 域名详解 3. 域名服务器详解 4. DNS 查询方式 5. 域名缓存 6. 完整域名解析过程 0. 前言 为了保证网址的正常访问,域名解析协议 ...

  4. 使用Wireshark进行DNS协议解析

    " DNS协议格式解析及说明." DNS即域名系统(Domain Name System),是用来将域名与IP地址建立映射的协议,通过DNS协议,可以方便记忆. DNS可基于TCP ...

  5. 【安全牛学习笔记】DNS协议隧道、DNS协议隧道-dns2tcp

    DNS协议隧道 防火墙禁止TCP出站访问流量 - SSH隧道.端口准发全部失效 - 使用基于UPD协议的隧道 - DNS的工作原理适合用于实现隧道 DNS工作原理 - DNS隧道原理: 注册受自己控制 ...

  6. mysql dns反向解析_Mysql DNS反向解析导致连接超时过程分析(skip-name-resolve)

    Mysql DNS反向解析导致连接超时过程分析(skip-name-resolve) 时间:2019-01-19 11:28作者:网友投稿 MySQL数据库收到一个网络连接后,首先拿到对方的IP地址, ...

  7. DNS域名解析服务1(高速缓存dns,dns正向解析,dns正向轮询解析,dns反向解析,dns双向解析)

    域名解析是把域名指向网站空间IP,让人们通过注册的域名可以方便地访问到网站的一种服务.IP地址是网络上标识站点的数字地址,为了方便记忆,采用域名来代替IP地址标识站点地址.域名解析就是域名到IP地址的 ...

  8. 首选dns协议版本6怎么填服务器,首选dns怎么填服务器?

    首选dns和备用dns分别设成208.67.222.222和208.67.220.220. DNS是英文Domain Name System的缩写,是域名解析服务器的意思,即域名管理系统.它在互联网的 ...

  9. 【.NET6+Modbus】Modbus TCP协议解析、仿真环境以及基于.NET实现基础通信

    接下来的内容,我会以从头开发一个简单的基于modbus tcp通信的案例,来实现一个基础的通信功能. 有关环境: 开发环境:VS 2022企业版 运行环境:Win 10 专业版 .NET 环境版本:. ...

  10. 基于mqtt协议的消息推送服务器,基于 MQTT 协议的推送服务

    一.简述 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级& ...

最新文章

  1. 不止视觉,CMU研究员让机器人学会了听音辨物
  2. 上海大学matlab实验报告,实验中心
  3. 变频器服务器电路板维修,变频器电路板维修技巧
  4. mybatis配置insert/update/delete同一个模板
  5. C#datagridView中嵌套控件
  6. php header 跳转 ie问题
  7. 为什么现在社交电商这么火?
  8. 数码相机冲洗照片的数据
  9. Footprint Analytics:多角度理解Layer 2生态:概念、扩容方案及代表项目
  10. java 问号运算符_JAVA问号?运算符的用法,问号表达式
  11. 【算法】动态规划之计算二项式系数(C++源码)
  12. python股票网格交易法详解_我的选股方法 大家好,我是阿兰,一个将网格交易法应用于股票的人。昨天我给大家介绍了我的网格交易体系,并简单说明了我的选股方法,今天我将... - 雪球...
  13. 数据加密 RSA非对称加密篇
  14. 隐藏Windows系统托盘图标
  15. 数据结构程序设计——山东省城际铁路建设建设
  16. C#程序探测未知网络环境的神操作
  17. 微信公众号采集 php,如何采集微信公众号历史消息页
  18. 魅惑低音按摩你的耳朵,飞利浦S301HIFI圈铁耳机评测
  19. Mac 使用brew 安装工具报错 Your CLT does not support macOS 11
  20. python将图片转为字符画_Python将图片转换为字符画的方法

热门文章

  1. 两个jquery 类似igoogle的portlets插件
  2. 微信小程序使用阿里字体图标库的方法
  3. 信息安全必备的8张思维导图
  4. HDDREG(硬盘坏道修复工具)v1.31绿色版
  5. Intelligent IME
  6. 东方时尚驾校学习心得
  7. Android数据库升级,android开发艺术探索pdf百度网盘
  8. 计算机应用工程师如何评定,个人如何申请工程师职称评定
  9. JavaScript 更新Dom节点
  10. 阿里云服务器配置DNS域名解析