forked from quic-go/quic-go
add a method to pack a retransmission in the packetPacker
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/ackhandler"
|
||||
"github.com/lucas-clemente/quic-go/frames"
|
||||
"github.com/lucas-clemente/quic-go/handshake"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
@@ -51,7 +52,19 @@ func (p *packetPacker) PackConnectionClose(ccf *frames.ConnectionCloseFrame, lea
|
||||
// in case the connection is closed, all queued control frames aren't of any use anymore
|
||||
// discard them and queue the ConnectionCloseFrame
|
||||
p.controlFrames = []frames.Frame{ccf}
|
||||
return p.packPacket(nil, leastUnacked)
|
||||
return p.packPacket(nil, leastUnacked, nil)
|
||||
}
|
||||
|
||||
// RetransmitNonForwardSecurePacket retransmits a handshake packet, that was sent with less than forward-secure encryption
|
||||
func (p *packetPacker) RetransmitNonForwardSecurePacket(stopWaitingFrame *frames.StopWaitingFrame, packet *ackhandler.Packet) (*packedPacket, error) {
|
||||
if packet.EncryptionLevel == protocol.EncryptionForwardSecure {
|
||||
return nil, errors.New("PacketPacker BUG: forward-secure encrypted handshake packets don't need special treatment")
|
||||
}
|
||||
if stopWaitingFrame == nil {
|
||||
return nil, errors.New("PacketPacker BUG: Handshake retransmissions must contain a StopWaitingFrame")
|
||||
}
|
||||
|
||||
return p.packPacket(stopWaitingFrame, 0, packet)
|
||||
}
|
||||
|
||||
// PackPacket packs a new packet
|
||||
@@ -59,10 +72,12 @@ func (p *packetPacker) PackConnectionClose(ccf *frames.ConnectionCloseFrame, lea
|
||||
// the other controlFrames are sent in the next packet, but might be queued and sent in the next packet if the packet would overflow MaxPacketSize otherwise
|
||||
func (p *packetPacker) PackPacket(stopWaitingFrame *frames.StopWaitingFrame, controlFrames []frames.Frame, leastUnacked protocol.PacketNumber) (*packedPacket, error) {
|
||||
p.controlFrames = append(p.controlFrames, controlFrames...)
|
||||
return p.packPacket(stopWaitingFrame, leastUnacked)
|
||||
return p.packPacket(stopWaitingFrame, leastUnacked, nil)
|
||||
}
|
||||
|
||||
func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, leastUnacked protocol.PacketNumber) (*packedPacket, error) {
|
||||
func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, leastUnacked protocol.PacketNumber, packetToRetransmit *ackhandler.Packet) (*packedPacket, error) {
|
||||
// packetToRetransmit is only set for handshake retransmissions
|
||||
isHandshakeRetransmission := (packetToRetransmit != nil)
|
||||
// cryptoSetup needs to be locked here, so that the AEADs are not changed between
|
||||
// calling DiversificationNonce() and Seal().
|
||||
p.cryptoSetup.LockForSealing()
|
||||
@@ -103,7 +118,19 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea
|
||||
}
|
||||
|
||||
var payloadFrames []frames.Frame
|
||||
if isConnectionClose {
|
||||
if isHandshakeRetransmission {
|
||||
payloadFrames = append(payloadFrames, stopWaitingFrame)
|
||||
// don't retransmit Acks and StopWaitings
|
||||
for _, f := range packetToRetransmit.Frames {
|
||||
switch f.(type) {
|
||||
case *frames.AckFrame:
|
||||
continue
|
||||
case *frames.StopWaitingFrame:
|
||||
continue
|
||||
}
|
||||
payloadFrames = append(payloadFrames, f)
|
||||
}
|
||||
} else if isConnectionClose {
|
||||
payloadFrames = []frames.Frame{p.controlFrames[0]}
|
||||
} else {
|
||||
maxSize := protocol.MaxFrameAndPublicHeaderSize - publicHeaderLength
|
||||
@@ -139,7 +166,7 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea
|
||||
if sf, ok := frame.(*frames.StreamFrame); ok && sf.StreamID != 1 {
|
||||
hasNonCryptoStreamData = true
|
||||
}
|
||||
err := frame.Write(buffer, p.version)
|
||||
err = frame.Write(buffer, p.version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -150,7 +177,16 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea
|
||||
}
|
||||
|
||||
raw = raw[0:buffer.Len()]
|
||||
_, encryptionLevel := p.cryptoSetup.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex])
|
||||
var encryptionLevel protocol.EncryptionLevel
|
||||
if isHandshakeRetransmission {
|
||||
var err error
|
||||
_, encryptionLevel, err = p.cryptoSetup.SealWith(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex], packetToRetransmit.EncryptionLevel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
_, encryptionLevel = p.cryptoSetup.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex])
|
||||
}
|
||||
raw = raw[0 : buffer.Len()+12]
|
||||
|
||||
if hasNonCryptoStreamData && encryptionLevel <= protocol.EncryptionUnencrypted {
|
||||
|
||||
@@ -3,6 +3,7 @@ package quic
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/ackhandler"
|
||||
"github.com/lucas-clemente/quic-go/frames"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
@@ -25,7 +26,7 @@ func (m *mockCryptoSetup) Seal(dst, src []byte, packetNumber protocol.PacketNumb
|
||||
return append(src, bytes.Repeat([]byte{0}, 12)...), m.encLevelSeal
|
||||
}
|
||||
func (m *mockCryptoSetup) SealWith(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte, encLevel protocol.EncryptionLevel) ([]byte, protocol.EncryptionLevel, error) {
|
||||
panic("not implemented")
|
||||
return append(src, bytes.Repeat([]byte{0}, 12)...), encLevel, nil
|
||||
}
|
||||
func (m *mockCryptoSetup) LockForSealing() {}
|
||||
func (m *mockCryptoSetup) UnlockForSealing() {}
|
||||
@@ -569,4 +570,98 @@ var _ = Describe("Packet packer", func() {
|
||||
Expect(p.frames).To(HaveLen(1))
|
||||
Expect(p.frames[0]).To(Equal(wuf))
|
||||
})
|
||||
|
||||
Context("retransmitting of handshake packets", func() {
|
||||
swf := &frames.StopWaitingFrame{LeastUnacked: 1}
|
||||
sf := &frames.StreamFrame{
|
||||
StreamID: 1,
|
||||
Data: []byte("foobar"),
|
||||
}
|
||||
|
||||
It("packs a retransmission for a packet sent with no encryption", func() {
|
||||
packet := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionUnencrypted,
|
||||
Frames: []frames.Frame{sf},
|
||||
}
|
||||
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p.frames).To(ContainElement(sf))
|
||||
Expect(p.frames).To(ContainElement(swf))
|
||||
Expect(p.encryptionLevel).To(Equal(protocol.EncryptionUnencrypted))
|
||||
})
|
||||
|
||||
It("packs a retransmission for a packet sent with initial encryption", func() {
|
||||
packet := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
Frames: []frames.Frame{sf},
|
||||
}
|
||||
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p.frames).To(ContainElement(sf))
|
||||
Expect(p.frames).To(ContainElement(swf))
|
||||
Expect(p.encryptionLevel).To(Equal(protocol.EncryptionSecure))
|
||||
})
|
||||
|
||||
It("removes non-retransmittable frames", func() {
|
||||
wuf := &frames.WindowUpdateFrame{StreamID: 5, ByteOffset: 10}
|
||||
packet := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
Frames: []frames.Frame{
|
||||
sf,
|
||||
&frames.StopWaitingFrame{},
|
||||
wuf,
|
||||
&frames.AckFrame{},
|
||||
},
|
||||
}
|
||||
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p.frames).To(HaveLen(3))
|
||||
Expect(p.frames).To(ContainElement(sf))
|
||||
Expect(p.frames).To(ContainElement(swf))
|
||||
Expect(p.frames).To(ContainElement(wuf))
|
||||
Expect(p.encryptionLevel).To(Equal(protocol.EncryptionSecure))
|
||||
})
|
||||
|
||||
It("doesn't pack a packet for a non-retransmittable packet", func() {
|
||||
packet := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
Frames: []frames.Frame{&frames.AckFrame{}, &frames.StopWaitingFrame{}},
|
||||
}
|
||||
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p).To(BeNil())
|
||||
})
|
||||
|
||||
// this should never happen, since non forward-secure packets are limited to a size smaller than MaxPacketSize, such that it is always possible to retransmit them without splitting the StreamFrame
|
||||
// (note that the retransmitted packet needs to have enough space for the StopWaitingFrame)
|
||||
It("refuses to send a packet larger than MaxPacketSize", func() {
|
||||
packet := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
Frames: []frames.Frame{
|
||||
&frames.StreamFrame{
|
||||
StreamID: 1,
|
||||
Data: bytes.Repeat([]byte{'f'}, int(protocol.MaxPacketSize-5)),
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
|
||||
Expect(err).To(MatchError("PacketPacker BUG: packet too large"))
|
||||
})
|
||||
|
||||
It("refuses to retransmit packets that were sent with forward-secure encryption", func() {
|
||||
p := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
}
|
||||
_, err := packer.RetransmitNonForwardSecurePacket(nil, p)
|
||||
Expect(err).To(MatchError("PacketPacker BUG: forward-secure encrypted handshake packets don't need special treatment"))
|
||||
})
|
||||
|
||||
It("refuses to retransmit packets without a StopWaitingFrame", func() {
|
||||
p := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
}
|
||||
_, err := packer.RetransmitNonForwardSecurePacket(nil, p)
|
||||
Expect(err).To(MatchError("PacketPacker BUG: Handshake retransmissions must contain a StopWaitingFrame"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user