Compare commits

...

18 Commits

Author SHA1 Message Date
李光春 803502b68a - update
1 year ago
李光春 e7336ba3ce - update
1 year ago
李光春 db75469df6 - update
2 years ago
李光春 18b8be2356 - update is
2 years ago
李光春 33e41cb5b1 - update analyse
2 years ago
李光春 07c723310f - update ip
2 years ago
李光春 a24d5383fb - update
2 years ago
李光春 8b0cd6dfee - update
2 years ago
李光春 bb3ce86430 - update geoip
2 years ago
李光春 e92e32634c - add geoip
2 years ago
李光春 96906f189e - update vendor
2 years ago
李光春 5cb7732037 - update v4
2 years ago
李光春 36b08917cb - update v4
2 years ago
李光春 73395b29a9 - update
2 years ago
李光春 ba6cce92eb - update v4
2 years ago
李光春 7b038db7cf - update v4
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 4207a3a2b0 - update
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build was killed Details
2 years ago
李光春 d31a69a439 - update
continuous-integration/drone/push Build is passing Details
2 years ago

4
.gitignore vendored

@ -4,6 +4,8 @@
.idea
.vscode
*.log
*_test.go
gomod.sh
*.zip
*.tar.gz
/vendor/
download_test.go

@ -2,7 +2,7 @@
<a href="https://www.dtapp.net/">Golang Ip</a>
</h1>
📦 Golang Ip
📦 Golang Ip
[comment]: <> (go)
[![godoc](https://pkg.go.dev/badge/go.dtapp.net/goip?status.svg)](https://pkg.go.dev/go.dtapp.net/goip)
@ -22,17 +22,18 @@ go get -v -u go.dtapp.net/goip
package main
import (
"context"
"go.dtapp.net/goip"
"testing"
)
func TestGoIp(t *testing.T) {
// 获取Mac地址
t.Log(goip.GetMacAddr())
t.Log(goip.GetMacAddr(context.Background()))
// 内网ip
t.Log(goip.GetInsideIp())
t.Log(goip.GetInsideIp(context.Background()))
// 外网ip
t.Log(goip.GetOutsideIp())
t.Log(goip.GetOutsideIp(context.Background()))
}
```

@ -1,53 +1,53 @@
package goip
import (
"net"
"strconv"
)
var (
ipv4 = "IPV4"
ipv6 = "IPV6"
)
type AnalyseResult 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"` // 运营商
Ip string `json:"ip"` // ip
Continent string `json:"continent"` // 大陆
Country string `json:"country"` // 国家
Province string `json:"province"` // 省份
City string `json:"city"` // 城市
Isp string `json:"isp"` // 运营商
LocationTimeZone string `json:"location_time_zone"` // 位置时区
LocationLatitude float64 `json:"location_latitude"` // 位置纬度
LocationLongitude float64 `json:"location_longitude"` // 位置经度
}
func (c *Client) Analyse(item string) AnalyseResult {
isIp := c.isIpv4OrIpv6(item)
ipByte := net.ParseIP(item)
switch isIp {
case ipv4:
info := c.V4db.Find(item)
search, err := c.V4Region.MemorySearch(item)
if err != nil {
return AnalyseResult{
IP: info.IP,
Country: info.Country,
Area: info.Area,
}
} else {
return AnalyseResult{
IP: search.IP,
Country: search.Country,
Province: search.Province,
City: search.City,
Isp: info.Area,
}
ip2regionV2Info, _ := c.QueryIp2RegionV2(ipByte)
geoIpInfo, _ := c.QueryGeoIp(ipByte)
return AnalyseResult{
Ip: ipByte.String(),
Continent: geoIpInfo.Continent.Name,
Country: geoIpInfo.Country.Name,
Province: ip2regionV2Info.Province,
City: ip2regionV2Info.City,
Isp: ip2regionV2Info.Operator,
LocationTimeZone: geoIpInfo.Location.TimeZone,
LocationLatitude: geoIpInfo.Location.Latitude,
LocationLongitude: geoIpInfo.Location.Longitude,
}
case ipv6:
info := c.V6db.Find(item)
geoIpInfo, _ := c.QueryGeoIp(ipByte)
ipv6Info, _ := c.QueryIpv6wry(ipByte)
return AnalyseResult{
IP: info.IP,
Country: info.Country,
Province: info.Province,
City: info.City,
Area: info.Area,
Isp: info.Isp,
Ip: ipByte.String(),
Continent: geoIpInfo.Continent.Name,
Country: geoIpInfo.Country.Name,
Province: ipv6Info.Province,
City: ipv6Info.City,
Isp: ipv6Info.Isp,
LocationTimeZone: geoIpInfo.Location.TimeZone,
LocationLatitude: geoIpInfo.Location.Latitude,
LocationLongitude: geoIpInfo.Location.Longitude,
}
default:
return AnalyseResult{}

@ -0,0 +1,39 @@
package goip
import (
"go.dtapp.net/goip/geoip"
"go.dtapp.net/goip/ip2region"
"go.dtapp.net/goip/ip2region_v2"
"go.dtapp.net/goip/ipv6wry"
"go.dtapp.net/goip/qqwry"
)
type Client struct {
ip2regionV2Client *ip2region_v2.Client
ip2regionClient *ip2region.Client
qqwryClient *qqwry.Client
geoIpClient *geoip.Client
ipv6wryClient *ipv6wry.Client
}
// NewIp 实例化
func NewIp() *Client {
c := &Client{}
c.ip2regionV2Client, _ = ip2region_v2.New()
c.ip2regionClient = ip2region.New()
c.qqwryClient = qqwry.New()
c.geoIpClient, _ = geoip.New()
c.ipv6wryClient = ipv6wry.New()
return c
}
func (c *Client) Close() {
c.geoIpClient.Close()
}

@ -1,3 +1,3 @@
package goip
const Version = "1.0.24"
const Version = "1.0.40"

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 MiB

Binary file not shown.

@ -0,0 +1,52 @@
package geoip
import (
_ "embed"
"github.com/oschwald/geoip2-golang"
)
//go:embed GeoLite2-ASN.mmdb
var asnBuff []byte
//go:embed GeoLite2-City.mmdb
var cityBuff []byte
//go:embed GeoLite2-Country.mmdb
var countryBuff []byte
type Client struct {
asnDb *geoip2.Reader
cityDb *geoip2.Reader
countryDb *geoip2.Reader
}
func New() (*Client, error) {
var err error
c := &Client{}
c.asnDb, err = geoip2.FromBytes(asnBuff)
if err != nil {
return nil, err
}
c.cityDb, err = geoip2.FromBytes(cityBuff)
if err != nil {
return nil, err
}
c.countryDb, err = geoip2.FromBytes(countryBuff)
if err != nil {
return nil, err
}
return c, err
}
func (c *Client) Close() {
c.asnDb.Close()
c.cityDb.Close()
c.countryDb.Close()
}

@ -0,0 +1,23 @@
package geoip
import (
"io/ioutil"
"log"
"net/http"
)
func OnlineDownload(downloadUrl string, downloadName string) {
resp, err := http.Get(downloadUrl)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
err = ioutil.WriteFile("./"+downloadName, body, 0644)
if err != nil {
panic(err)
}
log.Printf("已下载最新 ip2region.xdb 数据库 %s ", "./"+downloadName)
}

@ -0,0 +1,31 @@
package geoip
import (
"go.dtapp.net/gostring"
)
var licenseKey = "" // 许可证密钥
func GetGeoLite2AsnDownloadUrl(licenseKey string) string {
return gostring.Replace("https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN&license_key=YOUR_LICENSE_KEY&suffix=tar.gz", "YOUR_LICENSE_KEY", licenseKey)
}
//func GetGeoLite2AsnCsvDownloadUrl(licenseKey string) string {
// return gostring.Replace("https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN-CSV&license_key=YOUR_LICENSE_KEY&suffix=zip", "YOUR_LICENSE_KEY", licenseKey)
//}
func GetGeoLite2CityDownloadUrl(licenseKey string) string {
return gostring.Replace("https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=YOUR_LICENSE_KEY&suffix=tar.gz", "YOUR_LICENSE_KEY", licenseKey)
}
//func GetGeoLite2CityCsvDownloadUrl(licenseKey string) string {
// return gostring.Replace("https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City-CSV&license_key=YOUR_LICENSE_KEY&suffix=zip", "YOUR_LICENSE_KEY", licenseKey)
//}
func GetGeoLite2CountryDownloadUrl(licenseKey string) string {
return gostring.Replace("https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=YOUR_LICENSE_KEY&suffix=tar.gz", "YOUR_LICENSE_KEY", licenseKey)
}
//func GetGeoLite2CountryCsvDownloadUrl(licenseKey string) string {
// return gostring.Replace("https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&license_key=YOUR_LICENSE_KEY&suffix=zip", "YOUR_LICENSE_KEY", licenseKey)
//}

@ -0,0 +1,66 @@
package geoip
import (
_ "embed"
"net"
)
// QueryCityResult 返回
type QueryCityResult struct {
Ip string `json:"ip,omitempty"` // ip
Continent struct {
Code string `json:"code,omitempty"` // 大陆代码
Name string `json:"name,omitempty"` // 大陆名称
} `json:"continent,omitempty"`
Country struct {
Code string `json:"code,omitempty"` // 国家代码
Name string `json:"name,omitempty"` // 国家名称
} `json:"country,omitempty"`
Province struct {
Code string `json:"code,omitempty"` // 省份代码
Name string `json:"name,omitempty"` // 省份名称
} `json:"province,omitempty"`
City struct {
Name string `json:"name,omitempty"` // 城市名称
} `json:"city,omitempty"`
Location struct {
TimeZone string `json:"time_zone,omitempty"` // 位置时区
Latitude float64 `json:"latitude,omitempty"` // 坐标纬度
Longitude float64 `json:"longitude,omitempty"` // 坐标经度
} `json:"location,omitempty"`
}
func (c *Client) QueryCity(ipAddress net.IP) (result QueryCityResult, err error) {
record, err := c.cityDb.City(ipAddress)
if err != nil {
return QueryCityResult{}, err
}
// ip
result.Ip = ipAddress.String()
// 大陆
result.Continent.Code = record.Continent.Code
result.Continent.Name = record.Continent.Names["zh-CN"]
// 国家
result.Country.Code = record.Country.IsoCode
result.Country.Name = record.Country.Names["zh-CN"]
// 省份
if len(record.Subdivisions) > 0 {
result.Province.Code = record.Subdivisions[0].IsoCode
result.Province.Name = record.Subdivisions[0].Names["zh-CN"]
}
// 城市
result.City.Name = record.City.Names["zh-CN"]
// 位置
result.Location.TimeZone = record.Location.TimeZone
result.Location.Latitude = record.Location.Latitude
result.Location.Longitude = record.Location.Longitude
return result, err
}

@ -3,16 +3,37 @@ module go.dtapp.net/goip
go 1.19
require (
github.com/oschwald/geoip2-golang v1.8.0
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda
go.dtapp.net/gorequest v1.0.24
go.dtapp.net/gostring v1.0.6
golang.org/x/text v0.3.7
go.dtapp.net/gorequest v1.0.38
go.dtapp.net/gostring v1.0.10
golang.org/x/text v0.4.0
)
require (
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.8.1 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/saracen/go7z-fixtures v0.0.0-20190623165746-aa6b8fba1d2f // indirect
github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
go.dtapp.net/gorandom v1.0.1 // indirect
go.dtapp.net/gotime v1.0.5 // indirect
go.dtapp.net/gotrace_id v1.0.6 // indirect
golang.org/x/crypto v0.3.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.2.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

113
go.sum

@ -1,19 +1,120 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda h1:h+YpzUB/bGVJcLqW+d5GghcCmE/A25KbzjXvWJQi/+o=
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda/go.mod h1:MSotTrCv1PwoR8QgU1JurEx+lNNbtr25I+m0zbLyAGw=
github.com/saracen/go7z-fixtures v0.0.0-20190623165746-aa6b8fba1d2f h1:PF9WV5j/x6MT+x/sauUHd4objCvJbZb0wdxZkHSdd5A=
github.com/saracen/go7z-fixtures v0.0.0-20190623165746-aa6b8fba1d2f/go.mod h1:6Ff0ADODZ6S3gYepgZ2w7OqFrTqtFcfwDUhmm8jsUhs=
github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f h1:1cJITU3JUI8qNS5T0BlXwANsVdyoJQHQ4hvOxbunPCw=
github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f/go.mod h1:LyBTue+RWeyIfN3ZJ4wVxvDuvlGJtDgCLgCb6HCPgps=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
go.dtapp.net/gorandom v1.0.1 h1:IWfMClh1ECPvyUjlqD7MwLq4mZdUusD1qAwAdsvEJBs=
go.dtapp.net/gorandom v1.0.1/go.mod h1:ZPdgalKpvFV/ATQqR0k4ns/F/IpITAZpx6WkWirr5Y8=
go.dtapp.net/gorequest v1.0.24 h1:N2RJOpCXPWbsjfQ8iYJI1EYC2se3I4QhK1l94DSJsuE=
go.dtapp.net/gorequest v1.0.24/go.mod h1:Td+RpLB++20HRju/dMnkcKB4N+qS+Tg/DszDJ5Ix3nY=
go.dtapp.net/gostring v1.0.6 h1:XqNaThEfHpweLofru5sBqm1UUzc9JWsXyB/M/rTk29w=
go.dtapp.net/gostring v1.0.6/go.mod h1:AMnnLjyNxH+cphxyASJGYCzWpVrkP5RncuVo8xL8s3E=
go.dtapp.net/gorequest v1.0.38 h1:jrLLWdePnqVXNzgWH/dotNHtX8BRqLqYDM4T7Serj90=
go.dtapp.net/gorequest v1.0.38/go.mod h1:Llcxr9Ii+0iLycvoZjqNIJCAiYgsvsEqz36eUuTd7Bk=
go.dtapp.net/gostring v1.0.10 h1:eG+1kQehdJUitj9Hfwy79SndMHYOB7ABpWkTs7mDGeQ=
go.dtapp.net/gostring v1.0.10/go.mod h1:L4kREy89a9AraMHB5tUjjl+5rxP1gpXkDouRKKuzT50=
go.dtapp.net/gotime v1.0.5 h1:12aNgB2ULpP6QgQHEUkLilZ4ASvhpFxMFQkBwn0par8=
go.dtapp.net/gotime v1.0.5/go.mod h1:Gq7eNLr2iMLP18UNWONRq4V3Uhf/ADp4bIrS+Tc6ktY=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
go.dtapp.net/gotrace_id v1.0.6 h1:q6s8jy50vt1820b69JKQaFqbhGS5yJGMVtocwOGOPO0=
go.dtapp.net/gotrace_id v1.0.6/go.mod h1:o5kSzNK4s3GrrKpkRKXtAhArtBG1e5N5O5KGPlBlWG4=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -1,63 +0,0 @@
package goip
import (
"go.dtapp.net/goip/ip2region"
v4 "go.dtapp.net/goip/v4"
v6 "go.dtapp.net/goip/v6"
"log"
"strings"
)
type Client struct {
V4Region ip2region.Ip2Region // IPV4
V4db v4.Pointer // IPV4
V6db v6.Pointer // IPV6
}
// NewIp 实例化
func NewIp() *Client {
app := &Client{}
v4Num := app.V4db.InitIPV4Data()
log.Printf("IPV4 库加载完成 共加载:%d 条 IP 记录\n", v4Num)
v6Num := app.V6db.InitIPV4Data()
log.Printf("IPV6 库加载完成 共加载:%d 条 IP 记录\n", v6Num)
return app
}
func (c *Client) Ipv4(ip string) (res v4.Result, resInfo ip2region.IpInfo) {
res = c.V4db.Find(ip)
resInfo, _ = c.V4Region.MemorySearch(ip)
return res, resInfo
}
func (c *Client) Ipv6(ip string) (res v6.Result) {
res = c.V6db.Find(ip)
return res
}
func (c *Client) isIpv4OrIpv6(ip string) string {
if len(ip) < 7 {
return ""
}
arrIpv4 := strings.Split(ip, ".")
if len(arrIpv4) == 4 {
//. 判断IPv4
for _, val := range arrIpv4 {
if !c.CheckIpv4(val) {
return ""
}
}
return ipv4
}
arrIpv6 := strings.Split(ip, ":")
if len(arrIpv6) == 8 {
// 判断Ipv6
for _, val := range arrIpv6 {
if !c.CheckIpv6(val) {
return "Neither"
}
}
return ipv6
}
return ""
}

@ -1,66 +0,0 @@
package goip
import (
"net"
"testing"
)
func lod() *Client {
return NewIp()
}
func TestTo4(t *testing.T) {
t.Logf(string(net.ParseIP("61.241.55.180").To4()))
}
func BenchmarkTo4(b *testing.B) {
for i := 0; i < b.N; i++ {
b.Logf(string(net.ParseIP("61.241.55.180").To4()))
}
}
func TestTo16(t *testing.T) {
t.Logf(string(net.ParseIP("240e:3b4:38e4:3295:7093:af6c:e545:f2e9").To16()))
}
func BenchmarkTo16(b *testing.B) {
for i := 0; i < b.N; i++ {
b.Logf(string(net.ParseIP("240e:3b4:38e4:3295:7093:af6c:e545:f2e9").To16()))
}
}
func TestIpv4(t *testing.T) {
client := lod()
t.Log(client.V4db.Find("116.7.98.130"))
}
func BenchmarkIpv4(b *testing.B) {
client := lod()
for i := 0; i < b.N; i++ {
b.Log(client.V4db.Find("116.7.98.130"))
}
}
func TestV4Region(t *testing.T) {
client := lod()
t.Log(client.V4Region.MemorySearch("61.241.55.180"))
}
func BenchmarkV4Region(b *testing.B) {
client := lod()
for i := 0; i < b.N; i++ {
b.Log(client.V4Region.MemorySearch("61.241.55.180"))
}
}
func TestIpv6(t *testing.T) {
client := lod()
t.Log(client.V6db.Find("116.7.98.130"))
}
func BenchmarkIpv6(b *testing.B) {
client := lod()
for i := 0; i < b.N; i++ {
b.Log(client.V6db.Find("240e:3b4:38e4:3295:7093:af6c:e545:f2e9"))
}
}

50
ip.go

@ -1,13 +1,16 @@
package goip
import (
"context"
"encoding/json"
"go.dtapp.net/gorequest"
"log"
"net"
)
// GetInsideIp 内网ip
func GetInsideIp() string {
func GetInsideIp(ctx context.Context) string {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
panic(err)
@ -19,7 +22,7 @@ func GetInsideIp() string {
}
// Ips 获取全部网卡的全部IP
func Ips() (map[string]string, error) {
func Ips(ctx context.Context) (map[string]string, error) {
ips := make(map[string]string)
@ -46,34 +49,39 @@ func Ips() (map[string]string, error) {
return ips, nil
}
var respGetOutsideIp struct {
Data struct {
Ip string `json:"ip"`
} `json:"data"`
}
// GetOutsideIp 外网ip
func GetOutsideIp() (ip string) {
ip = "0.0.0.0"
get := gorequest.NewHttp()
get.SetUri("https://api.dtapp.net/ip")
response, err := get.Get()
func GetOutsideIp(ctx context.Context) string {
// 返回结果
type respGetOutsideIp struct {
Data struct {
Ip string `json:"ip,omitempty"`
} `json:"data"`
}
// 请求
getHttp := gorequest.NewHttp()
getHttp.SetUri("https://api.dtapp.net/ip")
response, err := getHttp.Get(ctx)
if err != nil {
return
log.Printf("[GetOutsideIp]getHttp.Get%s\n", err)
return "0.0.0.0"
}
err = json.Unmarshal(response.ResponseBody, &respGetOutsideIp)
// 解析
var responseJson respGetOutsideIp
err = json.Unmarshal(response.ResponseBody, &responseJson)
if err != nil {
return
log.Printf("[GetOutsideIp]json.Unmarshal%s\n", err)
return "0.0.0.0"
}
if respGetOutsideIp.Data.Ip == "" {
return
if responseJson.Data.Ip == "" {
responseJson.Data.Ip = "0.0.0.0"
}
ip = respGetOutsideIp.Data.Ip
return respGetOutsideIp.Data.Ip
return responseJson.Data.Ip
}
// GetMacAddr 获取Mac地址
func GetMacAddr() (arrays []string) {
func GetMacAddr(ctx context.Context) (arrays []string) {
netInterfaces, err := net.Interfaces()
if err != nil {
return arrays

@ -0,0 +1,103 @@
package ip2region
import (
_ "embed"
"errors"
"go.dtapp.net/gostring"
"os"
"strconv"
"strings"
)
const (
IndexBlockLength = 12
)
//go:embed ip2region.db
var dbBuff []byte
type Client 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
dbFile string
}
func New() *Client {
c := &Client{}
return c
}
// 获取Ip信息
func getIpInfo(ipStr string, cityId int64, line []byte) (result QueryResult) {
lineSlice := strings.Split(string(line), "|")
length := len(lineSlice)
result.CityId = cityId
if length < 5 {
for i := 0; i <= 5-length; i++ {
lineSlice = append(lineSlice, "")
}
}
if lineSlice[0] != "0" {
result.Country = gostring.SpaceAndLineBreak(lineSlice[0])
}
if lineSlice[1] != "0" {
result.Region = gostring.SpaceAndLineBreak(lineSlice[1])
}
if lineSlice[2] != "0" {
result.Province = gostring.SpaceAndLineBreak(lineSlice[2])
}
if lineSlice[3] != "0" {
result.City = gostring.SpaceAndLineBreak(lineSlice[3])
}
if lineSlice[4] != "0" {
result.Isp = gostring.SpaceAndLineBreak(lineSlice[4])
}
result.Ip = ipStr
return result
}
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
}

@ -2,16 +2,22 @@ package ip2region
import (
"io/ioutil"
"log"
"net/http"
)
func getOnline() ([]byte, error) {
resp, err := http.Get("https://ghproxy.com/?q=https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.db?raw=true")
func OnlineDownload() {
resp, err := http.Get("https://ghproxy.com/?q=https://github.com/lionsoul2014/ip2region/blob/master/v1.0/data/ip2region.db?raw=true")
if err != nil {
return nil, err
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
return body, err
err = ioutil.WriteFile("./ip2region.db", body, 0644)
if err != nil {
panic(err)
}
log.Printf("已下载最新 ip2region 数据库 %s ", "./ip2region.db")
}

@ -1,177 +0,0 @@
package ip2region
import (
_ "embed"
"errors"
"go.dtapp.net/gostring"
"io/ioutil"
"log"
"os"
"strconv"
"strings"
)
const (
IndexBlockLength = 12
)
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
dbFile string
}
//go:embed ip2region.db
var dbBinStr []byte
type IpInfo struct {
IP string `json:"ip,omitempty"` // 输入的ip地址
CityID int64 `json:"city_id,omitempty"` // 城市ID
Country string `json:"country,omitempty"` // 国家
Region string `json:"region,omitempty"` // 区域
Province string `json:"province,omitempty"` // 省份
City string `json:"city,omitempty"` // 城市
ISP string `json:"isp,omitempty"` // 运营商
}
func (ip IpInfo) String() string {
return ip.IP + "|" + strconv.FormatInt(ip.CityID, 10) + "|" + ip.Country + "|" + ip.Region + "|" + ip.Province + "|" + ip.City + "|" + ip.ISP
}
// 获取Ip信息
func getIpInfo(ipStr string, cityId int64, line []byte) (ipInfo IpInfo) {
lineSlice := strings.Split(string(line), "|")
length := len(lineSlice)
ipInfo.CityID = cityId
if length < 5 {
for i := 0; i <= 5-length; i++ {
lineSlice = append(lineSlice, "")
}
}
if lineSlice[0] != "0" {
ipInfo.Country = gostring.SpaceAndLineBreak(lineSlice[0])
}
if lineSlice[1] != "0" {
ipInfo.Region = gostring.SpaceAndLineBreak(lineSlice[1])
}
if lineSlice[2] != "0" {
ipInfo.Province = gostring.SpaceAndLineBreak(lineSlice[2])
}
if lineSlice[3] != "0" {
ipInfo.City = gostring.SpaceAndLineBreak(lineSlice[3])
}
if lineSlice[4] != "0" {
ipInfo.ISP = gostring.SpaceAndLineBreak(lineSlice[4])
}
ipInfo.IP = ipStr
return ipInfo
}
// MemorySearch memory算法整个数据库全部载入内存单次查询都在0.1x毫秒内
func (r *Ip2Region) MemorySearch(ipStr string) (ipInfo IpInfo, err error) {
ipInfo.IP = ipStr
if r.totalBlocks == 0 {
if err != nil {
return ipInfo, err
}
r.firstIndexPtr = getLong(dbBinStr, 0)
r.lastIndexPtr = getLong(dbBinStr, 4)
r.totalBlocks = (r.lastIndexPtr-r.firstIndexPtr)/IndexBlockLength + 1
}
ip, err := ip2long(ipStr)
if err != nil {
return ipInfo, err
}
h := r.totalBlocks
var dataPtr, l int64
for l <= h {
m := (l + h) >> 1
p := r.firstIndexPtr + m*IndexBlockLength
sip := getLong(dbBinStr, p)
if ip < sip {
h = m - 1
} else {
eip := getLong(dbBinStr, p+4)
if ip > eip {
l = m + 1
} else {
dataPtr = getLong(dbBinStr, p+8)
break
}
}
}
if dataPtr == 0 {
return ipInfo, errors.New("not found")
}
dataLen := (dataPtr >> 24) & 0xFF
dataPtr = dataPtr & 0x00FFFFFF
ipInfo = getIpInfo(ipStr, getLong(dbBinStr, dataPtr), dbBinStr[(dataPtr)+4:dataPtr+dataLen])
return ipInfo, nil
}
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
}
func (r *Ip2Region) OnlineDownload() (err error) {
tmpData, err := getOnline()
if err != nil {
return errors.New("下载失败 %s" + err.Error())
}
if err := ioutil.WriteFile("./ip2region.db", tmpData, 0644); err == nil {
log.Printf("已下载最新 ip2region 数据库 %s ", "./ip2region.db")
} else {
return errors.New("保存失败")
}
return nil
}

@ -0,0 +1,73 @@
package ip2region
import (
"errors"
"net"
"strconv"
)
type QueryResult struct {
Ip string `json:"ip,omitempty"` // ip
CityId int64 `json:"city_id,omitempty"` // 城市代码
Country string `json:"country,omitempty"` // 国家
Region string `json:"region,omitempty"` // 区域
Province string `json:"province,omitempty"` // 省份
City string `json:"city,omitempty"` // 城市
Isp string `json:"isp,omitempty"` // 运营商
}
func (ip QueryResult) String() string {
return ip.Ip + "|" + strconv.FormatInt(ip.CityId, 10) + "|" + ip.Country + "|" + ip.Region + "|" + ip.Province + "|" + ip.City + "|" + ip.Isp
}
// Query memory算法整个数据库全部载入内存单次查询都在0.1x毫秒内
func (c *Client) Query(ipAddress net.IP) (result QueryResult, err error) {
result.Ip = ipAddress.String()
if c.totalBlocks == 0 {
if err != nil {
return QueryResult{}, err
}
c.firstIndexPtr = getLong(dbBuff, 0)
c.lastIndexPtr = getLong(dbBuff, 4)
c.totalBlocks = (c.lastIndexPtr-c.firstIndexPtr)/IndexBlockLength + 1
}
ip, err := ip2long(result.Ip)
if err != nil {
return QueryResult{}, err
}
h := c.totalBlocks
var dataPtr, l int64
for l <= h {
m := (l + h) >> 1
p := c.firstIndexPtr + m*IndexBlockLength
sip := getLong(dbBuff, p)
if ip < sip {
h = m - 1
} else {
eip := getLong(dbBuff, p+4)
if ip > eip {
l = m + 1
} else {
dataPtr = getLong(dbBuff, p+8)
break
}
}
}
if dataPtr == 0 {
return QueryResult{}, errors.New("not found")
}
dataLen := (dataPtr >> 24) & 0xFF
dataPtr = dataPtr & 0x00FFFFFF
result = getIpInfo(result.Ip, getLong(dbBuff, dataPtr), dbBuff[(dataPtr)+4:dataPtr+dataLen])
return result, nil
}

@ -0,0 +1,26 @@
package ip2region_v2
import _ "embed"
//go:embed ip2region.xdb
var cBuff []byte
type Client struct {
db *Searcher
}
func New() (*Client, error) {
var err error
c := &Client{}
// 1、从 dbPath 加载整个 xdb 到内存
// 2、用全局的 cBuff 创建完全基于内存的查询对象。
c.db, err = NewWithBuffer(cBuff)
if err != nil {
return nil, err
}
return c, err
}

@ -0,0 +1,23 @@
package ip2region_v2
import (
"io/ioutil"
"log"
"net/http"
)
func OnlineDownload() {
resp, err := http.Get("https://ghproxy.com/?q=https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb?raw=true")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
err = ioutil.WriteFile("./ip2region.xdb", body, 0644)
if err != nil {
panic(err)
}
log.Printf("已下载最新 ip2region.xdb 数据库 %s ", "./ip2region.xdb")
}

Binary file not shown.

@ -0,0 +1,52 @@
package ip2region_v2
import (
_ "embed"
"go.dtapp.net/gostring"
"net"
)
// QueryResult 返回
type QueryResult struct {
Ip string `json:"ip,omitempty"` // ip
Country string `json:"country,omitempty"` // 国家
Province string `json:"province,omitempty"` // 省份
City string `json:"city,omitempty"` // 城市
Operator string `json:"operator,omitempty"` // 运营商
}
func (c *Client) Query(ipAddress net.IP) (result QueryResult, err error) {
// 备注:并发使用,用整个 xdb 缓存创建的 searcher 对象可以安全用于并发。
str, err := c.db.SearchByStr(ipAddress.String())
if err != nil {
return QueryResult{}, err
}
split := gostring.Split(str, "|")
if len(split) <= 0 {
return QueryResult{}, err
}
result.Ip = ipAddress.String()
result.Country = split[0]
if result.Country == "0" {
result.Country = ""
}
result.Province = split[2]
if result.Province == "0" {
result.Province = ""
}
result.City = split[3]
if result.City == "0" {
result.City = ""
}
result.Operator = split[4]
if result.Operator == "0" {
result.Operator = ""
}
return result, err
}

@ -0,0 +1,240 @@
package ip2region_v2
import (
"encoding/binary"
"fmt"
"os"
)
const (
HeaderInfoLength = 256
VectorIndexRows = 256
VectorIndexCols = 256
VectorIndexSize = 8
SegmentIndexBlockSize = 14
)
// --- Index policy define
type IndexPolicy int
const (
VectorIndexPolicy IndexPolicy = 1
BTreeIndexPolicy IndexPolicy = 2
)
func (i IndexPolicy) String() string {
switch i {
case VectorIndexPolicy:
return "VectorIndex"
case BTreeIndexPolicy:
return "BtreeIndex"
default:
return "unknown"
}
}
// --- Header define
type Header struct {
// data []byte
Version uint16
IndexPolicy IndexPolicy
CreatedAt uint32
StartIndexPtr uint32
EndIndexPtr uint32
}
func NewHeader(input []byte) (*Header, error) {
if len(input) < 16 {
return nil, fmt.Errorf("invalid input buffer")
}
return &Header{
Version: binary.LittleEndian.Uint16(input),
IndexPolicy: IndexPolicy(binary.LittleEndian.Uint16(input[2:])),
CreatedAt: binary.LittleEndian.Uint32(input[4:]),
StartIndexPtr: binary.LittleEndian.Uint32(input[8:]),
EndIndexPtr: binary.LittleEndian.Uint32(input[12:]),
}, nil
}
// --- searcher implementation
type Searcher struct {
handle *os.File
// header info
header *Header
ioCount int
// use it only when this feature enabled.
// Preload the vector index will reduce the number of IO operations
// thus speedup the search process
vectorIndex []byte
// content buffer.
// running with the whole xdb file cached
contentBuff []byte
}
func baseNew(dbFile string, vIndex []byte, cBuff []byte) (*Searcher, error) {
var err error
// content buff first
if cBuff != nil {
return &Searcher{
vectorIndex: nil,
contentBuff: cBuff,
}, nil
}
// open the xdb binary file
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, err
}
return &Searcher{
handle: handle,
vectorIndex: vIndex,
}, nil
}
func NewWithFileOnly(dbFile string) (*Searcher, error) {
return baseNew(dbFile, nil, nil)
}
func NewWithVectorIndex(dbFile string, vIndex []byte) (*Searcher, error) {
return baseNew(dbFile, vIndex, nil)
}
func NewWithBuffer(cBuff []byte) (*Searcher, error) {
return baseNew("", nil, cBuff)
}
func (s *Searcher) Close() {
if s.handle != nil {
err := s.handle.Close()
if err != nil {
return
}
}
}
// GetIOCount return the global io count for the last search
func (s *Searcher) GetIOCount() int {
return s.ioCount
}
// SearchByStr find the region for the specified ip string
func (s *Searcher) SearchByStr(str string) (string, error) {
ip, err := CheckIP(str)
if err != nil {
return "", err
}
return s.Search(ip)
}
// Search find the region for the specified long ip
func (s *Searcher) Search(ip uint32) (string, error) {
// reset the global ioCount
s.ioCount = 0
// locate the segment index block based on the vector index
var il0 = (ip >> 24) & 0xFF
var il1 = (ip >> 16) & 0xFF
var idx = il0*VectorIndexCols*VectorIndexSize + il1*VectorIndexSize
var sPtr, ePtr = uint32(0), uint32(0)
if s.vectorIndex != nil {
sPtr = binary.LittleEndian.Uint32(s.vectorIndex[idx:])
ePtr = binary.LittleEndian.Uint32(s.vectorIndex[idx+4:])
} else if s.contentBuff != nil {
sPtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx:])
ePtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx+4:])
} else {
// read the vector index block
var buff = make([]byte, VectorIndexSize)
err := s.read(int64(HeaderInfoLength+idx), buff)
if err != nil {
return "", fmt.Errorf("read vector index block at %d: %w", HeaderInfoLength+idx, err)
}
sPtr = binary.LittleEndian.Uint32(buff)
ePtr = binary.LittleEndian.Uint32(buff[4:])
}
// fmt.Printf("sPtr=%d, ePtr=%d", sPtr, ePtr)
// binary search the segment index to get the region
var dataLen, dataPtr = 0, uint32(0)
var buff = make([]byte, SegmentIndexBlockSize)
var l, h = 0, int((ePtr - sPtr) / SegmentIndexBlockSize)
for l <= h {
m := (l + h) >> 1
p := sPtr + uint32(m*SegmentIndexBlockSize)
err := s.read(int64(p), buff)
if err != nil {
return "", fmt.Errorf("read segment index at %d: %w", p, err)
}
// decode the data step by step to reduce the unnecessary operations
sip := binary.LittleEndian.Uint32(buff)
if ip < sip {
h = m - 1
} else {
eip := binary.LittleEndian.Uint32(buff[4:])
if ip > eip {
l = m + 1
} else {
dataLen = int(binary.LittleEndian.Uint16(buff[8:]))
dataPtr = binary.LittleEndian.Uint32(buff[10:])
break
}
}
}
//fmt.Printf("dataLen: %d, dataPtr: %d", dataLen, dataPtr)
if dataLen == 0 {
return "", nil
}
// load and return the region data
var regionBuff = make([]byte, dataLen)
err := s.read(int64(dataPtr), regionBuff)
if err != nil {
return "", fmt.Errorf("read region at %d: %w", dataPtr, err)
}
return string(regionBuff), nil
}
// do the data read operation based on the setting.
// content buffer first or will read from the file.
// this operation will invoke the Seek for file based read.
func (s *Searcher) read(offset int64, buff []byte) error {
if s.contentBuff != nil {
cLen := copy(buff, s.contentBuff[offset:])
if cLen != len(buff) {
return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
} else {
_, err := s.handle.Seek(offset, 0)
if err != nil {
return fmt.Errorf("seek to %d: %w", offset, err)
}
s.ioCount++
rLen, err := s.handle.Read(buff)
if err != nil {
return fmt.Errorf("handle read: %w", err)
}
if rLen != len(buff) {
return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
}
return nil
}

@ -0,0 +1,165 @@
package ip2region_v2
import (
"fmt"
"os"
"strconv"
"strings"
)
var shiftIndex = []int{24, 16, 8, 0}
func CheckIP(ip string) (uint32, error) {
var ps = strings.Split(ip, ".")
if len(ps) != 4 {
return 0, fmt.Errorf("invalid ip address `%s`", ip)
}
var val = uint32(0)
for i, s := range ps {
d, err := strconv.Atoi(s)
if err != nil {
return 0, fmt.Errorf("the %dth part `%s` is not an integer", i, s)
}
if d < 0 || d > 255 {
return 0, fmt.Errorf("the %dth part `%s` should be an integer bettween 0 and 255", i, s)
}
val |= uint32(d) << shiftIndex[i]
}
// convert the ip to integer
return val, nil
}
func Long2IP(ip uint32) string {
return fmt.Sprintf("%d.%d.%d.%d", (ip>>24)&0xFF, (ip>>16)&0xFF, (ip>>8)&0xFF, ip&0xFF)
}
func MidIP(sip uint32, eip uint32) uint32 {
return uint32((uint64(sip) + uint64(eip)) >> 1)
}
// LoadHeader load the header info from the specified handle
func LoadHeader(handle *os.File) (*Header, error) {
_, err := handle.Seek(0, 0)
if err != nil {
return nil, fmt.Errorf("seek to the header: %w", err)
}
var buff = make([]byte, HeaderInfoLength)
rLen, err := handle.Read(buff)
if err != nil {
return nil, err
}
if rLen != len(buff) {
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
return NewHeader(buff)
}
// LoadHeaderFromFile load header info from the specified db file path
func LoadHeaderFromFile(dbFile string) (*Header, error) {
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
}
header, err := LoadHeader(handle)
if err != nil {
return nil, err
}
_ = handle.Close()
return header, nil
}
// LoadHeaderFromBuff wrap the header info from the content buffer
func LoadHeaderFromBuff(cBuff []byte) (*Header, error) {
return NewHeader(cBuff[0:256])
}
// LoadVectorIndex util function to load the vector index from the specified file handle
func LoadVectorIndex(handle *os.File) ([]byte, error) {
// load all the vector index block
_, err := handle.Seek(HeaderInfoLength, 0)
if err != nil {
return nil, fmt.Errorf("seek to vector index: %w", err)
}
var buff = make([]byte, VectorIndexRows*VectorIndexCols*VectorIndexSize)
rLen, err := handle.Read(buff)
if err != nil {
return nil, err
}
if rLen != len(buff) {
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
return buff, nil
}
// LoadVectorIndexFromFile load vector index from a specified file path
func LoadVectorIndexFromFile(dbFile string) ([]byte, error) {
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
}
vIndex, err := LoadVectorIndex(handle)
if err != nil {
return nil, err
}
_ = handle.Close()
return vIndex, nil
}
// LoadContent load the whole xdb content from the specified file handle
func LoadContent(handle *os.File) ([]byte, error) {
// get file size
fi, err := handle.Stat()
if err != nil {
return nil, fmt.Errorf("stat: %w", err)
}
size := fi.Size()
// seek to the head of the file
_, err = handle.Seek(0, 0)
if err != nil {
return nil, fmt.Errorf("seek to get xdb file length: %w", err)
}
var buff = make([]byte, size)
rLen, err := handle.Read(buff)
if err != nil {
return nil, err
}
if rLen != len(buff) {
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
return buff, nil
}
// LoadContentFromFile load the whole xdb content from the specified db file path
func LoadContentFromFile(dbFile string) ([]byte, error) {
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
}
cBuff, err := LoadContent(handle)
if err != nil {
return nil, err
}
_ = handle.Close()
return cBuff, nil
}

@ -1,43 +0,0 @@
package goip
import "testing"
func TestGetOutsideIp(t *testing.T) {
t.Log(GetOutsideIp())
}
func BenchmarkGetOutsideIp(b *testing.B) {
for i := 0; i < b.N; i++ {
b.Log(GetOutsideIp())
}
}
func TestGetInsideIp(t *testing.T) {
t.Log(GetInsideIp())
}
func BenchmarkGetInsideIp(b *testing.B) {
for i := 0; i < b.N; i++ {
b.Log(GetInsideIp())
}
}
func TestGetMacAddr(t *testing.T) {
t.Log(GetMacAddr())
}
func BenchmarkGetMacAddr(b *testing.B) {
for i := 0; i < b.N; i++ {
b.Log(GetMacAddr())
}
}
func TestIps(t *testing.T) {
t.Log(Ips())
}
func BenchmarkIps(b *testing.B) {
for i := 0; i < b.N; i++ {
b.Log(Ips())
}
}

@ -0,0 +1,138 @@
package ipv6wry
import (
_ "embed"
"encoding/binary"
"log"
)
var (
header []byte
country []byte
area []byte
v6ip uint64
offset uint32
start uint32
end uint32
)
//go:embed ipv6wry.db
var datBuff []byte
type Client struct {
Offset uint32
ItemLen uint32
IndexLen uint32
}
func New() *Client {
c := &Client{}
buf := datBuff[0:8]
start := binary.LittleEndian.Uint32(buf[:4])
end := binary.LittleEndian.Uint32(buf[4:])
num := int64((end-start)/7 + 1)
log.Printf("ipv6wry.db 共加载:%d 条ip记录\n", num)
return c
}
// ReadData 从文件中读取数据
func (c *Client) readData(length uint32) (rs []byte) {
end := c.Offset + length
dataNum := uint32(len(datBuff))
if c.Offset > dataNum {
return nil
}
if end > dataNum {
end = dataNum
}
rs = datBuff[c.Offset:end]
c.Offset = end
return rs
}
func (c *Client) getAddr() ([]byte, []byte) {
mode := c.readData(1)[0]
if mode == 0x01 {
// [IP][0x01][国家和地区信息的绝对偏移地址]
c.Offset = byteToUInt32(c.readData(3))
return c.getAddr()
}
// [IP][0x02][信息的绝对偏移][...] or [IP][国家][...]
_offset := c.Offset - 1
c1 := c.readArea(_offset)
if mode == 0x02 {
c.Offset = 4 + _offset
} else {
c.Offset = _offset + uint32(1+len(c1))
}
c2 := c.readArea(c.Offset)
return c1, c2
}
func (c *Client) readArea(offset uint32) []byte {
c.Offset = offset
mode := c.readData(1)[0]
if mode == 0x01 || mode == 0x02 {
return c.readArea(byteToUInt32(c.readData(3)))
}
c.Offset = offset
return c.readString()
}
func (c *Client) readString() []byte {
data := make([]byte, 0)
for {
buf := c.readData(1)
if buf[0] == 0 {
break
}
data = append(data, buf[0])
}
return data
}
func (c *Client) searchIndex(ip uint64) uint32 {
c.ItemLen = 8
c.IndexLen = 11
header = datBuff[8:24]
start = binary.LittleEndian.Uint32(header[8:])
counts := binary.LittleEndian.Uint32(header[:8])
end = start + counts*c.IndexLen
buf := make([]byte, c.IndexLen)
for {
mid := start + c.IndexLen*(((end-start)/c.IndexLen)>>1)
buf = datBuff[mid : mid+c.IndexLen]
_ip := binary.LittleEndian.Uint64(buf[:c.ItemLen])
if end-start == c.IndexLen {
if ip >= binary.LittleEndian.Uint64(datBuff[end:end+c.ItemLen]) {
buf = datBuff[end : end+c.IndexLen]
}
return byteToUInt32(buf[c.ItemLen:])
}
if _ip > ip {
end = mid
} else if _ip < ip {
start = mid
} else if _ip == ip {
return byteToUInt32(buf[c.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
}

@ -1,4 +1,4 @@
package v6
package ipv6wry
import (
"github.com/saracen/go7z"
@ -9,28 +9,39 @@ import (
"os"
)
func getOnline() (data []byte, err error) {
func OnlineDownload() {
resp, err := http.Get("https://ip.zxinc.org/ip.7z")
if err != nil {
return nil, err
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
panic(err)
}
file7z, err := ioutil.TempFile("", "*")
if err != nil {
return nil, err
panic(err)
}
defer os.Remove(file7z.Name())
if err := ioutil.WriteFile(file7z.Name(), body, 0644); err == nil {
return Un7z(file7z.Name())
err = ioutil.WriteFile(file7z.Name(), body, 0644)
if err != nil {
panic(err)
}
tmpData, err := Un7z(file7z.Name())
if err != nil {
panic(err)
}
err = ioutil.WriteFile("./ipv6wry.db", tmpData, 0644)
if err != nil {
panic(err)
}
return
log.Printf("已下载最新 ZX IPv6数据库 %s ", "./ipv6wry.db")
}
func Un7z(filePath string) (data []byte, err error) {

@ -0,0 +1,82 @@
package ipv6wry
import (
"go.dtapp.net/gostring"
"math/big"
"net"
"strings"
)
// QueryResult 返回
type QueryResult 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"` // 运营商
}
// Query ip地址查询对应归属地信息
func (c *Client) Query(ipAddress net.IP) (result QueryResult) {
result.Ip = ipAddress.String()
c.Offset = 0
tp := big.NewInt(0)
op := big.NewInt(0)
tp.SetBytes(ipAddress.To16())
op.SetString("18446744073709551616", 10)
op.Div(tp, op)
tp.SetString("FFFFFFFFFFFFFFFF", 16)
op.And(op, tp)
v6ip = op.Uint64()
offset = c.searchIndex(v6ip)
c.Offset = offset
country, area = c.getAddr()
// 解析地区数据
info := strings.Split(string(country), "\t")
if len(info) > 0 {
i := 1
for {
if i > len(info) {
break
}
switch i {
case 1:
result.Country = info[i-1]
result.Country = gostring.SpaceAndLineBreak(result.Country)
case 2:
result.Province = info[i-1]
result.Province = gostring.SpaceAndLineBreak(result.Province)
case 3:
result.City = info[i-1]
result.City = gostring.SpaceAndLineBreak(result.City)
case 4:
result.Area = info[i-1]
result.Area = gostring.SpaceAndLineBreak(result.Area)
}
i++ // 自增
}
} else {
result.Country = string(country)
result.Country = gostring.SpaceAndLineBreak(result.Country)
}
// 运营商
result.Isp = string(area)
// Delete ZX (防止不相关的信息产生干扰)
if result.Isp == "ZX" || result.Isp == "" {
result.Isp = ""
} else {
result.Isp = " " + result.Isp
}
result.Isp = gostring.SpaceAndLineBreak(result.Isp)
return result
}

86
is.go

@ -0,0 +1,86 @@
package goip
import (
"go.dtapp.net/gostring"
"net"
"strings"
)
var (
ipv4 = "IPV4"
ipv6 = "IPV6"
)
func (c *Client) isIpv4OrIpv6(ip string) string {
if len(ip) < 7 {
return ""
}
arrIpv4 := strings.Split(ip, ".")
if len(arrIpv4) == 4 {
//. 判断IPv4
for _, val := range arrIpv4 {
if !c.CheckIpv4(val) {
return ""
}
}
return ipv4
}
arrIpv6 := strings.Split(ip, ":")
if len(arrIpv6) == 8 {
// 判断Ipv6
for _, val := range arrIpv6 {
if !c.CheckIpv6(val) {
return "Neither"
}
}
return ipv6
}
return ""
}
// IsIp 是否ip
func IsIp(ipStr string) string {
if ipStr == "" {
return ""
}
// ipv4
if gostring.Contains(ipStr, "/32") {
cidr, _, _ := net.ParseCIDR(ipStr)
if cidr != nil {
return cidr.String()
}
}
// ipv6
if gostring.Contains(ipStr, "/128") {
cidr, _, _ := net.ParseCIDR(ipStr)
if cidr != nil {
return cidr.String()
}
}
// 解析
result := net.ParseIP(ipStr).String()
if result != "<nil>" {
return result
}
return ""
}
// IsIpConsistent 两个ip是否一致
func IsIpConsistent(ipStr1, ipStr2 string) bool {
ip1Result := IsIp(ipStr1)
ip2Result := IsIp(ipStr2)
if ip1Result != "" && ip2Result != "" {
if ip1Result == ip2Result {
return true
}
}
return false
}

@ -0,0 +1,139 @@
package qqwry
import (
_ "embed"
"encoding/binary"
"log"
)
var (
header []byte
country []byte
area []byte
offset uint32
start uint32
end uint32
)
//go:embed qqwry.dat
var datBuff []byte
type Client struct {
Offset uint32
ItemLen uint32
IndexLen uint32
}
func New() *Client {
c := &Client{}
buf := datBuff[0:8]
start := binary.LittleEndian.Uint32(buf[:4])
end := binary.LittleEndian.Uint32(buf[4:])
num := int64((end-start)/7 + 1)
log.Printf("qqwry.dat 共加载:%d 条ip记录\n", num)
return c
}
// ReadData 从文件中读取数据
func (c *Client) readData(length uint32) (rs []byte) {
end := c.Offset + length
dataNum := uint32(len(datBuff))
if c.Offset > dataNum {
return nil
}
if end > dataNum {
end = dataNum
}
rs = datBuff[c.Offset:end]
c.Offset = end
return rs
}
// 获取地址信息
func (c *Client) getAddr() ([]byte, []byte) {
mode := c.readData(1)[0]
if mode == 0x01 {
// [IP][0x01][国家和地区信息的绝对偏移地址]
c.Offset = byteToUInt32(c.readData(3))
return c.getAddr()
}
// [IP][0x02][信息的绝对偏移][...] or [IP][国家][...]
_offset := c.Offset - 1
c1 := c.readArea(_offset)
if mode == 0x02 {
c.Offset = 4 + _offset
} else {
c.Offset = _offset + uint32(1+len(c1))
}
c2 := c.readArea(c.Offset)
return c1, c2
}
// 读取区
func (c *Client) readArea(offset uint32) []byte {
c.Offset = offset
mode := c.readData(1)[0]
if mode == 0x01 || mode == 0x02 {
return c.readArea(byteToUInt32(c.readData(3)))
}
c.Offset = offset
return c.readString()
}
// 读取字符串
func (c *Client) readString() []byte {
data := make([]byte, 0)
for {
buf := c.readData(1)
if buf[0] == 0 {
break
}
data = append(data, buf[0])
}
return data
}
// 搜索索引
func (c *Client) searchIndex(ip uint32) uint32 {
c.ItemLen = 4
c.IndexLen = 7
header = datBuff[0:8]
start = binary.LittleEndian.Uint32(header[:4])
end = binary.LittleEndian.Uint32(header[4:])
buf := make([]byte, c.IndexLen)
for {
mid := start + c.IndexLen*(((end-start)/c.IndexLen)>>1)
buf = datBuff[mid : mid+c.IndexLen]
_ip := binary.LittleEndian.Uint32(buf[:c.ItemLen])
if end-start == c.IndexLen {
if ip >= binary.LittleEndian.Uint32(datBuff[end:end+c.ItemLen]) {
buf = datBuff[end : end+c.IndexLen]
}
return byteToUInt32(buf[c.ItemLen:])
}
if _ip > ip {
end = mid
} else if _ip < ip {
start = mid
} else if _ip == ip {
return byteToUInt32(buf[c.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
}

@ -1,10 +1,11 @@
package v4
package qqwry
import (
"bytes"
"compress/zlib"
"encoding/binary"
"io/ioutil"
"log"
"net/http"
)
@ -25,34 +26,45 @@ func getKey() (uint32, error) {
}
}
// 在线获取内容
func getOnline() ([]byte, error) {
func OnlineDownload() {
resp, err := http.Get("https://update.cz88.net/ip/qqwry.rar")
if err != nil {
return nil, err
panic(err)
}
defer resp.Body.Close()
if body, err := ioutil.ReadAll(resp.Body); err != nil {
return nil, err
} else {
if key, err := getKey(); err != nil {
return nil, err
} else {
for i := 0; i < 0x200; i++ {
key = key * 0x805
key++
key = key & 0xff
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
key, err := getKey()
if err != nil {
panic(err)
}
for i := 0; i < 0x200; i++ {
key = key * 0x805
key++
key = key & 0xff
body[i] = byte(uint32(body[i]) ^ key)
}
body[i] = byte(uint32(body[i]) ^ key)
}
reader, err := zlib.NewReader(bytes.NewReader(body))
if err != nil {
panic(err)
}
reader, err := zlib.NewReader(bytes.NewReader(body))
if err != nil {
return nil, err
}
tmpData, err := ioutil.ReadAll(reader)
if err != nil {
panic(err)
}
return ioutil.ReadAll(reader)
}
err = ioutil.WriteFile("./qqwry.dat", tmpData, 0644)
if err != nil {
panic(err)
}
log.Printf("已下载最新 纯真 IPv4数据库 %s ", "./qqwry.dat")
}

Binary file not shown.

@ -0,0 +1,53 @@
package qqwry
import (
"encoding/binary"
"errors"
"go.dtapp.net/gostring"
"golang.org/x/text/encoding/simplifiedchinese"
"net"
)
// QueryResult 返回
type QueryResult struct {
Ip string `json:"ip,omitempty"` // ip
Country string `json:"country,omitempty"` // 国家或地区
Area string `json:"area,omitempty"` // 区域
}
// Query ip地址查询对应归属地信息
func (c *Client) Query(ipAddress net.IP) (result QueryResult, err error) {
c.Offset = 0
// 偏移
offset = c.searchIndex(binary.BigEndian.Uint32(ipAddress.To4()))
if offset <= 0 {
return QueryResult{}, errors.New("搜索失败")
}
result.Ip = ipAddress.String()
c.Offset = offset + c.ItemLen
country, area = c.getAddr()
enc := simplifiedchinese.GBK.NewDecoder()
result.Country, _ = enc.String(string(country))
result.Country = gostring.SpaceAndLineBreak(result.Country)
result.Area, _ = enc.String(string(area))
// Delete CZ88.NET (防止不相关的信息产生干扰)
if result.Area == " CZ88.NET" || result.Area == "" {
result.Area = ""
} else {
result.Area = " " + result.Area
}
result.Area = gostring.SpaceAndLineBreak(result.Area)
return result, nil
}

@ -0,0 +1,87 @@
package goip
import (
"errors"
"go.dtapp.net/goip/geoip"
"go.dtapp.net/goip/ip2region"
"go.dtapp.net/goip/ip2region_v2"
"go.dtapp.net/goip/ipv6wry"
"go.dtapp.net/goip/qqwry"
"net"
)
var (
QueryIncorrect = errors.New("ip地址不正确")
)
// QueryQqWry 纯真IP库
// https://www.cz88.net/
func (c *Client) QueryQqWry(ipAddress net.IP) (result qqwry.QueryResult, err error) {
if ipAddress.To4() == nil {
return result, QueryIncorrect
}
query, err := c.qqwryClient.Query(ipAddress)
if err != nil {
return qqwry.QueryResult{}, err
}
return query, err
}
// QueryIp2Region ip2region
// https://github.com/lionsoul2014/ip2region
func (c *Client) QueryIp2Region(ipAddress net.IP) (result ip2region.QueryResult, err error) {
if ipAddress.To4() == nil {
return result, QueryIncorrect
}
query, err := c.ip2regionClient.Query(ipAddress)
if err != nil {
return ip2region.QueryResult{}, err
}
return query, err
}
// QueryIp2RegionV2 ip2region
// https://github.com/lionsoul2014/ip2region
func (c *Client) QueryIp2RegionV2(ipAddress net.IP) (result ip2region_v2.QueryResult, err error) {
if ipAddress.To4() == nil {
return result, QueryIncorrect
}
query, err := c.ip2regionV2Client.Query(ipAddress)
if err != nil {
return ip2region_v2.QueryResult{}, err
}
return query, nil
}
// QueryGeoIp ip2region
// https://www.maxmind.com/
func (c *Client) QueryGeoIp(ipAddress net.IP) (result geoip.QueryCityResult, err error) {
if ipAddress.String() == "<nil>" {
return result, QueryIncorrect
}
query, err := c.geoIpClient.QueryCity(ipAddress)
if err != nil {
return geoip.QueryCityResult{}, err
}
return query, nil
}
// QueryIpv6wry ip2region
// https://ip.zxinc.org
func (c *Client) QueryIpv6wry(ipAddress net.IP) (result ipv6wry.QueryResult, err error) {
if ipAddress.To16() == nil {
return result, QueryIncorrect
}
query := c.ipv6wryClient.Query(ipAddress)
return query, nil
}

@ -1,201 +0,0 @@
package v4
import (
_ "embed"
"encoding/binary"
"errors"
"go.dtapp.net/gostring"
"golang.org/x/text/encoding/simplifiedchinese"
"io/ioutil"
"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(ip string) (res Result) {
// 赋值
res.IP = ip
if net.ParseIP("61.241.55.180").To4() == nil {
// 不是ip地址
return res
}
q.Offset = 0
// 偏移
offset = q.searchIndex(binary.BigEndian.Uint32(net.ParseIP(ip).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
}
// 获取地址信息
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
}
// OnlineDownload 在线下载
func (q *Pointer) OnlineDownload() (err error) {
tmpData, err := getOnline()
if err != nil {
return errors.New("下载失败")
}
if err := ioutil.WriteFile("./qqwry.dat", tmpData, 0644); err == nil {
log.Printf("已下载最新 纯真 IPv4数据库 %s ", "./qqwry.dat")
} else {
return errors.New("保存失败")
}
return nil
}

@ -1,226 +0,0 @@
package v6
import (
_ "embed"
"encoding/binary"
"errors"
"go.dtapp.net/gostring"
"io/ioutil"
"log"
"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
}
// 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(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
}
// OnlineDownload 在线下载
func (q *Pointer) OnlineDownload() (err error) {
tmpData, err := getOnline()
if err != nil {
return errors.New("下载失败")
}
if err := ioutil.WriteFile("./ipv6wry.db", tmpData, 0644); err == nil {
log.Printf("已下载最新 ZX IPv6数据库 %s ", "./ipv6wry.db")
} else {
return errors.New("保存失败")
}
return nil
}

@ -1,13 +0,0 @@
package goip
import "testing"
func TestVersion(t *testing.T) {
t.Log(Version)
}
func BenchmarkVersion(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Version
}
}
Loading…
Cancel
Save