fix race condition in the client

This commit is contained in:
Marten Seemann
2017-03-21 17:21:13 +07:00
parent 06446b95f3
commit b4d22b3c75

View File

@@ -151,6 +151,9 @@ func (c *client) handlePacket(remoteAddr net.Addr, packet []byte) error {
}
hdr.Raw = packet[:len(packet)-r.Len()]
c.mutex.Lock()
defer c.mutex.Unlock()
// ignore delayed / duplicated version negotiation packets
if c.connState >= ConnStateVersionNegotiated && hdr.VersionFlag {
return nil
@@ -159,58 +162,18 @@ func (c *client) handlePacket(remoteAddr net.Addr, packet []byte) error {
// this is the first packet after the client sent a packet with the VersionFlag set
// if the server doesn't send a version negotiation packet, it supports the suggested version
if !hdr.VersionFlag && c.connState == ConnStateInitial {
c.mutex.Lock()
c.connState = ConnStateVersionNegotiated
c.connStateChangeOrErrCond.Signal()
c.mutex.Unlock()
if c.config.ConnState != nil {
go c.config.ConnState(c.session, ConnStateVersionNegotiated)
}
}
if hdr.VersionFlag {
var hasCommonVersion bool // check if we're supporting any of the offered versions
for _, v := range hdr.SupportedVersions {
// check if the server sent the offered version in supported versions
if v == c.version {
return qerr.Error(qerr.InvalidVersionNegotiationPacket, "Server already supports client's version and should have accepted the connection.")
}
if v != protocol.VersionUnsupported {
hasCommonVersion = true
}
}
if !hasCommonVersion {
utils.Infof("No common version found.")
return qerr.InvalidVersion
}
ok, highestSupportedVersion := protocol.HighestSupportedVersion(hdr.SupportedVersions)
if !ok {
return qerr.VersionNegotiationMismatch
}
// switch to negotiated version
c.version = highestSupportedVersion
c.connState = ConnStateVersionNegotiated
c.connectionID, err = utils.GenerateConnectionID()
err = c.handlePacketWithVersionFlag(hdr)
if err != nil {
return err
}
utils.Infof("Switching to QUIC version %d. New connection ID: %x", highestSupportedVersion, c.connectionID)
c.session.Close(errCloseSessionForNewVersion)
err = c.createNewSession(hdr.SupportedVersions)
if err != nil {
return err
}
if c.config.ConnState != nil {
go c.config.ConnState(c.session, ConnStateVersionNegotiated)
}
if err != nil {
return err
}
return nil // version negotiation packets have no payload
}
c.session.handlePacket(&receivedPacket{
@@ -222,6 +185,52 @@ func (c *client) handlePacket(remoteAddr net.Addr, packet []byte) error {
return nil
}
func (c *client) handlePacketWithVersionFlag(hdr *PublicHeader) error {
var hasCommonVersion bool // check if we're supporting any of the offered versions
for _, v := range hdr.SupportedVersions {
// check if the server sent the offered version in supported versions
if v == c.version {
return qerr.Error(qerr.InvalidVersionNegotiationPacket, "Server already supports client's version and should have accepted the connection.")
}
if v != protocol.VersionUnsupported {
hasCommonVersion = true
}
}
if !hasCommonVersion {
utils.Infof("No common version found.")
return qerr.InvalidVersion
}
ok, highestSupportedVersion := protocol.HighestSupportedVersion(hdr.SupportedVersions)
if !ok {
return qerr.VersionNegotiationMismatch
}
// switch to negotiated version
c.version = highestSupportedVersion
c.connState = ConnStateVersionNegotiated
var err error
c.connectionID, err = utils.GenerateConnectionID()
if err != nil {
return err
}
utils.Infof("Switching to QUIC version %d. New connection ID: %x", highestSupportedVersion, c.connectionID)
c.session.Close(errCloseSessionForNewVersion)
err = c.createNewSession(hdr.SupportedVersions)
if err != nil {
return err
}
if c.config.ConnState != nil {
go c.config.ConnState(c.session, ConnStateVersionNegotiated)
}
if err != nil {
return err
}
return nil // version negotiation packets have no payload
}
func (c *client) cryptoChangeCallback(_ Session, isForwardSecure bool) {
var state ConnState
if isForwardSecure {