Merge branch 'Beta' into Meta

This commit is contained in:
metacubex 2023-01-12 01:33:56 +08:00
commit b9ffc82e53
39 changed files with 718 additions and 353 deletions

View file

@ -1,4 +1,4 @@
NAME=Clash.Meta NAME=clash.meta
BINDIR=bin BINDIR=bin
BRANCH=$(shell git branch --show-current) BRANCH=$(shell git branch --show-current)
ifeq ($(BRANCH),Alpha) ifeq ($(BRANCH),Alpha)

View file

@ -2,6 +2,7 @@ package outbound
import ( import (
"context" "context"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"net" "net"
@ -9,12 +10,14 @@ import (
"github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/common/structure"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/shadowtls"
obfs "github.com/Dreamacro/clash/transport/simple-obfs" obfs "github.com/Dreamacro/clash/transport/simple-obfs"
"github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/socks5"
v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin" v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin"
"github.com/metacubex/sing-shadowsocks" shadowsocks "github.com/metacubex/sing-shadowsocks"
"github.com/metacubex/sing-shadowsocks/shadowimpl" "github.com/metacubex/sing-shadowsocks/shadowimpl"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
@ -27,9 +30,11 @@ type ShadowSocks struct {
option *ShadowSocksOption option *ShadowSocksOption
// obfs // obfs
obfsMode string obfsMode string
obfsOption *simpleObfsOption obfsOption *simpleObfsOption
v2rayOption *v2rayObfs.Option v2rayOption *v2rayObfs.Option
shadowTLSOption *shadowTLSOption
tlsConfig *tls.Config
} }
type ShadowSocksOption struct { type ShadowSocksOption struct {
@ -61,6 +66,13 @@ type v2rayObfsOption struct {
Mux bool `obfs:"mux,omitempty"` Mux bool `obfs:"mux,omitempty"`
} }
type shadowTLSOption struct {
Password string `obfs:"password"`
Host string `obfs:"host"`
Fingerprint string `obfs:"fingerprint,omitempty"`
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
}
// StreamConn implements C.ProxyAdapter // StreamConn implements C.ProxyAdapter
func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
switch ss.obfsMode { switch ss.obfsMode {
@ -75,6 +87,8 @@ func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e
if err != nil { if err != nil {
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
} }
case shadowtls.Mode:
c = shadowtls.NewShadowTLS(c, ss.shadowTLSOption.Password, ss.tlsConfig)
} }
if metadata.NetWork == C.UDP && ss.option.UDPOverTCP { if metadata.NetWork == C.UDP && ss.option.UDPOverTCP {
return ss.method.DialConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")) return ss.method.DialConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443"))
@ -157,6 +171,8 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
var v2rayOption *v2rayObfs.Option var v2rayOption *v2rayObfs.Option
var obfsOption *simpleObfsOption var obfsOption *simpleObfsOption
var shadowTLSOpt *shadowTLSOption
var tlsConfig *tls.Config
obfsMode := "" obfsMode := ""
decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true}) decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true})
@ -192,6 +208,27 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
v2rayOption.TLS = true v2rayOption.TLS = true
v2rayOption.SkipCertVerify = opts.SkipCertVerify v2rayOption.SkipCertVerify = opts.SkipCertVerify
} }
} else if option.Plugin == shadowtls.Mode {
obfsMode = shadowtls.Mode
shadowTLSOpt = &shadowTLSOption{}
if err := decoder.Decode(option.PluginOpts, shadowTLSOpt); err != nil {
return nil, fmt.Errorf("ss %s initialize shadow-tls-plugin error: %w", addr, err)
}
tlsConfig = &tls.Config{
NextProtos: shadowtls.DefaultALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: shadowTLSOpt.SkipCertVerify,
ServerName: shadowTLSOpt.Host,
}
if len(shadowTLSOpt.Fingerprint) == 0 {
tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig)
} else {
if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, shadowTLSOpt.Fingerprint); err != nil {
return nil, err
}
}
} }
return &ShadowSocks{ return &ShadowSocks{
@ -206,10 +243,12 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
}, },
method: method, method: method,
option: &option, option: &option,
obfsMode: obfsMode, obfsMode: obfsMode,
v2rayOption: v2rayOption, v2rayOption: v2rayOption,
obfsOption: obfsOption, obfsOption: obfsOption,
shadowTLSOption: shadowTLSOpt,
tlsConfig: tlsConfig,
}, nil }, nil
} }

View file

