mirror of
https://github.com/safing/portmaster
synced 2025-09-01 10:09:11 +00:00
Move some Resolver information to ResolverInfo and propagate it
This commit is contained in:
parent
43cfba8445
commit
20383226f8
13 changed files with 275 additions and 180 deletions
|
@ -85,6 +85,9 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
|
||||||
// be added to it during the livetime of a connection. Access to
|
// be added to it during the livetime of a connection. Access to
|
||||||
// entity must be guarded by the connection lock.
|
// entity must be guarded by the connection lock.
|
||||||
Entity *intel.Entity
|
Entity *intel.Entity
|
||||||
|
// Resolver holds information about the resolver used to resolve
|
||||||
|
// Entity.Domain.
|
||||||
|
Resolver *resolver.ResolverInfo
|
||||||
// Verdict is the final decision that has been made for a connection.
|
// Verdict is the final decision that has been made for a connection.
|
||||||
// The verdict may change so any access to it must be guarded by the
|
// The verdict may change so any access to it must be guarded by the
|
||||||
// connection lock.
|
// connection lock.
|
||||||
|
@ -320,6 +323,7 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
||||||
scope = lastResolvedDomain.Domain
|
scope = lastResolvedDomain.Domain
|
||||||
entity.Domain = lastResolvedDomain.Domain
|
entity.Domain = lastResolvedDomain.Domain
|
||||||
entity.CNAME = lastResolvedDomain.CNAMEs
|
entity.CNAME = lastResolvedDomain.CNAMEs
|
||||||
|
resolverInfo = lastResolvedDomain.Resolver
|
||||||
removeOpenDNSRequest(proc.Pid, lastResolvedDomain.Domain)
|
removeOpenDNSRequest(proc.Pid, lastResolvedDomain.Domain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,6 +368,8 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
||||||
process: proc,
|
process: proc,
|
||||||
// remote endpoint
|
// remote endpoint
|
||||||
Entity: entity,
|
Entity: entity,
|
||||||
|
// resolver used to resolve dns request
|
||||||
|
Resolver: resolverInfo,
|
||||||
// meta
|
// meta
|
||||||
Started: time.Now().Unix(),
|
Started: time.Now().Unix(),
|
||||||
ProfileRevisionCounter: proc.Profile().RevisionCnt(),
|
ProfileRevisionCounter: proc.Profile().RevisionCnt(),
|
||||||
|
|
|
@ -40,6 +40,10 @@ type ResolvedDomain struct {
|
||||||
// Domain.
|
// Domain.
|
||||||
CNAMEs []string
|
CNAMEs []string
|
||||||
|
|
||||||
|
// Resolver holds basic information about the resolver that provided this
|
||||||
|
// information.
|
||||||
|
Resolver *ResolverInfo
|
||||||
|
|
||||||
// Expires holds the timestamp when this entry expires.
|
// Expires holds the timestamp when this entry expires.
|
||||||
// This does not mean that the entry may not be used anymore afterwards,
|
// This does not mean that the entry may not be used anymore afterwards,
|
||||||
// but that this is used to calcuate the TTL of the database record.
|
// but that this is used to calcuate the TTL of the database record.
|
||||||
|
|
|
@ -49,9 +49,20 @@ type NameRecord struct {
|
||||||
Extra []string
|
Extra []string
|
||||||
Expires int64
|
Expires int64
|
||||||
|
|
||||||
Server string
|
Resolver *ResolverInfo
|
||||||
ServerScope int8
|
}
|
||||||
ServerInfo string
|
|
||||||
|
// IsValid returns whether the NameRecord is valid and may be used. Otherwise,
|
||||||
|
// it should be disregarded.
|
||||||
|
func (nameRecord *NameRecord) IsValid() bool {
|
||||||
|
switch {
|
||||||
|
case nameRecord.Resolver == nil || nameRecord.Resolver.Type == "":
|
||||||
|
// Changed in v0.6.7: Introduced Resolver *ResolverInfo
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
// Up to date!
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeNameRecordKey(domain string, question string) string {
|
func makeNameRecordKey(domain string, question string) string {
|
||||||
|
@ -67,7 +78,7 @@ func GetNameRecord(domain, question string) (*NameRecord, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// unwrap
|
// Unwrap record if it's wrapped.
|
||||||
if r.IsWrapped() {
|
if r.IsWrapped() {
|
||||||
// only allocate a new struct, if we need it
|
// only allocate a new struct, if we need it
|
||||||
new := &NameRecord{}
|
new := &NameRecord{}
|
||||||
|
@ -75,14 +86,24 @@ func GetNameRecord(domain, question string) (*NameRecord, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Check if the record is valid.
|
||||||
|
if !new.IsValid() {
|
||||||
|
return nil, errors.New("record is invalid (outdated format)")
|
||||||
|
}
|
||||||
|
|
||||||
return new, nil
|
return new, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// or adjust type
|
// Or just adjust the type.
|
||||||
new, ok := r.(*NameRecord)
|
new, ok := r.(*NameRecord)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("record not of type *NameRecord, but %T", r)
|
return nil, fmt.Errorf("record not of type *NameRecord, but %T", r)
|
||||||
}
|
}
|
||||||
|
// Check if the record is valid.
|
||||||
|
if !new.IsValid() {
|
||||||
|
return nil, errors.New("record is invalid (outdated format)")
|
||||||
|
}
|
||||||
|
|
||||||
return new, nil
|
return new, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -175,9 +175,9 @@ func checkCache(ctx context.Context, q *Query) *RRCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the resolver that the rrCache was resolved with.
|
// Get the resolver that the rrCache was resolved with.
|
||||||
resolver := getActiveResolverByIDWithLocking(rrCache.Server)
|
resolver := getActiveResolverByIDWithLocking(rrCache.Resolver.ID())
|
||||||
if resolver == nil {
|
if resolver == nil {
|
||||||
log.Tracer(ctx).Debugf("resolver: ignoring RRCache %s%s because source server %s has been removed", q.FQDN, q.QType.String(), rrCache.Server)
|
log.Tracer(ctx).Debugf("resolver: ignoring RRCache %s%s because source server %q has been removed", q.FQDN, q.QType.String(), rrCache.Resolver.ID())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,11 +361,11 @@ resolveLoop:
|
||||||
continue
|
continue
|
||||||
case errors.Is(err, ErrTimeout):
|
case errors.Is(err, ErrTimeout):
|
||||||
resolver.Conn.ReportFailure()
|
resolver.Conn.ReportFailure()
|
||||||
log.Tracer(ctx).Debugf("resolver: query to %s timed out", resolver.GetName())
|
log.Tracer(ctx).Debugf("resolver: query to %s timed out", resolver.Info.ID())
|
||||||
continue
|
continue
|
||||||
default:
|
default:
|
||||||
resolver.Conn.ReportFailure()
|
resolver.Conn.ReportFailure()
|
||||||
log.Tracer(ctx).Debugf("resolver: query to %s failed: %s", resolver.GetName(), err)
|
log.Tracer(ctx).Debugf("resolver: query to %s failed: %s", resolver.Info.ID(), err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,13 @@ const (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
envResolver = &Resolver{
|
envResolver = &Resolver{
|
||||||
Server: ServerSourceEnv,
|
ConfigURL: ServerSourceEnv,
|
||||||
ServerType: ServerTypeEnv,
|
Info: &ResolverInfo{
|
||||||
ServerIPScope: netutils.SiteLocal,
|
Type: ServerTypeEnv,
|
||||||
ServerInfo: "Portmaster environment",
|
Source: ServerSourceEnv,
|
||||||
Source: ServerSourceEnv,
|
IPScope: netutils.SiteLocal,
|
||||||
Conn: &envResolverConn{},
|
},
|
||||||
|
Conn: &envResolverConn{},
|
||||||
}
|
}
|
||||||
envResolvers = []*Resolver{envResolver}
|
envResolvers = []*Resolver{envResolver}
|
||||||
|
|
||||||
|
@ -109,14 +110,12 @@ func (er *envResolverConn) makeRRCache(q *Query, answers []dns.RR) *RRCache {
|
||||||
q.NoCaching = true
|
q.NoCaching = true
|
||||||
|
|
||||||
return &RRCache{
|
return &RRCache{
|
||||||
Domain: q.FQDN,
|
Domain: q.FQDN,
|
||||||
Question: q.QType,
|
Question: q.QType,
|
||||||
RCode: dns.RcodeSuccess,
|
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,
|
Resolver: envResolver.Info.Copy(),
|
||||||
ServerScope: envResolver.ServerIPScope,
|
|
||||||
ServerInfo: envResolver.ServerInfo,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,13 @@ var (
|
||||||
questionsLock sync.Mutex
|
questionsLock sync.Mutex
|
||||||
|
|
||||||
mDNSResolver = &Resolver{
|
mDNSResolver = &Resolver{
|
||||||
Server: ServerSourceMDNS,
|
ConfigURL: ServerSourceMDNS,
|
||||||
ServerType: ServerTypeDNS,
|
Info: &ResolverInfo{
|
||||||
ServerIPScope: netutils.SiteLocal,
|
Type: ServerTypeMDNS,
|
||||||
ServerInfo: "mDNS resolver",
|
Source: ServerSourceMDNS,
|
||||||
Source: ServerSourceMDNS,
|
IPScope: netutils.SiteLocal,
|
||||||
Conn: &mDNSResolverConn{},
|
},
|
||||||
|
Conn: &mDNSResolverConn{},
|
||||||
}
|
}
|
||||||
mDNSResolvers = []*Resolver{mDNSResolver}
|
mDNSResolvers = []*Resolver{mDNSResolver}
|
||||||
)
|
)
|
||||||
|
@ -200,12 +201,10 @@ func handleMDNSMessages(ctx context.Context, messages chan *dns.Msg) error {
|
||||||
// create new and do not append
|
// create new and do not append
|
||||||
if err != nil || rrCache.Modified < time.Now().Add(-2*time.Second).Unix() || rrCache.Expired() {
|
if err != nil || rrCache.Modified < time.Now().Add(-2*time.Second).Unix() || rrCache.Expired() {
|
||||||
rrCache = &RRCache{
|
rrCache = &RRCache{
|
||||||
Domain: question.Name,
|
Domain: question.Name,
|
||||||
Question: dns.Type(question.Qtype),
|
Question: dns.Type(question.Qtype),
|
||||||
RCode: dns.RcodeSuccess,
|
RCode: dns.RcodeSuccess,
|
||||||
Server: mDNSResolver.Server,
|
Resolver: mDNSResolver.Info.Copy(),
|
||||||
ServerScope: mDNSResolver.ServerIPScope,
|
|
||||||
ServerInfo: mDNSResolver.ServerInfo,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,13 +301,11 @@ func handleMDNSMessages(ctx context.Context, messages chan *dns.Msg) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
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,
|
RCode: dns.RcodeSuccess,
|
||||||
Answer: []dns.RR{v},
|
Answer: []dns.RR{v},
|
||||||
Server: mDNSResolver.Server,
|
Resolver: mDNSResolver.Info.Copy(),
|
||||||
ServerScope: mDNSResolver.ServerIPScope,
|
|
||||||
ServerInfo: mDNSResolver.ServerInfo,
|
|
||||||
}
|
}
|
||||||
rrCache.Clean(minMDnsTTL)
|
rrCache.Clean(minMDnsTTL)
|
||||||
err := rrCache.Save()
|
err := rrCache.Save()
|
||||||
|
@ -423,12 +420,10 @@ func queryMulticastDNS(ctx context.Context, q *Query) (*RRCache, error) {
|
||||||
|
|
||||||
// Respond with NXDomain.
|
// Respond with NXDomain.
|
||||||
return &RRCache{
|
return &RRCache{
|
||||||
Domain: q.FQDN,
|
Domain: q.FQDN,
|
||||||
Question: q.QType,
|
Question: q.QType,
|
||||||
RCode: dns.RcodeNameError,
|
RCode: dns.RcodeNameError,
|
||||||
Server: mDNSResolver.Server,
|
Resolver: mDNSResolver.Info.Copy(),
|
||||||
ServerScope: mDNSResolver.ServerIPScope,
|
|
||||||
ServerInfo: mDNSResolver.ServerInfo,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,22 +72,20 @@ func (pr *PlainResolver) Query(ctx context.Context, q *Query) (*RRCache, error)
|
||||||
|
|
||||||
// check if blocked
|
// check if blocked
|
||||||
if pr.resolver.IsBlockedUpstream(reply) {
|
if pr.resolver.IsBlockedUpstream(reply) {
|
||||||
return nil, &BlockedUpstreamError{pr.resolver.GetName()}
|
return nil, &BlockedUpstreamError{pr.resolver.Info.DescriptiveName()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hint network environment at successful connection
|
// hint network environment at successful connection
|
||||||
netenv.ReportSuccessfulConnection()
|
netenv.ReportSuccessfulConnection()
|
||||||
|
|
||||||
newRecord := &RRCache{
|
newRecord := &RRCache{
|
||||||
Domain: q.FQDN,
|
Domain: q.FQDN,
|
||||||
Question: q.QType,
|
Question: q.QType,
|
||||||
RCode: reply.Rcode,
|
RCode: reply.Rcode,
|
||||||
Answer: reply.Answer,
|
Answer: reply.Answer,
|
||||||
Ns: reply.Ns,
|
Ns: reply.Ns,
|
||||||
Extra: reply.Extra,
|
Extra: reply.Extra,
|
||||||
Server: pr.resolver.Server,
|
Resolver: pr.resolver.Info.Copy(),
|
||||||
ServerScope: pr.resolver.ServerIPScope,
|
|
||||||
ServerInfo: pr.resolver.ServerInfo,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check if reply.Answer is valid
|
// TODO: check if reply.Answer is valid
|
||||||
|
|
|
@ -49,15 +49,13 @@ type InFlightQuery struct {
|
||||||
// MakeCacheRecord creates an RCache record from a reply.
|
// MakeCacheRecord creates an RCache record from a reply.
|
||||||
func (ifq *InFlightQuery) MakeCacheRecord(reply *dns.Msg) *RRCache {
|
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,
|
RCode: reply.Rcode,
|
||||||
Answer: reply.Answer,
|
Answer: reply.Answer,
|
||||||
Ns: reply.Ns,
|
Ns: reply.Ns,
|
||||||
Extra: reply.Extra,
|
Extra: reply.Extra,
|
||||||
Server: ifq.Resolver.Server,
|
Resolver: ifq.Resolver.Info.Copy(),
|
||||||
ServerScope: ifq.Resolver.ServerIPScope,
|
|
||||||
ServerInfo: ifq.Resolver.ServerInfo,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +170,7 @@ func (tr *TCPResolver) Query(ctx context.Context, q *Query) (*RRCache, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if tr.resolver.IsBlockedUpstream(reply) {
|
if tr.resolver.IsBlockedUpstream(reply) {
|
||||||
return nil, &BlockedUpstreamError{tr.resolver.GetName()}
|
return nil, &BlockedUpstreamError{tr.resolver.Info.DescriptiveName()}
|
||||||
}
|
}
|
||||||
|
|
||||||
return inFlight.MakeCacheRecord(reply), nil
|
return inFlight.MakeCacheRecord(reply), nil
|
||||||
|
@ -189,7 +187,7 @@ func (tr *TCPResolver) checkClientStatus() {
|
||||||
select {
|
select {
|
||||||
case tr.clientHeartbeat <- struct{}{}:
|
case tr.clientHeartbeat <- struct{}{}:
|
||||||
case <-time.After(heartbeatTimeout):
|
case <-time.After(heartbeatTimeout):
|
||||||
log.Warningf("resolver: heartbeat failed for %s dns client, stopping", tr.resolver.GetName())
|
log.Warningf("resolver: heartbeat failed for %s dns client, stopping", tr.resolver.Info.DescriptiveName())
|
||||||
stopClient()
|
stopClient()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -299,7 +297,7 @@ func (mgr *tcpResolverConnMgr) waitForWork(clientCtx context.Context) (proceed b
|
||||||
select {
|
select {
|
||||||
case mgr.tr.queries <- inFlight.Msg:
|
case mgr.tr.queries <- inFlight.Msg:
|
||||||
default:
|
default:
|
||||||
log.Warningf("resolver: failed to re-inject abandoned query to %s", mgr.tr.resolver.GetName())
|
log.Warningf("resolver: failed to re-inject abandoned query to %s", mgr.tr.resolver.Info.DescriptiveName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// in-flight queries that match the connection instance ID are not changed. They are already in the queue.
|
// in-flight queries that match the connection instance ID are not changed. They are already in the queue.
|
||||||
|
@ -317,7 +315,7 @@ func (mgr *tcpResolverConnMgr) waitForWork(clientCtx context.Context) (proceed b
|
||||||
select {
|
select {
|
||||||
case mgr.tr.queries <- msg:
|
case mgr.tr.queries <- msg:
|
||||||
case <-time.After(2 * time.Second):
|
case <-time.After(2 * time.Second):
|
||||||
log.Warningf("resolver: failed to re-inject waking query to %s", mgr.tr.resolver.GetName())
|
log.Warningf("resolver: failed to re-inject waking query to %s", mgr.tr.resolver.Info.DescriptiveName())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -343,7 +341,7 @@ func (mgr *tcpResolverConnMgr) establishConnection() (
|
||||||
var err error
|
var err error
|
||||||
conn, err = mgr.tr.dnsClient.Dial(mgr.tr.resolver.ServerAddress)
|
conn, err = mgr.tr.dnsClient.Dial(mgr.tr.resolver.ServerAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("resolver: failed to connect to %s (%s)", mgr.tr.resolver.GetName(), mgr.tr.resolver.ServerAddress)
|
log.Debugf("resolver: failed to connect to %s", mgr.tr.resolver.Info.DescriptiveName())
|
||||||
return nil, nil, nil, nil
|
return nil, nil, nil, nil
|
||||||
}
|
}
|
||||||
connCtx, cancelConnCtx = context.WithCancel(context.Background())
|
connCtx, cancelConnCtx = context.WithCancel(context.Background())
|
||||||
|
@ -356,9 +354,8 @@ func (mgr *tcpResolverConnMgr) establishConnection() (
|
||||||
|
|
||||||
// Log that a connection to the resolver was established.
|
// Log that a connection to the resolver was established.
|
||||||
log.Debugf(
|
log.Debugf(
|
||||||
"resolver: connected to %s (%s) with %d queries waiting",
|
"resolver: connected to %s with %d queries waiting",
|
||||||
mgr.tr.resolver.GetName(),
|
mgr.tr.resolver.Info.DescriptiveName(),
|
||||||
conn.RemoteAddr(),
|
|
||||||
waitingQueries,
|
waitingQueries,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -434,7 +431,7 @@ func (mgr *tcpResolverConnMgr) queryHandler( //nolint:golint // context.Context
|
||||||
activeQueries := len(mgr.tr.inFlightQueries)
|
activeQueries := len(mgr.tr.inFlightQueries)
|
||||||
mgr.tr.Unlock()
|
mgr.tr.Unlock()
|
||||||
if activeQueries == 0 {
|
if activeQueries == 0 {
|
||||||
log.Debugf("resolver: recycling conn to %s (%s)", mgr.tr.resolver.GetName(), conn.RemoteAddr())
|
log.Debugf("resolver: recycling conn to %s", mgr.tr.resolver.Info.DescriptiveName())
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -454,9 +451,8 @@ func (mgr *tcpResolverConnMgr) handleQueryResponse(conn *dns.Conn, msg *dns.Msg)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Debugf(
|
log.Debugf(
|
||||||
"resolver: received possibly unsolicited reply from %s (%s): txid=%d q=%+v",
|
"resolver: received possibly unsolicited reply from %s: txid=%d q=%+v",
|
||||||
mgr.tr.resolver.GetName(),
|
mgr.tr.resolver.Info.DescriptiveName(),
|
||||||
conn.RemoteAddr(),
|
|
||||||
msg.Id,
|
msg.Id,
|
||||||
msg.Question,
|
msg.Question,
|
||||||
)
|
)
|
||||||
|
@ -519,24 +515,21 @@ func (mgr *tcpResolverConnMgr) logConnectionError(err error, conn *dns.Conn, con
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, io.EOF):
|
case errors.Is(err, io.EOF):
|
||||||
log.Debugf(
|
log.Debugf(
|
||||||
"resolver: connection to %s (%s) was closed with %d in-flight queries",
|
"resolver: connection to %s was closed with %d in-flight queries",
|
||||||
mgr.tr.resolver.GetName(),
|
mgr.tr.resolver.Info.DescriptiveName(),
|
||||||
conn.RemoteAddr(),
|
|
||||||
inFlightQueries,
|
inFlightQueries,
|
||||||
)
|
)
|
||||||
case reading:
|
case reading:
|
||||||
log.Warningf(
|
log.Warningf(
|
||||||
"resolver: read error from %s (%s) with %d in-flight queries: %s",
|
"resolver: read error from %s with %d in-flight queries: %s",
|
||||||
mgr.tr.resolver.GetName(),
|
mgr.tr.resolver.Info.DescriptiveName(),
|
||||||
conn.RemoteAddr(),
|
|
||||||
inFlightQueries,
|
inFlightQueries,
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
log.Warningf(
|
log.Warningf(
|
||||||
"resolver: write error to %s (%s) with %d in-flight queries: %s",
|
"resolver: write error to %s with %d in-flight queries: %s",
|
||||||
mgr.tr.resolver.GetName(),
|
mgr.tr.resolver.Info.DescriptiveName(),
|
||||||
conn.RemoteAddr(),
|
|
||||||
inFlightQueries,
|
inFlightQueries,
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,21 +2,24 @@ package resolver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/safing/portmaster/netenv"
|
"github.com/safing/portmaster/netenv"
|
||||||
|
"github.com/safing/portmaster/network/netutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DNS Resolver Attributes
|
// DNS Resolver Attributes
|
||||||
const (
|
const (
|
||||||
ServerTypeDNS = "dns"
|
ServerTypeDNS = "dns"
|
||||||
ServerTypeTCP = "tcp"
|
ServerTypeTCP = "tcp"
|
||||||
ServerTypeDoT = "dot"
|
ServerTypeDoT = "dot"
|
||||||
ServerTypeDoH = "doh"
|
ServerTypeDoH = "doh"
|
||||||
ServerTypeEnv = "env"
|
ServerTypeMDNS = "mdns"
|
||||||
|
ServerTypeEnv = "env"
|
||||||
|
|
||||||
ServerSourceConfigured = "config"
|
ServerSourceConfigured = "config"
|
||||||
ServerSourceOperatingSystem = "system"
|
ServerSourceOperatingSystem = "system"
|
||||||
|
@ -39,14 +42,13 @@ type Resolver struct {
|
||||||
// - `empty`: NXDomain result, but without any other record in any section
|
// - `empty`: NXDomain result, but without any other record in any section
|
||||||
// - `refused`: Request was refused
|
// - `refused`: Request was refused
|
||||||
// - `zeroip`: Answer only contains zeroip
|
// - `zeroip`: Answer only contains zeroip
|
||||||
Server string
|
ConfigURL string
|
||||||
|
|
||||||
// Source describes from where the resolver configuration originated.
|
// Info holds the parsed configuration.
|
||||||
Source string
|
Info *ResolverInfo
|
||||||
|
|
||||||
// Name is the name of the resolver as passed via
|
// ServerAddress holds the resolver address for easier use.
|
||||||
// ?name=.
|
ServerAddress string
|
||||||
Name string
|
|
||||||
|
|
||||||
// UpstreamBlockDetection defines the detection type
|
// UpstreamBlockDetection defines the detection type
|
||||||
// to identifier upstream DNS query blocking.
|
// to identifier upstream DNS query blocking.
|
||||||
|
@ -57,14 +59,6 @@ type Resolver struct {
|
||||||
// - disabled
|
// - disabled
|
||||||
UpstreamBlockDetection string
|
UpstreamBlockDetection string
|
||||||
|
|
||||||
// Parsed config
|
|
||||||
ServerType string
|
|
||||||
ServerAddress string
|
|
||||||
ServerIP net.IP
|
|
||||||
ServerIPScope int8
|
|
||||||
ServerPort uint16
|
|
||||||
ServerInfo string
|
|
||||||
|
|
||||||
// Special Options
|
// Special Options
|
||||||
VerifyDomain string
|
VerifyDomain string
|
||||||
Search []string
|
Search []string
|
||||||
|
@ -73,25 +67,111 @@ type Resolver struct {
|
||||||
Conn ResolverConn `json:"-"`
|
Conn ResolverConn `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolverInfo is a subset of resolver attributes that is attached to answers
|
||||||
|
// from that server in order to use it later for decision making. It must not
|
||||||
|
// be changed by anyone after creation and initialization is complete.
|
||||||
|
type ResolverInfo struct {
|
||||||
|
// Name describes the name given to the resolver. The name is configured in the config URL using the name parameter.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Type describes the type of the resolver.
|
||||||
|
// Possible values include dns, tcp, dot, doh, mdns, env.
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// Source describes where the resolver configuration came from.
|
||||||
|
// Possible values include config, system, mdns, env.
|
||||||
|
Source string
|
||||||
|
|
||||||
|
// IP is the IP address of the resolver
|
||||||
|
IP net.IP
|
||||||
|
|
||||||
|
// IPScope is the network scope of the IP address.
|
||||||
|
IPScope netutils.IPScope
|
||||||
|
|
||||||
|
// Port is the udp/tcp port of the resolver.
|
||||||
|
Port uint16
|
||||||
|
|
||||||
|
// id holds a unique ID for this resolver.
|
||||||
|
id string
|
||||||
|
idGen sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the unique ID of the resolver.
|
||||||
|
func (info *ResolverInfo) ID() string {
|
||||||
|
// Generate the ID the first time.
|
||||||
|
info.idGen.Do(func() {
|
||||||
|
switch info.Type {
|
||||||
|
case ServerTypeMDNS:
|
||||||
|
info.id = ServerTypeMDNS
|
||||||
|
case ServerTypeEnv:
|
||||||
|
info.id = ServerTypeEnv
|
||||||
|
default:
|
||||||
|
info.id = fmt.Sprintf(
|
||||||
|
"%s://%s:%d#%s",
|
||||||
|
info.Type,
|
||||||
|
info.IP,
|
||||||
|
info.Port,
|
||||||
|
info.Source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return info.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescriptiveName returns a human readable, but also detailed representation
|
||||||
|
// of the resolver.
|
||||||
|
func (info *ResolverInfo) DescriptiveName() string {
|
||||||
|
switch {
|
||||||
|
case info.Type == ServerTypeMDNS:
|
||||||
|
return "MDNS"
|
||||||
|
case info.Type == ServerTypeEnv:
|
||||||
|
return "Portmaster Environment"
|
||||||
|
case info.Name != "":
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"%s (%s)",
|
||||||
|
info.Name,
|
||||||
|
info.ID(),
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"%s (%s)",
|
||||||
|
info.IP.String(),
|
||||||
|
info.ID(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a full copy of the ResolverInfo.
|
||||||
|
func (info *ResolverInfo) Copy() *ResolverInfo {
|
||||||
|
// Force idGen to run before we copy.
|
||||||
|
_ = info.ID()
|
||||||
|
|
||||||
|
// Copy manually in order to not copy the mutex.
|
||||||
|
cp := &ResolverInfo{
|
||||||
|
Name: info.Name,
|
||||||
|
Type: info.Type,
|
||||||
|
Source: info.Source,
|
||||||
|
IP: info.IP,
|
||||||
|
IPScope: info.IPScope,
|
||||||
|
Port: info.Port,
|
||||||
|
id: info.id,
|
||||||
|
}
|
||||||
|
// Trigger idGen.Do(), as the ID is already generated.
|
||||||
|
cp.idGen.Do(func() {})
|
||||||
|
|
||||||
|
return cp
|
||||||
|
}
|
||||||
|
|
||||||
// IsBlockedUpstream returns true if the request has been blocked
|
// IsBlockedUpstream returns true if the request has been blocked
|
||||||
// upstream.
|
// upstream.
|
||||||
func (resolver *Resolver) IsBlockedUpstream(answer *dns.Msg) bool {
|
func (resolver *Resolver) IsBlockedUpstream(answer *dns.Msg) bool {
|
||||||
return isBlockedUpstream(resolver, answer)
|
return isBlockedUpstream(resolver, answer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetName returns the name of the server. If no name
|
|
||||||
// is configured the server address is returned.
|
|
||||||
func (resolver *Resolver) GetName() string {
|
|
||||||
if resolver.Name != "" {
|
|
||||||
return resolver.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolver.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the URL representation of the resolver.
|
// String returns the URL representation of the resolver.
|
||||||
func (resolver *Resolver) String() string {
|
func (resolver *Resolver) String() string {
|
||||||
return resolver.GetName()
|
return resolver.Info.DescriptiveName()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolverConn is an interface to implement different types of query backends.
|
// ResolverConn is an interface to implement different types of query backends.
|
||||||
|
|
|
@ -52,7 +52,7 @@ func TestSingleResolving(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Logf("running bulk query test with resolver %s", resolver.Server)
|
t.Logf("running bulk query test with resolver %s", resolver.Info.DescriptiveName())
|
||||||
|
|
||||||
started := time.Now()
|
started := time.Now()
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ func TestBulkResolving(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Logf("running bulk query test with resolver %s", resolver.Server)
|
t.Logf("running bulk query test with resolver %s", resolver.Info.DescriptiveName())
|
||||||
|
|
||||||
started := time.Now()
|
started := time.Now()
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ func formatIPAndPort(ip net.IP, port uint16) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolverConnFactory(resolver *Resolver) ResolverConn {
|
func resolverConnFactory(resolver *Resolver) ResolverConn {
|
||||||
switch resolver.ServerType {
|
switch resolver.Info.Type {
|
||||||
case ServerTypeTCP:
|
case ServerTypeTCP:
|
||||||
return NewTCPResolver(resolver)
|
return NewTCPResolver(resolver)
|
||||||
case ServerTypeDoT:
|
case ServerTypeDoT:
|
||||||
|
@ -82,26 +83,36 @@ func createResolver(resolverURL, source string) (*Resolver, bool, error) {
|
||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
case ServerTypeDNS, ServerTypeDoT, ServerTypeTCP:
|
case ServerTypeDNS, ServerTypeDoT, ServerTypeTCP:
|
||||||
default:
|
default:
|
||||||
return nil, false, fmt.Errorf("invalid DNS resolver scheme %q", u.Scheme)
|
return nil, false, fmt.Errorf("DNS resolver scheme %q invalid", u.Scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := net.ParseIP(u.Hostname())
|
ip := net.ParseIP(u.Hostname())
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return nil, false, fmt.Errorf("invalid resolver IP")
|
return nil, false, fmt.Errorf("resolver IP %q invalid", u.Hostname())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add default port for scheme if it is missing.
|
// Add default port for scheme if it is missing.
|
||||||
if u.Port() == "" {
|
var port uint16
|
||||||
switch u.Scheme {
|
hostPort := u.Port()
|
||||||
case ServerTypeDNS, ServerTypeTCP:
|
switch {
|
||||||
u.Host += ":53"
|
case hostPort != "":
|
||||||
case ServerTypeDoT:
|
parsedPort, err := strconv.ParseUint(hostPort, 10, 16)
|
||||||
u.Host += ":853"
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("resolver port %q invalid", u.Port())
|
||||||
}
|
}
|
||||||
|
port = uint16(parsedPort)
|
||||||
|
case u.Scheme == ServerTypeDNS, u.Scheme == ServerTypeTCP:
|
||||||
|
port = 53
|
||||||
|
case u.Scheme == ServerTypeDoH:
|
||||||
|
port = 443
|
||||||
|
case u.Scheme == ServerTypeDoT:
|
||||||
|
port = 853
|
||||||
|
default:
|
||||||
|
return nil, false, fmt.Errorf("missing port in %q", u.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
scope := netutils.ClassifyIP(ip)
|
scope := netutils.GetIPScope(ip)
|
||||||
if scope == netutils.HostLocal {
|
if scope.IsLocalhost() {
|
||||||
return nil, true, nil // skip
|
return nil, true, nil // skip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,24 +138,20 @@ func createResolver(resolverURL, source string) (*Resolver, bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
new := &Resolver{
|
new := &Resolver{
|
||||||
Server: resolverURL,
|
ConfigURL: resolverURL,
|
||||||
ServerType: u.Scheme,
|
Info: &ResolverInfo{
|
||||||
ServerAddress: u.Host,
|
Name: query.Get("name"),
|
||||||
ServerIP: ip,
|
Type: u.Scheme,
|
||||||
ServerIPScope: scope,
|
Source: source,
|
||||||
Source: source,
|
IP: ip,
|
||||||
|
IPScope: scope,
|
||||||
|
Port: port,
|
||||||
|
},
|
||||||
|
ServerAddress: net.JoinHostPort(ip.String(), strconv.Itoa(int(port))),
|
||||||
VerifyDomain: verifyDomain,
|
VerifyDomain: verifyDomain,
|
||||||
Name: query.Get("name"),
|
|
||||||
UpstreamBlockDetection: blockType,
|
UpstreamBlockDetection: blockType,
|
||||||
}
|
}
|
||||||
|
|
||||||
u.RawQuery = "" // Remove options from parsed URL
|
|
||||||
if new.Name != "" {
|
|
||||||
new.ServerInfo = fmt.Sprintf("%s (%s, from %s)", new.Name, u, source)
|
|
||||||
} else {
|
|
||||||
new.ServerInfo = fmt.Sprintf("%s (from %s)", u, source)
|
|
||||||
}
|
|
||||||
|
|
||||||
new.Conn = resolverConnFactory(new)
|
new.Conn = resolverConnFactory(new)
|
||||||
return new, false, nil
|
return new, false, nil
|
||||||
}
|
}
|
||||||
|
@ -195,7 +202,7 @@ func getSystemResolvers() (resolvers []*Resolver) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if netutils.IPIsLAN(nameserver.IP) {
|
if resolver.Info.IPScope.IsLAN() {
|
||||||
configureSearchDomains(resolver, nameserver.Search)
|
configureSearchDomains(resolver, nameserver.Search)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,16 +251,16 @@ func loadResolvers() {
|
||||||
activeResolvers = make(map[string]*Resolver)
|
activeResolvers = make(map[string]*Resolver)
|
||||||
// add
|
// add
|
||||||
for _, resolver := range newResolvers {
|
for _, resolver := range newResolvers {
|
||||||
activeResolvers[resolver.Server] = resolver
|
activeResolvers[resolver.Info.ID()] = resolver
|
||||||
}
|
}
|
||||||
activeResolvers[mDNSResolver.Server] = mDNSResolver
|
activeResolvers[mDNSResolver.Info.ID()] = mDNSResolver
|
||||||
activeResolvers[envResolver.Server] = envResolver
|
activeResolvers[envResolver.Info.ID()] = envResolver
|
||||||
|
|
||||||
// log global resolvers
|
// log global resolvers
|
||||||
if len(globalResolvers) > 0 {
|
if len(globalResolvers) > 0 {
|
||||||
log.Trace("resolver: loaded global resolvers:")
|
log.Trace("resolver: loaded global resolvers:")
|
||||||
for _, resolver := range globalResolvers {
|
for _, resolver := range globalResolvers {
|
||||||
log.Tracef("resolver: %s", resolver.Server)
|
log.Tracef("resolver: %s", resolver.ConfigURL)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Warning("resolver: no global resolvers loaded")
|
log.Warning("resolver: no global resolvers loaded")
|
||||||
|
@ -263,7 +270,7 @@ func loadResolvers() {
|
||||||
if len(localResolvers) > 0 {
|
if len(localResolvers) > 0 {
|
||||||
log.Trace("resolver: loaded local resolvers:")
|
log.Trace("resolver: loaded local resolvers:")
|
||||||
for _, resolver := range localResolvers {
|
for _, resolver := range localResolvers {
|
||||||
log.Tracef("resolver: %s", resolver.Server)
|
log.Tracef("resolver: %s", resolver.ConfigURL)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Info("resolver: no local resolvers loaded")
|
log.Info("resolver: no local resolvers loaded")
|
||||||
|
@ -273,7 +280,7 @@ func loadResolvers() {
|
||||||
if len(systemResolvers) > 0 {
|
if len(systemResolvers) > 0 {
|
||||||
log.Trace("resolver: loaded system/network-assigned resolvers:")
|
log.Trace("resolver: loaded system/network-assigned resolvers:")
|
||||||
for _, resolver := range systemResolvers {
|
for _, resolver := range systemResolvers {
|
||||||
log.Tracef("resolver: %s", resolver.Server)
|
log.Tracef("resolver: %s", resolver.ConfigURL)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Info("resolver: no system/network-assigned resolvers loaded")
|
log.Info("resolver: no system/network-assigned resolvers loaded")
|
||||||
|
@ -285,7 +292,7 @@ func loadResolvers() {
|
||||||
for _, scope := range localScopes {
|
for _, scope := range localScopes {
|
||||||
var scopeServers []string
|
var scopeServers []string
|
||||||
for _, resolver := range scope.Resolvers {
|
for _, resolver := range scope.Resolvers {
|
||||||
scopeServers = append(scopeServers, resolver.Server)
|
scopeServers = append(scopeServers, resolver.ConfigURL)
|
||||||
}
|
}
|
||||||
log.Tracef("resolver: %s: %s", scope.Domain, strings.Join(scopeServers, ", "))
|
log.Tracef("resolver: %s: %s", scope.Domain, strings.Join(scopeServers, ", "))
|
||||||
}
|
}
|
||||||
|
@ -306,11 +313,11 @@ func setScopedResolvers(resolvers []*Resolver) {
|
||||||
localScopes = make([]*Scope, 0)
|
localScopes = make([]*Scope, 0)
|
||||||
|
|
||||||
for _, resolver := range resolvers {
|
for _, resolver := range resolvers {
|
||||||
if resolver.ServerIP != nil && netutils.IPIsLAN(resolver.ServerIP) {
|
if resolver.Info.IPScope.IsLAN() {
|
||||||
localResolvers = append(localResolvers, resolver)
|
localResolvers = append(localResolvers, resolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resolver.Source == ServerSourceOperatingSystem {
|
if resolver.Info.Source == ServerSourceOperatingSystem {
|
||||||
systemResolvers = append(systemResolvers, resolver)
|
systemResolvers = append(systemResolvers, resolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,8 @@ type RRCache struct {
|
||||||
Extra []dns.RR
|
Extra []dns.RR
|
||||||
Expires int64
|
Expires int64
|
||||||
|
|
||||||
// Source Information
|
// Resolver Information
|
||||||
Server string
|
Resolver *ResolverInfo
|
||||||
ServerScope int8
|
|
||||||
ServerInfo string
|
|
||||||
|
|
||||||
// Metadata about the request and handling
|
// Metadata about the request and handling
|
||||||
ServedFromCache bool
|
ServedFromCache bool
|
||||||
|
@ -133,13 +131,11 @@ func (rrCache *RRCache) ExportAllARecords() (ips []net.IP) {
|
||||||
// ToNameRecord converts the RRCache to a NameRecord for cleaner persistence.
|
// ToNameRecord converts the RRCache to a NameRecord for cleaner persistence.
|
||||||
func (rrCache *RRCache) ToNameRecord() *NameRecord {
|
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,
|
RCode: rrCache.RCode,
|
||||||
Expires: rrCache.Expires,
|
Expires: rrCache.Expires,
|
||||||
Server: rrCache.Server,
|
Resolver: rrCache.Resolver,
|
||||||
ServerScope: rrCache.ServerScope,
|
|
||||||
ServerInfo: rrCache.ServerInfo,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// stringify RR entries
|
// stringify RR entries
|
||||||
|
@ -204,9 +200,7 @@ func GetRRCache(domain string, question dns.Type) (*RRCache, error) {
|
||||||
rrCache.Extra = parseRR(rrCache.Extra, entry)
|
rrCache.Extra = parseRR(rrCache.Extra, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
rrCache.Server = nameRecord.Server
|
rrCache.Resolver = nameRecord.Resolver
|
||||||
rrCache.ServerScope = nameRecord.ServerScope
|
|
||||||
rrCache.ServerInfo = nameRecord.ServerInfo
|
|
||||||
rrCache.ServedFromCache = true
|
rrCache.ServedFromCache = true
|
||||||
rrCache.Modified = nameRecord.Meta().Modified
|
rrCache.Modified = nameRecord.Meta().Modified
|
||||||
return rrCache, nil
|
return rrCache, nil
|
||||||
|
@ -259,9 +253,7 @@ func (rrCache *RRCache) ShallowCopy() *RRCache {
|
||||||
Extra: rrCache.Extra,
|
Extra: rrCache.Extra,
|
||||||
Expires: rrCache.Expires,
|
Expires: rrCache.Expires,
|
||||||
|
|
||||||
Server: rrCache.Server,
|
Resolver: rrCache.Resolver,
|
||||||
ServerScope: rrCache.ServerScope,
|
|
||||||
ServerInfo: rrCache.ServerInfo,
|
|
||||||
|
|
||||||
ServedFromCache: rrCache.ServedFromCache,
|
ServedFromCache: rrCache.ServedFromCache,
|
||||||
RequestingNew: rrCache.RequestingNew,
|
RequestingNew: rrCache.RequestingNew,
|
||||||
|
@ -302,9 +294,9 @@ func (rrCache *RRCache) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns
|
||||||
func (rrCache *RRCache) GetExtraRRs(ctx context.Context, query *dns.Msg) (extra []dns.RR) {
|
func (rrCache *RRCache) GetExtraRRs(ctx context.Context, query *dns.Msg) (extra []dns.RR) {
|
||||||
// Add cache status and source of data.
|
// Add cache status and source of data.
|
||||||
if rrCache.ServedFromCache {
|
if rrCache.ServedFromCache {
|
||||||
extra = addExtra(ctx, extra, "served from cache, resolved by "+rrCache.ServerInfo)
|
extra = addExtra(ctx, extra, "served from cache, resolved by "+rrCache.Resolver.DescriptiveName())
|
||||||
} else {
|
} else {
|
||||||
extra = addExtra(ctx, extra, "freshly resolved by "+rrCache.ServerInfo)
|
extra = addExtra(ctx, extra, "freshly resolved by "+rrCache.Resolver.DescriptiveName())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add expiry and cache information.
|
// Add expiry and cache information.
|
||||||
|
|
|
@ -158,13 +158,13 @@ addNextResolver:
|
||||||
for _, resolver := range addResolvers {
|
for _, resolver := range addResolvers {
|
||||||
// check for compliance
|
// check for compliance
|
||||||
if err := resolver.checkCompliance(ctx, q); err != nil {
|
if err := resolver.checkCompliance(ctx, q); err != nil {
|
||||||
log.Tracer(ctx).Tracef("skipping non-compliant resolver %s: %s", resolver.GetName(), err)
|
log.Tracer(ctx).Tracef("skipping non-compliant resolver %s: %s", resolver.Info.DescriptiveName(), err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// deduplicate
|
// deduplicate
|
||||||
for _, selectedResolver := range selected {
|
for _, selectedResolver := range selected {
|
||||||
if selectedResolver.Server == resolver.Server {
|
if selectedResolver.Info.ID() == resolver.Info.ID() {
|
||||||
continue addNextResolver
|
continue addNextResolver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,7 @@ func (q *Query) checkCompliance() error {
|
||||||
|
|
||||||
func (resolver *Resolver) checkCompliance(_ context.Context, q *Query) error {
|
func (resolver *Resolver) checkCompliance(_ context.Context, q *Query) error {
|
||||||
if noInsecureProtocols(q.SecurityLevel) {
|
if noInsecureProtocols(q.SecurityLevel) {
|
||||||
switch resolver.ServerType {
|
switch resolver.Info.Type {
|
||||||
case ServerTypeDNS:
|
case ServerTypeDNS:
|
||||||
return errInsecureProtocol
|
return errInsecureProtocol
|
||||||
case ServerTypeTCP:
|
case ServerTypeTCP:
|
||||||
|
@ -218,20 +218,20 @@ func (resolver *Resolver) checkCompliance(_ context.Context, q *Query) error {
|
||||||
case ServerTypeDoH:
|
case ServerTypeDoH:
|
||||||
// compliant
|
// compliant
|
||||||
case ServerTypeEnv:
|
case ServerTypeEnv:
|
||||||
// compliant (data is sources from local network only and is highly limited)
|
// compliant (data is sourced from local network only and is highly limited)
|
||||||
default:
|
default:
|
||||||
return errInsecureProtocol
|
return errInsecureProtocol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if noAssignedNameservers(q.SecurityLevel) {
|
if noAssignedNameservers(q.SecurityLevel) {
|
||||||
if resolver.Source == ServerSourceOperatingSystem {
|
if resolver.Info.Source == ServerSourceOperatingSystem {
|
||||||
return errAssignedServer
|
return errAssignedServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if noMulticastDNS(q.SecurityLevel) {
|
if noMulticastDNS(q.SecurityLevel) {
|
||||||
if resolver.Source == ServerSourceMDNS {
|
if resolver.Info.Source == ServerSourceMDNS {
|
||||||
return errMulticastDNS
|
return errMulticastDNS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue