2013-07-23 11:10:38 +00:00
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
2015-10-13 17:00:28 +00:00
|
|
|
|
"net"
|
2013-07-24 16:09:07 +00:00
|
|
|
|
"time"
|
2015-02-03 12:32:18 +00:00
|
|
|
|
|
|
|
|
|
"github.com/miekg/dns"
|
2013-07-23 11:10:38 +00:00
|
|
|
|
)
|
|
|
|
|
|
2015-02-03 12:32:18 +00:00
|
|
|
|
const (
|
|
|
|
|
notIPQuery = 0
|
|
|
|
|
_IP4Query = 4
|
|
|
|
|
_IP6Query = 6
|
|
|
|
|
)
|
|
|
|
|
|
2015-02-11 09:11:32 +00:00
|
|
|
|
type Question struct {
|
|
|
|
|
qname string
|
|
|
|
|
qtype string
|
|
|
|
|
qclass string
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-24 10:29:38 +00:00
|
|
|
|
func (q *Question) String() string {
|
|
|
|
|
return q.qname + " " + q.qclass + " " + q.qtype
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-23 11:10:38 +00:00
|
|
|
|
type GODNSHandler struct {
|
2015-02-12 20:30:16 +00:00
|
|
|
|
resolver *Resolver
|
|
|
|
|
cache, negCache Cache
|
|
|
|
|
hosts Hosts
|
2013-07-23 11:10:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewHandler() *GODNSHandler {
|
|
|
|
|
|
|
|
|
|
var (
|
2015-02-12 20:30:16 +00:00
|
|
|
|
clientConfig *dns.ClientConfig
|
|
|
|
|
cacheConfig CacheSettings
|
|
|
|
|
resolver *Resolver
|
|
|
|
|
cache, negCache Cache
|
2013-07-23 11:10:38 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
resolvConfig := settings.ResolvConfig
|
|
|
|
|
clientConfig, err := dns.ClientConfigFromFile(resolvConfig.ResolvFile)
|
|
|
|
|
if err != nil {
|
2015-10-13 11:33:51 +00:00
|
|
|
|
logger.Warn(":%s is not a valid resolv.conf file\n", resolvConfig.ResolvFile)
|
|
|
|
|
logger.Error(err.Error())
|
2013-07-23 11:10:38 +00:00
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
clientConfig.Timeout = resolvConfig.Timeout
|
2013-07-24 10:29:38 +00:00
|
|
|
|
resolver = &Resolver{clientConfig}
|
2013-07-23 11:10:38 +00:00
|
|
|
|
|
|
|
|
|
cacheConfig = settings.Cache
|
2013-07-24 10:29:38 +00:00
|
|
|
|
switch cacheConfig.Backend {
|
|
|
|
|
case "memory":
|
|
|
|
|
cache = &MemoryCache{
|
2015-02-12 20:30:16 +00:00
|
|
|
|
Backend: make(map[string]Mesg, cacheConfig.Maxcount),
|
2013-07-24 16:09:07 +00:00
|
|
|
|
Expire: time.Duration(cacheConfig.Expire) * time.Second,
|
|
|
|
|
Maxcount: cacheConfig.Maxcount,
|
2015-02-12 20:30:16 +00:00
|
|
|
|
}
|
|
|
|
|
negCache = &MemoryCache{
|
|
|
|
|
Backend: make(map[string]Mesg),
|
|
|
|
|
Expire: time.Duration(cacheConfig.Expire) * time.Second / 2,
|
|
|
|
|
Maxcount: cacheConfig.Maxcount,
|
2013-07-24 10:29:38 +00:00
|
|
|
|
}
|
2016-02-12 16:08:48 +00:00
|
|
|
|
case "memcache":
|
|
|
|
|
cache = NewMemcachedCache(
|
|
|
|
|
settings.Memcache.Servers,
|
|
|
|
|
int32(cacheConfig.Expire))
|
|
|
|
|
negCache = NewMemcachedCache(
|
|
|
|
|
settings.Memcache.Servers,
|
|
|
|
|
int32(cacheConfig.Expire/2))
|
2013-07-24 10:29:38 +00:00
|
|
|
|
case "redis":
|
2013-07-24 14:40:18 +00:00
|
|
|
|
// cache = &MemoryCache{
|
2013-07-24 16:09:07 +00:00
|
|
|
|
// Backend: make(map[string]*dns.Msg),
|
|
|
|
|
// Expire: time.Duration(cacheConfig.Expire) * time.Second,
|
|
|
|
|
// Serializer: new(JsonSerializer),
|
|
|
|
|
// Maxcount: cacheConfig.Maxcount,
|
2013-07-24 14:40:18 +00:00
|
|
|
|
// }
|
|
|
|
|
panic("Redis cache backend not implement yet")
|
2013-07-24 10:29:38 +00:00
|
|
|
|
default:
|
2015-10-13 11:33:51 +00:00
|
|
|
|
logger.Error("Invalid cache backend %s", cacheConfig.Backend)
|
2013-07-24 10:29:38 +00:00
|
|
|
|
panic("Invalid cache backend")
|
|
|
|
|
}
|
2013-07-26 10:54:19 +00:00
|
|
|
|
|
2015-10-13 15:29:28 +00:00
|
|
|
|
var hosts Hosts
|
|
|
|
|
if settings.Hosts.Enable {
|
|
|
|
|
hosts = NewHosts(settings.Hosts, settings.Redis)
|
|
|
|
|
}
|
2013-07-26 10:54:19 +00:00
|
|
|
|
|
2015-02-12 20:30:16 +00:00
|
|
|
|
return &GODNSHandler{resolver, cache, negCache, hosts}
|
2013-07-23 11:10:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-07-26 10:54:19 +00:00
|
|
|
|
func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
|
2013-07-23 16:37:38 +00:00
|
|
|
|
q := req.Question[0]
|
2013-07-26 10:54:19 +00:00
|
|
|
|
Q := Question{UnFqdn(q.Name), dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass]}
|
2013-07-23 11:10:38 +00:00
|
|
|
|
|
2015-10-13 17:00:28 +00:00
|
|
|
|
var remote net.IP
|
|
|
|
|
if Net == "tcp" {
|
|
|
|
|
remote = w.RemoteAddr().(*net.TCPAddr).IP
|
|
|
|
|
} else {
|
|
|
|
|
remote = w.RemoteAddr().(*net.UDPAddr).IP
|
|
|
|
|
}
|
|
|
|
|
logger.Info("%s lookup %s", remote, Q.String())
|
2013-07-23 11:10:38 +00:00
|
|
|
|
|
2015-02-03 12:32:18 +00:00
|
|
|
|
IPQuery := h.isIPQuery(q)
|
|
|
|
|
|
2013-07-26 10:54:19 +00:00
|
|
|
|
// Query hosts
|
2015-02-03 12:32:18 +00:00
|
|
|
|
if settings.Hosts.Enable && IPQuery > 0 {
|
2015-10-14 17:08:25 +00:00
|
|
|
|
if ips, ok := h.hosts.Get(Q.qname, IPQuery); ok {
|
2013-07-26 10:54:19 +00:00
|
|
|
|
m := new(dns.Msg)
|
|
|
|
|
m.SetReply(req)
|
2015-02-03 12:32:18 +00:00
|
|
|
|
|
|
|
|
|
switch IPQuery {
|
|
|
|
|
case _IP4Query:
|
|
|
|
|
rr_header := dns.RR_Header{
|
|
|
|
|
Name: q.Name,
|
|
|
|
|
Rrtype: dns.TypeA,
|
|
|
|
|
Class: dns.ClassINET,
|
|
|
|
|
Ttl: settings.Hosts.TTL,
|
|
|
|
|
}
|
2015-10-14 17:08:25 +00:00
|
|
|
|
for _, ip := range ips {
|
|
|
|
|
a := &dns.A{rr_header, ip}
|
|
|
|
|
m.Answer = append(m.Answer, a)
|
|
|
|
|
}
|
2015-02-03 12:32:18 +00:00
|
|
|
|
case _IP6Query:
|
|
|
|
|
rr_header := dns.RR_Header{
|
|
|
|
|
Name: q.Name,
|
|
|
|
|
Rrtype: dns.TypeAAAA,
|
|
|
|
|
Class: dns.ClassINET,
|
|
|
|
|
Ttl: settings.Hosts.TTL,
|
|
|
|
|
}
|
2015-10-14 17:08:25 +00:00
|
|
|
|
for _, ip := range ips {
|
|
|
|
|
aaaa := &dns.AAAA{rr_header, ip}
|
|
|
|
|
m.Answer = append(m.Answer, aaaa)
|
|
|
|
|
}
|
2015-02-03 12:32:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-07-26 10:54:19 +00:00
|
|
|
|
w.WriteMsg(m)
|
2015-10-13 17:00:28 +00:00
|
|
|
|
logger.Debug("%s found in hosts file", Q.qname)
|
2013-07-26 10:54:19 +00:00
|
|
|
|
return
|
2015-02-04 06:37:48 +00:00
|
|
|
|
} else {
|
2015-10-13 17:00:28 +00:00
|
|
|
|
logger.Debug("%s didn't found in hosts file", Q.qname)
|
2013-07-26 10:54:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-11 09:11:32 +00:00
|
|
|
|
// Only query cache when qtype == 'A'|'AAAA' , qclass == 'IN'
|
2013-07-26 10:54:19 +00:00
|
|
|
|
key := KeyGen(Q)
|
2015-02-03 12:32:18 +00:00
|
|
|
|
if IPQuery > 0 {
|
2013-07-24 10:29:38 +00:00
|
|
|
|
mesg, err := h.cache.Get(key)
|
|
|
|
|
if err != nil {
|
2015-02-12 20:30:16 +00:00
|
|
|
|
if mesg, err = h.negCache.Get(key); err != nil {
|
2015-10-13 17:00:28 +00:00
|
|
|
|
logger.Debug("%s didn't hit cache", Q.String())
|
2015-02-12 20:30:16 +00:00
|
|
|
|
} else {
|
2015-10-13 17:00:28 +00:00
|
|
|
|
logger.Debug("%s hit negative cache", Q.String())
|
2015-02-12 20:30:16 +00:00
|
|
|
|
dns.HandleFailed(w, req)
|
|
|
|
|
return
|
|
|
|
|
}
|
2013-07-24 10:29:38 +00:00
|
|
|
|
} else {
|
2015-10-13 17:00:28 +00:00
|
|
|
|
logger.Debug("%s hit cache", Q.String())
|
2015-02-12 20:30:16 +00:00
|
|
|
|
// we need this copy against concurrent modification of Id
|
|
|
|
|
msg := *mesg
|
|
|
|
|
msg.Id = req.Id
|
|
|
|
|
w.WriteMsg(&msg)
|
2013-07-24 10:29:38 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-26 10:54:19 +00:00
|
|
|
|
mesg, err := h.resolver.Lookup(Net, req)
|
2013-07-23 11:10:38 +00:00
|
|
|
|
|
2013-07-24 02:52:59 +00:00
|
|
|
|
if err != nil {
|
2015-10-13 17:00:28 +00:00
|
|
|
|
logger.Warn("Resolve query error %s", err)
|
2013-07-24 02:52:59 +00:00
|
|
|
|
dns.HandleFailed(w, req)
|
2015-02-12 20:30:16 +00:00
|
|
|
|
|
|
|
|
|
// cache the failure, too!
|
|
|
|
|
if err = h.negCache.Set(key, nil); err != nil {
|
2015-10-13 17:00:28 +00:00
|
|
|
|
logger.Warn("Set %s negative cache failed: %v", Q.String(), err)
|
2015-02-12 20:30:16 +00:00
|
|
|
|
}
|
2013-07-24 02:52:59 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.WriteMsg(mesg)
|
|
|
|
|
|
2015-02-03 15:20:36 +00:00
|
|
|
|
if IPQuery > 0 && len(mesg.Answer) > 0 {
|
2013-07-24 10:29:38 +00:00
|
|
|
|
err = h.cache.Set(key, mesg)
|
|
|
|
|
if err != nil {
|
2015-10-13 17:00:28 +00:00
|
|
|
|
logger.Warn("Set %s cache failed: %s", Q.String(), err.Error())
|
2013-07-24 10:29:38 +00:00
|
|
|
|
}
|
2015-10-13 17:00:28 +00:00
|
|
|
|
logger.Debug("Insert %s into cache", Q.String())
|
2013-07-24 10:29:38 +00:00
|
|
|
|
}
|
2013-07-23 11:10:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *GODNSHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) {
|
|
|
|
|
h.do("tcp", w, req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *GODNSHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) {
|
|
|
|
|
h.do("udp", w, req)
|
|
|
|
|
}
|
2013-07-26 04:06:16 +00:00
|
|
|
|
|
2015-02-03 12:32:18 +00:00
|
|
|
|
func (h *GODNSHandler) isIPQuery(q dns.Question) int {
|
|
|
|
|
if q.Qclass != dns.ClassINET {
|
|
|
|
|
return notIPQuery
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch q.Qtype {
|
|
|
|
|
case dns.TypeA:
|
|
|
|
|
return _IP4Query
|
|
|
|
|
case dns.TypeAAAA:
|
|
|
|
|
return _IP6Query
|
|
|
|
|
default:
|
|
|
|
|
return notIPQuery
|
|
|
|
|
}
|
2013-07-26 04:06:16 +00:00
|
|
|
|
}
|
2013-07-26 10:54:19 +00:00
|
|
|
|
|
|
|
|
|
func UnFqdn(s string) string {
|
|
|
|
|
if dns.IsFqdn(s) {
|
|
|
|
|
return s[:len(s)-1]
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|