diff --git a/client.go b/client.go index 081f1259..435bc236 100644 --- a/client.go +++ b/client.go @@ -13,6 +13,7 @@ import ( "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/qerr" + "github.com/lucas-clemente/quic-go/wire" ) type client struct { @@ -224,7 +225,7 @@ func (c *client) handlePacket(remoteAddr net.Addr, packet []byte) { rcvTime := time.Now() r := bytes.NewReader(packet) - hdr, err := ParsePublicHeader(r, protocol.PerspectiveServer, c.version) + hdr, err := wire.ParsePublicHeader(r, protocol.PerspectiveServer, c.version) if err != nil { utils.Errorf("error parsing packet from %s: %s", remoteAddr.String(), err.Error()) // drop this packet if we can't parse the Public Header @@ -280,7 +281,7 @@ func (c *client) handlePacket(remoteAddr net.Addr, packet []byte) { }) } -func (c *client) handlePacketWithVersionFlag(hdr *PublicHeader) error { +func (c *client) handlePacketWithVersionFlag(hdr *wire.PublicHeader) error { for _, v := range hdr.SupportedVersions { if v == c.version { // the version negotiation packet contains the version that we offered diff --git a/client_test.go b/client_test.go index fc0271fb..ee8fc543 100644 --- a/client_test.go +++ b/client_test.go @@ -9,6 +9,7 @@ import ( "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/qerr" + "github.com/lucas-clemente/quic-go/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -228,7 +229,7 @@ var _ = Describe("Client", func() { Context("version negotiation", func() { It("recognizes that a packet without VersionFlag means that the server accepted the suggested version", func() { - ph := PublicHeader{ + ph := wire.PublicHeader{ PacketNumber: 1, PacketNumberLen: protocol.PacketNumberLen2, ConnectionID: 0x1337, @@ -262,7 +263,7 @@ var _ = Describe("Client", func() { Expect(newVersion).ToNot(Equal(cl.version)) Expect(sess.packetCount).To(BeZero()) cl.connectionID = 0x1337 - cl.handlePacket(nil, composeVersionNegotiation(0x1337, []protocol.VersionNumber{newVersion})) + cl.handlePacket(nil, wire.ComposeVersionNegotiation(0x1337, []protocol.VersionNumber{newVersion})) Expect(cl.version).To(Equal(newVersion)) Expect(cl.versionNegotiated).To(BeTrue()) // it swapped the sessions @@ -274,7 +275,7 @@ var _ = Describe("Client", func() { }) It("errors if no matching version is found", func() { - cl.handlePacket(nil, composeVersionNegotiation(0x1337, []protocol.VersionNumber{1})) + cl.handlePacket(nil, wire.ComposeVersionNegotiation(0x1337, []protocol.VersionNumber{1})) Expect(cl.session.(*mockSession).closed).To(BeTrue()) Expect(cl.session.(*mockSession).closeReason).To(MatchError(qerr.InvalidVersion)) }) @@ -283,13 +284,13 @@ var _ = Describe("Client", func() { v := protocol.SupportedVersions[1] Expect(v).ToNot(Equal(cl.version)) Expect(config.Versions).ToNot(ContainElement(v)) - cl.handlePacket(nil, composeVersionNegotiation(0x1337, []protocol.VersionNumber{v})) + cl.handlePacket(nil, wire.ComposeVersionNegotiation(0x1337, []protocol.VersionNumber{v})) Expect(cl.session.(*mockSession).closed).To(BeTrue()) Expect(cl.session.(*mockSession).closeReason).To(MatchError(qerr.InvalidVersion)) }) It("changes to the version preferred by the quic.Config", func() { - cl.handlePacket(nil, composeVersionNegotiation(0x1337, []protocol.VersionNumber{config.Versions[2], config.Versions[1]})) + cl.handlePacket(nil, wire.ComposeVersionNegotiation(0x1337, []protocol.VersionNumber{config.Versions[2], config.Versions[1]})) Expect(cl.version).To(Equal(config.Versions[1])) }) @@ -297,14 +298,14 @@ var _ = Describe("Client", func() { // if the version was not yet negotiated, handlePacket would return a VersionNegotiationMismatch error, see above test cl.versionNegotiated = true Expect(sess.packetCount).To(BeZero()) - cl.handlePacket(nil, composeVersionNegotiation(0x1337, []protocol.VersionNumber{1})) + cl.handlePacket(nil, wire.ComposeVersionNegotiation(0x1337, []protocol.VersionNumber{1})) Expect(cl.versionNegotiated).To(BeTrue()) Expect(sess.packetCount).To(BeZero()) }) It("drops version negotiation packets that contain the offered version", func() { ver := cl.version - cl.handlePacket(nil, composeVersionNegotiation(0x1337, []protocol.VersionNumber{ver})) + cl.handlePacket(nil, wire.ComposeVersionNegotiation(0x1337, []protocol.VersionNumber{ver})) Expect(cl.version).To(Equal(ver)) }) }) @@ -351,7 +352,7 @@ var _ = Describe("Client", func() { Context("handling packets", func() { It("handles packets", func() { - ph := PublicHeader{ + ph := wire.PublicHeader{ PacketNumber: 1, PacketNumberLen: protocol.PacketNumberLen2, ConnectionID: 0x1337, diff --git a/integrationtests/tools/proxy/proxy.go b/integrationtests/tools/proxy/proxy.go index bc72411d..77483a7a 100644 --- a/integrationtests/tools/proxy/proxy.go +++ b/integrationtests/tools/proxy/proxy.go @@ -7,8 +7,8 @@ import ( "sync/atomic" "time" - "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/protocol" + "github.com/lucas-clemente/quic-go/wire" ) // Connection is a UDP connection @@ -165,7 +165,7 @@ func (p *QuicProxy) runProxy() error { atomic.AddUint64(&conn.incomingPacketCounter, 1) r := bytes.NewReader(raw) - hdr, err := quic.ParsePublicHeader(r, protocol.PerspectiveClient, protocol.VersionWhatever) + hdr, err := wire.ParsePublicHeader(r, protocol.PerspectiveClient, protocol.VersionWhatever) if err != nil { return err } @@ -202,7 +202,7 @@ func (p *QuicProxy) runConnection(conn *connection) error { // TODO: Switch back to using the public header once Chrome properly sets the type byte. // r := bytes.NewReader(raw) - // , err := quic.ParsePublicHeader(r, protocol.PerspectiveServer) + // , err := wire.ParsePublicHeader(r, protocol.PerspectiveServer) // if err != nil { // return err // } diff --git a/integrationtests/tools/proxy/proxy_test.go b/integrationtests/tools/proxy/proxy_test.go index 565a8911..1ea73267 100644 --- a/integrationtests/tools/proxy/proxy_test.go +++ b/integrationtests/tools/proxy/proxy_test.go @@ -9,8 +9,8 @@ import ( "fmt" - "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/protocol" + "github.com/lucas-clemente/quic-go/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -22,7 +22,7 @@ var _ = Describe("QUIC Proxy", func() { makePacket := func(p protocol.PacketNumber, payload []byte) []byte { b := &bytes.Buffer{} - hdr := quic.PublicHeader{ + hdr := wire.PublicHeader{ PacketNumber: p, PacketNumberLen: protocol.PacketNumberLen6, ConnectionID: 1337, diff --git a/packet_packer.go b/packet_packer.go index 050b93a7..addd11a3 100644 --- a/packet_packer.go +++ b/packet_packer.go @@ -264,10 +264,10 @@ func (p *packetPacker) QueueControlFrame(frame wire.Frame) { } } -func (p *packetPacker) getPublicHeader(encLevel protocol.EncryptionLevel) *PublicHeader { +func (p *packetPacker) getPublicHeader(encLevel protocol.EncryptionLevel) *wire.PublicHeader { pnum := p.packetNumberGenerator.Peek() packetNumberLen := protocol.GetPacketNumberLengthForPublicHeader(pnum, p.leastUnacked) - publicHeader := &PublicHeader{ + publicHeader := &wire.PublicHeader{ ConnectionID: p.connectionID, PacketNumber: pnum, PacketNumberLen: packetNumberLen, @@ -286,7 +286,7 @@ func (p *packetPacker) getPublicHeader(encLevel protocol.EncryptionLevel) *Publi } func (p *packetPacker) writeAndSealPacket( - publicHeader *PublicHeader, + publicHeader *wire.PublicHeader, payloadFrames []wire.Frame, sealer handshake.Sealer, ) ([]byte, error) { diff --git a/packet_packer_test.go b/packet_packer_test.go index 24e5de94..37421189 100644 --- a/packet_packer_test.go +++ b/packet_packer_test.go @@ -238,7 +238,7 @@ var _ = Describe("Packet packer", func() { p, err := packer.PackPacket() Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) - hdr, err := ParsePublicHeader(bytes.NewReader(p.raw), protocol.PerspectiveClient, packer.version) + hdr, err := wire.ParsePublicHeader(bytes.NewReader(p.raw), protocol.PerspectiveClient, packer.version) Expect(err).ToNot(HaveOccurred()) Expect(hdr.VersionFlag).To(BeTrue()) Expect(hdr.VersionNumber).To(Equal(packer.version)) @@ -252,7 +252,7 @@ var _ = Describe("Packet packer", func() { p, err := packer.PackPacket() Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) - hdr, err := ParsePublicHeader(bytes.NewReader(p.raw), protocol.PerspectiveClient, packer.version) + hdr, err := wire.ParsePublicHeader(bytes.NewReader(p.raw), protocol.PerspectiveClient, packer.version) Expect(err).ToNot(HaveOccurred()) Expect(hdr.VersionFlag).To(BeFalse()) }) diff --git a/packet_unpacker.go b/packet_unpacker.go index 00ca7d19..8b5ba55d 100644 --- a/packet_unpacker.go +++ b/packet_unpacker.go @@ -24,7 +24,7 @@ type packetUnpacker struct { aead quicAEAD } -func (u *packetUnpacker) Unpack(publicHeaderBinary []byte, hdr *PublicHeader, data []byte) (*unpackedPacket, error) { +func (u *packetUnpacker) Unpack(publicHeaderBinary []byte, hdr *wire.PublicHeader, data []byte) (*unpackedPacket, error) { buf := getPacketBuffer() defer putPacketBuffer(buf) decrypted, encryptionLevel, err := u.aead.Open(buf, data, hdr.PacketNumber, publicHeaderBinary) diff --git a/packet_unpacker_test.go b/packet_unpacker_test.go index 86856b1d..5426a2a3 100644 --- a/packet_unpacker_test.go +++ b/packet_unpacker_test.go @@ -31,14 +31,14 @@ var _ quicAEAD = &mockAEAD{} var _ = Describe("Packet unpacker", func() { var ( unpacker *packetUnpacker - hdr *PublicHeader + hdr *wire.PublicHeader hdrBin []byte data []byte buf *bytes.Buffer ) BeforeEach(func() { - hdr = &PublicHeader{ + hdr = &wire.PublicHeader{ PacketNumber: 10, PacketNumberLen: 1, } diff --git a/server.go b/server.go index b45b760b..ea16ce59 100644 --- a/server.go +++ b/server.go @@ -13,6 +13,7 @@ import ( "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/qerr" + "github.com/lucas-clemente/quic-go/wire" ) // packetHandler handles packets @@ -212,7 +213,7 @@ func (s *server) handlePacket(pconn net.PacketConn, remoteAddr net.Addr, packet rcvTime := time.Now() r := bytes.NewReader(packet) - connID, err := peekConnectionID(r, protocol.PerspectiveClient) + connID, err := wire.PeekConnectionID(r, protocol.PerspectiveClient) if err != nil { return qerr.Error(qerr.InvalidPacketHeader, err.Error()) } @@ -231,8 +232,8 @@ func (s *server) handlePacket(pconn net.PacketConn, remoteAddr net.Addr, packet version = session.GetVersion() } - hdr, err := ParsePublicHeader(r, protocol.PerspectiveClient, version) - if err == errPacketWithUnknownVersion { + hdr, err := wire.ParsePublicHeader(r, protocol.PerspectiveClient, version) + if err == wire.ErrPacketWithUnknownVersion { _, err = pconn.WriteTo(writePublicReset(connID, 0, 0), remoteAddr) return err } @@ -271,7 +272,7 @@ func (s *server) handlePacket(pconn net.PacketConn, remoteAddr net.Addr, packet return errors.New("dropping small packet with unknown version") } utils.Infof("Client offered version %d, sending VersionNegotiationPacket", hdr.VersionNumber) - _, err = pconn.WriteTo(composeVersionNegotiation(hdr.ConnectionID, s.config.Versions), remoteAddr) + _, err = pconn.WriteTo(wire.ComposeVersionNegotiation(hdr.ConnectionID, s.config.Versions), remoteAddr) return err } @@ -337,20 +338,3 @@ func (s *server) removeConnection(id protocol.ConnectionID) { s.sessionsMutex.Unlock() }) } - -func composeVersionNegotiation(connectionID protocol.ConnectionID, versions []protocol.VersionNumber) []byte { - fullReply := &bytes.Buffer{} - responsePublicHeader := PublicHeader{ - ConnectionID: connectionID, - PacketNumber: 1, - VersionFlag: true, - } - err := responsePublicHeader.Write(fullReply, protocol.VersionWhatever, protocol.PerspectiveServer) - if err != nil { - utils.Errorf("error composing version negotiation packet: %s", err.Error()) - } - for _, v := range versions { - utils.LittleEndian.WriteUint32(fullReply, protocol.VersionNumberToTag(v)) - } - return fullReply.Bytes() -} diff --git a/server_test.go b/server_test.go index bc26a2ec..b5c24155 100644 --- a/server_test.go +++ b/server_test.go @@ -14,6 +14,7 @@ import ( "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/qerr" + "github.com/lucas-clemente/quic-go/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -128,14 +129,6 @@ var _ = Describe("Server", func() { Expect(serv.Addr().String()).To(Equal("192.168.13.37:1234")) }) - It("composes version negotiation packets", func() { - expected := append( - []byte{0x01 | 0x08, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - []byte{'Q', '0', '9', '9'}..., - ) - Expect(composeVersionNegotiation(1, []protocol.VersionNumber{99})).To(Equal(expected)) - }) - It("creates new sessions", func() { err := serv.handlePacket(nil, nil, firstPacket) Expect(err).ToNot(HaveOccurred()) @@ -334,7 +327,7 @@ var _ = Describe("Server", func() { It("doesn't respond with a version negotiation packet if the first packet is too small", func() { b := &bytes.Buffer{} - hdr := PublicHeader{ + hdr := wire.PublicHeader{ VersionFlag: true, ConnectionID: 0x1337, PacketNumber: 1, @@ -402,7 +395,7 @@ var _ = Describe("Server", func() { It("setups and responds with version negotiation", func() { config.Versions = []protocol.VersionNumber{99} b := &bytes.Buffer{} - hdr := PublicHeader{ + hdr := wire.PublicHeader{ VersionFlag: true, ConnectionID: 0x1337, PacketNumber: 1, diff --git a/session.go b/session.go index a316c701..2111ec99 100644 --- a/session.go +++ b/session.go @@ -20,12 +20,12 @@ import ( ) type unpacker interface { - Unpack(publicHeaderBinary []byte, hdr *PublicHeader, data []byte) (*unpackedPacket, error) + Unpack(publicHeaderBinary []byte, hdr *wire.PublicHeader, data []byte) (*unpackedPacket, error) } type receivedPacket struct { remoteAddr net.Addr - publicHeader *PublicHeader + publicHeader *wire.PublicHeader data []byte rcvTime time.Time } diff --git a/session_test.go b/session_test.go index 09ac903c..759a6c62 100644 --- a/session_test.go +++ b/session_test.go @@ -61,7 +61,7 @@ type mockUnpacker struct { unpackErr error } -func (m *mockUnpacker) Unpack(publicHeaderBinary []byte, hdr *PublicHeader, data []byte) (*unpackedPacket, error) { +func (m *mockUnpacker) Unpack(publicHeaderBinary []byte, hdr *wire.PublicHeader, data []byte) (*unpackedPacket, error) { if m.unpackErr != nil { return nil, m.unpackErr } @@ -847,11 +847,11 @@ var _ = Describe("Session", func() { }) Context("receiving packets", func() { - var hdr *PublicHeader + var hdr *wire.PublicHeader BeforeEach(func() { sess.unpacker = &mockUnpacker{} - hdr = &PublicHeader{PacketNumberLen: protocol.PacketNumberLen6} + hdr = &wire.PublicHeader{PacketNumberLen: protocol.PacketNumberLen6} }) It("sets the {last,largest}RcvdPacketNumber", func() { @@ -903,7 +903,7 @@ var _ = Describe("Session", func() { Expect(sess.conn.(*mockConnection).remoteAddr).ToNot(Equal(remoteIP)) p := receivedPacket{ remoteAddr: remoteIP, - publicHeader: &PublicHeader{PacketNumber: 1337}, + publicHeader: &wire.PublicHeader{PacketNumber: 1337}, } err := sess.handlePacketImpl(&p) Expect(err).ToNot(HaveOccurred()) @@ -919,7 +919,7 @@ var _ = Describe("Session", func() { sess.unpacker.(*packetUnpacker).aead = &mockAEAD{} p := receivedPacket{ remoteAddr: attackerIP, - publicHeader: &PublicHeader{PacketNumber: 1337}, + publicHeader: &wire.PublicHeader{PacketNumber: 1337}, } err := sess.handlePacketImpl(&p) quicErr := err.(*qerr.QuicError) @@ -933,7 +933,7 @@ var _ = Describe("Session", func() { Expect(sess.conn.(*mockConnection).remoteAddr).ToNot(Equal(remoteIP)) p := receivedPacket{ remoteAddr: remoteIP, - publicHeader: &PublicHeader{PacketNumber: 1337}, + publicHeader: &wire.PublicHeader{PacketNumber: 1337}, } sess.unpacker.(*mockUnpacker).unpackErr = testErr err := sess.handlePacketImpl(&p) @@ -1347,7 +1347,7 @@ var _ = Describe("Session", func() { // this completely fills up the undecryptable packets queue and triggers the public reset timer sendUndecryptablePackets := func() { for i := 0; i < protocol.MaxUndecryptablePackets+1; i++ { - hdr := &PublicHeader{ + hdr := &wire.PublicHeader{ PacketNumber: protocol.PacketNumber(i + 1), } sess.handlePacket(&receivedPacket{ @@ -1420,7 +1420,7 @@ var _ = Describe("Session", func() { It("unqueues undecryptable packets for later decryption", func() { sess.undecryptablePackets = []*receivedPacket{{ - publicHeader: &PublicHeader{PacketNumber: protocol.PacketNumber(42)}, + publicHeader: &wire.PublicHeader{PacketNumber: protocol.PacketNumber(42)}, }} Expect(sess.receivedPackets).NotTo(Receive()) sess.tryDecryptingQueuedPackets() @@ -1722,10 +1722,10 @@ var _ = Describe("Client Session", func() { }) Context("receiving packets", func() { - var hdr *PublicHeader + var hdr *wire.PublicHeader BeforeEach(func() { - hdr = &PublicHeader{PacketNumberLen: protocol.PacketNumberLen6} + hdr = &wire.PublicHeader{PacketNumberLen: protocol.PacketNumberLen6} sess.unpacker = &mockUnpacker{} }) diff --git a/public_header.go b/wire/public_header.go similarity index 91% rename from public_header.go rename to wire/public_header.go index 9140fc8c..20fec06a 100644 --- a/public_header.go +++ b/wire/public_header.go @@ -1,4 +1,4 @@ -package quic +package wire import ( "bytes" @@ -11,13 +11,13 @@ import ( ) var ( - errPacketNumberLenNotSet = errors.New("PublicHeader: PacketNumberLen not set") + // ErrPacketWithUnknownVersion occurs when a packet with an unknown version is parsed. + // This can happen when the server is restarted. The client will send a packet without a version number. + ErrPacketWithUnknownVersion = errors.New("PublicHeader: Received a packet without version number, that we don't know the version for") errResetAndVersionFlagSet = errors.New("PublicHeader: Reset Flag and Version Flag should not be set at the same time") errReceivedTruncatedConnectionID = qerr.Error(qerr.InvalidPacketHeader, "receiving packets with truncated ConnectionID is not supported") errInvalidConnectionID = qerr.Error(qerr.InvalidPacketHeader, "connection ID cannot be 0") errGetLengthNotForVersionNegotiation = errors.New("PublicHeader: GetLength cannot be called for VersionNegotiation packets") - // this can happen when the server is restarted. The client will send a packet without a version number - errPacketWithUnknownVersion = errors.New("PublicHeader: Received a packet without version number, that we don't know the version for") ) // The PublicHeader of a QUIC packet. Warning: This struct should not be considered stable and will change soon. @@ -93,10 +93,6 @@ func (h *PublicHeader) Write(b *bytes.Buffer, version protocol.VersionNumber, pe return nil } - if h.PacketNumberLen != protocol.PacketNumberLen1 && h.PacketNumberLen != protocol.PacketNumberLen2 && h.PacketNumberLen != protocol.PacketNumberLen4 && h.PacketNumberLen != protocol.PacketNumberLen6 { - return errPacketNumberLenNotSet - } - switch h.PacketNumberLen { case protocol.PacketNumberLen1: b.WriteByte(uint8(h.PacketNumber)) @@ -107,15 +103,15 @@ func (h *PublicHeader) Write(b *bytes.Buffer, version protocol.VersionNumber, pe case protocol.PacketNumberLen6: utils.GetByteOrder(version).WriteUint48(b, uint64(h.PacketNumber)&(1<<48-1)) default: - return errPacketNumberLenNotSet + return errors.New("PublicHeader: PacketNumberLen not set") } return nil } -// peekConnectionID parses the connection ID from a QUIC packet's public header. +// PeekConnectionID parses the connection ID from a QUIC packet's public header. // If no error occurs, it restores the read position in the bytes.Reader. -func peekConnectionID(b *bytes.Reader, packetSentBy protocol.Perspective) (protocol.ConnectionID, error) { +func PeekConnectionID(b *bytes.Reader, packetSentBy protocol.Perspective) (protocol.ConnectionID, error) { var connectionID protocol.ConnectionID publicFlagByte, err := b.ReadByte() if err != nil { @@ -156,7 +152,7 @@ func ParsePublicHeader(b *bytes.Reader, packetSentBy protocol.Perspective, versi header.ResetFlag = publicFlagByte&0x02 > 0 header.VersionFlag = publicFlagByte&0x01 > 0 if version == protocol.VersionUnknown && !(header.VersionFlag || header.ResetFlag) { - return nil, errPacketWithUnknownVersion + return nil, ErrPacketWithUnknownVersion } // TODO: activate this check once Chrome sends the correct value diff --git a/public_header_test.go b/wire/public_header_test.go similarity index 97% rename from public_header_test.go rename to wire/public_header_test.go index 6d151234..fbad6919 100644 --- a/public_header_test.go +++ b/wire/public_header_test.go @@ -1,4 +1,4 @@ -package quic +package wire import ( "bytes" @@ -15,14 +15,14 @@ var _ = Describe("Public Header", func() { Context("parsing the connection ID", func() { It("does not accept truncated connection ID as a server", func() { b := bytes.NewReader([]byte{0x00, 0x01}) - _, err := peekConnectionID(b, protocol.PerspectiveClient) + _, err := PeekConnectionID(b, protocol.PerspectiveClient) Expect(err).To(MatchError(errReceivedTruncatedConnectionID)) }) It("gets the connection ID", func() { b := bytes.NewReader([]byte{0x09, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x51, 0x30, 0x33, 0x34, 0x01}) len := b.Len() - connID, err := peekConnectionID(b, protocol.PerspectiveClient) + connID, err := PeekConnectionID(b, protocol.PerspectiveClient) Expect(err).ToNot(HaveOccurred()) Expect(connID).To(Equal(protocol.ConnectionID(0x4cfa9f9b668619f6))) Expect(b.Len()).To(Equal(len)) @@ -30,20 +30,20 @@ var _ = Describe("Public Header", func() { It("errors if the Public Header is too short", func() { b := bytes.NewReader([]byte{0x09, 0xf6, 0x19, 0x86, 0x66, 0x9b}) - _, err := peekConnectionID(b, protocol.PerspectiveClient) + _, err := PeekConnectionID(b, protocol.PerspectiveClient) Expect(err).To(HaveOccurred()) }) It("errors if the Public Header is empty", func() { b := bytes.NewReader([]byte{}) - _, err := peekConnectionID(b, protocol.PerspectiveClient) + _, err := PeekConnectionID(b, protocol.PerspectiveClient) Expect(err).To(HaveOccurred()) }) It("accepts a truncated connection ID as a client", func() { b := bytes.NewReader([]byte{0x00, 0x01}) len := b.Len() - connID, err := peekConnectionID(b, protocol.PerspectiveServer) + connID, err := PeekConnectionID(b, protocol.PerspectiveServer) Expect(err).ToNot(HaveOccurred()) Expect(connID).To(BeZero()) Expect(b.Len()).To(Equal(len)) @@ -116,7 +116,7 @@ var _ = Describe("Public Header", func() { It("returns an unknown version error when receiving a packet without a version for which the version is not given", func() { b := bytes.NewReader([]byte{0x10, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xef}) _, err := ParsePublicHeader(b, protocol.PerspectiveServer, protocol.VersionUnknown) - Expect(err).To(MatchError(errPacketWithUnknownVersion)) + Expect(err).To(MatchError(ErrPacketWithUnknownVersion)) }) PIt("rejects diversification nonces sent by the client", func() { @@ -136,7 +136,7 @@ var _ = Describe("Public Header", func() { } It("parses version negotiation packets sent by the server", func() { - b := bytes.NewReader(composeVersionNegotiation(0x1337, protocol.SupportedVersions)) + b := bytes.NewReader(ComposeVersionNegotiation(0x1337, protocol.SupportedVersions)) hdr, err := ParsePublicHeader(b, protocol.PerspectiveServer, protocol.VersionUnknown) Expect(err).ToNot(HaveOccurred()) Expect(hdr.VersionFlag).To(BeTrue()) @@ -169,7 +169,7 @@ var _ = Describe("Public Header", func() { }) It("errors on invalid version tags", func() { - data := composeVersionNegotiation(0x1337, protocol.SupportedVersions) + data := ComposeVersionNegotiation(0x1337, protocol.SupportedVersions) data = append(data, []byte{0x13, 0x37}...) b := bytes.NewReader(data) _, err := ParsePublicHeader(b, protocol.PerspectiveServer, protocol.VersionUnknown) @@ -300,7 +300,7 @@ var _ = Describe("Public Header", func() { } b := &bytes.Buffer{} err := hdr.Write(b, protocol.VersionWhatever, protocol.PerspectiveServer) - Expect(err).To(MatchError(errPacketNumberLenNotSet)) + Expect(err).To(MatchError("PublicHeader: PacketNumberLen not set")) }) It("truncates the connection ID", func() { @@ -526,7 +526,7 @@ var _ = Describe("Public Header", func() { PacketNumber: 0xDECAFBAD, } err := hdr.Write(b, protocol.VersionWhatever, protocol.PerspectiveServer) - Expect(err).To(MatchError(errPacketNumberLenNotSet)) + Expect(err).To(MatchError("PublicHeader: PacketNumberLen not set")) }) Context("in little endian", func() { diff --git a/wire/version_negotiation.go b/wire/version_negotiation.go new file mode 100644 index 00000000..7d3f6f23 --- /dev/null +++ b/wire/version_negotiation.go @@ -0,0 +1,26 @@ +package wire + +import ( + "bytes" + + "github.com/lucas-clemente/quic-go/internal/utils" + "github.com/lucas-clemente/quic-go/protocol" +) + +// ComposeVersionNegotiation composes a Version Negotiation Packet +func ComposeVersionNegotiation(connectionID protocol.ConnectionID, versions []protocol.VersionNumber) []byte { + fullReply := &bytes.Buffer{} + responsePublicHeader := PublicHeader{ + ConnectionID: connectionID, + PacketNumber: 1, + VersionFlag: true, + } + err := responsePublicHeader.Write(fullReply, protocol.VersionWhatever, protocol.PerspectiveServer) + if err != nil { + utils.Errorf("error composing version negotiation packet: %s", err.Error()) + } + for _, v := range versions { + utils.LittleEndian.WriteUint32(fullReply, protocol.VersionNumberToTag(v)) + } + return fullReply.Bytes() +} diff --git a/wire/version_negotiation_test.go b/wire/version_negotiation_test.go new file mode 100644 index 00000000..172d78e3 --- /dev/null +++ b/wire/version_negotiation_test.go @@ -0,0 +1,17 @@ +package wire + +import ( + "github.com/lucas-clemente/quic-go/protocol" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Version Negotiation Packet", func() { + It("composes version negotiation packets", func() { + expected := append( + []byte{0x01 | 0x08, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + []byte{'Q', '0', '9', '9'}..., + ) + Expect(ComposeVersionNegotiation(1, []protocol.VersionNumber{99})).To(Equal(expected)) + }) +})