From 8bb88ed20a0941aefadcc3a309b55c23659b14e4 Mon Sep 17 00:00:00 2001 From: mzz2017 <2017@duck.com> Date: Thu, 9 Feb 2023 20:17:45 +0800 Subject: [PATCH] feat: subscription supports file:// --- README.md | 3 +- cmd/internal/subscription.go | 59 +++++++++++++++++++++++++++++++++--- cmd/run.go | 3 +- common/utils.go | 16 ++++++++++ 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 25e1366..851bf49 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,5 @@ See [example.dae](https://github.com/v2rayA/dae/blob/main/example.dae). 1. MACv2 extension extraction. 1. Log to userspace. 1. Support include section. -1. Subscription section supports "file://" -1. Subscription section supports key. +1. Subscription section supports key. And support to filter by subscription key. 1. ... diff --git a/cmd/internal/subscription.go b/cmd/internal/subscription.go index 721067e..a03639a 100644 --- a/cmd/internal/subscription.go +++ b/cmd/internal/subscription.go @@ -1,6 +1,8 @@ package internal import ( + "bufio" + "bytes" "encoding/json" "fmt" "github.com/sirupsen/logrus" @@ -9,6 +11,8 @@ import ( "net" "net/http" "net/url" + "os" + "path/filepath" "strconv" "strings" ) @@ -80,26 +84,71 @@ func resolveSubscriptionAsSIP008(log *logrus.Logger, b []byte) (nodes []string, return nodes, nil } -func ResolveSubscription(log *logrus.Logger, subscription string) (nodes []string, err error) { +func resolveFile(u *url.URL, configDir string) (b []byte, err error) { + if u.Host == "" { + return nil, fmt.Errorf("not support absolute path") + } + /// Relative location. + // Make sure path safety. + path := filepath.Join(configDir, u.Host, u.Path) + if err = common.IsFileInSubDir(path, configDir); err != nil { + return nil, err + } + /// Read and resolve + f, err := os.Open(path) + if err != nil { + return nil, err + } + // Resolve the first line instruction. + fReader := bufio.NewReader(f) + b, err = fReader.Peek(1) + if err != nil { + return nil, err + } + if string(b[0]) == "@" { + // Instruction line. But not support yet. + _, _, err = fReader.ReadLine() + if err != nil { + return nil, err + } + } + + b, err = io.ReadAll(fReader) + if err != nil { + return nil, err + } + return bytes.TrimSpace(b), err +} + +func ResolveSubscription(log *logrus.Logger, configDir string, subscription string) (nodes []string, err error) { u, err := url.Parse(subscription) if err != nil { return nil, fmt.Errorf("failed to parse subscription \"%v\": %w", subscription, err) } + log.Debugf("ResolveSubscription: %v", subscription) + var ( + b []byte + resp *http.Response + ) switch u.Scheme { case "file": - // TODO + b, err = resolveFile(u, configDir) + if err != nil { + return nil, fmt.Errorf("failed to resolve file: %w", err) + } + goto resolve default: } - log.Debugf("ResolveSubscription: %v", subscription) - resp, err := http.Get(subscription) + resp, err = http.Get(subscription) if err != nil { return nil, err } defer resp.Body.Close() - b, err := io.ReadAll(resp.Body) + b, err = io.ReadAll(resp.Body) if err != nil { return nil, err } +resolve: if nodes, err = resolveSubscriptionAsSIP008(log, b); err == nil { return nodes, nil } else { diff --git a/cmd/run.go b/cmd/run.go index ea3c917..80e7ed5 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -11,6 +11,7 @@ import ( "github.com/v2rayA/dae/pkg/logger" "os" "os/signal" + "path/filepath" "syscall" ) @@ -55,7 +56,7 @@ func Run(log *logrus.Logger, param *config.Params) (err error) { nodeList := make([]string, len(param.Node)) copy(nodeList, param.Node) for _, sub := range param.Subscription { - nodes, err := internal.ResolveSubscription(log, sub) + nodes, err := internal.ResolveSubscription(log, filepath.Dir(cfgFile), sub) if err != nil { log.Warnf(`failed to resolve subscription "%v": %v`, sub, err) } diff --git a/common/utils.go b/common/utils.go index e6791f6..fc28c01 100644 --- a/common/utils.go +++ b/common/utils.go @@ -11,6 +11,7 @@ import ( "encoding/hex" "fmt" "net/url" + "path/filepath" "reflect" "strconv" "strings" @@ -307,3 +308,18 @@ func FuzzyDecode(to interface{}, val string) bool { } return true } + +func IsFileInSubDir(filePath string, dir string) (err error) { + fileDir := filepath.Dir(filePath) + if len(dir) == 0 { + return fmt.Errorf("bad dir: %v", dir) + } + rel, err := filepath.Rel(dir, fileDir) + if err != nil { + return err + } + if strings.HasPrefix(rel, "..") { + return fmt.Errorf("file is out of scope: %v", rel) + } + return nil +}