@ -4,12 +4,12 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
tlsC "github.com/Dreamacro/clash/component/tls"
"net" "net"
"net/http" "net/http"
"strconv" "strconv"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/gun" "github.com/Dreamacro/clash/transport/gun"
"github.com/Dreamacro/clash/transport/trojan" "github.com/Dreamacro/clash/transport/trojan"
@ -219,13 +219,16 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
Fingerprint: option.Fingerprint, Fingerprint: option.Fingerprint,
} }
if option.Network != "ws" && len(option.Flow) >= 16 { switch option.Network {
option.Flow = option.Flow[:16] case "", "tcp":
switch option.Flow { if len(option.Flow) >= 16 {
case vless.XRO, vless.XRD, vless.XRS: option.Flow = option.Flow[:16]
tOption.Flow = option.Flow switch option.Flow {
default: case vless.XRO, vless.XRD, vless.XRS:
return nil, fmt.Errorf("unsupported xtls flow type: %s", option.Flow) tOption.Flow = option.Flow
default:
return nil, fmt.Errorf("unsupported xtls flow type: %s", option.Flow)
}
} }
} }
@ -273,11 +276,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
} }
} }
if t.option.Flow != "" { t.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
t.transport = gun.NewHTTP2XTLSClient(dialFn, tlsConfig)
} else {
t.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
}
t.gunTLSConfig = tlsConfig t.gunTLSConfig = tlsConfig
t.gunConfig = &gun.Config{ t.gunConfig = &gun.Config{

View file

@ -12,6 +12,10 @@ import (
"strconv" "strconv"
"sync" "sync"
vmessSing "github.com/sagernet/sing-vmess"
"github.com/sagernet/sing-vmess/packetaddr"
M "github.com/sagernet/sing/common/metadata"
"github.com/Dreamacro/clash/common/convert" "github.com/Dreamacro/clash/common/convert"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
@ -49,6 +53,9 @@ type VlessOption struct {
FlowShow bool `proxy:"flow-show,omitempty"` FlowShow bool `proxy:"flow-show,omitempty"`
TLS bool `proxy:"tls,omitempty"` TLS bool `proxy:"tls,omitempty"`
UDP bool `proxy:"udp,omitempty"` UDP bool `proxy:"udp,omitempty"`
PacketAddr bool `proxy:"packet-addr,omitempty"`
XUDP bool `proxy:"xudp,omitempty"`
PacketEncoding string `proxy:"packet-encoding,omitempty"`
Network string `proxy:"network,omitempty"` Network string `proxy:"network,omitempty"`
HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"` HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"`
HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"`
@ -137,11 +144,7 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
c, err = vmess.StreamH2Conn(c, h2Opts) c, err = vmess.StreamH2Conn(c, h2Opts)
case "grpc": case "grpc":
if v.isXTLSEnabled() { c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig)
c, err = gun.StreamGunWithXTLSConn(c, v.gunTLSConfig, v.gunConfig)
} else {
c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig)
}
default: default:
// default tcp network // default tcp network
// handle TLS And XTLS // handle TLS And XTLS
@ -152,21 +155,17 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
return nil, err return nil, err
} }
return v.client.StreamConn(c, parseVlessAddr(metadata)) return v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP))
} }
func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) { func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) {
host, _, _ := net.SplitHostPort(v.addr) host, _, _ := net.SplitHostPort(v.addr)
if v.isXTLSEnabled() { if v.isXTLSEnabled() && !isH2 {
xtlsOpts := vless.XTLSConfig{ xtlsOpts := vless.XTLSConfig{
Host: host, Host: host,
SkipCertVerify: v.option.SkipCertVerify, SkipCertVerify: v.option.SkipCertVerify,
FingerPrint: v.option.Fingerprint, Fingerprint: v.option.Fingerprint,
}
if isH2 {
xtlsOpts.NextProtos = []string{"h2"}
} }
if v.option.ServerName != "" { if v.option.ServerName != "" {
@ -212,7 +211,7 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
safeConnClose(c, err) safeConnClose(c, err)
}(c) }(c)
c, err = v.client.StreamConn(c, parseVlessAddr(metadata)) c, err = v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -259,7 +258,15 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
safeConnClose(c, err) safeConnClose(c, err)
}(c) }(c)
c, err = v.client.StreamConn(c, parseVlessAddr(metadata)) if v.option.PacketAddr {
packetAddrMetadata := *metadata // make a copy
packetAddrMetadata.Host = packetaddr.SeqPacketMagicAddress
packetAddrMetadata.DstPort = "443"
c, err = v.client.StreamConn(c, parseVlessAddr(&packetAddrMetadata, false))
} else {
c, err = v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP))
}
if err != nil { if err != nil {
return nil, fmt.Errorf("new vless client error: %v", err) return nil, fmt.Errorf("new vless client error: %v", err)
@ -289,7 +296,15 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
safeConnClose(c, err) safeConnClose(c, err)
}(c) }(c)
c, err = v.StreamConn(c, metadata) if v.option.PacketAddr {
packetAddrMetadata := *metadata // make a copy
packetAddrMetadata.Host = packetaddr.SeqPacketMagicAddress
packetAddrMetadata.DstPort = "443"
c, err = v.StreamConn(c, &packetAddrMetadata)
} else {
c, err = v.StreamConn(c, metadata)
}
if err != nil { if err != nil {
return nil, fmt.Errorf("new vless client error: %v", err) return nil, fmt.Errorf("new vless client error: %v", err)
@ -305,6 +320,17 @@ func (v *Vless) SupportWithDialer() bool {
// ListenPacketOnStreamConn implements C.ProxyAdapter // ListenPacketOnStreamConn implements C.ProxyAdapter
func (v *Vless) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { func (v *Vless) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
if v.option.XUDP {
return newPacketConn(&threadSafePacketConn{
PacketConn: vmessSing.NewXUDPConn(c, M.ParseSocksaddr(metadata.RemoteAddress())),
}, v), nil
} else if v.option.PacketAddr {
return newPacketConn(&threadSafePacketConn{
PacketConn: packetaddr.NewConn(&vlessPacketConn{
Conn: c, rAddr: metadata.UDPAddr(),
}, M.ParseSocksaddr(metadata.RemoteAddress())),
}, v), nil
}
return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
} }
@ -313,7 +339,7 @@ func (v *Vless) SupportUOT() bool {
return true return true
} }
func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr { func parseVlessAddr(metadata *C.Metadata, xudp bool) *vless.DstAddr {
var addrType byte var addrType byte
var addr []byte var addr []byte
switch metadata.AddrType() { switch metadata.AddrType() {
@ -337,7 +363,8 @@ func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr {
UDP: metadata.NetWork == C.UDP, UDP: metadata.NetWork == C.UDP,
AddrType: addrType, AddrType: addrType,
Addr: addr, Addr: addr,
Port: uint(port), Port: uint16(port),
Mux: metadata.NetWork == C.UDP && xudp,
} }
} }
@ -459,12 +486,23 @@ func NewVless(option VlessOption) (*Vless, error) {
tp: C.Vless, tp: C.Vless,
udp: option.UDP, udp: option.UDP,
iface: option.Interface, iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion), prefer: C.NewDNSPrefer(option.IPVersion),
}, },
client: client, client: client,
option: &option, option: &option,
} }
switch option.PacketEncoding {
case "packetaddr", "packet":
option.PacketAddr = true
case "xudp":
option.XUDP = true
}
if option.XUDP {
option.PacketAddr = false
}
switch option.Network { switch option.Network {
case "h2": case "h2":
if len(option.HTTP2Opts.Host) == 0 { if len(option.HTTP2Opts.Host) == 0 {
@ -497,11 +535,7 @@ func NewVless(option VlessOption) (*Vless, error) {
v.gunTLSConfig = tlsConfig v.gunTLSConfig = tlsConfig
v.gunConfig = gunConfig v.gunConfig = gunConfig
if v.isXTLSEnabled() { v.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
v.transport = gun.NewHTTP2XTLSClient(dialFn, tlsConfig)
} else {
v.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
}
} }
return v, nil return v, nil

View file

@ -116,7 +116,6 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
if len(v.option.Fingerprint) == 0 { if len(v.option.Fingerprint) == 0 {
wsOpts.TLSConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) wsOpts.TLSConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig)
} else { } else {
var err error
if wsOpts.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint); err != nil { if wsOpts.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint); err != nil {
return nil, err return nil, err
} }
@ -290,16 +289,6 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
} }
return v.ListenPacketOnStreamConn(c, metadata) return v.ListenPacketOnStreamConn(c, metadata)
} }
c, err = dialer.DialContext(ctx, "tcp", v.addr, v.Base.DialOptions(opts...)...)
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
}
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)
}(c)
c, err = v.StreamConn(c, metadata)
return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata) return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata)
} }
@ -315,10 +304,18 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
} }
c, err := dialer.DialContext(ctx, "tcp", v.addr) c, err := dialer.DialContext(ctx, "tcp", v.addr)
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
}
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)
}(c)
c, err = v.StreamConn(c, metadata)
if err != nil { if err != nil {
return nil, fmt.Errorf("new vmess client error: %v", err) return nil, fmt.Errorf("new vmess client error: %v", err)
} }
return v.ListenPacketOnStreamConn(c, metadata) return v.ListenPacketOnStreamConn(c, metadata)
} }

View file

@ -4,11 +4,12 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"time"
"github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider" "github.com/Dreamacro/clash/constant/provider"
"time"
) )
type Fallback struct { type Fallback struct {
@ -132,6 +133,7 @@ func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider)
}, },
option.Filter, option.Filter,
option.ExcludeFilter, option.ExcludeFilter,
option.ExcludeType,
providers, providers,
}), }),
disableUDP: option.DisableUDP, disableUDP: option.DisableUDP,

View file

@ -3,23 +3,26 @@ package outboundgroup
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"sync"
"time"
"github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/adapter/outbound"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider" "github.com/Dreamacro/clash/constant/provider"
types "github.com/Dreamacro/clash/constant/provider" types "github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/tunnel" "github.com/Dreamacro/clash/tunnel"
"github.com/dlclark/regexp2" "github.com/dlclark/regexp2"
"go.uber.org/atomic" "go.uber.org/atomic"
"strings"
"sync"
"time"
) )
type GroupBase struct { type GroupBase struct {
*outbound.Base *outbound.Base
filterRegs []*regexp2.Regexp filterRegs []*regexp2.Regexp
excludeFilterReg *regexp2.Regexp excludeFilterReg *regexp2.Regexp
excludeTypeArray []string
providers []provider.ProxyProvider providers []provider.ProxyProvider
failedTestMux sync.Mutex failedTestMux sync.Mutex
failedTimes int failedTimes int
@ -33,6 +36,7 @@ type GroupBaseOption struct {
outbound.BaseOption outbound.BaseOption
filter string filter string
excludeFilter string excludeFilter string
excludeType string
providers []provider.ProxyProvider providers []provider.ProxyProvider
} }
@ -41,6 +45,10 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
if opt.excludeFilter != "" { if opt.excludeFilter != "" {
excludeFilterReg = regexp2.MustCompile(opt.excludeFilter, 0) excludeFilterReg = regexp2.MustCompile(opt.excludeFilter, 0)
} }
var excludeTypeArray []string
if opt.excludeType != "" {
excludeTypeArray = strings.Split(opt.excludeType, "|")
}
var filterRegs []*regexp2.Regexp var filterRegs []*regexp2.Regexp
if opt.filter != "" { if opt.filter != "" {
@ -54,6 +62,7 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
Base: outbound.NewBase(opt.BaseOption), Base: outbound.NewBase(opt.BaseOption),
filterRegs: filterRegs, filterRegs: filterRegs,
excludeFilterReg: excludeFilterReg, excludeFilterReg: excludeFilterReg,
excludeTypeArray: excludeTypeArray,
providers: opt.providers, providers: opt.providers,
failedTesting: atomic.NewBool(false), failedTesting: atomic.NewBool(false),
} }
@ -148,6 +157,24 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
} }
proxies = newProxies proxies = newProxies
} }
if gb.excludeTypeArray != nil {
var newProxies []C.Proxy
for _, p := range proxies {
mType := p.Type().String()
flag := false
for i := range gb.excludeTypeArray {
if strings.EqualFold(mType, gb.excludeTypeArray[i]) {
flag = true
}
}
if flag {
continue
}
newProxies = append(newProxies, p)
}
proxies = newProxies
}
if gb.excludeFilterReg != nil { if gb.excludeFilterReg != nil {
var newProxies []C.Proxy var newProxies []C.Proxy

View file

@ -5,11 +5,11 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/Dreamacro/clash/common/cache"
"net" "net"
"time" "time"
"github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/murmur3" "github.com/Dreamacro/clash/common/murmur3"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
@ -229,6 +229,7 @@ func NewLoadBalance(option *GroupCommonOption, providers []provider.ProxyProvide
}, },
option.Filter, option.Filter,
option.ExcludeFilter, option.ExcludeFilter,
option.ExcludeType,
providers, providers,
}), }),
strategyFn: strategyFn, strategyFn: strategyFn,

