mirror of
https://github.com/yusing/godoxy.git
synced 2026-01-11 14:20:32 +01:00
MaxMind
The maxmind package provides MaxMind GeoIP database integration for IP geolocation, including automatic database downloading and updates.
Overview
The maxmind package implements MaxMind GeoIP database management, providing IP geolocation lookups for country and city information. It supports automatic database downloading, scheduled updates, and thread-safe access.
Key Features
- MaxMind GeoIP database loading
- Automatic database downloading from MaxMind
- Scheduled updates every 24 hours
- City lookup with cache support
- IP geolocation (country, city, timezone)
- Thread-safe access
Architecture
graph TD
A[MaxMind Config] --> B[Load Database]
B --> C{Exists?}
C -->|No| D[Download]
C -->|Yes| E[Load]
D --> F[Extract from TarGz]
E --> G[Open Reader]
H[IP Lookup] --> I[City Lookup]
I --> J{Cache Hit?}
J -->|Yes| K[Return Cached]
J -->|No| L[Database Query]
L --> M[Cache Result]
M --> K
N[Update Scheduler] --> O[Check Daily]
O --> P{Different?}
P -->|Yes| Q[Download Update]
P -->|No| O
Core Components
MaxMind Structure
type MaxMind struct {
*Config
lastUpdate time.Time
db struct {
*maxminddb.Reader
sync.RWMutex
}
}
Configuration
type Config struct {
Database string // Database type (GeoLite2 or GeoIP2)
AccountID int
LicenseKey Secret
}
IP Information
type IPInfo struct {
IP net.IP
Str string
Country *Country
City *City
Location *Location
}
type Country struct {
IsoCode string
Name string
}
type City struct {
Country *Country
Name string
Location *Location
}
type Location struct {
TimeZone string
Latitude float64
Longitude float64
}
Public API
Initialization
// LoadMaxMindDB loads or downloads the MaxMind database.
func (cfg *MaxMind) LoadMaxMindDB(parent task.Parent) gperr.Error
Lookup
// LookupCity looks up city information for an IP.
func LookupCity(info *IPInfo) (city *City, ok bool)
Usage
Basic Setup
maxmindCfg := &maxmind.Config{
Database: maxmind.MaxMindGeoLite,
AccountID: 123456,
LicenseKey: "your-license-key",
}
err := maxmindCfg.LoadMaxMindDB(parent)
if err != nil {
log.Fatal(err)
}
IP Lookup
// Create IP info
ipInfo := &maxmind.IPInfo{
IP: net.ParseIP("8.8.8.8"),
Str: "8.8.8.8",
}
// Lookup city
city, ok := maxmind.LookupCity(ipInfo)
if ok {
fmt.Printf("Country: %s\n", city.Country.IsoCode)
fmt.Printf("City: %s\n", city.Name)
fmt.Printf("Timezone: %s\n", city.Location.TimeZone)
}
Database Types
const (
MaxMindGeoLite = "GeoLite2-Country"
MaxMindGeoIP2 = "GeoIP2-Country"
)
Data Flow
sequenceDiagram
participant Config
participant MaxMind
participant Database
participant Cache
participant UpdateScheduler
Config->>MaxMind: LoadMaxMindDB()
MaxMind->>Database: Open()
alt Database Missing
MaxMind->>MaxMind: Download()
MaxMind->>Database: Extract & Create
end
Database-->>MaxMind: Reader
Note over MaxMind: Start Update Scheduler
loop Every 24 Hours
UpdateScheduler->>MaxMind: Check Update
MaxMind->>MaxMind: Check Last-Modified
alt Update Available
MaxMind->>MaxMind: Download
MaxMind->>Database: Replace
end
end
participant Lookup
Lookup->>MaxMind: LookupCity(ip)
MaxMind->>Cache: Check
alt Cache Hit
Cache-->>Lookup: City Info
else Cache Miss
MaxMind->>Database: Query
Database-->>MaxMind: City Info
MaxMind->>Cache: Store
MaxMind-->>Lookup: City Info
end
Database Download
Download Process
func (cfg *MaxMind) download() error {
resp, err := cfg.doReq(http.MethodGet)
if err != nil {
return err
}
// Read response
databaseGZ, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
// Extract from tar.gz
err = extractFileFromTarGz(databaseGZ, cfg.dbFilename(), tmpDBPath)
if err != nil {
return err
}
// Validate
db, err := maxmindDBOpen(tmpDBPath)
if err != nil {
os.Remove(tmpDBPath)
return err
}
db.Close()
// Rename to final location
os.Rename(tmpDBPath, dbFile)
return nil
}
Security Checks
The download process includes tar bomb protection:
sumSize := int64(0)
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
sumSize += hdr.Size
if sumSize > 30*1024*1024 {
return errors.New("file size exceeds 30MB")
}
}
Update Scheduling
func (cfg *MaxMind) scheduleUpdate(parent task.Parent) {
task := parent.Subtask("maxmind_schedule_update", true)
ticker := time.NewTicker(updateInterval) // 24 hours
cfg.loadLastUpdate()
cfg.update()
for {
select {
case <-task.Context().Done():
return
case <-ticker.C:
cfg.update()
}
}
}
Thread Safety
The database uses a read-write mutex:
type MaxMind struct {
*Config
db struct {
*maxminddb.Reader
sync.RWMutex
}
}
// Lookups use RLock
func (cfg *MaxMind) lookup(ip net.IP) (*maxminddb.City, error) {
cfg.db.RLock()
defer cfg.db.RUnlock()
return cfg.db.Lookup(ip)
}
Configuration
Environment Variables
| Variable | Description |
|---|---|
MAXMIND_ACCOUNT_ID |
MaxMind account ID |
MAXMIND_LICENSE_KEY |
MaxMind license key |
YAML Configuration
providers:
maxmind:
database: geolite2
account_id: 123456
license_key: your-license-key
Integration Points
The maxmind package integrates with:
- ACL: IP-based access control (country/timezone matching)
- Config: Configuration management
- Logging: Update notifications
- City Cache: IP geolocation caching
Error Handling
var (
ErrResponseNotOK = gperr.New("response not OK")
ErrDownloadFailure = gperr.New("download failure")
)
Performance Considerations
- 24-hour update interval reduces unnecessary downloads
- Database size ~10-30MB
- City lookup cache reduces database queries
- RLock for concurrent reads