socket.inet_pton() is missing on some supported platforms (Python 2.7
on Windows). getaddrinfo() with AI_NUMERICHOST parses without
resolving and is available everywhere. It also handles scoped IPv6
literals.
Review follow-up for F-5621. wolfSSL_check_domain_name() only matches
DNS names: on this path CheckForAltNames() is called with isIP=0, so
iPAddress SANs are always skipped (verified on v5.8.4-stable and
master). The default invocation (host 127.0.0.1) therefore failed the
handshake with DOMAIN_NAME_MISMATCH (-322) once hostname verification
was enabled by default.
Skip the hostname check for IP literal hosts and say so, keeping
CERT_REQUIRED verification. This also stops offering an IP literal in
SNI, which RFC 6066 forbids. Connecting by DNS name still enables the
hostname check.
Add unit tests for the IP literal paths and an end-to-end test that
runs server.py and client.py with default arguments.
The bundled CRL expired 2024-11-11, so the client example's default
CRL load (enabled unless -C) fails. Take the current CRL from wolfSSL
v5.8.4-stable certs/crl/crl.pem: same Sawtooth CA and key, revokes
only serial 02, valid until 2028-08-09.
Note: the bundled ca/server/client certs expire 2026-09-08 and will
need a refresh of their own before then.
get_peer_x509() checked only whether the session was NULL and then built
a WolfSSLX509, whose __init__ called wolfSSL_get_peer_certificate() and
raised SSLError on NULL. On a valid connection where the peer presented
no certificate (e.g. a server not requesting a client cert), this raised
instead of returning None as the stdlib ssl getpeercert() contract
requires. Fetch the certificate in get_peer_x509(), return None when it
is NULL, and have WolfSSLX509 wrap the already-obtained pointer.
write() converted data with t2b(), which str()-encodes anything that is
not already bytes. Valid bytes-like inputs such as bytearray and
memoryview were transmitted as their Python repr ("bytearray(b'...')",
"<memory at ...>") instead of their contents, corrupting the stream.
Convert via the buffer protocol (bytes(memoryview(data))) and raise
TypeError for objects that are not bytes-like, matching the stdlib ssl
module.
The client example set CERT_REQUIRED and loaded CA roots but never set
check_hostname or passed server_hostname to wrap_socket, so wolfSSL
validated the chain to a trusted CA without binding the certificate to
the requested host. A peer presenting any CA-trusted certificate for a
different hostname would be accepted by anyone reusing this as a secure
client template. Make verification configure hostname checking by
default (via a new configure_verification helper) and add a -n flag to
opt out explicitly for IP literals or test certificates.
For DTLS, write()/read()/recv_into() called do_handshake() on every
call. do_handshake() runs wolfSSL_accept/connect, which on a
non-blocking socket can raise SSLWantReadError and abort an I/O long
after the handshake finished, and made DTLS write-side behaviour
inconsistent with TCP. Track completion with a _handshake_complete
flag set on a successful do_handshake(), and only drive the handshake
from I/O methods while that flag is False.
recv_into() shares read()'s error-mapping pattern and inherited the
same omission: wolfSSL_read returning WOLFSSL_ERROR_WANT_WRITE (during
a renegotiation needing a write) was reported as a generic SSLError
instead of SSLWantWriteError, breaking non-blocking callers that
distinguish readiness directions. Add the WANT_WRITE branch.
wolfSSL_read can return WOLFSSL_ERROR_WANT_WRITE when the SSL layer
must flush a handshake record (e.g. renegotiation) before returning
data. read() only handled WANT_READ, raising a generic SSLError
otherwise, which stops non-blocking callers from select()-ing on
writability. Add a WANT_WRITE branch raising SSLWantWriteError.
wolfSSL_write can return WOLFSSL_ERROR_WANT_READ (e.g. during a
renegotiation that must read a record before progressing; secure
renegotiation is enabled by default). write() only handled WANT_WRITE,
so WANT_READ fell through to a generic SSLError and non-blocking
callers tore the session down. Add a WANT_READ branch raising
SSLWantReadError, matching do_handshake().
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.