diff --git a/Makefile b/Makefile index 0e31c801..0d0085cf 100644 --- a/Makefile +++ b/Makefile @@ -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 = \ diff --git a/README.md b/README.md index 21e2b338..8a8372f4 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/README_zh-CN.md b/README_zh-CN.md index 199e22a1..91fb40a4 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -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] ``` ### 更换数据库目录 diff --git a/cmd/cdn.go b/cmd/cdn.go deleted file mode 100644 index 5b1895b9..00000000 --- a/cmd/cdn.go +++ /dev/null @@ -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") -} diff --git a/cmd/parse.go b/cmd/parse.go deleted file mode 100644 index c002076a..00000000 --- a/cmd/parse.go +++ /dev/null @@ -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) -} diff --git a/cmd/root.go b/cmd/root.go index 27092ebb..09a74d90 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -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") +} diff --git a/cmd/update.go b/cmd/update.go index 6e3efbd3..beab8da6 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -4,13 +4,11 @@ import ( "log" "path/filepath" + "github.com/spf13/cobra" + "github.com/zu1k/nali/internal/constant" "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/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 { diff --git a/constant/path.go b/constant/path.go deleted file mode 100644 index 0465455d..00000000 --- a/constant/path.go +++ /dev/null @@ -1,6 +0,0 @@ -package constant - -var ( - // HomePath db home path - HomePath string -) diff --git a/db/GeoLite2-City.mmdb b/database/GeoLite2-City.mmdb similarity index 100% rename from db/GeoLite2-City.mmdb rename to database/GeoLite2-City.mmdb diff --git a/db/cdn.json b/database/cdn.json similarity index 100% rename from db/cdn.json rename to database/cdn.json diff --git a/db/ipipfree.ipdb b/database/ipipfree.ipdb similarity index 100% rename from db/ipipfree.ipdb rename to database/ipipfree.ipdb diff --git a/db/ipv6wry.db b/database/ipv6wry.db similarity index 100% rename from db/ipv6wry.db rename to database/ipv6wry.db diff --git a/db/qqwry.dat b/database/qqwry.dat similarity index 100% rename from db/qqwry.dat rename to database/qqwry.dat diff --git a/go.mod b/go.mod index 33b77ce0..afd908fc 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index a3685784..b294dd1c 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/internal/app/cdn.go b/internal/app/cdn.go deleted file mode 100644 index c9748e4f..00000000 --- a/internal/app/cdn.go +++ /dev/null @@ -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 - } -} diff --git a/internal/app/command.go b/internal/app/command.go index 10eb1bdc..08f299b6 100644 --- a/internal/app/command.go +++ b/internal/app/command.go @@ -4,38 +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) { - 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.Printf("%s\n", ReplaceIPInString(ReplaceCDNInString(line))) - } - } else { - ParseIPs(args) - } -} - -func CDN(args []string) { +func Root(args []string, needTransform bool) { if len(args) == 0 { stdin := bufio.NewScanner(os.Stdin) for stdin.Scan() { @@ -46,9 +22,9 @@ func CDN(args []string) { if line == "quit" || line == "exit" { return } - fmt.Println(ReplaceCDNInString(line)) + fmt.Printf("%s\n", entity.ParseLine(line).ColorString()) } } else { - ParseCDNs(args) + fmt.Printf("%s\n", entity.ParseLine(strings.Join(args, " ")).ColorString()) } } diff --git a/internal/app/parse.go b/internal/app/parse.go deleted file mode 100644 index d21045d5..00000000 --- a/internal/app/parse.go +++ /dev/null @@ -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) -} diff --git a/internal/constant/path.go b/internal/constant/path.go new file mode 100644 index 00000000..2d82cf8c --- /dev/null +++ b/internal/constant/path.go @@ -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") + } + } +} diff --git a/constant/version.go b/internal/constant/version.go similarity index 100% rename from constant/version.go rename to internal/constant/version.go diff --git a/internal/db/cache.go b/internal/db/cache.go new file mode 100644 index 00000000..fe5345b7 --- /dev/null +++ b/internal/db/cache.go @@ -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) diff --git a/internal/db/db.go b/internal/db/db.go new file mode 100644 index 00000000..6d6ae186 --- /dev/null +++ b/internal/db/db.go @@ -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() +} diff --git a/internal/entity/entity.go b/internal/entity/entity.go new file mode 100644 index 00000000..53e0c774 --- /dev/null +++ b/internal/entity/entity.go @@ -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() +} diff --git a/internal/entity/parse.go b/internal/entity/parse.go new file mode 100644 index 00000000..cfe38e1b --- /dev/null +++ b/internal/entity/parse.go @@ -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 +} diff --git a/internal/entity/parse_test.go b/internal/entity/parse_test.go new file mode 100644 index 00000000..5d3dcba6 --- /dev/null +++ b/internal/entity/parse_test.go @@ -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()) +} diff --git a/internal/ipdb/db.go b/internal/ipdb/db.go deleted file mode 100644 index 5bbf8265..00000000 --- a/internal/ipdb/db.go +++ /dev/null @@ -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 - } -} diff --git a/internal/ipdb/ipdb.go b/internal/ipdb/ipdb.go deleted file mode 100644 index a51b3f7b..00000000 --- a/internal/ipdb/ipdb.go +++ /dev/null @@ -1,10 +0,0 @@ -package ipdb - -// ip db type -type IPDBType int - -const ( - GEOIP2 = iota // geoip2 - QQIP // chunzhen - IPIP // ipip.net -) diff --git a/internal/re/re.go b/internal/re/re.go new file mode 100644 index 00000000..532132e6 --- /dev/null +++ b/internal/re/re.go @@ -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})?`) +) diff --git a/internal/app/re_test.go b/internal/re/re_test.go similarity index 74% rename from internal/app/re_test.go rename to internal/re/re_test.go index 9cb7e36b..b3cf43b2 100644 --- a/internal/app/re_test.go +++ b/internal/re/re_test.go @@ -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)) } } diff --git a/internal/tools/ipparser.go b/internal/tools/ipparser.go deleted file mode 100644 index 69c459e8..00000000 --- a/internal/tools/ipparser.go +++ /dev/null @@ -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 -} diff --git a/internal/tools/iptools_test.go b/internal/tools/iptools_test.go deleted file mode 100644 index 38ed08e9..00000000 --- a/internal/tools/iptools_test.go +++ /dev/null @@ -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. -*/ diff --git a/internal/tools/replace.go b/internal/tools/replace.go deleted file mode 100644 index 1af42c92..00000000 --- a/internal/tools/replace.go +++ /dev/null @@ -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") -} \ No newline at end of file diff --git a/main.go b/main.go index f712404b..dbd52f24 100644 --- a/main.go +++ b/main.go @@ -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") - } - } -} diff --git a/pkg/cdn/cdn.go b/pkg/cdn/cdn.go index 554a04cd..30f221f7 100644 --- a/pkg/cdn/cdn.go +++ b/pkg/cdn/cdn.go @@ -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 } diff --git a/pkg/common/struct.go b/pkg/common/struct.go index 735d1894..e79c33a6 100644 --- a/pkg/common/struct.go +++ b/pkg/common/struct.go @@ -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) +} diff --git a/pkg/dbif/db.go b/pkg/dbif/db.go new file mode 100644 index 00000000..76fddd62 --- /dev/null +++ b/pkg/dbif/db.go @@ -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{} +) diff --git a/pkg/geoip/geoip.go b/pkg/geoip/geoip.go index 792fc8b2..1f75b05d 100644 --- a/pkg/geoip/geoip.go +++ b/pkg/geoip/geoip.go @@ -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) +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") + } + record, err := g.db.City(ip) if err != nil { - log.Fatal(err) + 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], } - country := record.Country.Names["zh-CN"] - city := record.City.Names["zh-CN"] - if city == "" { - return country + 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) } } diff --git a/pkg/ipip/ipipfree.go b/pkg/ipip/ipipfree.go index 6c8cadb3..0d2e4eff 100644 --- a/pkg/ipip/ipipfree.go +++ b/pkg/ipip/ipipfree.go @@ -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 } } diff --git a/pkg/qqwry/qqwry.go b/pkg/qqwry/qqwry.go index 834fe631..c24c1fc9 100644 --- a/pkg/qqwry/qqwry.go +++ b/pkg/qqwry/qqwry.go @@ -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 查找索引位置 diff --git a/pkg/zxipv6wry/zxipv6wry.go b/pkg/zxipv6wry/zxipv6wry.go index 23b29531..4db88ec4 100644 --- a/pkg/zxipv6wry/zxipv6wry.go +++ b/pkg/zxipv6wry/zxipv6wry.go @@ -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) {