From f1c57f714961f36e817d50b60ab9e2da5f472b38 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 25 Jun 2026 16:58:56 -0400 Subject: [PATCH] =?UTF-8?q?docs(c2):=20record=20WCF=20event-read=20spike?= =?UTF-8?q?=20live=20result=20(RED=20=E2=80=94=20transport=20not=20served?= =?UTF-8?q?=20on=202023=20R2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WCF net.tcp (RemoteTcpIntegrated) against the live 2023 R2 historian is reset at the socket-write/framing layer before any auth — both the event spike and a basic Probe/ReadRaw throw the identical CommunicationException/SocketException ("forcibly closed by the remote host"). The 2023 R2 box does not serve the legacy WCF transport; C2's "route via WCF" unblock is moot on this server class. Sanitized: counts + native return codes + buffer lengths only. Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii --- .../wcf-event-read-spike-results.md | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 docs/reverse-engineering/wcf-event-read-spike-results.md diff --git a/docs/reverse-engineering/wcf-event-read-spike-results.md b/docs/reverse-engineering/wcf-event-read-spike-results.md new file mode 100644 index 0000000..16c610d --- /dev/null +++ b/docs/reverse-engineering/wcf-event-read-spike-results.md @@ -0,0 +1,63 @@ +# WCF event-read spike — live result (2026-06-25): WCF transport not served on 2023 R2 + +Settles the open question behind **C2** ("event reads over gRPC are gated; the only listed unblock is +*route event reads via WCF*"). The gRPC event-read path is a proven server-side dead-end +(`grpc-event-query-capture.md`: auth fully solved, every client-controllable layer byte-matched to the +stock client, yet the server scopes 0 rows to our connection). This spike tested the **WCF** leg. + +## What was run + +A Windows-only, env-gated diagnostic (`tests/AVEVA.Historian.Client.Tests/WcfEventReadSpikeTests.cs`, +gated by `HISTORIAN_WCF_EVENT_HOST`) drove `HistorianWcfEventOrchestrator.ReadEventsAsync` directly +over `RemoteTcpIntegrated` (WCF `net.tcp`, port 32568) against the **live 2023 R2 historian**, with a +−90d window (the engine holds tens of thousands of events in that range), run from the native Windows +capture rig over VPN. Auth supplied as explicit domain credentials (consumed by the app-level +`ValidateClientCredential` SSPI rounds). + +## Result — RED (transport not served), sanitized + +Event spike: + +| field | value | +|---|---| +| outcome | `THREW System.ServiceModel.CommunicationException` ("The socket connection was aborted") | +| inner | `System.Net.Sockets.SocketException` — "An existing connection was forcibly closed by the remote host" | +| events observed | 0 | +| LastUpdC3ReturnCode / LastRTag2ReturnCode / LastAddReturnCode(EnsT2) | 0 / 0 / 0 | +| LastEnsT2PayloadSha256 | empty | +| LastResultBufferLength | 0 | + +All native return codes are `0` and the EnsT2 payload sha256 is empty: the chain failed at the **first +WCF call** (`GetInterfaceVersion`), *before* any auth token round or CM_EVENT registration ran. + +Corroboration — a basic (non-event) `RemoteTcpIntegrated` `ProbeAsync` + `ReadRawAsync` (the committed +`RemoteTcpIntegrationTests`) throws the **identical** exception, with the stack landing in +`System.ServiceModel.Channels.SocketConnection.WriteAsync` — i.e. the failure is **transport-wide**, not +event-specific, and not auth-specific (it never reaches auth). + +Phase 0 (reachability) had confirmed TCP 32568 is **open** (the connect succeeds). So the port accepts a +socket, but the moment the SDK writes its `net.tcp` binary-SOAP framing the server **resets the +connection** (RST at the socket-write layer). + +## Conclusion + +The **2023 R2 historian does not serve the legacy WCF NetTcp transport.** A raw RST at the first socket +write — before any security negotiation, SOAP fault, or auth exchange — is the signature of a listener +that does not speak `net.tcp` binary SOAP, not of an auth/SPN problem or event-row scoping. (The earlier +WCF event-chain native return codes 76/85 documented in `HistorianWcfEventOrchestrator` were only ever +observed against a **2020** historian; against 2023 R2 there is no WCF endpoint to reach at all.) + +Therefore **C2's "route event reads via WCF" unblock is moot on 2023 R2** — there is no WCF endpoint to +route to. Event reads are unavailable on the 2023 R2 historian over **both** transports: + +- **gRPC** — auth-solved but retrieval-server-gated (server scopes 0 rows to our connection; + `grpc-event-query-capture.md`). +- **WCF (`net.tcp`)** — transport not served on 2023 R2 (connection reset at framing). + +The WCF event-read managed path would only ever apply to a legacy **2020** historian, which the gateway +does not target (the gateway runs `RemoteGrpc` against 2023 R2). The only remaining theoretical unblock +is server-side (AVEVA exposing event-row retrieval to a managed gRPC connection) — not client-fixable. + +**C2 is closed won't-fix** for the gateway's target (2023 R2). `ReadEventsAsync` over gRPC keeps its +honest no-row throw; the gating messages are corrected so they no longer point operators at the WCF +transport as a live fallback on 2023 R2.