Skip to content

Conversation

@jahales
Copy link

@jahales jahales commented Nov 25, 2025

PR: Add DFS Client Support

Summary

This PR adds opt-in Distributed File System (DFS) client support to SMBLibrary, enabling automatic resolution of DFS paths to their underlying file server targets. DFS remains disabled by default to maintain full backward compatibility.

Motivation

DFS is widely used in enterprise environments to provide:

  • Unified namespace across multiple file servers
  • Transparent failover and load balancing
  • Site-aware target selection

Without DFS support, clients must manually resolve DFS paths before connecting.

Changes

Core DFS Client Implementation (SMBLibrary/Client/DFS/)

Component Description
DfsClientFactory Entry point for creating DFS-aware file stores
DfsClientOptions Configuration (enabled, caching TTLs, retries, site name)
DfsPathResolver Implements DFS path resolution algorithm per MS-DFSC
DfsAwareClientAdapter Wraps ISMBFileStore with transparent DFS resolution
DfsSessionManager Manages cross-server sessions for interlink scenarios
ReferralCache / DomainCache Caching infrastructure with TTL support
Smb2DfsReferralTransport Issues FSCTL_DFS_GET_REFERRALS via SMB2

DFS Protocol Types (SMBLibrary/DFS/)

  • ResponseGetDfsReferral — Extended to fully parse referral responses (v1–v4)
  • RequestGetDfsReferralEx — Support for FSCTL_DFS_GET_REFERRALS_EX with site hints
  • DfsReferralEntryV1V4 — Referral entry structures per MS-DFSC
  • DfsReferralHeaderFlags, DfsReferralEntryFlags, DfsServerType — Enums

Tests (SMBLibrary.Tests/)

  • 36 new test files covering:
    • Unit tests for all DFS components
    • Referral parsing tests with protocol vectors
    • Integration tests for end-to-end resolution
    • Lab tests for real DFS namespace validation

Documentation (docs/)

Document Description
dfs-usage.md DFS client configuration guide with examples
codebase-patterns.md Architecture and code style reference
lab-setup.md SMB/DFS test lab options (local, Windows, Samba)
lab-setup-hyperv.md Step-by-step Hyper-V Windows Server lab

Test Scripts (scripts/)

  • test-smb-client.ps1 — Basic SMB2 client validation
  • test-smb-client-full.ps1 — Comprehensive read/write/echo test
  • test-smb1.ps1 — SMB1 legacy protocol test

Other Changes

  • ClientExamples.md — Added DFS usage example
  • SMB2FileStore.cs — Minor adjustment for DFS compatibility

Usage Example

// Connect normally
SMB2Client client = new SMB2Client();
client.Connect("server.domain.com", SMBTransportType.DirectTCPTransport);
client.Login("DOMAIN", "username", "password");

// Get base file store
ISMBFileStore fileStore = client.TreeConnect("DfsShare", out status);

// Wrap with DFS support (opt-in)
DfsClientOptions options = new DfsClientOptions { Enabled = true };
ISMBFileStore dfsStore = DfsClientFactory.CreateDfsAwareFileStore(fileStore, null, options);

// DFS paths are now automatically resolved
dfsStore.CreateFile(out handle, out fileStatus, @"\DfsLink\file.txt", ...);

Backward Compatibility

This PR has zero impact on existing code. DFS support is entirely opt-in:

  • Disabled by default: DfsClientOptions.Enabled defaults to false
  • No changes to existing APIs: SMB2Client, SMB1Client, ISMBFileStore, and all existing interfaces remain unchanged
  • No new dependencies: Uses only existing SMBLibrary infrastructure
  • No behavioral changes: Unless you explicitly create a DFS-aware store via DfsClientFactory, all existing code paths are untouched
  • All existing tests pass: No regressions

What happens if you don't use DFS?

Nothing changes. Existing code like this continues to work exactly as before:

// This code is completely unaffected by this PR
SMB2Client client = new SMB2Client();
client.Connect(serverIP, SMBTransportType.DirectTCPTransport);
client.Login(domain, username, password);
ISMBFileStore store = client.TreeConnect("Share", out status);
// store behaves exactly as it always has — no DFS involvement

DFS is only activated when you explicitly opt in:

// Only this pattern enables DFS
DfsClientOptions options = new DfsClientOptions { Enabled = true };
ISMBFileStore dfsStore = DfsClientFactory.CreateDfsAwareFileStore(baseStore, null, options);

