Resolve PTR names over DNS-over-HTTPS and cache them for the record TTL (closes #238)
check / check (push) Failing after 1m22s
check / check (push) Failing after 1m22s
`internal/ptr` asks Google, Cloudflare, AdGuard and Quad9 in turn, 2 seconds in all per address. It keeps answers in memory and writes them through to `cache.db`, opened by the new `internal/cachedb`. Judgement calls: - Each resolver gets an equal share of the time left, so a hung resolver does not use up the 2 seconds. - The negative TTL is the smaller of the SOA minimum and the SOA's own TTL (RFC 2308). - Names that are not host names are dropped, as `net.Resolver.LookupAddr` does. Model: opus-5-5
This commit is contained in:
1 parent
cf50d195ad
commit
018372363f
18 files changed
+1608
-53
No files matched your search
@@ -24,6 +24,7 @@ import (
|
||||
"git.eeqj.de/sneak/routewatch/internal/iplist"
|
||||
"git.eeqj.de/sneak/routewatch/internal/logger"
|
||||
"git.eeqj.de/sneak/routewatch/internal/metrics"
|
||||
"git.eeqj.de/sneak/routewatch/internal/ptr"
|
||||
"git.eeqj.de/sneak/routewatch/internal/ristypes"
|
||||
"git.eeqj.de/sneak/routewatch/internal/routetable"
|
||||
"git.eeqj.de/sneak/routewatch/internal/streamer"
|
||||
@@ -523,8 +524,11 @@ func newServerWithTable(t *testing.T, table *routetable.Table) *Server {
|
||||
|
||||
t.Cleanup(func() { _ = db.Close() })
|
||||
|
||||
return New(db, table, streamer.New(logger.New(), metrics.New()), logger.New(),
|
||||
s := New(db, table, streamer.New(logger.New(), metrics.New()), logger.New(),
|
||||
&config.Config{})
|
||||
useFakeResolver(s, &fakeResolver{})
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// TestPrefixDetailHandlersParsePrefix checks that both prefix pages find a
|
||||
@@ -1213,7 +1217,7 @@ func TestIPLookupAnswers500WhenDatabaseReadFails(t *testing.T) {
|
||||
|
||||
s := New(failingIPLookupDB{}, newTestTable(),
|
||||
streamer.New(logger.New(), metrics.New()), logger.New(), &config.Config{})
|
||||
s.resolver = &fakeResolver{}
|
||||
useFakeResolver(s, &fakeResolver{})
|
||||
|
||||
tests := []struct {
|
||||
path string
|
||||
@@ -1255,8 +1259,8 @@ var errNoSuchHost = errors.New("no such host")
|
||||
|
||||
// fakeResolver answers a host name lookup with the addresses hosts lists for
|
||||
// the name, and a PTR lookup with no names or, when ptrNeverAnswers is set,
|
||||
// not at all: it waits until the lookup's context ends. hostLookups counts the
|
||||
// host name lookups.
|
||||
// with a failure after ptr.LookupTimeout, as the real one does when no
|
||||
// resolver answers. hostLookups counts the host name lookups.
|
||||
type fakeResolver struct {
|
||||
hosts map[string][]string
|
||||
ptrNeverAnswers bool
|
||||
@@ -1277,6 +1281,9 @@ func (r *fakeResolver) LookupHost(_ context.Context, host string) ([]string, err
|
||||
|
||||
func (r *fakeResolver) LookupAddr(ctx context.Context, _ string) ([]string, error) {
|
||||
if r.ptrNeverAnswers {
|
||||
ctx, cancel := context.WithTimeout(ctx, ptr.LookupTimeout)
|
||||
defer cancel()
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
return nil, ctx.Err()
|
||||
@@ -1285,6 +1292,12 @@ func (r *fakeResolver) LookupAddr(ctx context.Context, _ string) ([]string, erro
|
||||
return nil, errNoSuchHost
|
||||
}
|
||||
|
||||
// useFakeResolver makes r answer the server's host name and PTR lookups.
|
||||
func useFakeResolver(s *Server, r *fakeResolver) {
|
||||
s.resolver = r
|
||||
s.ptrResolver = r
|
||||
}
|
||||
|
||||
// countingIPLookupDB embeds database.Store (left nil) and answers the IP
|
||||
// lookup's reads with nothing stored. It counts the lookups, as the reads of a
|
||||
// prefix's first-seen time. Every other Store method is unused and would panic
|
||||
@@ -1353,7 +1366,7 @@ func TestIPLookupRefusesTooManyAddresses(t *testing.T) {
|
||||
s := New(db, newTestTable(), streamer.New(logger.New(), metrics.New()),
|
||||
logger.New(), &config.Config{})
|
||||
resolver := &fakeResolver{hosts: hosts}
|
||||
s.resolver = resolver
|
||||
useFakeResolver(s, resolver)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
s.router.ServeHTTP(rec, httptest.NewRequestWithContext(
|
||||
@@ -1395,10 +1408,10 @@ func TestIPLookupKeepsRouteWhenPTRLookupDoesNotAnswer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := newServerWithTestRoutes(t)
|
||||
s.resolver = &fakeResolver{ptrNeverAnswers: true}
|
||||
useFakeResolver(s, &fakeResolver{ptrNeverAnswers: true})
|
||||
|
||||
// The request's time limit passes after the PTR lookup's.
|
||||
ctx, cancel := context.WithTimeout(t.Context(), 2*ptrLookupTimeout)
|
||||
ctx, cancel := context.WithTimeout(t.Context(), 2*ptr.LookupTimeout)
|
||||
defer cancel()
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
@@ -1476,7 +1489,7 @@ func TestIPLookupFlags(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := newServerWithTestRoutes(t)
|
||||
s.resolver = &fakeResolver{}
|
||||
useFakeResolver(s, &fakeResolver{})
|
||||
s.SetIPLists(fakeIPLists{})
|
||||
|
||||
tests := []struct {
|
||||
@@ -1532,7 +1545,7 @@ func TestIPLookupReportsIPLists(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := newServerWithTestRoutes(t)
|
||||
s.resolver = &fakeResolver{}
|
||||
useFakeResolver(s, &fakeResolver{})
|
||||
s.SetIPLists(fakeIPLists{})
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
@@ -1609,7 +1622,7 @@ func TestIPLookupReportsIPListsOfAnAddressWithNoRoute(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := newServerWithTestRoutes(t)
|
||||
s.resolver = &fakeResolver{}
|
||||
useFakeResolver(s, &fakeResolver{})
|
||||
s.SetIPLists(fakeIPLists{})
|
||||
|
||||
// routeFields are the fields of a result that come from the address's
|
||||
@@ -1687,9 +1700,9 @@ func TestIPLookupResultsHaveTheSameFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := newServerWithTestRoutes(t)
|
||||
s.resolver = &fakeResolver{hosts: map[string][]string{
|
||||
useFakeResolver(s, &fakeResolver{hosts: map[string][]string{
|
||||
twoAddressHostName: {addrInTestPrefixV4, listedUnroutedAddr},
|
||||
}}
|
||||
}})
|
||||
s.SetIPLists(fakeIPLists{})
|
||||
|
||||
// want is the fields of the first result.
|
||||
@@ -1722,7 +1735,7 @@ func TestIPLookupOfAnAddressWithoutPTRNamesOrStoredNetblock(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := newServerWithTestRoutes(t)
|
||||
s.resolver = &fakeResolver{}
|
||||
useFakeResolver(s, &fakeResolver{})
|
||||
|
||||
path := "/api/v1/ip/" + addrInTestPrefixV4
|
||||
|
||||
@@ -1752,7 +1765,7 @@ func TestIPLookupAnswersMayBeCached(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := newServerWithTestRoutes(t)
|
||||
s.resolver = &fakeResolver{}
|
||||
useFakeResolver(s, &fakeResolver{})
|
||||
s.SetIPLists(fakeIPLists{})
|
||||
|
||||
tests := []struct {
|
||||
@@ -1803,10 +1816,10 @@ func TestIPLookupHidesPrivateAddresses(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := newServerWithTestRoutes(t)
|
||||
s.resolver = &fakeResolver{hosts: map[string][]string{
|
||||
useFakeResolver(s, &fakeResolver{hosts: map[string][]string{
|
||||
internalHostName: {internalHostAddr},
|
||||
"mixed.test": {internalHostAddr, addrInTestPrefixV4},
|
||||
}}
|
||||
}})
|
||||
|
||||
notPublic := internalHostName + ": " + hostNotPublicMessage
|
||||
|
||||
|
||||
Reference in new issue
Block a user