diff --git a/cmd/spoof-dpi/main.go b/cmd/spoof-dpi/main.go index 7f1cd0e..50ad70c 100644 --- a/cmd/spoof-dpi/main.go +++ b/cmd/spoof-dpi/main.go @@ -13,14 +13,16 @@ import ( ) func main() { - args := util.ParseArgs() + args := util.ParseArgs() if *args.Version { version.PrintVersion() os.Exit(0) } config := util.GetConfig() - config.Load(args) + if err := config.Load(args); err != nil { + log.Fatalf("loading config: %s", err) + } pxy := proxy.New(config) if *config.Debug { diff --git a/util/args.go b/util/args.go index d5b4230..2384968 100644 --- a/util/args.go +++ b/util/args.go @@ -16,6 +16,7 @@ type Args struct { SystemProxy *bool Timeout *int AllowedPattern *StringArray + PatternFile *string WindowSize *int Version *bool } @@ -47,14 +48,20 @@ try lower values if the default value doesn't bypass the DPI; when not given, the client hello packet will be sent in two parts: fragmentation for the first data packet and the rest `) - args.AllowedPattern = new(StringArray) - args.Version = flag.Bool("v", false, "print spoof-dpi's version; this may contain some other relevant information") + args.AllowedPattern = new(StringArray) flag.Var( args.AllowedPattern, "pattern", "bypass DPI only on packets matching this regex pattern; can be given multiple times", ) + args.PatternFile = flag.String( + "pattern-file", + "", + "bypass DPI only on packets matching regex patterns provided in a file (one per line)", + ) + + args.Version = flag.Bool("v", false, "print spoof-dpi's version; this may contain some other relevant information") flag.Parse() diff --git a/util/config.go b/util/config.go index 6e5e9a0..47b359a 100644 --- a/util/config.go +++ b/util/config.go @@ -31,7 +31,7 @@ func GetConfig() *Config { return config } -func (c *Config) Load(args *Args) { +func (c *Config) Load(args *Args) error { c.Addr = args.Addr c.Port = args.Port c.DnsAddr = args.DnsAddr @@ -41,18 +41,40 @@ func (c *Config) Load(args *Args) { c.NoBanner = args.NoBanner c.SystemProxy = args.SystemProxy c.Timeout = args.Timeout - c.AllowedPatterns = parseAllowedPattern(args.AllowedPattern) c.WindowSize = args.WindowSize + + patterns, err := parseAllowedPattern(args.AllowedPattern, *args.PatternFile) + if err != nil { + return fmt.Errorf("parsing patterns: %w", err) + } + c.AllowedPatterns = patterns + + return nil } -func parseAllowedPattern(patterns *StringArray) []*regexp.Regexp { - var allowedPatterns []*regexp.Regexp +func parseAllowedPattern(patterns *StringArray, filePath string) ([]*regexp.Regexp, error) { + patternSet := make(map[*regexp.Regexp]struct{}) - for _, pattern := range *patterns { - allowedPatterns = append(allowedPatterns, regexp.MustCompile(pattern)) + filePatterns, err := loadPatternsFromFile(filePath) + if err != nil { + return nil, fmt.Errorf("loading patterns from file: %w", err) + } + for _, pattern := range filePatterns { + patternSet[pattern] = struct{}{} } - return allowedPatterns + for _, rawPattern := range *patterns { + pattern := regexp.MustCompile(rawPattern) + patternSet[pattern] = struct{}{} + } + + allowedPatterns := make([]*regexp.Regexp, len(patternSet)) + writeI := 0 + for pattern := range patternSet { + allowedPatterns[writeI] = pattern + writeI++ + } + return allowedPatterns, nil } func PrintColoredBanner() { diff --git a/util/file.go b/util/file.go new file mode 100644 index 0000000..22854ef --- /dev/null +++ b/util/file.go @@ -0,0 +1,41 @@ +package util + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "regexp" +) + +func loadPatternsFromFile(path string) ([]*regexp.Regexp, error) { + if path == "" { + return nil, nil + } + path, err := filepath.Abs(path) + if err != nil { + return nil, fmt.Errorf("pattern file path: %w", err) + } + + file, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("opening pattern file: %w", err) + } + defer func() { + if e := file.Close(); e != nil && err == nil { + err = e + } + }() + + var patterns []*regexp.Regexp + scanner := bufio.NewScanner(file) + for scanner.Scan() { + pattern := regexp.MustCompile(scanner.Text()) + patterns = append(patterns, pattern) + } + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("parsing pattern file: %w", err) + } + + return patterns, nil +}