github.com/metacubex/mihomo@v1.18.5/adapter/outbound/vmess.go (about) 1 package outbound 2 3 import ( 4 "context" 5 "crypto/tls" 6 "errors" 7 "fmt" 8 "net" 9 "net/http" 10 "strconv" 11 "strings" 12 "sync" 13 14 N "github.com/metacubex/mihomo/common/net" 15 "github.com/metacubex/mihomo/common/utils" 16 "github.com/metacubex/mihomo/component/ca" 17 "github.com/metacubex/mihomo/component/dialer" 18 "github.com/metacubex/mihomo/component/proxydialer" 19 "github.com/metacubex/mihomo/component/resolver" 20 tlsC "github.com/metacubex/mihomo/component/tls" 21 C "github.com/metacubex/mihomo/constant" 22 "github.com/metacubex/mihomo/ntp" 23 "github.com/metacubex/mihomo/transport/gun" 24 mihomoVMess "github.com/metacubex/mihomo/transport/vmess" 25 26 vmess "github.com/metacubex/sing-vmess" 27 "github.com/metacubex/sing-vmess/packetaddr" 28 M "github.com/sagernet/sing/common/metadata" 29 ) 30 31 var ErrUDPRemoteAddrMismatch = errors.New("udp packet dropped due to mismatched remote address") 32 33 type Vmess struct { 34 *Base 35 client *vmess.Client 36 option *VmessOption 37 38 // for gun mux 39 gunTLSConfig *tls.Config 40 gunConfig *gun.Config 41 transport *gun.TransportWrap 42 43 realityConfig *tlsC.RealityConfig 44 } 45 46 type VmessOption struct { 47 BasicOption 48 Name string `proxy:"name"` 49 Server string `proxy:"server"` 50 Port int `proxy:"port"` 51 UUID string `proxy:"uuid"` 52 AlterID int `proxy:"alterId"` 53 Cipher string `proxy:"cipher"` 54 UDP bool `proxy:"udp,omitempty"` 55 Network string `proxy:"network,omitempty"` 56 TLS bool `proxy:"tls,omitempty"` 57 ALPN []string `proxy:"alpn,omitempty"` 58 SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` 59 Fingerprint string `proxy:"fingerprint,omitempty"` 60 ServerName string `proxy:"servername,omitempty"` 61 RealityOpts RealityOptions `proxy:"reality-opts,omitempty"` 62 HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"` 63 HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` 64 GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` 65 WSOpts WSOptions `proxy:"ws-opts,omitempty"` 66 PacketAddr bool `proxy:"packet-addr,omitempty"` 67 XUDP bool `proxy:"xudp,omitempty"` 68 PacketEncoding string `proxy:"packet-encoding,omitempty"` 69 GlobalPadding bool `proxy:"global-padding,omitempty"` 70 AuthenticatedLength bool `proxy:"authenticated-length,omitempty"` 71 ClientFingerprint string `proxy:"client-fingerprint,omitempty"` 72 } 73 74 type HTTPOptions struct { 75 Method string `proxy:"method,omitempty"` 76 Path []string `proxy:"path,omitempty"` 77 Headers map[string][]string `proxy:"headers,omitempty"` 78 } 79 80 type HTTP2Options struct { 81 Host []string `proxy:"host,omitempty"` 82 Path string `proxy:"path,omitempty"` 83 } 84 85 type GrpcOptions struct { 86 GrpcServiceName string `proxy:"grpc-service-name,omitempty"` 87 } 88 89 type WSOptions struct { 90 Path string `proxy:"path,omitempty"` 91 Headers map[string]string `proxy:"headers,omitempty"` 92 MaxEarlyData int `proxy:"max-early-data,omitempty"` 93 EarlyDataHeaderName string `proxy:"early-data-header-name,omitempty"` 94 V2rayHttpUpgrade bool `proxy:"v2ray-http-upgrade,omitempty"` 95 V2rayHttpUpgradeFastOpen bool `proxy:"v2ray-http-upgrade-fast-open,omitempty"` 96 } 97 98 // StreamConnContext implements C.ProxyAdapter 99 func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) { 100 var err error 101 102 if tlsC.HaveGlobalFingerprint() && (len(v.option.ClientFingerprint) == 0) { 103 v.option.ClientFingerprint = tlsC.GetGlobalFingerprint() 104 } 105 106 switch v.option.Network { 107 case "ws": 108 host, port, _ := net.SplitHostPort(v.addr) 109 wsOpts := &mihomoVMess.WebsocketConfig{ 110 Host: host, 111 Port: port, 112 Path: v.option.WSOpts.Path, 113 MaxEarlyData: v.option.WSOpts.MaxEarlyData, 114 EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName, 115 V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade, 116 V2rayHttpUpgradeFastOpen: v.option.WSOpts.V2rayHttpUpgradeFastOpen, 117 ClientFingerprint: v.option.ClientFingerprint, 118 Headers: http.Header{}, 119 } 120 121 if len(v.option.WSOpts.Headers) != 0 { 122 for key, value := range v.option.WSOpts.Headers { 123 wsOpts.Headers.Add(key, value) 124 } 125 } 126 127 if v.option.TLS { 128 wsOpts.TLS = true 129 tlsConfig := &tls.Config{ 130 ServerName: host, 131 InsecureSkipVerify: v.option.SkipCertVerify, 132 NextProtos: []string{"http/1.1"}, 133 } 134 135 wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint) 136 if err != nil { 137 return nil, err 138 } 139 140 if v.option.ServerName != "" { 141 wsOpts.TLSConfig.ServerName = v.option.ServerName 142 } else if host := wsOpts.Headers.Get("Host"); host != "" { 143 wsOpts.TLSConfig.ServerName = host 144 } 145 } 146 c, err = mihomoVMess.StreamWebsocketConn(ctx, c, wsOpts) 147 case "http": 148 // readability first, so just copy default TLS logic 149 if v.option.TLS { 150 host, _, _ := net.SplitHostPort(v.addr) 151 tlsOpts := &mihomoVMess.TLSConfig{ 152 Host: host, 153 SkipCertVerify: v.option.SkipCertVerify, 154 ClientFingerprint: v.option.ClientFingerprint, 155 Reality: v.realityConfig, 156 NextProtos: v.option.ALPN, 157 } 158 159 if v.option.ServerName != "" { 160 tlsOpts.Host = v.option.ServerName 161 } 162 c, err = mihomoVMess.StreamTLSConn(ctx, c, tlsOpts) 163 if err != nil { 164 return nil, err 165 } 166 } 167 168 host, _, _ := net.SplitHostPort(v.addr) 169 httpOpts := &mihomoVMess.HTTPConfig{ 170 Host: host, 171 Method: v.option.HTTPOpts.Method, 172 Path: v.option.HTTPOpts.Path, 173 Headers: v.option.HTTPOpts.Headers, 174 } 175 176 c = mihomoVMess.StreamHTTPConn(c, httpOpts) 177 case "h2": 178 host, _, _ := net.SplitHostPort(v.addr) 179 tlsOpts := mihomoVMess.TLSConfig{ 180 Host: host, 181 SkipCertVerify: v.option.SkipCertVerify, 182 FingerPrint: v.option.Fingerprint, 183 NextProtos: []string{"h2"}, 184 ClientFingerprint: v.option.ClientFingerprint, 185 Reality: v.realityConfig, 186 } 187 188 if v.option.ServerName != "" { 189 tlsOpts.Host = v.option.ServerName 190 } 191 192 c, err = mihomoVMess.StreamTLSConn(ctx, c, &tlsOpts) 193 if err != nil { 194 return nil, err 195 } 196 197 h2Opts := &mihomoVMess.H2Config{ 198 Hosts: v.option.HTTP2Opts.Host, 199 Path: v.option.HTTP2Opts.Path, 200 } 201 202 c, err = mihomoVMess.StreamH2Conn(c, h2Opts) 203 case "grpc": 204 c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig, v.realityConfig) 205 default: 206 // handle TLS 207 if v.option.TLS { 208 host, _, _ := net.SplitHostPort(v.addr) 209 tlsOpts := &mihomoVMess.TLSConfig{ 210 Host: host, 211 SkipCertVerify: v.option.SkipCertVerify, 212 FingerPrint: v.option.Fingerprint, 213 ClientFingerprint: v.option.ClientFingerprint, 214 Reality: v.realityConfig, 215 NextProtos: v.option.ALPN, 216 } 217 218 if v.option.ServerName != "" { 219 tlsOpts.Host = v.option.ServerName 220 } 221 222 c, err = mihomoVMess.StreamTLSConn(ctx, c, tlsOpts) 223 } 224 } 225 226 if err != nil { 227 return nil, err 228 } 229 return v.streamConn(c, metadata) 230 } 231 232 func (v *Vmess) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err error) { 233 if metadata.NetWork == C.UDP { 234 if v.option.XUDP { 235 var globalID [8]byte 236 if metadata.SourceValid() { 237 globalID = utils.GlobalID(metadata.SourceAddress()) 238 } 239 if N.NeedHandshake(c) { 240 conn = v.client.DialEarlyXUDPPacketConn(c, 241 globalID, 242 M.SocksaddrFromNet(metadata.UDPAddr())) 243 } else { 244 conn, err = v.client.DialXUDPPacketConn(c, 245 globalID, 246 M.SocksaddrFromNet(metadata.UDPAddr())) 247 } 248 } else if v.option.PacketAddr { 249 if N.NeedHandshake(c) { 250 conn = v.client.DialEarlyPacketConn(c, 251 M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443)) 252 } else { 253 conn, err = v.client.DialPacketConn(c, 254 M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443)) 255 } 256 conn = packetaddr.NewBindConn(conn) 257 } else { 258 if N.NeedHandshake(c) { 259 conn = v.client.DialEarlyPacketConn(c, 260 M.SocksaddrFromNet(metadata.UDPAddr())) 261 } else { 262 conn, err = v.client.DialPacketConn(c, 263 M.SocksaddrFromNet(metadata.UDPAddr())) 264 } 265 } 266 } else { 267 if N.NeedHandshake(c) { 268 conn = v.client.DialEarlyConn(c, 269 M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort)) 270 } else { 271 conn, err = v.client.DialConn(c, 272 M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort)) 273 } 274 } 275 if err != nil { 276 conn = nil 277 } 278 return 279 } 280 281 // DialContext implements C.ProxyAdapter 282 func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { 283 // gun transport 284 if v.transport != nil && len(opts) == 0 { 285 c, err := gun.StreamGunWithTransport(v.transport, v.gunConfig) 286 if err != nil { 287 return nil, err 288 } 289 defer func(c net.Conn) { 290 safeConnClose(c, err) 291 }(c) 292 293 c, err = v.client.DialConn(c, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort)) 294 if err != nil { 295 return nil, err 296 } 297 298 return NewConn(c, v), nil 299 } 300 return v.DialContextWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata) 301 } 302 303 // DialContextWithDialer implements C.ProxyAdapter 304 func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { 305 if len(v.option.DialerProxy) > 0 { 306 dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer) 307 if err != nil { 308 return nil, err 309 } 310 } 311 c, err := dialer.DialContext(ctx, "tcp", v.addr) 312 if err != nil { 313 return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) 314 } 315 N.TCPKeepAlive(c) 316 defer func(c net.Conn) { 317 safeConnClose(c, err) 318 }(c) 319 320 c, err = v.StreamConnContext(ctx, c, metadata) 321 return NewConn(c, v), err 322 } 323 324 // ListenPacketContext implements C.ProxyAdapter 325 func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { 326 // vmess use stream-oriented udp with a special address, so we need a net.UDPAddr 327 if !metadata.Resolved() { 328 ip, err := resolver.ResolveIP(ctx, metadata.Host) 329 if err != nil { 330 return nil, errors.New("can't resolve ip") 331 } 332 metadata.DstIP = ip 333 } 334 var c net.Conn 335 // gun transport 336 if v.transport != nil && len(opts) == 0 { 337 c, err = gun.StreamGunWithTransport(v.transport, v.gunConfig) 338 if err != nil { 339 return nil, err 340 } 341 defer func(c net.Conn) { 342 safeConnClose(c, err) 343 }(c) 344 345 c, err = v.streamConn(c, metadata) 346 if err != nil { 347 return nil, fmt.Errorf("new vmess client error: %v", err) 348 } 349 return v.ListenPacketOnStreamConn(ctx, c, metadata) 350 } 351 return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata) 352 } 353 354 // ListenPacketWithDialer implements C.ProxyAdapter 355 func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { 356 if len(v.option.DialerProxy) > 0 { 357 dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer) 358 if err != nil { 359 return nil, err 360 } 361 } 362 363 // vmess use stream-oriented udp with a special address, so we need a net.UDPAddr 364 if !metadata.Resolved() { 365 ip, err := resolver.ResolveIP(ctx, metadata.Host) 366 if err != nil { 367 return nil, errors.New("can't resolve ip") 368 } 369 metadata.DstIP = ip 370 } 371 372 c, err := dialer.DialContext(ctx, "tcp", v.addr) 373 if err != nil { 374 return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) 375 } 376 N.TCPKeepAlive(c) 377 defer func(c net.Conn) { 378 safeConnClose(c, err) 379 }(c) 380 381 c, err = v.StreamConnContext(ctx, c, metadata) 382 if err != nil { 383 return nil, fmt.Errorf("new vmess client error: %v", err) 384 } 385 return v.ListenPacketOnStreamConn(ctx, c, metadata) 386 } 387 388 // SupportWithDialer implements C.ProxyAdapter 389 func (v *Vmess) SupportWithDialer() C.NetWork { 390 return C.ALLNet 391 } 392 393 // ListenPacketOnStreamConn implements C.ProxyAdapter 394 func (v *Vmess) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { 395 // vmess use stream-oriented udp with a special address, so we need a net.UDPAddr 396 if !metadata.Resolved() { 397 ip, err := resolver.ResolveIP(ctx, metadata.Host) 398 if err != nil { 399 return nil, errors.New("can't resolve ip") 400 } 401 metadata.DstIP = ip 402 } 403 404 if pc, ok := c.(net.PacketConn); ok { 405 return newPacketConn(N.NewThreadSafePacketConn(pc), v), nil 406 } 407 return newPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil 408 } 409 410 // SupportUOT implements C.ProxyAdapter 411 func (v *Vmess) SupportUOT() bool { 412 return true 413 } 414 415 func NewVmess(option VmessOption) (*Vmess, error) { 416 security := strings.ToLower(option.Cipher) 417 var options []vmess.ClientOption 418 if option.GlobalPadding { 419 options = append(options, vmess.ClientWithGlobalPadding()) 420 } 421 if option.AuthenticatedLength { 422 options = append(options, vmess.ClientWithAuthenticatedLength()) 423 } 424 options = append(options, vmess.ClientWithTimeFunc(ntp.Now)) 425 client, err := vmess.NewClient(option.UUID, security, option.AlterID, options...) 426 if err != nil { 427 return nil, err 428 } 429 430 switch option.PacketEncoding { 431 case "packetaddr", "packet": 432 option.PacketAddr = true 433 case "xudp": 434 option.XUDP = true 435 } 436 if option.XUDP { 437 option.PacketAddr = false 438 } 439 440 v := &Vmess{ 441 Base: &Base{ 442 name: option.Name, 443 addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), 444 tp: C.Vmess, 445 udp: option.UDP, 446 xudp: option.XUDP, 447 tfo: option.TFO, 448 mpTcp: option.MPTCP, 449 iface: option.Interface, 450 rmark: option.RoutingMark, 451 prefer: C.NewDNSPrefer(option.IPVersion), 452 }, 453 client: client, 454 option: &option, 455 } 456 457 switch option.Network { 458 case "h2": 459 if len(option.HTTP2Opts.Host) == 0 { 460 option.HTTP2Opts.Host = append(option.HTTP2Opts.Host, "www.example.com") 461 } 462 case "grpc": 463 dialFn := func(network, addr string) (net.Conn, error) { 464 var err error 465 var cDialer C.Dialer = dialer.NewDialer(v.Base.DialOptions()...) 466 if len(v.option.DialerProxy) > 0 { 467 cDialer, err = proxydialer.NewByName(v.option.DialerProxy, cDialer) 468 if err != nil { 469 return nil, err 470 } 471 } 472 c, err := cDialer.DialContext(context.Background(), "tcp", v.addr) 473 if err != nil { 474 return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) 475 } 476 N.TCPKeepAlive(c) 477 return c, nil 478 } 479 480 gunConfig := &gun.Config{ 481 ServiceName: v.option.GrpcOpts.GrpcServiceName, 482 Host: v.option.ServerName, 483 ClientFingerprint: v.option.ClientFingerprint, 484 } 485 if option.ServerName == "" { 486 gunConfig.Host = v.addr 487 } 488 var tlsConfig *tls.Config 489 if option.TLS { 490 tlsConfig = ca.GetGlobalTLSConfig(&tls.Config{ 491 InsecureSkipVerify: v.option.SkipCertVerify, 492 ServerName: v.option.ServerName, 493 }) 494 if option.ServerName == "" { 495 host, _, _ := net.SplitHostPort(v.addr) 496 tlsConfig.ServerName = host 497 } 498 } 499 500 v.gunTLSConfig = tlsConfig 501 v.gunConfig = gunConfig 502 503 v.transport = gun.NewHTTP2Client(dialFn, tlsConfig, v.option.ClientFingerprint, v.realityConfig) 504 } 505 506 v.realityConfig, err = v.option.RealityOpts.Parse() 507 if err != nil { 508 return nil, err 509 } 510 511 return v, nil 512 } 513 514 type vmessPacketConn struct { 515 net.Conn 516 rAddr net.Addr 517 access sync.Mutex 518 } 519 520 // WriteTo implments C.PacketConn.WriteTo 521 // Since VMess doesn't support full cone NAT by design, we verify if addr matches uc.rAddr, and drop the packet if not. 522 func (uc *vmessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { 523 allowedAddr := uc.rAddr 524 destAddr := addr 525 if allowedAddr.String() != destAddr.String() { 526 return 0, ErrUDPRemoteAddrMismatch 527 } 528 uc.access.Lock() 529 defer uc.access.Unlock() 530 return uc.Conn.Write(b) 531 } 532 533 func (uc *vmessPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { 534 n, err := uc.Conn.Read(b) 535 return n, uc.rAddr, err 536 }