mirror of
https://gitee.com/samwaf/SamWaf.git
synced 2026-09-01 15:32:55 +08:00
@@ -53,6 +53,7 @@ type APIGroup struct {
|
||||
WafFirewallIPBlockApi
|
||||
WafPluginApi
|
||||
WafLogFileWriteApi
|
||||
WafIPLocationApi
|
||||
}
|
||||
|
||||
var APIGroupAPP = new(APIGroup)
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"SamWaf/global"
|
||||
"SamWaf/iplocation"
|
||||
"SamWaf/model/common/response"
|
||||
"SamWaf/utils"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type WafIPLocationApi struct {
|
||||
}
|
||||
|
||||
// GetIPDBStatusApi 获取 IP 数据库状态
|
||||
func (w *WafIPLocationApi) GetIPDBStatusApi(c *gin.Context) {
|
||||
if global.GIPLOCATION_MANAGER == nil {
|
||||
response.FailWithMessage("IP 数据库管理器未初始化", c)
|
||||
return
|
||||
}
|
||||
|
||||
status := global.GIPLOCATION_MANAGER.GetStatus()
|
||||
|
||||
// 获取文件的实际创建时间
|
||||
dataDir := filepath.Join(utils.GetCurrentDir(), "data")
|
||||
|
||||
// 获取 IPv4 文件创建时间
|
||||
var ipv4FilePath string
|
||||
if status.IPv4Source == "ip2region" {
|
||||
ipv4FilePath = filepath.Join(dataDir, "ip2region.xdb")
|
||||
} else if status.IPv4Source == "geolite2" {
|
||||
ipv4FilePath = filepath.Join(dataDir, "GeoLite2-Country.mmdb")
|
||||
}
|
||||
if ipv4FilePath != "" {
|
||||
if fileInfo, err := os.Stat(ipv4FilePath); err == nil {
|
||||
status.IPv4CreateTime = fileInfo.ModTime().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 IPv6 文件创建时间
|
||||
var ipv6FilePath string
|
||||
if status.IPv6Source == "ip2region" {
|
||||
ipv6FilePath = filepath.Join(dataDir, "ip2region_v6.xdb")
|
||||
} else if status.IPv6Source == "geolite2" {
|
||||
ipv6FilePath = filepath.Join(dataDir, "GeoLite2-Country.mmdb")
|
||||
}
|
||||
if ipv6FilePath != "" {
|
||||
if fileInfo, err := os.Stat(ipv6FilePath); err == nil {
|
||||
status.IPv6CreateTime = fileInfo.ModTime().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
}
|
||||
|
||||
response.OkWithDetailed(status, "获取成功", c)
|
||||
}
|
||||
|
||||
// UploadIPDBFileApi 上传 IP 数据库文件
|
||||
func (w *WafIPLocationApi) UploadIPDBFileApi(c *gin.Context) {
|
||||
// 获取文件类型 (ipv4/ipv6)
|
||||
ipType := c.PostForm("type")
|
||||
if ipType != "ipv4" && ipType != "ipv6" {
|
||||
response.FailWithMessage("无效的类型参数,必须是 ipv4 或 ipv6", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取上传的文件
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
response.FailWithMessage("文件上传失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查文件扩展名
|
||||
ext := filepath.Ext(file.Filename)
|
||||
if ext != ".xdb" && ext != ".mmdb" {
|
||||
response.FailWithMessage("不支持的文件类型,仅支持 .xdb 和 .mmdb 文件", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 确定保存路径
|
||||
var finalPath string
|
||||
dataDir := filepath.Join(utils.GetCurrentDir(), "data")
|
||||
|
||||
// 确保 data 目录存在
|
||||
if _, err := os.Stat(dataDir); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dataDir, 0755)
|
||||
if err != nil {
|
||||
response.FailWithMessage("创建数据目录失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ipType == "ipv4" {
|
||||
if ext == ".xdb" {
|
||||
finalPath = filepath.Join(dataDir, "ip2region.xdb")
|
||||
} else {
|
||||
finalPath = filepath.Join(dataDir, "GeoLite2-Country.mmdb")
|
||||
}
|
||||
} else {
|
||||
if ext == ".xdb" {
|
||||
finalPath = filepath.Join(dataDir, "ip2region_v6.xdb")
|
||||
} else {
|
||||
finalPath = filepath.Join(dataDir, "GeoLite2-Country.mmdb")
|
||||
}
|
||||
}
|
||||
|
||||
// 先保存到临时文件
|
||||
tempPath := finalPath + ".tmp"
|
||||
err = c.SaveUploadedFile(file, tempPath)
|
||||
if err != nil {
|
||||
response.FailWithMessage("保存临时文件失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 读取临时文件内容
|
||||
fileData, err := ioutil.ReadFile(tempPath)
|
||||
if err != nil {
|
||||
os.Remove(tempPath) // 清理临时文件
|
||||
response.FailWithMessage("读取文件失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 先热加载到内存,验证文件有效性
|
||||
if global.GIPLOCATION_MANAGER != nil {
|
||||
var reloadErr error
|
||||
|
||||
if ipType == "ipv4" {
|
||||
if ext == ".xdb" {
|
||||
reloadErr = global.GIPLOCATION_MANAGER.LoadV4Ip2Region(fileData, iplocation.DBFormat(global.GCONFIG_IP_V4_FORMAT))
|
||||
if reloadErr == nil {
|
||||
global.GCONFIG_IP_V4_SOURCE = "ip2region"
|
||||
}
|
||||
} else {
|
||||
reloadErr = global.GIPLOCATION_MANAGER.LoadV4GeoLite2(fileData)
|
||||
if reloadErr == nil {
|
||||
global.GCONFIG_IP_V4_SOURCE = "geolite2"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ext == ".xdb" {
|
||||
reloadErr = global.GIPLOCATION_MANAGER.LoadV6Ip2Region(fileData, iplocation.DBFormat(global.GCONFIG_IP_V6_FORMAT))
|
||||
if reloadErr == nil {
|
||||
global.GCONFIG_IP_V6_SOURCE = "ip2region"
|
||||
}
|
||||
} else {
|
||||
reloadErr = global.GIPLOCATION_MANAGER.LoadV6GeoLite2(fileData)
|
||||
if reloadErr == nil {
|
||||
global.GCONFIG_IP_V6_SOURCE = "geolite2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if reloadErr != nil {
|
||||
os.Remove(tempPath) // 清理临时文件
|
||||
response.FailWithMessage("加载数据库失败: "+reloadErr.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 热加载成功后,原子替换正式文件
|
||||
err = os.Rename(tempPath, finalPath)
|
||||
if err != nil {
|
||||
os.Remove(tempPath) // 清理临时文件
|
||||
response.FailWithMessage("替换文件失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("文件上传成功并已重新加载", c)
|
||||
}
|
||||
|
||||
// ReloadIPDBApi 重新加载 IP 数据库
|
||||
func (w *WafIPLocationApi) ReloadIPDBApi(c *gin.Context) {
|
||||
if global.GIPLOCATION_MANAGER == nil {
|
||||
response.FailWithMessage("IP 数据库管理器未初始化", c)
|
||||
return
|
||||
}
|
||||
|
||||
dataDir := filepath.Join(utils.GetCurrentDir(), "data")
|
||||
|
||||
// 重新加载 IPv4
|
||||
if global.GCONFIG_IP_V4_SOURCE == "ip2region" {
|
||||
ipv4Path := filepath.Join(dataDir, "ip2region.xdb")
|
||||
if _, err := os.Stat(ipv4Path); err == nil {
|
||||
data, err := ioutil.ReadFile(ipv4Path)
|
||||
if err == nil {
|
||||
err = global.GIPLOCATION_MANAGER.LoadV4Ip2Region(data, iplocation.DBFormat(global.GCONFIG_IP_V4_FORMAT))
|
||||
if err != nil {
|
||||
response.FailWithMessage("重新加载 IPv4 数据库失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if global.GCONFIG_IP_V4_SOURCE == "geolite2" {
|
||||
ipv4Path := filepath.Join(dataDir, "GeoLite2-Country.mmdb")
|
||||
if _, err := os.Stat(ipv4Path); err == nil {
|
||||
data, err := ioutil.ReadFile(ipv4Path)
|
||||
if err == nil {
|
||||
err = global.GIPLOCATION_MANAGER.LoadV4GeoLite2(data)
|
||||
if err != nil {
|
||||
response.FailWithMessage("重新加载 IPv4 数据库失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重新加载 IPv6
|
||||
if global.GCONFIG_IP_V6_SOURCE == "ip2region" {
|
||||
ipv6Path := filepath.Join(dataDir, "ip2region_v6.xdb")
|
||||
if _, err := os.Stat(ipv6Path); err == nil {
|
||||
data, err := ioutil.ReadFile(ipv6Path)
|
||||
if err == nil {
|
||||
err = global.GIPLOCATION_MANAGER.LoadV6Ip2Region(data, iplocation.DBFormat(global.GCONFIG_IP_V6_FORMAT))
|
||||
if err != nil {
|
||||
response.FailWithMessage("重新加载 IPv6 数据库失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if global.GCONFIG_IP_V6_SOURCE == "geolite2" {
|
||||
ipv6Path := filepath.Join(dataDir, "GeoLite2-Country.mmdb")
|
||||
if _, err := os.Stat(ipv6Path); err == nil {
|
||||
data, err := ioutil.ReadFile(ipv6Path)
|
||||
if err == nil {
|
||||
err = global.GIPLOCATION_MANAGER.LoadV6GeoLite2(data)
|
||||
if err != nil {
|
||||
response.FailWithMessage("重新加载 IPv6 数据库失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.OkWithMessage("数据库重新加载成功", c)
|
||||
}
|
||||
|
||||
// TestIPLookupApi 测试 IP 查询
|
||||
func (w *WafIPLocationApi) TestIPLookupApi(c *gin.Context) {
|
||||
var req struct {
|
||||
IP string `json:"ip" binding:"required"`
|
||||
}
|
||||
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage("参数解析失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
if global.GIPLOCATION_MANAGER == nil {
|
||||
response.FailWithMessage("IP 数据库管理器未初始化", c)
|
||||
return
|
||||
}
|
||||
|
||||
result := global.GIPLOCATION_MANAGER.Lookup(req.IP)
|
||||
|
||||
resp := map[string]interface{}{
|
||||
"ip": req.IP,
|
||||
"country": result.Country,
|
||||
"province": result.Province,
|
||||
"city": result.City,
|
||||
"isp": result.ISP,
|
||||
"region": result.Region,
|
||||
"district": result.District,
|
||||
"raw": fmt.Sprintf("%v", result.ToSlice()),
|
||||
}
|
||||
|
||||
response.OkWithDetailed(resp, "查询成功", c)
|
||||
}
|
||||
+66
-20
@@ -7,6 +7,7 @@ import (
|
||||
"SamWaf/enums"
|
||||
"SamWaf/global"
|
||||
"SamWaf/globalobj"
|
||||
"SamWaf/iplocation"
|
||||
"SamWaf/model"
|
||||
"SamWaf/model/wafenginmodel"
|
||||
"SamWaf/plugin"
|
||||
@@ -149,38 +150,83 @@ func (m *wafSystenService) run() {
|
||||
zlog.Info("执行位置:", executablePath)
|
||||
global.GWAF_RUNTIME_CURRENT_EXEPATH = executablePath
|
||||
//初始化步骤[加载ip数据库]
|
||||
// 从嵌入的文件中读取内容
|
||||
// 创建 IP Location Manager
|
||||
global.GIPLOCATION_MANAGER = iplocation.NewManager()
|
||||
|
||||
// 拼接文件路径
|
||||
// 加载 IPv4 数据库
|
||||
ip2RegionFilePath := filepath.Join(utils.GetCurrentDir(), "data", "ip2region.xdb")
|
||||
// 检查文件是否存在
|
||||
var ipv4Data []byte
|
||||
if _, err := os.Stat(ip2RegionFilePath); os.IsNotExist(err) {
|
||||
global.GCACHE_IP_CBUFF = Ip2regionBytes
|
||||
// 使用内置数据
|
||||
ipv4Data = Ip2regionBytes
|
||||
zlog.Info("Using embedded IPv4 database, size: ", len(ipv4Data))
|
||||
} else {
|
||||
// 读取文件内容
|
||||
// 读取外部文件
|
||||
fileBytes, err := ioutil.ReadFile(ip2RegionFilePath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read IP database file ip2region.xdb: %v", err)
|
||||
}
|
||||
global.GCACHE_IP_CBUFF = fileBytes
|
||||
// 检查是否成功读取
|
||||
zlog.Info("IP database ip2region.xdb loaded into cache, size: ", len(global.GCACHE_IP_CBUFF), ip2RegionFilePath)
|
||||
ipv4Data = fileBytes
|
||||
zlog.Info("IPv4 database ip2region.xdb loaded from file, size: ", len(ipv4Data), ip2RegionFilePath)
|
||||
}
|
||||
|
||||
//检测是否存在IPV6得数据包
|
||||
ipv6RegionFilePath := filepath.Join(utils.GetCurrentDir(), "data", "GeoLite2-Country.mmdb")
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(ipv6RegionFilePath); os.IsNotExist(err) {
|
||||
global.GCACHE_IP_V6_COUNTRY_CBUFF = Ipv6CountryBytes
|
||||
} else {
|
||||
// 读取文件内容
|
||||
fileBytes, err := ioutil.ReadFile(ipv6RegionFilePath)
|
||||
// 根据配置加载 IPv4 数据库
|
||||
if global.GCONFIG_IP_V4_SOURCE == "ip2region" {
|
||||
err := global.GIPLOCATION_MANAGER.LoadV4Ip2Region(ipv4Data, iplocation.DBFormat(global.GCONFIG_IP_V4_FORMAT))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read IPv6 database file GeoLite2-Country.mmdb: %v", err)
|
||||
log.Fatalf("Failed to load IPv4 ip2region database: %v", err)
|
||||
}
|
||||
global.GCACHE_IP_V6_COUNTRY_CBUFF = fileBytes
|
||||
// 检查是否成功读取
|
||||
zlog.Info("IPv6 database file GeoLite2-Country.mmdb loaded into cache, size: ", len(global.GCACHE_IP_V6_COUNTRY_CBUFF), ipv6RegionFilePath)
|
||||
zlog.Info("IPv4 ip2region database loaded successfully")
|
||||
} else if global.GCONFIG_IP_V4_SOURCE == "geolite2" {
|
||||
err := global.GIPLOCATION_MANAGER.LoadV4GeoLite2(ipv4Data)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load IPv4 GeoLite2 database: %v", err)
|
||||
}
|
||||
zlog.Info("IPv4 GeoLite2 database loaded successfully")
|
||||
}
|
||||
|
||||
// 加载 IPv6 数据库
|
||||
if global.GCONFIG_IP_V6_SOURCE == "ip2region" {
|
||||
// IPv6 ip2region 需要单独的文件
|
||||
ipv6Ip2RegionPath := filepath.Join(utils.GetCurrentDir(), "data", "ip2region_v6.xdb")
|
||||
if _, err := os.Stat(ipv6Ip2RegionPath); err == nil {
|
||||
fileBytes, err := ioutil.ReadFile(ipv6Ip2RegionPath)
|
||||
if err != nil {
|
||||
zlog.Warn("Failed to read IPv6 ip2region database file: ", err)
|
||||
} else {
|
||||
err = global.GIPLOCATION_MANAGER.LoadV6Ip2Region(fileBytes, iplocation.DBFormat(global.GCONFIG_IP_V6_FORMAT))
|
||||
if err != nil {
|
||||
zlog.Warn("Failed to load IPv6 ip2region database: ", err)
|
||||
} else {
|
||||
zlog.Info("IPv6 ip2region database loaded successfully, size: ", len(fileBytes))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zlog.Warn("IPv6 ip2region database file not found, please upload ip2region_v6.xdb")
|
||||
}
|
||||
} else if global.GCONFIG_IP_V6_SOURCE == "geolite2" {
|
||||
// IPv6 GeoLite2
|
||||
ipv6GeoLitePath := filepath.Join(utils.GetCurrentDir(), "data", "GeoLite2-Country.mmdb")
|
||||
var ipv6Data []byte
|
||||
if _, err := os.Stat(ipv6GeoLitePath); os.IsNotExist(err) {
|
||||
// 使用内置数据
|
||||
ipv6Data = Ipv6CountryBytes
|
||||
zlog.Info("Using embedded IPv6 GeoLite2 database, size: ", len(ipv6Data))
|
||||
} else {
|
||||
// 读取外部文件
|
||||
fileBytes, err := ioutil.ReadFile(ipv6GeoLitePath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read IPv6 GeoLite2 database file: %v", err)
|
||||
}
|
||||
ipv6Data = fileBytes
|
||||
zlog.Info("IPv6 GeoLite2 database loaded from file, size: ", len(ipv6Data), ipv6GeoLitePath)
|
||||
}
|
||||
|
||||
err := global.GIPLOCATION_MANAGER.LoadV6GeoLite2(ipv6Data)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load IPv6 GeoLite2 database: %v", err)
|
||||
}
|
||||
zlog.Info("IPv6 GeoLite2 database loaded successfully")
|
||||
}
|
||||
global.GWAF_DLP_CONFIG = ldpConfig
|
||||
global.GWAF_REG_PUBLIC_KEY = publicKey
|
||||
|
||||
+14
-4
@@ -4,6 +4,7 @@ import (
|
||||
"SamWaf/cache"
|
||||
"SamWaf/common/gwebsocket"
|
||||
"SamWaf/common/queue"
|
||||
"SamWaf/iplocation"
|
||||
"SamWaf/model"
|
||||
"SamWaf/model/spec"
|
||||
"SamWaf/wafnotify"
|
||||
@@ -119,10 +120,19 @@ var (
|
||||
GWAF_HTTP_SENSITIVE_REPLACE_STRING = "**" //HTTP 敏感内容替换成
|
||||
|
||||
/*********IP相关**************/
|
||||
GCACHE_IP_CBUFF []byte // IP相关缓存
|
||||
GCACHE_IP_V6_COUNTRY_CBUFF []byte // IPv6国家相关缓存
|
||||
GCACHE_IPV4_SEARCHER *xdb.Searcher //IPV4得查询器
|
||||
GCACHE_IPV6_SEARCHER *geoip2.Reader // IPV6得查询器
|
||||
GCACHE_IP_CBUFF []byte // IP相关缓存 (已废弃,由 GIPLOCATION_MANAGER 管理)
|
||||
GCACHE_IP_V6_COUNTRY_CBUFF []byte // IPv6国家相关缓存 (已废弃,由 GIPLOCATION_MANAGER 管理)
|
||||
GCACHE_IPV4_SEARCHER *xdb.Searcher //IPV4得查询器 (已废弃,由 GIPLOCATION_MANAGER 管理)
|
||||
GCACHE_IPV6_SEARCHER *geoip2.Reader // IPV6得查询器 (已废弃,由 GIPLOCATION_MANAGER 管理)
|
||||
|
||||
// IP 数据库配置
|
||||
GCONFIG_IP_V4_SOURCE string = "ip2region" // IPv4 数据来源: ip2region / geolite2
|
||||
GCONFIG_IP_V6_SOURCE string = "geolite2" // IPv6 数据来源: ip2region / geolite2
|
||||
GCONFIG_IP_V4_FORMAT string = "legacy" // IPv4 xdb 字段格式
|
||||
GCONFIG_IP_V6_FORMAT string = "legacy" // IPv6 xdb 字段格式(仅 ip2region 时有效)
|
||||
|
||||
// IP Location Manager 全局实例
|
||||
GIPLOCATION_MANAGER *iplocation.Manager
|
||||
|
||||
GDATA_DELETE_INTERVAL int64 = 180 // 删除180天前的数据
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ require (
|
||||
github.com/hashicorp/go-plugin v1.7.0
|
||||
github.com/hyperjumptech/grule-rule-engine v1.15.0
|
||||
github.com/kardianos/service v1.2.2
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20220907060842-b2ba5d58e48d
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20260202110255-d427b4dcd879
|
||||
github.com/oschwald/geoip2-golang v1.11.0
|
||||
github.com/pires/go-proxyproto v0.8.1
|
||||
github.com/pquerna/otp v1.5.0
|
||||
@@ -111,6 +111,7 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.69 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
|
||||
@@ -268,6 +268,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20220907060842-b2ba5d58e48d h1:E7Qr3vakQIk9KTpn6m4yhsEhOdo4g51EqE4cSMy2LCw=
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20220907060842-b2ba5d58e48d/go.mod h1:bChUKvbKVC3zL/lLLIcu6alhQaL8uWD/DA+jRdyggdI=
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20260202110255-d427b4dcd879 h1:ouxvoYN6WL482nK1zS1IplyniAut3G4D+0N5JS0YTuw=
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20260202110255-d427b4dcd879/go.mod h1:+mNMTBuDMdEGhWzoQgc6kBdqeaQpWh5ba8zqmp2MxCU=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/magefile/mage v1.15.1-0.20231118170541-2385abb49a1f h1:iiLWLoibjCL0XND6inF7bs2nc20lU/FYkiR//VIOLUc=
|
||||
@@ -286,6 +288,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
|
||||
github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package iplocation
|
||||
|
||||
import "strings"
|
||||
|
||||
// ParseRegion 根据 DBFormat 将 "|" 分隔的 region 字符串解析为统一的 IPLocationResult
|
||||
func ParseRegion(region string, format DBFormat) *IPLocationResult {
|
||||
if region == "" {
|
||||
return &IPLocationResult{}
|
||||
}
|
||||
|
||||
fields := strings.Split(region, "|")
|
||||
|
||||
// 辅助函数:安全获取字段,处理 "0" 和空值
|
||||
getField := func(index int) string {
|
||||
if index >= len(fields) {
|
||||
return ""
|
||||
}
|
||||
val := strings.TrimSpace(fields[index])
|
||||
if val == "0" || val == "" {
|
||||
return ""
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
result := &IPLocationResult{}
|
||||
|
||||
switch format {
|
||||
case FormatLegacy: // 国家|区域|省份|城市|ISP
|
||||
result.Country = getField(0)
|
||||
result.Region = getField(1)
|
||||
result.Province = getField(2)
|
||||
result.City = getField(3)
|
||||
result.ISP = getField(4)
|
||||
|
||||
case FormatOpenSource: // 国家|省份|城市|网络运营商
|
||||
result.Country = getField(0)
|
||||
result.Province = getField(1)
|
||||
result.City = getField(2)
|
||||
result.ISP = getField(3)
|
||||
|
||||
case FormatFull: // 大洲|国家|省份|城市|区县|网络运营商|其他
|
||||
result.Region = getField(0)
|
||||
result.Country = getField(1)
|
||||
result.Province = getField(2)
|
||||
result.City = getField(3)
|
||||
result.District = getField(4)
|
||||
result.ISP = getField(5)
|
||||
|
||||
case FormatStandard: // 国家|省份|城市|区县|网络运营商|其他
|
||||
result.Country = getField(0)
|
||||
result.Province = getField(1)
|
||||
result.City = getField(2)
|
||||
result.District = getField(3)
|
||||
result.ISP = getField(4)
|
||||
|
||||
case FormatCompact: // 国家|省份|城市|网络运营商|其他
|
||||
result.Country = getField(0)
|
||||
result.Province = getField(1)
|
||||
result.City = getField(2)
|
||||
result.ISP = getField(3)
|
||||
|
||||
default:
|
||||
// 默认按 legacy 格式处理
|
||||
result.Country = getField(0)
|
||||
result.Region = getField(1)
|
||||
result.Province = getField(2)
|
||||
result.City = getField(3)
|
||||
result.ISP = getField(4)
|
||||
}
|
||||
|
||||
// 处理特殊标记 "内网IP"
|
||||
if len(fields) > 0 {
|
||||
lastField := fields[len(fields)-1]
|
||||
if strings.Contains(lastField, "内网IP") {
|
||||
result.Country = "内网"
|
||||
result.Province = "内网"
|
||||
result.City = "内网"
|
||||
result.Region = ""
|
||||
result.ISP = ""
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
package iplocation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||
"github.com/oschwald/geoip2-golang"
|
||||
)
|
||||
|
||||
// Manager IP 地理位置查询管理器
|
||||
type Manager struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
// IPv4 后端
|
||||
v4Source DBSource
|
||||
v4Format DBFormat
|
||||
v4Searcher *xdb.Searcher // ip2region IPv4 (buffer 模式并发安全)
|
||||
v4GeoReader *geoip2.Reader // GeoLite2 (备选)
|
||||
v4LoadTime time.Time
|
||||
v4FileSize int64
|
||||
v4CreateTime time.Time // 文件创建时间
|
||||
|
||||
// IPv6 后端
|
||||
v6Source DBSource
|
||||
v6Format DBFormat
|
||||
v6Searcher *xdb.Searcher // ip2region IPv6
|
||||
v6GeoReader *geoip2.Reader // GeoLite2-Country (默认)
|
||||
v6LoadTime time.Time
|
||||
v6FileSize int64
|
||||
v6CreateTime time.Time // 文件创建时间
|
||||
}
|
||||
|
||||
// NewManager 创建新的 IP 地理位置查询管理器
|
||||
func NewManager() *Manager {
|
||||
return &Manager{
|
||||
v4Source: SourceIp2Region,
|
||||
v4Format: FormatLegacy,
|
||||
v6Source: SourceGeoLite2,
|
||||
v6Format: FormatLegacy,
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup 查询 IP 地理位置信息,自动判断 IPv4/IPv6
|
||||
func (m *Manager) Lookup(ipStr string) *IPLocationResult {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
// 判断是 IPv4 还是 IPv6
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
return &IPLocationResult{Country: "无效IP"}
|
||||
}
|
||||
|
||||
// 判断 IP 类型
|
||||
if ip.To4() != nil {
|
||||
// IPv4
|
||||
return m.lookupV4(ipStr)
|
||||
} else {
|
||||
// IPv6
|
||||
return m.lookupV6(ipStr)
|
||||
}
|
||||
}
|
||||
|
||||
// lookupV4 查询 IPv4 地址
|
||||
func (m *Manager) lookupV4(ipStr string) *IPLocationResult {
|
||||
if m.v4Source == SourceIp2Region && m.v4Searcher != nil {
|
||||
region, err := m.v4Searcher.SearchByStr(ipStr)
|
||||
if err != nil {
|
||||
return &IPLocationResult{Country: "查询失败"}
|
||||
}
|
||||
if region == "" {
|
||||
return &IPLocationResult{Country: "未知"}
|
||||
}
|
||||
return ParseRegion(region, m.v4Format)
|
||||
} else if m.v4Source == SourceGeoLite2 && m.v4GeoReader != nil {
|
||||
ip := net.ParseIP(ipStr)
|
||||
record, err := m.v4GeoReader.Country(ip)
|
||||
if err != nil {
|
||||
return &IPLocationResult{Country: "查询失败"}
|
||||
}
|
||||
countryName := record.Country.Names["zh-CN"]
|
||||
if countryName == "" {
|
||||
countryName = record.Country.Names["en"]
|
||||
}
|
||||
return &IPLocationResult{Country: countryName}
|
||||
}
|
||||
|
||||
return &IPLocationResult{Country: "未配置"}
|
||||
}
|
||||
|
||||
// lookupV6 查询 IPv6 地址
|
||||
func (m *Manager) lookupV6(ipStr string) *IPLocationResult {
|
||||
if m.v6Source == SourceIp2Region && m.v6Searcher != nil {
|
||||
region, err := m.v6Searcher.SearchByStr(ipStr)
|
||||
if err != nil {
|
||||
return &IPLocationResult{Country: "查询失败"}
|
||||
}
|
||||
if region == "" {
|
||||
return &IPLocationResult{Country: "未知"}
|
||||
}
|
||||
return ParseRegion(region, m.v6Format)
|
||||
} else if m.v6Source == SourceGeoLite2 && m.v6GeoReader != nil {
|
||||
ip := net.ParseIP(ipStr)
|
||||
record, err := m.v6GeoReader.Country(ip)
|
||||
if err != nil {
|
||||
return &IPLocationResult{Country: "查询失败"}
|
||||
}
|
||||
countryName := record.Country.Names["zh-CN"]
|
||||
if countryName == "" {
|
||||
countryName = record.Country.Names["en"]
|
||||
}
|
||||
if countryName == "" {
|
||||
countryName = "内网"
|
||||
}
|
||||
return &IPLocationResult{Country: countryName}
|
||||
}
|
||||
|
||||
return &IPLocationResult{Country: "未配置"}
|
||||
}
|
||||
|
||||
// LoadV4Ip2Region 加载 IPv4 ip2region 数据库
|
||||
func (m *Manager) LoadV4Ip2Region(data []byte, format DBFormat) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// 关闭旧的 searcher
|
||||
if m.v4Searcher != nil {
|
||||
m.v4Searcher.Close()
|
||||
m.v4Searcher = nil
|
||||
}
|
||||
|
||||
// 创建新的 searcher (buffer 模式并发安全)
|
||||
searcher, err := xdb.NewWithBuffer(xdb.IPv4, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建 IPv4 searcher 失败: %w", err)
|
||||
}
|
||||
|
||||
m.v4Searcher = searcher
|
||||
m.v4Source = SourceIp2Region
|
||||
m.v4Format = format
|
||||
m.v4LoadTime = time.Now()
|
||||
m.v4FileSize = int64(len(data))
|
||||
m.v4CreateTime = time.Now() // 记录创建时间
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadV4GeoLite2 加载 IPv4 GeoLite2 数据库
|
||||
func (m *Manager) LoadV4GeoLite2(data []byte) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// 关闭旧的 reader
|
||||
if m.v4GeoReader != nil {
|
||||
m.v4GeoReader.Close()
|
||||
m.v4GeoReader = nil
|
||||
}
|
||||
|
||||
// 创建新的 reader
|
||||
reader, err := geoip2.FromBytes(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建 IPv4 GeoLite2 reader 失败: %w", err)
|
||||
}
|
||||
|
||||
m.v4GeoReader = reader
|
||||
m.v4Source = SourceGeoLite2
|
||||
m.v4LoadTime = time.Now()
|
||||
m.v4FileSize = int64(len(data))
|
||||
m.v4CreateTime = time.Now() // 记录创建时间
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadV6Ip2Region 加载 IPv6 ip2region 数据库
|
||||
func (m *Manager) LoadV6Ip2Region(data []byte, format DBFormat) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// 关闭旧的 searcher
|
||||
if m.v6Searcher != nil {
|
||||
m.v6Searcher.Close()
|
||||
m.v6Searcher = nil
|
||||
}
|
||||
|
||||
// 创建新的 searcher (buffer 模式并发安全)
|
||||
searcher, err := xdb.NewWithBuffer(xdb.IPv6, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建 IPv6 searcher 失败: %w", err)
|
||||
}
|
||||
|
||||
m.v6Searcher = searcher
|
||||
m.v6Source = SourceIp2Region
|
||||
m.v6Format = format
|
||||
m.v6LoadTime = time.Now()
|
||||
m.v6FileSize = int64(len(data))
|
||||
m.v6CreateTime = time.Now() // 记录创建时间
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadV6GeoLite2 加载 IPv6 GeoLite2 数据库
|
||||
func (m *Manager) LoadV6GeoLite2(data []byte) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// 关闭旧的 reader
|
||||
if m.v6GeoReader != nil {
|
||||
m.v6GeoReader.Close()
|
||||
m.v6GeoReader = nil
|
||||
}
|
||||
|
||||
// 创建新的 reader
|
||||
reader, err := geoip2.FromBytes(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建 IPv6 GeoLite2 reader 失败: %w", err)
|
||||
}
|
||||
|
||||
m.v6GeoReader = reader
|
||||
m.v6Source = SourceGeoLite2
|
||||
m.v6LoadTime = time.Now()
|
||||
m.v6FileSize = int64(len(data))
|
||||
m.v6CreateTime = time.Now() // 记录创建时间
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReloadV4 热加载 IPv4 数据库
|
||||
func (m *Manager) ReloadV4(data []byte, source DBSource, format DBFormat) error {
|
||||
if source == SourceIp2Region {
|
||||
return m.LoadV4Ip2Region(data, format)
|
||||
} else if source == SourceGeoLite2 {
|
||||
return m.LoadV4GeoLite2(data)
|
||||
}
|
||||
return fmt.Errorf("未知的数据源: %s", source)
|
||||
}
|
||||
|
||||
// ReloadV6 热加载 IPv6 数据库
|
||||
func (m *Manager) ReloadV6(data []byte, source DBSource, format DBFormat) error {
|
||||
if source == SourceIp2Region {
|
||||
return m.LoadV6Ip2Region(data, format)
|
||||
} else if source == SourceGeoLite2 {
|
||||
return m.LoadV6GeoLite2(data)
|
||||
}
|
||||
return fmt.Errorf("未知的数据源: %s", source)
|
||||
}
|
||||
|
||||
// GetStatus 获取当前数据库状态
|
||||
func (m *Manager) GetStatus() *DBStatus {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
status := &DBStatus{
|
||||
IPv4Source: string(m.v4Source),
|
||||
IPv4Format: string(m.v4Format),
|
||||
IPv4FileSize: m.v4FileSize,
|
||||
IPv6Source: string(m.v6Source),
|
||||
IPv6Format: string(m.v6Format),
|
||||
IPv6FileSize: m.v6FileSize,
|
||||
}
|
||||
|
||||
if !m.v4LoadTime.IsZero() {
|
||||
status.IPv4LoadTime = m.v4LoadTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if !m.v6LoadTime.IsZero() {
|
||||
status.IPv6LoadTime = m.v6LoadTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if !m.v4CreateTime.IsZero() {
|
||||
status.IPv4CreateTime = m.v4CreateTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if !m.v6CreateTime.IsZero() {
|
||||
status.IPv6CreateTime = m.v6CreateTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
// Close 关闭所有数据库连接
|
||||
func (m *Manager) Close() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.v4Searcher != nil {
|
||||
m.v4Searcher.Close()
|
||||
m.v4Searcher = nil
|
||||
}
|
||||
if m.v4GeoReader != nil {
|
||||
m.v4GeoReader.Close()
|
||||
m.v4GeoReader = nil
|
||||
}
|
||||
if m.v6Searcher != nil {
|
||||
m.v6Searcher.Close()
|
||||
m.v6Searcher = nil
|
||||
}
|
||||
if m.v6GeoReader != nil {
|
||||
m.v6GeoReader.Close()
|
||||
m.v6GeoReader = nil
|
||||
}
|
||||
}
|
||||
|
||||
// SetV4Source 设置 IPv4 数据源
|
||||
func (m *Manager) SetV4Source(source DBSource) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.v4Source = source
|
||||
}
|
||||
|
||||
// SetV6Source 设置 IPv6 数据源
|
||||
func (m *Manager) SetV6Source(source DBSource) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.v6Source = source
|
||||
}
|
||||
|
||||
// SetV4Format 设置 IPv4 数据格式
|
||||
func (m *Manager) SetV4Format(format DBFormat) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.v4Format = format
|
||||
}
|
||||
|
||||
// SetV6Format 设置 IPv6 数据格式
|
||||
func (m *Manager) SetV6Format(format DBFormat) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.v6Format = format
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package iplocation
|
||||
|
||||
// IPLocationResult 统一的 IP 地理位置查询结果
|
||||
type IPLocationResult struct {
|
||||
Country string // 国家
|
||||
Province string // 省份
|
||||
City string // 城市
|
||||
ISP string // 运营商
|
||||
Region string // 区域/大洲
|
||||
District string // 区县
|
||||
}
|
||||
|
||||
// ToSlice 返回兼容老格式的 []string: [国家, 区域, 省份, 城市, ISP]
|
||||
func (r *IPLocationResult) ToSlice() []string {
|
||||
return []string{r.Country, r.Region, r.Province, r.City, r.ISP}
|
||||
}
|
||||
|
||||
// DBFormat IP 数据库字段格式
|
||||
type DBFormat string
|
||||
|
||||
const (
|
||||
FormatLegacy DBFormat = "legacy" // 国家|区域|省份|城市|ISP (老版本内置)
|
||||
FormatOpenSource DBFormat = "opensource" // 国家|省份|城市|网络运营商
|
||||
FormatFull DBFormat = "full" // 大洲|国家|省份|城市|区县|网络运营商|其他
|
||||
FormatStandard DBFormat = "standard" // 国家|省份|城市|区县|网络运营商|其他
|
||||
FormatCompact DBFormat = "compact" // 国家|省份|城市|网络运营商|其他
|
||||
)
|
||||
|
||||
// DBSource IP 数据库来源
|
||||
type DBSource string
|
||||
|
||||
const (
|
||||
SourceIp2Region DBSource = "ip2region"
|
||||
SourceGeoLite2 DBSource = "geolite2"
|
||||
)
|
||||
|
||||
// DBStatus 数据库状态信息
|
||||
type DBStatus struct {
|
||||
IPv4Source string `json:"ipv4_source"`
|
||||
IPv4Format string `json:"ipv4_format"`
|
||||
IPv4FileSize int64 `json:"ipv4_file_size"`
|
||||
IPv4LoadTime string `json:"ipv4_load_time"`
|
||||
IPv4CreateTime string `json:"ipv4_create_time"`
|
||||
|
||||
IPv6Source string `json:"ipv6_source"`
|
||||
IPv6Format string `json:"ipv6_format"`
|
||||
IPv6FileSize int64 `json:"ipv6_file_size"`
|
||||
IPv6LoadTime string `json:"ipv6_load_time"`
|
||||
IPv6CreateTime string `json:"ipv6_create_time"`
|
||||
}
|
||||
@@ -51,6 +51,7 @@ type ApiGroup struct {
|
||||
FirewallIPBlockRouter
|
||||
PluginRouter
|
||||
LogFileWriteRouter
|
||||
IPLocationRouter
|
||||
}
|
||||
type PublicApiGroup struct {
|
||||
LoginRouter
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"SamWaf/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type IPLocationRouter struct {
|
||||
}
|
||||
|
||||
func (receiver *IPLocationRouter) InitIPLocationRouter(group *gin.RouterGroup) {
|
||||
apiInstance := api.APIGroupAPP.WafIPLocationApi
|
||||
router := group.Group("/api/v1/iplocation")
|
||||
{
|
||||
router.GET("/status", apiInstance.GetIPDBStatusApi)
|
||||
router.POST("/upload", apiInstance.UploadIPDBFileApi)
|
||||
router.POST("/reload", apiInstance.ReloadIPDBApi)
|
||||
router.POST("/test", apiInstance.TestIPLookupApi)
|
||||
}
|
||||
}
|
||||
+6
-65
@@ -16,9 +16,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||
"github.com/oschwald/geoip2-golang"
|
||||
)
|
||||
|
||||
func GetExternalIp() string {
|
||||
@@ -149,73 +146,17 @@ func GetPublicIP() string {
|
||||
}
|
||||
|
||||
func GetCountry(ip string) []string {
|
||||
if IsValidIPv6(ip) {
|
||||
a := "ipv6|ipv6|ipv6|ipv6|ipv6"
|
||||
if global.GCACHE_IPV6_SEARCHER == nil {
|
||||
db, err := geoip2.FromBytes(global.GCACHE_IP_V6_COUNTRY_CBUFF)
|
||||
if err != nil {
|
||||
zlog.Error("Failed to open GeoLite2-Country.mmdb:", err)
|
||||
return strings.Split(a, "|")
|
||||
}
|
||||
global.GCACHE_IPV6_SEARCHER = db
|
||||
}
|
||||
ipv6 := net.ParseIP(ip)
|
||||
record, err := global.GCACHE_IPV6_SEARCHER.Country(ipv6)
|
||||
if err != nil {
|
||||
zlog.Error("Failed to Search GeoLite2-Country.mmdb:", err)
|
||||
return strings.Split(a, "|")
|
||||
}
|
||||
if record.Country.Names == nil {
|
||||
a = "内网" + "||||"
|
||||
} else {
|
||||
a = record.Country.Names["zh-CN"] + "||||"
|
||||
}
|
||||
|
||||
return strings.Split(a, "|")
|
||||
if global.GIPLOCATION_MANAGER != nil {
|
||||
result := global.GIPLOCATION_MANAGER.Lookup(ip)
|
||||
return result.ToSlice() // [国家, 区域, 省份, 城市, ISP]
|
||||
}
|
||||
|
||||
//IPV4得查询逻辑
|
||||
if global.GCACHE_IPV4_SEARCHER == nil {
|
||||
// 2、用全局的 cBuff 创建完全基于内存的查询对象。
|
||||
searcher, err := xdb.NewWithBuffer(global.GCACHE_IP_CBUFF)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create searcher with content: %s\n", err)
|
||||
|
||||
}
|
||||
global.GCACHE_IPV4_SEARCHER = searcher
|
||||
}
|
||||
|
||||
// do the search
|
||||
var tStart = time.Now()
|
||||
|
||||
// 备注:并发使用,每个 goroutine 需要创建一个独立的 searcher 对象。
|
||||
region, err := global.GCACHE_IPV4_SEARCHER.SearchByStr(ip)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to SearchIP(%s): %s\n", ip, err)
|
||||
return []string{"无", "无"}
|
||||
}
|
||||
|
||||
zlog.Debug("{region: %s, took: %s}\n", region, time.Since(tStart))
|
||||
regions := strings.Split(region, "|")
|
||||
//如果是内网IP情况下显示内网的内容
|
||||
if regions[4] == "内网IP" {
|
||||
regions[0] = "内网"
|
||||
regions[1] = "内网"
|
||||
regions[2] = "内网"
|
||||
}
|
||||
return regions
|
||||
return []string{"未知", "", "", "", ""}
|
||||
}
|
||||
|
||||
// CloseIPDatabase 关闭IP数据库
|
||||
func CloseIPDatabase() {
|
||||
if global.GCACHE_IPV4_SEARCHER != nil {
|
||||
global.GCACHE_IPV4_SEARCHER.Close()
|
||||
}
|
||||
if global.GCACHE_IPV6_SEARCHER != nil {
|
||||
err := global.GCACHE_IPV6_SEARCHER.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if global.GIPLOCATION_MANAGER != nil {
|
||||
global.GIPLOCATION_MANAGER.Close()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ func (web *WafWebManager) initRouter(r *gin.Engine) {
|
||||
router.ApiGroupApp.InitNotifyLogRouter(RouterGroup)
|
||||
router.ApiGroupApp.InitFirewallIPBlockRouter(RouterGroup)
|
||||
router.ApiGroupApp.InitLogFileWriteRouter(RouterGroup)
|
||||
router.ApiGroupApp.InitIPLocationRouter(RouterGroup)
|
||||
}
|
||||
|
||||
if global.GWAF_RELEASE == "true" {
|
||||
|
||||
@@ -3,6 +3,7 @@ package waftask
|
||||
import (
|
||||
"SamWaf/common/zlog"
|
||||
"SamWaf/global"
|
||||
"SamWaf/iplocation"
|
||||
"SamWaf/model"
|
||||
"SamWaf/model/request"
|
||||
"SamWaf/wafipban"
|
||||
@@ -243,6 +244,34 @@ func setConfigStringValue(name string, value string, change int) {
|
||||
case "log_file_write_custom_tpl":
|
||||
global.GCONFIG_LOG_FILE_WRITE_CUSTOM_TPL = value
|
||||
syncLogFileWriterConfig()
|
||||
case "ip_v4_source":
|
||||
global.GCONFIG_IP_V4_SOURCE = value
|
||||
if change == 1 {
|
||||
zlog.Info("IPv4 数据源配置已更改为: ", value)
|
||||
}
|
||||
case "ip_v6_source":
|
||||
global.GCONFIG_IP_V6_SOURCE = value
|
||||
if change == 1 {
|
||||
zlog.Info("IPv6 数据源配置已更改为: ", value)
|
||||
}
|
||||
case "ip_v4_format":
|
||||
global.GCONFIG_IP_V4_FORMAT = value
|
||||
if change == 1 {
|
||||
// 重新加载 IPv4 数据库
|
||||
if global.GIPLOCATION_MANAGER != nil {
|
||||
global.GIPLOCATION_MANAGER.SetV4Format(iplocation.DBFormat(value))
|
||||
zlog.Info("IPv4 数据格式配置已更改为: ", value)
|
||||
}
|
||||
}
|
||||
case "ip_v6_format":
|
||||
global.GCONFIG_IP_V6_FORMAT = value
|
||||
if change == 1 {
|
||||
// 重新加载 IPv6 数据库
|
||||
if global.GIPLOCATION_MANAGER != nil {
|
||||
global.GIPLOCATION_MANAGER.SetV6Format(iplocation.DBFormat(value))
|
||||
zlog.Info("IPv6 数据格式配置已更改为: ", value)
|
||||
}
|
||||
}
|
||||
default:
|
||||
zlog.Warn("Unknown config item:", name)
|
||||
}
|
||||
@@ -380,6 +409,20 @@ func TaskLoadSetting(initLoad bool) {
|
||||
updateConfigStringItem(initLoad, "ssl", "zerossl_eab_kid", global.GCONFIG_ZEROSSL_EAB_KID, "zerossl eab_kid", "string", "", configMap)
|
||||
updateConfigStringItem(initLoad, "ssl", "zerossl_eab_hmac_key", global.GCONFIG_ZEROSSL_EAB_HMAC_KEY, "zerossl eab_hmac_key", "string", "", configMap)
|
||||
|
||||
// IP数据库配置
|
||||
updateConfigStringItem(initLoad, "ip_database", "ip_v4_source",
|
||||
global.GCONFIG_IP_V4_SOURCE, "IPv4数据库来源",
|
||||
"options", "ip2region|ip2region,geolite2|GeoLite2", configMap)
|
||||
updateConfigStringItem(initLoad, "ip_database", "ip_v6_source",
|
||||
global.GCONFIG_IP_V6_SOURCE, "IPv6数据库来源",
|
||||
"options", "ip2region|ip2region,geolite2|GeoLite2", configMap)
|
||||
updateConfigStringItem(initLoad, "ip_database", "ip_v4_format",
|
||||
global.GCONFIG_IP_V4_FORMAT, "IPv4 xdb字段格式",
|
||||
"options", "legacy|老版本,opensource|开源版,full|满载版,standard|标准版,compact|精简版", configMap)
|
||||
updateConfigStringItem(initLoad, "ip_database", "ip_v6_format",
|
||||
global.GCONFIG_IP_V6_FORMAT, "IPv6 xdb字段格式(仅ip2region时有效)",
|
||||
"options", "legacy|老版本,opensource|开源版,full|满载版,standard|标准版,compact|精简版", configMap)
|
||||
|
||||
// 日志文件写入相关配置
|
||||
updateConfigIntItem(initLoad, "logfile", "log_file_write_enable", global.GCONFIG_LOG_FILE_WRITE_ENABLE, "日志文件写入开关(0关闭 1开启)", "options", "0|关闭,1|开启", configMap)
|
||||
updateConfigStringItem(initLoad, "logfile", "log_file_write_path", global.GCONFIG_LOG_FILE_WRITE_PATH, "日志文件输出路径", "string", "", configMap)
|
||||
|
||||
Reference in New Issue
Block a user