关于golang DNS服务器的简单实现,我们可以按如下步骤进行:
步骤1:准备工作在开始之前,需要安装Go编程语言和一些库,比如:
net
库:用于处理网络连接log
库:用于记录日志信息flag
库: 用于解析命令行参数
可以在终端中输入下面的命令来安装:
go get -u github.com/miekg/dns
步骤2:导入依赖的包和提供变量声明
在代码中导入必须的引用:
package main
import (
"flag"
"log"
"net"
"github.com/miekg/dns"
)
并声明全局变量:
var (
domain string
ip string
)
步骤3:解析和设置命令行参数
使用flag库,我们可以在脚本中去解析和配置命令行参数。例如我们可以提供一个domain
命令行参数和一个ip
命令行参数。定义代码如下:
func init() {
flag.StringVar(&domain, "domain", "example.com.", "-domain=example.com.")
flag.StringVar(&ip, "ip", "1.1.1.1", "-ip=1.1.1.1")
}
步骤4:设置DNS Handler
func dnsHandler(w dns.ResponseWriter, r *dns.Msg) {
m := new(dns.Msg)
m.SetReply(r)
for _, q := range r.Question {
if q.Qtype == dns.TypeA && q.Name == domain {
a := &dns.A{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600},
A: net.ParseIP(ip),
}
m.Answer = append(m.Answer, a)
}
}
w.WriteMsg(m)
}
以上代码是用于处理DNS请求的公共函数。它接受两个参数:一个Message指针和一个ResponseWriter。ResponseWriter在处理完请求后,用于回复客户端。在我们这个实现中,我们遍历每个DNS问题,并检查是否为A记录,并检查问题的名称是否与定义的域名相同。如果是,我们将创建一个A记录返回给客户端。最后,我们使用WriteMsg
方法将响应写回客户端。
最终的步骤是在main函数中实现监听DNS服务。示例代码如下:
func main() {
flag.Parse()
server := &dns.Server{Addr: ":53", Net: "udp"}
dns.HandleFunc(".", dnsHandler)
log.Printf("Listening on %s ...\n", server.Addr)
if err := server.ListenAndServe(); err != nil {
log.Fatalf("Failed to setup the DNS server: %s\n", err.Error())
}
}
在main函数中,我们首先解析命令行参数,然后初始化我们的DNS服务。监听DNS端口:53
和网络协议为UDP
。我们还将dnsHandler
方法设置为DNS的默认处理程序。最后,使用ListenAndServe
方法启动我们的DNS服务器。
服务启动后,我们就可以将我们的本地设备设置为DNS客户端,并访问我们定义的域名(example.com)来返回我们设置的IP(1.1.1.1),示例代码如下:
# 启动golang DNS服务
go run main.go -domain=example.com -ip=1.1.1.1
# 设置IP地址为127.0.0.1(MacOS操作系统上)
sudo networksetup -setdnsservers Wi-Fi 127.0.0.1
# Ping example.com应该返回我们定义的IP地址1.1.1.1
ping example.com
示例2:使用Go代码启动DNS服务
可以使用如下代码启动本地DNS服务:
package main
import (
"context"
"github.com/miekg/dns"
"log"
)
func main() {
server := &dns.Server{
Addr: ":53",
Handler: dns.HandlerFunc(handler),
}
log.Println("Listening on :53")
err := server.ListenAndServe()
if err != nil {
log.Fatalf("Failed to start server: %s\n ", err.Error())
}
}
func handler(w dns.ResponseWriter, r *dns.Msg) {
m := new(dns.Msg)
m.SetReply(r)
if len(r.Question) == 0 {
w.WriteMsg(m)
return
}
q := r.Question[0]
m.Authoritative = true
m.Answer = make([]dns.RR, 1)
switch q.Qtype {
case dns.TypeA:
a := &dns.A{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 300},
A: []byte{10, 0, 2, 15}}
m.Answer[0] = a
case dns.TypeAAAA:
aaaa := &dns.AAAA{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 300},
AAAA: []byte{0, 0, 0, 0, 0, 0, 0, 1},
}
m.Answer[0] = aaaa
default:
m.Rcode = dns.RcodeNotImplemented
}
w.WriteMsg(m)
}
上述代码中使用server
实例来监听端口:53
,处理程序使用我们定义的handler
函数。handler函数实现了DNS服务器逻辑,我们可以根据传入的question类型进行对应处理操作:
- 如果问题列表为空,我们返回一个空的响应。
- 如果question的类型为A,则返回一个IPv4地址。
- 如果question的类型为AAAA,则返回一个IPv6地址。
- 如果question类型既不为A也不为AAAA,则返回一个
dns.RcodeNotImplemented
响应。
如果handler函数出错,返回的消息的rcode
会设置为dns.RcodeServerFailure
。如果没有任何错误发生,函数将调用ResponseWriter
的WriteMsg
方法以向客户端发送处理过的消息。
启动后,我们可以执行下述代码获取结果:
dig @localhost example.com
输出:
; <<>> DiG 9.16.1-Ubuntu <<>> @localhost example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54195
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;example.com. IN A
;; ANSWER SECTION:
example.com. 300 IN A 10.0.2.15
;; Query time: 0 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Fri Sep 25 00:50:16 CST 2020
;; MSG SIZE rcvd: 49
【文章出处:阜宁网站开发 http://www.1234xp.com/funing.html 欢迎留下您的宝贵建议】