PasSMB
PasSMB is a protocol-native, read-only SMB2/SMB3 client for Delphi/Pascal
- Active · last change 6-3-2026
- ★ 3 on GitHub
README
PasSMB
PasSMB is a protocol-native, read-only SMB2/SMB3 client for Delphi/Pascal media workloads.
The current MVP focuses on connecting to an SMB share, listing directories, opening files, reading byte ranges, and exposing seekable TStream instances without relying on the host operating system SMB client.
Current Status
Implemented and live-tested:
- SMB 2.1, SMB 3.0, SMB 3.0.2, and SMB 3.1.1 negotiate over direct TCP/445
- SPNEGO-wrapped NTLMv2 authentication
- guest/anonymous login
- username/password login path
- tree connect/disconnect
- directory listing
- file open, query info, range read, close
- file/directory existence probes
- seekable
TSmbFileStream - buffered read stream via
OpenBufferedRead - SMB 2.1 HMAC-SHA256 request signing
- SMB 2.1 HMAC-SHA256 signed response verification
- SMB 3.0/3.0.2 AES-CMAC signing key derivation
- SMB 3.0/3.0.2 AES-CMAC request signing and signed response verification
- SMB 3.1.1 preauth integrity, NTLM exported-session-key handling, and AES-CMAC signing
- SMB 3.0.2 AES-128-CCM and SMB 3.1.1 AES-128-GCM/AES-128-CCM encryption for encrypted shares
- multi-credit read chunk sizing up to
1 MiB, bounded by granted credits and server max-read, with a larger steady credit request window for media reads
The live integration tools are intended to be pointed at your own Samba, Windows Server, NAS, or VM test shares.
Basic Usage
uses
Classes,
SysUtils,
PasSMB.Client,
PasSMB.Types;
var
Client: TSmbClient;
Stream: TStream;
Buffer: array[0..4095] of Byte;
Count: Integer;
begin
Client:=TSmbClient.Create;
try
Client.MinProtocol:=sdSmb210;
Client.MaxProtocol:=sdSmb311;
Client.Connect('fileserver.example.local');
Client.Negotiate;
Client.Login('WORKGROUP', 'username', 'password');
Client.OpenShare('media');
Stream:=Client.OpenBufferedRead('\Movies\sample.bin', SMB_DEFAULT_READ_BUFFER_SIZE);
try
Count:=Stream.Read(Buffer, SizeOf(Buffer));
Writeln('Read bytes: ', Count);
finally
Stream.Free;
end;
finally
Client.Free;
end;
end;Protocol range:
Client.MinProtocol:=sdSmb210;
Client.MaxProtocol:=sdSmb311;The default range is SMB 2.1 through SMB 3.1.1. Lowering MaxProtocol is useful when reproducing server-specific behavior, for example forcing SMB 3.0.2 encryption instead of SMB 3.1.1. The range API is also the intended extension point for future SMB1/CIFS support.
Guest login:
Client.Connect('fileserver.example.local');
Client.Negotiate;
Client.LoginGuest;
Client.OpenShare('media');Directory listing:
var
Entry: TSmbDirectoryEntry;
begin
for Entry in Client.ListDirectory('\Movies') do
Writeln(Entry.Name);
end;Existence probes:
if Client.DirectoryExists('\Movies') then
Writeln('Movies exists');
if Client.FileExists('\Movies\sample.bin') then
Writeln('sample.bin exists');Range read:
var
FileId: TSmbFileId;
Data: TBytes;
begin
FileId:=Client.OpenFileForRead('\Movies\sample.bin');
try
Data:=Client.ReadFileRangeBytes(FileId, 0, 20);
finally
Client.CloseFile(FileId);
end;
end;Read progress events:
procedure TReader.HandleReadProgress(
ASender: TObject;
const AFileId: TSmbFileId;
AOffset: UInt64;
ARequested: UInt32;
AReceived: UInt32;
ATotalReceived: UInt64;
ATotalRequested: UInt64
);
begin
Writeln(Format('Read %d/%d bytes', [ATotalReceived, ATotalRequested]));
end;
Client.OnReadProgress:=Reader.HandleReadProgress;Client trace logs:
procedure TReader.HandleLog(ASender: TObject; ALevel: TSmbLogLevel; const AMessage: string);
begin
Writeln(AMessage);
end;
Client.OnLog:=Reader.HandleLog;SMB status failures are raised as ESmbStatusError with the NTSTATUS name, hex value, and a short description when the status is known.
Build And Test
Build unit tests with Delphi:
./build-wsl-generic.sh Tests/PasSMB.Tests.dpr Win64Run the Win64 test executable from WSL:
/mnt/c/Windows/System32/cmd.exe /c "Bin\\win64\\PasSMB.Tests.exe"Run guest live integration:
./Scripts/run-live-integration.sh fileserver.example.local media "\\Movies\\sample.bin" 20Run large-read integration:
./Scripts/run-large-integration.sh fileserver.example.local media "\\Movies\\large-video.mkv" 16777216Run named-user live integration:
set PASMB_TEST_DOMAIN=WORKGROUP
set PASMB_TEST_USERNAME=username
set PASMB_TEST_PASSWORD=password
Scripts\run-live-user-integration.cmd fileserver.example.local media \Movies\sample.bin 20Run encrypted named-user live smoke against a share that requires SMB encryption:
set PASMB_TEST_DOMAIN=WORKGROUP
set PASMB_TEST_USERNAME=username
set PASMB_TEST_PASSWORD=password
Scripts\run-live-encrypted-user.cmd 0311 fileserver.example.local media \Movies\sample.bin
Scripts\run-live-encrypted-user.cmd 0302 fileserver.example.local media \Movies\sample.binIf PASMB_TEST_USERNAME is not set, the named-user integration skips cleanly.
Use expected size -1 for an auth/list-only smoke when the share does not contain the fixture file:
set PASMB_TEST_USERNAME=username
set PASMB_TEST_PASSWORD=password
Scripts\run-live-user-integration.cmd fileserver.example.local media \ -1Set PASMB_TRACE=1 before running PasSMB.Integration.LargeRead.exe to print client trace logs.
Set PASMB_MIN_PROTOCOL or PASMB_MAX_PROTOCOL to a dialect revision such as 0210, 0302, or 0311 when a live integration needs to force a protocol range.
Run all configured live checks from WSL:
PASMB_PUBLIC_SAMBA_HOST=samba.example.local PASMB_USER_HOST=samba.example.local PASMB_PUBLIC_SHARE=media PASMB_USER_SHARE=media PASMB_SMALL_PATH="\\Movies\\sample.bin" PASMB_SMALL_SIZE=20 ./Scripts/run-all-live.shPASMB_LARGE_PATH is optional; when it is omitted the small public/user/encrypted checks still run and the large encrypted read is skipped. The WSL wrapper forwards the PASMB_* environment variables to cmd.exe, so credentials and protocol overrides can be set on the Bash command line:
PASMB_TEST_USERNAME=username PASMB_TEST_PASSWORD=password PASMB_MAX_PROTOCOL=0302 ./Scripts/run-all-live.shUseful live-test environment variables:
| Variable | Default | Meaning |
|---|---|---|
PASMB_PUBLIC_SAMBA_HOST |
empty | Optional guest/public Samba target |
PASMB_PUBLIC_WINDOWS_HOST |
empty | Optional guest/public Windows target |
PASMB_USER_HOST |
empty | Optional named-user target |
PASMB_PUBLIC_SHARE |
empty | Public share name |
PASMB_USER_SHARE |
empty | Named-user share name |
PASMB_SMALL_PATH |
empty | Small fixture path |
PASMB_SMALL_SIZE |
20 |
Expected small fixture size |
PASMB_LARGE_PATH |
empty | Optional large media fixture path |
PASMB_LARGE_MIN_SIZE |
8388608 |
Minimum accepted large fixture size |
PASMB_MIN_PROTOCOL |
client default | Optional minimum dialect revision |
PASMB_MAX_PROTOCOL |
client default | Optional maximum dialect revision |
PASMB_TEST_DOMAIN |
empty | Optional auth domain |
PASMB_TEST_USERNAME |
empty | Named-user account |
PASMB_TEST_PASSWORD |
empty | Named-user password |
Examples
Examples/00_LiveSmoke/PasSMB.LiveSmoke.dpris the main manual smoke tool. It connects, negotiates, logs in as guest or user, lists the share root, reads a byte range, and opens a buffered stream.Tests/Integration/PasSMB.Integration.LivePublic.dprchecks guest/public directory and file reads.Tests/Integration/PasSMB.Integration.LiveUser.dprchecks named-user login, directory listing, and reads.Tests/Integration/PasSMB.Integration.LargeRead.dprchecks larger sequential media reads and can be forced to a specific dialect withPASMB_MIN_PROTOCOL/PASMB_MAX_PROTOCOL.
Current Limits
- SMB1/CIFS is not implemented yet. It will need a separate wire-protocol implementation, but the
MinProtocol/MaxProtocolAPI is intended to extend cleanly when legacy protocol support is added. - The SMB1/CIFS implementation plan lives in
Docs/SMB1_CIFS_Compatibility_Plan.md. - SMB 3.x AES-128-CCM and SMB 3.1.1 AES-128-GCM encryption are implemented; AES-256-CCM/AES-256-GCM are not implemented yet.
- Reads are still sequential, but each request can now use multiple credits up to
1 MiB. - The client is read-only: no writes, deletes, renames, or directory creation.
- Kerberos, DFS, leases/oplocks, durable handles, multichannel, compression, and compound requests are out of scope for the MVP.

