From b7abcd226b7d0b0a2c884db541d1463f8bc0ea29 Mon Sep 17 00:00:00 2001 From: Cottand Date: Tue, 14 Nov 2023 15:32:55 +0000 Subject: [PATCH 1/4] remove query-based activation --- activation.go | 40 +++++----------------------------- config.go | 18 ++++++--------- doc/src/Configuration.md | 47 +++++++++++++++++++++------------------- grimd_test.go | 4 ++-- handler.go | 15 ++----------- main.go | 2 +- 6 files changed, 42 insertions(+), 84 deletions(-) diff --git a/activation.go b/activation.go index 5539bb8..8aeb60e 100644 --- a/activation.go +++ b/activation.go @@ -1,9 +1,5 @@ package main -import ( - "time" -) - // ToggleData type type ToggleData struct { Mode uint @@ -17,9 +13,7 @@ type ActivationHandler struct { setChannel chan bool } -func startActivation(actChannel chan *ActivationHandler, quit chan bool, reactivationDelay uint) { - var reactivate time.Time - var reactivatePending bool +func startActivation(actChannel chan *ActivationHandler, quit chan bool) { a := &ActivationHandler{} a.queryChannel = make(chan bool) @@ -30,10 +24,6 @@ func startActivation(actChannel chan *ActivationHandler, quit chan bool, reactiv // then continue to the loop actChannel <- a - ticker := time.Tick(1 * time.Second) - - var nextToggleTime = time.Now() - forever: for { select { @@ -42,35 +32,15 @@ forever: case <-a.queryChannel: a.queryChannel <- lengActive case v := <-a.toggleChannel: - // Firefox is sending 2 queries in a row, so debouncing is needed. - if v.Mode == 1 && nextToggleTime.After(time.Now()) { - logger.Warning("Toggle is too close: wait 10 seconds\n") + if v.Mode == 1 { + lengActive = !lengActive } else { - if v.Mode == 1 { - lengActive = !lengActive - } else { - lengActive = false - } - nextToggleTime = time.Now().Add(time.Duration(10) * time.Second) - if !lengActive && reactivationDelay > 0 { - reactivate = time.Now().Add(time.Duration(v.Data) * time.Second) - reactivatePending = true - } else { - reactivatePending = false - } - a.queryChannel <- lengActive + lengActive = false } + a.queryChannel <- lengActive case v := <-a.setChannel: lengActive = v - reactivatePending = false a.setChannel <- lengActive - case <-ticker: - now := time.Now() - if reactivatePending && now.After(reactivate) { - logger.Notice("Reactivating leng (timer)") - lengActive = true - reactivatePending = false - } } } logger.Debugf("Activation goroutine exiting") diff --git a/config.go b/config.go index 47af460..28d8c89 100644 --- a/config.go +++ b/config.go @@ -30,7 +30,6 @@ type Config struct { NXDomain bool Nullroute string Nullroutev6 string - Nameservers []string Interval int Timeout int Expire uint32 @@ -40,15 +39,19 @@ type Config struct { Blocklist []string Whitelist []string CustomDNSRecords []string - ToggleName string - ReactivationDelay uint APIDebug bool - DoH string + Upstream Upstream Metrics Metrics `toml:"metrics"` DnsOverHttpServer DnsOverHttpServer FollowCnameDepth uint32 } +type Upstream struct { + DoH string + Nameservers []string + TimeoutS int `toml:"timeout_s"` +} + type Metrics struct { Enabled bool Path string @@ -153,13 +156,6 @@ customdnsrecords = [ # "example.other.tld IN CNAME wikipedia.org" ] -# When this string is queried, toggle leng on and off -togglename = "" - -# If not zero, the delay in seconds before leng automaticall reactivates after -# having been turned off. -reactivationdelay = 300 - # Dns over HTTPS upstream provider to use DoH = "https://cloudflare-dns.com/dns-query" diff --git a/doc/src/Configuration.md b/doc/src/Configuration.md index 21c19ab..2b68067 100644 --- a/doc/src/Configuration.md +++ b/doc/src/Configuration.md @@ -45,15 +45,9 @@ nullroute = "0.0.0.0" # ipv6 address to forward blocked queries to nullroutev6 = "0:0:0:0:0:0:0:0" -# nameservers to forward queries to -nameservers = ["1.1.1.1:53", "1.0.0.1:53"] - # concurrency interval for lookups in miliseconds interval = 200 -# query timeout for dns lookups in seconds -timeout = 5 - # cache entry lifespan in seconds expire = 600 @@ -66,13 +60,6 @@ questioncachecap = 5000 # manual blocklist entries blocklist = [] -# Drbl related settings -usedrbl = 0 -drblpeersfilename = "drblpeers.yaml" -drblblockweight = 128 -drbltimeout = 30 -drbldebug = 0 - # manual whitelist entries - comments for reference whitelist = [ # "getsentry.com", @@ -85,20 +72,36 @@ customdnsrecords = [ # "example.other.tld IN CNAME wikipedia.org" ] -# When this string is queried, toggle leng on and off -togglename = "" -# If not zero, the delay in seconds before leng automaticall reactivates after -# having been turned off. -reactivationdelay = 300 +[Upstream] + #Dns over HTTPS provider to use. + DoH = "https://cloudflare-dns.com/dns-query" + + # nameservers to forward queries to + nameservers = ["1.1.1.1:53", "1.0.0.1:53"] + + # query timeout for dns lookups in seconds + timeout_s = 5 + -#Dns over HTTPS provider to use. -DoH = "https://cloudflare-dns.com/dns-query" # Prometheus metrics - enable [Metrics] - enabled = false - path = "/metrics" + enabled = false + path = "/metrics" + +[DnsOverHttpServer] + enabled = false + bind = "0.0.0.0:80" + timeoutMs = 5000 + +# TLS config is not required for DoH if you have some proxy (ie, caddy, nginx, traefik...) manage HTTPS for you + [DnsOverHttpServer.TLS] + enabled = false + certPath = "" + keyPath = "" + # if empty, system CAs will be used + caPath = "" ``` The most up-to-date version can be found on [config.go](https://github.com/Cottand/leng/blob/master/config.go) \ No newline at end of file diff --git a/grimd_test.go b/grimd_test.go index faedf48..1a8ef94 100644 --- a/grimd_test.go +++ b/grimd_test.go @@ -39,7 +39,7 @@ func integrationTest(changeConfig func(c *Config), test func(client *dns.Client, quitActivation := make(chan bool) actChannel := make(chan *ActivationHandler) - go startActivation(actChannel, quitActivation, config.ReactivationDelay) + go startActivation(actChannel, quitActivation) lengActivation = <-actChannel lengActive = true close(actChannel) @@ -289,7 +289,7 @@ func TestConfigReloadForCustomRecords(t *testing.T) { quitActivation := make(chan bool) actChannel := make(chan *ActivationHandler) - go startActivation(actChannel, quitActivation, config.ReactivationDelay) + go startActivation(actChannel, quitActivation) lengActivation = <-actChannel close(actChannel) diff --git a/handler.go b/handler.go index 0b2ba8a..dc4d7f2 100644 --- a/handler.go +++ b/handler.go @@ -4,7 +4,6 @@ import ( "github.com/cottand/leng/internal/metric" "net" "slices" - "strings" "sync" "time" @@ -125,16 +124,6 @@ func (h *EventLoop) responseFor(Net string, req *dns.Msg, _local net.Addr, _remo Q := Question{UnFqdn(q.Name), dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass]} logger.Infof("%s lookup %s\n", remote, Q.String()) var lengActive = lengActivation.query() - if len(h.config.ToggleName) > 0 && strings.Contains(Q.Qname, h.config.ToggleName) { - logger.Noticef("Found ToggleName! (%s)\n", Q.Qname) - lengActive = lengActivation.toggle(h.config.ReactivationDelay) - - if lengActive { - logger.Notice("Leng Activated") - } else { - logger.Notice("Leng Deactivated") - } - } IPQuery := h.isIPQuery(q) @@ -225,7 +214,7 @@ func (h *EventLoop) responseFor(Net string, req *dns.Msg, _local net.Addr, _remo 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.Nameservers, h.config.DoH) + mesg, err := h.resolver.Lookup(Net, req, h.config.Timeout, h.config.Interval, h.config.Upstream.Nameservers, h.config.Upstream.DoH) if err != nil { logger.Errorf("resolve query error %s\n", err) @@ -238,7 +227,7 @@ func (h *EventLoop) responseFor(Net string, req *dns.Msg, _local net.Addr, _remo } if mesg.Truncated && Net == "udp" { - mesg, err = h.resolver.Lookup("tcp", req, h.config.Timeout, h.config.Interval, h.config.Nameservers, h.config.DoH) + mesg, err = h.resolver.Lookup("tcp", req, h.config.Timeout, h.config.Interval, h.config.Upstream.Nameservers, h.config.Upstream.DoH) if err != nil { logger.Errorf("resolve tcp query error %s\n", err) diff --git a/main.go b/main.go index 2080108..8b0b855 100644 --- a/main.go +++ b/main.go @@ -63,7 +63,7 @@ func main() { quitActivation := make(chan bool) actChannel := make(chan *ActivationHandler) - go startActivation(actChannel, quitActivation, config.ReactivationDelay) + go startActivation(actChannel, quitActivation) lengActivation = <-actChannel close(actChannel) From a3d8a603afde5cc0f5eff4f190ed1b7c45d22e9a Mon Sep 17 00:00:00 2001 From: Cottand Date: Tue, 14 Nov 2023 15:55:31 +0000 Subject: [PATCH 2/4] group further into upstream --- config.go | 40 ++++++++++++++++++---------------------- doc/src/Configuration.md | 16 +++++----------- handler.go | 8 ++++---- 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/config.go b/config.go index 28d8c89..d5233d2 100644 --- a/config.go +++ b/config.go @@ -32,8 +32,6 @@ type Config struct { Nullroutev6 string Interval int Timeout int - Expire uint32 - Maxcount int QuestionCacheCap int TTL uint32 Blocklist []string @@ -50,6 +48,8 @@ type Upstream struct { DoH string Nameservers []string TimeoutS int `toml:"timeout_s"` + Expire uint32 + Maxcount int } type Metrics struct { @@ -92,9 +92,7 @@ sources = [ ] # list of locations to recursively read blocklists from (warning, every file found is assumed to be a hosts-file or domain list) -sourcedirs = [ - "sources" -] +sourcedirs = ["sources"] # log configuration # format: comma separated list of options, where options is one of @@ -123,20 +121,9 @@ nullroute = "0.0.0.0" # ipv6 address to forward blocked queries to nullroutev6 = "0:0:0:0:0:0:0:0" -# nameservers to forward queries to -nameservers = ["1.1.1.1:53", "1.0.0.1:53"] - -# concurrency interval for lookups in miliseconds +# concurrency interval for lookups in milliseconds interval = 200 -# query timeout for dns lookups in seconds -timeout = 5 - -# cache entry lifespan in seconds -expire = 600 - -# cache capacity, 0 for infinite -maxcount = 0 # question cache capacity, 0 for infinite but not recommended (this is used for storing logs) questioncachecap = 5000 @@ -156,19 +143,28 @@ customdnsrecords = [ # "example.other.tld IN CNAME wikipedia.org" ] -# Dns over HTTPS upstream provider to use -DoH = "https://cloudflare-dns.com/dns-query" - # How deep to follow chains of CNAME records # set to 0 to disable CNAME-following entirely # (anything more than 10 should be more than plenty) # see https://github.com/Cottand/leng/wiki/CNAME%E2%80%90following-DNS followCnameDepth = 12 +[Upstream] + # Dns over HTTPS provider to use. + DoH = "https://cloudflare-dns.com/dns-query" + # nameservers to forward queries to + nameservers = ["1.1.1.1:53", "1.0.0.1:53"] + # query timeout for dns lookups in seconds + timeout_s = 5 + # cache entry lifespan in seconds + expire = 600 + # cache capacity, 0 for infinite + maxcount = 0 + # Prometheus metrics - disabled by default [Metrics] - enabled = false - path = "/metrics" + enabled = false + path = "/metrics" [DnsOverHttpServer] enabled = false diff --git a/doc/src/Configuration.md b/doc/src/Configuration.md index 2b68067..f1e6f7b 100644 --- a/doc/src/Configuration.md +++ b/doc/src/Configuration.md @@ -48,12 +48,6 @@ nullroutev6 = "0:0:0:0:0:0:0:0" # concurrency interval for lookups in miliseconds interval = 200 -# cache entry lifespan in seconds -expire = 600 - -# cache capacity, 0 for infinite -maxcount = 0 - # question cache capacity, 0 for infinite but not recommended (this is used for storing logs) questioncachecap = 5000 @@ -74,16 +68,16 @@ customdnsrecords = [ [Upstream] - #Dns over HTTPS provider to use. + # Dns over HTTPS provider to use. DoH = "https://cloudflare-dns.com/dns-query" - # nameservers to forward queries to nameservers = ["1.1.1.1:53", "1.0.0.1:53"] - # query timeout for dns lookups in seconds timeout_s = 5 - - + # cache entry lifespan in seconds + expire = 600 + # cache capacity, 0 for infinite + maxcount = 0 # Prometheus metrics - enable [Metrics] diff --git a/handler.go b/handler.go index dc4d7f2..e5ceb95 100644 --- a/handler.go +++ b/handler.go @@ -70,12 +70,12 @@ func NewEventLoop(config *Config, blockCache *MemoryBlockCache, questionCache *M resolver = &Resolver{clientConfig} cache = &MemoryCache{ - Backend: make(map[string]*Mesg, config.Maxcount), - Maxcount: config.Maxcount, + Backend: make(map[string]*Mesg, config.Upstream.Maxcount), + Maxcount: config.Upstream.Maxcount, } negCache = &MemoryCache{ Backend: make(map[string]*Mesg), - Maxcount: config.Maxcount, + Maxcount: config.Upstream.Maxcount, } handler := &EventLoop{ @@ -240,7 +240,7 @@ func (h *EventLoop) responseFor(Net string, req *dns.Msg, _local net.Addr, _remo } //find the smallest ttl - ttl := h.config.Expire + ttl := h.config.Upstream.Expire var candidateTTL uint32 for index, answer := range mesg.Answer { From a31fbd0f084e627a11264fc77eba5d5186f99d10 Mon Sep 17 00:00:00 2001 From: Cottand Date: Tue, 14 Nov 2023 16:30:12 +0000 Subject: [PATCH 3/4] group blocking section --- config.go | 66 +++++++++++++++++++++++++-------------------------- grimd_test.go | 4 ++-- handler.go | 6 ++--- updater.go | 4 ++-- 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/config.go b/config.go index d5233d2..a77f2f6 100644 --- a/config.go +++ b/config.go @@ -22,28 +22,32 @@ var ConfigVersion = "1.3.0" // Config holds the configuration parameters type Config struct { Version string - Sources []string - SourceDirs []string LogConfig string Bind string API string - NXDomain bool - Nullroute string - Nullroutev6 string Interval int Timeout int QuestionCacheCap int TTL uint32 - Blocklist []string - Whitelist []string CustomDNSRecords []string APIDebug bool + Blocking Blocking Upstream Upstream Metrics Metrics `toml:"metrics"` DnsOverHttpServer DnsOverHttpServer FollowCnameDepth uint32 } +type Blocking struct { + Sources []string + SourceDirs []string + Blocklist []string + Whitelist []string + NXDomain bool + Nullroute string + Nullroutev6 string +} + type Upstream struct { DoH string Nameservers []string @@ -81,19 +85,6 @@ var defaultConfig = ` # version this config was generated from version = "%s" -# list of sources to pull blocklists from, stores them in ./sources -sources = [ - "https://mirror1.malwaredomains.com/files/justdomains", - "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts", - "https://sysctl.org/cameleon/hosts", - "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt", - "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt", - "https://gitlab.com/quidsup/notrack-blocklists/raw/master/notrack-blocklist.txt" -] - -# list of locations to recursively read blocklists from (warning, every file found is assumed to be a hosts-file or domain list) -sourcedirs = ["sources"] - # log configuration # format: comma separated list of options, where options is one of # file:@ @@ -112,25 +103,12 @@ bind = "0.0.0.0:53" # address to bind to for the API server api = "127.0.0.1:8080" -# response to blocked queries with a NXDOMAIN -nxdomain = false - -# ipv4 address to forward blocked queries to -nullroute = "0.0.0.0" - -# ipv6 address to forward blocked queries to -nullroutev6 = "0:0:0:0:0:0:0:0" - # concurrency interval for lookups in milliseconds interval = 200 - # question cache capacity, 0 for infinite but not recommended (this is used for storing logs) questioncachecap = 5000 -# manual blocklist entries -blocklist = [] - # manual whitelist entries - comments for reference whitelist = [ # "getsentry.com", @@ -149,6 +127,28 @@ customdnsrecords = [ # see https://github.com/Cottand/leng/wiki/CNAME%E2%80%90following-DNS followCnameDepth = 12 +[Blocking] + # response to blocked queries with a NXDOMAIN + nxdomain = false + # ipv4 address to forward blocked queries to + nullroute = "0.0.0.0" + # ipv6 address to forward blocked queries to + nullroutev6 = "0:0:0:0:0:0:0:0" + # manual blocklist entries + blocklist = [] + # list of sources to pull blocklists from, stores them in ./sources + sources = [ + "https://mirror1.malwaredomains.com/files/justdomains", + "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts", + "https://sysctl.org/cameleon/hosts", + "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt", + "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt", + "https://gitlab.com/quidsup/notrack-blocklists/raw/master/notrack-blocklist.txt" + ] + # list of locations to recursively read blocklists from (warning, every file found is assumed to be a hosts-file or domain list) + sourcedirs = ["sources"] + + [Upstream] # Dns over HTTPS provider to use. DoH = "https://cloudflare-dns.com/dns-query" diff --git a/grimd_test.go b/grimd_test.go index 1a8ef94..5f1a97e 100644 --- a/grimd_test.go +++ b/grimd_test.go @@ -53,7 +53,7 @@ func integrationTest(changeConfig func(c *Config), test func(client *dns.Client, // BlockCache contains all blocked domains blockCache := &MemoryBlockCache{Backend: make(map[string]bool)} - for _, blocked := range config.Blocklist { + for _, blocked := range config.Blocking.Blocklist { _ = blockCache.Set(blocked, true) } // QuestionCache contains all queries to the dns server @@ -200,7 +200,7 @@ func TestCnameFollowWithBlocked(t *testing.T) { "first.com IN CNAME second.com ", "second.com IN CNAME example.com ", } - c.Blocklist = []string{"example.com"} + c.Blocking.Blocklist = []string{"example.com"} }, func(client *dns.Client, target string) { diff --git a/handler.go b/handler.go index e5ceb95..9d6da8f 100644 --- a/handler.go +++ b/handler.go @@ -163,11 +163,11 @@ func (h *EventLoop) responseFor(Net string, req *dns.Msg, _local net.Addr, _remo m := new(dns.Msg) m.SetReply(req) - if h.config.NXDomain { + if h.config.Blocking.NXDomain { m.SetRcode(req, dns.RcodeNameError) } else { - nullroute := net.ParseIP(h.config.Nullroute) - nullroutev6 := net.ParseIP(h.config.Nullroutev6) + nullroute := net.ParseIP(h.config.Blocking.Nullroute) + nullroutev6 := net.ParseIP(h.config.Blocking.Nullroutev6) switch IPQuery { case _IP4Query: diff --git a/updater.go b/updater.go index d1731a4..6e2a56a 100644 --- a/updater.go +++ b/updater.go @@ -179,11 +179,11 @@ func parseHostFile(fileName string, blockCache *MemoryBlockCache) error { func PerformUpdate(config *Config, forceUpdate bool) *MemoryBlockCache { newBlockCache := &MemoryBlockCache{Backend: make(map[string]bool), Special: make(map[string]*regexp.Regexp)} if _, err := os.Stat("lists"); os.IsNotExist(err) || forceUpdate { - if err := update(newBlockCache, config.Whitelist, config.Blocklist, config.Sources); err != nil { + if err := update(newBlockCache, config.Blocking.Whitelist, config.Blocking.Blocklist, config.Blocking.Sources); err != nil { logger.Fatal(err) } } - if err := updateBlockCache(newBlockCache, config.SourceDirs); err != nil { + if err := updateBlockCache(newBlockCache, config.Blocking.SourceDirs); err != nil { logger.Fatal(err) } From 8cddcda7ab1cca72d8bebccfcb228e3f0d7435a5 Mon Sep 17 00:00:00 2001 From: Cottand Date: Tue, 14 Nov 2023 16:31:57 +0000 Subject: [PATCH 4/4] update docs --- doc/src/Configuration.md | 62 +++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/doc/src/Configuration.md b/doc/src/Configuration.md index f1e6f7b..1549f5d 100644 --- a/doc/src/Configuration.md +++ b/doc/src/Configuration.md @@ -3,21 +3,6 @@ If leng.toml is not found the default configuration will be used. If it is found Here is the default configuration: ```toml -# list of sources to pull blocklists from, stores them in ./sources -sources = [ - "https://mirror1.malwaredomains.com/files/justdomains", - "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts", - "https://sysctl.org/cameleon/hosts", - "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt", - "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt", - "https://gitlab.com/quidsup/notrack-blocklists/raw/master/notrack-blocklist.txt" -] - -# list of locations to recursively read blocklists from (warning, every file found is assumed to be a hosts-file or domain list) -sourcedirs = [ - "sources" -] - # log configuration # format: comma separated list of options, where options is one of # file:@ @@ -36,36 +21,47 @@ bind = "0.0.0.0:53" # address to bind to for the API server api = "127.0.0.1:8080" -# response to blocked queries with a NXDOMAIN -nxdomain = false - -# ipv4 address to forward blocked queries to -nullroute = "0.0.0.0" - -# ipv6 address to forward blocked queries to -nullroutev6 = "0:0:0:0:0:0:0:0" - # concurrency interval for lookups in miliseconds interval = 200 # question cache capacity, 0 for infinite but not recommended (this is used for storing logs) questioncachecap = 5000 -# manual blocklist entries -blocklist = [] - -# manual whitelist entries - comments for reference -whitelist = [ - # "getsentry.com", - # "www.getsentry.com" -] - # manual custom dns entries - comments for reference customdnsrecords = [ # "example.mywebsite.tld IN A 10.0.0.1" # "example.other.tld IN CNAME wikipedia.org" ] +[Blocking] + # response to blocked queries with a NXDOMAIN + nxdomain = false + # ipv4 address to forward blocked queries to + nullroute = "0.0.0.0" + # ipv6 address to forward blocked queries to + nullroutev6 = "0:0:0:0:0:0:0:0" + # manual blocklist entries + blocklist = [] + # list of sources to pull blocklists from, stores them in ./sources + sources = [ + "https://mirror1.malwaredomains.com/files/justdomains", + "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts", + "https://sysctl.org/cameleon/hosts", + "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt", + "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt", + "https://gitlab.com/quidsup/notrack-blocklists/raw/master/notrack-blocklist.txt" + ] + # list of locations to recursively read blocklists from (warning, every file found is assumed to be a hosts-file or domain list) + sourcedirs = ["sources"] + # manual blocklist entries + blocklist = [] + # manual whitelist entries - comments for reference + whitelist = [ + # "getsentry.com", + # "www.getsentry.com" + ] + + [Upstream] # Dns over HTTPS provider to use.