bugfix for single IP lists
All checks were successful
release-tag / release-image (push) Successful in 1m32s
All checks were successful
release-tag / release-image (push) Successful in 1m32s
This commit is contained in:
66
main.go
66
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/<ip>?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)
|
||||
|
Reference in New Issue
Block a user