diff --git a/infra/conf/xray.go b/infra/conf/xray.go index d01dac2b..4d8443e1 100644 --- a/infra/conf/xray.go +++ b/infra/conf/xray.go @@ -15,6 +15,7 @@ import ( "github.com/xtls/xray-core/common/serial" core "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/browser_dialer" ) var ( @@ -362,6 +363,12 @@ type Config struct { BurstObservatory *BurstObservatoryConfig `json:"burstObservatory"` Version *VersionConfig `json:"version"` Geodata *GeodataConfig `json:"geodata"` + BrowserDialers []BrowserDialerConfig `json:"browserDialers"` +} + +type BrowserDialerConfig struct { + Tag string `json:"tag"` + URL string `json:"url"` } func (c *Config) findInboundTag(tag string) int { @@ -437,6 +444,9 @@ func (c *Config) Override(o *Config, fn string) { if o.Geodata != nil { c.Geodata = o.Geodata } + if o.BrowserDialers != nil { + c.BrowserDialers = o.BrowserDialers + } // update the Inbound in slice if the only one in override config has same tag if len(o.InboundConfigs) > 0 { @@ -605,6 +615,20 @@ func (c *Config) Build() (*core.Config, error) { return nil, errors.PrintRemovedFeatureError("Global transport config", "streamSettings in inbounds and outbounds") } + browserDialerTags := make(map[string]string, len(c.BrowserDialers)) + for _, browserDialer := range c.BrowserDialers { + if browserDialer.Tag == "" { + return nil, errors.New("browserDialers tag cannot be empty") + } + if _, found := browserDialerTags[browserDialer.Tag]; found { + return nil, errors.New("duplicate browserDialers tag: ", browserDialer.Tag) + } + browserDialerTags[browserDialer.Tag] = browserDialer.URL + } + if err := browser_dialer.ConfigureDialerTags(browserDialerTags); err != nil { + return nil, errors.New("failed to configure browserDialers").Base(err) + } + for _, rawInboundConfig := range inbounds { ic, err := rawInboundConfig.Build() if err != nil { diff --git a/transport/internet/browser_dialer/dialer.go b/transport/internet/browser_dialer/dialer.go index b1275280..0882d6b2 100644 --- a/transport/internet/browser_dialer/dialer.go +++ b/transport/internet/browser_dialer/dialer.go @@ -33,6 +33,7 @@ type task struct { var sockoptDialers map[string]*dialerInstance var dialerServers map[string]*dialerServer +var dialerTags map[string]string var mu sync.RWMutex const browserDialerSubprotocol = "browser-dialer" @@ -51,6 +52,37 @@ func HasBrowserDialerWithAddress(addr string) bool { return ok } +func GetAddressByTag(tag string) (string, bool) { + if tag == "" { + return "", false + } + mu.RLock() + defer mu.RUnlock() + addr, ok := dialerTags[tag] + return addr, ok +} + +func ConfigureDialerTags(tags map[string]string) error { + next := make(map[string]string, len(tags)) + for tag, addr := range tags { + if tag == "" { + return errors.New("browserDialers tag cannot be empty") + } + if addr == "" { + return errors.New("browserDialers url cannot be empty for tag: ", tag) + } + if err := EnsureDialerWithAddress(addr); err != nil { + return errors.New("invalid browserDialers entry for tag ", tag).Base(err) + } + next[tag] = addr + } + + mu.Lock() + dialerTags = next + mu.Unlock() + return nil +} + type webSocketExtra struct { Protocol string `json:"protocol,omitempty"` } diff --git a/transport/internet/dialer.go b/transport/internet/dialer.go index 9342f26f..6c4921f0 100644 --- a/transport/internet/dialer.go +++ b/transport/internet/dialer.go @@ -14,6 +14,7 @@ import ( "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/transport" + "github.com/xtls/xray-core/transport/internet/browser_dialer" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/pipe" ) @@ -269,6 +270,9 @@ func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig } if len(sockopt.DialerProxy) > 0 { + if _, ok := browser_dialer.GetAddressByTag(sockopt.DialerProxy); ok { + return nil, errors.New("dialerProxy tag ", sockopt.DialerProxy, " maps to browserDialers and only supports websocket or splithttp").AtError() + } if obm == nil { return nil, errors.New("there is no outbound manager for dialerProxy").AtError() } diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index cda9cadb..96a57259 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -40,6 +40,20 @@ type dialerConf struct { *internet.MemoryStreamConfig } +type errorDialerClient struct { + err error +} + +func (c *errorDialerClient) IsClosed() bool { return false } + +func (c *errorDialerClient) OpenStream(context.Context, string, string, io.Reader, bool) (io.ReadCloser, net.Addr, net.Addr, error) { + return nil, nil, nil, c.err +} + +func (c *errorDialerClient) PostPacket(context.Context, string, string, string, buf.MultiBuffer) error { + return c.err +} + var ( globalDialerMap map[dialerConf]*XmuxManager globalDialerAccess sync.Mutex @@ -50,11 +64,22 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in browserDialer := "" if streamSettings.SocketSettings != nil { browserDialer = streamSettings.SocketSettings.BrowserDialer + if browserDialer == "" { + if taggedDialer, ok := browser_dialer.GetAddressByTag(streamSettings.SocketSettings.DialerProxy); ok { + browserDialer = taggedDialer + } + } } if browser_dialer.HasBrowserDialerWithAddress(browserDialer) && realityConfig == nil { + transportConfig := streamSettings.ProtocolSettings.(*Config) + if transportConfig.Mode != "auto" && transportConfig.Mode != "packet-up" { + return &errorDialerClient{ + err: errors.New("dialerProxy/browserDialer with XHTTP only supports modes \"auto\" or \"packet-up\", got: \"", transportConfig.Mode, "\""), + }, nil + } return &BrowserDialerClient{ - transportConfig: streamSettings.ProtocolSettings.(*Config), + transportConfig: transportConfig, browserDialer: browserDialer, }, nil } diff --git a/transport/internet/websocket/dialer.go b/transport/internet/websocket/dialer.go index 99fbc877..6c7019cf 100644 --- a/transport/internet/websocket/dialer.go +++ b/transport/internet/websocket/dialer.go @@ -120,6 +120,11 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in browserDialer := "" if streamSettings.SocketSettings != nil { browserDialer = streamSettings.SocketSettings.BrowserDialer + if browserDialer == "" { + if taggedDialer, ok := browser_dialer.GetAddressByTag(streamSettings.SocketSettings.DialerProxy); ok { + browserDialer = taggedDialer + } + } } if browser_dialer.HasBrowserDialerWithAddress(browserDialer) { conn, err := browser_dialer.DialWSWithAddress(browserDialer, uri, ed)