mirror of
https://github.com/zu1k/nali.git
synced 2025-03-10 10:22:32 +08:00
feat: Define DB interface
This commit is contained in:
parent
f92b91af23
commit
001a5f1715
4
Makefile
4
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 = \
|
||||
|
31
cmd/cdn.go
31
cmd/cdn.go
@ -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")
|
||||
}
|
59
cmd/parse.go
59
cmd/parse.go
@ -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)
|
||||
}
|
@ -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)
|
||||
},
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -1,6 +0,0 @@
|
||||
package constant
|
||||
|
||||
var (
|
||||
// HomePath db home path
|
||||
HomePath string
|
||||
)
|
Before Width: | Height: | Size: 61 MiB After Width: | Height: | Size: 61 MiB |
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
28
internal/constant/path.go
Normal file
28
internal/constant/path.go
Normal 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
6
internal/db/cache.go
Normal 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)
|
45
internal/db/db.go
Normal file
45
internal/db/db.go
Normal file
@ -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)
|
||||
}
|
17
internal/db/find.go
Normal file
17
internal/db/find.go
Normal file
@ -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()
|
||||
}
|
@ -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")
|
@ -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 {
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
package ipdb
|
||||
|
||||
// ip db type
|
||||
type IPDBType int
|
||||
|
||||
const (
|
||||
GEOIP2 = iota // geoip2
|
||||
QQIP // chunzhen
|
||||
IPIP // ipip.net
|
||||
)
|
@ -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
|
||||
}
|
@ -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.
|
||||
*/
|
@ -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
23
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
33
pkg/dbif/db.go
Normal 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{}
|
||||
)
|
29
pkg/dbif/select.go
Normal file
29
pkg/dbif/select.go
Normal file
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 查找索引位置
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user