finally. fix hosts query
This commit is contained in:
parent
d22885557f
commit
9ddf8a8939
|
@ -59,7 +59,10 @@ If multi `namerserver` set at resolv.conf, the upsteam server will try in order
|
||||||
|
|
||||||
#### hosts
|
#### hosts
|
||||||
|
|
||||||
|
Force resolv domain to assigned ip, support two types hosts configuration: locale file and remote redis
|
||||||
|
|
||||||
|
#hosts file#
|
||||||
|
can be
|
||||||
|
|
||||||
|
|
||||||
#### cache
|
#### cache
|
||||||
|
|
|
@ -34,6 +34,9 @@ expire = 600 # 10 minutes
|
||||||
maxcount = 100000
|
maxcount = 100000
|
||||||
|
|
||||||
[hosts]
|
[hosts]
|
||||||
|
#If set false, will not query hosts file and redis hosts record
|
||||||
|
enable = true
|
||||||
host-file = "/etc/hosts"
|
host-file = "/etc/hosts"
|
||||||
redis-key = "godns:hosts"
|
redis-key = "godns:hosts"
|
||||||
|
ttl = 600
|
||||||
|
|
||||||
|
|
37
handler.go
37
handler.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
"net"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ func (q *Question) String() string {
|
||||||
type GODNSHandler struct {
|
type GODNSHandler struct {
|
||||||
resolver *Resolver
|
resolver *Resolver
|
||||||
cache Cache
|
cache Cache
|
||||||
|
hosts Hosts
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler() *GODNSHandler {
|
func NewHandler() *GODNSHandler {
|
||||||
|
@ -59,18 +61,36 @@ func NewHandler() *GODNSHandler {
|
||||||
logger.Printf("Invalid cache backend %s", cacheConfig.Backend)
|
logger.Printf("Invalid cache backend %s", cacheConfig.Backend)
|
||||||
panic("Invalid cache backend")
|
panic("Invalid cache backend")
|
||||||
}
|
}
|
||||||
return &GODNSHandler{resolver, cache}
|
|
||||||
|
hosts := NewHosts(settings.Hosts, settings.Redis)
|
||||||
|
|
||||||
|
return &GODNSHandler{resolver, cache, hosts}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *GODNSHandler) do(net string, w dns.ResponseWriter, req *dns.Msg) {
|
func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
|
||||||
|
|
||||||
q := req.Question[0]
|
q := req.Question[0]
|
||||||
Q := Question{q.Name, dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass]}
|
Q := Question{UnFqdn(q.Name), dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass]}
|
||||||
|
|
||||||
Debug("Question: %s", Q.String())
|
Debug("Question: %s", Q.String())
|
||||||
|
|
||||||
key := KeyGen(Q)
|
// Query hosts
|
||||||
|
if settings.Hosts.Enable && h.isIPQuery(q) {
|
||||||
|
if ip, ok := h.hosts.Get(Q.qname); ok {
|
||||||
|
m := new(dns.Msg)
|
||||||
|
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)}
|
||||||
|
m.Answer = append(m.Answer, a)
|
||||||
|
w.WriteMsg(m)
|
||||||
|
Debug("%s found in hosts", Q.qname)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Only query cache when qtype == 'A' , qclass == 'IN'
|
// Only query cache when qtype == 'A' , qclass == 'IN'
|
||||||
|
key := KeyGen(Q)
|
||||||
if h.isIPQuery(q) {
|
if h.isIPQuery(q) {
|
||||||
mesg, err := h.cache.Get(key)
|
mesg, err := h.cache.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -84,7 +104,7 @@ func (h *GODNSHandler) do(net string, w dns.ResponseWriter, req *dns.Msg) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mesg, err := h.resolver.Lookup(net, req)
|
mesg, err := h.resolver.Lookup(Net, req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Debug("%s", err)
|
Debug("%s", err)
|
||||||
|
@ -117,3 +137,10 @@ func (h *GODNSHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) {
|
||||||
func (h *GODNSHandler) isIPQuery(q dns.Question) bool {
|
func (h *GODNSHandler) isIPQuery(q dns.Question) bool {
|
||||||
return q.Qtype == dns.TypeA && q.Qclass == dns.ClassINET
|
return q.Qtype == dns.TypeA && q.Qclass == dns.ClassINET
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UnFqdn(s string) string {
|
||||||
|
if dns.IsFqdn(s) {
|
||||||
|
return s[:len(s)-1]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
91
hosts.go
91
hosts.go
|
@ -1,24 +1,89 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"github.com/hoisie/redis"
|
"github.com/hoisie/redis"
|
||||||
// "github.com/miekg/dns"
|
"os"
|
||||||
"fmt"
|
"regexp"
|
||||||
"io/ioutil"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HostsQueryFaild struct {
|
type Hosts struct {
|
||||||
domain string
|
FileHosts map[string]string
|
||||||
|
RedisHosts *RedisHosts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e HostsQueryFaild) Error() string {
|
func NewHosts(hs HostsSettings, rs RedisSettings) Hosts {
|
||||||
return e.domain + " hosts match failed"
|
fileHosts := &FileHosts{hs.HostsFile}
|
||||||
|
redis := &redis.Client{Addr: rs.Addr(), Db: rs.DB, Password: rs.Password}
|
||||||
|
redisHosts := &RedisHosts{redis, hs.RedisKey}
|
||||||
|
|
||||||
|
hosts := Hosts{fileHosts.GetAll(), redisHosts}
|
||||||
|
return hosts
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func readLocalHostsFile(file string) map[string]string {
|
/*
|
||||||
|
1. Resolve hosts file only one times
|
||||||
|
2. Request redis on every get called, may lead to performance lose serious
|
||||||
|
3. Match local /etc/hosts file first, remote redis records second
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (h *Hosts) Get(domain string) (ip string, ok bool) {
|
||||||
|
if ip, ok = h.FileHosts[domain]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ip, ok = h.RedisHosts.Get(domain); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hosts) GetAll() map[string]string {
|
||||||
|
|
||||||
|
m := make(map[string]string)
|
||||||
|
for domain, ip := range h.RedisHosts.GetAll() {
|
||||||
|
m[domain] = ip
|
||||||
|
}
|
||||||
|
for domain, ip := range h.FileHosts {
|
||||||
|
m[domain] = ip
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
type RedisHosts struct {
|
||||||
|
redis *redis.Client
|
||||||
|
key string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RedisHosts) GetAll() map[string]string {
|
||||||
var hosts = make(map[string]string)
|
var hosts = make(map[string]string)
|
||||||
f, _ := os.Open(file)
|
r.redis.Hgetall(r.key, hosts)
|
||||||
scanner := bufio.NewScanner(f)
|
return hosts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RedisHosts) Get(domain string) (ip string, ok bool) {
|
||||||
|
b, err := r.redis.Hget(r.key, domain)
|
||||||
|
return string(b), err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RedisHosts) Set(domain, ip string) (bool, error) {
|
||||||
|
return r.redis.Hset(r.key, domain, []byte(ip))
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileHosts struct {
|
||||||
|
file string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileHosts) GetAll() map[string]string {
|
||||||
|
var hosts = make(map[string]string)
|
||||||
|
|
||||||
|
buf, err := os.Open(f.file)
|
||||||
|
if err != nil {
|
||||||
|
panic("Can't open " + f.file)
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(buf)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
|
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
|
@ -39,7 +104,7 @@ func readLocalHostsFile(file string) map[string]string {
|
||||||
|
|
||||||
domain := sli[len(sli)-1]
|
domain := sli[len(sli)-1]
|
||||||
ip := sli[0]
|
ip := sli[0]
|
||||||
if !isDomain(domain) || !isIP(ip) {
|
if !f.isDomain(domain) || !f.isIP(ip) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,12 +113,12 @@ func readLocalHostsFile(file string) map[string]string {
|
||||||
return hosts
|
return hosts
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDomain(domain string) bool {
|
func (f *FileHosts) isDomain(domain string) bool {
|
||||||
match, _ := regexp.MatchString("^[a-z]", domain)
|
match, _ := regexp.MatchString("^[a-z]", domain)
|
||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
|
|
||||||
func isIP(ip string) bool {
|
func (f *FileHosts) isIP(ip string) bool {
|
||||||
match, _ := regexp.MatchString("^[1-9]", ip)
|
match, _ := regexp.MatchString("^[1-9]", ip)
|
||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ func (r *Resolver) Lookup(net string, req *dns.Msg) (message *dns.Msg, err error
|
||||||
Debug("%s failed to get an valid answer on %s", qname, nameserver)
|
Debug("%s failed to get an valid answer on %s", qname, nameserver)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
Debug("%s resolv on %s ttl: %d", qname, nameserver, rtt)
|
Debug("%s resolv on %s ttl: %d", UnFqdn(qname), nameserver, rtt)
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
return nil, ResolvError{qname, r.Nameservers()}
|
return nil, ResolvError{qname, r.Nameservers()}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -39,6 +40,10 @@ type RedisSettings struct {
|
||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s RedisSettings) Addr() string {
|
||||||
|
return s.Host + ":" + strconv.Itoa(s.Port)
|
||||||
|
}
|
||||||
|
|
||||||
type LogSettings struct {
|
type LogSettings struct {
|
||||||
File string
|
File string
|
||||||
}
|
}
|
||||||
|
@ -50,8 +55,10 @@ type CacheSettings struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type HostsSettings struct {
|
type HostsSettings struct {
|
||||||
|
Enable bool
|
||||||
HostsFile string `toml:"host-file"`
|
HostsFile string `toml:"host-file"`
|
||||||
RedisKey string `toml:"redis-key"`
|
RedisKey string `toml:"redis-key"`
|
||||||
|
TTL uint32 `toml:"ttl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
Loading…
Reference in New Issue