Commit 3064c168 authored by Benjamin Chelli's avatar Benjamin Chelli

Refactor the SMB2 Message connection + high level functions - step 2

parent 402f4060
......@@ -23,6 +23,7 @@ the parameter ```options``` accepts this list of attributes:
- ```domain``` (mandatory): the domain of which the user is registred
- ```username``` (mandatory): the username of the user that access the share
- ```password``` (mandatory): the password
- ```port``` (optional): default ```445```, the port of the SMB server
- ```packetConcurrency``` (optional): default ```20```, the number of simulatanous packet when writting / reading data from the share
- ```autoCloseTimeout``` (optional): default ```10000```, the timeout in milliseconds before to close the SMB2 session and the socket, if setted to ```0``` the connection will never be closed unless you do it
......
var SMB2Connection = require('../tools/smb2-connection');
/*
* close
* =====
*
* close your connection to the SMB2 server
*
* - close TCP connection
*
*/
module.exports = function(){
SMB2Connection.close(this);
}
var SMB2Forge = require('../tools/smb2-forge')
, SMB2Request = SMB2Forge.request
;
/*
* exists
* ======
*
* test the existence of a file
*
* - try to open the file
*
* - close the file
*
*/
module.exports = function(path, cb){
var connection = this;
SMB2Request('open', {path:path}, connection, function(err, file){
if(err) cb && cb(null, false);
else SMB2Request('close', file, connection, function(err){
cb && cb(null, true);
});
});
}
var SMB2Forge = require('../tools/smb2-forge')
, SMB2Request = SMB2Forge.request
;
/*
* mkdir
* =====
*
* create folder:
*
* - create the folder
*
* - close the folder
*
*/
module.exports = function(path, mode, cb){
if(typeof mode == 'function'){
cb = mode;
mode = '0777';
}
var connection = this;
connection.exists(path, function(err, exists){
if(err) cb && cb(err);
else if(!exists){
// SMB2 open file
SMB2Request('create_folder', {path:path}, connection, function(err, file){
if(err) cb && cb(err);
// SMB2 query directory
else SMB2Request('close', file, connection, function(err){
cb && cb(null);
});
});
} else {
cb(new Error('File/Folder already exists'));
}
});
}
var SMB2Forge = require('../tools/smb2-forge')
, SMB2Request = SMB2Forge.request
;
/*
* readdir
* =======
*
* list the file / directory from the path provided:
*
* - open the directory
*
* - query directory content
*
* - close the directory
*
*/
module.exports = function(path, cb){
var connection = this;
// SMB2 open directory
SMB2Request('open', {path:path}, connection, function(err, file){
if(err) cb && cb(err);
// SMB2 query directory
else SMB2Request('query_directory', file, connection, function(err, files){
if(err) cb && cb(err);
// SMB2 close directory
else SMB2Request('close', file, connection, function(err){
cb && cb(
null
, files
.map(function(v){ return v.Filename }) // get the filename only
.filter(function(v){ return v!='.' && v!='..' }) // remove '.' and '..' values
);
});
});
});
}
var SMB2Forge = require('../tools/smb2-forge')
, SMB2Request = SMB2Forge.request
, bigint = require('../tools/bigint')
;
/*
* readFile
* ========
*
* read the content of a file from the share
*
* - open the file
*
* - read the content
*
* - close the file
*
*/
module.exports = function(filename, options, cb){
var connection = this;
if(typeof options == 'function'){
cb = options;
options = {};
}
SMB2Request('open', {path:filename}, connection, function(err, file){
if(err) cb && cb(err);
// SMB2 read file content
else {
var fileLength = 0
, offset = new bigint(8)
, stop = false
, nbRemainingPackets = 0
, maxPacketSize = 0x00010000
;
// get file length
for(var i=0;i<file.EndofFile.length;i++){
fileLength |= file.EndofFile[i] << (i*8);
}
var result = new Buffer(fileLength);
// callback manager
function callback(offset){
return function(err, content){
if(stop) return;
if(err) {
cb && cb(err);
stop = true;
} else {
content.copy(result, offset.toNumber());
nbRemainingPackets--;
checkDone();
}
}
}
// callback manager
function checkDone(){
if(stop) return;
createPackets();
if(nbRemainingPackets==0 && offset.ge(fileLength)) {
SMB2Request('close', file, connection, function(err){
if(options.encoding){
result = result.toString(options.encoding);
}
cb && cb(null, result);
})
}
}
// create packets
function createPackets(){
while(nbRemainingPackets<connection.packetConcurrency && offset.lt(fileLength)){
// process packet size
var rest = offset.sub(fileLength).neg();
var packetSize = rest.gt(maxPacketSize) ? maxPacketSize : rest.toNumber();
// generate buffer
SMB2Request('read', {
'FileId':file.FileId
, 'Length':packetSize
, 'Offset':offset.toBuffer()
}, connection, callback(offset));
offset = offset.add(packetSize);
nbRemainingPackets++;
}
}
checkDone();
}
});
}
var SMB2Forge = require('../tools/smb2-forge')
, SMB2Request = SMB2Forge.request
, bigint = require('../tools/bigint')
;
/*
* rmdir
* =====
*
* remove directory:
*
* - open the folder
*
* - remove the folder
*
* - close the folder
*
*/
module.exports = function(path, cb){
var connection = this;
connection.exists(path, function(err, exists){
if(err) cb && cb(err);
else if(exists){
// SMB2 open file
SMB2Request('open_folder', {path:path}, connection, function(err, file){
if(err) cb && cb(err);
// SMB2 query directory
else SMB2Request('set_info', {FileId:file.FileId, FileInfoClass:'FileDispositionInformation',Buffer:(new bigint(1,1)).toBuffer()}, connection, function(err, files){
if(err) cb && cb(err);
// SMB2 close directory
else SMB2Request('close', file, connection, function(err){
cb && cb(null, files);
});
});
});
} else {
cb(new Error('Folder does not exists'));
}
});
}
var SMB2Forge = require('../tools/smb2-forge')
, SMB2Request = SMB2Forge.request
, bigint = require('../tools/bigint')
;
/*
* unlink
* ======
*
* remove file:
*
* - open the file
*
* - remove the file
*
* - close the file
*
*/
module.exports = function(path, cb){
var connection = this;
connection.exists(path, function(err, exists){
if(err) cb && cb(err);
else if(exists){
// SMB2 open file
SMB2Request('create', {path:path}, connection, function(err, file){
if(err) cb && cb(err);
// SMB2 query directory
else SMB2Request('set_info', {FileId:file.FileId, FileInfoClass:'FileDispositionInformation',Buffer:(new bigint(1,1)).toBuffer()}, connection, function(err, files){
if(err) cb && cb(err);
// SMB2 close directory
else SMB2Request('close', file, connection, function(err){
cb && cb(null, files);
});
});
});
} else {
cb(new Error('File does not exists'));
}
});
}
\ No newline at end of file
var SMB2Forge = require('../tools/smb2-forge')
, SMB2Request = SMB2Forge.request
, bigint = require('../tools/bigint')
;
/*
* writeFile
* =========
*
* create and write file on the share
*
* - create the file
*
* - set info of the file
*
* - set content of the file
*
* - close the file
*
*/
module.exports = function(filename, data, options, cb){
if(typeof options == 'function'){
cb = options;
options = {};
}
options.encoding = options.encoding || 'utf8';
var connection = this
, file
, fileContent = Buffer.isBuffer(data) ? data : new Buffer(data, options.encoding)
, fileLength = new bigint(8, fileContent.length)
;
function createFile(fileCreated){
SMB2Request('create', {path:filename}, connection, function(err, f){
if(err) cb && cb(err);
// SMB2 set file size
else {
file = f;
fileCreated();
}
});
}
function closeFile(fileClosed){
SMB2Request('close', file, connection, function(err){
if(err) cb && cb(err);
else {
file = null;
fileClosed();
}
});
}
function setFileSize(fileSizeSetted){
SMB2Request('set_info', {FileId:file.FileId, FileInfoClass:'FileEndOfFileInformation', Buffer:fileLength.toBuffer()}, connection, function(err){
if(err) cb && cb(err);
else fileSizeSetted();
});
}
function writeFile(fileWritten){
var offset = new bigint(8)
, stop = false
, nbRemainingPackets = 0
, maxPacketSize = new bigint(8, 0x00010000 - 0x71)
;
// callback manager
function callback(offset){
return function(err){
if(stop) return;
if(err) {
cb && cb(err);
stop = true;
} else {
nbRemainingPackets--;
checkDone();
}
}
}
// callback manager
function checkDone(){
if(stop) return;
createPackets();
if(nbRemainingPackets==0 && offset.ge(fileLength)) {
fileWritten();
}
}
// create packets
function createPackets(){
while(nbRemainingPackets<connection.packetConcurrency && offset.lt(fileLength)){
// process packet size
var rest = fileLength.sub(offset);
var packetSize = rest.gt(maxPacketSize) ? maxPacketSize : rest;
// generate buffer
SMB2Request('write', {
'FileId':file.FileId
, 'Offset':offset.toBuffer()
, 'Buffer':fileContent.slice(offset.toNumber(), offset.add(packetSize).toNumber())
}, connection, callback(offset));
offset = offset.add(packetSize);
nbRemainingPackets++;
}
}
checkDone();
}
createFile(function(){
setFileSize(function(){
writeFile(function(){
closeFile(cb);
});
});
});
}
This diff is collapsed.
/*
* DEPENDENCIES
*/
var net = require('net')
, SMB2Forge = require('./smb2-forge')
, SMB2Request = SMB2Forge.request
;
/*
* CONNECTION MANAGER
*/
var SMB2Connection = module.exports = {};
/*
* CLOSE CONNECTION
*/
SMB2Connection.close = function(connection){
clearAutoCloseTimeout(connection);
if(connection.connected){
connection.connected = false;
connection.socket.end();
}
}
/*
* OPEN CONNECTION
*/
SMB2Connection.requireConnect = function(method){
return function(){
var connection = this;
var args = Array.prototype.slice.call(arguments);
connect(connection, function(err){
// process the cb
var cb = args.pop();
cb = scheduleAutoClose(connection, cb);
args.push(cb);
// manage the connection error
if(err) cb(err);
else method.apply(connection, args);
});
}
}
/*
* INIT CONNECTION
*/
SMB2Connection.init = function(connection){
// create a socket
connection.connected = false;
connection.socket = new net.Socket({
allowHalfOpen:true
});
// attach data events to socket
connection.socket.on('data', SMB2Forge.response(connection));
connection.errorHandler = [];
connection.socket.on('error', function(err){
if(connection.errorHandler.length > 0){
connection.errorHandler[0].call(null, err)
}
if(connection.debug){
console.log('-- error');
console.log(arguments);
}
});
}
/*
* PRIVATE FUNCTION TO HANDLE CONNECTION
*/
function connect(connection, cb){
if(connection.connected){
cb && cb(null);
return;
}
cb = scheduleAutoClose(connection, cb);
// open TCP socket
connection.socket.connect(connection.port, connection.ip);
// SMB2 negotiate connection
SMB2Request('negotiate', {}, connection, function(err){
if(err) cb && cb(err);
// SMB2 setup session / negotiate ntlm
else SMB2Request('session_setup_step1', {}, connection, function(err){
if(err) cb && cb(err);
// SMB2 setup session / autheticate with ntlm
else SMB2Request('session_setup_step2', {}, connection, function(err){
if(err) cb && cb(err);
// SMB2 tree connect
else SMB2Request('tree_connect', {}, connection, function(err){
if(err) cb && cb(err);
else {
connection.connected = true;
cb && cb(null);
}
});
});
});
});
}
/*
* PRIVATE FUNCTION TO HANDLE CLOSING THE CONNECTION
*/
function clearAutoCloseTimeout(connection){
if(connection.scheduledAutoClose){
clearTimeout(connection.scheduledAutoClose);
connection.scheduledAutoClose = null;
}
}
function setAutoCloseTimeout(connection){
clearAutoCloseTimeout(connection);
if(connection.autoCloseTimeout != 0){
connection.scheduledAutoClose = setTimeout(function(){
connection.close();
}, connection.autoCloseTimeout);
}
}
function scheduleAutoClose(connection, cb){
addErrorListener(connection, cb);
clearAutoCloseTimeout(connection);
return function(){
removeErrorListener(connection);
setAutoCloseTimeout(connection);
cb.apply(null, arguments);
}
}
/*
* PRIVATE FUNCTIONS TO HANDLE ERRORS
*/
function addErrorListener(connection, callback){
connection.errorHandler.unshift(callback);
}
function removeErrorListener(connection){
connection.errorHandler.shift();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment