Commit Graph

2 Commits

Author SHA1 Message Date
funman300 84e81f2037 tls: extract a shared listener; move FIFA 17's profile into its adapter
The redirector was the only host that spoke TLS, so its TLS lived inside
it. The roster host needs the same listener, and that made the choice
explicit: share this code or copy it.

Copying it is what already went wrong. On 2026-08-11 the Rust redirector
served one certificate while the container served another. ProtoSSL
caches the server certificate per backend, so the redirector -- the first
TLS connection of a session -- decided what the client expected, and
every later service failed its handshake. Silently: Python's socketserver
swallows ssl.SSLError as OSError. Three gates went to it. One place to
configure TLS is the structural fix, so it exists before the second host
does rather than after.

Split along the line the architecture already draws:

  openfut-tls               how to build an acceptor. Game-independent.
                            Knows nothing about which suites any client
                            offers.
  adapter-fifa17::tls       what FIFA 17 was OBSERVED to offer: the six
                            enabled suites, the two refused, the TLS 1.2
                            window, the EA SNI. Plain strings, so the
                            adapter keeps its lean dependencies -- reading
                            a card table should not build OpenSSL.
  redirector-host           joins the two. Chooses no cipher of its own.

Behaviour is unchanged, and shown to be:

* tests/fifa17_tls_profile.rs carries over every case from the deleted
  module -- FIFA's eight suites negotiate AES256-GCM-SHA384, each enabled
  suite works alone, RC4-only is refused, ECDHE-only is refused. Deleting
  a module must not quietly delete its evidence.
* one test pins the composed values literally against the host as it was
  when gates 1-14 passed. A "pure refactor" that cannot fail is not a
  claim, it is an assumption.
* the rebuilt binary self-tests to the same TLSv1.2 / AES256-GCM-SHA384
  the retail client negotiated at 17:09 today.

Two improvements fall out of having one place to look:

* the startup banner now prints cert_sha256. The mismatch above raised no
  error at startup and broke the client much later with nothing logged;
  it is now the first line of the log.
* tls_min/tls_max print as TLSv1.2 rather than SslVersion(771). This line
  is gate evidence and gets read by people.

Nothing deployed and nothing restarted: FIFA is mid-session on the
running redirector, which is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 17:30:01 +00:00
funman300 b1bc7a764e adapter: port the FUT roster-update response, held to the live oracle's bytes
Next component in the migration order (Roster -> LSX -> UTAS). Adapter layer
only: no host, no runtime replacement, nothing armed.

The response is shaped as much by http.server.BaseHTTPRequestHandler as by the
oracle's handler code, so it is captured over the wire rather than reasoned
about:

  * HTTP/1.0 status line -- protocol_version is left at its default, so the
    reply is 1.0 even though the client asks for 1.1
  * send_response injects Server: and Date: BEFORE the handler's own headers
  * POST answers with headers only: the handler writes the body `if method ==
    "GET"`, so a POST advertises Content-Length: 67 and then sends nothing

That last one is preserved, not corrected. It looks like a bug, but "obviously a
bug" has been the wrong call before in this port, and a test now asserts it so a
future cleanup has to argue with something.

Date and Server are volatile and are MASKED in the fixture rather than dropped,
so their presence and position are still asserted. Server is additionally
recorded verbatim: it carries the container's Python version, so a drift away
from roster::ORACLE_SERVER fails a test instead of silently changing every byte
we emit.

generate_roster.py --check FAILS when it cannot reach the oracle rather than
passing, and mutation-testing the mutation harness itself caught two "surviving"
mutations that were really sed no-ops. With application verified, all four
mutations (header order, Content-Length, XML body, Connection) are killed.
2026-08-11 05:19:29 +00:00