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.
199 lines
4.0 KiB
199 lines
4.0 KiB
package v6
|
|
|
|
import (
|
|
_ "embed"
|
|
"encoding/binary"
|
|
"gopkg.in/dtapps/go-library.v3/utils/gostring"
|
|
"math/big"
|
|
"net"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
header []byte
|
|
country []byte
|
|
area []byte
|
|
v6ip uint64
|
|
offset uint32
|
|
start uint32
|
|
end uint32
|
|
)
|
|
|
|
type Result struct {
|
|
IP string `json:"ip,omitempty"` // 输入的ip地址
|
|
Country string `json:"country,omitempty"` // 国家
|
|
Province string `json:"province,omitempty"` // 省份
|
|
City string `json:"city,omitempty"` // 城市
|
|
Area string `json:"area,omitempty"` // 区域
|
|
Isp string `json:"isp,omitempty"` // 运营商
|
|
}
|
|
|
|
//go:embed ipv6wry.db
|
|
var dat []byte
|
|
|
|
type Pointer struct {
|
|
Offset uint32
|
|
ItemLen uint32
|
|
IndexLen uint32
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (q *Pointer) Find(ip string) (res Result) {
|
|
|
|
res = Result{}
|
|
res.IP = ip
|
|
q.Offset = 0
|
|
|
|
tp := big.NewInt(0)
|
|
op := big.NewInt(0)
|
|
tp.SetBytes(net.ParseIP(ip).To16())
|
|
op.SetString("18446744073709551616", 10)
|
|
op.Div(tp, op)
|
|
tp.SetString("FFFFFFFFFFFFFFFF", 16)
|
|
op.And(op, tp)
|
|
|
|
v6ip = op.Uint64()
|
|
offset = q.searchIndex(v6ip)
|
|
q.Offset = offset
|
|
|
|
country, area = q.getAddr()
|
|
|
|
// 解析地区数据
|
|
info := strings.Split(string(country), "\t")
|
|
if len(info) > 0 {
|
|
i := 1
|
|
for {
|
|
if i > len(info) {
|
|
break
|
|
}
|
|
switch i {
|
|
case 1:
|
|
res.Country = info[i-1]
|
|
res.Country = gostring.SpaceAndLineBreak(res.Country)
|
|
case 2:
|
|
res.Province = info[i-1]
|
|
res.Province = gostring.SpaceAndLineBreak(res.Province)
|
|
case 3:
|
|
res.City = info[i-1]
|
|
res.City = gostring.SpaceAndLineBreak(res.City)
|
|
case 4:
|
|
res.Area = info[i-1]
|
|
res.Area = gostring.SpaceAndLineBreak(res.Area)
|
|
}
|
|
i++ // 自增
|
|
}
|
|
} else {
|
|
res.Country = string(country)
|
|
res.Country = gostring.SpaceAndLineBreak(res.Country)
|
|
}
|
|
// 运营商
|
|
res.Isp = string(area)
|
|
|
|
// Delete ZX (防止不相关的信息产生干扰)
|
|
if res.Isp == "ZX" || res.Isp == "" {
|
|
res.Isp = ""
|
|
} else {
|
|
res.Isp = " " + res.Isp
|
|
}
|
|
|
|
res.Isp = gostring.SpaceAndLineBreak(res.Isp)
|
|
|
|
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 uint64) uint32 {
|
|
|
|
q.ItemLen = 8
|
|
q.IndexLen = 11
|
|
|
|
header = dat[8:24]
|
|
start = binary.LittleEndian.Uint32(header[8:])
|
|
counts := binary.LittleEndian.Uint32(header[:8])
|
|
end = start + counts*q.IndexLen
|
|
|
|
buf := make([]byte, q.IndexLen)
|
|
|
|
for {
|
|
mid := start + q.IndexLen*(((end-start)/q.IndexLen)>>1)
|
|
buf = dat[mid : mid+q.IndexLen]
|
|
_ip := binary.LittleEndian.Uint64(buf[:q.ItemLen])
|
|
|
|
if end-start == q.IndexLen {
|
|
if ip >= binary.LittleEndian.Uint64(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:])
|
|
}
|
|
}
|
|
}
|
|
|
|
func byteToUInt32(data []byte) uint32 {
|
|
i := uint32(data[0]) & 0xff
|
|
i |= (uint32(data[1]) << 8) & 0xff00
|
|
i |= (uint32(data[2]) << 16) & 0xff0000
|
|
return i
|
|
}
|