5d54aa9c5f
Now that we have properly refactored the caching resolvers we can move them into netxlite as optional resolvers created using the proper abstract factories we just added. This diff reduces the complexity and the code size of netx. See https://github.com/ooni/probe/issues/2121.
120 lines
3.2 KiB
Go
120 lines
3.2 KiB
Go
package netxlite
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"sync"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
|
)
|
|
|
|
// MaybeWrapWithCachingResolver wraps the provided resolver with a resolver
|
|
// that remembers the result of previous successful resolutions, if the enabled
|
|
// argument is true. Otherwise, we return the unmodified provided resolver.
|
|
//
|
|
// Bug: the returned resolver only applies caching to LookupHost and any other
|
|
// lookup operation returns ErrNoDNSTransport to the caller.
|
|
func MaybeWrapWithCachingResolver(enabled bool, reso model.Resolver) model.Resolver {
|
|
if enabled {
|
|
reso = &cacheResolver{
|
|
cache: map[string][]string{},
|
|
mu: sync.Mutex{},
|
|
readOnly: false,
|
|
resolver: reso,
|
|
}
|
|
}
|
|
return reso
|
|
}
|
|
|
|
// MaybeWrapWithStaticDNSCache wraps the provided resolver with a resolver that
|
|
// checks the given cache before issuing queries to the underlying DNS resolver.
|
|
//
|
|
// Bug: the returned resolver only applies caching to LookupHost and any other
|
|
// lookup operation returns ErrNoDNSTransport to the caller.
|
|
func MaybeWrapWithStaticDNSCache(cache map[string][]string, reso model.Resolver) model.Resolver {
|
|
if len(cache) > 0 {
|
|
reso = &cacheResolver{
|
|
cache: cache,
|
|
mu: sync.Mutex{},
|
|
readOnly: true,
|
|
resolver: reso,
|
|
}
|
|
}
|
|
return reso
|
|
}
|
|
|
|
// cacheResolver implements CachingResolver and StaticDNSCache.
|
|
type cacheResolver struct {
|
|
// cache is the underlying DNS cache.
|
|
cache map[string][]string
|
|
|
|
// mu provides mutual exclusion.
|
|
mu sync.Mutex
|
|
|
|
// readOnly means that we won't cache the result of successful resolutions.
|
|
readOnly bool
|
|
|
|
// resolver is the underlying resolver.
|
|
resolver model.Resolver
|
|
}
|
|
|
|
var _ model.Resolver = &cacheResolver{}
|
|
|
|
// LookupHost implements model.Resolver.LookupHost
|
|
func (r *cacheResolver) LookupHost(
|
|
ctx context.Context, hostname string) ([]string, error) {
|
|
if entry := r.get(hostname); entry != nil {
|
|
return entry, nil
|
|
}
|
|
entry, err := r.resolver.LookupHost(ctx, hostname)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !r.readOnly {
|
|
r.set(hostname, entry)
|
|
}
|
|
return entry, nil
|
|
}
|
|
|
|
// get gets the currently configured entry for domain, or nil
|
|
func (r *cacheResolver) get(domain string) []string {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
return r.cache[domain]
|
|
}
|
|
|
|
// set sets a valid inside the cache iff readOnly is false.
|
|
func (r *cacheResolver) set(domain string, addresses []string) {
|
|
r.mu.Lock()
|
|
if r.cache == nil {
|
|
r.cache = make(map[string][]string)
|
|
}
|
|
r.cache[domain] = addresses
|
|
r.mu.Unlock()
|
|
}
|
|
|
|
// Address implements model.Resolver.Address.
|
|
func (r *cacheResolver) Address() string {
|
|
return r.resolver.Address()
|
|
}
|
|
|
|
// Network implements model.Resolver.Network.
|
|
func (r *cacheResolver) Network() string {
|
|
return r.resolver.Network()
|
|
}
|
|
|
|
// CloseIdleConnections implements model.Resolver.CloseIdleConnections.
|
|
func (r *cacheResolver) CloseIdleConnections() {
|
|
r.resolver.CloseIdleConnections()
|
|
}
|
|
|
|
// LookupHTTPS implements model.Resolver.LookupHTTPS.
|
|
func (r *cacheResolver) LookupHTTPS(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
|
return nil, ErrNoDNSTransport
|
|
}
|
|
|
|
// LookupNS implements model.Resolver.LookupNS.
|
|
func (r *cacheResolver) LookupNS(ctx context.Context, domain string) ([]*net.NS, error) {
|
|
return nil, ErrNoDNSTransport
|
|
}
|