| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- package grpc
- import (
- "context"
- gonet "net"
- "sync"
- "time"
- "github.com/xtls/xray-core/common"
- "github.com/xtls/xray-core/common/net"
- "github.com/xtls/xray-core/common/session"
- "github.com/xtls/xray-core/transport/internet"
- "github.com/xtls/xray-core/transport/internet/grpc/encoding"
- "github.com/xtls/xray-core/transport/internet/reality"
- "github.com/xtls/xray-core/transport/internet/stat"
- "github.com/xtls/xray-core/transport/internet/tls"
- "google.golang.org/grpc"
- "google.golang.org/grpc/backoff"
- "google.golang.org/grpc/connectivity"
- "google.golang.org/grpc/credentials"
- "google.golang.org/grpc/keepalive"
- )
- func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
- newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx))
- conn, err := dialgRPC(ctx, dest, streamSettings)
- if err != nil {
- return nil, newError("failed to dial gRPC").Base(err)
- }
- return stat.Connection(conn), nil
- }
- func init() {
- common.Must(internet.RegisterTransportDialer(protocolName, Dial))
- }
- type dialerConf struct {
- net.Destination
- *internet.MemoryStreamConfig
- }
- var (
- globalDialerMap map[dialerConf]*grpc.ClientConn
- globalDialerAccess sync.Mutex
- )
- func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) {
- grpcSettings := streamSettings.ProtocolSettings.(*Config)
- conn, err := getGrpcClient(ctx, dest, streamSettings)
- if err != nil {
- return nil, newError("Cannot dial gRPC").Base(err)
- }
- client := encoding.NewGRPCServiceClient(conn)
- if grpcSettings.MultiMode {
- newError("using gRPC multi mode service name: `" + grpcSettings.getServiceName() + "` stream name: `" + grpcSettings.getTunMultiStreamName() + "`").AtDebug().WriteToLog()
- grpcService, err := client.(encoding.GRPCServiceClientX).TunMultiCustomName(ctx, grpcSettings.getServiceName(), grpcSettings.getTunMultiStreamName())
- if err != nil {
- return nil, newError("Cannot dial gRPC").Base(err)
- }
- return encoding.NewMultiHunkConn(grpcService, nil), nil
- }
- newError("using gRPC tun mode service name: `" + grpcSettings.getServiceName() + "` stream name: `" + grpcSettings.getTunStreamName() + "`").AtDebug().WriteToLog()
- grpcService, err := client.(encoding.GRPCServiceClientX).TunCustomName(ctx, grpcSettings.getServiceName(), grpcSettings.getTunStreamName())
- if err != nil {
- return nil, newError("Cannot dial gRPC").Base(err)
- }
- return encoding.NewHunkConn(grpcService, nil), nil
- }
- func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (*grpc.ClientConn, error) {
- globalDialerAccess.Lock()
- defer globalDialerAccess.Unlock()
- if globalDialerMap == nil {
- globalDialerMap = make(map[dialerConf]*grpc.ClientConn)
- }
- tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
- realityConfig := reality.ConfigFromStreamSettings(streamSettings)
- sockopt := streamSettings.SocketSettings
- grpcSettings := streamSettings.ProtocolSettings.(*Config)
- if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found && client.GetState() != connectivity.Shutdown {
- return client, nil
- }
- dialOptions := []grpc.DialOption{
- grpc.WithConnectParams(grpc.ConnectParams{
- Backoff: backoff.Config{
- BaseDelay: 500 * time.Millisecond,
- Multiplier: 1.5,
- Jitter: 0.2,
- MaxDelay: 19 * time.Second,
- },
- MinConnectTimeout: 5 * time.Second,
- }),
- grpc.WithContextDialer(func(gctx context.Context, s string) (gonet.Conn, error) {
- select {
- case <-gctx.Done():
- return nil, gctx.Err()
- default:
- }
- rawHost, rawPort, err := net.SplitHostPort(s)
- if err != nil {
- return nil, err
- }
- if len(rawPort) == 0 {
- rawPort = "443"
- }
- port, err := net.PortFromString(rawPort)
- if err != nil {
- return nil, err
- }
- address := net.ParseAddress(rawHost)
- gctx = session.ContextWithID(gctx, session.IDFromContext(ctx))
- gctx = session.ContextWithOutbound(gctx, session.OutboundFromContext(ctx))
- gctx = session.ContextWithTimeoutOnly(gctx, true)
- c, err := internet.DialSystem(gctx, net.TCPDestination(address, port), sockopt)
- if err == nil && realityConfig != nil {
- return reality.UClient(c, realityConfig, gctx, dest)
- }
- return c, err
- }),
- }
- if tlsConfig != nil {
- var transportCredential credentials.TransportCredentials
- if fingerprint := tls.GetFingerprint(tlsConfig.Fingerprint); fingerprint != nil {
- transportCredential = tls.NewGrpcUtls(tlsConfig.GetTLSConfig(), fingerprint)
- } else { // Fallback to normal gRPC TLS
- transportCredential = credentials.NewTLS(tlsConfig.GetTLSConfig())
- }
- dialOptions = append(dialOptions, grpc.WithTransportCredentials(transportCredential))
- } else {
- dialOptions = append(dialOptions, grpc.WithInsecure())
- }
- if grpcSettings.IdleTimeout > 0 || grpcSettings.HealthCheckTimeout > 0 || grpcSettings.PermitWithoutStream {
- dialOptions = append(dialOptions, grpc.WithKeepaliveParams(keepalive.ClientParameters{
- Time: time.Second * time.Duration(grpcSettings.IdleTimeout),
- Timeout: time.Second * time.Duration(grpcSettings.HealthCheckTimeout),
- PermitWithoutStream: grpcSettings.PermitWithoutStream,
- }))
- }
- if grpcSettings.InitialWindowsSize > 0 {
- dialOptions = append(dialOptions, grpc.WithInitialWindowSize(grpcSettings.InitialWindowsSize))
- }
- if grpcSettings.UserAgent != "" {
- dialOptions = append(dialOptions, grpc.WithUserAgent(grpcSettings.UserAgent))
- }
- var grpcDestHost string
- if dest.Address.Family().IsDomain() {
- grpcDestHost = dest.Address.Domain()
- } else {
- grpcDestHost = dest.Address.IP().String()
- }
- conn, err := grpc.Dial(
- gonet.JoinHostPort(grpcDestHost, dest.Port.String()),
- dialOptions...,
- )
- globalDialerMap[dialerConf{dest, streamSettings}] = conn
- return conn, err
- }
|