Skip to content

Latest commit

 

History

History
503 lines (414 loc) · 33.4 KB

File metadata and controls

503 lines (414 loc) · 33.4 KB

Changelog

All notable changes to smb2 will be documented in this file.

The format is based on keep a changelog, and we use Semantic Versioning 2.0.0.

[Unreleased]

[0.11.0] - 2026-05-21

Added

  • In-process diagnostics. SmbClient::diagnostics() and Connection::diagnostics() return a Diagnostics / ConnectionDiagnostics snapshot of the client's current state — negotiated dialect, credits + in-flight count + next MessageId, signing/encryption/compression status, RTT estimate, DFS cache, the per-connection session, and a MetricsSnapshot of 17 monotonic AtomicU64 counters (requests sent, wire bytes sent/received, the four disjoint routing outcomes — responses_routed_ok, responses_routed_err, responses_late_after_drop, responses_stray — plus status_pending_loops, unsolicited_notifications_received, signature_failures, decrypt_failures, decompress_failures, malformed_frames, session_expired_events, compound_requests_sent, explicit_cancels_sent, requests_returned_err). Counters survive connection teardown; per-connection counters reset on SmbClient::reconnect, client-level counters (reconnects, dfs_referrals_resolved, dfs_cache_hits) survive. A Display impl renders a compact terminal view. See docs/specs/diagnostics-plan.md.
  • Optional serde feature (off by default): Serialize impls on every diagnostics type plus the protocol enums they embed (Dialect, Cipher, SigningAlgorithm, Capabilities, Guid, SessionId, TreeId, MessageId). For consumers building MCP tools, dashboards, or any structured exporter. JSON form is the in-memory shape (newtypes are transparent, Capabilities is the raw u32 bits).
  • examples/diagnostics.rs: connect, run a couple of ops, dump the snapshot via Display — or --json when built with --features serde.

Fixed

  • Stale src/client/CLAUDE.md gotcha about silent frame discard on decrypt/decompress/malformed-header errors. Phase 3 P3.4 fixed this — the receiver task tears down the connection on those paths and fans Err(Disconnected) to every pending waiter. Note now matches the code, and the diagnostics counters above attribute each tear-down to its trigger.

[0.10.0] - 2026-05-19

Changed

  • Breaking: Watcher owns its Connection and Tree instead of borrowing them. The lifetime parameter is gone; Watcher is now 'static. Tree::watch and SmbClient::watch lose their 'a parameter and return Watcher instead of Watcher<'a>. Existing call sites that wrote let mut w = client.watch(&tree, "path", true).await?; keep compiling unchanged — only code that explicitly named the type as Watcher<'_> needs the <'_> removed. The connection clone is cheap (Arc::clone, multiplexes over the same SMB session), so the caller is free to keep using their Connection for other ops while a watcher runs. The previous "use a separate SmbClient to perform operations while watching" workaround is no longer needed.

Fixed

  • CHANGE_NOTIFY consumer-side loss window closed. Watcher::next_events now keeps one CHANGE_NOTIFY request continuously outstanding on the wire by pre-issuing the next request before awaiting the current response. Without this, server-side events that arrived during the consumer's response-processing window were dropped silently by strict servers (older Samba builds, NAS firmware) that don't queue events between consecutive CHANGE_NOTIFY requests. Reproduced in the field as 9 file copies → 4 watcher events delivered. Confirmed against Docker Samba, QNAP TS-464, and Windows-shape servers via tests/integration.rs::nas_accepts_stacked_change_notify and the new unit test in src/client/watcher.rs::loss_window_tests.

Added

  • Connection::dispatch / Connection::dispatch_with_credits (crate-internal): variant of execute that returns once transport.send().await completes, handing back the response oneshot::Receiver for the caller to await separately. Enables pipelining patterns where the next request must be on the wire before the previous one's response is processed.

Notes

  • Server compatibility check: the fix relies on the server accepting two simultaneous CHANGE_NOTIFY requests on the same FileId. MS-SMB2 allows it; Windows, modern Samba (Docker fixtures), and QNAP TS-464 firmware all confirmed-good. Servers that reject would surface Err(Error::Protocol { status: ..., command: ChangeNotify }) on the second next_events() call. If you hit this on a particular NAS, please file an issue with the NTSTATUS — a config-flag fallback to depth-0 (pre-issue off) is straightforward to add but hasn't been needed yet.

