Completed first pass with JsHint, updated windows MeshAgent.

This commit is contained in:
Ylian Saint-Hilaire 2018-08-30 12:05:23 -07:00
parent d48f24911a
commit 562310bed1
18 changed files with 664 additions and 435 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,185 @@
/*
Copyright 2018 Intel Corporation
Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var refTable = {};
function Promise(promiseFunc)
{
this._ObjectID = 'promise';
this._internal = { promise: this, func: promiseFunc, completed: false, errors: false, completedArgs: [] };
require('events').EventEmitter.call(this._internal);
this._internal.on('_eventHook', function (eventName, eventCallback)
{
//console.log('hook', eventName, 'errors/' + this.errors + ' completed/' + this.completed);
var r = null;
if (eventName == 'resolved' && !this.errors && this.completed)
{
r = eventCallback.apply(this, this.completedArgs);
if(r!=null)
{
this.emit_returnValue('resolved', r);
}
}
if (eventName == 'rejected' && this.errors && this.completed)
{
eventCallback.apply(this, this.completedArgs);
}
if (eventName == 'settled' && this.completed)
{
eventCallback.apply(this, []);
}
});
this._internal.resolver = function _resolver()
{
_resolver._self.errors = false;
_resolver._self.completed = true;
_resolver._self.completedArgs = [];
var args = ['resolved'];
if (this.emit_returnValue && this.emit_returnValue('resolved') != null)
{
_resolver._self.completedArgs.push(this.emit_returnValue('resolved'));
args.push(this.emit_returnValue('resolved'));
}
else
{
for (var a in arguments)
{
_resolver._self.completedArgs.push(arguments[a]);
args.push(arguments[a]);
}
}
_resolver._self.emit.apply(_resolver._self, args);
_resolver._self.emit('settled');
};
this._internal.rejector = function _rejector()
{
_rejector._self.errors = true;
_rejector._self.completed = true;
_rejector._self.completedArgs = [];
var args = ['rejected'];
for (var a in arguments)
{
_rejector._self.completedArgs.push(arguments[a]);
args.push(arguments[a]);
}
_rejector._self.emit.apply(_rejector._self, args);
_rejector._self.emit('settled');
};
this.catch = function(func)
{
this._internal.once('settled', func);
}
this.finally = function (func)
{
this._internal.once('settled', func);
};
this.then = function (resolved, rejected)
{
if (resolved) { this._internal.once('resolved', resolved); }
if (rejected) { this._internal.once('rejected', rejected); }
var retVal = new Promise(function (r, j) { });
this._internal.once('resolved', retVal._internal.resolver);
this._internal.once('rejected', retVal._internal.rejector);
return (retVal);
};
this._internal.resolver._self = this._internal;
this._internal.rejector._self = this._internal;;
try
{
promiseFunc.call(this, this._internal.resolver, this._internal.rejector);
}
catch(e)
{
this._internal.errors = true;
this._internal.completed = true;
this._internal.completedArgs = [e];
this._internal.emit('rejected', e);
this._internal.emit('settled');
}
if(!this._internal.completed)
{
// Save reference of this object
refTable[this._internal._hashCode()] = this._internal;
this._internal.once('settled', function () { refTable[this._hashCode()] = null; });
}
}
Promise.resolve = function resolve()
{
var retVal = new Promise(function (r, j) { });
var args = [];
for (var i in arguments)
{
args.push(arguments[i]);
}
retVal._internal.resolver.apply(retVal._internal, args);
return (retVal);
};
Promise.reject = function reject() {
var retVal = new Promise(function (r, j) { });
var args = [];
for (var i in arguments) {
args.push(arguments[i]);
}
retVal._internal.rejector.apply(retVal._internal, args);
return (retVal);
};
Promise.all = function all(promiseList)
{
var ret = new Promise(function (res, rej)
{
this.__rejector = rej;
this.__resolver = res;
this.__promiseList = promiseList;
this.__done = false;
this.__count = 0;
});
for (var i in promiseList)
{
promiseList[i].then(function ()
{
// Success
if(++ret.__count == ret.__promiseList.length)
{
ret.__done = true;
ret.__resolver(ret.__promiseList);
}
}, function (arg)
{
// Failure
if(!ret.__done)
{
ret.__done = true;
ret.__rejector(arg);
}
});
}
if (promiseList.length == 0)
{
ret.__resolver(promiseList);
}
return (ret);
};
module.exports = Promise;

View File

@ -109,29 +109,16 @@ function serviceManager()
} }
return admin; return admin;
}; };
this.getProgramFolder = function getProgramFolder() {
if (require('os').arch() == 'x64') { // 64 bit Windows
if (this.GM.PointerSize == 4) { return process.env['ProgramFiles(x86)']; } // 32 Bit App
return process.env['ProgramFiles']; // 64 bit App
}
return process.env['ProgramFiles']; // 32 bit Windows
};
this.getServiceFolder = function getServiceFolder() this.getServiceFolder = function getServiceFolder()
{ {
var destinationFolder = null; return this.getProgramFolder() + '\\mesh';
if (require('os').arch() == 'x64')
{
// 64 bit Windows
if (this.GM.PointerSize == 4)
{
// 32 Bit App
destinationFolder = process.env['ProgramFiles(x86)'];
}
else
{
// 64 bit App
destinationFolder = process.env['ProgramFiles'];
}
}
else
{
// 32 bit Windows
destinationFolder = process.env['ProgramFiles'];
}
return (destinationFolder + '\\mesh');
}; };
this.enumerateService = function () { this.enumerateService = function () {

View File

@ -6,23 +6,29 @@
* @version v0.0.1 * @version v0.0.1
*/ */
'use strict'; /*xjslint node: true */
/*xjslint plusplus: true */
/*xjslint maxlen: 256 */
/*jshint node: true */
/*jshint strict: false */
/*jshint esversion: 6 */
"use strict";
// If app metrics is available // If app metrics is available
if (process.argv[2] == '--launch') { try { require('appmetrics-dash').monitor({ url: '/', title: 'MeshCentral', port: 88, host: '127.0.0.1' }); } catch (e) { } } if (process.argv[2] == '--launch') { try { require('appmetrics-dash').monitor({ url: '/', title: 'MeshCentral', port: 88, host: '127.0.0.1' }); } catch (e) { } }
function CreateMeshCentralServer(config, args) { function CreateMeshCentralServer(config, args) {
var obj = {}; var obj = {};
obj.db; obj.db = null;
obj.webserver; obj.webserver = null;
obj.redirserver; obj.redirserver = null;
obj.mpsserver; obj.mpsserver = null;
obj.swarmserver; obj.swarmserver = null;
obj.mailserver; obj.mailserver = null;
obj.amtEventHandler; obj.amtEventHandler = null;
obj.amtScanner; obj.amtScanner = null;
obj.meshScanner; obj.meshScanner = null;
obj.letsencrypt; obj.letsencrypt = null;
obj.eventsDispatch = {}; obj.eventsDispatch = {};
obj.fs = require('fs'); obj.fs = require('fs');
obj.path = require('path'); obj.path = require('path');
@ -78,13 +84,14 @@ function CreateMeshCentralServer(config, args) {
// Start the Meshcentral server // Start the Meshcentral server
obj.Start = function () { obj.Start = function () {
var i;
try { require('./pass').hash('test', function () { }); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not. try { require('./pass').hash('test', function () { }); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not.
// Check for invalid arguments // Check for invalid arguments
var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify']; var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify'];
for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } } for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } }
if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; } if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; }
for (var i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence. for (i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence.
if ((obj.args.help == true) || (obj.args['?'] == true)) { if ((obj.args.help == true) || (obj.args['?'] == true)) {
console.log('MeshCentral2 Beta 2, a web-based remote computer management web portal.\r\n'); console.log('MeshCentral2 Beta 2, a web-based remote computer management web portal.\r\n');
@ -106,19 +113,19 @@ function CreateMeshCentralServer(config, args) {
console.log(' country and organization can optionaly be set.'); console.log(' country and organization can optionaly be set.');
return; return;
} }
// Check if we need to install, start, stop, remove ourself as a background service // Check if we need to install, start, stop, remove ourself as a background service
if ((obj.service != null) && ((obj.args.install == true) || (obj.args.uninstall == true) || (obj.args.start == true) || (obj.args.stop == true) || (obj.args.restart == true))) { if ((obj.service != null) && ((obj.args.install == true) || (obj.args.uninstall == true) || (obj.args.start == true) || (obj.args.stop == true) || (obj.args.restart == true))) {
var env = [], xenv = ['user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'exactport', 'debug']; var env = [], xenv = ['user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'exactport', 'debug'];
for (var i in xenv) { if (obj.args[xenv[i]] != null) { env.push({ name: 'mesh' + xenv[i], value: obj.args[xenv[i]] }); } } // Set some args as service environement variables. for (i in xenv) { if (obj.args[xenv[i]] != null) { env.push({ name: 'mesh' + xenv[i], value: obj.args[xenv[i]] }); } } // Set some args as service environement variables.
var svc = new obj.service({ name: 'MeshCentral', description: 'MeshCentral Remote Management Server', script: obj.path.join(__dirname, 'winservice.js'), env: env, wait: 2, grow: .5 }); var svc = new obj.service({ name: 'MeshCentral', description: 'MeshCentral Remote Management Server', script: obj.path.join(__dirname, 'winservice.js'), env: env, wait: 2, grow: 0.5 });
svc.on('install', function () { console.log('MeshCentral service installed.'); svc.start(); }); svc.on('install', function () { console.log('MeshCentral service installed.'); svc.start(); });
svc.on('uninstall', function () { console.log('MeshCentral service uninstalled.'); process.exit(); }); svc.on('uninstall', function () { console.log('MeshCentral service uninstalled.'); process.exit(); });
svc.on('start', function () { console.log('MeshCentral service started.'); process.exit(); }); svc.on('start', function () { console.log('MeshCentral service started.'); process.exit(); });
svc.on('stop', function () { console.log('MeshCentral service stopped.'); if (obj.args.stop) { process.exit(); } if (obj.args.restart) { console.log('Holding 5 seconds...'); setTimeout(function () { svc.start(); }, 5000); } }); svc.on('stop', function () { console.log('MeshCentral service stopped.'); if (obj.args.stop) { process.exit(); } if (obj.args.restart) { console.log('Holding 5 seconds...'); setTimeout(function () { svc.start(); }, 5000); } });
svc.on('alreadyinstalled', function () { console.log('MeshCentral service already installed.'); process.exit(); }); svc.on('alreadyinstalled', function () { console.log('MeshCentral service already installed.'); process.exit(); });
svc.on('invalidinstallation', function () { console.log('Invalid MeshCentral service installation.'); process.exit(); }); svc.on('invalidinstallation', function () { console.log('Invalid MeshCentral service installation.'); process.exit(); });
if (obj.args.install == true) { try { svc.install(); } catch (e) { logException(e); } } if (obj.args.install == true) { try { svc.install(); } catch (e) { logException(e); } }
if (obj.args.stop == true || obj.args.restart == true) { try { svc.stop(); } catch (e) { logException(e); } } if (obj.args.stop == true || obj.args.restart == true) { try { svc.stop(); } catch (e) { logException(e); } }
if (obj.args.start == true || obj.args.restart == true) { try { svc.start(); } catch (e) { logException(e); } } if (obj.args.start == true || obj.args.restart == true) { try { svc.start(); } catch (e) { logException(e); } }
@ -132,7 +139,7 @@ function CreateMeshCentralServer(config, args) {
} else { } else {
// if "--launch" is not specified, launch the server as a child process. // if "--launch" is not specified, launch the server as a child process.
var startLine = ''; var startLine = '';
for (var i in process.argv) { for (i in process.argv) {
var arg = process.argv[i]; var arg = process.argv[i];
if (arg.length > 0) { if (arg.length > 0) {
if (startLine.length > 0) startLine += ' '; if (startLine.length > 0) startLine += ' ';
@ -141,7 +148,7 @@ function CreateMeshCentralServer(config, args) {
} }
obj.launchChildServer(startLine); obj.launchChildServer(startLine);
} }
} };
// Launch MeshCentral as a child server and monitor it. // Launch MeshCentral as a child server and monitor it.
obj.launchChildServer = function (startLine) { obj.launchChildServer = function (startLine) {
@ -166,7 +173,7 @@ function CreateMeshCentralServer(config, args) {
console.log(error); console.log(error);
console.log('ERROR: MeshCentral failed with critical error, check MeshErrors.txt. Restarting in 5 seconds...'); console.log('ERROR: MeshCentral failed with critical error, check MeshErrors.txt. Restarting in 5 seconds...');
setTimeout(function () { obj.launchChildServer(startLine); }, 5000); setTimeout(function () { obj.launchChildServer(startLine); }, 5000);
} }
} }
}); });
xprocess.stdout.on('data', function (data) { if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); } if (data.indexOf('Updating settings folder...') >= 0) { xprocess.xrestart = 1; } else if (data.indexOf('Updating server certificates...') >= 0) { xprocess.xrestart = 1; } else if (data.indexOf('Server Ctrl-C exit...') >= 0) { xprocess.xrestart = 2; } else if (data.indexOf('Starting self upgrade...') >= 0) { xprocess.xrestart = 3; } console.log(data); }); xprocess.stdout.on('data', function (data) { if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); } if (data.indexOf('Updating settings folder...') >= 0) { xprocess.xrestart = 1; } else if (data.indexOf('Updating server certificates...') >= 0) { xprocess.xrestart = 1; } else if (data.indexOf('Server Ctrl-C exit...') >= 0) { xprocess.xrestart = 2; } else if (data.indexOf('Starting self upgrade...') >= 0) { xprocess.xrestart = 3; } console.log(data); });
@ -175,7 +182,7 @@ function CreateMeshCentralServer(config, args) {
if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); } obj.fs.appendFileSync(obj.getConfigFilePath('mesherrors.txt'), '-------- ' + new Date().toLocaleString() + ' --------\r\n\r\n' + data + '\r\n\r\n\r\n'); if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); } obj.fs.appendFileSync(obj.getConfigFilePath('mesherrors.txt'), '-------- ' + new Date().toLocaleString() + ' --------\r\n\r\n' + data + '\r\n\r\n\r\n');
}); });
xprocess.on('close', function (code) { if ((code != 0) && (code != 123)) { /* console.log("Exited with code " + code); */ } }); xprocess.on('close', function (code) { if ((code != 0) && (code != 123)) { /* console.log("Exited with code " + code); */ } });
} };
// Get current and latest MeshCentral server versions using NPM // Get current and latest MeshCentral server versions using NPM
obj.getLatestServerVersion = function (callback) { obj.getLatestServerVersion = function (callback) {
@ -190,44 +197,45 @@ function CreateMeshCentralServer(config, args) {
if (code == 0) { try { latestVer = xprocess.data.split(' ').join('').split('\r').join('').split('\n').join(''); } catch (e) { } } if (code == 0) { try { latestVer = xprocess.data.split(' ').join('').split('\r').join('').split('\n').join(''); } catch (e) { } }
callback(obj.currentVer, latestVer); callback(obj.currentVer, latestVer);
}); });
} };
// Initiate server self-update // Initiate server self-update
obj.performServerUpdate = function () { console.log('Starting self upgrade...'); process.exit(200); } obj.performServerUpdate = function () { console.log('Starting self upgrade...'); process.exit(200); };
// Initiate server self-update // Initiate server self-update
obj.performServerCertUpdate = function () { console.log('Updating server certificates...'); process.exit(200); } obj.performServerCertUpdate = function () { console.log('Updating server certificates...'); process.exit(200); };
obj.StartEx = function () { obj.StartEx = function () {
var i;
//var wincmd = require('node-windows'); //var wincmd = require('node-windows');
//wincmd.list(function (svc) { console.log(svc); }, true); //wincmd.list(function (svc) { console.log(svc); }, true);
// Write the server state // Write the server state
obj.updateServerState('state', 'starting'); obj.updateServerState('state', 'starting');
// Look to see if data and/or file path is specified // Look to see if data and/or file path is specified
if (obj.args.datapath) { obj.datapath = obj.args.datapath; } if (obj.args.datapath) { obj.datapath = obj.args.datapath; }
if (obj.args.filespath) { obj.filespath = obj.args.filespath; } if (obj.args.filespath) { obj.filespath = obj.args.filespath; }
// Read environment variables. For a subset of arguments, we allow them to be read from environment variables. // Read environment variables. For a subset of arguments, we allow them to be read from environment variables.
var xenv = ['user', 'port', 'mpsport', 'mpsaliasport', 'redirport', 'exactport', 'debug']; var xenv = ['user', 'port', 'mpsport', 'mpsaliasport', 'redirport', 'exactport', 'debug'];
for (var i in xenv) { if ((obj.args[xenv[i]] == null) && (process.env['mesh' + xenv[i]])) { obj.args[xenv[i]] = obj.common.toNumber(process.env['mesh' + xenv[i]]); } } for (i in xenv) { if ((obj.args[xenv[i]] == null) && (process.env['mesh' + xenv[i]])) { obj.args[xenv[i]] = obj.common.toNumber(process.env['mesh' + xenv[i]]); } }
// Validate the domains, this is used for multi-hosting // Validate the domains, this is used for multi-hosting
if (obj.config.domains == null) { obj.config.domains = {}; } if (obj.config.domains == null) { obj.config.domains = {}; }
if (obj.config.domains[''] == null) { obj.config.domains[''] = {}; } if (obj.config.domains[''] == null) { obj.config.domains[''] = {}; }
if (obj.config.domains[''].dns != null) { console.log("ERROR: Default domain can't have a DNS name."); return; } if (obj.config.domains[''].dns != null) { console.log("ERROR: Default domain can't have a DNS name."); return; }
var xdomains = {}; for (var i in obj.config.domains) { if (!obj.config.domains[i].title) { obj.config.domains[i].title = 'MeshCentral'; } if (!obj.config.domains[i].title2) { obj.config.domains[i].title2 = '2.0 Beta 2'; } xdomains[i.toLowerCase()] = obj.config.domains[i]; } obj.config.domains = xdomains; var xdomains = {}; for (i in obj.config.domains) { if (!obj.config.domains[i].title) { obj.config.domains[i].title = 'MeshCentral'; } if (!obj.config.domains[i].title2) { obj.config.domains[i].title2 = '2.0 Beta 2'; } xdomains[i.toLowerCase()] = obj.config.domains[i]; } obj.config.domains = xdomains;
var bannedDomains = ['public', 'private', 'images', 'scripts', 'styles', 'views']; // List of banned domains var bannedDomains = ['public', 'private', 'images', 'scripts', 'styles', 'views']; // List of banned domains
for (var i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in ./data/config.json."); return; } } } for (i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in ./data/config.json."); return; } } }
for (var i in obj.config.domains) { for (i in obj.config.domains) {
if (obj.config.domains[i].dns == null) { obj.config.domains[i].url = (i == '') ? '/' : ('/' + i + '/'); } else { obj.config.domains[i].url = '/'; } if (obj.config.domains[i].dns == null) { obj.config.domains[i].url = (i == '') ? '/' : ('/' + i + '/'); } else { obj.config.domains[i].url = '/'; }
obj.config.domains[i].id = i; obj.config.domains[i].id = i;
if (typeof obj.config.domains[i].userallowedip == 'string') { obj.config.domains[i].userallowedip = null; if (obj.config.domains[i].userallowedip != "") { obj.config.domains[i].userallowedip = obj.config.domains[i].userallowedip.split(','); } } if (typeof obj.config.domains[i].userallowedip == 'string') { obj.config.domains[i].userallowedip = null; if (obj.config.domains[i].userallowedip != "") { obj.config.domains[i].userallowedip = obj.config.domains[i].userallowedip.split(','); } }
} }
// Log passed arguments into Windows Service Log // Log passed arguments into Windows Service Log
//if (obj.servicelog != null) { var s = ''; for (var i in obj.args) { if (i != '_') { if (s.length > 0) { s += ', '; } s += i + "=" + obj.args[i]; } } logInfoEvent('MeshServer started with arguments: ' + s); } //if (obj.servicelog != null) { var s = ''; for (i in obj.args) { if (i != '_') { if (s.length > 0) { s += ', '; } s += i + "=" + obj.args[i]; } } logInfoEvent('MeshServer started with arguments: ' + s); }
// Look at passed in arguments // Look at passed in arguments
if ((obj.args.user != null) && (typeof obj.args.user != 'string')) { delete obj.args.user; } if ((obj.args.user != null) && (typeof obj.args.user != 'string')) { delete obj.args.user; }
@ -269,12 +277,12 @@ function CreateMeshCentralServer(config, args) {
if (obj.args.dbimport == true) { obj.args.dbimport = obj.getConfigFilePath('meshcentral.db.json'); } if (obj.args.dbimport == true) { obj.args.dbimport = obj.getConfigFilePath('meshcentral.db.json'); }
var json = null, json2 = "", badCharCount = 0; var json = null, json2 = "", badCharCount = 0;
try { json = obj.fs.readFileSync(obj.args.dbimport, { encoding: 'utf8' }); } catch (e) { console.log('Invalid JSON file: ' + obj.args.dbimport + '.'); process.exit(); } try { json = obj.fs.readFileSync(obj.args.dbimport, { encoding: 'utf8' }); } catch (e) { console.log('Invalid JSON file: ' + obj.args.dbimport + '.'); process.exit(); }
for (var i = 0; i < json.length; i++) { if (json.charCodeAt(i) >= 32) { json2 += json[i]; } else { var tt = json.charCodeAt(i); if (tt != 10 && tt != 13) { badCharCount++; } } } // Remove all bad chars for (i = 0; i < json.length; i++) { if (json.charCodeAt(i) >= 32) { json2 += json[i]; } else { var tt = json.charCodeAt(i); if (tt != 10 && tt != 13) { badCharCount++; } } } // Remove all bad chars
if (badCharCount > 0) { console.log(badCharCount + ' invalid character(s) where removed.'); } if (badCharCount > 0) { console.log(badCharCount + ' invalid character(s) where removed.'); }
try { json = JSON.parse(json2); } catch (e) { console.log('Invalid JSON format: ' + obj.args.dbimport + ': ' + e); process.exit(); } try { json = JSON.parse(json2); } catch (e) { console.log('Invalid JSON format: ' + obj.args.dbimport + ': ' + e); process.exit(); }
if ((json == null) || (typeof json.length != 'number') || (json.length < 1)) { console.log('Invalid JSON format: ' + obj.args.dbimport + '.'); } if ((json == null) || (typeof json.length != 'number') || (json.length < 1)) { console.log('Invalid JSON format: ' + obj.args.dbimport + '.'); }
for (var i in json) { if ((json[i].type == "mesh") && (json[i].links != null)) { for (var j in json[i].links) { var esc = obj.common.escapeFieldName(j); if (esc !== j) { json[i].links[esc] = json[i].links[j]; delete json[i].links[j]; } } } } // Escape MongoDB invalid field chars for (i in json) { if ((json[i].type == "mesh") && (json[i].links != null)) { for (var j in json[i].links) { var esc = obj.common.escapeFieldName(j); if (esc !== j) { json[i].links[esc] = json[i].links[j]; delete json[i].links[j]; } } } } // Escape MongoDB invalid field chars
//for (var i in json) { if ((json[i].type == "node") && (json[i].host != null)) { json[i].rname = json[i].host; delete json[i].host; } } // DEBUG: Change host to rname //for (i in json) { if ((json[i].type == "node") && (json[i].host != null)) { json[i].rname = json[i].host; delete json[i].host; } } // DEBUG: Change host to rname
obj.db.RemoveAll(function () { obj.db.InsertMany(json, function (err) { if (err != null) { console.log(err); } else { console.log('Imported ' + json.length + ' objects(s) from ' + obj.args.dbimport + '.'); } process.exit(); }); }); obj.db.RemoveAll(function () { obj.db.InsertMany(json, function (err) { if (err != null) { console.log(err); } else { console.log('Imported ' + json.length + ' objects(s) from ' + obj.args.dbimport + '.'); } process.exit(); }); });
return; return;
} }
@ -333,7 +341,7 @@ function CreateMeshCentralServer(config, args) {
obj.db.Get('dbconfig', function (err, dbconfig) { obj.db.Get('dbconfig', function (err, dbconfig) {
if (dbconfig.length == 1) { obj.dbconfig = dbconfig[0]; } else { obj.dbconfig = { _id: 'dbconfig', version: 1 }; } if (dbconfig.length == 1) { obj.dbconfig = dbconfig[0]; } else { obj.dbconfig = { _id: 'dbconfig', version: 1 }; }
if (obj.dbconfig.amtWsEventSecret == null) { require('crypto').randomBytes(32, function (err, buf) { obj.dbconfig.amtWsEventSecret = buf.toString('hex'); obj.db.Set(obj.dbconfig); }); } if (obj.dbconfig.amtWsEventSecret == null) { require('crypto').randomBytes(32, function (err, buf) { obj.dbconfig.amtWsEventSecret = buf.toString('hex'); obj.db.Set(obj.dbconfig); }); }
// This is used by the user to create a username/password for a Intel AMT WSMAN event subscription // This is used by the user to create a username/password for a Intel AMT WSMAN event subscription
if (obj.args.getwspass) { if (obj.args.getwspass) {
if (obj.args.getwspass.length == 64) { if (obj.args.getwspass.length == 64) {
@ -367,12 +375,12 @@ function CreateMeshCentralServer(config, args) {
} }
}); });
}); });
} };
// Done starting the redirection server, go on to load the server certificates // Done starting the redirection server, go on to load the server certificates
obj.StartEx2 = function () { obj.StartEx2 = function () {
// Load server certificates // Load server certificates
obj.certificateOperations = require('./certoperations.js').CertificateOperations() obj.certificateOperations = require('./certoperations.js').CertificateOperations();
obj.certificateOperations.GetMeshServerCertificate(obj, obj.args, obj.config, function (certs) { obj.certificateOperations.GetMeshServerCertificate(obj, obj.args, obj.config, function (certs) {
if (obj.config.letsencrypt == null) { if (obj.config.letsencrypt == null) {
obj.StartEx3(certs); // Just use the configured certificates obj.StartEx3(certs); // Just use the configured certificates
@ -387,10 +395,11 @@ function CreateMeshCentralServer(config, args) {
} }
} }
}); });
} };
// Start the server with the given certificates // Start the server with the given certificates
obj.StartEx3 = function (certs) { obj.StartEx3 = function (certs) {
var i;
obj.certificates = certs; obj.certificates = certs;
obj.certificateOperations.acceleratorStart(certs); // Set the state of the accelerators obj.certificateOperations.acceleratorStart(certs); // Set the state of the accelerators
@ -398,14 +407,14 @@ function CreateMeshCentralServer(config, args) {
if (obj.certificates.CommonName == 'un-configured') { console.log('Server name not configured, running in LAN-only mode.'); obj.args.lanonly = true; } if (obj.certificates.CommonName == 'un-configured') { console.log('Server name not configured, running in LAN-only mode.'); obj.args.lanonly = true; }
// Check that no sub-domains have the same DNS as the parent // Check that no sub-domains have the same DNS as the parent
for (var i in obj.config.domains) { for (i in obj.config.domains) {
if ((obj.config.domains[i].dns != null) && (obj.certificates.CommonName.toLowerCase() === obj.config.domains[i].dns.toLowerCase())) { if ((obj.config.domains[i].dns != null) && (obj.certificates.CommonName.toLowerCase() === obj.config.domains[i].dns.toLowerCase())) {
console.log("ERROR: Server sub-domain can't have same DNS name as the parent."); process.exit(0); return; console.log("ERROR: Server sub-domain can't have same DNS name as the parent."); process.exit(0); return;
} }
} }
// Load the list of mesh agents and install scripts // Load the list of mesh agents and install scripts
if (obj.args.noagentupdate == 1) { for (var i in obj.meshAgentsArchitectureNumbers) { obj.meshAgentsArchitectureNumbers[i].update = false; } } if (obj.args.noagentupdate == 1) { for (i in obj.meshAgentsArchitectureNumbers) { obj.meshAgentsArchitectureNumbers[i].update = false; } }
obj.updateMeshAgentsTable(function () { obj.updateMeshAgentsTable(function () {
obj.updateMeshAgentInstallScripts(); obj.updateMeshAgentInstallScripts();
@ -460,7 +469,7 @@ function CreateMeshCentralServer(config, args) {
obj.maintenanceTimer = setInterval(obj.maintenanceActions, 1000 * 60 * 60); // Run this every hour obj.maintenanceTimer = setInterval(obj.maintenanceActions, 1000 * 60 * 60); // Run this every hour
// Dispatch an event that the server is now running // Dispatch an event that the server is now running
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' }) obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' });
// Load the login cookie encryption key from the database if allowed // Load the login cookie encryption key from the database if allowed
if ((obj.config) && (obj.config.settings) && (obj.config.settings.allowlogintoken == true)) { if ((obj.config) && (obj.config.settings) && (obj.config.settings.allowlogintoken == true)) {
@ -473,12 +482,12 @@ function CreateMeshCentralServer(config, args) {
}); });
} }
obj.debug(1, 'Server started'); //obj.debug(1, 'Server started');
if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); } if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); }
obj.updateServerState('state', 'running'); obj.updateServerState('state', 'running');
}); });
}); });
} };
// Perform maintenance operations (called every hour) // Perform maintenance operations (called every hour)
obj.maintenanceActions = function () { obj.maintenanceActions = function () {
@ -499,7 +508,7 @@ function CreateMeshCentralServer(config, args) {
// Perform other database cleanup // Perform other database cleanup
obj.db.cleanup(); obj.db.cleanup();
} };
// Stop the Meshcentral server // Stop the Meshcentral server
obj.Stop = function (restoreFile) { obj.Stop = function (restoreFile) {
@ -507,7 +516,7 @@ function CreateMeshCentralServer(config, args) {
if (!obj.db) return; if (!obj.db) return;
// Dispatch an event saying the server is now stopping // Dispatch an event saying the server is now stopping
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'stopped', msg: 'Server stopped' }) obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'stopped', msg: 'Server stopped' });
// Set all nodes to power state of unknown (0) // Set all nodes to power state of unknown (0)
var record = { type: 'power', time: Date.now(), node: '*', power: 0, s: 2 }; var record = { type: 'power', time: Date.now(), node: '*', power: 0, s: 2 };
@ -543,7 +552,7 @@ function CreateMeshCentralServer(config, args) {
}); });
} }
}); });
zipfile.on("end", function () { setTimeout(function () { fs.unlinkSync(restoreFile); process.exit(123); }); }); zipfile.on("end", function () { setTimeout(function () { obj.fs.unlinkSync(restoreFile); process.exit(123); }); });
}); });
} else { } else {
obj.debug(1, 'Server stopped'); obj.debug(1, 'Server stopped');
@ -553,25 +562,25 @@ function CreateMeshCentralServer(config, args) {
// Update the server state // Update the server state
obj.updateServerState('state', 'stopped'); obj.updateServerState('state', 'stopped');
} };
// Event Dispatch // Event Dispatch
obj.AddEventDispatch = function (ids, target) { obj.AddEventDispatch = function (ids, target) {
obj.debug(3, 'AddEventDispatch', ids); obj.debug(3, 'AddEventDispatch', ids);
for (var i in ids) { var id = ids[i]; if (!obj.eventsDispatch[id]) { obj.eventsDispatch[id] = [target]; } else { obj.eventsDispatch[id].push(target); } } for (var i in ids) { var id = ids[i]; if (!obj.eventsDispatch[id]) { obj.eventsDispatch[id] = [target]; } else { obj.eventsDispatch[id].push(target); } }
} };
obj.RemoveEventDispatch = function (ids, target) { obj.RemoveEventDispatch = function (ids, target) {
obj.debug(3, 'RemoveEventDispatch', id); obj.debug(3, 'RemoveEventDispatch', id);
for (var i in ids) { var id = ids[i]; if (obj.eventsDispatch[id]) { var j = obj.eventsDispatch[id].indexOf(target); if (j >= 0) { array.splice(j, 1); } } } for (var i in ids) { var id = ids[i]; if (obj.eventsDispatch[id]) { var j = obj.eventsDispatch[id].indexOf(target); if (j >= 0) { obj.eventsDispatch[id].splice(j, 1); } } }
} };
obj.RemoveEventDispatchId = function (id) { obj.RemoveEventDispatchId = function (id) {
obj.debug(3, 'RemoveEventDispatchId', id); obj.debug(3, 'RemoveEventDispatchId', id);
if (obj.eventsDispatch[id] != null) { delete obj.eventsDispatch[id]; } if (obj.eventsDispatch[id] != null) { delete obj.eventsDispatch[id]; }
} };
obj.RemoveAllEventDispatch = function (target) { obj.RemoveAllEventDispatch = function (target) {
obj.debug(3, 'RemoveAllEventDispatch'); obj.debug(3, 'RemoveAllEventDispatch');
for (var i in obj.eventsDispatch) { var j = obj.eventsDispatch[i].indexOf(target); if (j >= 0) { obj.eventsDispatch[i].splice(j, 1); } } for (var i in obj.eventsDispatch) { var j = obj.eventsDispatch[i].indexOf(target); if (j >= 0) { obj.eventsDispatch[i].splice(j, 1); } }
} };
obj.DispatchEvent = function (ids, source, event, fromPeerServer) { obj.DispatchEvent = function (ids, source, event, fromPeerServer) {
// If the database is not setup, exit now. // If the database is not setup, exit now.
if (!obj.db) return; if (!obj.db) return;
@ -596,21 +605,21 @@ function CreateMeshCentralServer(config, args) {
} }
} }
if ((fromPeerServer == null) && (obj.multiServer != null) && ((typeof event != 'object') || (event.nopeers != 1))) { obj.multiServer.DispatchEvent(ids, source, event); } if ((fromPeerServer == null) && (obj.multiServer != null) && ((typeof event != 'object') || (event.nopeers != 1))) { obj.multiServer.DispatchEvent(ids, source, event); }
} };
// Get the connection state of a node // Get the connection state of a node
obj.GetConnectivityState = function (nodeid) { return obj.connectivityByNode[nodeid]; } obj.GetConnectivityState = function (nodeid) { return obj.connectivityByNode[nodeid]; };
// Get the routing server id for a given node and connection type, can never be self. // Get the routing server id for a given node and connection type, can never be self.
obj.GetRoutingServerId = function (nodeid, connectType) { obj.GetRoutingServerId = function (nodeid, connectType) {
if (obj.multiServer == null) return null; if (obj.multiServer == null) return null;
for (serverid in obj.peerConnectivityByNode) { for (var serverid in obj.peerConnectivityByNode) {
if (serverid == obj.serverId) continue; if (serverid == obj.serverId) continue;
var state = obj.peerConnectivityByNode[serverid][nodeid]; var state = obj.peerConnectivityByNode[serverid][nodeid];
if ((state != null) && ((state.connectivity & connectType) != 0)) { return { serverid: serverid, meshid: state.meshid }; } if ((state != null) && ((state.connectivity & connectType) != 0)) { return { serverid: serverid, meshid: state.meshid }; }
} }
return null; return null;
} };
// Update the connection state of a node when in multi-server mode // Update the connection state of a node when in multi-server mode
// Update obj.connectivityByNode using obj.peerConnectivityByNode for the list of nodes in argument // Update obj.connectivityByNode using obj.peerConnectivityByNode for the list of nodes in argument
@ -618,8 +627,8 @@ function CreateMeshCentralServer(config, args) {
for (var nodeid in nodeids) { for (var nodeid in nodeids) {
var meshid = null, state = null, oldConnectivity = 0, oldPowerState = 0, newConnectivity = 0, newPowerState = 0; var meshid = null, state = null, oldConnectivity = 0, oldPowerState = 0, newConnectivity = 0, newPowerState = 0;
var oldState = obj.connectivityByNode[nodeid]; var oldState = obj.connectivityByNode[nodeid];
if (oldState != null) { meshid = oldState.meshid; oldConnectivity = oldState.connectivity; oldPowerState = oldState.powerState; } if (oldState != null) { meshid = oldState.meshid; oldConnectivity = oldState.connectivity; oldPowerState = oldState.powerState; }
for (serverid in obj.peerConnectivityByNode) { for (var serverid in obj.peerConnectivityByNode) {
var peerState = obj.peerConnectivityByNode[serverid][nodeid]; var peerState = obj.peerConnectivityByNode[serverid][nodeid];
if (peerState != null) { if (peerState != null) {
if (state == null) { if (state == null) {
@ -652,7 +661,7 @@ function CreateMeshCentralServer(config, args) {
obj.DispatchEvent(['*', meshid], obj, { action: 'nodeconnect', meshid: meshid, nodeid: nodeid, conn: newConnectivity, pwr: newPowerState, nolog: 1, nopeers: 1 }); obj.DispatchEvent(['*', meshid], obj, { action: 'nodeconnect', meshid: meshid, nodeid: nodeid, conn: newConnectivity, pwr: newPowerState, nolog: 1, nopeers: 1 });
} }
} }
} };
// Set the connectivity state of a node and setup the server so that messages can be routed correctly. // Set the connectivity state of a node and setup the server so that messages can be routed correctly.
// meshId: mesh identifier of format mesh/domain/meshidhex // meshId: mesh identifier of format mesh/domain/meshidhex
@ -660,8 +669,8 @@ function CreateMeshCentralServer(config, args) {
// connectTime: time of connection, milliseconds elapsed since the UNIX epoch. // connectTime: time of connection, milliseconds elapsed since the UNIX epoch.
// connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local. // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local.
// powerState: Value, 0 = Unknown, 1 = S0 power on, 2 = S1 Sleep, 3 = S2 Sleep, 4 = S3 Sleep, 5 = S4 Hibernate, 6 = S5 Soft-Off, 7 = Present // powerState: Value, 0 = Unknown, 1 = S0 power on, 2 = S1 Sleep, 3 = S2 Sleep, 4 = S3 Sleep, 5 = S4 Hibernate, 6 = S5 Soft-Off, 7 = Present
var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local']; //var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local'];
var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present']; //var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present'];
obj.SetConnectivityState = function (meshid, nodeid, connectTime, connectType, powerState, serverid) { obj.SetConnectivityState = function (meshid, nodeid, connectTime, connectType, powerState, serverid) {
//console.log('SetConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + ', Power: ' + powerStateStrings[powerState] + (serverid == null ? ('') : (', ServerId: ' + serverid))); //console.log('SetConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + ', Power: ' + powerStateStrings[powerState] + (serverid == null ? ('') : (', ServerId: ' + serverid)));
if ((serverid == null) && (obj.multiServer != null)) { obj.multiServer.DispatchMessage({ action: 'SetConnectivityState', meshid: meshid, nodeid: nodeid, connectTime: connectTime, connectType: connectType, powerState: powerState }); } if ((serverid == null) && (obj.multiServer != null)) { obj.multiServer.DispatchMessage({ action: 'SetConnectivityState', meshid: meshid, nodeid: nodeid, connectTime: connectTime, connectType: connectType, powerState: powerState }); }
@ -716,7 +725,7 @@ function CreateMeshCentralServer(config, args) {
// Set node power state // Set node power state
if (connectType == 1) { state.agentPower = powerState; } else if (connectType == 2) { state.ciraPower = powerState; } else if (connectType == 4) { state.amtPower = powerState; } if (connectType == 1) { state.agentPower = powerState; } else if (connectType == 2) { state.ciraPower = powerState; } else if (connectType == 4) { state.amtPower = powerState; }
var powerState = 0; var powerState = 0, oldPowerState = state.powerState;
if ((state.connectivity & 1) != 0) { powerState = state.agentPower; } else if ((state.connectivity & 2) != 0) { powerState = state.ciraPower; } else if ((state.connectivity & 4) != 0) { powerState = state.amtPower; } if ((state.connectivity & 1) != 0) { powerState = state.agentPower; } else if ((state.connectivity & 2) != 0) { powerState = state.ciraPower; } else if ((state.connectivity & 4) != 0) { powerState = state.amtPower; }
if ((state.powerState == null) || (state.powerState != powerState)) { if ((state.powerState == null) || (state.powerState != powerState)) {
state.powerState = powerState; state.powerState = powerState;
@ -731,7 +740,7 @@ function CreateMeshCentralServer(config, args) {
var x = {}; x[nodeid] = 1; var x = {}; x[nodeid] = 1;
obj.UpdateConnectivityState(x); obj.UpdateConnectivityState(x);
} }
} };
// Clear the connectivity state of a node and setup the server so that messages can be routed correctly. // Clear the connectivity state of a node and setup the server so that messages can be routed correctly.
// meshId: mesh identifier of format mesh/domain/meshidhex // meshId: mesh identifier of format mesh/domain/meshidhex
@ -798,7 +807,7 @@ function CreateMeshCentralServer(config, args) {
var x = {}; x[nodeid] = 1; var x = {}; x[nodeid] = 1;
obj.UpdateConnectivityState(x); obj.UpdateConnectivityState(x);
} }
} };
// Update the default mesh core // Update the default mesh core
obj.updateMeshCoreTimer = 'notset'; obj.updateMeshCoreTimer = 'notset';
@ -845,9 +854,9 @@ function CreateMeshCentralServer(config, args) {
obj.fs.watch(obj.path.join(meshcorePath, 'meshcore.js'), function (eventType, filename) { obj.fs.watch(obj.path.join(meshcorePath, 'meshcore.js'), function (eventType, filename) {
if (obj.updateMeshCoreTimer != null) { clearTimeout(obj.updateMeshCoreTimer); obj.updateMeshCoreTimer = null; } if (obj.updateMeshCoreTimer != null) { clearTimeout(obj.updateMeshCoreTimer); obj.updateMeshCoreTimer = null; }
obj.updateMeshCoreTimer = setTimeout(function () { obj.updateMeshCore(); console.log('Updated meshcore.js.'); }, 5000); obj.updateMeshCoreTimer = setTimeout(function () { obj.updateMeshCore(); console.log('Updated meshcore.js.'); }, 5000);
}) });
} }
} };
// Update the default meshcmd // Update the default meshcmd
obj.updateMeshCmdTimer = 'notset'; obj.updateMeshCmdTimer = 'notset';
@ -860,7 +869,7 @@ function CreateMeshCentralServer(config, args) {
obj.defaultMeshCmd = null; if (func != null) { func(false); } // meshcmd.js not found obj.defaultMeshCmd = null; if (func != null) { func(false); } // meshcmd.js not found
} }
} }
// Read meshcore.js and all .js files in the modules folder. // Read meshcore.js and all .js files in the modules folder.
var moduleAdditions = 'var addedModules = [];', modulesDir = null; var moduleAdditions = 'var addedModules = [];', modulesDir = null;
var meshCmd = obj.fs.readFileSync(obj.path.join(meshcmdPath, 'meshcmd.js')).toString().replace("'***Mesh*Cmd*Version***'", '\'' + obj.currentVer + '\''); var meshCmd = obj.fs.readFileSync(obj.path.join(meshcmdPath, 'meshcmd.js')).toString().replace("'***Mesh*Cmd*Version***'", '\'' + obj.currentVer + '\'');
@ -886,9 +895,9 @@ function CreateMeshCentralServer(config, args) {
obj.fs.watch(obj.path.join(meshcmdPath, 'meshcmd.js'), function (eventType, filename) { obj.fs.watch(obj.path.join(meshcmdPath, 'meshcmd.js'), function (eventType, filename) {
if (obj.updateMeshCmdTimer != null) { clearTimeout(obj.updateMeshCmdTimer); obj.updateMeshCmdTimer = null; } if (obj.updateMeshCmdTimer != null) { clearTimeout(obj.updateMeshCmdTimer); obj.updateMeshCmdTimer = null; }
obj.updateMeshCmdTimer = setTimeout(function () { obj.updateMeshCmd(); console.log('Updated meshcmd.js.'); }, 5000); obj.updateMeshCmdTimer = setTimeout(function () { obj.updateMeshCmd(); console.log('Updated meshcmd.js.'); }, 5000);
}) });
} }
} };
// List of possible mesh agent install scripts // List of possible mesh agent install scripts
var meshAgentsInstallScriptList = { var meshAgentsInstallScriptList = {
@ -903,7 +912,7 @@ function CreateMeshCentralServer(config, args) {
var stream = null; var stream = null;
try { try {
stream = obj.fs.createReadStream(scriptpath); stream = obj.fs.createReadStream(scriptpath);
stream.on('data', function (data) { this.hash.update(data, 'binary') }); stream.on('data', function (data) { this.hash.update(data, 'binary'); });
stream.on('error', function (data) { stream.on('error', function (data) {
// If there is an error reading this file, make sure this agent is not in the agent table // If there is an error reading this file, make sure this agent is not in the agent table
if (obj.meshAgentInstallScripts[this.info.id] != null) { delete obj.meshAgentInstallScripts[this.info.id]; } if (obj.meshAgentInstallScripts[this.info.id] != null) { delete obj.meshAgentInstallScripts[this.info.id]; }
@ -915,7 +924,7 @@ function CreateMeshCentralServer(config, args) {
obj.meshAgentInstallScripts[this.info.id].path = this.agentpath; obj.meshAgentInstallScripts[this.info.id].path = this.agentpath;
obj.meshAgentInstallScripts[this.info.id].url = ((obj.args.notls == true) ? 'http://' : 'https://') + obj.certificates.CommonName + ':' + obj.args.port + '/meshagents?script=' + this.info.id; obj.meshAgentInstallScripts[this.info.id].url = ((obj.args.notls == true) ? 'http://' : 'https://') + obj.certificates.CommonName + ':' + obj.args.port + '/meshagents?script=' + this.info.id;
var stats = null; var stats = null;
try { stats = obj.fs.statSync(this.agentpath) } catch (e) { } try { stats = obj.fs.statSync(this.agentpath); } catch (e) { }
if (stats != null) { obj.meshAgentInstallScripts[this.info.id].size = stats.size; } if (stats != null) { obj.meshAgentInstallScripts[this.info.id].size = stats.size; }
}); });
stream.info = meshAgentsInstallScriptList[scriptid]; stream.info = meshAgentsInstallScriptList[scriptid];
@ -923,7 +932,7 @@ function CreateMeshCentralServer(config, args) {
stream.hash = obj.crypto.createHash('sha384', stream); stream.hash = obj.crypto.createHash('sha384', stream);
} catch (e) { } } catch (e) { }
} }
} };
// List of possible mesh agents // List of possible mesh agents
obj.meshAgentsArchitectureNumbers = { obj.meshAgentsArchitectureNumbers = {
@ -962,10 +971,10 @@ function CreateMeshCentralServer(config, args) {
var archcount = 0; var archcount = 0;
for (var archid in obj.meshAgentsArchitectureNumbers) { for (var archid in obj.meshAgentsArchitectureNumbers) {
var agentpath = obj.path.join(__dirname, 'agents', obj.meshAgentsArchitectureNumbers[archid].localname); var agentpath = obj.path.join(__dirname, 'agents', obj.meshAgentsArchitectureNumbers[archid].localname);
// Fetch all the agent binary information // Fetch all the agent binary information
var stats = null; var stats = null;
try { stats = obj.fs.statSync(agentpath) } catch (e) { } try { stats = obj.fs.statSync(agentpath); } catch (e) { }
if ((stats != null)) { if ((stats != null)) {
// If file exists // If file exists
archcount++; archcount++;
@ -991,7 +1000,7 @@ function CreateMeshCentralServer(config, args) {
} }
if ((obj.meshAgentBinaries[3] == null) && (obj.meshAgentBinaries[10003] != null)) { obj.meshAgentBinaries[3] = obj.meshAgentBinaries[10003]; } // If only the unsigned windows binaries are present, use them. if ((obj.meshAgentBinaries[3] == null) && (obj.meshAgentBinaries[10003] != null)) { obj.meshAgentBinaries[3] = obj.meshAgentBinaries[10003]; } // If only the unsigned windows binaries are present, use them.
if ((obj.meshAgentBinaries[4] == null) && (obj.meshAgentBinaries[10004] != null)) { obj.meshAgentBinaries[4] = obj.meshAgentBinaries[10004]; } // If only the unsigned windows binaries are present, use them. if ((obj.meshAgentBinaries[4] == null) && (obj.meshAgentBinaries[10004] != null)) { obj.meshAgentBinaries[4] = obj.meshAgentBinaries[10004]; } // If only the unsigned windows binaries are present, use them.
} };
// Generate a time limited user login token // Generate a time limited user login token
obj.getLoginToken = function (userid, func) { obj.getLoginToken = function (userid, func) {
@ -1016,7 +1025,7 @@ function CreateMeshCentralServer(config, args) {
}); });
} }
}); });
} };
// Show the yser login token generation key // Show the yser login token generation key
obj.showLoginTokenKey = function (func) { obj.showLoginTokenKey = function (func) {
@ -1031,13 +1040,13 @@ function CreateMeshCentralServer(config, args) {
obj.db.Set({ _id: 'LoginCookieEncryptionKey', key: obj.loginCookieEncryptionKey.toString('hex'), time: Date.now() }, function () { func(obj.loginCookieEncryptionKey.toString('hex')); }); obj.db.Set({ _id: 'LoginCookieEncryptionKey', key: obj.loginCookieEncryptionKey.toString('hex'), time: Date.now() }, function () { func(obj.loginCookieEncryptionKey.toString('hex')); });
} }
}); });
} };
// Generate a cryptographic key used to encode and decode cookies // Generate a cryptographic key used to encode and decode cookies
obj.generateCookieKey = function () { obj.generateCookieKey = function () {
return new Buffer(obj.crypto.randomBytes(32), 'binary'); return new Buffer(obj.crypto.randomBytes(32), 'binary');
//return Buffer.alloc(32, 0); // Sets the key to zeros, debug only. //return Buffer.alloc(32, 0); // Sets the key to zeros, debug only.
} };
// Encode an object as a cookie using a key. (key must be 32 bytes long) // Encode an object as a cookie using a key. (key must be 32 bytes long)
obj.encodeCookie = function (o, key) { obj.encodeCookie = function (o, key) {
@ -1048,7 +1057,7 @@ function CreateMeshCentralServer(config, args) {
var crypted = Buffer.concat([cipher.update(JSON.stringify(o), 'utf8'), cipher.final()]); var crypted = Buffer.concat([cipher.update(JSON.stringify(o), 'utf8'), cipher.final()]);
return Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); return Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
} catch (e) { return null; } } catch (e) { return null; }
} };
// Decode a cookie back into an object using a key. Return null if it's not a valid cookie. (key must be 32 bytes long) // Decode a cookie back into an object using a key. Return null if it's not a valid cookie. (key must be 32 bytes long)
obj.decodeCookie = function (cookie, key, timeout) { obj.decodeCookie = function (cookie, key, timeout) {
@ -1065,7 +1074,7 @@ function CreateMeshCentralServer(config, args) {
if ((o.dtime > (timeout * 60000)) || (o.dtime < -30000)) return null; // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right) if ((o.dtime > (timeout * 60000)) || (o.dtime < -30000)) return null; // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
return o; return o;
} catch (e) { return null; } } catch (e) { return null; }
} };
// Debug // Debug
obj.debug = function (lvl) { obj.debug = function (lvl) {
@ -1074,11 +1083,11 @@ function CreateMeshCentralServer(config, args) {
else if (arguments.length == 3) { console.log(arguments[1], arguments[2]); } else if (arguments.length == 3) { console.log(arguments[1], arguments[2]); }
else if (arguments.length == 4) { console.log(arguments[1], arguments[2], arguments[3]); } else if (arguments.length == 4) { console.log(arguments[1], arguments[2], arguments[3]); }
else if (arguments.length == 5) { console.log(arguments[1], arguments[2], arguments[3], arguments[4]); } else if (arguments.length == 5) { console.log(arguments[1], arguments[2], arguments[3], arguments[4]); }
} };
// Update server state. Writes a server state file. // Update server state. Writes a server state file.
var meshServerState = {}; var meshServerState = {};
obj.updateServerState = function(name, val) { obj.updateServerState = function (name, val) {
if ((name != null) && (val != null)) { if ((name != null) && (val != null)) {
var changed = false; var changed = false;
if ((name != null) && (meshServerState[name] != val)) { if ((val == null) && (meshServerState[name] != null)) { delete meshServerState[name]; changed = true; } else { if (meshServerState[name] != val) { meshServerState[name] = val; changed = true; } } } if ((name != null) && (meshServerState[name] != val)) { if ((val == null) && (meshServerState[name] != null)) { delete meshServerState[name]; changed = true; } else { if (meshServerState[name] != val) { meshServerState[name] = val; changed = true; } } }
@ -1087,7 +1096,7 @@ function CreateMeshCentralServer(config, args) {
var r = 'time=' + Date.now() + '\r\n'; var r = 'time=' + Date.now() + '\r\n';
for (var i in meshServerState) { r += (i + '=' + meshServerState[i] + '\r\n'); } for (var i in meshServerState) { r += (i + '=' + meshServerState[i] + '\r\n'); }
obj.fs.writeFileSync(obj.getConfigFilePath('serverstate.txt'), r); obj.fs.writeFileSync(obj.getConfigFilePath('serverstate.txt'), r);
} };
// Logging funtions // Logging funtions
function logException(e) { e += ''; logErrorEvent(e); } function logException(e) { e += ''; logErrorEvent(e); }
@ -1124,7 +1133,7 @@ function CreateMeshCentralServer(config, args) {
} }
//console.log('getConfigFilePath(\"' + filename + '\") = ' + obj.path.join(obj.datapath, filename)); //console.log('getConfigFilePath(\"' + filename + '\") = ' + obj.path.join(obj.datapath, filename));
return obj.path.join(obj.datapath, filename); return obj.path.join(obj.datapath, filename);
} };
return obj; return obj;
} }
@ -1132,6 +1141,7 @@ function CreateMeshCentralServer(config, args) {
// Return the server configuration // Return the server configuration
function getConfig() { function getConfig() {
// Figure out the datapath location // Figure out the datapath location
var i;
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var datapath = null; var datapath = null;
@ -1150,7 +1160,7 @@ function getConfig() {
// Load and validate the configuration file // Load and validate the configuration file
try { config = require(configFilePath); } catch (e) { console.log('ERROR: Unable to parse ' + configFilePath + '.'); return null; } try { config = require(configFilePath); } catch (e) { console.log('ERROR: Unable to parse ' + configFilePath + '.'); return null; }
if (config.domains == null) { config.domains = {}; } if (config.domains == null) { config.domains = {}; }
for (var i in config.domains) { if ((i.split('/').length > 1) || (i.split(' ').length > 1)) { console.log("ERROR: Error in config.json, domain names can't have spaces or /."); return null; } } for (i in config.domains) { if ((i.split('/').length > 1) || (i.split(' ').length > 1)) { console.log("ERROR: Error in config.json, domain names can't have spaces or /."); return null; } }
} else { } else {
// Copy the "sample-config.json" to give users a starting point // Copy the "sample-config.json" to give users a starting point
var sampleConfigPath = path.join(__dirname, 'sample-config.json'); var sampleConfigPath = path.join(__dirname, 'sample-config.json');
@ -1159,7 +1169,7 @@ function getConfig() {
// Set the command line arguments to the config file if they are not present // Set the command line arguments to the config file if they are not present
if (!config.settings) { config.settings = {}; } if (!config.settings) { config.settings = {}; }
for (var i in args) { config.settings[i] = args[i]; } for (i in args) { config.settings[i] = args[i]; }
// Lower case all keys in the config file // Lower case all keys in the config file
require('./common.js').objKeysToLower(config); require('./common.js').objKeysToLower(config);

View File

@ -6,7 +6,12 @@
* @version v0.0.1 * @version v0.0.1
*/ */
'use strict'; /*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict";
module.exports.CreateMeshRelay = function (parent, ws, req, domain) { module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
var obj = {}; var obj = {};
@ -23,9 +28,10 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
obj.close = function (arg) { obj.close = function (arg) {
if ((arg == 1) || (arg == null)) { try { obj.ws.close(); obj.parent.parent.debug(1, 'Relay: Soft disconnect (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket if ((arg == 1) || (arg == null)) { try { obj.ws.close(); obj.parent.parent.debug(1, 'Relay: Soft disconnect (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
if (arg == 2) { try { obj.ws._socket._parent.end(); obj.parent.parent.debug(1, 'Relay: Hard disconnect (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket if (arg == 2) { try { obj.ws._socket._parent.end(); obj.parent.parent.debug(1, 'Relay: Hard disconnect (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
} };
obj.sendAgentMessage = function (command, userid, domainid) { obj.sendAgentMessage = function (command, userid, domainid) {
var rights;
if (command.nodeid == null) return false; if (command.nodeid == null) return false;
var user = obj.parent.users[userid]; var user = obj.parent.users[userid];
if (user == null) return false; if (user == null) return false;
@ -37,7 +43,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
var agent = obj.parent.wsagents[command.nodeid]; var agent = obj.parent.wsagents[command.nodeid];
if (agent != null) { if (agent != null) {
// Check if we have permission to send a message to that node // Check if we have permission to send a message to that node
var rights = user.links[agent.dbMeshKey]; rights = user.links[agent.dbMeshKey];
if (rights != null || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking if (rights != null || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking
command.sessionid = ws.sessionId; // Set the session id, required for responses. command.sessionid = ws.sessionId; // Set the session id, required for responses.
command.rights = rights.rights; // Add user rights flags to the message command.rights = rights.rights; // Add user rights flags to the message
@ -50,7 +56,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
var routing = obj.parent.parent.GetRoutingServerId(command.nodeid, 1); // 1 = MeshAgent routing type var routing = obj.parent.parent.GetRoutingServerId(command.nodeid, 1); // 1 = MeshAgent routing type
if (routing != null) { if (routing != null) {
// Check if we have permission to send a message to that node // Check if we have permission to send a message to that node
var rights = user.links[routing.meshid]; rights = user.links[routing.meshid];
if (rights != null || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking if (rights != null || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking
command.fromSessionid = ws.sessionId; // Set the session id, required for responses. command.fromSessionid = ws.sessionId; // Set the session id, required for responses.
command.rights = rights.rights; // Add user rights flags to the message command.rights = rights.rights; // Add user rights flags to the message
@ -61,7 +67,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
} }
} }
return false; return false;
} };
if (req.query.auth == null) { if (req.query.auth == null) {
// Use ExpressJS session, check if this session is a logged in user, at least one of the two connections will need to be authenticated. // Use ExpressJS session, check if this session is a logged in user, at least one of the two connections will need to be authenticated.
@ -160,7 +166,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
// Wait for other relay connection // Wait for other relay connection
ws.pause(); // Hold traffic until the other connection ws.pause(); // Hold traffic until the other connection
parent.wsrelays[obj.id] = { peer1: obj, state: 1 }; parent.wsrelays[obj.id] = { peer1: obj, state: 1 };
obj.parent.parent.debug(1, 'Relay holding: ' + obj.id + ' (' + obj.remoteaddr + ') ' + (obj.authenticated?'Authenticated':'') ); obj.parent.parent.debug(1, 'Relay holding: ' + obj.id + ' (' + obj.remoteaddr + ') ' + (obj.authenticated ? 'Authenticated' : ''));
// Check if a peer server has this connection // Check if a peer server has this connection
if (parent.parent.multiServer != null) { if (parent.parent.multiServer != null) {
@ -213,6 +219,6 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
obj.id = null; obj.id = null;
} }
}); });
return obj; return obj;
} };

View File

@ -6,7 +6,12 @@
* @version v0.0.1 * @version v0.0.1
*/ */
'use strict'; /*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict";
// Construct a Mesh Scanner object // Construct a Mesh Scanner object
// TODO: We need once "server4" and "server6" per interface, or change the default multicast interface as we send. // TODO: We need once "server4" and "server6" per interface, or change the default multicast interface as we send.
@ -26,9 +31,10 @@ module.exports.CreateMeshScanner = function (parent) {
// Get a list of IPv4 and IPv6 interface addresses // Get a list of IPv4 and IPv6 interface addresses
function getInterfaceList() { function getInterfaceList() {
var i;
var ipv4 = ['*'], ipv6 = ['*']; // Bind to IN_ADDR_ANY always var ipv4 = ['*'], ipv6 = ['*']; // Bind to IN_ADDR_ANY always
var interfaces = require('os').networkInterfaces(); var interfaces = require('os').networkInterfaces();
for (var i in interfaces) { for (i in interfaces) {
var xinterface = interfaces[i]; var xinterface = interfaces[i];
for (var j in xinterface) { for (var j in xinterface) {
var interface2 = xinterface[j]; var interface2 = xinterface[j];
@ -43,11 +49,11 @@ module.exports.CreateMeshScanner = function (parent) {
// Setup all IPv4 and IPv6 servers // Setup all IPv4 and IPv6 servers
function setupServers() { function setupServers() {
var addresses = getInterfaceList(); var addresses = getInterfaceList(), i, localAddress, bindOptions;
for (var i in obj.servers4) { obj.servers4[i].xxclear = true; } for (i in obj.servers4) { obj.servers4[i].xxclear = true; }
for (var i in obj.servers6) { obj.servers6[i].xxclear = true; } for (i in obj.servers6) { obj.servers6[i].xxclear = true; }
for (var i in addresses.ipv4) { for (i in addresses.ipv4) {
var localAddress = addresses.ipv4[i]; localAddress = addresses.ipv4[i];
if (obj.servers4[localAddress] != null) { if (obj.servers4[localAddress] != null) {
// Server already exists // Server already exists
obj.servers4[localAddress].xxclear = false; obj.servers4[localAddress].xxclear = false;
@ -59,7 +65,7 @@ module.exports.CreateMeshScanner = function (parent) {
server4.xxtype = 4; server4.xxtype = 4;
server4.xxlocal = localAddress; server4.xxlocal = localAddress;
server4.on('error', function (err) { if (this.xxlocal == '*') { console.log("ERROR: Server port 16989 not available, check if server is running twice."); } this.close(); delete obj.servers6[this.xxlocal]; }); server4.on('error', function (err) { if (this.xxlocal == '*') { console.log("ERROR: Server port 16989 not available, check if server is running twice."); } this.close(); delete obj.servers6[this.xxlocal]; });
var bindOptions = { port: 16989, exclusive: true }; bindOptions = { port: 16989, exclusive: true };
if (server4.xxlocal != '*') { bindOptions.address = server4.xxlocal; } if (server4.xxlocal != '*') { bindOptions.address = server4.xxlocal; }
server4.bind(bindOptions, function () { server4.bind(bindOptions, function () {
try { try {
@ -77,8 +83,8 @@ module.exports.CreateMeshScanner = function (parent) {
} }
} }
for (var i in addresses.ipv6) { for (i in addresses.ipv6) {
var localAddress = addresses.ipv6[i]; localAddress = addresses.ipv6[i];
if (obj.servers6[localAddress] != null) { if (obj.servers6[localAddress] != null) {
// Server already exists // Server already exists
obj.servers6[localAddress].xxclear = false; obj.servers6[localAddress].xxclear = false;
@ -90,7 +96,7 @@ module.exports.CreateMeshScanner = function (parent) {
server6.xxtype = 6; server6.xxtype = 6;
server6.xxlocal = localAddress; server6.xxlocal = localAddress;
server6.on('error', function (err) { this.close(); delete obj.servers6[this.xxlocal]; }); server6.on('error', function (err) { this.close(); delete obj.servers6[this.xxlocal]; });
var bindOptions = { port: 16989, exclusive: true }; bindOptions = { port: 16989, exclusive: true };
if (server6.xxlocal != '*') { bindOptions.address = server6.xxlocal; } if (server6.xxlocal != '*') { bindOptions.address = server6.xxlocal; }
server6.bind(bindOptions, function () { server6.bind(bindOptions, function () {
try { try {
@ -108,14 +114,15 @@ module.exports.CreateMeshScanner = function (parent) {
} }
} }
for (var i in obj.servers4) { if (obj.servers4[i].xxclear == true) { obj.servers4[i].close(); delete obj.servers4[i]; }; } for (i in obj.servers4) { if (obj.servers4[i].xxclear == true) { obj.servers4[i].close(); delete obj.servers4[i]; } }
for (var i in obj.servers6) { if (obj.servers6[i].xxclear == true) { obj.servers6[i].close(); delete obj.servers6[i]; }; } for (i in obj.servers6) { if (obj.servers6[i].xxclear == true) { obj.servers6[i].close(); delete obj.servers6[i]; } }
} }
// Clear all IPv4 and IPv6 servers // Clear all IPv4 and IPv6 servers
function clearServers() { function clearServers() {
for (var i in obj.servers4) { obj.servers4[i].close(); delete obj.servers4[i]; } var i;
for (var i in obj.servers6) { obj.servers6[i].close(); delete obj.servers6[i]; } for (i in obj.servers4) { obj.servers4[i].close(); delete obj.servers4[i]; }
for (i in obj.servers6) { obj.servers6[i].close(); delete obj.servers6[i]; }
} }
// Start scanning for local network Mesh Agents // Start scanning for local network Mesh Agents
@ -128,27 +135,28 @@ module.exports.CreateMeshScanner = function (parent) {
setupServers(); setupServers();
obj.mainTimer = setInterval(obj.performScan, periodicScanTime); obj.mainTimer = setInterval(obj.performScan, periodicScanTime);
return obj; return obj;
} };
// Stop scanning for local network Mesh Agents // Stop scanning for local network Mesh Agents
obj.stop = function () { obj.stop = function () {
if (obj.mainTimer != null) { clearInterval(obj.mainTimer); obj.mainTimer = null; } if (obj.mainTimer != null) { clearInterval(obj.mainTimer); obj.mainTimer = null; }
clearServers(); clearServers();
} };
// Look for all Mesh Agents that may be locally reachable, indicating the presense of this server. // Look for all Mesh Agents that may be locally reachable, indicating the presense of this server.
obj.performScan = function (server) { obj.performScan = function (server) {
var i;
if (server != null) { if (server != null) {
if (server.xxtype == 4) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } catch (e) { } } if (server.xxtype == 4) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } catch (e) { } }
if (server.xxtype == 6) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } catch (e) { } } if (server.xxtype == 6) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } catch (e) { } }
if ((server.xxtype == 4) && (server.xxlocal == '*')) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '127.0.0.1'); } catch (e) { } try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '255.255.255.255'); } catch (e) { } } if ((server.xxtype == 4) && (server.xxlocal == '*')) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '127.0.0.1'); } catch (e) { } try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '255.255.255.255'); } catch (e) { } }
if ((server.xxtype == 6) && (server.xxlocal == '*')) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, '::1'); } catch (e) { } } if ((server.xxtype == 6) && (server.xxlocal == '*')) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, '::1'); } catch (e) { } }
} else { } else {
for (var i in obj.servers4) { try { obj.servers4[i].send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } catch (e) { } } for (i in obj.servers4) { try { obj.servers4[i].send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } catch (e) { } }
for (var i in obj.servers6) { try { obj.servers6[i].send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } catch (e) { } } for (i in obj.servers6) { try { obj.servers6[i].send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } catch (e) { } }
setupServers(); // Check if any network interfaces where added or removed setupServers(); // Check if any network interfaces where added or removed
} }
} };
// Called when a UDP packet is received from an agent. // Called when a UDP packet is received from an agent.
function onUdpPacket(msg, info, server) { function onUdpPacket(msg, info, server) {
@ -161,26 +169,27 @@ module.exports.CreateMeshScanner = function (parent) {
// As a side job, we also send server wake-on-lan packets // As a side job, we also send server wake-on-lan packets
obj.wakeOnLan = function (macs) { obj.wakeOnLan = function (macs) {
for (var i in macs) { var i, j;
for (i in macs) {
var mac = macs[i]; var mac = macs[i];
var hexpacket = 'FFFFFFFFFFFF'; var hexpacket = 'FFFFFFFFFFFF';
for (var i = 0; i < 16; i++) { hexpacket += mac; } for (j = 0; j < 16; j++) { hexpacket += mac; }
var wakepacket = Buffer.from(hexpacket, 'hex'); var wakepacket = Buffer.from(hexpacket, 'hex');
//console.log(wakepacket.toString('hex')); //console.log(wakepacket.toString('hex'));
// Send the wake packet 3 times with small time intervals // Send the wake packet 3 times with small time intervals
for (var i in obj.servers4) { obj.servers4[i].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); } for (j in obj.servers4) { obj.servers4[j].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); }
for (var i in obj.servers6) { obj.servers6[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); } for (j in obj.servers6) { obj.servers6[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
setTimeout(function () { setTimeout(function () {
for (var i in obj.servers4) { obj.servers4[i].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); } for (j in obj.servers4) { obj.servers4[j].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); }
for (var i in obj.servers6) { obj.servers6[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); } for (j in obj.servers6) { obj.servers6[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
}, 200); }, 200);
setTimeout(function () { setTimeout(function () {
for (var i in obj.servers4) { obj.servers4[i].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); } for (j in obj.servers4) { obj.servers4[j].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); }
for (var i in obj.servers6) { obj.servers6[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); } for (j in obj.servers6) { obj.servers6[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
}, 500); }, 500);
} }
} };
return obj; return obj;
} };

View File

@ -6,7 +6,12 @@
* @version v0.0.1 * @version v0.0.1
*/ */
'use strict'; /*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict";
// Construct a MeshAgent object, called upon connection // Construct a MeshAgent object, called upon connection
module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
@ -27,7 +32,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
obj.close = function (arg) { obj.close = function (arg) {
if ((arg == 1) || (arg == null)) { try { obj.ws.close(); obj.parent.parent.debug(1, 'Soft disconnect'); } catch (e) { console.log(e); } } // Soft close, close the websocket if ((arg == 1) || (arg == null)) { try { obj.ws.close(); obj.parent.parent.debug(1, 'Soft disconnect'); } catch (e) { console.log(e); } } // Soft close, close the websocket
if (arg == 2) { try { obj.ws._socket._parent.end(); obj.parent.parent.debug(1, 'Hard disconnect'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket if (arg == 2) { try { obj.ws._socket._parent.end(); obj.parent.parent.debug(1, 'Hard disconnect'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
} };
// Convert a mesh path array into a real path on the server side // Convert a mesh path array into a real path on the server side
function meshPathToRealPath(meshpath, user) { function meshPathToRealPath(meshpath, user) {
@ -52,22 +57,22 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// //
function copyFile(src, dest, func, tag) { function copyFile(src, dest, func, tag) {
//var ss = obj.fs.createReadStream(src, { flags: 'rb' }); //var ss = obj.fs.createReadStream(src, { flags: 'rb' });
//var ds = obj.fs.createWriteStream(dest, { flags: 'wb' }); //var ds = obj.fs.createWriteStream(dest, { flags: 'wb' });
var ss = obj.fs.createReadStream(src); var ss = obj.fs.createReadStream(src);
var ds = obj.fs.createWriteStream(dest); var ds = obj.fs.createWriteStream(dest);
ss.fs = obj.fs; ss.fs = obj.fs;
ss.pipe(ds); ss.pipe(ds);
ds.ss = ss; ds.ss = ss;
/* /*
if (!this._copyStreams) { this._copyStreams = {}; this._copyStreamID = 0; } if (!this._copyStreams) { this._copyStreams = {}; this._copyStreamID = 0; }
ss.id = this._copyStreamID++; ss.id = this._copyStreamID++;
this._copyStreams[ss.id] = ss; this._copyStreams[ss.id] = ss;
*/ */
if (arguments.length == 3 && typeof arguments[2] === 'function') { ds.on('close', arguments[2]); } if (arguments.length == 3 && typeof arguments[2] === 'function') { ds.on('close', arguments[2]); }
else if (arguments.length == 4 && typeof arguments[3] === 'function') { ds.on('close', arguments[3]); } else if (arguments.length == 4 && typeof arguments[3] === 'function') { ds.on('close', arguments[3]); }
ds.on('close', function() { /*delete this.ss.fs._copyStreams[this.ss.id];*/ func(tag); }); ds.on('close', function () { /*delete this.ss.fs._copyStreams[this.ss.id];*/ func(tag); });
}; }
try { try {
// Check if the user is logged in // Check if the user is logged in
@ -83,7 +88,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
obj.parent.wssessions2[ws.sessionId] = obj.ws; obj.parent.wssessions2[ws.sessionId] = obj.ws;
if (!obj.parent.wssessions[user._id]) { obj.parent.wssessions[user._id] = [ws]; } else { obj.parent.wssessions[user._id].push(obj.ws); } if (!obj.parent.wssessions[user._id]) { obj.parent.wssessions[user._id] = [ws]; } else { obj.parent.wssessions[user._id].push(obj.ws); }
if (obj.parent.parent.multiServer == null) { if (obj.parent.parent.multiServer == null) {
obj.parent.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: user.name, count: obj.parent.wssessions[user._id].length, nolog: 1, domain: obj.domain.id }) obj.parent.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: user.name, count: obj.parent.wssessions[user._id].length, nolog: 1, domain: obj.domain.id });
} else { } else {
obj.parent.recountSessions(obj.ws.sessionId); // Recount sessions obj.parent.recountSessions(obj.ws.sessionId); // Recount sessions
} }
@ -101,14 +106,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
else { ws.send(JSON.stringify({ action: 'event', event: event })); } else { ws.send(JSON.stringify({ action: 'event', event: event })); }
} catch (e) { } } catch (e) { }
} }
} };
user.subscriptions = obj.parent.subscribe(user._id, ws); // Subscribe to events user.subscriptions = obj.parent.subscribe(user._id, ws); // Subscribe to events
obj.ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive obj.ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive
// When data is received from the web socket // When data is received from the web socket
ws.on('message', function (msg) { ws.on('message', function (msg) {
var command, user = obj.parent.users[req.session.userid]; var command, user = obj.parent.users[req.session.userid], i = 0, mesh = null, meshid = null, nodeid = null, meshlinks = null, change = 0;
try { command = JSON.parse(msg.toString('utf8')); } catch (e) { return; } try { command = JSON.parse(msg.toString('utf8')); } catch (e) { return; }
if ((user == null) || (obj.common.validateString(command.action, 3, 32) == false)) return; // User must be set and action must be a string between 3 and 32 chars if ((user == null) || (obj.common.validateString(command.action, 3, 32) == false)) return; // User must be set and action must be a string between 3 and 32 chars
@ -118,7 +123,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
{ {
// Request a list of all meshes this user as rights to // Request a list of all meshes this user as rights to
var docs = []; var docs = [];
for (var i in user.links) { if (obj.parent.meshes[i]) { docs.push(obj.parent.meshes[i]); } } for (i in user.links) { if (obj.parent.meshes[i]) { docs.push(obj.parent.meshes[i]); } }
ws.send(JSON.stringify({ action: 'meshes', meshes: docs, tag: command.tag })); ws.send(JSON.stringify({ action: 'meshes', meshes: docs, tag: command.tag }));
break; break;
} }
@ -127,10 +132,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var links = []; var links = [];
if (command.meshid == null) { if (command.meshid == null) {
// Request a list of all meshes this user as rights to // Request a list of all meshes this user as rights to
for (var i in user.links) { links.push(i); } for (i in user.links) { links.push(i); }
} else { } else {
// Request list of all nodes for one specific meshid // Request list of all nodes for one specific meshid
var meshid = command.meshid; meshid = command.meshid;
if (obj.common.validateString(meshid, 0, 128) == false) return; if (obj.common.validateString(meshid, 0, 128) == false) return;
if (meshid.split('/').length == 0) { meshid = 'mesh/' + domain.id + '/' + command.meshid; } if (meshid.split('/').length == 0) { meshid = 'mesh/' + domain.id + '/' + command.meshid; }
if (user.links[meshid] != null) { links.push(meshid); } if (user.links[meshid] != null) { links.push(meshid); }
@ -139,7 +144,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Request a list of all nodes // Request a list of all nodes
obj.db.GetAllTypeNoTypeFieldMeshFiltered(links, domain.id, 'node', function (err, docs) { obj.db.GetAllTypeNoTypeFieldMeshFiltered(links, domain.id, 'node', function (err, docs) {
var r = {}; var r = {};
for (var i in docs) { for (i in docs) {
// Add the connection state // Add the connection state
var state = obj.parent.parent.GetConnectivityState(docs[i]._id); var state = obj.parent.parent.GetConnectivityState(docs[i]._id);
if (state) { if (state) {
@ -150,7 +155,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
} }
// Compress the meshid's // Compress the meshid's
var meshid = docs[i].meshid; meshid = docs[i].meshid;
if (!r[meshid]) { r[meshid] = []; } if (!r[meshid]) { r[meshid] = []; }
delete docs[i].meshid; delete docs[i].meshid;
@ -171,7 +176,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
obj.db.getPowerTimeline(command.nodeid, function (err, docs) { obj.db.getPowerTimeline(command.nodeid, function (err, docs) {
if (err == null && docs.length > 0) { if (err == null && docs.length > 0) {
var timeline = [], time = null, previousPower; var timeline = [], time = null, previousPower;
for (var i in docs) { for (i in docs) {
var doc = docs[i]; var doc = docs[i];
if (time == null) { if (time == null) {
// First element // First element
@ -228,14 +233,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if (path == null) break; if (path == null) break;
if ((command.fileop == 'createfolder') && (obj.common.IsFilenameValid(command.newfolder) == true)) { try { obj.fs.mkdirSync(path + "/" + command.newfolder); } catch (e) { } } // Create a new folder if ((command.fileop == 'createfolder') && (obj.common.IsFilenameValid(command.newfolder) == true)) { try { obj.fs.mkdirSync(path + "/" + command.newfolder); } catch (e) { } } // Create a new folder
else if (command.fileop == 'delete') { if (obj.common.validateArray(command.delfiles, 1) == false) return; for (var i in command.delfiles) { if (obj.common.IsFilenameValid(command.delfiles[i]) == true) { var fullpath = path + "/" + command.delfiles[i]; try { obj.fs.rmdirSync(fullpath); } catch (e) { try { obj.fs.unlinkSync(fullpath); } catch (e) { } } } } } // Delete else if (command.fileop == 'delete') { if (obj.common.validateArray(command.delfiles, 1) == false) return; for (i in command.delfiles) { if (obj.common.IsFilenameValid(command.delfiles[i]) == true) { var fullpath = path + "/" + command.delfiles[i]; try { obj.fs.rmdirSync(fullpath); } catch (e) { try { obj.fs.unlinkSync(fullpath); } catch (e) { } } } } } // Delete
else if ((command.fileop == 'rename') && (obj.common.IsFilenameValid(command.oldname) == true) && (obj.common.IsFilenameValid(command.newname) == true)) { try { obj.fs.renameSync(path + "/" + command.oldname, path + "/" + command.newname); } catch (e) { } } // Rename else if ((command.fileop == 'rename') && (obj.common.IsFilenameValid(command.oldname) == true) && (obj.common.IsFilenameValid(command.newname) == true)) { try { obj.fs.renameSync(path + "/" + command.oldname, path + "/" + command.newname); } catch (e) { } } // Rename
else if ((command.fileop == 'copy') || (command.fileop == 'move')) { else if ((command.fileop == 'copy') || (command.fileop == 'move')) {
if (obj.common.validateArray(command.names, 1) == false) return; if (obj.common.validateArray(command.names, 1) == false) return;
var scpath = meshPathToRealPath(command.scpath, user); // This will also check access rights var scpath = meshPathToRealPath(command.scpath, user); // This will also check access rights
if (scpath == null) break; if (scpath == null) break;
// TODO: Check quota if this is a copy!!!!!!!!!!!!!!!! // TODO: Check quota if this is a copy!!!!!!!!!!!!!!!!
for (var i in command.names) { for (i in command.names) {
var s = obj.path.join(scpath, command.names[i]), d = obj.path.join(path, command.names[i]); var s = obj.path.join(scpath, command.names[i]), d = obj.path.join(path, command.names[i]);
sendUpdate = false; sendUpdate = false;
copyFile(s, d, function (op) { if (op != null) { obj.fs.unlink(op, function () { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); }); } else { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); } }, ((command.fileop == 'move') ? s : null)); copyFile(s, d, function (op) { if (op != null) { obj.fs.unlink(op, function () { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); }); } else { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); } }, ((command.fileop == 'move') ? s : null));
@ -319,7 +324,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Delete all events // Delete all events
if (user.siteadmin != 0xFFFFFFFF) break; if (user.siteadmin != 0xFFFFFFFF) break;
obj.db.RemoveAllEvents(domain.id); obj.db.RemoveAllEvents(domain.id);
obj.parent.parent.DispatchEvent(['*', 'server-global'], obj, { action: 'clearevents', nolog: 1, domain: domain.id }) obj.parent.parent.DispatchEvent(['*', 'server-global'], obj, { action: 'clearevents', nolog: 1, domain: domain.id });
break; break;
} }
case 'users': case 'users':
@ -327,7 +332,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Request a list of all users // Request a list of all users
if ((user.siteadmin & 2) == 0) break; if ((user.siteadmin & 2) == 0) break;
var docs = []; var docs = [];
for (var i in obj.parent.users) { for (i in obj.parent.users) {
if ((obj.parent.users[i].domain == domain.id) && (obj.parent.users[i].name != '~')) { if ((obj.parent.users[i].domain == domain.id) && (obj.parent.users[i].name != '~')) {
var userinfo = obj.common.Clone(obj.parent.users[i]); var userinfo = obj.common.Clone(obj.parent.users[i]);
delete userinfo.hash; delete userinfo.hash;
@ -403,10 +408,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if ((user.siteadmin & 2) == 0) break; if ((user.siteadmin & 2) == 0) break;
if (obj.parent.parent.multiServer == null) { if (obj.parent.parent.multiServer == null) {
// No peering, use simple session counting // No peering, use simple session counting
for (var i in obj.parent.wssessions) { if (obj.parent.wssessions[i][0].domainid == domain.id) { wssessions[i] = obj.parent.wssessions[i].length; } } for (i in obj.parent.wssessions) { if (obj.parent.wssessions[i][0].domainid == domain.id) { wssessions[i] = obj.parent.wssessions[i].length; } }
} else { } else {
// We have peer servers, use more complex session counting // We have peer servers, use more complex session counting
for (var userid in obj.parent.sessionsCount) { if (userid.split('/')[1] == domain.id) { wssessions[userid] = obj.parent.sessionsCount[userid]; } } for (i in obj.parent.sessionsCount) { if (i.split('/')[1] == domain.id) { wssessions[i] = obj.parent.sessionsCount[i]; } }
} }
ws.send(JSON.stringify({ action: 'wssessioncount', wssessions: wssessions, tag: command.tag })); // wssessions is: userid --> count ws.send(JSON.stringify({ action: 'wssessioncount', wssessions: wssessions, tag: command.tag })); // wssessions is: userid --> count
break; break;
@ -422,15 +427,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Remove all the mesh links to this user // Remove all the mesh links to this user
if (deluser.links != null) { if (deluser.links != null) {
for (var meshid in deluser.links) { for (meshid in deluser.links) {
// Get the mesh // Get the mesh
var mesh = obj.parent.meshes[meshid]; mesh = obj.parent.meshes[meshid];
if (mesh) { if (mesh) {
// Remove user from the mesh // Remove user from the mesh
if (mesh.links[deluser._id] != null) { delete mesh.links[deluser._id]; obj.parent.db.Set(mesh); } if (mesh.links[deluser._id] != null) { delete mesh.links[deluser._id]; obj.parent.db.Set(mesh); }
// Notify mesh change // Notify mesh change
var change = 'Removed user ' + deluser.name + ' from mesh ' + mesh.name; change = 'Removed user ' + deluser.name + ' from mesh ' + mesh.name;
obj.parent.parent.DispatchEvent(['*', mesh._id, deluser._id, userid], obj, { etype: 'mesh', username: user.name, userid: userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }) obj.parent.parent.DispatchEvent(['*', mesh._id, deluser._id, user._id], obj, { etype: 'mesh', username: user.name, userid: user._id, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id });
} }
} }
} }
@ -446,7 +451,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
obj.db.Remove(deluserid); obj.db.Remove(deluserid);
delete obj.parent.users[deluserid]; delete obj.parent.users[deluserid];
obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: deluserid, username: deluser.name, action: 'accountremove', msg: 'Account removed', domain: domain.id }) obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: deluserid, username: deluser.name, action: 'accountremove', msg: 'Account removed', domain: domain.id });
obj.parent.parent.DispatchEvent([deluserid], obj, 'close'); obj.parent.parent.DispatchEvent([deluserid], obj, 'close');
break; break;
@ -474,7 +479,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if (newuser2.subscriptions) { delete newuser2.subscriptions; } if (newuser2.subscriptions) { delete newuser2.subscriptions; }
if (newuser2.salt) { delete newuser2.salt; } if (newuser2.salt) { delete newuser2.salt; }
if (newuser2.hash) { delete newuser2.hash; } if (newuser2.hash) { delete newuser2.hash; }
obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: newusername, account: newuser2, action: 'accountcreate', msg: 'Account created, email is ' + command.email, domain: domain.id }) obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: newusername, account: newuser2, action: 'accountcreate', msg: 'Account created, email is ' + command.email, domain: domain.id });
}); });
} }
break; break;
@ -483,12 +488,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
{ {
// Edit a user account, may involve changing email or administrator permissions // Edit a user account, may involve changing email or administrator permissions
if (((user.siteadmin & 2) != 0) || (user.name == command.name)) { if (((user.siteadmin & 2) != 0) || (user.name == command.name)) {
var chguserid = 'user/' + domain.id + '/' + command.name.toLowerCase(), chguser = obj.parent.users[chguserid], change = 0; var chguserid = 'user/' + domain.id + '/' + command.name.toLowerCase(), chguser = obj.parent.users[chguserid];
change = 0;
if (chguser) { if (chguser) {
if (obj.common.validateString(command.email, 1, 256) && (chguser.email != command.email)) { chguser.email = command.email; change = 1; } if (obj.common.validateString(command.email, 1, 256) && (chguser.email != command.email)) { chguser.email = command.email; change = 1; }
if ((command.emailVerified === true || command.emailVerified === false) && (chguser.emailVerified != command.emailVerified)) { chguser.emailVerified = command.emailVerified; change = 1; } if ((command.emailVerified === true || command.emailVerified === false) && (chguser.emailVerified != command.emailVerified)) { chguser.emailVerified = command.emailVerified; change = 1; }
if (obj.common.validateInt(command.quota, 0) && (command.quota != chguser.quota)) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; } if (obj.common.validateInt(command.quota, 0) && (command.quota != chguser.quota)) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; }
if ((user.siteadmin == 0xFFFFFFFF) && obj.common.validateInt(command.siteadmin) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1 } if ((user.siteadmin == 0xFFFFFFFF) && obj.common.validateInt(command.siteadmin) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1; }
if (change == 1) { if (change == 1) {
obj.db.SetUser(chguser); obj.db.SetUser(chguser);
obj.parent.parent.DispatchEvent([chguser._id], obj, 'resubscribe'); obj.parent.parent.DispatchEvent([chguser._id], obj, 'resubscribe');
@ -500,7 +506,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
delete userinfo.domain; delete userinfo.domain;
delete userinfo.subscriptions; delete userinfo.subscriptions;
delete userinfo.passtype; delete userinfo.passtype;
obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: userinfo, action: 'accountchange', msg: 'Account changed: ' + command.name, domain: domain.id }) obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: userinfo, action: 'accountchange', msg: 'Account changed: ' + command.name, domain: domain.id });
} }
if ((chguser.siteadmin) && (chguser.siteadmin != 0xFFFFFFFF) && (chguser.siteadmin & 32)) { if ((chguser.siteadmin) && (chguser.siteadmin != 0xFFFFFFFF) && (chguser.siteadmin & 32)) {
obj.parent.parent.DispatchEvent([chguser._id], obj, 'close'); // Disconnect all this user's sessions obj.parent.parent.DispatchEvent([chguser._id], obj, 'close'); // Disconnect all this user's sessions
@ -534,11 +540,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Get the list of sessions for this user // Get the list of sessions for this user
var sessions = obj.parent.wssessions[command.userid]; var sessions = obj.parent.wssessions[command.userid];
if (sessions != null) { for (var i in sessions) { sessions[i].send(JSON.stringify(notification)); } } if (sessions != null) { for (i in sessions) { sessions[i].send(JSON.stringify(notification)); } }
if (obj.parent.parent.multiServer != null) { if (obj.parent.parent.multiServer != null) {
// TODO: Add multi-server support // TODO: Add multi-server support
} }
break;
} }
case 'serverversion': case 'serverversion':
{ {
@ -564,10 +571,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if ((command.meshtype == 1) || (command.meshtype == 2)) { if ((command.meshtype == 1) || (command.meshtype == 2)) {
// Create a type 1 agent-less Intel AMT mesh. // Create a type 1 agent-less Intel AMT mesh.
obj.parent.crypto.randomBytes(48, function (err, buf) { obj.parent.crypto.randomBytes(48, function (err, buf) {
var meshid = 'mesh/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); meshid = 'mesh/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
var links = {} var links = {};
links[user._id] = { name: user.name, rights: 0xFFFFFFFF }; links[user._id] = { name: user.name, rights: 0xFFFFFFFF };
var mesh = { type: 'mesh', _id: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, domain: domain.id, links: links }; mesh = { type: 'mesh', _id: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, domain: domain.id, links: links };
obj.db.Set(obj.common.escapeLinksFieldName(mesh)); obj.db.Set(obj.common.escapeLinksFieldName(mesh));
obj.parent.meshes[meshid] = mesh; obj.parent.meshes[meshid] = mesh;
obj.parent.parent.AddEventDispatch([meshid], ws); obj.parent.parent.AddEventDispatch([meshid], ws);
@ -575,7 +582,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
user.links[meshid] = { rights: 0xFFFFFFFF }; user.links[meshid] = { rights: 0xFFFFFFFF };
user.subscriptions = obj.parent.subscribe(user._id, ws); user.subscriptions = obj.parent.subscribe(user._id, ws);
obj.db.SetUser(user); obj.db.SetUser(user);
obj.parent.parent.DispatchEvent(['*', meshid, user._id], obj, { etype: 'mesh', username: user.name, meshid: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, action: 'createmesh', links: links, msg: 'Mesh created: ' + command.meshname, domain: domain.id }) obj.parent.parent.DispatchEvent(['*', meshid, user._id], obj, { etype: 'mesh', username: user.name, meshid: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, action: 'createmesh', links: links, msg: 'Mesh created: ' + command.meshname, domain: domain.id });
}); });
} }
break; break;
@ -586,17 +593,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid
obj.db.Get(command.meshid, function (err, meshes) { obj.db.Get(command.meshid, function (err, meshes) {
if (meshes.length != 1) return; if (meshes.length != 1) return;
var mesh = obj.common.unEscapeLinksFieldName(meshes[0]); mesh = obj.common.unEscapeLinksFieldName(meshes[0]);
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || mesh.links[user._id].rights != 0xFFFFFFFF) return; if (mesh.links[user._id] == null || mesh.links[user._id].rights != 0xFFFFFFFF) return;
if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
// Fire the removal event first, because after this, the event will not route // Fire the removal event first, because after this, the event will not route
obj.parent.parent.DispatchEvent(['*', command.meshid], obj, { etype: 'mesh', username: user.name, meshid: command.meshid, name: command.meshname, action: 'deletemesh', msg: 'Mesh deleted: ' + command.meshname, domain: domain.id }) obj.parent.parent.DispatchEvent(['*', command.meshid], obj, { etype: 'mesh', username: user.name, meshid: command.meshid, name: command.meshname, action: 'deletemesh', msg: 'Mesh deleted: ' + command.meshname, domain: domain.id });
// Remove all user links to this mesh // Remove all user links to this mesh
for (var i in meshes) { for (i in meshes) {
var links = meshes[i].links; var links = meshes[i].links;
for (var j in links) { for (var j in links) {
var xuser = obj.parent.users[j]; var xuser = obj.parent.users[j];
@ -608,8 +615,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Delete all files on the server for this mesh // Delete all files on the server for this mesh
try { try {
var meshpath = getServerRootFilePath(mesh); var meshpath = obj.parent.getServerRootFilePath(mesh);
if (meshpath != null) { deleteFolderRec(meshpath); } if (meshpath != null) { obj.parent.deleteFolderRec(meshpath); }
} catch (e) { } } catch (e) { }
obj.parent.parent.RemoveEventDispatchId(command.meshid); // Remove all subscriptions to this mesh obj.parent.parent.RemoveEventDispatchId(command.meshid); // Remove all subscriptions to this mesh
@ -622,7 +629,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
{ {
// Change the name or description of a mesh // Change the name or description of a mesh
if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid
var mesh = obj.parent.meshes[command.meshid], change = ''; mesh = obj.parent.meshes[command.meshid];
change = '';
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 1) == 0)) return; if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 1) == 0)) return;
@ -630,7 +638,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if ((obj.common.validateString(command.meshname, 1, 64) == true) && (command.meshname != mesh.name)) { change = 'Mesh name changed from "' + mesh.name + '" to "' + command.meshname + '"'; mesh.name = command.meshname; } if ((obj.common.validateString(command.meshname, 1, 64) == true) && (command.meshname != mesh.name)) { change = 'Mesh name changed from "' + mesh.name + '" to "' + command.meshname + '"'; mesh.name = command.meshname; }
if ((obj.common.validateString(command.desc, 0, 1024) == true) && (command.desc != mesh.desc)) { if (change != '') change += ' and description changed'; else change += 'Mesh "' + mesh.name + '" description changed'; mesh.desc = command.desc; } if ((obj.common.validateString(command.desc, 0, 1024) == true) && (command.desc != mesh.desc)) { if (change != '') change += ' and description changed'; else change += 'Mesh "' + mesh.name + '" description changed'; mesh.desc = command.desc; }
if (change != '') { obj.db.Set(obj.common.escapeLinksFieldName(mesh)); obj.parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, { etype: 'mesh', username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }) } if (change != '') { obj.db.Set(obj.common.escapeLinksFieldName(mesh)); obj.parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, { etype: 'mesh', username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }); }
} }
break; break;
} }
@ -648,7 +656,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
} }
// Get the mesh // Get the mesh
var mesh = obj.parent.meshes[command.meshid], change = ''; mesh = obj.parent.meshes[command.meshid];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 2) == 0)) return; if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 2) == 0)) return;
@ -665,8 +673,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
obj.db.Set(obj.common.escapeLinksFieldName(mesh)); obj.db.Set(obj.common.escapeLinksFieldName(mesh));
// Notify mesh change // Notify mesh change
var change = 'Added user ' + newuser.name + ' to mesh ' + mesh.name; obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, newuserid], obj, { etype: 'mesh', username: newuser.name, userid: command.userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: 'Added user ' + newuser.name + ' to mesh ' + mesh.name, domain: domain.id });
obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, newuserid], obj, { etype: 'mesh', username: newuser.name, userid: command.userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id })
} }
break; break;
} }
@ -677,7 +684,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if ((command.userid.split('/').length != 3) || (command.userid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain if ((command.userid.split('/').length != 3) || (command.userid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
// Get the mesh // Get the mesh
var mesh = obj.parent.meshes[command.meshid]; mesh = obj.parent.meshes[command.meshid];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 2) == 0)) return; if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 2) == 0)) return;
@ -701,8 +708,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
obj.db.Set(obj.common.escapeLinksFieldName(mesh)); obj.db.Set(obj.common.escapeLinksFieldName(mesh));
// Notify mesh change // Notify mesh change
var change = 'Removed user ' + deluser.name + ' from mesh ' + mesh.name; obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, command.userid], obj, { etype: 'mesh', username: user.name, userid: deluser.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: 'Removed user ' + deluser.name + ' from mesh ' + mesh.name, domain: domain.id });
obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, command.userid], obj, { etype: 'mesh', username: user.name, userid: deluser.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id })
} }
} }
break; break;
@ -723,7 +729,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if ((obj.parent.parent.args.wanonly == true) && (command.hostname)) { delete command.hostname; } if ((obj.parent.parent.args.wanonly == true) && (command.hostname)) { delete command.hostname; }
// Get the mesh // Get the mesh
var mesh = obj.parent.meshes[command.meshid]; mesh = obj.parent.meshes[command.meshid];
if (mesh) { if (mesh) {
if (mesh.mtype != 1) return; // This operation is only allowed for mesh type 1, Intel AMT agentless mesh. if (mesh.mtype != 1) return; // This operation is only allowed for mesh type 1, Intel AMT agentless mesh.
@ -733,15 +739,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Create a new nodeid // Create a new nodeid
obj.parent.crypto.randomBytes(48, function (err, buf) { obj.parent.crypto.randomBytes(48, function (err, buf) {
// create the new node // create the new node
var nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');; nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
var device = { type: 'node', mtype: 1, _id: nodeid, meshid: command.meshid, name: command.devicename, host: command.hostname, domain: domain.id, intelamt: { user: command.amtusername, pass: command.amtpassword, tls: command.amttls } }; var device = { type: 'node', mtype: 1, _id: nodeid, meshid: command.meshid, name: command.devicename, host: command.hostname, domain: domain.id, intelamt: { user: command.amtusername, pass: command.amtpassword, tls: command.amttls } };
obj.db.Set(device); obj.db.Set(device);
// Event the new node // Event the new node
var device2 = obj.common.Clone(device); var device2 = obj.common.Clone(device);
delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this. delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
var change = 'Added device ' + command.devicename + ' to mesh ' + mesh.name; obj.parent.parent.DispatchEvent(['*', command.meshid], obj, { etype: 'node', username: user.name, action: 'addnode', node: device2, msg: 'Added device ' + command.devicename + ' to mesh ' + mesh.name, domain: domain.id });
obj.parent.parent.DispatchEvent(['*', command.meshid], obj, { etype: 'node', username: user.name, action: 'addnode', node: device2, msg: change, domain: domain.id })
}); });
} }
break; break;
@ -763,8 +768,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
{ {
if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
for (var i in command.nodeids) { for (i in command.nodeids) {
var nodeid = command.nodeids[i]; nodeid = command.nodeids[i];
if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
@ -774,7 +779,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0]; var node = nodes[0];
// Get the mesh for this device // Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid]; mesh = obj.parent.meshes[node.meshid];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return; if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return;
@ -786,8 +791,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
obj.db.RemoveNode(node._id); // Remove all entries with node:id obj.db.RemoveNode(node._id); // Remove all entries with node:id
// Event node deletion // Event node deletion
var change = 'Removed device ' + node.name + ' from mesh ' + mesh.name; obj.parent.parent.DispatchEvent(['*', node.meshid], obj, { etype: 'node', username: user.name, action: 'removenode', nodeid: node._id, msg: 'Removed device ' + node.name + ' from mesh ' + mesh.name, domain: domain.id });
obj.parent.parent.DispatchEvent(['*', node.meshid], obj, { etype: 'node', username: user.name, action: 'removenode', nodeid: node._id, msg: change, domain: domain.id })
// Disconnect all connections if needed // Disconnect all connections if needed
var state = obj.parent.parent.GetConnectivityState(nodeid); var state = obj.parent.parent.GetConnectivityState(nodeid);
@ -807,8 +811,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// TODO: We can optimize this a lot. // TODO: We can optimize this a lot.
// - We should get a full list of all MAC's to wake first. // - We should get a full list of all MAC's to wake first.
// - We should try to only have one agent per subnet (using Gateway MAC) send a wake-on-lan. // - We should try to only have one agent per subnet (using Gateway MAC) send a wake-on-lan.
for (var i in command.nodeids) { for (i in command.nodeids) {
var nodeid = command.nodeids[i], wakeActions = 0; nodeid = command.nodeids[i];
var wakeActions = 0;
if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
// Get the device // Get the device
@ -817,7 +822,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0]; var node = nodes[0];
// Get the mesh for this device // Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid]; mesh = obj.parent.meshes[node.meshid];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
@ -835,10 +840,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Get the list of mesh this user as access to // Get the list of mesh this user as access to
var targetMeshes = []; var targetMeshes = [];
for (var i in user.links) { targetMeshes.push(i); } for (i in user.links) { targetMeshes.push(i); }
// Go thru all the connected agents and send wake-on-lan on all the ones in the target mesh list // Go thru all the connected agents and send wake-on-lan on all the ones in the target mesh list
for (var i in obj.parent.wsagents) { for (i in obj.parent.wsagents) {
var agent = obj.parent.wsagents[i]; var agent = obj.parent.wsagents[i];
if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) { if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) {
//console.log('Asking agent ' + agent.dbNodeKey + ' to wake ' + macs.join(',')); //console.log('Asking agent ' + agent.dbNodeKey + ' to wake ' + macs.join(','));
@ -862,8 +867,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
case 'poweraction': case 'poweraction':
{ {
if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
for (var i in command.nodeids) { for (i in command.nodeids) {
var nodeid = command.nodeids[i], powerActions = 0; nodeid = command.nodeids[i];
var powerActions = 0;
if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
// Get the device // Get the device
@ -872,7 +878,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0]; var node = nodes[0];
// Get the mesh for this device // Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid]; mesh = obj.parent.meshes[node.meshid];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
@ -899,8 +905,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
if (obj.common.validateString(command.title, 1, 512) == false) break; // Check title if (obj.common.validateString(command.title, 1, 512) == false) break; // Check title
if (obj.common.validateString(command.msg, 1, 4096) == false) break; // Check message if (obj.common.validateString(command.msg, 1, 4096) == false) break; // Check message
for (var i in command.nodeids) { for (i in command.nodeids) {
var nodeid = command.nodeids[i], powerActions = 0; nodeid = command.nodeids[i];
var powerActions = 0;
if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
// Get the device // Get the device
@ -909,7 +916,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0]; var node = nodes[0];
// Get the mesh for this device // Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid]; mesh = obj.parent.meshes[node.meshid];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
@ -940,7 +947,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0]; var node = nodes[0];
// Get the mesh for this device // Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid]; mesh = obj.parent.meshes[node.meshid];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); return; } if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); return; }
@ -968,13 +975,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0]; var node = nodes[0];
// Get the mesh for this device // Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid]; mesh = obj.parent.meshes[node.meshid];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return; if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return;
// Ready the node change event // Ready the node change event
var changes = [], change = 0, event = { etype: 'node', username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id }; var changes = [], event = { etype: 'node', username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id };
change = 0;
event.msg = ": "; event.msg = ": ";
// If we are in WAN-only mode, host is not used // If we are in WAN-only mode, host is not used
@ -1041,7 +1049,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
data = obj.common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw) data = obj.common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw)
obj.parent.sendMeshAgentCore(user, domain, command.nodeid, data); obj.parent.sendMeshAgentCore(user, domain, command.nodeid, data);
} }
}) });
} }
} }
} else { } else {
@ -1071,7 +1079,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
obj.db.Get(command.nodeid, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???) obj.db.Get(command.nodeid, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???)
if (nodes.length == 1) { if (nodes.length == 1) {
var meshlinks = user.links[nodes[0].meshid]; meshlinks = user.links[nodes[0].meshid];
if ((meshlinks) && (meshlinks.rights) && (meshlinks.rights & obj.parent.MESHRIGHT_REMOTECONTROL != 0)) { if ((meshlinks) && (meshlinks.rights) && (meshlinks.rights & obj.parent.MESHRIGHT_REMOTECONTROL != 0)) {
// Add a user authentication cookie to a url // Add a user authentication cookie to a url
var cookieContent = { userid: user._id, domainid: user.domain }; var cookieContent = { userid: user._id, domainid: user.domain };
@ -1093,7 +1101,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
// Get the mesh // Get the mesh
var mesh = obj.parent.meshes[command.meshid]; mesh = obj.parent.meshes[command.meshid];
if (mesh) { if (mesh) {
if (mesh.mtype != 2) return; // This operation is only allowed for mesh type 2, agent mesh if (mesh.mtype != 2) return; // This operation is only allowed for mesh type 2, agent mesh
@ -1118,7 +1126,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Check if this user has rights on this id to set notes // Check if this user has rights on this id to set notes
obj.db.Get(command.id, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???) obj.db.Get(command.id, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???)
if (nodes.length == 1) { if (nodes.length == 1) {
var meshlinks = user.links[nodes[0].meshid]; meshlinks = user.links[nodes[0].meshid];
if ((meshlinks) && (meshlinks.rights) && (meshlinks.rights & obj.parent.MESHRIGHT_SETNOTES != 0)) { if ((meshlinks) && (meshlinks.rights) && (meshlinks.rights & obj.parent.MESHRIGHT_SETNOTES != 0)) {
// Set the id's notes // Set the id's notes
if (obj.common.validateString(command.notes, 1) == false) { if (obj.common.validateString(command.notes, 1) == false) {
@ -1131,7 +1139,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
}); });
} else if (idtype == 'mesh') { } else if (idtype == 'mesh') {
// Get the mesh for this device // Get the mesh for this device
var mesh = obj.parent.meshes[command.id]; mesh = obj.parent.meshes[command.id];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { return; } if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { return; }
@ -1170,7 +1178,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0]; var node = nodes[0];
// Get the mesh for this device // Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid]; mesh = obj.parent.meshes[node.meshid];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { return; } if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { return; }
@ -1184,7 +1192,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
}); });
} else if (idtype == 'mesh') { } else if (idtype == 'mesh') {
// Get the mesh for this device // Get the mesh for this device
var mesh = obj.parent.meshes[command.id]; mesh = obj.parent.meshes[command.id];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { return; } if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { return; }
@ -1223,7 +1231,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var user = obj.parent.users[ws.userid]; var user = obj.parent.users[ws.userid];
if (user) { if (user) {
if (obj.parent.parent.multiServer == null) { if (obj.parent.parent.multiServer == null) {
obj.parent.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: user.name, count: obj.parent.wssessions[ws.userid].length, nolog: 1, domain: obj.domain.id }) obj.parent.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: user.name, count: obj.parent.wssessions[ws.userid].length, nolog: 1, domain: obj.domain.id });
} else { } else {
obj.parent.recountSessions(ws.sessionId); // Recount sessions obj.parent.recountSessions(ws.sessionId); // Recount sessions
} }
@ -1241,7 +1249,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var httpport = ((obj.args.aliasport != null) ? obj.args.aliasport : obj.args.port); var httpport = ((obj.args.aliasport != null) ? obj.args.aliasport : obj.args.port);
// Build server information object // Build server information object
var serverinfo = { name: obj.parent.certificates.CommonName, mpsname: obj.parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: obj.args.mpspass, port: httpport, emailcheck: obj.parent.parent.mailserver != null } var serverinfo = { name: obj.parent.certificates.CommonName, mpsname: obj.parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: obj.args.mpspass, port: httpport, emailcheck: obj.parent.parent.mailserver != null };
if (obj.args.notls == true) { serverinfo.https = false; } else { serverinfo.https = true; serverinfo.redirport = obj.args.redirport; } if (obj.args.notls == true) { serverinfo.https = false; } else { serverinfo.https = true; serverinfo.redirport = obj.args.redirport; }
// Send server information // Send server information
@ -1259,7 +1267,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var r = {}, dir = obj.fs.readdirSync(path); var r = {}, dir = obj.fs.readdirSync(path);
for (var i in dir) { for (var i in dir) {
var f = { t: 3, d: 111 }; var f = { t: 3, d: 111 };
var stat = obj.fs.statSync(path + '/' + dir[i]) var stat = obj.fs.statSync(path + '/' + dir[i]);
if ((stat.mode & 0x004000) == 0) { f.s = stat.size; f.d = stat.mtime.getTime(); } else { f.t = 2; f.f = readFilesRec(path + '/' + dir[i]); } if ((stat.mode & 0x004000) == 0) { f.s = stat.size; f.d = stat.mtime.getTime(); } else { f.t = 2; f.f = readFilesRec(path + '/' + dir[i]); }
r[dir[i]] = f; r[dir[i]] = f;
} }
@ -1316,7 +1324,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
} }
function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; } function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, '&nbsp;&nbsp;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; } //function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, '&nbsp;&nbsp;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
return obj; return obj;
} };

