Compare commits

...

24 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
李光春 b56472cb82 - update v4
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 360b1ead59 - update v4
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 d407385736 - update
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 44114ef943 - update
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 5be5453e5b - update
2 years ago
李光春 35356ae48b - update v4
2 years ago

4
.gitignore vendored

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

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 李光春
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -2,43 +2,38 @@
<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)
[![goproxy.cn](https://goproxy.cn/stats/go.dtapp.net/goip/badges/download-count.svg)](https://goproxy.cn/stats/go.dtapp.net/goip)
[![goreportcard.com](https://goreportcard.com/badge/go.dtapp.net/goip )](https://goreportcard.com/report/go.dtapp.net/goip)
[![deps.dev](https://img.shields.io/badge/deps-go-red.svg)](https://deps.dev/go/go.dtapp.net/goip)
[![deps.dev](https://img.shields.io/badge/deps-go-red.svg)](https://deps.dev/go/go.dtapp.net%2Fgoip)
#### 安装使用
#### 安装
```go
go get -v -u go.dtapp.net/goip
```
#### 导入
```go
import (
"go.dtapp.net/goip"
)
```
#### 使用
```go
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()
}

@ -0,0 +1,3 @@
package goip
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
}

@ -1,17 +1,39 @@
module go.dtapp.net/goip
go 1.18
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.20
go.dtapp.net/gostring v1.0.3
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/gotime v1.0.2 // 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
)

119
go.sum

@ -1,17 +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/gorequest v1.0.20 h1:rddlXqcabDQNUb+iBvJthpNyD0Bv1yLbYftK+CSwSMA=
go.dtapp.net/gorequest v1.0.20/go.mod h1:EwOfdfxsWPszOWrphCWHTN4DbYtU6fyQ/fuWQyQwSnk=
go.dtapp.net/gostring v1.0.3 h1:KSOq4D77/g5yZN/bqWfZ0kOOaPr/P1240vg03+XdENI=
go.dtapp.net/gostring v1.0.3/go.mod h1:+ggrOvgQDQturi1QGsXEpyRN/ZPoRDaqhMujIk5lrgQ=
go.dtapp.net/gotime v1.0.2 h1:CFIJHQXC/4t9bsJhk2cLhjHd6rpdPcJXr8BcHKHDuQo=
go.dtapp.net/gotime v1.0.2/go.mod h1:Gq7eNLr2iMLP18UNWONRq4V3Uhf/ADp4bIrS+Tc6ktY=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
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.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=
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,3 +0,0 @@
package goip
const Version = "1.0.18"

@ -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