A modern Rust rewrite of the classic lrzsz file transfer tool. Transfer files over terminal connections using ZMODEM, XMODEM, and YMODEM protocols.
- Single binary — One
zzbinary (449KB static), all commands via symlinks - Drop-in replacement — Works as
rz,sz,rrz,rszvia argv[0] detection - Smart mode —
zz filesends,zzreceives, no separate commands needed - ZMODEM — CRC-16/32, adaptive block sizing, multi-file batch, crash recovery
- XMODEM — 128B/1K blocks, CRC-16 and checksum modes
- YMODEM — Batch transfer with file headers, size tracking
- Secure — Restricted mode by default, path traversal protection, filename sanitization
- Terminal compatible — Works with Xshell, SecureCRT, iTerm2, MobaXterm
- Zero dependencies — Static musl build, runs on any Linux (x86_64, aarch64)
# Unified command (auto-detect mode)
zz file1 file2 # Send files
zz # Receive files
# Traditional commands (symlinks to zz)
sz file1 file2 # Send files
rz # Receive files
# Common options
zz -r file # Resume interrupted transfer
zz -p # Receive: protect existing files (don't overwrite)
zz -E # Receive: rename if file exists (.1, .2, ...)
zz -e file # Escape all control characters
zz -T file # Turbo mode (less escaping, faster)
zz -8 file # Try 8K blocks
zz -q file # Quiet mode
zz -v file # Verbose mode
zz --help # Show all options| Option | Behavior |
|---|---|
| Default (no flags) | Overwrite existing files |
-p / --protect |
Skip existing files with message |
-E / --rename |
Auto-rename (file.1, file.2, ...) |
zz is a single binary that determines its behavior from how it is invoked:
| Invoked as | Mode | Protocol |
|---|---|---|
zz file |
Send | ZMODEM (default), XMODEM, YMODEM |
zz |
Receive | ZMODEM (default), XMODEM, YMODEM |
sz / rsz / lsz |
Send (forced) | ZMODEM |
rz / rrz / lrz |
Receive (forced) | ZMODEM |
sb / rsb |
Send | YMODEM |
rb / rrb |
Receive | YMODEM |
sx / rsx |
Send | XMODEM |
rx / rrx |
Receive | XMODEM |
X/YMODEM are provided for compatibility. Only ZMODEM mode honors the full option set. On X/YMODEM paths:
- XMODEM send:
-k/-8selects 1024-byte blocks; other send options are ignored. - XMODEM receive: the first non-flag argument is the destination filename (XMODEM has no filename in the protocol). Output is padded to the block boundary.
- YMODEM receive:
-qsuppresses thereceived:line;-p/-y/-E/-r/-R/-Uhave no effect (files are always overwritten into the current directory with the filename from block 0).
For full option support, use ZMODEM (the default).
| rzsz | lrzsz | |
|---|---|---|
| Language | Rust | C |
| Binary size (static) | 449 KB | ~200 KB |
| Binary count | 1 (zz) |
2 (lsz + lrz) |
| Unified command | zz (auto-detect) |
No |
| Memory safety | Compile-time guaranteed | Manual |
| Terminal restore | RAII (guaranteed on all exit paths) | Signal handler (fragile) |
| Timeout mechanism | poll() |
alarm()/SIGALRM |
| State management | Struct fields | 50+ global variables |
| Protocol state machine | Explicit enum + match | Implicit goto chains |
| Path traversal protection | Default on | Opt-in |
| Sender | Receiver | Throughput |
|---|---|---|
| C lsz | C lrz | 116 MB/s |
| Rust sz | C lrz | 49 MB/s |
| C lsz | Rust rz | 45 MB/s |
| Rust sz | Rust rz | 29 MB/s |
The Rust version is 2-3x slower in pipe benchmarks due to per-byte escape encoding (vs C's batch
zsendline_s). In real-world SSH/serial transfers, network latency dominates and the difference is imperceptible.
| Terminal | ZMODEM Support | Status |
|---|---|---|
| Xshell | Built-in | Tested |
| SecureCRT | Built-in | Compatible |
| iTerm2 | Configurable trigger | Compatible |
| MobaXterm | Built-in | Compatible |
| Tabby | Plugin | Compatible |
| Windows Terminal | None | Use scp instead |
| PuTTY | None | Use scp instead |
git clone https://github.com/kookob/rzsz.git
cd rzsz
# Development build
cargo build
# Release build
cargo build --release
# Static musl build (no runtime dependencies)
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl
# Run tests
cargo test
bash tests/interop.shsrc/
├── bin/zz.rs # Unified binary: auto-detect send/receive from argv[0]
├── sender.rs # ZMODEM send: handshake, file data, adaptive blocks
├── receiver.rs # ZMODEM receive: ZRINIT negotiation, file write, resume
├── xmodem.rs # XMODEM: 128B/1K blocks, CRC-16/checksum
├── ymodem.rs # YMODEM: batch transfer, file headers
├── zmodem/
│ ├── frame.rs # Frame encoding/decoding (hex/bin16/bin32)
│ ├── session.rs # Protocol state machine, header parsing
│ ├── crc.rs # CRC-16 and CRC-32 lookup tables
│ └── escape.rs # ZDLE escape table
└── serial/
├── mod.rs # ProtocolWriter/StatusWriter (type-safe I/O separation)
├── reader.rs # Buffered modem reader with poll() timeout
└── terminal.rs # TerminalGuard (RAII terminal mode restore)
See PUBLISHING.md for crates.io and GitHub Release instructions.
Apache-2.0
- Original rzsz by Chuck Forsberg (Omen Technology)
- lrzsz maintained by Uwe Ohse
- ZMODEM protocol specification (1988)