Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,9 @@ jobs:
echo "Compiling with features: $td_model, ${COMMON_FEATURES}"
cargo build --verbose --no-default-features --features "$td_model,${COMMON_FEATURES}"
done
- name: Doc
run: |
for td_model in async multi_threaded single_threaded; do
echo "Generating documentation with features: $td_model, ${COMMON_FEATURES}"
cargo doc --verbose --no-default-features --features "$td_model,${COMMON_FEATURES}"
done
93 changes: 33 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ This project is the first rust implementation of [SMB2 & 3](https://learn.micros
While most current implementations are mostly bindings to C libraries (such as libsmb2, samba, or windows' own libraries), this project is a full implementation in Rust, with no dependencies on C libraries!

## Getting started
Running the project is as simple as:
Running the project's CLI is as simple as executing:
```sh
cargo run -- --help
```
Check out the `info` and the `copy` sub-commands for more information.

For advanced usage, and crate usage, see the [Advanced Usage](#advanced-usage) section.
## Features
### General
- ✅ SMB 2.X & 3.X support.
- ✅ Async (`tokio`), Multi-threaded, or Single-threaded client.
- ✅ Compression & Encryption support.
Expand All @@ -27,69 +26,43 @@ For advanced usage, and crate usage, see the [Advanced Usage](#advanced-usage) s

You are welcome to see the project's roadmap in the [GitHub Project](https://github.com/users/AvivNaaman/projects/2).

### Feature Flags
| Type | Algorithm | | Feature Name |
| --------------- | ------------------- | --- | ---------------------- |
| Authentication | Kerberos | ✅ | `kerberos` |
| Transport | QUIC | ✅ | `quic` |
| **Signing** | * | | `sign` |
| Signing | HMAC_SHA256 | ✅ | `sign_hmac` |
| Signing | AES-128-GCM | ✅ | `sign_gmac` |
| Signing | AES-128-CCM | ✅ | `sign_cmac` |
| **Encryption** | * | | `encrypt` |
| Encryption | AES-128-CCM | ✅ | `encrypt_aes128ccm` |
| Encryption | AES-128-GCM | ✅ | `encrypt_aes128gcm` |
| Encryption | AES-256-CCM | ✅ | `encrypt_aes256ccm` |
| Encryption | AES-256-GCM | ✅ | `encrypt_aes256gcm` |
| **Compression** | * | | `compress` |
| Compression | LZ4 | ✅ | `compress_lz4` |
| Compression | Pattern_V1 | 🟡 | `compress_pattern_v1`* |
| Compression | LZNT1/LZ77/+Huffman | ❌ | - |

> [!NOTE]
> Some of SMB's suported compression algorithms are missing, since no proper crates are available for them.

## Advanced Usage
### Using the crate
## Using the crate
Check out the `Client` struct, exported from the `smb` crate, to initiate a connection to an SMB server:
```rust
let unc_path = smb::UncPath::from_str(r"\\server\share\\file.txt")?;
let smb = smb::Client::new(smb::ClientConfig::default());
smb.share_connect(&unc_path, "username", "password".to_string()).await?;
```

Opening a file for reading:
```rust
let mut file: smb::File = smb.create_file(&unc_path,
&FileCreateArgs::make_open_existing(
FileAccessMask::new().with_generic_read(true),
)).await.try_into()?;

// .. do some things with the file

file.close().await?; // don't forget to close it!
```rust,no_run
use smb::{Client, ClientConfig, UncPath, FileCreateArgs, FileAccessMask, ReadAt};
use std::str::FromStr;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// instantiate the client
let client = Client::new(ClientConfig::default());

// Connect to a share
let target_path = UncPath::from_str(r"\\server\share").unwrap();
client.share_connect(&target_path, "username", "password".to_string()).await?;

// And open a file on the server
let file_to_open = target_path.with_path("file.txt".to_string());
let file_open_args = FileCreateArgs::make_open_existing(FileAccessMask::new().with_generic_read(true));
let resource = client.create_file(&file_to_open, &file_open_args).await?;

// now, you can do a bunch of operations against `file`, and close it at the end.
let file = resource.unwrap_file();
let mut data: [u8; 1024] = [0; 1024];
file.read_at(&mut data, 0).await?;

// and close
file.close().await?;
Ok(())
}
```

>[!tip]
> Check out `smb-cli`'s commands implementation for more examples of how to use the crate.

### Switch Threading model
The project supports async, multi-threaded, and single-threaded backends. The `async` backend is the default one, but you can enable the other backends by using the following features:
- `async`: Enables the async backend (default)
- `single_threaded`: Enables the single-threaded backend. *Must disable default features.*
- `multi_threaded`: Enables the multi-threaded backend. *Must disable default features.*

For example, to run the CLI using multi-threaded backend, you can run:
```sh
cargo run --no-default-feature --features "multi_threaded,sign,encrypt,compress" -- --help
```
If you're using the crate, you can enable the features in your `Cargo.toml` file:
```toml
[dependencies]
smb = { version = "0.1", features = ["multi_threaded", "sign", "..."], no-default-features = true }
```
Check out the [docs.rs](https://docs.rs/smb/latest/smb/index.html) for more information regarding usage.

## Development
To set up a development environment, you may use any supported rust version.

It is highly recommended to use rust nightly, and install pre-commit hooks (using `pip install pre-commit && pre-commit install`)
* It is highly recommended to use rust nightly, and install pre-commit hooks (using `pip install pre-commit && pre-commit install`)
* Before committing your changes, run `cargo fmt` to format the code, and `cargo clippy` to check for linting issues.
* Run crate tests once you are ready to commit. Read tests' README.md before proceeding!
8 changes: 4 additions & 4 deletions smb-cli/src/copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl CopyFile {
#[maybe_async]
async fn open(
path: &Path,
client: &mut Client,
client: &Client,
cli: &Cli,
cmd: &CopyCmd,
read: bool,
Expand Down Expand Up @@ -125,9 +125,9 @@ pub async fn copy(cmd: &CopyCmd, cli: &Cli) -> Result<(), Box<dyn Error>> {
return Err("Copying between two local files is not supported".into());
}

let mut client = Client::new(cli.make_smb_client_config());
let from = CopyFile::open(&cmd.from, &mut client, cli, cmd, true).await?;
let to = CopyFile::open(&cmd.to, &mut client, cli, cmd, false).await?;
let client = Client::new(cli.make_smb_client_config());
let from = CopyFile::open(&cmd.from, &client, cli, cmd, true).await?;
let to = CopyFile::open(&cmd.to, &client, cli, cmd, false).await?;
from.copy_to(to).await?;

Ok(())
Expand Down
20 changes: 10 additions & 10 deletions smb-cli/src/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub struct InfoCmd {

#[maybe_async]
pub async fn info(cmd: &InfoCmd, cli: &Cli) -> Result<(), Box<dyn Error>> {
let mut client = Client::new(cli.make_smb_client_config());
let client = Client::new(cli.make_smb_client_config());

if cmd.path.share.is_none() || cmd.path.share.as_ref().unwrap().is_empty() {
client
Expand Down Expand Up @@ -90,9 +90,9 @@ pub async fn info(cmd: &InfoCmd, cli: &Cli) -> Result<(), Box<dyn Error>> {
&dir,
&cmd.path,
"*",
&mut IterateParams {
&IterateParams {
display_func: &display_item_info,
client: &mut client,
client: &client,
recursive: cmd.recursive,
},
)
Expand Down Expand Up @@ -144,7 +144,7 @@ fn display_item_info(info: &FileIdBothDirectoryInformation, dir_path: &UncPath)

struct IterateParams<'a> {
display_func: &'a DisplayFunc,
client: &'a mut Client,
client: &'a Client,
recursive: RecursiveMode,
}
struct IteratedItem {
Expand All @@ -157,7 +157,7 @@ async fn iterate_directory(
dir: &Arc<Directory>,
dir_path: &UncPath,
pattern: &str,
params: &mut IterateParams<'_>,
params: &IterateParams<'_>,
) -> smb::Result<()> {
let mut subdirs = VecDeque::new();
subdirs.push_back(IteratedItem {
Expand All @@ -178,10 +178,10 @@ async fn iterate_dir_items(
item: &IteratedItem,
pattern: &str,
subdirs: &mut VecDeque<IteratedItem>,
params: &mut IterateParams<'_>,
params: &IterateParams<'_>,
) -> smb::Result<()> {
let mut info_stream =
Directory::query_directory::<FileIdBothDirectoryInformation>(&item.dir, pattern).await?;
Directory::query::<FileIdBothDirectoryInformation>(&item.dir, pattern).await?;
while let Some(info) = info_stream.next().await {
if let Some(to_push) = handle_iteration_item(&info?, &item.path, params).await {
subdirs.push_back(to_push);
Expand All @@ -195,9 +195,9 @@ fn iterate_dir_items(
item: &IteratedItem,
pattern: &str,
subdirs: &mut VecDeque<IteratedItem>,
params: &mut IterateParams<'_>,
params: &IterateParams<'_>,
) -> smb::Result<()> {
for info in Directory::query_directory::<FileIdBothDirectoryInformation>(&item.dir, pattern)? {
for info in Directory::query::<FileIdBothDirectoryInformation>(&item.dir, pattern)? {
if let Some(to_push) = handle_iteration_item(&info?, &item.path, params) {
subdirs.push_back(to_push);
}
Expand All @@ -209,7 +209,7 @@ fn iterate_dir_items(
async fn handle_iteration_item(
info: &FileIdBothDirectoryInformation,
dir_path: &UncPath,
params: &mut IterateParams<'_>,
params: &IterateParams<'_>,
) -> Option<IteratedItem> {
(params.display_func)(info, dir_path);

Expand Down
2 changes: 1 addition & 1 deletion smb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ serial_test = "3.2"
temp-env = { version = "0.3.6", features = ["async_closure"] }

[features]
default = ["sign", "encrypt", "compress", "quic", "async", "kerberos"]
default = ["sign", "encrypt", "compress", "async", "kerberos"]

# Threading models
async = ["dep:tokio", "dep:futures-core", "dep:tokio-util", "dep:futures-util"]
Expand Down
Loading