Golang DNS 转发器:将 www.baidu.com 转发到 119.29.29.29,www.google.com 转发到 118.1.1.1
下面是一个简单的示例代码,展示如何使用 Golang 编写一个简单的 DNS 转发器,将 www.baidu.com 域名的请求转发到 119.29.29.29,将 www.google.com 域名的请求转发到 118.1.1.1。
package main
import (
"net"
"strings"
)
func main() {
// 监听本地 DNS 请求
server := &dnsServer{addr: ":53"}
go server.listenAndServe()
// 防止主线程退出
select {}
}
type dnsServer struct {
addr string
}
func (s *dnsServer) listenAndServe() error {
udpConn, err := net.ListenPacket("udp", s.addr)
if err != nil {
return err
}
defer udpConn.Close()
buf := make([]byte, 512)
for {
n, addr, err := udpConn.ReadFrom(buf)
if err != nil {
continue
}
// 解析 DNS 请求
req := &dnsRequest{}
if err := req.parse(buf[:n]); err != nil {
continue
}
// 处理 DNS 请求
resp := s.handleRequest(req)
// 发送 DNS 响应
if resp != nil {
udpConn.WriteTo(resp.encode(), addr)
}
}
}
func (s *dnsServer) handleRequest(req *dnsRequest) *dnsResponse {
// 判断域名是否为 www.baidu.com 或 www.google.com
if strings.ToLower(req.question.name) == "www.baidu.com" {
return &dnsResponse{
id: req.id,
flags: 0x8180,
answers: []dnsRR{{name: req.question.name, qtype: req.question.qtype, qclass: req.question.qclass, ttl: 600, addr: net.ParseIP("119.29.29.29")}},
}
} else if strings.ToLower(req.question.name) == "www.google.com" {
return &dnsResponse{
id: req.id,
flags: 0x8180,
answers: []dnsRR{{name: req.question.name, qtype: req.question.qtype, qclass: req.question.qclass, ttl: 600, addr: net.ParseIP("118.1.1.1")}},
}
}
// 不处理其他域名的请求
return nil
}
type dnsRequest struct {
id uint16
flags uint16
questions []dnsQuestion
answerRRs []dnsRR
}
func (req *dnsRequest) parse(data []byte) error {
// 解析 DNS 头部
req.id = uint16(data[0])<<8 | uint16(data[1])
req.flags = uint16(data[2])<<8 | uint16(data[3])
numQuestions := uint16(data[4])<<8 | uint16(data[5])
numAnswerRRs := uint16(data[6])<<8 | uint16(data[7])
// 解析 DNS 问题
offset := 12
for i := uint16(0); i < numQuestions; i++ {
question := dnsQuestion{}
offset, _ = question.parse(data, offset)
req.questions = append(req.questions, question)
}
// 解析 DNS 回答
for i := uint16(0); i < numAnswerRRs; i++ {
answer := dnsRR{}
offset, _ = answer.parse(data, offset)
req.answerRRs = append(req.answerRRs, answer)
}
return nil
}
type dnsResponse struct {
id uint16
flags uint16
answers []dnsRR
}
func (resp *dnsResponse) encode() []byte {
// 计算 DNS 响应长度
length := 12
for _, answer := range resp.answers {
length += answer.length()
}
// 构造 DNS 响应
data := make([]byte, length)
data[0] = byte(resp.id >> 8)
data[1] = byte(resp.id)
data[2] = byte(resp.flags >> 8)
data[3] = byte(resp.flags)
data[4] = 0
data[5] = 1
data[6] = byte(len(resp.answers) >> 8)
data[7] = byte(len(resp.answers))
// 编码 DNS 回答
offset := 12
for _, answer := range resp.answers {
offset += answer.encode(data, offset)
}
return data
}
type dnsQuestion struct {
name string
qtype uint16
qclass uint16
}
func (q *dnsQuestion) parse(data []byte, offset int) (int, error) {
// 解析 DNS 问题名称
name, n, err := decodeDNSName(data, offset)
if err != nil {
return 0, err
}
q.name = name
// 解析 DNS 问题类型和类别
q.qtype = uint16(data[n])<<8 | uint16(data[n+1])
q.qclass = uint16(data[n+2])<<8 | uint16(data[n+3])
return n + 4, nil
}
type dnsRR struct {
name string
qtype uint16
qclass uint16
ttl uint32
addr net.IP
}
func (rr *dnsRR) parse(data []byte, offset int) (int, error) {
// 解析 DNS 回答名称
name, n, err := decodeDNSName(data, offset)
if err != nil {
return 0, err
}
rr.name = name
// 解析 DNS 回答类型、类别和 TTL
rr.qtype = uint16(data[n])<<8 | uint16(data[n+1])
rr.qclass = uint16(data[n+2])<<8 | uint16(data[n+3])
rr.ttl = uint32(data[n+4])<<24 | uint32(data[n+5])<<16 | uint32(data[n+6])<<8 | uint32(data[n+7])
// 解析 DNS 回答地址
if rr.qtype == 1 { // A 类型地址
rr.addr = net.IPv4(data[n+12], data[n+13], data[n+14], data[n+15])
} else if rr.qtype == 28 { // AAAA 类型地址
rr.addr = net.IP(data[n+12 : n+28])
}
return n + 16, nil
}
func (rr *dnsRR) length() int {
if rr.qtype == 1 { // A 类型地址
return len(rr.name) + 16
} else if rr.qtype == 28 { // AAAA 类型地址
return len(rr.name) + 28
}
return 0
}
func (rr *dnsRR) encode(data []byte, offset int) int {
// 编码 DNS 回答名称
offset += encodeDNSName(rr.name, data, offset)
// 编码 DNS 回答类型、类别和 TTL
data[offset] = byte(rr.qtype >> 8)
data[offset+1] = byte(rr.qtype)
data[offset+2] = byte(rr.qclass >> 8)
data[offset+3] = byte(rr.qclass)
data[offset+4] = byte(rr.ttl >> 24)
data[offset+5] = byte(rr.ttl >> 16)
data[offset+6] = byte(rr.ttl >> 8)
data[offset+7] = byte(rr.ttl)
// 编码 DNS 回答地址
if rr.qtype == 1 { // A 类型地址
copy(data[offset+12:], rr.addr.To4())
return 16
} else if rr.qtype == 28 { // AAAA 类型地址
copy(data[offset+12:], rr.addr.To16())
return 28
}
return 0
}
func decodeDNSName(data []byte, offset int) (string, int, error) {
name := ""
for {
// 解析 DNS 名称段长度
length := int(data[offset])
if length == 0 {
break
} else if length >= 192 { // 跳转指针
nextOffset := int(data[offset+1]) | int(length&0x3f)<<8
if nextOffset >= offset {
return "", 0, net.DNSError{Name: "bad dns message", Err: "bad label pointer"}
}
name2, _, err := decodeDNSName(data, nextOffset)
if err != nil {
return "", 0, err
}
name += name2
offset += 2
break
}
// 解析 DNS 名称段
offset++
name += string(data[offset : offset+length]) + "."
offset += length
}
return name, offset, nil
}
func encodeDNSName(name string, data []byte, offset int) int {
labels := strings.Split(name, ".")
for _, label := range labels {
length := len(label)
data[offset] = byte(length)
offset++
copy(data[offset:], []byte(label))
offset += length
}
data[offset] = 0
return len(name) + 2
}
这段代码包含以下几个关键点:
- DNS 请求和响应结构体:
dnsRequest和dnsResponse结构体分别代表 DNS 请求和响应。它们包含id、flags、questions和answers等字段,用于解析和编码 DNS 数据。 - 解析和编码 DNS 数据:
parse和encode方法分别用于解析和编码 DNS 请求/响应的二进制数据。 - 处理 DNS 请求:
handleRequest方法用于处理 DNS 请求,根据请求的域名确定需要转发到哪个 IP 地址。 - 监听 DNS 请求:
listenAndServe方法用于监听本地 DNS 请求,并在接收到请求时调用handleRequest方法,构造并发送 DNS 响应。 - 解码和编码 DNS 名称:
decodeDNSName和encodeDNSName方法用于解析/编码 DNS 名称。 - DNS 服务器结构体:
dnsServer结构体表示一个 DNS 服务器,它将在id和addr字段中存储 DNS 服务器的标识和地址。 - 启动 DNS 服务器:
main函数创建一个dnsServer对象并调用listenAndServe方法来启动 DNS 服务器的监听。
这段代码只是演示了一个简单的 DNS 转发器,实际应用中可能需要更复杂的逻辑,比如缓存 DNS 响应,支持更多 DNS 类型,以及进行安全验证等。
原文地址: https://www.cveoy.top/t/topic/odsM 著作权归作者所有。请勿转载和采集!