Added SMTP email support, email verification and password reset support

This commit is contained in:
Ylian Saint-Hilaire 2017-12-12 16:04:54 -08:00
parent e740045b39
commit 72ee422623
24 changed files with 2586 additions and 166 deletions

View File

@ -21,10 +21,14 @@
<PropertyGroup Condition="'$(Configuration)' == 'Release'" />
<ItemGroup>
<Compile Include="agents\meshcore.js" />
<Compile Include="agents\modules_meshcmd\amt_heci.js" />
<Compile Include="agents\modules_meshcore\amt_heci.js" />
<Compile Include="agents\modules_meshcore\WifiScanner.js" />
<Compile Include="agents\tinycore.js" />
<Compile Include="amtevents.js" />
<Compile Include="amtscanner.js" />
<Compile Include="amtscript.js" />
<Compile Include="meshmail.js" />
<Compile Include="meshscanner.js" />
<Compile Include="certoperations.js" />
<Compile Include="common.js" />
@ -158,10 +162,13 @@
<Content Include="views\default.handlebars" />
<Content Include="views\download.handlebars" />
<Content Include="views\login.handlebars" />
<Content Include="views\message.handlebars" />
<Content Include="views\terms.handlebars" />
</ItemGroup>
<ItemGroup>
<Folder Include="agents\" />
<Folder Include="agents\modules_meshcmd\" />
<Folder Include="agents\modules_meshcore\" />
<Folder Include="public" />
<Folder Include="public\clickonce\" />
<Folder Include="public\clickonce\minirouter\" />

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -31,15 +31,35 @@ function createMeshCore(agent) {
var net = require('net');
var fs = require('fs');
var rtc = require('ILibWebRTC');
var amtMei = null, amtLms = null, amtLmsState = 0;
var amtMeiConnected = 0, amtMeiState = null, amtMeiTmpState = null;
var wifiScannerLib = null;
var wifiScanner = null;
var networkMonitor = null;
// Try to load up the network monitor
try {
networkMonitor = require('NetworkMonitor');
networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
} catch (e) { networkMonitor = null; }
// Try to load up the MEI module
try {
var amtMeiLib = require('amt_heci');
amtMeiConnected = 1;
amtMei = new amtMeiLib();
amtMei.on('error', function (e) { amtMeiLib = null; amtMei = null; sendPeriodicServerUpdate(); });
amtMei.on('connect', function () { amtMeiConnected = 2; getAmtInfo(); });
} catch (e) { amtMeiLib = null; amtMei = null; amtMeiConnected = -1; }
// Try to load up the WIFI scanner
try {
wifiScannerLib = require('WifiScanner');
var wifiScannerLib = require('WifiScanner');
wifiScanner = new wifiScannerLib();
wifiScanner.on('accessPoint', function (data) { sendConsoleText(JSON.stringify(data)); });
} catch (e) { }
} catch (e) { wifiScannerLib = null; wifiScanner = null; }
// If we are running in Duktape, agent will be null
if (agent == null) {
@ -656,7 +676,7 @@ function createMeshCore(agent) {
var response = null;
switch (cmd) {
case 'help': { // Displays available commands
response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, parseuri, httpget, wslist, wsconnect, wssend, wsclose, notify, ls, amt, netinfo, location, power, wakeonlan, modules, scanwifi.';
response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, parseuri, httpget, wslist, wsconnect, wssend, wsclose, notify, ls, amt, netinfo, location, power, wakeonlan, scanwifi.';
break;
}
case 'notify': { // Send a notification message to the mesh
@ -671,9 +691,11 @@ function createMeshCore(agent) {
break;
}
case 'info': { // Return information about the agent and agent core module
response = 'Current Core: ' + obj.meshCoreInfo + '.\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform Info: ' + process.platform + '.\r\nCapabilities: ' + obj.meshCoreCapabilities + '.\r\nNative Pipes: ' + obj.useNativePipes + '.\r\nServer URL: ' + mesh.ServerUrl + '.\r\n';
response = 'Current Core: ' + obj.meshCoreInfo + '.\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform Info: ' + process.platform + '.\r\nCapabilities: ' + obj.meshCoreCapabilities + '.\r\nNative Pipes: ' + obj.useNativePipes + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
if (amtLmsState >= 0) { response += '\r\nBuilt -in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amtLmsState] + '.'; }
response += '\r\nModules: ' + JSON.stringify(addedModules) + '';
var oldNodeId = db.Get('OldNodeId');
if (oldNodeId != null) { response += 'OldNodeID: ' + oldNodeId + '.\r\n'; }
if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; }
break;
}
case 'selfinfo': { // Return self information block
@ -849,12 +871,8 @@ function createMeshCore(agent) {
break;
}
case 'amt': { // Show Intel AMT status
//response = 'KVM: ' + mesh.hasKVM + ', HECI: ' + mesh.hasHECI + ', MicroLMS: ' + mesh.activeMicroLMS + '\r\n';
//response += JSON.stringify(mesh.MEInfo);
if (mesh.hasHECI == 1) {
var meinfo = mesh.MEInfo;
delete meinfo.TrustedHashes;
response = objToString(meinfo, 0, '.');
if (amtMeiState != null) {
response = objToString(amtMeiState, 0, '.');
} else {
response = 'This mesh agent does not support Intel AMT.';
}
@ -902,10 +920,6 @@ function createMeshCore(agent) {
response = JSON.stringify(http.parseUri(args['_'][0]));
break;
}
case 'modules': {
response = "Modules: " + JSON.stringify(addedModules);
break;
}
case 'scanwifi': {
if (wifiScanner != null) {
var wifiPresent = wifiScanner.hasWireless;
@ -943,7 +957,7 @@ function createMeshCore(agent) {
var oldNodeId = db.Get('OldNodeId');
if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); }
sendPeriodicServerUpdate(true);
if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 60000); } // Should be a long time, like 20 minutes. For now, 1 minute.
//if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 60000); } // Should be a long time, like 20 minutes. For now, 1 minute.
}
}
@ -952,23 +966,24 @@ function createMeshCore(agent) {
function buildSelfInfo() {
var r = { "action": "coreinfo", "value": obj.meshCoreInfo, "caps": obj.meshCoreCapabilities };
if (mesh.hasHECI == 1) {
var meinfo = mesh.MEInfo;
var meinfo = amtMeiState;
var amtPresent = false, intelamt = {};
if (meinfo.Versions && meinfo.Versions.AMT) { intelamt.ver = meinfo.Versions.AMT; amtPresent = true; }
if (meinfo.ProvisioningState) { intelamt.state = meinfo.ProvisioningState; amtPresent = true; }
if (meinfo.flags) { intelamt.flags = meinfo.Flags; amtPresent = true; }
if (meinfo.OsHostname) { intelamt.host = meinfo.OsHostname; amtPresent = true; }
if (amtPresent == true) { r.intelamt = intelamt }
if (meinfo != null) {
if (meinfo.Versions && meinfo.Versions.AMT) { intelamt.ver = meinfo.Versions.AMT; amtPresent = true; }
if (meinfo.ProvisioningState) { intelamt.state = meinfo.ProvisioningState; amtPresent = true; }
if (meinfo.flags) { intelamt.flags = meinfo.Flags; amtPresent = true; }
if (meinfo.OsHostname) { intelamt.host = meinfo.OsHostname; amtPresent = true; }
if (amtPresent == true) { r.intelamt = intelamt }
}
}
return JSON.stringify(r);
}
// Called periodically to check if we need to send updates to the server
function sendPeriodicServerUpdate(force) {
// Update the self information data
var selfInfo = buildSelfInfo();
var selfInfoStr = JSON.stringify(selfInfo);
if ((force == true) || (selfInfoStr != lastSelfInfo)) { mesh.SendCommand(selfInfo); lastSelfInfo = selfInfoStr; }
// Update the server with the latest network interface information
var sendNetworkUpdateNagleTimer = null;
function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
function sendNetworkUpdate(force) {
sendNetworkUpdateNagleTimer = null;
// Update the network interfaces information data
var netInfo = mesh.NetInfo;
@ -977,6 +992,32 @@ function createMeshCore(agent) {
if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
}
// Called periodically to check if we need to send updates to the server
function sendPeriodicServerUpdate(force) {
if (amtMeiConnected != 1) { // If we are pending MEI connection, hold off on updating the server on self-info
// Update the self information data
var selfInfo = buildSelfInfo(), selfInfoStr = JSON.stringify(selfInfo);
if ((force == true) || (selfInfoStr != lastSelfInfo)) { mesh.SendCommand(selfInfo); lastSelfInfo = selfInfoStr; }
}
// Update network information
sendNetworkUpdateNagle(force);
}
// Get Intel AMT information using MEI
function getAmtInfo(func) {
if (amtMei == null || amtMeiConnected != 2) { if (func != null) { func(null); } return; }
amtMeiTmpState = { Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM
amtMei.getProtocolVersion(function (result) { if (result != null) { amtMeiTmpState.MeiVersion = result; } });
amtMei.getVersion(function (val) { amtMeiTmpState.Versions = {}; for (var version in val.Versions) { amtMeiTmpState.Versions[val.Versions[version].Description] = val.Versions[version].Version; } });
amtMei.getProvisioningMode(function (result) { amtMeiTmpState.ProvisioningMode = result.mode; });
amtMei.getProvisioningState(function (result) { amtMeiTmpState.ProvisioningState = result.state; });
amtMei.getEHBCState(function (result) { if (result.EHBC == true) { amtMeiTmpState.Flags += 1; } });
amtMei.getControlMode(function (result) { if (result.controlMode == 1) { amtMeiTmpState.Flags += 2; } if (result.controlMode == 2) { amtMeiTmpState.Flags += 4; } });
//amtMei.getMACAddresses(function (result) { amtMeiTmpState.mac = result; });
amtMei.getDnsSuffix(function (result) { if (result != null) { amtMeiTmpState.dns = result; } amtMeiState = amtMeiTmpState; sendPeriodicServerUpdate(); if (func != null) { func(amtMeiState); } });
}
// Called on MicroLMS Intel AMT user notification
function handleAmtNotification(notification) {
var amtMessage = notification.messageId;
@ -1021,7 +1062,7 @@ function createMeshCore(agent) {
// Setup the mesh agent event handlers
mesh.AddCommandHandler(handleServerCommand);
mesh.AddConnectHandler(handleServerConnection);
mesh.lmsNotification = handleAmtNotification;
//mesh.lmsNotification = handleAmtNotification; // TODO
sendPeriodicServerUpdate(); // TODO: Check if connected before sending
// Parse input arguments
@ -1030,6 +1071,15 @@ function createMeshCore(agent) {
//console.log('Stopping.');
//process.exit();
// Launch LMS
try {
var lme_heci = require('lme_heci');
amtLmsState = 1;
amtLms = new lme_heci();
amtLms.on('error', function (e) { amtLmsState = 0; amtLms = null; });
amtLms.on('connect', function () { amtLmsState = 2; });
} catch (e) { amtLmsState = -1; amtLms = null; }
}
obj.stop = function () {

View File

@ -0,0 +1,281 @@
var Q = require('queue');
function amt_heci() {
var emitterUtils = require('events').inherits(this);
emitterUtils.createEvent('error');
emitterUtils.createEvent('connect');
var heci = require('heci');
this._amt = heci.create();
this._amt.BiosVersionLen = 65;
this._amt.UnicodeStringLen = 20;
this._amt.rq = new Q();
this._amt.Parent = this;
this._amt.on('error', function (e) { this.Parent.emit('error', e); });
this._amt.on('connect', function () {
this.Parent.emit('connect');
this.on('data', function (chunk) {
//console.log("Received: " + chunk.length + " bytes");
var header = this.Parent.getCommand(chunk);
//console.log("CMD = " + header.Command + " (Status: " + header.Status + ") Response = " + header.IsResponse);
var user = this.rq.deQueue();
var params = user.optional;
var callback = user.func;
params.unshift(header);
callback.apply(this.Parent, params);
});
});
this._amt.connect(heci.GUIDS.AMT, { noPipeline: 1 });
this.getCommand = function (chunk) {
var command = chunk.length == 0 ? (this._amt.rq.peekQueue().cmd | 0x800000) : chunk.readUInt32LE(4);
var ret = { IsResponse: (command & 0x800000) == 0x800000 ? true : false, Command: (command & 0x7FFFFF), Status: chunk.length != 0 ? chunk.readUInt32LE(12) : -1, Data: chunk.length != 0 ? chunk.slice(16) : null };
return (ret);
};
this.sendCommand = function () {
if (arguments.length < 3 || typeof (arguments[0]) != 'number' || typeof (arguments[1]) != 'object' || typeof (arguments[2]) != 'function') { throw ('invalid parameters'); }
var args = [];
for (var i = 3; i < arguments.length; ++i) { args.push(arguments[i]); }
this._amt.rq.enQueue({ cmd: arguments[0], func: arguments[2], optional: args });
var header = Buffer.from('010100000000000000000000', 'hex');
header.writeUInt32LE(arguments[0] | 0x04000000, 4);
header.writeUInt32LE(arguments[1] == null ? 0 : arguments[1].length, 8);
this._amt.write(arguments[1] == null ? header : Buffer.concat([header, arguments[1]]));
}
this.getVersion = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(26, null, function (header, fn, opt) {
if (header.Status == 0) {
var i, CodeVersion = header.Data, val = { BiosVersion: CodeVersion.slice(0, this._amt.BiosVersionLen), Versions: [] }, v = CodeVersion.slice(this._amt.BiosVersionLen + 4);
for (i = 0; i < CodeVersion.readUInt32LE(this._amt.BiosVersionLen) ; ++i) {
val.Versions[i] = { Description: v.slice(2, v.readUInt16LE(0) + 2).toString(), Version: v.slice(4 + this._amt.UnicodeStringLen, 4 + this._amt.UnicodeStringLen + v.readUInt16LE(2 + this._amt.UnicodeStringLen)).toString() };
v = v.slice(4 + (2 * this._amt.UnicodeStringLen));
}
opt.unshift(val);
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getProvisioningState = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(17, null, function (header, fn, opt) {
if (header.Status == 0) {
var result = {};
result.state = header.Data.readUInt32LE(0);
if (result.state < 3) { result.stateStr = ["PRE", "IN", "POST"][result.state]; }
opt.unshift(result);
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getProvisioningMode = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(8, null, function (header, fn, opt) {
if (header.Status == 0) {
var result = {};
result.mode = header.Data.readUInt32LE(0);
if (result.mode < 4) { result.modeStr = ["NONE", "ENTERPRISE", "SMALL_BUSINESS", "REMOTE_ASSISTANCE"][result.mode]; }
result.legacy = header.Data.readUInt32LE(4) == 0 ? false : true;
opt.unshift(result);
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getEHBCState = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(132, null, function (header, fn, opt) {
if (header.Status == 0) {
opt.unshift({ EHBC: header.Data.readUInt32LE(0) != 0 });
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getControlMode = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(107, null, function (header, fn, opt) {
if (header.Status == 0) {
var result = {};
result.controlMode = header.Data.readUInt32LE(0);
if (result.controlMode < 3) { result.controlModeStr = ["NONE_RPAT", "CLIENT", "ADMIN", "REMOTE_ASSISTANCE"][result.controlMode]; }
opt.unshift(result);
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getMACAddresses = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(37, null, function (header, fn, opt) {
if (header.Status == 0) {
opt.unshift({ DedicatedMAC: header.Data.slice(0, 6).toString('hex:'), HostMAC: header.Data.slice(6, 12).toString('hex:') });
} else { opt.unshift({ DedicatedMAC: null, HostMAC: null }); }
fn.apply(this, opt);
}, callback, optional);
};
this.getDnsSuffix = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(54, null, function (header, fn, opt) {
if (header.Status == 0) {
var resultLen = header.Data.readUInt16LE(0);
if (resultLen > 0) { opt.unshift(header.Data.slice(2, 2 + resultLen).toString()); } else { opt.unshift(null); }
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getHashHandles = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x2C, null, function (header, fn, opt) {
var result = [];
if (header.Status == 0) {
var resultLen = header.Data.readUInt32LE(0);
for (var i = 0; i < resultLen; ++i) {
result.push(header.Data.readUInt32LE(4 + (4 * i)));
}
}
opt.unshift(result);
fn.apply(this, opt);
}, callback, optional);
};
this.getCertHashEntry = function (handle, callback) {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
var data = new Buffer(4);
data.writeUInt32LE(handle, 0);
this.sendCommand(0x2D, data, function (header, fn, opt) {
if (header.Status == 0) {
var result = {};
result.isDefault = header.Data.readUInt32LE(0);
result.isActive = header.Data.readUInt32LE(4);
result.hashAlgorithm = header.Data.readUInt8(72);
if (result.hashAlgorithm < 4) {
result.hashAlgorithmStr = ["MD5", "SHA1", "SHA256", "SHA512"][result.hashAlgorithm];
result.hashAlgorithmSize = [16, 20, 32, 64][result.hashAlgorithm];
result.certificateHash = header.Data.slice(8, 8 + result.hashAlgorithmSize).toString('hex');
}
result.name = header.Data.slice(73 + 2, 73 + 2 + header.Data.readUInt16LE(73)).toString();
opt.unshift(result);
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getCertHashEntries = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.getHashHandles(function (handles, fn, opt) {
var entries = [];
this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles);
}, callback, optional);
};
this._getHashEntrySink = function (result, fn, opt, entries, handles) {
entries.push(result);
if (handles.length > 0) {
this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles);
} else {
opt.unshift(entries);
fn.apply(this, opt);
}
}
this.getLocalSystemAccount = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(103, Buffer.alloc(40), function (header, fn, opt) {
if (header.Data.length == 68) { opt.unshift({ user: header.Data.slice(0, 34).toString(), pass: header.Data.slice(34, 67).toString(), raw: header.Data }); } else { opt.unshift(null); }
fn.apply(this, opt);
}, callback, optional);
}
this.unprovision = function (mode, callback) {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
var data = new Buffer(4);
data.writeUInt32LE(mode, 0);
this.sendCommand(16, data, function (header, fn, opt) {
opt.unshift(header.Status);
fn.apply(this, opt);
}, callback, optional);
}
this.startConfiguration = function () {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x29, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
}
this.stopConfiguration = function () {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x5E, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
}
this.openUserInitiatedConnection = function () {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x44, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
}
this.closeUserInitiatedConnection = function () {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x45, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
}
this.getRemoteAccessConnectionStatus = function () {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x46, data, function (header, fn, opt) {
if (header.Status == 0) {
var hostname = v.slice(14, header.Data.readUInt16LE(12) + 14).toString()
opt.unshift({ status: header.Status, networkStatus: header.Data.readUInt32LE(0), remoteAccessStatus: header.Data.readUInt32LE(4), remoteAccessTrigger: header.Data.readUInt32LE(8), mpsHostname: hostname, raw: header.Data });
} else {
opt.unshift({ status: header.Status });
}
fn.apply(this, opt);
}, callback, optional);
}
this.getProtocolVersion = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { opt.push(arguments[i]); }
heci.doIoctl(heci.IOCTL.HECI_VERSION, Buffer.alloc(5), Buffer.alloc(5), function (status, buffer, self, fn, opt) {
if (status == 0) {
var result = buffer.readUInt8(0).toString() + '.' + buffer.readUInt8(1).toString() + '.' + buffer.readUInt8(2).toString() + '.' + buffer.readUInt16BE(3).toString();
opt.unshift(result);
fn.apply(self, opt);
}
else {
opt.unshift(null);
fn.apply(self, opt);
}
}, this, callback, optional);
}
}
module.exports = amt_heci;

View File

@ -0,0 +1,354 @@
var MemoryStream = require('MemoryStream');
var lme_id = 0;
var APF_DISCONNECT = 1;
var APF_SERVICE_REQUEST = 5;
var APF_SERVICE_ACCEPT = 6;
var APF_USERAUTH_REQUEST = 50;
var APF_USERAUTH_FAILURE = 51;
var APF_USERAUTH_SUCCESS = 52;
var APF_GLOBAL_REQUEST = 80;
var APF_REQUEST_SUCCESS = 81;
var APF_REQUEST_FAILURE = 82;
var APF_CHANNEL_OPEN = 90;
var APF_CHANNEL_OPEN_CONFIRMATION = 91;
var APF_CHANNEL_OPEN_FAILURE = 92;
var APF_CHANNEL_WINDOW_ADJUST = 93;
var APF_CHANNEL_DATA = 94;
var APF_CHANNEL_CLOSE = 97;
var APF_PROTOCOLVERSION = 192;
function lme_object()
{
this.ourId = ++lme_id;
this.amtId = -1;
this.LME_CHANNEL_STATUS = 'LME_CS_FREE';
this.txWindow = 0;
this.rxWindow = 0;
this.localPort = 0;
this.errorCount = 0;
}
function stream_bufferedWrite()
{
var emitterUtils = require('events').inherits(this);
this.buffer = [];
this._readCheckImmediate = undefined;
// Writable Events
emitterUtils.createEvent('close');
emitterUtils.createEvent('drain');
emitterUtils.createEvent('error');
emitterUtils.createEvent('finish');
emitterUtils.createEvent('pipe');
emitterUtils.createEvent('unpipe');
// Readable Events
emitterUtils.createEvent('readable');
this.isEmpty = function ()
{
return (this.buffer.length == 0);
};
this.isWaiting = function ()
{
return (this._readCheckImmediate == undefined);
};
this.write = function (chunk)
{
for (var args in arguments)
{
if (typeof (arguments[args]) == 'function') { this.once('drain', arguments[args]); break; }
}
var tmp = Buffer.alloc(chunk.length);
chunk.copy(tmp);
this.buffer.push({ offset: 0, data: tmp });
this.emit('readable');
return (this.buffer.length == 0 ? true : false);
};
this.read = function ()
{
var size = arguments.length == 0 ? undefined : arguments[0];
var bytesRead = 0;
var list = [];
while((size == undefined || bytesRead < size) && this.buffer.length > 0)
{
var len = this.buffer[0].data.length - this.buffer[0].offset;
var offset = this.buffer[0].offset;
if(len > (size - bytesRead))
{
// Only reading a subset
list.push(this.buffer[0].data.slice(offset, offset + size - bytesRead));
this.buffer[0].offset += (size - bytesRead);
bytesRead += (size - bytesRead);
}
else
{
// Reading the entire thing
list.push(this.buffer[0].data.slice(offset));
bytesRead += len;
this.buffer.shift();
}
}
this._readCheckImmediate = setImmediate(function (buffered)
{
buffered._readCheckImmediate = undefined;
if(buffered.buffer.length == 0)
{
// drained
buffered.emit('drain');
}
else
{
// not drained
buffered.emit('readable');
}
}, this);
return (Buffer.concat(list));
};
}
function lme_heci()
{
var emitterUtils = require('events').inherits(this);
emitterUtils.createEvent('error');
emitterUtils.createEvent('connect');
var heci = require('heci');
this.INITIAL_RXWINDOW_SIZE = 4096;
this._LME = heci.create();
this._LME.LMS = this;
this._LME.on('error', function (e) { this.Parent.emit('error', e); });
this._LME.on('connect', function ()
{
this.LMS.emit('connect');
this.on('data', function (chunk)
{
// this = HECI
var cmd = chunk.readUInt8(0);
switch(cmd)
{
default:
//console.log('Received ' + chunk.length + ' bytes of data for LMS');
//console.log('Command = ' + cmd);
break;
case APF_SERVICE_REQUEST:
var nameLen = chunk.readUInt32BE(1);
var name = chunk.slice(5, nameLen + 5);
//console.log("Service Request for: " + name);
if (name == 'pfwd@amt.intel.com' || name == 'auth@amt.intel.com')
{
var outBuffer = Buffer.alloc(5 + nameLen);
outBuffer.writeUInt8(6, 0);
outBuffer.writeUInt32BE(nameLen, 1);
outBuffer.write(name.toString(), 5);
this.write(outBuffer);
//console.log('Answering APF_SERVICE_REQUEST');
}
else
{
//console.log('UNKNOWN APF_SERVICE_REQUEST');
}
break;
case APF_GLOBAL_REQUEST:
var nameLen = chunk.readUInt32BE(1);
var name = chunk.slice(5, nameLen + 5).toString();
switch(name)
{
case 'tcpip-forward':
var len = chunk.readUInt32BE(nameLen + 6);
var port = chunk.readUInt32BE(nameLen + 10 + len);
//console.log("[" + chunk.length + "/" + len + "] APF_GLOBAL_REQUEST for: " + name + " on port " + port);
if (this[name] == undefined)
{
this[name] = {};
}
this[name][port] = require('net').createServer();
this[name][port].HECI = this;
this[name][port].listen({ port: port });
this[name][port].on('connection', function (socket)
{
//console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort);
this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort);
});
var outBuffer = Buffer.alloc(5);
outBuffer.writeUInt8(81, 0);
outBuffer.writeUInt32BE(port, 1);
this.write(outBuffer);
break;
case 'cancel-tcpip-forward':
break;
case 'udp-send-to@amt.intel.com':
break;
default:
//console.log("Unknown APF_GLOBAL_REQUEST for: " + name);
break;
}
break;
case APF_CHANNEL_OPEN_CONFIRMATION:
var rChannel = chunk.readUInt32BE(1);
var sChannel = chunk.readUInt32BE(5);
var wSize = chunk.readUInt32BE(9);
//console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize);
if (this.sockets[rChannel] != undefined)
{
this.sockets[rChannel].lme.amtId = sChannel;
this.sockets[rChannel].lme.rxWindow = wSize;
this.sockets[rChannel].lme.txWindow = wSize;
this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED';
//console.log('LME_CS_CONNECTED');
this.sockets[rChannel].bufferedStream = new stream_bufferedWrite();
this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel];
this.sockets[rChannel].bufferedStream.on('readable', function ()
{
if(this.socket.lme.txWindow > 0)
{
var buffer = this.read(this.socket.lme.txWindow);
var packet = Buffer.alloc(9 + buffer.length);
packet.writeUInt8(APF_CHANNEL_DATA, 0);
packet.writeUInt32BE(this.socket.lme.amtId, 1);
packet.writeUInt32BE(buffer.length, 5);
buffer.copy(packet, 9);
this.socket.lme.txWindow -= buffer.length;
this.socket.HECI.write(packet);
}
});
this.sockets[rChannel].bufferedStream.on('drain', function ()
{
this.socket.resume();
});
this.sockets[rChannel].on('data', function (chunk)
{
if (!this.bufferedStream.write(chunk)) { this.pause(); }
});
this.sockets[rChannel].on('end', function ()
{
var outBuffer = Buffer.alloc(5);
outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
outBuffer.writeUInt32BE(this.lme.amtId, 1);
this.HECI.write(outBuffer);
});
this.sockets[rChannel].resume();
}
break;
case APF_PROTOCOLVERSION:
var major = chunk.readUInt32BE(1);
var minor = chunk.readUInt32BE(5);
var reason = chunk.readUInt32BE(9);
var outBuffer = Buffer.alloc(93);
outBuffer.writeUInt8(192, 0);
outBuffer.writeUInt32BE(1, 1);
outBuffer.writeUInt32BE(0, 5);
outBuffer.writeUInt32BE(reason, 9);
//console.log('Answering PROTOCOL_VERSION');
this.write(outBuffer);
break;
case APF_CHANNEL_WINDOW_ADJUST:
var rChannelId = chunk.readUInt32BE(1);
var bytesToAdd = chunk.readUInt32BE(5);
if (this.sockets[rChannelId] != undefined)
{
this.sockets[rChannelId].lme.txWindow += bytesToAdd;
if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting())
{
this.sockets[rChannelId].bufferedStream.emit('readable');
}
}
else
{
//console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_WINDOW_ADJUST');
}
break;
case APF_CHANNEL_DATA:
var rChannelId = chunk.readUInt32BE(1);
var dataLen = chunk.readUInt32BE(5);
var data = chunk.slice(9, 9 + dataLen);
if (this.sockets[rChannelId] != undefined)
{
this.sockets[rChannelId].pendingBytes.push(data.length);
this.sockets[rChannelId].write(data, function ()
{
var written = this.pendingBytes.shift();
var outBuffer = Buffer.alloc(9);
outBuffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0);
outBuffer.writeUInt32BE(this.lme.amtId, 1);
outBuffer.writeUInt32BE(written, 5);
this.HECI.write(outBuffer);
});
}
else
{
//console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_DATA');
}
break;
case APF_CHANNEL_CLOSE:
var rChannelId = chunk.readUInt32BE(1);
if (this.sockets[rChannelId] != undefined)
{
this.sockets[rChannelId].end();
var amtId = this.sockets[rChannelId].lme.amtId;
var buffer = Buffer.alloc(5);
delete this.sockets[rChannelId];
buffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
buffer.writeUInt32BE(amtId, 1);
this.write(buffer);
}
else
{
//console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_CLOSE');
}
break;
}
});
});
this.bindDuplexStream = function (duplexStream, remoteFamily, localPort)
{
var socket = duplexStream;
//console.log('New [' + remoteFamily + '] Virtual Connection/' + socket.localPort);
socket.pendingBytes = [];
socket.HECI = this._LME;
socket.LMS = this;
socket.lme = new lme_object();
socket.lme.Socket = socket;
var buffer = new MemoryStream();
buffer.writeUInt8(0x5A);
buffer.writeUInt32BE(15);
buffer.write('forwarded-tcpip');
buffer.writeUInt32BE(socket.lme.ourId);
buffer.writeUInt32BE(this.INITIAL_RXWINDOW_SIZE);
buffer.writeUInt32BE(0xFFFFFFFF);
for (var i = 0; i < 2; ++i)
{
if (remoteFamily == 'IPv6')
{
buffer.writeUInt32BE(3);
buffer.write('::1');
}
else
{
buffer.writeUInt32BE(9);
buffer.write('127.0.0.1');
}
buffer.writeUInt32BE(localPort);
}
this._LME.write(buffer.buffer);
if (this._LME.sockets == undefined) { this._LME.sockets = {}; }
this._LME.sockets[socket.lme.ourId] = socket;
socket.pause();
};
this._LME.connect(heci.GUIDS.LME, { noPipeline: 0 });
}
module.exports = lme_heci;

View File

@ -0,0 +1,266 @@
var MemoryStream = require('MemoryStream');
var WindowsWireless = new Buffer([
0x0A, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x5F, 0x53, 0x63, 0x61, 0x6E, 0x28, 0x29, 0x0A, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x77, 0x6C, 0x61, 0x6E,
0x49, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x20, 0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6C, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x50, 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2E, 0x57, 0x6C, 0x61, 0x6E, 0x45,
0x6E, 0x75, 0x6D, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x2C, 0x20, 0x30, 0x2C, 0x20, 0x77, 0x6C,
0x61, 0x6E, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x20, 0x3D, 0x20,
0x77, 0x6C, 0x61, 0x6E, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x29, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x30, 0x2C, 0x20,
0x34, 0x29, 0x2E, 0x56, 0x61, 0x6C, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6E, 0x66, 0x6F, 0x20, 0x3D, 0x20, 0x77, 0x6C, 0x61, 0x6E, 0x49, 0x6E, 0x74, 0x65,
0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x29, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x38, 0x2C, 0x20, 0x35, 0x33, 0x32, 0x29, 0x3B, 0x0A, 0x20, 0x20,
0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x69, 0x6E, 0x66, 0x6F, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x31, 0x36, 0x2C, 0x20, 0x35, 0x31, 0x32,
0x29, 0x2E, 0x41, 0x6E, 0x73, 0x69, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3B, 0x0A, 0x20,
0x20, 0x20, 0x20, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, 0x28, 0x69, 0x6E, 0x66, 0x6F, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x35, 0x32, 0x38, 0x2C, 0x20, 0x34, 0x29, 0x2E, 0x56, 0x61,
0x6C, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x30, 0x3A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3D, 0x20, 0x22, 0x4E, 0x4F, 0x54, 0x20, 0x52, 0x45, 0x41, 0x44, 0x59, 0x22, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6B, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x31, 0x3A, 0x0A, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3D, 0x20, 0x22, 0x43, 0x4F, 0x4E, 0x4E, 0x45, 0x43, 0x54, 0x45, 0x44, 0x22, 0x3B, 0x0A,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6B, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20,
0x32, 0x3A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3D, 0x20, 0x22, 0x41, 0x44, 0x2D, 0x48, 0x4F, 0x43, 0x22,
0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6B, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73,
0x65, 0x20, 0x33, 0x3A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3D, 0x20, 0x22, 0x44, 0x49, 0x53, 0x43, 0x4F,
0x4E, 0x4E, 0x45, 0x43, 0x54, 0x49, 0x4E, 0x47, 0x22, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6B, 0x3B, 0x0A, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x34, 0x3A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x74, 0x61, 0x74, 0x65,
0x20, 0x3D, 0x20, 0x22, 0x44, 0x49, 0x53, 0x43, 0x4F, 0x4E, 0x4E, 0x45, 0x43, 0x54, 0x45, 0x44, 0x22, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62,
0x72, 0x65, 0x61, 0x6B, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x35, 0x3A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x69, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3D, 0x20, 0x22, 0x41, 0x53, 0x53, 0x4F, 0x43, 0x49, 0x41, 0x54, 0x49, 0x4E, 0x47, 0x22, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6B, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x36, 0x3A, 0x0A, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3D, 0x20, 0x22, 0x44, 0x49, 0x53, 0x43, 0x4F, 0x56, 0x45, 0x52, 0x49, 0x4E, 0x47, 0x22, 0x3B,
0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6B, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65,
0x20, 0x37, 0x3A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3D, 0x20, 0x22, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4E,
0x54, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4E, 0x47, 0x22, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6B, 0x3B, 0x0A, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x3A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x74, 0x61, 0x74,
0x65, 0x20, 0x3D, 0x20, 0x22, 0x55, 0x4E, 0x4B, 0x4E, 0x4F, 0x57, 0x4E, 0x22, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6B,
0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x67, 0x75, 0x69, 0x64, 0x20, 0x3D, 0x20, 0x69, 0x6E, 0x66, 0x6F, 0x2E, 0x44, 0x65,
0x72, 0x65, 0x66, 0x28, 0x30, 0x2C, 0x20, 0x31, 0x36, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2E,
0x57, 0x6C, 0x61, 0x6E, 0x53, 0x63, 0x61, 0x6E, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x2C, 0x20, 0x69, 0x67, 0x75, 0x69, 0x64, 0x2C, 0x20, 0x30, 0x2C, 0x20,
0x30, 0x2C, 0x20, 0x30, 0x29, 0x20, 0x3D, 0x3D, 0x20, 0x30, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E,
0x20, 0x28, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7B, 0x0A, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x28, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x7D, 0x0A, 0x0A, 0x66,
0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x28, 0x5F, 0x73, 0x73, 0x69, 0x64, 0x2C, 0x20, 0x5F, 0x62, 0x73, 0x73, 0x69,
0x64, 0x2C, 0x20, 0x5F, 0x72, 0x73, 0x73, 0x69, 0x2C, 0x20, 0x5F, 0x6C, 0x71, 0x29, 0x0A, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, 0x73, 0x69, 0x64, 0x20, 0x3D,
0x20, 0x5F, 0x73, 0x73, 0x69, 0x64, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x62, 0x73, 0x73, 0x69, 0x64, 0x20, 0x3D, 0x20, 0x5F, 0x62, 0x73, 0x73, 0x69, 0x64, 0x3B,
0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x72, 0x73, 0x73, 0x69, 0x20, 0x3D, 0x20, 0x5F, 0x72, 0x73, 0x73, 0x69, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73,
0x2E, 0x6C, 0x71, 0x20, 0x3D, 0x20, 0x5F, 0x6C, 0x71, 0x3B, 0x0A, 0x7D, 0x0A, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x2E, 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79,
0x70, 0x65, 0x2E, 0x74, 0x6F, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x0A, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72,
0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, 0x73, 0x69, 0x64, 0x20, 0x2B, 0x20, 0x22, 0x20, 0x5B, 0x22, 0x20, 0x2B, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x62,
0x73, 0x73, 0x69, 0x64, 0x20, 0x2B, 0x20, 0x22, 0x5D, 0x3A, 0x20, 0x22, 0x20, 0x2B, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6C, 0x71, 0x29, 0x3B, 0x0A, 0x7D, 0x0A, 0x0A, 0x66, 0x75, 0x6E, 0x63,
0x74, 0x69, 0x6F, 0x6E, 0x20, 0x4F, 0x6E, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x79, 0x28, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x44, 0x61, 0x74, 0x61, 0x29, 0x0A,
0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, 0x20, 0x4E,
0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x44, 0x61, 0x74, 0x61, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x30, 0x2C, 0x20, 0x34, 0x29, 0x2E, 0x56, 0x61, 0x6C, 0x3B,
0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x43, 0x6F, 0x64, 0x65, 0x20, 0x3D, 0x20, 0x4E, 0x6F, 0x74, 0x69,
0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x44, 0x61, 0x74, 0x61, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x34, 0x2C, 0x20, 0x34, 0x29, 0x2E, 0x56, 0x61, 0x6C, 0x3B, 0x0A, 0x20, 0x20,
0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x64, 0x61, 0x74, 0x61, 0x47, 0x75, 0x69, 0x64, 0x20, 0x3D, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x44, 0x61, 0x74,
0x61, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x38, 0x2C, 0x20, 0x31, 0x36, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69,
0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x26, 0x20, 0x30, 0x58, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x4E,
0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x43, 0x6F, 0x64, 0x65, 0x20, 0x3D, 0x3D, 0x20, 0x37, 0x29, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x73, 0x73, 0x20, 0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x50, 0x61, 0x72, 0x65, 0x6E, 0x74, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68,
0x61, 0x6C, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20,
0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x20, 0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x50, 0x61, 0x72, 0x65, 0x6E, 0x74, 0x2E, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2E, 0x47, 0x65, 0x74, 0x42,
0x53, 0x53, 0x4C, 0x69, 0x73, 0x74, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x50, 0x61, 0x72, 0x65, 0x6E, 0x74, 0x2E, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x2C, 0x20, 0x64, 0x61, 0x74, 0x61, 0x47,
0x75, 0x69, 0x64, 0x2C, 0x20, 0x30, 0x2C, 0x20, 0x33, 0x2C, 0x20, 0x30, 0x2C, 0x20, 0x30, 0x2C, 0x20, 0x62, 0x73, 0x73, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69,
0x66, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x20, 0x3D, 0x3D, 0x20, 0x30, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x6F, 0x74, 0x61, 0x6C, 0x53, 0x69, 0x7A, 0x65, 0x20, 0x3D, 0x20, 0x62, 0x73, 0x73, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28,
0x29, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x30, 0x2C, 0x20, 0x34, 0x29, 0x2E, 0x56, 0x61, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76,
0x61, 0x72, 0x20, 0x6E, 0x75, 0x6D, 0x49, 0x74, 0x65, 0x6D, 0x73, 0x20, 0x3D, 0x20, 0x62, 0x73, 0x73, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x29, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28,
0x34, 0x2C, 0x20, 0x34, 0x29, 0x2E, 0x56, 0x61, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x28, 0x69, 0x20, 0x3D, 0x20,
0x30, 0x3B, 0x20, 0x69, 0x20, 0x3C, 0x20, 0x6E, 0x75, 0x6D, 0x49, 0x74, 0x65, 0x6D, 0x73, 0x3B, 0x20, 0x2B, 0x2B, 0x69, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x3D, 0x20, 0x62,
0x73, 0x73, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x29, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x38, 0x20, 0x2B, 0x20, 0x28, 0x33, 0x36, 0x30, 0x20, 0x2A, 0x20, 0x69, 0x29, 0x2C, 0x20,
0x33, 0x36, 0x30, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x73, 0x69, 0x64, 0x20, 0x3D,
0x20, 0x69, 0x74, 0x65, 0x6D, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x34, 0x2C, 0x20, 0x33, 0x32, 0x29, 0x2E, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x2E, 0x74, 0x72, 0x69, 0x6D, 0x28, 0x29,
0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x73, 0x73, 0x69, 0x64, 0x20, 0x3D, 0x20, 0x69, 0x74,
0x65, 0x6D, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x34, 0x30, 0x2C, 0x20, 0x36, 0x29, 0x2E, 0x48, 0x65, 0x78, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x32, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x73, 0x73, 0x69, 0x20, 0x3D, 0x20, 0x69, 0x74, 0x65, 0x6D, 0x2E, 0x44, 0x65, 0x72, 0x65,
0x66, 0x28, 0x35, 0x36, 0x2C, 0x20, 0x34, 0x29, 0x2E, 0x56, 0x61, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61,
0x72, 0x20, 0x6C, 0x71, 0x20, 0x3D, 0x20, 0x69, 0x74, 0x65, 0x6D, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x36, 0x30, 0x2C, 0x20, 0x34, 0x29, 0x2E, 0x56, 0x61, 0x6C, 0x3B, 0x0A, 0x0A, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x50, 0x61, 0x72, 0x65, 0x6E, 0x74, 0x2E, 0x65, 0x6D, 0x69, 0x74, 0x28,
0x27, 0x53, 0x63, 0x61, 0x6E, 0x27, 0x2C, 0x20, 0x6E, 0x65, 0x77, 0x20, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x28, 0x73, 0x73, 0x69, 0x64, 0x2C, 0x20, 0x62, 0x73,
0x73, 0x69, 0x64, 0x2C, 0x20, 0x72, 0x73, 0x73, 0x69, 0x2C, 0x20, 0x6C, 0x71, 0x29, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x7D, 0x0A, 0x0A, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x57, 0x69, 0x72, 0x65,
0x6C, 0x65, 0x73, 0x73, 0x28, 0x29, 0x0A, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, 0x55, 0x74, 0x69, 0x6C, 0x73, 0x20, 0x3D, 0x20,
0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x65, 0x76, 0x65, 0x6E, 0x74, 0x73, 0x27, 0x29, 0x2E, 0x69, 0x6E, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29,
0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6C, 0x20, 0x3D, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x5F,
0x47, 0x65, 0x6E, 0x65, 0x72, 0x69, 0x63, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6C, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E, 0x61, 0x74, 0x69, 0x76,
0x65, 0x20, 0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6C, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6F,
0x78, 0x79, 0x28, 0x22, 0x77, 0x6C, 0x61, 0x6E, 0x61, 0x70, 0x69, 0x2E, 0x64, 0x6C, 0x6C, 0x22, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E, 0x61, 0x74, 0x69,
0x76, 0x65, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4D, 0x65, 0x74, 0x68, 0x6F, 0x64, 0x28, 0x22, 0x57, 0x6C, 0x61, 0x6E, 0x4F, 0x70, 0x65, 0x6E, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x22,
0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4D, 0x65, 0x74, 0x68, 0x6F, 0x64, 0x28,
0x22, 0x57, 0x6C, 0x61, 0x6E, 0x47, 0x65, 0x74, 0x4E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x42, 0x73, 0x73, 0x4C, 0x69, 0x73, 0x74, 0x22, 0x2C, 0x20, 0x22, 0x47, 0x65, 0x74, 0x42, 0x53, 0x53,
0x4C, 0x69, 0x73, 0x74, 0x22, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4D, 0x65,
0x74, 0x68, 0x6F, 0x64, 0x28, 0x22, 0x57, 0x6C, 0x61, 0x6E, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x22, 0x29,
0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4D, 0x65, 0x74, 0x68, 0x6F, 0x64, 0x28, 0x22,
0x57, 0x6C, 0x61, 0x6E, 0x45, 0x6E, 0x75, 0x6D, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x22, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E,
0x61, 0x74, 0x69, 0x76, 0x65, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4D, 0x65, 0x74, 0x68, 0x6F, 0x64, 0x28, 0x22, 0x57, 0x6C, 0x61, 0x6E, 0x53, 0x63, 0x61, 0x6E, 0x22, 0x29, 0x3B, 0x0A,
0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4D, 0x65, 0x74, 0x68, 0x6F, 0x64, 0x28, 0x22, 0x57, 0x6C,
0x61, 0x6E, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6E, 0x65, 0x67,
0x6F, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6C, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x6F, 0x69,
0x6E, 0x74, 0x65, 0x72, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x68, 0x20, 0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6C,
0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x28, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E, 0x61, 0x74, 0x69,
0x76, 0x65, 0x2E, 0x57, 0x6C, 0x61, 0x6E, 0x4F, 0x70, 0x65, 0x6E, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x28, 0x32, 0x2C, 0x20, 0x30, 0x2C, 0x20, 0x6E, 0x65, 0x67, 0x6F, 0x74, 0x69, 0x61, 0x74,
0x65, 0x64, 0x2C, 0x20, 0x68, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x20, 0x3D, 0x20, 0x68, 0x2E, 0x56, 0x61, 0x6C, 0x3B,
0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x5F, 0x4E, 0x4F, 0x54, 0x49, 0x46, 0x59, 0x5F, 0x50, 0x52, 0x4F, 0x58, 0x59, 0x5F, 0x4F, 0x42, 0x4A, 0x45, 0x43, 0x54, 0x20,
0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6C, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x62, 0x61, 0x63, 0x6B, 0x50, 0x72, 0x6F,
0x78, 0x79, 0x28, 0x4F, 0x6E, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x79, 0x2C, 0x20, 0x32, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x5F, 0x4E, 0x4F, 0x54, 0x49, 0x46,
0x59, 0x5F, 0x50, 0x52, 0x4F, 0x58, 0x59, 0x5F, 0x4F, 0x42, 0x4A, 0x45, 0x43, 0x54, 0x2E, 0x50, 0x61, 0x72, 0x65, 0x6E, 0x74, 0x20, 0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x3B, 0x0A, 0x20, 0x20,
0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x50, 0x72, 0x65, 0x76, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6C, 0x2E,
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x20,
0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2E, 0x57, 0x6C, 0x61, 0x6E, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69,
0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x2C, 0x20, 0x30, 0x58, 0x30, 0x30, 0x30, 0x30, 0x46, 0x46, 0x46, 0x46, 0x2C, 0x20,
0x30, 0x2C, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x5F, 0x4E, 0x4F, 0x54, 0x49, 0x46, 0x59, 0x5F, 0x50, 0x52, 0x4F, 0x58, 0x59, 0x5F, 0x4F, 0x42, 0x4A, 0x45, 0x43, 0x54, 0x2E, 0x43, 0x61, 0x6C,
0x6C, 0x62, 0x61, 0x63, 0x6B, 0x2C, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x5F, 0x4E, 0x4F, 0x54, 0x49, 0x46, 0x59, 0x5F, 0x50, 0x52, 0x4F, 0x58, 0x59, 0x5F, 0x4F, 0x42, 0x4A, 0x45, 0x43, 0x54,
0x2E, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2C, 0x20, 0x30, 0x2C, 0x20, 0x50, 0x72, 0x65, 0x76, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6D, 0x69,
0x74, 0x74, 0x65, 0x72, 0x55, 0x74, 0x69, 0x6C, 0x73, 0x2E, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x28, 0x27, 0x53, 0x63, 0x61, 0x6E, 0x27, 0x29, 0x3B, 0x0A, 0x20,
0x20, 0x20, 0x20, 0x65, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, 0x55, 0x74, 0x69, 0x6C, 0x73, 0x2E, 0x61, 0x64, 0x64, 0x4D, 0x65, 0x74, 0x68, 0x6F, 0x64, 0x28, 0x27, 0x53, 0x63, 0x61, 0x6E, 0x27,
0x2C, 0x20, 0x5F, 0x53, 0x63, 0x61, 0x6E, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x47, 0x65, 0x74, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x65, 0x64,
0x4E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x28, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x20, 0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61,
0x6C, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x28, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73,
0x2E, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2E, 0x57, 0x6C, 0x61, 0x6E, 0x45, 0x6E, 0x75, 0x6D, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E,
0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x2C, 0x20, 0x30, 0x2C, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x20, 0x3D, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x2E,
0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x29, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x30, 0x2C, 0x20, 0x34, 0x29, 0x2E, 0x56, 0x61, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6E, 0x66, 0x6F, 0x20, 0x3D, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x29, 0x2E, 0x44,
0x65, 0x72, 0x65, 0x66, 0x28, 0x38, 0x2C, 0x20, 0x35, 0x33, 0x32, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6E, 0x61, 0x6D, 0x65, 0x20,
0x3D, 0x20, 0x69, 0x6E, 0x66, 0x6F, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x31, 0x36, 0x2C, 0x20, 0x35, 0x31, 0x32, 0x29, 0x2E, 0x41, 0x6E, 0x73, 0x69, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67,
0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3D, 0x20, 0x69, 0x6E, 0x66, 0x6F, 0x2E, 0x44, 0x65, 0x72, 0x65,
0x66, 0x28, 0x35, 0x32, 0x38, 0x2C, 0x20, 0x34, 0x29, 0x2E, 0x49, 0x6E, 0x74, 0x56, 0x61, 0x6C, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x69, 0x6E,
0x66, 0x6F, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x35, 0x32, 0x38, 0x2C, 0x20, 0x34, 0x29, 0x2E, 0x49, 0x6E, 0x74, 0x56, 0x61, 0x6C, 0x20, 0x3D, 0x3D, 0x20, 0x31, 0x29, 0x20, 0x2F, 0x2F,
0x20, 0x43, 0x4F, 0x4E, 0x4E, 0x45, 0x43, 0x54, 0x45, 0x44, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x76, 0x61, 0x72, 0x20, 0x64, 0x61, 0x74, 0x61, 0x53, 0x69, 0x7A, 0x65, 0x20, 0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6C, 0x2E, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x70, 0x44,
0x61, 0x74, 0x61, 0x20, 0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6C, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72,
0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x20, 0x3D, 0x20,
0x74, 0x68, 0x69, 0x73, 0x2E, 0x4D, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6C, 0x2E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x67, 0x75, 0x69, 0x64, 0x20, 0x3D, 0x20, 0x69, 0x6E, 0x66, 0x6F, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66,
0x28, 0x30, 0x2C, 0x20, 0x31, 0x36, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x74, 0x56, 0x61, 0x6C, 0x20,
0x3D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2E, 0x57, 0x6C, 0x61, 0x6E, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65,
0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x2C, 0x20, 0x69, 0x67, 0x75, 0x69, 0x64, 0x2C, 0x20, 0x37, 0x2C, 0x20, 0x30, 0x2C, 0x20, 0x64, 0x61, 0x74, 0x61, 0x53,
0x69, 0x7A, 0x65, 0x2C, 0x20, 0x70, 0x44, 0x61, 0x74, 0x61, 0x2C, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x74, 0x56, 0x61, 0x6C, 0x20, 0x3D, 0x3D, 0x20, 0x30, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x73, 0x73, 0x6F, 0x63, 0x69, 0x61, 0x74, 0x65,
0x64, 0x53, 0x53, 0x49, 0x44, 0x20, 0x3D, 0x20, 0x70, 0x44, 0x61, 0x74, 0x61, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x29, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x35, 0x32, 0x34, 0x2C,
0x20, 0x33, 0x32, 0x29, 0x2E, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72,
0x20, 0x62, 0x73, 0x73, 0x69, 0x64, 0x20, 0x3D, 0x20, 0x70, 0x44, 0x61, 0x74, 0x61, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x29, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x35, 0x36, 0x30,
0x2C, 0x20, 0x36, 0x29, 0x2E, 0x48, 0x65, 0x78, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x76, 0x61, 0x72, 0x20, 0x6C, 0x71, 0x20, 0x3D, 0x20, 0x70, 0x44, 0x61, 0x74, 0x61, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x29, 0x2E, 0x44, 0x65, 0x72, 0x65, 0x66, 0x28, 0x35, 0x37, 0x36,
0x2C, 0x20, 0x34, 0x29, 0x2E, 0x49, 0x6E, 0x74, 0x56, 0x61, 0x6C, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
0x74, 0x75, 0x72, 0x6E, 0x20, 0x28, 0x6E, 0x65, 0x77, 0x20, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x28, 0x61, 0x73, 0x73, 0x6F, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64,
0x53, 0x53, 0x49, 0x44, 0x2C, 0x20, 0x62, 0x73, 0x73, 0x69, 0x64, 0x2C, 0x20, 0x30, 0x2C, 0x20, 0x6C, 0x71, 0x29, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x72, 0x6F, 0x77, 0x20, 0x28, 0x22, 0x47, 0x65,
0x74, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x73, 0x3A, 0x20, 0x46, 0x41, 0x49, 0x4C, 0x45, 0x44, 0x20, 0x28, 0x6E, 0x6F, 0x74, 0x20,
0x61, 0x73, 0x73, 0x6F, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x20, 0x6E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x29, 0x22, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20,
0x7D, 0x3B, 0x0A, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x3B, 0x0A, 0x7D, 0x0A, 0x0A, 0x6D, 0x6F, 0x64, 0x75, 0x6C,
0x65, 0x2E, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x20, 0x3D, 0x20, 0x6E, 0x65, 0x77, 0x20, 0x57, 0x69, 0x72, 0x65, 0x6C, 0x65, 0x73, 0x73, 0x28, 0x29, 0x3B, 0x0A]);
var WindowsChildScript = new Buffer([
0x76, 0x61, 0x72, 0x20, 0x70, 0x61, 0x72, 0x65, 0x6E, 0x74, 0x20, 0x3D, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6F, 0x6E, 0x74,
0x61, 0x69, 0x6E, 0x65, 0x72, 0x27, 0x29, 0x3B, 0x0D, 0x0A, 0x76, 0x61, 0x72, 0x20, 0x57, 0x69, 0x72, 0x65, 0x6C, 0x65, 0x73, 0x73, 0x20, 0x3D, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65,
0x28, 0x27, 0x57, 0x69, 0x72, 0x65, 0x6C, 0x65, 0x73, 0x73, 0x27, 0x29, 0x3B, 0x0D, 0x0A, 0x0D, 0x0A, 0x57, 0x69, 0x72, 0x65, 0x6C, 0x65, 0x73, 0x73, 0x2E, 0x6F, 0x6E, 0x28, 0x27, 0x53, 0x63,
0x61, 0x6E, 0x27, 0x2C, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x28, 0x61, 0x70, 0x29, 0x20, 0x7B, 0x20, 0x70, 0x61, 0x72, 0x65, 0x6E, 0x74, 0x2E, 0x73, 0x65, 0x6E, 0x64,
0x28, 0x61, 0x70, 0x29, 0x3B, 0x20, 0x7D, 0x29, 0x3B, 0x0D, 0x0A, 0x57, 0x69, 0x72, 0x65, 0x6C, 0x65, 0x73, 0x73, 0x2E, 0x53, 0x63, 0x61, 0x6E, 0x28, 0x29, 0x3B, 0x0D, 0x0A]);
function AccessPoint(_ssid, _bssid, _lq)
{
this.ssid = _ssid;
this.bssid = _bssid;
this.lq = _lq;
}
AccessPoint.prototype.toString = function ()
{
return (this.ssid + " [" + this.bssid + "]: " + this.lq);
}
function WiFiScanner()
{
var emitterUtils = require('events').inherits(this);
emitterUtils.createEvent('accessPoint');
this.hasWireless = function ()
{
var retVal = false;
var interfaces = require('os').networkInterfaces();
for (var name in interfaces)
{
if (interfaces[name][0].type == 'wireless') { retVal = true; break; }
}
return (retVal);
};
this.Scan = function ()
{
if (process.platform == 'win32')
{
this.master = require('ScriptContainer').Create(15, ContainerPermissions.DEFAULT);
this.master.parent = this;
this.master.on('data', function (j) { this.parent.emit('accessPoint', new AccessPoint(j.ssid, j.bssid, j.lq)); });
this.master.addModule('Wireless', WindowsWireless.toString());
this.master.ExecuteString(WindowsChildScript.toString());
}
else if (process.platform == 'linux')
{
// Need to get the wireless interface name
var interfaces = require('os').networkInterfaces();
var wlan = null;
for (var i in interfaces)
{
if (interfaces[i][0].type == 'wireless')
{
wlan = i;
break;
}
}
if (wlan != null)
{
this.child = require('ILibProcessPipe').CreateProcess("/sbin/iwlist", "iwlist", wlan, "scan");
this.child.parent = this;
this.child.ms = new MemoryStream();
this.child.ms.parent = this.child;
this.child.on('data', function (buffer) { this.ms.write(buffer); });
this.child.on('end', function () { this.ms.end(); });
this.child.ms.on('end', function ()
{
var str = this.buffer.toString();
tokens = str.split(' - Address: ');
for (var block in tokens)
{
if (block == 0) continue;
var ln = tokens[block].split('\n');
var _bssid = ln[0];
var _lq;
var _ssid;
for (var lnblock in ln)
{
lnblock = ln[lnblock].trim();
lnblock = lnblock.trim();
if (lnblock.startsWith('ESSID:'))
{
_ssid = lnblock.slice(7, lnblock.length - 1);
if (_ssid == '<hidden>') { _ssid = ''; }
}
if (lnblock.startsWith('Signal level='))
{
_lq = lnblock.slice(13,lnblock.length-4);
}
}
this.parent.parent.emit('accessPoint', new AccessPoint(_ssid, _bssid, _lq));
}
});
}
}
}
}
module.exports = WiFiScanner;

View File

@ -0,0 +1,281 @@
var Q = require('queue');
function amt_heci() {
var emitterUtils = require('events').inherits(this);
emitterUtils.createEvent('error');
emitterUtils.createEvent('connect');
var heci = require('heci');
this._amt = heci.create();
this._amt.BiosVersionLen = 65;
this._amt.UnicodeStringLen = 20;
this._amt.rq = new Q();
this._amt.Parent = this;
this._amt.on('error', function (e) { this.Parent.emit('error', e); });
this._amt.on('connect', function () {
this.Parent.emit('connect');
this.on('data', function (chunk) {
//console.log("Received: " + chunk.length + " bytes");
var header = this.Parent.getCommand(chunk);
//console.log("CMD = " + header.Command + " (Status: " + header.Status + ") Response = " + header.IsResponse);
var user = this.rq.deQueue();
var params = user.optional;
var callback = user.func;
params.unshift(header);
callback.apply(this.Parent, params);
});
});
this._amt.connect(heci.GUIDS.AMT, { noPipeline: 1 });
this.getCommand = function (chunk) {
var command = chunk.length == 0 ? (this._amt.rq.peekQueue().cmd | 0x800000) : chunk.readUInt32LE(4);
var ret = { IsResponse: (command & 0x800000) == 0x800000 ? true : false, Command: (command & 0x7FFFFF), Status: chunk.length != 0 ? chunk.readUInt32LE(12) : -1, Data: chunk.length != 0 ? chunk.slice(16) : null };
return (ret);
};
this.sendCommand = function () {
if (arguments.length < 3 || typeof (arguments[0]) != 'number' || typeof (arguments[1]) != 'object' || typeof (arguments[2]) != 'function') { throw ('invalid parameters'); }
var args = [];
for (var i = 3; i < arguments.length; ++i) { args.push(arguments[i]); }
this._amt.rq.enQueue({ cmd: arguments[0], func: arguments[2], optional: args });
var header = Buffer.from('010100000000000000000000', 'hex');
header.writeUInt32LE(arguments[0] | 0x04000000, 4);
header.writeUInt32LE(arguments[1] == null ? 0 : arguments[1].length, 8);
this._amt.write(arguments[1] == null ? header : Buffer.concat([header, arguments[1]]));
}
this.getVersion = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(26, null, function (header, fn, opt) {
if (header.Status == 0) {
var i, CodeVersion = header.Data, val = { BiosVersion: CodeVersion.slice(0, this._amt.BiosVersionLen), Versions: [] }, v = CodeVersion.slice(this._amt.BiosVersionLen + 4);
for (i = 0; i < CodeVersion.readUInt32LE(this._amt.BiosVersionLen) ; ++i) {
val.Versions[i] = { Description: v.slice(2, v.readUInt16LE(0) + 2).toString(), Version: v.slice(4 + this._amt.UnicodeStringLen, 4 + this._amt.UnicodeStringLen + v.readUInt16LE(2 + this._amt.UnicodeStringLen)).toString() };
v = v.slice(4 + (2 * this._amt.UnicodeStringLen));
}
opt.unshift(val);
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getProvisioningState = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(17, null, function (header, fn, opt) {
if (header.Status == 0) {
var result = {};
result.state = header.Data.readUInt32LE(0);
if (result.state < 3) { result.stateStr = ["PRE", "IN", "POST"][result.state]; }
opt.unshift(result);
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getProvisioningMode = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(8, null, function (header, fn, opt) {
if (header.Status == 0) {
var result = {};
result.mode = header.Data.readUInt32LE(0);
if (result.mode < 4) { result.modeStr = ["NONE", "ENTERPRISE", "SMALL_BUSINESS", "REMOTE_ASSISTANCE"][result.mode]; }
result.legacy = header.Data.readUInt32LE(4) == 0 ? false : true;
opt.unshift(result);
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getEHBCState = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(132, null, function (header, fn, opt) {
if (header.Status == 0) {
opt.unshift({ EHBC: header.Data.readUInt32LE(0) != 0 });
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getControlMode = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(107, null, function (header, fn, opt) {
if (header.Status == 0) {
var result = {};
result.controlMode = header.Data.readUInt32LE(0);
if (result.controlMode < 3) { result.controlModeStr = ["NONE_RPAT", "CLIENT", "ADMIN", "REMOTE_ASSISTANCE"][result.controlMode]; }
opt.unshift(result);
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getMACAddresses = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(37, null, function (header, fn, opt) {
if (header.Status == 0) {
opt.unshift({ DedicatedMAC: header.Data.slice(0, 6).toString('hex:'), HostMAC: header.Data.slice(6, 12).toString('hex:') });
} else { opt.unshift({ DedicatedMAC: null, HostMAC: null }); }
fn.apply(this, opt);
}, callback, optional);
};
this.getDnsSuffix = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(54, null, function (header, fn, opt) {
if (header.Status == 0) {
var resultLen = header.Data.readUInt16LE(0);
if (resultLen > 0) { opt.unshift(header.Data.slice(2, 2 + resultLen).toString()); } else { opt.unshift(null); }
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getHashHandles = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x2C, null, function (header, fn, opt) {
var result = [];
if (header.Status == 0) {
var resultLen = header.Data.readUInt32LE(0);
for (var i = 0; i < resultLen; ++i) {
result.push(header.Data.readUInt32LE(4 + (4 * i)));
}
}
opt.unshift(result);
fn.apply(this, opt);
}, callback, optional);
};
this.getCertHashEntry = function (handle, callback) {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
var data = new Buffer(4);
data.writeUInt32LE(handle, 0);
this.sendCommand(0x2D, data, function (header, fn, opt) {
if (header.Status == 0) {
var result = {};
result.isDefault = header.Data.readUInt32LE(0);
result.isActive = header.Data.readUInt32LE(4);
result.hashAlgorithm = header.Data.readUInt8(72);
if (result.hashAlgorithm < 4) {
result.hashAlgorithmStr = ["MD5", "SHA1", "SHA256", "SHA512"][result.hashAlgorithm];
result.hashAlgorithmSize = [16, 20, 32, 64][result.hashAlgorithm];
result.certificateHash = header.Data.slice(8, 8 + result.hashAlgorithmSize).toString('hex');
}
result.name = header.Data.slice(73 + 2, 73 + 2 + header.Data.readUInt16LE(73)).toString();
opt.unshift(result);
} else {
opt.unshift(null);
}
fn.apply(this, opt);
}, callback, optional);
};
this.getCertHashEntries = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.getHashHandles(function (handles, fn, opt) {
var entries = [];
this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles);
}, callback, optional);
};
this._getHashEntrySink = function (result, fn, opt, entries, handles) {
entries.push(result);
if (handles.length > 0) {
this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles);
} else {
opt.unshift(entries);
fn.apply(this, opt);
}
}
this.getLocalSystemAccount = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(103, Buffer.alloc(40), function (header, fn, opt) {
if (header.Data.length == 68) { opt.unshift({ user: header.Data.slice(0, 34).toString(), pass: header.Data.slice(34, 67).toString(), raw: header.Data }); } else { opt.unshift(null); }
fn.apply(this, opt);
}, callback, optional);
}
this.unprovision = function (mode, callback) {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
var data = new Buffer(4);
data.writeUInt32LE(mode, 0);
this.sendCommand(16, data, function (header, fn, opt) {
opt.unshift(header.Status);
fn.apply(this, opt);
}, callback, optional);
}
this.startConfiguration = function () {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x29, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
}
this.stopConfiguration = function () {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x5E, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
}
this.openUserInitiatedConnection = function () {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x44, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
}
this.closeUserInitiatedConnection = function () {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x45, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
}
this.getRemoteAccessConnectionStatus = function () {
var optional = [];
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
this.sendCommand(0x46, data, function (header, fn, opt) {
if (header.Status == 0) {
var hostname = v.slice(14, header.Data.readUInt16LE(12) + 14).toString()
opt.unshift({ status: header.Status, networkStatus: header.Data.readUInt32LE(0), remoteAccessStatus: header.Data.readUInt32LE(4), remoteAccessTrigger: header.Data.readUInt32LE(8), mpsHostname: hostname, raw: header.Data });
} else {
opt.unshift({ status: header.Status });
}
fn.apply(this, opt);
}, callback, optional);
}
this.getProtocolVersion = function (callback) {
var optional = [];
for (var i = 1; i < arguments.length; ++i) { opt.push(arguments[i]); }
heci.doIoctl(heci.IOCTL.HECI_VERSION, Buffer.alloc(5), Buffer.alloc(5), function (status, buffer, self, fn, opt) {
if (status == 0) {
var result = buffer.readUInt8(0).toString() + '.' + buffer.readUInt8(1).toString() + '.' + buffer.readUInt8(2).toString() + '.' + buffer.readUInt16BE(3).toString();
opt.unshift(result);
fn.apply(self, opt);
}
else {
opt.unshift(null);
fn.apply(self, opt);
}
}, this, callback, optional);
}
}
module.exports = amt_heci;

View File

@ -0,0 +1,354 @@
var MemoryStream = require('MemoryStream');
var lme_id = 0;
var APF_DISCONNECT = 1;
var APF_SERVICE_REQUEST = 5;
var APF_SERVICE_ACCEPT = 6;
var APF_USERAUTH_REQUEST = 50;
var APF_USERAUTH_FAILURE = 51;
var APF_USERAUTH_SUCCESS = 52;
var APF_GLOBAL_REQUEST = 80;
var APF_REQUEST_SUCCESS = 81;
var APF_REQUEST_FAILURE = 82;
var APF_CHANNEL_OPEN = 90;
var APF_CHANNEL_OPEN_CONFIRMATION = 91;
var APF_CHANNEL_OPEN_FAILURE = 92;
var APF_CHANNEL_WINDOW_ADJUST = 93;
var APF_CHANNEL_DATA = 94;
var APF_CHANNEL_CLOSE = 97;
var APF_PROTOCOLVERSION = 192;
function lme_object()
{
this.ourId = ++lme_id;
this.amtId = -1;
this.LME_CHANNEL_STATUS = 'LME_CS_FREE';
this.txWindow = 0;
this.rxWindow = 0;
this.localPort = 0;
this.errorCount = 0;
}
function stream_bufferedWrite()
{
var emitterUtils = require('events').inherits(this);
this.buffer = [];
this._readCheckImmediate = undefined;
// Writable Events
emitterUtils.createEvent('close');
emitterUtils.createEvent('drain');
emitterUtils.createEvent('error');
emitterUtils.createEvent('finish');
emitterUtils.createEvent('pipe');
emitterUtils.createEvent('unpipe');
// Readable Events
emitterUtils.createEvent('readable');
this.isEmpty = function ()
{
return (this.buffer.length == 0);
};
this.isWaiting = function ()
{
return (this._readCheckImmediate == undefined);
};
this.write = function (chunk)
{
for (var args in arguments)
{
if (typeof (arguments[args]) == 'function') { this.once('drain', arguments[args]); break; }
}
var tmp = Buffer.alloc(chunk.length);
chunk.copy(tmp);
this.buffer.push({ offset: 0, data: tmp });
this.emit('readable');
return (this.buffer.length == 0 ? true : false);
};
this.read = function ()
{
var size = arguments.length == 0 ? undefined : arguments[0];
var bytesRead = 0;
var list = [];
while((size == undefined || bytesRead < size) && this.buffer.length > 0)
{
var len = this.buffer[0].data.length - this.buffer[0].offset;
var offset = this.buffer[0].offset;
if(len > (size - bytesRead))
{
// Only reading a subset
list.push(this.buffer[0].data.slice(offset, offset + size - bytesRead));
this.buffer[0].offset += (size - bytesRead);
bytesRead += (size - bytesRead);
}
else
{
// Reading the entire thing
list.push(this.buffer[0].data.slice(offset));
bytesRead += len;
this.buffer.shift();
}
}
this._readCheckImmediate = setImmediate(function (buffered)
{
buffered._readCheckImmediate = undefined;
if(buffered.buffer.length == 0)
{
// drained
buffered.emit('drain');
}
else
{
// not drained
buffered.emit('readable');
}
}, this);
return (Buffer.concat(list));
};
}
function lme_heci()
{
var emitterUtils = require('events').inherits(this);
emitterUtils.createEvent('error');
emitterUtils.createEvent('connect');
var heci = require('heci');
this.INITIAL_RXWINDOW_SIZE = 4096;
this._LME = heci.create();
this._LME.LMS = this;
this._LME.on('error', function (e) { this.Parent.emit('error', e); });
this._LME.on('connect', function ()
{
this.LMS.emit('connect');
this.on('data', function (chunk)
{
// this = HECI
var cmd = chunk.readUInt8(0);
switch(cmd)
{
default:
//console.log('Received ' + chunk.length + ' bytes of data for LMS');
//console.log('Command = ' + cmd);
break;
case APF_SERVICE_REQUEST:
var nameLen = chunk.readUInt32BE(1);
var name = chunk.slice(5, nameLen + 5);
//console.log("Service Request for: " + name);
if (name == 'pfwd@amt.intel.com' || name == 'auth@amt.intel.com')
{
var outBuffer = Buffer.alloc(5 + nameLen);
outBuffer.writeUInt8(6, 0);
outBuffer.writeUInt32BE(nameLen, 1);
outBuffer.write(name.toString(), 5);
this.write(outBuffer);
//console.log('Answering APF_SERVICE_REQUEST');
}
else
{
//console.log('UNKNOWN APF_SERVICE_REQUEST');
}
break;
case APF_GLOBAL_REQUEST:
var nameLen = chunk.readUInt32BE(1);
var name = chunk.slice(5, nameLen + 5).toString();
switch(name)
{
case 'tcpip-forward':
var len = chunk.readUInt32BE(nameLen + 6);
var port = chunk.readUInt32BE(nameLen + 10 + len);
//console.log("[" + chunk.length + "/" + len + "] APF_GLOBAL_REQUEST for: " + name + " on port " + port);
if (this[name] == undefined)
{
this[name] = {};
}
this[name][port] = require('net').createServer();
this[name][port].HECI = this;
this[name][port].listen({ port: port });
this[name][port].on('connection', function (socket)
{
//console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort);
this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort);
});
var outBuffer = Buffer.alloc(5);
outBuffer.writeUInt8(81, 0);
outBuffer.writeUInt32BE(port, 1);
this.write(outBuffer);
break;
case 'cancel-tcpip-forward':
break;
case 'udp-send-to@amt.intel.com':
break;
default:
//console.log("Unknown APF_GLOBAL_REQUEST for: " + name);
break;
}
break;
case APF_CHANNEL_OPEN_CONFIRMATION:
var rChannel = chunk.readUInt32BE(1);
var sChannel = chunk.readUInt32BE(5);
var wSize = chunk.readUInt32BE(9);
//console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize);
if (this.sockets[rChannel] != undefined)
{
this.sockets[rChannel].lme.amtId = sChannel;
this.sockets[rChannel].lme.rxWindow = wSize;
this.sockets[rChannel].lme.txWindow = wSize;
this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED';
//console.log('LME_CS_CONNECTED');
this.sockets[rChannel].bufferedStream = new stream_bufferedWrite();
this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel];
this.sockets[rChannel].bufferedStream.on('readable', function ()
{
if(this.socket.lme.txWindow > 0)
{
var buffer = this.read(this.socket.lme.txWindow);
var packet = Buffer.alloc(9 + buffer.length);
packet.writeUInt8(APF_CHANNEL_DATA, 0);
packet.writeUInt32BE(this.socket.lme.amtId, 1);
packet.writeUInt32BE(buffer.length, 5);
buffer.copy(packet, 9);
this.socket.lme.txWindow -= buffer.length;
this.socket.HECI.write(packet);
}
});
this.sockets[rChannel].bufferedStream.on('drain', function ()
{
this.socket.resume();
});
this.sockets[rChannel].on('data', function (chunk)
{
if (!this.bufferedStream.write(chunk)) { this.pause(); }
});
this.sockets[rChannel].on('end', function ()
{
var outBuffer = Buffer.alloc(5);
outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
outBuffer.writeUInt32BE(this.lme.amtId, 1);
this.HECI.write(outBuffer);
});
this.sockets[rChannel].resume();
}
break;
case APF_PROTOCOLVERSION:
var major = chunk.readUInt32BE(1);
var minor = chunk.readUInt32BE(5);
var reason = chunk.readUInt32BE(9);
var outBuffer = Buffer.alloc(93);
outBuffer.writeUInt8(192, 0);
outBuffer.writeUInt32BE(1, 1);
outBuffer.writeUInt32BE(0, 5);
outBuffer.writeUInt32BE(reason, 9);
//console.log('Answering PROTOCOL_VERSION');
this.write(outBuffer);
break;
case APF_CHANNEL_WINDOW_ADJUST:
var rChannelId = chunk.readUInt32BE(1);
var bytesToAdd = chunk.readUInt32BE(5);
if (this.sockets[rChannelId] != undefined)
{
this.sockets[rChannelId].lme.txWindow += bytesToAdd;
if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting())
{
this.sockets[rChannelId].bufferedStream.emit('readable');
}
}
else
{
//console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_WINDOW_ADJUST');
}
break;
case APF_CHANNEL_DATA:
var rChannelId = chunk.readUInt32BE(1);
var dataLen = chunk.readUInt32BE(5);
var data = chunk.slice(9, 9 + dataLen);
if (this.sockets[rChannelId] != undefined)
{
this.sockets[rChannelId].pendingBytes.push(data.length);
this.sockets[rChannelId].write(data, function ()
{
var written = this.pendingBytes.shift();
var outBuffer = Buffer.alloc(9);
outBuffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0);
outBuffer.writeUInt32BE(this.lme.amtId, 1);
outBuffer.writeUInt32BE(written, 5);
this.HECI.write(outBuffer);
});
}
else
{
//console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_DATA');
}
break;
case APF_CHANNEL_CLOSE:
var rChannelId = chunk.readUInt32BE(1);
if (this.sockets[rChannelId] != undefined)
{
this.sockets[rChannelId].end();
var amtId = this.sockets[rChannelId].lme.amtId;
var buffer = Buffer.alloc(5);
delete this.sockets[rChannelId];
buffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
buffer.writeUInt32BE(amtId, 1);
this.write(buffer);
}
else
{
//console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_CLOSE');
}
break;
}
});
});
this.bindDuplexStream = function (duplexStream, remoteFamily, localPort)
{
var socket = duplexStream;
//console.log('New [' + remoteFamily + '] Virtual Connection/' + socket.localPort);
socket.pendingBytes = [];
socket.HECI = this._LME;
socket.LMS = this;
socket.lme = new lme_object();
socket.lme.Socket = socket;
var buffer = new MemoryStream();
buffer.writeUInt8(0x5A);
buffer.writeUInt32BE(15);
buffer.write('forwarded-tcpip');
buffer.writeUInt32BE(socket.lme.ourId);
buffer.writeUInt32BE(this.INITIAL_RXWINDOW_SIZE);
buffer.writeUInt32BE(0xFFFFFFFF);
for (var i = 0; i < 2; ++i)
{
if (remoteFamily == 'IPv6')
{
buffer.writeUInt32BE(3);
buffer.write('::1');
}
else
{
buffer.writeUInt32BE(9);
buffer.write('127.0.0.1');
}
buffer.writeUInt32BE(localPort);
}
this._LME.write(buffer.buffer);
if (this._LME.sockets == undefined) { this._LME.sockets = {}; }
this._LME.sockets[socket.lme.ourId] = socket;
socket.pause();
};
this._LME.connect(heci.GUIDS.LME, { noPipeline: 0 });
}
module.exports = lme_heci;

9
db.js
View File

@ -71,6 +71,13 @@ module.exports.CreateDB = function (args, datapath) {
if (err == null && docs.length > 0) { for (var i in docs) { meshlist.push(docs[i]._id); } }
obj.file.remove({ meshid: { $exists: true, $nin: meshlist } }, { multi: true });
});
// Clear up all users
/*
obj.GetAllType('user', function (err, docs) {
for (var i in docs) { if (docs[i].subscriptions != null) { console.log('Clean user: ' + docs[i].name); obj.SetUser(docs[i]); } } // Remove "subscriptions" that should not be there.
});
*/
}
obj.Set = function (data) { obj.file.update({ _id: data._id }, data, { upsert: true }); }
@ -80,6 +87,8 @@ module.exports.CreateDB = function (args, datapath) {
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, domain, type, func) { obj.file.find({ type: type, domain: domain, meshid: { $in: meshes } }, { type : 0 }, func); }
obj.GetAllType = function (type, func) { obj.file.find({ type: type }, func); }
obj.GetAllIdsOfType = function (ids, domain, type, func) { obj.file.find({ type: type, domain: domain, _id: { $in: ids } }, func); }
obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }, { type: 0 }, func); }
obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }, { type: 0 }, func); }
obj.Remove = function (id) { obj.file.remove({ _id: id }); }
obj.RemoveAll = function (func) { obj.file.remove({}, { multi: true }, func); }
obj.InsertMany = function (data, func) { obj.file.insert(data, func); }

View File

@ -64,14 +64,18 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// TODO: Check if we have a mesh specific core. If so, use that.
var agentMeshCoreHash = null;
if (msg.length == 52) { agentMeshCoreHash = msg.substring(4, 52); }
if (agentMeshCoreHash != obj.parent.parent.defaultMeshCoreHash) {
if ((agentMeshCoreHash != obj.parent.parent.defaultMeshCoreHash) && (agentMeshCoreHash != obj.parent.parent.defaultMeshCoreNoMeiHash)) {
if (obj.agentCoreCheck < 5) { // This check is in place to avoid a looping core update.
if (obj.parent.parent.defaultMeshCoreHash == null) {
// Update no core
obj.send(obj.common.ShortToStr(10) + obj.common.ShortToStr(0)); // Command 10, ask mesh agent to clear the core
} else {
// Update new core
obj.send(obj.common.ShortToStr(10) + obj.common.ShortToStr(0) + obj.parent.parent.defaultMeshCoreHash + obj.parent.parent.defaultMeshCore); // Command 10, ask mesh agent to set the core
if (obj.agentExeInfo.amt == true) {
obj.send(obj.common.ShortToStr(10) + obj.common.ShortToStr(0) + obj.parent.parent.defaultMeshCoreHash + obj.parent.parent.defaultMeshCore); // Command 10, ask mesh agent to set the core (with MEI support)
} else {
obj.send(obj.common.ShortToStr(10) + obj.common.ShortToStr(0) + obj.parent.parent.defaultMeshCoreNoMeiHash + obj.parent.parent.defaultMeshCoreNoMei); // Command 10, ask mesh agent to set the core (No MEI)
}
}
obj.agentCoreCheck++;
}
@ -313,9 +317,11 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
if (iplocs.length == 1) {
// We have a location in the database for this remote IP
var iploc = nodes[0], x = {};
x.publicip = iploc.ip;
x.iploc = iploc.loc + ',' + (Math.floor((new Date(iploc.date)) / 1000));
ChangeAgentLocationInfo(x);
if ((iploc != null) && (iploc.ip != null) && (iploc.loc != null)) {
x.publicip = iploc.ip;
x.iploc = iploc.loc + ',' + (Math.floor((new Date(iploc.date)) / 1000));
ChangeAgentLocationInfo(x);
}
} else {
// Check if we need to ask for the IP location
var doIpLocation = 0;

View File

@ -11,6 +11,7 @@ function CreateMeshCentralServer() {
obj.redirserver;
obj.mpsserver;
obj.swarmserver;
obj.mailserver;
obj.amtEventHandler;
obj.amtScanner;
obj.meshScanner;
@ -28,14 +29,18 @@ function CreateMeshCentralServer() {
obj.config = {}; // Configuration file
obj.dbconfig = {}; // Persistance values, loaded from database
obj.certificateOperations = require('./certoperations.js').CertificateOperations();
obj.defaultMeshCmd = null;
obj.defaultMeshCore = null;
obj.defaultMeshCoreHash = null;
obj.defaultMeshCoreNoMei = null;
obj.defaultMeshCoreNoMeiHash = null;
obj.meshAgentBinaries = {}; // Mesh Agent Binaries, Architecture type --> { hash:(sha384 hash), size:(binary size), path:(binary path) }
obj.meshAgentInstallScripts = {}; // Mesh Install Scripts, Script ID -- { hash:(sha384 hash), size:(binary size), path:(binary path) }
obj.multiServer = null;
obj.currentVer = null;
obj.maintenanceTimer = null;
obj.serverId = null;
obj.currentVer = null;
try { obj.currentVer = JSON.parse(require('fs').readFileSync(obj.path.join(__dirname, 'package.json'), 'utf8')).version; } catch (e) { } // Fetch server version
// Setup the default configuration and files paths
if ((__dirname.endsWith('/node_modules/meshcentral')) || (__dirname.endsWith('\\node_modules\\meshcentral')) || (__dirname.endsWith('/node_modules/meshcentral/')) || (__dirname.endsWith('\\node_modules\\meshcentral\\'))) {
@ -165,11 +170,9 @@ function CreateMeshCentralServer() {
xprocess.stdout.on('data', function (data) { xprocess.data += data; });
xprocess.stderr.on('data', function (data) { });
xprocess.on('close', function (code) {
var currentVer = null;
try { currentVer = JSON.parse(require('fs').readFileSync(obj.path.join(__dirname, 'package.json'), 'utf8')).version; } catch (e) { }
var latestVer = null;
if (code == 0) { try { latestVer = xprocess.data.split(' ').join('').split('\r').join('').split('\n').join(''); } catch (e) { } }
callback(currentVer, latestVer);
callback(obj.currentVer, latestVer);
});
}
@ -288,8 +291,9 @@ function CreateMeshCentralServer() {
return;
}
// Load the default mesh core
// Load the default meshcore and meshcmd
obj.updateMeshCore();
obj.updateMeshCmd();
// Load server certificates
obj.certificateOperations.GetMeshServerCertificate(obj.datapath, obj.args, function (certs) {
@ -348,6 +352,12 @@ function CreateMeshCentralServer() {
obj.swarmserver = require('./swarmserver.js').CreateSwarmServer(obj, obj.db, obj.args, obj.certificates);
}
// Setup email server
if ((obj.config.smtp != null) && (obj.config.smtp.host != null) && (obj.config.smtp.from != null)) {
obj.mailserver = require('./meshmail.js').CreateMeshMain(obj);
//obj.mailserver.sendMail('ylian.saint-hilaire@intel.com', 'Test Subject', 'This is a sample test', 'This is a <b>sample</b> html test');
}
// Start periodic maintenance
obj.maintenanceTimer = setInterval(obj.maintenanceActions, 1000 * 60 * 60); // Run this every hour
@ -649,29 +659,64 @@ function CreateMeshCentralServer() {
if (obj.fs.existsSync(obj.path.join(meshcorePath, 'meshcore.js')) == false) {
meshcorePath = obj.path.join(__dirname, 'agents');
if (obj.fs.existsSync(obj.path.join(meshcorePath, 'meshcore.js')) == false) {
obj.defaultMeshCore = obj.defaultMeshCoreHash = null; if (func != null) { func(false); } // meshcore.js not found
obj.defaultMeshCoreNoMei = obj.defaultMeshCoreNoMeiHash = obj.defaultMeshCore = obj.defaultMeshCoreHash = null; if (func != null) { func(false); } // meshcore.js not found
}
}
// Read meshcore.js and all .js files in the modules folder.
var moduleAdditions = 'var addedModules = [];', modulesDir = null;
var moduleAdditions = 'var addedModules = [];', moduleAdditionsNoMei = 'var addedModules = [];', modulesDir = null;
var meshCore = obj.fs.readFileSync(obj.path.join(meshcorePath, 'meshcore.js')).toString();
try { modulesDir = obj.fs.readdirSync(obj.path.join(meshcorePath, 'modules')); } catch (e) { }
try { modulesDir = obj.fs.readdirSync(obj.path.join(meshcorePath, 'modules_meshcore')); } catch (e) { }
if (modulesDir != null) {
for (var i in modulesDir) {
if (modulesDir[i].toLowerCase().endsWith('.js')) {
// Merge this module
var moduleName = modulesDir[i].substring(0, modulesDir[i].length - 3);
var moduleDataB64 = obj.fs.readFileSync(obj.path.join(meshcorePath, 'modules', modulesDir[i])).toString('base64');
var moduleDataB64 = obj.fs.readFileSync(obj.path.join(meshcorePath, 'modules_meshcore', modulesDir[i])).toString('base64');
moduleAdditions += 'try { addModule("' + moduleName + '", Buffer.from("' + moduleDataB64 + '", "base64")); addedModules.push("' + moduleName + '"); } catch (e) { }\r\n';
if ((moduleName != 'amt_heci') && (moduleName != 'lme_heci')) {
moduleAdditionsNoMei += 'try { addModule("' + moduleName + '", Buffer.from("' + moduleDataB64 + '", "base64")); addedModules.push("' + moduleName + '"); } catch (e) { }\r\n';
}
}
}
}
// Set the new default meshcore.js with and without MEI support
obj.defaultMeshCore = obj.common.IntToStr(0) + moduleAdditions + meshCore;
obj.defaultMeshCoreHash = obj.crypto.createHash('sha384').update(obj.defaultMeshCore).digest("binary");
obj.defaultMeshCoreNoMei = obj.common.IntToStr(0) + moduleAdditionsNoMei + meshCore;
obj.defaultMeshCoreNoMeiHash = obj.crypto.createHash('sha384').update(obj.defaultMeshCoreNoMei).digest("binary");
if (func != null) { func(true); }
}
// Update the default meshcmd
obj.updateMeshCmd = function (func) {
// Figure out where meshcmd.js is
var meshcmdPath = obj.datapath;
if (obj.fs.existsSync(obj.path.join(meshcmdPath, 'meshcmd.js')) == false) {
meshcmdPath = obj.path.join(__dirname, 'agents');
if (obj.fs.existsSync(obj.path.join(meshcmdPath, 'meshcmd.js')) == false) {
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 + '\'');
try { modulesDir = obj.fs.readdirSync(obj.path.join(meshcmdPath, 'modules_meshcmd')); } catch (e) { }
if (modulesDir != null) {
for (var i in modulesDir) {
if (modulesDir[i].toLowerCase().endsWith('.js')) {
// Merge this module
var moduleName = modulesDir[i].substring(0, modulesDir[i].length - 3);
var moduleDataB64 = obj.fs.readFileSync(obj.path.join(meshcmdPath, 'modules_meshcmd', modulesDir[i])).toString('base64');
moduleAdditions += 'try { addModule("' + moduleName + '", Buffer.from("' + moduleDataB64 + '", "base64")); addedModules.push("' + moduleName + '"); } catch (e) { }\r\n';
}
}
}
// Set the new default meshcore.js
meshCore = obj.common.IntToStr(0) + moduleAdditions + meshCore; // Add the 4 bytes encoding type & flags (Set to 0 for raw)
obj.defaultMeshCore = meshCore;
obj.defaultMeshCoreHash = obj.crypto.createHash('sha384').update(meshCore).digest("binary");
// Set the new default meshcmd.js
obj.defaultMeshCmd = moduleAdditions + meshCmd;
if (func != null) { func(true); }
}
@ -711,31 +756,31 @@ function CreateMeshCentralServer() {
// List of possible mesh agents
var meshAgentsArchitectureNumbers = {
1: { id: 1, localname: 'MeshConsole.exe', rname: 'MeshConsole.exe', desc: 'Windows x86-32 console', update: true },
2: { id: 2, localname: 'MeshConsole64.exe', rname: 'MeshConsole.exe', desc: 'Windows x86-64 console', update: true },
3: { id: 3, localname: 'MeshService.exe', rname: 'MeshAgent.exe', desc: 'Windows x86-32 service', update: true },
4: { id: 4, localname: 'MeshService64.exe', rname: 'MeshAgent.exe', desc: 'Windows x86-64 service', update: true },
5: { id: 5, localname: 'meshagent_x86', rname: 'meshagent', desc: 'Linux x86-32', update: true },
6: { id: 6, localname: 'meshagent_x86-64', rname: 'meshagent', desc: 'Linux x86-64', update: true },
7: { id: 7, localname: 'meshagent_mips', rname: 'meshagent', desc: 'Linux MIPS', update: true },
8: { id: 8, localname: 'MeshAgent-Linux-XEN-x86-32', rname: 'meshagent', desc: 'XEN x86-64', update: true },
9: { id: 9, localname: 'meshagent_arm', rname: 'meshagent', desc: 'Linux ARM5', update: true },
10: { id: 10, localname: 'MeshAgent-Linux-ARM-PlugPC', rname: 'meshagent', desc: 'Linux ARM PlugPC', update: true },
11: { id: 11, localname: 'MeshAgent-OSX-x86-32', rname: 'meshosx', desc: 'Apple OSX x86-32', update: true },
12: { id: 12, localname: 'MeshAgent-Android-x86', rname: 'meshandroid', desc: 'Android x86-32', update: true },
13: { id: 13, localname: 'meshagent_pogo', rname: 'meshagent', desc: 'Linux ARM PogoPlug', update: true },
14: { id: 14, localname: 'MeshAgent-Android-APK', rname: 'meshandroid', desc: 'Android Market', update: false }, // Get this one from Google Play
15: { id: 15, localname: 'meshagent_poky', rname: 'meshagent', desc: 'Linux Poky x86-32', update: true },
16: { id: 16, localname: 'MeshAgent-OSX-x86-64', rname: 'meshosx', desc: 'Apple OSX x86-64', update: true },
17: { id: 17, localname: 'MeshAgent-ChromeOS', rname: 'meshchrome', desc: 'Google ChromeOS', update: false }, // Get this one from Chrome store
18: { id: 18, localname: 'meshagent_poky64', rname: 'meshagent', desc: 'Linux Poky x86-64', update: true },
19: { id: 19, localname: 'meshagent_x86_nokvm', rname: 'meshagent', desc: 'Linux x86-32 NoKVM', update: true },
20: { id: 20, localname: 'meshagent_x86-64_nokvm', rname: 'meshagent', desc: 'Linux x86-64 NoKVM', update: true },
21: { id: 21, localname: 'MeshAgent-WinMinCore-Console-x86-32.exe', rname: 'MeshAgent.exe', desc: 'Windows MinCore Console x86-32', update: true },
22: { id: 22, localname: 'MeshAgent-WinMinCore-Service-x86-64.exe', rname: 'MeshAgent.exe', desc: 'Windows MinCore Service x86-32', update: true },
23: { id: 23, localname: 'MeshAgent-NodeJS', rname: 'meshagent', desc: 'NodeJS', update: false }, // Get this one from NPM
24: { id: 24, localname: 'meshagent_arm-linaro', rname: 'meshagent', desc: 'Linux ARM Linaro', update: true },
25: { id: 25, localname: 'meshagent_pi', rname: 'meshagent', desc: 'Linux ARM - Raspberry Pi', update: true } // "armv6l" and "armv7l"
1: { id: 1, localname: 'MeshConsole.exe', rname: 'meshconsole.exe', desc: 'Windows x86-32 console', update: true, amt: true },
2: { id: 2, localname: 'MeshConsole64.exe', rname: 'meshconsole.exe', desc: 'Windows x86-64 console', update: true, amt: true },
3: { id: 3, localname: 'MeshService.exe', rname: 'meshagent.exe', desc: 'Windows x86-32 service', update: true, amt: true },
4: { id: 4, localname: 'MeshService64.exe', rname: 'meshagent.exe', desc: 'Windows x86-64 service', update: true, amt: true },
5: { id: 5, localname: 'meshagent_x86', rname: 'meshagent', desc: 'Linux x86-32', update: true, amt: true },
6: { id: 6, localname: 'meshagent_x86-64', rname: 'meshagent', desc: 'Linux x86-64', update: true, amt: true },
7: { id: 7, localname: 'meshagent_mips', rname: 'meshagent', desc: 'Linux MIPS', update: true, amt: false },
8: { id: 8, localname: 'MeshAgent-Linux-XEN-x86-32', rname: 'meshagent', desc: 'XEN x86-64', update: true, amt: false },
9: { id: 9, localname: 'meshagent_arm', rname: 'meshagent', desc: 'Linux ARM5', update: true, amt: false },
10: { id: 10, localname: 'MeshAgent-Linux-ARM-PlugPC', rname: 'meshagent', desc: 'Linux ARM PlugPC', update: true, amt: false },
11: { id: 11, localname: 'MeshAgent-OSX-x86-32', rname: 'meshosx', desc: 'Apple OSX x86-32', update: true, amt: false },
12: { id: 12, localname: 'MeshAgent-Android-x86', rname: 'meshandroid', desc: 'Android x86-32', update: true, amt: false },
13: { id: 13, localname: 'meshagent_pogo', rname: 'meshagent', desc: 'Linux ARM PogoPlug', update: true, amt: false },
14: { id: 14, localname: 'MeshAgent-Android-APK', rname: 'meshandroid', desc: 'Android Market', update: false, amt: false }, // Get this one from Google Play
15: { id: 15, localname: 'meshagent_poky', rname: 'meshagent', desc: 'Linux Poky x86-32', update: true, amt: false },
16: { id: 16, localname: 'MeshAgent-OSX-x86-64', rname: 'meshagent', desc: 'Apple OSX x86-64', update: true, amt: false },
17: { id: 17, localname: 'MeshAgent-ChromeOS', rname: 'meshagent', desc: 'Google ChromeOS', update: false, amt: false }, // Get this one from Chrome store
18: { id: 18, localname: 'meshagent_poky64', rname: 'meshagent', desc: 'Linux Poky x86-64', update: true, amt: false },
19: { id: 19, localname: 'meshagent_x86_nokvm', rname: 'meshagent', desc: 'Linux x86-32 NoKVM', update: true, amt: true },
20: { id: 20, localname: 'meshagent_x86-64_nokvm', rname: 'meshagent', desc: 'Linux x86-64 NoKVM', update: true, amt: true },
21: { id: 21, localname: 'MeshAgent-WinMinCore-Console-x86-32.exe', rname: 'meshagent.exe', desc: 'Windows MinCore Console x86-32', update: true, amt: false },
22: { id: 22, localname: 'MeshAgent-WinMinCore-Service-x86-64.exe', rname: 'meshagent.exe', desc: 'Windows MinCore Service x86-32', update: true, amt: false },
23: { id: 23, localname: 'MeshAgent-NodeJS', rname: 'meshagent', desc: 'NodeJS', update: false, amt: false }, // Get this one from NPM
24: { id: 24, localname: 'meshagent_arm-linaro', rname: 'meshagent', desc: 'Linux ARM Linaro', update: true, amt: false },
25: { id: 25, localname: 'meshagent_pi', rname: 'meshagent', desc: 'Linux ARM - Raspberry Pi', update: true, amt: false } // "armv6l" and "armv7l"
};
// Update the list of available mesh agents
@ -835,7 +880,7 @@ function InstallModule(modulename, func, tag1, tag2) {
process.on('SIGINT', function () { if (meshserver != null) { meshserver.Stop(); meshserver = null; } console.log('Server Ctrl-C exit...'); process.exit(); });
// Build the list of required modules
var modules = ['nedb', 'https', 'unzip', 'xmldom', 'express', 'mongojs', 'archiver', 'minimist', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-session', 'express-handlebars'];
var modules = ['nedb', 'https', 'unzip', 'xmldom', 'express', 'mongojs', 'archiver', 'minimist', 'nodemailer', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-session', 'express-handlebars'];
if (require('os').platform() == 'win32') { modules.push("node-windows"); }
// Run as a command line, if we are not using service arguments, don't need to install the service package.

83
meshmail.js Normal file
View File

@ -0,0 +1,83 @@
/**
* @description Meshcentral MeshMail
* @author Ylian Saint-Hilaire
* @version v0.0.1
*/
// Construct a MeshAgent object, called upon connection
module.exports.CreateMeshMain = function (parent) {
var obj = {};
obj.pendingMails = [];
obj.parent = parent;
obj.retry = 0;
obj.sendingMail = false;
const nodemailer = require('nodemailer');
// Default account email validation mail
var accountCheckSubject = '[[[SERVERNAME]]] - Email Verification';
var accountCheckMailHtml = '<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b></td></tr></table><p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting email verification, click on the following link to complete the process.</p><p style="margin-left:30px"><a href="[[[CALLBACKURL]]]">Click here to verify your e-mail address.</a></p>If you did not initiate this request, please ignore this mail.</div>';
var accountCheckMailText = '[[[SERVERNAME]]] - Verification\r\n\r\nHi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is performing an e-mail verification. Nagivate to the following link to complete the process: [[[CALLBACKURL]]]\r\nIf you did not initiate this request, please ignore this mail.\r\n';
// Default account reset mail
var accountResetSubject = '[[[SERVERNAME]]] - Account Reset';
var accountResetMailHtml = '<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b></td></tr></table><p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting an account password reset, click on the following link to complete the process.</p><p style="margin-left:30px"><a href="[[[CALLBACKURL]]]">Click here to reset your account password.</a></p>If you did not initiate this request, please ignore this mail.</div>';
var accountResetMailText = '[[[SERVERNAME]]] - Account Reset\r\n\r\nHi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is requesting an account password reset. Nagivate to the following link to complete the process: [[[CALLBACKURL]]]\r\nIf you did not initiate this request, please ignore this mail.\r\n';
// Setup mail server
var options = { host: parent.config.smtp.host, secure: false, tls: { rejectUnauthorized: false } };
if (parent.config.smtp.port != null) { options.port = parent.config.smtp.port; }
obj.smtpServer = nodemailer.createTransport(options);
// Perform all e-mail substitution
function mailReplacements(text, domain, username, email, cookie) {
var url = 'http' + ((obj.parent.args.notls == null) ? 's' : '') + '://' + parent.certificates.CommonName + ':' + obj.parent.args.port + domain.url;
if (cookie != null) { text = text.split('[[[CALLBACKURL]]]').join(url + 'checkmail?c=' + cookie) }
return text.split('[[[USERNAME]]]').join(username).split('[[[SERVERURL]]]').join(url).split('[[[SERVERNAME]]]').join(domain.title);
}
// Send a mail
obj.sendMail = function (to, subject, text, html) {
obj.pendingMails.push({ to: to, from: parent.config.smtp.from, subject: subject, text: text, html: html });
sendNextMail();
}
// Send account check mail
obj.sendAccountCheckMail = function (domain, username, email) {
if ((parent.certificates == null) || (parent.certificates.CommonName == null)) return; // If the server name is not set, no reset possible.
var cookie = obj.parent.webserver.encodeCookie({ u: domain.id + '/' + username, e: email, a: 1 });
obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(accountCheckSubject, domain, username, email), text: mailReplacements(accountCheckMailText, domain, username, email, cookie), html: mailReplacements(accountCheckMailHtml, domain, username, email, cookie) });
sendNextMail();
}
// Send account reset mail
obj.sendAccountResetMail = function (domain, username, email) {
if ((parent.certificates == null) || (parent.certificates.CommonName == null)) return; // If the server name is not set, don't validate the email address.
var cookie = obj.parent.webserver.encodeCookie({ u: domain.id + '/' + username, e: email, a: 2 });
obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(accountResetSubject, domain, username, email), text: mailReplacements(accountResetMailText, domain, username, email, cookie), html: mailReplacements(accountResetMailHtml, domain, username, email, cookie) });
sendNextMail();
}
// Send out the next mail in the pending list
function sendNextMail() {
if ((obj.sendingMail == true) || (obj.pendingMails.length == 0)) { return; }
var mailToSend = obj.pendingMails[0];
obj.sendingMail = true;
//console.log('SMTP sending mail to ' + mailToSend.to + '.');
obj.smtpServer.sendMail(mailToSend, function (err, info) {
//console.log(JSON.stringify(err), JSON.stringify(info));
obj.sendingMail = false;
if (err == null) {
obj.pendingMails.shift();
obj.retry = 0;
sendNextMail(); // Send the next mail
} else {
obj.retry++;
//console.log('SMTP server failed, will try again in a minute (' + obj.retry + ').');
setTimeout(sendNextMail, 60000); // Wait and try again
}
});
}
return obj;
}

View File

@ -92,10 +92,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if ((state.connectivity & 1) != 0) { var agent = obj.parent.wsagents[docs[i]._id]; if (agent != null) { docs[i].agct = agent.connectTime; } }
if ((state.connectivity & 2) != 0) { var cira = obj.parent.parent.mpsserver.ciraConnections[docs[i]._id]; if (cira != null) { docs[i].cict = cira.tag.connectTime; } }
}
// Compress the meshid's
var meshid = docs[i].meshid;
if (!r[meshid]) { r[meshid] = []; }
delete docs[i].meshid;
// Remove Intel AMT credential if present
if (docs[i].intelamt != null && docs[i].intelamt.pass != null) { delete docs[i].intelamt.pass; }
r[meshid].push(docs[i]);
}
ws.send(JSON.stringify({ action: 'nodes', nodes: r }));
@ -233,10 +238,66 @@ 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) { if ((obj.parent.users[i].domain == domain.id) && (obj.parent.users[i].name != '~')) { docs.push(obj.parent.users[i]); } }
for (var 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;
delete userinfo.passhint;
delete userinfo.salt;
delete userinfo.type;
delete userinfo.domain;
delete userinfo.subscriptions;
delete userinfo.passtype;
docs.push(userinfo);
}
}
ws.send(JSON.stringify({ action: 'users', users: docs }));
break;
}
case 'changeemail':
{
// Change the email address
if ((command.email != null) && (typeof command.email == 'string') && (command.email.length < 1024)) {
var x = command.email.split('@');
if ((x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2)) {
if (obj.parent.users[req.session.userid].email != command.email) {
// Update the user's email
var oldemail = user.email;
user.email = command.email;
user.emailVerified = false;
obj.parent.db.SetUser(user);
// Event the change
var userinfo = obj.common.Clone(user);
delete userinfo.hash;
delete userinfo.passhint;
delete userinfo.salt;
delete userinfo.type;
delete userinfo.domain;
delete userinfo.subscriptions;
delete userinfo.passtype;
obj.parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Changed email of user ' + userinfo.name + ' from ' + oldemail + ' to ' + user.email, domain: domain.id })
}
}
}
break;
}
case 'verifyemail':
{
// Send a account email verification email
if ((command.email != null) && (typeof command.email == 'string') && (command.email.length < 1024)) {
var x = command.email.split('@');
if ((x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2)) {
if (obj.parent.users[req.session.userid].email == command.email) {
// Send the verification email
if (obj.parent.parent.mailserver != null) {
obj.parent.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email);
}
}
}
}
break;
}
case 'wssessioncount':
{
// Request a list of all web socket user session count
@ -307,12 +368,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if (command.quota != chguser.quota) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; }
if ((user.siteadmin == 0xFFFFFFFF) && (command.siteadmin != null) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1 }
if (change == 1) {
obj.db.Set(chguser);
obj.db.SetUser(chguser);
obj.parent.parent.DispatchEvent([chguser._id], obj, 'resubscribe');
var chguser2 = obj.common.Clone(chguser);
delete chguser2.salt;
delete chguser2.hash;
obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: chguser2, action: 'accountchange', msg: 'Account changed: ' + command.name, domain: domain.id })
var userinfo = obj.common.Clone(chguser);
delete userinfo.hash;
delete userinfo.passhint;
delete userinfo.salt;
delete userinfo.type;
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 })
}
}
}
@ -375,7 +441,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
for (var j in links) {
var xuser = obj.parent.users[j];
delete xuser.links[meshes[i]._id];
obj.db.Set(xuser);
obj.db.SetUser(xuser);
obj.parent.parent.DispatchEvent([xuser._id], obj, 'resubscribe');
}
}
@ -426,7 +492,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Add mesh to user
if (newuser.links == null) newuser.links = {};
newuser.links[command.meshid] = { rights: command.meshadmin };
obj.db.Set(newuser);
obj.db.SetUser(newuser);
obj.parent.parent.DispatchEvent([newuser._id], obj, 'resubscribe');
// Add a user to the mesh
@ -819,18 +885,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if (obj.parent.parent.multiServer != null) { obj.parent.parent.multiServer.DispatchMessage({ action: 'sessionEnd', sessionid: ws.sessionId }); }
});
// Send server information
if (obj.args.notls == true) {
ws.send(JSON.stringify({ action: 'serverinfo', serverinfo: { name: obj.parent.certificates.CommonName, mpsport: obj.args.mpsport, mpspass: obj.args.mpspass, port: obj.args.port, https: false, emailcheck: obj.parent.parent.mailserver != null } }));
} else {
ws.send(JSON.stringify({ action: 'serverinfo', serverinfo: { name: obj.parent.certificates.CommonName, mpsport: obj.args.mpsport, mpspass: obj.args.mpspass, redirport: obj.args.redirport, port: obj.args.port, https: true, emailcheck: obj.parent.parent.mailserver != null } }));
}
// Send user information to web socket, this is the first thing we send
var userinfo = obj.common.Clone(obj.parent.users[req.session.userid]);
delete userinfo.salt;
delete userinfo.hash;
ws.send(JSON.stringify({ action: 'userinfo', userinfo: userinfo }));
// Next, send server information
if (obj.args.notls == true) {
ws.send(JSON.stringify({ action: 'serverinfo', serverinfo: { name: obj.parent.certificates.CommonName, mpsport: obj.args.mpsport, mpspass: obj.args.mpspass, port: obj.args.port, https: false } }));
} else {
ws.send(JSON.stringify({ action: 'serverinfo', serverinfo: { name: obj.parent.certificates.CommonName, mpsport: obj.args.mpsport, mpspass: obj.args.mpspass, redirport: obj.args.redirport, port: obj.args.port, https: true } }));
}
} catch (e) { console.log(e); }
// Read the folder and all sub-folders and serialize that into json.

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.1.0-l",
"version": "0.1.0-m",
"keywords": [
"Remote Management",
"Intel AMT",
@ -40,6 +40,7 @@
"nedb": "^1.8.0",
"node-forge": "^0.6.49",
"node-windows": "^0.1.14",
"nodemailer": "^4.4.1",
"unzip": "^0.1.11",
"ws": "^3.2.0",
"xmldom": "^0.1.27"

View File

@ -376,6 +376,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
obj.mousedown = function (e) { return obj.xxMouseDown(e); }
obj.mouseup = function (e) { return obj.xxMouseUp(e); }
obj.mousemove = function (e) { return obj.xxMouseMove(e); }
obj.mousewheel = function (e) { return obj.xxMouseWheel(e); }
obj.xxMsTouchEvent = function (evt) {
if (evt.originalEvent.pointerType == 4) return; // If this is a mouse pointer, ignore this event. Touch & pen are ok.

View File

@ -630,6 +630,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) {
return obj.haltEvent(e);
}
obj.mousewheel = function (e) { }
obj.getPositionOfControl = function (Control) {
var Position = Array(2);

View File

@ -150,12 +150,12 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
// 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;
if (socket.tag.accumulator.substring(0, 3) == 'GET') { console.log("Swarm Connection, HTTP GET detected: " + socket.remoteAddress); socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>MeshCentral2 legacy swarm server.<br />MeshCentral1 mesh agents should connect here for updates.</body></html>'); socket.end(); return; }
if (socket.tag.accumulator.substring(0, 3) == 'GET') { /*console.log("Swarm Connection, HTTP GET detected: " + socket.remoteAddress);*/ socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>MeshCentral2 legacy swarm server.<br />MeshCentral1 mesh agents should connect here for updates.</body></html>'); socket.end(); return; }
socket.tag.first = false;
}
// A client certificate is required
if (!socket.tag.clientCert.subject) { console.log("Swarm Connection, no client cert: " + socket.remoteAddress); socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nMeshCentral2 legacy swarm server.\r\nNo client certificate given.'); socket.end(); return; }
if (!socket.tag.clientCert.subject) { /*console.log("Swarm Connection, no client cert: " + socket.remoteAddress);*/ socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nMeshCentral2 legacy swarm server.\r\nNo client certificate given.'); socket.end(); return; }
try {
// Parse all of the APF data we can

View File

@ -167,6 +167,8 @@
<div id="p2AccountActions">
<p><strong><img alt="" width=150 height=103 src=images/mainaccount.png style=margin-bottom:10px;margin-right:20px;float:right />Account actions</strong></p>
<p style="margin-left:40px">
<a onclick="account_showChangeEmail()" style="cursor:pointer">Change email address</a><br />
<span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()" style="cursor:pointer">Verify email</a><br /></span>
<a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a><br />
<a onclick="account_showDeleteAccount()" style="cursor:pointer">Delete account</a><br />
</p>
@ -686,6 +688,22 @@
if (t != null) { desktopsettings = JSON.parse(t); }
applyDesktopSettings();
// Mouse Scroll on desktop
if (navigator.userAgent.match(/mozilla/i)) {
Q('Desk').addEventListener('DOMMouseScroll', function (e) {
return dmousewheel(e);
//if (loginState == 3) { SendMouseMsg(KeyAction.SCROLL, e); return false; }
//return true;
});
}
else {
Q('Desk').addEventListener('mousewheel', function (e) {
return dmousewheel(e);
//if (loginState == 3) { SendMouseMsg(KeyAction.SCROLL, e); return false; }
//return true;
});
}
// Terminal special keys
var x = '';
for (var c = 1; c < 27; c++) x += "<option value='" + c + "'>Ctrl-" + String.fromCharCode(64 + c) + " (" + c + ")</option>";
@ -760,13 +778,14 @@
function onMessage(server, message) {
switch (message.action) {
case 'serverinfo': {
serverinfo = message.serverinfo;
break;
}
case 'userinfo': {
userinfo = message.userinfo;
updateSiteAdmin();
break;
}
case 'serverinfo': {
serverinfo = message.serverinfo;
QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
break;
}
case 'users': {
@ -910,6 +929,7 @@
if ((message.event.account.quota != userinfo.quota) || (((userinfo.siteadmin & 8) == 0) && ((message.event.account.siteadmin & 8) != 0))) { meshserver.Send({ action: 'files' }); }
userinfo = message.event.account;
if (oldsiteadmin != newsiteadmin) updateSiteAdmin();
QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
}
if (users == null) break;
users[message.event.account._id] = message.event.account;
@ -2971,6 +2991,7 @@
function dmousedown(e) { if (!xxdialogMode && desktop != undefined) desktop.m.mousedown(e) }
function dmouseup(e) { if (!xxdialogMode && desktop != undefined) desktop.m.mouseup(e) }
function dmousemove(e) { if (!xxdialogMode && desktop != undefined) desktop.m.mousemove(e) }
function dmousewheel(e) { if (!xxdialogMode && desktop != undefined) { desktop.m.mousewheel(e); haltEvent(e); return true; } return false; }
function drotate(x) { if (!xxdialogMode && desktop != undefined) { desktop.m.setRotation(desktop.m.rotation + x); deskAdjust(); deskAdjust(); } }
//
@ -3646,6 +3667,34 @@
// MY ACCOUNT
//
function account_showVerifyEmail() {
if (xxdialogMode || (userinfo.emailVerified == true) || (serverinfo.emailcheck != true)) return;
var x = "Click ok to send a verification mail to:<br /><div style=padding:8px><b>" + EscapeHtml(userinfo.email) + "</b></div>Please wait a few minute to receive the verification.";
setDialogMode(2, "Email Verification", 3, account_showVerifyEmailEx, x);
}
function account_showVerifyEmailEx() {
meshserver.Send({ action: 'verifyemail', email: userinfo.email });
}
function account_showChangeEmail() {
if (xxdialogMode) return;
var x = "Change your account e-mail address here.<br /><br />";
x += addHtmlValue('Email', '<input id=dp2email style=width:230px maxlength=32 onchange=account_validateEmail() onkeyup=account_validateEmail() />');
setDialogMode(2, "Email Address Change", 3, account_changeEmail, x);
if (userinfo.email != null) { Q('dp2email').value = userinfo.email; }
account_validateEmail();
}
function account_validateEmail() {
var x = Q('dp2email').value.split('@');
QE('idx_dlgOkButton', (x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2) && (Q('dp2email').value.length < 1024) && (Q('dp2email').value != userinfo.email));
}
function account_changeEmail() {
meshserver.Send({ action: 'changeemail', email: Q('dp2email').value });
}
function account_showDeleteAccount() {
if (xxdialogMode) return;
var x = "To delete this account, type in the account password in both boxes below and hit ok.<br /><br />";
@ -4229,7 +4278,7 @@
if ((user.quota != undefined) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
if (user.name != userinfo.name) { msg += "</a>"; }
if (user.email != null) {
msg = '<table style=width:100%><tr><td>' + EscapeHtml(user.name) + ', <a onclick=doemail(event,\"' + user.email + '\")>' + user.email + '</a><td align=right>' + msg + '</table>';
msg = '<table style=width:100%><tr><td>' + EscapeHtml(user.name) + ', <a onclick=doemail(event,\"' + user.email + '\")>' + user.email + '</a>' + (((serverinfo.emailcheck == true) && (user.emailVerified != true))?' (unverified)':'') + '<td align=right>' + msg + '</table>';
} else {
msg = '<table style=width:100%><tr><td>' + EscapeHtml(user.name) + '<td align=right>' + msg + '</table>';
}

View File

@ -53,8 +53,12 @@
<td align=right><input id=loginButton type=submit value="Log In" disabled="disabled" /></td>
</tr>
</table>
<div id="newAccountDiv" style="display:none">
<hr />Don&#39;t have an account? <a onclick=xgo(2) style=cursor:pointer>Create one</a>.
<div id="hrAccountDiv" style="display:none"><hr /></div>
<div id="resetAccountDiv" style="display:none;padding:2px">
Forgot username/password? <a onclick=xgo(3) style=cursor:pointer>Reset account</a>.
</div>
<div id="newAccountDiv" style="display:none;padding:2px">
Don&#39;t have an account? <a onclick=xgo(2) style=cursor:pointer>Create one</a>.
</div>
</form>
</div>
@ -101,6 +105,29 @@
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
</form>
</div>
<div id=resetpanel style="background-color: #979797;border-radius:16px;width:300px;padding:16px;text-align:center;display:none">
<form action=resetaccount method=post>
<div id=message3>
{{{message}}}
</div>
<div>
<b>Account Reset</b>
</div>
<table>
<tr>
<td align=right width=100>Email:</td>
<td><input id=remail type=text name=email onchange=validateReset() onkeyup=validateReset() /></td>
</tr>
<tr>
<td colspan=2>
<div style=float:right><input id=eresetButton type=submit value="Reset Account" disabled="disabled" /></div>
<div id=passWarning style="padding-top:6px"></div>
</td>
</tr>
</table>
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
</form>
</div>
</td>
</tr>
</table>
@ -141,6 +168,7 @@
<script>
var passhint = "{{{passhint}}}";
var newAccountPass = {{{newAccountPass}}};
var emailCheck = {{{emailcheck}}};
function startup() {
window.onresize = center;
@ -151,6 +179,8 @@
QV('newAccountDiv', '{{{newAccount}}}' != '0' );
if ((passhint != null) && (passhint.length > 0)) { QV("showPassHintLink", true); }
QV("newAccountPass", (newAccountPass == 1));
QV("resetAccountDiv", (emailCheck == true));
QV("hrAccountDiv", (emailCheck == true) || (newAccountPass == 1))
}
function showPassHint() {
@ -168,6 +198,7 @@
QV("showPassHintLink", false);
QV('loginpanel', x == 1);
QV('createpanel', x == 2);
QV('resetpanel', x == 3);
}
function validateLogin() {
@ -178,7 +209,7 @@
function validateCreate() {
setDialogMode(0);
var ok = (Q('ausername').value.length > 0 && Q('aemail').value.length > 0 && Q('apassword1').value.length > 0 && (Q('apassword2').value == Q('apassword1').value));
var ok = ((Q('ausername').value.length > 0) && (checkEmail(Q('aemail').value) == true) && (Q('apassword1').value.length > 0) && (Q('apassword2').value == Q('apassword1').value));
if ((newAccountPass == 1) && (Q('anewaccountpass').value.length == 0)) { ok = false; }
QE('createButton', ok);
if (Q('apassword1').value == '') {
@ -191,6 +222,19 @@
}
}
function validateReset() {
setDialogMode(0);
QE('eresetButton', checkEmail(Q('remail').value));
}
// Return true is the input string looks like an email address
function checkEmail(str) {
var x = str.split('@');
var ok = ((x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2));
if (ok == true) { var y = x[1].split('.'); for (var i in y) { if (y[i].length == 0) { ok = false; } } }
return ok;
}
// Return a password strength score
function checkPasswordStrength(password) {
var r = 0, letters = {}, varCount = 0, variations = { digits: /\d/.test(password), lower: /[a-z]/.test(password), upper: /[A-Z]/.test(password), nonWords: /\W/.test(password) }

42
views/message.handlebars Normal file
View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv=X-UA-Compatible content="IE=edge" />
<meta content="text/html;charset=utf-8" http-equiv=Content-Type />
<meta name=viewport content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
<meta name=apple-mobile-web-app-capable content=yes />
<meta name=format-detection content="telephone=no" />
<link type=text/css href="/styles/style.css" media="screen" rel="stylesheet" title="CSS" />
<title>MeshCentral - {{{title3}}}</title>
</head>
<body>
<div id=container style=max-height:100vh>
<div id=mastheadx></div>
<div id=masthead style="background:url(/images/logoback.png) 0px 0px;background-color:#036;background-repeat:no-repeat;height:66px;width:100%;overflow:hidden">
<div style=float:left;height:66px;color:#c8c8c8;padding-left:20px;padding-top:8px>
<strong><font style="font-size:46px;font-family:Arial,Helvetica,sans-serif">{{{title}}}</font></strong>
</div>
<div style=float:left;height:66px;color:#c8c8c8;padding-left:5px;padding-top:14px>
<strong><font style="font-size:14px;font-family:Arial,Helvetica,sans-serif">{{{title2}}}</font></strong>
</div>
</div>
<div id=page_content style=max-height:calc(100vh-138px)>
<div id=column_l>
<h1>{{{title3}}}</h1>
<p style=margin-left:20px>{{{message}}}</p>
<br />
</div>
<div id=footer>
<table cellpadding=0 cellspacing=10 style=width:100%>
<tr>
<td style=text-align:left></td>
<td style=text-align:right>
<a href=terms>Terms &amp; Privacy</a>
</td>
</tr>
</table>
</div>
</div>
</div>
</body>
</html>

View File

@ -326,40 +326,179 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';;
res.redirect(domain.url);
} else {
// Check if there is domain.newAccountToken, check if supplied token is valid
if ((domain.newaccountspass != null) && (domain.newaccountspass != '') && (req.body.anewaccountpass != domain.newaccountspass)) {
req.session.loginmode = 2;
req.session.error = '<b style=color:#8C001A>Invalid account creation token.</b>';
res.redirect(domain.url);
return;
}
// Check if user exists
if (obj.users['user/' + domain.id + '/' + req.body.username.toLowerCase()]) {
req.session.loginmode = 2;
req.session.error = '<b style=color:#8C001A>Username already exists.</b>';
} else {
var hint = req.body.apasswordhint;
if (hint.length > 250) hint = hint.substring(0, 250);
var user = { type: 'user', _id: 'user/' + domain.id + '/' + req.body.username.toLowerCase(), name: req.body.username, email: req.body.email, creation: Date.now(), login: Date.now(), domain: domain.id, passhint: hint };
var usercount = 0;
for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } }
if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newaccounts == 2) { domain.newaccounts = 0; } } // If this is the first user, give the account site admin.
obj.users[user._id] = user;
req.session.userid = user._id;
req.session.domainid = domain.id;
// Create a user, generate a salt and hash the password
require('./pass').hash(req.body.password1, function (err, salt, hash) {
if (err) throw err;
user.salt = salt;
user.hash = hash;
obj.db.SetUser(user);
});
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);
// 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>';;
res.redirect(domain.url);
} else {
// Check if there is domain.newAccountToken, check if supplied token is valid
if ((domain.newaccountspass != null) && (domain.newaccountspass != '') && (req.body.anewaccountpass != domain.newaccountspass)) {
req.session.loginmode = 2;
req.session.error = '<b style=color:#8C001A>Invalid account creation token.</b>';
res.redirect(domain.url);
return;
}
// Check if user exists
if (obj.users['user/' + domain.id + '/' + req.body.username.toLowerCase()]) {
req.session.loginmode = 2;
req.session.error = '<b style=color:#8C001A>Username already exists.</b>';
} else {
var hint = req.body.apasswordhint;
if (hint.length > 250) hint = hint.substring(0, 250);
var user = { type: 'user', _id: 'user/' + domain.id + '/' + req.body.username.toLowerCase(), name: req.body.username, email: req.body.email, creation: Date.now(), login: Date.now(), domain: domain.id, passhint: hint };
var usercount = 0;
for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } }
if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newaccounts == 2) { domain.newaccounts = 0; } } // If this is the first user, give the account site admin.
obj.users[user._id] = user;
req.session.userid = user._id;
req.session.domainid = domain.id;
// Create a user, generate a salt and hash the password
require('./pass').hash(req.body.password1, function (err, salt, hash) {
if (err) throw err;
user.salt = salt;
user.hash = hash;
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 })
}
res.redirect(domain.url);
}
});
}
}
// Called to process an account reset request
function handleResetAccountRequest(req, res) {
var domain = checkUserIpAddress(req, res);
if (domain == null) return;
if (domain.newaccounts == 0) { res.sendStatus(401); return; }
if (!req.body.email || checkEmail(req.body.email) == false) {
req.session.loginmode = 3;
req.session.error = '<b style=color:#8C001A>Invalid email.</b>';
res.redirect(domain.url);
} else {
obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) {
if (docs.length == 0) {
req.session.loginmode = 3;
req.session.error = '<b style=color:#8C001A>Account not found.</b>';
res.redirect(domain.url);
} else {
var userFound = docs[0];
if (obj.parent.mailserver != null) {
obj.parent.mailserver.sendAccountResetMail(domain, userFound.name, userFound.email);
req.session.loginmode = 1;
req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>';
res.redirect(domain.url);
} else {
req.session.loginmode = 3;
req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>';
res.redirect(domain.url);
}
}
});
}
}
// Called to process a web based email verification request
function handleCheckMailRequest(req, res) {
var domain = checkUserIpAddress(req, res);
if (domain == null) return;
if (req.query.c != null) {
var cookie = obj.decodeCookie(req.query.c, null, 30);
if ((cookie != null) && (cookie.u != null) && (cookie.e != null)) {
var idsplit = cookie.u.split('/');
if ((idsplit.length != 2) || (idsplit[0] != domain.id)) {
res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'ERROR: Invalid domain. <a href="' + domain.url + '">Go to login page</a>.' });
} 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>.' });
} else {
var user = docs[0];
if (user.email != cookie.e) {
res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'ERROR: Invalid e-mail \"' + EscapeHtml(user.email) + '\" for user \"' + EscapeHtml(user.name) + '\". <a href="' + domain.url + '">Go to login page</a>.' });
} else {
if (cookie.a == 1) {
// Account email verification
if (user.emailVerified == true) {
res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'E-mail \"' + EscapeHtml(user.email) + '\" for user \"' + EscapeHtml(user.name) + '\" already verified. <a href="' + domain.url + '">Go to login page</a>.' });
} else {
obj.db.GetUserWithVerifiedEmail(domain.id, user.email, 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: 'E-mail \"' + EscapeHtml(user.email) + '\" already in use on a different account. Change the email address and try again. <a href="' + domain.url + '">Go to login page</a>.' });
} else {
// Set the verified flag
obj.users[user._id].emailVerified = true;
user.emailVerified = true;
obj.db.SetUser(user);
// Event the change
var userinfo = obj.common.Clone(user);
delete userinfo.hash;
delete userinfo.passhint;
delete userinfo.salt;
delete userinfo.type;
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) + ' (' + 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 e-mail \"' + EscapeHtml(user.email) + '\" for user \"' + EscapeHtml(user.name) + '\". <a href="' + domain.url + '">Go to login page</a>.' });
}
});
}
} else if (cookie.a == 2) {
// Account reset
if (user.emailVerified != true) {
res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'E-mail \"' + EscapeHtml(user.email) + '\" for user \"' + EscapeHtml(user.name) + '\" not verified. <a href="' + domain.url + '">Go to login page</a>.' });
} else {
// Set a temporary password
obj.crypto.randomBytes(16, function (err, buf) {
var newpass = buf.toString('base64').split('=').join('').split('/').join('');
require('./pass').hash(newpass, function (err, salt, hash) {
if (err) throw err;
// Change the password
var userinfo = obj.users[user._id];
userinfo.salt = salt;
userinfo.hash = hash;
userinfo.passchange = Date.now();
userinfo.passhint = null;
obj.db.SetUser(userinfo);
// Event the change
var userinfo = obj.common.Clone(userinfo);
delete userinfo.hash;
delete userinfo.passhint;
delete userinfo.salt;
delete userinfo.type;
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 })
// Send the new password
res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'Password for \"' + EscapeHtml(user.name) + '\" has been reset to \"<b>' + EscapeHtml(newpass) + '</b>\", login and go to the \"My Account\" tab to update your password. <a href="' + domain.url + '">Go to login page</a>.' });
});
});
}
} else {
res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'ERROR: Invalid account check. <a href="' + domain.url + '">Go to login page</a>.' });
}
}
}
});
}
} else {
res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'ERROR: Invalid account check, verification url is only valid for 30 minutes. <a href="' + domain.url + '">Go to login page</a>.' });
}
}
}
function handleDeleteAccountRequest(req, res) {
var domain = checkUserIpAddress(req, res);
if (domain == null) return;
@ -462,7 +601,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
res.render(obj.path.join(__dirname, 'views/default'), { viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.certificates.CommonName, serverRedirPort: args.redirport, serverPublicPort: args.port, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, mpspass: args.mpspass, webcerthash: obj.webCertificateHashBase64 });
} else {
// Send back the login application
res.render(obj.path.join(__dirname, 'views/login'), { loginmode: req.session.loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == ''))?0:1), serverDnsName: obj.certificates.CommonName, serverPublicPort: obj.args.port });
res.render(obj.path.join(__dirname, 'views/login'), { loginmode: req.session.loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.certificates.CommonName, serverPublicPort: obj.args.port, emailcheck: obj.parent.mailserver != null });
}
}
@ -1184,19 +1323,16 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
} else if (req.query.meshcmd != null) {
// Send meshcmd for a specific platform back
var argentInfo = obj.parent.meshAgentBinaries[req.query.meshcmd];
if (argentInfo == null) { res.sendStatus(404); return; }
if ((argentInfo == null) || (obj.parent.defaultMeshCmd == null)) { res.sendStatus(404); return; }
// Load the agent
obj.fs.readFile(argentInfo.path, function (err, agentexe) {
if (err != null) { res.sendStatus(404); return; }
// Load meshcmd.js
obj.fs.readFile(obj.path.join(__dirname, 'agents', 'meshcmd.js'), function (err, meshcmdjs) {
if (err != null) { res.sendStatus(404); return; }
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' : '') });
var tail = new Buffer(8);
tail.writeInt32BE(meshcmdjs.length, 0);
tail.writeInt32BE(agentexe.length + meshcmdjs.length + 8, 4);
res.send(Buffer.concat([agentexe, meshcmdjs, tail]));
});
// Send out merged meshcmd
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' : '') });
var meshcmdbuf = new Buffer(obj.parent.defaultMeshCmd, 'utf8'), tail = new Buffer(8);
tail.writeInt32BE(meshcmdbuf.length, 0);
tail.writeInt32BE(agentexe.length + meshcmdbuf.length + 8, 4);
res.send(Buffer.concat([agentexe, meshcmdbuf, tail]));
});
} else if (req.query.meshaction != null) {
var domain = checkUserIpAddress(req, res);
@ -1226,12 +1362,13 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
} else {
// Send a list of available mesh agents
var response = '<html><head><title>Mesh Agents</title><style>table,th,td { border:1px solid black;border-collapse:collapse;padding:3px; }</style></head><body><table>';
response += '<tr style="background-color:lightgray"><th>ID</th><th>Description</th><th>Link</th><th>Size</th><th>SHA384</th></tr>';
response += '<tr style="background-color:lightgray"><th>ID</th><th>Description</th><th>Link</th><th>Size</th><th>SHA384</th><th>MeshCmd</th></tr>';
for (var agentid in obj.parent.meshAgentBinaries) {
var agentinfo = obj.parent.meshAgentBinaries[agentid];
response += '<tr><td>' + agentinfo.id + '</td><td>' + agentinfo.desc + '</td>';
response += '<td><a target=_blank href="' + req.originalUrl + '?id=' + agentinfo.id + '">' + agentinfo.rname + '</a></td>';
response += '<td>' + agentinfo.size + '</td><td>' + agentinfo.hash + '</td></tr>';
response += '<td>' + agentinfo.size + '</td><td>' + agentinfo.hash + '</td>';
response += '<td><a target=_blank href="' + req.originalUrl + '?meshcmd=' + agentinfo.id + '">' + agentinfo.rname.replace('agent', 'cmd') + '</a></td></tr>';
}
response += '</table></body></html>';
res.send(response);
@ -1298,6 +1435,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
obj.app.post(url + 'changepassword', handlePasswordChangeRequest);
obj.app.post(url + 'deleteaccount', handleDeleteAccountRequest);
obj.app.post(url + 'createaccount', handleCreateAccountRequest);
obj.app.post(url + 'resetaccount', handleResetAccountRequest);
obj.app.get(url + 'checkmail', handleCheckMailRequest);
obj.app.post(url + 'amtevents.ashx', obj.handleAmtEventRequest);
obj.app.get(url + 'webrelay.ashx', function (req, res) { res.send('Websocket connection expected'); });
obj.app.ws(url + 'webrelay.ashx', handleRelayWebSocket);
@ -1416,6 +1555,14 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
} catch (e) { if (called == false) { func(null); } }
}
// Return true is the input string looks like an email address
function checkEmail(str) {
var x = str.split('@');
var ok = ((x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2));
if (ok == true) { var y = x[1].split('.'); for (var i in y) { if (y[i].length == 0) { ok = false; } } }
return ok;
}
// Debug
function Debug(lvl) {
if (lvl > obj.parent.debugLevel) return;
@ -1513,7 +1660,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
}
// 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) {
obj.decodeCookie = function (cookie, key, timeout) {
try {
if (key == null) { key = obj.serverKey; }
cookie = new Buffer(cookie.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64');
@ -1523,7 +1670,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
if ((o.time == null) || (o.time == null) || (typeof o.time != 'number')) { return null; }
o.time = o.time * 1000; // Decode the cookie creation time
o.dtime = Date.now() - o.time; // Decode how long ago the cookie was created (in milliseconds)
if ((o.dtime > 120000) || (o.dtime < -30000)) return null; // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
if (timeout == null) { timeout = 2; }
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; }
}