uquic/packet_unpacker_test.go
Marten Seemann 759b0d87b1 refactor packet unpacking
This replaces version.UsesMaxDataFrame by versoin.UsesIETFFrameFormat.
That way, we can have two separate code paths in the unpacker to unpack
either gQUIC frames or IETF frames.
2017-12-08 09:18:54 +07:00

387 lines
12 KiB
Go

package quic
import (
"bytes"
"github.com/lucas-clemente/quic-go/internal/crypto"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/wire"
"github.com/lucas-clemente/quic-go/qerr"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type mockAEAD struct {
encLevelOpen protocol.EncryptionLevel
}
func (m *mockAEAD) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveClient, 0x1337, protocol.VersionWhatever)
Expect(err).ToNot(HaveOccurred())
res, err := nullAEAD.Open(dst, src, packetNumber, associatedData)
return res, m.encLevelOpen, err
}
func (m *mockAEAD) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel) {
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveServer, 0x1337, protocol.VersionWhatever)
Expect(err).ToNot(HaveOccurred())
return nullAEAD.Seal(dst, src, packetNumber, associatedData), protocol.EncryptionUnspecified
}
var _ quicAEAD = &mockAEAD{}
var _ = Describe("Packet unpacker", func() {
var (
unpacker *packetUnpacker
hdr *wire.Header
hdrBin []byte
data []byte
buf *bytes.Buffer
)
BeforeEach(func() {
hdr = &wire.Header{
PacketNumber: 10,
PacketNumberLen: 1,
}
hdrBin = []byte{0x04, 0x4c, 0x01}
unpacker = &packetUnpacker{aead: &mockAEAD{}}
data = nil
buf = &bytes.Buffer{}
})
setData := func(p []byte) {
data, _ = unpacker.aead.(*mockAEAD).Seal(nil, p, 0, hdrBin)
}
It("errors if the packet doesn't contain any payload", func() {
setData(nil)
_, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).To(MatchError(qerr.MissingPayload))
})
It("saves the encryption level", func() {
unpacker.version = versionGQUICFrames
f := &wire.ConnectionCloseFrame{ReasonPhrase: "foo"}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionSecure
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.encryptionLevel).To(Equal(protocol.EncryptionSecure))
})
Context("for gQUIC frames", func() {
BeforeEach(func() {
unpacker.version = versionGQUICFrames
})
It("handles PADDING frames", func() {
setData([]byte{0, 0, 0}) // 3 bytes PADDING
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(BeEmpty())
})
It("handles PADDING between two other frames", func() {
f := &wire.PingFrame{}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
_, err = buf.Write(bytes.Repeat([]byte{0}, 10)) // 10 bytes PADDING
Expect(err).ToNot(HaveOccurred())
err = f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(HaveLen(2))
})
It("unpacks RST_STREAM frames", func() {
f := &wire.RstStreamFrame{
StreamID: 0xdeadbeef,
ByteOffset: 0xdecafbad11223344,
ErrorCode: 0x13371234,
}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks CONNECTION_CLOSE frames", func() {
f := &wire.ConnectionCloseFrame{ReasonPhrase: "foo"}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks GOAWAY frames", func() {
f := &wire.GoawayFrame{
ErrorCode: 1,
LastGoodStream: 2,
ReasonPhrase: "foo",
}
err := f.Write(buf, 0)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks a stream-level WINDOW_UPDATE frame", func() {
f := &wire.MaxStreamDataFrame{
StreamID: 0xdeadbeef,
ByteOffset: 0xcafe000000001337,
}
buf := &bytes.Buffer{}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks a connection-level WINDOW_UPDATE frame", func() {
f := &wire.MaxDataFrame{
ByteOffset: 0xcafe000000001337,
}
buf := &bytes.Buffer{}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks connection-level BLOCKED frames", func() {
f := &wire.BlockedFrame{}
buf := &bytes.Buffer{}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks stream-level BLOCKED frames", func() {
f := &wire.StreamBlockedFrame{StreamID: 0xdeadbeef}
buf := &bytes.Buffer{}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks STOP_WAITING frames", func() {
setData([]byte{0x06, 0x03})
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{
&wire.StopWaitingFrame{LeastUnacked: 7},
}))
})
It("unpacks PING frames", func() {
setData([]byte{0x07})
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{
&wire.PingFrame{},
}))
})
It("errors on invalid type", func() {
setData([]byte{0xf})
_, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).To(MatchError("InvalidFrameData: unknown type byte 0xf"))
})
It("errors on invalid frames", func() {
for b, e := range map[byte]qerr.ErrorCode{
0x80: qerr.InvalidStreamData,
0x40: qerr.InvalidAckData,
0x01: qerr.InvalidRstStreamData,
0x02: qerr.InvalidConnectionCloseData,
0x03: qerr.InvalidGoawayData,
0x04: qerr.InvalidWindowUpdateData,
0x05: qerr.InvalidBlockedData,
0x06: qerr.InvalidStopWaitingData,
} {
setData([]byte{b})
_, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e))
}
})
It("unpacks ACK frames", func() {
f := &wire.AckFrame{
LargestAcked: 0x13,
LowestAcked: 1,
}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(HaveLen(1))
readFrame := packet.frames[0].(*wire.AckFrame)
Expect(readFrame).ToNot(BeNil())
Expect(readFrame.LargestAcked).To(Equal(protocol.PacketNumber(0x13)))
})
Context("unpacking STREAM frames", func() {
It("unpacks unencrypted STREAM frames on the crypto stream", func() {
unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionUnencrypted
f := &wire.StreamFrame{
StreamID: versionGQUICFrames.CryptoStreamID(),
Data: []byte("foobar"),
}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks encrypted STREAM frames on the crypto stream", func() {
unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionSecure
f := &wire.StreamFrame{
StreamID: versionGQUICFrames.CryptoStreamID(),
Data: []byte("foobar"),
}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("does not unpack unencrypted STREAM frames on higher streams", func() {
unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionUnencrypted
f := &wire.StreamFrame{
StreamID: 3,
Data: []byte("foobar"),
}
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
_, err = unpacker.Unpack(hdrBin, hdr, data)
Expect(err).To(MatchError(qerr.Error(qerr.UnencryptedStreamData, "received unencrypted stream data on stream 3")))
})
})
})
Context("for IETF draft frames", func() {
BeforeEach(func() {
unpacker.version = versionIETFFrames
})
It("unpacks RST_STREAM frames", func() {
f := &wire.RstStreamFrame{
StreamID: 0xdeadbeef,
ByteOffset: 0xdecafbad11223344,
ErrorCode: 0x13371234,
}
err := f.Write(buf, versionIETFFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks CONNECTION_CLOSE frames", func() {
f := &wire.ConnectionCloseFrame{ReasonPhrase: "foo"}
err := f.Write(buf, versionIETFFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks MAX_DATA frames", func() {
f := &wire.MaxDataFrame{
ByteOffset: 0xcafe000000001337,
}
buf := &bytes.Buffer{}
err := f.Write(buf, versionIETFFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks MAX_STREAM_DATA frames", func() {
f := &wire.MaxStreamDataFrame{
StreamID: 0xdeadbeef,
ByteOffset: 0xcafe000000001337,
}
buf := &bytes.Buffer{}
err := f.Write(buf, versionIETFFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks connection-level BLOCKED frames", func() {
f := &wire.BlockedFrame{}
buf := &bytes.Buffer{}
err := f.Write(buf, versionIETFFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("unpacks stream-level BLOCKED frames", func() {
f := &wire.StreamBlockedFrame{StreamID: 0xdeadbeef}
buf := &bytes.Buffer{}
err := f.Write(buf, versionIETFFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
It("errors on invalid type", func() {
setData([]byte{0xf})
_, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err).To(MatchError("InvalidFrameData: unknown type byte 0xf"))
})
It("errors on invalid frames", func() {
for b, e := range map[byte]qerr.ErrorCode{
0x01: qerr.InvalidRstStreamData,
0x02: qerr.InvalidConnectionCloseData,
0x04: qerr.InvalidWindowUpdateData,
0x05: qerr.InvalidWindowUpdateData,
0x09: qerr.InvalidBlockedData,
} {
setData([]byte{b})
_, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e))
}
})
})
})