Fix DTLS server example consuming the ClientHello before handshake (F-3481)
The DTLS branch called bind_socket.recvfrom(1) before creating the context. On UDP that removes the entire first datagram (the client's ClientHello) from the queue and discards everything past the first byte, so wolfSSL_accept() had nothing to consume and the handshake only recovered after the client's retransmit timer. The captured from_addr was also reused for every iteration of the -i loop. Replace it with a peek_peer_address() helper that uses MSG_PEEK to read the source address without consuming the datagram, and move the peek into the accept loop so the address is refreshed per connection.pull/70/head
parent
74a340db5e
commit
c29eb6760b
|
|
@ -115,6 +115,21 @@ def get_DTLSmethod(index):
|
|||
)[index]
|
||||
|
||||
|
||||
# Large enough to peek a DTLS ClientHello source address.
|
||||
PEEK_BUFSIZE = 1500
|
||||
|
||||
|
||||
def peek_peer_address(sock):
|
||||
"""
|
||||
Return the source address of the next pending datagram without removing
|
||||
it from the socket queue. MSG_PEEK leaves the datagram (the DTLS
|
||||
ClientHello) intact so wolfSSL_accept() can consume it during the
|
||||
handshake.
|
||||
"""
|
||||
_, from_addr = sock.recvfrom(PEEK_BUFSIZE, socket.MSG_PEEK)
|
||||
return from_addr
|
||||
|
||||
|
||||
def main():
|
||||
args = build_arg_parser().parse_args()
|
||||
# DTLS connection over UDP
|
||||
|
|
@ -124,7 +139,6 @@ def main():
|
|||
args.v = 1
|
||||
bind_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
|
||||
bind_socket.bind(("" if args.b else "localhost", args.p))
|
||||
data, from_addr = bind_socket.recvfrom(1)
|
||||
context = wolfssl.SSLContext(get_DTLSmethod(args.v), server_side=True)
|
||||
# SSL/TLS connection over TCP
|
||||
else:
|
||||
|
|
@ -156,6 +170,9 @@ def main():
|
|||
try:
|
||||
secure_socket = None
|
||||
if args.u:
|
||||
# Peek the client's address for this connection without
|
||||
# consuming the ClientHello datagram needed by the handshake.
|
||||
from_addr = peek_peer_address(bind_socket)
|
||||
secure_socket = context.wrap_socket(bind_socket)
|
||||
else:
|
||||
new_socket, from_addr = bind_socket.accept()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# test_dtls_server_example.py
|
||||
#
|
||||
# Copyright (C) 2006-2020 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
#
|
||||
# wolfSSL is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# wolfSSL is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
# pylint: disable=missing-docstring, invalid-name, import-error
|
||||
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "examples"))
|
||||
import server as server_example # noqa: E402
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def udp_pair():
|
||||
srv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
srv.bind(("localhost", 0))
|
||||
cli = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
yield srv, cli, srv.getsockname()
|
||||
finally:
|
||||
srv.close()
|
||||
cli.close()
|
||||
|
||||
|
||||
def test_peek_peer_address_returns_source(udp_pair):
|
||||
srv, cli, srv_addr = udp_pair
|
||||
cli.bind(("localhost", 0))
|
||||
cli.sendto(b"clienthello-payload", srv_addr)
|
||||
|
||||
addr = server_example.peek_peer_address(srv)
|
||||
|
||||
assert addr == cli.getsockname()
|
||||
|
||||
|
||||
def test_peek_peer_address_does_not_consume_datagram(udp_pair):
|
||||
"""
|
||||
Regression test for F-3481: peeking the client's address before the
|
||||
DTLS handshake must leave the ClientHello datagram intact. The previous
|
||||
example used recvfrom(1), which consumed the datagram and discarded
|
||||
everything past the first byte, breaking the handshake.
|
||||
"""
|
||||
srv, cli, srv_addr = udp_pair
|
||||
payload = b"X" * 256 # stand-in for a DTLS ClientHello record
|
||||
cli.sendto(payload, srv_addr)
|
||||
|
||||
server_example.peek_peer_address(srv)
|
||||
|
||||
# The datagram must still be fully available for wolfSSL_accept().
|
||||
srv.settimeout(2)
|
||||
data, _ = srv.recvfrom(4096)
|
||||
assert data == payload
|
||||
Loading…
Reference in New Issue