@sharelist/node-smb-server
Version:
Sharelist SMB Server Implementation Base On node-smb-server
192 lines (169 loc) • 5.72 kB
JavaScript
/*
* Copyright 2015 Adobe Systems Incorporated. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
;
var fs = require('fs');
var path = require('path');
var logger = require('winston').loggers.get('smb');
var put = require('put');
var async = require('async');
var utils = require('../utils');
var ntstatus = require('../ntstatus');
var message = require('./message');
var SMB2 = require('./constants');
var cmdHandlers = {};
function loadCmdHandlers() {
var p = path.join(__dirname, 'cmd');
var files = fs.readdirSync(p);
for (var i = 0; i < files.length; i++) {
var f = files[i];
var stat = fs.statSync(path.resolve(p, f));
if (stat.isDirectory()) {
continue;
}
if (f.substr(-3) === '.js') {
f = f.slice(0, -3);
cmdHandlers[f] = require(path.resolve(p, f));
}
}
}
loadCmdHandlers();
var out = put();
out.word16le(0x0009) // StructureSize (fixed according to spec)
.word8(0) // ErrorContextCount
.pad(1) // Reserved
.word32le(0) // ByteCount
.word8(0); // ErrorData
var SMBERROR_BODY = out.buffer();
/**
* Handles binary SMB 2.x/3.x messages
*
* @param {Buffer} msgBuf - raw message buffer
* @param {SMBConnection} connection - an SMBConnection instance
* @param {SMBServer} server - an SMBServer instance
* @param {Function} cb callback called on completion
*/
function handleRequest(msgBuf, connection, server, cb) {
var buf = msgBuf;
// dissect compounded requests
var compMsgs = [];
var msg = message.decode(buf);
compMsgs.push(msg);
while (msg.header.nextCommand) {
buf = buf.slice(msg.header.nextCommand);
msg = message.decode(buf);
compMsgs.put(msg);
}
if (compMsgs[0].header.relatedOp) {
sendResponse(compMsgs[0], ntstatus.STATUS_INVALID_PARAMETER, cb);
return;
}
var relatedOps = compMsgs.length > 1 && compMsgs[1].header.relatedOp;
// context for related operations
var relatedCtx = relatedOps ? {
sessionId: compMsgs[0].sessionId,
treeId: compMsgs[0].treeId,
fileId: null
} : null;
function processMsg(msg, callback) {
var command = SMB2.COMMAND_TO_STRING[msg.header.commandId];
if (!command) {
// unknown command
callback({
status: ntstatus.STATUS_SMB_BAD_COMMAND,
message: 'encountered invalid command 0x' + msg.header.ommandId.toString(16)
});
}
var handler = cmdHandlers[command];
if (handler) {
// process command
handler(msg, msg.header.commandId, msg.body, relatedCtx, connection, server, function (result) {
if (!result) {
// special case (see e.g. 'echo' handler): no further processing required
msg.processed = true;
} else {
if (result.status !== ntstatus.STATUS_SUCCESS) {
// command failed
logger.debug('\'' + command.toUpperCase() + '\' returned error status ' + ntstatus.STATUS_TO_STRING[result.status] + ' (0x' + result.status.toString(16) + ')');
}
// stash command result/response
msg.header.status = result.status;
msg.body = result.body;
}
callback();
});
} else {
// no handler found
logger.error('encountered unsupported command 0x' + msg.header.commandId.toString(16) + ' \'' + command.toUpperCase() + '\'');
msg.header.status = ntstatus.STATUS_NOT_IMPLEMENTED;
msg.body = SMBERROR_BODY;
callback();
}
}
function processResults(err) {
sendCompoundedResponses(compMsgs, connection, server, cb);
}
// invoke async command handlers
if (relatedOps) {
async.eachSeries(compMsgs,
processMsg,
processResults
);
} else {
async.each(compMsgs,
processMsg,
processResults
);
}
}
function sendCompoundedResponses(msgs, connection, server, cb) {
var out = put();
if (msgs.length === 1 && msgs[0].processed) {
// special case (see e.g. 'echo' handler): no further processing required
cb();
return;
}
// build compounded responses
msgs.forEach(function (msg, n, arr) {
// make sure the 'reply' flag is set
msg.header.flags.reply = true;
if (msg.header.status !== ntstatus.STATUS_SUCCESS) {
msg.body = SMBERROR_BODY;
}
// calculate nextCommand offset
var nextCommandOff = 0;
var padLength = 0;
if (n < arr.length - 1) {
nextCommandOff = SMB2.HEADER_LENGTH + msg.body.length;
// align nextCommand on 8-byte boundary
padLength = utils.calculatePadLength(nextCommandOff, 8);
nextCommandOff += padLength;
}
msg.header.nextCommand = nextCommandOff;
out.put(message.encode(msg));
if (padLength) {
out.pad(padLength);
}
});
connection.sendRawMessage(out.buffer(), cb);
}
function sendResponse(msg, status, connection, server, cb) {
// make sure the 'reply' flag is set
msg.header.flags.reply = true;
msg.header.status = status;
msg.header.nextCommand = 0;
if (status !== ntstatus.STATUS_SUCCESS) {
msg.body = SMBERROR_BODY;
}
connection.sendRawMessage(message.encode(msg), cb);
}
module.exports.handleRequest = handleRequest;
module.exports.sendResponse = sendResponse;