@sharelist/node-smb-server
Version:
Sharelist SMB Server Implementation Base On node-smb-server
127 lines (108 loc) • 4.15 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 logger = require('winston').loggers.get('smb');
var async = require('async');
var SMB = require('./smb/constants');
var SMB2 = require('./smb2/constants');
var utils = require('./utils');
var smb = require('./smb/handler');
var smb2 = require('./smb2/handler');
/**
* Creates an <code>SMBConnection</code> instance. This objects takes care of reading and writing
* SMB messages on the wire and delegating the message processing to specialised handlers.
*
* @param {Socket} socket - tcp socket
* @param {Object} server - smb server
* @constructor
*/
function SMBConnection(socket, server) {
this.socket = socket;
this.server = server;
this.socket.on('data', this.onData.bind(this));
this.socket.on('close', this.onClose.bind(this));
}
SMBConnection.prototype.onData = function (data) {
logger.silly('received %d bytes: %s', data.length, data.toString('hex'));
if (this.partialMsgChunk) {
// prepend leftover chunk from previous event
data = Buffer.concat([ this.partialMsgChunk, data ]);
delete this.partialMsgChunk;
}
var buf = data;
var msgChunks = [];
while (buf.length > 4) {
// parse NetBIOS session service header (RFC 1002)
var nbType = buf.readUInt8(0);
// since we're assuming native SMB (i.e. over TCP)
// we're only supporting NetBIOS session messages
if (nbType !== 0x00) {
logger.error('unsupported NetBIOS session service message type: %d', nbType);
this.socket.destroy();
return;
}
// native SMB message headers consist of 8-bit type and 24-bit length;
// since type is 0 we can read the length as 32-bit unsigned int.
// (NetBIOS uses big-endian (network) byte order)
var smbLen = buf.readUInt32BE(0);
if (smbLen > buf.length - 4) {
// partial msg chunk, wait for next chunk
break;
}
// strip NetBIOS session service header
buf = buf.slice(4);
msgChunks.push(buf.slice(0, smbLen));
buf = buf.slice(smbLen);
}
// by now we should have consumed all msg chunks
if (buf.length) {
// store leftover partial msg chunk and prepend on next data event
this.partialMsgChunk = buf;
}
if (!msgChunks.length) {
return;
}
var self = this;
async.each(msgChunks,
function (chunk, callback) {
// sniff SMB version
var protocolId = chunk.slice(0, 4);
if (utils.bufferEquals(protocolId, SMB.PROTOCOL_ID)) {
// CIFS/SMB 1.0
smb.handleRequest(chunk, self, self.server, callback);
} else if (utils.bufferEquals(protocolId, SMB2.PROTOCOL_ID)) {
// SMB 2.x/3.x
smb2.handleRequest(chunk, self, self.server, callback);
} else {
// ???
callback('invalid SMB protocol id: ' + protocolId.toString('hex') + ', data: ' + chunk.toString('hex'));
}
},
function (err) {
if (err) {
logger.error('encountered error during message processing', err);
}
}
);
};
SMBConnection.prototype.onClose = function (hadErrors) {
// todo cleanup connection state
};
SMBConnection.prototype.sendRawMessage = function (msgBuf, cb) {
var hdrBuf = new Buffer(4);
// native SMB message headers consist of 8-bit type and 24-bit length;
// since NetBIOS session message type is 0 we can write the length as 32-bit unsigned int.
// (NetBIOS uses big-endian (network) byte order)
hdrBuf.writeUInt32BE(msgBuf.length, 0);
this.socket.write(Buffer.concat([ hdrBuf, msgBuf ]), cb);
};
module.exports = SMBConnection;