# Contributing to smb2
Thanks for considering contributing! This document covers the practical stuff you need to know.
## Getting started
```bash
git clone https://github.com/vdavid/smb2
cd smb2
cargo build
cargo test
```
You don't need an SMB server for most development. The test suite uses mock transports for protocol logic.
### Dev tools
We use [`just`](https://github.com/casey/just) as a command runner. Install it, then:
```bash
just # Run all checks: format, lint, test, doc
just fix # Auto-fix formatting and clippy warnings
just check-all # Include MSRV check, security audit, and license check
```
Run `just --list` to see all available commands.
### MSRV (minimum supported Rust version)
We support Rust 1.85. Before submitting PRs, verify MSRV compatibility:
```bash
rustup toolchain install 1.85.0 # One-time setup
just msrv # Check MSRV compatibility
```
This catches issues that only appear on older Rust versions. CI runs this check, so `just msrv` locally saves a round-trip.
## Project structure
```
src/
├── lib.rs # Crate root
├── error.rs # Error types, NTSTATUS mapping
├── pack/ # Binary serialization (ReadCursor, WriteCursor)
├── types/ # Newtypes (SessionId, TreeId, FileId, etc.)
├── msg/ # Wire format message structs
├── transport/ # Transport trait, TCP implementation, mock
├── crypto/ # Signing, encryption, key derivation
├── auth/ # NTLM authentication
└── client/ # High-level API (SmbClient, Tree, Pipeline)
tests/
├── pack_roundtrip.rs # Property-based tests for pack/unpack
├── msg_wire_format.rs # Test messages against known byte sequences
├── protocol_flow.rs # Full protocol flows with mock transport
└── integration.rs # Tests against real Samba server (Docker)
```
## Running tests
```bash
# Unit tests (no server needed)
cargo test
# Integration tests (requires Docker Samba)
cargo test --test integration -- --ignored --nocapture
```
Integration tests need a Samba server running in Docker. See the test file for setup instructions.
## Code style
We follow standard Rust conventions with a few specific choices:
- `#![forbid(unsafe_code)]`: no unsafe code, ever
- `#![warn(missing_docs)]`: doc comments for all public APIs
- Hand-rolled binary serialization (no proc macros for wire format)
- Newtypes for protocol IDs (`SessionId`, `TreeId`, `FileId`, etc.)
- `thiserror` for error types
The quick version:
- Run `just` before committing (or `just fix` to auto-fix issues)
- Tests for new functionality
- Doc comments for public APIs
## Architecture decisions
A few things that might not be obvious:
- **Single crate:** Everything lives in one crate (like mtp-rs). Keeps things simple, avoids cross-crate dependency management.
- **Hand-rolled pack/unpack:** We serialize SMB messages manually with `ReadCursor`/`WriteCursor` instead of using derive macros. Full control, easier to debug protocol issues, and the wire format has too many variable-length fields and padding rules for serde to handle well.
- **`dyn Transport`:** The transport layer uses trait objects (`async_trait`) instead of generics. Simpler API, and the overhead is negligible compared to network I/O.
- **Pipeline as a core feature:** The pipeline isn't an optimization bolted on later. It's the reason this library exists. The credit window, message sequencing, and compounding are all designed around it.
- **Runtime-agnostic:** We don't depend on tokio directly. Use `futures` traits. Tokio is a dev-dependency for running async tests.
## What we're looking for
- Testing with real SMB servers (Windows, Samba, NAS devices) and reporting results
- Bug reports with reproduction steps
- Protocol edge cases (compound failures, credit management, etc.)
- Doc improvements
## What we're not looking for right now
- Server implementation (this is a client library)
- QUIC or RDMA transport
- Kerberos authentication (planned but not yet)
- SMB1 support (deprecated, insecure)
These might come later, but they're not the current focus.
## The protocol
If you need to understand SMB2/3, the spec files in `docs/specs/` are the primary reference. The implementation plan at `docs/specs/implementation-plan.md` has a good overview of the protocol flow and known pitfalls.
The protocol is essentially:
1. Negotiate capabilities (dialect, signing, encryption)
2. Authenticate (NTLM challenge/response)
3. Connect to a share (tree connect)
4. Open files, read/write/list, close files
5. Disconnect
Everything is little-endian on the wire (except the TCP transport framing, which is big-endian). Strings are UTF-16LE.
## Submitting changes
1. Fork and create a branch
2. Make your changes
3. Run `just` (checks format, lint, test, and doc)
4. Run `just msrv` to verify Rust 1.85 compatibility
5. If you have a Samba server, run integration tests
6. Open a PR with a clear description including how you tested your changes
For non-trivial changes, consider opening an issue first to discuss the approach.
## Questions?
Open an issue. Happy to chat!