node-smb-server is an open-source JavaScript implementation of the SMB/CIFS file sharing protocol.
Some highlights:
- Pure JavaScript
- Fully configurable/customizable
- Extensible: supports exposing non filesystem-based data as a mountable file system via an abstract backend, similar to Samba's VFS
- Installation
- Running a Standalone Server
- User Management
- Configuration
- Logging
- Developing a Custom Backend
- Unit Testing
- SMB Server as a Module
- Current Status
- ToDo's
Install latest release version from NPM:
npm install node-smb-server
Install from source code:
git clone https://github.com/adobe/node-smb-server.git
cd node-smb-server
npm install
Execute the following commands in a terminal:
cd <node-smb-server install dir>
npm start
In Finder, open the 'Connect to Server' dialog (⌘K) and enter the url smb://localhost:8445/fs
(user: test
, password: test
).
The following users are pre-configured by default when running standalone: test/test
, admin/admin
, guest/<empty password>
Users can be edited in the configuration file:
...
"users" : {
"test" : {
"lmHash" : "01fc5a6be7bc6929aad3b435b51404ee",
"ntlmHash" : "0cb6948805f797bf2a82807973b89537"
},
"admin" : {
"lmHash" : "f0d412bd764ffe81aad3b435b51404ee",
"ntlmHash" : "209c6174da490caeb422f3fa5a7ae634"
},
"guest" : {
"lmHash" : "aad3b435b51404eeaad3b435b51404ee",
"ntlmHash" : "31d6cfe0d16ae931b73c59d7e0c089c0"
}
}
...
Password hashes can be computed by running:
node createhash.js
The standalone server reads its configuration from file. When using the SMB Server as a module, the configuration is passed to the module's constructor.
The server supports the following configuration options:
- listen: (Standalone only) Specifies where the server should listen for connections when
running in standalone mode. Supports the following elements:
- port: The port on which the standalone server should listen. Default: 445.
- host: The IP address or host name on which the standalone server should listen. Default: 0.0.0.0.
- domainName: The domain value that the server will use for domain-based authentication. Default: empty string.
- smb2Support: If true, the server will use the SMB2 protocol. Otherwise uses SMB1. Default: false.
- extendedSecurity: If true, enables extended security capabilities when authenticating. Default: false.
- users: Specifies username/password combinations that can authenticate with the server. See User Management for more information. Default: no users.
- shares: Specifies which shares the server will use, and provides configuration options for each. See Share Configurations for more information. Default: no shares.
Each share supports the following options by default:
- backend: A unique value used to identify the share. In addition, the name is
used when determining which sub-directory of
the backends folder that the server will use to load the share's
module. It will also determine the path to use when connecting to the share.
For example, the URL for connecting to a share whose name is
fs
would besmb://localhost:8445/fs
, and the server willrequire
the share fromlib/backends/fs/share
. - description: An arbitrary value that will be displayed as the smb mount's description.
- unicodeNormalizeForm: The unicode form that the share should use to normalize unicode paths. Valid values are "nkfd", "none", or "nkfc". Default: nkfd.
See individual backend documentation for share-specific configurations.
The following is a sample shares
configuration element. Each share's element name
(FS
and JCR
in the example) is arbitrary.
...
"shares": {
"FS": {
"backend": "fs",
"description": "fs-based test share",
"path": "./smbroot"
},
"JCR": {
"backend": "jcr",
"description": "AEM-based test share",
"host": "localhost",
"port": 4502,
"protocol": "http:",
"auth": {
"user": "<user>",
"pass": "<pwd>"
},
"path": "/",
"maxSockets": 64,
"contentCacheTTL": 30000,
"binCacheTTL": 600000
},
...
The SMB Server uses winston for logging, and its logging configuration file supports the following:
- (root value): Each root element will become a new logger, whose name will be the element's name.
- file: If specified, the logger will have a
File
transport, meaning that messages sent to the logger will be written to a file. The value of the file element will be sent as-is towinston's
transport. - console: If specified, the logger will have a
Console
transport, meaning that messages sent to the logger will be written to the console. The value of the console element will be sent as-is towinston's
transport. - config: If specified, the logger definition will reuse the definition whose key matches the specified value.
- file: If specified, the logger will have a
Consider the following example use case:
You would like to enable your desktop applications to access data and documents stored in a RDBMS or a Cloud-based service.
You could write a custom backend by implementing the Share
, TreeConnection
, Tree
, FileConnection
, and File
interfaces of the virtual SPI backend. Check out the existing
implementations to see examples.
The SMB Server has a fairly extensive set of unit tests, along with a constantly evolving test framework designed
for easily writing unit tests in a contained environment. The framework swaps common dependencies like fs
, request
,
and stream
with mock implementations.
To run existing tests, do the following:
- Install the server's source code.
- Switch to the unit test directory using
cd spec
. - Install test dependencies using
npm install
. - Run the tests using
npm test
.
The tests use Jasmine as the test harness. To add a new test, create a new file
anywhere beneath the test directory; the new file name should end in -spec.js
. For example,
my_new_test-spec.js
. The npm test
command will automatically pick up the new file and run any tests inside it.
To take advantage of the framework's built-in mocking capabilities, use TestCommon.require
when importing modules
into your test case. For example, the following code will include the module from /lib/backends/fs/tree
, and will
automatically substitute modules that the common framework supports with mock implementations:
var TestCommon = require('./test-common.js');
var FSTree = TestCommon.require(__dirname, '../lib/backends/fs/tree');
describe('Suite description', function () {
it('Test case description', function () {
// your test case here.
});
});
requireStubs
is an alternate version of require
that takes additional dependencies to mock.
Implementation of these mocks is an exercise left up to the caller.
The SMB Server can be run either as a standalone server, or consumed as a module in external applications. To include the server as a module:
- Import the server into your application using
var SMBServer = require('node-smb-server/lib/smbserver');
- Create a new instance of the server using its constructor.
- Interact with the server using its methods. This will typically consist of calling
start
, followed by use-case specific calls, then concluded by callingstop
.
When using the server as a module, it's possible to programmatically communicate with the server using events. The SMBServer object itself is an event emitter and provides the following events:
- error: The server's TCP connection has encountered an unhandled error.
- (String) error: Details of the error.
- terminated: The server's TCP connection has closed.
- started: The server has completed its startup routine successfully.
- shareConnected: One of the server's shares has connected successfully.
- (String) share: The name of the share.
- shareDisconnected: One of the server's shares has disconnected successfully.
- (String) share: The name of the share.
- folderListed: A share has listed the contents of a folder.
- (String) share: The name of the share.
- (String) path: Path to the folder.
- fileCreated: A new file was created by a share.
- (String) share: The name of the share.
- (String) path: Path to the file.
- folderCreated: A new folder was created by a share.
- (String) share: The name of the share.
- (String) path: Path to the folder.
- fileDeleted: A share has deleted a file.
- (String) share: The name of the share.
- (String) path: Path to the file.
- folderDeleted: A share has deleted a folder.
- (String) share: The name of the share.
- (String) path: Path to the folder.
- itemMoved: A share has moved a file or folder.
- (String) share: The name of the share.
- (String) oldPath: Old path of the file or folder.
- (String) newPath: New path of the file or folder
- serverEvent: A more generic means of sending events from the server. The primary purpose of this
event is to allow a simple "passthrough" mechanism that the server's backends can use
to emit backend-specific events. Refer to an individual backend's documentation to see which
events each one supports.
- (Object) data: Details about the event.
- (String) event: The name of the event.
- (Object) data: Extended information specific to the event.
- (Function) callback: An optional callback that will be invoked when the server has finished processing the event. See backend's documentation to see which events support callbacks.
- (Object) data: Details about the event.
The SMB Server also provides a way for sending events to the server. Use a server instance's
processEvent
method to pass events and event data. Refer to an individual
backend's documentation for a list of which events the server supports.
- Implements CIFS and MS-SMB 1.0.
- Support for SMB2 is currently work in progress.
- Supports LM, LMv2, NTLM, NTLMSSP authentication protocols
- Supported backends
- Tested with Finder on OS X (Yosemite, El Capitan, Sierra).
- Test with other clients on other platforms (Windows, Linux).
- Add more test cases/suites
- CIFS/SMB:
- missing NT_TRANSACT subcommands
- missing TRANSACTION subcommands
- missing TRANSACTION2 subcommand information levels
- missing CIFS commands:
- TRANSACTION_SECONDARY
- TRANSACTION2_SECONDARY
- NT_TRANSACT_SECONDARY
- OPEN_PRINT_FILE
- support for named streams?
- SMB Signing?
- proper implementation of LOCKING_ANDX?
- Check/Implement the following protocol extensions/versions:
- SMB2/3