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;
};
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()
{
var destinationFolder = null;
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');
return this.getProgramFolder() + '\\mesh';
};
this.enumerateService = function () {

View File

@ -6,23 +6,29 @@
* @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 (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) {
var obj = {};
obj.db;
obj.webserver;
obj.redirserver;
obj.mpsserver;
obj.swarmserver;
obj.mailserver;
obj.amtEventHandler;
obj.amtScanner;
obj.meshScanner;
obj.letsencrypt;
obj.db = null;
obj.webserver = null;
obj.redirserver = null;
obj.mpsserver = null;
obj.swarmserver = null;
obj.mailserver = null;
obj.amtEventHandler = null;
obj.amtScanner = null;
obj.meshScanner = null;
obj.letsencrypt = null;
obj.eventsDispatch = {};
obj.fs = require('fs');
obj.path = require('path');
@ -78,13 +84,14 @@ function CreateMeshCentralServer(config, args) {
// Start the Meshcentral server
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.
// 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'];
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; }
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)) {
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.');
return;
}
// 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))) {
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.
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 });
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: 0.5 });
svc.on('install', function () { console.log('MeshCentral service installed.'); svc.start(); });
svc.on('uninstall', function () { console.log('MeshCentral service uninstalled.'); 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('alreadyinstalled', function () { console.log('MeshCentral service already installed.'); 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.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); } }
@ -132,7 +139,7 @@ function CreateMeshCentralServer(config, args) {
} else {
// if "--launch" is not specified, launch the server as a child process.
var startLine = '';
for (var i in process.argv) {
for (i in process.argv) {
var arg = process.argv[i];
if (arg.length > 0) {
if (startLine.length > 0) startLine += ' ';
@ -141,7 +148,7 @@ function CreateMeshCentralServer(config, args) {
}
obj.launchChildServer(startLine);
}
}
};
// Launch MeshCentral as a child server and monitor it.
obj.launchChildServer = function (startLine) {
@ -166,7 +173,7 @@ function CreateMeshCentralServer(config, args) {
console.log(error);
console.log('ERROR: MeshCentral failed with critical error, check MeshErrors.txt. Restarting in 5 seconds...');
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); });
@ -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');
});
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
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) { } }
callback(obj.currentVer, latestVer);
});
}
};
// 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
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 () {
var i;
//var wincmd = require('node-windows');
//wincmd.list(function (svc) { console.log(svc); }, true);
// Write the server state
obj.updateServerState('state', 'starting');
// Look to see if data and/or file path is specified
if (obj.args.datapath) { obj.datapath = obj.args.datapath; }
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.
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
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; }
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
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 (var i in obj.config.domains) {
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 (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 = '/'; }
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(','); } }
}
// 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
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'); }
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(); }
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.'); }
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 + '.'); }
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 (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 == "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 == "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(); }); });
return;
}
@ -333,7 +341,7 @@ function CreateMeshCentralServer(config, args) {
obj.db.Get('dbconfig', function (err, dbconfig) {
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); }); }
// 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.length == 64) {
@ -367,12 +375,12 @@ function CreateMeshCentralServer(config, args) {
}
});
});
}
};
// Done starting the redirection server, go on to load the server certificates
obj.StartEx2 = function () {
// 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) {
if (obj.config.letsencrypt == null) {
obj.StartEx3(certs); // Just use the configured certificates
@ -387,10 +395,11 @@ function CreateMeshCentralServer(config, args) {
}
}
});
}
};
// Start the server with the given certificates
obj.StartEx3 = function (certs) {
var i;
obj.certificates = certs;
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; }
// 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())) {
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
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.updateMeshAgentInstallScripts();
@ -460,7 +469,7 @@ function CreateMeshCentralServer(config, args) {
obj.maintenanceTimer = setInterval(obj.maintenanceActions, 1000 * 60 * 60); // Run this every hour
// 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
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'); }
obj.updateServerState('state', 'running');
});
});
}
};
// Perform maintenance operations (called every hour)
obj.maintenanceActions = function () {
@ -499,7 +508,7 @@ function CreateMeshCentralServer(config, args) {
// Perform other database cleanup
obj.db.cleanup();
}
};
// Stop the Meshcentral server
obj.Stop = function (restoreFile) {
@ -507,7 +516,7 @@ function CreateMeshCentralServer(config, args) {
if (!obj.db) return;
// 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)
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 {
obj.debug(1, 'Server stopped');
@ -553,25 +562,25 @@ function CreateMeshCentralServer(config, args) {
// Update the server state
obj.updateServerState('state', 'stopped');
}
};
// Event Dispatch
obj.AddEventDispatch = function (ids, target) {
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); } }
}
};
obj.RemoveEventDispatch = function (ids, target) {
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.debug(3, 'RemoveEventDispatchId', id);
if (obj.eventsDispatch[id] != null) { delete obj.eventsDispatch[id]; }
}
};
obj.RemoveAllEventDispatch = function (target) {
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); } }
}
};
obj.DispatchEvent = function (ids, source, event, fromPeerServer) {
// If the database is not setup, exit now.
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); }
}
};
// 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.
obj.GetRoutingServerId = function (nodeid, connectType) {
if (obj.multiServer == null) return null;
for (serverid in obj.peerConnectivityByNode) {
for (var serverid in obj.peerConnectivityByNode) {
if (serverid == obj.serverId) continue;
var state = obj.peerConnectivityByNode[serverid][nodeid];
if ((state != null) && ((state.connectivity & connectType) != 0)) { return { serverid: serverid, meshid: state.meshid }; }
}
return null;
}
};
// 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
@ -618,8 +627,8 @@ function CreateMeshCentralServer(config, args) {
for (var nodeid in nodeids) {
var meshid = null, state = null, oldConnectivity = 0, oldPowerState = 0, newConnectivity = 0, newPowerState = 0;
var oldState = obj.connectivityByNode[nodeid];
if (oldState != null) { meshid = oldState.meshid; oldConnectivity = oldState.connectivity; oldPowerState = oldState.powerState; }
for (serverid in obj.peerConnectivityByNode) {
if (oldState != null) { meshid = oldState.meshid; oldConnectivity = oldState.connectivity; oldPowerState = oldState.powerState; }
for (var serverid in obj.peerConnectivityByNode) {
var peerState = obj.peerConnectivityByNode[serverid][nodeid];
if (peerState != 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 });
}
}
}
};
// 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
@ -660,8 +669,8 @@ function CreateMeshCentralServer(config, args) {
// connectTime: time of connection, milliseconds elapsed since the UNIX epoch.
// 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
var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local'];
var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present'];
//var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local'];
//var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present'];
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)));
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
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.powerState == null) || (state.powerState != powerState)) {
state.powerState = powerState;
@ -731,7 +740,7 @@ function CreateMeshCentralServer(config, args) {
var x = {}; x[nodeid] = 1;
obj.UpdateConnectivityState(x);
}
}
};
// 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
@ -798,7 +807,7 @@ function CreateMeshCentralServer(config, args) {
var x = {}; x[nodeid] = 1;
obj.UpdateConnectivityState(x);
}
}
};
// Update the default mesh core
obj.updateMeshCoreTimer = 'notset';
@ -845,9 +854,9 @@ function CreateMeshCentralServer(config, args) {
obj.fs.watch(obj.path.join(meshcorePath, 'meshcore.js'), function (eventType, filename) {
if (obj.updateMeshCoreTimer != null) { clearTimeout(obj.updateMeshCoreTimer); obj.updateMeshCoreTimer = null; }
obj.updateMeshCoreTimer = setTimeout(function () { obj.updateMeshCore(); console.log('Updated meshcore.js.'); }, 5000);
})
});
}
}
};
// Update the default meshcmd
obj.updateMeshCmdTimer = 'notset';
@ -860,7 +869,7 @@ function CreateMeshCentralServer(config, args) {
obj.defaultMeshCmd = null; if (func != null) { func(false); } // meshcmd.js not found
}
}
// Read meshcore.js and all .js files in the modules folder.
var moduleAdditions = 'var addedModules = [];', modulesDir = null;
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) {
if (obj.updateMeshCmdTimer != null) { clearTimeout(obj.updateMeshCmdTimer); obj.updateMeshCmdTimer = null; }
obj.updateMeshCmdTimer = setTimeout(function () { obj.updateMeshCmd(); console.log('Updated meshcmd.js.'); }, 5000);
})
});
}
}
};
// List of possible mesh agent install scripts
var meshAgentsInstallScriptList = {
@ -903,7 +912,7 @@ function CreateMeshCentralServer(config, args) {
var stream = null;
try {
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) {
// 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]; }
@ -915,7 +924,7 @@ function CreateMeshCentralServer(config, args) {
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;
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; }
});
stream.info = meshAgentsInstallScriptList[scriptid];
@ -923,7 +932,7 @@ function CreateMeshCentralServer(config, args) {
stream.hash = obj.crypto.createHash('sha384', stream);
} catch (e) { }
}
}
};
// List of possible mesh agents
obj.meshAgentsArchitectureNumbers = {
@ -962,10 +971,10 @@ function CreateMeshCentralServer(config, args) {
var archcount = 0;
for (var archid in obj.meshAgentsArchitectureNumbers) {
var agentpath = obj.path.join(__dirname, 'agents', obj.meshAgentsArchitectureNumbers[archid].localname);
// Fetch all the agent binary information
var stats = null;
try { stats = obj.fs.statSync(agentpath) } catch (e) { }
try { stats = obj.fs.statSync(agentpath); } catch (e) { }
if ((stats != null)) {
// If file exists
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[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
obj.getLoginToken = function (userid, func) {
@ -1016,7 +1025,7 @@ function CreateMeshCentralServer(config, args) {
});
}
});
}
};
// Show the yser login token generation key
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')); });
}
});
}
};
// Generate a cryptographic key used to encode and decode cookies
obj.generateCookieKey = function () {
return new Buffer(obj.crypto.randomBytes(32), 'binary');
//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)
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()]);
return Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
} 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)
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)
return o;
} catch (e) { return null; }
}
};
// Debug
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 == 4) { console.log(arguments[1], arguments[2], arguments[3]); }
else if (arguments.length == 5) { console.log(arguments[1], arguments[2], arguments[3], arguments[4]); }
}
};
// Update server state. Writes a server state file.
var meshServerState = {};
obj.updateServerState = function(name, val) {
obj.updateServerState = function (name, val) {
if ((name != null) && (val != null)) {
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; } } }
@ -1087,7 +1096,7 @@ function CreateMeshCentralServer(config, args) {
var r = 'time=' + Date.now() + '\r\n';
for (var i in meshServerState) { r += (i + '=' + meshServerState[i] + '\r\n'); }
obj.fs.writeFileSync(obj.getConfigFilePath('serverstate.txt'), r);
}
};
// Logging funtions
function logException(e) { e += ''; logErrorEvent(e); }
@ -1124,7 +1133,7 @@ function CreateMeshCentralServer(config, args) {
}
//console.log('getConfigFilePath(\"' + filename + '\") = ' + obj.path.join(obj.datapath, filename));
return obj.path.join(obj.datapath, filename);
}
};
return obj;
}
@ -1132,6 +1141,7 @@ function CreateMeshCentralServer(config, args) {
// Return the server configuration
function getConfig() {
// Figure out the datapath location
var i;
var fs = require('fs');
var path = require('path');
var datapath = null;
@ -1150,7 +1160,7 @@ function getConfig() {
// Load and validate the configuration file
try { config = require(configFilePath); } catch (e) { console.log('ERROR: Unable to parse ' + configFilePath + '.'); return null; }
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 {
// Copy the "sample-config.json" to give users a starting point
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
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
require('./common.js').objKeysToLower(config);

View File

@ -6,7 +6,12 @@
* @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) {
var obj = {};
@ -23,9 +28,10 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
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 == 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) {
var rights;
if (command.nodeid == null) return false;
var user = obj.parent.users[userid];
if (user == null) return false;
@ -37,7 +43,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
var agent = obj.parent.wsagents[command.nodeid];
if (agent != null) {
// 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
command.sessionid = ws.sessionId; // Set the session id, required for responses.
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
if (routing != null) {
// 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
command.fromSessionid = ws.sessionId; // Set the session id, required for responses.
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;
}
};
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.
@ -160,7 +166,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
// Wait for other relay connection
ws.pause(); // Hold traffic until the other connection
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
if (parent.parent.multiServer != null) {
@ -213,6 +219,6 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
obj.id = null;
}
});
return obj;
}
};

