2021-03-14 18:02:07 +03:00
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"
2023-02-27 22:52:01 +03:00
"github.com/xtls/xray-core/transport/internet/reality"
2021-09-20 15:11:21 +03:00
"github.com/xtls/xray-core/transport/internet/stat"
2021-03-14 18:02:07 +03:00
"github.com/xtls/xray-core/transport/internet/tls"
2022-05-18 10:29:01 +03:00
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/keepalive"
2021-03-14 18:02:07 +03:00
)
2021-09-20 15:11:21 +03:00
func Dial ( ctx context . Context , dest net . Destination , streamSettings * internet . MemoryStreamConfig ) ( stat . Connection , error ) {
2021-03-14 18:02:07 +03:00
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 )
}
2021-09-20 15:11:21 +03:00
return stat . Connection ( conn ) , nil
2021-03-14 18:02:07 +03:00
}
func init ( ) {
common . Must ( internet . RegisterTransportDialer ( protocolName , Dial ) )
}
type dialerConf struct {
net . Destination
2021-07-03 11:01:59 +03:00
* internet . MemoryStreamConfig
2021-03-14 18:02:07 +03:00
}
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 )
2021-10-16 16:07:45 +03:00
conn , err := getGrpcClient ( ctx , dest , streamSettings )
2021-03-14 18:02:07 +03:00
if err != nil {
return nil , newError ( "Cannot dial gRPC" ) . Base ( err )
}
client := encoding . NewGRPCServiceClient ( conn )
if grpcSettings . MultiMode {
2023-03-26 08:58:19 +03:00
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 ( ) )
2021-03-14 18:02:07 +03:00
if err != nil {
return nil , newError ( "Cannot dial gRPC" ) . Base ( err )
}
return encoding . NewMultiHunkConn ( grpcService , nil ) , nil
}
2023-03-26 08:58:19 +03:00
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 ( ) )
2021-03-14 18:02:07 +03:00
if err != nil {
return nil , newError ( "Cannot dial gRPC" ) . Base ( err )
}
return encoding . NewHunkConn ( grpcService , nil ) , nil
}
2021-10-16 16:07:45 +03:00
func getGrpcClient ( ctx context . Context , dest net . Destination , streamSettings * internet . MemoryStreamConfig ) ( * grpc . ClientConn , error ) {
2021-03-14 18:02:07 +03:00
globalDialerAccess . Lock ( )
defer globalDialerAccess . Unlock ( )
if globalDialerMap == nil {
globalDialerMap = make ( map [ dialerConf ] * grpc . ClientConn )
}
2021-07-03 11:01:59 +03:00
tlsConfig := tls . ConfigFromStreamSettings ( streamSettings )
2023-02-27 22:52:01 +03:00
realityConfig := reality . ConfigFromStreamSettings ( streamSettings )
2021-07-03 11:01:59 +03:00
sockopt := streamSettings . SocketSettings
grpcSettings := streamSettings . ProtocolSettings . ( * Config )
2021-03-14 18:02:07 +03:00
2021-07-03 11:01:59 +03:00
if client , found := globalDialerMap [ dialerConf { dest , streamSettings } ] ; found && client . GetState ( ) != connectivity . Shutdown {
2021-10-16 16:07:45 +03:00
return client , nil
2021-03-14 18:02:07 +03:00
}
2021-10-19 19:57:14 +03:00
dialOptions := [ ] grpc . DialOption {
2021-03-14 18:02:07 +03:00
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 ) {
gctx = session . ContextWithID ( gctx , session . IDFromContext ( ctx ) )
gctx = session . ContextWithOutbound ( gctx , session . OutboundFromContext ( ctx ) )
rawHost , rawPort , err := net . SplitHostPort ( s )
select {
case <- gctx . Done ( ) :
return nil , gctx . Err ( )
default :
}
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 )
2023-02-27 22:52:01 +03:00
c , err := internet . DialSystem ( gctx , net . TCPDestination ( address , port ) , sockopt )
if err == nil && realityConfig != nil {
return reality . UClient ( c , realityConfig , ctx , dest )
}
return c , err
2021-03-14 18:02:07 +03:00
} ) ,
2021-07-03 11:01:59 +03:00
}
if tlsConfig != nil {
2022-10-22 04:06:36 +03:00
var transportCredential credentials . TransportCredentials
2023-02-01 15:58:17 +03:00
if fingerprint := tls . GetFingerprint ( tlsConfig . Fingerprint ) ; fingerprint != nil {
2022-10-22 04:06:36 +03:00
transportCredential = tls . NewGrpcUtls ( tlsConfig . GetTLSConfig ( ) , fingerprint )
} else { // Fallback to normal gRPC TLS
transportCredential = credentials . NewTLS ( tlsConfig . GetTLSConfig ( ) )
}
dialOptions = append ( dialOptions , grpc . WithTransportCredentials ( transportCredential ) )
2021-07-03 11:01:59 +03:00
} else {
dialOptions = append ( dialOptions , grpc . WithInsecure ( ) )
}
2021-07-05 16:25:21 +03:00
if grpcSettings . IdleTimeout > 0 || grpcSettings . HealthCheckTimeout > 0 || grpcSettings . PermitWithoutStream {
2021-07-03 11:01:59 +03:00
dialOptions = append ( dialOptions , grpc . WithKeepaliveParams ( keepalive . ClientParameters {
2021-07-05 16:25:21 +03:00
Time : time . Second * time . Duration ( grpcSettings . IdleTimeout ) ,
Timeout : time . Second * time . Duration ( grpcSettings . HealthCheckTimeout ) ,
PermitWithoutStream : grpcSettings . PermitWithoutStream ,
2021-07-03 11:01:59 +03:00
} ) )
}
2021-12-19 19:47:21 +03:00
if grpcSettings . InitialWindowsSize > 0 {
dialOptions = append ( dialOptions , grpc . WithInitialWindowSize ( grpcSettings . InitialWindowsSize ) )
}
2023-03-26 08:53:42 +03:00
if grpcSettings . UserAgent != "" {
dialOptions = append ( dialOptions , grpc . WithUserAgent ( grpcSettings . UserAgent ) )
}
2021-07-03 11:01:59 +03:00
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 ... ,
2021-03-14 18:02:07 +03:00
)
2021-07-03 11:01:59 +03:00
globalDialerMap [ dialerConf { dest , streamSettings } ] = conn
2021-10-16 16:07:45 +03:00
return conn , err
2021-03-14 18:02:07 +03:00
}