From 969c25ce9aae62d8ccd3904c488b566fa5d3ecf5 Mon Sep 17 00:00:00 2001 From: jbergner Date: Mon, 9 Jun 2025 18:32:34 +0200 Subject: [PATCH] bugfix for single IP lists --- main.go | 66 ++++++++++++++++++++++++++------------------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/main.go b/main.go index 3756b4b..7a27daf 100644 --- a/main.go +++ b/main.go @@ -25,33 +25,28 @@ import ( type Source struct { Category string // e.g. "spam", "tor", "malware" - URL []string // one or many URLs that belong to the same category + URL []string // one or many URLs belonging to this category } type Config struct { RedisAddr string - Sources []Source // each Source now groups URLs per category - TTLHours int // Redis TTL for block entries + Sources []Source // grouped by category + TTLHours int // TTL for block entries in Redis } func loadConfig() Config { - // --- default one‑category fallback -------------------------------------- + // default single source srcs := []Source{{ Category: "generic", - URL: []string{"https://ipv64.net/blocklists/ipv64_blocklist_firehole_l1.txt"}, + URL: []string{"https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset"}, }} /* - ENV syntax supporting multiple URLs per category: - + ENV format supporting many URLs per category: BLOCKLIST_SOURCES="spam:https://a.net|https://b.net,tor:https://c.net;https://d.net" - - – categories separated by comma - – URLs inside category separated by | or ; */ - if env := os.Getenv("BLOCKLIST_SOURCES"); env != "" { - srcs = nil // override default + srcs = nil for _, spec := range strings.Split(env, ",") { spec = strings.TrimSpace(spec) if spec == "" { @@ -59,12 +54,12 @@ func loadConfig() Config { } parts := strings.SplitN(spec, ":", 2) if len(parts) != 2 { - continue // malformed fragment + continue } cat := strings.TrimSpace(parts[0]) - rawURLs := strings.FieldsFunc(parts[1], func(r rune) bool { return r == '|' || r == ';' }) + raw := strings.FieldsFunc(parts[1], func(r rune) bool { return r == '|' || r == ';' }) var urls []string - for _, u := range rawURLs { + for _, u := range raw { if u = strings.TrimSpace(u); u != "" { urls = append(urls, u) } @@ -80,10 +75,8 @@ func loadConfig() Config { fmt.Sscanf(env, "%d", &ttl) } - fmt.Println(getenv("REDIS_ADDR", "localhost:6379"), srcs, ttl) - return Config{ - RedisAddr: getenv("REDIS_ADDR", "localhost:6379"), + RedisAddr: getenv("REDIS_ADDR", "redis:6379"), Sources: srcs, TTLHours: ttl, } @@ -104,12 +97,12 @@ func keyBlock(cat string, p netip.Prefix) string { return "bl:" + cat + ":" + p. func keyWhite(a netip.Addr) string { return "wl:" + a.String() } // ----------------------------------------------------------------------------- -// IN‑MEMORY RANGER – per‑category CIDR map +// IN-MEMORY RANGER // ----------------------------------------------------------------------------- type Ranger struct { mu sync.RWMutex - blocks map[string]map[netip.Prefix]struct{} // cat → set(prefix) + blocks map[string]map[netip.Prefix]struct{} whites map[netip.Addr]struct{} } @@ -132,7 +125,6 @@ func (r *Ranger) addWhite(a netip.Addr) { r.mu.Unlock() } -// blockedInCats – returns slice of categories in which IP is blocked func (r *Ranger) blockedInCats(a netip.Addr, cats []string) []string { r.mu.RLock() defer r.mu.RUnlock() @@ -163,7 +155,7 @@ func (r *Ranger) blockedInCats(a netip.Addr, cats []string) []string { } // ----------------------------------------------------------------------------- -// SYNC WORKER – fetch lists → Redis + Ranger +// SYNC WORKER // ----------------------------------------------------------------------------- func syncOnce(ctx context.Context, cfg Config, rdb *redis.Client, ranger *Ranger) error { @@ -179,7 +171,7 @@ func syncOnce(ctx context.Context, cfg Config, rdb *redis.Client, ranger *Ranger newBlocks[src.Category][p] = struct{}{} _ = rdb.Set(ctx, keyBlock(src.Category, p), "1", expiry).Err() }); err != nil { - fmt.Println("Error initialising List:", err, src.Category, src.URL) + return err } } } @@ -210,6 +202,14 @@ func parseStream(r io.Reader, cb func(netip.Prefix)) error { } if p, err := netip.ParsePrefix(line); err == nil { cb(p) + continue + } + if addr, err := netip.ParseAddr(line); err == nil { + plen := 32 + if addr.Is6() { + plen = 128 + } + cb(netip.PrefixFrom(addr, plen)) } } return s.Err() @@ -227,9 +227,9 @@ type Server struct { func (s *Server) routes() http.Handler { mux := http.NewServeMux() - mux.HandleFunc("/check/", s.handleCheck) // GET /check/?cats=spam,tor - mux.HandleFunc("/whitelist", s.handleAddWhite) // POST {"ip":"1.2.3.4"} - mux.HandleFunc("/categories", s.handleCats) // GET all categories + mux.HandleFunc("/check/", s.handleCheck) + mux.HandleFunc("/whitelist", s.handleAddWhite) + mux.HandleFunc("/categories", s.handleCats) return mux } @@ -240,18 +240,12 @@ func (s *Server) handleCheck(w http.ResponseWriter, r *http.Request) { http.Error(w, "bad ip", http.StatusBadRequest) return } - var cats []string if q := strings.TrimSpace(r.URL.Query().Get("cats")); q != "" { cats = strings.Split(q, ",") } - blocked := s.ranger.blockedInCats(addr, cats) - writeJSON(w, map[string]any{ - "ip": ipStr, - "blocked": len(blocked) > 0, - "categories": blocked, - }) + writeJSON(w, map[string]any{"ip": ipStr, "blocked": len(blocked) > 0, "categories": blocked}) } func (s *Server) handleAddWhite(w http.ResponseWriter, r *http.Request) { @@ -263,16 +257,16 @@ func (s *Server) handleAddWhite(w http.ResponseWriter, r *http.Request) { IP string `json:"ip"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - http.Error(w, "bad json", 400) + http.Error(w, "bad json", http.StatusBadRequest) return } addr, err := netip.ParseAddr(strings.TrimSpace(body.IP)) if err != nil { - http.Error(w, "bad ip", 400) + http.Error(w, "bad ip", http.StatusBadRequest) return } if err := s.rdb.Set(r.Context(), keyWhite(addr), "1", 0).Err(); err != nil { - http.Error(w, "redis", 500) + http.Error(w, "redis", http.StatusInternalServerError) return } s.ranger.addWhite(addr)