View File

@ -6,7 +6,12 @@
* @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
// 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
function getInterfaceList() {
var i;
var ipv4 = ['*'], ipv6 = ['*']; // Bind to IN_ADDR_ANY always
var interfaces = require('os').networkInterfaces();
for (var i in interfaces) {
for (i in interfaces) {
var xinterface = interfaces[i];
for (var j in xinterface) {
var interface2 = xinterface[j];
@ -43,11 +49,11 @@ module.exports.CreateMeshScanner = function (parent) {
// Setup all IPv4 and IPv6 servers
function setupServers() {
var addresses = getInterfaceList();
for (var i in obj.servers4) { obj.servers4[i].xxclear = true; }
for (var i in obj.servers6) { obj.servers6[i].xxclear = true; }
for (var i in addresses.ipv4) {
var localAddress = addresses.ipv4[i];
var addresses = getInterfaceList(), i, localAddress, bindOptions;
for (i in obj.servers4) { obj.servers4[i].xxclear = true; }
for (i in obj.servers6) { obj.servers6[i].xxclear = true; }
for (i in addresses.ipv4) {
localAddress = addresses.ipv4[i];
if (obj.servers4[localAddress] != null) {
// Server already exists
obj.servers4[localAddress].xxclear = false;
@ -59,7 +65,7 @@ module.exports.CreateMeshScanner = function (parent) {
server4.xxtype = 4;
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]; });
var bindOptions = { port: 16989, exclusive: true };
bindOptions = { port: 16989, exclusive: true };
if (server4.xxlocal != '*') { bindOptions.address = server4.xxlocal; }
server4.bind(bindOptions, function () {
try {
@ -77,8 +83,8 @@ module.exports.CreateMeshScanner = function (parent) {
}
}
for (var i in addresses.ipv6) {
var localAddress = addresses.ipv6[i];
for (i in addresses.ipv6) {
localAddress = addresses.ipv6[i];
if (obj.servers6[localAddress] != null) {
// Server already exists
obj.servers6[localAddress].xxclear = false;
@ -90,7 +96,7 @@ module.exports.CreateMeshScanner = function (parent) {
server6.xxtype = 6;
server6.xxlocal = localAddress;
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; }
server6.bind(bindOptions, function () {
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 (var i in obj.servers6) { if (obj.servers6[i].xxclear == true) { obj.servers6[i].close(); delete obj.servers6[i]; }; }
for (i in obj.servers4) { if (obj.servers4[i].xxclear == true) { obj.servers4[i].close(); delete obj.servers4[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
function clearServers() {
for (var i in obj.servers4) { obj.servers4[i].close(); delete obj.servers4[i]; }
for (var i in obj.servers6) { obj.servers6[i].close(); delete obj.servers6[i]; }
var 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
@ -128,27 +135,28 @@ module.exports.CreateMeshScanner = function (parent) {
setupServers();
obj.mainTimer = setInterval(obj.performScan, periodicScanTime);
return obj;
}
};
// Stop scanning for local network Mesh Agents
obj.stop = function () {
if (obj.mainTimer != null) { clearInterval(obj.mainTimer); obj.mainTimer = null; }
clearServers();
}
};
// Look for all Mesh Agents that may be locally reachable, indicating the presense of this server.
obj.performScan = function (server) {
var i;
if (server != null) {
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 == 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) { } }
} else {
for (var 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.servers4) { try { obj.servers4[i].send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } 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
}
}
};
// Called when a UDP packet is received from an agent.
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
obj.wakeOnLan = function (macs) {
for (var i in macs) {
var i, j;
for (i in macs) {
var mac = macs[i];
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');
//console.log(wakepacket.toString('hex'));
// 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 (var i in obj.servers6) { obj.servers6[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
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 (j in obj.servers6) { obj.servers6[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
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 (var i in obj.servers6) { obj.servers6[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
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 (j in obj.servers6) { obj.servers6[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
}, 200);
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 (var i in obj.servers6) { obj.servers6[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
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 (j in obj.servers6) { obj.servers6[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
}, 500);
}
}
};
return obj;
}
};

View File

@ -6,7 +6,12 @@
* @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
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) {
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
}
};
// Convert a mesh path array into a real path on the server side
function meshPathToRealPath(meshpath, user) {
@ -52,22 +57,22 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
//
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 ss = obj.fs.createReadStream(src);
var ds = obj.fs.createWriteStream(dest);
ss.fs = obj.fs;
ss.pipe(ds);
ss.pipe(ds);
ds.ss = ss;
/*
if (!this._copyStreams) { this._copyStreams = {}; this._copyStreamID = 0; }
ss.id = this._copyStreamID++;
this._copyStreams[ss.id] = ss;
*/
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]); }
ds.on('close', function() { /*delete this.ss.fs._copyStreams[this.ss.id];*/ func(tag); });
};
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]); }
ds.on('close', function () { /*delete this.ss.fs._copyStreams[this.ss.id];*/ func(tag); });
}
try {
// 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;
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) {
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 {
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 })); }
} catch (e) { }
}
}
};
user.subscriptions = obj.parent.subscribe(user._id, ws); // Subscribe to events
obj.ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive
// When data is received from the web socket
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; }
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
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 }));
break;
}
@ -127,10 +132,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var links = [];
if (command.meshid == null) {
// 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 {
// 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 (meshid.split('/').length == 0) { meshid = 'mesh/' + domain.id + '/' + command.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
obj.db.GetAllTypeNoTypeFieldMeshFiltered(links, domain.id, 'node', function (err, docs) {
var r = {};
for (var i in docs) {
for (i in docs) {
// Add the connection state
var state = obj.parent.parent.GetConnectivityState(docs[i]._id);
if (state) {
@ -150,7 +155,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
}
// Compress the meshid's
var meshid = docs[i].meshid;
meshid = docs[i].meshid;
if (!r[meshid]) { r[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) {
if (err == null && docs.length > 0) {
var timeline = [], time = null, previousPower;
for (var i in docs) {
for (i in docs) {
var doc = docs[i];
if (time == null) {
// First element
@ -228,14 +233,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
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
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 == 'copy') || (command.fileop == 'move')) {
if (obj.common.validateArray(command.names, 1) == false) return;
var scpath = meshPathToRealPath(command.scpath, user); // This will also check access rights
if (scpath == null) break;
// 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]);
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));
@ -319,7 +324,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Delete all events
if (user.siteadmin != 0xFFFFFFFF) break;
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;
}
case 'users':
@ -327,7 +332,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Request a list of all users
if ((user.siteadmin & 2) == 0) break;
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 != '~')) {
var userinfo = obj.common.Clone(obj.parent.users[i]);
delete userinfo.hash;
@ -403,10 +408,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if ((user.siteadmin & 2) == 0) break;
if (obj.parent.parent.multiServer == null) {
// 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 {
// 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
break;
@ -422,15 +427,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Remove all the mesh links to this user
if (deluser.links != null) {
for (var meshid in deluser.links) {
for (meshid in deluser.links) {
// Get the mesh
var mesh = obj.parent.meshes[meshid];
mesh = obj.parent.meshes[meshid];
if (mesh) {
// Remove user from the mesh
if (mesh.links[deluser._id] != null) { delete mesh.links[deluser._id]; obj.parent.db.Set(mesh); }
// Notify mesh change
var 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 })
change = 'Removed user ' + deluser.name + ' from mesh ' + mesh.name;
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);
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');
break;
@ -474,7 +479,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if (newuser2.subscriptions) { delete newuser2.subscriptions; }
if (newuser2.salt) { delete newuser2.salt; }
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;
@ -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
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 (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 (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) {
obj.db.SetUser(chguser);
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.subscriptions;
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)) {
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
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) {
// TODO: Add multi-server support
}
break;
}
case 'serverversion':
{
@ -564,10 +571,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if ((command.meshtype == 1) || (command.meshtype == 2)) {
// Create a type 1 agent-less Intel AMT mesh.
obj.parent.crypto.randomBytes(48, function (err, buf) {
var meshid = 'mesh/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
var links = {}
meshid = 'mesh/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
var links = {};
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.parent.meshes[meshid] = mesh;
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.subscriptions = obj.parent.subscribe(user._id, ws);
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;
@ -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
obj.db.Get(command.meshid, function (err, meshes) {
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
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
// 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
for (var i in meshes) {
for (i in meshes) {
var links = meshes[i].links;
for (var j in links) {
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
try {
var meshpath = getServerRootFilePath(mesh);
if (meshpath != null) { deleteFolderRec(meshpath); }
var meshpath = obj.parent.getServerRootFilePath(mesh);
if (meshpath != null) { obj.parent.deleteFolderRec(meshpath); }
} catch (e) { }
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
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) {
// Check if this user has rights to do this
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.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;
}
@ -648,7 +656,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
}
// Get the mesh
var mesh = obj.parent.meshes[command.meshid], change = '';
mesh = obj.parent.meshes[command.meshid];
if (mesh) {
// Check if this user has rights to do this
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));
// 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: change, 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: 'Added user ' + newuser.name + ' to mesh ' + mesh.name, domain: domain.id });
}
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
// Get the mesh
var mesh = obj.parent.meshes[command.meshid];
mesh = obj.parent.meshes[command.meshid];
if (mesh) {
// Check if this user has rights to do this
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));
// 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: change, 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: 'Removed user ' + deluser.name + ' from mesh ' + mesh.name, domain: domain.id });
}
}
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; }
// Get the mesh
var mesh = obj.parent.meshes[command.meshid];
mesh = obj.parent.meshes[command.meshid];
if (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
obj.parent.crypto.randomBytes(48, function (err, buf) {
// 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 } };
obj.db.Set(device);
// Event the new node
var device2 = obj.common.Clone(device);
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: change, domain: domain.id })
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 });
});
}
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
for (var i in command.nodeids) {
var nodeid = command.nodeids[i];
for (i in command.nodeids) {
nodeid = command.nodeids[i];
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
@ -774,7 +779,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0];
// Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid];
mesh = obj.parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
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
// 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: change, domain: domain.id })
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 });
// Disconnect all connections if needed
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.
// - 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.
for (var i in command.nodeids) {
var nodeid = command.nodeids[i], wakeActions = 0;
for (i in command.nodeids) {
nodeid = command.nodeids[i];
var wakeActions = 0;
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
// Get the device
@ -817,7 +822,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0];
// Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid];
mesh = obj.parent.meshes[node.meshid];
if (mesh) {
// 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
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
for (var i in obj.parent.wsagents) {
for (i in obj.parent.wsagents) {
var agent = obj.parent.wsagents[i];
if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) {
//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':
{
if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
for (var i in command.nodeids) {
var nodeid = command.nodeids[i], powerActions = 0;
for (i in command.nodeids) {
nodeid = command.nodeids[i];
var powerActions = 0;
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
// Get the device
@ -872,7 +878,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0];
// Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid];
mesh = obj.parent.meshes[node.meshid];
if (mesh) {
// 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.validateString(command.title, 1, 512) == false) break; // Check title
if (obj.common.validateString(command.msg, 1, 4096) == false) break; // Check message
for (var i in command.nodeids) {
var nodeid = command.nodeids[i], powerActions = 0;
for (i in command.nodeids) {
nodeid = command.nodeids[i];
var powerActions = 0;
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
// Get the device
@ -909,7 +916,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0];
// Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid];
mesh = obj.parent.meshes[node.meshid];
if (mesh) {
// 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];
// Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid];
mesh = obj.parent.meshes[node.meshid];
if (mesh) {
// 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; }
@ -968,13 +975,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var node = nodes[0];
// Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid];
mesh = obj.parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return;
// 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 = ": ";
// 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)
obj.parent.sendMeshAgentCore(user, domain, command.nodeid, data);
}
})
});
}
}
} 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
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) {
var meshlinks = user.links[nodes[0].meshid];
meshlinks = user.links[nodes[0].meshid];
if ((meshlinks) && (meshlinks.rights) && (meshlinks.rights & obj.parent.MESHRIGHT_REMOTECONTROL != 0)) {
// Add a user authentication cookie to a url
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
// Get the mesh
var mesh = obj.parent.meshes[command.meshid];
mesh = obj.parent.meshes[command.meshid];
if (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
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) {
var meshlinks = user.links[nodes[0].meshid];
meshlinks = user.links[nodes[0].meshid];
if ((meshlinks) && (meshlinks.rights) && (meshlinks.rights & obj.parent.MESHRIGHT_SETNOTES != 0)) {
// Set the id's notes
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') {
// Get the mesh for this device
var mesh = obj.parent.meshes[command.id];
mesh = obj.parent.meshes[command.id];
if (mesh) {
// Check if this user has rights to do this
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];
// Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid];
mesh = obj.parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
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') {
// Get the mesh for this device
var mesh = obj.parent.meshes[command.id];
mesh = obj.parent.meshes[command.id];
if (mesh) {
// Check if this user has rights to do this
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];
if (user) {
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 {
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);
// 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; }
// Send server information
@ -1259,7 +1267,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var r = {}, dir = obj.fs.readdirSync(path);
for (var i in dir) {
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]); }
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 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;
}
};

View File

@ -6,7 +6,12 @@
* @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.
module.exports.CreateMultiServer = function (parent, args) {
@ -46,7 +51,7 @@ module.exports.CreateMultiServer = function (parent, args) {
obj.stop = function () {
obj.connectionState = 0;
disconnect();
}
};
// Make one attempt at connecting to the server
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 == 'string') { obj.ws.send(msg); return; }
} catch (e) { }
}
};
// Process incoming peer server JSON data
function processServerData(msg) {
var str = msg.toString('utf8');
var str = msg.toString('utf8'), command = null;
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 (obj.authenticated != 3) {
// We get the peer's serverid and database identifier.
@ -198,7 +203,7 @@ module.exports.CreateMultiServer = function (parent, args) {
connect();
return obj;
}
};
// Create a mesh server module that received a connection to another server
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; }
obj.ws.send(data);
} catch (e) { }
}
};
// Disconnect this server
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 == 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; }
}
};
// When data is received from the peer server web socket
ws.on('message', function (msg) {
@ -244,7 +249,7 @@ module.exports.CreateMultiServer = function (parent, args) {
if (obj.authenticated >= 2) { // We are authenticated
if (msg.charCodeAt(0) == 123) { processServerData(msg); }
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.
}
else if (obj.authenticated < 2) { // We are not authenticated
@ -259,14 +264,14 @@ module.exports.CreateMultiServer = function (parent, args) {
obj.peernonce = msg.substring(50);
// 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
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
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) {
@ -337,9 +342,9 @@ module.exports.CreateMultiServer = function (parent, args) {
// Process incoming peer server JSON data
function processServerData(msg) {
var str = msg.toString('utf8');
var str = msg.toString('utf8'), command = null;
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 (obj.authenticated != 3) {
// We get the peer's serverid and database identifier.
@ -362,7 +367,7 @@ module.exports.CreateMultiServer = function (parent, args) {
}
return obj;
}
};
// If we have no peering configuration, don't setup this object
if (obj.peerConfig == null) { return null; }
@ -375,34 +380,34 @@ module.exports.CreateMultiServer = function (parent, args) {
var server = obj.peerServers[serverid];
if (server && server.peerServerKey) return server.peerServerKey;
return null;
}
};
// Dispatch an event to all other MeshCentral2 peer servers
obj.DispatchEvent = function (ids, source, event) {
var busmsg = JSON.stringify({ action: 'bus', ids: ids, event: event });
for (var serverid in obj.peerServers) { obj.peerServers[serverid].send(busmsg); }
}
};
// Dispatch a message to other MeshCentral2 peer servers
obj.DispatchMessage = function (msg) {
for (var serverid in obj.peerServers) { obj.peerServers[serverid].send(msg); }
}
};
// Dispatch a message to other MeshCentral2 peer servers
obj.DispatchMessageSingleServer = function (msg, serverid) {
var server = obj.peerServers[serverid];
if (server != null) { server.send(msg); }
}
};
// Attempt to connect to all peers
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).
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);
}
}
}
};
// We connected to a peer server, setup everything
obj.SetupPeerServer = function (server, peerServerId) {
@ -414,7 +419,7 @@ module.exports.CreateMultiServer = function (parent, args) {
// Send a list of user sessions to the peer
server.send(JSON.stringify({ action: 'sessionsTable', sessionsTable: Object.keys(obj.parent.webserver.wssessions2) }));
}
};
// We disconnected to a peer server, clean up everything
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.wsPeerSessions3[peerServerId];
obj.parent.webserver.recountSessions(); // Recount all sessions
}
};
// Process a message coming from a peer server
obj.ProcessPeerServerMessage = function (server, peerServerId, msg) {
var userid, i;
//console.log('ProcessPeerServerMessage', peerServerId, msg);
switch (msg.action) {
case 'bus': {
@ -449,10 +455,10 @@ module.exports.CreateMultiServer = function (parent, args) {
case 'sessionsTable': {
obj.parent.webserver.wsPeerSessions[peerServerId] = msg.sessionsTable;
var userToSession = {};
for (var i in msg.sessionsTable) {
for (i in msg.sessionsTable) {
var sessionid = msg.sessionsTable[i];
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 ]
}
obj.parent.webserver.wsPeerSessions3[peerServerId] = userToSession; // ServerId --> UserId --> SessionId
@ -462,17 +468,17 @@ module.exports.CreateMultiServer = function (parent, args) {
case 'sessionStart': {
obj.parent.webserver.wsPeerSessions[peerServerId].push(msg.sessionid);
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][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
break;
}
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); }
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) {
i = obj.parent.webserver.wsPeerSessions3[peerServerId][userid].indexOf(msg.sessionid);
if (i >= 0) {
@ -498,7 +504,7 @@ module.exports.CreateMultiServer = function (parent, args) {
// Yes, there is a waiting session, see if we must initiate.
if (peerServerId > obj.parent.serverId) {
// 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; }
obj.createPeerRelay(rsession.peer1.ws, rsession.peer1.req, peerServerId, userid);
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
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;
}
case 'msg': {
if (msg.sessionid != null) {
// Route this message to a connected user session
if (command.fromNodeid != null) { command.nodeid = command.fromNodeid; delete command.fromNodeid; }
var ws = obj.parent.webserver.wssessions2[command.sessionid];
if (ws != null) { ws.send(JSON.stringify(command)); }
if (msg.fromNodeid != null) { msg.nodeid = msg.fromNodeid; delete msg.fromNodeid; }
var ws = obj.parent.webserver.wssessions2[msg.sessionid];
if (ws != null) { ws.send(JSON.stringify(msg)); }
} else if (msg.nodeid != null) {
// 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];
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) {
// Route this message to all users of this mesh
if (command.fromNodeid != null) { command.nodeid = command.fromNodeid; delete command.fromNodeid; }
var cmdstr = JSON.stringify(command);
for (var userid in obj.parent.webserver.wssessions) { // Find all connected users for this mesh and send the message
if (msg.fromNodeid != null) { msg.nodeid = msg.fromNodeid; delete msg.fromNodeid; }
var cmdstr = JSON.stringify(msg);
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];
if (user) {
var rights = user.links[msg.meshid];
if (rights != null) { // TODO: Look at what rights are needed for message routing
var sessions = obj.parent.webserver.wssessions[userid];
// 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;
}
}
}
};
// Create a tunnel connection to a peer server
obj.createPeerRelay = function (ws, req, serverid, user) {
@ -558,7 +564,7 @@ module.exports.CreateMultiServer = function (parent, args) {
var path = req.path;
if (path[0] == '/') path = path.substring(1);
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]; }
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;
@ -566,7 +572,7 @@ module.exports.CreateMultiServer = function (parent, args) {
// Setup an connect the web socket
var tunnel = obj.createPeerRelayEx(ws, url, serverid);
tunnel.connect();
}
};
// Create a tunnel connection to a peer server
// 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.
peerTunnel.ws1.on('close', function (req) { peerTunnel.parent.parent.debug(1, 'FTunnel disconnect ' + peerTunnel.serverid); peerTunnel.close(); });
}
};
// Disconnect both sides of the tunnel
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.ws2 != null) { try { peerTunnel.ws2.close(); peerTunnel.parent.parent.debug(1, 'FTunnel2: Soft disconnect '); } catch (e) { console.log(e); } }
}
}
};
return peerTunnel;
}
};
setTimeout(function () { obj.ConnectToPeers(); }, 1000); // Delay this a little to make sure we are ready on our side.
return obj;
}
};

