Skip to content

Commit

Permalink
Refine: make lookup command more specific
Browse files Browse the repository at this point in the history
  • Loading branch information
Loyalsoldier committed Oct 24, 2024
1 parent 87404b7 commit 5b4809d
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 66 deletions.
186 changes: 156 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

## 使用本项目的项目
Expand Down
102 changes: 66 additions & 36 deletions lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"log"
"net/netip"
"os"
"strings"

Expand All @@ -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
Expand All @@ -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")
Expand All @@ -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(">> ")
}
Expand All @@ -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(`
{
Expand Down
Loading

0 comments on commit 5b4809d

Please sign in to comment.