[0.9.1] - 2026-05-17

Added

  • New consumer-class fixture smb-consumer-maxreadsize (port 10494, env SMB_CONSUMER_MAXREADSIZE_PORT) with smb2 max read = smb2 max write = 65536. Mirrors the internal-fixture smb-maxreadsize so consumer apps can guard against streaming-fallback regressions (every transfer >64 KB is chunked, exercising the same code path that hung pre-0.9.0) without standing up smb2's internal/ fixtures. Exposed via smb2::testing::maxreadsize_port() and shipped through write_compose_files.

[0.9.0] - 2026-05-17

Changed

  • Breaking: FileWriter owns its Connection and Arc<Tree> instead of borrowing &'a mut Connection. The lifetime parameter is gone; FileWriter is now 'static. Multiple writers built from clones of the same Connection pipeline their WRITEs over one SMB session, multiplexed by MessageId in the receiver task — no external locking needed.
  • Breaking: Tree::create_file_writer signature changed from (&self, conn: &mut Connection, path) to (self: &Arc<Self>, conn: Connection, path). Callers that previously held an owned Tree need Arc::new(tree) once; callers using SmbClient::create_file_writer are unaffected at the call site (the client wraps the cloning internally).
  • Breaking: SmbClient::create_file_writer takes &self instead of &mut self and returns FileWriter (no lifetime) instead of FileWriter<'_>. The previous &mut self requirement was the root cause of a deadlock in the cmdr SMB volume's write_from_stream under sustained concurrent pressure on QNAP — the consumer had to hold its session mutex for the entire streaming upload.
  • Breaking: Tree is now #[derive(Clone)]. Lets convenience constructors wrap a Tree in Arc without reconstructing field-by-field. Cloning a Tree is cheap (one TreeId, three short Strings, two bools).

Added

  • client::stream::open_file_writer(tree: Arc<Tree>, conn: Connection, path: &str) -> Result<FileWriter> — free function for building a FileWriter when you already hold a cloned Connection and an Arc<Tree> and don't want the convenience-wrapper indirection. Tree::create_file_writer and SmbClient::create_file_writer both delegate to it.

Notes

  • Internal callers in Tree::write_file_pipelined and Tree::write_file_streamed were not touched: they use their own per-chunk execute_with_credits loop and never built a FileWriter.

[0.8.0] - 2026-05-08

Changed

  • Breaking: ErrorKind is now #[non_exhaustive]. Match expressions over ErrorKind must include a _ arm. Adding a new variant in a future release is now a non-breaking change. Consumers that already use a _ fallback (the pattern recommended by the doc example) need no update.

Added

  • ErrorKind::AlreadyExistsSTATUS_OBJECT_NAME_COLLISION (returned by Create when a file or directory of the same name exists) now classifies as AlreadyExists instead of falling through to Other. Lets callers handle "merge into existing directory" or surface a friendly "name taken" message without substring-matching the error text.
  • ErrorKind::IsADirectorySTATUS_FILE_IS_A_DIRECTORY now classifies as IsADirectory. Useful for delete-fast-path callers that try delete_file first and fall back to delete_directory on this kind.
  • ErrorKind::NotADirectorySTATUS_NOT_A_DIRECTORY now classifies as NotADirectory (e.g., list_directory on a file).
  • error::tests::classify_status_contract: a single table-driven test that documents the full NtStatus → ErrorKind mapping, including statuses intentionally left as Other. Replaces the per-arm classification tests so the contract lives in one auditable place; adding a new NtStatus should also add a row here.

Notes

  • STATUS_OBJECT_NAME_INVALID, STATUS_DELETE_PENDING, STATUS_INSUFFICIENT_RESOURCES, and STATUS_INSUFF_SERVER_RESOURCES deliberately still classify as ErrorKind::Other — no current consumer needs to branch on them. Promoting any of these to its own variant is non-breaking under the #[non_exhaustive] policy.

[0.7.2] - 2026-04-21

