1
0
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:
zu1k 2021-08-02 12:01:25 +08:00
parent f92b91af23
commit 001a5f1715
35 changed files with 339 additions and 553 deletions

View File

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

View File

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

View File

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

View File

@ -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)
},
}

View File

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

View File

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

View File

Before

Width:  |  Height:  |  Size: 61 MiB

After

Width:  |  Height:  |  Size: 61 MiB

View File

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

View File

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

View File

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

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

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

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

45
internal/db/db.go Normal file
View 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
View 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()
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

23
main.go
View File

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

View File

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

View File

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

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

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

29
pkg/dbif/select.go Normal file
View 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)
}

View File

@ -1,6 +1,7 @@
package geoip
import (
"errors"
"fmt"
"log"
"net"
@ -14,7 +15,7 @@ type GeoIP struct {
db *geoip2.Reader
}
// new geoip from db file
// new geoip from database file
func NewGeoIP(filePath string) (geoip GeoIP) {
// 判断文件是否存在
_, err := os.Stat(filePath)
@ -31,18 +32,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)
}
}

View File

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

View File

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

View File

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