fix(deps): update module github.com/oschwald/geoip2-golang/v2 to v2.1.0 (main) (#20357)

Signed-off-by: renovate-sh-app[bot] <219655108+renovate-sh-app[bot]@users.noreply.github.com>
Co-authored-by: renovate-sh-app[bot] <219655108+renovate-sh-app[bot]@users.noreply.github.com>
This commit is contained in:
renovate-sh-app[bot]
2026-01-08 10:02:37 -05:00
committed by GitHub
parent f53a609be4
commit 8853d710d4
10 changed files with 309 additions and 18 deletions

2
go.mod
View File

@@ -79,7 +79,7 @@ require (
github.com/opentracing-contrib/go-grpc v0.1.2 // indirect
github.com/opentracing-contrib/go-stdlib v1.1.0 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b
github.com/oschwald/geoip2-golang/v2 v2.0.1
github.com/oschwald/geoip2-golang/v2 v2.1.0
// github.com/pierrec/lz4 v2.0.5+incompatible
github.com/pierrec/lz4/v4 v4.1.22
github.com/pkg/errors v0.9.1

4
go.sum
View File

@@ -941,8 +941,8 @@ github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mo
github.com/oracle/oci-go-sdk/v65 v65.41.1 h1:+lbosOyNiib3TGJDvLq1HwEAuFqkOjPJDIkyxM15WdQ=
github.com/oracle/oci-go-sdk/v65 v65.41.1/go.mod h1:MXMLMzHnnd9wlpgadPkdlkZ9YrwQmCOmbX5kjVEJodw=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/oschwald/geoip2-golang/v2 v2.0.1 h1:YcYoG/L+gmSfk7AlToTmoL0JvblNyhGC8NyVhwDzzi8=
github.com/oschwald/geoip2-golang/v2 v2.0.1/go.mod h1:qdVmcPgrTJ4q2eP9tHq/yldMTdp2VMr33uVdFbHBiBc=
github.com/oschwald/geoip2-golang/v2 v2.1.0 h1:DjnLhNJu9WHwTrmoiQFvgmyJoczhdnm7LB23UBI2Amo=
github.com/oschwald/geoip2-golang/v2 v2.1.0/go.mod h1:qdVmcPgrTJ4q2eP9tHq/yldMTdp2VMr33uVdFbHBiBc=
github.com/oschwald/maxminddb-golang/v2 v2.1.1 h1:lA8FH0oOrM4u7mLvowq8IT6a3Q/qEnqRzLQn9eH5ojc=
github.com/oschwald/maxminddb-golang/v2 v2.1.1/go.mod h1:PLdx6PR+siSIoXqqy7C7r3SB3KZnhxWr1Dp6g0Hacl8=
github.com/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE=

View File

@@ -1,5 +1,16 @@
# Changes
## 2.1.0 - 2025-12-22
- Added support for the GeoIP Anonymous Plus database. This database provides
VPN detection with confidence scoring, provider identification, and temporal
tracking via the new `AnonymousPlus()` method.
- Deprecated `IsLegitimateProxy` on `EnterpriseTraits`. MaxMind has deprecated
this field and it will be removed in the next major release.
- Deprecated `StaticIPScore` on `EnterpriseTraits`. This field was added in
error and has never been populated. It will be removed in the next major
release.
## 2.0.1 - 2025-11-26
- Upgraded `github.com/oschwald/geoip2-golang/v2` to 2.1.1, which fixes an

View File

@@ -321,6 +321,64 @@ func main() {
}
```
### Anonymous Plus Database
The Anonymous Plus database extends the Anonymous IP database with additional
fields for confidence scoring, provider identification, and temporal tracking.
```go
package main
import (
"fmt"
"log"
"net/netip"
"github.com/oschwald/geoip2-golang/v2"
)
func main() {
db, err := geoip2.Open("GeoIP-Anonymous-Plus.mmdb")
if err != nil {
log.Fatal(err)
}
defer db.Close()
ip, err := netip.ParseAddr("1.2.0.1")
if err != nil {
log.Fatal(err)
}
record, err := db.AnonymousPlus(ip)
if err != nil {
log.Fatal(err)
}
if !record.HasData() {
fmt.Println("No data found for this IP")
return
}
// Standard anonymous IP flags
fmt.Printf("Is Anonymous: %v\n", record.IsAnonymous)
fmt.Printf("Is Anonymous VPN: %v\n", record.IsAnonymousVPN)
fmt.Printf("Is Hosting Provider: %v\n", record.IsHostingProvider)
fmt.Printf("Is Public Proxy: %v\n", record.IsPublicProxy)
fmt.Printf("Is Residential Proxy: %v\n", record.IsResidentialProxy)
fmt.Printf("Is Tor Exit Node: %v\n", record.IsTorExitNode)
// Anonymous Plus specific fields
fmt.Printf("Anonymizer Confidence: %v\n", record.AnonymizerConfidence)
fmt.Printf("Provider Name: %v\n", record.ProviderName)
if !record.NetworkLastSeen.IsZero() {
fmt.Printf("Network Last Seen: %v\n", record.NetworkLastSeen.Format("2006-01-02"))
}
fmt.Printf("Network: %v\n", record.Network)
fmt.Printf("IP Address: %v\n", record.IPAddress)
}
```
### Enterprise Database
The Enterprise database provides the most comprehensive data, including all
@@ -374,9 +432,7 @@ func main() {
fmt.Printf("Connection Type: %v\n", record.Traits.ConnectionType)
fmt.Printf("Domain: %v\n", record.Traits.Domain)
fmt.Printf("User Type: %v\n", record.Traits.UserType)
fmt.Printf("Static IP Score: %v\n", record.Traits.StaticIPScore)
fmt.Printf("Is Anycast: %v\n", record.Traits.IsAnycast)
fmt.Printf("Is Legitimate Proxy: %v\n", record.Traits.IsLegitimateProxy)
// Mobile carrier information (if available)
if record.Traits.MobileCountryCode != "" {

View File

@@ -1,6 +1,43 @@
package geoip2
import "net/netip"
import (
"fmt"
"net/netip"
"time"
"github.com/oschwald/maxminddb-golang/v2/mmdbdata"
)
// Date represents a date value that is stored as an ISO 8601 string
// in the database but exposed as a time.Time.
type Date struct {
time.Time
}
// UnmarshalMaxMindDB implements the mmdbdata.Unmarshaler interface.
func (d *Date) UnmarshalMaxMindDB(decoder *mmdbdata.Decoder) error {
s, err := decoder.ReadString()
if err != nil {
return err
}
if s == "" {
return nil
}
t, err := time.Parse(time.DateOnly, s)
if err != nil {
return fmt.Errorf("parsing date %q: %w", s, err)
}
d.Time = t
return nil
}
// MarshalJSON implements json.Marshaler to serialize as ISO date string.
func (d Date) MarshalJSON() ([]byte, error) { //nolint:unparam // error required by json.Marshaler
if d.IsZero() {
return []byte("null"), nil
}
return []byte(`"` + d.Format(time.DateOnly) + `"`), nil
}
// Names contains localized names for geographic entities.
type Names struct {
@@ -35,6 +72,9 @@ var (
zeroEnterprisePostal EnterprisePostal
zeroEnterpriseSubdivision EnterpriseSubdivision
zeroEnterpriseCountryRecord EnterpriseCountryRecord
zeroEnterpriseTraits EnterpriseTraits
zeroCityTraits CityTraits
zeroCountryTraits CountryTraits
)
// HasData returns true if the Names struct has any localized names.
@@ -227,8 +267,9 @@ type EnterpriseTraits struct {
// UserType indicates the user type associated with the IP address
// (business, cafe, cellular, college, etc.)
UserType string `json:"user_type,omitzero" maxminddb:"user_type"`
// StaticIPScore is an indicator of how static or dynamic an IP address
// is, ranging from 0 to 99.99
// StaticIPScore was added in error and has never been populated.
//
// Deprecated: This field will be removed in the next major release.
StaticIPScore float64 `json:"static_ip_score,omitzero" maxminddb:"static_ip_score"`
// AutonomousSystemNumber for the IP address
AutonomousSystemNumber uint `json:"autonomous_system_number,omitzero" maxminddb:"autonomous_system_number"`
@@ -236,18 +277,19 @@ type EnterpriseTraits struct {
// See https://en.wikipedia.org/wiki/Anycast
IsAnycast bool `json:"is_anycast,omitzero" maxminddb:"is_anycast"`
// IsLegitimateProxy is true if MaxMind believes this IP address to be a
// legitimate proxy, such as an internal VPN used by a corporation
// legitimate proxy, such as an internal VPN used by a corporation.
//
// Deprecated: MaxMind has deprecated this field. It will be removed in
// the next major release.
IsLegitimateProxy bool `json:"is_legitimate_proxy,omitzero" maxminddb:"is_legitimate_proxy"`
}
// HasData returns true if the EnterpriseTraits has any data (excluding Network and IPAddress).
func (t EnterpriseTraits) HasData() bool {
return t.AutonomousSystemOrganization != "" || t.ConnectionType != "" ||
t.Domain != "" || t.ISP != "" || t.MobileCountryCode != "" ||
t.MobileNetworkCode != "" || t.Organization != "" ||
t.UserType != "" || t.StaticIPScore != 0 ||
t.AutonomousSystemNumber != 0 || t.IsAnycast ||
t.IsLegitimateProxy
cmp := t
cmp.Network = zeroEnterpriseTraits.Network
cmp.IPAddress = zeroEnterpriseTraits.IPAddress
return cmp != zeroEnterpriseTraits
}
// City/Country-specific types
@@ -323,7 +365,10 @@ type CityTraits struct {
// HasData returns true if the CityTraits has any data (excluding Network and IPAddress).
func (t CityTraits) HasData() bool {
return t.IsAnycast
cmp := t
cmp.Network = zeroCityTraits.Network
cmp.IPAddress = zeroCityTraits.IPAddress
return cmp != zeroCityTraits
}
// CountryTraits contains traits data for Country database records.
@@ -340,7 +385,10 @@ type CountryTraits struct {
// HasData returns true if the CountryTraits has any data (excluding Network and IPAddress).
func (t CountryTraits) HasData() bool {
return t.IsAnycast
cmp := t
cmp.Network = zeroCountryTraits.Network
cmp.IPAddress = zeroCountryTraits.IPAddress
return cmp != zeroCountryTraits
}
// The Enterprise struct corresponds to the data in the GeoIP2 Enterprise
@@ -507,6 +555,56 @@ func (a AnonymousIP) HasData() bool {
a.IsPublicProxy || a.IsResidentialProxy || a.IsTorExitNode
}
// The AnonymousPlus struct corresponds to the data in the GeoIP Anonymous Plus
// database. This database provides VPN detection with confidence scoring,
// provider identification, and temporal tracking.
type AnonymousPlus struct {
// IPAddress is the IP address used during the lookup
IPAddress netip.Addr `json:"ip_address,omitzero"`
// Network is the largest network prefix where all fields besides
// IPAddress have the same value.
Network netip.Prefix `json:"network,omitzero"`
// NetworkLastSeen is the last day the network was sighted in anonymized
// network analysis.
NetworkLastSeen Date `json:"network_last_seen,omitzero" maxminddb:"network_last_seen"`
// ProviderName is the name of the VPN provider (e.g., "NordVPN", "SurfShark").
ProviderName string `json:"provider_name,omitzero" maxminddb:"provider_name"`
// AnonymizerConfidence is a score from 1 to 99 indicating the confidence
// that the network is part of an actively used VPN.
AnonymizerConfidence uint16 `json:"anonymizer_confidence,omitzero" maxminddb:"anonymizer_confidence"`
// IsAnonymous is true if the IP address belongs to any sort of anonymous network.
IsAnonymous bool `json:"is_anonymous,omitzero" maxminddb:"is_anonymous"`
// IsAnonymousVPN is true if the IP address is registered to an anonymous
// VPN provider. If a VPN provider does not register subnets under names
// associated with them, we will likely only flag their IP ranges using the
// IsHostingProvider attribute.
IsAnonymousVPN bool `json:"is_anonymous_vpn,omitzero" maxminddb:"is_anonymous_vpn"`
// IsHostingProvider is true if the IP address belongs to a hosting or VPN provider
// (see description of IsAnonymousVPN attribute).
IsHostingProvider bool `json:"is_hosting_provider,omitzero" maxminddb:"is_hosting_provider"`
// IsPublicProxy is true if the IP address belongs to a public proxy.
IsPublicProxy bool `json:"is_public_proxy,omitzero" maxminddb:"is_public_proxy"`
// IsResidentialProxy is true if the IP address is on a suspected
// anonymizing network and belongs to a residential ISP.
IsResidentialProxy bool `json:"is_residential_proxy,omitzero" maxminddb:"is_residential_proxy"`
// IsTorExitNode is true if the IP address is a Tor exit node.
IsTorExitNode bool `json:"is_tor_exit_node,omitzero" maxminddb:"is_tor_exit_node"`
}
// HasData returns true if any data was found for the IP in the AnonymousPlus database.
// This excludes the Network and IPAddress fields which are always populated for found IPs.
func (a AnonymousPlus) HasData() bool {
return a.AnonymizerConfidence != 0 ||
a.IsAnonymous ||
a.IsAnonymousVPN ||
a.IsHostingProvider ||
a.IsPublicProxy ||
a.IsResidentialProxy ||
a.IsTorExitNode ||
!a.NetworkLastSeen.IsZero() ||
a.ProviderName != ""
}
// The ASN struct corresponds to the data in the GeoLite2 ASN database.
type ASN struct {
// IPAddress is the IP address used during the lookup

View File

@@ -62,6 +62,7 @@ type databaseType int
const (
isAnonymousIP = 1 << iota
isAnonymousPlus
isASN
isCity
isConnectionType
@@ -146,6 +147,8 @@ func OpenBytes(bytes []byte, options ...Option) (*Reader, error) {
func getDBType(reader *maxminddb.Reader) (databaseType, error) {
switch reader.Metadata.DatabaseType {
case "GeoIP-Anonymous-Plus":
return isAnonymousPlus, nil
case "GeoIP2-Anonymous-IP":
return isAnonymousIP, nil
case "DBIP-ASN-Lite (compat=GeoLite2-ASN)",
@@ -256,6 +259,24 @@ func (r *Reader) AnonymousIP(ipAddress netip.Addr) (*AnonymousIP, error) {
return &anonIP, nil
}
// AnonymousPlus takes an IP address as a netip.Addr and returns an
// AnonymousPlus struct and/or an error. This is intended to be used with
// the GeoIP Anonymous Plus database.
func (r *Reader) AnonymousPlus(ipAddress netip.Addr) (*AnonymousPlus, error) {
if isAnonymousPlus&r.databaseType == 0 {
return nil, InvalidMethodError{"AnonymousPlus", r.Metadata().DatabaseType}
}
result := r.mmdbReader.Lookup(ipAddress)
var anonPlus AnonymousPlus
err := result.Decode(&anonPlus)
if err != nil {
return &anonPlus, err
}
anonPlus.IPAddress = ipAddress
anonPlus.Network = result.Prefix()
return &anonPlus, nil
}
// ASN takes an IP address as a netip.Addr and returns a ASN struct and/or
// an error.
func (r *Reader) ASN(ipAddress netip.Addr) (*ASN, error) {

View File

@@ -0,0 +1,57 @@
// Package mmdbdata provides low-level types and interfaces for custom MaxMind DB decoding.
//
// This package allows custom decoding logic for applications that need fine-grained
// control over how MaxMind DB data is processed. For most use cases, the high-level
// maxminddb.Reader API is recommended instead.
//
// # Manual Decoding Example
//
// Custom types can implement the Unmarshaler interface for custom decoding:
//
// type City struct {
// Names map[string]string `maxminddb:"names"`
// GeoNameID uint `maxminddb:"geoname_id"`
// }
//
// func (c *City) UnmarshalMaxMindDB(d *mmdbdata.Decoder) error {
// mapIter, _, err := d.ReadMap()
// if err != nil { return err }
// for key, err := range mapIter {
// if err != nil { return err }
// switch string(key) {
// case "names":
// nameIter, size, err := d.ReadMap()
// if err != nil { return err }
// names := make(map[string]string, size) // Pre-allocate with size
// for nameKey, nameErr := range nameIter {
// if nameErr != nil { return nameErr }
// value, valueErr := d.ReadString()
// if valueErr != nil { return valueErr }
// names[string(nameKey)] = value
// }
// c.Names = names
// case "geoname_id":
// geoID, err := d.ReadUint32()
// if err != nil { return err }
// c.GeoNameID = uint(geoID)
// default:
// if err := d.SkipValue(); err != nil { return err }
// }
// }
// return nil
// }
//
// Types implementing Unmarshaler will automatically use custom decoding logic
// instead of reflection when used with maxminddb.Reader.Lookup, similar to how
// json.Unmarshaler works with encoding/json.
//
// # Direct Decoder Usage
//
// For even more control, you can use the Decoder directly:
//
// decoder := mmdbdata.NewDecoder(buffer, offset)
// value, err := decoder.ReadString()
// if err != nil {
// return err
// }
package mmdbdata

View File

@@ -0,0 +1,7 @@
package mmdbdata
// Unmarshaler is implemented by types that can unmarshal MaxMind DB data.
// This follows the same pattern as json.Unmarshaler and other Go standard library interfaces.
type Unmarshaler interface {
UnmarshalMaxMindDB(d *Decoder) error
}

View File

@@ -0,0 +1,40 @@
package mmdbdata
import "github.com/oschwald/maxminddb-golang/v2/internal/decoder"
// Kind represents MMDB data kinds.
type Kind = decoder.Kind
// Decoder provides methods for decoding MMDB data.
type Decoder = decoder.Decoder
// DecoderOption configures a Decoder.
type DecoderOption = decoder.DecoderOption
// NewDecoder creates a new Decoder with the given buffer, offset, and options.
// Error messages automatically include contextual information like offset and
// path (e.g., "/city/names/en") with zero impact on successful operations.
func NewDecoder(buffer []byte, offset uint, options ...DecoderOption) *Decoder {
d := decoder.NewDataDecoder(buffer)
return decoder.NewDecoder(d, offset, options...)
}
// Kind constants for MMDB data.
const (
KindExtended = decoder.KindExtended
KindPointer = decoder.KindPointer
KindString = decoder.KindString
KindFloat64 = decoder.KindFloat64
KindBytes = decoder.KindBytes
KindUint16 = decoder.KindUint16
KindUint32 = decoder.KindUint32
KindMap = decoder.KindMap
KindInt32 = decoder.KindInt32
KindUint64 = decoder.KindUint64
KindUint128 = decoder.KindUint128
KindSlice = decoder.KindSlice
KindContainer = decoder.KindContainer
KindEndMarker = decoder.KindEndMarker
KindBool = decoder.KindBool
KindFloat32 = decoder.KindFloat32
)

3
vendor/modules.txt vendored
View File

@@ -1653,7 +1653,7 @@ github.com/opentracing-contrib/go-stdlib/nethttp
github.com/opentracing/opentracing-go
github.com/opentracing/opentracing-go/ext
github.com/opentracing/opentracing-go/log
# github.com/oschwald/geoip2-golang/v2 v2.0.1
# github.com/oschwald/geoip2-golang/v2 v2.1.0
## explicit; go 1.24.0
github.com/oschwald/geoip2-golang/v2
# github.com/oschwald/maxminddb-golang/v2 v2.1.1
@@ -1661,6 +1661,7 @@ github.com/oschwald/geoip2-golang/v2
github.com/oschwald/maxminddb-golang/v2
github.com/oschwald/maxminddb-golang/v2/internal/decoder
github.com/oschwald/maxminddb-golang/v2/internal/mmdberrors
github.com/oschwald/maxminddb-golang/v2/mmdbdata
# github.com/parquet-go/bitpack v0.2.0
## explicit; go 1.24.0
github.com/parquet-go/bitpack