Fixed

  • DFS referral response parser (msg::dfs::RespGetDfsReferral::unpack): a V2 entry whose server-declared entry_size was small enough to pass the initial entry_size < 4 guard but short enough to truncate the fixed body would trigger a panic (out-of-bounds) on the final network_address_offset read. The V2 body is 18 bytes after the 4-byte version/size prefix, not 16. Found by fuzzing; regression covered by msg::dfs::tests::resp_parse_v2_short_entry_returns_clean_error.

Added

  • fuzz/ crate using cargo-fuzz with 12 fuzz targets across the parse entry points: top-level SMB2 header, TRANSFORM/COMPRESSION transform headers, compound-frame splitter, full frame + sub-frame body parse, Negotiate request/response (negotiate contexts), Create request/response (create contexts), QueryInfo response, and DFS referral response. Seed corpus generator at tests/fuzz_seeds.rs (run with cargo test --test fuzz_seeds -- --ignored). Targets are feature-gated behind fuzzing; the feature exposes parse entry points via smb2::fuzzing and is not meant for application use.
  • .github/workflows/fuzz.yml: weekly 5-minute-per-target fuzz run on schedule + manual dispatch.

[0.7.1] - 2026-04-21

Added

  • Tree::download for streaming reads driven by a borrowed Connection, mirroring the existing SmbClient::download but unlocking concurrent downloads on a single SMB session (via Connection::clone).

Changed

  • Tree::open_file and FileDownload::new are now pub (were pub(crate)), so callers can build custom chunk loops or reuse a handle across multiple readers.
  • SmbClient::download now delegates to Tree::download — zero behavior change.

[0.7.0] - 2026-04-21

First public release on crates.io. Bundles the FileWriter streaming write API with the Phase 3 Connection actor refactor (breaking API change).

Changed

  • Breaking: Connection's send/receive API collapsed into execute / execute_compound (Phase 3 of the actor refactor). The legacy send_request, send_request_with_credits, send_compound, receive_response, receive_compound, and receive_compound_expected are gone; callers use a single awaitable call per op. Connection::execute(command, body, tree_id) returns Result<Frame> where Frame = { header, body, raw }. Connection::execute_compound(&[CompoundOp]) returns Result<Vec<Result<Frame>>> — the outer Result is "did the compound hit the wire", the inner one is per-sub-op so partial-failure handling (for example, CREATE ok, READ fails, issue standalone CLOSE with the returned FileId) is straightforward. Connection: Clone; clones share the receiver task so concurrent execute calls from different tasks/clones multiplex over the same SMB session. Cancelling (dropping) a future mid-flight is safe by construction — the matching late-arriving frame is discarded.
  • Connection now uses a background receiver task with per-request oneshot::Sender routing (Phase 2 of the actor refactor). See docs/specs/connection-actor.md. A caller's dropped future (for example, tokio::task::JoinHandle::abort() in a downstream consumer) now correctly discards the corresponding late-arriving response instead of letting it pollute the next operation's receive. Credits still tick on dropped-caller frames so throughput stays correct under cancellation churn.
  • tokio is formalized as a hard runtime requirement; added "rt" to the tokio feature set. The library spawns a receiver task per Connection.
  • MockTransport::receive() now awaits via tokio::sync::Notify when the queue is empty instead of returning Err(Disconnected) immediately. Added MockTransport::close() to signal end-of-stream. This lets the background receiver task stay alive across a test's interleaved queue_response calls. External consumers writing tests with MockTransport directly need to call close() to get the old behavior.
  • MockTransport::enable_auto_rewrite_msg_id() — opt-in test-mode shim that rewrites each zero-msg_id sub-frame of a queued response to match the next pending sent msg_id in FIFO order, so canned build_*_response helpers that hardcode MessageId(0) route through the Phase 3 receiver task without needing to predict the caller's allocated msg_ids. Replaces the pre-Phase-3 Connection::set_orphan_filter_enabled(false) escape hatch.

Migration guide (pre-Phase-3 → Phase 3)

Single request:

// Before
conn.send_request(Command::Create, &req, Some(tree_id)).await?;
let (header, body, _raw) = conn.receive_response().await?;
// After
let frame = conn.execute(Command::Create, &req, Some(tree_id)).await?;
// frame.header, frame.body, frame.raw

