2021-03-14 15:02:07 +00:00
|
|
|
package grpc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
gonet "net"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/xtls/xray-core/common"
|
2024-06-29 18:32:57 +00:00
|
|
|
c "github.com/xtls/xray-core/common/ctx"
|
2024-12-19 10:45:35 +00:00
|
|
|
"github.com/xtls/xray-core/common/dice"
|
2024-06-29 18:32:57 +00:00
|
|
|
"github.com/xtls/xray-core/common/errors"
|
2021-03-14 15:02:07 +00:00
|
|
|
"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 19:52:01 +00:00
|
|
|
"github.com/xtls/xray-core/transport/internet/reality"
|
2021-09-20 12:11:21 +00:00
|
|
|
"github.com/xtls/xray-core/transport/internet/stat"
|
2021-03-14 15:02:07 +00:00
|
|
|
"github.com/xtls/xray-core/transport/internet/tls"
|
2022-05-18 07:29:01 +00:00
|
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/grpc/backoff"
|
|
|
|
"google.golang.org/grpc/connectivity"
|
2024-03-03 15:52:22 +00:00
|
|
|
"google.golang.org/grpc/credentials/insecure"
|
2022-05-18 07:29:01 +00:00
|
|
|
"google.golang.org/grpc/keepalive"
|
2021-03-14 15:02:07 +00:00
|
|
|
)
|
|
|
|
|
2021-09-20 12:11:21 +00:00
|
|
|
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
2024-06-29 18:32:57 +00:00
|
|
|
errors.LogInfo(ctx, "creating connection to ", dest)
|
2021-03-14 15:02:07 +00:00
|
|
|
|
|
|
|
conn, err := dialgRPC(ctx, dest, streamSettings)
|
|
|
|
if err != nil {
|
2024-06-29 18:32:57 +00:00
|
|
|
return nil, errors.New("failed to dial gRPC").Base(err)
|
2021-03-14 15:02:07 +00:00
|
|
|
}
|
2021-09-20 12:11:21 +00:00
|
|
|
return stat.Connection(conn), nil
|
2021-03-14 15:02:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
common.Must(internet.RegisterTransportDialer(protocolName, Dial))
|
|
|
|
}
|
|
|
|
|
|
|
|
type dialerConf struct {
|
|
|
|
net.Destination
|
2021-07-03 08:01:59 +00:00
|
|
|
*internet.MemoryStreamConfig
|
2021-03-14 15:02:07 +00:00
|
|
|
}
|
|
|
|
|
2024-12-19 10:45:35 +00:00
|
|
|
type globalDialers struct {
|
|
|
|
connMap map[dialerConf][]*grpc.ClientConn
|
|
|
|
}
|
|
|
|
|
|
|
|
// getClientConn returns a client connection from the global dialer if the connections already reached the target number
|
|
|
|
// otherwise return nil
|
|
|
|
func (d *globalDialers) getClientConn(conf dialerConf) (*grpc.ClientConn, int) {
|
|
|
|
if d.connMap == nil {
|
|
|
|
d.connMap = make(map[dialerConf][]*grpc.ClientConn)
|
|
|
|
}
|
|
|
|
if d.connMap[conf] == nil {
|
|
|
|
d.connMap[conf] = []*grpc.ClientConn{}
|
|
|
|
}
|
|
|
|
|
|
|
|
conns := d.connMap[conf]
|
|
|
|
|
|
|
|
targetConnsNum := conf.MemoryStreamConfig.ProtocolSettings.(*Config).MultiConnections
|
|
|
|
if targetConnsNum > int32(len(conns)) {
|
|
|
|
return nil, 0
|
|
|
|
} else {
|
|
|
|
index := dice.Roll(len(conns))
|
|
|
|
return conns[index], index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// addClientConn adds a client connection to the global dialer
|
|
|
|
func (d *globalDialers) addClientConn(conf dialerConf, conn *grpc.ClientConn) error {
|
|
|
|
if d.connMap == nil {
|
|
|
|
d.connMap = make(map[dialerConf][]*grpc.ClientConn)
|
|
|
|
}
|
|
|
|
if d.connMap[conf] == nil {
|
|
|
|
d.connMap[conf] = []*grpc.ClientConn{}
|
|
|
|
}
|
|
|
|
|
|
|
|
conns := d.connMap[conf]
|
|
|
|
targetConnsNum := conf.MemoryStreamConfig.ProtocolSettings.(*Config).MultiConnections
|
|
|
|
if targetConnsNum <= int32(len(conns)) {
|
|
|
|
return errors.New("failed to add client connection beacuse reach the limit")
|
|
|
|
} else {
|
|
|
|
conns = append(conns, conn)
|
|
|
|
d.connMap[conf] = conns
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// updateClientConnWithIndex updates a client connection with the given index
|
|
|
|
// in the case if the clientConn is shutting down, replace it with the new one
|
|
|
|
func (d *globalDialers) updateClientConnWithIndex(conf dialerConf, conn *grpc.ClientConn, index int) error {
|
|
|
|
conns := d.connMap[conf]
|
|
|
|
conns[index] = conn
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-03-14 15:02:07 +00:00
|
|
|
var (
|
2024-12-19 10:45:35 +00:00
|
|
|
globalDialer globalDialers
|
2021-03-14 15:02:07 +00:00
|
|
|
globalDialerAccess sync.Mutex
|
|
|
|
)
|
|
|
|
|
|
|
|
func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) {
|
|
|
|
grpcSettings := streamSettings.ProtocolSettings.(*Config)
|
|
|
|
|
2021-10-16 13:07:45 +00:00
|
|
|
conn, err := getGrpcClient(ctx, dest, streamSettings)
|
2021-03-14 15:02:07 +00:00
|
|
|
if err != nil {
|
2024-06-29 18:32:57 +00:00
|
|
|
return nil, errors.New("Cannot dial gRPC").Base(err)
|
2021-03-14 15:02:07 +00:00
|
|
|
}
|
|
|
|
client := encoding.NewGRPCServiceClient(conn)
|
|
|
|
if grpcSettings.MultiMode {
|
2024-11-09 11:16:11 +00:00
|
|
|
errors.LogDebug(ctx, "using gRPC multi mode service name: `"+grpcSettings.getServiceName()+"` stream name: `"+grpcSettings.getTunMultiStreamName()+"`")
|
2023-03-26 05:58:19 +00:00
|
|
|
grpcService, err := client.(encoding.GRPCServiceClientX).TunMultiCustomName(ctx, grpcSettings.getServiceName(), grpcSettings.getTunMultiStreamName())
|
2021-03-14 15:02:07 +00:00
|
|
|
if err != nil {
|
2024-06-29 18:32:57 +00:00
|
|
|
return nil, errors.New("Cannot dial gRPC").Base(err)
|
2021-03-14 15:02:07 +00:00
|
|
|
}
|
|
|
|
return encoding.NewMultiHunkConn(grpcService, nil), nil
|
|
|
|
}
|
|
|
|
|
2024-11-09 11:16:11 +00:00
|
|
|
errors.LogDebug(ctx, "using gRPC tun mode service name: `"+grpcSettings.getServiceName()+"` stream name: `"+grpcSettings.getTunStreamName()+"`")
|
2023-03-26 05:58:19 +00:00
|
|
|
grpcService, err := client.(encoding.GRPCServiceClientX).TunCustomName(ctx, grpcSettings.getServiceName(), grpcSettings.getTunStreamName())
|
2021-03-14 15:02:07 +00:00
|
|
|
if err != nil {
|
2024-06-29 18:32:57 +00:00
|
|
|
return nil, errors.New("Cannot dial gRPC").Base(err)
|
2021-03-14 15:02:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return encoding.NewHunkConn(grpcService, nil), nil
|
|
|
|
}
|
|
|
|
|
2021-10-16 13:07:45 +00:00
|
|
|
func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (*grpc.ClientConn, error) {
|
2021-03-14 15:02:07 +00:00
|
|
|
globalDialerAccess.Lock()
|
|
|
|
defer globalDialerAccess.Unlock()
|
|
|
|
|
2021-07-03 08:01:59 +00:00
|
|
|
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
2023-02-27 19:52:01 +00:00
|
|
|
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
2021-07-03 08:01:59 +00:00
|
|
|
sockopt := streamSettings.SocketSettings
|
|
|
|
grpcSettings := streamSettings.ProtocolSettings.(*Config)
|
2021-03-14 15:02:07 +00:00
|
|
|
|
2024-12-19 10:45:35 +00:00
|
|
|
client, index := globalDialer.getClientConn(dialerConf{dest, streamSettings})
|
|
|
|
if client != nil && client.GetState() != connectivity.Shutdown {
|
2021-10-16 13:07:45 +00:00
|
|
|
return client, nil
|
2021-03-14 15:02:07 +00:00
|
|
|
}
|
|
|
|
|
2021-10-19 16:57:14 +00:00
|
|
|
dialOptions := []grpc.DialOption{
|
2021-03-14 15:02:07 +00: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) {
|
|
|
|
select {
|
|
|
|
case <-gctx.Done():
|
|
|
|
return nil, gctx.Err()
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
2023-08-27 05:55:58 +00:00
|
|
|
rawHost, rawPort, err := net.SplitHostPort(s)
|
2021-03-14 15:02:07 +00:00
|
|
|
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-08-27 05:55:58 +00:00
|
|
|
|
2024-06-29 18:32:57 +00:00
|
|
|
gctx = c.ContextWithID(gctx, c.IDFromContext(ctx))
|
2024-05-14 01:52:24 +00:00
|
|
|
gctx = session.ContextWithOutbounds(gctx, session.OutboundsFromContext(ctx))
|
2023-08-27 05:55:58 +00:00
|
|
|
gctx = session.ContextWithTimeoutOnly(gctx, true)
|
|
|
|
|
2023-02-27 19:52:01 +00:00
|
|
|
c, err := internet.DialSystem(gctx, net.TCPDestination(address, port), sockopt)
|
2024-02-29 14:22:14 +00:00
|
|
|
if err == nil {
|
|
|
|
if tlsConfig != nil {
|
|
|
|
config := tlsConfig.GetTLSConfig()
|
|
|
|
if config.ServerName == "" && address.Family().IsDomain() {
|
|
|
|
config.ServerName = address.Domain()
|
|
|
|
}
|
|
|
|
if fingerprint := tls.GetFingerprint(tlsConfig.Fingerprint); fingerprint != nil {
|
|
|
|
return tls.UClient(c, config, fingerprint), nil
|
|
|
|
} else { // Fallback to normal gRPC TLS
|
|
|
|
return tls.Client(c, config), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if realityConfig != nil {
|
|
|
|
return reality.UClient(c, realityConfig, gctx, dest)
|
|
|
|
}
|
2023-02-27 19:52:01 +00:00
|
|
|
}
|
|
|
|
return c, err
|
2021-03-14 15:02:07 +00:00
|
|
|
}),
|
2021-07-03 08:01:59 +00:00
|
|
|
}
|
|
|
|
|
2024-03-03 15:52:22 +00:00
|
|
|
dialOptions = append(dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
2024-02-29 14:22:14 +00:00
|
|
|
|
|
|
|
authority := ""
|
|
|
|
if grpcSettings.Authority != "" {
|
|
|
|
authority = grpcSettings.Authority
|
|
|
|
} else if tlsConfig != nil && tlsConfig.ServerName != "" {
|
|
|
|
authority = tlsConfig.ServerName
|
|
|
|
} else if realityConfig == nil && dest.Address.Family().IsDomain() {
|
|
|
|
authority = dest.Address.Domain()
|
2021-07-03 08:01:59 +00:00
|
|
|
}
|
2024-02-29 14:22:14 +00:00
|
|
|
dialOptions = append(dialOptions, grpc.WithAuthority(authority))
|
2021-07-03 08:01:59 +00:00
|
|
|
|
2021-07-05 13:25:21 +00:00
|
|
|
if grpcSettings.IdleTimeout > 0 || grpcSettings.HealthCheckTimeout > 0 || grpcSettings.PermitWithoutStream {
|
2021-07-03 08:01:59 +00:00
|
|
|
dialOptions = append(dialOptions, grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
2021-07-05 13:25:21 +00:00
|
|
|
Time: time.Second * time.Duration(grpcSettings.IdleTimeout),
|
|
|
|
Timeout: time.Second * time.Duration(grpcSettings.HealthCheckTimeout),
|
|
|
|
PermitWithoutStream: grpcSettings.PermitWithoutStream,
|
2021-07-03 08:01:59 +00:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2021-12-19 16:47:21 +00:00
|
|
|
if grpcSettings.InitialWindowsSize > 0 {
|
|
|
|
dialOptions = append(dialOptions, grpc.WithInitialWindowSize(grpcSettings.InitialWindowsSize))
|
|
|
|
}
|
|
|
|
|
2023-03-26 05:53:42 +00:00
|
|
|
if grpcSettings.UserAgent != "" {
|
|
|
|
dialOptions = append(dialOptions, grpc.WithUserAgent(grpcSettings.UserAgent))
|
|
|
|
}
|
|
|
|
|
2021-07-03 08:01:59 +00: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 15:02:07 +00:00
|
|
|
)
|
2024-12-19 10:45:35 +00:00
|
|
|
if client == nil {
|
|
|
|
err := globalDialer.addClientConn(dialerConf{dest, streamSettings}, conn)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if client != nil && client.GetState() == connectivity.Shutdown {
|
|
|
|
globalDialer.updateClientConnWithIndex(dialerConf{dest, streamSettings}, conn, index)
|
|
|
|
}
|
2021-10-16 13:07:45 +00:00
|
|
|
return conn, err
|
2021-03-14 15:02:07 +00:00
|
|
|
}
|