fix: restore recurring GeoIP checks

This commit is contained in:
Patrick Asmus
2026-06-10 13:22:12 +02:00
parent 0716458426
commit 86db4935b8
11 changed files with 137 additions and 22 deletions

View File

@@ -14,6 +14,7 @@ import (
"path/filepath"
"regexp"
"strings"
"sync"
"time"
"github.com/oschwald/maxminddb-golang"
@@ -31,6 +32,7 @@ type Resolver struct {
Dir string
TTL int64
Store Store
mu sync.RWMutex
reader *maxminddb.Reader
cache map[string]string
mtime int64
@@ -65,7 +67,9 @@ func (r *Resolver) Open(ctx context.Context) error {
r.mtime = st.ModTime().Unix()
if r.Store != nil {
if c, err := r.Store.LoadGeoIPCache(r.TTL, r.mtime); err == nil {
r.mu.Lock()
r.cache = c
r.mu.Unlock()
}
}
return nil
@@ -79,9 +83,12 @@ func (r *Resolver) Close() error {
}
func (r *Resolver) Lookup(ip string) (string, error) {
r.mu.RLock()
if v, ok := r.cache[ip]; ok {
r.mu.RUnlock()
return v, nil
}
r.mu.RUnlock()
if r.reader == nil {
return r.lookupLegacy(ip)
}
@@ -104,33 +111,44 @@ func (r *Resolver) Lookup(ip string) (string, error) {
if cc == "" {
cc = strings.ToUpper(rec.RegisteredCountry.ISOCode)
}
if cc != "" {
r.cache[ip] = cc
if r.Store != nil {
_ = r.Store.UpsertGeoIP(ip, cc, r.mtime)
}
}
r.storeCache(ip, cc)
return cc, nil
}
func (r *Resolver) lookupLegacy(ip string) (string, error) {
if strings.Contains(ip, ":") {
if cc, err := runGeoIPCommand("geoiplookup6", ip); err == nil && cc != "" {
r.storeCache(ip, cc)
return cc, nil
}
} else {
if cc, err := runGeoIPCommand("geoiplookup", ip); err == nil && cc != "" {
r.storeCache(ip, cc)
return cc, nil
}
}
if r.effectivePath != "" {
if cc, err := runGeoIPCommand("mmdblookup", "--file", r.effectivePath, "--ip", ip, "country", "iso_code"); err == nil && cc != "" {
r.storeCache(ip, cc)
return cc, nil
}
}
return "", fmt.Errorf("no GeoIP result for %s", ip)
}
func (r *Resolver) storeCache(ip, country string) {
country = strings.ToUpper(strings.TrimSpace(country))
if country == "" {
return
}
r.mu.Lock()
r.cache[ip] = country
r.mu.Unlock()
if r.Store != nil {
_ = r.Store.UpsertGeoIP(ip, country, r.mtime)
}
}
func runGeoIPCommand(name string, args ...string) (string, error) {
if _, err := exec.LookPath(name); err != nil {
return "", err

View File

@@ -2,6 +2,23 @@ package geoip
import "testing"
type fakeGeoIPStore struct {
ip string
country string
mtime int64
}
func (s *fakeGeoIPStore) LoadGeoIPCache(ttl, dbMtime int64) (map[string]string, error) {
return map[string]string{}, nil
}
func (s *fakeGeoIPStore) UpsertGeoIP(ip, country string, dbMtime int64) error {
s.ip = ip
s.country = country
s.mtime = dbMtime
return nil
}
func TestShouldBlockModes(t *testing.T) {
countries := []string{"CN", "RU"}
if !ShouldBlock("cn", "blocklist", countries) {
@@ -28,3 +45,18 @@ func TestIsPrivateIP(t *testing.T) {
t.Fatal("8.8.8.8 should be public")
}
}
func TestStoreCachePersistsLegacyLookupResult(t *testing.T) {
store := &fakeGeoIPStore{}
r := New("", "", "", 86400, store)
r.mtime = 123
r.storeCache("203.0.113.7", "cn")
if got := r.cache["203.0.113.7"]; got != "CN" {
t.Fatalf("cache = %q, want CN", got)
}
if store.ip != "203.0.113.7" || store.country != "CN" || store.mtime != 123 {
t.Fatalf("store got ip=%q country=%q mtime=%d", store.ip, store.country, store.mtime)
}
}