feat: 更换 IP 库 (#6890)

This commit is contained in:
zhengkunwang 2024-10-30 15:02:40 +08:00 committed by GitHub
parent a03b131057
commit ef793f278a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 51 additions and 177 deletions

View File

@ -1,6 +0,0 @@
package qqwry
import _ "embed"
//go:embed qqwry.dat
var QQwryByte []byte

Binary file not shown.

View File

@ -2,6 +2,7 @@ package v2
import (
"encoding/base64"
"github.com/1Panel-dev/1Panel/core/utils/geo"
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/core/app/dto"
@ -10,7 +11,6 @@ import (
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/middleware"
"github.com/1Panel-dev/1Panel/core/utils/captcha"
"github.com/1Panel-dev/1Panel/core/utils/qqwry"
"github.com/gin-gonic/gin"
)
@ -175,12 +175,15 @@ func saveLoginLogs(c *gin.Context, err error) {
logs.Status = constant.StatusSuccess
}
logs.IP = c.ClientIP()
qqWry, err := qqwry.NewQQwry()
if err != nil {
global.LOG.Errorf("load qqwry datas failed: %s", err)
lang := c.GetHeader("Accept-Language")
if lang == "" {
lang = "zh"
}
address, err := geo.GetIPLocation(logs.IP, lang)
if err != nil {
global.LOG.Errorf("get ip location failed: %s", err)
}
res := qqWry.Find(logs.IP)
logs.Agent = c.GetHeader("User-Agent")
logs.Address = res.Area
logs.Address = address
_ = logService.CreateLoginLog(logs)
}

39
core/utils/geo/geo.go Normal file
View File

@ -0,0 +1,39 @@
package geo
import (
"github.com/1Panel-dev/1Panel/core/global"
"github.com/oschwald/maxminddb-golang"
"net"
"path"
)
type Location struct {
En string `maxminddb:"en"`
Zh string `maxminddb:"zh"`
}
type LocationRes struct {
Iso string `maxminddb:"iso"`
Country Location `maxminddb:"country"`
Latitude float64 `maxminddb:"latitude"`
Longitude float64 `maxminddb:"longitude"`
Province Location `maxminddb:"province"`
}
func GetIPLocation(ip, lang string) (string, error) {
geoPath := path.Join(global.CONF.System.BaseDir, "1panel", "geo", "GeoIP.mmdb")
reader, err := maxminddb.Open(geoPath)
if err != nil {
return "", err
}
var geoLocation LocationRes
ipNet := net.ParseIP(ip)
err = reader.Lookup(ipNet, &geoLocation)
if err != nil {
return "", err
}
if lang == "en" {
return geoLocation.Country.En + geoLocation.Province.En, nil
}
return geoLocation.Country.Zh + geoLocation.Province.Zh, nil
}

View File

@ -1,165 +0,0 @@
package qqwry
import (
"encoding/binary"
"net"
"strings"
"github.com/1Panel-dev/1Panel/cmd/server/qqwry"
"golang.org/x/text/encoding/simplifiedchinese"
)
const (
indexLen = 7
redirectMode1 = 0x01
redirectMode2 = 0x02
)
var IpCommonDictionary []byte
type QQwry struct {
Data []byte
Offset int64
}
func NewQQwry() (*QQwry, error) {
IpCommonDictionary := qqwry.QQwryByte
return &QQwry{Data: IpCommonDictionary}, nil
}
// readData 从文件中读取数据
func (q *QQwry) readData(num int, offset ...int64) (rs []byte) {
if len(offset) > 0 {
q.setOffset(offset[0])
}
nums := int64(num)
end := q.Offset + nums
dataNum := int64(len(q.Data))
if q.Offset > dataNum {
return nil
}
if end > dataNum {
end = dataNum
}
rs = q.Data[q.Offset:end]
q.Offset = end
return
}
// setOffset 设置偏移量
func (q *QQwry) setOffset(offset int64) {
q.Offset = offset
}
// Find ip地址查询对应归属地信息
func (q *QQwry) Find(ip string) (res ResultQQwry) {
res = ResultQQwry{}
res.IP = ip
if strings.Count(ip, ".") != 3 {
return res
}
offset := q.searchIndex(binary.BigEndian.Uint32(net.ParseIP(ip).To4()))
if offset <= 0 {
return
}
var area []byte
mode := q.readMode(offset + 4)
if mode == redirectMode1 {
countryOffset := q.readUInt24()
mode = q.readMode(countryOffset)
if mode == redirectMode2 {
c := q.readUInt24()
area = q.readString(c)
} else {
area = q.readString(countryOffset)
}
} else if mode == redirectMode2 {
countryOffset := q.readUInt24()
area = q.readString(countryOffset)
} else {
area = q.readString(offset + 4)
}
enc := simplifiedchinese.GBK.NewDecoder()
res.Area, _ = enc.String(string(area))
return
}
type ResultQQwry struct {
IP string `json:"ip"`
Area string `json:"area"`
}
// readMode 获取偏移值类型
func (q *QQwry) readMode(offset uint32) byte {
mode := q.readData(1, int64(offset))
return mode[0]
}
// readString 获取字符串
func (q *QQwry) readString(offset uint32) []byte {
q.setOffset(int64(offset))
data := make([]byte, 0, 30)
for {
buf := q.readData(1)
if buf[0] == 0 {
break
}
data = append(data, buf[0])
}
return data
}
// searchIndex 查找索引位置
func (q *QQwry) searchIndex(ip uint32) uint32 {
header := q.readData(8, 0)
start := binary.LittleEndian.Uint32(header[:4])
end := binary.LittleEndian.Uint32(header[4:])
for {
mid := q.getMiddleOffset(start, end)
buf := q.readData(indexLen, int64(mid))
_ip := binary.LittleEndian.Uint32(buf[:4])
if end-start == indexLen {
offset := byteToUInt32(buf[4:])
buf = q.readData(indexLen)
if ip < binary.LittleEndian.Uint32(buf[:4]) {
return offset
}
return 0
}
if _ip > ip {
end = mid
} else if _ip < ip {
start = mid
} else if _ip == ip {
return byteToUInt32(buf[4:])
}
}
}
// readUInt24
func (q *QQwry) readUInt24() uint32 {
buf := q.readData(3)
return byteToUInt32(buf)
}
// getMiddleOffset
func (q *QQwry) getMiddleOffset(start uint32, end uint32) uint32 {
records := ((end - start) / indexLen) >> 1
return start + records*indexLen
}
// byteToUInt32 将 byte 转换为uint32
func byteToUInt32(data []byte) uint32 {
i := uint32(data[0]) & 0xff
i |= (uint32(data[1]) << 8) & 0xff00
i |= (uint32(data[2]) << 16) & 0xff0000
return i
}

1
go.mod
View File

@ -99,6 +99,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mozillazg/go-httpheader v0.2.1 // indirect
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rs/xid v1.5.0 // indirect

2
go.sum
View File

@ -300,6 +300,8 @@ github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISe
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=