From e4a95aade06883d075e236ccae0f74a11f7cbd48 Mon Sep 17 00:00:00 2001 From: mzz <2017@duck.com> Date: Sat, 29 Jul 2023 23:41:40 +0800 Subject: [PATCH] feat: support juicity (#248) * feat: support juicity * optimize(juicity): lower reserved streams * fix(juicity): support to detach from client pool * docs: add docs * fix(trojan): udp * fix: panic if tuic and juice are both used --------- Co-authored-by: dae-bot[bot] <136105375+dae-bot[bot]@users.noreply.github.com> --- component/outbound/dialer/juicity/juicity.go | 133 +++++++++++++++++++ component/outbound/dialer/tuic/tuic.go | 2 +- component/outbound/outbound.go | 2 + docs/en/proxy-protocols.md | 1 + docs/zh/proxy-protocols.md | 3 + example.dae | 2 +- go.mod | 2 +- go.sum | 4 +- 8 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 component/outbound/dialer/juicity/juicity.go diff --git a/component/outbound/dialer/juicity/juicity.go b/component/outbound/dialer/juicity/juicity.go new file mode 100644 index 0000000..f01d926 --- /dev/null +++ b/component/outbound/dialer/juicity/juicity.go @@ -0,0 +1,133 @@ +package juicity + +import ( + "crypto/tls" + "fmt" + "net" + "net/url" + "strconv" + + "github.com/daeuniverse/dae/common" + "github.com/daeuniverse/dae/component/outbound/dialer" + "github.com/mzz2017/softwind/netproxy" + "github.com/mzz2017/softwind/protocol" +) + +func init() { + dialer.FromLinkRegister("juicity", NewJuice) +} + +type Juice struct { + Name string + Server string + Port int + User string + Password string + Sni string + AllowInsecure bool + CongestionControl string + Protocol string +} + +func NewJuice(option *dialer.GlobalOption, nextDialer netproxy.Dialer, link string) (netproxy.Dialer, *dialer.Property, error) { + s, err := ParseJuiceURL(link) + if err != nil { + return nil, nil, err + } + return s.Dialer(option, nextDialer) +} + +func (s *Juice) Dialer(option *dialer.GlobalOption, nextDialer netproxy.Dialer) (netproxy.Dialer, *dialer.Property, error) { + d := nextDialer + var err error + var flags protocol.Flags + if d, err = protocol.NewDialer("juicity", d, protocol.Header{ + ProxyAddress: net.JoinHostPort(s.Server, strconv.Itoa(s.Port)), + Feature1: s.CongestionControl, + TlsConfig: &tls.Config{ + NextProtos: []string{"h3"}, + MinVersion: tls.VersionTLS13, + ServerName: s.Sni, + InsecureSkipVerify: s.AllowInsecure || option.AllowInsecure, + }, + User: s.User, + Password: s.Password, + IsClient: true, + Flags: flags, + }); err != nil { + return nil, nil, err + } + return d, &dialer.Property{ + Name: s.Name, + Address: net.JoinHostPort(s.Server, strconv.Itoa(s.Port)), + Protocol: s.Protocol, + Link: s.ExportToURL(), + }, nil +} + +func ParseJuiceURL(u string) (data *Juice, err error) { + //trojan://password@server:port#escape(remarks) + t, err := url.Parse(u) + if err != nil { + err = fmt.Errorf("invalid trojan format") + return + } + allowInsecure, _ := strconv.ParseBool(t.Query().Get("allowInsecure")) + if !allowInsecure { + allowInsecure, _ = strconv.ParseBool(t.Query().Get("allow_insecure")) + } + if !allowInsecure { + allowInsecure, _ = strconv.ParseBool(t.Query().Get("allowinsecure")) + } + if !allowInsecure { + allowInsecure, _ = strconv.ParseBool(t.Query().Get("skipVerify")) + } + sni := t.Query().Get("peer") + if sni == "" { + sni = t.Query().Get("sni") + } + if sni == "" { + sni = t.Hostname() + } + disableSni, _ := strconv.ParseBool(t.Query().Get("disable_sni")) + if disableSni { + sni = "" + allowInsecure = true + } + port, err := strconv.Atoi(t.Port()) + if err != nil { + return nil, dialer.InvalidParameterErr + } + password, _ := t.User.Password() + data = &Juice{ + Name: t.Fragment, + Server: t.Hostname(), + Port: port, + User: t.User.Username(), + Password: password, + Sni: sni, + AllowInsecure: allowInsecure, + CongestionControl: t.Query().Get("congestion_control"), + Protocol: "juicity", + } + return data, nil +} + +func (t *Juice) ExportToURL() string { + u := &url.URL{ + Scheme: "juicity", + User: url.UserPassword(t.User, t.Password), + Host: net.JoinHostPort(t.Server, strconv.Itoa(t.Port)), + Fragment: t.Name, + } + q := u.Query() + if t.AllowInsecure { + q.Set("allow_insecure", "1") + } + common.SetValue(&q, "sni", t.Sni) + if t.CongestionControl != "" { + common.SetValue(&q, "congestion_control", t.CongestionControl) + } + u.RawQuery = q.Encode() + return u.String() +} diff --git a/component/outbound/dialer/tuic/tuic.go b/component/outbound/dialer/tuic/tuic.go index 1abde02..4b372a8 100644 --- a/component/outbound/dialer/tuic/tuic.go +++ b/component/outbound/dialer/tuic/tuic.go @@ -132,7 +132,7 @@ func ParseTuicURL(u string) (data *Tuic, err error) { func (t *Tuic) ExportToURL() string { u := &url.URL{ - Scheme: "trojan", + Scheme: "tuic", User: url.UserPassword(t.User, t.Password), Host: net.JoinHostPort(t.Server, strconv.Itoa(t.Port)), Fragment: t.Name, diff --git a/component/outbound/outbound.go b/component/outbound/outbound.go index 8a09e2c..11c081f 100644 --- a/component/outbound/outbound.go +++ b/component/outbound/outbound.go @@ -7,6 +7,7 @@ package outbound import ( _ "github.com/daeuniverse/dae/component/outbound/dialer/http" + _ "github.com/daeuniverse/dae/component/outbound/dialer/juicity" _ "github.com/daeuniverse/dae/component/outbound/dialer/shadowsocks" _ "github.com/daeuniverse/dae/component/outbound/dialer/shadowsocksr" _ "github.com/daeuniverse/dae/component/outbound/dialer/socks" @@ -16,6 +17,7 @@ import ( _ "github.com/daeuniverse/dae/component/outbound/transport/simpleobfs" _ "github.com/daeuniverse/dae/component/outbound/transport/tls" _ "github.com/daeuniverse/dae/component/outbound/transport/ws" + _ "github.com/mzz2017/softwind/protocol/juicity" _ "github.com/mzz2017/softwind/protocol/shadowsocks" _ "github.com/mzz2017/softwind/protocol/trojanc" _ "github.com/mzz2017/softwind/protocol/tuic" diff --git a/docs/en/proxy-protocols.md b/docs/en/proxy-protocols.md index a8ea4ed..00bed32 100644 --- a/docs/en/proxy-protocols.md +++ b/docs/en/proxy-protocols.md @@ -23,6 +23,7 @@ - [x] Trojan-gfw - [x] Trojan-go - [x] [Tuic (v5)](https://github.com/daeuniverse/dae/discussions/182) +- [x] [Juicity](https://github.com/juicity/juicity) - [x] [Proxy chain (flexible protocol)](https://github.com/daeuniverse/dae/discussions/236) For other requirements, one way to expand protocol support is by using external proxy programs. Below is an example of using the external naiveproxy. diff --git a/docs/zh/proxy-protocols.md b/docs/zh/proxy-protocols.md index ee19ac9..20d03a5 100644 --- a/docs/zh/proxy-protocols.md +++ b/docs/zh/proxy-protocols.md @@ -22,6 +22,9 @@ - [x] Trojan-gfw - [x] Trojan-go - [x] Tuic (v5) +- [x] [Tuic (v5)](https://github.com/daeuniverse/dae/discussions/182) +- [x] [Juicity](https://github.com/juicity/juicity) +- [x] [Proxy chain (flexible protocol)](https://github.com/daeuniverse/dae/discussions/236) 有其他需求的,一种方式是通过外接其他代理程序来扩展协议支持。下面给出外接 naiveproxy 的例子。 diff --git a/example.dae b/example.dae index 861024a..5d3b777 100644 --- a/example.dae +++ b/example.dae @@ -107,7 +107,7 @@ subscription { # Nodes defined here will be merged as a part of the global node pool. node { # Add your node links here. - # Support socks5, http, https, ss, ssr, vmess, vless, trojan, tuic, etc. + # Support socks5, http, https, ss, ssr, vmess, vless, trojan, tuic, juicity, etc. # Full support list: https://github.com/daeuniverse/dae/blob/main/docs/en/proxy-protocols.md 'socks5://localhost:1080' mylink: 'ss://LINK' diff --git a/go.mod b/go.mod index 6a4a0e3..5048070 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/miekg/dns v1.1.55 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 - github.com/mzz2017/softwind v0.0.0-20230723115304-666ec11098b8 + github.com/mzz2017/softwind v0.0.0-20230729115435-7bfb07d4e1f6 github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd github.com/safchain/ethtool v0.3.0 github.com/sirupsen/logrus v1.9.3 diff --git a/go.sum b/go.sum index ab89806..78574f0 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/mzz2017/disk-bloom v1.0.1 h1:rEF9MiXd9qMW3ibRpqcerLXULoTgRlM21yqqJl1B github.com/mzz2017/disk-bloom v1.0.1/go.mod h1:JLHETtUu44Z6iBmsqzkOtFlRvXSlKnxjwiBRDapizDI= github.com/mzz2017/quic-go v0.0.0-20230706143320-cc858d4932b7 h1:9zmZilN02x3byMB2X3x+B4iyKHkucv70WA4hsyZkjo8= github.com/mzz2017/quic-go v0.0.0-20230706143320-cc858d4932b7/go.mod h1:3H6d55CEofIWWr3gQThiB27+hA3WG5tATtPovzEYPAA= -github.com/mzz2017/softwind v0.0.0-20230723115304-666ec11098b8 h1:T/VA4g5qkPeZFvZ+gkx4SK9wijDSOgFwumEZAxdHZ8U= -github.com/mzz2017/softwind v0.0.0-20230723115304-666ec11098b8/go.mod h1:Fz8fgR7/dbnfR6RLpeOMkUDyebq4xShdmjj+cE5jnJ4= +github.com/mzz2017/softwind v0.0.0-20230729115435-7bfb07d4e1f6 h1:9xuiuwjy5nMtWBUmcGDnGhEzobUnLEG+0T2A4RRnCPE= +github.com/mzz2017/softwind v0.0.0-20230729115435-7bfb07d4e1f6/go.mod h1:Fz8fgR7/dbnfR6RLpeOMkUDyebq4xShdmjj+cE5jnJ4= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=