View File

@ -1,6 +1,11 @@
// 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.
const crypto = require('crypto');

View File

@ -6,7 +6,12 @@
* @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
module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
@ -18,7 +23,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
obj.legacyAgentConnections = {};
obj.migrationAgents = {};
const common = require('./common.js');
const net = require('net');
//const net = require('net');
const tls = require('tls');
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)
GUESTREMOTEDESKTOP: 2001, // Guest usage: Remote Desktop
GUESTWEBRTCMESH: 2002 // Guest usage: WebRTC Mesh
}
};
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(); } });
@ -146,11 +151,11 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
socket.setEncoding('binary');
socket.pingTimer = setInterval(function () { obj.SendCommand(socket, LegacyMeshProtocol.PING); }, 20000);
Debug(1, 'SWARM:New legacy agent connection');
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
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.
if (socket.tag.first == true) {
if (socket.tag.accumulator.length < 3) return;
@ -214,7 +219,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
Debug(3, 'Swarm:GETSTATE');
if (len < 12) break;
var statecmd = common.ReadInt(data, 0);
var statesync = common.ReadInt(data, 4);
//var statesync = common.ReadInt(data, 4);
switch (statecmd) {
case 6: { // Ask for agent block
if (socket.tag.update != null) {
@ -247,14 +252,14 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
}
return len;
}
socket.addListener("close", function () {
Debug(1, 'Swarm:Connection closed');
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
if (socket.pingTimer != null) { clearInterval(socket.pingTimer); delete socket.pingTimer; }
});
socket.addListener("error", function () {
//console.log("Swarm Error: " + socket.remoteAddress);
});
@ -311,19 +316,19 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
console.log(e);
}
return null;
}
};
// Disconnect legacy agent connection
obj.close = function (socket) {
try { socket.close(); } catch (e) { }
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
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 = ''; }
Write(socket, common.ShortToStr(cmdid) + common.ShortToStr(data.length + 4) + data);
}
};
function Write(socket, data) {
if (args.swarmdebug) {
@ -335,7 +340,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
socket.write(new Buffer(data, "binary"));
}
}
// Debug
function Debug(lvl) {
if (lvl > obj.parent.debugLevel) return;
@ -348,4 +353,4 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
}
return obj;
}
};