Design Principles

  1. Opt-in by defaultDfsClientOptions.Enabled = false preserves existing behavior
  2. No breaking changes — Existing APIs unchanged; DFS is additive
  3. Per-connection configuration — No global/static state
  4. Spec compliance — Implements MS-DFSC referral protocol
  5. Testable — Interfaces allow mocking; extensive test coverage

Testing

  • All existing tests pass
  • New tests cover DFS parsing, resolution, caching, and integration
  • Validated against Windows Server DFS Namespaces in lab environment

References

…ution

- Add DFS referral structures (V1-V4) and response codecs

- Implement DFS path resolver with caching support

- Add DFS-aware client adapter and file store factory

- Implement IOCTL request builder for DFS referrals

- Add comprehensive unit and integration tests

- Include client usage examples and documentation
- docs/codebase-patterns.md: Architecture and code style guide

- docs/dfs-usage.md: DFS client configuration and troubleshooting

- docs/lab-setup.md: General SMB/DFS lab setup options

- docs/lab-setup-hyperv.md: Detailed Hyper-V Windows Server lab walkthrough

- scripts/test-smb-client.ps1: Basic SMB2 client test

- scripts/test-smb-client-full.ps1: Comprehensive read/write/echo test

- scripts/test-smb1.ps1: SMB1 legacy protocol test

- ClientExamples.md: Add reference to DFS usage guide
@jahales
Copy link
Author

jahales commented Nov 25, 2025

Should be fully backwards-compatible / not break existing code. I tested what I could in a local environment, but I no longer have access to an enterprise environment.

Note that this code was written with the help of LLMs--if that's a concern, I can just keep this in a fork.

I am willing to be the maintainer for the DFS logic.

@TalAloni
Copy link
Owner

Hi Jacob,
What a nice surprise,
This is, by far, the largest contribution to this project.
Before I start looking under the hood, please see that you agree to the contribution terms.

I see that in addition to the code (and unit-tests) you also contributed documentation and PowerShell tests scripts that need to be run manually.
Any documentation regarding DFS should be much more succinct, anybody wishing to set up a DFS environment should be able to find that information elsewhere IMO.
I'm not a fan of adding PowerShell to my repo, all code should be in C# for uniformity and simplicity.

Thanks!
Tal

@jahales
Copy link
Author

jahales commented Nov 26, 2025

Hey Tal,

Happy to give a little back to the open source community. I agree to the contribution terms. I removed the scripts and trimmed down the documentation. When I get time I'll try to make a better lab setup and do some cross-platform testing.

Thanks,
Jacob

@TalAloni
Copy link
Owner

Thanks Jacob,
I took an initial peek and I'm intrigued:

  1. I saw that you made changes to SMB2FileStore but it's not clear why it was mandatory to make that change, DeviceIOControl expects "object handle" which is then casted to FileId, I get that it's cleaner to have a method that accepts FileId, but IIUC you could have easily passed a FileId instance to the existing method and avoided this change.
    BTW: I opted to use object instead of FileId to hide internal implementation and to keep API uniformity with SMB1FileStore.

  2. The more intriguing part is that (other than the SMB2FileStore change that is not mandatory) you did not change existing client code, this means that in theory, someone can get DFS working by using the existing SMBLibrary NuGet package, and implement only the DFS part like you did, is that right?
    To be clear: I do think it would be nice to have DFS support as part of the SMBLibrary NuGet package - but I'm just trying to understand all the possible ways forward.

  3. I try to keep pull requests small, it's clear to me I'll start by first merging the request / response and associated data types first (the MS-DFSC data types) and some styling changes may be required. I can use "Co-authored-by" when pushing the code so in that case you don't have to be bothered with subjective code comments.

@jahales
Copy link
Author

jahales commented Nov 26, 2025

Hey Tal,

  1. Yes, that's not strictly mandatory. You're welcome to undo that change.

  2. That's right, in theory the DFS implementation could be a separate repo altogether. I don't have a strong opinion here. I intended it to be opt-in that way there's low risk of impacting anyone already using your library.

  3. Sure, that makes sense to me.

I'm honestly open to whatever you prefer. I have no concerns with you modifying the code for style or anything like that and merging it piecemeal. Or if it's easier, I can maintain a separate wrapper package that consumes SMBLibrary as a dependency, and you can keep your focus on the core SMB implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants