1
0
mirror of https://github.com/zu1k/nali.git synced 2025-01-22 13:19:02 +08:00

Merge pull request #57 from zu1k/refactor

Refactor
This commit is contained in:
zu1k 2021-08-11 10:03:23 +08:00 committed by GitHub
commit 0151892abb
35 changed files with 571 additions and 712 deletions

View File

@ -2,8 +2,8 @@ NAME=nali
BINDIR=bin
VERSION=$(shell git describe --tags || echo "unknown version")
BUILDTIME=$(shell date -u)
GOBUILD=CGO_ENABLED=0 go build -trimpath -ldflags '-X "github.com/zu1k/nali/constant.Version=$(VERSION)" \
-X "github.com/zu1k/nali/constant.BuildTime=$(BUILDTIME)" \
GOBUILD=CGO_ENABLED=0 go build -trimpath -ldflags '-X "github.com/zu1k/nali/internal/constant.Version=$(VERSION)" \
-X "github.com/zu1k/nali/internal/constant.BuildTime=$(BUILDTIME)" \
-w -s'
PLATFORM_LIST = \

View File

@ -36,8 +36,10 @@ However the C version has too few functions, and the js version is too big and t
- Interactive query
- Offline query
- Both ipv4 and ipv6 supported
- Multilingual support
- CDN provider query
- Full platform support
- Color print
## Install
@ -154,28 +156,6 @@ Address: 2a00:1450:400e:809::200e [荷兰Amsterdam Google Inc. 服务器网段]
### Query CDN provider
#### Query CDN provider only
```
$ nslookup www.gov.cn | nali cdn
Server: 127.0.0.53
Address: 127.0.0.53#53
Non-authoritative answer:
www.gov.cn canonical name = www.gov.cn.bsgslb.cn [白山云 CDN].
www.gov.cn.bsgslb.cn [白山云 CDN] canonical name = zgovweb.v.bsgslb.cn [白山云 CDN].
Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 185.232.56.148
Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 185.232.56.147
Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 2001:428:6402:21b::6
Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 2001:428:6402:21b::5
```
#### Also query IP geo
```
$ nslookup www.gov.cn | nali
Server: 127.0.0.53 [局域网 IP]
@ -192,14 +172,6 @@ Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 2001:428:6402:21b::6 [美国Louisiana州Monroe Qwest Communications Company, LLC (CenturyLink)]
```
#### Use standalone
You should parse cname by yourself
```
$ nali cdn cdn.somecdncname.com
```
## Interface
### Help
@ -211,19 +183,18 @@ Usage:
nali [command]
Available Commands:
cdn Query cdn service provider
completion generate the autocompletion script for the specified shell
help Help about any command
parse Query IP information
update update chunzhen ip database
Flags:
-h, --help help for nali
-t, --toggle Help message for toggle
--gbk Use GBK decoder
-h, --help help for nali
Use "nali [command] --help" for more information about a command.
```
### Update chunzhen IP database
### Update database
```
$ nali update
@ -231,9 +202,9 @@ $ nali update
2020/07/17 12:54:05 已将最新的纯真 IP 库保存到本地 /root/.nali/qqwry.dat
```
### Use other database
### Select database
Set environment variables `NALI_DB`
Users can specify which database to use set environment variables `NALI_DB_IP4`, `NALI_DB_IP6` or both.
supported database:
@ -246,21 +217,21 @@ supported database:
##### Use geoip db
```
set NALI_DB=geoip
set NALI_DB_IP4=geoip
or use powershell
$env:NALI_DB="geoip"
$env:NALI_DB_IP4="geoip"
```
##### Use ipip db
```
set NALI_DB=ipip
set NALI_DB_IP6=ipip
or use powershell
$env:NALI_DB="ipip"
$env:NALI_DB_IP6="ipip"
```
#### Linux
@ -268,13 +239,24 @@ $env:NALI_DB="ipip"
##### Use geoip db
```
export NALI_DB=geoip
export NALI_DB_IP4=geoip
```
##### Use ipip db
```
export NALI_DB=ipip
export NALI_DB_IP6=ipip
```
### Multilingual support
Specify the language to be used by modifying the environment variable `NALI_LANG`, when using a non-Chinese language only the GeoIP2 database is supported
The values that can be set for this parameter can be found in the list of supported databases for GeoIP2
```
# NALI_LANG=en nali 1.1.1.1
1.1.1.1 [Australia]
```
### Change database directory

View File

@ -35,8 +35,10 @@
- 支持管道处理
- 支持交互式查询
- 同时支持IPv4和IPv6
- 支持多语言
- 查询完全离线
- 全平台支持
- 支持彩色输出
## 安装
@ -159,28 +161,6 @@ Address: 2a00:1450:400e:809::200e [荷兰Amsterdam Google Inc. 服务器网段]
因为 CDN 服务通常使用 CNAME 的域名解析方式,所以推荐与 `nslookup` 或者 `dig` 配合使用,在已经知道 CNAME 后可单独使用
#### 只查询 CDN 服务提供商
```
$ nslookup www.gov.cn | nali cdn
Server: 127.0.0.53
Address: 127.0.0.53#53
Non-authoritative answer:
www.gov.cn canonical name = www.gov.cn.bsgslb.cn [白山云 CDN].
www.gov.cn.bsgslb.cn [白山云 CDN] canonical name = zgovweb.v.bsgslb.cn [白山云 CDN].
Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 185.232.56.148
Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 185.232.56.147
Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 2001:428:6402:21b::6
Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 2001:428:6402:21b::5
```
#### 查询所有信息
```
$ nslookup www.gov.cn | nali
Server: 127.0.0.53 [局域网 IP]
@ -197,14 +177,6 @@ Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 2001:428:6402:21b::6 [美国Louisiana州Monroe Qwest Communications Company, LLC (CenturyLink)]
```
#### 单独使用
需要提前查询到 CNAME 域名
```
$ nali cdn cdn.somecdncname.com
```
## 用户交互
### 查看帮助
@ -216,9 +188,7 @@ Usage:
nali [command]
Available Commands:
cdn Query cdn service provider
help Help about any command
parse Query IP information
update update chunzhen ip database
Flags:
@ -228,7 +198,7 @@ Flags:
Use "nali [command] --help" for more information about a command.
```
### 更新纯真数据库
### 更新数据库
```
$ nali update
@ -236,35 +206,36 @@ $ nali update
2020/07/17 12:54:05 已将最新的纯真 IP 库保存到本地 /root/.nali/qqwry.dat
```
### 使用 Geoip2 数据库
### 自选数据库
需要设置环境变量: `NALI_DB`
用户可以指定使用哪个数据库,需要设置环境变量: `NALI_DB_IP4`、`NALI_DB_IP6` 或者两个同时设置
支持的变量内容:
- Geoip2 `['geoip', 'geoip2', 'geo']`
- Chunzhen `['chunzhen', 'qqip', 'qqwry']`
- IPIP `['ipip', 'ipipfree', 'ipip.net']`
#### Windows平台
##### 使用geoip数据库
```
set NALI_DB=geoip
set NALI_DB_IP4=geoip
或者使用 powershell
$env:NALI_DB="geoip"
$env:NALI_DB_IP4="geoip"
```
##### 使用ipip数据库
```
set NALI_DB=ipip
set NALI_DB_IP6=ipip
或者使用 powershell
$env:NALI_DB="ipip"
$env:NALI_DB_IP6="ipip"
```
#### Linux平台
@ -272,13 +243,24 @@ $env:NALI_DB="ipip"
##### 使用geoip数据库
```
export NALI_DB=geoip
export NALI_DB_IP4=geoip
```
##### 使用ipip数据库
```
export NALI_DB=ipip
export NALI_DB_IP4=ipip
```
### 多语言支持
通过修改环境变量 `NALI_LANG` 来指定使用的语言当使用非中文语言时仅支持GeoIP2这个数据库
该参数可设置的值见 GeoIP2 这个数据库的支持列表
```
# NALI_LANG=en nali 1.1.1.1
1.1.1.1 [Australia]
```
### 更换数据库目录