Compound request:

// Before
let ops = vec![(Command::Create, &create_req, CreditCharge(1)), /*...*/];
conn.send_compound(tree_id, &ops).await?;
let responses = conn.receive_compound_expected(3).await?;
// responses: Vec<(Header, Vec<u8>)>
// After
let ops = [
    CompoundOp { command: Command::Create, body: &create_req, tree_id: Some(tree_id), credit_charge: CreditCharge(1) },
    // ...
];
let frames = conn.execute_compound(&ops).await?;
// frames: Vec<Result<Frame>> — each entry may independently be Err (session expired, signature
// verify failure, etc.). Sub-op status codes (OBJECT_NAME_NOT_FOUND and friends) ride in
// frames[i].as_ref()?.header.status, NOT in the inner Result.

Pipelined sliding-window reads/writes: use futures_util::stream::FuturesUnordered of boxed execute_with_credits futures across conn.clone()s — see src/client/tree.rs::read_pipelined_loop for the canonical pattern. MAX_PIPELINE_WINDOW and conn.credits()-based pacing stay on the caller side.

Added

  • FileWriter — push-based streaming write API with pipelined I/O. Consumer drives the loop, pushing chunks via write_chunk() with automatic backpressure (sliding window, credit-aware). Complement to FileDownload for reads. Created via SmbClient::create_file_writer().
  • FileWriter::abort() — fast-cancel companion to finish(). Discards any unsent data, drains in-flight WRITE responses to keep credits and message IDs in sync, skips the server-side FLUSH (fsync), and does a best-effort CLOSE. Errors during drain/close are swallowed so callers can exit quickly. Use on cancellation or error paths where the partial remote file is going to be deleted anyway — it saves the fsync round-trip vs. calling finish(). The caller is responsible for deleting the partial file.
  • Connection::receive_compound_expected(n) — gathers exactly n compound sub-responses, transparently reading additional transport frames when the server splits the chain. All compound-using methods (read_file_compound, write_file_compound, fs_info, stat, rename, delete_file/delete_directory, and the batch delete_files/rename_files/stat_files) now use it.
  • 13 new Docker integration tests for FileWriter: basic, large (5 MB), empty, single byte, overwrite, equivalence with write_file_pipelined, binary data integrity, 64 KB max-write-size, signing, encryption, read-only rejection, 100 MB stress (guest), 100 MB stress (200ms latency)
  • 10 new unit tests for FileWriter pipelining, backpressure, chunk splitting, error handling

Fixed

  • Unrecoverable frame errors (decrypt auth-tag mismatch, decompression failure, malformed sub-frame header after compound splitting) no longer hang pending waiters indefinitely. Previously the receiver task log-at-WARN'd and continued, which left any waiter matching the discarded frame's MessageId stuck on rx.await forever — the msg_id isn't recoverable from an unparseable frame, so no targeted error could be delivered. Per decision E6 in the Phase 3 design, the receiver task now tears the connection down on these conditions: fans Err(Disconnected) to every pending waiter and exits. Callers see the error promptly and can reconnect. Sub-frame signature-verification failures and STATUS_NETWORK_SESSION_EXPIRED stay targeted (delivered only to the matching waiter) because the msg_id is known.
  • Caller futures that get dropped mid-flight (for example, a cancelled listing task in a consuming application) no longer leave in-flight SMB requests on the wire that corrupt subsequent operations. The receiver task discards late-arriving frames whose waiter's oneshot::Receiver has been dropped. Reproduces and fixes the cmdr listing_task.abort() regression.
  • Compound requests no longer error with invalid_data: expected N compound responses, got M when the server sends responses as separate frames instead of one compounded frame. Per MS-SMB2 3.3.4.1.3 the server SHOULD compound but MAY split, and Samba (including QNAP NAS firmware built on Samba) splits in some scenarios. Hit in the wild via fs_info against a QNAP NAS.

[0.6.0] - 2026-04-15

