179 lines
4.8 KiB
Go
179 lines
4.8 KiB
Go
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||
|
||
package iplibrary
|
||
|
||
import (
|
||
_ "embed"
|
||
"fmt"
|
||
"github.com/iwind/TeaGo/Tea"
|
||
"github.com/iwind/TeaGo/logs"
|
||
"net"
|
||
"os"
|
||
"sync"
|
||
)
|
||
|
||
//go:embed GeoLite2-City.mmdb
|
||
var maxMindCityDBData []byte
|
||
|
||
//go:embed GeoLite2-ASN.mmdb
|
||
var maxMindASNDBData []byte
|
||
|
||
var defaultLibrary *IPLibrary
|
||
var commonLibrary *IPLibrary
|
||
|
||
var libraryLocker = &sync.Mutex{} // 为了保持加载顺序性
|
||
|
||
// InitDefault 加载默认的IP库
|
||
// 优先使用后台上传的 MaxMind 文件,如果没有上传则使用代码中嵌入的 MaxMind 库
|
||
func InitDefault() error {
|
||
libraryLocker.Lock()
|
||
defer libraryLocker.Unlock()
|
||
|
||
// 1. 优先检查后台上传的 MaxMind 文件(data/iplibrary/ 目录)
|
||
iplibDir := Tea.Root + "/data/iplibrary"
|
||
cityDBPath := iplibDir + "/maxmind-city.mmdb"
|
||
asnDBPath := iplibDir + "/maxmind-asn.mmdb"
|
||
|
||
// 检查上传的文件是否存在
|
||
uploadedFileExists := false
|
||
if _, err := os.Stat(cityDBPath); err == nil {
|
||
uploadedFileExists = true
|
||
}
|
||
|
||
// 如果 commonLibrary 已存在,需要检查是否应该重新加载
|
||
if commonLibrary != nil {
|
||
if commonLibrary.reader != nil {
|
||
// 检查是否是 MaxMindReader
|
||
_, isMaxMind := commonLibrary.reader.(*MaxMindReader)
|
||
if isMaxMind {
|
||
// 如果之前使用的是上传的文件,但现在文件不存在了,需要重新加载
|
||
// 如果之前使用的是嵌入的库,且现在也没有上传的文件,可以继续使用
|
||
// 这里通过检查文件是否存在来判断:如果文件存在,且库已加载,直接使用
|
||
if uploadedFileExists {
|
||
defaultLibrary = commonLibrary
|
||
return nil
|
||
}
|
||
// 文件不存在了,需要重新加载(使用嵌入的库)
|
||
commonLibrary.Destroy()
|
||
commonLibrary = nil
|
||
defaultLibrary = nil
|
||
} else {
|
||
// 不是 MaxMind 库,销毁并重新初始化
|
||
commonLibrary.Destroy()
|
||
commonLibrary = nil
|
||
defaultLibrary = nil
|
||
}
|
||
} else {
|
||
commonLibrary = nil
|
||
defaultLibrary = nil
|
||
}
|
||
}
|
||
|
||
if uploadedFileExists {
|
||
// 检查 ASN 文件是否存在
|
||
asnPath := ""
|
||
if _, err := os.Stat(asnDBPath); err == nil {
|
||
asnPath = asnDBPath
|
||
}
|
||
reader, err := NewMaxMindReader(cityDBPath, asnPath)
|
||
if err == nil {
|
||
defaultLibrary = NewIPLibraryWithReader(reader)
|
||
commonLibrary = defaultLibrary
|
||
logs.Println("[IP_LIBRARY]uploaded MaxMind database loaded successfully from: " + cityDBPath)
|
||
return nil
|
||
}
|
||
// 上传的文件加载失败,继续使用嵌入的库
|
||
logs.Println("[IP_LIBRARY]failed to load uploaded MaxMind database: " + err.Error() + ", will use embedded database")
|
||
}
|
||
|
||
// 2. 使用嵌入的 MaxMind 数据库作为默认
|
||
if len(maxMindCityDBData) == 0 {
|
||
return fmt.Errorf("embedded MaxMind database is empty (this should not happen if build is correct), please upload MaxMind database file or rebuild the application")
|
||
}
|
||
|
||
reader, err := NewMaxMindReaderFromBytes(maxMindCityDBData, maxMindASNDBData)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to load embedded MaxMind database: %w", err)
|
||
}
|
||
|
||
defaultLibrary = NewIPLibraryWithReader(reader)
|
||
commonLibrary = defaultLibrary
|
||
logs.Println("[IP_LIBRARY]embedded MaxMind database loaded successfully (size: " + fmt.Sprintf("%d", len(maxMindCityDBData)) + " bytes)")
|
||
return nil
|
||
}
|
||
|
||
// Lookup 查询IP信息
|
||
func Lookup(ip net.IP) *QueryResult {
|
||
if defaultLibrary == nil {
|
||
// 如果 defaultLibrary 未初始化,尝试初始化
|
||
_ = InitDefault()
|
||
if defaultLibrary == nil {
|
||
return &QueryResult{}
|
||
}
|
||
}
|
||
return defaultLibrary.Lookup(ip)
|
||
}
|
||
|
||
// LookupIP 查询IP信息
|
||
func LookupIP(ip string) *QueryResult {
|
||
if defaultLibrary == nil {
|
||
// 如果 defaultLibrary 未初始化,尝试初始化
|
||
_ = InitDefault()
|
||
if defaultLibrary == nil {
|
||
return &QueryResult{}
|
||
}
|
||
}
|
||
return defaultLibrary.LookupIP(ip)
|
||
}
|
||
|
||
// LookupIPSummaries 查询一组IP对应的区域描述
|
||
func LookupIPSummaries(ipList []string) map[string]string /** ip => summary **/ {
|
||
var result = map[string]string{}
|
||
for _, ip := range ipList {
|
||
var region = LookupIP(ip)
|
||
if region != nil && region.IsOk() {
|
||
result[ip] = region.Summary()
|
||
}
|
||
}
|
||
return result
|
||
}
|
||
|
||
type IPLibrary struct {
|
||
reader ReaderInterface
|
||
}
|
||
|
||
func NewIPLibrary() *IPLibrary {
|
||
return &IPLibrary{}
|
||
}
|
||
|
||
func NewIPLibraryWithReader(reader ReaderInterface) *IPLibrary {
|
||
return &IPLibrary{reader: reader}
|
||
}
|
||
|
||
func (this *IPLibrary) Lookup(ip net.IP) *QueryResult {
|
||
if this.reader == nil {
|
||
return &QueryResult{}
|
||
}
|
||
|
||
var result = this.reader.Lookup(ip)
|
||
if result == nil {
|
||
result = &QueryResult{}
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
func (this *IPLibrary) LookupIP(ip string) *QueryResult {
|
||
if this.reader == nil {
|
||
return &QueryResult{}
|
||
}
|
||
return this.Lookup(net.ParseIP(ip))
|
||
}
|
||
|
||
func (this *IPLibrary) Destroy() {
|
||
if this.reader != nil {
|
||
this.reader.Destroy()
|
||
this.reader = nil
|
||
}
|
||
}
|