mirror of
https://github.com/dcarrillo/whatismyip.git
synced 2026-07-22 11:04:46 +00:00
49ffcadb0a
High (behavioral bugs):
- service.Geo: add RLock on lookups, handle Reload error, keep old DBs on failure
- models.GeoDB.Reload: open new DBs before closing old ones (no reader gap)
- router: guard nil geo records (404 instead of 500) in geo.go, generic.go, dns.go
- router/port_scanner: reject undetermined client IPs (nil->localhost dial)
- internal/httputils: fix access log date layout (02/Jan/2006, not literal Nov)
- server/quic: log.Printf instead of log.Fatal in request handler
Medium:
- server/*: 10s shutdown timeout; SIGTERM drains servers before closing geo DBs
- httputils: Clone() request headers instead of mutating the live map; strings.Builder
- setting/app: handle all os.Stat errors; fix "truster" typos; use errors.New;
remove dead Token field
- cmd: errors.Is for sentinel checks; os.Stderr; log.Fatal instead of panic;
pass http.Handler by value (not pointer-to-interface)
- resolver: validate config at startup (pre-parse RRs, validate IPs); check WriteMsg
errors; Setup returns error
Low:
- .golangci.yaml: enable errcheck, govet, errorlint; fix all resulting findings
- server/tls: explicit TLSConfig{MinVersion: tls.VersionTLS12}
- router/dns: label-exact vhost suffix check, strip port; template.Must
- models: errors.Join instead of %s on []error
- service/geo_test: race-regression test (concurrent lookups during Reload)
- README: Go >= 1.25; fix latent test bug (store.Add->Set in dns_test.go)
135 lines
2.7 KiB
Go
135 lines
2.7 KiB
Go
package models
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
|
|
"github.com/oschwald/maxminddb-golang"
|
|
)
|
|
|
|
type GeoRecord struct {
|
|
Country struct {
|
|
ISOCode string `maxminddb:"iso_code"`
|
|
Names map[string]string `maxminddb:"names"`
|
|
} `maxminddb:"country"`
|
|
City struct {
|
|
Names map[string]string `maxminddb:"names"`
|
|
} `maxminddb:"city"`
|
|
Location struct {
|
|
Latitude float64 `maxminddb:"latitude"`
|
|
Longitude float64 `maxminddb:"longitude"`
|
|
TimeZone string `maxminddb:"time_zone"`
|
|
} `maxminddb:"location"`
|
|
Postal struct {
|
|
Code string `maxminddb:"code"`
|
|
} `maxminddb:"postal"`
|
|
}
|
|
|
|
type ASNRecord struct {
|
|
AutonomousSystemNumber uint `maxminddb:"autonomous_system_number"`
|
|
AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"`
|
|
}
|
|
|
|
type GeoDB struct {
|
|
cityPath string
|
|
asnPath string
|
|
City *maxminddb.Reader
|
|
ASN *maxminddb.Reader
|
|
}
|
|
|
|
func Setup(cityPath string, asnPath string) (*GeoDB, error) {
|
|
city, asn, err := openDatabases(cityPath, asnPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &GeoDB{
|
|
cityPath: cityPath,
|
|
asnPath: asnPath,
|
|
City: city,
|
|
ASN: asn,
|
|
}, nil
|
|
}
|
|
|
|
func (db *GeoDB) CloseDBs() error {
|
|
return closeReaders(db.City, db.ASN)
|
|
}
|
|
|
|
func (db *GeoDB) Reload() error {
|
|
city, asn, err := openDatabases(db.cityPath, db.asnPath)
|
|
if err != nil {
|
|
return fmt.Errorf("opening new databases: %w", err)
|
|
}
|
|
|
|
oldCity, oldASN := db.City, db.ASN
|
|
db.City, db.ASN = city, asn
|
|
|
|
if err := closeReaders(oldCity, oldASN); err != nil {
|
|
return fmt.Errorf("closing previous databases: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func closeReaders(city *maxminddb.Reader, asn *maxminddb.Reader) error {
|
|
var errs []error
|
|
|
|
if city != nil {
|
|
if err := city.Close(); err != nil {
|
|
errs = append(errs, fmt.Errorf("closing city db: %w", err))
|
|
}
|
|
}
|
|
|
|
if asn != nil {
|
|
if err := asn.Close(); err != nil {
|
|
errs = append(errs, fmt.Errorf("closing ASN db: %w", err))
|
|
}
|
|
}
|
|
|
|
return errors.Join(errs...)
|
|
}
|
|
|
|
func (db *GeoDB) LookupCity(ip net.IP) (*GeoRecord, error) {
|
|
record := &GeoRecord{}
|
|
err := db.City.Lookup(ip, record)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return record, nil
|
|
}
|
|
|
|
func (db *GeoDB) LookupASN(ip net.IP) (*ASNRecord, error) {
|
|
record := &ASNRecord{}
|
|
err := db.ASN.Lookup(ip, record)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return record, nil
|
|
}
|
|
|
|
func openDatabases(cityPath, asnPath string) (*maxminddb.Reader, *maxminddb.Reader, error) {
|
|
city, err := openMMDB(cityPath)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
asn, err := openMMDB(asnPath)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return city, asn, nil
|
|
}
|
|
|
|
func openMMDB(path string) (*maxminddb.Reader, error) {
|
|
db, err := maxminddb.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
log.Printf("Database %s has been loaded\n", path)
|
|
|
|
return db, nil
|
|
}
|