(原) 内网DNS

原创文章,请后转载,并注明出处。

自建内网的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")	 
 }

相关文章