View File

@ -6,7 +6,12 @@
* @version v0.0.1 * @version v0.0.1
*/ */
'use strict'; /*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict";
// Construct a Mesh Multi-Server object. This is used for MeshCentral-to-MeshCentral communication. // Construct a Mesh Multi-Server object. This is used for MeshCentral-to-MeshCentral communication.
module.exports.CreateMultiServer = function (parent, args) { module.exports.CreateMultiServer = function (parent, args) {
@ -46,7 +51,7 @@ module.exports.CreateMultiServer = function (parent, args) {
obj.stop = function () { obj.stop = function () {
obj.connectionState = 0; obj.connectionState = 0;
disconnect(); disconnect();
} };
// Make one attempt at connecting to the server // Make one attempt at connecting to the server
function connect() { function connect() {
@ -169,13 +174,13 @@ module.exports.CreateMultiServer = function (parent, args) {
if (typeof msg == 'object') { obj.ws.send(JSON.stringify(msg)); return; } if (typeof msg == 'object') { obj.ws.send(JSON.stringify(msg)); return; }
if (typeof msg == 'string') { obj.ws.send(msg); return; } if (typeof msg == 'string') { obj.ws.send(msg); return; }
} catch (e) { } } catch (e) { }
} };
// Process incoming peer server JSON data // Process incoming peer server JSON data
function processServerData(msg) { function processServerData(msg) {
var str = msg.toString('utf8'); var str = msg.toString('utf8'), command = null;
if (str[0] == '{') { if (str[0] == '{') {
try { command = JSON.parse(str) } catch (e) { obj.parent.parent.debug(1, 'Unable to parse server JSON (' + obj.remoteaddr + ').'); return; } // If the command can't be parsed, ignore it. try { command = JSON.parse(str); } catch (e) { obj.parent.parent.debug(1, 'Unable to parse server JSON (' + obj.remoteaddr + ').'); return; } // If the command can't be parsed, ignore it.
if (command.action == 'info') { if (command.action == 'info') {
if (obj.authenticated != 3) { if (obj.authenticated != 3) {
// We get the peer's serverid and database identifier. // We get the peer's serverid and database identifier.
@ -198,7 +203,7 @@ module.exports.CreateMultiServer = function (parent, args) {
connect(); connect();
return obj; return obj;
} };
// Create a mesh server module that received a connection to another server // Create a mesh server module that received a connection to another server
obj.CreatePeerInServer = function (parent, ws, req) { obj.CreatePeerInServer = function (parent, ws, req) {
@ -227,14 +232,14 @@ module.exports.CreateMultiServer = function (parent, args) {
if (typeof data == 'object') { obj.ws.send(JSON.stringify(data)); return; } if (typeof data == 'object') { obj.ws.send(JSON.stringify(data)); return; }
obj.ws.send(data); obj.ws.send(data);
} catch (e) { } } catch (e) { }
} };
// Disconnect this server // Disconnect this server
obj.close = function (arg) { obj.close = function (arg) {
if ((arg == 1) || (arg == null)) { try { obj.ws.close(); obj.parent.parent.debug(1, 'InPeer: Soft disconnect ' + obj.peerServerId + ' (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket if ((arg == 1) || (arg == null)) { try { obj.ws.close(); obj.parent.parent.debug(1, 'InPeer: Soft disconnect ' + obj.peerServerId + ' (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
if (arg == 2) { try { obj.ws._socket._parent.end(); obj.parent.parent.debug(1, 'InPeer: Hard disconnect ' + obj.peerServerId + ' (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket if (arg == 2) { try { obj.ws._socket._parent.end(); obj.parent.parent.debug(1, 'InPeer: Hard disconnect ' + obj.peerServerId + ' (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
if (obj.authenticated == 3) { obj.parent.ClearPeerServer(obj, obj.peerServerId); obj.authenticated = 0; } if (obj.authenticated == 3) { obj.parent.ClearPeerServer(obj, obj.peerServerId); obj.authenticated = 0; }
} };
// When data is received from the peer server web socket // When data is received from the peer server web socket
ws.on('message', function (msg) { ws.on('message', function (msg) {
@ -244,7 +249,7 @@ module.exports.CreateMultiServer = function (parent, args) {
if (obj.authenticated >= 2) { // We are authenticated if (obj.authenticated >= 2) { // We are authenticated
if (msg.charCodeAt(0) == 123) { processServerData(msg); } if (msg.charCodeAt(0) == 123) { processServerData(msg); }
if (msg.length < 2) return; if (msg.length < 2) return;
var cmdid = obj.common.ReadShort(msg, 0); //var cmdid = obj.common.ReadShort(msg, 0);
// Process binary commands (if any). None right now. // Process binary commands (if any). None right now.
} }
else if (obj.authenticated < 2) { // We are not authenticated else if (obj.authenticated < 2) { // We are not authenticated
@ -259,14 +264,14 @@ module.exports.CreateMultiServer = function (parent, args) {
obj.peernonce = msg.substring(50); obj.peernonce = msg.substring(50);
// Perform the hash signature using the server agent certificate // Perform the hash signature using the server agent certificate
obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, obj, function (signature) { obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, obj, function (obj2, signature) {
// Send back our certificate + signature // Send back our certificate + signature
obj2.send(obj2.common.ShortToStr(2) + obj.common.ShortToStr(obj2.agentCertificateAsn1.length) + obj2.agentCertificateAsn1 + signature); // Command 2, certificate + signature obj2.send(obj2.common.ShortToStr(2) + obj2.common.ShortToStr(obj2.agentCertificateAsn1.length) + obj2.agentCertificateAsn1 + signature); // Command 2, certificate + signature
}); });
// Check the peer server signature if we can // Check the peer server signature if we can
if (obj.unauthsign != null) { if (obj.unauthsign != null) {
if (processPeerSignature(obj.unauthsign) == false) { disconnect(); return; } else { completePeerServerConnection(); } if (processPeerSignature(obj.unauthsign) == false) { obj.close(); return; } else { completePeerServerConnection(); }
} }
} }
else if (cmd == 2) { else if (cmd == 2) {
@ -337,9 +342,9 @@ module.exports.CreateMultiServer = function (parent, args) {
// Process incoming peer server JSON data // Process incoming peer server JSON data
function processServerData(msg) { function processServerData(msg) {
var str = msg.toString('utf8'); var str = msg.toString('utf8'), command = null;
if (str[0] == '{') { if (str[0] == '{') {
try { command = JSON.parse(str) } catch (e) { obj.parent.parent.debug(1, 'Unable to parse server JSON (' + obj.remoteaddr + ').'); return; } // If the command can't be parsed, ignore it. try { command = JSON.parse(str); } catch (e) { obj.parent.parent.debug(1, 'Unable to parse server JSON (' + obj.remoteaddr + ').'); return; } // If the command can't be parsed, ignore it.
if (command.action == 'info') { if (command.action == 'info') {
if (obj.authenticated != 3) { if (obj.authenticated != 3) {
// We get the peer's serverid and database identifier. // We get the peer's serverid and database identifier.
@ -362,7 +367,7 @@ module.exports.CreateMultiServer = function (parent, args) {
} }
return obj; return obj;
} };
// If we have no peering configuration, don't setup this object // If we have no peering configuration, don't setup this object
if (obj.peerConfig == null) { return null; } if (obj.peerConfig == null) { return null; }
@ -375,34 +380,34 @@ module.exports.CreateMultiServer = function (parent, args) {
var server = obj.peerServers[serverid]; var server = obj.peerServers[serverid];
if (server && server.peerServerKey) return server.peerServerKey; if (server && server.peerServerKey) return server.peerServerKey;
return null; return null;
} };
// Dispatch an event to all other MeshCentral2 peer servers // Dispatch an event to all other MeshCentral2 peer servers
obj.DispatchEvent = function (ids, source, event) { obj.DispatchEvent = function (ids, source, event) {
var busmsg = JSON.stringify({ action: 'bus', ids: ids, event: event }); var busmsg = JSON.stringify({ action: 'bus', ids: ids, event: event });
for (var serverid in obj.peerServers) { obj.peerServers[serverid].send(busmsg); } for (var serverid in obj.peerServers) { obj.peerServers[serverid].send(busmsg); }
} };
// Dispatch a message to other MeshCentral2 peer servers // Dispatch a message to other MeshCentral2 peer servers
obj.DispatchMessage = function (msg) { obj.DispatchMessage = function (msg) {
for (var serverid in obj.peerServers) { obj.peerServers[serverid].send(msg); } for (var serverid in obj.peerServers) { obj.peerServers[serverid].send(msg); }
} };
// Dispatch a message to other MeshCentral2 peer servers // Dispatch a message to other MeshCentral2 peer servers
obj.DispatchMessageSingleServer = function (msg, serverid) { obj.DispatchMessageSingleServer = function (msg, serverid) {
var server = obj.peerServers[serverid]; var server = obj.peerServers[serverid];
if (server != null) { server.send(msg); } if (server != null) { server.send(msg); }
} };
// Attempt to connect to all peers // Attempt to connect to all peers
obj.ConnectToPeers = function () { obj.ConnectToPeers = function () {
for (serverId in obj.peerConfig.servers) { for (var serverId in obj.peerConfig.servers) {
// We will only connect to names that are larger then ours. This way, eveyone has one connection to everyone else (no cross-connections). // We will only connect to names that are larger then ours. This way, eveyone has one connection to everyone else (no cross-connections).
if ((serverId > obj.serverid) && (obj.peerConfig.servers[serverId].url != null) && (obj.outPeerServers[serverId] == null)) { if ((serverId > obj.serverid) && (obj.peerConfig.servers[serverId].url != null) && (obj.outPeerServers[serverId] == null)) {
obj.outPeerServers[serverId] = obj.CreatePeerOutServer(obj, serverId, obj.peerConfig.servers[serverId].url); obj.outPeerServers[serverId] = obj.CreatePeerOutServer(obj, serverId, obj.peerConfig.servers[serverId].url);
} }
} }
} };
// We connected to a peer server, setup everything // We connected to a peer server, setup everything
obj.SetupPeerServer = function (server, peerServerId) { obj.SetupPeerServer = function (server, peerServerId) {
@ -414,7 +419,7 @@ module.exports.CreateMultiServer = function (parent, args) {
// Send a list of user sessions to the peer // Send a list of user sessions to the peer
server.send(JSON.stringify({ action: 'sessionsTable', sessionsTable: Object.keys(obj.parent.webserver.wssessions2) })); server.send(JSON.stringify({ action: 'sessionsTable', sessionsTable: Object.keys(obj.parent.webserver.wssessions2) }));
} };
// We disconnected to a peer server, clean up everything // We disconnected to a peer server, clean up everything
obj.ClearPeerServer = function (server, peerServerId) { obj.ClearPeerServer = function (server, peerServerId) {
@ -431,10 +436,11 @@ module.exports.CreateMultiServer = function (parent, args) {
delete obj.parent.webserver.wsPeerSessions[peerServerId]; delete obj.parent.webserver.wsPeerSessions[peerServerId];
delete obj.parent.webserver.wsPeerSessions3[peerServerId]; delete obj.parent.webserver.wsPeerSessions3[peerServerId];
obj.parent.webserver.recountSessions(); // Recount all sessions obj.parent.webserver.recountSessions(); // Recount all sessions
} };
// Process a message coming from a peer server // Process a message coming from a peer server
obj.ProcessPeerServerMessage = function (server, peerServerId, msg) { obj.ProcessPeerServerMessage = function (server, peerServerId, msg) {
var userid, i;
//console.log('ProcessPeerServerMessage', peerServerId, msg); //console.log('ProcessPeerServerMessage', peerServerId, msg);
switch (msg.action) { switch (msg.action) {
case 'bus': { case 'bus': {
@ -449,10 +455,10 @@ module.exports.CreateMultiServer = function (parent, args) {
case 'sessionsTable': { case 'sessionsTable': {
obj.parent.webserver.wsPeerSessions[peerServerId] = msg.sessionsTable; obj.parent.webserver.wsPeerSessions[peerServerId] = msg.sessionsTable;
var userToSession = {}; var userToSession = {};
for (var i in msg.sessionsTable) { for (i in msg.sessionsTable) {
var sessionid = msg.sessionsTable[i]; var sessionid = msg.sessionsTable[i];
obj.parent.webserver.wsPeerSessions2[sessionid] = peerServerId; obj.parent.webserver.wsPeerSessions2[sessionid] = peerServerId;
var userid = sessionid.split('/').slice(0, 3).join('/'); // Take the sessionid and keep only the userid partion userid = sessionid.split('/').slice(0, 3).join('/'); // Take the sessionid and keep only the userid partion
if (userToSession[userid] == null) { userToSession[userid] = [sessionid]; } else { userToSession[userid].push(sessionid); } // UserId -> [ SessionId ] if (userToSession[userid] == null) { userToSession[userid] = [sessionid]; } else { userToSession[userid].push(sessionid); } // UserId -> [ SessionId ]
} }
obj.parent.webserver.wsPeerSessions3[peerServerId] = userToSession; // ServerId --> UserId --> SessionId obj.parent.webserver.wsPeerSessions3[peerServerId] = userToSession; // ServerId --> UserId --> SessionId
@ -462,17 +468,17 @@ module.exports.CreateMultiServer = function (parent, args) {
case 'sessionStart': { case 'sessionStart': {
obj.parent.webserver.wsPeerSessions[peerServerId].push(msg.sessionid); obj.parent.webserver.wsPeerSessions[peerServerId].push(msg.sessionid);
obj.parent.webserver.wsPeerSessions2[msg.sessionid] = peerServerId; obj.parent.webserver.wsPeerSessions2[msg.sessionid] = peerServerId;
var userid = msg.sessionid.split('/').slice(0, 3).join('/'); userid = msg.sessionid.split('/').slice(0, 3).join('/');
if (obj.parent.webserver.wsPeerSessions3[peerServerId] == null) { obj.parent.webserver.wsPeerSessions3[peerServerId] = {}; } if (obj.parent.webserver.wsPeerSessions3[peerServerId] == null) { obj.parent.webserver.wsPeerSessions3[peerServerId] = {}; }
if (obj.parent.webserver.wsPeerSessions3[peerServerId][userid] == null) { obj.parent.webserver.wsPeerSessions3[peerServerId][userid] = [ msg.sessionid ]; } else { obj.parent.webserver.wsPeerSessions3[peerServerId][userid].push(msg.sessionid); } if (obj.parent.webserver.wsPeerSessions3[peerServerId][userid] == null) { obj.parent.webserver.wsPeerSessions3[peerServerId][userid] = [msg.sessionid]; } else { obj.parent.webserver.wsPeerSessions3[peerServerId][userid].push(msg.sessionid); }
obj.parent.webserver.recountSessions(msg.sessionid); // Recount a specific user obj.parent.webserver.recountSessions(msg.sessionid); // Recount a specific user
break; break;
} }
case 'sessionEnd': { case 'sessionEnd': {
var i = obj.parent.webserver.wsPeerSessions[peerServerId].indexOf(msg.sessionid); i = obj.parent.webserver.wsPeerSessions[peerServerId].indexOf(msg.sessionid);
if (i >= 0) { obj.parent.webserver.wsPeerSessions[peerServerId].splice(i, 1); } if (i >= 0) { obj.parent.webserver.wsPeerSessions[peerServerId].splice(i, 1); }
delete obj.parent.webserver.wsPeerSessions2[msg.sessionid]; delete obj.parent.webserver.wsPeerSessions2[msg.sessionid];
var userid = msg.sessionid.split('/').slice(0, 3).join('/'); userid = msg.sessionid.split('/').slice(0, 3).join('/');
if (obj.parent.webserver.wsPeerSessions3[peerServerId][userid] != null) { if (obj.parent.webserver.wsPeerSessions3[peerServerId][userid] != null) {
i = obj.parent.webserver.wsPeerSessions3[peerServerId][userid].indexOf(msg.sessionid); i = obj.parent.webserver.wsPeerSessions3[peerServerId][userid].indexOf(msg.sessionid);
if (i >= 0) { if (i >= 0) {
@ -498,7 +504,7 @@ module.exports.CreateMultiServer = function (parent, args) {
// Yes, there is a waiting session, see if we must initiate. // Yes, there is a waiting session, see if we must initiate.
if (peerServerId > obj.parent.serverId) { if (peerServerId > obj.parent.serverId) {
// We must initiate the connection to the peer // We must initiate the connection to the peer
var userid = null; userid = null;
if (rsession.peer1.req.session != null) { userid = rsession.peer1.req.session.userid; } if (rsession.peer1.req.session != null) { userid = rsession.peer1.req.session.userid; }
obj.createPeerRelay(rsession.peer1.ws, rsession.peer1.req, peerServerId, userid); obj.createPeerRelay(rsession.peer1.ws, rsession.peer1.req, peerServerId, userid);
delete obj.parent.webserver.wsrelays[msg.id]; delete obj.parent.webserver.wsrelays[msg.id];
@ -509,33 +515,33 @@ module.exports.CreateMultiServer = function (parent, args) {
// Clear all relay sessions that are more than 1 minute // Clear all relay sessions that are more than 1 minute
var oneMinuteAgo = Date.now() - 60000; var oneMinuteAgo = Date.now() - 60000;
for (var id in obj.parent.webserver.wsPeerRelays) { if (obj.parent.webserver.wsPeerRelays[id].time < oneMinuteAgo) { delete obj.parent.webserver.wsPeerRelays[id]; } } for (i in obj.parent.webserver.wsPeerRelays) { if (obj.parent.webserver.wsPeerRelays[i].time < oneMinuteAgo) { delete obj.parent.webserver.wsPeerRelays[i]; } }
} }
break; break;
} }
case 'msg': { case 'msg': {
if (msg.sessionid != null) { if (msg.sessionid != null) {
// Route this message to a connected user session // Route this message to a connected user session
if (command.fromNodeid != null) { command.nodeid = command.fromNodeid; delete command.fromNodeid; } if (msg.fromNodeid != null) { msg.nodeid = msg.fromNodeid; delete msg.fromNodeid; }
var ws = obj.parent.webserver.wssessions2[command.sessionid]; var ws = obj.parent.webserver.wssessions2[msg.sessionid];
if (ws != null) { ws.send(JSON.stringify(command)); } if (ws != null) { ws.send(JSON.stringify(msg)); }
} else if (msg.nodeid != null) { } else if (msg.nodeid != null) {
// Route this message to a connected agent // Route this message to a connected agent
if (command.fromSessionid != null) { command.sessionid = command.fromSessionid; delete command.fromSessionid; } if (msg.fromSessionid != null) { msg.sessionid = msg.fromSessionid; delete msg.fromSessionid; }
var agent = obj.parent.webserver.wsagents[msg.nodeid]; var agent = obj.parent.webserver.wsagents[msg.nodeid];
if (agent != null) { delete msg.nodeid; agent.send(JSON.stringify(msg)); } // Remove the nodeid since it's implyed and send the message to the agent if (agent != null) { delete msg.nodeid; agent.send(JSON.stringify(msg)); } // Remove the nodeid since it's implyed and send the message to the agent
} else if (msg.meshid != null) { } else if (msg.meshid != null) {
// Route this message to all users of this mesh // Route this message to all users of this mesh
if (command.fromNodeid != null) { command.nodeid = command.fromNodeid; delete command.fromNodeid; } if (msg.fromNodeid != null) { msg.nodeid = msg.fromNodeid; delete msg.fromNodeid; }
var cmdstr = JSON.stringify(command); var cmdstr = JSON.stringify(msg);
for (var userid in obj.parent.webserver.wssessions) { // Find all connected users for this mesh and send the message for (userid in obj.parent.webserver.wssessions) { // Find all connected users for this mesh and send the message
var user = obj.parent.webserver.users[userid]; var user = obj.parent.webserver.users[userid];
if (user) { if (user) {
var rights = user.links[msg.meshid]; var rights = user.links[msg.meshid];
if (rights != null) { // TODO: Look at what rights are needed for message routing if (rights != null) { // TODO: Look at what rights are needed for message routing
var sessions = obj.parent.webserver.wssessions[userid]; var sessions = obj.parent.webserver.wssessions[userid];
// Send the message to all users on this server // Send the message to all users on this server
for (var i in sessions) { sessions[i].send(cmdstr); } for (i in sessions) { sessions[i].send(cmdstr); }
} }
} }
} }
@ -543,7 +549,7 @@ module.exports.CreateMultiServer = function (parent, args) {
break; break;
} }
} }
} };
// Create a tunnel connection to a peer server // Create a tunnel connection to a peer server
obj.createPeerRelay = function (ws, req, serverid, user) { obj.createPeerRelay = function (ws, req, serverid, user) {
@ -558,7 +564,7 @@ module.exports.CreateMultiServer = function (parent, args) {
var path = req.path; var path = req.path;
if (path[0] == '/') path = path.substring(1); if (path[0] == '/') path = path.substring(1);
if (path.substring(path.length - 11) == '/.websocket') { path = path.substring(0, path.length - 11); } if (path.substring(path.length - 11) == '/.websocket') { path = path.substring(0, path.length - 11); }
var queryStr = '' var queryStr = '';
for (var i in req.query) { queryStr += ((queryStr == '') ? '?' : '&') + i + '=' + req.query[i]; } for (var i in req.query) { queryStr += ((queryStr == '') ? '?' : '&') + i + '=' + req.query[i]; }
if (user != null) { queryStr += ((queryStr == '') ? '?' : '&') + 'auth=' + obj.parent.encodeCookie({ userid: user._id, domainid: user.domain }, cookieKey); } if (user != null) { queryStr += ((queryStr == '') ? '?' : '&') + 'auth=' + obj.parent.encodeCookie({ userid: user._id, domainid: user.domain }, cookieKey); }
var url = obj.peerConfig.servers[serverid].url + path + queryStr; var url = obj.peerConfig.servers[serverid].url + path + queryStr;
@ -566,7 +572,7 @@ module.exports.CreateMultiServer = function (parent, args) {
// Setup an connect the web socket // Setup an connect the web socket
var tunnel = obj.createPeerRelayEx(ws, url, serverid); var tunnel = obj.createPeerRelayEx(ws, url, serverid);
tunnel.connect(); tunnel.connect();
} };
// Create a tunnel connection to a peer server // Create a tunnel connection to a peer server
// We assume that "ws" is paused already. // We assume that "ws" is paused already.
@ -610,7 +616,7 @@ module.exports.CreateMultiServer = function (parent, args) {
// If the web socket is closed, close the associated TCP connection. // If the web socket is closed, close the associated TCP connection.
peerTunnel.ws1.on('close', function (req) { peerTunnel.parent.parent.debug(1, 'FTunnel disconnect ' + peerTunnel.serverid); peerTunnel.close(); }); peerTunnel.ws1.on('close', function (req) { peerTunnel.parent.parent.debug(1, 'FTunnel disconnect ' + peerTunnel.serverid); peerTunnel.close(); });
} };
// Disconnect both sides of the tunnel // Disconnect both sides of the tunnel
peerTunnel.close = function (arg) { peerTunnel.close = function (arg) {
@ -623,11 +629,11 @@ module.exports.CreateMultiServer = function (parent, args) {
if (peerTunnel.ws1 != null) { try { peerTunnel.ws1.close(); peerTunnel.parent.parent.debug(1, 'FTunnel1: Soft disconnect '); } catch (e) { console.log(e); } } if (peerTunnel.ws1 != null) { try { peerTunnel.ws1.close(); peerTunnel.parent.parent.debug(1, 'FTunnel1: Soft disconnect '); } catch (e) { console.log(e); } }
if (peerTunnel.ws2 != null) { try { peerTunnel.ws2.close(); peerTunnel.parent.parent.debug(1, 'FTunnel2: Soft disconnect '); } catch (e) { console.log(e); } } if (peerTunnel.ws2 != null) { try { peerTunnel.ws2.close(); peerTunnel.parent.parent.debug(1, 'FTunnel2: Soft disconnect '); } catch (e) { console.log(e); } }
} }
} };
return peerTunnel; return peerTunnel;
} };
setTimeout(function () { obj.ConnectToPeers(); }, 1000); // Delay this a little to make sure we are ready on our side. setTimeout(function () { obj.ConnectToPeers(); }, 1000); // Delay this a little to make sure we are ready on our side.
return obj; return obj;
} };

View File

@ -1,6 +1,11 @@
// check out https://github.com/tj/node-pwd // check out https://github.com/tj/node-pwd
'use strict'; /*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict";
// Module dependencies. // Module dependencies.
const crypto = require('crypto'); const crypto = require('crypto');

View File

@ -6,7 +6,12 @@
* @version v0.0.1 * @version v0.0.1
*/ */
'use strict'; /*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict";
// Construct a legacy Swarm Server server object // Construct a legacy Swarm Server server object
module.exports.CreateSwarmServer = function (parent, db, args, certificates) { module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
@ -18,7 +23,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
obj.legacyAgentConnections = {}; obj.legacyAgentConnections = {};
obj.migrationAgents = {}; obj.migrationAgents = {};
const common = require('./common.js'); const common = require('./common.js');
const net = require('net'); //const net = require('net');
const tls = require('tls'); const tls = require('tls');
const forge = require('node-forge'); const forge = require('node-forge');
@ -115,7 +120,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
USERAUTH2: 1031, // Authenticate a user to the swarm server (Uses SHA1 SALT) USERAUTH2: 1031, // Authenticate a user to the swarm server (Uses SHA1 SALT)
GUESTREMOTEDESKTOP: 2001, // Guest usage: Remote Desktop GUESTREMOTEDESKTOP: 2001, // Guest usage: Remote Desktop
GUESTWEBRTCMESH: 2002 // Guest usage: WebRTC Mesh GUESTWEBRTCMESH: 2002 // Guest usage: WebRTC Mesh
} };
obj.server = tls.createServer({ key: certificates.swarmserver.key, cert: certificates.swarmserver.cert, requestCert: true }, onConnection); obj.server = tls.createServer({ key: certificates.swarmserver.key, cert: certificates.swarmserver.cert, requestCert: true }, onConnection);
obj.server.listen(args.swarmport, function () { console.log('MeshCentral Legacy Swarm Server running on ' + certificates.CommonName + ':' + args.swarmport + '.'); obj.parent.updateServerState('swarm-port', args.swarmport); }).on('error', function (err) { console.error('ERROR: MeshCentral Swarm Server server port ' + args.swarmport + ' is not available.'); if (args.exactports) { process.exit(); } }); obj.server.listen(args.swarmport, function () { console.log('MeshCentral Legacy Swarm Server running on ' + certificates.CommonName + ':' + args.swarmport + '.'); obj.parent.updateServerState('swarm-port', args.swarmport); }).on('error', function (err) { console.error('ERROR: MeshCentral Swarm Server server port ' + args.swarmport + ' is not available.'); if (args.exactports) { process.exit(); } });
@ -146,11 +151,11 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
socket.setEncoding('binary'); socket.setEncoding('binary');
socket.pingTimer = setInterval(function () { obj.SendCommand(socket, LegacyMeshProtocol.PING); }, 20000); socket.pingTimer = setInterval(function () { obj.SendCommand(socket, LegacyMeshProtocol.PING); }, 20000);
Debug(1, 'SWARM:New legacy agent connection'); Debug(1, 'SWARM:New legacy agent connection');
socket.addListener("data", function (data) { socket.addListener("data", function (data) {
if (args.swarmdebug) { var buf = new Buffer(data, "binary"); console.log('SWARM <-- (' + buf.length + '):' + buf.toString('hex')); } // Print out received bytes if (args.swarmdebug) { var buf = new Buffer(data, "binary"); console.log('SWARM <-- (' + buf.length + '):' + buf.toString('hex')); } // Print out received bytes
socket.tag.accumulator += data; socket.tag.accumulator += data;
// Detect if this is an HTTPS request, if it is, return a simple answer and disconnect. This is useful for debugging access to the MPS port. // Detect if this is an HTTPS request, if it is, return a simple answer and disconnect. This is useful for debugging access to the MPS port.
if (socket.tag.first == true) { if (socket.tag.first == true) {
if (socket.tag.accumulator.length < 3) return; if (socket.tag.accumulator.length < 3) return;
@ -214,7 +219,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
Debug(3, 'Swarm:GETSTATE'); Debug(3, 'Swarm:GETSTATE');
if (len < 12) break; if (len < 12) break;
var statecmd = common.ReadInt(data, 0); var statecmd = common.ReadInt(data, 0);
var statesync = common.ReadInt(data, 4); //var statesync = common.ReadInt(data, 4);
switch (statecmd) { switch (statecmd) {
case 6: { // Ask for agent block case 6: { // Ask for agent block
if (socket.tag.update != null) { if (socket.tag.update != null) {
@ -247,14 +252,14 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
} }
return len; return len;
} }
socket.addListener("close", function () { socket.addListener("close", function () {
Debug(1, 'Swarm:Connection closed'); Debug(1, 'Swarm:Connection closed');
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
if (socket.pingTimer != null) { clearInterval(socket.pingTimer); delete socket.pingTimer; } if (socket.pingTimer != null) { clearInterval(socket.pingTimer); delete socket.pingTimer; }
}); });
socket.addListener("error", function () { socket.addListener("error", function () {
//console.log("Swarm Error: " + socket.remoteAddress); //console.log("Swarm Error: " + socket.remoteAddress);
}); });
@ -311,19 +316,19 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
console.log(e); console.log(e);
} }
return null; return null;
} };
// Disconnect legacy agent connection // Disconnect legacy agent connection
obj.close = function (socket) { obj.close = function (socket) {
try { socket.close(); } catch (e) { } try { socket.close(); } catch (e) { }
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
} };
obj.SendCommand = function(socket, cmdid, data) { obj.SendCommand = function (socket, cmdid, data) {
if (data == null) { data = ''; } if (data == null) { data = ''; }
Write(socket, common.ShortToStr(cmdid) + common.ShortToStr(data.length + 4) + data); Write(socket, common.ShortToStr(cmdid) + common.ShortToStr(data.length + 4) + data);
} };
function Write(socket, data) { function Write(socket, data) {
if (args.swarmdebug) { if (args.swarmdebug) {
@ -335,7 +340,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
socket.write(new Buffer(data, "binary")); socket.write(new Buffer(data, "binary"));
} }
} }
// Debug // Debug
function Debug(lvl) { function Debug(lvl) {
if (lvl > obj.parent.debugLevel) return; if (lvl > obj.parent.debugLevel) return;
@ -348,4 +353,4 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
} }
return obj; return obj;
} };

View File

@ -2251,9 +2251,8 @@
var boundingBox = null; var boundingBox = null;
for (var i in nodes) { for (var i in nodes) {
try { try {
var loc = map_parseNodeLoc(nodes[i]); var loc = map_parseNodeLoc(nodes[i]), feature = xxmap.markersSource.getFeatureById(nodes[i]._id);
var feature = xxmap.markersSource.getFeatureById(nodes[i]._id); if ((loc != null) && ((nodes[i].meshid == selectedMesh) || (selectedMesh == null))) { // Draw markers for devices with locations
if ((typeof loc == 'object') && ((nodes[i].meshid == selectedMesh) || (selectedMesh == null))) { // Draw markers for devices with locations
var lat = loc[0], lon = loc[1], type = loc[2]; var lat = loc[0], lon = loc[1], type = loc[2];
if (boundingBox == null) { boundingBox = [ lat, lon, lat, lon, 0 ]; } else { if (lat < boundingBox[0]) { boundingBox[0] = lat; } if (lon < boundingBox[1]) { boundingBox[1] = lon; } if (lat > boundingBox[2]) { boundingBox[2] = lat; } if (lon > boundingBox[3]) { boundingBox[3] = lon; } } if (boundingBox == null) { boundingBox = [ lat, lon, lat, lon, 0 ]; } else { if (lat < boundingBox[0]) { boundingBox[0] = lat; } if (lon < boundingBox[1]) { boundingBox[1] = lon; } if (lat > boundingBox[2]) { boundingBox[2] = lat; } if (lon > boundingBox[3]) { boundingBox[3] = lon; } }
if (feature == null) { addFeature(nodes[i]); boundingBox[4] = 1; } else { updateFeature(nodes[i], feature); feature.setStyle(markerStyle(nodes[i], loc[2])); } // Update Feature if (feature == null) { addFeature(nodes[i]); boundingBox[4] = 1; } else { updateFeature(nodes[i], feature); feature.setStyle(markerStyle(nodes[i], loc[2])); } // Update Feature
@ -2311,7 +2310,7 @@
if (node.wifiloc) { loc = node.wifiloc; t = 2; } if (node.wifiloc) { loc = node.wifiloc; t = 2; }
if (node.gpsloc) { loc = node.gpsloc; t = 3; } if (node.gpsloc) { loc = node.gpsloc; t = 3; }
if (node.userloc) { loc = node.userloc; t = 4; } if (node.userloc) { loc = node.userloc; t = 4; }
if ((loc == null) || (typeof loc != 'string')) return; if ((loc == null) || (typeof loc != 'string')) return null;
loc = loc.split(','); loc = loc.split(',');
if (t == 1) { if (t == 1) {
// If this is IP location, randomize the position a little. // If this is IP location, randomize the position a little.
@ -2450,12 +2449,14 @@
} }
// Since this is IP address location, add some fixed randomness to the location. Avoid pin pile-up. // Since this is IP address location, add some fixed randomness to the location. Avoid pin pile-up.
var loc = map_parseNodeLoc(node); lat = loc[0]; lon = loc[1]; var loc = map_parseNodeLoc(node);
if (loc != null) {
if ((lat != feature.get('lat')) || (lon != feature.get('lon'))) { // Update lat and lon if changed var lat = loc[0], lon = loc[1];
feature.set('lat', lat); feature.set('lon', lon); if ((lat != feature.get('lat')) || (lon != feature.get('lon'))) { // Update lat and lon if changed
var modifiedCoordinates = ol.proj.transform([parseFloat(lon), parseFloat(lat)], 'EPSG:4326','EPSG:3857'); feature.set('lat', lat); feature.set('lon', lon);
feature.getGeometry().setCoordinates(modifiedCoordinates); var modifiedCoordinates = ol.proj.transform([parseFloat(lon), parseFloat(lat)], 'EPSG:4326', 'EPSG:3857');
feature.getGeometry().setCoordinates(modifiedCoordinates);
}
} }
if (node.name != feature.get('name') ) { feature.set('name', node.name); } // Update name if (node.name != feature.get('name') ) { feature.set('name', node.name); } // Update name

View File

@ -6,7 +6,12 @@
* @version v0.0.1 * @version v0.0.1
*/ */
'use strict'; /*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict";
/* /*
class SerialTunnel extends require('stream').Duplex { class SerialTunnel extends require('stream').Duplex {
@ -22,9 +27,9 @@ class SerialTunnel extends require('stream').Duplex {
function SerialTunnel(options) { function SerialTunnel(options) {
var obj = new require('stream').Duplex(options); var obj = new require('stream').Duplex(options);
obj.forwardwrite = null; obj.forwardwrite = null;
obj.updateBuffer = function (chunk) { this.push(chunk); } obj.updateBuffer = function (chunk) { this.push(chunk); };
obj._write = function (chunk, encoding, callback) { if (obj.forwardwrite != null) { obj.forwardwrite(chunk); } else { console.err("Failed to fwd _write."); } if (callback) callback(); } // Pass data written to forward obj._write = function (chunk, encoding, callback) { if (obj.forwardwrite != null) { obj.forwardwrite(chunk); } else { console.err("Failed to fwd _write."); } if (callback) callback(); }; // Pass data written to forward
obj._read = function(size) { } // Push nothing, anything to read should be pushed from updateBuffer() obj._read = function (size) { }; // Push nothing, anything to read should be pushed from updateBuffer()
return obj; return obj;
} }
@ -37,7 +42,7 @@ if (!String.prototype.endsWith) { String.prototype.endsWith = function (searchSt
// Construct a HTTP web server object // Construct a HTTP web server object
module.exports.CreateWebServer = function (parent, db, args, certificates) { module.exports.CreateWebServer = function (parent, db, args, certificates) {
var obj = {}; var obj = {}, i = 0;
// Modules // Modules
obj.fs = require('fs'); obj.fs = require('fs');
@ -52,8 +57,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.common = require('./common.js'); obj.common = require('./common.js');
obj.express = require('express'); obj.express = require('express');
obj.meshAgentHandler = require('./meshagent.js'); obj.meshAgentHandler = require('./meshagent.js');
obj.meshRelayHandler = require('./meshrelay.js') obj.meshRelayHandler = require('./meshrelay.js');
obj.meshUserHandler = require('./meshuser.js') obj.meshUserHandler = require('./meshuser.js');
obj.interceptor = require('./interceptor'); obj.interceptor = require('./interceptor');
// Variables // Variables
@ -63,13 +68,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.app = obj.express(); obj.app = obj.express();
obj.app.use(require('compression')()); obj.app.use(require('compression')());
obj.tlsServer = null; obj.tlsServer = null;
obj.tcpServer; obj.tcpServer = null;
obj.certificates = certificates; obj.certificates = certificates;
obj.args = args; obj.args = args;
obj.users = {}; obj.users = {};
obj.meshes = {}; obj.meshes = {};
obj.userAllowedIp = args.userallowedip; // List of allowed IP addresses for users obj.userAllowedIp = args.userallowedip; // List of allowed IP addresses for users
obj.tlsSniCredentials; obj.tlsSniCredentials = null;
obj.dnsDomains = {}; obj.dnsDomains = {};
//obj.agentConnCount = 0; //obj.agentConnCount = 0;
@ -93,13 +98,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Setup SSPI authentication if needed // Setup SSPI authentication if needed
if ((obj.parent.platform == 'win32') && (obj.args.nousers != true) && (obj.parent.config != null) && (obj.parent.config.domains != null)) { if ((obj.parent.platform == 'win32') && (obj.args.nousers != true) && (obj.parent.config != null) && (obj.parent.config.domains != null)) {
for (var i in obj.parent.config.domains) { if (obj.parent.config.domains[i].auth == 'sspi') { var nodeSSPI = require('node-sspi'); obj.parent.config.domains[i].sspi = new nodeSSPI({ retrieveGroups: true, offerBasic: false }); } } for (i in obj.parent.config.domains) { if (obj.parent.config.domains[i].auth == 'sspi') { var nodeSSPI = require('node-sspi'); obj.parent.config.domains[i].sspi = new nodeSSPI({ retrieveGroups: true, offerBasic: false }); } }
} }
// Perform hash on web certificate and agent certificate // Perform hash on web certificate and agent certificate
obj.webCertificateHash = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }); obj.webCertificateHash = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' });
obj.webCertificateHashs = { '': obj.webCertificateHash }; obj.webCertificateHashs = { '': obj.webCertificateHash };
for (var i in obj.parent.config.domains) { if (obj.parent.config.domains[i].dns != null) { obj.webCertificateHashs[i] = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.parent.config.domains[i].certs.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }); } } for (i in obj.parent.config.domains) { if (obj.parent.config.domains[i].dns != null) { obj.webCertificateHashs[i] = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.parent.config.domains[i].certs.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }); } }
obj.webCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); obj.webCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
obj.agentCertificateHashHex = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'hex' }); obj.agentCertificateHashHex = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'hex' });
obj.agentCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); obj.agentCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
@ -130,13 +135,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
{ {
var dnscount = 0; var dnscount = 0;
obj.tlsSniCredentials = {}; obj.tlsSniCredentials = {};
for (var i in obj.certificates.dns) { if (obj.parent.config.domains[i].dns != null) { obj.dnsDomains[obj.parent.config.domains[i].dns.toLowerCase()] = obj.parent.config.domains[i]; obj.tlsSniCredentials[obj.parent.config.domains[i].dns] = obj.tls.createSecureContext(obj.certificates.dns[i]).context; dnscount++; } } for (i in obj.certificates.dns) { if (obj.parent.config.domains[i].dns != null) { obj.dnsDomains[obj.parent.config.domains[i].dns.toLowerCase()] = obj.parent.config.domains[i]; obj.tlsSniCredentials[obj.parent.config.domains[i].dns] = obj.tls.createSecureContext(obj.certificates.dns[i]).context; dnscount++; } }
if (dnscount > 0) { obj.tlsSniCredentials[''] = obj.tls.createSecureContext({ cert: obj.certificates.web.cert, key: obj.certificates.web.key, ca: obj.certificates.web.ca }).context; } else { obj.tlsSniCredentials = null; } if (dnscount > 0) { obj.tlsSniCredentials[''] = obj.tls.createSecureContext({ cert: obj.certificates.web.cert, key: obj.certificates.web.key, ca: obj.certificates.web.ca }).context; } else { obj.tlsSniCredentials = null; }
} }
function TlsSniCallback(name, cb) { var c = obj.tlsSniCredentials[name]; if (c != null) { cb(null, c); } else { cb(null, obj.tlsSniCredentials['']); } } function TlsSniCallback(name, cb) { var c = obj.tlsSniCredentials[name]; if (c != null) { cb(null, c); } else { cb(null, obj.tlsSniCredentials['']); } }
function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; } function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, '&nbsp;&nbsp;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; } //function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, '&nbsp;&nbsp;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
if (obj.args.notls || obj.args.tlsoffload) { if (obj.args.notls || obj.args.tlsoffload) {
// Setup the HTTP server without TLS // Setup the HTTP server without TLS
@ -172,27 +177,28 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Session-persisted message middleware // Session-persisted message middleware
obj.app.use(function (req, res, next) { obj.app.use(function (req, res, next) {
var err = null, msg = null, passhint = null;
if (req.session != null) { if (req.session != null) {
var err = req.session.error; err = req.session.error;
var msg = req.session.success; msg = req.session.success;
var passhint = req.session.passhint; passhint = req.session.passhint;
delete req.session.error; delete req.session.error;
delete req.session.success; delete req.session.success;
delete req.session.passhint; delete req.session.passhint;
} }
res.locals.message = ''; res.locals.message = '';
if (err) res.locals.message = '<p class="msg error">' + err + '</p>'; if (err != null) res.locals.message = '<p class="msg error">' + err + '</p>';
if (msg) res.locals.message = '<p class="msg success">' + msg + '</p>'; if (msg != null) res.locals.message = '<p class="msg success">' + msg + '</p>';
if (passhint) res.locals.passhint = EscapeHtml(passhint); if (passhint != null) res.locals.passhint = EscapeHtml(passhint);
next(); next();
}); });
// Fetch all users from the database, keep this in memory // Fetch all users from the database, keep this in memory
obj.db.GetAllType('user', function (err, docs) { obj.db.GetAllType('user', function (err, docs) {
var domainUserCount = {}; var domainUserCount = {}, i = 0;
for (var i in parent.config.domains) { domainUserCount[i] = 0; } for (i in parent.config.domains) { domainUserCount[i] = 0; }
for (var i in docs) { var u = obj.users[docs[i]._id] = docs[i]; domainUserCount[u.domain]++; } for (i in docs) { var u = obj.users[docs[i]._id] = docs[i]; domainUserCount[u.domain]++; }
for (var i in parent.config.domains) { for (i in parent.config.domains) {
if (domainUserCount[i] == 0) { if (domainUserCount[i] == 0) {
if (parent.config.domains[i].newaccounts == 0) { parent.config.domains[i].newaccounts = 2; } if (parent.config.domains[i].newaccounts == 0) { parent.config.domains[i].newaccounts = 2; }
console.log('Server ' + ((i == '') ? '' : (i + ' ')) + 'has no users, next new account will be site administrator.'); console.log('Server ' + ((i == '') ? '' : (i + ' ')) + 'has no users, next new account will be site administrator.');
@ -237,7 +243,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}); });
} }
} }
} };
/* /*
obj.restrict = function (req, res, next) { obj.restrict = function (req, res, next) {
@ -249,7 +255,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
req.session.error = 'Access denied!'; req.session.error = 'Access denied!';
res.redirect(domain.url + 'login'); res.redirect(domain.url + 'login');
} }
} };
*/ */
// Check if the source IP address is allowed for a given allowed list, return false if not // Check if the source IP address is allowed for a given allowed list, return false if not
@ -287,8 +293,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (req.headers.host != null) { var d = obj.dnsDomains[req.headers.host.toLowerCase()]; if (d != null) return d; } // If this is a DNS name domain, return it here. if (req.headers.host != null) { var d = obj.dnsDomains[req.headers.host.toLowerCase()]; if (d != null) return d; } // If this is a DNS name domain, return it here.
var x = req.url.split('/'); var x = req.url.split('/');
if (x.length < 2) return parent.config.domains['']; if (x.length < 2) return parent.config.domains[''];
var d = parent.config.domains[x[1].toLowerCase()]; var y = parent.config.domains[x[1].toLowerCase()];
if ((d != null) && (d.dns == null)) return parent.config.domains[x[1].toLowerCase()]; if ((y != null) && (y.dns == null)) return parent.config.domains[x[1].toLowerCase()];
return parent.config.domains['']; return parent.config.domains[''];
} }
@ -298,8 +304,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
// Destroy the user's session to log them out will be re-created next request // Destroy the user's session to log them out will be re-created next request
if (req.session.userid) { if (req.session.userid) {
var user = obj.users[req.session.userid] var user = obj.users[req.session.userid];
obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'logout', msg: 'Account logout', domain: domain.id }) obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'logout', msg: 'Account logout', domain: domain.id });
} }
req.session = null; req.session = null;
res.redirect(domain.url); res.redirect(domain.url);
@ -319,35 +325,35 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Regenerate session when signing in to prevent fixation // Regenerate session when signing in to prevent fixation
//req.session.regenerate(function () { //req.session.regenerate(function () {
// Store the user's primary key in the session store to be retrieved, or in this case the entire user object // Store the user's primary key in the session store to be retrieved, or in this case the entire user object
// req.session.success = 'Authenticated as ' + user.name + 'click to <a href="/logout">logout</a>. You may now access <a href="/restricted">/restricted</a>.'; // req.session.success = 'Authenticated as ' + user.name + 'click to <a href="/logout">logout</a>. You may now access <a href="/restricted">/restricted</a>.';
delete req.session.loginmode; delete req.session.loginmode;
req.session.userid = userid; req.session.userid = userid;
req.session.domainid = domain.id; req.session.domainid = domain.id;
req.session.currentNode = ''; req.session.currentNode = '';
if (req.session.passhint) { delete req.session.passhint; } if (req.session.passhint) { delete req.session.passhint; }
if (req.body.viewmode) { req.session.viewmode = req.body.viewmode; } if (req.body.viewmode) { req.session.viewmode = req.body.viewmode; }
if (req.body.host) { if (req.body.host) {
// TODO: This is a terrible search!!! FIX THIS. // TODO: This is a terrible search!!! FIX THIS.
/* /*
obj.db.GetAllType('node', function (err, docs) { obj.db.GetAllType('node', function (err, docs) {
for (var i = 0; i < docs.length; i++) { for (var i = 0; i < docs.length; i++) {
if (docs[i].name == req.body.host) { if (docs[i].name == req.body.host) {
req.session.currentNode = docs[i]._id; req.session.currentNode = docs[i]._id;
break; break;
}
} }
console.log("CurrentNode: " + req.session.currentNode); }
// This redirect happens after finding node is completed console.log("CurrentNode: " + req.session.currentNode);
res.redirect(domain.url); // This redirect happens after finding node is completed
});
*/
} else {
res.redirect(domain.url); res.redirect(domain.url);
} });
*/
} else {
res.redirect(domain.url);
}
//}); //});
obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id }) obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id });
} else { } else {
delete req.session.loginmode; delete req.session.loginmode;
if (err == 'locked') { req.session.error = '<b style=color:#8C001A>Account locked.</b>'; } else { req.session.error = '<b style=color:#8C001A>Login failed, check username and password.</b>'; } if (err == 'locked') { req.session.error = '<b style=color:#8C001A>Account locked.</b>'; } else { req.session.error = '<b style=color:#8C001A>Login failed, check username and password.</b>'; }
@ -367,14 +373,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (domain.newaccounts == 0) { res.sendStatus(401); return; } if (domain.newaccounts == 0) { res.sendStatus(401); return; }
if (!obj.common.validateUsername(req.body.username, 1, 64) || !obj.common.validateEmail(req.body.email, 1, 256) || !obj.common.validateString(req.body.password1, 1, 256) || !obj.common.validateString(req.body.password2, 1, 256) || (req.body.password1 != req.body.password2) || req.body.username == '~') { if (!obj.common.validateUsername(req.body.username, 1, 64) || !obj.common.validateEmail(req.body.email, 1, 256) || !obj.common.validateString(req.body.password1, 1, 256) || !obj.common.validateString(req.body.password2, 1, 256) || (req.body.password1 != req.body.password2) || req.body.username == '~') {
req.session.loginmode = 2; req.session.loginmode = 2;
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';; req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
res.redirect(domain.url); res.redirect(domain.url);
} else { } else {
// Check if this email was already verified // Check if this email was already verified
obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) { obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) {
if (docs.length > 0) { if (docs.length > 0) {
req.session.loginmode = 2; req.session.loginmode = 2;
req.session.error = '<b style=color:#8C001A>Existing account with this email address.</b>';; req.session.error = '<b style=color:#8C001A>Existing account with this email address.</b>';
res.redirect(domain.url); res.redirect(domain.url);
} else { } else {
// Check if there is domain.newAccountToken, check if supplied token is valid // Check if there is domain.newAccountToken, check if supplied token is valid
@ -406,7 +412,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.db.SetUser(user); obj.db.SetUser(user);
if (obj.parent.mailserver != null) { obj.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email); } if (obj.parent.mailserver != null) { obj.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email); }
}); });
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: user, action: 'accountcreate', msg: 'Account created, email is ' + req.body.email, domain: domain.id }) obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: user, action: 'accountcreate', msg: 'Account created, email is ' + req.body.email, domain: domain.id });
} }
res.redirect(domain.url); res.redirect(domain.url);
} }
@ -459,7 +465,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} else { } else {
obj.db.Get('user/' + cookie.u, function (err, docs) { obj.db.Get('user/' + cookie.u, function (err, docs) {
if (docs.length == 0) { if (docs.length == 0) {
res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'ERROR: Invalid username \"' + EscapeHtml(user.name) + '\". <a href="' + domain.url + '">Go to login page</a>.' }); res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'ERROR: Invalid username \"' + EscapeHtml(cookie.u) + '\". <a href="' + domain.url + '">Go to login page</a>.' });
} else { } else {
var user = docs[0]; var user = docs[0];
if (user.email != cookie.e) { if (user.email != cookie.e) {
@ -488,13 +494,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete userinfo.domain; delete userinfo.domain;
delete userinfo.subscriptions; delete userinfo.subscriptions;
delete userinfo.passtype; delete userinfo.passtype;
obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Verified email of user ' + EscapeHtml(user.name) + ' (' + EscapeHtml(userinfo.email) + ')', domain: domain.id }) obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Verified email of user ' + EscapeHtml(user.name) + ' (' + EscapeHtml(userinfo.email) + ')', domain: domain.id });
// Send the confirmation page // Send the confirmation page
res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'Verified email <b>' + EscapeHtml(user.email) + '</b> for user account <b>' + EscapeHtml(user.name) + '</b>. <a href="' + domain.url + '">Go to login page</a>.' }); res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'Verified email <b>' + EscapeHtml(user.email) + '</b> for user account <b>' + EscapeHtml(user.name) + '</b>. <a href="' + domain.url + '">Go to login page</a>.' });
// Send a notification // Send a notification
obj.parent.DispatchEvent([user._id], obj, { action: 'notify', value: 'Email verified:<br /><b>' + EscapeHtml(userinfo.email) + '</b>.', nolog: 1 }) obj.parent.DispatchEvent([user._id], obj, { action: 'notify', value: 'Email verified:<br /><b>' + EscapeHtml(userinfo.email) + '</b>.', nolog: 1 });
} }
}); });
} }
@ -507,10 +513,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.crypto.randomBytes(16, function (err, buf) { obj.crypto.randomBytes(16, function (err, buf) {
var newpass = buf.toString('base64').split('=').join('').split('/').join(''); var newpass = buf.toString('base64').split('=').join('').split('/').join('');
require('./pass').hash(newpass, function (err, salt, hash) { require('./pass').hash(newpass, function (err, salt, hash) {
var userinfo = null;
if (err) throw err; if (err) throw err;
// Change the password // Change the password
var userinfo = obj.users[user._id]; userinfo = obj.users[user._id];
userinfo.salt = salt; userinfo.salt = salt;
userinfo.hash = hash; userinfo.hash = hash;
userinfo.passchange = Date.now(); userinfo.passchange = Date.now();
@ -518,7 +525,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.db.SetUser(userinfo); obj.db.SetUser(userinfo);
// Event the change // Event the change
var userinfo = obj.common.Clone(userinfo); userinfo = obj.common.Clone(userinfo);
delete userinfo.hash; delete userinfo.hash;
delete userinfo.passhint; delete userinfo.passhint;
delete userinfo.salt; delete userinfo.salt;
@ -526,7 +533,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete userinfo.domain; delete userinfo.domain;
delete userinfo.subscriptions; delete userinfo.subscriptions;
delete userinfo.passtype; delete userinfo.passtype;
obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Password reset for user ' + EscapeHtml(user.name), domain: domain.id }) obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Password reset for user ' + EscapeHtml(user.name), domain: domain.id });
// Send the new password // Send the new password
res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: '<div>Password for account <b>' + EscapeHtml(user.name) + '</b> has been reset to:</div><div style=padding:14px;font-size:18px><b>' + EscapeHtml(newpass) + '</b></div>Login and go to the \"My Account\" tab to update your password. <a href="' + domain.url + '">Go to login page</a>.' }); res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: '<div>Password for account <b>' + EscapeHtml(user.name) + '</b> has been reset to:</div><div style=padding:14px;font-size:18px><b>' + EscapeHtml(newpass) + '</b></div>Login and go to the \"My Account\" tab to update your password. <a href="' + domain.url + '">Go to login page</a>.' });
@ -569,7 +576,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (mesh.links[escUserId] != null) { delete mesh.links[escUserId]; obj.db.Set(mesh); } if (mesh.links[escUserId] != null) { delete mesh.links[escUserId]; obj.db.Set(mesh); }
// Notify mesh change // Notify mesh change
var change = 'Removed user ' + user.name + ' from mesh ' + mesh.name; var change = 'Removed user ' + user.name + ' from mesh ' + mesh.name;
obj.parent.DispatchEvent(['*', mesh._id, user._id, userid], obj, { etype: 'mesh', username: user.name, userid: userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }) obj.parent.DispatchEvent(['*', mesh._id, user._id, userid], obj, { etype: 'mesh', username: user.name, userid: userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id });
} }
} }
} }
@ -582,7 +589,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete obj.users[user._id]; delete obj.users[user._id];
req.session = null; req.session = null;
res.redirect(domain.url); res.redirect(domain.url);
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'accountremove', msg: 'Account removed', domain: domain.id }) obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'accountremove', msg: 'Account removed', domain: domain.id });
} else { } else {
res.redirect(domain.url); res.redirect(domain.url);
} }
@ -609,7 +616,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.db.SetUser(user); obj.db.SetUser(user);
req.session.viewmode = 2; req.session.viewmode = 2;
res.redirect(domain.url); res.redirect(domain.url);
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id }) obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
}); });
} }
@ -618,11 +625,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var domain = checkUserIpAddress(req, res); var domain = checkUserIpAddress(req, res);
if (domain == null) return; if (domain == null) return;
if (!obj.args) { res.sendStatus(500); return; } if (!obj.args) { res.sendStatus(500); return; }
var domain = getDomain(req);
if ((domain.sspi != null) && ((req.query.login == null) || (obj.parent.loginCookieEncryptionKey == null))) { if ((domain.sspi != null) && ((req.query.login == null) || (obj.parent.loginCookieEncryptionKey == null))) {
// Login using SSPI // Login using SSPI
domain.sspi.authenticate(req, res, function (err) { if ((err != null) || (req.connection.user == null)) { res.end('Authentication Required...'); } else { handleRootRequestEx(req, res, domain); } }) domain.sspi.authenticate(req, res, function (err) { if ((err != null) || (req.connection.user == null)) { res.end('Authentication Required...'); } else { handleRootRequestEx(req, res, domain); } });
} else { } else {
// Login using a different system // Login using a different system
handleRootRequestEx(req, res, domain); handleRootRequestEx(req, res, domain);
@ -630,7 +636,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
function handleRootRequestEx(req, res, domain) { function handleRootRequestEx(req, res, domain) {
var nologout = false; var nologout = false, user = null, features = 0;
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
// Check if we have an incomplete domain name in the path // Check if we have an incomplete domain name in the path
@ -676,15 +682,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
req.session.currentNode = ''; req.session.currentNode = '';
// Check if this user exists, create it if not. // Check if this user exists, create it if not.
var user = obj.users[req.session.userid]; user = obj.users[req.session.userid];
if ((user == null) || (user.sid != req.session.usersid)) { if ((user == null) || (user.sid != req.session.usersid)) {
// Create the domain user // Create the domain user
var usercount = 0, user = { type: 'user', _id: req.session.userid, name: req.connection.user, domain: domain.id, sid: req.session.usersid }; var usercount = 0, user2 = { type: 'user', _id: req.session.userid, name: req.connection.user, domain: domain.id, sid: req.session.usersid };
for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } }
if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; } // If this is the first user, give the account site admin. if (usercount == 0) { user2.siteadmin = 0xFFFFFFFF; } // If this is the first user, give the account site admin.
obj.users[req.session.userid] = user; obj.users[req.session.userid] = user2;
obj.db.SetUser(user); obj.db.SetUser(user2);
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: req.connection.user, account: user, action: 'accountcreate', msg: 'Domain account created, user ' + req.connection.user, domain: domain.id }) obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: req.connection.user, account: user2, action: 'accountcreate', msg: 'Domain account created, user ' + req.connection.user, domain: domain.id });
} }
} }
} }
@ -706,15 +712,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} else if (req.query.node) { } else if (req.query.node) {
currentNode = 'node/' + domain.id + '/' + req.query.node; currentNode = 'node/' + domain.id + '/' + req.query.node;
} }
var user; var logoutcontrol = '';
var logoutcontrol; if (obj.args.nousers != true) { logoutcontrol = 'Welcome ' + obj.users[req.session.userid].name + '.'; }
if (obj.args.nousers != true) {
user = obj.users[req.session.userid]
logoutcontrol = 'Welcome ' + user.name + '.';
}
// Give the web page a list of supported server features // Give the web page a list of supported server features
var features = 0; features = 0;
if (obj.args.wanonly == true) { features += 1; } // WAN-only mode if (obj.args.wanonly == true) { features += 1; } // WAN-only mode
if (obj.args.lanonly == true) { features += 2; } // LAN-only mode if (obj.args.lanonly == true) { features += 2; } // LAN-only mode
if (obj.args.nousers == true) { features += 4; } // Single user mode if (obj.args.nousers == true) { features += 4; } // Single user mode
@ -745,7 +747,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
} else { } else {
// Send back the login application // Send back the login application
var loginmode = req.session.loginmode, features = 0; var loginmode = req.session.loginmode;
features = 0;
delete req.session.loginmode; // Clear this state, if the user hits refresh, we want to go back to the login page. delete req.session.loginmode; // Clear this state, if the user hits refresh, we want to go back to the login page.
if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 32; } // Allow site within iframe if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 32; } // Allow site within iframe
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
@ -900,7 +903,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
for (var i = 3; i < spliturl.length; i++) { if (obj.common.IsFilenameValid(spliturl[i]) == true) { path += '/' + spliturl[i]; filename = spliturl[i]; } else { res.sendStatus(404); return; } } for (var i = 3; i < spliturl.length; i++) { if (obj.common.IsFilenameValid(spliturl[i]) == true) { path += '/' + spliturl[i]; filename = spliturl[i]; } else { res.sendStatus(404); return; } }
var stat = null; var stat = null;
try { stat = obj.fs.statSync(path) } catch (e) { } try { stat = obj.fs.statSync(path); } catch (e) { }
if ((stat != null) && ((stat.mode & 0x004000) == 0)) { if ((stat != null) && ((stat.mode & 0x004000) == 0)) {
if (req.query.download == 1) { if (req.query.download == 1) {
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=\"' + filename + '\"' }); res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=\"' + filename + '\"' });
@ -923,9 +926,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (splitpath[1] != '') { serverpath += '-' + splitpath[1]; } // Add the domain if needed if (splitpath[1] != '') { serverpath += '-' + splitpath[1]; } // Add the domain if needed
serverpath += ('/' + splitpath[0] + '-' + splitpath[2]); serverpath += ('/' + splitpath[0] + '-' + splitpath[2]);
for (var i = 3; i < splitpath.length; i++) { if (obj.common.IsFilenameValid(splitpath[i]) == true) { serverpath += '/' + splitpath[i]; filename = splitpath[i]; } else { return null; } } // Check that each folder is correct for (var i = 3; i < splitpath.length; i++) { if (obj.common.IsFilenameValid(splitpath[i]) == true) { serverpath += '/' + splitpath[i]; filename = splitpath[i]; } else { return null; } } // Check that each folder is correct
var fullpath = obj.path.resolve(obj.filespath, serverpath), quota = 0; return { fullpath: obj.path.resolve(obj.filespath, serverpath), path: serverpath, name: filename, quota: obj.getQuota(objid, domain) };
return { fullpath: fullpath, path: serverpath, name: filename, quota: obj.getQuota(objid, domain) }; };
}
// Return the maximum number of bytes allowed in the user account "My Files". // Return the maximum number of bytes allowed in the user account "My Files".
obj.getQuota = function (objid, domain) { obj.getQuota = function (objid, domain) {
@ -944,7 +946,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
return 1048576; // By default, the server will have a 1 meg limit on mesh accounts return 1048576; // By default, the server will have a 1 meg limit on mesh accounts
} }
return 0; return 0;
} };
// Download a file from the server // Download a file from the server
function handleDownloadFile(req, res) { function handleDownloadFile(req, res) {
@ -1014,7 +1016,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.fs.mkdir(xfile.fullpath, function () { obj.fs.mkdir(xfile.fullpath, function () {
// Write the file // Write the file
obj.fs.writeFile(obj.path.join(xfile.fullpath, filename), filedata, function () { obj.fs.writeFile(obj.path.join(xfile.fullpath, filename), filedata, function () {
obj.parent.DispatchEvent([user._id], obj, 'updatefiles') // Fire an event causing this user to update this files obj.parent.DispatchEvent([user._id], obj, 'updatefiles'); // Fire an event causing this user to update this files
}); });
}); });
})(xfile.fullpath, names[i], filedata); })(xfile.fullpath, names[i], filedata);
@ -1027,7 +1029,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var file = files.files[i], fpath = obj.path.join(xfile.fullpath, file.originalFilename); var file = files.files[i], fpath = obj.path.join(xfile.fullpath, file.originalFilename);
if (obj.common.IsFilenameValid(file.originalFilename) && ((totalsize + file.size) < xfile.quota)) { // Check if quota would not be broken if we add this file if (obj.common.IsFilenameValid(file.originalFilename) && ((totalsize + file.size) < xfile.quota)) { // Check if quota would not be broken if we add this file
obj.fs.rename(file.path, fpath, function () { obj.fs.rename(file.path, fpath, function () {
obj.parent.DispatchEvent([user._id], obj, 'updatefiles') // Fire an event causing this user to update this files obj.parent.DispatchEvent([user._id], obj, 'updatefiles'); // Fire an event causing this user to update this files
}); });
} else { } else {
try { obj.fs.unlink(file.path); } catch (e) { } try { obj.fs.unlink(file.path); } catch (e) { }
@ -1051,7 +1053,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.parent.RemoveAllEventDispatch(target); obj.parent.RemoveAllEventDispatch(target);
obj.parent.AddEventDispatch(subscriptions, target); obj.parent.AddEventDispatch(subscriptions, target);
return subscriptions; return subscriptions;
} };
// Handle a web socket relay request // Handle a web socket relay request
function handleRelayWebSocket(ws, req) { function handleRelayWebSocket(ws, req) {
@ -1143,13 +1145,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// CIRA ---> TLS // CIRA ---> TLS
Debug(3, 'Relay TLS CIRA data', data.length); Debug(3, 'Relay TLS CIRA data', data.length);
if (data.length > 0) { try { ser.updateBuffer(Buffer.from(data, 'binary')); } catch (e) { } } if (data.length > 0) { try { ser.updateBuffer(Buffer.from(data, 'binary')); } catch (e) { } }
} };
// Handke CIRA tunnel state change // Handke CIRA tunnel state change
chnl.onStateChange = function (ciraconn, state) { chnl.onStateChange = function (ciraconn, state) {
Debug(2, 'Relay TLS CIRA state change', state); Debug(2, 'Relay TLS CIRA state change', state);
if (state == 0) { try { ws.close(); } catch (e) { } } if (state == 0) { try { ws.close(); } catch (e) { } }
} };
// TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF // TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
var TLSSocket = require('tls').TLSSocket; var TLSSocket = require('tls').TLSSocket;
@ -1199,18 +1201,18 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
ws.forwardclient.onStateChange = function (ciraconn, state) { ws.forwardclient.onStateChange = function (ciraconn, state) {
Debug(2, 'Relay CIRA state change', state); Debug(2, 'Relay CIRA state change', state);
if (state == 0) { try { ws.close(); } catch (e) { } } if (state == 0) { try { ws.close(); } catch (e) { } }
} };
ws.forwardclient.onData = function (ciraconn, data) { ws.forwardclient.onData = function (ciraconn, data) {
Debug(4, 'Relay CIRA data', data.length); Debug(4, 'Relay CIRA data', data.length);
if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor
if (data.length > 0) { try { ws.send(data); } catch (e) { } } // TODO: Add TLS support if (data.length > 0) { try { ws.send(data); } catch (e) { } } // TODO: Add TLS support
} };
ws.forwardclient.onSendOk = function (ciraconn) { ws.forwardclient.onSendOk = function (ciraconn) {
// TODO: Flow control? (Dont' really need it with AMT, but would be nice) // TODO: Flow control? (Dont' really need it with AMT, but would be nice)
//console.log('onSendOk'); //console.log('onSendOk');
} };
// Fetch Intel AMT credentials & Setup interceptor // Fetch Intel AMT credentials & Setup interceptor
if (req.query.p == 1) { if (req.query.p == 1) {
@ -1345,7 +1347,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var r = 0, dir; var r = 0, dir;
try { dir = obj.fs.readdirSync(path); } catch (e) { return 0; } try { dir = obj.fs.readdirSync(path); } catch (e) { return 0; }
for (var i in dir) { for (var i in dir) {
var stat = obj.fs.statSync(path + '/' + dir[i]) var stat = obj.fs.statSync(path + '/' + dir[i]);
if ((stat.mode & 0x004000) == 0) { r += stat.size; } else { r += readTotalFileSize(path + '/' + dir[i]); } if ((stat.mode & 0x004000) == 0) { r += stat.size; } else { r += readTotalFileSize(path + '/' + dir[i]); }
} }
return r; return r;
@ -1359,15 +1361,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (obj.fs.lstatSync(pathx).isDirectory()) { deleteFolderRec(pathx); } else { obj.fs.unlinkSync(pathx); } if (obj.fs.lstatSync(pathx).isDirectory()) { deleteFolderRec(pathx); } else { obj.fs.unlinkSync(pathx); }
}); });
obj.fs.rmdirSync(path); obj.fs.rmdirSync(path);
}; }
// Handle Intel AMT events // Handle Intel AMT events
// To subscribe, add "http://server:port/amtevents.ashx" to Intel AMT subscriptions. // To subscribe, add "http://server:port/amtevents.ashx" to Intel AMT subscriptions.
obj.handleAmtEventRequest = function (req, res) { obj.handleAmtEventRequest = function (req, res) {
var domain = getDomain(req); var domain = getDomain(req);
try { try {
if (req.headers['authorization']) { if (req.headers.authorization) {
var authstr = req.headers['authorization']; var authstr = req.headers.authorization;
if (authstr.substring(0, 7) == "Digest ") { if (authstr.substring(0, 7) == "Digest ") {
var auth = obj.common.parseNameValueList(obj.common.quoteSplit(authstr.substring(7))); var auth = obj.common.parseNameValueList(obj.common.quoteSplit(authstr.substring(7)));
if ((req.url === auth.uri) && (obj.httpAuthRealm === auth.realm) && (auth.opaque === obj.crypto.createHmac('SHA384', obj.httpAuthRandom).update(auth.nonce).digest('hex'))) { if ((req.url === auth.uri) && (obj.httpAuthRealm === auth.realm) && (auth.opaque === obj.crypto.createHmac('SHA384', obj.httpAuthRandom).update(auth.nonce).digest('hex'))) {
@ -1436,7 +1438,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
res.set({ 'WWW-Authenticate': 'Digest realm="' + obj.httpAuthRealm + '", qop="auth,auth-int", nonce="' + nonce + '", opaque="' + opaque + '"' }); res.set({ 'WWW-Authenticate': 'Digest realm="' + obj.httpAuthRealm + '", qop="auth,auth-int", nonce="' + nonce + '", opaque="' + opaque + '"' });
res.sendStatus(401); res.sendStatus(401);
}); });
} };
// Handle a server backup request // Handle a server backup request
function handleBackupRequest(req, res) { function handleBackupRequest(req, res) {
@ -1541,11 +1543,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// If the agentid is 3 or 4, check if we have a signed MeshCmd.exe // If the agentid is 3 or 4, check if we have a signed MeshCmd.exe
if ((agentid == 3)) { // Signed Windows MeshCmd.exe x86 if ((agentid == 3)) { // Signed Windows MeshCmd.exe x86
var stats = null, meshCmdPath = obj.path.join(__dirname, 'agents', 'MeshCmd-signed.exe'); var stats = null, meshCmdPath = obj.path.join(__dirname, 'agents', 'MeshCmd-signed.exe');
try { stats = obj.fs.statSync(meshCmdPath) } catch (e) { } try { stats = obj.fs.statSync(meshCmdPath); } catch (e) { }
if ((stats != null)) { res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshcmd' + ((req.query.meshcmd <= 3) ? '.exe' : '') }); res.sendFile(meshCmdPath); return; } if ((stats != null)) { res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshcmd' + ((req.query.meshcmd <= 3) ? '.exe' : '') }); res.sendFile(meshCmdPath); return; }
} else if ((agentid == 4)) { // Signed Windows MeshCmd64.exe x64 } else if ((agentid == 4)) { // Signed Windows MeshCmd64.exe x64
var stats = null, meshCmd64Path = obj.path.join(__dirname, 'agents', 'MeshCmd64-signed.exe'); var stats = null, meshCmd64Path = obj.path.join(__dirname, 'agents', 'MeshCmd64-signed.exe');
try { stats = obj.fs.statSync(meshCmd64Path) } catch (e) { } try { stats = obj.fs.statSync(meshCmd64Path); } catch (e) { }
if ((stats != null)) { res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshcmd' + ((req.query.meshcmd <= 4) ? '.exe' : '') }); res.sendFile(meshCmd64Path); return; } if ((stats != null)) { res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshcmd' + ((req.query.meshcmd <= 4) ? '.exe' : '') }); res.sendFile(meshCmd64Path); return; }
} }
// No signed agents, we are going to merge a new MeshCmd. // No signed agents, we are going to merge a new MeshCmd.
@ -1581,7 +1583,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key
serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
debugLevel: 0 debugLevel: 0
} };
if (user != null) { meshaction.username = user.name; } if (user != null) { meshaction.username = user.name; }
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + getWebServerName(domain) + ':' + httpsPort + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; } if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + getWebServerName(domain) + ':' + httpsPort + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; }
@ -1596,7 +1598,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key
serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
debugLevel: 0 debugLevel: 0
} };
if (user != null) { meshaction.username = user.name; } if (user != null) { meshaction.username = user.name; }
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + getWebServerName(domain) + ':' + httpsPort + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; } if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + getWebServerName(domain) + ':' + httpsPort + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; }
@ -1619,7 +1621,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
response += '</table></body></html>'; response += '</table></body></html>';
res.send(response); res.send(response);
} }
} };
// Get the web server hostname. This may change if using a domain with a DNS name. // Get the web server hostname. This may change if using a domain with a DNS name.
function getWebServerName(domain) { function getWebServerName(domain) {
@ -1660,7 +1662,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshagent.msh' }); res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshagent.msh' });
res.send(meshsettings); res.send(meshsettings);
} };
// Add HTTP security headers to all responses // Add HTTP security headers to all responses
obj.app.use(function (req, res, next) { obj.app.use(function (req, res, next) {
@ -1785,7 +1787,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check we have agent rights // Check we have agent rights
var rights = user.links[agent.dbMeshKey].rights; var rights = user.links[agent.dbMeshKey].rights;
if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) && (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); } if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) && (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); }
} };
// Send the core module to the mesh agent // Send the core module to the mesh agent
obj.sendMeshAgentCore = function (user, domain, nodeid, core) { obj.sendMeshAgentCore = function (user, domain, nodeid, core) {
@ -1815,7 +1817,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
agent.send(obj.common.ShortToStr(10) + obj.common.ShortToStr(0) + hash + core); agent.send(obj.common.ShortToStr(10) + obj.common.ShortToStr(0) + hash + core);
} }
} }
} };
// Get the server path of a user or mesh object // Get the server path of a user or mesh object
function getServerRootFilePath(obj) { function getServerRootFilePath(obj) {
@ -1878,36 +1880,37 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Count sessions and event any changes // Count sessions and event any changes
obj.recountSessions = function (changedSessionId) { obj.recountSessions = function (changedSessionId) {
var userid, oldcount, newcount, x, serverid;
if (changedSessionId == null) { if (changedSessionId == null) {
// Recount all sessions // Recount all sessions
// Calculate the session count for all userid's // Calculate the session count for all userid's
var newSessionsCount = {}; var newSessionsCount = {};
for (var userid in obj.wssessions) { newSessionsCount[userid] = obj.wssessions[userid].length; } for (userid in obj.wssessions) { newSessionsCount[userid] = obj.wssessions[userid].length; }
for (var serverid in obj.wsPeerSessions3) { for (serverid in obj.wsPeerSessions3) {
for (var userid in obj.wsPeerSessions3[serverid]) { for (userid in obj.wsPeerSessions3[serverid]) {
var c = obj.wsPeerSessions3[serverid][userid].length; x = obj.wsPeerSessions3[serverid][userid].length;
if (newSessionsCount[userid] == null) { newSessionsCount[userid] = c; } else { newSessionsCount[userid] += c; } if (newSessionsCount[userid] == null) { newSessionsCount[userid] = x; } else { newSessionsCount[userid] += x; }
} }
} }
// See what session counts have changed, event any changes // See what session counts have changed, event any changes
for (var userid in newSessionsCount) { for (userid in newSessionsCount) {
var newcount = newSessionsCount[userid]; newcount = newSessionsCount[userid];
var oldcount = obj.sessionsCount[userid]; oldcount = obj.sessionsCount[userid];
if (oldcount == null) { oldcount = 0; } else { delete obj.sessionsCount[userid]; } if (oldcount == null) { oldcount = 0; } else { delete obj.sessionsCount[userid]; }
if (newcount != oldcount) { if (newcount != oldcount) {
var x = userid.split('/'); x = userid.split('/');
obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 }) obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 });
} }
} }
// If there are any counts left in the old counts, event to zero // If there are any counts left in the old counts, event to zero
for (var userid in obj.sessionsCount) { for (userid in obj.sessionsCount) {
var oldcount = obj.sessionsCount[userid]; oldcount = obj.sessionsCount[userid];
if ((oldcount != null) && (oldcount != 0)) { if ((oldcount != null) && (oldcount != 0)) {
var x = userid.split('/'); x = userid.split('/');
obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: 0, domain: x[1], nolog: 1, nopeers: 1 }) obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: 0, domain: x[1], nolog: 1, nopeers: 1 });
} }
} }
@ -1915,23 +1918,23 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.sessionsCount = newSessionsCount; obj.sessionsCount = newSessionsCount;
} else { } else {
// Figure out the userid // Figure out the userid
var userid = changedSessionId.split('/').slice(0, 3).join('/'); userid = changedSessionId.split('/').slice(0, 3).join('/');
// Recount only changedSessionId // Recount only changedSessionId
var newcount = 0; newcount = 0;
if (obj.wssessions[userid] != null) { newcount = obj.wssessions[userid].length; } if (obj.wssessions[userid] != null) { newcount = obj.wssessions[userid].length; }
for (var serverid in obj.wsPeerSessions3) { if (obj.wsPeerSessions3[serverid][userid] != null) { newcount += obj.wsPeerSessions3[serverid][userid].length; } } for (serverid in obj.wsPeerSessions3) { if (obj.wsPeerSessions3[serverid][userid] != null) { newcount += obj.wsPeerSessions3[serverid][userid].length; } }
var oldcount = obj.sessionsCount[userid]; oldcount = obj.sessionsCount[userid];
if (oldcount == null) { oldcount = 0; } if (oldcount == null) { oldcount = 0; }
// If the count changed, update and event // If the count changed, update and event
if (newcount != oldcount) { if (newcount != oldcount) {
var x = userid.split('/'); x = userid.split('/');
obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 }) obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 });
obj.sessionsCount[userid] = newcount; obj.sessionsCount[userid] = newcount;
} }
} }
} };
// Return true if a mobile browser is detected. // Return true if a mobile browser is detected.
// This code comes from "http://detectmobilebrowsers.com/" and was modified, This is free and unencumbered software released into the public domain. For more information, please refer to the http://unlicense.org/ // This code comes from "http://detectmobilebrowsers.com/" and was modified, This is free and unencumbered software released into the public domain. For more information, please refer to the http://unlicense.org/
@ -1943,5 +1946,4 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
return obj; return obj;
} };

View File

@ -6,7 +6,12 @@
* @version v0.0.1 * @version v0.0.1
*/ */
'use strict'; /*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict";
// This module is only called when MeshCentral is running as a Windows service. // This module is only called when MeshCentral is running as a Windows service.
// In this case, we don't want to start a child process, so we launch directly without arguments. // In this case, we don't want to start a child process, so we launch directly without arguments.