You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

121 lines
2.8 KiB

package internal
import (
"log"
"strings"
"time"
"github.com/miekg/dns"
)
type DomainManager struct {
Cache
Storage
RuleEvaluator
Recursors Recursor
}
func (dm *DomainManager) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
// only grab first question: https://stackoverflow.com/questions/4082081/requesting-a-and-aaaa-records-in-single-dns-query/4083071#4083071
start := time.Now()
q := r.Question[0]
responseMessage := new(dns.Msg)
ql := QueryLog{
Started: start.UTC().Format(ISO8601),
Protocol: w.RemoteAddr().Network(),
ClientIP: w.RemoteAddr().String()[:strings.LastIndex(w.RemoteAddr().String(), ":")],
Domain: q.Name,
Status: NoAnswer,
}
var resolved Resolved
var err error
// lookup in cache
if dest := dm.LookupRecord(q.Name); dest != nil {
responseMessage = new(dns.Msg)
responseMessage.Answer = dest
ql.Status = CacheHit
} else if rule, ok := dm.Evaluate(q.Name); ok {
// evaluate domain in rules engine
responseMessage = rule.CreateAnswer(q.Name)
responseMessage.Authoritative = true
ql.Status = CustomRule
} else if resolved, err = dm.Recursors.Resolve(r); err == nil {
dm.SaveAnswers(q.Name, resolved.Message.Answer)
responseMessage = resolved.Message
ql.RecurseUpstreamIP = resolved.UpstreamUsed
ql.RecurseRoundTripTimeMs = int(resolved.RoundtripTime.Milliseconds())
ql.Status = RecursedUpstream
} else if err != nil {
ql.Error = err.Error()
}
responseMessage.SetReply(r)
responseMessage.RecursionAvailable = true
responseMessage.Compress = true
ql.TotalTimeMs = int(time.Since(start).Milliseconds())
log.Printf("%+v", ql)
go func(q QueryLog) {
if err := dm.Storage.Log(q); err != nil {
log.Printf("ERROR WRITING LOG: %v", err)
}
}(ql)
if err := w.WriteMsg(responseMessage); err != nil {
log.Println(err)
}
}
type ResponseStatus string
const (
CacheMiss = ResponseStatus("CACHE MISS")
CacheHit = ResponseStatus("CACHE HIT")
CustomRule = ResponseStatus("CUSTOM RULE")
RecursedUpstream = ResponseStatus("RECURSED")
NoAnswer = ResponseStatus("NO ANSWER")
)
func (ql QueryLog) StartedTime() time.Time {
if ql.Started == "" {
return time.Time{}
}
out, _ := time.Parse(ISO8601, ql.Started)
return out
}
type QueryLog struct {
Started string
ClientIP string
Protocol string
Domain string
TotalTimeMs int
RecurseRoundTripTimeMs int
RecurseUpstreamIP string
Error string
Status ResponseStatus
}
func GetAggregateColumnHeader(ql QueryLog, h LogAggregateColumn) string {
switch h {
case ClientIP:
return ql.ClientIP
case Status:
return string(ql.Status)
case Protocol:
return ql.Protocol
case Domain:
return ql.Domain
case RecurseIP:
return ql.RecurseUpstreamIP
case LookupError:
return ql.Error
}
return ql.Domain
}