Skip to content

Commit

Permalink
api: (BREAKING) remove question cache (#81)
Browse files Browse the repository at this point in the history
This removes a feature inherited from grimd, which keeps
track of historic dns queries and exposed them in the API.

This is not to be confised with the response cache (which caches DNS
queries' responses).

The question 'cache' keeps only DNS questions and is not functional to
answering DNS queries.

Since inheriting this feature, leng now has metrics which achieve
similar functionality - only it exposes it via a different API
(prometheus).

Thes endpoints are thus now deprecated.
  • Loading branch information
cottand authored Dec 16, 2024
1 parent 6f48473 commit b5b2ddb
Show file tree
Hide file tree
Showing 8 changed files with 26 additions and 158 deletions.
47 changes: 7 additions & 40 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"

Expand All @@ -30,10 +29,7 @@ func isRunningInDockerContainer() bool {
}

// StartAPIServer starts the API server
func StartAPIServer(config *Config,
reloadChan chan bool,
blockCache *MemoryBlockCache,
questionCache *MemoryQuestionCache) (*http.Server, error) {
func StartAPIServer(config *Config, reloadChan chan bool, blockCache *MemoryBlockCache) (*http.Server, error) {
if !config.APIDebug {
gin.SetMode(gin.ReleaseMode)
}
Expand Down Expand Up @@ -167,53 +163,24 @@ func StartAPIServer(config *Config,
})

router.GET("/questioncache", func(c *gin.Context) {
highWater, err := strconv.ParseInt(c.DefaultQuery("highWater", "-1"), 10, 64)
if err != nil {
highWater = -1
}
c.IndentedJSON(http.StatusOK, gin.H{
"length": questionCache.Length(),
"items": questionCache.GetOlder(highWater),
})
c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "invalid request - endpoint has been deprecated"})
})

router.GET("/questioncache/length", func(c *gin.Context) {
c.IndentedJSON(http.StatusOK, gin.H{"length": questionCache.Length()})
c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "invalid request - endpoint has been deprecated"})
})

router.GET("/questioncache/clear", func(c *gin.Context) {
questionCache.Clear()
c.IndentedJSON(http.StatusOK, gin.H{"success": true})
c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "invalid request - endpoint has been deprecated"})
})

router.GET("/questioncache/client/:client", func(c *gin.Context) {
var filteredCache []QuestionCacheEntry

questionCache.mu.RLock()
for _, entry := range questionCache.Backend {
if entry.Remote == c.Param("client") {
filteredCache = append(filteredCache, entry)
}
}
questionCache.mu.RUnlock()

c.IndentedJSON(http.StatusOK, filteredCache)
c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "invalid request - endpoint has been deprecated"})
})

router.GET("/questioncache/client", func(c *gin.Context) {
clientList := make(map[string]bool)
questionCache.mu.RLock()
for _, entry := range questionCache.Backend {
if _, ok := clientList[entry.Remote]; !ok {
clientList[entry.Remote] = true
}
}
questionCache.mu.RUnlock()
var clients []string
for client := range clientList {
clients = append(clients, client)
}
c.IndentedJSON(http.StatusOK, clients)
c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "invalid request - endpoint has been deprecated"})

})

