Projects STRLCPY sing-box Commits c7f89ad8
🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■
    .gitignore
    skipped 11 lines
    12 12  /*.aar
    13 13  /*.xcframework/
    14 14  .DS_Store
     15 +/config.d/
    15 16   
  • ■ ■ ■ ■
    cmd/sing-box/cmd_check.go
    skipped 25 lines
    26 26  }
    27 27   
    28 28  func check() error {
    29  - options, err := readConfig()
     29 + options, err := readConfigAndMerge()
    30 30   if err != nil {
    31 31   return err
    32 32   }
    skipped 9 lines
  • ■ ■ ■ ■ ■ ■
    cmd/sing-box/cmd_format.go
    skipped 32 lines
    33 33  }
    34 34   
    35 35  func format() error {
     36 + optionsList, err := readConfig()
     37 + if err != nil {
     38 + return err
     39 + }
     40 + for _, optionsEntry := range optionsList {
     41 + buffer := new(bytes.Buffer)
     42 + encoder := json.NewEncoder(buffer)
     43 + encoder.SetIndent("", " ")
     44 + err = encoder.Encode(optionsEntry.options)
     45 + if err != nil {
     46 + return E.Cause(err, "encode config")
     47 + }
     48 + outputPath, _ := filepath.Abs(optionsEntry.path)
     49 + if !commandFormatFlagWrite {
     50 + if len(optionsList) > 1 {
     51 + os.Stdout.WriteString(outputPath + "\n")
     52 + }
     53 + os.Stdout.WriteString(buffer.String() + "\n")
     54 + continue
     55 + }
     56 + if bytes.Equal(optionsEntry.content, buffer.Bytes()) {
     57 + continue
     58 + }
     59 + output, err := os.Create(optionsEntry.path)
     60 + if err != nil {
     61 + return E.Cause(err, "open output")
     62 + }
     63 + _, err = output.Write(buffer.Bytes())
     64 + output.Close()
     65 + if err != nil {
     66 + return E.Cause(err, "write output")
     67 + }
     68 + os.Stderr.WriteString(outputPath + "\n")
     69 + }
     70 + return nil
     71 +}
     72 + 
     73 +func formatOne(configPath string) error {
    36 74   configContent, err := os.ReadFile(configPath)
    37 75   if err != nil {
    38 76   return E.Cause(err, "read config")
    skipped 34 lines
  • ■ ■ ■ ■ ■
    cmd/sing-box/cmd_run.go
    skipped 4 lines
    5 5   "io"
    6 6   "os"
    7 7   "os/signal"
     8 + "path/filepath"
    8 9   runtimeDebug "runtime/debug"
     10 + "sort"
     11 + "strings"
    9 12   "syscall"
    10 13   
    11 14   "github.com/sagernet/sing-box"
     15 + "github.com/sagernet/sing-box/common/badjsonmerge"
    12 16   "github.com/sagernet/sing-box/log"
    13 17   "github.com/sagernet/sing-box/option"
    14 18   E "github.com/sagernet/sing/common/exceptions"
    skipped 16 lines
    31 35   mainCommand.AddCommand(commandRun)
    32 36  }
    33 37   
    34  -func readConfig() (option.Options, error) {
     38 +type OptionsEntry struct {
     39 + content []byte
     40 + path string
     41 + options option.Options
     42 +}
     43 + 
     44 +func readConfigAt(path string) (*OptionsEntry, error) {
    35 45   var (
    36 46   configContent []byte
    37 47   err error
    38 48   )
    39  - if configPath == "stdin" {
     49 + if path == "stdin" {
    40 50   configContent, err = io.ReadAll(os.Stdin)
    41 51   } else {
    42  - configContent, err = os.ReadFile(configPath)
     52 + configContent, err = os.ReadFile(path)
    43 53   }
    44 54   if err != nil {
    45  - return option.Options{}, E.Cause(err, "read config")
     55 + return nil, E.Cause(err, "read config at ", path)
    46 56   }
    47 57   var options option.Options
    48 58   err = options.UnmarshalJSON(configContent)
    49 59   if err != nil {
    50  - return option.Options{}, E.Cause(err, "decode config")
     60 + return nil, E.Cause(err, "decode config at ", path)
    51 61   }
    52  - return options, nil
     62 + return &OptionsEntry{
     63 + content: configContent,
     64 + path: path,
     65 + options: options,
     66 + }, nil
     67 +}
     68 + 
     69 +func readConfig() ([]*OptionsEntry, error) {
     70 + var optionsList []*OptionsEntry
     71 + for _, path := range configPaths {
     72 + optionsEntry, err := readConfigAt(path)
     73 + if err != nil {
     74 + return nil, err
     75 + }
     76 + optionsList = append(optionsList, optionsEntry)
     77 + }
     78 + for _, directory := range configDirectories {
     79 + entries, err := os.ReadDir(directory)
     80 + if err != nil {
     81 + return nil, E.Cause(err, "read config directory at ", directory)
     82 + }
     83 + for _, entry := range entries {
     84 + if !strings.HasSuffix(entry.Name(), ".json") || entry.IsDir() {
     85 + continue
     86 + }
     87 + optionsEntry, err := readConfigAt(filepath.Join(directory, entry.Name()))
     88 + if err != nil {
     89 + return nil, err
     90 + }
     91 + optionsList = append(optionsList, optionsEntry)
     92 + }
     93 + }
     94 + sort.Slice(optionsList, func(i, j int) bool {
     95 + return optionsList[i].path < optionsList[j].path
     96 + })
     97 + return optionsList, nil
     98 +}
     99 + 
     100 +func readConfigAndMerge() (option.Options, error) {
     101 + optionsList, err := readConfig()
     102 + if err != nil {
     103 + return option.Options{}, err
     104 + }
     105 + var mergedOptions option.Options
     106 + for _, options := range optionsList {
     107 + mergedOptions, err = badjsonmerge.MergeOptions(options.options, mergedOptions)
     108 + if err != nil {
     109 + return option.Options{}, E.Cause(err, "merge config at ", options.path)
     110 + }
     111 + }
     112 + return mergedOptions, nil
    53 113  }
    54 114   
    55 115  func create() (*box.Box, context.CancelFunc, error) {
    56  - options, err := readConfig()
     116 + options, err := readConfigAndMerge()
    57 117   if err != nil {
    58 118   return nil, nil, err
    59 119   }
    skipped 63 lines
  • ■ ■ ■ ■ ■ ■
    cmd/sing-box/main.go
    skipped 10 lines
    11 11  )
    12 12   
    13 13  var (
    14  - configPath string
    15  - workingDir string
    16  - disableColor bool
     14 + configPaths []string
     15 + configDirectories []string
     16 + workingDir string
     17 + disableColor bool
    17 18  )
    18 19   
    19 20  var mainCommand = &cobra.Command{
    skipped 2 lines
    22 23  }
    23 24   
    24 25  func init() {
    25  - mainCommand.PersistentFlags().StringVarP(&configPath, "config", "c", "config.json", "set configuration file path")
     26 + mainCommand.PersistentFlags().StringArrayVarP(&configPaths, "config", "c", nil, "set configuration file path")
     27 + mainCommand.PersistentFlags().StringArrayVarP(&configDirectories, "config-directory", "C", nil, "set configuration directory path")
    26 28   mainCommand.PersistentFlags().StringVarP(&workingDir, "directory", "D", "", "set working directory")
    27 29   mainCommand.PersistentFlags().BoolVarP(&disableColor, "disable-color", "", false, "disable color output")
    28 30  }
    skipped 12 lines
    41 43   if err := os.Chdir(workingDir); err != nil {
    42 44   log.Fatal(err)
    43 45   }
     46 + }
     47 + if len(configPaths) == 0 && len(configDirectories) == 0 {
     48 + configPaths = append(configPaths, "config.json")
    44 49   }
    45 50  }
    46 51   
  • ■ ■ ■ ■ ■ ■
    common/badjsonmerge/merge.go
     1 +package badjsonmerge
     2 + 
     3 +import (
     4 + "encoding/json"
     5 + "reflect"
     6 + 
     7 + "github.com/sagernet/sing-box/common/badjson"
     8 + "github.com/sagernet/sing-box/option"
     9 + E "github.com/sagernet/sing/common/exceptions"
     10 +)
     11 + 
     12 +func MergeOptions(source option.Options, destination option.Options) (option.Options, error) {
     13 + rawSource, err := json.Marshal(source)
     14 + if err != nil {
     15 + return option.Options{}, E.Cause(err, "marshal source")
     16 + }
     17 + rawDestination, err := json.Marshal(destination)
     18 + if err != nil {
     19 + return option.Options{}, E.Cause(err, "marshal destination")
     20 + }
     21 + rawMerged, err := MergeJSON(rawSource, rawDestination)
     22 + if err != nil {
     23 + return option.Options{}, E.Cause(err, "merge options")
     24 + }
     25 + var merged option.Options
     26 + err = json.Unmarshal(rawMerged, &merged)
     27 + if err != nil {
     28 + return option.Options{}, E.Cause(err, "unmarshal merged options")
     29 + }
     30 + return merged, nil
     31 +}
     32 + 
     33 +func MergeJSON(rawSource json.RawMessage, rawDestination json.RawMessage) (json.RawMessage, error) {
     34 + source, err := badjson.Decode(rawSource)
     35 + if err != nil {
     36 + return nil, E.Cause(err, "decode source")
     37 + }
     38 + destination, err := badjson.Decode(rawDestination)
     39 + if err != nil {
     40 + return nil, E.Cause(err, "decode destination")
     41 + }
     42 + merged, err := mergeJSON(source, destination)
     43 + if err != nil {
     44 + return nil, err
     45 + }
     46 + return json.Marshal(merged)
     47 +}
     48 + 
     49 +func mergeJSON(anySource any, anyDestination any) (any, error) {
     50 + switch destination := anyDestination.(type) {
     51 + case badjson.JSONArray:
     52 + switch source := anySource.(type) {
     53 + case badjson.JSONArray:
     54 + destination = append(destination, source...)
     55 + default:
     56 + destination = append(destination, source)
     57 + }
     58 + return destination, nil
     59 + case *badjson.JSONObject:
     60 + switch source := anySource.(type) {
     61 + case *badjson.JSONObject:
     62 + for _, entry := range source.Entries() {
     63 + oldValue, loaded := destination.Get(entry.Key)
     64 + if loaded {
     65 + var err error
     66 + entry.Value, err = mergeJSON(entry.Value, oldValue)
     67 + if err != nil {
     68 + return nil, E.Cause(err, "merge object item ", entry.Key)
     69 + }
     70 + }
     71 + destination.Put(entry.Key, entry.Value)
     72 + }
     73 + default:
     74 + return nil, E.New("cannot merge json object into ", reflect.TypeOf(destination))
     75 + }
     76 + return destination, nil
     77 + default:
     78 + return destination, nil
     79 + }
     80 +}
     81 + 
  • ■ ■ ■ ■ ■ ■
    common/badjsonmerge/merge_test.go
     1 +package badjsonmerge
     2 + 
     3 +import (
     4 + "testing"
     5 + 
     6 + C "github.com/sagernet/sing-box/constant"
     7 + "github.com/sagernet/sing-box/option"
     8 + N "github.com/sagernet/sing/common/network"
     9 + 
     10 + "github.com/stretchr/testify/require"
     11 +)
     12 + 
     13 +func TestMergeJSON(t *testing.T) {
     14 + t.Parallel()
     15 + options := option.Options{
     16 + Log: &option.LogOptions{
     17 + Level: "info",
     18 + },
     19 + Route: &option.RouteOptions{
     20 + Rules: []option.Rule{
     21 + {
     22 + Type: C.RuleTypeDefault,
     23 + DefaultOptions: option.DefaultRule{
     24 + Network: N.NetworkTCP,
     25 + Outbound: "direct",
     26 + },
     27 + },
     28 + },
     29 + },
     30 + }
     31 + anotherOptions := option.Options{
     32 + Outbounds: []option.Outbound{
     33 + {
     34 + Type: C.TypeDirect,
     35 + Tag: "direct",
     36 + },
     37 + },
     38 + }
     39 + thirdOptions := option.Options{
     40 + Route: &option.RouteOptions{
     41 + Rules: []option.Rule{
     42 + {
     43 + Type: C.RuleTypeDefault,
     44 + DefaultOptions: option.DefaultRule{
     45 + Network: N.NetworkUDP,
     46 + Outbound: "direct",
     47 + },
     48 + },
     49 + },
     50 + },
     51 + }
     52 + mergeOptions, err := MergeOptions(options, anotherOptions)
     53 + require.NoError(t, err)
     54 + mergeOptions, err = MergeOptions(thirdOptions, mergeOptions)
     55 + require.NoError(t, err)
     56 + require.Equal(t, "info", mergeOptions.Log.Level)
     57 + require.Equal(t, 2, len(mergeOptions.Route.Rules))
     58 + require.Equal(t, C.TypeDirect, mergeOptions.Outbounds[0].Type)
     59 +}
     60 + 
  • ■ ■ ■ ■
    go.mod
    skipped 24 lines
    25 25   github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca
    26 26   github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
    27 27   github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8
    28  - github.com/sagernet/sing v0.2.1-0.20230318083058-18cd006d266e
     28 + github.com/sagernet/sing v0.2.1-0.20230318094614-4bbf5f2c3046
    29 29   github.com/sagernet/sing-dns v0.1.4
    30 30   github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
    31 31   github.com/sagernet/sing-shadowtls v0.1.0
    skipped 64 lines
  • ■ ■ ■ ■ ■ ■
    go.sum
    skipped 110 lines
    111 111  github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
    112 112  github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
    113 113  github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
    114  -github.com/sagernet/sing v0.2.1-0.20230318083058-18cd006d266e h1:KDaZ0GIlpdhCVn2vf7YL2r/8E5kSZiMMeMgn5CF7eJU=
    115  -github.com/sagernet/sing v0.2.1-0.20230318083058-18cd006d266e/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
     114 +github.com/sagernet/sing v0.2.1-0.20230318094614-4bbf5f2c3046 h1:/+ZWbxRvQmco9ES2qT5Eh/x/IiQRjAcUyRG/vQ4dpxc=
     115 +github.com/sagernet/sing v0.2.1-0.20230318094614-4bbf5f2c3046/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
    116 116  github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
    117 117  github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk=
    118 118  github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=
    skipped 125 lines
  • ■ ■ ■ ■ ■ ■
    route/router.go
    skipped 168 lines
    169 169   } else {
    170 170   tag = F.ToString(i)
    171 171   }
     172 + if transportTagMap[tag] {
     173 + return nil, E.New("duplicate dns server tag: ", tag)
     174 + }
    172 175   transportTags[i] = tag
    173 176   transportTagMap[tag] = true
    174 177   }
    skipped 66 lines
    241 244   }), func(index int, server option.DNSServerOptions) string {
    242 245   return transportTags[index]
    243 246   })
     247 + if len(unresolvedTags) == 0 {
     248 + panic(F.ToString("unexpected unresolved dns servers: ", len(transports), " ", len(dummyTransportMap), " ", len(transportMap)))
     249 + }
    244 250   return nil, E.New("found circular reference in dns servers: ", strings.Join(unresolvedTags, " "))
    245 251   }
    246 252   var defaultTransport dns.Transport
    skipped 867 lines
Please wait...
Page is in error, reload to recover