From e2c75161b17ef4a71557e92916f64bedcda1e60e Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Sat, 30 Apr 2022 11:36:42 +0800 Subject: [PATCH] support udp in relay if last proxy could udp-over-tcp --- adapter/outbound/base.go | 10 ++++ adapter/outbound/snell.go | 11 ++++ adapter/outbound/trojan.go | 10 ++++ adapter/outbound/vless.go | 10 ++++ adapter/outbound/vmess.go | 10 ++++ adapter/outboundgroup/relay.go | 103 +++++++++++++++++++++++++++++---- constant/adapters.go | 4 ++ 7 files changed, 147 insertions(+), 11 deletions(-) diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index 6b5c61f6..167bac2a 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -42,6 +42,16 @@ func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, op return nil, errors.New("no support") } +// ListenPacketOnStreamConn implements C.ProxyAdapter +func (b *Base) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { + return nil, errors.New("no support") +} + +// SupportUOT implements C.ProxyAdapter +func (b *Base) SupportUOT() bool { + return false +} + // SupportUDP implements C.ProxyAdapter func (b *Base) SupportUDP() bool { return b.udp diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index 07f3d89d..b54d328f 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -95,6 +95,12 @@ func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o tcpKeepAlive(c) c = streamConn(c, streamOption{s.psk, s.version, s.addr, s.obfsOption}) + return s.ListenPacketOnStreamConn(c, metadata) +} + +// ListenPacketOnStreamConn implements C.ProxyAdapter +func (s *Snell) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { + err = snell.WriteUDPHeader(c, s.version) if err != nil { return nil, err @@ -104,6 +110,11 @@ func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return newPacketConn(pc, s), nil } +// SupportUOT implements C.ProxyAdapter +func (s *Snell) SupportUOT() bool { + return true +} + func NewSnell(option SnellOption) (*Snell, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) psk := []byte(option.Psk) diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index aa389b34..65f3cbea 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -157,6 +157,11 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, } } + return t.ListenPacketOnStreamConn(c, metadata) +} + +// ListenPacketOnStreamConn implements C.ProxyAdapter +func (t *Trojan) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) if err != nil { return nil, err @@ -166,6 +171,11 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return newPacketConn(pc, t), err } +// SupportUOT implements C.ProxyAdapter +func (t *Trojan) SupportUOT() bool { + return true +} + func NewTrojan(option TrojanOption) (*Trojan, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 1cea9ab5..8ac78b92 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -249,9 +249,19 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return nil, fmt.Errorf("new vless client error: %v", err) } + return v.ListenPacketOnStreamConn(c, metadata) +} + +// ListenPacketOnStreamConn implements C.ProxyAdapter +func (v *Vless) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil } +// SupportUOT implements C.ProxyAdapter +func (v *Vless) SupportUOT() bool { + return true +} + func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr { var addrType byte var addr []byte diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index be3def96..bf0ac7a8 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -260,9 +260,19 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return nil, fmt.Errorf("new vmess client error: %v", err) } + return v.ListenPacketOnStreamConn(c, metadata) +} + +// ListenPacketOnStreamConn implements C.ProxyAdapter +func (v *Vmess) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { return newPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil } +// SupportUOT implements C.ProxyAdapter +func (v *Vmess) SupportUOT() bool { + return true +} + func NewVmess(option VmessOption) (*Vmess, error) { security := strings.ToLower(option.Cipher) client, err := vmess.NewClient(vmess.Config{ diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index 14f5a546..40df8e44 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -17,12 +17,7 @@ type Relay struct { // DialContext implements C.ProxyAdapter func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - var proxies []C.Proxy - for _, proxy := range r.proxies(metadata, true) { - if proxy.Type() != C.Direct && proxy.Type() != C.Compatible { - proxies = append(proxies, proxy) - } - } + proxies, chainProxies := r.proxies(metadata, true) switch len(proxies) { case 0: @@ -60,7 +55,80 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return nil, fmt.Errorf("%s connect error: %w", last.Addr(), err) } - return outbound.NewConn(c, r), nil + conn := outbound.NewConn(c, last) + + for i := len(chainProxies) - 2; i >= 0; i-- { + conn.AppendToChains(chainProxies[i]) + } + + conn.AppendToChains(r) + + return conn, nil +} + +// ListenPacketContext implements C.ProxyAdapter +func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { + proxies, chainProxies := r.proxies(metadata, true) + + switch len(proxies) { + case 0: + return outbound.NewDirect().ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...) + case 1: + return proxies[0].ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...) + } + + first := proxies[0] + last := proxies[len(proxies)-1] + + c, err := dialer.DialContext(ctx, "tcp", first.Addr(), r.Base.DialOptions(opts...)...) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) + } + tcpKeepAlive(c) + + var currentMeta *C.Metadata + for _, proxy := range proxies[1:] { + currentMeta, err = addrToMetadata(proxy.Addr()) + if err != nil { + return nil, err + } + + c, err = first.StreamConn(c, currentMeta) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) + } + + first = proxy + } + + c, err = last.StreamConn(c, metadata) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", last.Addr(), err) + } + + var pc C.PacketConn + pc, err = last.ListenPacketOnStreamConn(c, metadata) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) + } + + for i := len(chainProxies) - 2; i >= 0; i-- { + pc.AppendToChains(chainProxies[i]) + } + + pc.AppendToChains(r) + + return pc, nil +} + +// SupportUDP implements C.ProxyAdapter +func (r *Relay) SupportUDP() bool { + proxies, _ := r.proxies(nil, false) + if len(proxies) == 0 { // C.Direct + return true + } + last := proxies[len(proxies)-1] + return last.SupportUDP() && last.SupportUOT() } // MarshalJSON implements C.ProxyAdapter @@ -75,18 +143,31 @@ func (r *Relay) MarshalJSON() ([]byte, error) { }) } -func (r *Relay) proxies(metadata *C.Metadata, touch bool) []C.Proxy { - proxies := r.GetProxies(touch) +func (r *Relay) proxies(metadata *C.Metadata, touch bool) ([]C.Proxy, []C.Proxy) { + rawProxies := r.GetProxies(touch) - for n, proxy := range proxies { + var proxies []C.Proxy + var chainProxies []C.Proxy + var targetProxies []C.Proxy + + for n, proxy := range rawProxies { + proxies = append(proxies, proxy) + chainProxies = append(chainProxies, proxy) subproxy := proxy.Unwrap(metadata) for subproxy != nil { + chainProxies = append(chainProxies, subproxy) proxies[n] = subproxy subproxy = subproxy.Unwrap(metadata) } } - return proxies + for _, proxy := range proxies { + if proxy.Type() != C.Direct && proxy.Type() != C.Compatible { + targetProxies = append(targetProxies, proxy) + } + } + + return targetProxies, chainProxies } func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay { diff --git a/constant/adapters.go b/constant/adapters.go index b91704de..b2339c5d 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -99,6 +99,10 @@ type ProxyAdapter interface { DialContext(ctx context.Context, metadata *Metadata, opts ...dialer.Option) (Conn, error) ListenPacketContext(ctx context.Context, metadata *Metadata, opts ...dialer.Option) (PacketConn, error) + // SupportUOT return UDP over TCP support + SupportUOT() bool + ListenPacketOnStreamConn(c net.Conn, metadata *Metadata) (PacketConn, error) + // Unwrap extracts the proxy from a proxy-group. It returns nil when nothing to extract. Unwrap(metadata *Metadata) Proxy }