Added

  • write_file_streamed — write files from a streaming callback source with pipelined I/O, bounded memory usage (sliding window, not full file), automatic chunk splitting at MaxWriteSize, works with signing and encryption (f5ade78)
  • 14 new tests: 3 unit (basic, empty, callback error), 9 Docker integration (guest, small, large 10 MB, empty, early stop, 64 KB max-write-size, mandatory signing, mandatory encryption, read-only rejection), 2 NAS integration (write + verify, performance comparison vs write_file_pipelined)

Fixed

  • Bumped rand 0.9.2 → 0.9.4 (RUSTSEC-2026-0097)

[0.5.0] - 2026-04-10

Added

  • DFS (Distributed File System) support — resolve \\domain\dfs-namespace\path transparently, follow referrals across servers, multi-target failover, TTL-based referral cache (d353490, bfd8557, 03c4c2a, 87a7d78)
  • Compound delete (1 RTT instead of 2), compound rename (1 RTT instead of 3), compound stat (1 RTT instead of 4) (33938591, 5e0f7a5, 4dc8fb8)
  • read_file_with_progress for pipelined reads with progress callback and cancellation (37e3370)
  • Batch operations — delete_files, rename_files, stat_files send all compound requests before waiting for responses, partial failures are independent (afe4395)
  • DFS wire format types: ReqGetDfsReferral, RespGetDfsReferral with V2/V3/V4 referral entries (e9e5bf9)
  • Auto-set SMB2_FLAGS_DFS_OPERATIONS based on tree capabilities (f254e96)
  • Connection pool for DFS cross-server routing, ClientConfig.dfs_enabled and dfs_target_overrides (03c4c2a, edcf730)
  • Docker DFS test containers (smb-dfs-root:10456, smb-dfs-target:10457) with 4 integration tests (edcf730)

Fixed

  • IOCTL InputOffset double-counted Header::SIZE, causing STATUS_INVALID_PARAMETER on DFS referral requests (edcf730)
  • DFS paths missing server\share prefix in Tree::format_path (edcf730)
  • Cross-server routing matched hostname-only instead of addr:port (edcf730)

[0.4.0] - 2026-04-09

Added

  • Kerberos authentication — full AS + TGS + AP-REQ flow with pre-auth, tested end-to-end against Windows Server 2022 with Active Directory Domain Services (9b40b00)
  • Kerberos credential cache (ccache) support — read MIT Kerberos ccache files (v3 and v4) for password-less auth from kinit tickets, Session::setup_kerberos_from_ccache() (2344f15)
  • AP-REP mutual authentication — server sub-session key extraction for cryptographic server identity proof (b966d2c)
  • SPNEGO token wrapping — hand-rolled ASN.1/DER encoding for NegTokenInit/NegTokenResp (RFC 4178 / MS-SPNG), no external ASN.1 dependency (c27c88f)
  • Kerberos crypto: AES-CTS (RFC 3962), RC4-HMAC (RFC 4757), PBKDF2 string-to-key, n-fold, HMAC-SHA1-96 checksums (e23b851)
  • Kerberos ASN.1 messages: AS-REQ, TGS-REQ, AP-REQ, Authenticator, KDC-REP parsing — all hand-rolled DER (97b57a5)
  • KDC client: UDP primary with TCP fallback on KRB_ERR_RESPONSE_TOO_BIG, exponential backoff retries (97b57a5)
  • Session::setup_kerberos() and Session::setup_kerberos_from_ccache() public API (3a63337, 2344f15)
  • Support for AES-256, AES-128, and RC4-HMAC encryption types, with AES-256 preferred (01ad252)

Fixed

  • KDC-REP field tags: pvno is [0] not [1] per RFC 4120 (3a63337)
  • AES-CTS key derivation: Ki constant is 0x55 (encrypt/decrypt integrity), not 0x99 (standalone checksum) (661a245)
  • TGS-REQ AP-REQ Authenticator was missing body checksum over KDC-REQ-BODY (RFC 4120 section 7.2.2, key usage 6) (661a245)

Key implementation details

