Closes F10 and F11 per option (b) of each followup's resolve
criterion: hand-rolled body codecs derived from the [MS-DCOM]
spec, ship structurally with no live fixture (the .NET reference
doesn't call these opnums), validate against captured frames when
they become available.
F10 — IObjectExporter::ResolveOxid2 (opnum 4):
Per [MS-DCOM] §3.1.2.5.1.4. New parse_resolve_oxid2_result
mirrors parse_resolve_oxid_result exactly except for the extra
4-byte COMVERSION slot (u16 major + u16 minor) between
authn_hint and error_status. Trailing-fields check tightens
from 24 bytes (opnum 0) to 28 bytes (opnum 4). New ComVersion +
ResolveOxid2Result types. referent_id=0 short-circuit returns
empty bindings + default ComVersion + tail-status, matching
opnum 0's pattern.
F11 — IRemUnknown::RemAddRef + RemRelease (opnums 4 and 5):
Per [MS-DCOM] §3.1.1.5.6 + §2.2.19 (REMINTERFACEREF). Both
opnums share the wire shape, so:
- encode_rem_add_ref_request + encode_rem_release_request
both delegate to a shared encode_remref_array_request
helper.
- parse_remref_response is shared between them too — they
have an identical OrpcThat + referent_id + max_count +
N×HRESULT + error_code layout.
New RemInterfaceRef struct (ipid + public_refs + private_refs,
ENCODED_LEN = 24) + RemRefResponse decoded shape.
8 new structural tests across both modules pin every documented
edge of each shape — short stubs, referent-zero short-circuits,
truncated-trailing detection, full multi-element round-trips.
mxaccess-rpc 183 → 188 tests; default-feature clippy clean.
Both followups documented "**Status:** Awaiting wire-fixture
capture" prior to this commit; the structural-port option was
explicitly listed as resolution path (b) in each. Future captured
frames will validate the byte-by-byte match — until then the
port is byte-faithful to the spec but unverified against a live
peer (which is fine for shipping since neither opnum is on the
NMX session's hot path).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lands M2 wave 2 — two pure-Rust body-codec modules under
crates/mxaccess-rpc, plus a small inline ORPC framing port and a
crate-level type consolidation. Resolves F7+F8 from wave 1.
New modules
- guid.rs (4 tests) — hoisted from objref::Guid; shared by all of
mxaccess-rpc. Resolves F7.
- error.rs — hoisted RpcError union (ShortRead, UnexpectedPacketType,
UnknownPacketType, InvalidFragmentLength, TruncatedBindBody,
InvalidAuthTrailer, MissingAuthValue, Decode). Resolves F8.
- orpc.rs (8 tests) — port of OrpcStructures.cs:1-141. ComVersion,
OrpcThis (32-byte header), OrpcThat (8-byte header),
MInterfacePointer (length-prefixed OBJREF), StdObjRef (40 bytes).
- object_exporter.rs (~530 LoC, 20 tests) — port of
ObjectExporterMessages.cs:1-141. IObjectExporter IID, opnums,
ResolveOxid request encoder + ResolveOxidResult/Failure parsers.
Owned-string protocol labels cleaned up via Cow upgrade rather than
Box::leak (ComDualStringEntry::protocol is now Cow<'static, str>).
- rem_unknown.rs (~340 LoC, 11 tests) — port of RemUnknownMessages.cs.
IRemUnknown IID, RemQueryInterface request/response, RemQiResult.
4-byte NDR pad in REMQIRESULT preserved as pad_after_hresult per
CLAUDE.md unknown-bytes rule.
Test count delta: 277 -> 319 (+42; codec 215 unchanged, mxaccess-rpc
60 -> 102, codec parity 2 unchanged).
Open followups touched: F7 + F8 resolved; F9, F10, F11 added.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>