[M2] mxaccess-rpc: OXID + RemQI body codecs (wave 2)

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>
This commit is contained in:
Joseph Doherty
2026-05-05 07:14:29 -04:00
parent 95bd218183
commit 30138629d3
9 changed files with 1894 additions and 142 deletions
+77
View File
@@ -0,0 +1,77 @@
//! Crate-level RPC error type.
//!
//! Hoisted from per-module enums in M2 wave 2 — see `design/followups.md` F8.
//! Every parser/encoder in `mxaccess-rpc` returns this single shared
//! [`RpcError`] so consumers can match on one error surface across PDU
//! decode, OBJREF parse, ORPC `ResolveOxid` body decode, and
//! `IRemUnknown::RemQueryInterface` response decode.
//!
//! Variants here are the union of what M1 wave 1 defined locally in
//! `pdu.rs` and `objref.rs` (`design/followups.md` F8 source list),
//! plus a generic [`RpcError::Decode`] for one-off conditions wave 2's
//! ORPC parsers need (referent-id mismatches, conformant-array max-count
//! underflow, NDR alignment overrun) without growing the enum further.
use thiserror::Error;
/// Errors raised by any codec under `mxaccess-rpc`.
#[derive(Debug, Error, PartialEq, Eq)]
#[non_exhaustive]
pub enum RpcError {
/// Buffer was shorter than required to decode the type.
#[error("short read: expected {expected} bytes, got {actual}")]
ShortRead { expected: usize, actual: usize },
/// Packet type byte at offset 2 (`DceRpcPdu.cs:52`) did not match the
/// expected `DceRpcPacketType` for the parser invoked.
#[error("unexpected packet type {actual}, expected {expected}")]
UnexpectedPacketType { expected: u8, actual: u8 },
/// Packet type byte was not a known [`crate::pdu::PacketType`] value.
#[error("unknown packet type byte {0}")]
UnknownPacketType(u8),
/// `header.frag_length` is inconsistent with the supplied buffer or
/// `auth_length` (`DceRpcPdu.cs:94,150,188,226,101-104,156-159,195-198`).
#[error(
"fragment length {frag_length} inconsistent with buffer length {buffer_len} \
(auth_length={auth_length})"
)]
InvalidFragmentLength {
frag_length: usize,
buffer_len: usize,
auth_length: usize,
},
/// A bind PDU's per-context list ran past `frag_length`
/// (`DceRpcPdu.cs:237`) or a syntax identifier was truncated
/// (`DceRpcPdu.cs:354`).
#[error("truncated bind body at offset {offset}; need {need} bytes, frag_length={frag_length}")]
TruncatedBindBody {
offset: usize,
need: usize,
frag_length: usize,
},
/// Auth-trailer offset is below the 16-byte header
/// (`DceRpcPdu.cs:341-345`).
#[error("invalid auth trailer offset {offset}")]
InvalidAuthTrailer { offset: usize },
/// Tried to extract an auth value from a PDU whose `auth_length` is 0
/// (`DceRpcPdu.cs:336-339`).
#[error("PDU has no auth value")]
MissingAuthValue,
/// Generic decode failure with a position and reason. Used by ORPC
/// body decoders for one-off conditions that don't justify a typed
/// variant (e.g. NDR conformant-array max-count underflow per
/// `ObjectExporterMessages.cs:66-69`, referent-id of zero with no
/// trailing status per `:57-61`, NDR alignment overrun, etc.).
#[error("decode at offset {offset} ({reason}); buffer len {buffer_len}")]
Decode {
offset: usize,
reason: &'static str,
buffer_len: usize,
},
}