summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoyalsoldier <[email protected]>2024-10-25 07:24:30 +0800
committerLoyalsoldier <[email protected]>2024-10-25 07:48:14 +0800
commit38dfd08332dda5809191bc54ef06b781b1d81e44 (patch)
tree7a673c45c0789fb626abbd556dac4f402057fa0b
parent87404b7cbcdaa441ed812434852c8aef5c7a5405 (diff)
Refine: make lookup command more specific
-rw-r--r--README.md186
-rw-r--r--lib/container.go50
-rw-r--r--lookup.go102
-rw-r--r--plugin/special/lookup.go4
4 files changed, 245 insertions, 97 deletions
diff --git a/README.md b/README.md
index fc6ae41f..2a0473ba 100644
--- a/README.md
+++ b/README.md
@@ -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())
}
}
}
diff --git a/lookup.go b/lookup.go
index 079e1cc3..090c2802 100644
--- a/lookup.go
+++ b/lookup.go
@@ -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