You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
goip/v4/ipv4.go

224 lines
4.4 KiB

package v4
import (
_ "embed"
"encoding/binary"
"go.dtapp.net/gostring"
"golang.org/x/text/encoding/simplifiedchinese"
"log"
"net"
)
var (
header []byte
country []byte
area []byte
offset uint32
start uint32
end uint32
)
//go:embed qqwry.dat
var dat []byte
type Pointer struct {
Offset uint32
ItemLen uint32
IndexLen uint32
}
// Result 返回
type Result struct {
IP string `json:"ip,omitempty"` // 输入的ip地址
Country string `json:"country,omitempty"` // 国家或地区
Area string `json:"area,omitempty"` // 区域
}
// InitIPV4Data 加载
func (q *Pointer) InitIPV4Data() int64 {
buf := dat[0:8]
start := binary.LittleEndian.Uint32(buf[:4])
end := binary.LittleEndian.Uint32(buf[4:])
return int64((end-start)/7 + 1)
}
// ReadData 从文件中读取数据
func (q *Pointer) readData(length uint32) (rs []byte) {
end := q.Offset + length
dataNum := uint32(len(dat))
if q.Offset > dataNum {
return nil
}
if end > dataNum {
end = dataNum
}
rs = dat[q.Offset:end]
q.Offset = end
return rs
}
// Find ip地址查询对应归属地信息
func (q *Pointer) Find(ipStr string) (res Result) {
// 赋值
res.IP = ipStr
if net.ParseIP(ipStr).To4() == nil {
log.Println("不是ip地址")
// 不是ip地址
return res
}
q.Offset = 0
// 偏移
offset = q.searchIndex(binary.BigEndian.Uint32(net.ParseIP(ipStr).To4()))
if offset <= 0 {
return
}
q.Offset = offset + q.ItemLen
country, area = q.getAddr()
enc := simplifiedchinese.GBK.NewDecoder()
res.Country, _ = enc.String(string(country))
res.Country = gostring.SpaceAndLineBreak(res.Country)
res.Area, _ = enc.String(string(area))
// Delete CZ88.NET (防止不相关的信息产生干扰)
if res.Area == " CZ88.NET" || res.Area == "" {
res.Area = ""
} else {
res.Area = " " + res.Area
}
res.Area = gostring.SpaceAndLineBreak(res.Area)
return
}
// Query ip地址查询对应归属地信息
func (q *Pointer) Query(ipAddress net.IP) (res Result) {
q.Offset = 0
// 偏移
offset = q.searchIndex(binary.BigEndian.Uint32(ipAddress.To4()))
if offset <= 0 {
return
}
q.Offset = offset + q.ItemLen
country, area = q.getAddr()
enc := simplifiedchinese.GBK.NewDecoder()
res.Country, _ = enc.String(string(country))
res.Country = gostring.SpaceAndLineBreak(res.Country)
res.Area, _ = enc.String(string(area))
// Delete CZ88.NET (防止不相关的信息产生干扰)
if res.Area == " CZ88.NET" || res.Area == "" {
res.Area = ""
} else {
res.Area = " " + res.Area
}
res.Area = gostring.SpaceAndLineBreak(res.Area)
res.IP = ipAddress.String()
return
}
// 获取地址信息
func (q *Pointer) getAddr() ([]byte, []byte) {
mode := q.readData(1)[0]
if mode == 0x01 {
// [IP][0x01][国家和地区信息的绝对偏移地址]
q.Offset = byteToUInt32(q.readData(3))
return q.getAddr()
}
// [IP][0x02][信息的绝对偏移][...] or [IP][国家][...]
_offset := q.Offset - 1
c1 := q.readArea(_offset)
if mode == 0x02 {
q.Offset = 4 + _offset
} else {
q.Offset = _offset + uint32(1+len(c1))
}
c2 := q.readArea(q.Offset)
return c1, c2
}
// 读取区
func (q *Pointer) readArea(offset uint32) []byte {
q.Offset = offset
mode := q.readData(1)[0]
if mode == 0x01 || mode == 0x02 {
return q.readArea(byteToUInt32(q.readData(3)))
}
q.Offset = offset
return q.readString()
}
// 读取字符串
func (q *Pointer) readString() []byte {
data := make([]byte, 0)
for {
buf := q.readData(1)
if buf[0] == 0 {
break
}
data = append(data, buf[0])
}
return data
}
// 搜索索引
func (q *Pointer) searchIndex(ip uint32) uint32 {
q.ItemLen = 4
q.IndexLen = 7
header = dat[0:8]
start = binary.LittleEndian.Uint32(header[:4])
end = binary.LittleEndian.Uint32(header[4:])
buf := make([]byte, q.IndexLen)
for {
mid := start + q.IndexLen*(((end-start)/q.IndexLen)>>1)
buf = dat[mid : mid+q.IndexLen]
_ip := binary.LittleEndian.Uint32(buf[:q.ItemLen])
if end-start == q.IndexLen {
if ip >= binary.LittleEndian.Uint32(dat[end:end+q.ItemLen]) {
buf = dat[end : end+q.IndexLen]
}
return byteToUInt32(buf[q.ItemLen:])
}
if _ip > ip {
end = mid
} else if _ip < ip {
start = mid
} else if _ip == ip {
return byteToUInt32(buf[q.ItemLen:])
}
}
}
// 字节转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
}