View File

@ -1,31 +0,0 @@
package cmd
import (
"github.com/zu1k/nali/internal/app"
"github.com/spf13/cobra"
)
// cdnCmd represents the cdn command
var cdnCmd = &cobra.Command{
Use: "cdn",
Short: "Query cdn service provider",
Long: `Query cdn service provider`,
Run: func(cmd *cobra.Command, args []string) {
if update {
app.UpdateDB()
}
app.InitCDNDB()
app.CDN(args)
},
}
var (
update = false
)
func init() {
rootCmd.AddCommand(cdnCmd)
cdnCmd.Flags().BoolVarP(&update, "update", "u", false, "Update CDN database")
}

View File

@ -1,59 +0,0 @@
package cmd
import (
"github.com/zu1k/nali/internal/app"
"github.com/zu1k/nali/internal/ipdb"
"github.com/spf13/cobra"
)
var parseCmd = &cobra.Command{
Use: "parse",
Short: "Query IP information",
Long: `Query IP information.
#1 Query a simple IP address
$ nali 1.2.3.4
or use pipe
$ echo IP 6.6.6.6 | nali
#2 Query multiple IP addresses
$ nali 1.2.3.4 4.3.2.1 123.23.3.0
#3 Interactive query
$ nali
123.23.23.23
123.23.23.23 [越南 越南邮电集团公司]
quit
#4 Use with dig
$ dig nali.lgf.im +short | nali
#5 Use with nslookup
$ nslookup nali.lgf.im 8.8.8.8 | nali
#6 Use with any other program
bash abc.sh | nali
#7 IPV6 support
$ nslookup google.com | nali
`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
app.InitIPDB(ipdb.GetIPDBType())
app.ParseIPs(args)
},
}
func init() {
rootCmd.AddCommand(parseCmd)
}

View File

