smb2
Version:
335 lines (274 loc) • 7.52 kB
JavaScript
/*
* CONSTANTS
*/
const protocolId = new Buffer([0xFE, 'S'.charCodeAt(0), 'M'.charCodeAt(0), 'B'.charCodeAt(0)])
, headerTranslates = {
'Command': {
'NEGOTIATE': 0x0000
, 'SESSION_SETUP': 0x0001
, 'LOGOFF': 0x0002
, 'TREE_CONNECT': 0x0003
, 'TREE_DISCONNECT': 0x0004
, 'CREATE': 0x0005
, 'CLOSE': 0x0006
, 'FLUSH': 0x0007
, 'READ': 0x0008
, 'WRITE': 0x0009
, 'LOCK': 0x000A
, 'IOCTL': 0x000B
, 'CANCEL': 0x000C
, 'ECHO': 0x000D
, 'QUERY_DIRECTORY': 0x000E
, 'CHANGE_NOTIFY': 0x000F
, 'QUERY_INFO': 0x0010
, 'SET_INFO': 0x0011
, 'OPLOCK_BREAK': 0x0012
}
}
, flags = {
'SERVER_TO_REDIR': 0x00000001
, 'ASYNC_COMMAND': 0x00000002
, 'RELATED_OPERATIONS': 0x00000004
, 'SIGNED': 0x00000008
, 'DFS_OPERATIONS': 0x10000000
, 'REPLAY_OPERATION': 0x20000000
}
, headerLength = 64
, headerSync = function(processId, sessionId) {
return [
['ProtocolId',4,protocolId]
, ['StructureSize',2,headerLength]
, ['CreditCharge',2,0]
, ['Status',4,0]
, ['Command',2]
, ['Credit',2,126]
, ['Flags',4,0]
, ['NextCommand',4,0]
, ['MessageId',4]
, ['MessageIdHigh',4,0]
, ['ProcessId',4,processId]
, ['TreeId',4,0]
, ['SessionId',8,sessionId]
, ['Signature',16,0]
];
}
, headerASync = function(processId, sessionId) {
return [
['ProtocolId',4,protocolId]
, ['StructureSize',2,headerLength]
, ['CreditCharge',2,0]
, ['Status',4,0]
, ['Command',2]
, ['Credit',2,126]
, ['Flags',4,0]
, ['NextCommand',4,0]
, ['MessageId',4]
, ['MessageIdHigh',4,0]
, ['AsyncId',8]
, ['SessionId',8,sessionId]
, ['Signature',16,0]
];
}
;
/*
* CONSTRUCTOR
*/
var SMB2Message = module.exports = function(options){
// INIT HEADERS
this.headers = {};
if(options && options.headers){
this.setHeaders(options.headers);
}
// INIT REQUEST
this.request = {};
if(options && options.request){
this.setRequest(options.request);
}
// INIT RESPONSE
this.response = {};
}
var proto = SMB2Message.prototype = {};
proto.setHeaders = function(obj){
for(var key in obj){
this.headers[key] = obj[key];
}
this.structure = require('../structures/'+this.headers['Command'].toLowerCase());
}
proto.getHeaders = function(){
return this.headers;
}
proto.setRequest = function(request){
this.request = request;
}
proto.getResponse = function(){
return this.response;
}
proto.getBuffer = function(connection){
var buffer = new Buffer(0xFFFF)
, length = 0
;
// SET MESSAGE ID
if(!this.isMessageIdSetted){
this.isMessageIdSetted = true;
this.headers['MessageId'] = connection.messageId++;
}
// HEADERS
length += writeHeaders(this, buffer);
// REQUEST
length += writeRequest(this, buffer, headerLength)
// extract the data
var output = new Buffer(length);
buffer.copy(output, 0, 0, length);
return output;
}
proto.parseBuffer = function(buffer){
// HEADERS
readHeaders(this, buffer)
// RESPONSE
readResponse(this, buffer, headerLength)
}
/*
* HELPERS
*/
function dataToBuffer(data, length) {
// buffers will be buffers
if(Buffer.isBuffer(data)){
return data;
}
// string to buffer
if(typeof data == 'string'){
return new Buffer(data);
}
// raw data to buffer
var result = new Buffer(length);
for(var i=0; i<length;i++){
result.writeUInt8(
0xFF & (data >> (i*8))
, i
);
}
return result;
}
function bufferToData(buffer) {
// not a buffer go away
if(!Buffer.isBuffer(buffer)){
return buffer;
}
// raw data to buffer
var result = 0;
for(var i=0; i<buffer.length;i++){
result += buffer.readUInt8(i) << (i*8);
}
return result;
}
function writeData(buffer, data, offset, length){
dataToBuffer(data, length).copy(buffer, offset, 0)
}
function readData(buffer, offset, length){
return buffer.slice(offset, offset+length);
}
function translate(key, value){
if(headerTranslates[key] && typeof headerTranslates[key][value] != 'undefined'){
return headerTranslates[key][value];
}
return value;
}
function unTranslate(key, value){
if(headerTranslates[key]){
for(var t in headerTranslates[key]){
if(headerTranslates[key][t] == value){
return t;
}
}
}
return null;
}
/*
* PRIVATE FUNCTIONS
*/
function readHeaders(message, buffer){
var header = (message.isAsync ? headerASync : headerSync)(message.ProcessId, message.SessionId)
, offset = 0
;
for(var i in header){
var key = header[i][0]
, length = header[i][1]
;
message.headers[key] = readData(
buffer
, offset
, length
);
if(length <= 8){
message.headers[key] = unTranslate(key, bufferToData(message.headers[key])) || message.headers[key];
}
offset += length;
}
message.structure = require('../structures/'+message.headers['Command'].toLowerCase());
}
function writeHeaders(message, buffer){
var header = (message.isAsync ? headerASync : headerSync)(message.ProcessId, message.SessionId)
, offset = 0
;
for(var i in header){
var key = header[i][0]
, length = header[i][1]
, defaultValue = header[i][2] || 0
;
writeData(
buffer
, translate(key, message.headers[key] || defaultValue)
, offset
, length
);
offset += length;
}
return offset;
}
function readResponse(message, buffer, offset){
for(var i in message.structure.response){
var key = message.structure.response[i][0]
, length = message.structure.response[i][1] || 1
;
if(typeof length == 'string'){
length = bufferToData(message.response[length]);
}
message.response[key] = readData(buffer, offset, length);
offset += length;
}
}
function writeRequest(message, buffer, offset){
var initOffset = offset
, needsRewrite = false
, tmpBuffer = new Buffer(buffer.length)
;
offset = 0;
for(var i in message.structure.request){
var key = message.structure.request[i][0]
, length = message.structure.request[i][1] || 1
, defaultValue = message.structure.request[i][2] || 0
;
if(typeof length == 'string'){
message.request[key] = message.request[key] || '';
if(message.request[length] != message.request[key].length) {
message.request[length] = message.request[key].length;
needsRewrite = true;
}
length = message.request[key].length;
} else {
message.request[key] = message.request[key] || defaultValue;
}
writeData(
tmpBuffer
, message.request[key]
, offset
, length
);
offset += length;
}
if(needsRewrite){
writeRequest(message, tmpBuffer, 0);
}
tmpBuffer.copy(buffer, initOffset, 0, offset);
return offset;
}