diff options
| author | radhitya <alif@radhitya.org> | 2026-06-14 17:17:56 +0700 |
|---|---|---|
| committer | radhitya <alif@radhitya.org> | 2026-06-14 17:17:56 +0700 |
| commit | d173554892339e5211020c60d6af610840eef7ed (patch) | |
| tree | 295ce37851532e6180b47c63ed34146a01adc12c | |
| parent | 4e6a897a0b55ee533c05f89fa38dbe0704f2798d (diff) | |
config, rebranding, fix cache
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 26 | ||||
| -rw-r--r-- | go.mod | 26 | ||||
| -rw-r--r-- | go.sum | 36 | ||||
| -rw-r--r-- | internal/cache/cache.go | 9 | ||||
| -rw-r--r-- | internal/config/config.go | 152 | ||||
| -rw-r--r-- | internal/server/handler.go | 6 | ||||
| -rw-r--r-- | internal/server/server.go | 6 | ||||
| -rw-r--r-- | internal/server/server_test.go | 2 | ||||
| -rw-r--r-- | linum.toml | 22 | ||||
| -rw-r--r-- | main.go | 111 | ||||
| -rw-r--r-- | readme | 2 |
12 files changed, 324 insertions, 76 deletions
@@ -1,3 +1,3 @@ +build/ etc/blocklist/*.txt -sdns todo.md @@ -1,14 +1,17 @@ -BINARY = sdns +BINARY = linum GO = go -GOMOD = . +MAIN = . +OUTPUT = build/$(BINARY) +LDFLAGS = -ldflags="-s -w -X main.version=$(VERSION)" -.PHONY: default build test lint fmt clean run install fuzz race +.PHONY: default build test lint fmt clean run install fuzz race \ + build-linux default: test lint build build: - $(GO) build -o $(BINARY) $(GOMOD) + $(GO) build $(LDFLAGS) -o $(OUTPUT) $(MAIN) test: $(GO) test -race -count=1 ./... @@ -20,16 +23,25 @@ fmt: gofmt -w . clean: - rm -f $(BINARY) + rm -f $(OUTPUT) run: build - ./$(BINARY) + ./$(OUTPUT) install: - $(GO) install $(GOMOD) + $(GO) install $(LDFLAGS) $(MAIN) fuzz: $(GO) test -fuzz=FuzzBuildResponse -fuzztime=30s ./internal/server/ +build-linux: + GOOS=linux GOARCH=amd64 $(GO) build $(LDFLAGS) -o build/$(BINARY)-linux-amd64 $(MAIN) + +build-darwin: + GOOS=darwin GOARCH=amd64 $(GO) build $(LDFLAGS) -o build/$(BINARY)-darwin-amd64 $(MAIN) + +build-arm: + GOOS=linux GOARCH=arm64 $(GO) build $(LDFLAGS) -o build/$(BINARY)-linux-arm64 $(MAIN) + race: $(GO) test -race -count=1 ./... @@ -1,13 +1,25 @@ -module sdns +module linum go 1.26.1 -require github.com/miekg/dns v1.1.72 +require ( + github.com/BurntSushi/toml v1.6.0 + github.com/miekg/dns v1.1.72 + modernc.org/sqlite v1.52.0 +) require ( - golang.org/x/mod v0.31.0 // indirect - golang.org/x/net v0.48.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.39.0 // indirect - golang.org/x/tools v0.40.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/ncruces/go-strftime v1.0.0 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + golang.org/x/mod v0.33.0 // indirect + golang.org/x/net v0.50.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/tools v0.42.0 // indirect + modernc.org/libc v1.72.3 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.11.0 // indirect ) @@ -1,9 +1,15 @@ +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= @@ -12,32 +18,42 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= -golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= -golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +modernc.org/cc/v4 v4.28.2 h1:3tQ0lf2ADtoby2EtSP+J7IE2SHwEJdP8ioR59wx7XpY= +modernc.org/cc/v4 v4.28.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= +modernc.org/ccgo/v4 v4.34.0 h1:yRLPFZieg532OT4rp4JFNIVcquwalMX26G95WQDqwCQ= +modernc.org/ccgo/v4 v4.34.0/go.mod h1:AS5WYMyBakQ+fhsHhtP8mWB82KTGPkNNJDGfGQCe0/A= +modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= +modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo= +modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= modernc.org/libc v1.72.3 h1:ZnDF4tXn4NBXFutMMQC4vtbTFSXhhKzR73fv0beZEAU= modernc.org/libc v1.72.3/go.mod h1:dn0dZNnnn1clLyvRxLxYExxiKRZIRENOfqQ8XEeg4Qs= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg= +modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sqlite v1.52.0 h1:p4dhYh2tXZCiyaqHwRVJDjIGKWyXayiQpThxgDzJaxo= modernc.org/sqlite v1.52.0/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/internal/cache/cache.go b/internal/cache/cache.go index a2d86a0..d6a31f3 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -2,6 +2,7 @@ package cache import ( "database/sql" + "log/slog" "sync" "sync/atomic" "time" @@ -199,16 +200,22 @@ func (c *Cache) evictLoop() { } } func (c *Cache) writeToDB(key Key, e *entry) { + if c.db == nil { + return + } data, err := e.msg.Pack() if err != nil { return } - c.db.Exec( + _, err = c.db.Exec( `INSERT OR REPLACE INTO cache (name, qtype, class, data, stored_at, ttl_ns) VALUES (?, ?, ?, ?, ?, ?)`, key.Name, key.Qtype, key.Class, data, e.storedAt.UnixNano(), int64(e.ttl), ) + if err != nil { + slog.Warn("cache write to db failed", "err", err) + } } func (c *Cache) loadFromDB() { diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..f2624c2 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,152 @@ +package config + +import ( + "flag" + "fmt" + + "github.com/BurntSushi/toml" +) + +type Config struct { + Server ServerConfig `toml:"server"` + Cache CacheConfig `toml:"cache"` + Resolver ResolverConfig `toml:"resolver"` + Blocklist BlocklistConfig `toml:"blocklist"` + Log LogConfig `toml:"log"` +} + +type ServerConfig struct { + ListenUDP string `toml:"listen_udp"` + ListenTCP string `toml:"listen_tcp"` + ListenDOH string `toml:"listen_doh"` +} + +type CacheConfig struct { + MaxEntries int `toml:"max_entries"` + DBPath string `toml:"db_path"` +} + +type ResolverConfig struct { + Timeout string `toml:"timeout"` + MaxDelegations int `toml:"max_delegations"` +} + +type BlocklistConfig struct { + Response string `toml:"response"` + Files []string `toml:"files"` + URLs []string `toml:"urls"` +} + +type LogConfig struct { + Level string `toml:"level"` +} + +type CLIFlags struct { + Config string + LogLevel string + ListenUDP string + ListenTCP string + ListenDOH string +} + +func ParseFlags() CLIFlags { + var f CLIFlags + flag.StringVar(&f.Config, "config", "linum.toml", "path to config file") + flag.StringVar(&f.LogLevel, "loglevel", "", "log level (debug|info|warn|error)") + flag.StringVar(&f.ListenUDP, "udp", "", "UDP listen address") + flag.StringVar(&f.ListenTCP, "tcp", "", "TCP listen address") + flag.StringVar(&f.ListenDOH, "doh", "", "DoH listen address") + flag.Parse() + return f +} + +func Default() Config{ + return Config{ + Server: ServerConfig{ + ListenUDP: ":5353", + ListenTCP: ":5353", + ListenDOH: ":8443", + }, + Cache: CacheConfig{ + MaxEntries: 100000, + }, + Resolver: ResolverConfig{ + Timeout: "2s", + MaxDelegations: 30, + }, + Blocklist: BlocklistConfig{ + Response: "zero_ip", + }, + Log: LogConfig{ + Level: "info", + }, + } +} + +func LoadFile(path string) (Config, error) { + var cfg Config + _, err := toml.DecodeFile(path, &cfg) + return cfg, err +} + +func Merge(dst, src Config) Config { + if src.Server.ListenUDP != "" { + dst.Server.ListenUDP = src.Server.ListenUDP + } + if src.Server.ListenTCP != "" { + dst.Server.ListenTCP = src.Server.ListenTCP + } + if src.Server.ListenDOH != "" { + dst.Server.ListenDOH = src.Server.ListenDOH + } + if src.Cache.MaxEntries > 0 { + dst.Cache.MaxEntries = src.Cache.MaxEntries + } + if src.Cache.DBPath != "" { + dst.Cache.DBPath = src.Cache.DBPath + } + if src.Resolver.Timeout != "" { + dst.Resolver.Timeout = src.Resolver.Timeout + } + if src.Resolver.MaxDelegations > 0 { + dst.Resolver.MaxDelegations = src.Resolver.MaxDelegations + } + if src.Blocklist.Response != "" { + dst.Blocklist.Response = src.Blocklist.Response + } + if len(src.Blocklist.Files) > 0 { + dst.Blocklist.Files = src.Blocklist.Files + } + if len(src.Blocklist.URLs) > 0 { + dst.Blocklist.URLs = src.Blocklist.URLs + } + if src.Log.Level != "" { + dst.Log.Level = src.Log.Level + } + return dst +} + +func (f CLIFlags) Apply(cfg Config) Config { + if f.ListenUDP != "" { + cfg.Server.ListenUDP = f.ListenUDP + } + if f.ListenTCP != "" { + cfg.Server.ListenTCP = f.ListenTCP + } + if f.ListenDOH != "" { + cfg.Server.ListenDOH = f.ListenDOH + } + if f.LogLevel != "" { + cfg.Log.Level = f.LogLevel + } + return cfg +} + +func (c Config) Validate() error { + switch c.Blocklist.Response { + case "zero_ip", "nxdomain", "": + default: + return fmt.Errorf("invalid blocklist response %q (want zero_ip or nxdomain)", c.Blocklist.Response) + } + return nil +} diff --git a/internal/server/handler.go b/internal/server/handler.go index 4aa771f..406b7ed 100644 --- a/internal/server/handler.go +++ b/internal/server/handler.go @@ -7,8 +7,8 @@ import ( "time" "github.com/miekg/dns" - "sdns/internal/blocklist" - "sdns/internal/cache" + "linum/internal/blocklist" + "linum/internal/cache" ) func (s *Server) handleQuery(w dns.ResponseWriter, req *dns.Msg) { @@ -33,7 +33,6 @@ func (s *Server) handleQuery(w dns.ResponseWriter, req *dns.Msg) { "qname", req.Question[0].Name, "qtype", dns.TypeToString[req.Question[0].Qtype], "rcode", dns.RcodeToString[resp.Rcode], - "client", w.RemoteAddr().String(), "blocked", blocked, ) } @@ -47,6 +46,7 @@ func (s *Server) buildResponse(req *dns.Msg) (*dns.Msg, bool) { if s.cache != nil { key := cache.Key{Name: q.Name, Qtype: q.Qtype, Class: q.Qclass} if cached, ok := s.cache.Get(key); ok { + cached.Id = req.Id return cached, false } } diff --git a/internal/server/server.go b/internal/server/server.go index e0490bd..ec0dec9 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -7,9 +7,9 @@ import ( "time" "github.com/miekg/dns" - "sdns/internal/resolver" - "sdns/internal/blocklist" - "sdns/internal/cache" + "linum/internal/resolver" + "linum/internal/blocklist" + "linum/internal/cache" ) type Server struct { diff --git a/internal/server/server_test.go b/internal/server/server_test.go index c49d5f3..e59a131 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -6,7 +6,7 @@ import ( "time" "github.com/miekg/dns" - "sdns/internal/resolver" + "linum/internal/resolver" ) func testServer(t *testing.T) *Server { diff --git a/linum.toml b/linum.toml new file mode 100644 index 0000000..bdb30ba --- /dev/null +++ b/linum.toml @@ -0,0 +1,22 @@ +[server] +listen_udp = ":5353" +listen_tcp = ":5353" +listen_doh = ":8443" + +[cache] +max_entries = 100000 +db_path = "/tmp/cache.db" +[resolver] +timeout = "2s" +max_delegations = 30 + +[blocklist] +response = "zero_ip" +files = ["etc/blocklist/*.txt"] +#urls = [ + # "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts", + # "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt", +#] + +[log] +level = "info" @@ -7,28 +7,47 @@ import ( "os/signal" "path/filepath" "syscall" - - "sdns/internal/blocklist" - "sdns/internal/cache" - "sdns/internal/resolver" - "sdns/internal/server" + "fmt" + "linum/internal/blocklist" + "linum/internal/cache" + "linum/internal/config" + "linum/internal/resolver" + "linum/internal/server" ) func main() { - logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ - Level: slog.LevelInfo, - })) - slog.SetDefault(logger) + flags := config.ParseFlags() - r := resolver.New() + cfg := config.Default() + if _, err := os.Stat(flags.Config); err == nil { + fileCfg, err := config.LoadFile(flags.Config) + if err != nil { + slog.Error("invalid config file", "err", err) + os.Exit(1) + } + cfg = config.Merge(cfg, fileCfg) + } + cfg = flags.Apply(cfg) - dbPath := os.Getenv("SDNS_CACHE_DB") - if dbPath != "" { - logger.Info("cache using sqlite", "path", dbPath) - } else { - logger.Info("cache using in-memory") + if err := cfg.Validate(); err != nil { + slog.Error("config validation failed", "err", err) + os.Exit(1) } - c, err := cache.NewCache(100000, dbPath) + + var lvl slog.Level + _ = lvl.UnmarshalText([]byte(cfg.Log.Level)) + logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: lvl})) + slog.SetDefault(logger) + + fmt.Println("linum - simple dns recursive") + fmt.Printf("github.com/radhityax/linum\n\n") + + logger.Info("config loaded", "file", flags.Config) + + r := resolver.New( + resolver.WithTimeout(2 * 1000 * 1000 * 1000),) + + c, err := cache.NewCache(cfg.Cache.MaxEntries, cfg.Cache.DBPath) if err != nil { logger.Error("create cache failed", "err", err) os.Exit(1) @@ -36,49 +55,57 @@ func main() { defer c.Stop() var bl *blocklist.Blocklist - matches, _ := filepath.Glob("etc/blocklist/*.txt") - if len(matches) > 0 { - bl = blocklist.New(blocklist.ResponseZeroIP) - for _, f := range matches { - if err := bl.LoadFile(f); err != nil { - logger.Error("load blocklist failed", "file", f, "err", err) - os.Exit(1) + if len(cfg.Blocklist.Files) > 0 || len(cfg.Blocklist.URLs) > 0 { + resp := blocklist.ResponseZeroIP + if cfg.Blocklist.Response == "nxdomain" { + resp = blocklist.ResponseNXDOMAIN + } + bl = blocklist.New(resp) + + for _, pattern := range cfg.Blocklist.Files { + matches, err := filepath.Glob(pattern) + if err != nil { + logger.Warn("invalid blocklist glob", "pattern", pattern, "err", err) + continue + } + for _, f := range matches { + if err := bl.LoadFile(f); err != nil { + logger.Error("load blocklist failed", "file", f, "err", err) + os.Exit(1) + } + logger.Info("blocklist loaded", "file", f, "rules", bl.TotalRules) + } + } + for _, u := range cfg.Blocklist.URLs { + if err := bl.LoadURL(u); err != nil { + logger.Warn("load blocklist url failed", "url", u, "err", err) + continue } - logger.Info("blocklist loaded", "file", f, "rules", bl.TotalRules) + logger.Info("blocklist url loaded", "url", u) } } else { - logger.Info("no blocklist files in etc/blocklist/, ad-blocking disabled") - } - udp := os.Getenv("SDNS_LISTEN_UDP") - if udp == "" { - udp = ":5353" - } - - tcp := os.Getenv("SDNS_LISTEN_TCP") - if tcp == "" { - tcp = ":5353" - } - - doh := os.Getenv("SDNS_LISTEN_DOH") - if doh == "" { - doh = ":8443" + logger.Info("no blocklist configured, ad-blocking disabled") } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() - srv, err := server.New(udp, tcp, doh, logger, r, c, bl) + srv, err := server.New(cfg.Server.ListenUDP, cfg.Server.ListenTCP, cfg.Server.ListenDOH, logger, r, c, bl) if err != nil { logger.Error("create server failed", "err", err) os.Exit(1) } defer srv.Close() - logger.Info("sdns starting", "udp", udp, "tcp", tcp, "doh", doh) + logger.Info("linum starting", + "udp", cfg.Server.ListenUDP, + "tcp", cfg.Server.ListenTCP, + "doh", cfg.Server.ListenDOH, + ) if err := srv.Run(ctx); err != nil && err != context.Canceled { logger.Error("server stopped with error", "err", err) os.Exit(1) } - logger.Info("sdns stopped cleanly") + logger.Info("linum stopped cleanly") } @@ -1,4 +1,4 @@ -sdns +linum ==== simple authoritative dns written in golang |
