Go语言网络编程: 模拟实现DNS服务器

环境: 两台虚拟机,不限系统

写在前面

DNS服务器是干什么的?DNS服务器(Domain Name Server,域名服务器)是进行域名和与之相对应的IP地址进行转换的服务器,保存了一张域名和与之相对应的IP地址 的表,以解析消息的域名。

在Linux上使用nslookup可以查询域名对应的IP

$ nslookup google.com
Server:         114.114.114.114
Address:        114.114.114.114#53

Non-authoritative answer:
Name:   google.com
Address: 142.251.43.14

初步环境搭建

目前有一台ubuntu虚拟机将作为DNS服务器,在ubuntu中执行命令 sudo lsof -i:53,查看占用53端口的进程,强行关掉该进程,解除对UDP 53端口的占用。

另一台虚拟机系统为Linux Lite,后续将用来发送DNS请求。在ubuntu的终端中输入ifconfig查看IP地址。

ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.53.129  netmask 255.255.255.0  broadcast 192.168.53.255
        inet6 fe80::f60:737c:cda6:87b2  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:92:bc:64  txqueuelen 1000  (Ethernet)

于是ubuntu的IP地址为192.168.53.129,将该地址作为Linux Lite的DNS服务器地址。

在Linux Lite的终端中输入sudo vim /etc/resolv.conf,用管理员权限修改该文件,将nameserver 对应的IP修改为ubuntu的IP地址。

# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
# 127.0.0.53 is the systemd-resolved stub resolver.
# run "systemd-resolve --status" to see details about the actual nameservers.

nameserver 192.168.53.129
search localdomain

修改后的文件内容如上,这样Linux Lite的DNS查询会发到ubuntu上。

编写DNS服务器

使用github.com/miekg/dns,可通过go get下载

DNS服务器将处理到来的DNS请求,并返回应答。

代码如下

package main

import (
	"github.com/miekg/dns"
	"log"
	"net"
)

// 处理到来的请求
func handler(writer dns.ResponseWriter, req *dns.Msg) {
	var resp dns.Msg
	resp.SetReply(req) // 创建应答
	for _, question := range req.Question {
		recordA := dns.A{
			Hdr: dns.RR_Header{
				Name:   question.Name,
				Rrtype: dns.TypeA,
				Class:  dns.ClassINET,
				Ttl:    0,
			},
			A: net.ParseIP("127.0.0.1").To4(), // 全部解析为127.0.0.1
		}
		resp.Answer = append(resp.Answer, &recordA) // 写入应答
	}
	err := writer.WriteMsg(&resp) // 回写信息
	if err != nil {
		return
	}
}

func main() {
	dns.HandleFunc(".", handler) // 绑定函数
	err := dns.ListenAndServe(":53", "udp", nil) // 启动
	if err != nil {
		log.Println(err)
	}
}

如上代码所示,首先调用HandleFunc,该函数的第一个参数是匹配的查询模式,第二个参数是处理函数。查询模式指示了处理函数将处理哪些请求,使用"."意味着handler将处理所有请求。handler函数负责处理到来的请求,具有两个参数: ResponseWriter和请求本身。在该函数内部,首先要使用SetReply创建响应消息并进行设置。

使用for 循环遍历请求中的每一个询问,使用A记录为每一个询问创建应答。使用append函数将指向A记录的指针添加到应答中,然后使用WriteMsg函数将消息写回客户端。

最后调用ListenAndServe启动DNS服务器,将所有的请求解析为127.0.0.1

测试

用管理员权限在ubuntu上运行程序,然后在Linux Lite的终端输入nslookup google.com,即查询google.com的IP地址。

输出结果

Server:		192.168.53.129
Address:	192.168.53.129#53

Non-authoritative answer:
Name:	google.com
Address: 127.0.0.1
Name:	google.com
Address: 127.0.0.1

被解析为了127.0.0.1

posted @ 2022-05-29 12:15  N3ptune  阅读(941)  评论(0编辑  收藏  举报