mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-04 20:57:36 +03:00
The RTTStats are used by the logging package. In order to instrument the congestion package, the RTTStats can't be part of that package any more (to avoid an import loop).
233 lines
7.7 KiB
Go
233 lines
7.7 KiB
Go
package handshake
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"net"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
|
|
|
"github.com/marten-seemann/qtls"
|
|
)
|
|
|
|
func init() {
|
|
if !structsEqual(&tls.ClientHelloInfo{}, &clientHelloInfo{}) {
|
|
panic("clientHelloInfo not compatible with tls.ClientHelloInfo")
|
|
}
|
|
if !structsEqual(&qtls.ClientHelloInfo{}, &qtlsClientHelloInfo{}) {
|
|
panic("qtlsClientHelloInfo not compatible with qtls.ClientHelloInfo")
|
|
}
|
|
}
|
|
|
|
type conn struct {
|
|
localAddr, remoteAddr net.Addr
|
|
}
|
|
|
|
func newConn(local, remote net.Addr) net.Conn {
|
|
return &conn{
|
|
localAddr: local,
|
|
remoteAddr: remote,
|
|
}
|
|
}
|
|
|
|
var _ net.Conn = &conn{}
|
|
|
|
func (c *conn) Read([]byte) (int, error) { return 0, nil }
|
|
func (c *conn) Write([]byte) (int, error) { return 0, nil }
|
|
func (c *conn) Close() error { return nil }
|
|
func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr }
|
|
func (c *conn) LocalAddr() net.Addr { return c.localAddr }
|
|
func (c *conn) SetReadDeadline(time.Time) error { return nil }
|
|
func (c *conn) SetWriteDeadline(time.Time) error { return nil }
|
|
func (c *conn) SetDeadline(time.Time) error { return nil }
|
|
|
|
func tlsConfigToQtlsConfig(
|
|
c *tls.Config,
|
|
recordLayer qtls.RecordLayer,
|
|
extHandler tlsExtensionHandler,
|
|
rttStats *utils.RTTStats,
|
|
getDataForSessionState func() []byte,
|
|
setDataFromSessionState func([]byte),
|
|
accept0RTT func([]byte) bool,
|
|
rejected0RTT func(),
|
|
enable0RTT bool,
|
|
) *qtls.Config {
|
|
if c == nil {
|
|
c = &tls.Config{}
|
|
}
|
|
// Clone the config first. This executes the tls.Config.serverInit().
|
|
// This sets the SessionTicketKey, if the user didn't supply one.
|
|
c = c.Clone()
|
|
// QUIC requires TLS 1.3 or newer
|
|
minVersion := c.MinVersion
|
|
if minVersion < qtls.VersionTLS13 {
|
|
minVersion = qtls.VersionTLS13
|
|
}
|
|
maxVersion := c.MaxVersion
|
|
if maxVersion < qtls.VersionTLS13 {
|
|
maxVersion = qtls.VersionTLS13
|
|
}
|
|
var getConfigForClient func(ch *qtls.ClientHelloInfo) (*qtls.Config, error)
|
|
if c.GetConfigForClient != nil {
|
|
getConfigForClient = func(ch *qtls.ClientHelloInfo) (*qtls.Config, error) {
|
|
tlsConf, err := c.GetConfigForClient(toTLSClientHelloInfo(ch))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if tlsConf == nil {
|
|
return nil, nil
|
|
}
|
|
return tlsConfigToQtlsConfig(tlsConf, recordLayer, extHandler, rttStats, getDataForSessionState, setDataFromSessionState, accept0RTT, rejected0RTT, enable0RTT), nil
|
|
}
|
|
}
|
|
var getCertificate func(ch *qtls.ClientHelloInfo) (*qtls.Certificate, error)
|
|
if c.GetCertificate != nil {
|
|
getCertificate = func(ch *qtls.ClientHelloInfo) (*qtls.Certificate, error) {
|
|
cert, err := c.GetCertificate(toTLSClientHelloInfo(ch))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if cert == nil {
|
|
return nil, nil
|
|
}
|
|
return (*qtls.Certificate)(unsafe.Pointer(cert)), nil
|
|
}
|
|
}
|
|
var csc qtls.ClientSessionCache
|
|
if c.ClientSessionCache != nil {
|
|
csc = &clientSessionCache{c.ClientSessionCache}
|
|
}
|
|
conf := &qtls.Config{
|
|
Rand: c.Rand,
|
|
Time: c.Time,
|
|
Certificates: *(*[]qtls.Certificate)(unsafe.Pointer(&c.Certificates)),
|
|
// NameToCertificate is deprecated, but we still need to copy it if the user sets it.
|
|
//nolint:staticcheck
|
|
NameToCertificate: *(*map[string]*qtls.Certificate)(unsafe.Pointer(&c.NameToCertificate)),
|
|
GetCertificate: getCertificate,
|
|
GetClientCertificate: *(*func(*qtls.CertificateRequestInfo) (*qtls.Certificate, error))(unsafe.Pointer(&c.GetClientCertificate)),
|
|
GetConfigForClient: getConfigForClient,
|
|
VerifyPeerCertificate: c.VerifyPeerCertificate,
|
|
RootCAs: c.RootCAs,
|
|
NextProtos: c.NextProtos,
|
|
EnforceNextProtoSelection: true,
|
|
ServerName: c.ServerName,
|
|
ClientAuth: c.ClientAuth,
|
|
ClientCAs: c.ClientCAs,
|
|
InsecureSkipVerify: c.InsecureSkipVerify,
|
|
CipherSuites: c.CipherSuites,
|
|
PreferServerCipherSuites: c.PreferServerCipherSuites,
|
|
SessionTicketsDisabled: c.SessionTicketsDisabled,
|
|
SessionTicketKey: c.SessionTicketKey,
|
|
ClientSessionCache: csc,
|
|
MinVersion: minVersion,
|
|
MaxVersion: maxVersion,
|
|
CurvePreferences: c.CurvePreferences,
|
|
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
|
|
// no need to copy Renegotiation, it's not supported by TLS 1.3
|
|
KeyLogWriter: c.KeyLogWriter,
|
|
AlternativeRecordLayer: recordLayer,
|
|
GetExtensions: extHandler.GetExtensions,
|
|
ReceivedExtensions: extHandler.ReceivedExtensions,
|
|
Accept0RTT: accept0RTT,
|
|
Rejected0RTT: rejected0RTT,
|
|
GetAppDataForSessionState: getDataForSessionState,
|
|
SetAppDataFromSessionState: setDataFromSessionState,
|
|
}
|
|
if enable0RTT {
|
|
conf.Enable0RTT = true
|
|
conf.MaxEarlyData = 0xffffffff
|
|
}
|
|
return conf
|
|
}
|
|
|
|
type clientSessionCache struct {
|
|
tls.ClientSessionCache
|
|
}
|
|
|
|
var _ qtls.ClientSessionCache = &clientSessionCache{}
|
|
|
|
func (c *clientSessionCache) Get(sessionKey string) (*qtls.ClientSessionState, bool) {
|
|
sess, ok := c.ClientSessionCache.Get(sessionKey)
|
|
if sess == nil {
|
|
return nil, ok
|
|
}
|
|
// qtls.ClientSessionState is identical to the tls.ClientSessionState.
|
|
// In order to allow users of quic-go to use a tls.Config,
|
|
// we need this workaround to use the ClientSessionCache.
|
|
// In unsafe.go we check that the two structs are actually identical.
|
|
return (*qtls.ClientSessionState)(unsafe.Pointer(sess)), ok
|
|
}
|
|
|
|
func (c *clientSessionCache) Put(sessionKey string, cs *qtls.ClientSessionState) {
|
|
if cs == nil {
|
|
c.ClientSessionCache.Put(sessionKey, nil)
|
|
return
|
|
}
|
|
// qtls.ClientSessionState is identical to the tls.ClientSessionState.
|
|
// In order to allow users of quic-go to use a tls.Config,
|
|
// we need this workaround to use the ClientSessionCache.
|
|
// In unsafe.go we check that the two structs are actually identical.
|
|
c.ClientSessionCache.Put(sessionKey, (*tls.ClientSessionState)(unsafe.Pointer(cs)))
|
|
}
|
|
|
|
type clientHelloInfo struct {
|
|
CipherSuites []uint16
|
|
ServerName string
|
|
SupportedCurves []tls.CurveID
|
|
SupportedPoints []uint8
|
|
SignatureSchemes []tls.SignatureScheme
|
|
SupportedProtos []string
|
|
SupportedVersions []uint16
|
|
Conn net.Conn
|
|
|
|
config *tls.Config
|
|
}
|
|
|
|
type qtlsClientHelloInfo struct {
|
|
CipherSuites []uint16
|
|
ServerName string
|
|
SupportedCurves []tls.CurveID
|
|
SupportedPoints []uint8
|
|
SignatureSchemes []tls.SignatureScheme
|
|
SupportedProtos []string
|
|
SupportedVersions []uint16
|
|
Conn net.Conn
|
|
|
|
config *qtls.Config
|
|
}
|
|
|
|
func toTLSClientHelloInfo(chi *qtls.ClientHelloInfo) *tls.ClientHelloInfo {
|
|
if chi == nil {
|
|
return nil
|
|
}
|
|
qtlsCHI := (*qtlsClientHelloInfo)(unsafe.Pointer(chi))
|
|
var config *tls.Config
|
|
if qtlsCHI.config != nil {
|
|
config = qtlsConfigToTLSConfig((*qtls.Config)(unsafe.Pointer(qtlsCHI.config)))
|
|
}
|
|
return (*tls.ClientHelloInfo)(unsafe.Pointer(&clientHelloInfo{
|
|
CipherSuites: chi.CipherSuites,
|
|
ServerName: chi.ServerName,
|
|
SupportedCurves: chi.SupportedCurves,
|
|
SupportedPoints: chi.SupportedPoints,
|
|
SignatureSchemes: chi.SignatureSchemes,
|
|
SupportedProtos: chi.SupportedProtos,
|
|
SupportedVersions: chi.SupportedVersions,
|
|
Conn: chi.Conn,
|
|
config: config,
|
|
}))
|
|
}
|
|
|
|
// qtlsConfigToTLSConfig is used to transform a qtls.Config to a tls.Config.
|
|
// It is used to create the tls.Config in the ClientHelloInfo.
|
|
// It doesn't copy all values, but only those used by ClientHelloInfo.SupportsCertificate.
|
|
func qtlsConfigToTLSConfig(config *qtls.Config) *tls.Config {
|
|
return &tls.Config{
|
|
MinVersion: config.MinVersion,
|
|
MaxVersion: config.MaxVersion,
|
|
CipherSuites: config.CipherSuites,
|
|
CurvePreferences: config.CurvePreferences,
|
|
}
|
|
}
|