chore: support relay native udp when using ss and ssr protocol
This commit is contained in:
parent
ff01d845b4
commit
c63dd62ed2
@ -62,6 +62,16 @@ func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, op
|
|||||||
return nil, errors.New("no support")
|
return nil, errors.New("no support")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListenPacketOnPacketConn implements C.ProxyAdapter
|
||||||
|
func (b *Base) ListenPacketOnPacketConn(ctx context.Context, c C.PacketConn, metadata *C.Metadata) (C.PacketConn, error) {
|
||||||
|
return nil, errors.New("no support")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportLPPC implements C.ProxyAdapter
|
||||||
|
func (b *Base) SupportLPPC() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
||||||
func (b *Base) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (b *Base) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
return nil, errors.New("no support")
|
return nil, errors.New("no support")
|
||||||
|
@ -120,6 +120,22 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta
|
|||||||
return newPacketConn(pc, ss), nil
|
return newPacketConn(pc, ss), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListenPacketOnPacketConn implements C.ProxyAdapter
|
||||||
|
func (ss *ShadowSocks) ListenPacketOnPacketConn(ctx context.Context, c C.PacketConn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pc := ss.method.DialPacketConn(&bufio.BindPacketConn{PacketConn: c, Addr: addr})
|
||||||
|
return newPacketConn(pc, ss), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportLPPC implements C.ProxyAdapter
|
||||||
|
func (ss *ShadowSocks) SupportLPPC() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
||||||
func (ss *ShadowSocks) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (ss *ShadowSocks) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
if ss.option.UDPOverTCP {
|
if ss.option.UDPOverTCP {
|
||||||
|
@ -91,6 +91,23 @@ func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Me
|
|||||||
return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil
|
return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListenPacketOnPacketConn implements C.ProxyAdapter
|
||||||
|
func (ssr *ShadowSocksR) ListenPacketOnPacketConn(ctx context.Context, c C.PacketConn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pc := ssr.cipher.PacketConn(c)
|
||||||
|
pc = ssr.protocol.PacketConn(pc)
|
||||||
|
return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportLPPC implements C.ProxyAdapter
|
||||||
|
func (ssr *ShadowSocksR) SupportLPPC() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
|
func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
|
||||||
// SSR protocol compatibility
|
// SSR protocol compatibility
|
||||||
// https://github.com/Dreamacro/clash/pull/2056
|
// https://github.com/Dreamacro/clash/pull/2056
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
@ -29,14 +30,23 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
|
|||||||
first := proxies[0]
|
first := proxies[0]
|
||||||
last := proxies[len(proxies)-1]
|
last := proxies[len(proxies)-1]
|
||||||
|
|
||||||
c, err := dialer.DialContext(ctx, "tcp", first.Addr(), r.Base.DialOptions(opts...)...)
|
var c net.Conn
|
||||||
|
var currentMeta *C.Metadata
|
||||||
|
var err error
|
||||||
|
|
||||||
|
currentMeta, err = addrToMetadata(proxies[1].Addr())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = first.DialContext(ctx, currentMeta, r.Base.DialOptions(opts...)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err)
|
return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err)
|
||||||
}
|
}
|
||||||
tcpKeepAlive(c)
|
|
||||||
|
|
||||||
var currentMeta *C.Metadata
|
first = proxies[1]
|
||||||
for _, proxy := range proxies[1:] {
|
|
||||||
|
for _, proxy := range proxies[2:] {
|
||||||
currentMeta, err = addrToMetadata(proxy.Addr())
|
currentMeta, err = addrToMetadata(proxy.Addr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -77,23 +87,85 @@ func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||||||
return proxies[0].ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...)
|
return proxies[0].ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
udtId := -1
|
||||||
|
for i, proxy := range proxies {
|
||||||
|
if !proxy.SupportUDP() {
|
||||||
|
return nil, fmt.Errorf("%s don't support udp", proxy.Name())
|
||||||
|
}
|
||||||
|
if proxy.SupportUOT() {
|
||||||
|
udtId = i // we need the latest id, so don't break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
first := proxies[0]
|
first := proxies[0]
|
||||||
last := proxies[len(proxies)-1]
|
last := proxies[len(proxies)-1]
|
||||||
|
|
||||||
c, err := dialer.DialContext(ctx, "tcp", first.Addr(), r.Base.DialOptions(opts...)...)
|
var pc C.PacketConn
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err)
|
|
||||||
}
|
|
||||||
tcpKeepAlive(c)
|
|
||||||
|
|
||||||
var currentMeta *C.Metadata
|
var currentMeta *C.Metadata
|
||||||
for _, proxy := range proxies[1:] {
|
if udtId != -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)
|
||||||
|
|
||||||
|
for _, proxy := range proxies[1 : udtId+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
|
||||||
|
}
|
||||||
|
|
||||||
|
if first == last {
|
||||||
|
currentMeta = metadata
|
||||||
|
} else {
|
||||||
|
currentMeta, err = addrToMetadata(proxies[udtId+1].Addr())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
currentMeta.NetWork = C.UDP
|
||||||
|
}
|
||||||
|
c, err = first.StreamConn(c, currentMeta)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pc, err = first.ListenPacketOnStreamConn(c, currentMeta)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err)
|
||||||
|
}
|
||||||
|
if first == last {
|
||||||
|
return pc, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentMeta, err = addrToMetadata(proxies[1].Addr())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
currentMeta.NetWork = C.UDP
|
||||||
|
pc, err = first.ListenPacketContext(ctx, currentMeta, r.Base.DialOptions(opts...)...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err)
|
||||||
|
}
|
||||||
|
udtId = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
first = proxies[udtId+1]
|
||||||
|
for _, proxy := range proxies[udtId+2:] {
|
||||||
currentMeta, err = addrToMetadata(proxy.Addr())
|
currentMeta, err = addrToMetadata(proxy.Addr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
currentMeta.NetWork = C.UDP
|
||||||
|
|
||||||
c, err = first.StreamConn(c, currentMeta)
|
pc, err = first.ListenPacketOnPacketConn(ctx, pc, currentMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err)
|
return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err)
|
||||||
}
|
}
|
||||||
@ -101,17 +173,11 @@ func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||||||
first = proxy
|
first = proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err = last.StreamConn(c, metadata)
|
pc, err = last.ListenPacketOnPacketConn(ctx, pc, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", last.Addr(), err)
|
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-- {
|
for i := len(chainProxies) - 2; i >= 0; i-- {
|
||||||
pc.AppendToChains(chainProxies[i])
|
pc.AppendToChains(chainProxies[i])
|
||||||
}
|
}
|
||||||
@ -127,8 +193,19 @@ func (r *Relay) SupportUDP() bool {
|
|||||||
if len(proxies) == 0 { // C.Direct
|
if len(proxies) == 0 { // C.Direct
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
last := proxies[len(proxies)-1]
|
for i := len(proxies) - 1; i >= 0; i-- {
|
||||||
return last.SupportUDP() && last.SupportUOT()
|
proxy := proxies[i]
|
||||||
|
if !proxy.SupportUDP() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if proxy.SupportUOT() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !proxy.SupportLPPC() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON implements C.ProxyAdapter
|
// MarshalJSON implements C.ProxyAdapter
|
||||||
|
@ -108,6 +108,9 @@ type ProxyAdapter interface {
|
|||||||
SupportUOT() bool
|
SupportUOT() bool
|
||||||
ListenPacketOnStreamConn(c net.Conn, metadata *Metadata) (PacketConn, error)
|
ListenPacketOnStreamConn(c net.Conn, metadata *Metadata) (PacketConn, error)
|
||||||
|
|
||||||
|
SupportLPPC() bool
|
||||||
|
ListenPacketOnPacketConn(ctx context.Context, c PacketConn, metadata *Metadata) (PacketConn, error)
|
||||||
|
|
||||||
// Unwrap extracts the proxy from a proxy-group. It returns nil when nothing to extract.
|
// Unwrap extracts the proxy from a proxy-group. It returns nil when nothing to extract.
|
||||||
Unwrap(metadata *Metadata, touch bool) Proxy
|
Unwrap(metadata *Metadata, touch bool) Proxy
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user