github.com/igoogolx/clash@v1.19.8/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 13 "github.com/igoogolx/clash/component/dialer" 14 "github.com/igoogolx/clash/component/resolver" 15 C "github.com/igoogolx/clash/constant" 16 "github.com/igoogolx/clash/transport/gun" 17 "github.com/igoogolx/clash/transport/socks5" 18 "github.com/igoogolx/clash/transport/vmess" 19 20 "golang.org/x/net/http2" 21 ) 22 23 var ErrUDPRemoteAddrMismatch = errors.New("udp packet dropped due to mismatched remote address") 24 25 type Vmess struct { 26 *Base 27 client *vmess.Client 28 option *VmessOption 29 30 // for gun mux 31 gunTLSConfig *tls.Config 32 gunConfig *gun.Config 33 transport *http2.Transport 34 } 35 36 type VmessOption struct { 37 BasicOption 38 Name string `proxy:"name"` 39 Server string `proxy:"server"` 40 Port int `proxy:"port"` 41 UUID string `proxy:"uuid"` 42 AlterID int `proxy:"alterId,omitempty"` 43 Cipher string `proxy:"cipher,omitempty"` 44 UDP bool `proxy:"udp,omitempty"` 45 Network string `proxy:"network,omitempty"` 46 TLS bool `proxy:"tls,omitempty"` 47 SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` 48 ServerName string `proxy:"servername,omitempty"` 49 HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"` 50 HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` 51 GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` 52 WSOpts WSOptions `proxy:"ws-opts,omitempty"` 53 } 54 55 type HTTPOptions struct { 56 Method string `proxy:"method,omitempty"` 57 Path []string `proxy:"path,omitempty"` 58 Headers map[string][]string `proxy:"headers,omitempty"` 59 } 60 61 type HTTP2Options struct { 62 Host []string `proxy:"host,omitempty"` 63 Path string `proxy:"path,omitempty"` 64 } 65 66 type GrpcOptions struct { 67 GrpcServiceName string `proxy:"grpc-service-name,omitempty"` 68 } 69 70 type WSOptions struct { 71 Path string `proxy:"path,omitempty"` 72 Headers map[string]string `proxy:"headers,omitempty"` 73 MaxEarlyData int `proxy:"max-early-data,omitempty"` 74 EarlyDataHeaderName string `proxy:"early-data-header-name,omitempty"` 75 } 76 77 // StreamConn implements C.ProxyAdapter 78 func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { 79 var err error 80 switch v.option.Network { 81 case "ws": 82 host, port, _ := net.SplitHostPort(v.addr) 83 wsOpts := &vmess.WebsocketConfig{ 84 Host: host, 85 Port: port, 86 Path: v.option.WSOpts.Path, 87 MaxEarlyData: v.option.WSOpts.MaxEarlyData, 88 EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName, 89 } 90 91 if len(v.option.WSOpts.Headers) != 0 { 92 header := http.Header{} 93 for key, value := range v.option.WSOpts.Headers { 94 header.Add(key, value) 95 } 96 wsOpts.Headers = header 97 } 98 99 if v.option.TLS { 100 wsOpts.TLS = true 101 wsOpts.TLSConfig = &tls.Config{ 102 ServerName: host, 103 InsecureSkipVerify: v.option.SkipCertVerify, 104 NextProtos: []string{"http/1.1"}, 105 } 106 if v.option.ServerName != "" { 107 wsOpts.TLSConfig.ServerName = v.option.ServerName 108 } else if host := wsOpts.Headers.Get("Host"); host != "" { 109 wsOpts.TLSConfig.ServerName = host 110 } 111 } 112 c, err = vmess.StreamWebsocketConn(c, wsOpts) 113 case "http": 114 // readability first, so just copy default TLS logic 115 if v.option.TLS { 116 host, _, _ := net.SplitHostPort(v.addr) 117 tlsOpts := &vmess.TLSConfig{ 118 Host: host, 119 SkipCertVerify: v.option.SkipCertVerify, 120 } 121 122 if v.option.ServerName != "" { 123 tlsOpts.Host = v.option.ServerName 124 } 125 126 c, err = vmess.StreamTLSConn(c, tlsOpts) 127 if err != nil { 128 return nil, err 129 } 130 } 131 132 host, _, _ := net.SplitHostPort(v.addr) 133 httpOpts := &vmess.HTTPConfig{ 134 Host: host, 135 Method: v.option.HTTPOpts.Method, 136 Path: v.option.HTTPOpts.Path, 137 Headers: v.option.HTTPOpts.Headers, 138 } 139 140 c = vmess.StreamHTTPConn(c, httpOpts) 141 case "h2": 142 host, _, _ := net.SplitHostPort(v.addr) 143 tlsOpts := vmess.TLSConfig{ 144 Host: host, 145 SkipCertVerify: v.option.SkipCertVerify, 146 NextProtos: []string{"h2"}, 147 } 148 149 if v.option.ServerName != "" { 150 tlsOpts.Host = v.option.ServerName 151 } 152 153 c, err = vmess.StreamTLSConn(c, &tlsOpts) 154 if err != nil { 155 return nil, err 156 } 157 158 h2Opts := &vmess.H2Config{ 159 Hosts: v.option.HTTP2Opts.Host, 160 Path: v.option.HTTP2Opts.Path, 161 } 162 163 c, err = vmess.StreamH2Conn(c, h2Opts) 164 case "grpc": 165 c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig) 166 default: 167 // handle TLS 168 if v.option.TLS { 169 host, _, _ := net.SplitHostPort(v.addr) 170 tlsOpts := &vmess.TLSConfig{ 171 Host: host, 172 SkipCertVerify: v.option.SkipCertVerify, 173 } 174 175 if v.option.ServerName != "" { 176 tlsOpts.Host = v.option.ServerName 177 } 178 179 c, err = vmess.StreamTLSConn(c, tlsOpts) 180 } 181 } 182 183 if err != nil { 184 return nil, err 185 } 186 187 return v.client.StreamConn(c, parseVmessAddr(metadata)) 188 } 189 190 // DialContext implements C.ProxyAdapter 191 func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { 192 // gun transport 193 if v.transport != nil && len(opts) == 0 { 194 c, err := gun.StreamGunWithTransport(v.transport, v.gunConfig) 195 if err != nil { 196 return nil, err 197 } 198 defer func(c net.Conn) { 199 safeConnClose(c, err) 200 }(c) 201 202 c, err = v.client.StreamConn(c, parseVmessAddr(metadata)) 203 if err != nil { 204 return nil, err 205 } 206 207 return NewConn(c, v), nil 208 } 209 210 c, err := dialer.DialContext(ctx, "tcp", v.addr, v.Base.DialOptions(opts...)...) 211 if err != nil { 212 return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) 213 } 214 tcpKeepAlive(c) 215 defer func(c net.Conn) { 216 safeConnClose(c, err) 217 }(c) 218 219 c, err = v.StreamConn(c, metadata) 220 return NewConn(c, v), err 221 } 222 223 // ListenPacketContext implements C.ProxyAdapter 224 func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { 225 // vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr 226 if !metadata.Resolved() { 227 ip, err := resolver.ResolveIP(metadata.Host) 228 if err != nil { 229 return nil, errors.New("can't resolve ip") 230 } 231 metadata.DstIP = ip 232 } 233 234 var c net.Conn 235 // gun transport 236 if v.transport != nil && len(opts) == 0 { 237 c, err = gun.StreamGunWithTransport(v.transport, v.gunConfig) 238 if err != nil { 239 return nil, err 240 } 241 defer func(c net.Conn) { 242 safeConnClose(c, err) 243 }(c) 244 245 c, err = v.client.StreamConn(c, parseVmessAddr(metadata)) 246 } else { 247 c, err = dialer.DialContext(ctx, "tcp", v.addr, v.Base.DialOptions(opts...)...) 248 if err != nil { 249 return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) 250 } 251 tcpKeepAlive(c) 252 defer func(c net.Conn) { 253 safeConnClose(c, err) 254 }(c) 255 256 c, err = v.StreamConn(c, metadata) 257 } 258 259 if err != nil { 260 return nil, fmt.Errorf("new vmess client error: %v", err) 261 } 262 263 return newPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil 264 } 265 266 func NewVmess(option VmessOption) (*Vmess, error) { 267 return newVmess(option, false) 268 } 269 270 func newVmess(option VmessOption, isVless bool) (*Vmess, error) { 271 security := strings.ToLower(option.Cipher) 272 if security == "" { 273 security = "auto" 274 } 275 client, err := vmess.NewClient(vmess.Config{ 276 UUID: option.UUID, 277 AlterID: uint16(option.AlterID), 278 Security: security, 279 HostName: option.Server, 280 Port: strconv.Itoa(option.Port), 281 IsAead: option.AlterID == 0, 282 IsVless: isVless, 283 }) 284 if err != nil { 285 return nil, err 286 } 287 288 switch option.Network { 289 case "h2", "grpc": 290 if !option.TLS { 291 return nil, fmt.Errorf("TLS must be true with h2/grpc network") 292 } 293 } 294 295 tp := C.Vmess 296 if isVless { 297 tp = C.Vless 298 } 299 300 v := &Vmess{ 301 Base: &Base{ 302 name: option.Name, 303 addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), 304 tp: tp, 305 udp: option.UDP, 306 iface: option.Interface, 307 rmark: option.RoutingMark, 308 }, 309 client: client, 310 option: &option, 311 } 312 313 switch option.Network { 314 case "h2": 315 if len(option.HTTP2Opts.Host) == 0 { 316 option.HTTP2Opts.Host = append(option.HTTP2Opts.Host, "www.example.com") 317 } 318 case "grpc": 319 dialFn := func(network, addr string) (net.Conn, error) { 320 c, err := dialer.DialContext(context.Background(), "tcp", v.addr, v.Base.DialOptions()...) 321 if err != nil { 322 return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) 323 } 324 tcpKeepAlive(c) 325 return c, nil 326 } 327 328 gunConfig := &gun.Config{ 329 ServiceName: v.option.GrpcOpts.GrpcServiceName, 330 Host: v.option.ServerName, 331 } 332 tlsConfig := &tls.Config{ 333 InsecureSkipVerify: v.option.SkipCertVerify, 334 ServerName: v.option.ServerName, 335 } 336 337 if v.option.ServerName == "" { 338 host, _, _ := net.SplitHostPort(v.addr) 339 tlsConfig.ServerName = host 340 gunConfig.Host = host 341 } 342 343 v.gunTLSConfig = tlsConfig 344 v.gunConfig = gunConfig 345 v.transport = gun.NewHTTP2Client(dialFn, tlsConfig) 346 } 347 348 return v, nil 349 } 350 351 func parseVmessAddr(metadata *C.Metadata) *vmess.DstAddr { 352 var addrType byte 353 var addr []byte 354 switch metadata.AddrType() { 355 case socks5.AtypIPv4: 356 addrType = vmess.AtypIPv4 357 addr = make([]byte, net.IPv4len) 358 copy(addr[:], metadata.DstIP.To4()) 359 case socks5.AtypIPv6: 360 addrType = vmess.AtypIPv6 361 addr = make([]byte, net.IPv6len) 362 copy(addr[:], metadata.DstIP.To16()) 363 case socks5.AtypDomainName: 364 addrType = vmess.AtypDomainName 365 addr = make([]byte, len(metadata.Host)+1) 366 addr[0] = byte(len(metadata.Host)) 367 copy(addr[1:], []byte(metadata.Host)) 368 } 369 370 return &vmess.DstAddr{ 371 UDP: metadata.NetWork == C.UDP, 372 AddrType: addrType, 373 Addr: addr, 374 Port: uint(metadata.DstPort), 375 } 376 } 377 378 type vmessPacketConn struct { 379 net.Conn 380 rAddr net.Addr 381 } 382 383 // WriteTo implments C.PacketConn.WriteTo 384 // Since VMess doesn't support full cone NAT by design, we verify if addr matches uc.rAddr, and drop the packet if not. 385 func (uc *vmessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { 386 allowedAddr := uc.rAddr.(*net.UDPAddr) 387 destAddr := addr.(*net.UDPAddr) 388 if !(allowedAddr.IP.Equal(destAddr.IP) && allowedAddr.Port == destAddr.Port) { 389 return 0, ErrUDPRemoteAddrMismatch 390 } 391 return uc.Conn.Write(b) 392 } 393 394 func (uc *vmessPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { 395 n, err := uc.Conn.Read(b) 396 return n, uc.rAddr, err 397 }