-
Notifications
You must be signed in to change notification settings - Fork 4
/
qqwry.go
126 lines (107 loc) · 2.5 KB
/
qqwry.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package qqwry
import (
"bytes"
iconv "github.com/zyxar/go-iconv"
"net"
"os"
)
const (
_REDIRECT_MODE_1 = byte(0x01)
_REDIRECT_MODE_2 = byte(0x02)
)
var locale string = "UTF-8"
type QQWry struct {
*os.File
first int64
last int64
}
func NewQQWry(path string) (*QQWry, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
return &QQWry{file, int64(read4byte(file, 0)), int64(read4byte(file, 4))}, nil
}
func (qw *QQWry) QueryIP(ip string) (countryStr, areaStr string) {
var first, last, current int64
addr := ip2Int64(ip)
if addr == -1 {
return "UNKNOWN", "UNKNOWN"
}
first = qw.first
last = qw.last
current = ((last-first)/7/2)*7 + first
for current > first {
if read4byte(qw, current) > addr {
last = current
current = ((last-first)/7/2)*7 + first
} else {
first = current
current = ((last-first)/7/2)*7 + first
}
}
offset := read3byte(qw, current+4)
end := read4byte(qw, offset)
if addr > end {
return "UNKNOWN", "UNKNOWN"
}
offset += 4
mode := read1byte(qw, offset)
country := make([]byte, 256)
var area []byte
n := 0
if mode == _REDIRECT_MODE_1 {
offset = read3byte(qw, offset+1)
if read1byte(qw, offset) == _REDIRECT_MODE_2 {
off := read3byte(qw, offset+1)
qw.ReadAt(country, off)
n = bytes.IndexByte(country, 0x00)
country = country[:n]
offset += 4
} else {
qw.ReadAt(country, offset)
n = bytes.IndexByte(country, 0x00)
country = country[:n]
offset += int64(n + 1)
}
} else if mode == _REDIRECT_MODE_2 {
off := read3byte(qw, offset+1)
qw.ReadAt(country, off)
n = bytes.IndexByte(country, 0x00)
country = country[:n]
offset += 4
} else {
qw.ReadAt(country, offset)
n = bytes.IndexByte(country, 0x00)
country = country[:n]
offset += int64(n + 1)
}
mode = read1byte(qw, offset)
if mode == _REDIRECT_MODE_1 || mode == _REDIRECT_MODE_2 {
offset = read3byte(qw, offset+1)
}
if offset != 0 {
area = make([]byte, 256)
qw.ReadAt(area, offset)
n = bytes.IndexByte(area, 0x00)
area = area[:n]
}
cz88 := []byte("CZ88.NET")
if len(country) > 1 && bytes.Compare(country[1:], cz88) != 0 {
countryStr, _ = iconv.Conv(string(country), locale, "GBK")
}
if len(area) > 1 && bytes.Compare(area[1:], cz88) != 0 {
areaStr, _ = iconv.Conv(string(area), locale, "GBK")
}
return countryStr, areaStr
}
func ip2Int64(ipStr string) int64 {
ip := net.ParseIP(ipStr)
if ip == nil {
return -1
}
if len(ip) != 16 {
return -1
}
return int64(ip[15]) | int64(ip[14])<<8 | int64(ip[13])<<16 | int64(ip[12])<<24
}