github.com/xxf098/lite-proxy@v0.15.1-0.20230422081941-12c69f323218/transport/dialer/dialer.go (about)

     1  package dialer
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net"
     7  
     8  	"github.com/xxf098/lite-proxy/transport/resolver"
     9  )
    10  
    11  func DialContext(ctx context.Context, network, address string) (net.Conn, error) {
    12  	switch network {
    13  	case "tcp4", "tcp6", "udp4", "udp6":
    14  		host, port, err := net.SplitHostPort(address)
    15  		if err != nil {
    16  			return nil, err
    17  		}
    18  
    19  		dialer, err := Dialer()
    20  		if err != nil {
    21  			return nil, err
    22  		}
    23  
    24  		var ip net.IP
    25  		switch network {
    26  		case "tcp4", "udp4":
    27  			ip, err = resolver.ResolveIPv4(host)
    28  		default:
    29  			ip, err = resolver.ResolveIPv6(host)
    30  		}
    31  
    32  		if err != nil {
    33  			return nil, err
    34  		}
    35  
    36  		return dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port))
    37  	case "tcp", "udp":
    38  		return dualStackDialContext(ctx, network, address)
    39  	default:
    40  		return nil, errors.New("network invalid")
    41  	}
    42  }
    43  
    44  func ListenPacket(network, address string) (net.PacketConn, error) {
    45  	// cfg := &net.ListenConfig{}
    46  	// return cfg.ListenPacket(context.Background(), network, address)
    47  	return effectiveListener.ListenPacket(context.Background(), network, address)
    48  }
    49  
    50  func dualStackDialContext(ctx context.Context, network, address string) (net.Conn, error) {
    51  	host, port, err := net.SplitHostPort(address)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	returned := make(chan struct{})
    57  	defer close(returned)
    58  
    59  	type dialResult struct {
    60  		net.Conn
    61  		error
    62  		resolved bool
    63  		ipv6     bool
    64  		done     bool
    65  	}
    66  	results := make(chan dialResult)
    67  	var primary, fallback dialResult
    68  
    69  	startRacer := func(ctx context.Context, network, host string, ipv6 bool) {
    70  		result := dialResult{ipv6: ipv6, done: true}
    71  		defer func() {
    72  			select {
    73  			case results <- result:
    74  			case <-returned:
    75  				if result.Conn != nil {
    76  					result.Conn.Close()
    77  				}
    78  			}
    79  		}()
    80  
    81  		dialer, err := Dialer()
    82  		if err != nil {
    83  			result.error = err
    84  			return
    85  		}
    86  
    87  		var ip net.IP
    88  		if ipv6 {
    89  			ip, result.error = resolver.ResolveIPv6(host)
    90  		} else {
    91  			ip, result.error = resolver.ResolveIPv4(host)
    92  		}
    93  		if result.error != nil {
    94  			return
    95  		}
    96  		result.resolved = true
    97  
    98  		result.Conn, result.error = dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port))
    99  	}
   100  
   101  	go startRacer(ctx, network+"4", host, false)
   102  	go startRacer(ctx, network+"6", host, true)
   103  
   104  	for res := range results {
   105  		if res.error == nil {
   106  			return res.Conn, nil
   107  		}
   108  
   109  		if !res.ipv6 {
   110  			primary = res
   111  		} else {
   112  			fallback = res
   113  		}
   114  
   115  		if primary.done && fallback.done {
   116  			if primary.resolved {
   117  				return nil, primary.error
   118  			} else if fallback.resolved {
   119  				return nil, fallback.error
   120  			} else {
   121  				return nil, primary.error
   122  			}
   123  		}
   124  	}
   125  
   126  	return nil, errors.New("never touched")
   127  }