diff --git a/internal/protocol/protocol.go b/internal/protocol/protocol.go index ef861c2b..b7f5705f 100644 --- a/internal/protocol/protocol.go +++ b/internal/protocol/protocol.go @@ -64,6 +64,10 @@ const MaxReceivePacketSize ByteCount = 1452 // MinInitialPacketSize is the minimum size an Initial packet is required to have. const MinInitialPacketSize = 1200 +// MinUnknownVersionPacketSize is the minimum size a packet with an unknown version +// needs to have in order to trigger a Version Negotiation packet. +const MinUnknownVersionPacketSize = MinInitialPacketSize + // MinStatelessResetSize is the minimum size of a stateless reset packet that we send const MinStatelessResetSize = 1 /* first byte */ + 20 /* max. conn ID length */ + 4 /* max. packet number length */ + 1 /* min. payload length */ + 16 /* token */ diff --git a/server.go b/server.go index b1847010..b62fe73f 100644 --- a/server.go +++ b/server.go @@ -351,6 +351,13 @@ func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer s } // send a Version Negotiation Packet if the client is speaking a different protocol version if !protocol.IsSupportedVersion(s.config.Versions, hdr.Version) { + if p.Size() < protocol.MinUnknownVersionPacketSize { + s.logger.Debugf("Dropping a packet with an unknown version that is too small (%d bytes)", p.Size()) + if s.config.Tracer != nil { + s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) + } + return false + } go s.sendVersionNegotiationPacket(p, hdr) return false } diff --git a/server_test.go b/server_test.go index fa2294b6..a3c8b009 100644 --- a/server_test.go +++ b/server_test.go @@ -385,7 +385,7 @@ var _ = Describe("Server", func() { SrcConnectionID: srcConnID, DestConnectionID: destConnID, Version: 0x42, - }, make([]byte, protocol.MinInitialPacketSize)) + }, make([]byte, protocol.MinUnknownVersionPacketSize)) raddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} packet.remoteAddr = raddr tracer.EXPECT().SentPacket(packet.remoteAddr, gomock.Any(), gomock.Any(), nil).Do(func(_ net.Addr, replyHdr *logging.Header, _ logging.ByteCount, _ []logging.Frame) { @@ -431,6 +431,29 @@ var _ = Describe("Server", func() { time.Sleep(scaleDuration(20 * time.Millisecond)) }) + It("doesn't send a Version Negotiation Packet for unsupported versions, if the packet is too small", func() { + srcConnID := protocol.ConnectionID{1, 2, 3, 4, 5} + destConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6} + p := getPacket(&wire.Header{ + IsLongHeader: true, + Type: protocol.PacketTypeHandshake, + SrcConnectionID: srcConnID, + DestConnectionID: destConnID, + Version: 0x42, + }, make([]byte, protocol.MinUnknownVersionPacketSize-50)) + Expect(p.Size()).To(BeNumerically("<", protocol.MinUnknownVersionPacketSize)) + raddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} + p.remoteAddr = raddr + done := make(chan struct{}) + tracer.EXPECT().DroppedPacket(raddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket).Do(func(net.Addr, logging.PacketType, protocol.ByteCount, logging.PacketDropReason) { + close(done) + }) + serv.handlePacket(p) + Eventually(done).Should(BeClosed()) + // make sure no other packet is sent + time.Sleep(scaleDuration(20 * time.Millisecond)) + }) + It("replies with a Retry packet, if a Token is required", func() { serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return false } hdr := &wire.Header{