Ryan Malloy 345838fe2d Phase 14: TLS / SSL transport
Optional TLS via the ``tls`` parameter on connect() and IfxSocket.
Three modes:

  tls=False (default)  — plain TCP, current behavior unchanged
  tls=True             — TLS w/ verification disabled (dev / self-signed)
  tls=ssl.SSLContext   — caller-supplied context (production)

Plus tls_server_hostname for SNI / cert verification.

Architectural choice: Informix uses dedicated TLS-enabled listener
ports (configured in server's sqlhosts), NOT STARTTLS upgrade. The
SSL handshake runs immediately after TCP connect with no protocol-
level negotiation. Wrapping happens inside IfxSocket.__init__ so the
rest of the protocol layer (login PDU, SQ_BIND, fast-path, file
transfer) is fully unaware of whether TLS is in use.

Why tls=True defaults to insecure: most Informix dev installations
use self-signed certs. tls=True produces a context with
check_hostname=False and verify_mode=CERT_NONE. Minimum protocol is
still TLSv1.2 (per ssl.PROTOCOL_TLS_CLIENT). Production users are
expected to pass ssl.SSLContext explicitly.

Tests: 5 unit tests in test_tls.py
* tls=True dev context properties
* default context uses TLSv1.2+
* real handshake against in-process TLS echo server (proves wrap_socket
  works end-to-end)
* custom SSLContext honored verbatim
* tls=True against non-TLS port raises OperationalError clearly

Test certs are generated via openssl CLI subprocess instead of
adding cryptography as a dev dep (saves ~5MB transitive deps for
one phase).

Total: 69 unit + 139 integration = 208 tests.

Architectural milestone: with Phase 14 complete, the driver now
implements EVERYTHING in the SQLI wire-protocol family that a Python
application needs. Remaining backlog (async, pooling) is library-
design work, not protocol work.
2026-05-04 14:46:53 -06:00
..
2026-05-04 14:46:53 -06:00