Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Catalog · How the catalog works
Anything · Lazarus or Delphi
Random projectRandom project

PasSMB

PasSMB is a protocol-native, read-only SMB2/SMB3 client for Delphi/Pascal

View on GitHub

Member rating

No ratings yet.

Sign in to rate this project.

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

pascal
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:

pascal
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:

pascal
Client.Connect('fileserver.example.local');
Client.Negotiate;
Client.LoginGuest;
Client.OpenShare('media');

Directory listing:

pascal
var
  Entry: TSmbDirectoryEntry;
begin
  for Entry in Client.ListDirectory('\Movies') do
    Writeln(Entry.Name);
end;

Existence probes:

pascal
if Client.DirectoryExists('\Movies') then
  Writeln('Movies exists');

if Client.FileExists('\Movies\sample.bin') then
  Writeln('sample.bin exists');

Range read:

pascal
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:

pascal
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:

pascal
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:

bash
./build-wsl-generic.sh Tests/PasSMB.Tests.dpr Win64

Run the Win64 test executable from WSL:

bash
/mnt/c/Windows/System32/cmd.exe /c "Bin\\win64\\PasSMB.Tests.exe"

Run guest live integration:

bash
./Scripts/run-live-integration.sh fileserver.example.local media "\\Movies\\sample.bin" 20

Run large-read integration:

bash
./Scripts/run-large-integration.sh fileserver.example.local media "\\Movies\\large-video.mkv" 16777216

Run named-user live integration:

bat
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 20

Run encrypted named-user live smoke against a share that requires SMB encryption:

bat
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.bin

If 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:

bat
set PASMB_TEST_USERNAME=username
set PASMB_TEST_PASSWORD=password
Scripts\run-live-user-integration.cmd fileserver.example.local media \ -1

Set 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:

bash
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.sh

PASMB_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:

bash
PASMB_TEST_USERNAME=username PASMB_TEST_PASSWORD=password PASMB_MAX_PROTOCOL=0302 ./Scripts/run-all-live.sh

Useful 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.dpr is 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.dpr checks guest/public directory and file reads.
  • Tests/Integration/PasSMB.Integration.LiveUser.dpr checks named-user login, directory listing, and reads.
  • Tests/Integration/PasSMB.Integration.LargeRead.dpr checks larger sequential media reads and can be forced to a specific dialect with PASMB_MIN_PROTOCOL / PASMB_MAX_PROTOCOL.

Current Limits

  • SMB1/CIFS is not implemented yet. It will need a separate wire-protocol implementation, but the MinProtocol/MaxProtocol API 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.