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 }