View file

@ -31,6 +31,7 @@ type GroupCommonOption struct {
DisableUDP bool `group:"disable-udp,omitempty"` DisableUDP bool `group:"disable-udp,omitempty"`
Filter string `group:"filter,omitempty"` Filter string `group:"filter,omitempty"`
ExcludeFilter string `group:"exclude-filter,omitempty"` ExcludeFilter string `group:"exclude-filter,omitempty"`
ExcludeType string `group:"exclude-type,omitempty"`
} }
func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, providersMap map[string]types.ProxyProvider) (C.ProxyAdapter, error) { func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, providersMap map[string]types.ProxyProvider) (C.ProxyAdapter, error) {

View file

@ -191,6 +191,7 @@ func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Re
}, },
"", "",
"", "",
"",
providers, providers,
}), }),
} }

View file

@ -100,6 +100,7 @@ func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider)
}, },
option.Filter, option.Filter,
option.ExcludeFilter, option.ExcludeFilter,
option.ExcludeType,
providers, providers,
}), }),
selected: "COMPATIBLE", selected: "COMPATIBLE",

View file

@ -144,6 +144,7 @@ func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, o
option.Filter, option.Filter,
option.ExcludeFilter, option.ExcludeFilter,
option.ExcludeType,
providers, providers,
}), }),
fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10), fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10),

View file

@ -3,10 +3,10 @@ package provider
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/Dreamacro/clash/component/resource"
"time" "time"
"github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/common/structure"
"github.com/Dreamacro/clash/component/resource"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
types "github.com/Dreamacro/clash/constant/provider" types "github.com/Dreamacro/clash/constant/provider"
) )
@ -27,6 +27,7 @@ type proxyProviderSchema struct {
Interval int `provider:"interval,omitempty"` Interval int `provider:"interval,omitempty"`
Filter string `provider:"filter,omitempty"` Filter string `provider:"filter,omitempty"`
ExcludeFilter string `provider:"exclude-filter,omitempty"` ExcludeFilter string `provider:"exclude-filter,omitempty"`
ExcludeType string `provider:"exclude-type,omitempty"`
HealthCheck healthCheckSchema `provider:"health-check,omitempty"` HealthCheck healthCheckSchema `provider:"health-check,omitempty"`
} }
@ -63,5 +64,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
interval := time.Duration(uint(schema.Interval)) * time.Second interval := time.Duration(uint(schema.Interval)) * time.Second
filter := schema.Filter filter := schema.Filter
excludeFilter := schema.ExcludeFilter excludeFilter := schema.ExcludeFilter
return NewProxySetProvider(name, interval, filter, excludeFilter, vehicle, hc) excludeType := schema.ExcludeType
return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, vehicle, hc)
} }

View file