View File

@ -2251,9 +2251,8 @@
var boundingBox = null;
for (var i in nodes) {
try {
var loc = map_parseNodeLoc(nodes[i]);
var feature = xxmap.markersSource.getFeatureById(nodes[i]._id);
if ((typeof loc == 'object') && ((nodes[i].meshid == selectedMesh) || (selectedMesh == null))) { // Draw markers for devices with locations
var loc = map_parseNodeLoc(nodes[i]), feature = xxmap.markersSource.getFeatureById(nodes[i]._id);
if ((loc != null) && ((nodes[i].meshid == selectedMesh) || (selectedMesh == null))) { // Draw markers for devices with locations
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 (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.gpsloc) { loc = node.gpsloc; t = 3; }
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(',');
if (t == 1) {
// 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.
var loc = map_parseNodeLoc(node); lat = loc[0]; lon = loc[1];
if ((lat != feature.get('lat')) || (lon != feature.get('lon'))) { // Update lat and lon if changed
feature.set('lat', lat); feature.set('lon', lon);
var modifiedCoordinates = ol.proj.transform([parseFloat(lon), parseFloat(lat)], 'EPSG:4326','EPSG:3857');
feature.getGeometry().setCoordinates(modifiedCoordinates);
var loc = map_parseNodeLoc(node);
if (loc != null) {
var lat = loc[0], lon = loc[1];
if ((lat != feature.get('lat')) || (lon != feature.get('lon'))) { // Update lat and lon if changed
feature.set('lat', lat); feature.set('lon', lon);
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

View File

@ -6,7 +6,12 @@
* @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 {
@ -22,9 +27,9 @@ class SerialTunnel extends require('stream').Duplex {
function SerialTunnel(options) {
var obj = new require('stream').Duplex(options);
obj.forwardwrite = null;
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._read = function(size) { } // Push nothing, anything to read should be pushed from updateBuffer()
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._read = function (size) { }; // Push nothing, anything to read should be pushed from updateBuffer()
return obj;
}
@ -37,7 +42,7 @@ if (!String.prototype.endsWith) { String.prototype.endsWith = function (searchSt
// Construct a HTTP web server object
module.exports.CreateWebServer = function (parent, db, args, certificates) {
var obj = {};
var obj = {}, i = 0;
// Modules
obj.fs = require('fs');
@ -52,8 +57,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.common = require('./common.js');
obj.express = require('express');
obj.meshAgentHandler = require('./meshagent.js');
obj.meshRelayHandler = require('./meshrelay.js')
obj.meshUserHandler = require('./meshuser.js')
obj.meshRelayHandler = require('./meshrelay.js');
obj.meshUserHandler = require('./meshuser.js');
obj.interceptor = require('./interceptor');
// Variables
@ -63,13 +68,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.app = obj.express();
obj.app.use(require('compression')());
obj.tlsServer = null;
obj.tcpServer;
obj.tcpServer = null;
obj.certificates = certificates;
obj.args = args;
obj.users = {};
obj.meshes = {};
obj.userAllowedIp = args.userallowedip; // List of allowed IP addresses for users
obj.tlsSniCredentials;
obj.tlsSniCredentials = null;
obj.dnsDomains = {};
//obj.agentConnCount = 0;
@ -93,13 +98,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Setup SSPI authentication if needed
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
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 };
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.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, '$');
@ -130,13 +135,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
{
var dnscount = 0;
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; }
}
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 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) {
// Setup the HTTP server without TLS
@ -172,27 +177,28 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Session-persisted message middleware
obj.app.use(function (req, res, next) {
var err = null, msg = null, passhint = null;
if (req.session != null) {
var err = req.session.error;
var msg = req.session.success;
var passhint = req.session.passhint;
err = req.session.error;
msg = req.session.success;
passhint = req.session.passhint;
delete req.session.error;
delete req.session.success;
delete req.session.passhint;
}
res.locals.message = '';
if (err) res.locals.message = '<p class="msg error">' + err + '</p>';
if (msg) res.locals.message = '<p class="msg success">' + msg + '</p>';
if (passhint) res.locals.passhint = EscapeHtml(passhint);
if (err != null) res.locals.message = '<p class="msg error">' + err + '</p>';
if (msg != null) res.locals.message = '<p class="msg success">' + msg + '</p>';
if (passhint != null) res.locals.passhint = EscapeHtml(passhint);
next();
});
// Fetch all users from the database, keep this in memory
obj.db.GetAllType('user', function (err, docs) {
var domainUserCount = {};
for (var 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 (var i in parent.config.domains) {
var domainUserCount = {}, i = 0;
for (i in parent.config.domains) { domainUserCount[i] = 0; }
for (i in docs) { var u = obj.users[docs[i]._id] = docs[i]; domainUserCount[u.domain]++; }
for (i in parent.config.domains) {
if (domainUserCount[i] == 0) {
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.');
@ -237,7 +243,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
});
}
}
}
};
/*
obj.restrict = function (req, res, next) {
@ -249,7 +255,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
req.session.error = 'Access denied!';
res.redirect(domain.url + 'login');
}
}
};
*/
// 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.
var x = req.url.split('/');
if (x.length < 2) return parent.config.domains[''];
var d = parent.config.domains[x[1].toLowerCase()];
if ((d != null) && (d.dns == null)) return parent.config.domains[x[1].toLowerCase()];
var y = parent.config.domains[x[1].toLowerCase()];
if ((y != null) && (y.dns == null)) return parent.config.domains[x[1].toLowerCase()];
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' });
// Destroy the user's session to log them out will be re-created next request
if (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 })
var user = obj.users[req.session.userid];
obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'logout', msg: 'Account logout', domain: domain.id });
}
req.session = null;
res.redirect(domain.url);
@ -319,35 +325,35 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Regenerate session when signing in to prevent fixation
//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
// 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;
req.session.userid = userid;
req.session.domainid = domain.id;
req.session.currentNode = '';
if (req.session.passhint) { delete req.session.passhint; }
if (req.body.viewmode) { req.session.viewmode = req.body.viewmode; }
if (req.body.host) {
// TODO: This is a terrible search!!! FIX THIS.
/*
obj.db.GetAllType('node', function (err, docs) {
for (var i = 0; i < docs.length; i++) {
if (docs[i].name == req.body.host) {
req.session.currentNode = docs[i]._id;
break;
}
// 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>.';
delete req.session.loginmode;
req.session.userid = userid;
req.session.domainid = domain.id;
req.session.currentNode = '';
if (req.session.passhint) { delete req.session.passhint; }
if (req.body.viewmode) { req.session.viewmode = req.body.viewmode; }
if (req.body.host) {
// TODO: This is a terrible search!!! FIX THIS.
/*
obj.db.GetAllType('node', function (err, docs) {
for (var i = 0; i < docs.length; i++) {
if (docs[i].name == req.body.host) {
req.session.currentNode = docs[i]._id;
break;
}
console.log("CurrentNode: " + req.session.currentNode);
// This redirect happens after finding node is completed
res.redirect(domain.url);
});
*/
} else {
}
console.log("CurrentNode: " + req.session.currentNode);
// This redirect happens after finding node is completed
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 {
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>'; }
@ -367,14 +373,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
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 == '~') {
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);
} else {
// Check if this email was already verified
obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) {
if (docs.length > 0) {
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);
} else {
// 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);
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);
}
@ -459,7 +465,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} else {
obj.db.Get('user/' + cookie.u, function (err, docs) {
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 {
var user = docs[0];
if (user.email != cookie.e) {
@ -488,13 +494,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete userinfo.domain;
delete userinfo.subscriptions;
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
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
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) {
var newpass = buf.toString('base64').split('=').join('').split('/').join('');
require('./pass').hash(newpass, function (err, salt, hash) {
var userinfo = null;
if (err) throw err;
// Change the password
var userinfo = obj.users[user._id];
userinfo = obj.users[user._id];
userinfo.salt = salt;
userinfo.hash = hash;
userinfo.passchange = Date.now();
@ -518,7 +525,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.db.SetUser(userinfo);
// Event the change
var userinfo = obj.common.Clone(userinfo);
userinfo = obj.common.Clone(userinfo);
delete userinfo.hash;
delete userinfo.passhint;
delete userinfo.salt;
@ -526,7 +533,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete userinfo.domain;
delete userinfo.subscriptions;
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
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); }
// Notify mesh change
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];
req.session = null;
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 {
res.redirect(domain.url);
}
@ -609,7 +616,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.db.SetUser(user);
req.session.viewmode = 2;
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);
if (domain == null) return;
if (!obj.args) { res.sendStatus(500); return; }
var domain = getDomain(req);
if ((domain.sspi != null) && ((req.query.login == null) || (obj.parent.loginCookieEncryptionKey == null))) {
// 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 {
// Login using a different system
handleRootRequestEx(req, res, domain);
@ -630,7 +636,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}
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' });
// 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 = '';
// 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)) {
// 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++; } }
if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; } // If this is the first user, give the account site admin.
obj.users[req.session.userid] = user;
obj.db.SetUser(user);
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 })
if (usercount == 0) { user2.siteadmin = 0xFFFFFFFF; } // If this is the first user, give the account site admin.
obj.users[req.session.userid] = user2;
obj.db.SetUser(user2);
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) {
currentNode = 'node/' + domain.id + '/' + req.query.node;
}
var user;
var logoutcontrol;
if (obj.args.nousers != true) {
user = obj.users[req.session.userid]
logoutcontrol = 'Welcome ' + user.name + '.';
}
var logoutcontrol = '';
if (obj.args.nousers != true) { logoutcontrol = 'Welcome ' + obj.users[req.session.userid].name + '.'; }
// 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.lanonly == true) { features += 2; } // LAN-only mode
if (obj.args.nousers == true) { features += 4; } // Single user mode
@ -745,7 +747,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}
} else {
// 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.
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
@ -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; } }
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 (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 + '\"' });
@ -923,9 +926,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (splitpath[1] != '') { serverpath += '-' + splitpath[1]; } // Add the domain if needed
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
var fullpath = obj.path.resolve(obj.filespath, serverpath), quota = 0;
return { fullpath: fullpath, path: serverpath, name: filename, quota: obj.getQuota(objid, domain) };
}
return { fullpath: obj.path.resolve(obj.filespath, serverpath), path: serverpath, name: filename, quota: obj.getQuota(objid, domain) };
};
// Return the maximum number of bytes allowed in the user account "My Files".
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 0;
}
};
// Download a file from the server
function handleDownloadFile(req, res) {
@ -1014,7 +1016,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.fs.mkdir(xfile.fullpath, function () {
// Write the file
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);
@ -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);
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.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 {
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.AddEventDispatch(subscriptions, target);
return subscriptions;
}
};
// Handle a web socket relay request
function handleRelayWebSocket(ws, req) {
@ -1143,13 +1145,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// CIRA ---> TLS
Debug(3, 'Relay TLS CIRA data', data.length);
if (data.length > 0) { try { ser.updateBuffer(Buffer.from(data, 'binary')); } catch (e) { } }
}
};
// Handke CIRA tunnel state change
chnl.onStateChange = function (ciraconn, state) {
Debug(2, 'Relay TLS CIRA state change', state);
if (state == 0) { try { ws.close(); } catch (e) { } }
}
};
// TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
var TLSSocket = require('tls').TLSSocket;
@ -1199,18 +1201,18 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
ws.forwardclient.onStateChange = function (ciraconn, state) {
Debug(2, 'Relay CIRA state change', state);
if (state == 0) { try { ws.close(); } catch (e) { } }
}
};
ws.forwardclient.onData = function (ciraconn, data) {
Debug(4, 'Relay CIRA data', data.length);
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
}
};
ws.forwardclient.onSendOk = function (ciraconn) {
// TODO: Flow control? (Dont' really need it with AMT, but would be nice)
//console.log('onSendOk');
}
};
// Fetch Intel AMT credentials & Setup interceptor
if (req.query.p == 1) {
@ -1345,7 +1347,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var r = 0, dir;
try { dir = obj.fs.readdirSync(path); } catch (e) { return 0; }
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]); }
}
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); }
});
obj.fs.rmdirSync(path);
};
}
// Handle Intel AMT events
// To subscribe, add "http://server:port/amtevents.ashx" to Intel AMT subscriptions.
obj.handleAmtEventRequest = function (req, res) {
var domain = getDomain(req);
try {
if (req.headers['authorization']) {
var authstr = req.headers['authorization'];
if (req.headers.authorization) {
var authstr = req.headers.authorization;
if (authstr.substring(0, 7) == "Digest ") {
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'))) {
@ -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.sendStatus(401);
});
}
};
// Handle a server backup request
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 ((agentid == 3)) { // Signed Windows MeshCmd.exe x86
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; }
} else if ((agentid == 4)) { // Signed Windows MeshCmd64.exe x64
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; }
}
// 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
serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
debugLevel: 0
}
};
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
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
serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
debugLevel: 0
}
};
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
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>';
res.send(response);
}
}
};
// Get the web server hostname. This may change if using a domain with a DNS name.
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.send(meshsettings);
}
};
// Add HTTP security headers to all responses
obj.app.use(function (req, res, next) {
@ -1785,7 +1787,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check we have agent rights
var rights = user.links[agent.dbMeshKey].rights;
if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) && (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); }
}
};
// Send the core module to the mesh agent
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);
}
}
}
};
// Get the server path of a user or mesh object
function getServerRootFilePath(obj) {
@ -1878,36 +1880,37 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Count sessions and event any changes
obj.recountSessions = function (changedSessionId) {
var userid, oldcount, newcount, x, serverid;
if (changedSessionId == null) {
// Recount all sessions
// Calculate the session count for all userid's
var newSessionsCount = {};
for (var userid in obj.wssessions) { newSessionsCount[userid] = obj.wssessions[userid].length; }
for (var serverid in obj.wsPeerSessions3) {
for (var userid in obj.wsPeerSessions3[serverid]) {
var c = obj.wsPeerSessions3[serverid][userid].length;
if (newSessionsCount[userid] == null) { newSessionsCount[userid] = c; } else { newSessionsCount[userid] += c; }
for (userid in obj.wssessions) { newSessionsCount[userid] = obj.wssessions[userid].length; }
for (serverid in obj.wsPeerSessions3) {
for (userid in obj.wsPeerSessions3[serverid]) {
x = obj.wsPeerSessions3[serverid][userid].length;
if (newSessionsCount[userid] == null) { newSessionsCount[userid] = x; } else { newSessionsCount[userid] += x; }
}
}
// See what session counts have changed, event any changes
for (var userid in newSessionsCount) {
var newcount = newSessionsCount[userid];
var oldcount = obj.sessionsCount[userid];
for (userid in newSessionsCount) {
newcount = newSessionsCount[userid];
oldcount = obj.sessionsCount[userid];
if (oldcount == null) { oldcount = 0; } else { delete obj.sessionsCount[userid]; }
if (newcount != oldcount) {
var x = userid.split('/');
obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 })
x = userid.split('/');
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
for (var userid in obj.sessionsCount) {
var oldcount = obj.sessionsCount[userid];
for (userid in obj.sessionsCount) {
oldcount = obj.sessionsCount[userid];
if ((oldcount != null) && (oldcount != 0)) {
var x = userid.split('/');
obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: 0, domain: x[1], nolog: 1, nopeers: 1 })
x = userid.split('/');
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;
} else {
// Figure out the userid
var userid = changedSessionId.split('/').slice(0, 3).join('/');
userid = changedSessionId.split('/').slice(0, 3).join('/');
// Recount only changedSessionId
var newcount = 0;
newcount = 0;
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; } }
var oldcount = obj.sessionsCount[userid];
for (serverid in obj.wsPeerSessions3) { if (obj.wsPeerSessions3[serverid][userid] != null) { newcount += obj.wsPeerSessions3[serverid][userid].length; } }
oldcount = obj.sessionsCount[userid];
if (oldcount == null) { oldcount = 0; }
// If the count changed, update and event
if (newcount != oldcount) {
var x = userid.split('/');
obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 })
x = userid.split('/');
obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 });
obj.sessionsCount[userid] = newcount;
}
}
}
};
// 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/
@ -1943,5 +1946,4 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}
return obj;
}
};

View File

@ -6,7 +6,12 @@
* @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.
// In this case, we don't want to start a child process, so we launch directly without arguments.