Skip to content

Commit

Permalink
handle Version Negotiation packets in the session
Browse files Browse the repository at this point in the history
  • Loading branch information
marten-seemann committed Jul 1, 2020
1 parent 361d870 commit 6125b90
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 298 deletions.
89 changes: 11 additions & 78 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
"github.com/lucas-clemente/quic-go/qlog"
)

Expand All @@ -27,20 +26,15 @@ type client struct {

packetHandlers packetHandlerManager

versionNegotiated utils.AtomicBool // has the server accepted our version
receivedVersionNegotiationPacket bool
negotiatedVersions []protocol.VersionNumber // the list of versions from the version negotiation packet

tlsConf *tls.Config
config *Config

srcConnID protocol.ConnectionID
destConnID protocol.ConnectionID

initialPacketNumber protocol.PacketNumber

initialVersion protocol.VersionNumber
version protocol.VersionNumber
initialPacketNumber protocol.PacketNumber
hasNegotiatedVersion bool
version protocol.VersionNumber

handshakeChan chan struct{}

Expand Down Expand Up @@ -268,8 +262,9 @@ func (c *client) dial(ctx context.Context) error {
c.config,
c.tlsConf,
c.initialPacketNumber,
c.initialVersion,
c.version,
c.use0RTT,
c.hasNegotiatedVersion,
c.qlogger,
c.logger,
c.version,
Expand All @@ -280,7 +275,7 @@ func (c *client) dial(ctx context.Context) error {
errorChan := make(chan error, 1)
go func() {
err := c.session.run() // returns as soon as the session is closed
if err != errCloseForRecreating && c.createdPacketConn {
if !errors.Is(err, errCloseForRecreating{}) && c.createdPacketConn {
c.packetHandlers.Destroy()
}
errorChan <- err
Expand All @@ -298,7 +293,11 @@ func (c *client) dial(ctx context.Context) error {
c.session.shutdown()
return ctx.Err()
case err := <-errorChan:
if err == errCloseForRecreating {
var recreateErr *errCloseForRecreating
if errors.As(err, &recreateErr) {
c.initialPacketNumber = recreateErr.nextPacketNumber
c.version = recreateErr.nextVersion
c.hasNegotiatedVersion = true
return c.dial(ctx)
}
return err
Expand All @@ -312,75 +311,9 @@ func (c *client) dial(ctx context.Context) error {
}

func (c *client) handlePacket(p *receivedPacket) {
if wire.IsVersionNegotiationPacket(p.data) {
go c.handleVersionNegotiationPacket(p)
return
}

// 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.Get() {
c.versionNegotiated.Set(true)
}

c.session.handlePacket(p)
}

func (c *client) handleVersionNegotiationPacket(p *receivedPacket) {
c.mutex.Lock()
defer c.mutex.Unlock()

hdr, _, _, err := wire.ParsePacket(p.data, 0)
if err != nil {
if c.qlogger != nil {
c.qlogger.DroppedPacket(qlog.PacketTypeVersionNegotiation, protocol.ByteCount(len(p.data)), qlog.PacketDropHeaderParseError)
}
c.logger.Debugf("Error parsing Version Negotiation packet: %s", err)
return
}

// ignore delayed / duplicated version negotiation packets
if c.receivedVersionNegotiationPacket || c.versionNegotiated.Get() {
if c.qlogger != nil {
c.qlogger.DroppedPacket(qlog.PacketTypeVersionNegotiation, protocol.ByteCount(len(p.data)), qlog.PacketDropUnexpectedPacket)
}
c.logger.Debugf("Received a delayed Version Negotiation packet.")
return
}

for _, v := range hdr.SupportedVersions {
if v == c.version {
if c.qlogger != nil {
c.qlogger.DroppedPacket(qlog.PacketTypeVersionNegotiation, protocol.ByteCount(len(p.data)), qlog.PacketDropUnexpectedVersion)
}
// The Version Negotiation packet contains the version that we offered.
// This might be a packet sent by an attacker (or by a terribly broken server implementation).
return
}
}

c.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", hdr.SupportedVersions)
if c.qlogger != nil {
c.qlogger.ReceivedVersionNegotiationPacket(hdr)
}
newVersion, ok := protocol.ChooseSupportedVersion(c.config.Versions, hdr.SupportedVersions)
if !ok {
//nolint:stylecheck
c.session.destroy(fmt.Errorf("No compatible QUIC version found. We support %s, server offered %s", c.config.Versions, hdr.SupportedVersions))
c.logger.Debugf("No compatible QUIC version found.")
return
}
c.receivedVersionNegotiationPacket = true
c.negotiatedVersions = hdr.SupportedVersions

// switch to negotiated version
c.initialVersion = c.version
c.version = newVersion

c.logger.Infof("Switching to QUIC version %s. New connection ID: %s", newVersion, c.destConnID)
c.initialPacketNumber = c.session.closeForRecreating()
}

func (c *client) shutdown() {
c.mutex.Lock()
defer c.mutex.Unlock()
Expand Down

0 comments on commit 6125b90

Please sign in to comment.