diff --git a/handshake/crypto_setup_client.go b/handshake/crypto_setup_client.go new file mode 100644 index 000000000..e3a40940b --- /dev/null +++ b/handshake/crypto_setup_client.go @@ -0,0 +1,91 @@ +package handshake + +import ( + "bytes" + "io" + + "github.com/lucas-clemente/quic-go/crypto" + "github.com/lucas-clemente/quic-go/protocol" + "github.com/lucas-clemente/quic-go/qerr" + "github.com/lucas-clemente/quic-go/utils" +) + +type cryptoSetupClient struct { + connID protocol.ConnectionID + version protocol.VersionNumber + + cryptoStream utils.Stream + + hasServerConfig bool +} + +var _ crypto.AEAD = &cryptoSetupClient{} +var _ CryptoSetup = &cryptoSetupClient{} + +// NewCryptoSetupClient creates a new CryptoSetup instance for a client +func NewCryptoSetupClient( + connID protocol.ConnectionID, + version protocol.VersionNumber, + cryptoStream utils.Stream, +) (CryptoSetup, error) { + return &cryptoSetupClient{ + connID: connID, + version: version, + cryptoStream: cryptoStream, + }, nil +} + +func (h *cryptoSetupClient) HandleCryptoStream() error { + h.sendInchoateCHLO() + + for { + var chloData bytes.Buffer + messageTag, cryptoData, err := ParseHandshakeMessage(io.TeeReader(h.cryptoStream, &chloData)) + _ = cryptoData + utils.Debugf("Received message on Crypto Stream. MessageTag: %#v", messageTag) + if err != nil { + return qerr.HandshakeFailed + } + } +} + +func (h *cryptoSetupClient) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) { + return (&crypto.NullAEAD{}).Open(dst, src, packetNumber, associatedData) +} + +func (h *cryptoSetupClient) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte { + return (&crypto.NullAEAD{}).Seal(dst, src, packetNumber, associatedData) +} + +func (h *cryptoSetupClient) DiversificationNonce() []byte { + return nil +} + +func (h *cryptoSetupClient) LockForSealing() { + +} + +func (h *cryptoSetupClient) UnlockForSealing() { + +} + +func (h *cryptoSetupClient) HandshakeComplete() bool { + return false +} + +func (h *cryptoSetupClient) sendInchoateCHLO() error { + b := &bytes.Buffer{} + + tags := make(map[Tag][]byte) + tags[TagSNI] = []byte("quic.clemente.io") // TODO: use real SNI here + tags[TagPDMD] = []byte("X509") + tags[TagPAD] = bytes.Repeat([]byte("0"), 1000) + + WriteHandshakeMessage(b, TagCHLO, tags) + + _, err := h.cryptoStream.Write(b.Bytes()) + if err != nil { + return err + } + return nil +} diff --git a/handshake/crypto_setup_client_test.go b/handshake/crypto_setup_client_test.go new file mode 100644 index 000000000..895aa563e --- /dev/null +++ b/handshake/crypto_setup_client_test.go @@ -0,0 +1 @@ +package handshake diff --git a/handshake/crypto_setup_server.go b/handshake/crypto_setup_server.go index 23d77790c..2c154c6c6 100644 --- a/handshake/crypto_setup_server.go +++ b/handshake/crypto_setup_server.go @@ -46,7 +46,7 @@ type cryptoSetupServer struct { var _ crypto.AEAD = &cryptoSetupServer{} -// NewCryptoSetup creates a new CryptoSetup instance +// NewCryptoSetup creates a new CryptoSetup instance for a server func NewCryptoSetup( connID protocol.ConnectionID, ip net.IP, diff --git a/session.go b/session.go index 24a161611..c7ba8b447 100644 --- a/session.go +++ b/session.go @@ -43,6 +43,7 @@ type closeCallback func(id protocol.ConnectionID) // A Session is a QUIC session type Session struct { connectionID protocol.ConnectionID + perspective protocol.Perspective version protocol.VersionNumber streamCallback StreamCallback @@ -95,56 +96,89 @@ type Session struct { // newSession makes a new session func newSession(conn connection, v protocol.VersionNumber, connectionID protocol.ConnectionID, sCfg *handshake.ServerConfig, streamCallback StreamCallback, closeCallback closeCallback) (packetHandler, error) { - connectionParameters := handshake.NewConnectionParamatersManager(v) - - var sentPacketHandler ackhandler.SentPacketHandler - rttStats := &congestion.RTTStats{} - - sentPacketHandler = ackhandler.NewSentPacketHandler(rttStats) - flowControlManager := flowcontrol.NewFlowControlManager(connectionParameters, rttStats) - - now := time.Now() session := &Session{ conn: conn, connectionID: connectionID, + perspective: protocol.PerspectiveServer, version: v, streamCallback: streamCallback, closeCallback: closeCallback, - - connectionParameters: connectionParameters, - sentPacketHandler: sentPacketHandler, - flowControlManager: flowControlManager, - - receivedPackets: make(chan *receivedPacket, protocol.MaxSessionUnprocessedPackets), - closeChan: make(chan *qerr.QuicError, 1), - sendingScheduled: make(chan struct{}, 1), - undecryptablePackets: make([]*receivedPacket, 0, protocol.MaxUndecryptablePackets), - aeadChanged: make(chan struct{}, 1), - runClosed: make(chan struct{}, 1), // this channel will receive once the run loop has been stopped - - timer: time.NewTimer(0), - lastNetworkActivityTime: now, - sessionCreationTime: now, } - session.receivedPacketHandler = ackhandler.NewReceivedPacketHandler(session.ackAlarmChanged) - session.streamsMap = newStreamsMap(session.newStream, session.connectionParameters) - + session.setup() cryptoStream, _ := session.GetOrOpenStream(1) + var err error session.cryptoSetup, err = handshake.NewCryptoSetup(connectionID, conn.RemoteAddr().IP, v, sCfg, cryptoStream, session.connectionParameters, session.aeadChanged) if err != nil { return nil, err } - session.streamFramer = newStreamFramer(session.streamsMap, flowControlManager) - session.packer = newPacketPacker(connectionID, session.cryptoSetup, session.connectionParameters, session.streamFramer, v) - session.unpacker = &packetUnpacker{aead: session.cryptoSetup, version: v} + session.packer = newPacketPacker(connectionID, session.cryptoSetup, session.connectionParameters, session.streamFramer, session.version) + session.unpacker = &packetUnpacker{aead: session.cryptoSetup, version: session.version} return session, err } +func newClientSession(conn *net.UDPConn, addr *net.UDPAddr, v protocol.VersionNumber, connectionID protocol.ConnectionID, streamCallback StreamCallback, closeCallback closeCallback) (*Session, error) { + session := &Session{ + conn: &udpConn{conn: conn, currentAddr: addr}, + connectionID: connectionID, + perspective: protocol.PerspectiveClient, + version: v, + + streamCallback: streamCallback, + closeCallback: closeCallback, + } + + session.receivedPacketHandler = ackhandler.NewReceivedPacketHandler(session.ackAlarmChanged) + session.setup() + + cryptoStream, _ := session.GetOrOpenStream(1) + var err error + session.cryptoSetup, err = handshake.NewCryptoSetupClient(connectionID, v, cryptoStream) + if err != nil { + return nil, err + } + + session.packer = newPacketPacker(connectionID, session.cryptoSetup, session.connectionParameters, session.streamFramer, session.version) + session.unpacker = &packetUnpacker{aead: session.cryptoSetup, version: session.version} + + return session, err +} + +// setup is called from newSession and newClientSession and initializes values that are independent of the perspective +func (s *Session) setup() { + s.rttStats = &congestion.RTTStats{} + connectionParameters := handshake.NewConnectionParamatersManager(s.version) + flowControlManager := flowcontrol.NewFlowControlManager(connectionParameters, s.rttStats) + + var sentPacketHandler ackhandler.SentPacketHandler + sentPacketHandler = ackhandler.NewSentPacketHandler(s.rttStats) + + now := time.Now() + + s.connectionParameters = connectionParameters + s.sentPacketHandler = sentPacketHandler + s.flowControlManager = flowControlManager + s.receivedPacketHandler = ackhandler.NewReceivedPacketHandler(s.ackAlarmChanged) + + s.receivedPackets = make(chan *receivedPacket, protocol.MaxSessionUnprocessedPackets) + s.closeChan = make(chan *qerr.QuicError, 1) + s.sendingScheduled = make(chan struct{}, 1) + s.undecryptablePackets = make([]*receivedPacket, 0, protocol.MaxUndecryptablePackets) + s.aeadChanged = make(chan struct{}, 1) + s.runClosed = make(chan struct{}, 1) + + s.timer = time.NewTimer(0) + s.lastNetworkActivityTime = now + s.sessionCreationTime = now + + s.streamsMap = newStreamsMap(s.newStream, s.connectionParameters) + s.streamFramer = newStreamFramer(s.streamsMap, s.flowControlManager) +} + // run the session main loop func (s *Session) run() { // Start the crypto stream handler