Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(x): make config support PacketListeners and make dependencies explicit and decoupled #304

Merged
merged 31 commits into from
Nov 1, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use Providers
  • Loading branch information
fortuna committed Oct 30, 2024
commit 47c9ad63a8bdf4f28b2294dc7582510931e744f7
2 changes: 1 addition & 1 deletion x/configurl/doc.go
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ Package configurl provides convenience functions to create network objects based
This is experimental and mostly for illustrative purposes at this point.

Configurable strategies simplifies the way you create and manage strategies.
With the configurl package, you can use [ConfigModule.NewPacketDialer], [ConfigModule.NewStreamDialer] and [ConfigModule.NewPacketListener] to create objects using a simple text string.
With the configurl package, you can use [ProviderContainer.NewPacketDialer], [ProviderContainer.NewStreamDialer] and [ProviderContainer.NewPacketListener] to create objects using a simple text string.

Key Benefits:

66 changes: 37 additions & 29 deletions x/configurl/module.go
fortuna marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -22,51 +22,59 @@ import (
"github.com/Jigsaw-Code/outline-sdk/transport"
)

// ConfigModule enables the creation of network objects based on a config. The config is
// extensible by registering providers for config subtypes.
type ConfigModule struct {
// ProviderContainer contains providers for the creation of network objects based on a config. The config is
// extensible by registering providers for different config subtypes.
type ProviderContainer struct {
StreamDialers ExtensibleProvider[transport.StreamDialer]
PacketDialers ExtensibleProvider[transport.PacketDialer]
PacketListeners ExtensibleProvider[transport.PacketListener]
}

// NewDefaultConfigModule creates a [ConfigModule] with a set of default wrappers already registered.
func NewDefaultConfigModule() *ConfigModule {
p := new(ConfigModule)

p.StreamDialers.BaseInstance = &transport.TCPDialer{}
p.PacketDialers.BaseInstance = &transport.UDPDialer{}
p.PacketListeners.BaseInstance = &transport.UDPListener{}
// NewProviderContainer creates a [ProviderContainer] with the base instances properly initialized.
func NewProviderContainer() *ProviderContainer {
return &ProviderContainer{
StreamDialers: ExtensibleProvider[transport.StreamDialer]{BaseInstance: &transport.TCPDialer{}},
PacketDialers: ExtensibleProvider[transport.PacketDialer]{BaseInstance: &transport.UDPDialer{}},
PacketListeners: ExtensibleProvider[transport.PacketListener]{BaseInstance: &transport.UDPListener{}},
}
}

// RegisterDefaultProviders registers a set of default providers with the providers in [ProviderContainer].
func RegisterDefaultProviders(c *ProviderContainer) *ProviderContainer {
// Please keep the list in alphabetical order.
registerDO53StreamDialer(&p.StreamDialers, "do53", p.StreamDialers.NewInstance, p.PacketDialers.NewInstance)
registerDOHStreamDialer(&p.StreamDialers, "doh", p.StreamDialers.NewInstance)
registerDO53StreamDialer(&c.StreamDialers, "do53", c.StreamDialers.NewInstance, c.PacketDialers.NewInstance)
registerDOHStreamDialer(&c.StreamDialers, "doh", c.StreamDialers.NewInstance)

registerOverrideStreamDialer(&p.StreamDialers, "override", p.StreamDialers.NewInstance)
registerOverridePacketDialer(&p.PacketDialers, "override", p.PacketDialers.NewInstance)
registerOverrideStreamDialer(&c.StreamDialers, "override", c.StreamDialers.NewInstance)
registerOverridePacketDialer(&c.PacketDialers, "override", c.PacketDialers.NewInstance)

registerSOCKS5StreamDialer(&p.StreamDialers, "socks5", p.StreamDialers.NewInstance)
registerSOCKS5PacketDialer(&p.PacketDialers, "socks5", p.StreamDialers.NewInstance, p.PacketDialers.NewInstance)
registerSOCKS5PacketListener(&p.PacketListeners, "socks5", p.StreamDialers.NewInstance, p.PacketDialers.NewInstance)
registerSOCKS5StreamDialer(&c.StreamDialers, "socks5", c.StreamDialers.NewInstance)
registerSOCKS5PacketDialer(&c.PacketDialers, "socks5", c.StreamDialers.NewInstance, c.PacketDialers.NewInstance)
registerSOCKS5PacketListener(&c.PacketListeners, "socks5", c.StreamDialers.NewInstance, c.PacketDialers.NewInstance)

registerSplitStreamDialer(&p.StreamDialers, "split", p.StreamDialers.NewInstance)
registerSplitStreamDialer(&c.StreamDialers, "split", c.StreamDialers.NewInstance)

registerShadowsocksStreamDialer(&p.StreamDialers, "ss", p.StreamDialers.NewInstance)
registerShadowsocksPacketDialer(&p.PacketDialers, "ss", p.PacketDialers.NewInstance)
registerShadowsocksPacketListener(&p.PacketListeners, "ss", p.PacketDialers.NewInstance)
registerShadowsocksStreamDialer(&c.StreamDialers, "ss", c.StreamDialers.NewInstance)
registerShadowsocksPacketDialer(&c.PacketDialers, "ss", c.PacketDialers.NewInstance)
registerShadowsocksPacketListener(&c.PacketListeners, "ss", c.PacketDialers.NewInstance)

registerTLSStreamDialer(&p.StreamDialers, "tls", p.StreamDialers.NewInstance)
registerTLSStreamDialer(&c.StreamDialers, "tls", c.StreamDialers.NewInstance)

registerTLSFragStreamDialer(&p.StreamDialers, "tlsfrag", p.StreamDialers.NewInstance)
registerTLSFragStreamDialer(&c.StreamDialers, "tlsfrag", c.StreamDialers.NewInstance)

registerWebsocketStreamDialer(&p.StreamDialers, "ws", p.StreamDialers.NewInstance)
registerWebsocketPacketDialer(&p.PacketDialers, "ws", p.StreamDialers.NewInstance)
registerWebsocketStreamDialer(&c.StreamDialers, "ws", c.StreamDialers.NewInstance)
registerWebsocketPacketDialer(&c.PacketDialers, "ws", c.StreamDialers.NewInstance)

return c
}

return p
// NewDefaultProviders creates a [ProviderContainer] with a set of default providers already registered.
func NewDefaultProviders() *ProviderContainer {
return RegisterDefaultProviders(NewProviderContainer())
}

// NewStreamDialer creates a [transport.StreamDialer] according to the config text.
func (p *ConfigModule) NewStreamDialer(ctx context.Context, configText string) (transport.StreamDialer, error) {
func (p *ProviderContainer) NewStreamDialer(ctx context.Context, configText string) (transport.StreamDialer, error) {
config, err := ParseConfig(configText)
if err != nil {
return nil, err
@@ -75,7 +83,7 @@ func (p *ConfigModule) NewStreamDialer(ctx context.Context, configText string) (
}

// NewPacketDialer creates a [transport.PacketDialer] according to the config text.
func (p *ConfigModule) NewPacketDialer(ctx context.Context, configText string) (transport.PacketDialer, error) {
func (p *ProviderContainer) NewPacketDialer(ctx context.Context, configText string) (transport.PacketDialer, error) {
config, err := ParseConfig(configText)
if err != nil {
return nil, err
@@ -84,7 +92,7 @@ func (p *ConfigModule) NewPacketDialer(ctx context.Context, configText string) (
}

// NewPacketListner creates a [transport.PacketListener] according to the config text.
func (p *ConfigModule) NewPacketListener(ctx context.Context, configText string) (transport.PacketListener, error) {
func (p *ProviderContainer) NewPacketListener(ctx context.Context, configText string) (transport.PacketListener, error) {
config, err := ParseConfig(configText)
if err != nil {
return nil, err
2 changes: 1 addition & 1 deletion x/examples/fetch-speed/main.go
Original file line number Diff line number Diff line change
@@ -58,7 +58,7 @@ func main() {
os.Exit(1)
}

dialer, err := configurl.NewDefaultConfigModule().NewStreamDialer(context.Background(), *transportFlag)
dialer, err := configurl.NewDefaultProviders().NewStreamDialer(context.Background(), *transportFlag)
if err != nil {
log.Fatalf("Could not create dialer: %v\n", err)
}
2 changes: 1 addition & 1 deletion x/examples/fetch/main.go
Original file line number Diff line number Diff line change
@@ -84,7 +84,7 @@ func main() {
os.Exit(1)
}

dialer, err := configurl.NewDefaultConfigModule().NewStreamDialer(context.Background(), *transportFlag)
dialer, err := configurl.NewDefaultProviders().NewStreamDialer(context.Background(), *transportFlag)
if err != nil {
log.Fatalf("Could not create dialer: %v\n", err)
}
2 changes: 1 addition & 1 deletion x/examples/http2transport/main.go
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ func main() {
urlProxyPrefixFlag := flag.String("urlProxyPrefix", "/proxy", "Path where to run the URL proxy. Set to empty (\"\") to disable it.")
flag.Parse()

dialer, err := configurl.NewDefaultConfigModule().NewStreamDialer(context.Background(), *transportFlag)
dialer, err := configurl.NewDefaultProviders().NewStreamDialer(context.Background(), *transportFlag)

if err != nil {
log.Fatalf("Could not create dialer: %v", err)
2 changes: 1 addition & 1 deletion x/examples/outline-cli/outline_device.go
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ type OutlineDevice struct {
svrIP net.IP
}

var configModule = configurl.NewDefaultConfigModule()
var configModule = configurl.NewDefaultProviders()

func NewOutlineDevice(transportConfig string) (od *OutlineDevice, err error) {
ip, err := resolveShadowsocksServerIPFromConfig(transportConfig)
2 changes: 1 addition & 1 deletion x/examples/outline-cli/outline_packet_proxy.go
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ type outlinePacketProxy struct {
func newOutlinePacketProxy(transportConfig string) (opp *outlinePacketProxy, err error) {
opp = &outlinePacketProxy{}

if opp.remotePl, err = configurl.NewDefaultConfigModule().NewPacketListener(context.TODO(), transportConfig); err != nil {
if opp.remotePl, err = configurl.NewDefaultProviders().NewPacketListener(context.TODO(), transportConfig); err != nil {
return nil, fmt.Errorf("failed to create UDP packet listener: %w", err)
}
if opp.remote, err = network.NewPacketProxyFromPacketListener(opp.remotePl); err != nil {
6 changes: 3 additions & 3 deletions x/examples/resolve/main.go
Original file line number Diff line number Diff line change
@@ -66,15 +66,15 @@ func main() {
resolverAddr := *resolverFlag

var resolver dns.Resolver
configModule := configurl.NewDefaultConfigModule()
providers := configurl.NewDefaultProviders()
if *tcpFlag {
streamDialer, err := configModule.NewStreamDialer(context.Background(), *transportFlag)
streamDialer, err := providers.NewStreamDialer(context.Background(), *transportFlag)
if err != nil {
log.Fatalf("Could not create stream dialer: %v", err)
}
resolver = dns.NewTCPResolver(streamDialer, resolverAddr)
} else {
packetDialer, err := configModule.NewPacketDialer(context.Background(), *transportFlag)
packetDialer, err := providers.NewPacketDialer(context.Background(), *transportFlag)
if err != nil {
log.Fatalf("Could not create packet dialer: %v", err)
}
6 changes: 3 additions & 3 deletions x/examples/smart-proxy/main.go
Original file line number Diff line number Diff line change
@@ -86,12 +86,12 @@ func main() {
log.Fatalf("Could not read config: %v", err)
}

configModule := configurl.NewDefaultConfigModule()
packetDialer, err := configModule.NewPacketDialer(context.Background(), *transportFlag)
providers := configurl.NewDefaultProviders()
packetDialer, err := providers.NewPacketDialer(context.Background(), *transportFlag)
if err != nil {
log.Fatalf("Could not create packet dialer: %v", err)
}
streamDialer, err := configModule.NewStreamDialer(context.Background(), *transportFlag)
streamDialer, err := providers.NewStreamDialer(context.Background(), *transportFlag)
if err != nil {
log.Fatalf("Could not create stream dialer: %v", err)
}
10 changes: 5 additions & 5 deletions x/examples/test-connectivity/main.go
Original file line number Diff line number Diff line change
@@ -240,7 +240,7 @@ func main() {
var mu sync.Mutex
dnsReports := make([]dnsReport, 0)
tcpReports := make([]tcpReport, 0)
configModule := configurl.NewDefaultConfigModule()
providers := configurl.NewDefaultProviders()
onDNS := func(ctx context.Context, domain string) func(di httptrace.DNSDoneInfo) {
dnsStart := time.Now()
return func(di httptrace.DNSDoneInfo) {
@@ -260,7 +260,7 @@ func main() {
mu.Unlock()
}
}
configModule.StreamDialers.BaseInstance = transport.FuncStreamDialer(func(ctx context.Context, addr string) (transport.StreamConn, error) {
providers.StreamDialers.BaseInstance = transport.FuncStreamDialer(func(ctx context.Context, addr string) (transport.StreamConn, error) {
hostname, _, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
@@ -284,21 +284,21 @@ func main() {
}
return newTCPTraceDialer(onDNS, onDial).DialStream(ctx, addr)
})
configModule.PacketDialers.BaseInstance = transport.FuncPacketDialer(func(ctx context.Context, addr string) (net.Conn, error) {
providers.PacketDialers.BaseInstance = transport.FuncPacketDialer(func(ctx context.Context, addr string) (net.Conn, error) {
return newUDPTraceDialer(onDNS).DialPacket(ctx, addr)
})

switch proto {
case "tcp":
streamDialer, err := configModule.NewStreamDialer(context.Background(), *transportFlag)
streamDialer, err := providers.NewStreamDialer(context.Background(), *transportFlag)
if err != nil {
slog.Error("Failed to create StreamDialer", "error", err)
os.Exit(1)
}
resolver = dns.NewTCPResolver(streamDialer, resolverAddress)

case "udp":
packetDialer, err := configModule.NewPacketDialer(context.Background(), *transportFlag)
packetDialer, err := providers.NewPacketDialer(context.Background(), *transportFlag)
if err != nil {
slog.Error("Failed to create PacketDialer", "error", err)
os.Exit(1)
6 changes: 3 additions & 3 deletions x/examples/ws2endpoint/main.go
Original file line number Diff line number Diff line change
@@ -60,10 +60,10 @@ func main() {
defer listener.Close()
log.Printf("Proxy listening on %v\n", listener.Addr().String())

configModule := configurl.NewDefaultConfigModule()
providers := configurl.NewDefaultProviders()
mux := http.NewServeMux()
if *tcpPathFlag != "" {
dialer, err := configModule.NewStreamDialer(context.Background(), *transportFlag)
dialer, err := providers.NewStreamDialer(context.Background(), *transportFlag)
if err != nil {
log.Fatalf("Could not create stream dialer: %v", err)
}
@@ -90,7 +90,7 @@ func main() {
mux.Handle(*tcpPathFlag, http.StripPrefix(*tcpPathFlag, handler))
}
if *udpPathFlag != "" {
dialer, err := configModule.NewPacketDialer(context.Background(), *transportFlag)
dialer, err := providers.NewPacketDialer(context.Background(), *transportFlag)
if err != nil {
log.Fatalf("Could not create stream dialer: %v", err)
}
12 changes: 6 additions & 6 deletions x/httpproxy/connect_handler.go
Original file line number Diff line number Diff line change
@@ -51,8 +51,8 @@ func (d *sanitizeErrorDialer) DialStream(ctx context.Context, addr string) (tran
}

type connectHandler struct {
dialer *sanitizeErrorDialer
configModule *configurl.ConfigModule
dialer *sanitizeErrorDialer
providers *configurl.ProviderContainer
}

var _ http.Handler = (*connectHandler)(nil)
@@ -77,7 +77,7 @@ func (h *connectHandler) ServeHTTP(proxyResp http.ResponseWriter, proxyReq *http

// Dial the target.
transportConfig := proxyReq.Header.Get("Transport")
dialer, err := h.configModule.NewStreamDialer(proxyReq.Context(), transportConfig)
dialer, err := h.providers.NewStreamDialer(proxyReq.Context(), 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)
@@ -149,7 +149,7 @@ func NewConnectHandler(dialer transport.StreamDialer) http.Handler {
// of the base dialer (e.g. access key credentials) to the user.
sd := &sanitizeErrorDialer{dialer}
// TODO(fortuna): Inject the config parser
configModule := configurl.NewDefaultConfigModule()
configModule.StreamDialers.BaseInstance = sd
return &connectHandler{sd, configModule}
providers := configurl.NewDefaultProviders()
providers.StreamDialers.BaseInstance = sd
return &connectHandler{sd, providers}
}
2 changes: 1 addition & 1 deletion x/mobileproxy/mobileproxy.go
Original file line number Diff line number Diff line change
@@ -152,7 +152,7 @@ type StreamDialer struct {
transport.StreamDialer
}

var configModule = configurl.NewDefaultConfigModule()
var configModule = configurl.NewDefaultProviders()

// 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.
2 changes: 1 addition & 1 deletion x/smart/stream_dialer.go
Original file line number Diff line number Diff line change
@@ -231,7 +231,7 @@ func (f *StrategyFinder) findTLS(ctx context.Context, testDomains []string, base
if len(tlsConfig) == 0 {
return nil, errors.New("config for TLS is empty. Please specify at least one transport")
}
var configModule = configurl.NewDefaultConfigModule()
var configModule = configurl.NewDefaultProviders()
configModule.StreamDialers.BaseInstance = baseDialer

ctx, searchDone := context.WithCancel(ctx)