mirror of
https://github.com/xiaoqidun/qqwry.git
synced 2025-12-09 01:12:55 +08:00
feat(适配特性): 适配纯真IP库社区版更新(2024-06-19)地理位置格式
This commit is contained in:
19
README.md
19
README.md
@@ -5,7 +5,6 @@ Golang QQWry,高性能纯真IP查询库。
|
|||||||
# 使用须知
|
# 使用须知
|
||||||
|
|
||||||
1. 仅支持ipv4查询。
|
1. 仅支持ipv4查询。
|
||||||
2. city也可能是国家。
|
|
||||||
|
|
||||||
# 使用说明
|
# 使用说明
|
||||||
|
|
||||||
@@ -13,8 +12,8 @@ Golang QQWry,高性能纯真IP查询库。
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/xiaoqidun/qqwry"
|
"github.com/xiaoqidun/qqwry"
|
||||||
"log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -23,8 +22,18 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
// 从内存或缓存查询IP
|
// 从内存或缓存查询IP
|
||||||
city, isp, err := qqwry.QueryIP("1.1.1.1")
|
location, err := qqwry.QueryIP("119.29.29.29")
|
||||||
log.Printf("城市:%s,运营商:%s,错误:%v", city, isp, err)
|
if err != nil {
|
||||||
|
fmt.Printf("错误:%v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("国家:%s,省份:%s,城市:%s,区县:%s,运营商:%s\n",
|
||||||
|
location.Country,
|
||||||
|
location.Province,
|
||||||
|
location.City,
|
||||||
|
location.District,
|
||||||
|
location.ISP,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -42,7 +51,7 @@ func main() {
|
|||||||
|
|
||||||
1. 自行根据需要调整server下源码。
|
1. 自行根据需要调整server下源码。
|
||||||
2. 可以通过-listen参数指定http服务地址。
|
2. 可以通过-listen参数指定http服务地址。
|
||||||
3. json api:curl http://127.0.0.1/ip/1.1.1.1
|
3. json api:curl http://127.0.0.1/ip/119.29.29.29
|
||||||
|
|
||||||
# 特别感谢
|
# 特别感谢
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,22 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
queryIp := os.Args[1]
|
queryIp := os.Args[1]
|
||||||
city, isp, err := qqwry.QueryIP(queryIp)
|
location, err := qqwry.QueryIP(queryIp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("错误:%v\n", err)
|
fmt.Printf("错误:%v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("城市:%s,运营商:%s\n", city, isp)
|
emptyVal := func(val string) string {
|
||||||
|
if val != "" {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return "未知"
|
||||||
|
}
|
||||||
|
fmt.Printf("国家:%s,省份:%s,城市:%s,区县:%s,运营商:%s\n",
|
||||||
|
emptyVal(location.Country),
|
||||||
|
emptyVal(location.Province),
|
||||||
|
emptyVal(location.City),
|
||||||
|
emptyVal(location.District),
|
||||||
|
emptyVal(location.ISP),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
67
qqwry.go
67
qqwry.go
@@ -16,7 +16,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
data []byte
|
data []byte
|
||||||
dataLen uint32
|
dataLen uint32
|
||||||
ipCache = &sync.Map{}
|
locationCache = &sync.Map{}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -25,9 +25,13 @@ const (
|
|||||||
redirectMode2 = 0x02
|
redirectMode2 = 0x02
|
||||||
)
|
)
|
||||||
|
|
||||||
type cache struct {
|
type Location struct {
|
||||||
City string
|
Country string // 国家
|
||||||
Isp string
|
Province string // 省份
|
||||||
|
City string // 城市
|
||||||
|
District string // 区县
|
||||||
|
ISP string // 运营商
|
||||||
|
IP string // IP地址
|
||||||
}
|
}
|
||||||
|
|
||||||
func byte3ToUInt32(data []byte) uint32 {
|
func byte3ToUInt32(data []byte) uint32 {
|
||||||
@@ -45,16 +49,13 @@ func gb18030Decode(src []byte) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// QueryIP 从内存或缓存查询IP
|
// QueryIP 从内存或缓存查询IP
|
||||||
func QueryIP(queryIp string) (city string, isp string, err error) {
|
func QueryIP(ipv4 string) (location *Location, err error) {
|
||||||
if v, ok := ipCache.Load(queryIp); ok {
|
if v, ok := locationCache.Load(ipv4); ok {
|
||||||
city = v.(cache).City
|
return v.(*Location), nil
|
||||||
isp = v.(cache).Isp
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
ip := net.ParseIP(queryIp).To4()
|
ip := net.ParseIP(ipv4).To4()
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
err = errors.New("ip is not ipv4")
|
return nil, errors.New("ip is not ipv4")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
ip32 := binary.BigEndian.Uint32(ip)
|
ip32 := binary.BigEndian.Uint32(ip)
|
||||||
posA := binary.LittleEndian.Uint32(data[:4])
|
posA := binary.LittleEndian.Uint32(data[:4])
|
||||||
@@ -84,12 +85,12 @@ func QueryIP(queryIp string) (city string, isp string, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if offset <= 0 {
|
if offset <= 0 {
|
||||||
err = errors.New("ip not found")
|
return nil, errors.New("ip not found")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
posM := offset + 4
|
posM := offset + 4
|
||||||
mode := data[posM]
|
mode := data[posM]
|
||||||
var ispPos uint32
|
var ispPos uint32
|
||||||
|
var addr, isp string
|
||||||
switch mode {
|
switch mode {
|
||||||
case redirectMode1:
|
case redirectMode1:
|
||||||
posC := byte3ToUInt32(data[posM+1 : posM+4])
|
posC := byte3ToUInt32(data[posM+1 : posM+4])
|
||||||
@@ -101,19 +102,19 @@ func QueryIP(queryIp string) (city string, isp string, err error) {
|
|||||||
}
|
}
|
||||||
for i := posCA; i < dataLen; i++ {
|
for i := posCA; i < dataLen; i++ {
|
||||||
if data[i] == 0 {
|
if data[i] == 0 {
|
||||||
city = string(data[posCA:i])
|
addr = string(data[posCA:i])
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if mode != redirectMode2 {
|
if mode != redirectMode2 {
|
||||||
posC += uint32(len(city) + 1)
|
posC += uint32(len(addr) + 1)
|
||||||
}
|
}
|
||||||
ispPos = posC
|
ispPos = posC
|
||||||
case redirectMode2:
|
case redirectMode2:
|
||||||
posCA := byte3ToUInt32(data[posM+1 : posM+4])
|
posCA := byte3ToUInt32(data[posM+1 : posM+4])
|
||||||
for i := posCA; i < dataLen; i++ {
|
for i := posCA; i < dataLen; i++ {
|
||||||
if data[i] == 0 {
|
if data[i] == 0 {
|
||||||
city = string(data[posCA:i])
|
addr = string(data[posCA:i])
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,14 +123,14 @@ func QueryIP(queryIp string) (city string, isp string, err error) {
|
|||||||
posCA := offset + 4
|
posCA := offset + 4
|
||||||
for i := posCA; i < dataLen; i++ {
|
for i := posCA; i < dataLen; i++ {
|
||||||
if data[i] == 0 {
|
if data[i] == 0 {
|
||||||
city = string(data[posCA:i])
|
addr = string(data[posCA:i])
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ispPos = offset + uint32(5+len(city))
|
ispPos = offset + uint32(5+len(addr))
|
||||||
}
|
}
|
||||||
if city != "" {
|
if addr != "" {
|
||||||
city = strings.TrimSpace(gb18030Decode([]byte(city)))
|
addr = strings.TrimSpace(gb18030Decode([]byte(addr)))
|
||||||
}
|
}
|
||||||
ispMode := data[ispPos]
|
ispMode := data[ispPos]
|
||||||
if ispMode == redirectMode1 || ispMode == redirectMode2 {
|
if ispMode == redirectMode1 || ispMode == redirectMode2 {
|
||||||
@@ -150,8 +151,9 @@ func QueryIP(queryIp string) (city string, isp string, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ipCache.Store(queryIp, cache{City: city, Isp: isp})
|
location = SplitResult(addr, isp, ipv4)
|
||||||
return
|
locationCache.Store(ipv4, location)
|
||||||
|
return location, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadData 从内存加载IP数据库
|
// LoadData 从内存加载IP数据库
|
||||||
@@ -169,3 +171,22 @@ func LoadFile(filepath string) (err error) {
|
|||||||
dataLen = uint32(len(data))
|
dataLen = uint32(len(data))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SplitResult 按照调整后的纯真社区版IP库地理位置格式返回结果
|
||||||
|
func SplitResult(addr string, isp string, ipv4 string) (location *Location) {
|
||||||
|
location = &Location{ISP: isp, IP: ipv4}
|
||||||
|
splitList := strings.Split(addr, "–")
|
||||||
|
for i := 0; i < len(splitList); i++ {
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
location.Country = splitList[i]
|
||||||
|
case 1:
|
||||||
|
location.Province = splitList[i]
|
||||||
|
case 2:
|
||||||
|
location.City = splitList[i]
|
||||||
|
case 3:
|
||||||
|
location.District = splitList[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,10 +11,22 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQueryIP(t *testing.T) {
|
func TestQueryIP(t *testing.T) {
|
||||||
queryIp := "1.1.1.1"
|
queryIp := "119.29.29.29"
|
||||||
city, isp, err := QueryIP(queryIp)
|
location, err := QueryIP(queryIp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Logf("城市:%s,运营商:%s", city, isp)
|
emptyVal := func(val string) string {
|
||||||
|
if val != "" {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return "未知"
|
||||||
|
}
|
||||||
|
t.Logf("国家:%s,省份:%s,城市:%s,区县:%s,运营商:%s",
|
||||||
|
emptyVal(location.Country),
|
||||||
|
emptyVal(location.Province),
|
||||||
|
emptyVal(location.City),
|
||||||
|
emptyVal(location.District),
|
||||||
|
emptyVal(location.ISP),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type resp struct {
|
type resp struct {
|
||||||
IP string `json:"ip"`
|
Data *qqwry.Location `json:"data"`
|
||||||
Err string `json:"err"`
|
Success bool `json:"success"`
|
||||||
City string `json:"city"`
|
Message string `json:"message"`
|
||||||
Isp string `json:"isp"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -34,14 +33,14 @@ func IpAPI(writer http.ResponseWriter, request *http.Request) {
|
|||||||
if ip == "" {
|
if ip == "" {
|
||||||
ip, _, _ = net.SplitHostPort(request.RemoteAddr)
|
ip, _, _ = net.SplitHostPort(request.RemoteAddr)
|
||||||
}
|
}
|
||||||
rw := &resp{IP: ip}
|
write := &resp{}
|
||||||
city, isp, err := qqwry.QueryIP(ip)
|
location, err := qqwry.QueryIP(ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rw.Err = err.Error()
|
write.Message = err.Error()
|
||||||
} else {
|
} else {
|
||||||
rw.City = city
|
write.Data = location
|
||||||
rw.Isp = isp
|
write.Success = true
|
||||||
}
|
}
|
||||||
b, _ := json.MarshalIndent(rw, "", " ")
|
b, _ := json.MarshalIndent(write, "", " ")
|
||||||
_, _ = writer.Write(b)
|
_, _ = writer.Write(b)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user