@@ -82,13 +82,14 @@ func (s handshakeState) String() string {
8282}
8383
8484type handshakeFSM struct {
85- currentFlight flightVal
86- flights []* packet
87- retransmit bool
88- state * State
89- cache * handshakeCache
90- cfg * handshakeConfig
91- closed chan struct {}
85+ currentFlight flightVal
86+ flights []* packet
87+ retransmit bool
88+ retransmitInterval time.Duration
89+ state * State
90+ cache * handshakeCache
91+ cfg * handshakeConfig
92+ closed chan struct {}
9293}
9394
9495type handshakeConfig struct {
@@ -109,7 +110,8 @@ type handshakeConfig struct {
109110 sessionStore SessionStore
110111 rootCAs * x509.CertPool
111112 clientCAs * x509.CertPool
112- retransmitInterval time.Duration
113+ initialRetransmitInterval time.Duration
114+ disableRetransmitBackoff bool
113115 customCipherSuites func () []CipherSuite
114116 ellipticCurves []elliptic.Curve
115117 insecureSkipHelloVerify bool
@@ -165,11 +167,12 @@ func newHandshakeFSM(
165167 initialFlight flightVal ,
166168) * handshakeFSM {
167169 return & handshakeFSM {
168- currentFlight : initialFlight ,
169- state : s ,
170- cache : cache ,
171- cfg : cfg ,
172- closed : make (chan struct {}),
170+ currentFlight : initialFlight ,
171+ state : s ,
172+ cache : cache ,
173+ cfg : cfg ,
174+ retransmitInterval : cfg .initialRetransmitInterval ,
175+ closed : make (chan struct {}),
173176 }
174177}
175178
@@ -274,11 +277,12 @@ func (s *handshakeFSM) wait(ctx context.Context, c flightConn) (handshakeState,
274277 return handshakeErrored , errFlight
275278 }
276279
277- retransmitTimer := time .NewTimer (s .cfg . retransmitInterval )
280+ retransmitTimer := time .NewTimer (s .retransmitInterval )
278281 for {
279282 select {
280283 case done := <- c .recvHandshake ():
281284 nextFlight , alert , err := parse (ctx , c , s .state , s .cache , s .cfg )
285+ s .retransmitInterval = s .cfg .initialRetransmitInterval
282286 close (done )
283287 if alert != nil {
284288 if alertErr := c .notify (ctx , alert .Level , alert .Description ); alertErr != nil {
@@ -304,8 +308,19 @@ func (s *handshakeFSM) wait(ctx context.Context, c flightConn) (handshakeState,
304308 if ! s .retransmit {
305309 return handshakeWaiting , nil
306310 }
311+
312+ // RFC 4347 4.2.4.1:
313+ // Implementations SHOULD use an initial timer value of 1 second (the minimum defined in RFC 2988 [RFC2988])
314+ // and double the value at each retransmission, up to no less than the RFC 2988 maximum of 60 seconds.
315+ if ! s .cfg .disableRetransmitBackoff {
316+ s .retransmitInterval *= 2
317+ }
318+ if s .retransmitInterval > time .Second * 60 {
319+ s .retransmitInterval = time .Second * 60
320+ }
307321 return handshakeSending , nil
308322 case <- ctx .Done ():
323+ s .retransmitInterval = s .cfg .initialRetransmitInterval
309324 return handshakeErrored , ctx .Err ()
310325 }
311326 }
@@ -320,11 +335,12 @@ func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState
320335 return handshakeErrored , errFlight
321336 }
322337
323- retransmitTimer := time .NewTimer (s .cfg . retransmitInterval )
338+ retransmitTimer := time .NewTimer (s .retransmitInterval )
324339 select {
325340 case done := <- c .recvHandshake ():
326341 nextFlight , alert , err := parse (ctx , c , s .state , s .cache , s .cfg )
327342 close (done )
343+ s .retransmitInterval = s .cfg .initialRetransmitInterval
328344 if alert != nil {
329345 if alertErr := c .notify (ctx , alert .Level , alert .Description ); alertErr != nil {
330346 if err != nil {
@@ -342,10 +358,16 @@ func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState
342358 return handshakeFinished , nil
343359 }
344360 <- retransmitTimer .C
361+ // RFC 4347 4.2.4.1
362+ s .retransmitInterval *= 2
363+ if s .retransmitInterval > time .Second * 60 {
364+ s .retransmitInterval = time .Second * 60
365+ }
345366 // Retransmit last flight
346367 return handshakeSending , nil
347368
348369 case <- ctx .Done ():
370+ s .retransmitInterval = s .cfg .initialRetransmitInterval
349371 return handshakeErrored , ctx .Err ()
350372 }
351373 return handshakeFinished , nil
0 commit comments