Support non-standard format on dns query name

This commit is contained in:
Daniel
2020-09-29 09:02:49 +02:00
parent bab3cc67cd
commit 7b5803482c
2 changed files with 39 additions and 17 deletions

View File

@@ -82,10 +82,19 @@ func handleRequestAsWorker(w dns.ResponseWriter, query *dns.Msg) {
func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg) error { //nolint:gocognit // TODO func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg) error { //nolint:gocognit // TODO
// Only process first question, that's how everyone does it. // Only process first question, that's how everyone does it.
question := request.Question[0] originalQuestion := request.Question[0]
// Check if we are handling a non-standard query name.
var nonStandardQuestionFormat bool
lowerCaseQuestion := strings.ToLower(originalQuestion.Name)
if lowerCaseQuestion != originalQuestion.Name {
nonStandardQuestionFormat = true
}
// Create query for the resolver.
q := &resolver.Query{ q := &resolver.Query{
FQDN: question.Name, FQDN: lowerCaseQuestion,
QType: dns.Type(question.Qtype), QType: dns.Type(originalQuestion.Qtype),
} }
// Get remote address of request. // Get remote address of request.
@@ -118,9 +127,9 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
} }
// Check the Query Class. // Check the Query Class.
if question.Qclass != dns.ClassINET { if originalQuestion.Qclass != dns.ClassINET {
// we only serve IN records, return nxdomain // we only serve IN records, return nxdomain
tracer.Warningf("nameserver: only IN record requests are supported but received Qclass %d, returning NXDOMAIN", question.Qclass) tracer.Warningf("nameserver: only IN record requests are supported but received QClass %d, returning NXDOMAIN", originalQuestion.Qclass)
return reply(nsutil.Refused("unsupported qclass")) return reply(nsutil.Refused("unsupported qclass"))
} }
@@ -243,6 +252,11 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
// Save dns request as open. // Save dns request as open.
defer network.SaveOpenDNSRequest(conn) defer network.SaveOpenDNSRequest(conn)
// Revert back to non-standard question format, if we had to convert.
if nonStandardQuestionFormat {
rrCache.ReplaceAnswerNames(originalQuestion.Name)
}
// Reply with successful response. // Reply with successful response.
tracer.Infof("nameserver: returning %s response for %s to %s", conn.Verdict.Verb(), q.ID(), conn.Process()) tracer.Infof("nameserver: returning %s response for %s to %s", conn.Verdict.Verb(), q.ID(), conn.Process())
return reply(rrCache, conn, rrCache) return reply(rrCache, conn, rrCache)

View File

@@ -24,7 +24,7 @@ type RRCache struct {
Question dns.Type // constant Question dns.Type // constant
RCode int // constant RCode int // constant
Answer []dns.RR // constant Answer []dns.RR // mutable (mixing, non-standard formats)
Ns []dns.RR // constant Ns []dns.RR // constant
Extra []dns.RR // constant Extra []dns.RR // constant
TTL int64 // constant TTL int64 // constant
@@ -273,25 +273,33 @@ func (rrCache *RRCache) ShallowCopy() *RRCache {
} }
} }
// ReplaceAnswerNames is a helper function that replaces all answer names, that
// match the query domain, with another value. This is used to support handling
// non-standard query names, which are resolved normalized, but have to be
// reverted back for the origin non-standard query name in order for the
// clients to recognize the response.
func (rrCache *RRCache) ReplaceAnswerNames(fqdn string) {
for _, answer := range rrCache.Answer {
if answer.Header().Name == rrCache.Domain {
answer.Header().Name = fqdn
}
}
}
// ReplyWithDNS creates a new reply to the given query with the data from the RRCache, and additional informational records. // ReplyWithDNS creates a new reply to the given query with the data from the RRCache, and additional informational records.
func (rrCache *RRCache) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns.Msg { func (rrCache *RRCache) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns.Msg {
// reply to query // reply to query
reply := new(dns.Msg) reply := new(dns.Msg)
reply.SetRcode(request, rrCache.RCode) reply.SetRcode(request, rrCache.RCode)
reply.Answer = rrCache.Answer
reply.Ns = rrCache.Ns reply.Ns = rrCache.Ns
reply.Extra = rrCache.Extra reply.Extra = rrCache.Extra
if len(rrCache.Answer) > 0 { // Randomize the order of the answer records a little to allow dumb clients
// Copy answers, as we randomize their order a little. // (who only look at the first record) to reliably connect.
reply.Answer = make([]dns.RR, len(rrCache.Answer)) for i := range reply.Answer {
copy(reply.Answer, rrCache.Answer) j := rand.Intn(i + 1)
reply.Answer[i], reply.Answer[j] = reply.Answer[j], reply.Answer[i]
// Randomize the order of the answer records a little to allow dumb clients
// (who only look at the first record) to reliably connect.
for i := range reply.Answer {
j := rand.Intn(i + 1)
reply.Answer[i], reply.Answer[j] = reply.Answer[j], reply.Answer[i]
}
} }
return reply return reply