@ -4,9 +4,9 @@ import (
"log"
"os"
"github.com/spf13/cobra"
"github.com/zu1k/nali/internal/app"
"github.com/zu1k/nali/internal/ipdb"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
@ -53,9 +53,8 @@ Find document on: https://github.com/zu1k/nali
`,
Args: cobra.MinimumNArgs(0),
Run: func(cmd *cobra.Command, args []string) {
app.InitIPDB(ipdb.GetIPDBType())
app.InitCDNDB()
app.Root(args)
gbk, _ := cmd.Flags().GetBool("gbk")
app.Root(args, gbk)
},
}
@ -66,3 +65,7 @@ func Execute() {
os.Exit(1)
}
}
func init() {
rootCmd.Flags().Bool("gbk", false, "Use GBK decoder")
}

View File

@ -4,13 +4,11 @@ import (
"log"
"path/filepath"
"github.com/zu1k/nali/pkg/cdn"
"github.com/zu1k/nali/pkg/zxipv6wry"
"github.com/zu1k/nali/constant"
"github.com/zu1k/nali/pkg/qqwry"
"github.com/spf13/cobra"
"github.com/zu1k/nali/internal/constant"
"github.com/zu1k/nali/pkg/cdn"
"github.com/zu1k/nali/pkg/qqwry"
"github.com/zu1k/nali/pkg/zxipv6wry"
)
// updateCmd represents the update command
@ -29,7 +27,7 @@ var updateCmd = &cobra.Command{
}
// ZX ipv6
filePath = filepath.Join(constant.HomePath, "ipv6wry.db")
filePath = filepath.Join(constant.HomePath, "ipv6wry.database")
log.Println("正在下载最新 ZX IPv6数据库...")
_, err = zxipv6wry.Download(filePath)
if err != nil {

View File

@ -1,6 +0,0 @@
package constant
var (
// HomePath db home path
HomePath string
)

1
go.mod
View File

@ -3,6 +3,7 @@ module github.com/zu1k/nali
go 1.14
require (
github.com/fatih/color v1.12.0 // indirect
github.com/ipipdotnet/ipdb-go v1.3.1
github.com/oschwald/geoip2-golang v1.5.0
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda

7
go.sum
View File

@ -67,6 +67,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@ -177,7 +179,11 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@ -372,6 +378,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -1,86 +0,0 @@
package app
import (
"fmt"
"log"
"path/filepath"
"regexp"
"strings"
"github.com/zu1k/nali/constant"
"github.com/zu1k/nali/internal/tools"
"github.com/zu1k/nali/pkg/cdn"
)
var (
cdnDB cdn.CDN
domainRe *regexp.Regexp
)
func init() {
domainRe = regexp.MustCompile(`[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+`)
}
func InitCDNDB() {
cdnDB = cdn.NewCDN(filepath.Join(constant.HomePath, "cdn.json"))
}
func ParseCDNs(str []string) {
for _, cname := range str {
name := find(cname)
fmt.Printf("%s [%s]\n", cname, name)
}
}
func find(cname string) string {
baseCname := parseBaseCname(cname)
if baseCname == "" {
return "无法解析"
}
cdnResult, found := cdnDB.Data[baseCname]
if found {
return cdnResult.Name
}
if strings.Contains(baseCname, "kunlun") {
return "阿里云 CDN"
}
return "未找到"
}
func ReplaceCDNInString(str string) (result string) {
done := make(map[string]bool)
cnames := domainRe.FindAllString(str, -1)
result = str
for _, cname := range cnames {
name := find(cname)
if name != "未找到" && name != "无法解析" {
if _, found := done[cname]; found {
continue
}
result = tools.AddInfoDomain(result, cname, name)
done[cname] = true
}
}
return
}
func parseBaseCname(domain string) string {
hostParts := strings.Split(domain, ".")
if len(hostParts) < 2 {
return domain
}
baseCname := hostParts[len(hostParts)-2] + "." + hostParts[len(hostParts)-1]
return baseCname
}
func UpdateDB() {
filePath := filepath.Join(constant.HomePath, "cdn.json")
log.Println("正在下载最新 CDN数据库...")
_, err := cdn.Download(filePath)
if err != nil {
log.Fatalln("下载失败", err.Error())
return
}
}

View File

@ -4,20 +4,14 @@ import (
"bufio"
"fmt"
"os"
"runtime"
"strings"
"github.com/zu1k/nali/internal/entity"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
var needTransform = false
func init() {
stat, _ := os.Stdin.Stat()
needTransform = ((stat.Mode() & os.ModeNamedPipe) != 0) && runtime.GOOS == "windows"
}
func Root(args []string) {
func Root(args []string, needTransform bool) {
if len(args) == 0 {
stdin := bufio.NewScanner(os.Stdin)
for stdin.Scan() {
@ -28,27 +22,9 @@ func Root(args []string) {
if line == "quit" || line == "exit" {
return
}
fmt.Printf("%s\n", ReplaceIPInString(ReplaceCDNInString(line)))
fmt.Printf("%s\n", entity.ParseLine(line).ColorString())
}
} else {
ParseIPs(args)
}
}
func CDN(args []string) {
if len(args) == 0 {
stdin := bufio.NewScanner(os.Stdin)
for stdin.Scan() {
line := stdin.Text()
if needTransform {
line, _, _ = transform.String(simplifiedchinese.GBK.NewDecoder(), line)
}
if line == "quit" || line == "exit" {
return
}
fmt.Println(ReplaceCDNInString(line))
}
} else {
ParseCDNs(args)
fmt.Printf("%s\n", entity.ParseLine(strings.Join(args, " ")).ColorString())
}
}

View File

@ -1,111 +0,0 @@
package app
import (
"fmt"
"path/filepath"
"github.com/zu1k/nali/constant"
"github.com/zu1k/nali/internal/ipdb"
"github.com/zu1k/nali/internal/tools"
geoip2 "github.com/zu1k/nali/pkg/geoip"
"github.com/zu1k/nali/pkg/ipip"
"github.com/zu1k/nali/pkg/qqwry"
"github.com/zu1k/nali/pkg/zxipv6wry"
)
var (
db []ipdb.IPDB
qqip qqwry.QQwry
geoip geoip2.GeoIP
)
// InitIPDB init ip db content
func InitIPDB(ipdbtype ipdb.IPDBType) {
db = make([]ipdb.IPDB, 1)
switch ipdbtype {
case ipdb.GEOIP2:
db[0] = geoip2.NewGeoIP(filepath.Join(constant.HomePath, "GeoLite2-City.mmdb"))
case ipdb.QQIP:
db[0] = qqwry.NewQQwry(filepath.Join(constant.HomePath, "qqwry.dat"))
db = append(db, zxipv6wry.NewZXwry(filepath.Join(constant.HomePath, "ipv6wry.db")))
case ipdb.IPIP:
db[0] = ipip.NewIPIPFree(filepath.Join(constant.HomePath, "ipipfree.ipdb"))
db = append(db, zxipv6wry.NewZXwry(filepath.Join(constant.HomePath, "ipv6wry.db")))
}
}
// parse several ips
func ParseIPs(ips []string) {
db0 := db[0]
var db1 ipdb.IPDB
if len(db) > 1 {
db1 = db[1]
} else {
db1 = db[0]
}
for _, ip := range ips {
v := tools.ValidIP(ip)
switch v {
case tools.ValidIPv4:
result := db0.Find(ip)
fmt.Println(formatResult(ip, result))
case tools.ValidIPv6:
if db1 != nil {
result := db1.Find(ip)
fmt.Println(formatResult(ip, result))
}
default:
fmt.Println(ReplaceIPInString(ip))
}
}
}
func RemoveRepeatedElement(arr []string) (newArr []string) {
newArr = make([]string, 0)
for i := 0; i < len(arr); i++ {
repeat := false
for j := i + 1; j < len(arr); j++ {
if arr[i] == arr[j] {
repeat = true
break
}
}
if !repeat {
newArr = append(newArr, arr[i])
}
}
return
}
func ReplaceIPInString(str string) (result string) {
db0 := db[0]
var db1 ipdb.IPDB
if len(db) > 1 {
db1 = db[1]
} else {
db1 = db[0]
}
result = str
ip4s := tools.GetIP4FromString(str)
ip4s = RemoveRepeatedElement(ip4s)
for _, ip := range ip4s {
info := db0.Find(ip)
result = tools.AddInfoIp4(result, ip, info)
}
ip6s := tools.GetIP6FromString(str)
ip6s = RemoveRepeatedElement(ip6s)
for _, ip := range ip6s {
info := db1.Find(ip)
result = tools.AddInfoIp6(result, ip, info)
}
return
}
func formatResult(ip string, result string) string {
if result == "" {
result = "未找到"
}
return fmt.Sprintf("%s [%s]", ip, result)
}

28
internal/constant/path.go Normal file
View File

@ -0,0 +1,28 @@
package constant
import (
"log"
"os"
"path/filepath"
)
var (
// HomePath database home path
HomePath string
)
func init() {
HomePath = os.Getenv("NALI_DB_HOME")
if HomePath == "" {
homeDir, err := os.UserHomeDir()
if err != nil {
panic(err)
}
HomePath = filepath.Join(homeDir, ".nali")
}
if _, err := os.Stat(HomePath); os.IsNotExist(err) {
if err := os.MkdirAll(HomePath, 0777); err != nil {
log.Fatal("can not create", HomePath, ", use bin dir instead")
}
}
}

6
internal/db/cache.go Normal file
View File

@ -0,0 +1,6 @@
package db
import "github.com/zu1k/nali/pkg/dbif"
var dbCache = make(map[dbif.QueryType]dbif.DB)
var queryCache = make(map[string]string)

111
internal/db/db.go Normal file
View File

@ -0,0 +1,111 @@
package db
import (
"os"
"path/filepath"
"github.com/zu1k/nali/pkg/ipip"
"github.com/zu1k/nali/pkg/geoip"
"github.com/zu1k/nali/internal/constant"
"github.com/zu1k/nali/pkg/cdn"
"github.com/zu1k/nali/pkg/dbif"
"github.com/zu1k/nali/pkg/qqwry"
"github.com/zu1k/nali/pkg/zxipv6wry"
)
var (
QQWryPath = filepath.Join(constant.HomePath, "qqwry.dat")
ZXIPv6WryPath = filepath.Join(constant.HomePath, "zxipv6wry.db")
GeoLite2CityPath = filepath.Join(constant.HomePath, "GeoLite2-City.mmdb")
IPIPFreePath = filepath.Join(constant.HomePath, "ipipfree.ipdb")
CDNPath = filepath.Join(constant.HomePath, "cdn.json")
Language = "zh-CN"
IPv4DBSelected = ""
IPv6DBSelected = ""
)
func init() {
lang := os.Getenv("NALI_LANG")
if lang != "" {
Language = lang
}
ipv4DB := os.Getenv("NALI_DB_IP4")
if ipv4DB != "" {
IPv4DBSelected = ipv4DB
}
ipv6DB := os.Getenv("NALI_DB_IP6")
if ipv6DB != "" {
IPv6DBSelected = ipv6DB
}
}
func GetDB(typ dbif.QueryType) (db dbif.DB) {
if db, found := dbCache[typ]; found {
return db
}
switch typ {
case dbif.TypeIPv4:
if IPv4DBSelected != "" {
db = GetIPDBbyName(IPv4DBSelected)
} else {
if Language == "zh-CN" {
db = qqwry.NewQQwry(QQWryPath)
} else {
db = geoip.NewGeoIP(GeoLite2CityPath)
}
}
case dbif.TypeIPv6:
if IPv6DBSelected != "" {
db = GetIPDBbyName(IPv6DBSelected)
} else {
if Language == "zh-CN" {
db = zxipv6wry.NewZXwry(ZXIPv6WryPath)
} else {
geoip.NewGeoIP(GeoLite2CityPath)
}
}
case dbif.TypeDomain:
db = cdn.NewCDN(CDNPath)
default:
panic("Query type not supported!")
}
dbCache[typ] = db
return
}
func GetIPDBbyName(name string) (db dbif.DB) {
switch name {
case "geo", "geoip", "geoip2":
return geoip.NewGeoIP(GeoLite2CityPath)
case "chunzhen", "qqip", "qqwry":
return qqwry.NewQQwry(QQWryPath)
case "ipip", "ipipfree", "ipip.net":
return ipip.NewIPIPFree(IPIPFreePath)
default:
return qqwry.NewQQwry(QQWryPath)
}
}
func Update() {
qqwry.Download(QQWryPath)
zxipv6wry.Download(ZXIPv6WryPath)
cdn.Download(CDNPath)
}
func Find(typ dbif.QueryType, query string) string {
if result, found := queryCache[query]; found {
return result
}
result, err := GetDB(typ).Find(query, Language)
if err != nil {
return ""
}
return result.String()
}

76
internal/entity/entity.go Normal file
View File

@ -0,0 +1,76 @@
package entity
import (
"strings"
"github.com/fatih/color"
"github.com/zu1k/nali/pkg/dbif"
)
type EntityType uint
const (
TypeIPv4 = dbif.TypeIPv4
TypeIPv6 = dbif.TypeIPv6
TypeDomain = dbif.TypeDomain
TypePlain = 100
)
type Entity struct {
Loc []int // s[Loc[0]:Loc[1]]
Type EntityType
Text string
Info string
}
func (e Entity) ParseInfo() error {
return nil
}
type Entities []*Entity
func (es Entities) Len() int {
return len(es)
}
func (es Entities) Less(i, j int) bool {
return es[i].Loc[0] < es[j].Loc[0]
}
func (es Entities) Swap(i, j int) {
es[i], es[j] = es[j], es[i]
}
func (es Entities) String() string {
var result strings.Builder
for _, entity := range es {
result.WriteString(entity.Text)
if entity.Type != TypePlain && len(entity.Info) > 0 {
result.WriteString("[" + entity.Info + "] ")
}
}
return result.String()
}
func (es Entities) ColorString() string {
var line strings.Builder
for _, e := range es {
s := e.Text
switch e.Type {
case TypeIPv4:
s = color.GreenString(e.Text)
case TypeIPv6:
s = color.BlueString(e.Text)
case TypeDomain:
s = color.YellowString(e.Text)
}
if e.Type != TypePlain && len(e.Info) > 0 {
s += " [" + color.RedString(e.Info) + "] "
}
line.WriteString(s)
}
return line.String()
}

68
internal/entity/parse.go Normal file
View File

@ -0,0 +1,68 @@
package entity
import (
"sort"
"github.com/zu1k/nali/internal/db"
"github.com/zu1k/nali/internal/re"
"github.com/zu1k/nali/pkg/dbif"
)
// ParseLine parse a line into entities
func ParseLine(line string) Entities {
ip4sLoc := re.IPv4Re.FindAllStringIndex(line, -1)
ip6sLoc := re.IPv6Re.FindAllStringIndex(line, -1)
domainsLoc := re.DomainRe.FindAllStringIndex(line, -1)
tmp := make(Entities, 0, len(ip4sLoc)+len(ip6sLoc)+len(domainsLoc))
for _, e := range ip4sLoc {
tmp = append(tmp, &Entity{
Loc: e,
Type: TypeIPv4,
Text: line[e[0]:e[1]],
})
}
for _, e := range ip6sLoc {
tmp = append(tmp, &Entity{
Loc: e,
Type: TypeIPv6,
Text: line[e[0]:e[1]],
})
}
for _, e := range domainsLoc {
tmp = append(tmp, &Entity{
Loc: e,
Type: TypeDomain,
Text: line[e[0]:e[1]],
})
}
sort.Sort(tmp)
es := make(Entities, 0, len(tmp))
idx := 0
for _, e := range tmp {
start := e.Loc[0]
if start >= idx {
if start > idx {
es = append(es, &Entity{
Loc: []int{idx, start},
Type: TypePlain,
Text: line[idx:start],
})
}
e.Info = db.Find(dbif.QueryType(e.Type), e.Text)
es = append(es, e)
idx = e.Loc[1]
}
}
if total := len(line); idx < total {
es = append(es, &Entity{
Loc: []int{idx, total},
Type: TypePlain,
Text: line[idx:total],
})
}
return es
}

View File

@ -0,0 +1,15 @@
package entity
import (
"fmt"
"testing"
)
func TestParse(t *testing.T) {
fmt.Println(ParseLine("2001:0db8:85a3:0000:0000:8a2e:0370:7334 baidu.com 1.2.3.4 baidu.com"))
fmt.Println(ParseLine("a.cn.b.com.c.org d.com"))
}
func TestColorPrint(t *testing.T) {
fmt.Println(ParseLine("2001:0db8:85a3:0000:0000:8a2e:0370:7334 baidu.com 1.2.3.4 baidu.com").ColorString())
}

View File

@ -1,26 +0,0 @@
package ipdb
import (
"os"
"strings"
)
// ip db interface
type IPDB interface {
Find(ip string) string
}
func GetIPDBType() IPDBType {
dbname := os.Getenv("NALI_DB")
dbname = strings.ToLower(dbname)
switch dbname {
case "geo", "geoip", "geoip2":
return GEOIP2
case "chunzhen", "qqip", "qqwry":
return QQIP
case "ipip", "ipipfree", "ipip.net":
return IPIP
default:
return QQIP
}
}

View File

@ -1,10 +0,0 @@
package ipdb
// ip db type
type IPDBType int
const (
GEOIP2 = iota // geoip2
QQIP // chunzhen
IPIP // ipip.net
)

10
internal/re/re.go Normal file
View File

@ -0,0 +1,10 @@
package re
import "regexp"
var (
DomainRe = regexp.MustCompile(`[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+`)
IPv4Re = regexp.MustCompile(`(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`)
IPv6Re = regexp.MustCompile(`fe80:(:[0-9a-fA-F]{1,4}){0,4}(%\w+)?|([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?::(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?`)
)

View File

@ -1,4 +1,4 @@
package app
package re
import (
"fmt"
@ -14,10 +14,10 @@ var domainList = []string{
func TestDomainRe(t *testing.T) {
for _, domain := range domainList {
if !domainRe.MatchString(domain) {
if !DomainRe.MatchString(domain) {
t.Error(domain)
t.Fail()
}
fmt.Println(domainRe.FindAllString(domain, -1))
fmt.Println(DomainRe.FindAllString(domain, -1))
}
}

View File

@ -1,54 +0,0 @@
package tools
import (
"net"
"regexp"
"strings"
)
var (
ipv4re *regexp.Regexp
ipv6re0 *regexp.Regexp
ipv6re *regexp.Regexp
)
func init() {
ipv4re = regexp.MustCompile(`(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`)
ipv6re0 = regexp.MustCompile(`^fe80:(:[0-9a-fA-F]{1,4}){0,4}(%\w+)?|([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?::(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?$`)
ipv6re = regexp.MustCompile(`fe80:(:[0-9a-fA-F]{1,4}){0,4}(%\w+)?|([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?::(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?`)
}
func GetIP4FromString(str string) []string {
str = strings.Trim(str, " ")
return ipv4re.FindAllString(str, -1)
}
func GetIP6FromString(str string) []string {
str = strings.Trim(str, " ")
return ipv6re.FindAllString(str, -1)
}
const (
ValidIPv4 = iota
ValidIPv6
InvalidIP
)
type Valid int
func ValidIP(IP string) (v Valid) {
for i := 0; i < len(IP); i++ {
switch IP[i] {
case '.':
v = ValidIPv4
case ':':
v = ValidIPv6
}
}
if ip := net.ParseIP(IP); ip != nil {
return v
}
return InvalidIP
}

View File

@ -1,103 +0,0 @@
package tools
import (
"fmt"
"testing"
)
func TestIP4Re(t *testing.T) {
str := "aaa1.1.11.23a36.36.32.200"
fmt.Println(GetIP4FromString(str))
ValidIP(str)
}
func TestValidIP6(t *testing.T) {
ipv6Valid := []string{
"1:2:3:4:5:6:7::",
"1:2:3:4:5:6:7:8",
"1:2:3:4:5:6::",
"1:2:3:4:5:6::8",
"1:2:3:4:5::",
"1:2:3:4:5::8",
"1:2:3:4::",
"1:2:3:4::8",
"1:2:3::",
"1:2:3::8",
"1:2::",
"1:2::8",
"1::",
"1::8",
"::",
"::8",
"::7:8",
"::6:7:8",
"::5:6:7:8",
"::4:5:6:7:8",
"::3:4:5:6:7:8",
"::2:3:4:5:6:7:8",
"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"::192.168.1.1",
"::ffff:135.75.43.52",
"A:0f:0F:FFFF:5:6:7:8",
}
ipv6Invalid := []string{
"A:0f:0F:FFFF1:5:6:7:8",
"G:0f:0F:FFFF:5:6:7:8",
"2001::25de::cade",
"2001:0db8:85a3:0:0:8A2E:0370:73341",
"a1:a2:a3:a4::b1:b2:b3:b4",
}
for _, i := range ipv6Valid {
if v := ValidIP(i); v == InvalidIP {
t.Log("valid:", i)
}
}
for _, i := range ipv6Invalid {
if v := ValidIP(i); v != InvalidIP {
t.Log("invalid:", i)
}
}
}
func BenchmarkValidIP6STD(b *testing.B) {
b.ResetTimer()
origin := "::ffff:135.75.43.52"
for i := 0; i < b.N; i++ {
ValidIP(origin)
}
}
/*
IPv6-addr = IPv6-full / IPv6-comp / IPv6v4-full / IPv6v4-comp
IPv6-hex = 1*4HEXDIG
IPv6-full = IPv6-hex 7(":" IPv6-hex)
IPv6-comp = [IPv6-hex *5(":" IPv6-hex)] "::"
[IPv6-hex *5(":" IPv6-hex)]
; The "::" represents at least 2 16-bit groups of
; zeros. No more than 6 groups in addition to the
; "::" may be present.
IPv6v4-full = IPv6-hex 5(":" IPv6-hex) ":" IPv4-address-literal
IPv6v4-comp = [IPv6-hex *3(":" IPv6-hex)] "::"
[IPv6-hex *3(":" IPv6-hex) ":"]
IPv4-address-literal
; The "::" represents at least 2 16-bit groups of
; zeros. No more than 4 groups in addition to the
; "::" and IPv4-address-literal may be present.
*/

View File

@ -1,24 +0,0 @@
package tools
import (
"strings"
"regexp"
)
func AddInfoIp4(origin string, ip string, info string) (result string) {
re := regexp.MustCompile("(^|[^0-9.])(" + strings.ReplaceAll(ip, ".", "\\.") + ")($|[^0-9.])")
result = re.ReplaceAllString(origin, "$1$2"+" ["+info+"]$3")
return strings.TrimRight(result, " \t")
}
func AddInfoIp6(origin string, ip string, info string) (result string) {
re := regexp.MustCompile("(^|[^0-9a-fA-F:])(" + strings.ReplaceAll(ip, ".", "\\.") + ")($|[^0-9a-fA-F:])")
result = re.ReplaceAllString(origin, "$1$2"+" ["+info+"]$3")
return strings.TrimRight(result, " \t")
}
func AddInfoDomain(origin string, domain string, info string) (result string) {
re := regexp.MustCompile("(^|[^0-9a-zA-Z-])(" + strings.ReplaceAll(domain, ".", "\\.") + ")($|[^0-9a-zA-Z-\\.])")
result = re.ReplaceAllString(origin, "$1$2"+" ["+info+"]$3")
return strings.TrimRight(result, " \t")
}

23
main.go
View File

@ -1,32 +1,9 @@
package main
import (
"log"
"os"
"path/filepath"
"github.com/zu1k/nali/cmd"
"github.com/zu1k/nali/constant"
)
func main() {
setHomePath()
cmd.Execute()
}
func setHomePath() {
homePath := os.Getenv("NALI_DB_HOME")
if homePath == "" {
homeDir, err := os.UserHomeDir()
if err != nil {
panic(err)
}
homePath = filepath.Join(homeDir, ".nali")
}
constant.HomePath = homePath
if _, err := os.Stat(homePath); os.IsNotExist(err) {
if err := os.MkdirAll(homePath, 0777); err != nil {
log.Fatal("can not create", homePath, ", use bin dir instead")
}
}
}

View File

@ -2,9 +2,12 @@ package cdn
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
)
type CDN struct {
@ -18,7 +21,11 @@ type CDNResult struct {
Link string `json:"link"`
}
func NewCDN(filePath string) CDN {
func (r CDNResult) String() string {
return r.Name
}
func NewCDN(filePath string) *CDN {
cdnDist := make(CDNDist)
cdnData := make([]byte, 0)
@ -46,5 +53,32 @@ func NewCDN(filePath string) CDN {
if err != nil {
panic("cdn data parse failed!")
}
return CDN{Data: cdnDist}
return &CDN{Data: cdnDist}
}
func (db CDN) Find(query string, params ...string) (result fmt.Stringer, err error) {
baseCname := parseBaseCname(query)
if baseCname == "" {
return nil, errors.New("base domain parse failed")
}
cdnResult, found := db.Data[baseCname]
if found {
return cdnResult, nil
}
if strings.Contains(baseCname, "kunlun") {
return CDNResult{
Name: "阿里云 CDN",
}, nil
}
return nil, errors.New("not found")
}
func parseBaseCname(domain string) string {
hostParts := strings.Split(domain, ".")
if len(hostParts) < 2 {
return domain
}
baseCname := hostParts[len(hostParts)-2] + "." + hostParts[len(hostParts)-1]
return baseCname
}

View File

@ -1,8 +1,11 @@
package common
import "os"
import (
"fmt"
"os"
)
// FileData: info of db file
// FileData: info of database file
type FileData struct {
Data []byte
FilePath string
@ -85,3 +88,12 @@ func GetMiddleOffset(start uint32, end uint32, indexLen uint32) uint32 {
records := ((end - start) / indexLen) >> 1
return start + records*indexLen
}
type Result struct {
Country string
Area string
}
func (r Result) String() string {
return fmt.Sprintf("%s %s", r.Country, r.Area)
}

33
pkg/dbif/db.go Normal file
View File

@ -0,0 +1,33 @@
package dbif
import (
"fmt"
"github.com/zu1k/nali/pkg/cdn"
"github.com/zu1k/nali/pkg/geoip"
"github.com/zu1k/nali/pkg/ipip"
"github.com/zu1k/nali/pkg/qqwry"
"github.com/zu1k/nali/pkg/zxipv6wry"
)
type QueryType uint
const (
TypeIPv4 = iota
TypeIPv6
TypeDomain
)
type DB interface {
Find(query string, params ...string) (result fmt.Stringer, err error)
}
var (
_ DB = qqwry.QQwry{}
_ DB = zxipv6wry.ZXwry{}
_ DB = ipip.IPIPFree{}
_ DB = geoip.GeoIP{}
_ DB = cdn.CDN{}
)

View File

@ -1,6 +1,7 @@
package geoip
import (
"errors"
"fmt"
"log"
"net"
@ -14,7 +15,7 @@ type GeoIP struct {
db *geoip2.Reader
}
// new geoip from db file
// new geoip from database file
func NewGeoIP(filePath string) (geoip GeoIP) {
// 判断文件是否存在
_, err := os.Stat(filePath)
@ -31,18 +32,39 @@ func NewGeoIP(filePath string) (geoip GeoIP) {
return
}
// find ip info
func (g GeoIP) Find(ip string) string {
ipData := net.ParseIP(ip)
record, err := g.db.City(ipData)
if err != nil {
log.Fatal(err)
func (g GeoIP) Find(query string, params ...string) (result fmt.Stringer, err error) {
ip := net.ParseIP(query)
if ip == nil {
return nil, errors.New("Query should be valid IP")
}
country := record.Country.Names["zh-CN"]
city := record.City.Names["zh-CN"]
if city == "" {
return country
record, err := g.db.City(ip)
if err != nil {
return
}
lang := "zh-CN"
if len(params) > 0 {
if _, ok := record.Country.Names[params[0]]; ok {
lang = params[0]
}
}
result = Result{
Country: record.Country.Names[lang],
City: record.City.Names[lang],
}
return
}
type Result struct {
Country string
City string
}
func (r Result) String() string {
if r.City == "" {
return r.Country
} else {
return fmt.Sprintf("%s %s", country, city)
return fmt.Sprintf("%s %s", r.Country, r.City)
}
}

View File

@ -30,15 +30,30 @@ func NewIPIPFree(filePath string) IPIPFree {
}
}
func (db IPIPFree) Find(ip string) string {
info, err := db.FindInfo(ip, "CN")
if err != nil {
log.Fatalln("IPIP 查询失败:", err.Error())
return ""
type Result struct {
Country string
Region string
City string
}
func (r Result) String() string {
if r.City == "" {
return fmt.Sprintf("%s %s", r.Country, r.Region)
}
return fmt.Sprintf("%s %s %s", r.Country, r.Region, r.City)
}
func (db IPIPFree) Find(query string, params ...string) (result fmt.Stringer, err error) {
info, err := db.FindInfo(query, "CN")
if err != nil || info == nil {
return nil, err
} else {
if info.CityName == "" {
return fmt.Sprintf("%s %s", info.CountryName, info.RegionName)
// info contains more info
result = Result{
Country: info.CountryName,
Region: info.RegionName,
City: info.CityName,
}
return fmt.Sprintf("%s %s %s", info.CountryName, info.RegionName, info.CityName)
return
}
}

View File

@ -2,6 +2,7 @@ package qqwry
import (
"encoding/binary"
"errors"
"fmt"
"io/ioutil"
"log"
@ -17,7 +18,7 @@ type QQwry struct {
common.IPDB
}
// NewQQwry new db from path
// NewQQwry new database from path
func NewQQwry(filePath string) QQwry {
var fileData []byte
var fileInfo common.FileData
@ -55,25 +56,28 @@ func NewQQwry(filePath string) QQwry {
}
}
// Find ip地址查询对应归属地信息
func (db QQwry) Find(ip string) (res string) {
if strings.Count(ip, ".") != 3 {
return
func (db QQwry) Find(query string, params ...string) (result fmt.Stringer, err error) {
ip := net.ParseIP(query)
if ip == nil {
return nil, errors.New("Query should be IPv4")
}
ip4 := ip.To4()
if ip4 == nil {
return nil, errors.New("Query should be IPv4")
}
ip4uint := binary.BigEndian.Uint32(ip4)
ip4 := binary.BigEndian.Uint32(net.ParseIP(ip).To4())
offset := db.searchIndex(ip4)
offset := db.searchIndex(ip4uint)
if offset <= 0 {
return
return nil, errors.New("Query not valid")
}
var gbkCountry []byte
var gbkArea []byte
mode := db.ReadMode(offset + 4)
// [IP][0x01][国家和地区信息的绝对偏移地址]
if mode == common.RedirectMode1 {
switch mode {
case common.RedirectMode1: // [IP][0x01][国家和地区信息的绝对偏移地址]
countryOffset := db.ReadUInt24()
mode = db.ReadMode(countryOffset)
if mode == common.RedirectMode2 {
@ -85,11 +89,11 @@ func (db QQwry) Find(ip string) (res string) {
countryOffset += uint32(len(gbkCountry) + 1)
}
gbkArea = db.ReadArea(countryOffset)
} else if mode == common.RedirectMode2 {
case common.RedirectMode2:
countryOffset := db.ReadUInt24()
gbkCountry = db.ReadString(countryOffset)
gbkArea = db.ReadArea(offset + 8)
} else {
default:
gbkCountry = db.ReadString(offset + 4)
gbkArea = db.ReadArea(offset + uint32(5+len(gbkCountry)))
}
@ -98,13 +102,11 @@ func (db QQwry) Find(ip string) (res string) {
country, _ := enc.String(string(gbkCountry))
area, _ := enc.String(string(gbkArea))
country = strings.ReplaceAll(country, " CZ88.NET", "")
area = strings.ReplaceAll(area, " CZ88.NET", "")
if area == "" {
return country
result = common.Result{
Country: strings.ReplaceAll(country, " CZ88.NET", ""),
Area: strings.ReplaceAll(area, " CZ88.NET", ""),
}
return fmt.Sprintf("%s %s", country, area)
return result, nil
}
// searchIndex 查找索引位置

View File

@ -2,6 +2,7 @@ package zxipv6wry
import (
"encoding/binary"
"errors"
"fmt"
"io/ioutil"
"log"
@ -50,10 +51,19 @@ func NewZXwry(filePath string) ZXwry {
}
}
func (db ZXwry) Find(ip string) (result string) {
func (db ZXwry) Find(query string, params ...string) (result fmt.Stringer, err error) {
ip := net.ParseIP(query)
if ip == nil {
return nil, errors.New("Query should be IPv6")
}
ip6 := ip.To16()
if ip6 == nil {
return nil, errors.New("Query should be IPv6")
}
tp := big.NewInt(0)
op := big.NewInt(0)
tp.SetBytes(net.ParseIP(ip).To16())
tp.SetBytes(ip6)
op.SetString("18446744073709551616", 10)
op.Div(tp, op)
tp.SetString("FFFFFFFFFFFFFFFF", 16)
@ -63,10 +73,11 @@ func (db ZXwry) Find(ip string) (result string) {
offset := db.searchIndex(ipv6)
country, area := db.getAddr(offset)
country = strings.ReplaceAll(country, " CZ88.NET", "")
area = strings.ReplaceAll(area, " CZ88.NET", "")
return fmt.Sprintf("%s %s", country, area)
result = common.Result{
Country: strings.ReplaceAll(country, " CZ88.NET", ""),
Area: strings.ReplaceAll(area, " CZ88.NET", ""),
}
return result, nil
}
func (db *ZXwry) getAddr(offset uint32) (string, string) {