dns: add some comment

This commit is contained in:
nadoo 2018-01-09 23:43:56 +08:00
parent 969034fb97
commit 2e10672c3b

256
dns.go
View File

@ -10,11 +10,8 @@ import (
"strings" "strings"
) )
// DNSUDPHeaderLen is the length of UDP dns msg header // DNSHeaderLen is the length of dns msg header
const DNSUDPHeaderLen = 12 const DNSHeaderLen = 12
// DNSTCPHeaderLen is the length of TCP dns msg header
const DNSTCPHeaderLen = 2 + DNSUDPHeaderLen
// DNSUDPMaxLen is the max size of udp dns request. // DNSUDPMaxLen is the max size of udp dns request.
// https://tools.ietf.org/html/rfc1035#section-4.2.1 // https://tools.ietf.org/html/rfc1035#section-4.2.1
@ -25,26 +22,114 @@ const DNSTCPHeaderLen = 2 + DNSUDPHeaderLen
// so we should also serve tcp requests. // so we should also serve tcp requests.
const DNSUDPMaxLen = 512 const DNSUDPMaxLen = 512
// DNSQueryTypeA ipv4 // DNSQTypeA ipv4
const DNSQueryTypeA = 1 const DNSQTypeA = 1
// DNSQueryTypeAAAA ipv6 // DNSQTypeAAAA ipv6
const DNSQueryTypeAAAA = 28 const DNSQTypeAAAA = 28
type dnsQuery struct { // DNSMsg format
DomainName string // https://tools.ietf.org/html/rfc1035#section-4.1
QueryType uint16 // All communications inside of the domain protocol are carried in a single
QueryClass uint16 // format called a message. The top level format of message is divided
Offset int // into 5 sections (some of which are empty in certain cases) shown below:
//
// +---------------------+
// | Header |
// +---------------------+
// | Question | the question for the name server
// +---------------------+
// | Answer | RRs answering the question
// +---------------------+
// | Authority | RRs pointing toward an authority
// +---------------------+
// | Additional | RRs holding additional information
type DNSMsg struct {
DNSHeader
Questions []*DNSQuestion
Answers []*DNSRR
} }
type dnsAnswer struct { // DNSHeader format
// DomainName string // https://tools.ietf.org/html/rfc1035#section-4.1.1
QueryType uint16 // The header contains the following fields:
QueryClass uint16 //
TTL uint32 // 1 1 1 1 1 1
DataLength uint16 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
Data []byte // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ID |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | QDCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ANCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | NSCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ARCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
//
type DNSHeader struct {
ID uint16
}
// DNSQuestion format
// https://tools.ietf.org/html/rfc1035#section-4.1.2
// The question section is used to carry the "question" in most queries,
// i.e., the parameters that define what is being asked. The section
// contains QDCOUNT (usually 1) entries, each of the following format:
//
// 1 1 1 1 1 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | |
// / QNAME /
// / /
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | QTYPE |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | QCLASS |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
type DNSQuestion struct {
QNAME string
QTYPE uint16
QCLASS uint16
Offset int
}
// DNSRR format
// https://tools.ietf.org/html/rfc1035#section-3.2.1
// All RRs have the same top level format shown below:
//
// 1 1 1 1 1 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | |
// / /
// / NAME /
// | |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | TYPE |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | CLASS |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | TTL |
// | |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | RDLENGTH |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
// / RDATA /
// / /
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
type DNSRR struct {
// NAME string
TYPE uint16
CLASS uint16
TTL uint32
RDLENGTH uint16
RDATA []byte
IP string IP string
} }
@ -57,12 +142,12 @@ type DNS struct {
*Forwarder // as proxy client *Forwarder // as proxy client
sDialer Dialer // dialer for server sDialer Dialer // dialer for server
tunnel bool Tunnel bool
dnsServer string DNSServer string
dnsServerMap map[string]string DNSServerMap map[string]string
answerHandlers []DNSAnswerHandler AnswerHandlers []DNSAnswerHandler
} }
// NewDNS returns a dns forwarder. client[dns.udp] -> glider[tcp] -> forwarder[dns.tcp] -> remote dns addr // NewDNS returns a dns forwarder. client[dns.udp] -> glider[tcp] -> forwarder[dns.tcp] -> remote dns addr
@ -71,10 +156,10 @@ func NewDNS(addr, raddr string, sDialer Dialer, tunnel bool) (*DNS, error) {
Forwarder: NewForwarder(addr, nil), Forwarder: NewForwarder(addr, nil),
sDialer: sDialer, sDialer: sDialer,
tunnel: tunnel, Tunnel: tunnel,
dnsServer: raddr, DNSServer: raddr,
dnsServerMap: make(map[string]string), DNSServerMap: make(map[string]string),
} }
return s, nil return s, nil
@ -90,31 +175,34 @@ func (s *DNS) ListenAndServe() {
func (s *DNS) ListenAndServeUDP() { func (s *DNS) ListenAndServeUDP() {
c, err := net.ListenPacket("udp", s.addr) c, err := net.ListenPacket("udp", s.addr)
if err != nil { if err != nil {
logf("proxy-dns failed to listen on %s: %v", s.addr, err) logf("proxy-dns failed to listen on %s, error: %v", s.addr, err)
return return
} }
defer c.Close() defer c.Close()
logf("proxy-dns listening on udp:%s", s.addr) logf("proxy-dns listening UDP on %s", s.addr)
for { for {
data := make([]byte, DNSUDPMaxLen) b := make([]byte, DNSUDPMaxLen)
n, clientAddr, err := c.ReadFrom(b)
n, clientAddr, err := c.ReadFrom(data)
if err != nil { if err != nil {
logf("proxy-dns DNS local read error: %v", err) logf("proxy-dns local read error: %v", err)
continue continue
} }
data = data[:n] reqLen := uint16(n)
if reqLen <= DNSHeaderLen {
logf("proxy-dns not enough data")
continue
}
b = b[:n]
go func() { go func() {
_, respMsg := s.handleReqMsg(reqLen, b)
_, respMsg := s.handleReqMsg(uint16(len(data)), data)
_, err = c.WriteTo(respMsg, clientAddr) _, err = c.WriteTo(respMsg, clientAddr)
if err != nil { if err != nil {
logf("proxy-dns error in local write: %s\n", err) logf("proxy-dns error in local write: %s", err)
return
} }
// logf("proxy-dns %s <-> %s, type: %d, %s: %s", clientAddr.String(), dnsServer, query.QueryType, domain, ip) // logf("proxy-dns %s <-> %s, type: %d, %s: %s", clientAddr.String(), dnsServer, query.QueryType, domain, ip)
@ -131,7 +219,7 @@ func (s *DNS) ListenAndServeTCP() {
return return
} }
logf("proxy-dns-tcp listening on tcp:%s", s.addr) logf("proxy-dns-tcp listening TCP on %s", s.addr)
for { for {
c, err := l.Accept() c, err := l.Accept()
@ -157,6 +245,11 @@ func (s *DNS) ServeTCP(c net.Conn) {
return return
} }
if reqLen <= DNSHeaderLen {
logf("proxy-dns not enough data")
return
}
reqMsg := make([]byte, reqLen) reqMsg := make([]byte, reqLen)
_, err := io.ReadFull(c, reqMsg) _, err := io.ReadFull(c, reqMsg)
if err != nil { if err != nil {
@ -165,7 +258,6 @@ func (s *DNS) ServeTCP(c net.Conn) {
} }
respLen, respMsg := s.handleReqMsg(reqLen, reqMsg) respLen, respMsg := s.handleReqMsg(reqLen, reqMsg)
if err := binary.Write(c, binary.BigEndian, respLen); err != nil { if err := binary.Write(c, binary.BigEndian, respLen); err != nil {
logf("proxy-dns-tcp error in local write respLen: %s\n", err) logf("proxy-dns-tcp error in local write respLen: %s\n", err)
} }
@ -178,19 +270,19 @@ func (s *DNS) ServeTCP(c net.Conn) {
// handle request msg and return response msg // handle request msg and return response msg
func (s *DNS) handleReqMsg(reqLen uint16, reqMsg []byte) (respLen uint16, respMsg []byte) { func (s *DNS) handleReqMsg(reqLen uint16, reqMsg []byte) (respLen uint16, respMsg []byte) {
// fmt.Printf("dns req len %d:\n%s\n\n", reqLen, hex.Dump(reqMsg[:]))
query, err := parseQuery(reqMsg) query, err := parseQuestion(reqMsg)
if err != nil { if err != nil {
logf("proxy-dns-tcp error in parseQuery reqMsg %s", err) logf("proxy-dns error in parseQuestion reqMsg %s", err)
return return
} }
dnsServer := s.GetServer(query.DomainName) dnsServer := s.GetServer(query.QNAME)
if s.tunnel { if s.Tunnel {
dnsServer = s.dnsServer dnsServer = s.DNSServer
} }
rc, err := s.sDialer.NextDialer(query.DomainName+":53").Dial("tcp", dnsServer) rc, err := s.sDialer.NextDialer(query.QNAME+":53").Dial("tcp", dnsServer)
if err != nil { if err != nil {
logf("proxy-dns failed to connect to server %v: %v", dnsServer, err) logf("proxy-dns failed to connect to server %v: %v", dnsServer, err)
return return
@ -198,21 +290,23 @@ func (s *DNS) handleReqMsg(reqLen uint16, reqMsg []byte) (respLen uint16, respMs
defer rc.Close() defer rc.Close()
if err := binary.Write(rc, binary.BigEndian, reqLen); err != nil { if err := binary.Write(rc, binary.BigEndian, reqLen); err != nil {
logf("proxy-dns failed to connect to server %v: %v", dnsServer, err) logf("proxy-dns failed to write req length: %v", err)
return
} }
if err := binary.Write(rc, binary.BigEndian, reqMsg); err != nil { if err := binary.Write(rc, binary.BigEndian, reqMsg); err != nil {
logf("proxy-dns failed to connect to server %v: %v", dnsServer, err) logf("proxy-dns failed to write req message: %v", err)
return
} }
if err := binary.Read(rc, binary.BigEndian, &respLen); err != nil { if err := binary.Read(rc, binary.BigEndian, &respLen); err != nil {
logf("proxy-dns-tcp failed to read response length: %v", err) logf("proxy-dns failed to read response length: %v", err)
return return
} }
respMsg = make([]byte, respLen) respMsg = make([]byte, respLen)
_, err = io.ReadFull(rc, respMsg) _, err = io.ReadFull(rc, respMsg)
if err != nil { if err != nil {
logf("proxy-dns-tcp error in read respMsg %s\n", err) logf("proxy-dns error in read respMsg %s\n", err)
return return
} }
@ -220,24 +314,23 @@ func (s *DNS) handleReqMsg(reqLen uint16, reqMsg []byte) (respLen uint16, respMs
var ip string var ip string
if respLen > 0 { if respLen > 0 {
query, err := parseQuery(respMsg) query, err := parseQuestion(respMsg)
if err != nil { if err != nil {
logf("proxy-dns error in parseQuery respMsg %s", err) logf("proxy-dns error in parseQuestion respMsg %s", err)
return return
} }
if (query.QueryType == DNSQueryTypeA || query.QueryType == DNSQueryTypeAAAA) && if (query.QTYPE == DNSQTypeA || query.QTYPE == DNSQTypeAAAA) &&
len(respMsg) > query.Offset { len(respMsg) > query.Offset {
answers := parseAnswers(respMsg[query.Offset:]) answers := parseAnswers(respMsg[query.Offset:])
for _, answer := range answers { for _, answer := range answers {
if answer.IP != "" { if answer.IP != "" {
ip += answer.IP + "," ip += answer.IP + ","
} }
for _, h := range s.answerHandlers { for _, h := range s.AnswerHandlers {
h(query.DomainName, answer.IP) h(query.QNAME, answer.IP)
} }
} }
} }
@ -249,36 +342,35 @@ func (s *DNS) handleReqMsg(reqLen uint16, reqMsg []byte) (respLen uint16, respMs
// SetServer . // SetServer .
func (s *DNS) SetServer(domain, server string) { func (s *DNS) SetServer(domain, server string) {
s.dnsServerMap[domain] = server s.DNSServerMap[domain] = server
} }
// GetServer . // GetServer .
func (s *DNS) GetServer(domain string) string { func (s *DNS) GetServer(domain string) string {
domainParts := strings.Split(domain, ".") domainParts := strings.Split(domain, ".")
length := len(domainParts) length := len(domainParts)
for i := length - 2; i >= 0; i-- { for i := length - 2; i >= 0; i-- {
domain := strings.Join(domainParts[i:length], ".") domain := strings.Join(domainParts[i:length], ".")
if server, ok := s.dnsServerMap[domain]; ok { if server, ok := s.DNSServerMap[domain]; ok {
return server return server
} }
} }
return s.dnsServer return s.DNSServer
} }
// AddAnswerHandler . // AddAnswerHandler .
func (s *DNS) AddAnswerHandler(h DNSAnswerHandler) { func (s *DNS) AddAnswerHandler(h DNSAnswerHandler) {
s.answerHandlers = append(s.answerHandlers, h) s.AnswerHandlers = append(s.AnswerHandlers, h)
} }
func parseQuery(p []byte) (*dnsQuery, error) { func parseQuestion(p []byte) (*DNSQuestion, error) {
q := &dnsQuery{} q := &DNSQuestion{}
var i int var i int
var domain []byte var domain []byte
for i = DNSUDPHeaderLen; i < len(p); { for i = DNSHeaderLen; i < len(p); {
l := int(p[i]) l := int(p[i])
if l == 0 { if l == 0 {
@ -292,21 +384,21 @@ func parseQuery(p []byte) (*dnsQuery, error) {
i = i + l + 1 i = i + l + 1
} }
q.DomainName = string(domain[:len(domain)-1]) q.QNAME = string(domain[:len(domain)-1])
if len(p) < i+4 { if len(p) < i+4 {
return nil, errors.New("parseQuery error, not enough data") return nil, errors.New("parseQuestion error, not enough data")
} }
q.QueryType = binary.BigEndian.Uint16(p[i:]) q.QTYPE = binary.BigEndian.Uint16(p[i:])
q.QueryClass = binary.BigEndian.Uint16(p[i+2:]) q.QCLASS = binary.BigEndian.Uint16(p[i+2:])
q.Offset = i + 4 q.Offset = i + 4
return q, nil return q, nil
} }
func parseAnswers(p []byte) []*dnsAnswer { func parseAnswers(p []byte) []*DNSRR {
var answers []*dnsAnswer var answers []*DNSRR
for i := 0; i < len(p); { for i := 0; i < len(p); {
@ -323,23 +415,23 @@ func parseAnswers(p []byte) []*dnsAnswer {
break break
} }
answer := &dnsAnswer{} answer := &DNSRR{}
answer.QueryType = binary.BigEndian.Uint16(p[i:]) answer.TYPE = binary.BigEndian.Uint16(p[i:])
answer.QueryClass = binary.BigEndian.Uint16(p[i+2:]) answer.CLASS = binary.BigEndian.Uint16(p[i+2:])
answer.TTL = binary.BigEndian.Uint32(p[i+4:]) answer.TTL = binary.BigEndian.Uint32(p[i+4:])
answer.DataLength = binary.BigEndian.Uint16(p[i+8:]) answer.RDLENGTH = binary.BigEndian.Uint16(p[i+8:])
answer.Data = p[i+10 : i+10+int(answer.DataLength)] answer.RDATA = p[i+10 : i+10+int(answer.RDLENGTH)]
if answer.QueryType == DNSQueryTypeA { if answer.TYPE == DNSQTypeA {
answer.IP = net.IP(answer.Data[:net.IPv4len]).String() answer.IP = net.IP(answer.RDATA[:net.IPv4len]).String()
} else if answer.QueryType == DNSQueryTypeAAAA { } else if answer.TYPE == DNSQTypeAAAA {
answer.IP = net.IP(answer.Data[:net.IPv6len]).String() answer.IP = net.IP(answer.RDATA[:net.IPv6len]).String()
} }
answers = append(answers, answer) answers = append(answers, answer)
i = i + 10 + int(answer.DataLength) i = i + 10 + int(answer.RDLENGTH)
} }
return answers return answers