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