@ -5,8 +5,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/dlclark/regexp2"
"gopkg.in/yaml.v3"
"net/http" "net/http"
"runtime" "runtime"
"strings" "strings"
@ -19,6 +17,9 @@ import (
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
types "github.com/Dreamacro/clash/constant/provider" types "github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"github.com/dlclark/regexp2"
"gopkg.in/yaml.v3"
) )
const ( const (
@ -141,11 +142,16 @@ func stopProxyProvider(pd *ProxySetProvider) {
_ = pd.Fetcher.Destroy() _ = pd.Fetcher.Destroy()
} }
func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) {
excludeFilterReg, err := regexp2.Compile(excludeFilter, 0) excludeFilterReg, err := regexp2.Compile(excludeFilter, 0)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid excludeFilter regex: %w", err) return nil, fmt.Errorf("invalid excludeFilter regex: %w", err)
} }
var excludeTypeArray []string
if excludeType != "" {
excludeTypeArray = strings.Split(excludeType, "|")
}
var filterRegs []*regexp2.Regexp var filterRegs []*regexp2.Regexp
for _, filter := range strings.Split(filter, "`") { for _, filter := range strings.Split(filter, "`") {
filterReg, err := regexp2.Compile(filter, 0) filterReg, err := regexp2.Compile(filter, 0)
@ -164,7 +170,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc
healthCheck: hc, healthCheck: hc,
} }
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, filterRegs, excludeFilterReg), proxiesOnUpdate(pd)) fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg), proxiesOnUpdate(pd))
pd.Fetcher = fetcher pd.Fetcher = fetcher
pd.getSubscriptionInfo() pd.getSubscriptionInfo()
@ -262,7 +268,7 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) {
} }
} }
func proxiesParseAndFilter(filter string, excludeFilter string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp) resource.Parser[[]C.Proxy] { func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp) resource.Parser[[]C.Proxy] {
return func(buf []byte) ([]C.Proxy, error) { return func(buf []byte) ([]C.Proxy, error) {
schema := &ProxySchema{} schema := &ProxySchema{}
@ -282,6 +288,27 @@ func proxiesParseAndFilter(filter string, excludeFilter string, filterRegs []*re
proxiesSet := map[string]struct{}{} proxiesSet := map[string]struct{}{}
for _, filterReg := range filterRegs { for _, filterReg := range filterRegs {
for idx, mapping := range schema.Proxies { for idx, mapping := range schema.Proxies {
if nil != excludeTypeArray && len(excludeTypeArray) > 0 {
mType, ok := mapping["type"]
if !ok {
continue
}
pType, ok := mType.(string)
if !ok {
continue
}
flag := false
for i := range excludeTypeArray {
if strings.EqualFold(pType, excludeTypeArray[i]) {
flag = true
}
}
if flag {
continue
}
}
mName, ok := mapping["name"] mName, ok := mapping["name"]
if !ok { if !ok {
continue continue

View file

@ -144,14 +144,6 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
if encryption := query.Get("encryption"); encryption != "" { if encryption := query.Get("encryption"); encryption != "" {
vmess["cipher"] = encryption vmess["cipher"] = encryption
} }
if packetEncoding := query.Get("packetEncoding"); packetEncoding != "" {
switch packetEncoding {
case "packet":
vmess["packet-addr"] = true
case "xudp":
vmess["xudp"] = true
}
}
proxies = append(proxies, vmess) proxies = append(proxies, vmess)
continue continue
} }
@ -162,8 +154,11 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
if jsonDc.Decode(&values) != nil { if jsonDc.Decode(&values) != nil {
continue continue
} }
tempName, ok := values["ps"].(string)
name := uniqueName(names, values["ps"].(string)) if !ok {
continue
}
name := uniqueName(names, tempName)
vmess := make(map[string]any, 20) vmess := make(map[string]any, 20)
vmess["name"] = name vmess["name"] = name
@ -177,6 +172,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
vmess["alterId"] = 0 vmess["alterId"] = 0
} }
vmess["udp"] = true vmess["udp"] = true
vmess["xudp"] = true
vmess["tls"] = false vmess["tls"] = false
vmess["skip-cert-verify"] = false vmess["skip-cert-verify"] = false
@ -272,19 +268,22 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
} }
var ( var (
cipher = urlSS.User.Username() cipherRaw = urlSS.User.Username()
password string cipher string
password string
) )
if password, found = urlSS.User.Password(); !found { if password, found = urlSS.User.Password(); !found {
dcBuf, _ := enc.DecodeString(cipher) dcBuf, _ := enc.DecodeString(cipherRaw)
if !strings.Contains(string(dcBuf), "2022-blake3") {
dcBuf, _ = encRaw.DecodeString(cipher)
}
cipher, password, found = strings.Cut(string(dcBuf), ":") cipher, password, found = strings.Cut(string(dcBuf), ":")
if !found { if !found {
continue continue
} }
err := VerifyMethod(cipher, password)
if err != nil {
dcBuf, _ := encRaw.DecodeString(cipherRaw)
cipher, password, found = strings.Cut(string(dcBuf), ":")
}
} }
ss := make(map[string]any, 10) ss := make(map[string]any, 10)

View file

@ -2,6 +2,7 @@ package convert
import ( import (
"encoding/base64" "encoding/base64"
"github.com/metacubex/sing-shadowsocks/shadowimpl"
"math/rand" "math/rand"
"net/http" "net/http"
"strings" "strings"
@ -314,3 +315,8 @@ func SetUserAgent(header http.Header) {
userAgent := RandUserAgent() userAgent := RandUserAgent()
header.Set("User-Agent", userAgent) header.Set("User-Agent", userAgent)
} }
func VerifyMethod(cipher, password string) (err error) {
_, err = shadowimpl.FetchMethod(cipher, password)
return
}

View file

@ -25,6 +25,14 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m
proxy["servername"] = sni proxy["servername"] = sni
} }
switch query.Get("packetEncoding") {
case "none":
case "packet":
proxy["packet-addr"] = true
default:
proxy["xudp"] = true
}
network := strings.ToLower(query.Get("type")) network := strings.ToLower(query.Get("type"))
if network == "" { if network == "" {
network = "tcp" network = "tcp"

View file

@ -2,6 +2,7 @@ package geodata
import ( import (
"fmt" "fmt"
"github.com/Dreamacro/clash/component/mmdb"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"io" "io"
@ -9,7 +10,8 @@ import (
"os" "os"
) )
var initFlag bool var initGeoSite bool
var initGeoIP int
func InitGeoSite() error { func InitGeoSite() error {
if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) { if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) {
@ -18,8 +20,9 @@ func InitGeoSite() error {
return fmt.Errorf("can't download GeoSite.dat: %s", err.Error()) return fmt.Errorf("can't download GeoSite.dat: %s", err.Error())
} }
log.Infoln("Download GeoSite.dat finish") log.Infoln("Download GeoSite.dat finish")
initGeoSite = false
} }
if !initFlag { if !initGeoSite {
if err := Verify(C.GeositeName); err != nil { if err := Verify(C.GeositeName); err != nil {
log.Warnln("GeoSite.dat invalid, remove and download: %s", err) log.Warnln("GeoSite.dat invalid, remove and download: %s", err)
if err := os.Remove(C.Path.GeoSite()); err != nil { if err := os.Remove(C.Path.GeoSite()); err != nil {
@ -29,7 +32,7 @@ func InitGeoSite() error {
return fmt.Errorf("can't download GeoSite.dat: %s", err.Error()) return fmt.Errorf("can't download GeoSite.dat: %s", err.Error())
} }
} }
initFlag = true initGeoSite = true
} }
return nil return nil
} }
@ -50,3 +53,68 @@ func downloadGeoSite(path string) (err error) {
return err return err
} }
func downloadGeoIP(path string) (err error) {
resp, err := http.Get(C.GeoIpUrl)
if err != nil {
return
}
defer resp.Body.Close()
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
return err
}
func InitGeoIP() error {
if C.GeodataMode {
if _, err := os.Stat(C.Path.GeoIP()); os.IsNotExist(err) {
log.Infoln("Can't find GeoIP.dat, start download")
if err := downloadGeoIP(C.Path.GeoIP()); err != nil {
return fmt.Errorf("can't download GeoIP.dat: %s", err.Error())
}
log.Infoln("Download GeoIP.dat finish")
initGeoIP = 0
}
if initGeoIP != 1 {
if err := Verify(C.GeoipName); err != nil {
log.Warnln("GeoIP.dat invalid, remove and download: %s", err)
if err := os.Remove(C.Path.GeoIP()); err != nil {
return fmt.Errorf("can't remove invalid GeoIP.dat: %s", err.Error())
}
if err := downloadGeoIP(C.Path.GeoIP()); err != nil {
return fmt.Errorf("can't download GeoIP.dat: %s", err.Error())
}
}
initGeoIP = 1
}
return nil
}
if _, err := os.Stat(C.Path.MMDB()); os.IsNotExist(err) {
log.Infoln("Can't find MMDB, start download")
if err := mmdb.DownloadMMDB(C.Path.MMDB()); err != nil {
return fmt.Errorf("can't download MMDB: %s", err.Error())
}
}
if initGeoIP != 2 {
if !mmdb.Verify() {
log.Warnln("MMDB invalid, remove and download")
if err := os.Remove(C.Path.MMDB()); err != nil {
return fmt.Errorf("can't remove invalid MMDB: %s", err.Error())
}
if err := mmdb.DownloadMMDB(C.Path.MMDB()); err != nil {
return fmt.Errorf("can't download MMDB: %s", err.Error())
}
}
initGeoIP = 2
}
return nil
}

View file

@ -2,6 +2,9 @@ package mmdb
import ( import (
"github.com/oschwald/geoip2-golang" "github.com/oschwald/geoip2-golang"
"io"
"net/http"
"os"
"sync" "sync"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
@ -42,3 +45,20 @@ func Instance() *geoip2.Reader {
return mmdb return mmdb
} }
func DownloadMMDB(path string) (err error) {
resp, err := http.Get(C.MmdbUrl)
if err != nil {
return
}
defer resp.Body.Close()
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
return err
}

View file

@ -0,0 +1,57 @@
package process
import (
"encoding/json"
"errors"
"strings"
)
const (
FindProcessAlways = "always"
FindProcessStrict = "strict"
FindProcessOff = "off"
)
var (
validModes = map[string]struct{}{
FindProcessAlways: {},
FindProcessOff: {},
FindProcessStrict: {},
}
)
type FindProcessMode string
func (m FindProcessMode) Always() bool {
return m == FindProcessAlways
}
func (m FindProcessMode) Off() bool {
return m == FindProcessOff
}
func (m *FindProcessMode) UnmarshalYAML(unmarshal func(any) error) error {
var tp string
if err := unmarshal(&tp); err != nil {
return err
}
return m.Set(tp)
}
func (m *FindProcessMode) UnmarshalJSON(data []byte) error {
var tp string
if err := json.Unmarshal(data, &tp); err != nil {
return err
}
return m.Set(tp)
}
func (m *FindProcessMode) Set(value string) error {
mode := strings.ToLower(value)
_, exist := validModes[mode]
if !exist {
return errors.New("invalid find process mode")
}
*m = FindProcessMode(mode)
return nil
}

View file

@ -4,6 +4,7 @@ import (
"container/list" "container/list"
"errors" "errors"
"fmt" "fmt"
P "github.com/Dreamacro/clash/component/process"
"net" "net"
"net/netip" "net/netip"
"net/url" "net/url"
@ -42,18 +43,19 @@ import (
type General struct { type General struct {
Inbound Inbound
Controller Controller
Mode T.TunnelMode `json:"mode"` Mode T.TunnelMode `json:"mode"`
UnifiedDelay bool UnifiedDelay bool
LogLevel log.LogLevel `json:"log-level"` LogLevel log.LogLevel `json:"log-level"`
IPv6 bool `json:"ipv6"` IPv6 bool `json:"ipv6"`
Interface string `json:"interface-name"` Interface string `json:"interface-name"`
RoutingMark int `json:"-"` RoutingMark int `json:"-"`
GeodataMode bool `json:"geodata-mode"` GeodataMode bool `json:"geodata-mode"`
GeodataLoader string `json:"geodata-loader"` GeodataLoader string `json:"geodata-loader"`
TCPConcurrent bool `json:"tcp-concurrent"` TCPConcurrent bool `json:"tcp-concurrent"`
EnableProcess bool `json:"enable-process"` EnableProcess bool `json:"enable-process"`
Sniffing bool `json:"sniffing"` FindProcessMode P.FindProcessMode `json:"find-process-mode"`
EBpf EBpf `json:"-"` Sniffing bool `json:"sniffing"`
EBpf EBpf `json:"-"`
} }
// Inbound config // Inbound config
@ -226,32 +228,33 @@ type RawTuicServer struct {
} }
type RawConfig struct { type RawConfig struct {
Port int `yaml:"port"` Port int `yaml:"port"`
SocksPort int `yaml:"socks-port"` SocksPort int `yaml:"socks-port"`
RedirPort int `yaml:"redir-port"` RedirPort int `yaml:"redir-port"`
TProxyPort int `yaml:"tproxy-port"` TProxyPort int `yaml:"tproxy-port"`
MixedPort int `yaml:"mixed-port"` MixedPort int `yaml:"mixed-port"`
ShadowSocksConfig string `yaml:"ss-config"` ShadowSocksConfig string `yaml:"ss-config"`
VmessConfig string `yaml:"vmess-config"` VmessConfig string `yaml:"vmess-config"`
InboundTfo bool `yaml:"inbound-tfo"` InboundTfo bool `yaml:"inbound-tfo"`
Authentication []string `yaml:"authentication"` Authentication []string `yaml:"authentication"`
AllowLan bool `yaml:"allow-lan"` AllowLan bool `yaml:"allow-lan"`
BindAddress string `yaml:"bind-address"` BindAddress string `yaml:"bind-address"`
Mode T.TunnelMode `yaml:"mode"` Mode T.TunnelMode `yaml:"mode"`
UnifiedDelay bool `yaml:"unified-delay"` UnifiedDelay bool `yaml:"unified-delay"`
LogLevel log.LogLevel `yaml:"log-level"` LogLevel log.LogLevel `yaml:"log-level"`
IPv6 bool `yaml:"ipv6"` IPv6 bool `yaml:"ipv6"`
ExternalController string `yaml:"external-controller"` ExternalController string `yaml:"external-controller"`
ExternalControllerTLS string `yaml:"external-controller-tls"` ExternalControllerTLS string `yaml:"external-controller-tls"`
ExternalUI string `yaml:"external-ui"` ExternalUI string `yaml:"external-ui"`
Secret string `yaml:"secret"` Secret string `yaml:"secret"`
Interface string `yaml:"interface-name"` Interface string `yaml:"interface-name"`
RoutingMark int `yaml:"routing-mark"` RoutingMark int `yaml:"routing-mark"`
Tunnels []LC.Tunnel `yaml:"tunnels"` Tunnels []LC.Tunnel `yaml:"tunnels"`
GeodataMode bool `yaml:"geodata-mode"` GeodataMode bool `yaml:"geodata-mode"`
GeodataLoader string `yaml:"geodata-loader"` GeodataLoader string `yaml:"geodata-loader"`
TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"`
EnableProcess bool `yaml:"enable-process" json:"enable-process"` EnableProcess bool `yaml:"enable-process" json:"enable-process"`
FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"`
Sniffer RawSniffer `yaml:"sniffer"` Sniffer RawSniffer `yaml:"sniffer"`
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
@ -314,21 +317,22 @@ func Parse(buf []byte) (*Config, error) {
func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
// config with default value // config with default value
rawCfg := &RawConfig{ rawCfg := &RawConfig{
AllowLan: false, AllowLan: false,
BindAddress: "*", BindAddress: "*",
IPv6: true, IPv6: true,
Mode: T.Rule, Mode: T.Rule,
GeodataMode: C.GeodataMode, GeodataMode: C.GeodataMode,
GeodataLoader: "memconservative", GeodataLoader: "memconservative",
UnifiedDelay: false, UnifiedDelay: false,
Authentication: []string{}, Authentication: []string{},
LogLevel: log.INFO, LogLevel: log.INFO,
Hosts: map[string]string{}, Hosts: map[string]string{},
Rule: []string{}, Rule: []string{},
Proxy: []map[string]any{}, Proxy: []map[string]any{},
ProxyGroup: []map[string]any{}, ProxyGroup: []map[string]any{},
TCPConcurrent: false, TCPConcurrent: false,
EnableProcess: false, EnableProcess: false,
FindProcessMode: P.FindProcessStrict,
Tun: RawTun{ Tun: RawTun{
Enable: false, Enable: false,
Device: "", Device: "",
@ -536,17 +540,18 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
Secret: cfg.Secret, Secret: cfg.Secret,
ExternalControllerTLS: cfg.ExternalControllerTLS, ExternalControllerTLS: cfg.ExternalControllerTLS,
}, },
UnifiedDelay: cfg.UnifiedDelay, UnifiedDelay: cfg.UnifiedDelay,
Mode: cfg.Mode, Mode: cfg.Mode,
LogLevel: cfg.LogLevel, LogLevel: cfg.LogLevel,
IPv6: cfg.IPv6, IPv6: cfg.IPv6,
Interface: cfg.Interface, Interface: cfg.Interface,
RoutingMark: cfg.RoutingMark, RoutingMark: cfg.RoutingMark,
GeodataMode: cfg.GeodataMode, GeodataMode: cfg.GeodataMode,
GeodataLoader: cfg.GeodataLoader, GeodataLoader: cfg.GeodataLoader,
TCPConcurrent: cfg.TCPConcurrent, TCPConcurrent: cfg.TCPConcurrent,
EnableProcess: cfg.EnableProcess, EnableProcess: cfg.EnableProcess,
EBpf: cfg.EBpf, FindProcessMode: cfg.FindProcessMode,
EBpf: cfg.EBpf,
}, nil }, nil
} }

View file

@ -3,92 +3,12 @@ package config
import ( import (
"fmt" "fmt"
"github.com/Dreamacro/clash/component/geodata" "github.com/Dreamacro/clash/component/geodata"
"github.com/Dreamacro/clash/component/mmdb"
"io"
"net/http"
"os" "os"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
) )
func downloadMMDB(path string) (err error) {
resp, err := http.Get(C.MmdbUrl)
if err != nil {
return
}
defer resp.Body.Close()
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
return err
}
func downloadGeoIP(path string) (err error) {
resp, err := http.Get(C.GeoIpUrl)
if err != nil {
return
}
defer resp.Body.Close()
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
return err
}
func initGeoIP() error {
if C.GeodataMode {
if _, err := os.Stat(C.Path.GeoIP()); os.IsNotExist(err) {
log.Infoln("Can't find GeoIP.dat, start download")
if err := downloadGeoIP(C.Path.GeoIP()); err != nil {
return fmt.Errorf("can't download GeoIP.dat: %s", err.Error())
}
log.Infoln("Download GeoIP.dat finish")
}
if err := geodata.Verify(C.GeoipName); err != nil {
log.Warnln("GeoIP.dat invalid, remove and download: %s", err)
if err := os.Remove(C.Path.GeoIP()); err != nil {
return fmt.Errorf("can't remove invalid GeoIP.dat: %s", err.Error())
}
if err := downloadGeoIP(C.Path.GeoIP()); err != nil {
return fmt.Errorf("can't download GeoIP.dat: %s", err.Error())
}
}
return nil
}
if _, err := os.Stat(C.Path.MMDB()); os.IsNotExist(err) {
log.Infoln("Can't find MMDB, start download")
if err := downloadMMDB(C.Path.MMDB()); err != nil {
return fmt.Errorf("can't download MMDB: %s", err.Error())
}
}
if !mmdb.Verify() {
log.Warnln("MMDB invalid, remove and download")
if err := os.Remove(C.Path.MMDB()); err != nil {
return fmt.Errorf("can't remove invalid MMDB: %s", err.Error())
}
if err := downloadMMDB(C.Path.MMDB()); err != nil {
return fmt.Errorf("can't download MMDB: %s", err.Error())
}
}
return nil
}
// Init prepare necessary files // Init prepare necessary files
func Init(dir string) error { func Init(dir string) error {
// initial homedir // initial homedir
@ -122,7 +42,7 @@ func Init(dir string) error {
C.GeoSiteUrl = rawCfg.GeoXUrl.GeoSite C.GeoSiteUrl = rawCfg.GeoXUrl.GeoSite
C.MmdbUrl = rawCfg.GeoXUrl.Mmdb C.MmdbUrl = rawCfg.GeoXUrl.Mmdb
// initial GeoIP // initial GeoIP
if err := initGeoIP(); err != nil { if err := geodata.InitGeoIP(); err != nil {
return fmt.Errorf("can't initial GeoIP: %w", err) return fmt.Errorf("can't initial GeoIP: %w", err)
} }

View file

@ -29,9 +29,11 @@ const (
func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) { func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) {
// skip dns cache for acme challenge // skip dns cache for acme challenge
if q := msg.Question[0]; q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge") { if len(msg.Question) != 0 {
log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name) if q := msg.Question[0]; q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge") {
return log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name)
return
}
} }
var ttl uint32 var ttl uint32
switch { switch {

View file

@ -258,6 +258,17 @@ proxies:
# headers: # headers:
# custom: value # custom: value
- name: "ss4"
type: ss
server: server
port: 443
cipher: chacha20-ietf-poly1305
password: "password"
plugin: shadow-tls
plugin-opts:
host: "cloud.tencent.com"
password: "shadow_tls_password"
# vmess # vmess
# cipher支持 auto/aes-128-gcm/chacha20-poly1305/none # cipher支持 auto/aes-128-gcm/chacha20-poly1305/none
- name: "vmess" - name: "vmess"

View file

@ -309,7 +309,7 @@ func updateTunnels(tunnels []LC.Tunnel) {
func updateGeneral(general *config.General, force bool) { func updateGeneral(general *config.General, force bool) {
tunnel.SetMode(general.Mode) tunnel.SetMode(general.Mode)
tunnel.SetAlwaysFindProcess(general.EnableProcess) tunnel.SetFindProcessMode(general.EnableProcess, general.FindProcessMode)
dialer.DisableIPv6 = !general.IPv6 dialer.DisableIPv6 = !general.IPv6
if !dialer.DisableIPv6 { if !dialer.DisableIPv6 {
log.Infoln("Use IPv6") log.Infoln("Use IPv6")

View file

@ -6,7 +6,6 @@ import (
var ( var (
errPayload = errors.New("payloadRule error") errPayload = errors.New("payloadRule error")
initFlag bool
noResolve = "no-resolve" noResolve = "no-resolve"
) )

View file

@ -71,6 +71,11 @@ func (g *GEOIP) GetRecodeSize() int {
} }
func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error) { func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error) {
if err := geodata.InitGeoIP(); err != nil {
log.Errorln("can't initial GeoIP: %s", err)
return nil, err
}
if !C.GeodataMode || strings.EqualFold(country, "LAN") { if !C.GeodataMode || strings.EqualFold(country, "LAN") {
geoip := &GEOIP{ geoip := &GEOIP{
Base: &Base{}, Base: &Base{},

View file

@ -50,12 +50,9 @@ func (gs *GEOSITE) GetRecodeSize() int {
} }
func NewGEOSITE(country string, adapter string) (*GEOSITE, error) { func NewGEOSITE(country string, adapter string) (*GEOSITE, error) {
if !initFlag { if err := geodata.InitGeoSite(); err != nil {
if err := geodata.InitGeoSite(); err != nil { log.Errorln("can't initial GeoSite: %s", err)
log.Errorln("can't initial GeoSite: %s", err) return nil, err
return nil, err
}
initFlag = true
} }
matcher, size, err := geodata.LoadGeoSiteMatcher(country) matcher, size, err := geodata.LoadGeoSiteMatcher(country)
@ -76,4 +73,4 @@ func NewGEOSITE(country string, adapter string) (*GEOSITE, error) {
return geoSite, nil return geoSite, nil
} }
//var _ C.Rule = (*GEOSITE)(nil) var _ C.Rule = (*GEOSITE)(nil)

View file

@ -8,10 +8,11 @@ import (
) )
type classicalStrategy struct { type classicalStrategy struct {
rules []C.Rule rules []C.Rule
count int count int
shouldResolveIP bool shouldResolveIP bool
parse func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) shouldFindProcess bool
parse func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error)
} }
func (c *classicalStrategy) Match(metadata *C.Metadata) bool { func (c *classicalStrategy) Match(metadata *C.Metadata) bool {
@ -32,6 +33,10 @@ func (c *classicalStrategy) ShouldResolveIP() bool {
return c.shouldResolveIP return c.shouldResolveIP
} }
func (c *classicalStrategy) ShouldFindProcess() bool {
return c.shouldFindProcess
}
func (c *classicalStrategy) OnUpdate(rules []string) { func (c *classicalStrategy) OnUpdate(rules []string) {
var classicalRules []C.Rule var classicalRules []C.Rule
shouldResolveIP := false shouldResolveIP := false
@ -45,6 +50,10 @@ func (c *classicalStrategy) OnUpdate(rules []string) {
shouldResolveIP = r.ShouldResolveIP() shouldResolveIP = r.ShouldResolveIP()
} }
if !c.shouldFindProcess {
c.shouldFindProcess = r.ShouldFindProcess()
}
classicalRules = append(classicalRules, r) classicalRules = append(classicalRules, r)
} }
} }

View file

@ -1,60 +0,0 @@
// Modified from: https://github.com/Qv2ray/gun-lite
// License: MIT
package gun
import (
"crypto/tls"
"fmt"
"net"
xtls "github.com/xtls/go"
"golang.org/x/net/http2"
)
func NewHTTP2XTLSClient(dialFn DialFn, tlsConfig *tls.Config) *TransportWrap {
wrap := TransportWrap{}
dialFunc := func(network, addr string, cfg *tls.Config) (net.Conn, error) {
pconn, err := dialFn(network, addr)
if err != nil {
return nil, err
}
wrap.remoteAddr = pconn.RemoteAddr()
xtlsConfig := &xtls.Config{
InsecureSkipVerify: cfg.InsecureSkipVerify,
ServerName: cfg.ServerName,
}
cn := xtls.Client(pconn, xtlsConfig)
if err := cn.Handshake(); err != nil {
pconn.Close()
return nil, err
}
state := cn.ConnectionState()
if p := state.NegotiatedProtocol; p != http2.NextProtoTLS {
cn.Close()
return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http2.NextProtoTLS)
}
return cn, nil
}
wrap.Transport = &http2.Transport{
DialTLS: dialFunc,
TLSClientConfig: tlsConfig,
AllowHTTP: false,
DisableCompression: true,
PingTimeout: 0,
}
return &wrap
}
func StreamGunWithXTLSConn(conn net.Conn, tlsConfig *tls.Config, cfg *Config) (net.Conn, error) {
dialFn := func(network, addr string) (net.Conn, error) {
return conn, nil
}
transport := NewHTTP2XTLSClient(dialFn, tlsConfig)
return StreamGunWithTransport(transport, cfg)
}

View file

@ -0,0 +1,152 @@
package shadowtls
import (
"context"
"crypto/hmac"
"crypto/sha1"
"crypto/tls"
"encoding/binary"
"fmt"
"hash"
"io"
"net"
"github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant"
)
const (
chunkSize = 1 << 13
Mode string = "shadow-tls"
hashLen int = 8
tlsHeaderLen int = 5
)
var (
DefaultALPN = []string{"h2", "http/1.1"}
)
// ShadowTLS is shadow-tls implementation
type ShadowTLS struct {
net.Conn
password []byte
remain int
firstRequest bool
tlsConfig *tls.Config
}
type HashedConn struct {
net.Conn
hasher hash.Hash
}
func newHashedStream(conn net.Conn, password []byte) HashedConn {
return HashedConn{
Conn: conn,
hasher: hmac.New(sha1.New, password),
}
}
func (h HashedConn) Read(b []byte) (n int, err error) {
n, err = h.Conn.Read(b)
h.hasher.Write(b[:n])
return
}
func (s *ShadowTLS) read(b []byte) (int, error) {
buf := pool.Get(tlsHeaderLen)
_, err := io.ReadFull(s.Conn, buf)
if err != nil {
return 0, fmt.Errorf("shadowtls read failed %w", err)
}
if buf[0] != 0x17 || buf[1] != 0x3 || buf[2] != 0x3 {
return 0, fmt.Errorf("invalid shadowtls header %v", buf)
}
length := int(binary.BigEndian.Uint16(buf[3:]))
pool.Put(buf)
if length > len(b) {
n, err := s.Conn.Read(b)
if err != nil {
return n, err
}
s.remain = length - n
return n, nil
}
return io.ReadFull(s.Conn, b[:length])
}
func (s *ShadowTLS) Read(b []byte) (int, error) {
if s.remain > 0 {
length := s.remain
if length > len(b) {
length = len(b)
}
n, err := io.ReadFull(s.Conn, b[:length])
if err != nil {
return n, fmt.Errorf("shadowtls Read failed with %w", err)
}
s.remain -= n
return n, nil
}
return s.read(b)
}
func (s *ShadowTLS) Write(b []byte) (int, error) {
length := len(b)
for i := 0; i < length; i += chunkSize {
end := i + chunkSize
if end > length {
end = length
}
n, err := s.write(b[i:end])
if err != nil {
return n, fmt.Errorf("shadowtls Write failed with %w, i=%d, end=%d, n=%d", err, i, end, n)
}
}
return length, nil
}
func (s *ShadowTLS) write(b []byte) (int, error) {
var hashVal []byte
if s.firstRequest {
hashedConn := newHashedStream(s.Conn, s.password)
tlsConn := tls.Client(hashedConn, s.tlsConfig)
// fix tls handshake not timeout
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
if err := tlsConn.HandshakeContext(ctx); err != nil {
return 0, fmt.Errorf("tls connect failed with %w", err)
}
hashVal = hashedConn.hasher.Sum(nil)[:hashLen]
s.firstRequest = false
}
buf := pool.GetBuffer()
defer pool.PutBuffer(buf)
buf.Write([]byte{0x17, 0x03, 0x03})
binary.Write(buf, binary.BigEndian, uint16(len(b)+len(hashVal)))
buf.Write(hashVal)
buf.Write(b)
_, err := s.Conn.Write(buf.Bytes())
if err != nil {
// return 0 because errors occur here make the
// whole situation irrecoverable
return 0, err
}
return len(b), nil
}
// NewShadowTLS return a ShadowTLS
func NewShadowTLS(conn net.Conn, password string, tlsConfig *tls.Config) net.Conn {
return &ShadowTLS{
Conn: conn,
password: []byte(password),
firstRequest: true,
tlsConfig: tlsConfig,
}
}

View file

@ -109,7 +109,12 @@ func (to *TLSObfs) write(b []byte) (int, error) {
binary.Write(buf, binary.BigEndian, uint16(len(b))) binary.Write(buf, binary.BigEndian, uint16(len(b)))
buf.Write(b) buf.Write(b)
_, err := to.Conn.Write(buf.Bytes()) _, err := to.Conn.Write(buf.Bytes())
return len(b), err if err != nil {
// return 0 because errors occur here make the
// whole situation irrecoverable
return 0, err
}
return len(b), nil
} }
// NewTLSObfs return a SimpleObfs // NewTLSObfs return a SimpleObfs

View file

@ -8,18 +8,17 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
tlsC "github.com/Dreamacro/clash/component/tls"
"io" "io"
"net" "net"
"net/http" "net/http"
"sync" "sync"
"github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/common/pool"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/socks5"
"github.com/Dreamacro/clash/transport/vless" "github.com/Dreamacro/clash/transport/vless"
"github.com/Dreamacro/clash/transport/vmess" "github.com/Dreamacro/clash/transport/vmess"
xtls "github.com/xtls/go" xtls "github.com/xtls/go"
) )
@ -117,9 +116,6 @@ func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) {
} }
tlsConn := tls.Client(conn, tlsConfig) tlsConn := tls.Client(conn, tlsConfig)
if err := tlsConn.Handshake(); err != nil {
return nil, err
}
// fix tls handshake not timeout // fix tls handshake not timeout
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel() defer cancel()

View file

@ -12,12 +12,14 @@ import (
"github.com/metacubex/quic-go/congestion" "github.com/metacubex/quic-go/congestion"
) )
const DefaultTCPMSS congestion.ByteCount = 1460 const (
const DefaultBBRMaxCongestionWindow congestion.ByteCount = 2000 * DefaultTCPMSS MaxDatagramSize = 1252
const InitialCongestionWindow congestion.ByteCount = 32 * DefaultTCPMSS DefaultBBRMaxCongestionWindow congestion.ByteCount = 2000 * MaxDatagramSize
const MinInitialPacketSize = 1200 InitialCongestionWindow congestion.ByteCount = 10 * MaxDatagramSize
const InitialPacketSizeIPv4 = 1252 MinInitialPacketSize = 1200
const InitialPacketSizeIPv6 = 1232 InitialPacketSizeIPv4 = 1252
InitialPacketSizeIPv6 = 1232
)
func GetMaxPacketSize(addr net.Addr) congestion.ByteCount { func GetMaxPacketSize(addr net.Addr) congestion.ByteCount {
maxSize := congestion.ByteCount(MinInitialPacketSize) maxSize := congestion.ByteCount(MinInitialPacketSize)
@ -33,17 +35,32 @@ func GetMaxPacketSize(addr net.Addr) congestion.ByteCount {
return maxSize return maxSize
} }
func GetMaxOutgoingPacketSize(addr net.Addr) congestion.ByteCount {
maxSize := congestion.ByteCount(MinInitialPacketSize)
// If this is not a UDP address, we don't know anything about the MTU.
// Use the minimum size of an Initial packet as the max packet size.
if udpAddr, ok := addr.(*net.UDPAddr); ok {
if udpAddr.IP.To4() != nil {
//The maximum packet size of any QUIC packet over IPv4. 1500(Ethernet) - 20(IPv4 header) - 8(UDP header) = 1472.
maxSize = congestion.ByteCount(1472)
} else {
// The maximum outgoing packet size allowed.
// The maximum packet size of any QUIC packet over IPv6, based on ethernet's max
// size, minus the IP and UDP headers. IPv6 has a 40 byte header, UDP adds an
// additional 8 bytes. This is a total overhead of 48 bytes. Ethernet's
// max packet size is 1500 bytes, 1500 - 48 = 1452.
maxSize = congestion.ByteCount(1452)
}
}
return maxSize
}
var ( var (
// The maximum outgoing packet size allowed.
// The maximum packet size of any QUIC packet over IPv6, based on ethernet's max
// size, minus the IP and UDP headers. IPv6 has a 40 byte header, UDP adds an
// additional 8 bytes. This is a total overhead of 48 bytes. Ethernet's
// max packet size is 1500 bytes, 1500 - 48 = 1452.
MaxOutgoingPacketSize = congestion.ByteCount(1452)
// Default maximum packet size used in the Linux TCP implementation. // Default maximum packet size used in the Linux TCP implementation.
// Used in QUIC for congestion window computations in bytes. // Used in QUIC for congestion window computations in bytes.
MaxSegmentSize = DefaultTCPMSS MaxSegmentSize = MaxDatagramSize
// Default initial rtt used before any samples are received. // Default initial rtt used before any samples are received.
InitialRtt = 100 * time.Millisecond InitialRtt = 100 * time.Millisecond
@ -51,10 +68,10 @@ var (
// Constants based on TCP defaults. // Constants based on TCP defaults.
// The minimum CWND to ensure delayed acks don't reduce bandwidth measurements. // The minimum CWND to ensure delayed acks don't reduce bandwidth measurements.
// Does not inflate the pacing rate. // Does not inflate the pacing rate.
DefaultMinimumCongestionWindow = 4 * DefaultTCPMSS DefaultMinimumCongestionWindow = 4 * MaxDatagramSize
// The gain used for the STARTUP, equal to 2/ln(2). // The gain used for the STARTUP, equal to 2/ln(2).
DefaultHighGain = 2.885 DefaultHighGain = 2.89
// The gain used in STARTUP after loss has been detected. // The gain used in STARTUP after loss has been detected.
// 1.5 is enough to allow for 25% exogenous loss and still observe a 25% growth // 1.5 is enough to allow for 25% exogenous loss and still observe a 25% growth
@ -249,11 +266,14 @@ type bbrSender struct {
// Latched value of --quic_always_get_bw_sample_when_acked. // Latched value of --quic_always_get_bw_sample_when_acked.
alwaysGetBwSampleWhenAcked bool alwaysGetBwSampleWhenAcked bool
pacer *pacer pacer *pacer
maxDatagramSize congestion.ByteCount maxDatagramSize congestion.ByteCount
MaxOutgoingPacketSize congestion.ByteCount
} }
func NewBBRSender(clock Clock, initialMaxDatagramSize, initialCongestionWindow, maxCongestionWindow congestion.ByteCount) *bbrSender { func NewBBRSender(clock Clock, initialMaxDatagramSize, initialCongestionWindow, initialMaxOutgoingPacketSize, maxCongestionWindow congestion.ByteCount) *bbrSender {
b := &bbrSender{ b := &bbrSender{
mode: STARTUP, mode: STARTUP,
clock: clock, clock: clock,
@ -263,7 +283,7 @@ func NewBBRSender(clock Clock, initialMaxDatagramSize, initialCongestionWindow,
congestionWindow: initialCongestionWindow, congestionWindow: initialCongestionWindow,
initialCongestionWindow: initialCongestionWindow, initialCongestionWindow: initialCongestionWindow,
maxCongestionWindow: maxCongestionWindow, maxCongestionWindow: maxCongestionWindow,
minCongestionWindow: DefaultMinimumCongestionWindow, minCongestionWindow: congestion.ByteCount(DefaultMinimumCongestionWindow),
highGain: DefaultHighGain, highGain: DefaultHighGain,
highCwndGain: DefaultHighGain, highCwndGain: DefaultHighGain,
drainGain: 1.0 / DefaultHighGain, drainGain: 1.0 / DefaultHighGain,
@ -274,6 +294,7 @@ func NewBBRSender(clock Clock, initialMaxDatagramSize, initialCongestionWindow,
recoveryState: NOT_IN_RECOVERY, recoveryState: NOT_IN_RECOVERY,
recoveryWindow: maxCongestionWindow, recoveryWindow: maxCongestionWindow,
minRttSinceLastProbeRtt: InfiniteRTT, minRttSinceLastProbeRtt: InfiniteRTT,
MaxOutgoingPacketSize: initialMaxOutgoingPacketSize,
maxDatagramSize: initialMaxDatagramSize, maxDatagramSize: initialMaxDatagramSize,
} }
b.pacer = newPacer(b.BandwidthEstimate) b.pacer = newPacer(b.BandwidthEstimate)
@ -790,7 +811,7 @@ func (b *bbrSender) MaybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRtt
// PROBE_RTT. The CWND during PROBE_RTT is kMinimumCongestionWindow, but // PROBE_RTT. The CWND during PROBE_RTT is kMinimumCongestionWindow, but
// we allow an extra packet since QUIC checks CWND before sending a // we allow an extra packet since QUIC checks CWND before sending a
// packet. // packet.
if b.GetBytesInFlight() < b.ProbeRttCongestionWindow()+MaxOutgoingPacketSize { if b.GetBytesInFlight() < b.ProbeRttCongestionWindow()+b.MaxOutgoingPacketSize {
b.exitProbeRttAt = now.Add(ProbeRttTime) b.exitProbeRttAt = now.Add(ProbeRttTime)
b.probeRttRoundPassed = false b.probeRttRoundPassed = false
} }
@ -924,7 +945,7 @@ func (b *bbrSender) CalculateRecoveryWindow(ackedBytes, lostBytes congestion.Byt
if b.recoveryWindow >= lostBytes { if b.recoveryWindow >= lostBytes {
b.recoveryWindow -= lostBytes b.recoveryWindow -= lostBytes
} else { } else {
b.recoveryWindow = MaxSegmentSize b.recoveryWindow = congestion.ByteCount(MaxSegmentSize)
} }
// In CONSERVATION mode, just subtracting losses is sufficient. In GROWTH, // In CONSERVATION mode, just subtracting losses is sufficient. In GROWTH,
// release additional |bytes_acked| to achieve a slow-start-like behavior. // release additional |bytes_acked| to achieve a slow-start-like behavior.

View file

@ -45,6 +45,7 @@ func SetCongestionController(quicConn quic.Connection, cc string) {
congestion.NewBBRSender( congestion.NewBBRSender(
congestion.DefaultClock{}, congestion.DefaultClock{},
congestion.GetMaxPacketSize(quicConn.RemoteAddr()), congestion.GetMaxPacketSize(quicConn.RemoteAddr()),
congestion.GetMaxOutgoingPacketSize(quicConn.RemoteAddr()),
congestion.InitialCongestionWindow, congestion.InitialCongestionWindow,
congestion.DefaultBBRMaxCongestionWindow, congestion.DefaultBBRMaxCongestionWindow,
), ),

View file

@ -51,17 +51,20 @@ func (vc *Conn) sendRequest() error {
buf.WriteByte(0) // addon data length. 0 means no addon data buf.WriteByte(0) // addon data length. 0 means no addon data
} }
// command if vc.dst.Mux {
if vc.dst.UDP { buf.WriteByte(CommandMux)
buf.WriteByte(CommandUDP)
} else { } else {
buf.WriteByte(CommandTCP) if vc.dst.UDP {
} buf.WriteByte(CommandUDP)
} else {
buf.WriteByte(CommandTCP)
}
// Port AddrType Addr // Port AddrType Addr
binary.Write(buf, binary.BigEndian, uint16(vc.dst.Port)) binary.Write(buf, binary.BigEndian, vc.dst.Port)
buf.WriteByte(vc.dst.AddrType) buf.WriteByte(vc.dst.AddrType)
buf.Write(vc.dst.Addr) buf.Write(vc.dst.Addr)
}
_, err := vc.Conn.Write(buf.Bytes()) _, err := vc.Conn.Write(buf.Bytes())
return err return err

View file

@ -1,9 +1,10 @@
package vless package vless
import ( import (
"github.com/Dreamacro/clash/common/utils"
"net" "net"
"github.com/Dreamacro/clash/common/utils"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
) )
@ -19,6 +20,7 @@ const (
const ( const (
CommandTCP byte = 1 CommandTCP byte = 1
CommandUDP byte = 2 CommandUDP byte = 2
CommandMux byte = 3
) )
// Addr types // Addr types
@ -33,7 +35,8 @@ type DstAddr struct {
UDP bool UDP bool
AddrType byte AddrType byte
Addr []byte Addr []byte
Port uint Port uint16
Mux bool // currently used for XUDP only
} }
// Client is vless connection generator // Client is vless connection generator

View file

@ -12,7 +12,7 @@ import (
type XTLSConfig struct { type XTLSConfig struct {
Host string Host string
SkipCertVerify bool SkipCertVerify bool
FingerPrint string Fingerprint string
NextProtos []string NextProtos []string
} }
@ -22,11 +22,11 @@ func StreamXTLSConn(conn net.Conn, cfg *XTLSConfig) (net.Conn, error) {
InsecureSkipVerify: cfg.SkipCertVerify, InsecureSkipVerify: cfg.SkipCertVerify,
NextProtos: cfg.NextProtos, NextProtos: cfg.NextProtos,
} }
if len(cfg.FingerPrint) == 0 { if len(cfg.Fingerprint) == 0 {
xtlsConfig = tlsC.GetGlobalFingerprintXTLCConfig(xtlsConfig) xtlsConfig = tlsC.GetGlobalFingerprintXTLCConfig(xtlsConfig)
} else { } else {
var err error var err error
if xtlsConfig, err = tlsC.GetSpecifiedFingerprintXTLSConfig(xtlsConfig, cfg.FingerPrint); err != nil { if xtlsConfig, err = tlsC.GetSpecifiedFingerprintXTLSConfig(xtlsConfig, cfg.Fingerprint); err != nil {
return nil, err return nil, err
} }
} }

View file

@ -43,7 +43,7 @@ var (
// default timeout for UDP session // default timeout for UDP session
udpTimeout = 60 * time.Second udpTimeout = 60 * time.Second
alwaysFindProcess = false findProcessMode P.FindProcessMode
fakeIPRange netip.Prefix fakeIPRange netip.Prefix
) )
@ -146,9 +146,14 @@ func SetMode(m TunnelMode) {
mode = m mode = m
} }
// SetAlwaysFindProcess set always find process info, may be increase many memory // SetFindProcessMode replace SetAlwaysFindProcess
func SetAlwaysFindProcess(findProcess bool) { // always find process info if legacyAlways = true or mode.Always() = true, may be increase many memory
alwaysFindProcess = findProcess func SetFindProcessMode(legacyAlways bool, mode P.FindProcessMode) {
if legacyAlways {
findProcessMode = P.FindProcessAlways
} else {
findProcessMode = mode
}
} }
// processUDP starts a loop to handle udp packet // processUDP starts a loop to handle udp packet
@ -463,7 +468,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
}() }()
} }
if !processFound && (alwaysFindProcess || rule.ShouldFindProcess()) { if !findProcessMode.Off() && !processFound && (findProcessMode.Always() || rule.ShouldFindProcess()) {
srcPort, err := strconv.ParseUint(metadata.SrcPort, 10, 16) srcPort, err := strconv.ParseUint(metadata.SrcPort, 10, 16)
uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(srcPort)) uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(srcPort))
if err != nil { if err != nil {