diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index fe1f69fa..1044c8ec 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -188,7 +188,8 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } if len(option.Peers) > 0 { - for i, peer := range option.Peers { + for i := range option.Peers { + peer := &option.Peers[i] // we need modify option here bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey) if err != nil { return nil, E.Cause(err, "decode public key for peer ", i) diff --git a/common/atomic/value.go b/common/atomic/value.go index 36623b3e..82d40076 100644 --- a/common/atomic/value.go +++ b/common/atomic/value.go @@ -43,7 +43,7 @@ func (t *TypedValue[T]) Swap(new T) T { if old == nil { return DefaultValue[T]() } - return old.(T) + return old.(tValue[T]).value } func (t *TypedValue[T]) CompareAndSwap(old, new T) bool { diff --git a/common/convert/converter.go b/common/convert/converter.go index 9b0eedae..f77ebe39 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -334,7 +334,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { vmess["h2-opts"] = h2Opts - case "ws": + case "ws", "httpupgrade": headers := make(map[string]any) wsOpts := make(map[string]any) wsOpts["path"] = []string{"/"} @@ -342,7 +342,30 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { headers["Host"] = host.(string) } if path, ok := values["path"]; ok && path != "" { - wsOpts["path"] = path.(string) + path := path.(string) + pathURL, err := url.Parse(path) + if err == nil { + query := pathURL.Query() + if earlyData := query.Get("ed"); earlyData != "" { + med, err := strconv.Atoi(earlyData) + if err == nil { + switch network { + case "ws": + wsOpts["max-early-data"] = med + wsOpts["early-data-header-name"] = "Sec-WebSocket-Protocol" + case "httpupgrade": + wsOpts["v2ray-http-upgrade-fast-open"] = true + } + query.Del("ed") + pathURL.RawQuery = query.Encode() + path = pathURL.String() + } + } + if earlyDataHeader := query.Get("eh"); earlyDataHeader != "" { + wsOpts["early-data-header-name"] = earlyDataHeader + } + } + wsOpts["path"] = path } wsOpts["headers"] = headers vmess["ws-opts"] = wsOpts diff --git a/common/convert/v.go b/common/convert/v.go index 2d8cf732..4102ab75 100644 --- a/common/convert/v.go +++ b/common/convert/v.go @@ -100,7 +100,7 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m h2Opts["headers"] = headers proxy["h2-opts"] = h2Opts - case "ws": + case "ws", "httpupgrade": headers := make(map[string]any) wsOpts := make(map[string]any) headers["User-Agent"] = RandUserAgent() @@ -113,7 +113,13 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m if err != nil { return fmt.Errorf("bad WebSocket max early data size: %v", err) } - wsOpts["max-early-data"] = med + switch network { + case "ws": + wsOpts["max-early-data"] = med + wsOpts["early-data-header-name"] = "Sec-WebSocket-Protocol" + case "httpupgrade": + wsOpts["v2ray-http-upgrade-fast-open"] = true + } } if earlyDataHeader := query.Get("eh"); earlyDataHeader != "" { wsOpts["early-data-header-name"] = earlyDataHeader diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index 8cbc62fa..f9b56e47 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -213,11 +213,7 @@ func ResolveIP(ctx context.Context, host string) (netip.Addr, error) { // ResolveIPv4ProxyServerHost proxies server host only func ResolveIPv4ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { if ProxyServerHostResolver != nil { - if ip, err := ResolveIPv4WithResolver(ctx, host, ProxyServerHostResolver); err != nil { - return ResolveIPv4(ctx, host) - } else { - return ip, nil - } + return ResolveIPv4WithResolver(ctx, host, ProxyServerHostResolver) } return ResolveIPv4(ctx, host) } @@ -225,11 +221,7 @@ func ResolveIPv4ProxyServerHost(ctx context.Context, host string) (netip.Addr, e // ResolveIPv6ProxyServerHost proxies server host only func ResolveIPv6ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { if ProxyServerHostResolver != nil { - if ip, err := ResolveIPv6WithResolver(ctx, host, ProxyServerHostResolver); err != nil { - return ResolveIPv6(ctx, host) - } else { - return ip, nil - } + return ResolveIPv6WithResolver(ctx, host, ProxyServerHostResolver) } return ResolveIPv6(ctx, host) } @@ -237,11 +229,7 @@ func ResolveIPv6ProxyServerHost(ctx context.Context, host string) (netip.Addr, e // ResolveProxyServerHost proxies server host only func ResolveProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { if ProxyServerHostResolver != nil { - if ip, err := ResolveIPWithResolver(ctx, host, ProxyServerHostResolver); err != nil { - return ResolveIP(ctx, host) - } else { - return ip, err - } + return ResolveIPWithResolver(ctx, host, ProxyServerHostResolver) } return ResolveIP(ctx, host) } diff --git a/config/config.go b/config/config.go index ca179ed0..c7931573 100644 --- a/config/config.go +++ b/config/config.go @@ -482,6 +482,11 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { "www.msftconnecttest.com", }, }, + Experimental: Experimental{ + // https://github.com/quic-go/quic-go/issues/4178 + // Quic-go currently cannot automatically fall back on platforms that do not support ecn, so this feature is turned off by default. + QUICGoDisableECN: true, + }, Sniffer: RawSniffer{ Enable: false, Sniffing: []string{}, @@ -916,7 +921,7 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s l := len(rule) - if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" || ruleName == "SUB-RULE" { + if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" || ruleName == "SUB-RULE" || ruleName == "DOMAIN-REGEX" { target = rule[l-1] payload = strings.Join(rule[1:l-1], ",") } else { diff --git a/docs/config.yaml b/docs/config.yaml index 4d0e995e..6aa06862 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -383,6 +383,7 @@ proxies: # socks5 # headers: # custom: value # v2ray-http-upgrade: false + # v2ray-http-upgrade-fast-open: false - name: "ss4-shadow-tls" type: ss @@ -461,6 +462,7 @@ proxies: # socks5 # max-early-data: 2048 # early-data-header-name: Sec-WebSocket-Protocol # v2ray-http-upgrade: false + # v2ray-http-upgrade-fast-open: false - name: "vmess-h2" type: vmess @@ -589,6 +591,7 @@ proxies: # socks5 headers: Host: example.com # v2ray-http-upgrade: false + # v2ray-http-upgrade-fast-open: false # Trojan - name: "trojan" @@ -633,6 +636,7 @@ proxies: # socks5 # headers: # Host: example.com # v2ray-http-upgrade: false + # v2ray-http-upgrade-fast-open: false - name: "trojan-xtls" type: trojan diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 96ec1573..384ff016 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -173,6 +173,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis closed: false, options: options, handler: handler, + tunName: tunName, } defer func() { if err != nil { @@ -279,7 +280,6 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis func (l *Listener) FlushDefaultInterface() { if l.options.AutoDetectInterface { - targetInterface := dialer.DefaultInterface.Load() for _, destination := range []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified(), netip.MustParseAddr("1.1.1.1")} { autoDetectInterfaceName := l.defaultInterfaceMonitor.DefaultInterfaceName(destination) if autoDetectInterfaceName == l.tunName { @@ -287,17 +287,16 @@ func (l *Listener) FlushDefaultInterface() { } else if autoDetectInterfaceName == "" || autoDetectInterfaceName == "" { log.Warnln("[TUN] Auto detect interface by %s get empty name.", destination.String()) } else { - targetInterface = autoDetectInterfaceName - if old := dialer.DefaultInterface.Load(); old != targetInterface { - log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, targetInterface) - - dialer.DefaultInterface.Store(targetInterface) - + if old := dialer.DefaultInterface.Swap(autoDetectInterfaceName); old != autoDetectInterfaceName { + log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, autoDetectInterfaceName) iface.FlushCache() } return } } + if dialer.DefaultInterface.CompareAndSwap("", "") { + log.Warnln("[TUN] Auto detect interface failed, set '' to DefaultInterface to avoid lookback") + } } } diff --git a/rules/provider/classical_strategy.go b/rules/provider/classical_strategy.go index f8042164..6a2dccd5 100644 --- a/rules/provider/classical_strategy.go +++ b/rules/provider/classical_strategy.go @@ -2,9 +2,10 @@ package provider import ( "fmt" + "strings" + C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" - "strings" ) type classicalStrategy struct { @@ -76,7 +77,7 @@ func ruleParse(ruleRaw string) (string, string, []string) { } else if len(item) == 2 { return item[0], item[1], nil } else if len(item) > 2 { - if item[0] == "NOT" || item[0] == "OR" || item[0] == "AND" || item[0] == "SUB-RULE" { + if item[0] == "NOT" || item[0] == "OR" || item[0] == "AND" || item[0] == "SUB-RULE" || item[0] == "DOMAIN-REGEX" { return item[0], strings.Join(item[1:len(item)], ","), nil } else { return item[0], item[1], item[2:] diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index f8fdcf11..d5a226e9 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -596,7 +596,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { defer configMux.RUnlock() var ( resolved bool - attemptProcessLookup = true + attemptProcessLookup = metadata.Type != C.INNER ) if node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok {