Files
mxaccess/rust
Joseph Doherty d4ee5f3a18 [M5] examples: asb-relay TCP middleman for live wire-byte capture
Listens on MX_RELAY_LISTEN (default 127.0.0.1:8088) and forwards to
MX_RELAY_UPSTREAM (default 127.0.0.1:808 — AVEVA's NetTcpPortSharing
SMSvcHost listener). Hex-dumps every byte both directions to stderr
with C->S / S->C tags + per-direction offset prefixes.

Usage:
  $env:MX_RELAY_LISTEN = '0.0.0.0:8088'
  .\rust\target\debug\examples\asb-relay.exe 2> relay.log
  # then in another shell:
  dotnet run --project src\MxAsbClient.Probe -c Release -- `
    '--endpoint=net.tcp://desktop-6jl3kko:8088/ASBService/Default_ZB_MxDataProvider/IDataV2'

Tested against the live AVEVA install on this box — captured a
620-byte client→server exchange including the full .NET probe's
preamble, SizedEnvelope, and End record. The capture surfaced one
critical missing piece in our wire format:

**WCF binary message framing prepends Action + To strings out-of-band**
before the actual NBFX SOAP envelope. The .NET probe's envelope
payload begins:

  74 27 [39 bytes "http://asb.contracts/20111111:connectIn"]    ← Action
  4b    [75 bytes "net.tcp://desktop-6jl3kko:8088/.../IDataV2"]  ← To
  56 02 ...                                                      ← <s:Envelope>

The 0x74 / 0x4b prefix bytes appear to be WCF-internal framing that
stores Action and To headers OUT of the SOAP envelope as a binary
optimization. Our F25 envelope encoder doesn't emit this — it goes
straight to `<s:Envelope>` (which the probe captured as `56 02 ...`
PrefixDictionaryElement_s + dict id 2). This is likely why the
server fault'd at AddressFilter mismatch in the previous iteration.

Note: when going through the relay, the .NET probe's `:8088` port
appears in the To URL inside the binary header, which doesn't match
the registered service URL on SMSvcHost — so this exact relay setup
returns the AddressFilterMismatch fault. The capture is still
valuable (we see what bytes WCF emits for our action/header
structure). For a fault-free dispatch, we'd need to:
* rewrite the binary header's port (0x4b length / URL bytes) at
  the relay, OR
* listen on port 808 directly (requires stopping SMSvcHost), OR
* run an admin-elevated Wireshark/Npcap loopback capture.

Cleanup: dotnet probe must use `--endpoint=URL` (single arg with `=`),
not space-separated; the probe's GetArg helper splits on `=`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 15:27:54 -04:00
..

mxaccess (Rust port)

Native Rust replacement for AVEVA / Wonderware MXAccess. See ../design/ for the architectural specification, ../src/ for the .NET reference (the executable spec), and ../CLAUDE.md for project-wide rules.

Status

M0 — Workspace skeleton. Stub types compile; nothing is implemented yet. See ../design/60-roadmap.md for the M0M6 milestone plan.

Layout

rust/
  Cargo.toml                 workspace root
  rust-toolchain.toml        1.85 stable
  crates/
    mxaccess-codec/          pure protocol codec, no I/O
    mxaccess-galaxy/         Galaxy SQL resolver (tiberius)
    mxaccess-rpc/            DCE/RPC + NTLMv2 + OXID + OBJREF
    mxaccess-callback/       INmxSvcCallback RPC server
    mxaccess-nmx/            INmxService2 client
    mxaccess-asb-nettcp/     net.tcp framing (MC-NMF + MC-NBFX/NBFS)
    mxaccess-asb/            IASBIDataV2 client
    mxaccess/                async session + Transport trait + public API
    mxaccess-compat/         LMXProxyServer-shaped facade

Build

cargo build --workspace
cargo test --workspace
cargo clippy --workspace -- -D warnings
cargo fmt --check

Live probes

. ..\tools\Setup-LiveProbeEnv.ps1
cargo test -p mxaccess --features live -- --ignored

The setup script fetches credentials from Infisical via wwtools/secrets/Get-Secret.ps1. Never inline plaintext credentials.

License

MIT — see ../LICENSE.