github.com/yaling888/clash@v1.53.0/component/dialer/dialer.go (about) 1 package dialer 2 3 import ( 4 "context" 5 "errors" 6 "net" 7 "net/netip" 8 9 "github.com/yaling888/clash/component/resolver" 10 ) 11 12 func DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) { 13 opt := &option{ 14 interfaceName: DefaultInterface.Load(), 15 routingMark: int(DefaultRoutingMark.Load()), 16 } 17 18 for _, o := range DefaultOptions { 19 o(opt) 20 } 21 22 for _, o := range options { 23 o(opt) 24 } 25 26 switch network { 27 case "tcp4", "tcp6", "udp4", "udp6": 28 host, port, err := net.SplitHostPort(address) 29 if err != nil { 30 return nil, err 31 } 32 33 var ip netip.Addr 34 switch network { 35 case "tcp4", "udp4": 36 if !opt.direct { 37 ip, err = resolver.ResolveIPv4ProxyServerHost(host) 38 } else { 39 ip, err = resolver.ResolveIPv4(host) 40 } 41 default: 42 if !opt.direct { 43 ip, err = resolver.ResolveIPv6ProxyServerHost(host) 44 } else { 45 ip, err = resolver.ResolveIPv6(host) 46 } 47 } 48 if err != nil { 49 return nil, err 50 } 51 52 return dialContext(ctx, network, ip, port, opt) 53 case "tcp", "udp": 54 return dualStackDialContext(ctx, network, address, opt) 55 default: 56 return nil, errors.New("network invalid") 57 } 58 } 59 60 func ListenPacket(ctx context.Context, network, address string, options ...Option) (net.PacketConn, error) { 61 cfg := &option{ 62 interfaceName: DefaultInterface.Load(), 63 routingMark: int(DefaultRoutingMark.Load()), 64 } 65 66 for _, o := range DefaultOptions { 67 o(cfg) 68 } 69 70 for _, o := range options { 71 o(cfg) 72 } 73 74 lc := &net.ListenConfig{} 75 if cfg.interfaceName != "" { 76 var ( 77 addr string 78 err error 79 ) 80 if cfg.fallbackBind { 81 addr, err = fallbackBindIfaceToListenConfig(cfg.interfaceName, lc, network, address) 82 } else { 83 addr, err = bindIfaceToListenConfig(cfg.interfaceName, lc, network, address) 84 } 85 if err != nil { 86 return nil, err 87 } 88 address = addr 89 } 90 if cfg.addrReuse { 91 addrReuseToListenConfig(lc) 92 } 93 if cfg.routingMark != 0 { 94 bindMarkToListenConfig(cfg.routingMark, lc, network, address) 95 } 96 97 return lc.ListenPacket(ctx, network, address) 98 } 99 100 func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) { 101 dialer := &net.Dialer{} 102 if opt.interfaceName != "" { 103 if err := bindIfaceToDialer(opt.interfaceName, dialer, network, destination); err != nil { 104 return nil, err 105 } 106 } 107 if opt.routingMark != 0 { 108 bindMarkToDialer(opt.routingMark, dialer, network, destination) 109 } 110 111 return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port)) 112 } 113 114 func dualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { 115 host, port, err := net.SplitHostPort(address) 116 if err != nil { 117 return nil, err 118 } 119 120 returned := make(chan struct{}) 121 defer close(returned) 122 123 type dialResult struct { 124 net.Conn 125 error 126 resolved bool 127 ipv6 bool 128 done bool 129 } 130 results := make(chan dialResult) 131 var primary, fallback dialResult 132 133 startRacer := func(ctx context.Context, network, host string, direct bool, ipv6 bool) { 134 result := dialResult{ipv6: ipv6, done: true} 135 defer func() { 136 select { 137 case results <- result: 138 case <-returned: 139 if result.Conn != nil { 140 _ = result.Conn.Close() 141 } 142 } 143 }() 144 145 var ip netip.Addr 146 if ipv6 { 147 if !direct { 148 ip, result.error = resolver.ResolveIPv6ProxyServerHost(host) 149 } else { 150 ip, result.error = resolver.ResolveIPv6(host) 151 } 152 } else { 153 if !direct { 154 ip, result.error = resolver.ResolveIPv4ProxyServerHost(host) 155 } else { 156 ip, result.error = resolver.ResolveIPv4(host) 157 } 158 } 159 if result.error != nil { 160 return 161 } 162 result.resolved = true 163 164 result.Conn, result.error = dialContext(ctx, network, ip, port, opt) 165 } 166 167 go startRacer(ctx, network+"4", host, opt.direct, false) 168 go startRacer(ctx, network+"6", host, opt.direct, true) 169 170 for res := range results { 171 if res.error == nil { 172 return res.Conn, nil 173 } 174 175 if !res.ipv6 { 176 primary = res 177 } else { 178 fallback = res 179 } 180 181 if primary.done && fallback.done { 182 if primary.resolved { 183 return nil, primary.error 184 } else if fallback.resolved { 185 return nil, fallback.error 186 } else { 187 return nil, primary.error 188 } 189 } 190 } 191 192 return nil, errors.New("never touched") 193 }