github.com/yaling888/clash@v1.53.0/adapter/outboundgroup/relay.go (about) 1 package outboundgroup 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "net" 8 "time" 9 10 "github.com/yaling888/clash/adapter" 11 "github.com/yaling888/clash/adapter/outbound" 12 "github.com/yaling888/clash/common/singledo" 13 "github.com/yaling888/clash/component/dialer" 14 "github.com/yaling888/clash/component/resolver" 15 C "github.com/yaling888/clash/constant" 16 "github.com/yaling888/clash/constant/provider" 17 "github.com/yaling888/clash/tunnel" 18 ) 19 20 var _ C.ProxyAdapter = (*Relay)(nil) 21 22 type Relay struct { 23 *outbound.Base 24 disableDNS bool 25 single *singledo.Single[[]C.Proxy] 26 providers []provider.ProxyProvider 27 } 28 29 // DialContext implements C.ProxyAdapter 30 func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { 31 var proxies []C.Proxy 32 for _, proxy := range r.proxies(metadata, true) { 33 if proxy.Type() != C.Direct { 34 proxies = append(proxies, proxy) 35 } 36 } 37 38 length := len(proxies) 39 40 switch length { 41 case 0: 42 return outbound.NewDirect().DialContext(ctx, metadata, r.Base.DialOptions(opts...)...) 43 case 1: 44 return proxies[0].DialContext(ctx, metadata, r.Base.DialOptions(opts...)...) 45 } 46 47 timeout := time.Duration(length) * C.DefaultTCPTimeout 48 subCtx, cancel := context.WithTimeout(ctx, timeout) 49 defer cancel() 50 ctx = subCtx 51 52 c, err := r.streamContext(ctx, proxies, r.Base.DialOptions(opts...)...) 53 if err != nil { 54 return nil, err 55 } 56 57 last := proxies[len(proxies)-1] 58 c, err = last.StreamConn(c, metadata) 59 if err != nil { 60 return nil, fmt.Errorf("%s connect error: %w", last.Addr(), err) 61 } 62 63 return outbound.NewConn(c, r), nil 64 } 65 66 // ListenPacketContext implements C.ProxyAdapter 67 func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { 68 var proxies []C.Proxy 69 for _, proxy := range r.proxies(metadata, true) { 70 if proxy.Type() != C.Direct { 71 proxies = append(proxies, proxy) 72 } 73 } 74 75 length := len(proxies) 76 77 switch length { 78 case 0: 79 return outbound.NewDirect().ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...) 80 case 1: 81 proxy := proxies[0] 82 if !proxy.SupportUDP() { 83 return nil, fmt.Errorf("%s connect error: proxy [%s] UDP is not supported", proxy.Addr(), proxy.Name()) 84 } 85 return proxy.ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...) 86 } 87 88 var ( 89 firstIndex = 0 90 nextIndex = 1 91 lastUDPOverTCPIndex int 92 rawUDPRelay bool 93 94 first = proxies[firstIndex] 95 last = proxies[length-1] 96 97 c net.Conn 98 cc net.Conn 99 err error 100 ) 101 102 if !supportPacketConn(last) { 103 return nil, fmt.Errorf( 104 "%s connect error: proxy [%s] UDP is not supported in relay chains", last.Addr(), last.Name(), 105 ) 106 } 107 108 timeout := time.Duration(length) * C.DefaultTCPTimeout 109 subCtx, cancel := context.WithTimeout(ctx, timeout) 110 defer cancel() 111 ctx = subCtx 112 113 rawUDPRelay, lastUDPOverTCPIndex = isRawUDPRelay(proxies) 114 115 if first.Type() == C.Socks5 { 116 cc1, err1 := dialer.DialContext(ctx, "tcp", first.Addr(), r.Base.DialOptions(opts...)...) 117 if err1 != nil { 118 return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) 119 } 120 cc = cc1 121 tcpKeepAlive(cc) 122 123 var pc net.PacketConn 124 pc, err = dialer.ListenPacket(ctx, "udp", "", r.Base.DialOptions(opts...)...) 125 c = outbound.WrapConn(pc) 126 } else if rawUDPRelay { 127 var pc net.PacketConn 128 pc, err = dialer.ListenPacket(ctx, "udp", "", r.Base.DialOptions(opts...)...) 129 c = outbound.WrapConn(pc) 130 } else { 131 firstIndex = lastUDPOverTCPIndex 132 nextIndex = firstIndex + 1 133 first = proxies[firstIndex] 134 c, err = r.streamContext(ctx, proxies[:nextIndex], r.Base.DialOptions(opts...)...) 135 } 136 137 if err != nil { 138 return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) 139 } 140 141 if nextIndex < length { 142 var currentMeta *C.Metadata 143 for i, proxy := range proxies[nextIndex:] { // raw udp in loop 144 currentMeta, err = addrToMetadata(proxy.Addr()) 145 if err != nil { 146 return nil, err 147 } 148 currentMeta.NetWork = C.UDP 149 150 if !supportPacketConn(first) { 151 return nil, fmt.Errorf( 152 "%s connect error: proxy [%s] UDP is not supported in relay chains", first.Addr(), first.Name(), 153 ) 154 } 155 156 err = resolveDNS(currentMeta) 157 if err != nil { 158 return nil, fmt.Errorf("can't resolve ip: %w", err) 159 } 160 161 if cc != nil { // socks5 162 c, err = streamSocks5PacketConn(first, cc, c, currentMeta) 163 cc = nil 164 } else { 165 c, err = first.StreamPacketConn(c, currentMeta) 166 } 167 168 if err != nil { 169 return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) 170 } 171 172 if proxy.Type() == C.Socks5 { 173 endIndex := nextIndex + i + 1 174 cc, err = r.streamContext(ctx, proxies[:endIndex], r.Base.DialOptions(opts...)...) 175 if err != nil { 176 return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) 177 } 178 } 179 180 first = proxy 181 } 182 } 183 184 if cc != nil { 185 c, err = streamSocks5PacketConn(last, cc, c, metadata) 186 } else { 187 c, err = last.StreamPacketConn(c, metadata) 188 } 189 190 if err != nil { 191 return nil, fmt.Errorf("%s connect error: %w", last.Addr(), err) 192 } 193 194 return outbound.NewPacketConn(c.(net.PacketConn), r), nil 195 } 196 197 // SupportUDP implements C.ProxyAdapter 198 func (r *Relay) SupportUDP() bool { 199 proxies := r.rawProxies(true) 200 201 l := len(proxies) 202 203 if l == 0 { 204 return true 205 } 206 207 last := proxies[l-1] 208 209 return isRawUDP(last) || last.SupportUDP() 210 } 211 212 // DisableDnsResolve implements C.DisableDnsResolve 213 func (r *Relay) DisableDnsResolve() bool { 214 return r.disableDNS 215 } 216 217 // MarshalJSON implements C.ProxyAdapter 218 func (r *Relay) MarshalJSON() ([]byte, error) { 219 var all []string 220 for _, proxy := range r.rawProxies(false) { 221 all = append(all, proxy.Name()) 222 } 223 return json.Marshal(map[string]any{ 224 "type": r.Type().String(), 225 "all": all, 226 }) 227 } 228 229 // Cleanup implements C.ProxyAdapter 230 func (r *Relay) Cleanup() { 231 r.single.Reset() 232 } 233 234 func (r *Relay) rawProxies(touch bool) []C.Proxy { 235 elm, _, _ := r.single.Do(func() ([]C.Proxy, error) { 236 return getProvidersProxies(r.providers, touch), nil 237 }) 238 239 return elm 240 } 241 242 func (r *Relay) proxies(metadata *C.Metadata, touch bool) []C.Proxy { 243 proxies := r.rawProxies(touch) 244 245 for n, proxy := range proxies { 246 subProxy := proxy.Unwrap(metadata) 247 for subProxy != nil { 248 proxies[n] = subProxy 249 subProxy = subProxy.Unwrap(metadata) 250 } 251 } 252 253 return proxies 254 } 255 256 func (r *Relay) streamContext(ctx context.Context, proxies []C.Proxy, opts ...dialer.Option) (net.Conn, error) { 257 first := proxies[0] 258 259 c, err := dialer.DialContext(ctx, "tcp", first.Addr(), opts...) 260 if err != nil { 261 return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) 262 } 263 tcpKeepAlive(c) 264 265 if len(proxies) > 1 { 266 var currentMeta *C.Metadata 267 for _, proxy := range proxies[1:] { 268 currentMeta, err = addrToMetadata(proxy.Addr()) 269 if err != nil { 270 return nil, err 271 } 272 273 c, err = first.StreamConn(c, currentMeta) 274 if err != nil { 275 return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) 276 } 277 278 first = proxy 279 } 280 } 281 282 return c, nil 283 } 284 285 func streamSocks5PacketConn(proxy C.Proxy, cc, c net.Conn, metadata *C.Metadata) (net.Conn, error) { 286 pc, err := proxy.(*adapter.Proxy).ProxyAdapter.(*outbound.Socks5). 287 StreamSocks5PacketConn(cc, c.(net.PacketConn), metadata) 288 return outbound.WrapConn(pc), err 289 } 290 291 func isRawUDPRelay(proxies []C.Proxy) (bool, int) { 292 var ( 293 lastIndex = len(proxies) - 1 294 last = proxies[lastIndex] 295 isLastRawUDP = isRawUDP(last) 296 isUDPOverTCP = false 297 lastUDPOverTCPIndex = -1 298 ) 299 300 for i := lastIndex; i >= 0; i-- { 301 p := proxies[i] 302 303 isUDPOverTCP = isUDPOverTCP || !isRawUDP(p) 304 305 if isLastRawUDP && isUDPOverTCP && lastUDPOverTCPIndex == -1 { 306 lastUDPOverTCPIndex = i 307 } 308 } 309 310 if !isLastRawUDP { 311 lastUDPOverTCPIndex = lastIndex 312 } 313 314 return !isUDPOverTCP, lastUDPOverTCPIndex 315 } 316 317 func isRawUDP(proxy C.ProxyAdapter) bool { 318 tp := proxy.Type() 319 if ((tp == C.Shadowsocks || tp == C.ShadowsocksR) && proxy.SupportUDP()) || tp == C.WireGuard || tp == C.Socks5 { 320 return true 321 } 322 return false 323 } 324 325 func supportPacketConn(proxy C.Proxy) bool { 326 tp := proxy.Type() 327 if (tp == C.Shadowsocks || tp == C.ShadowsocksR) && !proxy.SupportUDP() { 328 return false 329 } 330 return proxy.SupportUDP() || !tunnel.UDPFallbackMatch.Load() 331 } 332 333 func resolveDNS(metadata *C.Metadata) error { 334 if metadata.Host == "" || metadata.Resolved() { 335 return nil 336 } 337 338 rAddrs, err := resolver.LookupIP(context.Background(), metadata.Host) 339 if err != nil { 340 return err 341 } 342 metadata.DstIP = rAddrs[0] 343 return nil 344 } 345 346 func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay { 347 return &Relay{ 348 Base: outbound.NewBase(outbound.BaseOption{ 349 Name: option.Name, 350 Type: C.Relay, 351 Interface: option.Interface, 352 RoutingMark: option.RoutingMark, 353 }), 354 single: singledo.NewSingle[[]C.Proxy](defaultGetProxiesDuration), 355 providers: providers, 356 disableDNS: option.DisableDNS, 357 } 358 }