From 7cb8e875802ad076ee5bf15aaf34af9aadb5b2fc Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 14 Oct 2017 12:31:26 -0700 Subject: [PATCH] implement parsing of IETF Version Negotiation Packets --- internal/wire/ietf_header.go | 42 +++++++++++++++++-------- internal/wire/ietf_header_test.go | 52 ++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 21 deletions(-) diff --git a/internal/wire/ietf_header.go b/internal/wire/ietf_header.go index 4f18fd76..38188641 100644 --- a/internal/wire/ietf_header.go +++ b/internal/wire/ietf_header.go @@ -6,34 +6,36 @@ import ( "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" + "github.com/lucas-clemente/quic-go/qerr" ) // The Header is the header of a QUIC Packet. // TODO: add support for the key phase type Header struct { - Type uint8 - IsLongHeader bool - KeyPhase int - OmitConnectionID bool - ConnectionID protocol.ConnectionID - PacketNumber protocol.PacketNumber - PacketNumberLen protocol.PacketNumberLen - Version protocol.VersionNumber + Type uint8 + IsLongHeader bool + KeyPhase int + OmitConnectionID bool + ConnectionID protocol.ConnectionID + PacketNumber protocol.PacketNumber + PacketNumberLen protocol.PacketNumberLen + Version protocol.VersionNumber + SupportedVersions []protocol.VersionNumber // version number sent in a Version Negotiation Packet by the server } // ParseHeader parses a header -func ParseHeader(b *bytes.Reader) (*Header, error) { +func ParseHeader(b *bytes.Reader, packetSentBy protocol.Perspective) (*Header, error) { typeByte, err := b.ReadByte() if err != nil { return nil, err } if typeByte&0x80 > 0 { - return parseLongHeader(b, typeByte) + return parseLongHeader(b, packetSentBy, typeByte) } return parseShortHeader(b, typeByte) } -func parseLongHeader(b *bytes.Reader, typeByte byte) (*Header, error) { +func parseLongHeader(b *bytes.Reader, packetSentBy protocol.Perspective, typeByte byte) (*Header, error) { connID, err := utils.BigEndian.ReadUint64(b) if err != nil { return nil, err @@ -46,14 +48,28 @@ func parseLongHeader(b *bytes.Reader, typeByte byte) (*Header, error) { if err != nil { return nil, err } - return &Header{ + h := &Header{ Type: typeByte & 0x7f, IsLongHeader: true, ConnectionID: protocol.ConnectionID(connID), PacketNumber: protocol.PacketNumber(pn), PacketNumberLen: protocol.PacketNumberLen4, Version: protocol.VersionNumber(v), - }, nil + } + if h.Type == 0x1 { // Version Negotiation Packet + if packetSentBy == protocol.PerspectiveClient { + return nil, qerr.Error(qerr.InvalidVersionNegotiationPacket, "sent by the client") + } + h.SupportedVersions = make([]protocol.VersionNumber, b.Len()/4) + for i := 0; b.Len() > 0; i++ { + v, err := utils.BigEndian.ReadUint32(b) + if err != nil { + return nil, qerr.InvalidVersionNegotiationPacket + } + h.SupportedVersions[i] = protocol.VersionNumber(v) + } + } + return h, nil } func parseShortHeader(b *bytes.Reader, typeByte byte) (*Header, error) { diff --git a/internal/wire/ietf_header_test.go b/internal/wire/ietf_header_test.go index da675de8..16f6c031 100644 --- a/internal/wire/ietf_header_test.go +++ b/internal/wire/ietf_header_test.go @@ -5,6 +5,8 @@ import ( "io" "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/qerr" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -25,7 +27,7 @@ var _ = Describe("Header", func() { It("parses a long header", func() { b := bytes.NewReader(data) - h, err := ParseHeader(b) + h, err := ParseHeader(b, protocol.PerspectiveClient) Expect(err).ToNot(HaveOccurred()) Expect(h.Type).To(BeEquivalentTo(3)) Expect(h.IsLongHeader).To(BeTrue()) @@ -39,10 +41,44 @@ var _ = Describe("Header", func() { It("errors on EOF", func() { for i := 0; i < len(data); i++ { - _, err := ParseHeader(bytes.NewReader(data[:i])) + _, err := ParseHeader(bytes.NewReader(data[:i]), protocol.PerspectiveClient) Expect(err).To(Equal(io.EOF)) } }) + + Context("Version Negotiation Packets", func() { + BeforeEach(func() { + data[0] = 0x80 ^ 0x1 // set the type byte to Version Negotiation Packet + }) + + It("parses", func() { + data = append(data, []byte{ + 0x22, 0x33, 0x44, 0x55, + 0x33, 0x44, 0x55, 0x66}..., + ) + b := bytes.NewReader(data) + h, err := ParseHeader(b, protocol.PerspectiveServer) + Expect(err).ToNot(HaveOccurred()) + Expect(h.SupportedVersions).To(Equal([]protocol.VersionNumber{ + 0x22334455, + 0x33445566, + })) + }) + + It("errors if it contains versions of the wrong length", func() { + data = append(data, []byte{0x22, 0x33}...) // too short. Should be 4 bytes. + b := bytes.NewReader(data) + _, err := ParseHeader(b, protocol.PerspectiveServer) + Expect(err).To(MatchError(qerr.InvalidVersionNegotiationPacket)) + }) + + It("errors if it was sent by the client", func() { + data = append(data, []byte{0x22, 0x33, 0x44, 0x55}...) + b := bytes.NewReader(data) + _, err := ParseHeader(b, protocol.PerspectiveClient) + Expect(err).To(MatchError("InvalidVersionNegotiationPacket: sent by the client")) + }) + }) }) Context("short headers", func() { @@ -53,7 +89,7 @@ var _ = Describe("Header", func() { 0x42, // packet number } b := bytes.NewReader(data) - h, err := ParseHeader(b) + h, err := ParseHeader(b, protocol.PerspectiveClient) Expect(err).ToNot(HaveOccurred()) Expect(h.IsLongHeader).To(BeFalse()) Expect(h.KeyPhase).To(Equal(0)) @@ -69,7 +105,7 @@ var _ = Describe("Header", func() { 0x11, } b := bytes.NewReader(data) - h, err := ParseHeader(b) + h, err := ParseHeader(b, protocol.PerspectiveClient) Expect(err).ToNot(HaveOccurred()) Expect(h.IsLongHeader).To(BeFalse()) Expect(h.KeyPhase).To(Equal(1)) @@ -82,7 +118,7 @@ var _ = Describe("Header", func() { 0x21, // packet number } b := bytes.NewReader(data) - h, err := ParseHeader(b) + h, err := ParseHeader(b, protocol.PerspectiveClient) Expect(err).ToNot(HaveOccurred()) Expect(h.IsLongHeader).To(BeFalse()) Expect(h.OmitConnectionID).To(BeTrue()) @@ -97,7 +133,7 @@ var _ = Describe("Header", func() { 0x13, 0x37, // packet number } b := bytes.NewReader(data) - h, err := ParseHeader(b) + h, err := ParseHeader(b, protocol.PerspectiveClient) Expect(err).ToNot(HaveOccurred()) Expect(h.IsLongHeader).To(BeFalse()) Expect(h.PacketNumber).To(Equal(protocol.PacketNumber(0x1337))) @@ -111,7 +147,7 @@ var _ = Describe("Header", func() { 0xde, 0xad, 0xbe, 0xef, // packet number } b := bytes.NewReader(data) - h, err := ParseHeader(b) + h, err := ParseHeader(b, protocol.PerspectiveClient) Expect(err).ToNot(HaveOccurred()) Expect(h.IsLongHeader).To(BeFalse()) Expect(h.PacketNumber).To(Equal(protocol.PacketNumber(0xdeadbeef))) @@ -126,7 +162,7 @@ var _ = Describe("Header", func() { 0xde, 0xca, 0xfb, 0xad, // packet number } for i := 0; i < len(data); i++ { - _, err := ParseHeader(bytes.NewReader(data[:i])) + _, err := ParseHeader(bytes.NewReader(data[:i]), protocol.PerspectiveClient) Expect(err).To(Equal(io.EOF)) } })