commit
3acb551f94
@ -0,0 +1,11 @@
|
|||||||
|
.idea
|
||||||
|
go.sum
|
||||||
|
main.exe
|
||||||
|
server.exe
|
||||||
|
log.txt
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
uploads
|
||||||
|
gin.log
|
||||||
|
cert
|
||||||
|
challenge
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
# ip地址归属地查询
|
||||||
|
http://127.0.0.1:8080?address=sre.ink
|
||||||
|
## bench
|
||||||
|
```bash
|
||||||
|
goos: windows
|
||||||
|
goarch: amd64
|
||||||
|
pkg: IPRegion/ip2region
|
||||||
|
cpu: AMD Ryzen 9 5900HX with Radeon Graphics
|
||||||
|
BenchmarkBtreeSearch
|
||||||
|
BenchmarkBtreeSearch-16 215227 5310 ns/op
|
||||||
|
BenchmarkMemorySearch
|
||||||
|
BenchmarkMemorySearch-16 5435004 220.3 ns/op
|
||||||
|
BenchmarkBinarySearch
|
||||||
|
BenchmarkBinarySearch-16 34378 34766 ns/op
|
||||||
|
PASS
|
||||||
|
```
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"IPRegion/ip2region"
|
||||||
|
"IPRegion/util"
|
||||||
|
"fmt"
|
||||||
|
//"github.com/lionsoul2014/ip2region/binding/golang/ip2region"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetIPInfo(address, searchType string) (ipInfo ip2region.IpInfo, err error) {
|
||||||
|
//https://gitee.com/lionsoul/ip2region/raw/master/data/ip2region.db
|
||||||
|
region, err := ip2region.New("ip2region.db")
|
||||||
|
defer region.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var ipAddress string
|
||||||
|
if util.MatchIP(address) {
|
||||||
|
ipAddress = address
|
||||||
|
} else {
|
||||||
|
ipAddress, err = util.GetIPByDomain(address)
|
||||||
|
if err != nil {
|
||||||
|
//提供非法ip 返回空结构体
|
||||||
|
ipAddress = "266.266.266.266"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch searchType {
|
||||||
|
case "Memory":
|
||||||
|
ip, err := region.MemorySearch(ipAddress)
|
||||||
|
return ip, err
|
||||||
|
case "Binary":
|
||||||
|
ip, err := region.BinarySearch(ipAddress)
|
||||||
|
return ip, err
|
||||||
|
case "Btree":
|
||||||
|
ip, err := region.BtreeSearch(ipAddress)
|
||||||
|
return ip, err
|
||||||
|
default:
|
||||||
|
ip, err := region.MemorySearch(ipAddress)
|
||||||
|
return ip, err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
const SearchType = "Memory"
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
module IPRegion
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require github.com/lionsoul2014/ip2region v2.2.0-release+incompatible
|
||||||
Binary file not shown.
@ -0,0 +1,347 @@
|
|||||||
|
package ip2region
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
INDEX_BLOCK_LENGTH = 12
|
||||||
|
TOTAL_HEADER_LENGTH = 8192
|
||||||
|
)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var ipInfo IpInfo
|
||||||
|
|
||||||
|
type Ip2Region struct {
|
||||||
|
// db file handler
|
||||||
|
dbFileHandler *os.File
|
||||||
|
|
||||||
|
//header block info
|
||||||
|
|
||||||
|
headerSip []int64
|
||||||
|
headerPtr []int64
|
||||||
|
headerLen int64
|
||||||
|
|
||||||
|
// super block index info
|
||||||
|
firstIndexPtr int64
|
||||||
|
lastIndexPtr int64
|
||||||
|
totalBlocks int64
|
||||||
|
|
||||||
|
// for memory mode only
|
||||||
|
// the original db binary string
|
||||||
|
|
||||||
|
dbBinStr []byte
|
||||||
|
dbFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
type IpInfo struct {
|
||||||
|
CityId int64
|
||||||
|
Country string
|
||||||
|
Region string
|
||||||
|
Province string
|
||||||
|
City string
|
||||||
|
ISP string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ip IpInfo) String() string {
|
||||||
|
return strconv.FormatInt(ip.CityId, 10) + "|" + ip.Country + "|" + ip.Region + "|" + ip.Province + "|" + ip.City + "|" + ip.ISP
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIpInfo(cityId int64, line []byte) IpInfo {
|
||||||
|
|
||||||
|
lineSlice := strings.Split(string(line), "|")
|
||||||
|
ipInfo := IpInfo{}
|
||||||
|
length := len(lineSlice)
|
||||||
|
ipInfo.CityId = cityId
|
||||||
|
if length < 5 {
|
||||||
|
for i := 0; i <= 5-length; i++ {
|
||||||
|
lineSlice = append(lineSlice, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipInfo.Country = lineSlice[0]
|
||||||
|
ipInfo.Region = lineSlice[1]
|
||||||
|
ipInfo.Province = lineSlice[2]
|
||||||
|
ipInfo.City = lineSlice[3]
|
||||||
|
ipInfo.ISP = lineSlice[4]
|
||||||
|
return ipInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(path string) (*Ip2Region, error) {
|
||||||
|
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Ip2Region{
|
||||||
|
dbFile: path,
|
||||||
|
dbFileHandler: file,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Ip2Region) Close() {
|
||||||
|
this.dbFileHandler.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Ip2Region) MemorySearch(ipStr string) (ipInfo IpInfo, err error) {
|
||||||
|
ipInfo = IpInfo{}
|
||||||
|
|
||||||
|
if this.totalBlocks == 0 {
|
||||||
|
this.dbBinStr, err = ioutil.ReadFile(this.dbFile)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return ipInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
this.firstIndexPtr = getLong(this.dbBinStr, 0)
|
||||||
|
this.lastIndexPtr = getLong(this.dbBinStr, 4)
|
||||||
|
this.totalBlocks = (this.lastIndexPtr-this.firstIndexPtr)/INDEX_BLOCK_LENGTH + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := ip2long(ipStr)
|
||||||
|
if err != nil {
|
||||||
|
return ipInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h := this.totalBlocks
|
||||||
|
var dataPtr, l int64
|
||||||
|
for l <= h {
|
||||||
|
|
||||||
|
m := (l + h) >> 1
|
||||||
|
p := this.firstIndexPtr + m*INDEX_BLOCK_LENGTH
|
||||||
|
sip := getLong(this.dbBinStr, p)
|
||||||
|
if ip < sip {
|
||||||
|
h = m - 1
|
||||||
|
} else {
|
||||||
|
eip := getLong(this.dbBinStr, p+4)
|
||||||
|
if ip > eip {
|
||||||
|
l = m + 1
|
||||||
|
} else {
|
||||||
|
dataPtr = getLong(this.dbBinStr, p+8)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dataPtr == 0 {
|
||||||
|
return ipInfo, errors.New("not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
dataLen := ((dataPtr >> 24) & 0xFF)
|
||||||
|
dataPtr = (dataPtr & 0x00FFFFFF)
|
||||||
|
ipInfo = getIpInfo(getLong(this.dbBinStr, dataPtr), this.dbBinStr[(dataPtr)+4:dataPtr+dataLen])
|
||||||
|
return ipInfo, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Ip2Region) BinarySearch(ipStr string) (ipInfo IpInfo, err error) {
|
||||||
|
ipInfo = IpInfo{}
|
||||||
|
if this.totalBlocks == 0 {
|
||||||
|
this.dbFileHandler.Seek(0, 0)
|
||||||
|
superBlock := make([]byte, 8)
|
||||||
|
this.dbFileHandler.Read(superBlock)
|
||||||
|
this.firstIndexPtr = getLong(superBlock, 0)
|
||||||
|
this.lastIndexPtr = getLong(superBlock, 4)
|
||||||
|
this.totalBlocks = (this.lastIndexPtr-this.firstIndexPtr)/INDEX_BLOCK_LENGTH + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var l, dataPtr, p int64
|
||||||
|
|
||||||
|
h := this.totalBlocks
|
||||||
|
|
||||||
|
ip, err := ip2long(ipStr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for l <= h {
|
||||||
|
m := (l + h) >> 1
|
||||||
|
|
||||||
|
p = m * INDEX_BLOCK_LENGTH
|
||||||
|
|
||||||
|
_, err = this.dbFileHandler.Seek(this.firstIndexPtr+p, 0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := make([]byte, INDEX_BLOCK_LENGTH)
|
||||||
|
_, err = this.dbFileHandler.Read(buffer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
sip := getLong(buffer, 0)
|
||||||
|
if ip < sip {
|
||||||
|
h = m - 1
|
||||||
|
} else {
|
||||||
|
eip := getLong(buffer, 4)
|
||||||
|
if ip > eip {
|
||||||
|
l = m + 1
|
||||||
|
} else {
|
||||||
|
dataPtr = getLong(buffer, 8)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if dataPtr == 0 {
|
||||||
|
err = errors.New("not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dataLen := ((dataPtr >> 24) & 0xFF)
|
||||||
|
dataPtr = (dataPtr & 0x00FFFFFF)
|
||||||
|
|
||||||
|
this.dbFileHandler.Seek(dataPtr, 0)
|
||||||
|
data := make([]byte, dataLen)
|
||||||
|
this.dbFileHandler.Read(data)
|
||||||
|
ipInfo = getIpInfo(getLong(data, 0), data[4:dataLen])
|
||||||
|
err = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Ip2Region) BtreeSearch(ipStr string) (ipInfo IpInfo, err error) {
|
||||||
|
ipInfo = IpInfo{}
|
||||||
|
ip, err := ip2long(ipStr)
|
||||||
|
|
||||||
|
if this.headerLen == 0 {
|
||||||
|
this.dbFileHandler.Seek(8, 0)
|
||||||
|
|
||||||
|
buffer := make([]byte, TOTAL_HEADER_LENGTH)
|
||||||
|
this.dbFileHandler.Read(buffer)
|
||||||
|
var idx int64
|
||||||
|
for i := 0; i < TOTAL_HEADER_LENGTH; i += 8 {
|
||||||
|
startIp := getLong(buffer, int64(i))
|
||||||
|
dataPar := getLong(buffer, int64(i+4))
|
||||||
|
if dataPar == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
this.headerSip = append(this.headerSip, startIp)
|
||||||
|
this.headerPtr = append(this.headerPtr, dataPar)
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
this.headerLen = idx
|
||||||
|
}
|
||||||
|
|
||||||
|
var l, sptr, eptr int64
|
||||||
|
h := this.headerLen
|
||||||
|
|
||||||
|
for l <= h {
|
||||||
|
m := int64(l+h) >> 1
|
||||||
|
if m < this.headerLen {
|
||||||
|
if ip == this.headerSip[m] {
|
||||||
|
if m > 0 {
|
||||||
|
sptr = this.headerPtr[m-1]
|
||||||
|
eptr = this.headerPtr[m]
|
||||||
|
} else {
|
||||||
|
sptr = this.headerPtr[m]
|
||||||
|
eptr = this.headerPtr[m+1]
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if ip < this.headerSip[m] {
|
||||||
|
if m == 0 {
|
||||||
|
sptr = this.headerPtr[m]
|
||||||
|
eptr = this.headerPtr[m+1]
|
||||||
|
break
|
||||||
|
} else if ip > this.headerSip[m-1] {
|
||||||
|
sptr = this.headerPtr[m-1]
|
||||||
|
eptr = this.headerPtr[m]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
h = m - 1
|
||||||
|
} else {
|
||||||
|
if m == this.headerLen-1 {
|
||||||
|
sptr = this.headerPtr[m-1]
|
||||||
|
eptr = this.headerPtr[m]
|
||||||
|
break
|
||||||
|
} else if ip <= this.headerSip[m+1] {
|
||||||
|
sptr = this.headerPtr[m]
|
||||||
|
eptr = this.headerPtr[m+1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l = m + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if sptr == 0 {
|
||||||
|
err = errors.New("not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
blockLen := eptr - sptr
|
||||||
|
this.dbFileHandler.Seek(sptr, 0)
|
||||||
|
index := make([]byte, blockLen+INDEX_BLOCK_LENGTH)
|
||||||
|
this.dbFileHandler.Read(index)
|
||||||
|
var dataptr int64
|
||||||
|
h = blockLen / INDEX_BLOCK_LENGTH
|
||||||
|
l = 0
|
||||||
|
|
||||||
|
for l <= h {
|
||||||
|
m := int64(l+h) >> 1
|
||||||
|
p := m * INDEX_BLOCK_LENGTH
|
||||||
|
sip := getLong(index, p)
|
||||||
|
if ip < sip {
|
||||||
|
h = m - 1
|
||||||
|
} else {
|
||||||
|
eip := getLong(index, p+4)
|
||||||
|
if ip > eip {
|
||||||
|
l = m + 1
|
||||||
|
} else {
|
||||||
|
dataptr = getLong(index, p+8)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dataptr == 0 {
|
||||||
|
err = errors.New("not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dataLen := (dataptr >> 24) & 0xFF
|
||||||
|
dataPtr := dataptr & 0x00FFFFFF
|
||||||
|
|
||||||
|
this.dbFileHandler.Seek(dataPtr, 0)
|
||||||
|
data := make([]byte, dataLen)
|
||||||
|
this.dbFileHandler.Read(data)
|
||||||
|
ipInfo = getIpInfo(getLong(data, 0), data[4:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLong(b []byte, offset int64) int64 {
|
||||||
|
|
||||||
|
val := (int64(b[offset]) |
|
||||||
|
int64(b[offset+1])<<8 |
|
||||||
|
int64(b[offset+2])<<16 |
|
||||||
|
int64(b[offset+3])<<24)
|
||||||
|
|
||||||
|
return val
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ip2long(IpStr string) (int64, error) {
|
||||||
|
bits := strings.Split(IpStr, ".")
|
||||||
|
if len(bits) != 4 {
|
||||||
|
return 0, errors.New("ip format error")
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum int64
|
||||||
|
for i, n := range bits {
|
||||||
|
bit, _ := strconv.ParseInt(n, 10, 64)
|
||||||
|
sum += bit << uint(24-8*i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
package ip2region
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkBtreeSearch(B *testing.B) {
|
||||||
|
region, err := New("../ip2region.db ")
|
||||||
|
if err != nil {
|
||||||
|
B.Error(err)
|
||||||
|
}
|
||||||
|
for i := 0; i < B.N; i++ {
|
||||||
|
region.BtreeSearch("127.0.0.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMemorySearch(B *testing.B) {
|
||||||
|
region, err := New("../ip2region.db ")
|
||||||
|
if err != nil {
|
||||||
|
B.Error(err)
|
||||||
|
}
|
||||||
|
for i := 0; i < B.N; i++ {
|
||||||
|
region.MemorySearch("127.0.0.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkBinarySearch(B *testing.B) {
|
||||||
|
region, err := New("../ip2region.db ")
|
||||||
|
if err != nil {
|
||||||
|
B.Error(err)
|
||||||
|
}
|
||||||
|
for i := 0; i < B.N; i++ {
|
||||||
|
region.BinarySearch("127.0.0.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIp2long(t *testing.T) {
|
||||||
|
ip, err := ip2long("127.0.0.1")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if ip != 2130706433 {
|
||||||
|
t.Error("result error")
|
||||||
|
}
|
||||||
|
t.Log(ip)
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"IPRegion/global"
|
||||||
|
"IPRegion/ip2region"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", IPHandle)
|
||||||
|
http.ListenAndServe(":8080", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IPHandle(w http.ResponseWriter, req *http.Request) {
|
||||||
|
address := req.FormValue("address")
|
||||||
|
var ipInfo ip2region.IpInfo
|
||||||
|
if address != "" {
|
||||||
|
ipInfo, _ = GetIPInfo(address, global.SearchType)
|
||||||
|
}
|
||||||
|
res, _ := json.Marshal(ipInfo)
|
||||||
|
fmt.Fprintln(w, string(res))
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type IpInfo struct {
|
||||||
|
CityId int64
|
||||||
|
Country string
|
||||||
|
Region string
|
||||||
|
Province string
|
||||||
|
City string
|
||||||
|
ISP string
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetIPByDomain(domain string) (string, error) {
|
||||||
|
addr, err := net.ResolveIPAddr("ip", domain)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return addr.String(), nil
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
func MatchIP(ip string) bool {
|
||||||
|
ipReg := `^((0|[1-9]\d?|1\d\d|2[0-4]\d|25[0-5])\.){3}(0|[1-9]\d?|1\d\d|2[0-4]\d|25[0-5])$`
|
||||||
|
match, _ := regexp.MatchString(ipReg, ip)
|
||||||
|
if match {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
Loading…
Reference in new issue