Hard-won lessons from testing against Windows AD (documented in src/auth/CLAUDE.md):

  • MS Kerberos OID (1.2.840.48018.1.2.2) required as primary SPNEGO mechanism for Windows
  • Key usage 11 (not 7) for AP-REQ Authenticator encryption in SPNEGO exchanges
  • GSS-API wrapping of AP-REQ inside SPNEGO mechToken
  • Raw ticket byte pass-through (re-encoding corrupts the encrypted ticket)
  • Session key etype detection from TGS-REP (may differ from ticket encryption type)

[0.3.0] - 2026-04-08

Added

  • Compound requests — CREATE+READ+CLOSE (3-way) and CREATE+WRITE+FLUSH+CLOSE (4-way) as single transport frames, reducing round-trips from 3-4 to 1 per file operation (a9293b6, cb022bc)
  • read_file() and write_file() auto-select compound (small) or pipelined (large) — callers don't choose (25b2f68, cb022bc)
  • Streaming upload with FileUpload — compound for small files, chunked for large, same caller API either way (8b2283a)
  • Streaming download with FileDownload, progress reporting with ControlFlow-based cancellation (c11f5f3)
  • Sliding window pipeline — each response immediately triggers the next chunk, keeping the TCP pipe full (dd36181)
  • SMB 3.x encryption wired into client layer — TRANSFORM_HEADER wrapping, sign-then-compress-then-encrypt send path, AES-128/256-CCM/GCM (101d22d)
  • LZ4 compression wired into connection layer — negotiated during handshake, applied per-message when it reduces size (e2921f9)
  • File watching via CHANGE_NOTIFYWatcher struct with next_events() long-poll, recursive watching support (75b281d)
  • Disk space query (fs_info) via compound CREATE+QUERY_INFO+CLOSE (6d3a05c)
  • ErrorKind for high-level error classification — NotFound, AccessDenied, ConnectionLost, etc. instead of raw NTSTATUS codes (58aead2)
  • Oplock break notification handling — detected by MessageId 0xFFFF..., logged and skipped without crashing (371b984)
  • STATUS_BUFFER_OVERFLOW accepted as partial success in QueryInfo responses (4122d16)
  • CANCEL requests and session expiry detection with Error::SessionExpired (4924a2a)
  • Docker test infrastructure — 12 Samba containers covering guest, auth, signing, readonly, ancient (SMB1), flaky, slow, encryption (GCM + CCM), 50 shares, max read size, with 43 integration tests (8edf837, 7ee088f, 812ad39)
  • Docker tests in CI — builds containers and runs 43 tests on every PR (488e1d0)
  • 7 runnable examples: list_shares, list_directory, read_file, streaming_download, disk_space, watch_directory, write_file (7203bdb)
  • Send + Sync bounds on Pack trait for async callers (4538205)
  • GitHub Actions CI: fmt, clippy, test, doc, MSRV 1.85 (f0b00cd)

Fixed

  • STATUS_PENDING handling — receive_response() now loops past interim responses instead of treating them as errors (8edf837)
  • Multi-credit charges — streaming download/upload and write_file_with_progress were sending credit_charge=1 for MB-sized payloads, Samba rejects with STATUS_INVALID_PARAMETER (8edf837)
  • Cipher fallback — fall back to AES-128-CCM when server omits encryption negotiate context (812ad39)

Improved

  • Smart read selection — sequential for files < MaxReadSize (1 RTT), pipelined only when beneficial (3f0cd77)
  • Credit request bumped from 32 to 256 per request, credits grow rapidly (dd36181)
  • Pipeline uses server-negotiated MaxReadSize/MaxWriteSize with correct multi-credit CreditCharge (b0cacdd)
  • trivial_message! and nt_status_codes! macros to reduce boilerplate (fb4b9e4)
  • CLAUDE.md files for all 8 modules, agent docs colocated with code (2d884d0)

[0.2.0] - 2026-04-08

