From 001a5f1715ba484eee48aac1844b39717a665b43 Mon Sep 17 00:00:00 2001 From: zu1k Date: Mon, 2 Aug 2021 12:01:25 +0800 Subject: [PATCH] feat: Define DB interface --- Makefile | 4 +- cmd/cdn.go | 31 ------- cmd/parse.go | 59 ------------ cmd/root.go | 3 - cmd/update.go | 5 +- constant/path.go | 6 -- {db => database}/GeoLite2-City.mmdb | Bin {db => database}/cdn.json | 0 {db => database}/ipipfree.ipdb | Bin {db => database}/ipv6wry.db | Bin {db => database}/qqwry.dat | Bin internal/app/cdn.go | 81 ---------------- internal/app/command.go | 25 +---- internal/app/parse.go | 102 +++----------------- internal/constant/path.go | 28 ++++++ {constant => internal/constant}/version.go | 0 internal/db/cache.go | 6 ++ internal/db/db.go | 45 +++++++++ internal/db/find.go | 17 ++++ internal/{ipdb/db.go => db/ipdb.go} | 14 ++- internal/entity/entity.go | 11 ++- internal/entity/parse.go | 5 + internal/ipdb/ipdb.go | 10 -- internal/tools/ipparser.go | 41 -------- internal/tools/iptools_test.go | 103 --------------------- internal/tools/replace.go | 24 ----- main.go | 23 ----- pkg/cdn/cdn.go | 38 +++++++- pkg/common/struct.go | 16 +++- pkg/dbif/db.go | 33 +++++++ pkg/dbif/select.go | 29 ++++++ pkg/geoip/geoip.go | 39 +++++--- pkg/ipip/ipipfree.go | 31 +++++-- pkg/qqwry/qqwry.go | 40 ++++---- pkg/zxipv6wry/zxipv6wry.go | 23 +++-- 35 files changed, 339 insertions(+), 553 deletions(-) delete mode 100644 cmd/cdn.go delete mode 100644 cmd/parse.go delete mode 100644 constant/path.go rename {db => database}/GeoLite2-City.mmdb (100%) rename {db => database}/cdn.json (100%) rename {db => database}/ipipfree.ipdb (100%) rename {db => database}/ipv6wry.db (100%) rename {db => database}/qqwry.dat (100%) delete mode 100644 internal/app/cdn.go create mode 100644 internal/constant/path.go rename {constant => internal/constant}/version.go (100%) create mode 100644 internal/db/cache.go create mode 100644 internal/db/db.go create mode 100644 internal/db/find.go rename internal/{ipdb/db.go => db/ipdb.go} (69%) delete mode 100644 internal/ipdb/ipdb.go delete mode 100644 internal/tools/ipparser.go delete mode 100644 internal/tools/iptools_test.go delete mode 100644 internal/tools/replace.go create mode 100644 pkg/dbif/db.go create mode 100644 pkg/dbif/select.go diff --git a/Makefile b/Makefile index 0e31c80..0d0085c 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/cmd/cdn.go b/cmd/cdn.go deleted file mode 100644 index 5b1895b..0000000 --- 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 c002076..0000000 --- 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 27092eb..69d43db 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -6,7 +6,6 @@ import ( "github.com/spf13/cobra" "github.com/zu1k/nali/internal/app" - "github.com/zu1k/nali/internal/ipdb" ) var rootCmd = &cobra.Command{ @@ -53,8 +52,6 @@ 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) }, } diff --git a/cmd/update.go b/cmd/update.go index 6e3efbd..7f26ad2 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -4,10 +4,11 @@ import ( "log" "path/filepath" + "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" @@ -29,7 +30,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 0465455..0000000 --- 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/internal/app/cdn.go b/internal/app/cdn.go deleted file mode 100644 index aeb4358..0000000 --- a/internal/app/cdn.go +++ /dev/null @@ -1,81 +0,0 @@ -package app - -import ( - "fmt" - "log" - "path/filepath" - "strings" - - "github.com/zu1k/nali/constant" - "github.com/zu1k/nali/internal/re" - "github.com/zu1k/nali/internal/tools" - "github.com/zu1k/nali/pkg/cdn" -) - -var ( - cdnDB cdn.CDN -) - -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 := re.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 10eb1bd..9a0e3bb 100644 --- a/internal/app/command.go +++ b/internal/app/command.go @@ -2,7 +2,6 @@ package app import ( "bufio" - "fmt" "os" "runtime" @@ -28,27 +27,11 @@ func Root(args []string) { if line == "quit" || line == "exit" { return } - fmt.Printf("%s\n", ReplaceIPInString(ReplaceCDNInString(line))) + // TODO: pring line + //fmt.Printf("%s\n", ReplaceIPInString(ReplaceCDNInString(line))) } } 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) + // TODO: do something + //ParseIPs(args) } } diff --git a/internal/app/parse.go b/internal/app/parse.go index d21045d..decab5e 100644 --- a/internal/app/parse.go +++ b/internal/app/parse.go @@ -1,12 +1,12 @@ package app import ( - "fmt" "path/filepath" - "github.com/zu1k/nali/constant" - "github.com/zu1k/nali/internal/ipdb" - "github.com/zu1k/nali/internal/tools" + db2 "github.com/zu1k/nali/internal/db" + + "github.com/zu1k/nali/internal/constant" + geoip2 "github.com/zu1k/nali/pkg/geoip" "github.com/zu1k/nali/pkg/ipip" "github.com/zu1k/nali/pkg/qqwry" @@ -14,98 +14,22 @@ import ( ) var ( - db []ipdb.IPDB + db []db2.IPDB qqip qqwry.QQwry geoip geoip2.GeoIP ) -// InitIPDB init ip db content -func InitIPDB(ipdbtype ipdb.IPDBType) { - db = make([]ipdb.IPDB, 1) +// InitIPDB init ip database content +func InitIPDB(ipdbtype db2.IPDBType) { + db = make([]db2.IPDB, 1) switch ipdbtype { - case ipdb.GEOIP2: + case db2.GEOIP2: db[0] = geoip2.NewGeoIP(filepath.Join(constant.HomePath, "GeoLite2-City.mmdb")) - case ipdb.QQIP: + case db2.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 = append(db, zxipv6wry.NewZXwry(filepath.Join(constant.HomePath, "ipv6wry.database"))) + case db2.IPIP: db[0] = ipip.NewIPIPFree(filepath.Join(constant.HomePath, "ipipfree.ipdb")) - db = append(db, zxipv6wry.NewZXwry(filepath.Join(constant.HomePath, "ipv6wry.db"))) + db = append(db, zxipv6wry.NewZXwry(filepath.Join(constant.HomePath, "ipv6wry.database"))) } } - -// 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 0000000..2d82cf8 --- /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 0000000..fe5345b --- /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 0000000..48d6821 --- /dev/null +++ b/internal/db/db.go @@ -0,0 +1,45 @@ +package db + +import ( + "path/filepath" + + "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") +) + +func GetDB(typ dbif.QueryType) (db dbif.DB) { + if db, found := dbCache[typ]; found { + return db + } + + switch typ { + case dbif.TypeIPv4: + db = qqwry.NewQQwry(QQWryPath) + case dbif.TypeIPv6: + db = zxipv6wry.NewZXwry(ZXIPv6WryPath) + // geoip2.NewGeoIP(GeoLite2CityPath) + // ipip.NewIPIPFree(IPIPFreePath) + case dbif.TypeDomain: + db = cdn.NewCDN(CDNPath) + default: + panic("Query type not supported!") + } + return +} + +func Update() { + qqwry.Download(QQWryPath) + zxipv6wry.Download(ZXIPv6WryPath) + cdn.Download(CDNPath) +} diff --git a/internal/db/find.go b/internal/db/find.go new file mode 100644 index 0000000..7e41cd3 --- /dev/null +++ b/internal/db/find.go @@ -0,0 +1,17 @@ +package db + +import ( + "github.com/zu1k/nali/pkg/dbif" +) + +func Find(typ dbif.QueryType, query string) string { + if result, found := queryCache[query]; found { + return result + } + result, err := GetDB(typ).Find(query) + if err != nil { + //log.Printf("Query [%s] error: %s\n", query, err) + return "" + } + return result.String() +} diff --git a/internal/ipdb/db.go b/internal/db/ipdb.go similarity index 69% rename from internal/ipdb/db.go rename to internal/db/ipdb.go index 5bbf826..a2f7799 100644 --- a/internal/ipdb/db.go +++ b/internal/db/ipdb.go @@ -1,14 +1,18 @@ -package ipdb +package db import ( "os" "strings" ) -// ip db interface -type IPDB interface { - Find(ip string) string -} +// ip database type +type IPDBType int + +const ( + GEOIP2 = iota // geoip2 + QQIP // chunzhen + IPIP // ipip.net +) func GetIPDBType() IPDBType { dbname := os.Getenv("NALI_DB") diff --git a/internal/entity/entity.go b/internal/entity/entity.go index 5e6320b..0c3dd21 100644 --- a/internal/entity/entity.go +++ b/internal/entity/entity.go @@ -2,15 +2,18 @@ package entity import ( "strings" + + "github.com/zu1k/nali/pkg/dbif" ) type EntityType uint const ( - TypePlain = iota - TypeIPv4 - TypeIPv6 - TypeDomain + TypeIPv4 = dbif.TypeIPv4 + TypeIPv6 = dbif.TypeIPv6 + TypeDomain = dbif.TypeDomain + + TypePlain = 100 ) type Entity struct { diff --git a/internal/entity/parse.go b/internal/entity/parse.go index 76f43fc..24b210a 100644 --- a/internal/entity/parse.go +++ b/internal/entity/parse.go @@ -3,6 +3,10 @@ package entity import ( "sort" + "github.com/zu1k/nali/pkg/dbif" + + "github.com/zu1k/nali/internal/db" + "github.com/zu1k/nali/internal/re" ) @@ -49,6 +53,7 @@ func ParseLine(line string) Entities { Text: line[idx:start], }) } + e.Info = db.Find(dbif.QueryType(e.Type), e.Text) es = append(es, e) idx = e.Loc[1] } diff --git a/internal/ipdb/ipdb.go b/internal/ipdb/ipdb.go deleted file mode 100644 index a51b3f7..0000000 --- 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/tools/ipparser.go b/internal/tools/ipparser.go deleted file mode 100644 index ac9118b..0000000 --- a/internal/tools/ipparser.go +++ /dev/null @@ -1,41 +0,0 @@ -package tools - -import ( - "net" - "strings" - - "github.com/zu1k/nali/internal/re" -) - -func GetIP4FromString(str string) []string { - str = strings.Trim(str, " ") - return re.IPv4Re.FindAllString(str, -1) -} - -func GetIP6FromString(str string) []string { - str = strings.Trim(str, " ") - return re.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 38ed08e..0000000 --- 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 1af42c9..0000000 --- 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 f712404..dbd52f2 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 554a04c..30f221f 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 735d189..e79c33a 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 0000000..76fddd6 --- /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/dbif/select.go b/pkg/dbif/select.go new file mode 100644 index 0000000..f146cce --- /dev/null +++ b/pkg/dbif/select.go @@ -0,0 +1,29 @@ +package dbif + +func init() { + +} + +type langMap map[string][]DB +type dataTypeMap map[QueryType][]DB + +var ( + lang2DB = make(langMap) + type2DB = make(dataTypeMap) +) + +func RegistLang(lang string, db DB) { + originDBs, found := lang2DB[lang] + if !found { + originDBs = make([]DB, 0, 1) + } + lang2DB[lang] = append(originDBs, db) +} + +func RegistType(typ QueryType, db DB) { + originDBs, found := type2DB[typ] + if !found { + originDBs = make([]DB, 0, 1) + } + type2DB[typ] = append(originDBs, db) +} diff --git a/pkg/geoip/geoip.go b/pkg/geoip/geoip.go index 792fc8b..75426f9 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,32 @@ 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 + } + + result = Result{ + Country: record.Country.Names["zh-CN"], + City: record.City.Names["zh-CN"], + } + 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 6c8cadb..0d2e4ef 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 834fe63..c24c1fc 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 23b2953..4db88ec 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) {