diff options
| author | Loyalsoldier <[email protected]> | 2024-10-25 07:24:30 +0800 |
|---|---|---|
| committer | Loyalsoldier <[email protected]> | 2024-10-25 07:48:14 +0800 |
| commit | 38dfd08332dda5809191bc54ef06b781b1d81e44 (patch) | |
| tree | 7a673c45c0789fb626abbd556dac4f402057fa0b | |
| parent | 87404b7cbcdaa441ed812434852c8aef5c7a5405 (diff) | |
Refine: make lookup command more specific
| -rw-r--r-- | README.md | 186 | ||||
| -rw-r--r-- | lib/container.go | 50 | ||||
| -rw-r--r-- | lookup.go | 102 | ||||
| -rw-r--r-- | plugin/special/lookup.go | 4 |
4 files changed, 245 insertions, 97 deletions
@@ -602,60 +602,186 @@ $ ./geoip convert -c config.json ### 查找 IP 或 CIDR 所在类别(`lookup`) +可能的返回结果: + +- 查询的字符串不是有效的 IP 或 CIDR,返回 `false` +- 查询的 IP 或 CIDR 不存在于任何一个类别中,返回 `false` +- 查询的 IP 或 CIDR 存在于某种格式文件的单个类别中: + - 若该格式文件只包含一个类别,返回 `true` + - 若该格式文件包含多个类别,返回匹配的类别名称 +- 查询的 IP 或 CIDR 存在于多个类别中,返回以英文逗号分隔的类别名称,如 `au,cloudflare` + ```bash -# lookup one IP from local file -$ ./geoip lookup -f text -u ./cn.txt -n cn 1.0.1.1 -cn +# ================= One-time Mode ================= # +# 从 text 格式的本地文件(只包含一个类别)中查找某个 IP 地址 +# lookup IP from local file (with only one list) in text format +$ ./geoip lookup -f text -u ./cn.txt 1.0.1.1 +true -# lookup one CIDR from local file -$ ./geoip lookup -f text -u ./cn.txt -n cn 1.0.1.1/24 -cn +# 从 text 格式的本地文件(只包含一个类别)中查找某个 IP 地址 +# lookup IP from local file (with only one list) in text format +$ ./geoip lookup -f text -u ./cn.txt 2.2.2.2 +false -# lookup IP or CIDR in REPL mode from local file -$ ./geoip lookup -f text -u ./cn.txt -n cn -Enter IP or CIDR (type `exit` to quit): ->> 1.0.1.1 -cn ->> 1.0.1.1/24 + +# 从 text 格式的本地文件(只包含一个类别)中查找某个 CIDR +# lookup CIDR from local file (with only one list) in text format +$ ./geoip lookup -f text -u ./cn.txt 1.0.1.1/24 +true + + +# 从 text 格式的本地文件(只包含一个类别)中查找某个 CIDR +# lookup CIDR from local file (with only one list) in text format +$ ./geoip lookup -f text -u ./cn.txt 1.0.1.1/23 +false + + +# 从 text 格式的远程 URL(只包含一个类别)中查找某个 IP 地址 +# lookup IP from remote URL (with only one list) in text format +$ ./geoip lookup -f text -u https://example.com/cn.txt 1.0.1.1 +true + + +# 从 v2rayGeoIPDat 格式的本地文件(只包含一个类别)中查找某个 IP 地址 +# lookup IP from local file (with only one list) in v2rayGeoIPDat format +$ ./geoip lookup -f v2rayGeoIPDat -u ./cn.dat 1.0.1.1 +true + + +# 从 v2rayGeoIPDat 格式的本地文件(包含多个类别)中查找某个 IP 地址 +# lookup IP from local file (with multiple lists) in v2rayGeoIPDat format +$ ./geoip lookup -f v2rayGeoIPDat -u ./geoip.dat 1.0.1.1 cn -# lookup IP or CIDR in REPL mode from remote file -$ ./geoip lookup -f text -u https://example.com/cn.txt -n cn -Enter IP or CIDR (type `exit` to quit): +# 从 v2rayGeoIPDat 格式的本地文件(包含多个类别)中查找某个 IP 地址 +# lookup IP from local file (with multiple lists) in v2rayGeoIPDat format +$ ./geoip lookup -f v2rayGeoIPDat -u ./geoip.dat 1.0.0.1 +au,cloudflare + + +# 从 v2rayGeoIPDat 格式的远程 URL(包含多个类别)中查找某个 CIDR +# lookup CIDR from remote URL (with multiple lists) in v2rayGeoIPDat format +$ ./geoip lookup -f v2rayGeoIPDat -u https://example.com/geoip.dat 1.0.0.1/24 +au,cloudflare + + + + +# ================= REPL Mode ================= # + +# 从 text 格式的本地文件(只包含一个类别)中查找某个 IP 地址或 CIDR +# lookup IP or CIDR from local file (with only one list) in text format +$ ./geoip lookup -f text -u ./cn.txt +Enter IP or CIDR (type "exit" to quit): >> 1.0.1.1 -cn +true + >> 1.0.1.1/24 -cn +true + +>> 1.0.1.1/23 +false + +>> 2.2.2.2 +false +>> 2.2.2.2/24 +false -# lookup IP or CIDR in REPL mode from local directory, got two lists joined with comma -$ ./geoip lookup -f text -d ./path/to/your/directory/ -Enter IP or CIDR (type `exit` to quit): +>> 300.300.300.300 +false + +>> 300.300.300.300/24 +false + +>> exit + + +# 从 text 格式的远程 URL(只包含一个类别)中查找某个 IP 地址或 CIDR +# lookup IP or CIDR from remote URL (with only one list) in text format +$ ./geoip lookup -f text -u https://example.com/cn.txt +Enter IP or CIDR (type "exit" to quit): >> 1.0.1.1 -cn,my-custom-list +true + >> 1.0.1.1/24 -cn,my-custom-list +true + +>> 1.0.1.1/23 +false + +>> 2.2.2.2 +false +>> 2.2.2.2/24 +false -# lookup IP or CIDR in REPL mode from specified lists in local directory -$ ./geoip lookup -f text -d ./path/to/your/directory/ -l cn,us,jp -Enter IP or CIDR (type `exit` to quit): +>> 300.300.300.300 +false + +>> 300.300.300.300/24 +false + +>> exit + + +# 从 v2rayGeoIPDat 格式的本地文件(只包含一个类别)中查找某个 IP 地址或 CIDR +# lookup IP or CIDR from local file (with only one list) in v2rayGeoIPDat format +$ ./geoip lookup -f v2rayGeoIPDat -u ./cn.dat +Enter IP or CIDR (type "exit" to quit): >> 1.0.1.1 -cn +true + >> 1.0.1.1/24 -cn +true +>> 1.0.1.1/23 +false -# lookup IP or CIDR in REPL mode with another format from specified lists in remote file -$ ./geoip lookup -f v2rayGeoIPDat -u https://example.com/geoip.dat -l cn,us,jp -Enter IP or CIDR (type `exit` to quit): +>> 2.2.2.2 +false + +>> 2.2.2.2/24 +false + +>> 300.300.300.300 +false + +>> 300.300.300.300/24 +false + +>> exit + + +# 从 v2rayGeoIPDat 格式的远程 URL(包含多个类别)中查找某个 IP 地址或 CIDR +# lookup IP or CIDR from remote URL (with multiple list) in v2rayGeoIPDat format +$ ./geoip lookup -f v2rayGeoIPDat -u https://example.com/geoip.dat +Enter IP or CIDR (type "exit" to quit): >> 1.0.1.1 cn + >> 1.0.1.1/24 cn + +>> 1.0.1.1/23 +false + +>> 1.0.0.1 +au,cloudflare + +>> 1.0.0.1/24 +au,cloudflare + +>> 300.300.300.300 +false + +>> 300.300.300.300/24 +false + +>> exit ``` ## 使用本项目的项目 diff --git a/lib/container.go b/lib/container.go index 3ecb5b0e..b08cbc13 100644 --- a/lib/container.go +++ b/lib/container.go @@ -232,41 +232,29 @@ func (c *container) lookup(addrOrPrefix any, iptype IPType, searchList ...string continue } + var ipset *netipx.IPSet + var err error switch iptype { case IPv4: - ipset, err := entry.GetIPv4Set() - if err != nil { - return nil, false, err - } - switch addrOrPrefix := addrOrPrefix.(type) { - case netip.Prefix: - if found := ipset.ContainsPrefix(addrOrPrefix); found { - isfound = true - result = append(result, entry.GetName()) - } - case netip.Addr: - if found := ipset.Contains(addrOrPrefix); found { - isfound = true - result = append(result, entry.GetName()) - } - } - + ipset, err = entry.GetIPv4Set() case IPv6: - ipset, err := entry.GetIPv6Set() - if err != nil { - return nil, false, err + ipset, err = entry.GetIPv6Set() + } + + if err != nil { + return nil, false, err + } + + switch addrOrPrefix := addrOrPrefix.(type) { + case netip.Prefix: + if found := ipset.ContainsPrefix(addrOrPrefix); found { + isfound = true + result = append(result, entry.GetName()) } - switch addrOrPrefix := addrOrPrefix.(type) { - case netip.Prefix: - if found := ipset.ContainsPrefix(addrOrPrefix); found { - isfound = true - result = append(result, entry.GetName()) - } - case netip.Addr: - if found := ipset.Contains(addrOrPrefix); found { - isfound = true - result = append(result, entry.GetName()) - } + case netip.Addr: + if found := ipset.Contains(addrOrPrefix); found { + isfound = true + result = append(result, entry.GetName()) } } } @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "log" + "net/netip" "os" "strings" @@ -12,34 +13,34 @@ import ( ) var supportedInputFormats = map[string]bool{ - strings.ToLower("maxmindMMDB"): true, - strings.ToLower("clashRuleSetClassical"): true, strings.ToLower("clashRuleSet"): true, + strings.ToLower("clashRuleSetClassical"): true, + strings.ToLower("maxmindMMDB"): true, + strings.ToLower("mihomoMRS"): true, + strings.ToLower("singboxSRS"): true, strings.ToLower("surgeRuleSet"): true, strings.ToLower("text"): true, - strings.ToLower("singboxSRS"): true, strings.ToLower("v2rayGeoIPDat"): true, } func init() { rootCmd.AddCommand(lookupCmd) - lookupCmd.Flags().StringP("format", "f", "", "The input format, available options: text, v2rayGeoIPDat, maxmindMMDB, singboxSRS, clashRuleSet, clashRuleSetClassical, surgeRuleSet") - lookupCmd.Flags().StringP("name", "n", "", "The name of the list, use with \"uri\" flag") - lookupCmd.Flags().StringP("uri", "u", "", "URI of the input file, support both local file path and remote HTTP(S) URL") - lookupCmd.Flags().StringP("dir", "d", "", "Path to the input directory. The filename without extension will be as the name of the list") + lookupCmd.Flags().StringP("format", "f", "", "(Required) The input format. Available formats: text, v2rayGeoIPDat, maxmindMMDB, mihomoMRS, singboxSRS, clashRuleSet, clashRuleSetClassical, surgeRuleSet") + lookupCmd.Flags().StringP("uri", "u", "", "URI of the input file, support both local file path and remote HTTP(S) URL. (Cannot be used with \"dir\" flag)") + lookupCmd.Flags().StringP("dir", "d", "", "Path to the input directory. The filename without extension will be as the name of the list. (Cannot be used with \"uri\" flag)") lookupCmd.Flags().StringSliceP("searchlist", "l", []string{}, "The lists to search from, separated by comma") lookupCmd.MarkFlagRequired("format") lookupCmd.MarkFlagsOneRequired("uri", "dir") - lookupCmd.MarkFlagsRequiredTogether("name", "uri") + lookupCmd.MarkFlagsMutuallyExclusive("uri", "dir") lookupCmd.MarkFlagDirname("dir") } var lookupCmd = &cobra.Command{ Use: "lookup", Aliases: []string{"find"}, - Short: "Lookup specified IP or CIDR in specified lists", + Short: "Lookup if specified IP or CIDR is in specified lists", Args: cobra.RangeArgs(0, 1), Run: func(cmd *cobra.Command, args []string) { // Validate format @@ -49,9 +50,8 @@ var lookupCmd = &cobra.Command{ log.Fatal("unsupported input format") } - // Get name - name, _ := cmd.Flags().GetString("name") - name = strings.ToLower(strings.TrimSpace(name)) + // Set name + name := "true" // Get uri uri, _ := cmd.Flags().GetString("uri") @@ -68,42 +68,38 @@ var lookupCmd = &cobra.Command{ switch len(args) > 0 { case true: // With search arg, run in once mode - search := strings.ToLower(args[0]) - config := generateConfigForLookup(format, name, uri, dir, search, searchListStr) - - instance, err := lib.NewInstance() - if err != nil { - log.Fatal(err) - } - if err := instance.InitFromBytes([]byte(config)); err != nil { - log.Fatal(err) + search := strings.ToLower(strings.TrimSpace(args[0])) + if !isValidIPOrCIDR(search) { + fmt.Println("false") + return } - if err := instance.Run(); err != nil { - log.Fatal(err) - } + execute(format, name, uri, dir, search, searchListStr) case false: // No search arg, run in REPL mode - fmt.Println("Enter IP or CIDR (type `exit` to quit):") + fmt.Println(`Enter IP or CIDR (type "exit" to quit):`) fmt.Print(">> ") scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { search := strings.ToLower(strings.TrimSpace(scanner.Text())) - if search == "exit" { + if search == "" { + fmt.Println() + fmt.Print(">> ") + continue + } + if search == "exit" || search == `"exit"` { break } - config := generateConfigForLookup(format, name, uri, dir, search, searchListStr) - instance, err := lib.NewInstance() - if err != nil { - log.Fatal(err) - } - if err := instance.InitFromBytes([]byte(config)); err != nil { - log.Fatal(err) - } - if err := instance.Run(); err != nil { - log.Fatal(err) + if !isValidIPOrCIDR(search) { + fmt.Println("false") + fmt.Println() + fmt.Print(">> ") + continue } + + execute(format, name, uri, dir, search, searchListStr) + fmt.Println() fmt.Print(">> ") } @@ -114,6 +110,40 @@ var lookupCmd = &cobra.Command{ }, } +// Check if the input is a valid IP or CIDR +func isValidIPOrCIDR(search string) bool { + if search == "" { + return false + } + + var err error + switch strings.Contains(search, "/") { + case true: // CIDR + _, err = netip.ParsePrefix(search) + case false: // IP + _, err = netip.ParseAddr(search) + } + + return err == nil +} + +func execute(format, name, uri, dir, search, searchListStr string) { + config := generateConfigForLookup(format, name, uri, dir, search, searchListStr) + + instance, err := lib.NewInstance() + if err != nil { + log.Fatal(err) + } + + if err := instance.InitFromBytes([]byte(config)); err != nil { + log.Fatal(err) + } + + if err := instance.Run(); err != nil { + log.Fatal(err) + } +} + func generateConfigForLookup(format, name, uri, dir, search, searchListStr string) string { return fmt.Sprintf(` { diff --git a/plugin/special/lookup.go b/plugin/special/lookup.go index 34ba0207..6b9e6af0 100644 --- a/plugin/special/lookup.go +++ b/plugin/special/lookup.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net/netip" + "slices" "strings" "github.com/Loyalsoldier/geoip/lib" @@ -85,7 +86,10 @@ func (l *lookup) Output(container lib.Container) error { lists, found, _ := container.Lookup(l.Search, l.SearchList...) if found { + slices.Sort(lists) fmt.Println(strings.ToLower(strings.Join(lists, ","))) + } else { + fmt.Println("false") } return nil |
