@sharelist/node-smb-server
Version:
Sharelist SMB Server Implementation Base On node-smb-server
222 lines (196 loc) • 6.38 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.
*/
'use strict';
var put = require('put');
var binary = require('binary');
var ntstatus = require('../ntstatus');
var consts = require('./constants');
var flags = require('./flags');
function decode(buf) {
// SMB uses little endian byte order!
var parser = binary.parse(buf);
var raw = parser.buffer('protocolId', 4)// 0xff, 'S', 'M', 'B'
.word8('command')
.word32le('status')
.word8('flags')
.word16le('flags2')
.word16le('extra.pidHigh')
.buffer('extra.signature', 8)
.skip(2)
.word16le('tid')
.word16le('pidLow')
.word16le('uid')
.word16le('mid')
.word8('params.wordCount')
.buffer('params.words', 2 * parser.vars['params']['wordCount'])
.word16le('data.byteCount')
.buffer('data.bytes', 'data.byteCount')
.vars;
var cmdId = raw.command;
var header = {
commandId: cmdId,
command: consts.COMMAND_TO_STRING[cmdId],
status: raw.status,
flags: flags.decode(raw.flags, raw.flags2),
extra: {
pidHigh: raw.extra.pidHigh,
signature: raw.extra.signature
},
tid: raw.tid,
pidLow: raw.pidLow,
pid: (raw.extra.pidHigh << 16) + (raw.pidLow),
uid: raw.uid,
mid: raw.mid
};
var params = raw.params.words;
var paramsOffset = consts.SMB_MIN_LENGTH;
var data = raw.data.bytes;
var dataOffset = paramsOffset + params.length;
var msg = {
protocolId: raw.protocolId,
header: header,
params: params,
data: data,
buf: buf // raw message buffer
};
// unify andX and non-andX style message format: commands will be represented as array, even for andX-style messages
msg.commands = [];
decodeCommands(buf, header.commandId, params, data, paramsOffset, dataOffset, msg.commands);
return msg;
}
function decodeCommands(buf, commandId, params, data, paramsOffset, dataOffset, commands) {
commands.push({
commandId: commandId,
params: params,
data: data,
paramsOffset: paramsOffset,
dataOffset: dataOffset
});
if (!consts.IS_ANDX_COMMAND(commandId)) {
return;
}
// parse andX prefix
var nextCmdId = params.readUInt8(0);
var offset = params.readUInt16LE(2);
var nextParamsOffset, nextDataOffset;
if (nextCmdId !== 0xff && offset) {
// extract params & data
var nextBody = buf.slice(offset);
var off = 0;
var wordCount = nextBody.readUInt8(off);
nextParamsOffset = off += 1;
var nextParams = nextBody.slice(off, off + (2 * wordCount));
off += (2 * wordCount);
var byteCount = nextBody.readUInt16LE(off);
nextDataOffset = off += 2;
var nextData = nextBody.slice(off, off + byteCount);
// recurse
decodeCommands(buf, nextCmdId, nextParams, nextData, nextParamsOffset, nextDataOffset, commands);
}
}
function encode(msg) {
var out = put();
var flgs = flags.encode(msg.header.flags);
// header
out.put(msg.protocolId)
.word8(msg.header.commandId)
.word32le(msg.header.status)
.word8(flgs.flags)
.word16le(flgs.flags2)
.word16le(msg.header.extra.pidHigh)
.put(msg.header.extra.signature)
.pad(2)
.word16le(msg.header.tid)
.word16le(msg.header.pid)
.word16le(msg.header.uid)
.word16le(msg.header.mid);
var offset = consts.SMB_HEADER_LENGTH;
var wordCount, byteCount;
// body (params, data)
if (msg.header.status !== ntstatus.STATUS_SUCCESS
&& msg.header.status !== ntstatus.STATUS_MORE_PROCESSING_REQUIRED) {
// error message
// according to the CIFS spec:
// "Error responses SHOULD be sent with empty SMB Parameters and SMB Data blocks"
out.word8(0) // wordCount
.word16le(0); // byteCount
} else if (!consts.IS_ANDX_COMMAND(msg.header.commandId)) {
// regular message body with single params/data pair
msg.params = msg.commands[0].params;
wordCount = msg.params.length / 2;
if (msg.commands[0].wordCount) {
// override wordCount
wordCount = msg.commands[0].wordCount;
}
msg.data = msg.commands[0].data;
byteCount = msg.data.length;
if (msg.commands[0].byteCount) {
// override byteCount
byteCount = msg.commands[0].byteCount;
}
// wordCount
out.word8(wordCount)
// params
.put(msg.params)
// byteCount
.word16le(byteCount)
// data
.put(msg.data);
} else {
// andX-type message body with multiple params/data pairs
for (var i = 0; i < msg.commands.length; i++) {
var cmd = msg.commands[i];
// next params/data block offset
offset += (1 + cmd.params.length + 2 + cmd.data.length);
wordCount = cmd.params.length / 2;
if (cmd.wordCount) {
// override wordCount
wordCount = cmd.wordCount;
}
byteCount = cmd.data.length;
if (cmd.byteCount) {
// override byteCount
byteCount = cmd.byteCount;
}
// wordCount
out.word8(wordCount);
// andX header
if (i === msg.commands.length - 1) {
// last command in chain
if (consts.IS_ANDX_COMMAND(cmd.commandId)) {
// next andX command
cmd.params.writeUInt8(0xff, 0);
// andX reserved
cmd.params.writeUInt8(0x0, 1);
// andX offset
cmd.params.writeUInt16LE(0x0, 2);
}
} else {
// next andX command
cmd.params.writeUInt8(msg.commands[i + 1].commandId, 0);
// andX reserved
cmd.params.writeUInt8(0x0, 1);
// andX offset
cmd.params.writeUInt16LE(offset, 2);
}
// params
out.put(cmd.params)
// byteCount
.word16le(byteCount)
// data
.put(cmd.data);
}
}
return out.buffer();
}
module.exports.decode = decode;
module.exports.encode = encode;