Server `Handshaker` will not transition to `Connection` until client sends data
ShadowJonathan opened this issue · 3 comments
The DTLS 1.2 handshake looks like thus;
All of this is able to be encapsulated in Handshaker
objects, which handle all of this (+ retransmission) for the clients.
Because of this, handshakes have a special API which makes them able to alert the program to wait until a certain moment to retransmit the packets they'd want to send.
However, the 6th flight could get lost, either the CCS or the Finished packet, which means the client can get in a deadlock, see the following passage from [RFC6347];
In addition, for at least twice the default MSL defined for [TCP],
when in the FINISHED state, the node that transmits the last flight
(the server in an ordinary handshake or the client in a resumed
handshake) MUST respond to a retransmit of the peer's last flight
with a retransmit of the last flight. This avoids deadlock
conditions if the last flight gets lost.
However, this would block the server handshaker from transitioning into a connection object until the client has sent any application data.
It would be more "correct" to wait until a client sends data, but depending on the application, the server might want to immediately start to send data.
This could be especially problematic with resuming sessions, or #9, which might require this kind of "last state" to be certain before either side starts sending buffered data again.
For now, I think it's more clean to wait for application data, but in the future I could add a "server handshaker mode" which immediately transitions to a connection object after finishing the last flight, but keeps it sealed and keeps it ready to be sent if the client were to send flight 5 again, this would be a ton of extra logic to keep, though.
This would actually maybe be a problem for SRTP, especially in combination with #15, which would separate away the sink for application data.
Though, after thinking about it for a little more while, it is possible that this is okay.
We could probably let the connection object trigger the flight retransmission if it sees any part of the 5th flight instead, as the application would just blindly feed data from the socket, in hope of application data. If we see that (and verify it), we should then send the flight back. Though in this case, we're relying on the client to be the retransmission timer.
This would also be fine in a situation with #15, as the connection object would then just hold the sealed last flight data forever, if needed, only sending it when the application feeds it handshake messages from the last flight. This would be a little overhead in exchange for a nicer and cleaner API.
Alright, I'll store this in the server Connection object as a Option<Box<HandshakeFinalizer>>
, the box will make sure it has less overhead on the Connection object after it's been disposed of.
I'll also expose a function like unchecked_dispose_handshake_finaliser
, which can be called by the application if it receives evidence of the connection being properly finalized (such as SRTP data being received), this'll allow applications to more tightly (and optionally) control this, at the expense of potentially turning the connection into an error state if at any point hereafter some packets of fight 5 are received, though those would potentially be ignored instead, possibly turning the connection into a deadlock state.