diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 5f53edae..4d25e9d2 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -19,6 +19,7 @@ import ( "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/bufio" M "github.com/sagernet/sing/common/metadata" + "github.com/sagernet/sing/common/uot" ) func init() { @@ -29,6 +30,7 @@ type ShadowSocks struct { *Base method shadowsocks.Method + option *ShadowSocksOption // obfs obfsMode string obfsOption *simpleObfsOption @@ -45,6 +47,7 @@ type ShadowSocksOption struct { UDP bool `proxy:"udp,omitempty"` Plugin string `proxy:"plugin,omitempty"` PluginOpts map[string]any `proxy:"plugin-opts,omitempty"` + UDPOverTCP bool `proxy:"udp-over-tcp,omitempty"` } type simpleObfsOption struct { @@ -77,6 +80,10 @@ func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } } + if metadata.NetWork == C.UDP && ss.option.UDPOverTCP { + metadata.Host = uot.UOTMagicAddress + metadata.DstPort = "443" + } return ss.method.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) } @@ -96,6 +103,13 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, op // ListenPacketContext implements C.ProxyAdapter func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { + if ss.option.UDPOverTCP { + tcpConn, err := ss.DialContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + return newPacketConn(uot.NewClientConn(tcpConn), ss), nil + } pc, err := dialer.ListenPacket(ctx, "udp", "", ss.Base.DialOptions(opts...)...) if err != nil { return nil, err @@ -110,6 +124,19 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta return newPacketConn(pc, ss), nil } +// ListenPacketOnStreamConn implements C.ProxyAdapter +func (ss *ShadowSocks) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { + if ss.option.UDPOverTCP { + return newPacketConn(uot.NewClientConn(c), ss), nil + } + return nil, errors.New("no support") +} + +// SupportUOT implements C.ProxyAdapter +func (ss *ShadowSocks) SupportUOT() bool { + return ss.option.UDPOverTCP +} + func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) method, err := shadowimpl.FetchMethod(option.Cipher, option.Password) @@ -167,6 +194,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { }, method: method, + option: &option, obfsMode: obfsMode, v2rayOption: v2rayOption, obfsOption: obfsOption, diff --git a/adapter/provider/fetcher.go b/adapter/provider/fetcher.go index 3fcda09a..47b364e6 100644 --- a/adapter/provider/fetcher.go +++ b/adapter/provider/fetcher.go @@ -40,9 +40,10 @@ func (f *fetcher[V]) VehicleType() types.VehicleType { func (f *fetcher[V]) Initial() (V, error) { var ( - buf []byte - err error - isLocal bool + buf []byte + err error + isLocal bool + forceUpdate bool ) if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil { @@ -51,10 +52,8 @@ func (f *fetcher[V]) Initial() (V, error) { f.updatedAt = &modTime isLocal = true if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) { - defer func() { - log.Infoln("[Provider] %s's proxies not updated for a long time, force refresh", f.Name()) - go f.Update() - }() + log.Infoln("[Provider] %s not updated for a long time, force refresh", f.Name()) + forceUpdate = true } } else { buf, err = f.vehicle.Read() @@ -64,7 +63,21 @@ func (f *fetcher[V]) Initial() (V, error) { return getZero[V](), err } - proxies, err := f.parser(buf) + var proxies V + if forceUpdate { + var forceBuf []byte + if forceBuf, err = f.vehicle.Read(); err == nil { + if proxies, err = f.parser(forceBuf); err == nil { + isLocal = false + buf = forceBuf + } + } + } + + if err != nil || !forceUpdate { + proxies, err = f.parser(buf) + } + if err != nil { if !isLocal { return getZero[V](), err diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index bd1b262b..d2c036d5 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -173,7 +173,7 @@ func ResolveAllIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) { ip, err := netip.ParseAddr(host) if err == nil { - if ip.Is4() { + if ip.Is4() || ip.Is4In6() { return []netip.Addr{ip}, nil } return []netip.Addr{}, ErrIPVersion diff --git a/go.mod b/go.mod index de4528e8..624989ba 100644 --- a/go.mod +++ b/go.mod @@ -13,9 +13,9 @@ require ( github.com/lucas-clemente/quic-go v0.27.2 github.com/miekg/dns v1.1.49 github.com/oschwald/geoip2-golang v1.7.0 - github.com/sagernet/sing v0.0.0-20220615170357-aa30c4140ca9 - github.com/sagernet/sing-shadowsocks v0.0.0-20220615081955-91a0901d1c89 - github.com/sagernet/sing-vmess v0.0.0-20220614092239-0052a5a383f5 + github.com/sagernet/sing v0.0.0-20220619130320-8793fe5e067d + github.com/sagernet/sing-shadowsocks v0.0.0-20220619134218-830a2f478eb1 + github.com/sagernet/sing-vmess v0.0.0-20220616051646-3d3fc5d01eec github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.2 github.com/tobyxdd/hysteria v1.0.4 @@ -28,7 +28,7 @@ require ( golang.org/x/exp v0.0.0-20220608143224-64259d1afd70 golang.org/x/net v0.0.0-20220607020251-c690dde0001d golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f - golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d + golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c golang.org/x/time v0.0.0-20220411224347-583f2d630306 golang.zx2c4.com/wireguard v0.0.0-20220601130007-6a08d81f6bc4 golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e diff --git a/go.sum b/go.sum index 36867fd5..41764462 100644 --- a/go.sum +++ b/go.sum @@ -305,12 +305,12 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/sagernet/sing v0.0.0-20220615170357-aa30c4140ca9 h1:jsrgPBWIMRBk71zKtEpVLJ10AqSQO+K2psplU8Wrouo= -github.com/sagernet/sing v0.0.0-20220615170357-aa30c4140ca9/go.mod h1:Bgwxr10oTxYlQ33MgsXW3GuS2w5St11qqk4DqzJOdU4= -github.com/sagernet/sing-shadowsocks v0.0.0-20220615081955-91a0901d1c89 h1:l7TGNYSm4H2nZfvp2iOlCxvaGZO0tSts+o7+YJE8M2I= -github.com/sagernet/sing-shadowsocks v0.0.0-20220615081955-91a0901d1c89/go.mod h1:r3xDib+IPJH/cT1DBzez3O3me4HpGLbzdsMzqwyGX6U= -github.com/sagernet/sing-vmess v0.0.0-20220614092239-0052a5a383f5 h1:mTeGXZ/mNvUgiEPQ0hhMtqCdkPQlOjmgdl40TQNQnLQ= -github.com/sagernet/sing-vmess v0.0.0-20220614092239-0052a5a383f5/go.mod h1:LHcJcRDCJnAeI7tlMmVqeJapURFJr7HQfDrBxkylu/g= +github.com/sagernet/sing v0.0.0-20220619130320-8793fe5e067d h1:zr8y4wmNIxv6Kkvgqysx8Piy82ATAThEj1jaEf23YQs= +github.com/sagernet/sing v0.0.0-20220619130320-8793fe5e067d/go.mod h1:I67R/q5f67xDExL2kL3RLIP7kGJBOPkYXkpRAykgC+E= +github.com/sagernet/sing-shadowsocks v0.0.0-20220619134218-830a2f478eb1 h1:3GEdnWbuSX4XwSKnxLUB/1rMXUxSVKeyRhEeT7k7N1Q= +github.com/sagernet/sing-shadowsocks v0.0.0-20220619134218-830a2f478eb1/go.mod h1:xk8Hh1hQiTeiY6jHCQjaxxN8M6B94JoGaNx5q61naE8= +github.com/sagernet/sing-vmess v0.0.0-20220616051646-3d3fc5d01eec h1:jUSfKmyL6K9O2TvIvcVacZ4eNXHYbNSfdph+DRPyVlU= +github.com/sagernet/sing-vmess v0.0.0-20220616051646-3d3fc5d01eec/go.mod h1:jDZ8fJgOea7Y7MMHWgfqwLBVLnhtW2zuxS5wjtDaB84= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= @@ -566,8 +566,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/hub/route/configs.go b/hub/route/configs.go index f87fa5ce..82f65284 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -46,6 +46,7 @@ type configSchema struct { IPv6 *bool `json:"ipv6"` Sniffing *bool `json:"sniffing"` TcpConcurrent *bool `json:"tcp-concurrent"` + InterfaceName *string `json:"interface-name"` } func getConfigs(w http.ResponseWriter, r *http.Request) { @@ -85,6 +86,10 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) { dialer.SetDial(*general.TcpConcurrent) } + if general.InterfaceName != nil { + dialer.DefaultInterface.Store(*general.InterfaceName) + } + ports := P.GetPorts() tcpIn := tunnel.TCPIn() diff --git a/rules/common/domain.go b/rules/common/domain.go index f52f1f23..5638afe1 100644 --- a/rules/common/domain.go +++ b/rules/common/domain.go @@ -1,6 +1,7 @@ package common import ( + "golang.org/x/net/idna" "strings" C "github.com/Dreamacro/clash/constant" @@ -8,8 +9,9 @@ import ( type Domain struct { *Base - domain string - adapter string + domain string + rawDomain string + adapter string } func (d *Domain) RuleType() C.RuleType { @@ -28,14 +30,16 @@ func (d *Domain) Adapter() string { } func (d *Domain) Payload() string { - return d.domain + return d.rawDomain } func NewDomain(domain string, adapter string) *Domain { + actualDomain, _ := idna.ToASCII(domain) return &Domain{ - Base: &Base{}, - domain: strings.ToLower(domain), - adapter: adapter, + Base: &Base{}, + domain: strings.ToLower(actualDomain), + adapter: adapter, + rawDomain: domain, } } diff --git a/rules/common/domain_keyword.go b/rules/common/domain_keyword.go index ea9f5703..c13dcdbf 100644 --- a/rules/common/domain_keyword.go +++ b/rules/common/domain_keyword.go @@ -1,6 +1,7 @@ package common import ( + "golang.org/x/net/idna" "strings" C "github.com/Dreamacro/clash/constant" @@ -8,8 +9,9 @@ import ( type DomainKeyword struct { *Base - keyword string - adapter string + keyword string + adapter string + rawKeyword string } func (dk *DomainKeyword) RuleType() C.RuleType { @@ -29,14 +31,16 @@ func (dk *DomainKeyword) Adapter() string { } func (dk *DomainKeyword) Payload() string { - return dk.keyword + return dk.rawKeyword } func NewDomainKeyword(keyword string, adapter string) *DomainKeyword { + actualDomainKeyword, _ := idna.ToASCII(keyword) return &DomainKeyword{ - Base: &Base{}, - keyword: strings.ToLower(keyword), - adapter: adapter, + Base: &Base{}, + keyword: strings.ToLower(actualDomainKeyword), + adapter: adapter, + rawKeyword: keyword, } } diff --git a/rules/common/domain_suffix.go b/rules/common/domain_suffix.go index dd350983..278052f2 100644 --- a/rules/common/domain_suffix.go +++ b/rules/common/domain_suffix.go @@ -1,6 +1,7 @@ package common import ( + "golang.org/x/net/idna" "strings" C "github.com/Dreamacro/clash/constant" @@ -8,8 +9,9 @@ import ( type DomainSuffix struct { *Base - suffix string - adapter string + suffix string + adapter string + rawSuffix string } func (ds *DomainSuffix) RuleType() C.RuleType { @@ -29,14 +31,16 @@ func (ds *DomainSuffix) Adapter() string { } func (ds *DomainSuffix) Payload() string { - return ds.suffix + return ds.rawSuffix } func NewDomainSuffix(suffix string, adapter string) *DomainSuffix { + actualDomainKeyword, _ := idna.ToASCII(suffix) return &DomainSuffix{ - Base: &Base{}, - suffix: strings.ToLower(suffix), - adapter: adapter, + Base: &Base{}, + suffix: strings.ToLower(actualDomainKeyword), + adapter: adapter, + rawSuffix: suffix, } } diff --git a/rules/provider/classical_strategy.go b/rules/provider/classical_strategy.go index 2fc52090..430acead 100644 --- a/rules/provider/classical_strategy.go +++ b/rules/provider/classical_strategy.go @@ -4,6 +4,7 @@ import ( "fmt" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" + "strings" ) type classicalStrategy struct { @@ -52,6 +53,19 @@ func (c *classicalStrategy) OnUpdate(rules []string) { c.count = len(classicalRules) } +func ruleParse(ruleRaw string) (string, string, []string) { + item := strings.Split(ruleRaw, ",") + if len(item) == 1 { + return "", item[0], nil + } else if len(item) == 2 { + return item[0], item[1], nil + } else if len(item) > 2 { + return item[0], item[1], item[2:] + } + + return "", "", nil +} + func NewClassicalStrategy(parse func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error)) *classicalStrategy { return &classicalStrategy{rules: []C.Rule{}, parse: func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) { switch tp { diff --git a/rules/provider/domain_strategy.go b/rules/provider/domain_strategy.go index d78bb44c..a6145383 100644 --- a/rules/provider/domain_strategy.go +++ b/rules/provider/domain_strategy.go @@ -4,7 +4,7 @@ import ( "github.com/Dreamacro/clash/component/trie" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" - "strings" + "golang.org/x/net/idna" ) type domainStrategy struct { @@ -28,7 +28,8 @@ func (d *domainStrategy) OnUpdate(rules []string) { domainTrie := trie.New[bool]() count := 0 for _, rule := range rules { - err := domainTrie.Insert(rule, true) + actualDomain, _ := idna.ToASCII(rule) + err := domainTrie.Insert(actualDomain, true) if err != nil { log.Warnln("invalid domain:[%s]", rule) } else { @@ -40,19 +41,6 @@ func (d *domainStrategy) OnUpdate(rules []string) { d.count = count } -func ruleParse(ruleRaw string) (string, string, []string) { - item := strings.Split(ruleRaw, ",") - if len(item) == 1 { - return "", item[0], nil - } else if len(item) == 2 { - return item[0], item[1], nil - } else if len(item) > 2 { - return item[0], item[1], item[2:] - } - - return "", "", nil -} - func NewDomainStrategy() *domainStrategy { return &domainStrategy{} } diff --git a/rules/provider/fetcher.go b/rules/provider/fetcher.go index 89dfd5db..b542a8e2 100644 --- a/rules/provider/fetcher.go +++ b/rules/provider/fetcher.go @@ -40,9 +40,10 @@ func (f *fetcher) VehicleType() P.VehicleType { func (f *fetcher) Initial() (interface{}, error) { var ( - buf []byte - hasLocal bool - err error + buf []byte + hasLocal bool + err error + forceUpdate bool ) defer func() { @@ -57,10 +58,8 @@ func (f *fetcher) Initial() (interface{}, error) { f.updatedAt = &modTime hasLocal = true if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) { - defer func() { - log.Infoln("[Provider] %s's rules not updated for a long time, force refresh", f.Name()) - go f.update() - }() + forceUpdate = true + log.Infoln("[Provider] %s not updated for a long time, force refresh", f.Name()) } } else { buf, err = f.vehicle.Read() @@ -70,7 +69,21 @@ func (f *fetcher) Initial() (interface{}, error) { return nil, err } - rules, err := f.parser(buf) + var rules interface{} + if forceUpdate { + var forceBuf []byte + if forceBuf, err = f.vehicle.Read(); err == nil { + if rules, err = f.parser(forceBuf); err == nil { + hasLocal = false + buf = forceBuf + } + } + } + + if err != nil || !forceUpdate { + rules, err = f.parser(buf) + } + if err != nil { if !hasLocal { return nil, err diff --git a/test/config/xray-shadowsocks.json b/test/config/xray-shadowsocks.json new file mode 100644 index 00000000..1df376dc --- /dev/null +++ b/test/config/xray-shadowsocks.json @@ -0,0 +1,27 @@ +{ + "inbounds": [ + { + "port": 10002, + "listen": "0.0.0.0", + "protocol": "shadowsocks", + "settings": { + "network": "tcp,udp", + "clients": [ + { + "method": "aes-128-gcm", + "level": 0, + "password": "FzcLbKs2dY9mhL" + } + ] + } + } + ], + "outbounds": [ + { + "protocol": "freedom" + } + ], + "log": { + "loglevel": "debug" + } +} \ No newline at end of file diff --git a/test/go.mod b/test/go.mod index 1046817e..f1d4896b 100644 --- a/test/go.mod +++ b/test/go.mod @@ -61,9 +61,9 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/sagernet/sing v0.0.0-20220614034114-7caa1d0c0851 // indirect - github.com/sagernet/sing-shadowsocks v0.0.0-20220610074818-432dcbdb1d7c // indirect - github.com/sagernet/sing-vmess v0.0.0-20220614042419-6f7c1431421a // indirect + github.com/sagernet/sing v0.0.0-20220619130320-8793fe5e067d // indirect + github.com/sagernet/sing-shadowsocks v0.0.0-20220619134218-830a2f478eb1 // indirect + github.com/sagernet/sing-vmess v0.0.0-20220616051646-3d3fc5d01eec // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/tobyxdd/hysteria v1.0.4 // indirect github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect @@ -79,7 +79,7 @@ require ( golang.org/x/exp v0.0.0-20220608143224-64259d1afd70 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect - golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect + golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect golang.org/x/tools v0.1.10 // indirect diff --git a/test/go.sum b/test/go.sum index 15e2096f..f306a73e 100644 --- a/test/go.sum +++ b/test/go.sum @@ -323,12 +323,12 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/sagernet/sing v0.0.0-20220614034114-7caa1d0c0851 h1:Pp+9IPHtlwxrkiKMSrTOPS/eg+qJLdfZoeM89QX40BI= -github.com/sagernet/sing v0.0.0-20220614034114-7caa1d0c0851/go.mod h1:ZEo7wZBfJmzm8uwnbCtWEHw9GsIfSThSylZYcR+H/Zw= -github.com/sagernet/sing-shadowsocks v0.0.0-20220610074818-432dcbdb1d7c h1:dapkcUcFbOwqnBwut6Dct7L695PVS6GoEqJeSRMWe0k= -github.com/sagernet/sing-shadowsocks v0.0.0-20220610074818-432dcbdb1d7c/go.mod h1:ty1OoG/SgB6IccsWUS5DZBkEGKUKM8nRSWxLlqYUPx0= -github.com/sagernet/sing-vmess v0.0.0-20220614042419-6f7c1431421a h1:vEJtwy2Ysw9ftf+5TALig2ZiRFf1pDnWGxhwRqDs+Cs= -github.com/sagernet/sing-vmess v0.0.0-20220614042419-6f7c1431421a/go.mod h1:KZxqBTXh1v6q7TnVm36y+oCzPQLQKgv0z/TeBuozb2s= +github.com/sagernet/sing v0.0.0-20220619130320-8793fe5e067d h1:zr8y4wmNIxv6Kkvgqysx8Piy82ATAThEj1jaEf23YQs= +github.com/sagernet/sing v0.0.0-20220619130320-8793fe5e067d/go.mod h1:I67R/q5f67xDExL2kL3RLIP7kGJBOPkYXkpRAykgC+E= +github.com/sagernet/sing-shadowsocks v0.0.0-20220619134218-830a2f478eb1 h1:3GEdnWbuSX4XwSKnxLUB/1rMXUxSVKeyRhEeT7k7N1Q= +github.com/sagernet/sing-shadowsocks v0.0.0-20220619134218-830a2f478eb1/go.mod h1:xk8Hh1hQiTeiY6jHCQjaxxN8M6B94JoGaNx5q61naE8= +github.com/sagernet/sing-vmess v0.0.0-20220616051646-3d3fc5d01eec h1:jUSfKmyL6K9O2TvIvcVacZ4eNXHYbNSfdph+DRPyVlU= +github.com/sagernet/sing-vmess v0.0.0-20220616051646-3d3fc5d01eec/go.mod h1:jDZ8fJgOea7Y7MMHWgfqwLBVLnhtW2zuxS5wjtDaB84= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= @@ -586,8 +586,8 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0= -golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/test/ss_test.go b/test/ss_test.go index dec98ed4..bec1734b 100644 --- a/test/ss_test.go +++ b/test/ss_test.go @@ -3,11 +3,13 @@ package main import ( "crypto/rand" "encoding/base64" + "fmt" "net" "testing" "time" "github.com/Dreamacro/clash/adapter/outbound" + C "github.com/Dreamacro/clash/constant" "github.com/docker/docker/api/types/container" "github.com/stretchr/testify/require" ) @@ -277,3 +279,37 @@ func Benchmark_Shadowsocks(b *testing.B) { require.True(b, TCPing(net.JoinHostPort(localIP.String(), "10002"))) benchmarkProxy(b, proxy) } + +func TestClash_ShadowsocksUoT(t *testing.T) { + configPath := C.Path.Resolve("xray-shadowsocks.json") + + cfg := &container.Config{ + Image: ImageVless, + ExposedPorts: defaultExposedPorts, + } + hostCfg := &container.HostConfig{ + PortBindings: defaultPortBindings, + Binds: []string{fmt.Sprintf("%s:/etc/xray/config.json", configPath)}, + } + + id, err := startContainer(cfg, hostCfg, "xray-ss") + require.NoError(t, err) + + t.Cleanup(func() { + cleanContainer(id) + }) + + proxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{ + Name: "ss", + Server: localIP.String(), + Port: 10002, + Password: "FzcLbKs2dY9mhL", + Cipher: "aes-128-gcm", + UDP: true, + UDPOverTCP: true, + }) + require.NoError(t, err) + + time.Sleep(waitTime) + testSuit(t, proxy) +}