diff --git a/x/config/config.go b/x/config/config.go index fe43c0a9..007af0f1 100644 --- a/x/config/config.go +++ b/x/config/config.go @@ -26,6 +26,94 @@ import ( "github.com/Jigsaw-Code/outline-sdk/transport/tlsfrag" ) +// ConfigParser enables the creation of stream and packet dialers based on a config. The config is +// extensible by registering wrappers for config subtypes. +type ConfigParser struct { + sdWrapers map[string]WrapStreamDialerFunc + pdWrappers map[string]WrapPacketDialerFunc +} + +// NewDefaultConfigParser creates a [ConfigParser] with a set of default wrappers already registered. +func NewDefaultConfigParser() *ConfigParser { + p := new(ConfigParser) + + // Please keep the list in alphabetical order. + + p.RegisterStreamDialerWrapper("override", wrapStreamDialerWithOverride) + p.RegisterPacketDialerWrapper("override", wrapPacketDialerWithOverride) + + p.RegisterStreamDialerWrapper("socks5", wrapStreamDialerWithSOCKS5) + p.RegisterPacketDialerWrapper("socks5", func(baseDialer transport.PacketDialer, wrapConfig *url.URL) (transport.PacketDialer, error) { + return nil, errors.New("socks5 is not supported for PacketDialers") + }) + + p.RegisterStreamDialerWrapper("split", func(baseDialer transport.StreamDialer, wrapConfig *url.URL) (transport.StreamDialer, error) { + prefixBytesStr := wrapConfig.Opaque + prefixBytes, err := strconv.Atoi(prefixBytesStr) + if err != nil { + return nil, fmt.Errorf("prefixBytes is not a number: %v. Split config should be in split: format", prefixBytesStr) + } + return split.NewStreamDialer(baseDialer, int64(prefixBytes)) + }) + p.RegisterPacketDialerWrapper("split", func(baseDialer transport.PacketDialer, wrapConfig *url.URL) (transport.PacketDialer, error) { + return nil, errors.New("split is not supported for PacketDialers") + }) + + p.RegisterStreamDialerWrapper("ss", wrapStreamDialerWithShadowsocks) + p.RegisterPacketDialerWrapper("ss", wrapPacketDialerWithShadowsocks) + + p.RegisterStreamDialerWrapper("tls", wrapStreamDialerWithTLS) + p.RegisterPacketDialerWrapper("tls", func(baseDialer transport.PacketDialer, wrapConfig *url.URL) (transport.PacketDialer, error) { + return nil, errors.New("tls is not supported for PacketDialers") + }) + + p.RegisterStreamDialerWrapper("tlsfrag", func(baseDialer transport.StreamDialer, wrapConfig *url.URL) (transport.StreamDialer, error) { + lenStr := wrapConfig.Opaque + fixedLen, err := strconv.Atoi(lenStr) + if err != nil { + return nil, fmt.Errorf("invalid tlsfrag option: %v. It should be in tlsfrag: format", lenStr) + } + return tlsfrag.NewFixedLenStreamDialer(baseDialer, fixedLen) + }) + p.RegisterPacketDialerWrapper("tlsfrag", func(baseDialer transport.PacketDialer, wrapConfig *url.URL) (transport.PacketDialer, error) { + return nil, errors.New("tlsfrag is not supported for PacketDialers") + }) + + return p +} + +// WrapStreamDialerFunc wraps a [transport.StreamDialer] based on the wrapConfig. +type WrapStreamDialerFunc func(dialer transport.StreamDialer, wrapConfig *url.URL) (transport.StreamDialer, error) + +// RegisterStreamDialerWrapper will register a wrapper for stream dialers under the given subtype. +func (p *ConfigParser) RegisterStreamDialerWrapper(subtype string, wrapper WrapStreamDialerFunc) error { + if p.sdWrapers == nil { + p.sdWrapers = make(map[string]WrapStreamDialerFunc) + } + + if _, found := p.sdWrapers[subtype]; found { + return fmt.Errorf("config parser %v for StreamDialer added twice", subtype) + } + p.sdWrapers[subtype] = wrapper + return nil +} + +// WrapPacketDialerFunc wraps a [transport.PacketDialer] based on the wrapConfig. +type WrapPacketDialerFunc func(dialer transport.PacketDialer, wrapConfig *url.URL) (transport.PacketDialer, error) + +// RegisterPacketDialerWrapper will register a wrapper for packet dialers under the given subtype. +func (p *ConfigParser) RegisterPacketDialerWrapper(subtype string, wrapper WrapPacketDialerFunc) error { + if p.pdWrappers == nil { + p.pdWrappers = make(map[string]WrapPacketDialerFunc) + } + + if _, found := p.pdWrappers[subtype]; found { + return fmt.Errorf("config parser %v for PacketDialer added twice", subtype) + } + p.pdWrappers[subtype] = wrapper + return nil +} + func parseConfigPart(oneDialerConfig string) (*url.URL, error) { oneDialerConfig = strings.TrimSpace(oneDialerConfig) if oneDialerConfig == "" { @@ -42,14 +130,9 @@ func parseConfigPart(oneDialerConfig string) (*url.URL, error) { return url, nil } -// NewStreamDialer creates a new [transport.StreamDialer] according to the given config. -func NewStreamDialer(transportConfig string) (transport.StreamDialer, error) { - return WrapStreamDialer(&transport.TCPDialer{}, transportConfig) -} - -// WrapStreamDialer created a [transport.StreamDialer] according to transportConfig, using dialer as the +// WrapStreamDialer creates a [transport.StreamDialer] according to transportConfig, using dialer as the // base [transport.StreamDialer]. The given dialer must not be nil. -func WrapStreamDialer(dialer transport.StreamDialer, transportConfig string) (transport.StreamDialer, error) { +func (p *ConfigParser) WrapStreamDialer(dialer transport.StreamDialer, transportConfig string) (transport.StreamDialer, error) { if dialer == nil { return nil, errors.New("base dialer must not be nil") } @@ -57,66 +140,43 @@ func WrapStreamDialer(dialer transport.StreamDialer, transportConfig string) (tr if transportConfig == "" { return dialer, nil } - var err error for _, part := range strings.Split(transportConfig, "|") { - dialer, err = newStreamDialerFromPart(dialer, part) + url, err := parseConfigPart(part) if err != nil { return nil, err } - } - return dialer, nil -} - -func newStreamDialerFromPart(innerDialer transport.StreamDialer, oneDialerConfig string) (transport.StreamDialer, error) { - url, err := parseConfigPart(oneDialerConfig) - if err != nil { - return nil, fmt.Errorf("failed to parse config part: %w", err) - } - - // Please keep scheme list sorted. - switch strings.ToLower(url.Scheme) { - case "override": - return newOverrideStreamDialerFromURL(innerDialer, url) - - case "socks5": - return newSOCKS5StreamDialerFromURL(innerDialer, url) - - case "split": - prefixBytesStr := url.Opaque - prefixBytes, err := strconv.Atoi(prefixBytesStr) - if err != nil { - return nil, fmt.Errorf("prefixBytes is not a number: %v. Split config should be in split: format", prefixBytesStr) + w, ok := p.sdWrapers[url.Scheme] + if !ok { + return nil, fmt.Errorf("config scheme '%v' is not supported", url.Scheme) } - return split.NewStreamDialer(innerDialer, int64(prefixBytes)) - - case "ss": - return newShadowsocksStreamDialerFromURL(innerDialer, url) - - case "tls": - return newTlsStreamDialerFromURL(innerDialer, url) - - case "tlsfrag": - lenStr := url.Opaque - fixedLen, err := strconv.Atoi(lenStr) + dialer, err = w(dialer, url) if err != nil { - return nil, fmt.Errorf("invalid tlsfrag option: %v. It should be in tlsfrag: format", lenStr) + return nil, err } - return tlsfrag.NewFixedLenStreamDialer(innerDialer, fixedLen) - - default: - return nil, fmt.Errorf("config scheme '%v' is not supported", url.Scheme) } + return dialer, nil } -// NewPacketDialer creates a new [transport.PacketDialer] according to the given config. -func NewPacketDialer(transportConfig string) (dialer transport.PacketDialer, err error) { - dialer = &transport.UDPDialer{} +// WrapPacketDialer creates a [transport.PacketDialer] according to transportConfig, using dialer as the +// base [transport.PacketDialer]. The given dialer must not be nil. +func (p *ConfigParser) WrapPacketDialer(dialer transport.PacketDialer, transportConfig string) (transport.PacketDialer, error) { + if dialer == nil { + return nil, errors.New("base dialer must not be nil") + } transportConfig = strings.TrimSpace(transportConfig) if transportConfig == "" { return dialer, nil } for _, part := range strings.Split(transportConfig, "|") { - dialer, err = newPacketDialerFromPart(dialer, part) + url, err := parseConfigPart(part) + if err != nil { + return nil, err + } + w, ok := p.pdWrappers[url.Scheme] + if !ok { + return nil, fmt.Errorf("config scheme '%v' is not supported", url.Scheme) + } + dialer, err = w(dialer, url) if err != nil { return nil, err } @@ -124,36 +184,9 @@ func NewPacketDialer(transportConfig string) (dialer transport.PacketDialer, err return dialer, nil } -func newPacketDialerFromPart(innerDialer transport.PacketDialer, oneDialerConfig string) (transport.PacketDialer, error) { - url, err := parseConfigPart(oneDialerConfig) - if err != nil { - return nil, fmt.Errorf("failed to parse config part: %w", err) - } - - // Please keep scheme list sorted. - switch strings.ToLower(url.Scheme) { - case "override": - return newOverridePacketDialerFromURL(innerDialer, url) - - case "socks5": - return nil, errors.New("socks5 is not supported for PacketDialers") - - case "split": - return nil, errors.New("split is not supported for PacketDialers") - - case "ss": - return newShadowsocksPacketDialerFromURL(innerDialer, url) - - case "tls": - return nil, errors.New("tls is not yet supported for PacketDialers") - - default: - return nil, fmt.Errorf("config scheme '%v' is not supported", url.Scheme) - } -} - // NewpacketListener creates a new [transport.PacketListener] according to the given config, // the config must contain only one "ss://" segment. +// TODO: make NewPacketListener configurable. func NewPacketListener(transportConfig string) (transport.PacketListener, error) { if transportConfig = strings.TrimSpace(transportConfig); transportConfig == "" { return nil, errors.New("config is required") diff --git a/x/config/doc.go b/x/config/doc.go index 90feeed2..34b49721 100644 --- a/x/config/doc.go +++ b/x/config/doc.go @@ -99,6 +99,20 @@ DPI Evasion - To add packet splitting to a Shadowsocks server for enhanced DPI e split:2|ss://[USERINFO]@[HOST]:[PORT] +Defining custom transport - You can define your custom transport by implementing and registering the [WrapStreamDialerFunc] and [WrapPacketDialerFunc] functions: + + // create new config parser + // p := new(ConfigParser) + // or + p := NewDefaultConfigParser() + // register your custom dialer + p.RegisterPacketDialerWrapper("custom", wrapStreamDialerWithCustom) + p.RegisterStreamDialerWrapper("custom", wrapPacketDialerWithCustom) + // then use it + dialer, err := p.WrapStreamDialer(innerDialer, "custom://config") + +where wrapStreamDialerWithCustom and wrapPacketDialerWithCustom implement [WrapPacketDialerFunc] and [WrapStreamDialerFunc]. + [Onion Routing]: https://en.wikipedia.org/wiki/Onion_routing */ package config diff --git a/x/config/override.go b/x/config/override.go index 885bc87e..daac524d 100644 --- a/x/config/override.go +++ b/x/config/override.go @@ -66,7 +66,7 @@ func newOverrideFromURL(configURL *url.URL) (func(string) (string, error), error }, nil } -func newOverrideStreamDialerFromURL(innerDialer transport.StreamDialer, configURL *url.URL) (transport.StreamDialer, error) { +func wrapStreamDialerWithOverride(innerDialer transport.StreamDialer, configURL *url.URL) (transport.StreamDialer, error) { override, err := newOverrideFromURL(configURL) if err != nil { return nil, err @@ -80,7 +80,7 @@ func newOverrideStreamDialerFromURL(innerDialer transport.StreamDialer, configUR }), nil } -func newOverridePacketDialerFromURL(innerDialer transport.PacketDialer, configURL *url.URL) (transport.PacketDialer, error) { +func wrapPacketDialerWithOverride(innerDialer transport.PacketDialer, configURL *url.URL) (transport.PacketDialer, error) { override, err := newOverrideFromURL(configURL) if err != nil { return nil, err diff --git a/x/config/shadowsocks.go b/x/config/shadowsocks.go index e8620a23..531bea9f 100644 --- a/x/config/shadowsocks.go +++ b/x/config/shadowsocks.go @@ -25,7 +25,7 @@ import ( "github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks" ) -func newShadowsocksStreamDialerFromURL(innerDialer transport.StreamDialer, configURL *url.URL) (transport.StreamDialer, error) { +func wrapStreamDialerWithShadowsocks(innerDialer transport.StreamDialer, configURL *url.URL) (transport.StreamDialer, error) { config, err := parseShadowsocksURL(configURL) if err != nil { return nil, err @@ -41,7 +41,7 @@ func newShadowsocksStreamDialerFromURL(innerDialer transport.StreamDialer, confi return dialer, nil } -func newShadowsocksPacketDialerFromURL(innerDialer transport.PacketDialer, configURL *url.URL) (transport.PacketDialer, error) { +func wrapPacketDialerWithShadowsocks(innerDialer transport.PacketDialer, configURL *url.URL) (transport.PacketDialer, error) { config, err := parseShadowsocksURL(configURL) if err != nil { return nil, err diff --git a/x/config/socks5.go b/x/config/socks5.go index 103e65be..f3d79012 100644 --- a/x/config/socks5.go +++ b/x/config/socks5.go @@ -7,7 +7,7 @@ import ( "github.com/Jigsaw-Code/outline-sdk/transport/socks5" ) -func newSOCKS5StreamDialerFromURL(innerDialer transport.StreamDialer, configURL *url.URL) (transport.StreamDialer, error) { +func wrapStreamDialerWithSOCKS5(innerDialer transport.StreamDialer, configURL *url.URL) (transport.StreamDialer, error) { endpoint := transport.StreamDialerEndpoint{Dialer: innerDialer, Address: configURL.Host} dialer, err := socks5.NewStreamDialer(&endpoint) if err != nil { diff --git a/x/config/tls.go b/x/config/tls.go index 0789696e..b896f455 100644 --- a/x/config/tls.go +++ b/x/config/tls.go @@ -50,7 +50,7 @@ func parseOptions(configURL *url.URL) ([]tls.ClientOption, error) { return options, nil } -func newTlsStreamDialerFromURL(innerDialer transport.StreamDialer, configURL *url.URL) (transport.StreamDialer, error) { +func wrapStreamDialerWithTLS(innerDialer transport.StreamDialer, configURL *url.URL) (transport.StreamDialer, error) { options, err := parseOptions(configURL) if err != nil { return nil, err diff --git a/x/config/tls_test.go b/x/config/tls_test.go index 47e20ddc..4ef07eb6 100644 --- a/x/config/tls_test.go +++ b/x/config/tls_test.go @@ -26,7 +26,7 @@ import ( func TestTLS(t *testing.T) { tlsURL, err := url.Parse("tls") require.NoError(t, err) - _, err = newTlsStreamDialerFromURL(&transport.TCPDialer{}, tlsURL) + _, err = wrapStreamDialerWithTLS(&transport.TCPDialer{}, tlsURL) require.NoError(t, err) } diff --git a/x/examples/fetch-speed/main.go b/x/examples/fetch-speed/main.go index ab00a8e4..e05d7ecb 100644 --- a/x/examples/fetch-speed/main.go +++ b/x/examples/fetch-speed/main.go @@ -27,6 +27,7 @@ import ( "strings" "time" + "github.com/Jigsaw-Code/outline-sdk/transport" "github.com/Jigsaw-Code/outline-sdk/x/config" ) @@ -43,7 +44,7 @@ func main() { verboseFlag := flag.Bool("v", false, "Enable debug output") transportFlag := flag.String("transport", "", "Transport config") methodFlag := flag.String("method", "GET", "The HTTP method to use") - timeoutFlag := flag.Duration("timeout", 10 * time.Second, "The HTTP timeout value") + timeoutFlag := flag.Duration("timeout", 10*time.Second, "The HTTP timeout value") flag.Parse() @@ -58,7 +59,7 @@ func main() { os.Exit(1) } - dialer, err := config.NewStreamDialer(*transportFlag) + dialer, err := config.NewDefaultConfigParser().WrapStreamDialer(&transport.TCPDialer{}, *transportFlag) if err != nil { log.Fatalf("Could not create dialer: %v\n", err) } diff --git a/x/examples/fetch/main.go b/x/examples/fetch/main.go index 7c1e08e6..71c443d0 100644 --- a/x/examples/fetch/main.go +++ b/x/examples/fetch/main.go @@ -27,6 +27,7 @@ import ( "strings" "time" + "github.com/Jigsaw-Code/outline-sdk/transport" "github.com/Jigsaw-Code/outline-sdk/x/config" ) @@ -68,7 +69,7 @@ func main() { os.Exit(1) } - dialer, err := config.NewStreamDialer(*transportFlag) + dialer, err := config.NewDefaultConfigParser().WrapStreamDialer(&transport.TCPDialer{}, *transportFlag) if err != nil { log.Fatalf("Could not create dialer: %v\n", err) } diff --git a/x/examples/http2transport/main.go b/x/examples/http2transport/main.go index 02b9d5d1..214d1144 100644 --- a/x/examples/http2transport/main.go +++ b/x/examples/http2transport/main.go @@ -24,6 +24,7 @@ import ( "os/signal" "time" + "github.com/Jigsaw-Code/outline-sdk/transport" "github.com/Jigsaw-Code/outline-sdk/x/config" "github.com/Jigsaw-Code/outline-sdk/x/httpproxy" ) @@ -34,7 +35,8 @@ func main() { urlProxyPrefixFlag := flag.String("urlProxyPrefix", "/proxy", "Path where to run the URL proxy. Set to empty (\"\") to disable it.") flag.Parse() - dialer, err := config.NewStreamDialer(*transportFlag) + dialer, err := config.NewDefaultConfigParser().WrapStreamDialer(&transport.TCPDialer{}, *transportFlag) + if err != nil { log.Fatalf("Could not create dialer: %v", err) } diff --git a/x/examples/outline-cli/outline_device.go b/x/examples/outline-cli/outline_device.go index 0a17a28b..69f03bec 100644 --- a/x/examples/outline-cli/outline_device.go +++ b/x/examples/outline-cli/outline_device.go @@ -39,6 +39,8 @@ type OutlineDevice struct { svrIP net.IP } +var configParser = config.NewDefaultConfigParser() + func NewOutlineDevice(transportConfig string) (od *OutlineDevice, err error) { ip, err := resolveShadowsocksServerIPFromConfig(transportConfig) if err != nil { @@ -48,7 +50,7 @@ func NewOutlineDevice(transportConfig string) (od *OutlineDevice, err error) { svrIP: ip, } - if od.sd, err = config.NewStreamDialer(transportConfig); err != nil { + if od.sd, err = configParser.WrapStreamDialer(&transport.TCPDialer{}, transportConfig); err != nil { return nil, fmt.Errorf("failed to create TCP dialer: %w", err) } if od.pp, err = newOutlinePacketProxy(transportConfig); err != nil { diff --git a/x/examples/resolve/main.go b/x/examples/resolve/main.go index 9c73484d..4daab84b 100644 --- a/x/examples/resolve/main.go +++ b/x/examples/resolve/main.go @@ -27,6 +27,7 @@ import ( "time" "github.com/Jigsaw-Code/outline-sdk/dns" + "github.com/Jigsaw-Code/outline-sdk/transport" "github.com/Jigsaw-Code/outline-sdk/x/config" "golang.org/x/net/dns/dnsmessage" ) @@ -66,14 +67,15 @@ func main() { resolverAddr := *resolverFlag var resolver dns.Resolver + configParser := config.NewDefaultConfigParser() if *tcpFlag { - streamDialer, err := config.NewStreamDialer(*transportFlag) + streamDialer, err := configParser.WrapStreamDialer(&transport.TCPDialer{}, *transportFlag) if err != nil { log.Fatalf("Could not create stream dialer: %v", err) } resolver = dns.NewTCPResolver(streamDialer, resolverAddr) } else { - packetDialer, err := config.NewPacketDialer(*transportFlag) + packetDialer, err := configParser.WrapPacketDialer(&transport.UDPDialer{}, *transportFlag) if err != nil { log.Fatalf("Could not create packet dialer: %v", err) } diff --git a/x/examples/smart-proxy/main.go b/x/examples/smart-proxy/main.go index 61c368bc..b3f91f30 100644 --- a/x/examples/smart-proxy/main.go +++ b/x/examples/smart-proxy/main.go @@ -86,11 +86,12 @@ func main() { log.Fatalf("Could not read config: %v", err) } - packetDialer, err := config.NewPacketDialer(*transportFlag) + configParser := config.NewDefaultConfigParser() + packetDialer, err := configParser.WrapPacketDialer(&transport.UDPDialer{}, *transportFlag) if err != nil { log.Fatalf("Could not create packet dialer: %v", err) } - streamDialer, err := config.NewStreamDialer(*transportFlag) + streamDialer, err := configParser.WrapStreamDialer(&transport.TCPDialer{}, *transportFlag) if err != nil { log.Fatalf("Could not create stream dialer: %v", err) } diff --git a/x/examples/test-connectivity/main.go b/x/examples/test-connectivity/main.go index 8cc76c2c..5e8f707c 100644 --- a/x/examples/test-connectivity/main.go +++ b/x/examples/test-connectivity/main.go @@ -31,6 +31,7 @@ import ( "time" "github.com/Jigsaw-Code/outline-sdk/dns" + "github.com/Jigsaw-Code/outline-sdk/transport" "github.com/Jigsaw-Code/outline-sdk/x/config" "github.com/Jigsaw-Code/outline-sdk/x/connectivity" "github.com/Jigsaw-Code/outline-sdk/x/report" @@ -161,6 +162,7 @@ func main() { success := false jsonEncoder := json.NewEncoder(os.Stdout) jsonEncoder.SetEscapeHTML(false) + configParser := config.NewDefaultConfigParser() for _, resolverHost := range strings.Split(*resolverFlag, ",") { resolverHost := strings.TrimSpace(resolverHost) resolverAddress := net.JoinHostPort(resolverHost, "53") @@ -169,13 +171,13 @@ func main() { var resolver dns.Resolver switch proto { case "tcp": - streamDialer, err := config.NewStreamDialer(*transportFlag) + streamDialer, err := configParser.WrapStreamDialer(&transport.TCPDialer{}, *transportFlag) if err != nil { log.Fatalf("Failed to create StreamDialer: %v", err) } resolver = dns.NewTCPResolver(streamDialer, resolverAddress) case "udp": - packetDialer, err := config.NewPacketDialer(*transportFlag) + packetDialer, err := configParser.WrapPacketDialer(&transport.UDPDialer{}, *transportFlag) if err != nil { log.Fatalf("Failed to create PacketDialer: %v", err) } diff --git a/x/examples/ws2endpoint/main.go b/x/examples/ws2endpoint/main.go index 6fe70821..678daf71 100644 --- a/x/examples/ws2endpoint/main.go +++ b/x/examples/ws2endpoint/main.go @@ -37,7 +37,7 @@ func main() { pathPrefix := flag.String("path", "/", "Path where to run the Websocket forwarder") flag.Parse() - dialer, err := config.NewStreamDialer(*transportFlag) + dialer, err := config.NewDefaultConfigParser().WrapStreamDialer(&transport.TCPDialer{}, *transportFlag) if err != nil { log.Fatalf("Could not create dialer: %v", err) } diff --git a/x/httpproxy/connect_handler.go b/x/httpproxy/connect_handler.go index 87602921..25845d25 100644 --- a/x/httpproxy/connect_handler.go +++ b/x/httpproxy/connect_handler.go @@ -56,6 +56,9 @@ type connectHandler struct { var _ http.Handler = (*connectHandler)(nil) +// TODO(fortuna): Inject the config parser +var configParser = config.NewDefaultConfigParser() + func (h *connectHandler) ServeHTTP(proxyResp http.ResponseWriter, proxyReq *http.Request) { if proxyReq.Method != http.MethodConnect { proxyResp.Header().Add("Allow", "CONNECT") @@ -76,7 +79,7 @@ func (h *connectHandler) ServeHTTP(proxyResp http.ResponseWriter, proxyReq *http // Dial the target. transportConfig := proxyReq.Header.Get("Transport") - dialer, err := config.WrapStreamDialer(h.dialer, transportConfig) + dialer, err := configParser.WrapStreamDialer(h.dialer, transportConfig) if err != nil { // Because we sanitize the base dialer error, it's safe to return error details here. http.Error(proxyResp, fmt.Sprintf("Invalid config in Transport header: %v", err), http.StatusBadRequest) diff --git a/x/mobileproxy/mobileproxy.go b/x/mobileproxy/mobileproxy.go index 072da66f..81a793ee 100644 --- a/x/mobileproxy/mobileproxy.go +++ b/x/mobileproxy/mobileproxy.go @@ -152,10 +152,12 @@ type StreamDialer struct { transport.StreamDialer } +var configParser = config.NewDefaultConfigParser() + // NewStreamDialerFromConfig creates a [StreamDialer] based on the given config. // The config format is specified in https://pkg.go.dev/github.com/Jigsaw-Code/outline-sdk/x/config#hdr-Config_Format. func NewStreamDialerFromConfig(transportConfig string) (*StreamDialer, error) { - dialer, err := config.NewStreamDialer(transportConfig) + dialer, err := configParser.WrapStreamDialer(&transport.TCPDialer{}, transportConfig) if err != nil { return nil, err } diff --git a/x/smart/racer.go b/x/smart/racer.go index 70d4826d..ddf89def 100644 --- a/x/smart/racer.go +++ b/x/smart/racer.go @@ -53,7 +53,7 @@ func raceTests[E any, R any](ctx context.Context, maxWait time.Duration, entries entry := entries[next] next++ - waitCtx, waitDone := context.WithTimeout(ctx, 250*time.Millisecond) + waitCtx, waitDone := context.WithTimeout(ctx, maxWait) if next == len(entries) { // Done with entries. No longer trigger on waitCh. waitCh = nil diff --git a/x/smart/stream_dialer.go b/x/smart/stream_dialer.go index c34b61da..5f53e709 100644 --- a/x/smart/stream_dialer.go +++ b/x/smart/stream_dialer.go @@ -227,6 +227,8 @@ func (f *StrategyFinder) findDNS(ctx context.Context, testDomains []string, dnsC return resolver.Resolver, nil } +var configParser = config.NewDefaultConfigParser() + func (f *StrategyFinder) findTLS(ctx context.Context, testDomains []string, baseDialer transport.StreamDialer, tlsConfig []string) (transport.StreamDialer, error) { if len(tlsConfig) == 0 { return nil, errors.New("config for TLS is empty. Please specify at least one transport") @@ -240,7 +242,7 @@ func (f *StrategyFinder) findTLS(ctx context.Context, testDomains []string, base Config string } result, err := raceTests(ctx, 250*time.Millisecond, tlsConfig, func(transportCfg string) (*SearchResult, error) { - tlsDialer, err := config.WrapStreamDialer(baseDialer, transportCfg) + tlsDialer, err := configParser.WrapStreamDialer(baseDialer, transportCfg) if err != nil { return nil, fmt.Errorf("WrapStreamDialer failed: %w", err) }