Added

  • Concurrent pipelined read and write — send multiple READ/WRITE requests by filling the credit window, reassemble responses in offset order, handles out-of-order responses (7f3068a)
  • Share enumeration — IPC$ + srvsvc RPC flow, QNAP returns 8 disk shares, Pi returns 1 (cbed0ab)
  • SmbClient high-level API — connect(), list_shares(), connect_share(), reconnect(), stored credentials (cbed0ab)
  • Convenience smb2::client::connect("host:445", "user", "pass") one-liner (cbed0ab)
  • File operations: write_file, delete_file, stat, rename, create_directory, delete_directory (c80f126)
  • Clean re-exports from lib.rsuse smb2::{SmbClient, Tree} instead of reaching into submodules (cdb203e)
  • 4 runnable examples: list_shares, list_directory, read_file, write_file (cdb203e)
  • Benchmarks against native macOS SMB and smb crate: small files 2-3x faster, medium files match native on upload, 8.5x faster than smb on download (031d52b, 4cbc961)
  • Integration tests against QNAP NAS (SMB 3.1.1, NTLM, AES-GMAC) and Raspberry Pi 4 (SMB 3.1.1, guest) (c80f126)

Fixed

  • Preauth hash: exclude final SESSION_SETUP success response from hash — including it produces wrong keys for SMB 3.1.1 (32f0f30)
  • QueryDirectory: cap OutputBufferLength to 65536, always send "*" pattern (empty filename rejected by QNAP + Samba) (b9d49f7)
  • GMAC uses AES-128-GCM (16-byte key), not AES-256-GCM (dc91351)
  • GMAC nonce needs server role bit for response verification (dc91351)
  • Only verify signature when SMB2_FLAGS_SIGNED is set (skip STATUS_PENDING and oplock breaks) (dc91351)

[0.1.0] - 2026-04-07

Added

  • SMB 2.0.2, 2.1, 3.0, 3.0.2, 3.1.1 dialect support — negotiate with all five dialects, preauth integrity, encryption, compression, and signing negotiate contexts (c3a2e43, 36428728)
  • NTLM authentication (NTLMv2 with MIC) — full NEGOTIATE/CHALLENGE/AUTHENTICATE flow, session key exchange, known-answer test vectors from MS-NLMP section 4.2.4 (60c4163)
  • Wire format pack/unpack for all 19 SMB2 commands — header, negotiate, session setup, tree connect, create, close, read, write, flush, lock, ioctl, query directory, change notify, query info, set info, echo, cancel, logoff, oplock break, transform header, compression header (c3a2e43)
  • Binary serialization primitives — ReadCursor/WriteCursor with LE primitives, UTF-16LE, alignment, backpatching, Pack/Unpack traits, Guid with mixed-endian layout, FileTime with Windows epoch conversion (8be0549)
  • Newtypes for all protocol IDs: SessionId, MessageId, TreeId, CreditCharge, FileId with sentinel constants (8be0549)
  • TCP transport with split send/receive traits (avoids deadlock in pipeline's select! loop), correct framing (0x00 + 3-byte BE length), 16 MB max frame, Nagle disabled (5ae8027)
  • MockTransport for TDD — FIFO response queue, message recording, assertion helpers (5ae8027)
  • SMB 3.x signing: HMAC-SHA256 (2.0.2/2.1), AES-128-CMAC (3.0/3.0.2), AES-256-GMAC (3.1.1) (a7080f3)
  • SMB 3.x encryption: AES-128/256-CCM and AES-128/256-GCM with monotonic nonce generator (a7080f3)
  • SP800-108 key derivation for SMB 3.0/3.0.2 (legacy labels) and 3.1.1 (preauth hash context) (a7080f3)
  • LZ4 compression via lz4_flex (pure Rust, zero C deps) (a7080f3)
  • Connection layer: negotiate, credit management, message ID sequencing, preauth hash tracking, signing integration (36428728)
  • Session layer: multi-round-trip SESSION_SETUP with NTLM, key derivation per dialect, signing activation (36428728)
  • Tree layer: TREE_CONNECT with UNC path, directory listing, file reading (36428728)
  • RPC module: DCE/RPC PDU encoding, NDR encoding for NetShareEnumAll (36428728)
  • Structured logging via log crate — info for lifecycle, debug for protocol, trace for bytes, never logs secrets (1d7273a)
  • Error types with is_retryable(), status(), Auth, Timeout, Disconnected, SessionExpired variants (5ae8027)
  • MAX_UNPACK_BUFFER (16 MB) allocation cap to prevent OOM from malicious packets (073452c)
  • 512 unit tests, 10 integration tests against real hardware, zero clippy warnings