router.OPTIONS("/application/active", func(c *gin.Context) {
Expand Down
43 changes: 0 additions & 43 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,6 @@ type MemoryBlockCache struct {
mu sync.RWMutex
}

// MemoryQuestionCache type
type MemoryQuestionCache struct {
Backend []QuestionCacheEntry `json:"entry"`
Maxcount int
mu sync.RWMutex
}

// Get returns the entry for a key or an error
func (c *MemoryCache) Get(key string) (*dns.Msg, bool, error) {
key = strings.ToLower(key)
Expand Down Expand Up @@ -278,39 +271,3 @@ func (c *MemoryBlockCache) Length() int {
defer c.mu.RUnlock()
return len(c.Backend)
}

// Add adds a question to the cache
func (c *MemoryQuestionCache) Add(q QuestionCacheEntry) {
c.mu.Lock()
if c.Maxcount != 0 && len(c.Backend) >= c.Maxcount {
c.Backend = nil
}
c.Backend = append(c.Backend, q)
c.mu.Unlock()
}

// Clear clears the contents of the cache
func (c *MemoryQuestionCache) Clear() {
c.mu.Lock()
c.Backend = make([]QuestionCacheEntry, 0, 0)
c.mu.Unlock()
}

// Length returns the caches length
func (c *MemoryQuestionCache) Length() int {
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.Backend)
}

// GetOlder eturns a slice of the entries older than `time`
func (c *MemoryQuestionCache) GetOlder(time int64) []QuestionCacheEntry {
c.mu.RLock()
defer c.mu.RUnlock()
for i, e := range c.Backend {
if e.Date > time {
return c.Backend[i:]
}
}
return []QuestionCacheEntry{}
}
25 changes: 0 additions & 25 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,31 +296,6 @@ func TestExpirationRace(t *testing.T) {
}
*/

func addToCache(cache *MemoryQuestionCache, time int64) {
cache.Add(QuestionCacheEntry{
Date: time,
Remote: fmt.Sprintf("%d", time),
Blocked: true,
Query: Question{},
})
}

func TestQuestionCacheGetFromTimestamp(t *testing.T) {
memCache := makeQuestionCache(100)
for i := 0; i < 100; i++ {
addToCache(memCache, int64(i))
}

entries := memCache.GetOlder(50)
assert.Len(t, entries, 49)
entries = memCache.GetOlder(0)
assert.Len(t, entries, 99)
entries = memCache.GetOlder(-1)
assert.Len(t, entries, 100)
entries = memCache.GetOlder(200)
assert.Len(t, entries, 0)
}

func BenchmarkSetCache(b *testing.B) {
cache := makeCache()

Expand Down
10 changes: 3 additions & 7 deletions grimd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,11 @@ func integrationTest(changeConfig func(c *Config), test func(client *dns.Client,
for _, blocked := range config.Blocking.Blocklist {
_ = blockCache.Set(blocked, true)
}
// QuestionCache contains all queries to the dns server
questionCache := makeQuestionCache(config.QuestionCacheCap)

reloadChan := make(chan bool)
_, _ = StartAPIServer(&config, reloadChan, blockCache, questionCache)
_, _ = StartAPIServer(&config, reloadChan, blockCache)
defer close(reloadChan)
server.Run(&config, blockCache, questionCache)
server.Run(&config, blockCache)

time.Sleep(200 * time.Millisecond)
defer server.Stop()
Expand Down Expand Up @@ -358,10 +356,8 @@ func TestConfigReloadForCustomRecords(t *testing.T) {

// BlockCache contains all blocked domains
blockCache := &MemoryBlockCache{Backend: make(map[string]bool)}
// QuestionCache contains all queries to the dns server
questionCache := makeQuestionCache(config.QuestionCacheCap)

server.Run(&config, blockCache, questionCache)
server.Run(&config, blockCache)

time.Sleep(200 * time.Millisecond)
defer server.Stop()
Expand Down
28 changes: 8 additions & 20 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ package main

import (
"github.com/cottand/leng/internal/metric"
"github.com/miekg/dns"
"net"
"slices"
"sync"
"time"

"github.com/miekg/dns"
)

const (
Expand Down Expand Up @@ -42,13 +40,12 @@ type EventLoop struct {
resolver *Resolver
cache Cache
// negCache caches failures
negCache Cache
active bool
muActive sync.RWMutex
config *Config
blockCache *MemoryBlockCache
questionCache *MemoryQuestionCache
customDns *CustomRecordsResolver
negCache Cache
active bool
muActive sync.RWMutex
config *Config
blockCache *MemoryBlockCache
customDns *CustomRecordsResolver
}

// DNSOperationData type
Expand All @@ -59,7 +56,7 @@ type DNSOperationData struct {
}

// NewEventLoop returns a new eventLoop
func NewEventLoop(config *Config, blockCache *MemoryBlockCache, questionCache *MemoryQuestionCache) *EventLoop {
func NewEventLoop(config *Config, blockCache *MemoryBlockCache) *EventLoop {
var (
clientConfig *dns.ClientConfig
resolver *Resolver
Expand All @@ -84,7 +81,6 @@ func NewEventLoop(config *Config, blockCache *MemoryBlockCache, questionCache *M
cache: cache,
negCache: negCache,
blockCache: blockCache,
questionCache: questionCache,
active: true,
config: config,
customDns: NewCustomRecordsResolver(NewCustomDNSRecordsFromText(config.CustomDNSRecords)),
Expand Down Expand Up @@ -194,10 +190,6 @@ func (h *EventLoop) responseFor(Net string, req *dns.Msg, _local net.Addr, _remo

logger.Noticef("%s found in blocklist\n", Q.Qname)

// log query
NewEntry := QuestionCacheEntry{Date: time.Now().Unix(), Remote: remote.String(), Query: Q, Blocked: true}
go h.questionCache.Add(NewEntry)

// cache the block; we don't know the true TTL for blocked entries: we just enforce our config
err := h.cache.Set(key, m, true)
if err != nil {
Expand All @@ -209,10 +201,6 @@ func (h *EventLoop) responseFor(Net string, req *dns.Msg, _local net.Addr, _remo
logger.Debugf("%s not found in blocklist\n", Q.Qname)
}

// log query
NewEntry := QuestionCacheEntry{Date: time.Now().Unix(), Remote: remote.String(), Query: Q, Blocked: false}
go h.questionCache.Add(NewEntry)

mesg, err := h.resolver.Lookup(Net, req, h.config.Timeout, h.config.Interval, h.config.Upstream.Nameservers, h.config.Upstream.DoH)

if err != nil {
Expand Down
19 changes: 6 additions & 13 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,7 @@ var (
lengActive bool
)

func reloadBlockCache(config *Config,
blockCache *MemoryBlockCache,
questionCache *MemoryQuestionCache,
apiServer *http.Server,
server *Server,
reloadChan chan bool) (*MemoryBlockCache, *http.Server, error) {
func reloadBlockCache(config *Config, blockCache *MemoryBlockCache, apiServer *http.Server, server *Server, reloadChan chan bool) (*MemoryBlockCache, *http.Server, error) {

logger.Debug("Reloading the blockcache")
blockCache = PerformUpdate(config, true)
Expand All @@ -33,8 +28,8 @@ func reloadBlockCache(config *Config,
logger.Debugf("error shutting down api server: %v", err)
}
}
server.Run(config, blockCache, questionCache)
apiServer, err := StartAPIServer(config, reloadChan, blockCache, questionCache)
server.Run(config, blockCache)
apiServer, err := StartAPIServer(config, reloadChan, blockCache)
if err != nil {
logger.Fatal(err)
return nil, nil, err
Expand Down Expand Up @@ -81,18 +76,16 @@ func main() {

// BlockCache contains all blocked domains
blockCache := &MemoryBlockCache{Backend: make(map[string]bool)}
// QuestionCache contains all queries to the dns server
questionCache := makeQuestionCache(config.QuestionCacheCap)

reloadChan := make(chan bool)

// The server will start with an empty blockcache soe we can dowload the lists if leng is the
// system's dns server.
server.Run(config, blockCache, questionCache)
server.Run(config, blockCache)

var apiServer *http.Server
// Load the block cache, restart the server with the new context
blockCache, apiServer, err = reloadBlockCache(config, blockCache, questionCache, apiServer, server, reloadChan)
blockCache, apiServer, err = reloadBlockCache(config, blockCache, apiServer, server, reloadChan)

if err != nil {
logger.Fatalf("Cannot start the API server %s", err)
Expand Down Expand Up @@ -122,7 +115,7 @@ forever:
reloadConfigFromFile(server)
}
case <-reloadChan:
blockCache, apiServer, err = reloadBlockCache(config, blockCache, questionCache, apiServer, server, reloadChan)
blockCache, apiServer, err = reloadBlockCache(config, blockCache, apiServer, server, reloadChan)
if err != nil {
logger.Fatalf("Cannot start the API server %s", err)
}
Expand Down
8 changes: 2 additions & 6 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,9 @@ type Server struct {
}

// Run starts the server
func (s *Server) Run(
config *Config,
blockCache *MemoryBlockCache,
questionCache *MemoryQuestionCache,
) {
func (s *Server) Run(config *Config, blockCache *MemoryBlockCache) {

s.eventLoop = NewEventLoop(config, blockCache, questionCache)
s.eventLoop = NewEventLoop(config, blockCache)

tcpHandler := dns.NewServeMux()
tcpHandler.HandleFunc(".", s.eventLoop.DoTCP)
Expand Down
4 changes: 0 additions & 4 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ func makeCache() MemoryCache {
}
}

func makeQuestionCache(maxCount int) *MemoryQuestionCache {
return &MemoryQuestionCache{Backend: make([]QuestionCacheEntry, 0), Maxcount: maxCount}
}

// Difference between to lists: A - B
func difference[T comparable](a, b []T) (diff []T) {
m := make(map[T]bool)
Expand Down

0 comments on commit b5b2ddb

Please sign in to comment.