From a5a300d5a631e2b8b1196a1c57264f27f30e4102 Mon Sep 17 00:00:00 2001 From: Loyalsoldier <10487845+Loyalsoldier@users.noreply.github.com> Date: Sat, 31 Aug 2024 11:51:28 +0800 Subject: [PATCH] Feat: support to specify IP or CIDR in config file for text input format --- configuration.md | 43 ++++++++++++++++++++++++++++--- plugin/plaintext/text_in.go | 51 +++++++++++++++++++++++++++++++------ plugin/v2ray/dat_in.go | 2 +- 3 files changed, 83 insertions(+), 13 deletions(-) diff --git a/configuration.md b/configuration.md index fc0327924..308883de1 100644 --- a/configuration.md +++ b/configuration.md @@ -191,9 +191,10 @@ Supported `output` formats: - **type**: (required) the name of the input format - **action**: (required) action type, the value could be `add`(to add IP / CIDR) or `remove`(to remove IP / CIDR) - **args**: (required) - - **name**: the list name (cannot be used with `inputDir`; must be used with `uri`) - - **uri**: the path to plaintext txt file, can be local file path or remote `http` or `https` URL (cannot be used with `inputDir`; must be used with `name`) - - **inputDir**: the directory of the files to walk through (excluded children directories). (the filename will be the list name; cannot be used with `name` or `uri`) + - **name**: (optional) the list name (cannot be used with `inputDir`; must be used with `uri` or `ipOrCIDR`) + - **uri**: (optional) the path to plaintext txt file, can be local file path or remote `http` or `https` URL (cannot be used with `inputDir`; must be used with `name`; can be used with `ipOrCIDR`) + - **ipOrCIDR**: (optional, array) an array of plaintext IP addresses or CIDRs (cannot be used with `inputDir`; must be used with `name`; can be used with `uri`) + - **inputDir**: (optional) the directory of the files to walk through (excluded children directories). (the filename will be the list name; cannot be used with `name` or `uri` or `ipOrCIDR`) - **wantedList**: (optional, array) specified wanted files. (used with `inputDir`) - **onlyIPType**: (optional) the IP address type to be processed, the value is `ipv4` or `ipv6` - **removePrefixesInLine**: (optional, array) the array of string prefixes to be removed in each line @@ -202,7 +203,7 @@ Supported `output` formats: ```jsonc { "type": "text", - "action": "add", // add IP or CIDR + "action": "add", // add IP or CIDR "args": { "name": "cn", "uri": "./cn.txt", // get IPv4 and IPv6 addresses from local file cn.txt, and add to list cn @@ -212,6 +213,40 @@ Supported `output` formats: } ``` +```jsonc +{ + "type": "text", + "action": "add", // add IP or CIDR + "args": { + "name": "cn", + "ipOrCIDR": ["1.0.0.1", "1.0.0.1/24"] // add IP or CIDR to cn list + } +} +``` + +```jsonc +{ + "type": "text", + "action": "remove", // remove IP or CIDR + "args": { + "name": "cn", + "ipOrCIDR": ["1.0.0.1", "1.0.0.1/24"] // remove IP or CIDR from cn list + } +} +``` + +```jsonc +{ + "type": "text", + "action": "add", // add IP or CIDR + "args": { + "name": "cn", + "uri": "./cn.txt", // get IPv4 and IPv6 addresses from local file cn.txt, and add to list cn + "ipOrCIDR": ["1.0.0.1", "1.0.0.1/24"] // add IP or CIDR to cn list + } +} +``` + ```jsonc { "type": "text", diff --git a/plugin/plaintext/text_in.go b/plugin/plaintext/text_in.go index c676ceb3d..89f18b3e5 100644 --- a/plugin/plaintext/text_in.go +++ b/plugin/plaintext/text_in.go @@ -32,6 +32,7 @@ func newTextIn(action lib.Action, data json.RawMessage) (lib.InputConverter, err var tmp struct { Name string `json:"name"` URI string `json:"uri"` + IPOrCIDR []string `json:"ipOrCIDR"` InputDir string `json:"inputDir"` Want []string `json:"wantedList"` OnlyIPType lib.IPType `json:"onlyIPType"` @@ -46,12 +47,15 @@ func newTextIn(action lib.Action, data json.RawMessage) (lib.InputConverter, err } } - if tmp.Name == "" && tmp.URI == "" && tmp.InputDir == "" { - return nil, fmt.Errorf("type %s | action %s missing inputdir or name or uri", typeTextIn, action) - } - - if (tmp.Name != "" && tmp.URI == "") || (tmp.Name == "" && tmp.URI != "") { - return nil, fmt.Errorf("type %s | action %s name & uri must be specified together", typeTextIn, action) + if tmp.InputDir == "" { + if tmp.Name == "" { + return nil, fmt.Errorf("❌ [type %s | action %s] missing inputDir or name", typeTextIn, action) + } + if tmp.URI == "" && len(tmp.IPOrCIDR) == 0 { + return nil, fmt.Errorf("❌ [type %s | action %s] missing uri or ipOrCIDR", typeTextIn, action) + } + } else if tmp.Name != "" || tmp.URI != "" || len(tmp.IPOrCIDR) > 0 { + return nil, fmt.Errorf("❌ [type %s | action %s] inputDir is not allowed to be used with name or uri or ipOrCIDR", typeTextIn, action) } // Filter want list @@ -68,6 +72,7 @@ func newTextIn(action lib.Action, data json.RawMessage) (lib.InputConverter, err Description: descTextIn, Name: tmp.Name, URI: tmp.URI, + IPOrCIDR: tmp.IPOrCIDR, InputDir: tmp.InputDir, Want: wantList, OnlyIPType: tmp.OnlyIPType, @@ -83,6 +88,7 @@ type textIn struct { Description string Name string URI string + IPOrCIDR []string InputDir string Want map[string]bool OnlyIPType lib.IPType @@ -110,6 +116,7 @@ func (t *textIn) Input(container lib.Container) (lib.Container, error) { switch { case t.InputDir != "": err = t.walkDir(t.InputDir, entries) + case t.Name != "" && t.URI != "": switch { case strings.HasPrefix(strings.ToLower(t.URI), "http://"), strings.HasPrefix(strings.ToLower(t.URI), "https://"): @@ -117,8 +124,17 @@ func (t *textIn) Input(container lib.Container) (lib.Container, error) { default: err = t.walkLocalFile(t.URI, t.Name, entries) } + if err != nil { + return nil, err + } + + fallthrough + + case t.Name != "" && len(t.IPOrCIDR) > 0: + err = t.appendIPOrCIDR(t.IPOrCIDR, t.Name, entries) + default: - return nil, fmt.Errorf("config missing argument inputDir or name or uri") + return nil, fmt.Errorf("❌ [type %s | action %s] config missing argument inputDir or name or uri or ipOrCIDR", t.Type, t.Action) } if err != nil { @@ -134,7 +150,7 @@ func (t *textIn) Input(container lib.Container) (lib.Container, error) { } if len(entries) == 0 { - return nil, fmt.Errorf("type %s | action %s no entry is generated", t.Type, t.Action) + return nil, fmt.Errorf("❌ [type %s | action %s] no entry is generated", t.Type, t.Action) } for _, entry := range entries { @@ -280,3 +296,22 @@ func (t *textIn) scanFile(reader io.Reader, entry *lib.Entry) error { return nil } + +func (t *textIn) appendIPOrCIDR(ipOrCIDR []string, name string, entries map[string]*lib.Entry) error { + name = strings.ToUpper(name) + + entry, found := entries[name] + if !found { + entry = lib.NewEntry(name) + } + + for _, cidr := range ipOrCIDR { + if err := entry.AddPrefix(strings.TrimSpace(cidr)); err != nil { + return err + } + } + + entries[name] = entry + + return nil +} diff --git a/plugin/v2ray/dat_in.go b/plugin/v2ray/dat_in.go index ddf9c9ec7..defb0958b 100644 --- a/plugin/v2ray/dat_in.go +++ b/plugin/v2ray/dat_in.go @@ -42,7 +42,7 @@ func newGeoIPDatIn(action lib.Action, data json.RawMessage) (lib.InputConverter, } if tmp.URI == "" { - return nil, fmt.Errorf("[type %s | action %s] uri must be specified in config", typeGeoIPdatIn, action) + return nil, fmt.Errorf("❌ [type %s | action %s] uri must be specified in config", typeGeoIPdatIn, action) } // Filter want list