Support non-standard format on dns query name
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user