Browse Source

Merge pull request #1504 from lucas-clemente/quic-44

add support for gQUIC 44
Marten Seemann 9 months ago
parent
commit
3e8d87bd6a

+ 1 - 1
Changelog.md

@@ -2,7 +2,7 @@
 
 ## v0.10.0 (unreleased)
 
-- Drop support for QUIC 42.
+- Add support for QUIC 44, drop support for QUIC 42.
 
 ## v0.9.0 (2018-08-15)
 

+ 46 - 42
client.go

@@ -128,6 +128,13 @@ func dialContext(
 	createdPacketConn bool,
 ) (Session, error) {
 	config = populateClientConfig(config, createdPacketConn)
+	if !createdPacketConn {
+		for _, v := range config.Versions {
+			if v == protocol.Version44 {
+				return nil, errors.New("Cannot multiplex connections using gQUIC 44, see https://groups.google.com/a/chromium.org/forum/#!topic/proto-quic/pE9NlLLjizE. Please disable gQUIC 44 in the quic.Config, or use DialAddr")
+			}
+		}
+	}
 	packetHandlers, err := getMultiplexer().AddConn(pconn, config.ConnectionIDLength)
 	if err != nil {
 		return nil, err
@@ -234,6 +241,11 @@ func populateClientConfig(config *Config, createdPacketConn bool) *Config {
 	if connIDLen == 0 && !createdPacketConn {
 		connIDLen = protocol.DefaultConnectionIDLength
 	}
+	for _, v := range versions {
+		if v == protocol.Version44 {
+			connIDLen = 0
+		}
+	}
 
 	return &Config{
 		Versions:                              versions,
@@ -267,6 +279,9 @@ func (c *client) generateConnectionIDs() error {
 	}
 	c.srcConnID = srcConnID
 	c.destConnID = destConnID
+	if c.version == protocol.Version44 {
+		c.srcConnID = nil
+	}
 	return nil
 }
 
@@ -372,23 +387,32 @@ func (c *client) handlePacketImpl(p *receivedPacket) error {
 		return err
 	}
 
-	if p.header.IsPublicHeader {
-		return c.handleGQUICPacket(p)
+	if !c.version.UsesIETFHeaderFormat() {
+		connID := p.header.DestConnectionID
+		// reject packets with truncated connection id if we didn't request truncation
+		if !c.config.RequestConnectionIDOmission && connID.Len() == 0 {
+			return errors.New("received packet with truncated connection ID, but didn't request truncation")
+		}
+		// reject packets with the wrong connection ID
+		if connID.Len() > 0 && !connID.Equal(c.srcConnID) {
+			return fmt.Errorf("received a packet with an unexpected connection ID (%s, expected %s)", connID, c.srcConnID)
+		}
+		if p.header.ResetFlag {
+			return c.handlePublicReset(p)
+		}
+	} else {
+		// reject packets with the wrong connection ID
+		if !p.header.DestConnectionID.Equal(c.srcConnID) {
+			return fmt.Errorf("received a packet with an unexpected connection ID (%s, expected %s)", p.header.DestConnectionID, c.srcConnID)
+		}
 	}
-	return c.handleIETFQUICPacket(p)
-}
 
-func (c *client) handleIETFQUICPacket(p *receivedPacket) error {
-	// reject packets with the wrong connection ID
-	if !p.header.DestConnectionID.Equal(c.srcConnID) {
-		return fmt.Errorf("received a packet with an unexpected connection ID (%s, expected %s)", p.header.DestConnectionID, c.srcConnID)
-	}
 	if p.header.IsLongHeader {
 		switch p.header.Type {
 		case protocol.PacketTypeRetry:
 			c.handleRetryPacket(p.header)
 			return nil
-		case protocol.PacketTypeHandshake:
+		case protocol.PacketTypeHandshake, protocol.PacketType0RTT:
 		default:
 			return fmt.Errorf("Received unsupported packet type: %s", p.header.Type)
 		}
@@ -404,40 +428,19 @@ func (c *client) handleIETFQUICPacket(p *receivedPacket) error {
 	return nil
 }
 
-func (c *client) handleGQUICPacket(p *receivedPacket) error {
-	connID := p.header.DestConnectionID
-	// reject packets with truncated connection id if we didn't request truncation
-	if !c.config.RequestConnectionIDOmission && connID.Len() == 0 {
-		return errors.New("received packet with truncated connection ID, but didn't request truncation")
-	}
-	// reject packets with the wrong connection ID
-	if connID.Len() > 0 && !connID.Equal(c.srcConnID) {
-		return fmt.Errorf("received a packet with an unexpected connection ID (%s, expected %s)", connID, c.srcConnID)
-	}
-
-	if p.header.ResetFlag {
-		cr := c.conn.RemoteAddr()
-		// check if the remote address and the connection ID match
-		// otherwise this might be an attacker trying to inject a PUBLIC_RESET to kill the connection
-		if cr.Network() != p.remoteAddr.Network() || cr.String() != p.remoteAddr.String() || !connID.Equal(c.srcConnID) {
-			return errors.New("Received a spoofed Public Reset")
-		}
-		pr, err := wire.ParsePublicReset(bytes.NewReader(p.data))
-		if err != nil {
-			return fmt.Errorf("Received a Public Reset. An error occurred parsing the packet: %s", err)
-		}
-		c.session.closeRemote(qerr.Error(qerr.PublicReset, fmt.Sprintf("Received a Public Reset for packet number %#x", pr.RejectedPacketNumber)))
-		c.logger.Infof("Received Public Reset, rejected packet number: %#x", pr.RejectedPacketNumber)
-		return nil
+func (c *client) handlePublicReset(p *receivedPacket) error {
+	cr := c.conn.RemoteAddr()
+	// check if the remote address and the connection ID match
+	// otherwise this might be an attacker trying to inject a PUBLIC_RESET to kill the connection
+	if cr.Network() != p.remoteAddr.Network() || cr.String() != p.remoteAddr.String() || !p.header.DestConnectionID.Equal(c.srcConnID) {
+		return errors.New("Received a spoofed Public Reset")
 	}
-
-	// this is the first packet we are receiving
-	// since it is not a Version Negotiation Packet, this means the server supports the suggested version
-	if !c.versionNegotiated {
-		c.versionNegotiated = true
+	pr, err := wire.ParsePublicReset(bytes.NewReader(p.data))
+	if err != nil {
+		return fmt.Errorf("Received a Public Reset. An error occurred parsing the packet: %s", err)
 	}
-
-	c.session.handlePacket(p)
+	c.session.closeRemote(qerr.Error(qerr.PublicReset, fmt.Sprintf("Received a Public Reset for packet number %#x", pr.RejectedPacketNumber)))
+	c.logger.Infof("Received Public Reset, rejected packet number: %#x", pr.RejectedPacketNumber)
 	return nil
 }
 
@@ -513,6 +516,7 @@ func (c *client) createNewGQUICSession() error {
 		c.hostname,
 		c.version,
 		c.destConnID,
+		c.srcConnID,
 		c.tlsConf,
 		c.config,
 		c.initialVersion,

+ 117 - 24
client_test.go

@@ -31,7 +31,9 @@ var _ = Describe("Client", func() {
 		mockMultiplexer *MockMultiplexer
 		origMultiplexer multiplexer
 
-		originalClientSessConstructor func(connection, sessionRunner, string, protocol.VersionNumber, protocol.ConnectionID, *tls.Config, *Config, protocol.VersionNumber, []protocol.VersionNumber, utils.Logger) (quicSession, error)
+		supportedVersionsWithoutGQUIC44 []protocol.VersionNumber
+
+		originalClientSessConstructor func(connection, sessionRunner, string, protocol.VersionNumber, protocol.ConnectionID, protocol.ConnectionID, *tls.Config, *Config, protocol.VersionNumber, []protocol.VersionNumber, utils.Logger) (quicSession, error)
 	)
 
 	// generate a packet sent by the server that accepts the QUIC version suggested by the client
@@ -78,6 +80,12 @@ var _ = Describe("Client", func() {
 		mockMultiplexer = NewMockMultiplexer(mockCtrl)
 		origMultiplexer = connMuxer
 		connMuxer = mockMultiplexer
+		for _, v := range protocol.SupportedVersions {
+			if v != protocol.Version44 {
+				supportedVersionsWithoutGQUIC44 = append(supportedVersionsWithoutGQUIC44, v)
+			}
+		}
+		Expect(supportedVersionsWithoutGQUIC44).ToNot(BeEmpty())
 	})
 
 	AfterEach(func() {
@@ -114,7 +122,7 @@ var _ = Describe("Client", func() {
 
 		It("resolves the address", func() {
 			manager := NewMockPacketHandlerManager(mockCtrl)
-			manager.EXPECT().Add(connID, gomock.Any())
+			manager.EXPECT().Add(gomock.Any(), gomock.Any())
 			mockMultiplexer.EXPECT().AddConn(gomock.Any(), gomock.Any()).Return(manager, nil)
 
 			if os.Getenv("APPVEYOR") == "True" {
@@ -127,6 +135,7 @@ var _ = Describe("Client", func() {
 				_ string,
 				_ protocol.VersionNumber,
 				_ protocol.ConnectionID,
+				_ protocol.ConnectionID,
 				_ *tls.Config,
 				_ *Config,
 				_ protocol.VersionNumber,
@@ -145,7 +154,7 @@ var _ = Describe("Client", func() {
 
 		It("uses the tls.Config.ServerName as the hostname, if present", func() {
 			manager := NewMockPacketHandlerManager(mockCtrl)
-			manager.EXPECT().Add(connID, gomock.Any())
+			manager.EXPECT().Add(gomock.Any(), gomock.Any())
 			mockMultiplexer.EXPECT().AddConn(gomock.Any(), gomock.Any()).Return(manager, nil)
 
 			hostnameChan := make(chan string, 1)
@@ -155,6 +164,7 @@ var _ = Describe("Client", func() {
 				h string,
 				_ protocol.VersionNumber,
 				_ protocol.ConnectionID,
+				_ protocol.ConnectionID,
 				_ *tls.Config,
 				_ *Config,
 				_ protocol.VersionNumber,
@@ -173,7 +183,7 @@ var _ = Describe("Client", func() {
 
 		It("returns after the handshake is complete", func() {
 			manager := NewMockPacketHandlerManager(mockCtrl)
-			manager.EXPECT().Add(connID, gomock.Any())
+			manager.EXPECT().Add(gomock.Any(), gomock.Any())
 			mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any()).Return(manager, nil)
 
 			run := make(chan struct{})
@@ -183,6 +193,7 @@ var _ = Describe("Client", func() {
 				_ string,
 				_ protocol.VersionNumber,
 				_ protocol.ConnectionID,
+				_ protocol.ConnectionID,
 				_ *tls.Config,
 				_ *Config,
 				_ protocol.VersionNumber,
@@ -194,15 +205,33 @@ var _ = Describe("Client", func() {
 				runner.onHandshakeComplete(sess)
 				return sess, nil
 			}
-			s, err := Dial(packetConn, addr, "quic.clemente.io:1337", nil, nil)
+			s, err := Dial(
+				packetConn,
+				addr,
+				"quic.clemente.io:1337",
+				nil,
+				&Config{Versions: supportedVersionsWithoutGQUIC44},
+			)
 			Expect(err).ToNot(HaveOccurred())
 			Expect(s).ToNot(BeNil())
 			Eventually(run).Should(BeClosed())
 		})
 
+		It("refuses to multiplex gQUIC 44", func() {
+			_, err := Dial(
+				packetConn,
+				addr,
+				"quic.clemente.io:1337",
+				nil,
+				&Config{Versions: []protocol.VersionNumber{protocol.Version44}},
+			)
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(ContainSubstring("Cannot multiplex connections using gQUIC 44"))
+		})
+
 		It("returns an error that occurs while waiting for the connection to become secure", func() {
 			manager := NewMockPacketHandlerManager(mockCtrl)
-			manager.EXPECT().Add(connID, gomock.Any())
+			manager.EXPECT().Add(gomock.Any(), gomock.Any())
 			mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any()).Return(manager, nil)
 
 			testErr := errors.New("early handshake error")
@@ -212,6 +241,7 @@ var _ = Describe("Client", func() {
 				_ string,
 				_ protocol.VersionNumber,
 				_ protocol.ConnectionID,
+				_ protocol.ConnectionID,
 				_ *tls.Config,
 				_ *Config,
 				_ protocol.VersionNumber,
@@ -223,13 +253,19 @@ var _ = Describe("Client", func() {
 				return sess, nil
 			}
 			packetConn.dataToRead <- acceptClientVersionPacket(cl.srcConnID)
-			_, err := Dial(packetConn, addr, "quic.clemente.io:1337", nil, nil)
+			_, err := Dial(
+				packetConn,
+				addr,
+				"quic.clemente.io:1337",
+				nil,
+				&Config{Versions: supportedVersionsWithoutGQUIC44},
+			)
 			Expect(err).To(MatchError(testErr))
 		})
 
 		It("closes the session when the context is canceled", func() {
 			manager := NewMockPacketHandlerManager(mockCtrl)
-			manager.EXPECT().Add(connID, gomock.Any())
+			manager.EXPECT().Add(gomock.Any(), gomock.Any())
 			mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any()).Return(manager, nil)
 
 			sessionRunning := make(chan struct{})
@@ -244,6 +280,7 @@ var _ = Describe("Client", func() {
 				_ string,
 				_ protocol.VersionNumber,
 				_ protocol.ConnectionID,
+				_ protocol.ConnectionID,
 				_ *tls.Config,
 				_ *Config,
 				_ protocol.VersionNumber,
@@ -256,7 +293,14 @@ var _ = Describe("Client", func() {
 			dialed := make(chan struct{})
 			go func() {
 				defer GinkgoRecover()
-				_, err := DialContext(ctx, packetConn, addr, "quic.clemnte.io:1337", nil, nil)
+				_, err := DialContext(
+					ctx,
+					packetConn,
+					addr,
+					"quic.clemnte.io:1337",
+					nil,
+					&Config{Versions: supportedVersionsWithoutGQUIC44},
+				)
 				Expect(err).To(MatchError(context.Canceled))
 				close(dialed)
 			}()
@@ -279,7 +323,8 @@ var _ = Describe("Client", func() {
 				runnerP sessionRunner,
 				_ string,
 				_ protocol.VersionNumber,
-				connID protocol.ConnectionID,
+				_ protocol.ConnectionID,
+				_ protocol.ConnectionID,
 				_ *tls.Config,
 				_ *Config,
 				_ protocol.VersionNumber,
@@ -293,7 +338,14 @@ var _ = Describe("Client", func() {
 				runner.removeConnectionID(connID)
 			})
 
-			_, err := DialContext(context.Background(), packetConn, addr, "quic.clemnte.io:1337", nil, nil)
+			_, err := DialContext(
+				context.Background(),
+				packetConn,
+				addr,
+				"quic.clemnte.io:1337",
+				nil,
+				&Config{Versions: supportedVersionsWithoutGQUIC44},
+			)
 			Expect(err).ToNot(HaveOccurred())
 		})
 
@@ -311,7 +363,8 @@ var _ = Describe("Client", func() {
 				_ sessionRunner,
 				_ string,
 				_ protocol.VersionNumber,
-				connID protocol.ConnectionID,
+				_ protocol.ConnectionID,
+				_ protocol.ConnectionID,
 				_ *tls.Config,
 				_ *Config,
 				_ protocol.VersionNumber,
@@ -358,6 +411,7 @@ var _ = Describe("Client", func() {
 					MaxIncomingStreams:          1234,
 					MaxIncomingUniStreams:       4321,
 					ConnectionIDLength:          13,
+					Versions:                    supportedVersionsWithoutGQUIC44,
 				}
 				c := populateClientConfig(config, false)
 				Expect(c.HandshakeTimeout).To(Equal(1337 * time.Minute))
@@ -368,6 +422,22 @@ var _ = Describe("Client", func() {
 				Expect(c.ConnectionIDLength).To(Equal(13))
 			})
 
+			It("uses a 0 byte connection IDs if gQUIC 44 is supported", func() {
+				config := &Config{
+					Versions:           []protocol.VersionNumber{protocol.Version43, protocol.Version44},
+					ConnectionIDLength: 13,
+				}
+				c := populateClientConfig(config, false)
+				Expect(c.Versions).To(Equal([]protocol.VersionNumber{protocol.Version43, protocol.Version44}))
+				Expect(c.ConnectionIDLength).To(BeZero())
+			})
+
+			It("doesn't use 0-byte connection IDs when dialing an address", func() {
+				config := &Config{Versions: supportedVersionsWithoutGQUIC44}
+				c := populateClientConfig(config, false)
+				Expect(c.ConnectionIDLength).To(Equal(protocol.DefaultConnectionIDLength))
+			})
+
 			It("errors when the Config contains an invalid version", func() {
 				manager := NewMockPacketHandlerManager(mockCtrl)
 				mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any()).Return(manager, nil)
@@ -403,12 +473,6 @@ var _ = Describe("Client", func() {
 				Expect(c.ConnectionIDLength).To(BeZero())
 			})
 
-			It("doesn't use 0-byte connection IDs when dialing an address", func() {
-				config := &Config{}
-				c := populateClientConfig(config, false)
-				Expect(c.ConnectionIDLength).To(Equal(protocol.DefaultConnectionIDLength))
-			})
-
 			It("fills in default values if options are not set in the Config", func() {
 				c := populateClientConfig(&Config{}, false)
 				Expect(c.Versions).To(Equal(protocol.SupportedVersions))
@@ -430,6 +494,7 @@ var _ = Describe("Client", func() {
 					_ string,
 					_ protocol.VersionNumber,
 					_ protocol.ConnectionID,
+					_ protocol.ConnectionID,
 					_ *tls.Config,
 					_ *Config,
 					_ protocol.VersionNumber,
@@ -438,7 +503,13 @@ var _ = Describe("Client", func() {
 				) (quicSession, error) {
 					return nil, testErr
 				}
-				_, err := Dial(packetConn, addr, "quic.clemente.io:1337", nil, nil)
+				_, err := Dial(
+					packetConn,
+					addr,
+					"quic.clemente.io:1337",
+					nil,
+					&Config{Versions: supportedVersionsWithoutGQUIC44},
+				)
 				Expect(err).To(MatchError(testErr))
 			})
 		})
@@ -615,6 +686,7 @@ var _ = Describe("Client", func() {
 					_ string,
 					_ protocol.VersionNumber,
 					_ protocol.ConnectionID,
+					_ protocol.ConnectionID,
 					_ *tls.Config,
 					_ *Config,
 					_ protocol.VersionNumber,
@@ -626,7 +698,13 @@ var _ = Describe("Client", func() {
 					sess.EXPECT().run().Return(testErr)
 					return sess, nil
 				}
-				_, err := Dial(packetConn, addr, "quic.clemente.io:1337", nil, nil)
+				_, err := Dial(
+					packetConn,
+					addr,
+					"quic.clemente.io:1337",
+					nil,
+					&Config{Versions: supportedVersionsWithoutGQUIC44},
+				)
 				Expect(err).To(MatchError(testErr))
 			})
 
@@ -634,6 +712,7 @@ var _ = Describe("Client", func() {
 				sess := NewMockQuicSession(mockCtrl)
 				sess.EXPECT().handlePacket(gomock.Any())
 				cl.session = sess
+				cl.config = &Config{}
 				ph := &wire.Header{
 					PacketNumber:     1,
 					PacketNumberLen:  protocol.PacketNumberLen2,
@@ -667,7 +746,8 @@ var _ = Describe("Client", func() {
 					_ sessionRunner,
 					_ string,
 					_ protocol.VersionNumber,
-					connectionID protocol.ConnectionID,
+					_ protocol.ConnectionID,
+					_ protocol.ConnectionID,
 					_ *tls.Config,
 					_ *Config,
 					_ protocol.VersionNumber,
@@ -713,7 +793,8 @@ var _ = Describe("Client", func() {
 					_ sessionRunner,
 					_ string,
 					_ protocol.VersionNumber,
-					connectionID protocol.ConnectionID,
+					_ protocol.ConnectionID,
+					_ protocol.ConnectionID,
 					_ *tls.Config,
 					_ *Config,
 					_ protocol.VersionNumber,
@@ -786,6 +867,7 @@ var _ = Describe("Client", func() {
 	})
 
 	It("ignores packets with the wrong Long Header Type", func() {
+		cl.config = &Config{}
 		hdr := &wire.Header{
 			IsLongHeader:     true,
 			Type:             protocol.PacketTypeInitial,
@@ -804,6 +886,7 @@ var _ = Describe("Client", func() {
 	})
 
 	It("ignores packets without connection id, if it didn't request connection id trunctation", func() {
+		cl.version = versionGQUICFrames
 		cl.session = NewMockQuicSession(mockCtrl) // don't EXPECT any handlePacket calls
 		cl.config = &Config{RequestConnectionIDOmission: false}
 		hdr := &wire.Header{
@@ -843,7 +926,6 @@ var _ = Describe("Client", func() {
 		manager.EXPECT().Add(gomock.Any(), gomock.Any())
 		mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any()).Return(manager, nil)
 
-		config := &Config{Versions: protocol.SupportedVersions}
 		c := make(chan struct{})
 		var cconn connection
 		var hostname string
@@ -855,6 +937,7 @@ var _ = Describe("Client", func() {
 			hostnameP string,
 			versionP protocol.VersionNumber,
 			connIDP protocol.ConnectionID,
+			_ protocol.ConnectionID,
 			_ *tls.Config,
 			configP *Config,
 			_ protocol.VersionNumber,
@@ -872,7 +955,14 @@ var _ = Describe("Client", func() {
 			return sess, nil
 		}
 
-		_, err := Dial(packetConn, addr, "quic.clemente.io:1337", nil, config)
+		config := &Config{Versions: supportedVersionsWithoutGQUIC44}
+		_, err := Dial(
+			packetConn,
+			addr,
+			"quic.clemente.io:1337",
+			nil,
+			config,
+		)
 		Expect(err).ToNot(HaveOccurred())
 		Eventually(c).Should(BeClosed())
 		Expect(cconn.(*conn).pconn).To(Equal(packetConn))
@@ -901,6 +991,7 @@ var _ = Describe("Client", func() {
 		})
 
 		It("closes the session when receiving a Public Reset", func() {
+			cl.version = versionGQUICFrames
 			sess := NewMockQuicSession(mockCtrl)
 			sess.EXPECT().closeRemote(gomock.Any()).Do(func(err error) {
 				Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(qerr.PublicReset))
@@ -914,6 +1005,7 @@ var _ = Describe("Client", func() {
 		})
 
 		It("ignores Public Resets from the wrong remote address", func() {
+			cl.version = versionGQUICFrames
 			cl.session = NewMockQuicSession(mockCtrl) // don't EXPECT any calls
 			spoofedAddr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 5678}
 			err := cl.handlePacketImpl(&receivedPacket{
@@ -925,6 +1017,7 @@ var _ = Describe("Client", func() {
 		})
 
 		It("ignores unparseable Public Resets", func() {
+			cl.version = versionGQUICFrames
 			cl.session = NewMockQuicSession(mockCtrl) // don't EXPECT any calls
 			err := cl.handlePacketImpl(&receivedPacket{
 				remoteAddr: addr,

+ 5 - 0
integrationtests/chrome/chrome_test.go

@@ -13,6 +13,11 @@ var _ = Describe("Chrome tests", func() {
 	for i := range protocol.SupportedVersions {
 		version := protocol.SupportedVersions[i]
 
+		// TODO: activate Chrome integration tests with gQUIC 44
+		if version == protocol.Version44 {
+			continue
+		}
+
 		Context(fmt.Sprintf("with version %s", version), func() {
 			JustBeforeEach(func() {
 				testserver.StartQuicServer([]protocol.VersionNumber{version})

+ 6 - 0
integrationtests/self/multiplex_test.go

@@ -21,6 +21,12 @@ var _ = Describe("Multiplexing", func() {
 	for _, v := range append(protocol.SupportedVersions, protocol.VersionTLS) {
 		version := v
 
+		// gQUIC 44 uses 0 byte connection IDs for packets sent to the client
+		// It's not possible to do demultiplexing.
+		if v == protocol.Version44 {
+			continue
+		}
+
 		Context(fmt.Sprintf("with QUIC version %s", version), func() {
 			runServer := func(ln quic.Listener) {
 				go func() {

+ 2 - 0
interface.go

@@ -21,6 +21,8 @@ const (
 	VersionGQUIC39 = protocol.Version39
 	// VersionGQUIC43 is gQUIC version 43.
 	VersionGQUIC43 = protocol.Version43
+	// VersionGQUIC43 is gQUIC version 44.
+	VersionGQUIC44 = protocol.Version44
 )
 
 // A Cookie can be used to verify the ownership of the client address.

+ 19 - 1
internal/protocol/version.go

@@ -20,6 +20,7 @@ const (
 const (
 	Version39       VersionNumber = gquicVersion0 + 3*0x100 + 0x9
 	Version43       VersionNumber = gquicVersion0 + 4*0x100 + 0x3
+	Version44       VersionNumber = gquicVersion0 + 4*0x100 + 0x4
 	VersionTLS      VersionNumber = 101
 	VersionWhatever VersionNumber = 0 // for when the version doesn't matter
 	VersionUnknown  VersionNumber = math.MaxUint32
@@ -28,6 +29,7 @@ const (
 // SupportedVersions lists the versions that the server supports
 // must be in sorted descending order
 var SupportedVersions = []VersionNumber{
+	Version44,
 	Version43,
 	Version39,
 }
@@ -79,11 +81,27 @@ func (vn VersionNumber) UsesIETFFrameFormat() bool {
 	return !vn.isGQUIC()
 }
 
+// UsesIETFHeaderFormat tells if this version uses the IETF header format
+func (vn VersionNumber) UsesIETFHeaderFormat() bool {
+	return !vn.isGQUIC() || vn >= Version44
+}
+
+// UsesLengthInHeader tells if this version uses the Length field in the IETF header
+func (vn VersionNumber) UsesLengthInHeader() bool {
+	return !vn.isGQUIC()
+}
+
+// UsesTokenInHeader tells if this version uses the Token field in the IETF header
+func (vn VersionNumber) UsesTokenInHeader() bool {
+	return !vn.isGQUIC()
+}
+
 // UsesStopWaitingFrames tells if this version uses STOP_WAITING frames
 func (vn VersionNumber) UsesStopWaitingFrames() bool {
-	return vn.isGQUIC()
+	return vn.isGQUIC() && vn <= Version43
 }
 
+// UsesVarintPacketNumbers tells if this version uses 7/14/30 bit packet numbers
 func (vn VersionNumber) UsesVarintPacketNumbers() bool {
 	return !vn.isGQUIC()
 }

+ 25 - 5
internal/protocol/version_test.go

@@ -14,11 +14,13 @@ var _ = Describe("Version", func() {
 	It("has the right gQUIC version number", func() {
 		Expect(Version39).To(BeEquivalentTo(0x51303339))
 		Expect(Version43).To(BeEquivalentTo(0x51303433))
+		Expect(Version44).To(BeEquivalentTo(0x51303434))
 	})
 
 	It("says if a version is valid", func() {
 		Expect(IsValidVersion(Version39)).To(BeTrue())
 		Expect(IsValidVersion(Version43)).To(BeTrue())
+		Expect(IsValidVersion(Version44)).To(BeTrue())
 		Expect(IsValidVersion(VersionTLS)).To(BeTrue())
 		Expect(IsValidVersion(VersionWhatever)).To(BeFalse())
 		Expect(IsValidVersion(VersionUnknown)).To(BeFalse())
@@ -28,12 +30,14 @@ var _ = Describe("Version", func() {
 	It("says if a version supports TLS", func() {
 		Expect(Version39.UsesTLS()).To(BeFalse())
 		Expect(Version43.UsesTLS()).To(BeFalse())
+		Expect(Version44.UsesTLS()).To(BeFalse())
 		Expect(VersionTLS.UsesTLS()).To(BeTrue())
 	})
 
 	It("versions don't have reserved version numbers", func() {
 		Expect(isReservedVersion(Version39)).To(BeFalse())
 		Expect(isReservedVersion(Version43)).To(BeFalse())
+		Expect(isReservedVersion(Version44)).To(BeFalse())
 		Expect(isReservedVersion(VersionTLS)).To(BeFalse())
 	})
 
@@ -53,6 +57,7 @@ var _ = Describe("Version", func() {
 	It("has the right representation for the H2 Alt-Svc tag", func() {
 		Expect(Version39.ToAltSvc()).To(Equal("39"))
 		Expect(Version43.ToAltSvc()).To(Equal("43"))
+		Expect(Version44.ToAltSvc()).To(Equal("44"))
 		Expect(VersionTLS.ToAltSvc()).To(Equal("101"))
 		// check with unsupported version numbers from the wiki
 		Expect(VersionNumber(0x51303133).ToAltSvc()).To(Equal("13"))
@@ -63,35 +68,50 @@ var _ = Describe("Version", func() {
 	It("tells the Stream ID of the crypto stream", func() {
 		Expect(Version39.CryptoStreamID()).To(Equal(StreamID(1)))
 		Expect(Version43.CryptoStreamID()).To(Equal(StreamID(1)))
+		Expect(Version44.CryptoStreamID()).To(Equal(StreamID(1)))
 		Expect(VersionTLS.CryptoStreamID()).To(Equal(StreamID(0)))
 	})
 
 	It("tells if a version uses the IETF frame types", func() {
 		Expect(Version39.UsesIETFFrameFormat()).To(BeFalse())
 		Expect(Version43.UsesIETFFrameFormat()).To(BeFalse())
+		Expect(Version44.UsesIETFFrameFormat()).To(BeFalse())
 		Expect(VersionTLS.UsesIETFFrameFormat()).To(BeTrue())
 	})
 
+	It("tells if a version uses the IETF header format", func() {
+		Expect(Version39.UsesIETFHeaderFormat()).To(BeFalse())
+		Expect(Version43.UsesIETFHeaderFormat()).To(BeFalse())
+		Expect(Version44.UsesIETFHeaderFormat()).To(BeTrue())
+		Expect(VersionTLS.UsesIETFHeaderFormat()).To(BeTrue())
+	})
+
 	It("tells if a version uses varint packet numbers", func() {
 		Expect(Version39.UsesVarintPacketNumbers()).To(BeFalse())
 		Expect(Version43.UsesVarintPacketNumbers()).To(BeFalse())
+		Expect(Version44.UsesVarintPacketNumbers()).To(BeFalse())
 		Expect(VersionTLS.UsesVarintPacketNumbers()).To(BeTrue())
 	})
 
-	It("tells if a version uses the IETF frame types", func() {
-		Expect(Version39.UsesIETFFrameFormat()).To(BeFalse())
-		Expect(Version43.UsesIETFFrameFormat()).To(BeFalse())
-		Expect(VersionTLS.UsesIETFFrameFormat()).To(BeTrue())
+	It("tells if a version uses the Length field in the IETF header", func() {
+		Expect(Version44.UsesLengthInHeader()).To(BeFalse())
+		Expect(VersionTLS.UsesLengthInHeader()).To(BeTrue())
+	})
+
+	It("tells if a version uses the Token field in the IETF header", func() {
+		Expect(Version44.UsesTokenInHeader()).To(BeFalse())
+		Expect(VersionTLS.UsesTokenInHeader()).To(BeTrue())
 	})
 
 	It("tells if a version uses STOP_WAITING frames", func() {
 		Expect(Version39.UsesStopWaitingFrames()).To(BeTrue())
 		Expect(Version43.UsesStopWaitingFrames()).To(BeTrue())
+		Expect(Version44.UsesStopWaitingFrames()).To(BeFalse())
 		Expect(VersionTLS.UsesStopWaitingFrames()).To(BeFalse())
 	})
 
 	It("says if a stream contributes to connection-level flowcontrol, for gQUIC", func() {
-		for _, v := range []VersionNumber{Version39, Version43} {
+		for _, v := range []VersionNumber{Version39, Version43, Version44} {
 			version := v
 			Expect(version.StreamContributesToConnectionFlowControl(1)).To(BeFalse())
 			Expect(version.StreamContributesToConnectionFlowControl(2)).To(BeTrue())

+ 5 - 0
internal/wire/frame_parser.go

@@ -2,6 +2,7 @@ package wire
 
 import (
 	"bytes"
+	"errors"
 	"fmt"
 
 	"github.com/lucas-clemente/quic-go/internal/protocol"
@@ -154,6 +155,10 @@ func parseGQUICFrame(r *bytes.Reader, typeByte byte, hdr *Header, v protocol.Ver
 			err = qerr.Error(qerr.InvalidBlockedData, err.Error())
 		}
 	case 0x6:
+		if !v.UsesStopWaitingFrames() {
+			err = errors.New("STOP_WAITING frames not supported by this QUIC version")
+			break
+		}
 		frame, err = parseStopWaitingFrame(r, hdr.PacketNumber, hdr.PacketNumberLen, v)
 		if err != nil {
 			err = qerr.Error(qerr.InvalidStopWaitingData, err.Error())

+ 17 - 0
internal/wire/frame_parser_test.go

@@ -134,6 +134,23 @@ var _ = Describe("Frame parsing", func() {
 			Expect(frame.(*StopWaitingFrame).LeastUnacked).To(Equal(protocol.PacketNumber(0x1337)))
 		})
 
+		It("errors on STOP_WAITING frames in QUIC 44", func() {
+			Expect(protocol.Version44.UsesStopWaitingFrames()).To(BeFalse())
+			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())
+			_, err = ParseNextFrame(bytes.NewReader(buf.Bytes()), hdr, protocol.Version44)
+			Expect(err).To(MatchError("STOP_WAITING frames not supported by this QUIC version"))
+		})
+
 		It("unpacks PING frames", func() {
 			f := &PingFrame{}
 			err := f.Write(buf, versionBigEndian)

+ 69 - 20
internal/wire/header.go

@@ -42,23 +42,23 @@ type Header struct {
 	Token        []byte
 }
 
-var errInvalidPacketNumberLen6 = errors.New("invalid packet number length: 6 bytes")
+var errInvalidPacketNumberLen = errors.New("invalid packet number length")
 
 // Write writes the Header.
-func (h *Header) Write(b *bytes.Buffer, pers protocol.Perspective, version protocol.VersionNumber) error {
-	if !version.UsesTLS() {
+func (h *Header) Write(b *bytes.Buffer, pers protocol.Perspective, ver protocol.VersionNumber) error {
+	if !ver.UsesIETFHeaderFormat() {
 		h.IsPublicHeader = true // save that this is a Public Header, so we can log it correctly later
-		return h.writePublicHeader(b, pers, version)
+		return h.writePublicHeader(b, pers, ver)
 	}
 	// write an IETF QUIC header
 	if h.IsLongHeader {
-		return h.writeLongHeader(b)
+		return h.writeLongHeader(b, ver)
 	}
-	return h.writeShortHeader(b)
+	return h.writeShortHeader(b, ver)
 }
 
 // TODO: add support for the key phase
-func (h *Header) writeLongHeader(b *bytes.Buffer) error {
+func (h *Header) writeLongHeader(b *bytes.Buffer, v protocol.VersionNumber) error {
 	b.WriteByte(byte(0x80 | h.Type))
 	utils.BigEndian.WriteUint32(b, uint32(h.Version))
 	connIDLen, err := encodeConnIDLen(h.DestConnectionID, h.SrcConnectionID)
@@ -69,7 +69,7 @@ func (h *Header) writeLongHeader(b *bytes.Buffer) error {
 	b.Write(h.DestConnectionID.Bytes())
 	b.Write(h.SrcConnectionID.Bytes())
 
-	if h.Type == protocol.PacketTypeInitial {
+	if h.Type == protocol.PacketTypeInitial && v.UsesTokenInHeader() {
 		utils.WriteVarInt(b, uint64(len(h.Token)))
 		b.Write(h.Token)
 	}
@@ -89,16 +89,51 @@ func (h *Header) writeLongHeader(b *bytes.Buffer) error {
 		return nil
 	}
 
-	utils.WriteVarInt(b, uint64(h.PayloadLen))
-	return utils.WriteVarIntPacketNumber(b, h.PacketNumber, h.PacketNumberLen)
+	if v.UsesLengthInHeader() {
+		utils.WriteVarInt(b, uint64(h.PayloadLen))
+	}
+	if v.UsesVarintPacketNumbers() {
+		return utils.WriteVarIntPacketNumber(b, h.PacketNumber, h.PacketNumberLen)
+	}
+	utils.BigEndian.WriteUint32(b, uint32(h.PacketNumber))
+	if h.Type == protocol.PacketType0RTT && v == protocol.Version44 {
+		if len(h.DiversificationNonce) != 32 {
+			return errors.New("invalid diversification nonce length")
+		}
+		b.Write(h.DiversificationNonce)
+	}
+	return nil
 }
 
-func (h *Header) writeShortHeader(b *bytes.Buffer) error {
+func (h *Header) writeShortHeader(b *bytes.Buffer, v protocol.VersionNumber) error {
 	typeByte := byte(0x30)
 	typeByte |= byte(h.KeyPhase << 6)
-	b.WriteByte(typeByte)
+	if !v.UsesVarintPacketNumbers() {
+		switch h.PacketNumberLen {
+		case protocol.PacketNumberLen1:
+		case protocol.PacketNumberLen2:
+			typeByte |= 0x1
+		case protocol.PacketNumberLen4:
+			typeByte |= 0x2
+		default:
+			return errInvalidPacketNumberLen
+		}
+	}
 
+	b.WriteByte(typeByte)
 	b.Write(h.DestConnectionID.Bytes())
+
+	if !v.UsesVarintPacketNumbers() {
+		switch h.PacketNumberLen {
+		case protocol.PacketNumberLen1:
+			b.WriteByte(uint8(h.PacketNumber))
+		case protocol.PacketNumberLen2:
+			utils.BigEndian.WriteUint16(b, uint16(h.PacketNumber))
+		case protocol.PacketNumberLen4:
+			utils.BigEndian.WriteUint32(b, uint32(h.PacketNumber))
+		}
+		return nil
+	}
 	return utils.WriteVarIntPacketNumber(b, h.PacketNumber, h.PacketNumberLen)
 }
 
@@ -155,7 +190,7 @@ func (h *Header) writePublicHeader(b *bytes.Buffer, pers protocol.Perspective, _
 	case protocol.PacketNumberLen4:
 		utils.BigEndian.WriteUint32(b, uint32(h.PacketNumber))
 	case protocol.PacketNumberLen6:
-		return errInvalidPacketNumberLen6
+		return errInvalidPacketNumberLen
 	default:
 		return errors.New("PublicHeader: PacketNumberLen not set")
 	}
@@ -164,19 +199,25 @@ func (h *Header) writePublicHeader(b *bytes.Buffer, pers protocol.Perspective, _
 }
 
 // GetLength determines the length of the Header.
-func (h *Header) GetLength(version protocol.VersionNumber) (protocol.ByteCount, error) {
-	if !version.UsesTLS() {
+func (h *Header) GetLength(v protocol.VersionNumber) (protocol.ByteCount, error) {
+	if !v.UsesIETFHeaderFormat() {
 		return h.getPublicHeaderLength()
 	}
-	return h.getHeaderLength()
+	return h.getHeaderLength(v)
 }
 
-func (h *Header) getHeaderLength() (protocol.ByteCount, error) {
+func (h *Header) getHeaderLength(v protocol.VersionNumber) (protocol.ByteCount, error) {
 	if h.IsLongHeader {
-		length := 1 /* type byte */ + 4 /* version */ + 1 /* conn id len byte */ + protocol.ByteCount(h.DestConnectionID.Len()+h.SrcConnectionID.Len()) + utils.VarIntLen(uint64(h.PayloadLen)) + protocol.ByteCount(h.PacketNumberLen)
-		if h.Type == protocol.PacketTypeInitial {
+		length := 1 /* type byte */ + 4 /* version */ + 1 /* conn id len byte */ + protocol.ByteCount(h.DestConnectionID.Len()+h.SrcConnectionID.Len()) + protocol.ByteCount(h.PacketNumberLen)
+		if v.UsesLengthInHeader() {
+			length += utils.VarIntLen(uint64(h.PayloadLen))
+		}
+		if h.Type == protocol.PacketTypeInitial && v.UsesTokenInHeader() {
 			length += utils.VarIntLen(uint64(len(h.Token))) + protocol.ByteCount(len(h.Token))
 		}
+		if h.Type == protocol.PacketType0RTT && v == protocol.Version44 {
+			length += protocol.ByteCount(len(h.DiversificationNonce))
+		}
 		return length, nil
 	}
 
@@ -193,7 +234,7 @@ func (h *Header) getHeaderLength() (protocol.ByteCount, error) {
 func (h *Header) getPublicHeaderLength() (protocol.ByteCount, error) {
 	length := protocol.ByteCount(1) // 1 byte for public flags
 	if h.PacketNumberLen == protocol.PacketNumberLen6 {
-		return 0, errInvalidPacketNumberLen6
+		return 0, errInvalidPacketNumberLen
 	}
 	if h.PacketNumberLen != protocol.PacketNumberLen1 && h.PacketNumberLen != protocol.PacketNumberLen2 && h.PacketNumberLen != protocol.PacketNumberLen4 {
 		return 0, errPacketNumberLenNotSet
@@ -234,6 +275,14 @@ func (h *Header) logHeader(logger utils.Logger) {
 				logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sOrigDestConnectionID: %s, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.OrigDestConnectionID, h.Version)
 				return
 			}
+			if h.Version == protocol.Version44 {
+				var divNonce string
+				if h.Type == protocol.PacketType0RTT {
+					divNonce = fmt.Sprintf("Diversification Nonce: %#x, ", h.DiversificationNonce)
+				}
+				logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, PacketNumber: %#x, PacketNumberLen: %d, %sVersion: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, h.PacketNumber, h.PacketNumberLen, divNonce, h.Version)
+				return
+			}
 			logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sPacketNumber: %#x, PacketNumberLen: %d, PayloadLen: %d, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.PacketNumber, h.PacketNumberLen, h.PayloadLen, h.Version)
 		}
 	} else {

+ 58 - 20
internal/wire/header_parser.go

@@ -79,7 +79,7 @@ func (iv *InvariantHeader) Parse(b *bytes.Reader, sentBy protocol.Perspective, v
 		if iv.Version == 0 { // Version Negotiation Packet
 			return iv.parseVersionNegotiationPacket(b)
 		}
-		return iv.parseLongHeader(b)
+		return iv.parseLongHeader(b, sentBy, ver)
 	}
 	// The Public Header never uses 6 byte packet numbers.
 	// Therefore, the third and fourth bit will never be 11.
@@ -90,8 +90,7 @@ func (iv *InvariantHeader) Parse(b *bytes.Reader, sentBy protocol.Perspective, v
 		}
 		return iv.parsePublicHeader(b, sentBy, ver)
 	}
-	return iv.parseShortHeader(b)
-
+	return iv.parseShortHeader(b, ver)
 }
 
 func (iv *InvariantHeader) toHeader() *Header {
@@ -121,7 +120,7 @@ func (iv *InvariantHeader) parseVersionNegotiationPacket(b *bytes.Reader) (*Head
 	return h, nil
 }
 
-func (iv *InvariantHeader) parseLongHeader(b *bytes.Reader) (*Header, error) {
+func (iv *InvariantHeader) parseLongHeader(b *bytes.Reader, sentBy protocol.Perspective, v protocol.VersionNumber) (*Header, error) {
 	h := iv.toHeader()
 	h.Type = protocol.PacketType(iv.typeByte & 0x7f)
 
@@ -146,7 +145,7 @@ func (iv *InvariantHeader) parseLongHeader(b *bytes.Reader) (*Header, error) {
 		return h, nil
 	}
 
-	if h.Type == protocol.PacketTypeInitial {
+	if h.Type == protocol.PacketTypeInitial && v.UsesTokenInHeader() {
 		tokenLen, err := utils.ReadVarInt(b)
 		if err != nil {
 			return nil, err
@@ -160,30 +159,69 @@ func (iv *InvariantHeader) parseLongHeader(b *bytes.Reader) (*Header, error) {
 		}
 	}
 
-	pl, err := utils.ReadVarInt(b)
-	if err != nil {
-		return nil, err
+	if v.UsesLengthInHeader() {
+		pl, err := utils.ReadVarInt(b)
+		if err != nil {
+			return nil, err
+		}
+		h.PayloadLen = protocol.ByteCount(pl)
 	}
-	h.PayloadLen = protocol.ByteCount(pl)
-	pn, pnLen, err := utils.ReadVarIntPacketNumber(b)
-	if err != nil {
-		return nil, err
+	if v.UsesVarintPacketNumbers() {
+		pn, pnLen, err := utils.ReadVarIntPacketNumber(b)
+		if err != nil {
+			return nil, err
+		}
+		h.PacketNumber = pn
+		h.PacketNumberLen = pnLen
+	} else {
+		pn, err := utils.BigEndian.ReadUint32(b)
+		if err != nil {
+			return nil, err
+		}
+		h.PacketNumber = protocol.PacketNumber(pn)
+		h.PacketNumberLen = protocol.PacketNumberLen4
+	}
+	if h.Type == protocol.PacketType0RTT && v == protocol.Version44 && sentBy == protocol.PerspectiveServer {
+		h.DiversificationNonce = make([]byte, 32)
+		if _, err := io.ReadFull(b, h.DiversificationNonce); err != nil {
+			if err == io.ErrUnexpectedEOF {
+				return nil, io.EOF
+			}
+			return nil, err
+		}
 	}
-	h.PacketNumber = pn
-	h.PacketNumberLen = pnLen
 
 	return h, nil
 }
 
-func (iv *InvariantHeader) parseShortHeader(b *bytes.Reader) (*Header, error) {
+func (iv *InvariantHeader) parseShortHeader(b *bytes.Reader, v protocol.VersionNumber) (*Header, error) {
 	h := iv.toHeader()
 	h.KeyPhase = int(iv.typeByte&0x40) >> 6
-	pn, pnLen, err := utils.ReadVarIntPacketNumber(b)
-	if err != nil {
-		return nil, err
+
+	if v.UsesVarintPacketNumbers() {
+		pn, pnLen, err := utils.ReadVarIntPacketNumber(b)
+		if err != nil {
+			return nil, err
+		}
+		h.PacketNumber = pn
+		h.PacketNumberLen = pnLen
+	} else {
+		switch iv.typeByte & 0x3 {
+		case 0x0:
+			h.PacketNumberLen = protocol.PacketNumberLen1
+		case 0x1:
+			h.PacketNumberLen = protocol.PacketNumberLen2
+		case 0x2:
+			h.PacketNumberLen = protocol.PacketNumberLen4
+		default:
+			return nil, errInvalidPacketNumberLen
+		}
+		p, err := utils.BigEndian.ReadUintN(b, uint8(h.PacketNumberLen))
+		if err != nil {
+			return nil, err
+		}
+		h.PacketNumber = protocol.PacketNumber(p)
 	}
-	h.PacketNumber = pn
-	h.PacketNumberLen = pnLen
 	return h, nil
 }
 

+ 147 - 0
internal/wire/header_parser_test.go

@@ -383,6 +383,153 @@ var _ = Describe("Header Parsing", func() {
 		})
 	})
 
+	Context("gQUIC 44", func() {
+		Context("Long Headers", func() {
+			It("parses a Long Header", func() {
+				destConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}
+				srcConnID := protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1}
+				data := []byte{
+					0x80 ^ uint8(protocol.PacketTypeInitial),
+					0x1, 0x2, 0x3, 0x4, // version
+					0x55, // connection ID lengths
+				}
+				data = append(data, destConnID...)
+				data = append(data, srcConnID...)
+				data = append(data, []byte{0xde, 0xad, 0xbe, 0xef}...)
+				b := bytes.NewReader(data)
+				iHdr, err := ParseInvariantHeader(b, 0)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(iHdr.IsLongHeader).To(BeTrue())
+				Expect(iHdr.Version).To(Equal(protocol.VersionNumber(0x1020304)))
+				Expect(iHdr.DestConnectionID).To(Equal(destConnID))
+				Expect(iHdr.SrcConnectionID).To(Equal(srcConnID))
+				hdr, err := iHdr.Parse(b, protocol.PerspectiveServer, protocol.Version44)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(hdr.IsPublicHeader).To(BeFalse())
+				Expect(hdr.Type).To(Equal(protocol.PacketTypeInitial))
+				Expect(hdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen4))
+				Expect(hdr.PacketNumber).To(Equal(protocol.PacketNumber(0xdeadbeef)))
+			})
+
+			It("parses a Long Header containing a Diversification Nonce", func() {
+				srcConnID := protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1}
+				divNonce := bytes.Repeat([]byte{'f'}, 32)
+				data := []byte{
+					0x80 ^ uint8(protocol.PacketType0RTT),
+					0x1, 0x2, 0x3, 0x4, // version
+					0x5, // connection ID lengths
+				}
+				data = append(data, srcConnID...)
+				data = append(data, []byte{0xde, 0xad, 0xbe, 0xef}...)
+				data = append(data, divNonce...)
+				b := bytes.NewReader(data)
+				iHdr, err := ParseInvariantHeader(b, 0)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(iHdr.IsLongHeader).To(BeTrue())
+				Expect(iHdr.Version).To(Equal(protocol.VersionNumber(0x1020304)))
+				Expect(iHdr.SrcConnectionID).To(Equal(srcConnID))
+				hdr, err := iHdr.Parse(b, protocol.PerspectiveServer, protocol.Version44)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(hdr.IsPublicHeader).To(BeFalse())
+				Expect(hdr.Type).To(Equal(protocol.PacketType0RTT))
+				Expect(hdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen4))
+				Expect(hdr.PacketNumber).To(Equal(protocol.PacketNumber(0xdeadbeef)))
+				Expect(hdr.DiversificationNonce).To(Equal(divNonce))
+			})
+
+			It("errors on EOF, for Long Headers containing a Diversification Nonce", func() {
+				data := []byte{
+					0x80 ^ uint8(protocol.PacketType0RTT),
+					0x1, 0x2, 0x3, 0x4, // version
+					0x5,
+					0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37, // connection ID
+				}
+				iHdrLen := len(data)
+				data = append(data, []byte{0xde, 0xca, 0xfb, 0xad}...) // packet number
+				data = append(data, bytes.Repeat([]byte{'d'}, 32)...)
+				for i := iHdrLen; i < len(data); i++ {
+					b := bytes.NewReader(data[:i])
+					iHdr, err := ParseInvariantHeader(b, 8)
+					Expect(err).ToNot(HaveOccurred())
+					Expect(iHdr.IsLongHeader).To(BeTrue())
+					_, err = iHdr.Parse(b, protocol.PerspectiveServer, protocol.Version44)
+					Expect(err).To(Equal(io.EOF))
+				}
+			})
+		})
+
+		Context("Short Headers", func() {
+			It("parses a Short Header with a 1 byte packet number", func() {
+				destConnID := protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1}
+				data := []byte{0x30}
+				data = append(data, destConnID...)
+				data = append(data, 0x42) // packet number
+				b := bytes.NewReader(data)
+				iHdr, err := ParseInvariantHeader(b, 8)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(iHdr.IsLongHeader).To(BeFalse())
+				Expect(iHdr.DestConnectionID).To(Equal(destConnID))
+				hdr, err := iHdr.Parse(b, protocol.PerspectiveServer, protocol.Version44)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(hdr.IsPublicHeader).To(BeFalse())
+				Expect(hdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen1))
+				Expect(hdr.PacketNumber).To(Equal(protocol.PacketNumber(0x42)))
+			})
+
+			It("parses a Short Header with a 2 byte packet number", func() {
+				data := []byte{0x30 ^ 0x1, 0xca, 0xfe}
+				b := bytes.NewReader(data)
+				iHdr, err := ParseInvariantHeader(b, 0)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(iHdr.IsLongHeader).To(BeFalse())
+				Expect(iHdr.DestConnectionID.Len()).To(BeZero())
+				hdr, err := iHdr.Parse(b, protocol.PerspectiveServer, protocol.Version44)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(hdr.IsPublicHeader).To(BeFalse())
+				Expect(hdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen2))
+				Expect(hdr.PacketNumber).To(Equal(protocol.PacketNumber(0xcafe)))
+			})
+
+			It("parses a Short Header with a 4 byte packet number", func() {
+				data := []byte{0x30 ^ 0x2, 0xde, 0xad, 0xbe, 0xef}
+				b := bytes.NewReader(data)
+				iHdr, err := ParseInvariantHeader(b, 0)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(iHdr.IsLongHeader).To(BeFalse())
+				Expect(iHdr.DestConnectionID.Len()).To(BeZero())
+				hdr, err := iHdr.Parse(b, protocol.PerspectiveServer, protocol.Version44)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(hdr.IsPublicHeader).To(BeFalse())
+				Expect(hdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen4))
+				Expect(hdr.PacketNumber).To(Equal(protocol.PacketNumber(0xdeadbeef)))
+			})
+
+			It("errors on an invalid packet number length flag", func() {
+				data := []byte{0x30 ^ 0x3, 0xde, 0xad, 0xbe, 0xef}
+				b := bytes.NewReader(data)
+				iHdr, err := ParseInvariantHeader(b, 0)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(iHdr.IsLongHeader).To(BeFalse())
+				Expect(iHdr.DestConnectionID.Len()).To(BeZero())
+				_, err = iHdr.Parse(b, protocol.PerspectiveServer, protocol.Version44)
+				Expect(err).To(MatchError(errInvalidPacketNumberLen))
+			})
+
+			It("errors on EOF", func() {
+				data := []byte{0x30 ^ 0x2, 0xde, 0xad, 0xbe, 0xef}
+				iHdrLen := 1
+				for i := iHdrLen; i < len(data); i++ {
+					b := bytes.NewReader(data[:i])
+					iHdr, err := ParseInvariantHeader(b, 0)
+					Expect(err).ToNot(HaveOccurred())
+					Expect(iHdr.IsLongHeader).To(BeFalse())
+					_, err = iHdr.Parse(b, protocol.PerspectiveServer, protocol.Version44)
+					Expect(err).To(Equal(io.EOF))
+				}
+			})
+		})
+	})
+
 	Context("Public Header", func() {
 		It("accepts a sample client header", func() {
 			data := []byte{

+ 239 - 60
internal/wire/header_test.go

@@ -217,6 +217,114 @@ var _ = Describe("Header", func() {
 			})
 		})
 
+		Context("gQUIC 44", func() {
+			Context("Long Header", func() {
+				It("writes", func() {
+					err := (&Header{
+						IsLongHeader:     true,
+						Type:             protocol.PacketTypeInitial,
+						DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
+						Version:          0xdeadbeef,
+						PacketNumberLen:  protocol.PacketNumberLen4,
+						PacketNumber:     0xdecafbad,
+					}).Write(buf, protocol.PerspectiveServer, protocol.Version44)
+					Expect(err).ToNot(HaveOccurred())
+					expected := []byte{
+						0x80 ^ uint8(protocol.PacketTypeInitial),
+						0xde, 0xad, 0xbe, 0xef, // version
+						0x50,                   // connection ID lengths
+						1, 2, 3, 4, 5, 6, 7, 8, // connection ID
+						0xde, 0xca, 0xfb, 0xad, // packet number
+					}
+					Expect(buf.Bytes()).To(Equal(expected))
+				})
+
+				It("writes a 0-RTT packet with a Diversification Nonce", func() {
+					divNonce := bytes.Repeat([]byte{'c'}, 32)
+					err := (&Header{
+						IsLongHeader:         true,
+						Type:                 protocol.PacketType0RTT,
+						DestConnectionID:     protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
+						Version:              0xdeadbeef,
+						PacketNumberLen:      protocol.PacketNumberLen4,
+						PacketNumber:         0xdecafbad,
+						DiversificationNonce: divNonce,
+					}).Write(buf, protocol.PerspectiveServer, protocol.Version44)
+					Expect(err).ToNot(HaveOccurred())
+					expected := []byte{
+						0x80 ^ uint8(protocol.PacketType0RTT),
+						0xde, 0xad, 0xbe, 0xef, // version
+						0x50,                   // connection ID lengths
+						1, 2, 3, 4, 5, 6, 7, 8, // connection ID
+						0xde, 0xca, 0xfb, 0xad, // packet number
+					}
+					expected = append(expected, divNonce...)
+					Expect(buf.Bytes()).To(Equal(expected))
+				})
+
+				It("refuses to write a 0-RTT packet with a wrong length Diversification Nonce", func() {
+					divNonce := bytes.Repeat([]byte{'c'}, 31)
+					err := (&Header{
+						IsLongHeader:         true,
+						Type:                 protocol.PacketType0RTT,
+						DestConnectionID:     protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
+						Version:              0xdeadbeef,
+						PacketNumberLen:      protocol.PacketNumberLen4,
+						PacketNumber:         0xdecafbad,
+						DiversificationNonce: divNonce,
+					}).Write(buf, protocol.PerspectiveServer, protocol.Version44)
+					Expect(err).To(MatchError("invalid diversification nonce length"))
+				})
+			})
+
+			Context("Short Header", func() {
+				It("writes a Short Header with a 1 byte packet number", func() {
+					err := (&Header{
+						DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
+						PacketNumberLen:  protocol.PacketNumberLen1,
+						PacketNumber:     0x42,
+					}).Write(buf, protocol.PerspectiveServer, protocol.Version44)
+					Expect(err).ToNot(HaveOccurred())
+					expected := []byte{
+						0x30,
+						1, 2, 3, 4, 5, 6, 7, 8, // connection ID
+						0x42, // packet number
+					}
+					Expect(buf.Bytes()).To(Equal(expected))
+				})
+
+				It("writes a Short Header with a 2 byte packet number", func() {
+					err := (&Header{
+						DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
+						PacketNumberLen:  protocol.PacketNumberLen2,
+						PacketNumber:     0xcafe,
+					}).Write(buf, protocol.PerspectiveServer, protocol.Version44)
+					Expect(err).ToNot(HaveOccurred())
+					expected := []byte{
+						0x30 ^ 0x1,
+						1, 2, 3, 4, 5, 6, 7, 8, // connection ID
+						0xca, 0xfe, // packet number
+					}
+					Expect(buf.Bytes()).To(Equal(expected))
+				})
+
+				It("writes a Short Header with a 4 byte packet number", func() {
+					err := (&Header{
+						DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
+						PacketNumberLen:  protocol.PacketNumberLen4,
+						PacketNumber:     0xdeadbeef,
+					}).Write(buf, protocol.PerspectiveServer, protocol.Version44)
+					Expect(err).ToNot(HaveOccurred())
+					expected := []byte{
+						0x30 ^ 0x2,
+						1, 2, 3, 4, 5, 6, 7, 8, // connection ID
+						0xde, 0xad, 0xbe, 0xef, // packet number
+					}
+					Expect(buf.Bytes()).To(Equal(expected))
+				})
+			})
+		})
+
 		Context("Public Header", func() {
 			connID := protocol.ConnectionID{0x4c, 0xfa, 0x9f, 0x9b, 0x66, 0x86, 0x19, 0xf6}
 
@@ -390,7 +498,7 @@ var _ = Describe("Header", func() {
 						PacketNumberLen:  protocol.PacketNumberLen6,
 					}
 					err := hdr.writePublicHeader(buf, protocol.PerspectiveServer, versionPublicHeader)
-					Expect(err).To(MatchError(errInvalidPacketNumberLen6))
+					Expect(err).To(MatchError(errInvalidPacketNumberLen))
 				})
 			})
 		})
@@ -508,72 +616,116 @@ var _ = Describe("Header", func() {
 				Expect(err).To(MatchError("invalid packet number length: 5"))
 			})
 		})
-	})
 
-	Context("Public Header", func() {
-		connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}
+		Context("gQUIC 44", func() {
+			It("has the right length for the Long Header", func() {
+				h := &Header{
+					IsLongHeader:     true,
+					Type:             protocol.PacketTypeInitial,
+					PayloadLen:       1,
+					DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
+					PacketNumberLen:  protocol.PacketNumberLen4,
+				}
+				expectedLen := 1 /* type byte */ + 4 /* version */ + 1 /* conn ID len */ + 8 /* dest conn id */ + 4 /* packet number */
+				Expect(h.GetLength(protocol.Version44)).To(BeEquivalentTo(expectedLen))
+				err := h.Write(buf, protocol.PerspectiveClient, protocol.Version44)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(buf.Len()).To(Equal(expectedLen))
+			})
 
-		It("errors when PacketNumberLen is not set", func() {
-			hdr := Header{
-				DestConnectionID: connID,
-				PacketNumber:     0xdecafbad,
-			}
-			_, err := hdr.GetLength(versionPublicHeader)
-			Expect(err).To(MatchError(errPacketNumberLenNotSet))
-		})
+			It("has the right length for the Long Header containing a Diversification Nonce", func() {
+				h := &Header{
+					IsLongHeader:         true,
+					Type:                 protocol.PacketType0RTT,
+					DestConnectionID:     protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
+					PacketNumberLen:      protocol.PacketNumberLen4,
+					DiversificationNonce: bytes.Repeat([]byte{'d'}, 32),
+				}
+				expectedLen := 1 /* type byte */ + 4 /* version */ + 1 /* conn ID len */ + 8 /* dest conn id */ + 4 /* packet number */ + 32 /* div nonce */
+				Expect(h.GetLength(protocol.Version44)).To(BeEquivalentTo(expectedLen))
+				err := h.Write(buf, protocol.PerspectiveServer, protocol.Version44)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(buf.Len()).To(Equal(expectedLen))
+			})
 
-		It("gets the length of a packet with longest packet number length and connectionID", func() {
-			hdr := Header{
-				DestConnectionID: connID,
-				PacketNumber:     0xdecafbad,
-				PacketNumberLen:  protocol.PacketNumberLen4,
-			}
-			length, err := hdr.GetLength(versionPublicHeader)
-			Expect(err).ToNot(HaveOccurred())
-			Expect(length).To(Equal(protocol.ByteCount(1 + 8 + 4))) // 1 byte public flag, 8 bytes connectionID, and packet number
+			It("has the right length for a Short Header", func() {
+				h := &Header{
+					DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
+					PacketNumberLen:  protocol.PacketNumberLen2,
+				}
+				expectedLen := 1 /*type byte*/ + 8 /* conn ID */ + 2 /* packet number */
+				Expect(h.GetLength(protocol.Version44)).To(BeEquivalentTo(expectedLen))
+				err := h.Write(buf, protocol.PerspectiveServer, protocol.Version44)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(buf.Len()).To(Equal(expectedLen))
+			})
 		})
 
-		It("gets the lengths of a packet sent by the client with the VersionFlag set", func() {
-			hdr := Header{
-				PacketNumber:    0xdecafbad,
-				PacketNumberLen: protocol.PacketNumberLen4,
-				VersionFlag:     true,
-				Version:         versionPublicHeader,
-			}
-			length, err := hdr.GetLength(versionPublicHeader)
-			Expect(err).ToNot(HaveOccurred())
-			Expect(length).To(Equal(protocol.ByteCount(1 + 4 + 4))) // 1 byte public flag, 4 version number, and packet number
-		})
+		Context("Public Header", func() {
+			connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}
 
-		It("gets the length of a packet with longest packet number length and omitted connectionID", func() {
-			hdr := Header{
-				PacketNumber:    0xDECAFBAD,
-				PacketNumberLen: protocol.PacketNumberLen4,
-			}
-			length, err := hdr.GetLength(versionPublicHeader)
-			Expect(err).ToNot(HaveOccurred())
-			Expect(length).To(Equal(protocol.ByteCount(1 + 4))) // 1 byte public flag, and packet number
-		})
+			It("errors when PacketNumberLen is not set", func() {
+				hdr := Header{
+					DestConnectionID: connID,
+					PacketNumber:     0xdecafbad,
+				}
+				_, err := hdr.GetLength(versionPublicHeader)
+				Expect(err).To(MatchError(errPacketNumberLenNotSet))
+			})
 
-		It("gets the length of a packet 2 byte packet number length ", func() {
-			hdr := Header{
-				DestConnectionID: connID,
-				PacketNumber:     0xDECAFBAD,
-				PacketNumberLen:  protocol.PacketNumberLen2,
-			}
-			length, err := hdr.GetLength(versionPublicHeader)
-			Expect(err).ToNot(HaveOccurred())
-			Expect(length).To(Equal(protocol.ByteCount(1 + 8 + 2))) // 1 byte public flag, 8 byte connectionID, and packet number
-		})
+			It("gets the length of a packet with longest packet number length and connectionID", func() {
+				hdr := Header{
+					DestConnectionID: connID,
+					PacketNumber:     0xdecafbad,
+					PacketNumberLen:  protocol.PacketNumberLen4,
+				}
+				length, err := hdr.GetLength(versionPublicHeader)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(length).To(Equal(protocol.ByteCount(1 + 8 + 4))) // 1 byte public flag, 8 bytes connectionID, and packet number
+			})
 
-		It("works with diversification nonce", func() {
-			hdr := Header{
-				DiversificationNonce: []byte("foo"),
-				PacketNumberLen:      protocol.PacketNumberLen1,
-			}
-			length, err := hdr.GetLength(versionPublicHeader)
-			Expect(err).NotTo(HaveOccurred())
-			Expect(length).To(Equal(protocol.ByteCount(1 + 3 + 1))) // 1 byte public flag, 3 byte DiversificationNonce, 1 byte PacketNumber
+			It("gets the lengths of a packet sent by the client with the VersionFlag set", func() {
+				hdr := Header{
+					PacketNumber:    0xdecafbad,
+					PacketNumberLen: protocol.PacketNumberLen4,
+					VersionFlag:     true,
+					Version:         versionPublicHeader,
+				}
+				length, err := hdr.GetLength(versionPublicHeader)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(length).To(Equal(protocol.ByteCount(1 + 4 + 4))) // 1 byte public flag, 4 version number, and packet number
+			})
+
+			It("gets the length of a packet with longest packet number length and omitted connectionID", func() {
+				hdr := Header{
+					PacketNumber:    0xDECAFBAD,
+					PacketNumberLen: protocol.PacketNumberLen4,
+				}
+				length, err := hdr.GetLength(versionPublicHeader)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(length).To(Equal(protocol.ByteCount(1 + 4))) // 1 byte public flag, and packet number
+			})
+
+			It("gets the length of a packet 2 byte packet number length ", func() {
+				hdr := Header{
+					DestConnectionID: connID,
+					PacketNumber:     0xDECAFBAD,
+					PacketNumberLen:  protocol.PacketNumberLen2,
+				}
+				length, err := hdr.GetLength(versionPublicHeader)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(length).To(Equal(protocol.ByteCount(1 + 8 + 2))) // 1 byte public flag, 8 byte connectionID, and packet number
+			})
+
+			It("works with diversification nonce", func() {
+				hdr := Header{
+					DiversificationNonce: []byte("foo"),
+					PacketNumberLen:      protocol.PacketNumberLen1,
+				}
+				length, err := hdr.GetLength(versionPublicHeader)
+				Expect(err).NotTo(HaveOccurred())
+				Expect(length).To(Equal(protocol.ByteCount(1 + 3 + 1))) // 1 byte public flag, 3 byte DiversificationNonce, 1 byte PacketNumber
+			})
 		})
 	})
 
@@ -678,6 +830,34 @@ var _ = Describe("Header", func() {
 			})
 		})
 
+		Context("gQUIC 44", func() {
+			It("logs Long Headers", func() {
+				(&Header{
+					IsLongHeader:     true,
+					Type:             protocol.PacketTypeHandshake,
+					PacketNumber:     0x1337,
+					PacketNumberLen:  protocol.PacketNumberLen4,
+					DestConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37},
+					SrcConnectionID:  protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad, 0x013, 0x37, 0x13, 0x37},
+					Version:          protocol.Version44,
+				}).Log(logger)
+				Expect(buf.String()).To(ContainSubstring("Long Header{Type: Handshake, DestConnectionID: 0xdeadbeefcafe1337, SrcConnectionID: 0xdecafbad13371337, PacketNumber: 0x1337, PacketNumberLen: 4, Version: gQUIC 44}"))
+			})
+
+			It("logs a Long Header with a Diversification Nonce", func() {
+				(&Header{
+					IsLongHeader:         true,
+					Type:                 protocol.PacketType0RTT,
+					PacketNumber:         0x1337,
+					PacketNumberLen:      protocol.PacketNumberLen4,
+					SrcConnectionID:      protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad, 0x013, 0x37, 0x13, 0x37},
+					DiversificationNonce: []byte{0xde, 0xad, 0xbe, 0xef},
+					Version:              protocol.Version44,
+				}).Log(logger)
+				Expect(buf.String()).To(ContainSubstring("Long Header{Type: 0-RTT Protected, DestConnectionID: (empty), SrcConnectionID: 0xdecafbad13371337, PacketNumber: 0x1337, PacketNumberLen: 4, Diversification Nonce: 0xdeadbeef, Version: gQUIC 44}"))
+			})
+		})
+
 		Context("Public Header", func() {
 			It("logs a Public Header containing a connection ID", func() {
 				(&Header{
@@ -718,6 +898,5 @@ var _ = Describe("Header", func() {
 				Expect(buf.String()).To(ContainSubstring("DiversificationNonce: []byte{0xba, 0xdf, 0x0, 0xd}"))
 			})
 		})
-
 	})
 })

+ 1 - 1
packet_handler_map.go

@@ -180,7 +180,7 @@ func (h *packetHandlerMap) handlePacket(addr net.Addr, data []byte) error {
 	hdr.Raw = data[:len(data)-r.Len()]
 	packetData := data[len(data)-r.Len():]
 
-	if hdr.IsLongHeader {
+	if hdr.IsLongHeader && hdr.Version.UsesLengthInHeader() {
 		if protocol.ByteCount(len(packetData)) < hdr.PayloadLen {
 			return fmt.Errorf("packet payload (%d bytes) is smaller than the expected payload length (%d bytes)", len(packetData), hdr.PayloadLen)
 		}

+ 6 - 6
packet_packer.go

@@ -457,11 +457,15 @@ func (p *packetPacker) getHeader(encLevel protocol.EncryptionLevel) *wire.Header
 	header := &wire.Header{
 		PacketNumber:    pnum,
 		PacketNumberLen: packetNumberLen,
+		Version:         p.version,
 	}
 
-	if p.version.UsesTLS() && encLevel != protocol.EncryptionForwardSecure {
+	if p.version.UsesIETFHeaderFormat() && encLevel != protocol.EncryptionForwardSecure {
 		header.IsLongHeader = true
 		header.SrcConnectionID = p.srcConnID
+		if !p.version.UsesVarintPacketNumbers() {
+			header.PacketNumberLen = protocol.PacketNumberLen4
+		}
 		// Set the payload len to maximum size.
 		// Since it is encoded as a varint, this guarantees us that the header will end up at most as big as GetLength() returns.
 		header.PayloadLen = p.maxPacketSize
@@ -478,15 +482,11 @@ func (p *packetPacker) getHeader(encLevel protocol.EncryptionLevel) *wire.Header
 	}
 	if !p.version.UsesTLS() {
 		if p.perspective == protocol.PerspectiveServer && encLevel == protocol.EncryptionSecure {
+			header.Type = protocol.PacketType0RTT
 			header.DiversificationNonce = p.divNonce
 		}
 		if p.perspective == protocol.PerspectiveClient && encLevel != protocol.EncryptionForwardSecure {
 			header.VersionFlag = true
-			header.Version = p.version
-		}
-	} else {
-		if encLevel != protocol.EncryptionForwardSecure {
-			header.Version = p.version
 		}
 	}
 	return header

+ 48 - 0
packet_packer_test.go

@@ -103,6 +103,7 @@ var _ = Describe("Packet packer", func() {
 
 	Context("determining the maximum packet size", func() {
 		connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}
+
 		It("uses the minimum initial size, if it can't determine if the remote address is IPv4 or IPv6", func() {
 			remoteAddr := &net.TCPAddr{}
 			packer = newPacketPacker(connID, connID, 1, nil, remoteAddr, nil, nil, nil, nil, protocol.PerspectiveServer, protocol.VersionWhatever)
@@ -176,6 +177,7 @@ var _ = Describe("Packet packer", func() {
 			})
 
 			It("it omits the connection ID for forward-secure packets", func() {
+				packer.version = protocol.Version43
 				ph := packer.getHeader(protocol.EncryptionForwardSecure)
 				Expect(ph.DestConnectionID.Len()).ToNot(BeZero())
 				packer.SetOmitConnectionID()
@@ -225,6 +227,52 @@ var _ = Describe("Packet packer", func() {
 			})
 		})
 
+		Context("Header (for gQUIC 44)", func() {
+			BeforeEach(func() {
+				packer.version = protocol.Version44
+			})
+
+			It("sends an Initial packet as the first packets, for the client", func() {
+				packer.perspective = protocol.PerspectiveClient
+				packer.hasSentPacket = false
+				h := packer.getHeader(protocol.EncryptionUnencrypted)
+				Expect(h.IsLongHeader).To(BeTrue())
+				Expect(h.Type).To(Equal(protocol.PacketTypeInitial))
+				Expect(h.Version).To(Equal(protocol.Version44))
+				Expect(h.DestConnectionID).To(Equal(packer.destConnID))
+				Expect(h.SrcConnectionID).To(Equal(packer.srcConnID))
+				Expect(h.PacketNumberLen).To(Equal(protocol.PacketNumberLen4))
+			})
+
+			It("sends a Handshake for non-forward-secure packets, for the server", func() {
+				packer.perspective = protocol.PerspectiveServer
+				h := packer.getHeader(protocol.EncryptionUnencrypted)
+				Expect(h.IsLongHeader).To(BeTrue())
+				Expect(h.Type).To(Equal(protocol.PacketTypeHandshake))
+				Expect(h.Version).To(Equal(protocol.Version44))
+				Expect(h.DestConnectionID).To(Equal(packer.destConnID))
+				Expect(h.SrcConnectionID).To(Equal(packer.srcConnID))
+				Expect(h.PacketNumberLen).To(Equal(protocol.PacketNumberLen4))
+			})
+
+			It("sets the Diversification Nonce for secure packets", func() {
+				packer.perspective = protocol.PerspectiveServer
+				Expect(divNonce).ToNot(BeEmpty())
+				h := packer.getHeader(protocol.EncryptionSecure)
+				Expect(h.IsLongHeader).To(BeTrue())
+				Expect(h.Version).To(Equal(protocol.Version44))
+				Expect(h.Type).To(Equal(protocol.PacketType0RTT))
+				Expect(h.DiversificationNonce).To(Equal(divNonce))
+			})
+
+			It("uses the Short Header for forward-secure packets", func() {
+				h := packer.getHeader(protocol.EncryptionForwardSecure)
+				Expect(h.IsLongHeader).To(BeFalse())
+				Expect(h.IsPublicHeader).To(BeFalse())
+				Expect(h.DestConnectionID).To(Equal(packer.destConnID))
+			})
+		})
+
 		Context("Header (for IETF draft QUIC)", func() {
 			BeforeEach(func() {
 				packer.version = versionIETFHeader

+ 17 - 4
server.go

@@ -89,7 +89,7 @@ type server struct {
 
 	sessionRunner sessionRunner
 	// set as a member, so they can be set in the tests
-	newSession func(connection, sessionRunner, protocol.VersionNumber, protocol.ConnectionID, *handshake.ServerConfig, *tls.Config, *Config, utils.Logger) (quicSession, error)
+	newSession func(connection, sessionRunner, protocol.VersionNumber, protocol.ConnectionID, protocol.ConnectionID, *handshake.ServerConfig, *tls.Config, *Config, utils.Logger) (quicSession, error)
 
 	logger utils.Logger
 }
@@ -269,6 +269,11 @@ func populateServerConfig(config *Config) *Config {
 	if connIDLen == 0 {
 		connIDLen = protocol.DefaultConnectionIDLength
 	}
+	for _, v := range versions {
+		if v == protocol.Version44 {
+			connIDLen = protocol.ConnectionIDLenGQUIC
+		}
+	}
 
 	return &Config{
 		Versions:                              versions,
@@ -351,13 +356,13 @@ func (s *server) handlePacketImpl(p *receivedPacket) error {
 			return s.sendVersionNegotiationPacket(p)
 		}
 	}
-	if hdr.Type == protocol.PacketTypeInitial {
+	if hdr.Type == protocol.PacketTypeInitial && hdr.Version.UsesTLS() {
 		go s.serverTLS.HandleInitial(p)
 		return nil
 	}
 
 	// TODO(#943): send Stateless Reset, if this an IETF QUIC packet
-	if !hdr.VersionFlag {
+	if !hdr.VersionFlag && !hdr.Version.UsesIETFHeaderFormat() {
 		_, err := s.conn.WriteTo(wire.WritePublicReset(hdr.DestConnectionID, 0, 0), p.remoteAddr)
 		return err
 	}
@@ -368,12 +373,20 @@ func (s *server) handlePacketImpl(p *receivedPacket) error {
 		return errors.New("dropping small packet for unknown connection")
 	}
 
+	var destConnID, srcConnID protocol.ConnectionID
+	if hdr.Version.UsesIETFHeaderFormat() {
+		srcConnID = hdr.DestConnectionID
+	} else {
+		destConnID = hdr.DestConnectionID
+		srcConnID = hdr.DestConnectionID
+	}
 	s.logger.Infof("Serving new connection: %s, version %s from %v", hdr.DestConnectionID, hdr.Version, p.remoteAddr)
 	sess, err := s.newSession(
 		&conn{pconn: s.conn, currentAddr: p.remoteAddr},
 		s.sessionRunner,
 		hdr.Version,
-		hdr.DestConnectionID,
+		destConnID,
+		srcConnID,
 		s.scfg,
 		s.tlsConf,
 		s.config,

+ 1 - 1
server_session.go

@@ -46,7 +46,7 @@ func (s *serverSession) handlePacketImpl(p *receivedPacket) error {
 
 	if hdr.IsLongHeader {
 		switch hdr.Type {
-		case protocol.PacketTypeHandshake:
+		case protocol.PacketTypeHandshake, protocol.PacketType0RTT: // 0-RTT accepted for gQUIC 44
 			// nothing to do here. Packet will be passed to the session.
 		default:
 			// Note that this also drops 0-RTT packets.

+ 2 - 2
server_session_test.go

@@ -72,13 +72,13 @@ var _ = Describe("Server Session", func() {
 		p := &receivedPacket{
 			header: &wire.Header{
 				IsLongHeader:     true,
-				Type:             protocol.PacketType0RTT,
+				Type:             protocol.PacketTypeRetry,
 				Version:          protocol.VersionNumber(100),
 				DestConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef},
 			},
 		}
 		err := sess.handlePacketImpl(p)
-		Expect(err).To(MatchError("Received unsupported packet type: 0-RTT Protected"))
+		Expect(err).To(MatchError("Received unsupported packet type: Retry"))
 	})
 
 	It("passes on Handshake packets", func() {

+ 22 - 6
server_test.go

@@ -50,6 +50,7 @@ var _ = Describe("Server", func() {
 				MaxIncomingStreams:          1234,
 				MaxIncomingUniStreams:       4321,
 				ConnectionIDLength:          12,
+				Versions:                    []protocol.VersionNumber{VersionGQUIC43},
 			}
 			c := populateServerConfig(config)
 			Expect(c.HandshakeTimeout).To(Equal(1337 * time.Minute))
@@ -58,6 +59,25 @@ var _ = Describe("Server", func() {
 			Expect(c.MaxIncomingStreams).To(Equal(1234))
 			Expect(c.MaxIncomingUniStreams).To(Equal(4321))
 			Expect(c.ConnectionIDLength).To(Equal(12))
+			Expect(c.Versions).To(Equal([]protocol.VersionNumber{VersionGQUIC43}))
+		})
+
+		It("uses 8 byte connection IDs if gQUIC 44 is supported", func() {
+			config := &Config{
+				Versions:           []protocol.VersionNumber{protocol.Version43, protocol.Version44},
+				ConnectionIDLength: 13,
+			}
+			c := populateServerConfig(config)
+			Expect(c.Versions).To(Equal([]protocol.VersionNumber{protocol.Version43, protocol.Version44}))
+			Expect(c.ConnectionIDLength).To(Equal(8))
+		})
+
+		It("uses 4 byte connection IDs by default, if gQUIC 44 is not supported", func() {
+			config := &Config{
+				Versions: []protocol.VersionNumber{protocol.Version39},
+			}
+			c := populateServerConfig(config)
+			Expect(c.ConnectionIDLength).To(Equal(protocol.DefaultConnectionIDLength))
 		})
 
 		It("disables bidirectional streams", func() {
@@ -79,12 +99,6 @@ var _ = Describe("Server", func() {
 			Expect(c.MaxIncomingStreams).To(Equal(1234))
 			Expect(c.MaxIncomingUniStreams).To(BeZero())
 		})
-
-		It("doesn't use 0-byte connection IDs", func() {
-			config := &Config{}
-			c := populateServerConfig(config)
-			Expect(c.ConnectionIDLength).To(Equal(protocol.DefaultConnectionIDLength))
-		})
 	})
 
 	Context("with mock session", func() {
@@ -103,6 +117,7 @@ var _ = Describe("Server", func() {
 				runner sessionRunner,
 				_ protocol.VersionNumber,
 				connID protocol.ConnectionID,
+				_ protocol.ConnectionID,
 				_ *handshake.ServerConfig,
 				_ *tls.Config,
 				_ *Config,
@@ -322,6 +337,7 @@ var _ = Describe("Server", func() {
 				remoteAddr: udpAddr,
 				header: &wire.Header{
 					IsPublicHeader: true,
+					Version:        versionGQUICFrames,
 				},
 			})
 			Expect(err).ToNot(HaveOccurred())

+ 16 - 12
session.go

@@ -152,19 +152,21 @@ func newSession(
 	conn connection,
 	sessionRunner sessionRunner,
 	v protocol.VersionNumber,
-	connectionID protocol.ConnectionID,
+	destConnID protocol.ConnectionID,
+	srcConnID protocol.ConnectionID,
 	scfg *handshake.ServerConfig,
 	tlsConf *tls.Config,
 	config *Config,
 	logger utils.Logger,
 ) (quicSession, error) {
+	logger.Debugf("Creating new session. Destination Connection ID: %s, Source Connection ID: %s", destConnID, srcConnID)
 	paramsChan := make(chan handshake.TransportParameters)
 	handshakeEvent := make(chan struct{}, 1)
 	s := &session{
 		conn:           conn,
 		sessionRunner:  sessionRunner,
-		srcConnID:      connectionID,
-		destConnID:     connectionID,
+		srcConnID:      srcConnID,
+		destConnID:     destConnID,
 		perspective:    protocol.PerspectiveServer,
 		version:        v,
 		config:         config,
@@ -185,7 +187,7 @@ func newSession(
 	}
 	cs, err := newCryptoSetup(
 		s.cryptoStream,
-		connectionID,
+		srcConnID,
 		s.conn.RemoteAddr(),
 		s.version,
 		divNonce,
@@ -205,8 +207,8 @@ func newSession(
 	s.streamsMap = newStreamsMapLegacy(s.newStream, s.config.MaxIncomingStreams, s.perspective)
 	s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
 	s.packer = newPacketPacker(
-		connectionID,
-		nil, // no src connection ID
+		destConnID,
+		srcConnID,
 		1,
 		s.sentPacketHandler.GetPacketNumberLen,
 		s.RemoteAddr(),
@@ -226,20 +228,22 @@ var newClientSession = func(
 	sessionRunner sessionRunner,
 	hostname string,
 	v protocol.VersionNumber,
-	connectionID protocol.ConnectionID,
+	destConnID protocol.ConnectionID,
+	srcConnID protocol.ConnectionID,
 	tlsConf *tls.Config,
 	config *Config,
 	initialVersion protocol.VersionNumber,
 	negotiatedVersions []protocol.VersionNumber, // needed for validation of the GQUIC version negotiation
 	logger utils.Logger,
 ) (quicSession, error) {
+	logger.Debugf("Creating new session. Destination Connection ID: %s, Source Connection ID: %s", destConnID, srcConnID)
 	paramsChan := make(chan handshake.TransportParameters)
 	handshakeEvent := make(chan struct{}, 1)
 	s := &session{
 		conn:           conn,
 		sessionRunner:  sessionRunner,
-		srcConnID:      connectionID,
-		destConnID:     connectionID,
+		srcConnID:      srcConnID,
+		destConnID:     destConnID,
 		perspective:    protocol.PerspectiveClient,
 		version:        v,
 		config:         config,
@@ -258,7 +262,7 @@ var newClientSession = func(
 	cs, err := newCryptoSetupClient(
 		s.cryptoStream,
 		hostname,
-		connectionID,
+		destConnID,
 		s.version,
 		tlsConf,
 		transportParams,
@@ -276,8 +280,8 @@ var newClientSession = func(
 	s.streamsMap = newStreamsMapLegacy(s.newStream, s.config.MaxIncomingStreams, s.perspective)
 	s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
 	s.packer = newPacketPacker(
-		connectionID,
-		nil, // no src connection ID
+		destConnID,
+		srcConnID,
 		1,
 		s.sentPacketHandler.GetPacketNumberLen,
 		s.RemoteAddr(),

+ 3 - 0
session_test.go

@@ -111,6 +111,7 @@ var _ = Describe("Session", func() {
 			sessionRunner,
 			protocol.Version39,
 			protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1},
+			protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1},
 			scfg,
 			nil,
 			populateServerConfig(&Config{}),
@@ -165,6 +166,7 @@ var _ = Describe("Session", func() {
 				sessionRunner,
 				protocol.Version39,
 				protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1},
+				protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1},
 				scfg,
 				nil,
 				conf,
@@ -1746,6 +1748,7 @@ var _ = Describe("Client Session", func() {
 			"hostname",
 			protocol.Version39,
 			protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1},
+			protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1},
 			nil,
 			populateClientConfig(&Config{}, false),
 			protocol.VersionWhatever,