diff --git a/cmd/cdn.go b/cmd/cdn.go new file mode 100644 index 0000000..656e5cf --- /dev/null +++ b/cmd/cdn.go @@ -0,0 +1,47 @@ +package cmd + +import ( + "bufio" + "fmt" + "os" + + "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() + + if len(args) == 0 { + stdin := bufio.NewScanner(os.Stdin) + for stdin.Scan() { + line := stdin.Text() + if line == "quit" || line == "exit" { + return + } + fmt.Println(app.ReplaceCDNInString(line)) + } + } else { + app.ParseCDNs(args) + } + }, +} + +var ( + update = false +) + +func init() { + rootCmd.AddCommand(cdnCmd) + cdnCmd.Flags().BoolVarP(&update, "update", "u", false, "Update CDN database") +} diff --git a/internal/app/cdn.go b/internal/app/cdn.go new file mode 100644 index 0000000..0f22892 --- /dev/null +++ b/internal/app/cdn.go @@ -0,0 +1,93 @@ +package app + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/zu1k/nali/constant" + + "github.com/zu1k/nali/pkg/cdn" +) + +var ( + cdnDB cdn.CDN + domainRe *regexp.Regexp +) + +func init() { + domainRe = regexp.MustCompile(`[0-9A-Za-z]{2,}\.[0-9A-Za-z]{2,3}\.[0-9A-Za-z]{2,3}|[0-9A-Za-z]{2,}\.[0-9A-Za-z]{2,3}`) +} + +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 + } + return "未找到" +} + +func ReplaceCDNInString(str string) (result string) { + cnames := domainRe.FindAllString(str, -1) + result = str + for _, cname := range cnames { + name := find(cname) + if name != "未找到" && name != "无法解析" { + result = strings.ReplaceAll(result, cname, fmt.Sprintf("%s [%s]", cname, name)) + } + } + 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数据库...") + tmpData, err := cdn.Download() + if err != nil { + log.Fatalln("下载失败", err.Error()) + return + } + + // 文件存在就删除 + _, err = os.Stat(filePath) + if err == nil { + err = os.Remove(filePath) + if err != nil { + log.Fatalln("旧文件删除失败", err.Error()) + os.Exit(1) + } + } + + if err := ioutil.WriteFile(filePath, tmpData, 0644); err == nil { + log.Printf("已将最新的CDN数据库保存到本地 %s ", filePath) + } +} diff --git a/pkg/cdn/cdn.go b/pkg/cdn/cdn.go new file mode 100644 index 0000000..8704f14 --- /dev/null +++ b/pkg/cdn/cdn.go @@ -0,0 +1,56 @@ +package cdn + +import ( + "encoding/json" + "io/ioutil" + "log" + "os" +) + +type CDN struct { + Data CDNDist +} + +type CDNDist map[string]CDNResult + +type CDNResult struct { + Name string `json:"name"` + Link string `json:"link"` +} + +func NewCDN(filePath string) CDN { + cdnDist := make(CDNDist) + cdnData := make([]byte, 0) + + // 判断文件是否存在 + _, err := os.Stat(filePath) + if err != nil && os.IsNotExist(err) { + log.Println("文件不存在,尝试从网络获取最新CDN数据库") + cdnData, err = Download() + if err != nil { + panic(err) + } else { + if err := ioutil.WriteFile(filePath, cdnData, 0644); err == nil { + log.Printf("已将最新的 CDN数据库 保存到本地: %s ", filePath) + } + } + } else { + // 打开文件句柄 + cdnFile, err := os.OpenFile(filePath, os.O_RDONLY, 0400) + if err != nil { + panic(err) + } + defer cdnFile.Close() + + cdnData, err = ioutil.ReadAll(cdnFile) + if err != nil { + panic(err) + } + } + + err = json.Unmarshal(cdnData, &cdnDist) + if err != nil { + panic("cdn data parse failed!") + } + return CDN{Data: cdnDist} +} diff --git a/pkg/cdn/update.go b/pkg/cdn/update.go new file mode 100644 index 0000000..619f9f9 --- /dev/null +++ b/pkg/cdn/update.go @@ -0,0 +1,21 @@ +package cdn + +import ( + "io/ioutil" + "net/http" +) + +func Download() (data []byte, err error) { + //resp, err := http.Get("https://raw.githubusercontent.com/SukkaLab/cdn/master/dist/cdn.json") + resp, err := http.Get("https://cdn.jsdelivr.net/gh/SukkaLab/cdn/dist/cdn.json") + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return body, nil +} diff --git a/pkg/common/struct.go b/pkg/common/struct.go index 1669509..58849a3 100644 --- a/pkg/common/struct.go +++ b/pkg/common/struct.go @@ -2,8 +2,8 @@ package common import "os" -// FileInfo: info of db file -type FileInfo struct { +// FileData: info of db file +type FileData struct { Data []byte FilePath string FileBase *os.File @@ -11,7 +11,7 @@ type FileInfo struct { // IPDB common ip database type IPDB struct { - Data *FileInfo + Data *FileData Offset uint32 ItemLen uint32 IndexLen uint32 diff --git a/pkg/qqwry/qqwry.go b/pkg/qqwry/qqwry.go index ae75b32..607e778 100644 --- a/pkg/qqwry/qqwry.go +++ b/pkg/qqwry/qqwry.go @@ -20,7 +20,7 @@ type QQwry struct { // NewQQwry new db from path func NewQQwry(filePath string) QQwry { var fileData []byte - var fileInfo common.FileInfo + var fileInfo common.FileData // 判断文件是否存在 _, err := os.Stat(filePath) diff --git a/pkg/zxipv6wry/zxipv6wry.go b/pkg/zxipv6wry/zxipv6wry.go index a68ddd6..963cac6 100644 --- a/pkg/zxipv6wry/zxipv6wry.go +++ b/pkg/zxipv6wry/zxipv6wry.go @@ -18,7 +18,7 @@ type ZXwry struct { func NewZXwry(filePath string) ZXwry { var tmpData []byte - var fileInfo common.FileInfo + var fileInfo common.FileData // 判断文件是否存在 _, err := os.Stat(filePath)