From f53f3eee9f8e41a4fd927eb6f4dbf41b8e0475dd Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sun, 22 Apr 2018 18:09:02 +0900 Subject: [PATCH] implement parsing and writing of the PATH_CHALLENGE frame --- internal/wire/frame_parser.go | 5 +++ internal/wire/frame_parser_test.go | 12 ++++++ internal/wire/path_challenge_frame.go | 39 ++++++++++++++++++ internal/wire/path_challenge_frame_test.go | 47 ++++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 internal/wire/path_challenge_frame.go create mode 100644 internal/wire/path_challenge_frame_test.go diff --git a/internal/wire/frame_parser.go b/internal/wire/frame_parser.go index 041a64d5..b4409532 100644 --- a/internal/wire/frame_parser.go +++ b/internal/wire/frame_parser.go @@ -90,6 +90,11 @@ func parseIETFFrame(r *bytes.Reader, typeByte byte, v protocol.VersionNumber) (F if err != nil { err = qerr.Error(qerr.InvalidAckData, err.Error()) } + case 0xe: + frame, err = parsePathChallengeFrame(r, v) + if err != nil { + err = qerr.Error(qerr.InvalidFrameData, err.Error()) + } default: err = qerr.Error(qerr.InvalidFrameData, fmt.Sprintf("unknown type byte 0x%x", typeByte)) } diff --git a/internal/wire/frame_parser_test.go b/internal/wire/frame_parser_test.go index e6d9a4c0..c081b715 100644 --- a/internal/wire/frame_parser_test.go +++ b/internal/wire/frame_parser_test.go @@ -289,6 +289,17 @@ var _ = Describe("Frame parsing", func() { Expect(frame.(*AckFrame).LargestAcked()).To(Equal(protocol.PacketNumber(0x13))) }) + It("unpacks PATH_CHALLENGE frames", func() { + f := &PathChallengeFrame{Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}} + 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.(*PathChallengeFrame).Data).To(Equal([8]byte{1, 2, 3, 4, 5, 6, 7, 8})) + }) + It("errors on invalid type", func() { _, err := ParseNextFrame(bytes.NewReader([]byte{0xf}), nil, versionIETFFrames) Expect(err).To(MatchError("InvalidFrameData: unknown type byte 0xf")) @@ -306,6 +317,7 @@ var _ = Describe("Frame parsing", func() { 0x0a: qerr.InvalidFrameData, 0x0c: qerr.InvalidFrameData, 0x0d: qerr.InvalidAckData, + 0x0e: qerr.InvalidFrameData, 0x10: qerr.InvalidStreamData, } { _, err := ParseNextFrame(bytes.NewReader([]byte{b}), nil, versionIETFFrames) diff --git a/internal/wire/path_challenge_frame.go b/internal/wire/path_challenge_frame.go new file mode 100644 index 00000000..f2a27d84 --- /dev/null +++ b/internal/wire/path_challenge_frame.go @@ -0,0 +1,39 @@ +package wire + +import ( + "bytes" + "io" + + "github.com/lucas-clemente/quic-go/internal/protocol" +) + +// A PathChallengeFrame is a PATH_CHALLENGE frame +type PathChallengeFrame struct { + Data [8]byte +} + +func parsePathChallengeFrame(r *bytes.Reader, version protocol.VersionNumber) (*PathChallengeFrame, error) { + if _, err := r.ReadByte(); err != nil { + return nil, err + } + frame := &PathChallengeFrame{} + if _, err := io.ReadFull(r, frame.Data[:]); err != nil { + if err == io.ErrUnexpectedEOF { + return nil, io.EOF + } + return nil, err + } + return frame, nil +} + +func (f *PathChallengeFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { + typeByte := uint8(0x0e) + b.WriteByte(typeByte) + b.Write(f.Data[:]) + return nil +} + +// Length of a written frame +func (f *PathChallengeFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { + return 1 + 8 +} diff --git a/internal/wire/path_challenge_frame_test.go b/internal/wire/path_challenge_frame_test.go new file mode 100644 index 00000000..bbfc7109 --- /dev/null +++ b/internal/wire/path_challenge_frame_test.go @@ -0,0 +1,47 @@ +package wire + +import ( + "bytes" + "io" + + "github.com/lucas-clemente/quic-go/internal/protocol" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("PATH_CHALLENGE frame", func() { + Context("when parsing", func() { + It("accepts sample frame", func() { + b := bytes.NewReader([]byte{0x0e, 1, 2, 3, 4, 5, 6, 7, 8}) + f, err := parsePathChallengeFrame(b, protocol.VersionWhatever) + Expect(err).ToNot(HaveOccurred()) + Expect(b.Len()).To(BeZero()) + Expect(f.Data).To(Equal([8]byte{1, 2, 3, 4, 5, 6, 7, 8})) + }) + + It("errors on EOFs", func() { + data := []byte{0x0e, 1, 2, 3, 4, 5, 6, 7, 8} + _, err := parsePathChallengeFrame(bytes.NewReader(data), versionIETFFrames) + Expect(err).NotTo(HaveOccurred()) + for i := range data { + _, err := parsePathChallengeFrame(bytes.NewReader(data[0:i]), versionIETFFrames) + Expect(err).To(MatchError(io.EOF)) + } + }) + }) + + Context("when writing", func() { + It("writes a sample frame", func() { + b := &bytes.Buffer{} + frame := PathChallengeFrame{Data: [8]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}} + err := frame.Write(b, protocol.VersionWhatever) + Expect(err).ToNot(HaveOccurred()) + Expect(b.Bytes()).To(Equal([]byte{0x0e, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37})) + }) + + It("has the correct min length", func() { + frame := PathChallengeFrame{} + Expect(frame.Length(protocol.VersionWhatever)).To(Equal(protocol.ByteCount(9))) + }) + }) +})