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  }