mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-06 05:37:36 +03:00
118 lines
3.4 KiB
Go
118 lines
3.4 KiB
Go
package handshake
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/tls"
|
|
"io"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/marten-seemann/qtls"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/congestion"
|
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
|
)
|
|
|
|
const clientSessionStateRevision = 1
|
|
|
|
type clientSessionCache struct {
|
|
tls.ClientSessionCache
|
|
rttStats *congestion.RTTStats
|
|
|
|
getAppData func() []byte
|
|
setAppData func([]byte)
|
|
}
|
|
|
|
func newClientSessionCache(
|
|
cache tls.ClientSessionCache,
|
|
rttStats *congestion.RTTStats,
|
|
get func() []byte,
|
|
set func([]byte),
|
|
) *clientSessionCache {
|
|
return &clientSessionCache{
|
|
ClientSessionCache: cache,
|
|
rttStats: rttStats,
|
|
getAppData: get,
|
|
setAppData: set,
|
|
}
|
|
}
|
|
|
|
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.
|
|
tlsSessBytes := (*[unsafe.Sizeof(*sess)]byte)(unsafe.Pointer(sess))[:]
|
|
var session clientSessionState
|
|
sessBytes := (*[unsafe.Sizeof(session)]byte)(unsafe.Pointer(&session))[:]
|
|
copy(sessBytes, tlsSessBytes)
|
|
r := bytes.NewReader(session.nonce)
|
|
rev, err := utils.ReadVarInt(r)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
if rev != clientSessionStateRevision {
|
|
return nil, false
|
|
}
|
|
rtt, err := utils.ReadVarInt(r)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
appDataLen, err := utils.ReadVarInt(r)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
appData := make([]byte, appDataLen)
|
|
if _, err := io.ReadFull(r, appData); err != nil {
|
|
return nil, false
|
|
}
|
|
nonceLen, err := utils.ReadVarInt(r)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
nonce := make([]byte, nonceLen)
|
|
if _, err := io.ReadFull(r, nonce); err != nil {
|
|
return nil, false
|
|
}
|
|
c.setAppData(appData)
|
|
session.nonce = nonce
|
|
c.rttStats.SetInitialRTT(time.Duration(rtt) * time.Microsecond)
|
|
var qtlsSession qtls.ClientSessionState
|
|
qtlsSessBytes := (*[unsafe.Sizeof(qtlsSession)]byte)(unsafe.Pointer(&qtlsSession))[:]
|
|
copy(qtlsSessBytes, sessBytes)
|
|
return &qtlsSession, 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.
|
|
qtlsSessBytes := (*[unsafe.Sizeof(*cs)]byte)(unsafe.Pointer(cs))[:]
|
|
var session clientSessionState
|
|
sessBytes := (*[unsafe.Sizeof(session)]byte)(unsafe.Pointer(&session))[:]
|
|
copy(sessBytes, qtlsSessBytes)
|
|
appData := c.getAppData()
|
|
buf := &bytes.Buffer{}
|
|
utils.WriteVarInt(buf, clientSessionStateRevision)
|
|
utils.WriteVarInt(buf, uint64(c.rttStats.SmoothedRTT().Microseconds()))
|
|
utils.WriteVarInt(buf, uint64(len(appData)))
|
|
buf.Write(appData)
|
|
utils.WriteVarInt(buf, uint64(len(session.nonce)))
|
|
buf.Write(session.nonce)
|
|
session.nonce = buf.Bytes()
|
|
var tlsSession tls.ClientSessionState
|
|
tlsSessBytes := (*[unsafe.Sizeof(tlsSession)]byte)(unsafe.Pointer(&tlsSession))[:]
|
|
copy(tlsSessBytes, sessBytes)
|
|
c.ClientSessionCache.Put(sessionKey, &tlsSession)
|
|
}
|