github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/transport/internet/tls/grpc.go (about) 1 package tls 2 3 import ( 4 "context" 5 gotls "crypto/tls" 6 "net" 7 "net/url" 8 "strconv" 9 10 utls "github.com/refraction-networking/utls" 11 "google.golang.org/grpc/credentials" 12 ) 13 14 // grpcUtlsInfo contains the auth information for a TLS authenticated connection. 15 // It implements the AuthInfo interface. 16 type grpcUtlsInfo struct { 17 State utls.ConnectionState 18 credentials.CommonAuthInfo 19 // This API is experimental. 20 SPIFFEID *url.URL 21 } 22 23 // AuthType returns the type of TLSInfo as a string. 24 func (t grpcUtlsInfo) AuthType() string { 25 return "utls" 26 } 27 28 // GetSecurityValue returns security info requested by channelz. 29 func (t grpcUtlsInfo) GetSecurityValue() credentials.ChannelzSecurityValue { 30 v := &credentials.TLSChannelzSecurityValue{ 31 StandardName: "0x" + strconv.FormatUint(uint64(t.State.CipherSuite), 16), 32 } 33 // Currently there's no way to get LocalCertificate info from tls package. 34 if len(t.State.PeerCertificates) > 0 { 35 v.RemoteCertificate = t.State.PeerCertificates[0].Raw 36 } 37 return v 38 } 39 40 // grpcUtls is the credentials required for authenticating a connection using TLS. 41 type grpcUtls struct { 42 config *gotls.Config 43 fingerprint *utls.ClientHelloID 44 } 45 46 func (c grpcUtls) Info() credentials.ProtocolInfo { 47 return credentials.ProtocolInfo{ 48 SecurityProtocol: "tls", 49 SecurityVersion: "1.2", 50 ServerName: c.config.ServerName, 51 } 52 } 53 54 func (c *grpcUtls) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ credentials.AuthInfo, err error) { 55 // use local cfg to avoid clobbering ServerName if using multiple endpoints 56 cfg := c.config.Clone() 57 if cfg.ServerName == "" { 58 serverName, _, err := net.SplitHostPort(authority) 59 if err != nil { 60 // If the authority had no host port or if the authority cannot be parsed, use it as-is. 61 serverName = authority 62 } 63 cfg.ServerName = serverName 64 } 65 conn := UClient(rawConn, cfg, c.fingerprint).(*UConn) 66 errChannel := make(chan error, 1) 67 go func() { 68 errChannel <- conn.HandshakeContext(ctx) 69 close(errChannel) 70 }() 71 select { 72 case err := <-errChannel: 73 if err != nil { 74 conn.Close() 75 return nil, nil, err 76 } 77 case <-ctx.Done(): 78 conn.Close() 79 return nil, nil, ctx.Err() 80 } 81 tlsInfo := grpcUtlsInfo{ 82 State: conn.ConnectionState(), 83 CommonAuthInfo: credentials.CommonAuthInfo{ 84 SecurityLevel: credentials.PrivacyAndIntegrity, 85 }, 86 } 87 return conn, tlsInfo, nil 88 } 89 90 // ServerHandshake will always panic. We don't support running uTLS as server. 91 func (c *grpcUtls) ServerHandshake(net.Conn) (net.Conn, credentials.AuthInfo, error) { 92 panic("not available!") 93 } 94 95 func (c *grpcUtls) Clone() credentials.TransportCredentials { 96 return NewGrpcUtls(c.config, c.fingerprint) 97 } 98 99 func (c *grpcUtls) OverrideServerName(serverNameOverride string) error { 100 c.config.ServerName = serverNameOverride 101 return nil 102 } 103 104 // NewGrpcUtls uses c to construct a TransportCredentials based on uTLS. 105 func NewGrpcUtls(c *gotls.Config, fingerprint *utls.ClientHelloID) credentials.TransportCredentials { 106 tc := &grpcUtls{c.Clone(), fingerprint} 107 return tc 108 }