diff --git a/internal/wire/frame_parser.go b/internal/wire/frame_parser.go new file mode 100644 index 00000000..9745a9ba --- /dev/null +++ b/internal/wire/frame_parser.go @@ -0,0 +1,152 @@ +package wire + +import ( + "bytes" + "fmt" + + "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/qerr" +) + +// ParseNextFrame parses the next frame +// It skips PADDING frames. +func ParseNextFrame(r *bytes.Reader, hdr *Header, v protocol.VersionNumber) (Frame, error) { + if r.Len() == 0 { + return nil, nil + } + typeByte, _ := r.ReadByte() + if typeByte == 0x0 { // PADDING frame + return ParseNextFrame(r, hdr, v) + } + r.UnreadByte() + + if !v.UsesIETFFrameFormat() { + return parseGQUICFrame(r, typeByte, hdr, v) + } + return parseIETFFrame(r, typeByte, v) +} + +func parseIETFFrame(r *bytes.Reader, typeByte byte, v protocol.VersionNumber) (Frame, error) { + var frame Frame + var err error + if typeByte&0xf8 == 0x10 { + frame, err = ParseStreamFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidStreamData, err.Error()) + } + return frame, err + } + // TODO: implement all IETF QUIC frame types + switch typeByte { + case 0x1: + frame, err = ParseRstStreamFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidRstStreamData, err.Error()) + } + case 0x2: + frame, err = ParseConnectionCloseFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidConnectionCloseData, err.Error()) + } + case 0x4: + frame, err = ParseMaxDataFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) + } + case 0x5: + frame, err = ParseMaxStreamDataFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) + } + case 0x6: + frame, err = ParseMaxStreamIDFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidFrameData, err.Error()) + } + case 0x7: + frame, err = ParsePingFrame(r, v) + case 0x8: + frame, err = ParseBlockedFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidBlockedData, err.Error()) + } + case 0x9: + frame, err = ParseStreamBlockedFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidBlockedData, err.Error()) + } + case 0xa: + frame, err = ParseStreamIDBlockedFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidFrameData, err.Error()) + } + case 0xc: + frame, err = ParseStopSendingFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidFrameData, err.Error()) + } + case 0xe: + frame, err = ParseAckFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidAckData, err.Error()) + } + default: + err = qerr.Error(qerr.InvalidFrameData, fmt.Sprintf("unknown type byte 0x%x", typeByte)) + } + return frame, err +} + +func parseGQUICFrame(r *bytes.Reader, typeByte byte, hdr *Header, v protocol.VersionNumber) (Frame, error) { + var frame Frame + var err error + if typeByte&0x80 == 0x80 { + frame, err = ParseStreamFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidStreamData, err.Error()) + } + return frame, err + } else if typeByte&0xc0 == 0x40 { + frame, err = ParseAckFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidAckData, err.Error()) + } + return frame, err + } + switch typeByte { + case 0x1: + frame, err = ParseRstStreamFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidRstStreamData, err.Error()) + } + case 0x2: + frame, err = ParseConnectionCloseFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidConnectionCloseData, err.Error()) + } + case 0x3: + frame, err = ParseGoawayFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidGoawayData, err.Error()) + } + case 0x4: + frame, err = ParseWindowUpdateFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) + } + case 0x5: + frame, err = ParseBlockedFrameLegacy(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidBlockedData, err.Error()) + } + case 0x6: + frame, err = ParseStopWaitingFrame(r, hdr.PacketNumber, hdr.PacketNumberLen, v) + if err != nil { + err = qerr.Error(qerr.InvalidStopWaitingData, err.Error()) + } + case 0x7: + frame, err = ParsePingFrame(r, v) + default: + err = qerr.Error(qerr.InvalidFrameData, fmt.Sprintf("unknown type byte 0x%x", typeByte)) + } + return frame, err +} diff --git a/internal/wire/frame_parser_test.go b/internal/wire/frame_parser_test.go new file mode 100644 index 00000000..e809b231 --- /dev/null +++ b/internal/wire/frame_parser_test.go @@ -0,0 +1,323 @@ +package wire + +import ( + "bytes" + + "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/qerr" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Frame parsing", func() { + var buf *bytes.Buffer + + BeforeEach(func() { + buf = &bytes.Buffer{} + }) + + It("returns nil if there's nothing more to read", func() { + f, err := ParseNextFrame(bytes.NewReader(nil), nil, protocol.VersionWhatever) + Expect(err).ToNot(HaveOccurred()) + Expect(f).To(BeNil()) + }) + + It("skips PADDING frames", func() { + buf.Write([]byte{0}) // PADDING frame + (&PingFrame{}).Write(buf, versionIETFFrames) + f, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(f).To(Equal(&PingFrame{})) + }) + + It("handles PADDING at the end", func() { + r := bytes.NewReader([]byte{0, 0, 0}) + f, err := ParseNextFrame(r, nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(f).To(BeNil()) + Expect(r.Len()).To(BeZero()) + }) + + Context("for gQUIC frames", func() { + It("unpacks RST_STREAM frames", func() { + f := &RstStreamFrame{ + StreamID: 0xdeadbeef, + ByteOffset: 0xdecafbad11223344, + ErrorCode: 0x1337, + } + err := f.Write(buf, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks CONNECTION_CLOSE frames", func() { + f := &ConnectionCloseFrame{ReasonPhrase: "foo"} + err := f.Write(buf, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks GOAWAY frames", func() { + f := &GoawayFrame{ + ErrorCode: 1, + LastGoodStream: 2, + ReasonPhrase: "foo", + } + err := f.Write(buf, 0) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks a stream-level WINDOW_UPDATE frame", func() { + f := &MaxStreamDataFrame{ + StreamID: 0xdeadbeef, + ByteOffset: 0xcafe000000001337, + } + buf := &bytes.Buffer{} + err := f.Write(buf, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks a connection-level WINDOW_UPDATE frame", func() { + f := &MaxDataFrame{ + ByteOffset: 0xcafe000000001337, + } + err := f.Write(buf, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks connection-level BLOCKED frames", func() { + f := &BlockedFrame{} + err := f.Write(buf, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks stream-level BLOCKED frames", func() { + f := &StreamBlockedFrame{StreamID: 0xdeadbeef} + err := f.Write(buf, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks STOP_WAITING frames", func() { + hdr := &Header{ + PacketNumber: 0x1338, + PacketNumberLen: protocol.PacketNumberLen4, + } + f := &StopWaitingFrame{ + LeastUnacked: 0x1337, + PacketNumber: hdr.PacketNumber, + PacketNumberLen: hdr.PacketNumberLen, + } + err := f.Write(buf, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), hdr, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(BeAssignableToTypeOf(f)) + Expect(frame.(*StopWaitingFrame).LeastUnacked).To(Equal(protocol.PacketNumber(0x1337))) + }) + + It("unpacks PING frames", func() { + f := &PingFrame{} + err := f.Write(buf, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks ACK frames", func() { + f := &AckFrame{ + LargestAcked: 0x13, + LowestAcked: 1, + } + err := f.Write(buf, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionBigEndian) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).ToNot(BeNil()) + Expect(frame).To(BeAssignableToTypeOf(f)) + Expect(frame.(*AckFrame).LargestAcked).To(Equal(protocol.PacketNumber(0x13))) + }) + + It("errors on invalid type", func() { + _, err := ParseNextFrame(bytes.NewReader([]byte{0xf}), nil, versionBigEndian) + 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, + } { + _, err := ParseNextFrame(bytes.NewReader([]byte{b}), &Header{PacketNumberLen: 2}, versionBigEndian) + Expect(err).To(HaveOccurred()) + Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e)) + } + }) + }) + + Context("for IETF draft frames", func() { + It("unpacks RST_STREAM frames", func() { + f := &RstStreamFrame{ + StreamID: 0xdeadbeef, + ByteOffset: 0xdecafbad1234, + ErrorCode: 0x1337, + } + err := f.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks CONNECTION_CLOSE frames", func() { + f := &ConnectionCloseFrame{ReasonPhrase: "foo"} + err := f.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks MAX_DATA frames", func() { + f := &MaxDataFrame{ + ByteOffset: 0xcafe, + } + buf := &bytes.Buffer{} + err := f.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks MAX_STREAM_DATA frames", func() { + f := &MaxStreamDataFrame{ + StreamID: 0xdeadbeef, + ByteOffset: 0xdecafbad, + } + buf := &bytes.Buffer{} + err := f.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks MAX_STREAM_ID frames", func() { + f := &MaxStreamIDFrame{StreamID: 0x1337} + buf := &bytes.Buffer{} + err := f.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks connection-level BLOCKED frames", func() { + f := &BlockedFrame{Offset: 0x1234} + buf := &bytes.Buffer{} + err := f.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks stream-level BLOCKED frames", func() { + f := &StreamBlockedFrame{ + StreamID: 0xdeadbeef, + Offset: 0xdead, + } + buf := &bytes.Buffer{} + err := f.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks STREAM_ID_BLOCKED frames", func() { + f := &StreamIDBlockedFrame{StreamID: 0x1234567} + buf := &bytes.Buffer{} + err := f.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks STOP_SENDING frames", func() { + f := &StopSendingFrame{StreamID: 0x42} + buf := &bytes.Buffer{} + err := f.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) + }) + + It("unpacks ACK frames", func() { + f := &AckFrame{ + LargestAcked: 0x13, + LowestAcked: 1, + } + err := f.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + frame, err := ParseNextFrame(bytes.NewReader(buf.Bytes()), nil, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).ToNot(BeNil()) + Expect(frame).To(BeAssignableToTypeOf(f)) + Expect(frame.(*AckFrame).LargestAcked).To(Equal(protocol.PacketNumber(0x13))) + }) + + It("errors on invalid type", func() { + _, err := ParseNextFrame(bytes.NewReader([]byte{0xf}), nil, versionIETFFrames) + 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, + 0x06: qerr.InvalidFrameData, + 0x08: qerr.InvalidBlockedData, + 0x09: qerr.InvalidBlockedData, + 0x0a: qerr.InvalidFrameData, + 0x0c: qerr.InvalidFrameData, + 0x0e: qerr.InvalidAckData, + 0x10: qerr.InvalidStreamData, + } { + _, err := ParseNextFrame(bytes.NewReader([]byte{b}), nil, versionIETFFrames) + Expect(err).To(HaveOccurred()) + Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e)) + } + }) + }) +}) diff --git a/packet_unpacker.go b/packet_unpacker.go index a978675d..0a98dc06 100644 --- a/packet_unpacker.go +++ b/packet_unpacker.go @@ -41,25 +41,20 @@ func (u *packetUnpacker) Unpack(headerBinary []byte, hdr *wire.Header, data []by fs := make([]wire.Frame, 0, 2) // Read all frames in the packet - for r.Len() > 0 { - typeByte, _ := r.ReadByte() - if typeByte == 0x0 { // PADDING frame - continue - } - r.UnreadByte() - - frame, err := u.parseFrame(r, typeByte, hdr) + for { + frame, err := wire.ParseNextFrame(r, hdr, u.version) if err != nil { return nil, err } + if frame == nil { + break + } if sf, ok := frame.(*wire.StreamFrame); ok { if sf.StreamID != u.version.CryptoStreamID() && encryptionLevel <= protocol.EncryptionUnencrypted { return nil, qerr.Error(qerr.UnencryptedStreamData, fmt.Sprintf("received unencrypted stream data on stream %d", sf.StreamID)) } } - if frame != nil { - fs = append(fs, frame) - } + fs = append(fs, frame) } return &unpackedPacket{ @@ -67,135 +62,3 @@ func (u *packetUnpacker) Unpack(headerBinary []byte, hdr *wire.Header, data []by frames: fs, }, nil } - -func (u *packetUnpacker) parseFrame(r *bytes.Reader, typeByte byte, hdr *wire.Header) (wire.Frame, error) { - if u.version.UsesIETFFrameFormat() { - return u.parseIETFFrame(r, typeByte, hdr) - } - return u.parseGQUICFrame(r, typeByte, hdr) -} - -func (u *packetUnpacker) parseIETFFrame(r *bytes.Reader, typeByte byte, hdr *wire.Header) (wire.Frame, error) { - var frame wire.Frame - var err error - if typeByte&0xf8 == 0x10 { - frame, err = wire.ParseStreamFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidStreamData, err.Error()) - } - return frame, err - } - // TODO: implement all IETF QUIC frame types - switch typeByte { - case 0x1: - frame, err = wire.ParseRstStreamFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidRstStreamData, err.Error()) - } - case 0x2: - frame, err = wire.ParseConnectionCloseFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidConnectionCloseData, err.Error()) - } - case 0x4: - frame, err = wire.ParseMaxDataFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) - } - case 0x5: - frame, err = wire.ParseMaxStreamDataFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) - } - case 0x6: - frame, err = wire.ParseMaxStreamIDFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidFrameData, err.Error()) - } - case 0x7: - frame, err = wire.ParsePingFrame(r, u.version) - case 0x8: - frame, err = wire.ParseBlockedFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidBlockedData, err.Error()) - } - case 0x9: - frame, err = wire.ParseStreamBlockedFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidBlockedData, err.Error()) - } - case 0xa: - frame, err = wire.ParseStreamIDBlockedFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidFrameData, err.Error()) - } - case 0xc: - frame, err = wire.ParseStopSendingFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidFrameData, err.Error()) - } - case 0xe: - frame, err = wire.ParseAckFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidAckData, err.Error()) - } - default: - err = qerr.Error(qerr.InvalidFrameData, fmt.Sprintf("unknown type byte 0x%x", typeByte)) - } - return frame, err -} - -func (u *packetUnpacker) parseGQUICFrame(r *bytes.Reader, typeByte byte, hdr *wire.Header) (wire.Frame, error) { - var frame wire.Frame - var err error - if typeByte&0x80 == 0x80 { - frame, err = wire.ParseStreamFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidStreamData, err.Error()) - } - return frame, err - } else if typeByte&0xc0 == 0x40 { - frame, err = wire.ParseAckFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidAckData, err.Error()) - } - return frame, err - } - switch typeByte { - case 0x1: - frame, err = wire.ParseRstStreamFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidRstStreamData, err.Error()) - } - case 0x2: - frame, err = wire.ParseConnectionCloseFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidConnectionCloseData, err.Error()) - } - case 0x3: - frame, err = wire.ParseGoawayFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidGoawayData, err.Error()) - } - case 0x4: - frame, err = wire.ParseWindowUpdateFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) - } - case 0x5: - frame, err = wire.ParseBlockedFrameLegacy(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidBlockedData, err.Error()) - } - case 0x6: - frame, err = wire.ParseStopWaitingFrame(r, hdr.PacketNumber, hdr.PacketNumberLen, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidStopWaitingData, err.Error()) - } - case 0x7: - frame, err = wire.ParsePingFrame(r, u.version) - default: - err = qerr.Error(qerr.InvalidFrameData, fmt.Sprintf("unknown type byte 0x%x", typeByte)) - } - return frame, err -} diff --git a/packet_unpacker_test.go b/packet_unpacker_test.go index 88a342d5..50548d7a 100644 --- a/packet_unpacker_test.go +++ b/packet_unpacker_test.go @@ -72,37 +72,16 @@ var _ = Describe("Packet unpacker", func() { Expect(packet.encryptionLevel).To(Equal(protocol.EncryptionSecure)) }) - Context("for gQUIC frames", func() { + Context("unpacking STREAM 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: 0x1337, + 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()) @@ -112,377 +91,31 @@ var _ = Describe("Packet unpacker", func() { 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, + 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(HaveLen(1)) - readFrame := packet.frames[0].(*wire.AckFrame) - Expect(readFrame).ToNot(BeNil()) - Expect(readFrame.LargestAcked).To(Equal(protocol.PacketNumber(0x13))) + Expect(packet.frames).To(Equal([]wire.Frame{f})) }) - 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: 0xdecafbad1234, - ErrorCode: 0x1337, + 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, versionIETFFrames) + 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, 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: 0xcafe, - } - 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: 0xdecafbad, - } - 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_ID frames", func() { - f := &wire.MaxStreamIDFrame{StreamID: 0x1337} - 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{Offset: 0x1234} - 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, - Offset: 0xdead, - } - 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_ID_BLOCKED frames", func() { - f := &wire.StreamIDBlockedFrame{StreamID: 0x1234567} - 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 STOP_SENDING frames", func() { - f := &wire.StopSendingFrame{StreamID: 0x42} - 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 ACK frames", func() { - f := &wire.AckFrame{ - LargestAcked: 0x13, - LowestAcked: 1, - } - 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(HaveLen(1)) - readFrame := packet.frames[0].(*wire.AckFrame) - Expect(readFrame).ToNot(BeNil()) - Expect(readFrame.LargestAcked).To(Equal(protocol.PacketNumber(0x13))) - }) - - 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, - 0x06: qerr.InvalidFrameData, - 0x08: qerr.InvalidBlockedData, - 0x09: qerr.InvalidBlockedData, - 0x0a: qerr.InvalidFrameData, - 0x0c: qerr.InvalidFrameData, - 0x0e: qerr.InvalidAckData, - 0x10: qerr.InvalidStreamData, - } { - setData([]byte{b}) - _, err := unpacker.Unpack(hdrBin, hdr, data) - Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e)) - } - }) - - 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: versionIETFFrames.CryptoStreamID(), - Data: []byte("foobar"), - } - 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 encrypted STREAM frames on the crypto stream", func() { - unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionSecure - f := &wire.StreamFrame{ - StreamID: versionIETFFrames.CryptoStreamID(), - Data: []byte("foobar"), - } - 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("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, versionIETFFrames) - 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"))) - }) + _, err = unpacker.Unpack(hdrBin, hdr, data) + Expect(err).To(MatchError(qerr.Error(qerr.UnencryptedStreamData, "received unencrypted stream data on stream 3"))) }) }) })