github.com/metacubex/mihomo@v1.18.5/adapter/outbound/util.go (about)

     1  package outbound
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/tls"
     7  	"fmt"
     8  	"net"
     9  	"net/netip"
    10  	"regexp"
    11  	"strconv"
    12  	"sync"
    13  
    14  	"github.com/metacubex/mihomo/component/resolver"
    15  	C "github.com/metacubex/mihomo/constant"
    16  	"github.com/metacubex/mihomo/transport/socks5"
    17  )
    18  
    19  var (
    20  	globalClientSessionCache tls.ClientSessionCache
    21  	once                     sync.Once
    22  )
    23  
    24  func getClientSessionCache() tls.ClientSessionCache {
    25  	once.Do(func() {
    26  		globalClientSessionCache = tls.NewLRUClientSessionCache(128)
    27  	})
    28  	return globalClientSessionCache
    29  }
    30  
    31  func serializesSocksAddr(metadata *C.Metadata) []byte {
    32  	var buf [][]byte
    33  	addrType := metadata.AddrType()
    34  	aType := uint8(addrType)
    35  	p := uint(metadata.DstPort)
    36  	port := []byte{uint8(p >> 8), uint8(p & 0xff)}
    37  	switch addrType {
    38  	case socks5.AtypDomainName:
    39  		lenM := uint8(len(metadata.Host))
    40  		host := []byte(metadata.Host)
    41  		buf = [][]byte{{aType, lenM}, host, port}
    42  	case socks5.AtypIPv4:
    43  		host := metadata.DstIP.AsSlice()
    44  		buf = [][]byte{{aType}, host, port}
    45  	case socks5.AtypIPv6:
    46  		host := metadata.DstIP.AsSlice()
    47  		buf = [][]byte{{aType}, host, port}
    48  	}
    49  	return bytes.Join(buf, nil)
    50  }
    51  
    52  func resolveUDPAddr(ctx context.Context, network, address string) (*net.UDPAddr, error) {
    53  	host, port, err := net.SplitHostPort(address)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	ip, err := resolver.ResolveProxyServerHost(ctx, host)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	return net.ResolveUDPAddr(network, net.JoinHostPort(ip.String(), port))
    63  }
    64  
    65  func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, prefer C.DNSPrefer) (*net.UDPAddr, error) {
    66  	host, port, err := net.SplitHostPort(address)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	var ip netip.Addr
    71  	var fallback netip.Addr
    72  	switch prefer {
    73  	case C.IPv4Only:
    74  		ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host)
    75  	case C.IPv6Only:
    76  		ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host)
    77  	case C.IPv6Prefer:
    78  		var ips []netip.Addr
    79  		ips, err = resolver.LookupIPProxyServerHost(ctx, host)
    80  		if err == nil {
    81  			for _, addr := range ips {
    82  				if addr.Is6() {
    83  					ip = addr
    84  					break
    85  				} else {
    86  					if !fallback.IsValid() {
    87  						fallback = addr
    88  					}
    89  				}
    90  			}
    91  		}
    92  	default:
    93  		// C.IPv4Prefer, C.DualStack and other
    94  		var ips []netip.Addr
    95  		ips, err = resolver.LookupIPProxyServerHost(ctx, host)
    96  		if err == nil {
    97  			for _, addr := range ips {
    98  				if addr.Is4() {
    99  					ip = addr
   100  					break
   101  				} else {
   102  					if !fallback.IsValid() {
   103  						fallback = addr
   104  					}
   105  				}
   106  			}
   107  
   108  		}
   109  	}
   110  
   111  	if !ip.IsValid() && fallback.IsValid() {
   112  		ip = fallback
   113  	}
   114  
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	return net.ResolveUDPAddr(network, net.JoinHostPort(ip.String(), port))
   119  }
   120  
   121  func safeConnClose(c net.Conn, err error) {
   122  	if err != nil && c != nil {
   123  		_ = c.Close()
   124  	}
   125  }
   126  
   127  var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`)
   128  
   129  func StringToBps(s string) uint64 {
   130  	if s == "" {
   131  		return 0
   132  	}
   133  
   134  	// when have not unit, use Mbps
   135  	if v, err := strconv.Atoi(s); err == nil {
   136  		return StringToBps(fmt.Sprintf("%d Mbps", v))
   137  	}
   138  
   139  	m := rateStringRegexp.FindStringSubmatch(s)
   140  	if m == nil {
   141  		return 0
   142  	}
   143  	var n uint64 = 1
   144  	switch m[2] {
   145  	case "T":
   146  		n *= 1000
   147  		fallthrough
   148  	case "G":
   149  		n *= 1000
   150  		fallthrough
   151  	case "M":
   152  		n *= 1000
   153  		fallthrough
   154  	case "K":
   155  		n *= 1000
   156  	}
   157  	v, _ := strconv.ParseUint(m[1], 10, 64)
   158  	n *= v
   159  	if m[3] == "b" {
   160  		// Bits, need to convert to bytes
   161  		n /= 8
   162  	}
   163  	return n
   164  }