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