mirror of
https://github.com/safing/portmaster
synced 2025-09-02 10:39:22 +00:00
Handle DNS Rcodes
This commit is contained in:
parent
00de73d65e
commit
4e14439112
8 changed files with 53 additions and 22 deletions
|
@ -98,7 +98,12 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
|
||||||
// Start context tracer for context-aware logging.
|
// Start context tracer for context-aware logging.
|
||||||
ctx, tracer := log.AddTracer(ctx)
|
ctx, tracer := log.AddTracer(ctx)
|
||||||
defer tracer.Submit()
|
defer tracer.Submit()
|
||||||
tracer.Tracef("nameserver: handling new request for %s%s from %s:%d", q.FQDN, q.QType, remoteAddr.IP, remoteAddr.Port)
|
tracer.Tracef("nameserver: handling new request for %s from %s:%d", q.ID(), remoteAddr.IP, remoteAddr.Port)
|
||||||
|
|
||||||
|
// Check if there are more than one question.
|
||||||
|
if len(request.Question) > 1 {
|
||||||
|
tracer.Warningf("nameserver: received more than one question from (%s:%d), first question is %s", remoteAddr.IP, remoteAddr.Port, q.ID())
|
||||||
|
}
|
||||||
|
|
||||||
// Setup quick reply function.
|
// Setup quick reply function.
|
||||||
reply := func(responder nsutil.Responder, rrProviders ...nsutil.RRProvider) error {
|
reply := func(responder nsutil.Responder, rrProviders ...nsutil.RRProvider) error {
|
||||||
|
@ -197,10 +202,10 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
|
||||||
// React to special errors.
|
// React to special errors.
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, resolver.ErrNotFound):
|
case errors.Is(err, resolver.ErrNotFound):
|
||||||
tracer.Tracef("nameserver: NXDomain via error: %s", err)
|
tracer.Tracef("nameserver: %s", err)
|
||||||
return reply(nsutil.NxDomain("nxdomain: " + err.Error()))
|
return reply(nsutil.NxDomain("nxdomain: " + err.Error()))
|
||||||
case errors.Is(err, resolver.ErrBlocked):
|
case errors.Is(err, resolver.ErrBlocked):
|
||||||
tracer.Tracef("nameserver: block via error: %s", err)
|
tracer.Tracef("nameserver: %s", err)
|
||||||
return reply(nsutil.ZeroIP("blocked: " + err.Error()))
|
return reply(nsutil.ZeroIP("blocked: " + err.Error()))
|
||||||
case errors.Is(err, resolver.ErrLocalhost):
|
case errors.Is(err, resolver.ErrLocalhost):
|
||||||
tracer.Tracef("nameserver: returning localhost records")
|
tracer.Tracef("nameserver: returning localhost records")
|
||||||
|
|
|
@ -28,6 +28,7 @@ type NameRecord struct {
|
||||||
|
|
||||||
Domain string
|
Domain string
|
||||||
Question string
|
Question string
|
||||||
|
RCode int
|
||||||
Answer []string
|
Answer []string
|
||||||
Ns []string
|
Ns []string
|
||||||
Extra []string
|
Extra []string
|
||||||
|
|
|
@ -206,10 +206,10 @@ func checkCache(ctx context.Context, q *Query) *RRCache {
|
||||||
// We still return the cache, if it isn't NXDomain, as it will be used if the
|
// We still return the cache, if it isn't NXDomain, as it will be used if the
|
||||||
// new query fails.
|
// new query fails.
|
||||||
if rrCache.Expired() {
|
if rrCache.Expired() {
|
||||||
if rrCache.IsNXDomain() {
|
if rrCache.RCode == dns.RcodeSuccess {
|
||||||
return nil
|
return rrCache
|
||||||
}
|
}
|
||||||
return rrCache
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the cache will expire soon and start an async request.
|
// Check if the cache will expire soon and start an async request.
|
||||||
|
@ -377,8 +377,8 @@ resolveLoop:
|
||||||
// Defensive: This should normally not happen.
|
// Defensive: This should normally not happen.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Check if we got NXDomain and whether we should try another resolver.
|
// Check if request suceeded and whether we should try another resolver.
|
||||||
if rrCache.IsNXDomain() && tryAll {
|
if rrCache.RCode != dns.RcodeSuccess && tryAll {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
break resolveLoop
|
break resolveLoop
|
||||||
|
@ -404,7 +404,7 @@ resolveLoop:
|
||||||
// There was an error during resolving, return the old cache entry instead.
|
// There was an error during resolving, return the old cache entry instead.
|
||||||
log.Tracer(ctx).Debugf("resolver: serving backup cache of %s because query failed: %s", q.ID(), err)
|
log.Tracer(ctx).Debugf("resolver: serving backup cache of %s because query failed: %s", q.ID(), err)
|
||||||
return oldCache, nil
|
return oldCache, nil
|
||||||
case rrCache.IsNXDomain():
|
case !rrCache.Cacheable():
|
||||||
// The new result is NXDomain, return the old cache entry instead.
|
// The new result is NXDomain, return the old cache entry instead.
|
||||||
log.Tracer(ctx).Debugf("resolver: serving backup cache of %s because fresh response is NXDomain", q.ID())
|
log.Tracer(ctx).Debugf("resolver: serving backup cache of %s because fresh response is NXDomain", q.ID())
|
||||||
return oldCache, nil
|
return oldCache, nil
|
||||||
|
@ -417,7 +417,7 @@ resolveLoop:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the new entry if cache is enabled.
|
// Save the new entry if cache is enabled.
|
||||||
if !q.NoCaching {
|
if !q.NoCaching && rrCache.Cacheable() {
|
||||||
rrCache.Clean(minTTL)
|
rrCache.Clean(minTTL)
|
||||||
err = rrCache.Save()
|
err = rrCache.Save()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -111,6 +111,7 @@ func (er *envResolverConn) makeRRCache(q *Query, answers []dns.RR) *RRCache {
|
||||||
return &RRCache{
|
return &RRCache{
|
||||||
Domain: q.FQDN,
|
Domain: q.FQDN,
|
||||||
Question: q.QType,
|
Question: q.QType,
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
Answer: answers,
|
Answer: answers,
|
||||||
Extra: []dns.RR{internalSpecialUseComment}, // Always add comment about this TLD.
|
Extra: []dns.RR{internalSpecialUseComment}, // Always add comment about this TLD.
|
||||||
Server: envResolver.Server,
|
Server: envResolver.Server,
|
||||||
|
|
|
@ -202,6 +202,7 @@ func handleMDNSMessages(ctx context.Context, messages chan *dns.Msg) error {
|
||||||
rrCache = &RRCache{
|
rrCache = &RRCache{
|
||||||
Domain: question.Name,
|
Domain: question.Name,
|
||||||
Question: dns.Type(question.Qtype),
|
Question: dns.Type(question.Qtype),
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
Server: mDNSResolver.Server,
|
Server: mDNSResolver.Server,
|
||||||
ServerScope: mDNSResolver.ServerIPScope,
|
ServerScope: mDNSResolver.ServerIPScope,
|
||||||
ServerInfo: mDNSResolver.ServerInfo,
|
ServerInfo: mDNSResolver.ServerInfo,
|
||||||
|
@ -303,6 +304,7 @@ func handleMDNSMessages(ctx context.Context, messages chan *dns.Msg) error {
|
||||||
rrCache = &RRCache{
|
rrCache = &RRCache{
|
||||||
Domain: v.Header().Name,
|
Domain: v.Header().Name,
|
||||||
Question: dns.Type(v.Header().Class),
|
Question: dns.Type(v.Header().Class),
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
Answer: []dns.RR{v},
|
Answer: []dns.RR{v},
|
||||||
Server: mDNSResolver.Server,
|
Server: mDNSResolver.Server,
|
||||||
ServerScope: mDNSResolver.ServerIPScope,
|
ServerScope: mDNSResolver.ServerIPScope,
|
||||||
|
@ -423,6 +425,7 @@ func queryMulticastDNS(ctx context.Context, q *Query) (*RRCache, error) {
|
||||||
return &RRCache{
|
return &RRCache{
|
||||||
Domain: q.FQDN,
|
Domain: q.FQDN,
|
||||||
Question: q.QType,
|
Question: q.QType,
|
||||||
|
RCode: dns.RcodeNameError,
|
||||||
Server: mDNSResolver.Server,
|
Server: mDNSResolver.Server,
|
||||||
ServerScope: mDNSResolver.ServerIPScope,
|
ServerScope: mDNSResolver.ServerIPScope,
|
||||||
ServerInfo: mDNSResolver.ServerInfo,
|
ServerInfo: mDNSResolver.ServerInfo,
|
||||||
|
|
|
@ -81,6 +81,7 @@ func (pr *PlainResolver) Query(ctx context.Context, q *Query) (*RRCache, error)
|
||||||
newRecord := &RRCache{
|
newRecord := &RRCache{
|
||||||
Domain: q.FQDN,
|
Domain: q.FQDN,
|
||||||
Question: q.QType,
|
Question: q.QType,
|
||||||
|
RCode: reply.Rcode,
|
||||||
Answer: reply.Answer,
|
Answer: reply.Answer,
|
||||||
Ns: reply.Ns,
|
Ns: reply.Ns,
|
||||||
Extra: reply.Extra,
|
Extra: reply.Extra,
|
||||||
|
|
|
@ -50,6 +50,7 @@ func (ifq *InFlightQuery) MakeCacheRecord(reply *dns.Msg) *RRCache {
|
||||||
return &RRCache{
|
return &RRCache{
|
||||||
Domain: ifq.Query.FQDN,
|
Domain: ifq.Query.FQDN,
|
||||||
Question: ifq.Query.QType,
|
Question: ifq.Query.QType,
|
||||||
|
RCode: reply.Rcode,
|
||||||
Answer: reply.Answer,
|
Answer: reply.Answer,
|
||||||
Ns: reply.Ns,
|
Ns: reply.Ns,
|
||||||
Extra: reply.Extra,
|
Extra: reply.Extra,
|
||||||
|
@ -477,6 +478,10 @@ func (mgr *tcpResolverConnMgr) handleQueryResponse(conn *dns.Conn, msg *dns.Msg)
|
||||||
|
|
||||||
// persist to database
|
// persist to database
|
||||||
rrCache := inFlight.MakeCacheRecord(msg)
|
rrCache := inFlight.MakeCacheRecord(msg)
|
||||||
|
if !rrCache.Cacheable() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
rrCache.Clean(minTTL)
|
rrCache.Clean(minTTL)
|
||||||
err := rrCache.Save()
|
err := rrCache.Save()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -22,6 +22,7 @@ type RRCache struct {
|
||||||
|
|
||||||
Domain string // constant
|
Domain string // constant
|
||||||
Question dns.Type // constant
|
Question dns.Type // constant
|
||||||
|
RCode int // constant
|
||||||
|
|
||||||
Answer []dns.RR // constant
|
Answer []dns.RR // constant
|
||||||
Ns []dns.RR // constant
|
Ns []dns.RR // constant
|
||||||
|
@ -84,8 +85,8 @@ func (rrCache *RRCache) Clean(minExpires uint32) {
|
||||||
|
|
||||||
// shorten caching
|
// shorten caching
|
||||||
switch {
|
switch {
|
||||||
case rrCache.IsNXDomain():
|
case rrCache.RCode != dns.RcodeSuccess:
|
||||||
// NXDomain
|
// Any sort of error.
|
||||||
lowestTTL = 10
|
lowestTTL = 10
|
||||||
case netenv.IsConnectivityDomain(rrCache.Domain):
|
case netenv.IsConnectivityDomain(rrCache.Domain):
|
||||||
// Responses from these domains might change very quickly depending on the environment.
|
// Responses from these domains might change very quickly depending on the environment.
|
||||||
|
@ -127,6 +128,7 @@ func (rrCache *RRCache) ToNameRecord() *NameRecord {
|
||||||
new := &NameRecord{
|
new := &NameRecord{
|
||||||
Domain: rrCache.Domain,
|
Domain: rrCache.Domain,
|
||||||
Question: rrCache.Question.String(),
|
Question: rrCache.Question.String(),
|
||||||
|
RCode: rrCache.RCode,
|
||||||
TTL: rrCache.TTL,
|
TTL: rrCache.TTL,
|
||||||
Server: rrCache.Server,
|
Server: rrCache.Server,
|
||||||
ServerScope: rrCache.ServerScope,
|
ServerScope: rrCache.ServerScope,
|
||||||
|
@ -147,8 +149,27 @@ func (rrCache *RRCache) ToNameRecord() *NameRecord {
|
||||||
return new
|
return new
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rcodeIsCacheable returns whether a record with the given RCode should be cached.
|
||||||
|
func rcodeIsCacheable(rCode int) bool {
|
||||||
|
switch rCode {
|
||||||
|
case dns.RcodeSuccess, dns.RcodeNameError, dns.RcodeRefused:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cacheable returns whether the record should be cached.
|
||||||
|
func (rrCache *RRCache) Cacheable() bool {
|
||||||
|
return rcodeIsCacheable(rrCache.RCode)
|
||||||
|
}
|
||||||
|
|
||||||
// Save saves the RRCache to the database as a NameRecord.
|
// Save saves the RRCache to the database as a NameRecord.
|
||||||
func (rrCache *RRCache) Save() error {
|
func (rrCache *RRCache) Save() error {
|
||||||
|
if !rrCache.Cacheable() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return rrCache.ToNameRecord().Save()
|
return rrCache.ToNameRecord().Save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +185,7 @@ func GetRRCache(domain string, question dns.Type) (*RRCache, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rrCache.RCode = nameRecord.RCode
|
||||||
rrCache.TTL = nameRecord.TTL
|
rrCache.TTL = nameRecord.TTL
|
||||||
for _, entry := range nameRecord.Answer {
|
for _, entry := range nameRecord.Answer {
|
||||||
rrCache.Answer = parseRR(rrCache.Answer, entry)
|
rrCache.Answer = parseRR(rrCache.Answer, entry)
|
||||||
|
@ -227,16 +249,12 @@ func (rrCache *RRCache) Flags() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNXDomain returnes whether the result is nxdomain.
|
|
||||||
func (rrCache *RRCache) IsNXDomain() bool {
|
|
||||||
return len(rrCache.Answer) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShallowCopy returns a shallow copy of the cache. slices are not copied, but referenced.
|
// ShallowCopy returns a shallow copy of the cache. slices are not copied, but referenced.
|
||||||
func (rrCache *RRCache) ShallowCopy() *RRCache {
|
func (rrCache *RRCache) ShallowCopy() *RRCache {
|
||||||
return &RRCache{
|
return &RRCache{
|
||||||
Domain: rrCache.Domain,
|
Domain: rrCache.Domain,
|
||||||
Question: rrCache.Question,
|
Question: rrCache.Question,
|
||||||
|
RCode: rrCache.RCode,
|
||||||
Answer: rrCache.Answer,
|
Answer: rrCache.Answer,
|
||||||
Ns: rrCache.Ns,
|
Ns: rrCache.Ns,
|
||||||
Extra: rrCache.Extra,
|
Extra: rrCache.Extra,
|
||||||
|
@ -259,14 +277,11 @@ func (rrCache *RRCache) ShallowCopy() *RRCache {
|
||||||
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, dns.RcodeSuccess)
|
reply.SetRcode(request, rrCache.RCode)
|
||||||
reply.Ns = rrCache.Ns
|
reply.Ns = rrCache.Ns
|
||||||
reply.Extra = rrCache.Extra
|
reply.Extra = rrCache.Extra
|
||||||
|
|
||||||
if rrCache.IsNXDomain() {
|
if len(rrCache.Answer) > 0 {
|
||||||
// Set NXDomain return code if not the reply has no answers.
|
|
||||||
reply.Rcode = dns.RcodeNameError
|
|
||||||
} else {
|
|
||||||
// Copy answers, as we randomize their order a little.
|
// Copy answers, as we randomize their order a little.
|
||||||
reply.Answer = make([]dns.RR, len(rrCache.Answer))
|
reply.Answer = make([]dns.RR, len(rrCache.Answer))
|
||||||
copy(reply.Answer, rrCache.Answer)
|
copy(reply.Answer, rrCache.Answer)
|
||||||
|
|
Loading…
Add table
Reference in a new issue