Skip to content

Commit 5cc6097

Browse files
committed
reviewer comments
1 parent 376dab2 commit 5cc6097

File tree

2 files changed

+62
-16
lines changed

2 files changed

+62
-16
lines changed

x/examples/smart-proxy/config_broken.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fallback:
2626
# Nonexistent Outline Server
2727
- ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTprSzdEdHQ0MkJLOE9hRjBKYjdpWGFK@1.2.3.4:9999/?outline=1
2828
# Nonexistant Psiphon Config JSON. Not yet supported
29-
- {
29+
- psiphon: {
3030
"LocalHttpProxyPort":8081,
3131
"LocalSocksProxyPort":1081,
3232
"PropagationChannelId":"FFFFFFFFFFFFFFFF",

x/smart/stream_dialer.go

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import (
2727
"time"
2828

2929
"github.com/goccy/go-yaml"
30+
"github.com/goccy/go-yaml/ast"
31+
"github.com/goccy/go-yaml/parser"
3032
"github.com/Jigsaw-Code/outline-sdk/dns"
3133
"github.com/Jigsaw-Code/outline-sdk/transport"
3234
"github.com/Jigsaw-Code/outline-sdk/x/configurl"
@@ -95,12 +97,13 @@ type dnsEntryConfig struct {
9597
TCP *tcpEntryConfig `yaml:"tcp,omitempty"`
9698
}
9799

98-
type psiphonEntryConfig struct {
99-
// Don't verify the psiphon config format here, just pass it forward
100-
Psiphon map[string]interface{} `yaml:"psiphon,omitempty"`
100+
type fallbackEntryStructConfig struct {
101+
Psiphon any `yaml:"psiphon,omitempty"`
102+
// As we allow more fallback types beyond psiphon they will be added here
101103
}
102104

103-
// can be either a configurl string or a psiphon config
105+
// This contains either a configURL string or a fallbackEntryStructConfig
106+
// It is parsed into the correct type later
104107
type fallbackEntryConfig any
105108

106109
type configConfig struct {
@@ -331,22 +334,23 @@ func (f *StrategyFinder) findFallback(ctx context.Context, testDomains []string,
331334
fallback, err := raceTests(ctx, 250*time.Millisecond, fallbackConfigs, func(fallbackConfig fallbackEntryConfig) (*SearchResult, error) {
332335
switch v := fallbackConfig.(type) {
333336
case string:
334-
fallbackUrl := v
335-
dialer, err := configModule.NewStreamDialer(ctx, fallbackUrl)
337+
configUrl := v
338+
dialer, err := configModule.NewStreamDialer(ctx, configUrl)
336339
if err != nil {
337340
return nil, fmt.Errorf("getStreamDialer failed: %w", err)
338341
}
339342

340-
err = f.testDialer(ctx, dialer, testDomains, fallbackUrl)
343+
err = f.testDialer(ctx, dialer, testDomains, configUrl)
341344
if err != nil {
342345
return nil, err
343346
}
344347

345348
return &SearchResult{dialer, fallbackConfig}, nil
346-
case map[string]interface{}:
347-
psiphonJSON, err := json.Marshal(v)
349+
case fallbackEntryStructConfig:
350+
psiphonCfg := v.Psiphon
351+
psiphonJSON, err := json.Marshal(psiphonCfg)
348352
if err != nil {
349-
f.logCtx(ctx, "Error marshaling to JSON: %v, %v", v, err)
353+
f.logCtx(ctx, "Error marshaling to JSON: %v, %v", psiphonCfg, err)
350354
}
351355

352356
// TODO(laplante): pass this forward into psiphon.go, which takes raw json
@@ -355,10 +359,8 @@ func (f *StrategyFinder) findFallback(ctx context.Context, testDomains []string,
355359
default:
356360
return nil, fmt.Errorf("unknown fallback type: %v", v)
357361
}
358-
359-
// If neither, it's an unknown type
360-
return nil, fmt.Errorf("unknown fallback type: %v", fallbackConfig)
361362
})
363+
362364
if err != nil {
363365
return nil, fmt.Errorf("could not find a working fallback: %w", err)
364366
}
@@ -392,14 +394,58 @@ func (f *StrategyFinder) newProxylessDialer(ctx context.Context, testDomains []s
392394
return f.findTLS(ctx, testDomains, dnsDialer, config.TLS)
393395
}
394396

397+
func (f *StrategyFinder) parseConfig(configBytes []byte) (configConfig, error) {
398+
parsedBytes, err := parser.ParseBytes(configBytes, 0)
399+
if err != nil {
400+
return configConfig{}, fmt.Errorf("failed to parse config: %v", err)
401+
}
402+
403+
var parsedConfig configConfig
404+
err = yaml.NodeToValue(parsedBytes.Docs[0].Body, &parsedConfig)
405+
if err != nil {
406+
return configConfig{}, fmt.Errorf("failed to parse config: %v", err)
407+
}
408+
409+
// Parse fallback list
410+
mapping, ok := parsedBytes.Docs[0].Body.(*ast.MappingNode)
411+
if !ok {
412+
return configConfig{}, fmt.Errorf("failed to parse config: root is not a mapping")
413+
}
414+
for _, value := range mapping.Values {
415+
if key, ok := value.Key.(*ast.StringNode); ok && key.Value == "fallback" {
416+
sequence, ok := value.Value.(*ast.SequenceNode)
417+
if !ok {
418+
return configConfig{}, fmt.Errorf("failed to parse config: fallback is not a sequence")
419+
}
420+
for i, fallback := range sequence.Values {
421+
switch v := fallback.(type) {
422+
case *ast.StringNode:
423+
parsedConfig.Fallback[i] = v.Value
424+
case *ast.MappingNode:
425+
var fallbackEntry fallbackEntryStructConfig
426+
err := yaml.NodeToValue(v, &fallbackEntry)
427+
if err != nil {
428+
return configConfig{}, fmt.Errorf("failed to parse config as fallbackEntryStructConfig: %v", err)
429+
}
430+
parsedConfig.Fallback[i] = fallbackEntry
431+
default:
432+
return configConfig{}, fmt.Errorf("unknown fallback type: %v", v)
433+
}
434+
}
435+
}
436+
}
437+
438+
return parsedConfig, nil
439+
}
440+
395441
// NewDialer uses the config in configBytes to search for a strategy that unblocks DNS and TLS for all of the testDomains, returning a dialer with the found strategy.
396442
// It returns an error if no strategy was found that unblocks the testDomains.
397443
// The testDomains must be domains with a TLS service running on port 443.
398444
func (f *StrategyFinder) NewDialer(ctx context.Context, testDomains []string, configBytes []byte) (transport.StreamDialer, error) {
399445
var parsedConfig configConfig
400-
err := yaml.Unmarshal(configBytes, &parsedConfig)
446+
parsedConfig, err := f.parseConfig(configBytes)
401447
if err != nil {
402-
return nil, fmt.Errorf("failed to parse config: %v", err)
448+
return nil, err
403449
}
404450

405451
// Make domain fully-qualified to prevent confusing domain search.

0 commit comments

Comments
 (0)