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