add IPv6 support

This commit is contained in:
bigeagle 2015-02-03 20:32:18 +08:00
parent dd25168945
commit ce840f4f50
5 changed files with 108 additions and 21 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/bin

BIN
bin/godns

Binary file not shown.

View File

@ -1,10 +1,10 @@
package main package main
import ( import (
"github.com/miekg/dns"
"net"
"sync" "sync"
"time" "time"
"github.com/miekg/dns"
) )
type Question struct { type Question struct {
@ -13,6 +13,12 @@ type Question struct {
qclass string qclass string
} }
const (
notIPQuery = 0
_IP4Query = 4
_IP6Query = 6
)
func (q *Question) String() string { func (q *Question) String() string {
return q.qname + " " + q.qclass + " " + q.qtype return q.qname + " " + q.qclass + " " + q.qtype
} }
@ -76,14 +82,35 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
Debug("Question: %s", Q.String()) Debug("Question: %s", Q.String())
IPQuery := h.isIPQuery(q)
// Query hosts // Query hosts
if settings.Hosts.Enable && h.isIPQuery(q) { if settings.Hosts.Enable && IPQuery > 0 {
if ip, ok := h.hosts.Get(Q.qname); ok { if ip, ok := h.hosts.Get(Q.qname, IPQuery); ok {
m := new(dns.Msg) m := new(dns.Msg)
m.SetReply(req) m.SetReply(req)
rr_header := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: settings.Hosts.TTL}
a := &dns.A{rr_header, net.ParseIP(ip)} switch IPQuery {
case _IP4Query:
rr_header := dns.RR_Header{
Name: q.Name,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: settings.Hosts.TTL,
}
a := &dns.A{rr_header, ip}
m.Answer = append(m.Answer, a) m.Answer = append(m.Answer, a)
case _IP6Query:
rr_header := dns.RR_Header{
Name: q.Name,
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: settings.Hosts.TTL,
}
aaaa := &dns.AAAA{rr_header, ip}
m.Answer = append(m.Answer, aaaa)
}
w.WriteMsg(m) w.WriteMsg(m)
Debug("%s found in hosts", Q.qname) Debug("%s found in hosts", Q.qname)
return return
@ -93,7 +120,7 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
// Only query cache when qtype == 'A' , qclass == 'IN' // Only query cache when qtype == 'A' , qclass == 'IN'
key := KeyGen(Q) key := KeyGen(Q)
if h.isIPQuery(q) { if IPQuery > 0 {
mesg, err := h.cache.Get(key) mesg, err := h.cache.Get(key)
if err != nil { if err != nil {
Debug("%s didn't hit cache: %s", Q.String(), err) Debug("%s didn't hit cache: %s", Q.String(), err)
@ -118,7 +145,7 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
w.WriteMsg(mesg) w.WriteMsg(mesg)
if h.isIPQuery(q) { if IPQuery > 0 {
err = h.cache.Set(key, mesg) err = h.cache.Set(key, mesg)
if err != nil { if err != nil {
@ -138,8 +165,19 @@ func (h *GODNSHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) {
h.do("udp", w, req) h.do("udp", w, req)
} }
func (h *GODNSHandler) isIPQuery(q dns.Question) bool { func (h *GODNSHandler) isIPQuery(q dns.Question) int {
return q.Qtype == dns.TypeA && q.Qclass == dns.ClassINET if q.Qclass != dns.ClassINET {
return notIPQuery
}
switch q.Qtype {
case dns.TypeA:
return _IP4Query
case dns.TypeAAAA:
return _IP6Query
default:
return notIPQuery
}
} }
func UnFqdn(s string) string { func UnFqdn(s string) string {

View File

@ -2,10 +2,12 @@ package main
import ( import (
"bufio" "bufio"
"github.com/hoisie/redis" "net"
"os" "os"
"regexp" "regexp"
"strings" "strings"
"github.com/hoisie/redis"
) )
type Hosts struct { type Hosts struct {
@ -29,14 +31,24 @@ func NewHosts(hs HostsSettings, rs RedisSettings) Hosts {
3. Match local /etc/hosts file first, remote redis records second 3. Match local /etc/hosts file first, remote redis records second
*/ */
func (h *Hosts) Get(domain string) (ip string, ok bool) { func (h *Hosts) Get(domain string, family int) (ip net.IP, ok bool) {
if ip, ok = h.FileHosts[domain]; ok { var sip string
return
if sip, ok = h.FileHosts[domain]; !ok {
if sip, ok = h.RedisHosts.Get(domain); !ok {
return nil, false
} }
if ip, ok = h.RedisHosts.Get(domain); ok {
return
} }
return "", false
switch family {
case _IP4Query:
ip = net.ParseIP(sip).To4()
return ip, (ip != nil)
case _IP6Query:
ip = net.ParseIP(sip).To16()
return ip, (ip != nil)
}
return nil, false
} }
func (h *Hosts) GetAll() map[string]string { func (h *Hosts) GetAll() map[string]string {
@ -114,11 +126,13 @@ func (f *FileHosts) GetAll() map[string]string {
} }
func (f *FileHosts) isDomain(domain string) bool { func (f *FileHosts) isDomain(domain string) bool {
match, _ := regexp.MatchString("^[a-z]", domain) if f.isIP(domain) {
return false
}
match, _ := regexp.MatchString("^[a-zA-Z0-9][a-zA-Z0-9-]", domain)
return match return match
} }
func (f *FileHosts) isIP(ip string) bool { func (f *FileHosts) isIP(ip string) bool {
match, _ := regexp.MatchString("^[1-9]", ip) return (net.ParseIP(ip) != nil)
return match
} }

34
hosts_test.go Normal file
View File

@ -0,0 +1,34 @@
package main
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestHostDomainAndIP(t *testing.T) {
Convey("Test Host File Domain and IP regex", t, func() {
f := &FileHosts{}
Convey("1.1.1.1 should be IP and not domain", func() {
So(f.isIP("1.1.1.1"), ShouldEqual, true)
So(f.isDomain("1.1.1.1"), ShouldEqual, false)
})
Convey("2001:470:20::2 should be IP and not domain", func() {
So(f.isIP("2001:470:20::2"), ShouldEqual, true)
So(f.isDomain("2001:470:20::2"), ShouldEqual, false)
})
Convey("`host` should be domain and not IP", func() {
So(f.isDomain("host"), ShouldEqual, true)
So(f.isIP("host"), ShouldEqual, false)
})
Convey("`123.test` should be domain and not IP", func() {
So(f.isDomain("123.test"), ShouldEqual, true)
So(f.isIP("123.test"), ShouldEqual, false)
})
})
}