自建内网的DNS有几个使用场景:
1、内网用户不用强记IP,使用自己喜欢的短域名,好记好输入。
2、从DNS开始就阻止用户访问一些网站(先封掉所有向外网的53端口,只允许内网服务器通过)
3、访问统计、用户上网管理(上班期间开视频网站?一目了然)
这里简单做了一个DNS服务器,将DNS指向它,或者在路由器上设置。
//
// DNS服务器
//
// A记录: 由DNS域名到IP地址的查询,即正向查询。
// AAAA记录:该记录是将域名解析到一个指定的IPV6的IP上。
// CNAME记录:通常称别名解析。可以将注册的不同域名都转到一个域名记录上,由这个域名记录统一解析管理,与A记录不同的是,CNAME别名记录设置的可以是一个域名的描述而不一定是IP地址。
// NS记录 :NS(Name Server)记录是域名服务器记录,用来指定该域名由哪个DNS服务器来进行解析。
// MX记录 :MX(Mail Exchanger)记录是邮件交换记录,它指向一个邮件服务器
// TXT记录:一般指某个主机名或域名的说明,如:admin IN TXT "管理员, 电话:XXXXXXXXXXX"。也就是您可以设置 TXT 内容以便使别人联系到您。
// PTR记录:由IP到域名的查询,即逆向查询
//
package main
import (
"fmt"
"net"
"errors"
"golang.org/x/net/dns/dnsmessage"
//"github.com/miekg/dns"
)
func main() {
conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 53})
if err != nil {
panic(err)
}
defer conn.Close()
fmt.Println("Listing ...")
for {
buf := make([]byte, 512)
_, addr, _ := conn.ReadFromUDP(buf)
var msg dnsmessage.Message
if err := msg.Unpack(buf); err != nil {
fmt.Println(err)
continue
}
go ServerDNS(addr, conn, msg)
}
}
// 地址簿
var (
addressBookOfA = map[string][4]byte{
"0.pi.my.": [4]byte{192, 168, 8, 14},
"w.pi.my.": [4]byte{192, 168, 8, 88},
"4.pi.my.": [4]byte{192, 168, 8, 18},
"3.pi.my.": [4]byte{192, 168, 8, 10},
}
addressBookOfPTR = map[string]string{
"150.38.181.220.in-addr.arpa.": "www.baidu.com.",
}
)
// DNS服务
func ServerDNS(addr *net.UDPAddr, conn *net.UDPConn, msg dnsmessage.Message) {
// query info
if len(msg.Questions) < 1 {
return
}
question := msg.Questions[0]
var (
queryTypeStr = question.Type.String()
queryNameStr = question.Name.String()
queryType = question.Type
queryName, _ = dnsmessage.NewName(queryNameStr)
)
fmt.Printf("------------------ 类型: %s 查询: [%s] \n", queryTypeStr, queryNameStr)
// 查找记录
var resource dnsmessage.Resource
switch queryType {
case dnsmessage.TypeA:
if rst, ok := addressBookOfA[queryNameStr]; ok {
resource = NewAResource(queryName, rst)
} else {
rs, err := LookupIP(queryNameStr)
if err==nil {
resource = NewAResource(queryName, rs)
} else {
fmt.Printf("没有找到A记录: [%s] \n", queryNameStr)
Response(addr, conn, msg)
return
}
}
case dnsmessage.TypePTR:
if rst, ok := addressBookOfPTR[queryName.String()]; ok {
resource = NewPTRResource(queryName, rst)
} else {
rs,err := LookupRPT("125.65.40.219")
if err== nil {
resource = NewPTRResource(queryName, rs)
} else {
fmt.Printf("没找到PTR记录: [%s] \n", queryNameStr)
Response(addr, conn, msg)
return
}
}
default:
rs, err := LookupIP(queryNameStr)
if err==nil {
resource = NewAResource(queryName, rs)
} else {
fmt.Printf("不支持的查询类型: [%s] \n", queryTypeStr)
Response(addr, conn, msg)
return
}
}
// 发送回复消息
msg.Response = true
msg.Answers = append(msg.Answers, resource)
Response(addr, conn, msg)
}
// 消息回复
func Response(addr *net.UDPAddr, conn *net.UDPConn, msg dnsmessage.Message) {
packed, err := msg.Pack()
if err != nil {
fmt.Println(err)
return
}
if _, err := conn.WriteToUDP(packed, addr); err != nil {
fmt.Println(err)
}
}
// NewAResource A record
func NewAResource(query dnsmessage.Name, a [4]byte) dnsmessage.Resource {
return dnsmessage.Resource{
Header: dnsmessage.ResourceHeader{
Name: query,
Class: dnsmessage.ClassINET,
TTL: 600,
},
Body: &dnsmessage.AResource{
A: a,
},
}
}
// NewPTRResource PTR record
func NewPTRResource(query dnsmessage.Name, ptr string) dnsmessage.Resource {
name, _ := dnsmessage.NewName(ptr)
return dnsmessage.Resource{
Header: dnsmessage.ResourceHeader{
Name: query,
Class: dnsmessage.ClassINET,
},
Body: &dnsmessage.PTRResource{
PTR: name,
},
}
}
// 查询上级DNS
func LookupIP(domain string) ([4]byte,error) {
ipRecords, _ := net.LookupIP(domain)
for _, ip := range ipRecords {
if len(ip)!=16 {
fmt.Println(len(ip)," >>>> ",ip)
}
return [4]byte{ip[12],ip[13],ip[14],ip[15]},nil
}
return [4]byte{0,0,0,0}, errors.New("error")
}
//查找DNS PTR记录
func LookupRPT(ip string) (string,error) {
ptr,e :=net.LookupAddr(ip)
if e != nil {
return "",e
}
for _, ptrval:=range ptr{
return ptrval,nil
}
return "",errors.New("error")
}