[M2/M3] mxaccess-rpc: tokio DCE/RPC TCP transport (DceRpcTcpClient port)
Lands the async DCE/RPC TCP client — the transport that bridges the M2
PDU codec to a real socket. Unblocks M3 stream B (mxaccess-nmx, the
NmxClient) and brings F9 (ResolveOxid wrappers) within reach.
New
- transport.rs (~700 LoC, 10 tests including 2 real-socket tokio tests)
— port of src/MxNativeClient/DceRpcTcpClient.cs.
- DceRpcTcpClient::connect/bind/bind_with_managed_ntlm_packet_integrity/
call/call_bound/call_bound_object — async over tokio::net::TcpStream.
- encode_packet_integrity_request: 4-byte 0xBB pad + 8-byte AuthTrailer
+ 16-byte NtlmClientContext::sign signature, frag_length and
auth_length rewritten in the embedded header per cs:201-250.
- encode_request_bytes: PFC_OBJECT_UUID flag (0x80) and inserted
16-byte object UUID slot per cs:269-278.
- TransportError enum unifies io / codec / NTLM / fault / not-connected
surfaces. Mirrors DceRpcFaultException as the typed Fault variant.
- NTLM_AUTH_CONTEXT_ID = 79232 = 0x13580 (cs:90,133) exposed publicly.
Deliberately skipped: BindWithNtlmConnect / BindWithNtlmPacketIntegrity
(SSPI flavours at cs:55-63,108-149) — those wrap .NET's
System.Net.Security.SspiClientContext, which has no portable analogue.
Managed-NTLM path covers what the production Rust client needs.
mxaccess-rpc/Cargo.toml: added tokio (workspace-pinned).
design/followups.md: F9 downgraded P1 → P2 (transport landed; only the
two pure-codec ResolveOxid wrappers remain).
Test count delta: 354 -> 364 (+10).
Open followups touched: F9 partially advanced.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+4
-4
@@ -42,11 +42,11 @@ move to `## Resolved` with a date + commit hash.
|
||||
**Why deferred:** The provider is a wrapper around `ole32::CoMarshalInterface` / `IStream` / `GlobalLock` / `GlobalSize`. It needs `windows-rs`, which is currently behind the `windows-com` feature in `mxaccess-rpc/Cargo.toml`. The pure-Rust parser stands alone for the inbound activation-response path that M2 wave 1 needs.
|
||||
**Resolves when:** `windows-rs` is wired into `mxaccess-rpc` (M2 wave 3 callback exporter needs to publish its own OBJREF for `IRemUnknown` / `INmxSvcCallback` registration) and an emitter port lands behind the `windows-com` feature.
|
||||
|
||||
### F9 — Port `ObjectExporterClient.cs` (4 NTLM-variant ResolveOxid transport wrappers)
|
||||
**Severity:** P1
|
||||
### F9 — `ObjectExporterClient.cs` ResolveOxid wrapper methods
|
||||
**Severity:** P2 (was P1 — downgraded after `DceRpcTcpClient` transport landed)
|
||||
**Source:** M2 wave 2, `crates/mxaccess-rpc/src/object_exporter.rs`
|
||||
**Why deferred:** Those four methods (`ResolveOxidUnauthenticated`, `ResolveOxidWithNtlmConnect`, `ResolveOxidWithNtlmPacketIntegrity`, `ResolveOxidWithManagedNtlmPacketIntegrity`) drive a `DceRpcTcpClient` that has not been ported. They are transport-layer code, not pure-codec, so they sit one wave below the OXID body codec that M2 wave 2 landed.
|
||||
**Resolves when:** A `DceRpcTcpClient` Rust port lands (likely M2 wave 3 alongside the callback exporter, or M3 if the NMX session takes the lead). Then the four `resolve_oxid_with_*` flavors can be a thin call layer over `encode_resolve_oxid_request` + `parse_resolve_oxid_result`.
|
||||
**Why deferred:** The transport prerequisite (`DceRpcTcpClient`) is now ported in `crates/mxaccess-rpc/src/transport.rs`. What remains is two thin wrapper methods that wire the codec to the transport: `resolve_oxid_unauthenticated(addr, oxid, protseqs) -> Result<ResolveOxidResult, _>` (mirrors `ObjectExporterClient.cs:14-30`) and `resolve_oxid_with_managed_ntlm_packet_integrity(addr, oxid, protseqs, ntlm) -> Result<ResolveOxidResult, _>` (mirrors `cs:66-81`). The two SSPI variants (`ResolveOxidWithNtlmConnect` at `cs:32-47` and `ResolveOxidWithNtlmPacketIntegrity` at `cs:49-64`) are .NET-specific (`System.Net.Security.SspiClientContext`) and explicitly out of scope.
|
||||
**Resolves when:** Both wrapper methods land, calling `DceRpcTcpClient::connect`/`bind`/`call_bound` against `IObjectExporter` opnum 0 and parsing via `parse_resolve_oxid_result` / `parse_resolve_oxid_failure`.
|
||||
|
||||
### F10 — `IObjectExporter::ResolveOxid2` (opnum 4) body codec
|
||||
**Severity:** P2
|
||||
|
||||
Reference in New Issue
Block a user