2018-01-03 19:19:49 +00:00
|
|
|
package quic
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
|
|
|
|
"github.com/bifurcation/mint"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/crypto"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/handshake"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/wire"
|
2018-01-20 18:07:01 +00:00
|
|
|
"github.com/lucas-clemente/quic-go/qerr"
|
2018-01-03 19:19:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type nullAEAD struct {
|
|
|
|
aead crypto.AEAD
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ quicAEAD = &nullAEAD{}
|
|
|
|
|
|
|
|
func (n *nullAEAD) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
|
|
|
|
data, err := n.aead.Open(dst, src, packetNumber, associatedData)
|
|
|
|
return data, protocol.EncryptionUnencrypted, err
|
|
|
|
}
|
|
|
|
|
|
|
|
type serverTLS struct {
|
|
|
|
conn net.PacketConn
|
|
|
|
config *Config
|
|
|
|
supportedVersions []protocol.VersionNumber
|
|
|
|
mintConf *mint.Config
|
|
|
|
cookieProtector mint.CookieProtector
|
|
|
|
params *handshake.TransportParameters
|
|
|
|
newMintConn func(*handshake.CryptoStreamConn, protocol.VersionNumber) (handshake.MintTLS, <-chan handshake.TransportParameters, error)
|
|
|
|
|
|
|
|
sessionChan chan<- packetHandler
|
|
|
|
}
|
|
|
|
|
|
|
|
func newServerTLS(
|
|
|
|
conn net.PacketConn,
|
|
|
|
config *Config,
|
|
|
|
cookieHandler *handshake.CookieHandler,
|
|
|
|
tlsConf *tls.Config,
|
|
|
|
) (*serverTLS, <-chan packetHandler, error) {
|
|
|
|
mconf, err := tlsToMintConfig(tlsConf, protocol.PerspectiveServer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
mconf.RequireCookie = true
|
|
|
|
cs, err := mint.NewDefaultCookieProtector()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
mconf.CookieProtector = cs
|
|
|
|
mconf.CookieHandler = cookieHandler
|
|
|
|
|
|
|
|
sessionChan := make(chan packetHandler)
|
|
|
|
s := &serverTLS{
|
|
|
|
conn: conn,
|
|
|
|
config: config,
|
|
|
|
supportedVersions: config.Versions,
|
|
|
|
mintConf: mconf,
|
|
|
|
sessionChan: sessionChan,
|
|
|
|
params: &handshake.TransportParameters{
|
|
|
|
StreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow,
|
|
|
|
ConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow,
|
|
|
|
MaxStreams: protocol.MaxIncomingStreams,
|
|
|
|
IdleTimeout: config.IdleTimeout,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
s.newMintConn = s.newMintConnImpl
|
|
|
|
return s, sessionChan, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *serverTLS) HandleInitial(remoteAddr net.Addr, hdr *wire.Header, data []byte) {
|
|
|
|
utils.Debugf("Received a Packet. Handling it statelessly.")
|
|
|
|
sess, err := s.handleInitialImpl(remoteAddr, hdr, data)
|
|
|
|
if err != nil {
|
|
|
|
utils.Errorf("Error occured handling initial packet: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if sess == nil { // a stateless reset was done
|
|
|
|
return
|
|
|
|
}
|
|
|
|
s.sessionChan <- sess
|
|
|
|
}
|
|
|
|
|
|
|
|
// will be set to s.newMintConn by the constructor
|
|
|
|
func (s *serverTLS) newMintConnImpl(bc *handshake.CryptoStreamConn, v protocol.VersionNumber) (handshake.MintTLS, <-chan handshake.TransportParameters, error) {
|
|
|
|
conn := mint.Server(bc, s.mintConf)
|
|
|
|
extHandler := handshake.NewExtensionHandlerServer(s.params, s.config.Versions, v)
|
|
|
|
if err := conn.SetExtensionHandler(extHandler); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
tls := newMintController(bc, s.mintConf, protocol.PerspectiveServer)
|
|
|
|
tls.SetExtensionHandler(extHandler)
|
|
|
|
return tls, extHandler.GetPeerParams(), nil
|
|
|
|
}
|
|
|
|
|
2018-01-20 18:07:01 +00:00
|
|
|
func (s *serverTLS) sendConnectionClose(remoteAddr net.Addr, clientHdr *wire.Header, aead crypto.AEAD, closeErr error) error {
|
|
|
|
ccf := &wire.ConnectionCloseFrame{
|
|
|
|
ErrorCode: qerr.HandshakeFailed,
|
|
|
|
ReasonPhrase: closeErr.Error(),
|
|
|
|
}
|
|
|
|
replyHdr := &wire.Header{
|
|
|
|
IsLongHeader: true,
|
|
|
|
Type: protocol.PacketTypeHandshake,
|
|
|
|
ConnectionID: clientHdr.ConnectionID, // echo the client's connection ID
|
|
|
|
PacketNumber: 1, // random packet number
|
|
|
|
Version: clientHdr.Version,
|
|
|
|
}
|
|
|
|
data, err := packUnencryptedPacket(aead, replyHdr, ccf, protocol.PerspectiveServer)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = s.conn.WriteTo(data, remoteAddr)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-01-03 19:19:49 +00:00
|
|
|
func (s *serverTLS) handleInitialImpl(remoteAddr net.Addr, hdr *wire.Header, data []byte) (packetHandler, error) {
|
|
|
|
if len(hdr.Raw)+len(data) < protocol.MinInitialPacketSize {
|
|
|
|
return nil, errors.New("dropping too small Initial packet")
|
|
|
|
}
|
|
|
|
// check version, if not matching send VNP
|
|
|
|
if !protocol.IsSupportedVersion(s.supportedVersions, hdr.Version) {
|
|
|
|
utils.Debugf("Client offered version %s, sending VersionNegotiationPacket", hdr.Version)
|
|
|
|
_, err := s.conn.WriteTo(wire.ComposeVersionNegotiation(hdr.ConnectionID, hdr.PacketNumber, s.supportedVersions), remoteAddr)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// unpack packet and check stream frame contents
|
2018-01-20 18:07:01 +00:00
|
|
|
aead, err := crypto.NewNullAEAD(protocol.PerspectiveServer, hdr.ConnectionID, hdr.Version)
|
2018-01-03 19:19:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-20 18:07:01 +00:00
|
|
|
frame, err := unpackInitialPacket(aead, hdr, data, hdr.Version)
|
2018-01-03 19:19:49 +00:00
|
|
|
if err != nil {
|
|
|
|
utils.Debugf("Error unpacking initial packet: %s", err)
|
|
|
|
return nil, nil
|
|
|
|
}
|
2018-01-20 18:07:01 +00:00
|
|
|
sess, err := s.handleUnpackedInitial(remoteAddr, hdr, frame, aead)
|
|
|
|
if err != nil {
|
|
|
|
if ccerr := s.sendConnectionClose(remoteAddr, hdr, aead, err); ccerr != nil {
|
|
|
|
utils.Debugf("Error sending CONNECTION_CLOSE: ", ccerr)
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return sess, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *serverTLS) handleUnpackedInitial(remoteAddr net.Addr, hdr *wire.Header, frame *wire.StreamFrame, aead crypto.AEAD) (packetHandler, error) {
|
|
|
|
version := hdr.Version
|
2018-01-03 19:19:49 +00:00
|
|
|
bc := handshake.NewCryptoStreamConn(remoteAddr)
|
|
|
|
bc.AddDataForReading(frame.Data)
|
2018-01-20 18:07:01 +00:00
|
|
|
tls, paramsChan, err := s.newMintConn(bc, version)
|
2018-01-03 19:19:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
alert := tls.Handshake()
|
|
|
|
if alert == mint.AlertStatelessRetry {
|
|
|
|
// the HelloRetryRequest was written to the bufferConn
|
|
|
|
// Take that data and write send a Retry packet
|
|
|
|
replyHdr := &wire.Header{
|
|
|
|
IsLongHeader: true,
|
|
|
|
Type: protocol.PacketTypeRetry,
|
|
|
|
ConnectionID: hdr.ConnectionID, // echo the client's connection ID
|
|
|
|
PacketNumber: hdr.PacketNumber, // echo the client's packet number
|
|
|
|
Version: version,
|
|
|
|
}
|
|
|
|
f := &wire.StreamFrame{
|
|
|
|
StreamID: version.CryptoStreamID(),
|
|
|
|
Data: bc.GetDataForWriting(),
|
|
|
|
}
|
|
|
|
data, err := packUnencryptedPacket(aead, replyHdr, f, protocol.PerspectiveServer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
_, err = s.conn.WriteTo(data, remoteAddr)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if alert != mint.AlertNoAlert {
|
|
|
|
return nil, alert
|
|
|
|
}
|
|
|
|
if tls.State() != mint.StateServerNegotiated {
|
|
|
|
return nil, fmt.Errorf("Expected mint state to be %s, got %s", mint.StateServerNegotiated, tls.State())
|
|
|
|
}
|
|
|
|
if alert := tls.Handshake(); alert != mint.AlertNoAlert {
|
|
|
|
return nil, alert
|
|
|
|
}
|
|
|
|
if tls.State() != mint.StateServerWaitFlight2 {
|
|
|
|
return nil, fmt.Errorf("Expected mint state to be %s, got %s", mint.StateServerWaitFlight2, tls.State())
|
|
|
|
}
|
|
|
|
params := <-paramsChan
|
|
|
|
sess, err := newTLSServerSession(
|
|
|
|
&conn{pconn: s.conn, currentAddr: remoteAddr},
|
|
|
|
hdr.ConnectionID, // TODO: we can use a server-chosen connection ID here
|
|
|
|
protocol.PacketNumber(1), // TODO: use a random packet number here
|
|
|
|
s.config,
|
|
|
|
tls,
|
|
|
|
bc,
|
|
|
|
aead,
|
|
|
|
¶ms,
|
|
|
|
version,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cs := sess.getCryptoStream()
|
2018-01-20 18:07:01 +00:00
|
|
|
cs.setReadOffset(frame.DataLen())
|
2018-01-03 19:19:49 +00:00
|
|
|
bc.SetStream(cs)
|
|
|
|
return sess, nil
|
|
|
|
}
|