feat: geo ip.

This commit is contained in:
tx7do
2023-10-19 15:05:49 +08:00
parent 92be8b878a
commit 01bdf5cc06
21 changed files with 343 additions and 38 deletions

204
geoip/qqwry/qqwry.go Normal file
View File

@@ -0,0 +1,204 @@
package qqwry
import (
"encoding/binary"
"errors"
"net"
"strings"
"sync"
"github.com/tx7do/kratos-utils/geoip/qqwry/assets"
)
type Client struct {
data []byte
dataLen uint32
ipCache sync.Map
IPNum int64
startPos uint32
endPos uint32
}
func NewClient() *Client {
cli := &Client{
ipCache: sync.Map{},
}
cli.init()
return cli
}
func (c *Client) init() {
c.startPos, c.endPos = c.readHeader()
c.IPNum = int64((c.endPos-c.startPos)/ipRecordLength + 1)
}
// parseIp 解析IP
func (c *Client) parseIp(queryIp string) (uint32, error) {
ip := net.ParseIP(queryIp).To4()
if ip == nil {
return 0, errors.New("ip is not ipv4")
}
ip32 := binary.BigEndian.Uint32(ip)
return ip32, nil
}
// readHeader 读取文件头
func (c *Client) readHeader() (uint32, uint32) {
startPos := binary.LittleEndian.Uint32(assets.QQWryDat[:4])
endPos := binary.LittleEndian.Uint32(assets.QQWryDat[4:8])
return startPos, endPos
}
// readMode 获取偏移值类型
func (c *Client) readMode(offset uint32) byte {
return assets.QQWryDat[offset]
}
// readIpRecord 读取IP记录 前4字节起始IP后3字节偏移量
func (c *Client) readIpRecord(offset uint32) (ip32 uint32, ipOffset uint32) {
buf := assets.QQWryDat[offset : offset+ipRecordLength]
ip32 = binary.LittleEndian.Uint32(buf[:4])
ipOffset = byte3ToUInt32(buf[4:])
return ip32, ipOffset
}
// locateIP 定位IP
func (c *Client) locateIP(ip32 uint32) int32 {
var _ip32 uint32
var _ipOffset uint32
var offset uint32
var mid uint32
i := c.startPos
j := c.endPos
for {
mid = getMiddleOffset(i, j)
_ip32, _ipOffset = c.readIpRecord(mid)
if j-i == ipRecordLength {
offset = _ipOffset
_ip32, _ipOffset = c.readIpRecord(mid + ipRecordLength)
if ip32 < _ip32 {
break
} else {
offset = 0
break
}
}
if _ip32 > ip32 {
j = mid
} else if _ip32 < ip32 {
i = mid
} else if _ip32 == ip32 {
offset = _ipOffset
break
}
}
return int32(offset)
}
// readArea 读取区域
func (c *Client) readArea(offset uint32) []byte {
mode := c.readMode(offset)
if mode == redirectMode1 || mode == redirectMode2 {
areaOffset := c.readUInt24(int32(offset) + 1)
if areaOffset == 0 {
return []byte{}
}
return c.readString(areaOffset)
}
return c.readString(offset)
}
// readString 获取字符串
func (c *Client) readString(offset uint32) []byte {
data := make([]byte, 0, 30)
for i := offset; i < uint32(len(assets.QQWryDat)); i++ {
if assets.QQWryDat[i] == 0 {
data = assets.QQWryDat[offset:i]
break
}
}
return data
}
func (c *Client) readUInt24(offset int32) uint32 {
i := uint32(assets.QQWryDat[offset+0]) & 0xFF
i |= (uint32(assets.QQWryDat[offset+1]) << 8) & 0xFF00
i |= (uint32(assets.QQWryDat[offset+2]) << 16) & 0xFF0000
return i
}
func (c *Client) Query(queryIp string) (country, city, isp string, err error) {
ip32, err := c.parseIp(queryIp)
if err != nil {
return
}
offset := c.locateIP(ip32)
if offset <= 0 {
err = errors.New("ip not found")
return
}
//读取第一个字节判断是否是标志字节
offset += 4
mode := c.readMode(uint32(offset))
var _city []byte
var ispPos uint32
switch mode {
case redirectMode1:
posC := c.readUInt24(offset + 1)
mode = c.readMode(posC)
posCA := posC
if mode == redirectMode2 {
posCA = c.readUInt24(int32(posC) + 1)
posC += 4
}
_city = c.readString(posCA)
if mode != redirectMode2 {
posC += uint32(len(city) + 1)
}
ispPos = posC
case redirectMode2:
posCA := c.readUInt24(offset + 1)
_city = c.readString(posCA)
ispPos = uint32(offset) + 4
default:
posCA := offset + 0
_city = c.readString(uint32(posCA))
ispPos = uint32(offset) + uint32(len(city)) + 1
}
if len(_city) != 0 {
city = strings.TrimSpace(gb18030Decode(_city))
}
ispMode := assets.QQWryDat[ispPos]
if ispMode == redirectMode1 || ispMode == redirectMode2 {
ispPos = c.readUInt24(int32(ispPos + 1))
}
if ispPos > 0 {
var _isp []byte
_isp = c.readString(ispPos)
isp = strings.TrimSpace(gb18030Decode(_isp))
if isp != "" {
if strings.Contains(isp, "CZ88.NET") {
isp = ""
}
}
}
return
}