1. 程式人生 > 其它 >Go語言網路程式設計: 模擬實現DNS伺服器

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