node-smb-server
Version:
A Pure JavaScript SMB Server Implementation
268 lines (231 loc) • 8.19 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 logger = require('winston').loggers.get('dcerpc');
var consts = require('../constants');
var packet = require('../packet');
var utils = require('../../utils');
/**
* REQUEST (0x00): The request PDU is used for an initial call request.
*
* @param {Object} hdr - common PDU header
* @param {Buffer} buf - the raw PDU buffer
* @param {SMBFile} pipe - an SMBFile instance
* @param {SMBServer} server - an SMBServer instance
* @param {Function} cb callback called with the PDU's response
* @param {String|Error} cb.error error (non-null if an error occurred)
* @param {Buffer} cb.response buffer holding the bytes of the encoded response PDU
*/
function handle(hdr, buf, pipe, server, cb) {
// parse request specific header fields
var off = consts.COMMON_HEADER_LENGTH;
hdr.allocHint = buf.readUInt32LE(off);
off += 4;
hdr.ctxId = buf.readUInt16LE(off);
off += 2;
hdr.opnum = buf.readUInt16LE(off);
off += 2;
if (hdr.flags & consts.PFC_OBJECT_UUID) {
hdr.objectUUIDRaw = buf.slice(off, off + 16);
hdr.objectUUID = utils.rawUUIDToString(hdr.objectUUIDRaw);
off += 16;
}
var stubDataIn = buf.slice(off, hdr.fragLength - hdr.authLength);
var iface = consts.SUPPORTED_INTERFACES[pipe.syntaxSpec.uuid][pipe.syntaxSpec.version + '.' + pipe.syntaxSpec.versionMinor];
var funcName = iface[hdr.opnum];
if (!funcName) {
logger.error('encountered unsupported operation 0x%s (%s v%d.%d)', hdr.opnum.toString(16), consts.SUPPORTED_INTERFACES[pipe.syntaxSpec.uuid].name, pipe.syntaxSpec.version, pipe.syntaxSpec.versionMinor);
onFault(hdr, consts.NCA_UNSPEC_REJECT, cb);
return;
}
logger.debug('%s (%s v%d.%d)', funcName, consts.SUPPORTED_INTERFACES[pipe.syntaxSpec.uuid].name, pipe.syntaxSpec.version, pipe.syntaxSpec.versionMinor);
function resultHandler(err, outData) {
if (err) {
onFault(hdr, consts.NCA_UNSPEC_REJECT, cb);
} else {
onSuccess(hdr, outData, cb);
}
}
switch (funcName) {
case 'NetShareEnumAll':
netShareEnumAll(stubDataIn, server, resultHandler);
break;
default:
logger.error('encountered unsupported operation %s (%s v%d.%d)', funcName, consts.SUPPORTED_INTERFACES[pipe.syntaxSpec.uuid].name, pipe.syntaxSpec.version, pipe.syntaxSpec.versionMinor);
onFault(hdr, consts.NCA_UNSPEC_REJECT, cb);
}
}
function onFault(hdr, status, cb) {
hdr.type = consts.STRING_TO_PDUTYPE['fault'];
var hdrBuf = packet.serializeCommonHeaderFields(hdr);
var out = put();
out.put(hdrBuf);
out.word32le(0) // alloc_hint
.word16le(hdr.ctxId) // p_cont_id
.word8(0) // cancel_count
.word8(0) // reserved
.word32le(status) // status
.put(new Buffer([ 0x00, 0x00, 0x00, 0x00 ])); // reserved2
var outBuf = out.buffer();
// set actual fragLength
outBuf.writeUInt16LE(outBuf.length, consts.HEADER_FRAGLENGTH_OFFSET);
cb(null, outBuf);
}
function onSuccess(hdr, stubData, cb) {
hdr.type = consts.STRING_TO_PDUTYPE['response'];
var hdrBuf = packet.serializeCommonHeaderFields(hdr);
var out = put();
out.put(hdrBuf);
out.word32le(0) // alloc_hint
.word16le(hdr.ctxId) // p_cont_id
.word8(0) // cancel_count
.word8(0) // reserved
.put(stubData); // stub data
var outBuf = out.buffer();
// set actual fragLength
outBuf.writeUInt16LE(outBuf.length, consts.HEADER_FRAGLENGTH_OFFSET);
cb(null, outBuf);
}
function netShareEnumAll(dataIn, server, cb) {
/*
typedef [handle, string] wchar_t * SRVSVC_HANDLE;
typedef struct _SHARE_ENUM_STRUCT
{
DWORD Level;
[switch_is(Level)] SHARE_ENUM_UNION ShareInfo;
} SHARE_ENUM_STRUCT,
*PSHARE_ENUM_STRUCT,
*LPSHARE_ENUM_STRUCT;
typedef [switch_type(DWORD)] union _SHARE_ENUM_UNION {
[case(0)]
SHARE_INFO_0_CONTAINER* Level0;
[case(1)]
SHARE_INFO_1_CONTAINER* Level1;
[case(2)]
SHARE_INFO_2_CONTAINER* Level2;
[case(501)]
SHARE_INFO_501_CONTAINER* Level501;
[case(502)]
SHARE_INFO_502_CONTAINER* Level502;
[case(503)]
SHARE_INFO_503_CONTAINER* Level503;
} SHARE_ENUM_UNION;
typedef struct _SHARE_INFO_1_CONTAINER
{
DWORD EntriesRead;
[size_is(EntriesRead)] LPSHARE_INFO_1 Buffer;
} SHARE_INFO_1_CONTAINER;
typedef struct _SHARE_INFO_1
{
[string] wchar_t * shi1_netname;
DWORD shi1_type;
[string] wchar_t * shi1_remark;
} SHARE_INFO_1,
*PSHARE_INFO_1,
*LPSHARE_INFO_1;
NET_API_STATUS
NetrShareEnum (
[in,string,unique] SRVSVC_HANDLE ServerName,
[in,out] LPSHARE_ENUM_STRUCT InfoStruct,
[in] DWORD PreferedMaximumLength,
[out] DWORD * TotalEntries,
[in,out,unique] DWORD * ResumeHandle
);
*/
// parse stub data in
var off = 0;
// [in,string,unique] SRVSVC_HANDLE ServerName,
var refId = dataIn.readUInt32LE(off);
off += 4;
var maxCount = dataIn.readUInt32LE(off);
off += 4;
var offset = dataIn.readUInt32LE(off);
off += 4;
var actCount = dataIn.readUInt32LE(off);
off += 4;
var serverName = utils.extractUnicodeBytes(dataIn, off).toString('utf16le');
off += (2 * (serverName.length + 1));
// [in,out] LPSHARE_ENUM_STRUCT InfoStruct,
var level = dataIn.readUInt32LE(off); // level === 1
off += 4;
var entriesRead = dataIn.readUInt32LE(off);
off += 4;
refId = dataIn.readUInt32LE(off);
off += 4;
var count = dataIn.readUInt32LE(off);
off += 4;
var buffer = dataIn.readUInt32LE(off);
off += 4;
// [in] DWORD PreferedMaximumLength,
var preferedMaximumLength = dataIn.readUInt32LE(off);
off += 4;
// [in,out,unique] DWORD * ResumeHandle
refId = dataIn.readUInt32LE(off);
off += 4;
var resumeHandle = dataIn.readUInt32LE(off);
off += 4;
// retrieve list of shares
var shares = server.listShares();
// build stub data out
var dummyRefId = 0x12345678;
var out = put();
// [in,out] LPSHARE_ENUM_STRUCT InfoStruct,
out.word32le(level);
out.word32le(entriesRead);
out.word32le(dummyRefId);
out.word32le(shares.length);
out.word32le(dummyRefId);
out.word32le(shares.length);
var i, share, type, len;
for (i = 0; i < shares.length; i++) {
share = shares[i];
type = 0x00000000; // STYPE_DISKTREE
if (share.name === 'IPC$') {
type = 0x00000003 | 0x80000000; // STYPE_IPC | STYPE_SPECIAL
} else if (share.name.substr(-1) === '$') {
type |= 0x80000000; // |= STYPE_SPECIAL
}
out.word32le(dummyRefId); // name
out.word32le(type);
out.word32le(dummyRefId); // description
}
for (i = 0; i < shares.length; i++) {
share = shares[i];
// name
len = share.name.length + 1;
// align on 32bit boundary
out.pad(utils.calculatePadLength(out.length(), 4)); // align on 32bit boundary
out.word32le(len); // maxCount
out.word32le(0); // offset
out.word32le(len); // actCount
out.put(new Buffer(share.name, 'utf16le'));
out.pad(2); // terminator
// description
len = share.description.length + 1;
out.pad(utils.calculatePadLength(out.length(), 4)); // align on 32bit boundary
out.word32le(len); // maxCount
out.word32le(0); // offset
out.word32le(len); // actCount
out.put(new Buffer(share.description, 'utf16le'));
out.pad(2); // terminator
}
out.pad(utils.calculatePadLength(out.length(), 4)); // align on 32bit boundary
// [out] DWORD * TotalEntries,
out.word32le(shares.length);
// [in,out,unique] DWORD * ResumeHandle
out.word32le(0); // NULL
// NET_API_STATUS
out.word32le(0);
cb(null, out.buffer());
}
module.exports = handle;