181 lines
3.9 KiB
Go
181 lines
3.9 KiB
Go
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||
|
||
package iplibrary
|
||
|
||
import (
|
||
"fmt"
|
||
"github.com/oschwald/geoip2-golang"
|
||
"net"
|
||
"os"
|
||
"sync"
|
||
)
|
||
|
||
// MaxMindReader MaxMind GeoIP2 Reader
|
||
type MaxMindReader struct {
|
||
db *geoip2.Reader
|
||
dbASN *geoip2.Reader // ASN 数据库(可选)
|
||
meta *Meta
|
||
initialized bool
|
||
mutex sync.RWMutex
|
||
|
||
// 临时文件路径,Destroy 时自动清理
|
||
tmpFiles []string
|
||
}
|
||
|
||
// NewMaxMindReader 创建 MaxMind Reader
|
||
// 参数:
|
||
// - cityDBPath: City 数据库路径(包含 Country 和 City 信息)
|
||
// - asnDBPath: ASN 数据库路径(可选,用于获取 ISP 信息)
|
||
func NewMaxMindReader(cityDBPath, asnDBPath string) (*MaxMindReader, error) {
|
||
if len(cityDBPath) == 0 {
|
||
return nil, fmt.Errorf("city database path is required")
|
||
}
|
||
|
||
// 打开 City 数据库(包含 Country 信息)
|
||
db, err := geoip2.Open(cityDBPath)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("open MaxMind city database failed: %w", err)
|
||
}
|
||
|
||
reader := &MaxMindReader{
|
||
db: db,
|
||
}
|
||
|
||
// 打开 ASN 数据库(可选)
|
||
if len(asnDBPath) > 0 {
|
||
dbASN, err := geoip2.Open(asnDBPath)
|
||
if err == nil {
|
||
reader.dbASN = dbASN
|
||
}
|
||
// 如果 ASN 数据库打开失败,不影响主功能,只记录错误
|
||
}
|
||
|
||
// 初始化 Meta
|
||
reader.meta = reader.initMeta()
|
||
|
||
reader.initialized = true
|
||
|
||
return reader, nil
|
||
}
|
||
|
||
// NewMaxMindReaderFromBytes 从字节数据创建 MaxMind Reader
|
||
// 参数:
|
||
// - cityDBData: City 数据库字节数据(包含 Country 和 City 信息)
|
||
// - asnDBData: ASN 数据库字节数据(可选,用于获取 ISP 信息)
|
||
func NewMaxMindReaderFromBytes(cityDBData, asnDBData []byte) (*MaxMindReader, error) {
|
||
if len(cityDBData) == 0 {
|
||
return nil, fmt.Errorf("city database data is required")
|
||
}
|
||
|
||
// 直接从内存字节加载,避免在 /tmp 持续生成 mmdb 临时文件。
|
||
db, err := geoip2.FromBytes(cityDBData)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("open MaxMind city database failed: %w", err)
|
||
}
|
||
|
||
reader := &MaxMindReader{
|
||
db: db,
|
||
}
|
||
|
||
// 加载 ASN 数据库(可选)
|
||
if len(asnDBData) > 0 {
|
||
dbASN, err := geoip2.FromBytes(asnDBData)
|
||
if err == nil {
|
||
reader.dbASN = dbASN
|
||
}
|
||
}
|
||
|
||
// 初始化 Meta
|
||
reader.meta = reader.initMeta()
|
||
|
||
reader.initialized = true
|
||
|
||
return reader, nil
|
||
}
|
||
|
||
// initMeta 初始化 Meta
|
||
func (this *MaxMindReader) initMeta() *Meta {
|
||
meta := &Meta{
|
||
Version: 3, // MaxMind 版本
|
||
Code: "maxmind",
|
||
Author: "MaxMind",
|
||
Countries: []*Country{},
|
||
Provinces: []*Province{},
|
||
Cities: []*City{},
|
||
Towns: []*Town{},
|
||
Providers: []*Provider{},
|
||
CreatedAt: 0,
|
||
}
|
||
meta.Init()
|
||
return meta
|
||
}
|
||
|
||
// Lookup 查询 IP 信息
|
||
func (this *MaxMindReader) Lookup(ip net.IP) *QueryResult {
|
||
if !this.initialized || this.db == nil {
|
||
return &QueryResult{}
|
||
}
|
||
|
||
this.mutex.RLock()
|
||
defer this.mutex.RUnlock()
|
||
|
||
// 查询 City 数据库
|
||
record, err := this.db.City(ip)
|
||
if err != nil {
|
||
return &QueryResult{}
|
||
}
|
||
|
||
// 构建查询结果
|
||
result := &QueryResult{
|
||
item: &maxMindItem{
|
||
Record: *record,
|
||
},
|
||
meta: this.meta,
|
||
}
|
||
|
||
// 如果有 ASN 数据库,查询 ASN 信息
|
||
if this.dbASN != nil {
|
||
asnRecord, err := this.dbASN.ASN(ip)
|
||
if err == nil {
|
||
if maxItem, ok := result.item.(*maxMindItem); ok {
|
||
maxItem.ASN = asnRecord.AutonomousSystemNumber
|
||
maxItem.ASNOrg = asnRecord.AutonomousSystemOrganization
|
||
}
|
||
}
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
// Meta 获取 Meta 信息
|
||
func (this *MaxMindReader) Meta() *Meta {
|
||
return this.meta
|
||
}
|
||
|
||
// Destroy 销毁 Reader 并清理临时文件
|
||
func (this *MaxMindReader) Destroy() {
|
||
this.mutex.Lock()
|
||
defer this.mutex.Unlock()
|
||
|
||
if this.db != nil {
|
||
this.db.Close()
|
||
this.db = nil
|
||
}
|
||
if this.dbASN != nil {
|
||
this.dbASN.Close()
|
||
this.dbASN = nil
|
||
}
|
||
for _, f := range this.tmpFiles {
|
||
os.Remove(f)
|
||
}
|
||
this.tmpFiles = nil
|
||
this.initialized = false
|
||
}
|
||
|
||
// maxMindItem MaxMind 查询结果项
|
||
type maxMindItem struct {
|
||
Record geoip2.City
|
||
ASN uint
|
||
ASNOrg string
|
||
}
|