feat(protocol): bound DTLS handshake deadline
This commit is contained in:
7 files changed
+513
-111
No files matched your search
@@ -0,0 +1,72 @@
|
||||
"""Shared memory-BIO driver for bounded DTLS handshakes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
|
||||
from OpenSSL import SSL
|
||||
|
||||
from .coap import split_dtls
|
||||
|
||||
_HANDSHAKE_POLL_S = 0.5
|
||||
_MAX_DATAGRAM_SIZE = 65535
|
||||
|
||||
|
||||
def _drive_dtls_handshake(
|
||||
connection,
|
||||
sock,
|
||||
*,
|
||||
deadline: float,
|
||||
retries: int | None = None,
|
||||
on_datagram: Callable[[bytes], None] | None = None,
|
||||
) -> bool:
|
||||
"""Drive one memory-BIO DTLS handshake up to a monotonic deadline.
|
||||
|
||||
OpenSSL owns the retransmission schedule. ``retries`` optionally limits
|
||||
how many expired retransmission timers are serviced; a normal session is
|
||||
bounded only by its deadline, while the diagnostic probe retains its
|
||||
explicit retry budget.
|
||||
|
||||
Return ``True`` only when the handshake completes before the deadline.
|
||||
TLS and socket failures are left to the caller to classify.
|
||||
"""
|
||||
retransmits = 0
|
||||
while time.monotonic() < deadline:
|
||||
try:
|
||||
connection.do_handshake()
|
||||
return time.monotonic() < deadline
|
||||
except SSL.WantReadError:
|
||||
pass
|
||||
|
||||
try:
|
||||
output = connection.bio_read(_MAX_DATAGRAM_SIZE)
|
||||
except SSL.WantReadError:
|
||||
output = None
|
||||
if output:
|
||||
for record in split_dtls(output):
|
||||
if sock.send(record) != len(record):
|
||||
raise OSError("incomplete UDP send")
|
||||
|
||||
remaining = deadline - time.monotonic()
|
||||
if remaining <= 0:
|
||||
break
|
||||
sock.settimeout(min(_HANDSHAKE_POLL_S, remaining))
|
||||
try:
|
||||
datagram = sock.recv(_MAX_DATAGRAM_SIZE)
|
||||
except TimeoutError:
|
||||
timer = connection.DTLSv1_get_timeout()
|
||||
if timer is not None and timer <= 0:
|
||||
if retries is not None and retransmits >= retries:
|
||||
break
|
||||
connection.DTLSv1_handle_timeout()
|
||||
retransmits += 1
|
||||
continue
|
||||
|
||||
if not datagram:
|
||||
continue
|
||||
if on_datagram is not None:
|
||||
on_datagram(datagram)
|
||||
connection.bio_write(datagram)
|
||||
|
||||
return False
|
||||
Reference in new issue
Block a user