2013-07-23 11:10:38 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-01-25 17:43:02 +00:00
|
|
|
"crypto/md5"
|
|
|
|
"fmt"
|
2020-01-25 18:48:26 +00:00
|
|
|
"github.com/miekg/dns"
|
2021-04-15 03:42:24 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
2020-01-25 17:43:02 +00:00
|
|
|
"meow.tf/joker/godns/cache"
|
|
|
|
"meow.tf/joker/godns/hosts"
|
|
|
|
"meow.tf/joker/godns/resolver"
|
|
|
|
"meow.tf/joker/godns/utils"
|
2015-10-13 17:00:28 +00:00
|
|
|
"net"
|
2013-07-24 16:09:07 +00:00
|
|
|
"time"
|
2013-07-23 11:10:38 +00:00
|
|
|
)
|
|
|
|
|
2020-01-25 18:48:26 +00:00
|
|
|
type Handler struct {
|
2020-01-25 17:43:02 +00:00
|
|
|
resolver *resolver.Resolver
|
2021-04-15 03:42:24 +00:00
|
|
|
middleware []MiddlewareFunc
|
2020-01-25 17:43:02 +00:00
|
|
|
cache, negCache cache.Cache
|
|
|
|
hosts hosts.Hosts
|
2013-07-23 11:10:38 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 03:42:24 +00:00
|
|
|
type MiddlewareFunc func(w dns.ResponseWriter, r *dns.Msg, m *dns.Msg) *dns.Msg
|
|
|
|
|
|
|
|
func TsigMiddleware(secretKey string) MiddlewareFunc {
|
|
|
|
return func(w dns.ResponseWriter, r *dns.Msg, m *dns.Msg) *dns.Msg {
|
|
|
|
if r.IsTsig() != nil {
|
|
|
|
if w.TsigStatus() == nil {
|
|
|
|
m.SetTsig(r.Extra[len(r.Extra)-1].(*dns.TSIG).Hdr.Name, dns.HmacSHA256, 300, time.Now().Unix())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-25 18:48:26 +00:00
|
|
|
func NewHandler(r *resolver.Resolver, resolverCache, negCache cache.Cache, h hosts.Hosts) *Handler {
|
2021-04-15 03:42:24 +00:00
|
|
|
return &Handler{r, make([]MiddlewareFunc, 0), resolverCache, negCache, h}
|
2013-07-23 11:10:38 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 03:42:24 +00:00
|
|
|
// do handles a dns request.
|
|
|
|
// network will decide which network type it is (udp, tcp, https, etc)
|
2020-02-08 03:38:22 +00:00
|
|
|
func (h *Handler) do(network string, w dns.ResponseWriter, req *dns.Msg) {
|
2021-04-15 03:42:24 +00:00
|
|
|
switch req.Opcode {
|
|
|
|
case dns.OpcodeQuery:
|
|
|
|
h.query(network, w, req)
|
|
|
|
case dns.OpcodeUpdate:
|
|
|
|
if req.IsTsig() == nil {
|
|
|
|
m := new(dns.Msg)
|
|
|
|
m.SetRcode(req, dns.RcodeBadSig)
|
|
|
|
w.WriteMsg(m)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
packed, err := req.Pack()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
m := new(dns.Msg)
|
|
|
|
m.SetRcode(req, dns.RcodeBadSig)
|
|
|
|
w.WriteMsg(m)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := dns.TsigVerify(packed, "", "", false); err != nil {
|
|
|
|
m := new(dns.Msg)
|
|
|
|
m.SetRcode(req, dns.RcodeBadSig)
|
|
|
|
w.WriteMsg(m)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
h.update(network, w, req)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// writeMsg writes a *dns.Msg after passing it through middleware.
|
|
|
|
func (h *Handler) writeMsg(w dns.ResponseWriter, r *dns.Msg, m *dns.Msg) {
|
|
|
|
for _, f := range h.middleware {
|
|
|
|
m = f(w, r, m)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.WriteMsg(m)
|
|
|
|
}
|
|
|
|
|
|
|
|
// query handles dns queries.
|
|
|
|
func (h *Handler) query(network string, w dns.ResponseWriter, req *dns.Msg) {
|
2013-07-23 16:37:38 +00:00
|
|
|
q := req.Question[0]
|
2020-02-08 03:38:22 +00:00
|
|
|
question := resolver.Question{Name: utils.UnFqdn(q.Name), Type: q.Qtype, Class: dns.ClassToString[q.Qclass]}
|
2013-07-23 11:10:38 +00:00
|
|
|
|
2015-10-13 17:00:28 +00:00
|
|
|
var remote net.IP
|
2020-02-08 03:38:22 +00:00
|
|
|
|
|
|
|
switch t := w.RemoteAddr().(type) {
|
|
|
|
case *net.TCPAddr:
|
|
|
|
remote = t.IP
|
|
|
|
case *net.UDPAddr:
|
|
|
|
remote = t.IP
|
|
|
|
default:
|
|
|
|
return
|
2015-10-13 17:00:28 +00:00
|
|
|
}
|
2013-07-23 11:10:38 +00:00
|
|
|
|
2021-04-15 03:42:24 +00:00
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"remote": remote,
|
|
|
|
"question": question.String(),
|
|
|
|
}).Debug("Lookup question")
|
|
|
|
|
|
|
|
key := KeyGen(question)
|
|
|
|
|
|
|
|
// Check cache first
|
|
|
|
mesg, err := h.cache.Get(key)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if mesg, err = h.negCache.Get(key); err != nil {
|
|
|
|
log.WithField("question", question.String()).Debug("no negative cache hit")
|
|
|
|
} else {
|
|
|
|
log.WithField("question", question.String()).Debug("negative cache hit")
|
|
|
|
m := new(dns.Msg)
|
|
|
|
m.SetRcode(req, dns.RcodeServerFailure)
|
|
|
|
w.WriteMsg(m)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.WithField("question", question.String()).Debug("hit cache")
|
|
|
|
|
|
|
|
// we need this copy against concurrent modification of Id
|
|
|
|
msg := *mesg
|
|
|
|
msg.Id = req.Id
|
|
|
|
h.writeMsg(w, req, &msg)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
2015-02-03 12:32:18 +00:00
|
|
|
|
2013-07-26 10:54:19 +00:00
|
|
|
// Query hosts
|
2020-02-08 03:38:22 +00:00
|
|
|
if h.hosts != nil {
|
2021-04-15 03:42:24 +00:00
|
|
|
if host, err := h.hosts.Get(question.Type, question.Name); err == nil {
|
2013-07-26 10:54:19 +00:00
|
|
|
m := new(dns.Msg)
|
|
|
|
m.SetReply(req)
|
2015-02-03 12:32:18 +00:00
|
|
|
|
2020-02-08 03:38:22 +00:00
|
|
|
hdr := dns.RR_Header{
|
|
|
|
Name: q.Name,
|
|
|
|
Rrtype: question.Type,
|
|
|
|
Class: dns.ClassINET,
|
2021-04-15 03:42:24 +00:00
|
|
|
Ttl: uint32(host.TTL / time.Second),
|
2020-02-08 03:38:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch question.Type {
|
|
|
|
case dns.TypeA:
|
2021-04-15 03:42:24 +00:00
|
|
|
for _, val := range host.Values {
|
2020-02-08 03:38:22 +00:00
|
|
|
m.Answer = append(m.Answer, &dns.A{Hdr: hdr, A: net.ParseIP(val).To4()})
|
2015-10-14 17:08:25 +00:00
|
|
|
}
|
2020-02-08 03:38:22 +00:00
|
|
|
case dns.TypeAAAA:
|
2021-04-15 03:42:24 +00:00
|
|
|
for _, val := range host.Values {
|
2020-02-08 03:38:22 +00:00
|
|
|
m.Answer = append(m.Answer, &dns.AAAA{Hdr: hdr, AAAA: net.ParseIP(val).To16()})
|
2015-02-03 12:32:18 +00:00
|
|
|
}
|
2020-02-08 03:38:22 +00:00
|
|
|
case dns.TypeCNAME:
|
2021-04-15 03:42:24 +00:00
|
|
|
for _, val := range host.Values {
|
2020-02-08 03:38:22 +00:00
|
|
|
m.Answer = append(m.Answer, &dns.CNAME{Hdr: hdr, Target: val})
|
2015-10-14 17:08:25 +00:00
|
|
|
}
|
2021-04-15 03:42:24 +00:00
|
|
|
case dns.TypeTXT:
|
|
|
|
m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: host.Values})
|
2015-02-03 12:32:18 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 03:42:24 +00:00
|
|
|
// Insert into cache before using any middleware
|
|
|
|
err = h.cache.Set(key, m)
|
2013-07-26 10:54:19 +00:00
|
|
|
|
2021-04-15 03:42:24 +00:00
|
|
|
if err != nil {
|
|
|
|
log.WithError(err).Error("Unable to insert hosts entry into cache")
|
|
|
|
}
|
2020-02-08 03:38:22 +00:00
|
|
|
|
2021-04-15 03:42:24 +00:00
|
|
|
// Write the message
|
|
|
|
h.writeMsg(w, req, m)
|
2020-02-08 03:38:22 +00:00
|
|
|
|
2021-04-15 03:42:24 +00:00
|
|
|
log.WithField("question", question.Name).Debug("Found entry in hosts file")
|
2013-07-24 10:29:38 +00:00
|
|
|
return
|
2021-04-15 03:42:24 +00:00
|
|
|
} else {
|
|
|
|
log.WithField("question", question.Name).Debug("No entry found in hosts file")
|
2013-07-24 10:29:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-08 03:38:22 +00:00
|
|
|
mesg, err = h.resolver.Lookup(network, req)
|
2013-07-23 11:10:38 +00:00
|
|
|
|
2013-07-24 02:52:59 +00:00
|
|
|
if err != nil {
|
2021-04-15 03:42:24 +00:00
|
|
|
log.WithError(err).Warn("Query resolution error")
|
|
|
|
|
|
|
|
m := new(dns.Msg)
|
|
|
|
m.SetRcode(req, dns.RcodeServerFailure)
|
|
|
|
w.WriteMsg(m)
|
2015-02-12 20:30:16 +00:00
|
|
|
|
|
|
|
// cache the failure, too!
|
|
|
|
if err = h.negCache.Set(key, nil); err != nil {
|
2021-04-15 03:42:24 +00:00
|
|
|
log.WithError(err).Warn("Negative cache save failed")
|
2015-02-12 20:30:16 +00:00
|
|
|
}
|
2013-07-24 02:52:59 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-04-15 03:42:24 +00:00
|
|
|
// Cache if mesg.Answer length > 0
|
|
|
|
// This is done BEFORE sending it so we don't modify the request first
|
2020-02-08 03:38:22 +00:00
|
|
|
if len(mesg.Answer) > 0 {
|
2013-07-24 10:29:38 +00:00
|
|
|
err = h.cache.Set(key, mesg)
|
2021-04-15 03:42:24 +00:00
|
|
|
|
|
|
|
fields := log.Fields{
|
|
|
|
"question": question.String(),
|
|
|
|
}
|
|
|
|
|
|
|
|
log.WithFields(fields).Debug("Insert record into cache")
|
|
|
|
|
2013-07-24 10:29:38 +00:00
|
|
|
if err != nil {
|
2021-04-15 03:42:24 +00:00
|
|
|
fields["error"] = err
|
|
|
|
|
|
|
|
log.WithFields(fields).Warn("Unable to add to cache")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write message
|
|
|
|
h.writeMsg(w, req, mesg)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Handler) update(network string, w dns.ResponseWriter, req *dns.Msg) {
|
|
|
|
for _, question := range req.Question {
|
|
|
|
if _, ok := dns.IsDomainName(question.Name); !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, rr := range req.Ns {
|
|
|
|
hdr := rr.Header()
|
|
|
|
|
|
|
|
if hdr.Class == dns.ClassANY && hdr.Rdlength == 0 {
|
|
|
|
// Delete record
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-24 10:29:38 +00:00
|
|
|
}
|
|
|
|
}
|
2013-07-23 11:10:38 +00:00
|
|
|
}
|
|
|
|
|
2020-01-25 18:48:26 +00:00
|
|
|
func (h *Handler) Bind(net string) func(w dns.ResponseWriter, req *dns.Msg) {
|
2020-01-25 17:43:02 +00:00
|
|
|
return func(w dns.ResponseWriter, req *dns.Msg) {
|
|
|
|
h.do(net, w, req)
|
|
|
|
}
|
2013-07-23 11:10:38 +00:00
|
|
|
}
|
2013-07-26 04:06:16 +00:00
|
|
|
|
2020-01-25 17:43:02 +00:00
|
|
|
func KeyGen(q resolver.Question) string {
|
|
|
|
h := md5.New()
|
|
|
|
h.Write([]byte(q.String()))
|
|
|
|
x := h.Sum(nil)
|
|
|
|
key := fmt.Sprintf("%x", x)
|
|
|
|
return key
|
2013-07-26 10:54:19 +00:00
|
|
|
}
|