Initial main commit
|
@ -1,5 +1,9 @@
|
|||
## Ignore node_modules
|
||||
## Ignore nodejs things
|
||||
[Nn]ode_modules/
|
||||
[Tt]yping/
|
||||
[Ii]mages-spare/
|
||||
[Dd]aemon/
|
||||
[Bb]in/
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
del MeshCentral2.zip
|
||||
"C:\Program Files\WinRAR\WinRAR.exe" a MeshCentral2.zip @SourceFileList.txt
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "MeshCentralServer", "MeshCentralServer.njsproj", "{00C71E60-81CC-4B15-B486-10D27935D7EB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{00C71E60-81CC-4B15-B486-10D27935D7EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{00C71E60-81CC-4B15-B486-10D27935D7EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{00C71E60-81CC-4B15-B486-10D27935D7EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{00C71E60-81CC-4B15-B486-10D27935D7EB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,146 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{00c71e60-81cc-4b15-b486-10d27935d7eb}</ProjectGuid>
|
||||
<ProjectHome />
|
||||
<ProjectView>ShowAllFiles</ProjectView>
|
||||
<StartupFile>meshcentral.js</StartupFile>
|
||||
<WorkingDirectory>.</WorkingDirectory>
|
||||
<OutputPath>.</OutputPath>
|
||||
<ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
<Name>MeshCentralServer</Name>
|
||||
<StartWebBrowser>False</StartWebBrowser>
|
||||
<ScriptArguments>
|
||||
</ScriptArguments>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'" />
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'" />
|
||||
<ItemGroup>
|
||||
<Compile Include="agents\meshcore.js" />
|
||||
<Compile Include="agents\tinycore.js" />
|
||||
<Compile Include="amtevents.js" />
|
||||
<Compile Include="amtscanner.js" />
|
||||
<Compile Include="amtscript.js" />
|
||||
<Compile Include="meshscanner.js" />
|
||||
<Compile Include="certoperations.js" />
|
||||
<Compile Include="common.js" />
|
||||
<Compile Include="db.js" />
|
||||
<Compile Include="interceptor.js" />
|
||||
<Compile Include="meshcentral.js" />
|
||||
<Compile Include="meshagent.js" />
|
||||
<Compile Include="meshrelay.js" />
|
||||
<Compile Include="mpsserver.js" />
|
||||
<Compile Include="multiserver.js" />
|
||||
<Compile Include="pass.js" />
|
||||
<Compile Include="public\scripts\amt-0.2.0.js" />
|
||||
<Compile Include="public\scripts\agent-desktop-0.0.2.js" />
|
||||
<Compile Include="public\scripts\amt-desktop-0.0.2.js" />
|
||||
<Compile Include="public\scripts\amt-ider-ws-0.0.1.js" />
|
||||
<Compile Include="public\scripts\agent-redir-ws-0.1.0.js" />
|
||||
<Compile Include="public\scripts\amt-redir-ws-0.1.0.js" />
|
||||
<Compile Include="public\scripts\amt-script-0.2.0.js" />
|
||||
<Compile Include="public\scripts\amt-setupbin-0.1.0.js" />
|
||||
<Compile Include="public\scripts\amt-terminal-0.0.2.js" />
|
||||
<Compile Include="public\scripts\amt-wsman-0.2.0.js" />
|
||||
<Compile Include="public\scripts\amt-wsman-ws-0.2.0.js" />
|
||||
<Compile Include="public\scripts\common-0.0.1.js" />
|
||||
<Compile Include="public\scripts\filesaver.1.1.20151003.js" />
|
||||
<Compile Include="public\scripts\inflate.js" />
|
||||
<Compile Include="public\scripts\meshcentral.js" />
|
||||
<Compile Include="redirserver.js" />
|
||||
<Compile Include="webserver.js" />
|
||||
<Content Include="package.json" />
|
||||
<Content Include="public\images-isdu\ComputerIcon.png" />
|
||||
<Content Include="public\images-isdu\ComputerIcon2.png" />
|
||||
<Content Include="public\images-isdu\IntelLogo.png" />
|
||||
<Content Include="public\images-isdu\IntelLogoWhite42.png" />
|
||||
<Content Include="public\images-isdu\ISDUTitle.png" />
|
||||
<Content Include="public\images-isdu\TabConfig.png" />
|
||||
<Content Include="public\images-isdu\TabConnection.png" />
|
||||
<Content Include="public\images-isdu\TabDesktop.png" />
|
||||
<Content Include="public\images-isdu\TabDiscovery.png" />
|
||||
<Content Include="public\images-isdu\TabEvents.png" />
|
||||
<Content Include="public\images-isdu\TabNetwork.png" />
|
||||
<Content Include="public\images-isdu\TabPolicies.png" />
|
||||
<Content Include="public\images-isdu\TabRemote.png" />
|
||||
<Content Include="public\images-isdu\caution.gif" />
|
||||
<Content Include="public\images-isdu\info.gif" />
|
||||
<Content Include="public\images-isdu\warning.gif" />
|
||||
<Content Include="public\images-isdu\isdu.ico" />
|
||||
<Content Include="public\styles\style.css" />
|
||||
<Content Include="readme.txt" />
|
||||
<Content Include="views\default.handlebars" />
|
||||
<Content Include="views\download.handlebars" />
|
||||
<Content Include="views\login.handlebars" />
|
||||
<Content Include="views\terms.handlebars" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="agents\" />
|
||||
<Folder Include="public" />
|
||||
<Folder Include="public\images-isdu" />
|
||||
<Folder Include="public\scripts\" />
|
||||
<Folder Include="public\styles\" />
|
||||
<Folder Include="typings\" />
|
||||
<Folder Include="typings\globals\" />
|
||||
<Folder Include="typings\globals\connect-redis\" />
|
||||
<Folder Include="typings\globals\express-handlebars\" />
|
||||
<Folder Include="typings\globals\express-session\" />
|
||||
<Folder Include="typings\globals\node-forge\" />
|
||||
<Folder Include="typings\globals\node\" />
|
||||
<Folder Include="views\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TypeScriptCompile Include="typings\globals\connect-redis\index.d.ts" />
|
||||
<TypeScriptCompile Include="typings\globals\express-handlebars\index.d.ts" />
|
||||
<TypeScriptCompile Include="typings\globals\express-session\index.d.ts" />
|
||||
<TypeScriptCompile Include="typings\globals\node-forge\index.d.ts" />
|
||||
<TypeScriptCompile Include="typings\globals\node\index.d.ts" />
|
||||
<TypeScriptCompile Include="typings\index.d.ts" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<!--Do not delete the following Import Project. While this appears to do nothing it is a marker for setting TypeScript properties before our import that depends on them.-->
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="False" />
|
||||
<Import Project="$(VSToolsPath)\Node.js Tools\Microsoft.NodejsTools.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>0</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:48022/</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>True</UseCustomServer>
|
||||
<CustomServerUrl>http://localhost:1337</CustomServerUrl>
|
||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}" User="">
|
||||
<WebProjectProperties>
|
||||
<StartPageUrl>
|
||||
</StartPageUrl>
|
||||
<StartAction>CurrentPage</StartAction>
|
||||
<AspNetDebugging>True</AspNetDebugging>
|
||||
<SilverlightDebugging>False</SilverlightDebugging>
|
||||
<NativeDebugging>False</NativeDebugging>
|
||||
<SQLDebugging>False</SQLDebugging>
|
||||
<ExternalProgram>
|
||||
</ExternalProgram>
|
||||
<StartExternalURL>
|
||||
</StartExternalURL>
|
||||
<StartCmdLineArguments>
|
||||
</StartCmdLineArguments>
|
||||
<StartWorkingDirectory>
|
||||
</StartWorkingDirectory>
|
||||
<EnableENC>False</EnableENC>
|
||||
<AlwaysStartWebServerOnDebug>False</AlwaysStartWebServerOnDebug>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
|
@ -0,0 +1,12 @@
|
|||
readme.txt
|
||||
license.txt
|
||||
package.json
|
||||
*.js
|
||||
views/*
|
||||
public/*
|
||||
public/images/*
|
||||
public/scripts/*
|
||||
public/styles/*
|
||||
agents/MeshAgent-Win32.exe
|
||||
MeshCentral.sln
|
||||
MeshCentralServer.njsproj
|
|
@ -0,0 +1,4 @@
|
|||
MeshName=TestMesh
|
||||
MeshID=0xF5565E916018381CE5FA9F01AAF526A2984F13A3622D8B82C433A92B795CB840
|
||||
MeshServer=wss://devbox.mesh.meshcentral.com:443/agent.ashx
|
||||
ServerID=02CC5EC709F5954D28EED3A4AF4618A622C8E148CF79989CF15333476A78DDBE
|
|
@ -0,0 +1,4 @@
|
|||
MeshName=TestMesh
|
||||
MeshID=0xF5565E916018381CE5FA9F01AAF526A2984F13A3622D8B82C433A92B795CB840
|
||||
MeshServer=wss://devbox.mesh.meshcentral.com:443/agent.ashx
|
||||
ServerID=02CC5EC709F5954D28EED3A4AF4618A622C8E148CF79989CF15333476A78DDBE
|
|
@ -0,0 +1,18 @@
|
|||
MeshAgent 2.0, 32bit Windows Executable
|
||||
|
||||
This is a super early development sample. Agent can't do anything yet.
|
||||
|
||||
Command parameters:
|
||||
--info (shows info)
|
||||
--update-key (update key, pertinent keys include “MeshServer”, “MeshID”, “ServerID”
|
||||
--update-key-hex
|
||||
--delete-key
|
||||
--dump-key
|
||||
--dump-key-hex
|
||||
--mesh-server (same as doing –update-key MeshServer)
|
||||
|
||||
Note: MeshServer, specify uri… For now, use ws:// as it doesn’t connect TLS to server right now, because the Mesh Server I have right now doesn’t do TLS, so I couldn’t test.
|
||||
|
||||
Use –update-key-hex to specify MeshID and ServerID
|
||||
MeshAgent.exe –update-key-hex MeshID 5BFC963FA34940E24C032A43E1AD675EEBF27AD1E4CE24C678D081E8B2725FD9
|
||||
|
|
@ -0,0 +1,802 @@
|
|||
/*
|
||||
Copyright 2017 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
function createMeshCore(agent) {
|
||||
var obj = {};
|
||||
|
||||
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
|
||||
obj.meshCoreInfo = "MeshCore v3k";
|
||||
obj.meshCoreCapabilities = 14; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript
|
||||
var meshServerConnectionState = 0;
|
||||
var tunnels = {};
|
||||
var lastSelfInfo = null;
|
||||
var lastNetworkInfo = null;
|
||||
var selfInfoUpdateTimer = null;
|
||||
|
||||
var http = require('http');
|
||||
var fs = require('fs');
|
||||
|
||||
// If we are running in Duktape, agent will be null
|
||||
if (agent == null) {
|
||||
// Running in native agent, Import libraries
|
||||
var db = require('SimpleDataStore').Shared();
|
||||
var sha = require('SHA256Stream');
|
||||
var mesh = require('MeshAgent');
|
||||
var processManager = require('ILibProcessPipe');
|
||||
if (mesh.hasKVM == 1) { obj.meshCoreCapabilities |= 1; }
|
||||
} else {
|
||||
// Running in nodejs
|
||||
obj.meshCoreInfo += '-NodeJS';
|
||||
obj.meshCoreCapabilities = 8;
|
||||
var mesh = agent.getMeshApi();
|
||||
}
|
||||
|
||||
// Polyfill String.endsWith
|
||||
if (!String.prototype.endsWith) {
|
||||
String.prototype.endsWith = function (searchString, position) {
|
||||
var subjectString = this.toString();
|
||||
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
|
||||
position -= searchString.length;
|
||||
var lastIndex = subjectString.lastIndexOf(searchString, position);
|
||||
return lastIndex !== -1 && lastIndex === position;
|
||||
};
|
||||
}
|
||||
|
||||
// Polyfill path.join
|
||||
obj.path = {
|
||||
join: function () {
|
||||
var x = [];
|
||||
for (var i in arguments) {
|
||||
var w = arguments[i];
|
||||
if (w != null) {
|
||||
while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
|
||||
while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
|
||||
x.push(w);
|
||||
}
|
||||
}
|
||||
if (x.length == 0) return '/';
|
||||
return x.join('/');
|
||||
}
|
||||
};
|
||||
|
||||
// Replace a string with a number if the string is an exact number
|
||||
function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) == x)) { x = parseInt(x); } return x; }
|
||||
|
||||
// Convert decimal to hex
|
||||
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
|
||||
|
||||
// Convert a raw string to a hex string
|
||||
function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
|
||||
|
||||
// Convert a buffer into a string
|
||||
function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
|
||||
|
||||
// Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
|
||||
function hex2rstr(d) {
|
||||
if (typeof d != "string" || d.length == 0) return '';
|
||||
var r = '', m = ('' + d).match(/../g), t;
|
||||
while (t = m.shift()) r += String.fromCharCode('0x' + t);
|
||||
return r
|
||||
}
|
||||
|
||||
// Convert an object to string with all functions
|
||||
function objToString(x, p, ret) {
|
||||
if (ret == undefined) ret = '';
|
||||
if (p == undefined) p = 0;
|
||||
if (x == null) { return '[null]'; }
|
||||
if (p > 8) { return '[...]'; }
|
||||
if (x == undefined) { return '[undefined]'; }
|
||||
if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
|
||||
if (typeof x == 'buffer') { return '[buffer]'; }
|
||||
if (typeof x != 'object') { return x; }
|
||||
var r = '{' + (ret ? '\r\n' : ' ');
|
||||
for (var i in x) { r += (addPad(p + 2, ret) + i + ': ' + objToString(x[i], p + 2, ret) + (ret ? '\r\n' : ' ')); }
|
||||
return r + addPad(p, ret) + '}';
|
||||
}
|
||||
|
||||
// Return p number of spaces
|
||||
function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
|
||||
|
||||
// Split a string taking into account the quoats. Used for command line parsing
|
||||
function splitArgs(str) {
|
||||
var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
|
||||
do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
|
||||
return myArray;
|
||||
}
|
||||
|
||||
// Parse arguments string array into an object
|
||||
function parseArgs(argv) {
|
||||
var results = { '_': [] }, current = null;
|
||||
for (var i = 1, len = argv.length; i < len; i++) {
|
||||
var x = argv[i];
|
||||
if (x.length > 2 && x[0] == '-' && x[1] == '-') {
|
||||
if (current != null) { results[current] = true; }
|
||||
current = x.substring(2);
|
||||
} else {
|
||||
if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
|
||||
}
|
||||
}
|
||||
if (current != null) { results[current] = true; }
|
||||
return results;
|
||||
}
|
||||
|
||||
// Parge a URL string into an options object
|
||||
function parseUrl(url) {
|
||||
var x = url.split('/');
|
||||
if (x.length < 4) return null;
|
||||
var y = x[2].split(':');
|
||||
var options = {};
|
||||
var options = { protocol: x[0], hostname: y[0], path: '/' + x.splice(3).join('/') };
|
||||
if (y.length == 1) { options.port = ((x[0] == 'https:') || (x[0] == 'wss:')) ? 443 : 80; } else { options.port = parseInt(y[1]); }
|
||||
if (isNaN(options.port) == true) return null;
|
||||
return options;
|
||||
}
|
||||
|
||||
// Handle a mesh agent command
|
||||
function handleServerCommand(data) {
|
||||
if (typeof data == 'object') {
|
||||
// If this is a console command, parse it and call the console handler
|
||||
if (data.action == 'msg') {
|
||||
if (data.type == 'console') { // Process a console command
|
||||
if (data.value && data.sessionid) {
|
||||
var args = splitArgs(data.value);
|
||||
processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
|
||||
}
|
||||
}
|
||||
else if (data.type == 'tunnel') { // Process a new tunnel connection request
|
||||
if (data.value && data.sessionid) {
|
||||
// Create a new tunnel object
|
||||
var tunnel = http.request(parseUrl(data.value));
|
||||
tunnel.upgrade = onTunnelUpgrade;
|
||||
tunnel.sessionid = data.sessionid;
|
||||
tunnel.rights = data.rights;
|
||||
tunnel.state = 0;
|
||||
tunnel.url = data.value;
|
||||
tunnel.protocol = 0;
|
||||
|
||||
// Put the tunnel in the tunnels list
|
||||
var index = 1;
|
||||
while (tunnels[index]) { index++; }
|
||||
tunnel.index = index;
|
||||
tunnels[index] = tunnel;
|
||||
|
||||
sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (data.action == 'wakeonlan') {
|
||||
// Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
|
||||
sendConsoleText('Server requesting wake-on-lan for: ' + data.macs.join(', '));
|
||||
// TODO!!!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a file changed in the file system
|
||||
function onFileWatcher(a, b) {
|
||||
//console.log('onFileWatcher', a, b, this.path);
|
||||
var response = getDirectoryInfo(this.path);
|
||||
if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
|
||||
}
|
||||
|
||||
// Get a formated response for a given directory path
|
||||
function getDirectoryInfo(reqpath) {
|
||||
var response = { path: reqpath, dir: [] };
|
||||
if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
|
||||
// List all the drives in the root, or the root itself
|
||||
var results = null;
|
||||
try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
|
||||
//console.log('a', objToString(results, 0, '.'));
|
||||
if (results != null) {
|
||||
for (var i = 0; i < results.length; ++i) {
|
||||
var drive = { n: results[i].name, t: 1 };
|
||||
if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
|
||||
response.dir.push(drive);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// List all the files and folders in this path
|
||||
if (reqpath == '') { reqpath = '/'; }
|
||||
var xpath = obj.path.join(reqpath, '*');
|
||||
var results = null;
|
||||
try { results = fs.readdirSync(xpath); } catch (e) { }
|
||||
if (results != null) {
|
||||
for (var i = 0; i < results.length; ++i) {
|
||||
if ((results[i] != '.') && (results[i] != '..')) {
|
||||
var stat = null, p = obj.path.join(reqpath, results[i]);
|
||||
try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
|
||||
if ((stat != null) && (stat != undefined)) {
|
||||
if (stat.isDirectory() == true) {
|
||||
response.dir.push({ n: results[i], t: 2, d: stat.mtime });
|
||||
} else {
|
||||
response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// Tunnel callback operations
|
||||
function onTunnelUpgrade(response, s, head) { this.s = s; s.httprequest = this; s.end = onTunnelClosed; s.data = onTunnelData; }
|
||||
function onTunnelClosed() {
|
||||
sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
|
||||
if (this.httprequest.protocol == 1) { this.httprequest.process.end(); delete this.httprequest.process; }
|
||||
delete tunnels[this.httprequest.index];
|
||||
|
||||
// Close the watcher if required
|
||||
if (this.httprequest.watcher != undefined) {
|
||||
//console.log('Closing watcher: ' + this.httprequest.watcher.path);
|
||||
//this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
|
||||
delete this.httprequest.watcher;
|
||||
}
|
||||
|
||||
// If there is a upload or download active on this connection, close the file
|
||||
if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
|
||||
if (this.httprequest.downloadFile) { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; }
|
||||
}
|
||||
function onTunnelSendOk() { sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid); }
|
||||
function onTunnelData(data) {
|
||||
// If this is upload data, save it to file
|
||||
if (this.httprequest.uploadFile) {
|
||||
try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { this.write(JSON.stringify({ action: 'uploaderror' })); return; } // Write to the file, if there is a problem, error out.
|
||||
this.write(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid })); // Ask for more data
|
||||
return;
|
||||
}
|
||||
// If this is a download, send more of the file
|
||||
if (this.httprequest.downloadFile) {
|
||||
var buf = new Buffer(4096);
|
||||
var len = fs.readSync(this.httprequest.downloadFile, buf, 0, 4096, null);
|
||||
this.httprequest.downloadFilePtr += len;
|
||||
if (len > 0) { this.write(buf.slice(0, len)); } else { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; this.end(); }
|
||||
return;
|
||||
}
|
||||
// (****) Remote Desktop without using native pipes (TODO: This is in use now because native pipes don't work correctly on Linux)
|
||||
if (this.httprequest.desktop) { this.httprequest.desktop.kvm.write(data); return; }
|
||||
// (****) Remote Terminal without using native pipes (TODO: This is in use now because native pipes don't work correctly on Linux)
|
||||
if (this.httprequest.terminal) { this.httprequest.terminal.write(data); return; }
|
||||
|
||||
if (this.httprequest.state == 0) {
|
||||
// Check if this is a relay connection
|
||||
if (data == 'c') { this.httprequest.state = 1; sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid); }
|
||||
} else {
|
||||
// Handle tunnel data
|
||||
if (this.httprequest.protocol == 0) { // 1 = SOL, 2 = KVM, 3 = IDER, 4 = Files, 5 = FileTransfer
|
||||
// Take a look at the protocolab
|
||||
this.httprequest.protocol = parseInt(data);
|
||||
if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
|
||||
if (this.httprequest.protocol == 1) {
|
||||
// (****) Remote Terminal without using native pipes (TODO: This is in use now because native pipes don't work correctly on Linux)
|
||||
if (process.platform == "win32") {
|
||||
this.httprequest.terminal = processManager.CreateProcess("%windir%\\system32\\cmd.exe");
|
||||
} else {
|
||||
this.httprequest.terminal = processManager.CreateProcess("/bin/sh", "sh", ILibProcessPipe_SpawnTypes.TERM);
|
||||
}
|
||||
this.httprequest.terminal.tunnel = this;
|
||||
this.httprequest.terminal.on('data', function (chunk) { this.tunnel.write(chunk); });
|
||||
this.httprequest.terminal.error.data = function (chunk) { this.parent.tunnel.write(chunk); }
|
||||
|
||||
/*
|
||||
// Remote terminal using native pipes
|
||||
if (process.platform == "win32") {
|
||||
this.httprequest.process = processManager.CreateProcess("%windir%\\system32\\cmd.exe");
|
||||
} else {
|
||||
this.httprequest.process = processManager.CreateProcess("/bin/sh", "sh", ILibProcessPipe_SpawnTypes.TERM);
|
||||
}
|
||||
this.httprequest.process.tunnel = this;
|
||||
this.httprequest.process.error.data = function (chunk) { this.parent.tunnel.write(chunk); }
|
||||
this.httprequest.process.pipe(this);
|
||||
this.pipe(this.httprequest.process);
|
||||
*/
|
||||
}
|
||||
if (this.httprequest.protocol == 2) {
|
||||
// (****) Remote Desktop without using native pipes (TODO: This is in use now because native pipes don't work correctly on Linux)
|
||||
this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this };
|
||||
this.httprequest.desktop.kvm.tunnel = this;
|
||||
this.httprequest.desktop.kvm.on('data', function (data) { this.tunnel.write(data); });
|
||||
this.desktop = this.httprequest.desktop;
|
||||
this.end = function () { if (--this.desktop.kvm.connectionCount == 0) { this.httprequest.desktop.kvm.end(); } };
|
||||
if (this.httprequest.desktop.kvm.hasOwnProperty("connectionCount")) { this.httprequest.desktop.kvm.connectionCount++; } else { this.httprequest.desktop.kvm.connectionCount = 1; }
|
||||
|
||||
/*
|
||||
// Remote desktop using native pipes
|
||||
this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this };
|
||||
this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
|
||||
this.desktop = this.httprequest.desktop;
|
||||
this.end = function () {
|
||||
--this.desktop.kvm.connectionCount;
|
||||
this.unpipe(this.httprequest.desktop.kvm);
|
||||
this.httprequest.desktop.kvm.unpipe(this);
|
||||
if (this.desktop.kvm.connectionCount == 0) { this.httprequest.desktop.kvm.end(); }
|
||||
};
|
||||
if (this.httprequest.desktop.kvm.hasOwnProperty("connectionCount")) { this.httprequest.desktop.kvm.connectionCount++; } else { this.httprequest.desktop.kvm.connectionCount = 1; }
|
||||
this.pipe(this.httprequest.desktop.kvm);
|
||||
this.httprequest.desktop.kvm.pipe(this);
|
||||
*/
|
||||
}
|
||||
else if (this.httprequest.protocol == 5) {
|
||||
// Setup files
|
||||
// NOP
|
||||
}
|
||||
} else if (this.httprequest.protocol == 1) {
|
||||
// Send data into terminal stdin
|
||||
//this.write(data); // Echo back the keys (Does not seem to be a good idea)
|
||||
this.httprequest.process.write(data);
|
||||
} else if (this.httprequest.protocol == 2) {
|
||||
// Send data into remote desktop
|
||||
// TODO ADD REMOTE DESKTOP (This is test code)
|
||||
if (this.httprequest.desktop.state == 0) {
|
||||
this.write(new Buffer(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
|
||||
this.httprequest.desktop.state = 1;
|
||||
} else {
|
||||
this.httprequest.desktop.write(data);
|
||||
}
|
||||
} else if (this.httprequest.protocol == 5) {
|
||||
// Process files commands
|
||||
var cmd = null;
|
||||
try { cmd = JSON.parse(data); } catch (e) { };
|
||||
if ((cmd == null) || (cmd.action == undefined)) { return; }
|
||||
//console.log(objToString(cmd, 0, '.'));
|
||||
switch (cmd.action) {
|
||||
case 'ls': {
|
||||
// Close the watcher if required
|
||||
var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
|
||||
if ((this.httprequest.watcher != undefined) && (samepath == false)) {
|
||||
//console.log('Closing watcher: ' + this.httprequest.watcher.path);
|
||||
//this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
|
||||
delete this.httprequest.watcher;
|
||||
}
|
||||
|
||||
// Send the folder content to the browser
|
||||
var response = getDirectoryInfo(cmd.path);
|
||||
if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
|
||||
this.write(JSON.stringify(response));
|
||||
|
||||
// Start the directory watcher
|
||||
if ((cmd.path != '') && (samepath == false)) {
|
||||
var watcher = fs.watch(cmd.path, onFileWatcher);
|
||||
watcher.tunnel = this.httprequest;
|
||||
watcher.path = cmd.path;
|
||||
this.httprequest.watcher = watcher;
|
||||
//console.log('Starting watcher: ' + this.httprequest.watcher.path);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'mkdir': {
|
||||
// Create a new empty folder
|
||||
fs.mkdirSync(cmd.path);
|
||||
break;
|
||||
}
|
||||
case 'rm': {
|
||||
// Remove many files or folders
|
||||
for (var i in cmd.delfiles) {
|
||||
var fullpath = obj.path.join(cmd.path, cmd.delfiles[i]);
|
||||
try { fs.unlinkSync(fullpath); } catch (e) { console.log(e); }
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'rename': {
|
||||
// Rename a file or folder
|
||||
var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
|
||||
var newfullpath = obj.path.join(cmd.path, cmd.newname);
|
||||
try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
|
||||
break;
|
||||
}
|
||||
case 'download': {
|
||||
// Packet download of a file, agent to browser
|
||||
if (cmd.path == undefined) break;
|
||||
var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
|
||||
//console.log('Download: ' + filepath);
|
||||
try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid })); break; }
|
||||
this.httprequest.downloadFileId = cmd.reqid;
|
||||
this.httprequest.downloadFilePtr = 0;
|
||||
if (this.httprequest.downloadFile) { this.write(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId })); }
|
||||
break;
|
||||
}
|
||||
case 'download2': {
|
||||
// Stream download of a file, agent to browser
|
||||
if (cmd.path == undefined) break;
|
||||
var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
|
||||
try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
|
||||
this.httprequest.downloadFile.pipe(this);
|
||||
this.httprequest.downloadFile.end = function () { }
|
||||
break;
|
||||
}
|
||||
case 'upload': {
|
||||
// Upload a file, browser to agent
|
||||
if (this.httprequest.uploadFile != undefined) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
|
||||
if (cmd.path == undefined) break;
|
||||
var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
|
||||
try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid })); break; }
|
||||
this.httprequest.uploadFileid = cmd.reqid;
|
||||
if (this.httprequest.uploadFile) { this.write(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid })); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
|
||||
}
|
||||
}
|
||||
|
||||
// Console state
|
||||
var consoleWebSockets = {};
|
||||
var consoleHttpRequest = null;
|
||||
|
||||
// Console HTTP response
|
||||
function consoleHttpResponse(response) {
|
||||
response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
|
||||
response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
|
||||
};
|
||||
|
||||
// Process a mesh agent console command
|
||||
function processConsoleCommand(cmd, args, rights, sessionid) {
|
||||
try {
|
||||
var response = null;
|
||||
switch (cmd) {
|
||||
case 'help': { // Displays available commands
|
||||
response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, parseurl, httpget, wsconnect, wssend, wsclose, notify, ls, amt, netinfo.';
|
||||
break;
|
||||
}
|
||||
case 'notify': { // Send a notification message to the mesh
|
||||
if (args['_'].length != 1) {
|
||||
response = 'Proper usage: notify "message" [--session]'; // Display correct command usage
|
||||
} else {
|
||||
var notification = { "action": "msg", "type": "notify", "value": args['_'][0], "tag": "console" };
|
||||
if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
|
||||
mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
|
||||
response = 'ok';
|
||||
}
|
||||
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\Capabilities: ' + obj.meshCoreCapabilities + '.';
|
||||
break;
|
||||
}
|
||||
case 'selfinfo': { // Return self information block
|
||||
response = JSON.stringify(buildSelfInfo());
|
||||
break;
|
||||
}
|
||||
case 'args': { // Displays parsed command arguments
|
||||
response = 'args ' + objToString(args, 0, '.');
|
||||
break;
|
||||
}
|
||||
case 'print': { // Print a message on the mesh agent console, does nothing when running in the background
|
||||
var r = [];
|
||||
for (var i in args['_']) { r.push(args['_'][i]); }
|
||||
console.log(r.join(' '));
|
||||
response = 'Message printed on agent console.';
|
||||
break;
|
||||
}
|
||||
case 'type': { // Returns the content of a file
|
||||
if (args['_'].length == 0) {
|
||||
response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage
|
||||
} else {
|
||||
var max = 4096;
|
||||
if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; }
|
||||
if (max > 4096) max = 4096;
|
||||
var buf = new Buffer(max), fd = fs.openSync(args['_'][0], "r"), r = fs.readSync(fd, buf, 0, max); // Read the file content
|
||||
response = buf.toString();
|
||||
var i = response.indexOf('\n');
|
||||
if ((i > 0) && (response[i - 1] != '\r')) { response = response.split('\n').join('\r\n'); }
|
||||
if (r == max) response += '...';
|
||||
fs.closeSync(fd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'dbget': { // Return the data store value for a given key
|
||||
if (db == null) { response = 'Database not accessible.'; break; }
|
||||
if (args['_'].length != 1) {
|
||||
response = 'Proper usage: dbget (key)'; // Display the value for a given database key
|
||||
} else {
|
||||
response = db.Get(args['_'][0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'dbset': { // Set a data store key and value pair
|
||||
if (db == null) { response = 'Database not accessible.'; break; }
|
||||
if (args['_'].length != 2) {
|
||||
response = 'Proper usage: dbset (key) (value)'; // Set a database key
|
||||
} else {
|
||||
var r = db.Put(args['_'][0], args['_'][1]);
|
||||
response = 'Key set: ' + r;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'dbcompact': { // Compact the data store
|
||||
if (db == null) { response = 'Database not accessible.'; break; }
|
||||
var r = db.Compact();
|
||||
response = 'Database compacted: ' + r;
|
||||
break;
|
||||
}
|
||||
case 'parseurl': {
|
||||
response = objToString(parseUrl(args['_'][0]));
|
||||
break;
|
||||
}
|
||||
case 'httpget': {
|
||||
if (consoleHttpRequest != null) {
|
||||
response = 'HTTP operation already in progress.';
|
||||
} else {
|
||||
if (args['_'].length != 1) {
|
||||
response = 'Proper usage: httpget (url)';
|
||||
} else {
|
||||
var options = parseUrl(args['_'][0]);
|
||||
options.method = 'GET';
|
||||
if (options == null) {
|
||||
response = 'Invalid url.';
|
||||
} else {
|
||||
try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (e) { response = 'Invalid HTTP GET request'; }
|
||||
consoleHttpRequest.sessionid = sessionid;
|
||||
if (consoleHttpRequest != null) {
|
||||
consoleHttpRequest.end();
|
||||
response = 'HTTPGET ' + options.protocol + '//' + options.hostname + ':' + options.port + options.path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'wslist': { // List all web sockets
|
||||
response = '';
|
||||
for (var i in consoleWebSockets) {
|
||||
var httprequest = consoleWebSockets[i];
|
||||
response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
|
||||
}
|
||||
if (response == '') { response = 'no websocket sessions.'; }
|
||||
break;
|
||||
}
|
||||
case 'wsconnect': { // Setup a web socket
|
||||
if (args['_'].length == 0) {
|
||||
response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage
|
||||
} else {
|
||||
var httprequest = null;
|
||||
try { http.request(parseUrl(args['_'][0])); } catch (e) { response = 'Invalid HTTP websocket request'; }
|
||||
if (httprequest != null) {
|
||||
httprequest.upgrade = onWebSocketUpgrade;
|
||||
|
||||
var index = 1;
|
||||
while (consoleWebSockets[index]) { index++; }
|
||||
httprequest.sessionid = sessionid;
|
||||
httprequest.index = index;
|
||||
httprequest.url = args['_'][0];
|
||||
consoleWebSockets[index] = httprequest;
|
||||
response = 'New websocket session #' + index;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'wssend': { // Send data on a web socket
|
||||
if (args['_'].length == 0) {
|
||||
response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage
|
||||
for (var i in consoleWebSockets) {
|
||||
var httprequest = consoleWebSockets[i];
|
||||
response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
|
||||
}
|
||||
} else {
|
||||
var i = parseInt(args['_'][0]);
|
||||
var httprequest = consoleWebSockets[i];
|
||||
if (httprequest != undefined) {
|
||||
httprequest.s.write(args['_'][1]);
|
||||
response = 'ok';
|
||||
} else {
|
||||
response = 'Invalid web socket number';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'wsclose': { // Close a websocket
|
||||
if (args['_'].length == 0) {
|
||||
response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage
|
||||
} else {
|
||||
var i = parseInt(args['_'][0]);
|
||||
var httprequest = consoleWebSockets[i];
|
||||
if (httprequest != undefined) {
|
||||
httprequest.s.end();
|
||||
response = 'ok';
|
||||
} else {
|
||||
response = 'Invalid web socket number';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'tunnels': { // Show the list of current tunnels
|
||||
response = '';
|
||||
for (var i in tunnels) { response += 'Tunnel #' + i + ', ' + tunnels[i].url + '\r\n'; }
|
||||
if (response == '') { response = 'No websocket sessions.'; }
|
||||
break;
|
||||
}
|
||||
case 'ls': { // Show list of files and folders
|
||||
response = '';
|
||||
var xpath = '*';
|
||||
if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); }
|
||||
response = 'List of ' + xpath + '\r\n';
|
||||
var results = fs.readdirSync(xpath);
|
||||
for (var i = 0; i < results.length; ++i) {
|
||||
var stat = null, p = obj.path.join(args['_'][0], results[i]);
|
||||
try { stat = fs.statSync(p); } catch (e) { }
|
||||
if ((stat == null) || (stat == undefined)) {
|
||||
response += (results[i] + "\r\n");
|
||||
} else {
|
||||
response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n");
|
||||
}
|
||||
}
|
||||
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, '.');
|
||||
} else {
|
||||
response = 'This mesh agent does not support Intel AMT.';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'netinfo': { // Show network interface information
|
||||
response = objToString(mesh.NetInfo, 0, '.');
|
||||
break;
|
||||
}
|
||||
case 'sendall': { // Send a message to all consoles on this mesh
|
||||
sendConsoleText(args['_'].join(' '));
|
||||
break;
|
||||
}
|
||||
default: { // This is an unknown command, return an error message
|
||||
response = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) { response = 'Command returned an exception error: ' + e; console.log(e); }
|
||||
if (response != null) { sendConsoleText(response, sessionid); }
|
||||
}
|
||||
|
||||
// Send a mesh agent console command
|
||||
function sendConsoleText(text, sessionid) { mesh.SendCommand({ "action": "msg", "type": "console", "value": text, "sessionid": sessionid }); }
|
||||
|
||||
// Called before the process exits
|
||||
//process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
|
||||
|
||||
// Called when the server connection state changes
|
||||
function handleServerConnection(state) {
|
||||
meshServerConnectionState = state;
|
||||
if (meshServerConnectionState == 0) {
|
||||
// Server disconnected
|
||||
if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
|
||||
lastSelfInfo = null;
|
||||
} else {
|
||||
// Server connected, send mesh core information
|
||||
sendPeriodicServerUpdate(true);
|
||||
if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 60000); } // Should be a long time, like 20 minutes. For now, 1 minute.
|
||||
}
|
||||
}
|
||||
|
||||
// Build a bunch a self information data that will be sent to the server
|
||||
// We need to do this periodically and if anything changes, send the update to the server.
|
||||
function buildSelfInfo() {
|
||||
var r = { "action": "coreinfo", "value": obj.meshCoreInfo, "caps": obj.meshCoreCapabilities };
|
||||
if (mesh.hasHECI == 1) {
|
||||
var meinfo = mesh.MEInfo;
|
||||
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 }
|
||||
}
|
||||
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 network interfaces information data
|
||||
var netInfo = mesh.NetInfo;
|
||||
netInfo.action = 'netinfo';
|
||||
var netInfoStr = JSON.stringify(netInfo);
|
||||
if ((force == true) || (netInfoStr != lastNetworkInfo)) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
|
||||
}
|
||||
|
||||
// Called on MicroLMS Intel AMT user notification
|
||||
function handleAmtNotification(notification) {
|
||||
var amtMessage = notification.messageId;
|
||||
var amtMessageArg = notification.messageArguments;
|
||||
var notify = null;
|
||||
|
||||
switch (amtMessage) {
|
||||
case 'iAMT0050': {
|
||||
// Serial over lan
|
||||
if (amtMessageArg == '48') {
|
||||
// Connected
|
||||
notify = 'Intel® AMT Serial-over-LAN connected';
|
||||
}
|
||||
else if (amtMessageArg == '49') {
|
||||
// Disconnected
|
||||
notify = 'Intel® AMT Serial-over-LAN disconnected';
|
||||
}
|
||||
}
|
||||
case 'iAMT0052': {
|
||||
// HWKVM
|
||||
if (amtMessageArg == '1') {
|
||||
// Connected
|
||||
notify = 'Intel® AMT KVM connected';
|
||||
}
|
||||
else if (amtMessageArg == '2') {
|
||||
// Disconnected
|
||||
notify = 'Intel® AMT KVM disconnected';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (notify != null) {
|
||||
var notification = { "action": "msg", "type": "notify", "value": notify, "tag": "general" };
|
||||
//mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
|
||||
//console.log("handleAmtNotification", JSON.stringify(notification));
|
||||
}
|
||||
}
|
||||
|
||||
// Starting function
|
||||
obj.start = function () {
|
||||
// Setup the mesh agent event handlers
|
||||
mesh.AddCommandHandler(handleServerCommand);
|
||||
mesh.AddConnectHandler(handleServerConnection);
|
||||
mesh.lmsNotification = handleAmtNotification;
|
||||
sendPeriodicServerUpdate(); // TODO: Check if connected before sending
|
||||
|
||||
// Parse input arguments
|
||||
//var args = parseArgs(process.argv);
|
||||
//console.log(args);
|
||||
|
||||
//console.log('Stopping.');
|
||||
//process.exit();
|
||||
}
|
||||
|
||||
obj.stop = function () {
|
||||
mesh.AddCommandHandler(null);
|
||||
mesh.AddConnectHandler(null);
|
||||
}
|
||||
|
||||
function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
|
||||
function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
|
||||
function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
|
||||
|
||||
function onWebSocketUpgrade(response, s, head) {
|
||||
sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
|
||||
this.s = s;
|
||||
s.httprequest = this;
|
||||
s.end = onWebSocketClosed;
|
||||
s.data = onWebSocketData;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
var xexports = null;
|
||||
try { xexports = module.exports; } catch (e) { }
|
||||
|
||||
if (xexports != null) {
|
||||
// If we are running within NodeJS, export the core
|
||||
module.exports.createMeshCore = createMeshCore;
|
||||
} else {
|
||||
// If we are not running in NodeJS, launch the core
|
||||
createMeshCore().start(null);
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
#!/bin/bash
|
||||
|
||||
CheckStartupType() {
|
||||
# echo "Checking process autostart system..."
|
||||
if [[ `systemctl` =~ -\.mount ]]; then return 1; # systemd;
|
||||
elif [[ `/sbin/init --version` =~ upstart ]]; then return 2; # upstart;
|
||||
elif [[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]]; then return 3; # sysv-init;
|
||||
fi
|
||||
return 0;
|
||||
}
|
||||
|
||||
CheckInstallAgent() {
|
||||
# echo "Checking mesh identifier..."
|
||||
if [ -e "/usr/local" ]
|
||||
then
|
||||
installpath="/usr/local/mesh"
|
||||
else
|
||||
installpath="/usr/mesh"
|
||||
fi
|
||||
if [ $# -eq 2 ]
|
||||
then
|
||||
url=$1
|
||||
meshid=$2
|
||||
meshidlen=${#meshid}
|
||||
if [ $meshidlen -eq 64 ]
|
||||
then
|
||||
# echo "Detecting computer type..."
|
||||
machinetype=$( uname -m )
|
||||
machineid=0
|
||||
if [ $machinetype == 'x86_64' ]
|
||||
then
|
||||
# Linux x86, 64 bit
|
||||
machineid=6
|
||||
fi
|
||||
if [ $machinetype == 'x86' ]
|
||||
then
|
||||
# Linux x86, 32 bit
|
||||
machineid=5
|
||||
fi
|
||||
if [ $machinetype == 'armv7l' ]
|
||||
then
|
||||
# Raspberry Pi 2 or Raspberry Pi 3
|
||||
machineid=25
|
||||
fi
|
||||
# TODO: Add more machine types, detect KVM support, etc.
|
||||
if [ $machineid -eq 0 ]
|
||||
then
|
||||
echo "Unsupported machine type: $machinetype, check with server administrator."
|
||||
else
|
||||
DownloadAgent $url $meshid $machineid
|
||||
fi
|
||||
else
|
||||
echo "MeshID is not correct, must be 64 HEX characters long."
|
||||
fi
|
||||
else
|
||||
echo "URI and/or MeshID have not been specified, must be passed in as arguments."
|
||||
return 0;
|
||||
fi
|
||||
}
|
||||
|
||||
DownloadAgent() {
|
||||
url=$1
|
||||
meshid=$2
|
||||
machineid=$3
|
||||
# Create folder
|
||||
mkdir -p /usr/local/mesh
|
||||
cd /usr/local/mesh
|
||||
# echo "Downloading mesh agent..."
|
||||
wget $url/meshagents?id=$machineid -q --no-check-certificate -O /usr/local/mesh/meshagent
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
echo "Mesh agent download."
|
||||
# TODO: Check meshagent sha256 hash
|
||||
chmod 755 /usr/local/mesh/meshagent
|
||||
wget $url/meshsettings?id=$meshid -q --no-check-certificate -O /usr/local/mesh/meshagent.msh
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
if [ $starttype -eq 1 ]
|
||||
then
|
||||
echo -e "[Unit]\nDescription=MeshCentral Agent\n[Service]\nExecStart=/usr/local/mesh/meshagent\nStandardOutput=null\n[Install]\nWantedBy=multi-user.target\nAlias=meshcentral.service\n" > /lib/systemd/system/meshcentral.service
|
||||
systemctl enable meshcentral
|
||||
systemctl start meshcentral
|
||||
else
|
||||
./meshagent start
|
||||
ln -s /usr/local/mesh/meshagent /sbin/meshcmd
|
||||
ln -s /usr/local/mesh/meshagent /etc/rc2.d/S20mesh
|
||||
ln -s /usr/local/mesh/meshagent /etc/rc3.d/S20mesh
|
||||
ln -s /usr/local/mesh/meshagent /etc/rc5.d/S20mesh
|
||||
fi
|
||||
else
|
||||
echo "Unable to download mesh settings at: $url/meshsettings?id=$meshid."
|
||||
fi
|
||||
else
|
||||
echo "Unable to download mesh agent at: $url/meshagents?id=$machineid."
|
||||
fi
|
||||
}
|
||||
|
||||
UninstallAgent() {
|
||||
# Uninstall agent
|
||||
if [ -e "/usr/local" ]
|
||||
then
|
||||
installpath="/usr/local/mesh"
|
||||
else
|
||||
installpath="/usr/mesh"
|
||||
fi
|
||||
|
||||
if [ $starttype -eq 1 ]
|
||||
then
|
||||
rm -f /sbin/meshcmd /lib/systemd/system/meshcentral.service
|
||||
systemctl disable meshcentral
|
||||
systemctl stop meshcentral
|
||||
else
|
||||
rm -f /sbin/meshcmd /etc/rc2.d/S20mesh /etc/rc3.d/S20mesh /etc/rc5.d/S20mesh
|
||||
fi
|
||||
|
||||
if [ -e $installpath ]
|
||||
then
|
||||
cd $installpath
|
||||
if [ -e "$installpath/meshagent" ]
|
||||
then
|
||||
./meshagent stop
|
||||
fi
|
||||
rm -rf $installpath/*
|
||||
rmdir $installpath
|
||||
fi
|
||||
echo "Agent uninstalled."
|
||||
}
|
||||
|
||||
|
||||
CheckStartupType
|
||||
starttype=$?
|
||||
#echo "Type: $starttype"
|
||||
|
||||
currentuser=$( whoami )
|
||||
if [ $currentuser == 'root' ]
|
||||
then
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
echo -e "This script will install or uninstall a mesh agent, usage:\n $0 [serverurl] [meshid]\n $0 uninstall"
|
||||
else
|
||||
if [ $# -eq 1 ]
|
||||
then
|
||||
if [ $1 == 'uninstall' ] || [ $1 == 'UNINSTALL' ]
|
||||
then
|
||||
UninstallAgent
|
||||
fi
|
||||
else
|
||||
CheckInstallAgent $1 $2
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Must be root to install or uninstall mesh agent."
|
||||
fi
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
Copyright 2017 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
function createMeshCore(agent) {
|
||||
var obj = {};
|
||||
|
||||
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
|
||||
obj.meshCoreInfo = "TinyCore v1";
|
||||
|
||||
if (agent == null) {
|
||||
// If we are running in Duktape, agent will be null
|
||||
var mesh = require('MeshAgent');
|
||||
} else {
|
||||
// Running in nodejs
|
||||
var mesh = agent.getMeshApi();
|
||||
}
|
||||
|
||||
// Handle a mesh agent command
|
||||
function handleServerCommand(data) {
|
||||
if ((typeof data == 'object') && (data.action == 'msg') && (data.type == 'console') && data.value && data.sessionid) {
|
||||
mesh.SendCommand({ "action": "msg", "type": "console", "value": "Tiny core: " + data.value, "sessionid": data.sessionid });
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the server connection state changes
|
||||
function handleServerConnection(state) {
|
||||
if (state == 1) { mesh.SendCommand({ "action": "coreinfo", "value": obj.meshCoreInfo }); } // Server connected, send mesh core information
|
||||
}
|
||||
|
||||
obj.start = function() {
|
||||
// Hook up mesh agent events
|
||||
mesh.AddCommandHandler(handleServerCommand);
|
||||
mesh.AddConnectHandler(handleServerConnection);
|
||||
mesh.SendCommand({ "action": "coreinfo", "value": obj.meshCoreInfo }); // TODO: Check if connected before sending
|
||||
}
|
||||
|
||||
obj.stop = function() {
|
||||
mesh.AddCommandHandler(null);
|
||||
mesh.AddConnectHandler(null);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
var xexports = null;
|
||||
try { xexports = module.exports; } catch (e) { }
|
||||
|
||||
if (xexports != null) {
|
||||
// If we are running within NodeJS, export the core
|
||||
module.exports.createMeshCore = createMeshCore;
|
||||
} else {
|
||||
// If we are not running in NodeJS, launch the core
|
||||
createMeshCore().start(null);
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* @description Meshcentral Intel AMT Event Parser
|
||||
* @author Ylian Saint-Hilaire & Bryan Roe
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
// Construct a MeshAgent object, called upon connection
|
||||
module.exports.CreateAmtEventsHandler = function (parent) {
|
||||
var obj = {};
|
||||
obj.parent = parent;
|
||||
|
||||
// Private method
|
||||
function ParseWsman(xml) {
|
||||
try {
|
||||
if (!xml.childNodes) xml = _turnToXml(xml);
|
||||
var r = { Header: {} }, header = xml.getElementsByTagName("Header")[0], t;
|
||||
if (!header) header = xml.getElementsByTagName("a:Header")[0];
|
||||
if (!header) return null;
|
||||
for (var i = 0; i < header.childNodes.length; i++) {
|
||||
var child = header.childNodes[i];
|
||||
r.Header[child.localName] = child.textContent;
|
||||
}
|
||||
var body = xml.getElementsByTagName("Body")[0];
|
||||
if (!body) body = xml.getElementsByTagName("a:Body")[0];
|
||||
if (!body) return null;
|
||||
if (body.childNodes.length > 0) {
|
||||
t = body.childNodes[0].localName;
|
||||
if (t.indexOf("_OUTPUT") == t.length - 7) { t = t.substring(0, t.length - 7); }
|
||||
r.Header['Method'] = t;
|
||||
r.Body = _ParseWsmanRec(body.childNodes[0]);
|
||||
}
|
||||
return r;
|
||||
} catch (e) {
|
||||
console.log("Unable to parse XML: " + xml);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Private method
|
||||
function _ParseWsmanRec(node) {
|
||||
var data, r = {};
|
||||
for (var i = 0; i < node.childNodes.length; i++) {
|
||||
var child = node.childNodes[i];
|
||||
if (child.childNodes == null) { data = child.textContent; } else { data = _ParseWsmanRec(child); }
|
||||
if (data == 'true') data = true; // Convert 'true' into true
|
||||
if (data == 'false') data = false; // Convert 'false' into false
|
||||
|
||||
var childObj = data;
|
||||
if (child.attributes != null) {
|
||||
childObj = { 'Value': data };
|
||||
for (var j = 0; j < child.attributes.length; j++) {
|
||||
childObj['@' + child.attributes[j].name] = child.attributes[j].value;
|
||||
}
|
||||
}
|
||||
|
||||
if (r[child.localName] instanceof Array) { r[child.localName].push(childObj); }
|
||||
else if (r[child.localName] == undefined) { r[child.localName] = childObj; }
|
||||
else { r[child.localName] = [r[child.localName], childObj]; }
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Private method
|
||||
function _turnToXml(text) {
|
||||
var DOMParser = require('xmldom').DOMParser;
|
||||
return new DOMParser().parseFromString(text, 'text/xml');
|
||||
}
|
||||
|
||||
// Parse and handle an event coming from Intel AMT
|
||||
obj.handleAmtEvent = function (data, nodeid, amthost) {
|
||||
var x = ParseWsman(data);
|
||||
if (x != null) {
|
||||
// TODO: Dispatch this event, we need to keep a running Intel AMT log for each machine.
|
||||
console.log('Got Intel AMT event from ' + amthost + ', nodeid: ' + nodeid.substring(0, 8));
|
||||
//console.log(x);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
// DEBUG: This is an example event, to test parsing and dispatching
|
||||
//obj.handleAmtEvent('<?xml version="1.0" encoding="UTF-8"?><a:Envelope xmlns:a="http://www.w3.org/2003/05/soap-envelope" xmlns:b="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:c="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:d="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:e="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:f="http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd" xmlns:g="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_AlertIndication" xmlns:h="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><a:Header><b:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</b:To><b:ReplyTo><b:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</b:Address></b:ReplyTo><c:AckRequested></c:AckRequested><b:Action a:mustUnderstand="true">http://schemas.dmtf.org/wbem/wsman/1/wsman/Event</b:Action><b:MessageID>uuid:00000000-8086-8086-8086-000000128538</b:MessageID><c:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_AlertIndication</c:ResourceURI></a:Header><a:Body><g:CIM_AlertIndication><g:AlertType>8</g:AlertType><g:AlertingElementFormat>2</g:AlertingElementFormat><g:AlertingManagedElement>Interop:CIM_ComputerSystem.CreationClassName="CIM_ComputerSystem",Name="Intel(r) AMT"</g:AlertingManagedElement><g:IndicationFilterName>Intel(r) AMT:AllEvents</g:IndicationFilterName><g:IndicationIdentifier>Intel(r):2950234687</g:IndicationIdentifier><g:IndicationTime><h:Datetime>2017-01-31T15:40:09.000Z</h:Datetime></g:IndicationTime><g:Message></g:Message><g:MessageArguments>0</g:MessageArguments><g:MessageArguments>Interop:CIM_ComputerSystem.CreationClassName=CIM_ComputerSystem,Name=Intel(r) AMT</g:MessageArguments><g:MessageID>iAMT0005</g:MessageID><g:OtherAlertingElementFormat></g:OtherAlertingElementFormat><g:OtherSeverity></g:OtherSeverity><g:OwningEntity>Intel(r) AMT</g:OwningEntity><g:PerceivedSeverity>2</g:PerceivedSeverity><g:ProbableCause>0</g:ProbableCause><g:SystemName>Intel(r) AMT</g:SystemName></g:CIM_AlertIndication></a:Body></a:Envelope>', 'aabbccdd', '1.2.3.4');
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
/**
|
||||
* @description Meshcentral Intel AMT Local Scanner
|
||||
* @author Ylian Saint-Hilaire & Joko Sastriawan
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
// Construct a Intel AMT Scanner object
|
||||
module.exports.CreateAmtScanner = function (parent) {
|
||||
var obj = {};
|
||||
obj.active = false;
|
||||
obj.parent = parent;
|
||||
obj.dns = require('dns');
|
||||
obj.dgram = require('dgram');
|
||||
obj.common = require('./common.js');
|
||||
obj.servers = {};
|
||||
obj.rserver = {};
|
||||
obj.rpacket = null;
|
||||
obj.tagToId = {}; // Tag --> { lastpong: time, id: NodeId }
|
||||
obj.scanTable = {}; // NodeId --> ScanInfo : { lastping: time, lastpong: time, nodeinfo:{node} }
|
||||
obj.scanTableTags = {}; // Tag --> ScanInfo
|
||||
obj.pendingSends = []; // We was to stagger the sends using a 10ms timer
|
||||
obj.pendingSendTimer = null;
|
||||
obj.mainTimer = null;
|
||||
obj.nextTag = 0;
|
||||
var PeriodicScanTime = 30000; // Interval between scan sweeps
|
||||
var PeriodicScanTimeout = 65000; // After this time, timeout the device.
|
||||
|
||||
// Build a RMCP packet with a given tag field
|
||||
obj.buildRmcpPing = function (tag) {
|
||||
var packet = new Buffer(obj.common.hex2rstr('06000006000011BE80000000'), 'ascii');
|
||||
packet[9] = tag;
|
||||
return packet;
|
||||
}
|
||||
|
||||
// Start scanning for local network Intel AMT computers
|
||||
obj.start = function () {
|
||||
obj.active = true;
|
||||
obj.performScan();
|
||||
obj.mainTimer = setInterval(obj.performScan, PeriodicScanTime);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Stop scanning for local network Intel AMT computers
|
||||
obj.stop = function () {
|
||||
obj.active = false;
|
||||
for (var i in obj.servers) { obj.servers[i].close(); } // Stop all servers
|
||||
obj.servers = {};
|
||||
if (obj.mainTimer != null) { clearInterval(obj.mainTimer); obj.mainTimer = null; }
|
||||
}
|
||||
|
||||
// Scan for Intel AMT computers using network multicast
|
||||
obj.performRangeScan = function (userid, rangestr) {
|
||||
if (obj.rpacket == null) { obj.rpacket = obj.buildRmcpPing(0); }
|
||||
var range = obj.parseIpv4Range(rangestr);
|
||||
//console.log(obj.IPv4NumToStr(range.min), obj.IPv4NumToStr(range.max));
|
||||
if (range == null || (range.min > range.max)) return false;
|
||||
var rangeinfo = { id: userid, range: rangestr, min: range.min, max: range.max, results: {} };
|
||||
obj.rserver[userid] = rangeinfo;
|
||||
rangeinfo.server = obj.dgram.createSocket("udp4");
|
||||
rangeinfo.server.bind(0);
|
||||
rangeinfo.server.on('error', (err) => { console.log(err); });
|
||||
rangeinfo.server.on('message', (data, rinfo) => { obj.parseRmcpPacket(data, rinfo, 0, obj.reportMachineState, rangeinfo); });
|
||||
rangeinfo.server.on('listening', () => {
|
||||
for (var i = rangeinfo.min; i <= rangeinfo.max; i++) { rangeinfo.server.send(obj.rpacket, 623, obj.IPv4NumToStr(i)); }
|
||||
});
|
||||
rangeinfo.timer = setTimeout(function () {
|
||||
obj.parent.DispatchEvent(['*', userid], obj, { action: 'scanamtdevice', range: rangeinfo.range, results: rangeinfo.results, nolog: 1 });
|
||||
rangeinfo.server.close();
|
||||
delete rangeinfo.server;
|
||||
delete rangeinfo;
|
||||
}, 3000);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse range, used to parse "ip", "ip/mask" or "ip-ip" notation.
|
||||
// Return the start and end value of the scan
|
||||
obj.parseIpv4Range = function (range) {
|
||||
if (range == undefined || range == null) return null;
|
||||
var x = range.split('-');
|
||||
if (x.length == 2) { return { min: obj.parseIpv4Addr(x[0]), max: obj.parseIpv4Addr(x[1]) }; }
|
||||
x = range.split('/');
|
||||
if (x.length == 2) {
|
||||
var ip = obj.parseIpv4Addr(x[0]), masknum = parseInt(x[1]), mask = 0;
|
||||
if (masknum <= 16 || masknum > 32) return null;
|
||||
masknum = 32 - masknum;
|
||||
for (var i = 0; i < masknum; i++) { mask = (mask << 1); mask++; }
|
||||
return { min: ip & (0xFFFFFFFF - mask), max: (ip & (0xFFFFFFFF - mask)) + mask };
|
||||
}
|
||||
x = obj.parseIpv4Addr(range);
|
||||
if (x == null) return null;
|
||||
return { min: x, max: x };
|
||||
}
|
||||
|
||||
// Parse IP address. Takes a
|
||||
obj.parseIpv4Addr = function (addr) {
|
||||
var x = addr.split('.');
|
||||
if (x.length == 4) { return (parseInt(x[0]) << 24) + (parseInt(x[1]) << 16) + (parseInt(x[2]) << 8) + (parseInt(x[3]) << 0); }
|
||||
return null;
|
||||
}
|
||||
|
||||
// IP address number to string
|
||||
obj.IPv4NumToStr = function (num) {
|
||||
return ((num >> 24) & 0xFF) + '.' + ((num >> 16) & 0xFF) + '.' + ((num >> 8) & 0xFF) + '.' + (num & 0xFF);
|
||||
}
|
||||
|
||||
// Look for all AMT computers that may be locally reachable and poll their presence
|
||||
obj.performScan = function () {
|
||||
if (obj.action == false) { return false; }
|
||||
obj.parent.db.getLocalAmtNodes(function (err, docs) {
|
||||
for (var i in obj.scanTable) { obj.scanTable[i].present = false; }
|
||||
if (err == null && docs.length > 0) {
|
||||
for (var i in docs) {
|
||||
var doc = docs[i];
|
||||
var host = doc.host.toLowerCase();
|
||||
if ((host != '127.0.0.1') && (host != '::1') && (host != 'localhost')) { // Don't scan localhost
|
||||
var scaninfo = obj.scanTable[doc._id];
|
||||
if (scaninfo == undefined) {
|
||||
var tag = obj.nextTag++;
|
||||
obj.scanTableTags[tag] = obj.scanTable[doc._id] = scaninfo = { nodeinfo: doc, present: true, tag: tag, state: 0 };
|
||||
} else {
|
||||
scaninfo.present = true;
|
||||
if (scaninfo.state == 1) {
|
||||
var delta = Date.now() - scaninfo.lastpong;
|
||||
if (delta > PeriodicScanTimeout) { // More than 10 seconds without a response, mark the node as unknown state
|
||||
scaninfo.state = 0;
|
||||
obj.parent.ClearConnectivityState(scaninfo.nodeinfo.meshid, scaninfo.nodeinfo._id, 4); // Clear connectivity state
|
||||
}
|
||||
}
|
||||
}
|
||||
// Start scanning this node
|
||||
scaninfo.lastping = Date.now();
|
||||
obj.checkAmtPresence(doc.host, scaninfo.tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i in obj.scanTable) {
|
||||
if (obj.scanTable[i].present == false) {
|
||||
// Stop scanning this node
|
||||
delete obj.scanTableTags[obj.scanTable[i].tag];
|
||||
delete obj.scanTable[i];
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the presense of a specific Intel AMT computer
|
||||
obj.checkAmtPresence = function (host, tag) {
|
||||
var serverid = Math.floor(tag / 255);
|
||||
var servertag = (tag % 255);
|
||||
var packet = obj.buildRmcpPing(servertag);
|
||||
var server = obj.servers[serverid];
|
||||
if (server == undefined) {
|
||||
// Start new server
|
||||
server = obj.dgram.createSocket('udp4');
|
||||
server.on('error', (err) => { });
|
||||
server.on('message', (data, rinfo) => { obj.parseRmcpPacket(data, rinfo, serverid, obj.changeConnectState, null); });
|
||||
server.on('listening', () => {
|
||||
obj.pendingSends.push([ server, packet, host ]);
|
||||
if (obj.pendingSendTimer == null) { obj.pendingSendTimer = setInterval(obj.sendPendingPacket, 10); }
|
||||
});
|
||||
server.bind(0);
|
||||
obj.servers[serverid] = server;
|
||||
} else {
|
||||
// Use existing server
|
||||
obj.pendingSends.push([ server, packet, host ]);
|
||||
if (obj.pendingSendTimer == null) { obj.pendingSendTimer = setInterval(obj.sendPendingPacket, 10); }
|
||||
}
|
||||
}
|
||||
|
||||
// Send a pending RMCP packet
|
||||
obj.sendPendingPacket = function() {
|
||||
try {
|
||||
var p = obj.pendingSends.shift();
|
||||
if (p != undefined) {
|
||||
p[0].send(p[1], 623, p[2]);
|
||||
p[0].send(p[1], 623, p[2]);
|
||||
} else {
|
||||
clearInterval(obj.pendingSendTimer);
|
||||
obj.pendingSendTimer = null;
|
||||
}
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
// Parse RMCP packet
|
||||
obj.parseRmcpPacket = function (data, rinfo, serverid, func, user) {
|
||||
if (data == null || data.length < 20) return;
|
||||
if (((data[12] == 0) || (data[13] != 0) || (data[14] != 1) || (data[15] != 0x57)) && (data[21] & 32)) {
|
||||
var servertag = data[9];
|
||||
var tag = (serverid * 255) + servertag;
|
||||
var minorVersion = data[18] & 0x0F;
|
||||
var majorVersion = (data[18] >> 4) & 0x0F;
|
||||
var provisioningState = data[19] & 0x03; // Pre = 0, In = 1, Post = 2
|
||||
|
||||
var openPort = (data[16] * 256) + data[17];
|
||||
var dualPorts = ((data[19] & 0x04) != 0) ? true : false;
|
||||
var openPorts = [openPort];
|
||||
if (dualPorts == true) { openPorts = [16992, 16993]; }
|
||||
if (provisioningState <= 2) { func(tag, minorVersion, majorVersion, provisioningState, openPort, dualPorts, rinfo, user); }
|
||||
}
|
||||
}
|
||||
|
||||
// Use the RMCP packet to change the computer state
|
||||
obj.changeConnectState = function (tag, minorVersion, majorVersion, provisioningState, openPort, dualPorts, rinfo, user) {
|
||||
//var provisioningStates = { 0: 'Pre', 1: 'in', 2: 'Post' };
|
||||
//var provisioningStateStr = provisioningStates[provisioningState];
|
||||
//console.log('Intel AMT ' + majorVersion + '.' + minorVersion + ', ' + provisioningStateStr + '-Provisioning at ' + rinfo.address + ', Open Ports: [' + openPorts.join(', ') + '], tag: ' + tag);
|
||||
var scaninfo = obj.scanTableTags[tag];
|
||||
if (scaninfo != undefined) {
|
||||
scaninfo.lastpong = Date.now();
|
||||
if (scaninfo.state == 0) {
|
||||
scaninfo.state = 1;
|
||||
scaninfo.nodeinfo.intelamt.tls = (((openPort == 16993) || (dualPorts == true)) ? 1 : 0);
|
||||
scaninfo.nodeinfo.intelamt.ver = majorVersion + '.' + minorVersion;
|
||||
scaninfo.nodeinfo.intelamt.state = provisioningState;
|
||||
obj.parent.SetConnectivityState(scaninfo.nodeinfo.meshid, scaninfo.nodeinfo._id, scaninfo.lastpong, 4, 7); // Report power state as "present" (7).
|
||||
obj.changeAmtState(scaninfo.nodeinfo._id, scaninfo.nodeinfo.intelamt.ver, provisioningState, scaninfo.nodeinfo.intelamt.tls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the RMCP packet to change the computer state
|
||||
obj.reportMachineState = function (tag, minorVersion, majorVersion, provisioningState, openPort, dualPorts, rinfo, user) {
|
||||
//var provisioningStates = { 0: 'Pre', 1: 'in', 2: 'Post' };
|
||||
//var provisioningStateStr = provisioningStates[provisioningState];
|
||||
//console.log(rinfo.address + ': Intel AMT ' + majorVersion + '.' + minorVersion + ', ' + provisioningStateStr + '-Provisioning, Open Ports: [' + openPorts.join(', ') + ']');
|
||||
obj.dns.reverse(rinfo.address, function (err, hostname) {
|
||||
if ((err != undefined) && (hostname != undefined)) {
|
||||
user.results[rinfo.address] = { ver: majorVersion + '.' + minorVersion, tls: (((openPort == 16993) || (dualPorts == true)) ? 1 : 0), state: provisioningState, hostname: hostname[0] };
|
||||
} else {
|
||||
user.results[rinfo.address] = { ver: majorVersion + '.' + minorVersion, tls: (((openPort == 16993) || (dualPorts == true)) ? 1 : 0), state: provisioningState, hostname: rinfo.address };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Change Intel AMT information in the database and event the changes
|
||||
obj.changeAmtState = function (nodeid, version, provisioningState, tls) {
|
||||
//console.log('changeAmtState', nodeid, version, provisioningState, tls);
|
||||
obj.parent.db.Get(nodeid, function (err, nodes) {
|
||||
if (nodes.length != 1) return;
|
||||
var node = nodes[0];
|
||||
|
||||
// Get the mesh for this device
|
||||
obj.parent.db.Get(node.meshid, function (err, meshes) {
|
||||
if (meshes.length != 1) return;
|
||||
var mesh = meshes[0];
|
||||
|
||||
// Ready the node change event
|
||||
var changes = [], event = { etype: 'node', action: 'changenode', nodeid: node._id };
|
||||
event.msg = +": ";
|
||||
|
||||
// Make the change & save
|
||||
var change = false;
|
||||
if (node.intelamt == undefined) { node.intelamt = {}; }
|
||||
if (node.intelamt.tls != tls) { node.intelamt.tls = tls; change = true; changes.push(tls==1?'TLS':'NoTLS'); }
|
||||
if (obj.compareAmtVersionStr(node.intelamt.ver, version)) { node.intelamt.ver = version; change = true; changes.push('Version ' + version); }
|
||||
if (node.intelamt.state != provisioningState) { node.intelamt.state = provisioningState; change = true; changes.push('State'); }
|
||||
if (change == true) {
|
||||
// Make the change in the database
|
||||
obj.parent.db.Set(node);
|
||||
|
||||
// Event the node change
|
||||
event.msg = 'Intel® AMT changed device ' + node.name + ' from mesh ' + mesh.name + ': ' + changes.join(', ');
|
||||
var node2 = obj.parent.common.Clone(node);
|
||||
if (node2.intelamt && node2.intelamt.pass) delete node2.intelamt.pass; // Remove the Intel AMT password before eventing this.
|
||||
event.node = node2;
|
||||
obj.parent.DispatchEvent(['*', node.meshid], obj, event);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Return true if we should change the Intel AMT version number
|
||||
obj.compareAmtVersionStr = function (oldVer, newVer) {
|
||||
if (oldVer == newVer) return false; // Versions are same already, don't update.
|
||||
if (newVer == undefined || newVer == null) return false; // New version is bad, don't update it.
|
||||
if (oldVer == undefined || oldVer == null) return true; // Old version is no good anyway, update it.
|
||||
var oldVerArr = oldVer.split('.');
|
||||
var newVerArr = newVer.split('.');
|
||||
if ((oldVerArr.length < 2) || (newVerArr.length < 2)) return false;
|
||||
if ((oldVerArr[0] != newVerArr[0]) || (oldVerArr[1] != newVerArr[1])) return true;
|
||||
if (newVerArr.length > oldVerArr.length) return true;
|
||||
if ((newVerArr.length == 3) && (oldVerArr.length == 3) && (oldVerArr[2] != newVerArr[2])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,469 @@
|
|||
/**
|
||||
* @fileoverview Script Compiler / Decompiler / Runner
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.1.0e
|
||||
*/
|
||||
|
||||
module.exports.CreateAmtScriptEngine = function () {
|
||||
var o = {};
|
||||
|
||||
// Core functions
|
||||
script_functionTable1 = ['nop', 'jump', 'set', 'print', 'dialog', 'getitem', 'substr', 'indexof', 'split', 'join', 'length', 'jsonparse', 'jsonstr', 'add', 'substract', 'parseint', 'wsbatchenum', 'wsput', 'wscreate', 'wsdelete', 'wsexec', 'scriptspeed', 'wssubscribe', 'wsunsubscribe', 'readchar', 'signwithdummyca'];
|
||||
|
||||
// functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
script_functionTable2 = ['encodeuri', 'decodeuri', 'passwordcheck', 'atob', 'btoa', 'hex2str', 'str2hex', 'random', 'md5', 'maketoarray', 'readshort', 'readshortx', 'readint', 'readsint', 'readintx', 'shorttostr', 'shorttostrx', 'inttostr', 'inttostrx'];
|
||||
|
||||
// functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
//script_functionTableX2 = [encodeURI, decodeURI, passwordcheck, window.atob.bind(window), window.btoa.bind(window), hex2rstr, rstr2hex, random, rstr_md5, MakeToArray, ReadShort, ReadShortX, ReadInt, ReadSInt, ReadIntX, ShortToStr, ShortToStrX, IntToStr, IntToStrX];
|
||||
|
||||
// Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
script_functionTable3 = ['pullsystemstatus', 'pulleventlog', 'pullauditlog', 'pullcertificates', 'pullwatchdog', 'pullsystemdefense', 'pullhardware', 'pulluserinfo', 'pullremoteaccess', 'highlightblock', 'disconnect', 'getsidstring', 'getsidbytearray'];
|
||||
|
||||
/*
|
||||
// Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
script_functionTableX3 = [
|
||||
PullSystemStatus
|
||||
,
|
||||
// ###BEGIN###{EventLog}
|
||||
PullEventLog
|
||||
// ###END###{EventLog}
|
||||
,
|
||||
// ###BEGIN###{AuditLog}
|
||||
PullAuditLog
|
||||
// ###END###{AuditLog}
|
||||
,
|
||||
// ###BEGIN###{Certificates}
|
||||
PullCertificates
|
||||
// ###END###{Certificates}
|
||||
,
|
||||
// ###BEGIN###{AgentPresence}
|
||||
PullWatchdog
|
||||
// ###END###{AgentPresence}
|
||||
,
|
||||
// ###BEGIN###{SystemDefense}
|
||||
PullSystemDefense
|
||||
// ###END###{SystemDefense}
|
||||
,
|
||||
// ###BEGIN###{HardwareInfo}
|
||||
PullHardware
|
||||
// ###END###{HardwareInfo}
|
||||
,
|
||||
PullUserInfo
|
||||
,
|
||||
// ###BEGIN###{RemoteAccess}
|
||||
PullRemoteAccess
|
||||
// ###END###{RemoteAccess}
|
||||
,
|
||||
// ###BEGIN###{Scripting-Editor}
|
||||
script_HighlightBlock
|
||||
// ###END###{Scripting-Editor}
|
||||
,
|
||||
// ###BEGIN###{ComputerSelector}
|
||||
disconnect
|
||||
// ###END###{ComputerSelector}
|
||||
,
|
||||
function (runner, x) { return GetSidString(x); }
|
||||
,
|
||||
function (runner, x) { return GetSidByteArray(x); }
|
||||
];
|
||||
|
||||
// Setup the script state
|
||||
o.script_setup = function(binary, startvars) {
|
||||
var obj = { startvars: startvars };
|
||||
if (binary.length < 6) { console.error('Invalid script length'); return null; } // Script must have at least 6 byte header
|
||||
if (ReadInt(binary, 0) != 0x247D2945) { console.error('Invalid binary script'); return null; } // Check the script magic header
|
||||
if (ReadShort(binary, 4) > 1) { console.error('Unsupported script version'); return null; } // Check the script version
|
||||
obj.script = binary.substring(6);
|
||||
// obj.onStep;
|
||||
// obj.onConsole;
|
||||
|
||||
// Reset the script to the start
|
||||
obj.reset = function (stepspeed) {
|
||||
obj.stop();
|
||||
obj.ip = 0;
|
||||
obj.variables = startvars;
|
||||
obj.state = 1;
|
||||
}
|
||||
|
||||
// Start the script
|
||||
obj.start = function (stepspeed) {
|
||||
obj.stop();
|
||||
obj.stepspeed = stepspeed;
|
||||
if (stepspeed > 0) { obj.timer = setInterval(function () { obj.step() }, stepspeed); }
|
||||
}
|
||||
|
||||
// Stop the script
|
||||
obj.stop = function () {
|
||||
if (obj.timer != null) { clearInterval(obj.timer); }
|
||||
obj.timer = null;
|
||||
obj.stepspeed = 0;
|
||||
}
|
||||
|
||||
// function used to load and store variable values
|
||||
obj.getVar = function (name) { if (name == undefined) return undefined; return obj.getVarEx(name.split('.'), obj.variables); }
|
||||
obj.getVarEx = function (name, val) { try { if (name == undefined) return undefined; if (name.length == 0) return val; return obj.getVarEx(name.slice(1), val[name[0]]); } catch (e) { return null; } }
|
||||
obj.setVar = function (name, val) { obj.setVarEx(name.split('.'), obj.variables, val); }
|
||||
obj.setVarEx = function (name, vars, val) { if (name.length == 1) { vars[name[0]] = val; } else { obj.setVarEx(name.slice(1), vars[name[0]], val); } }
|
||||
|
||||
// Run the script one step forward
|
||||
obj.step = function () {
|
||||
if (obj.state != 1) return;
|
||||
if (obj.ip < obj.script.length) {
|
||||
var cmdid = ReadShort(obj.script, obj.ip);
|
||||
var cmdlen = ReadShort(obj.script, obj.ip + 2);
|
||||
var argcount = ReadShort(obj.script, obj.ip + 4);
|
||||
var argptr = obj.ip + 6;
|
||||
var args = [];
|
||||
|
||||
// Clear all temp variables (This is optional)
|
||||
for (var i in obj.variables) { if (i.startsWith('__')) { delete obj.variables[i]; } }
|
||||
|
||||
// Loop on each argument, moving forward by the argument length each time
|
||||
for (var i = 0; i < argcount; i++) {
|
||||
var arglen = ReadShort(obj.script, argptr);
|
||||
var argval = obj.script.substring(argptr + 2, argptr + 2 + arglen);
|
||||
var argtyp = argval.charCodeAt(0);
|
||||
argval = argval.substring(1);
|
||||
if (argtyp < 2) {
|
||||
// Get the value and replace all {var} with variable values
|
||||
while (argval.split("{").length > 1) { var t = argval.split("{").pop().split("}").shift(); argval = argval.replace('{' + t + '}', obj.getVar(t)); }
|
||||
if (argtyp == 1) { obj.variables['__' + i] = decodeURI(argval); argval = '__' + i; } // If argtyp is 1, this is a literal. Store in temp variable.
|
||||
args.push(argval);
|
||||
}
|
||||
if (argtyp == 2 || argtyp == 3) {
|
||||
obj.variables['__' + i] = ReadSInt(argval, 0);
|
||||
args.push('__' + i);
|
||||
}
|
||||
argptr += (2 + arglen);
|
||||
}
|
||||
|
||||
// Move instruction pointer forward by command size
|
||||
obj.ip += cmdlen;
|
||||
|
||||
// Get all variable values
|
||||
var argsval = [];
|
||||
for (var i = 0; i < 10; i++) { argsval.push(obj.getVar(args[i])); }
|
||||
var storeInArg0;
|
||||
|
||||
try {
|
||||
if (cmdid < 10000) {
|
||||
// Lets run the actual command
|
||||
switch (cmdid) {
|
||||
case 0: // nop
|
||||
break;
|
||||
case 1: // jump(label) or jump(label, a, compare, b)
|
||||
if (argsval[2]) {
|
||||
if (
|
||||
(argsval[2] == '<' && argsval[1] < argsval[3]) ||
|
||||
(argsval[2] == '<=' && argsval[1] <= argsval[3]) ||
|
||||
(argsval[2] == '!=' && argsval[1] != argsval[3]) ||
|
||||
(argsval[2] == '=' && argsval[1] == argsval[3]) ||
|
||||
(argsval[2] == '>=' && argsval[1] >= argsval[3]) ||
|
||||
(argsval[2] == '>' && argsval[1] > argsval[3])
|
||||
) { obj.ip = argsval[0]; }
|
||||
} else {
|
||||
obj.ip = argsval[0]; // Set the instruction pointer to the new location in the script
|
||||
}
|
||||
break;
|
||||
case 2: // set(variable, value)
|
||||
if (args[1] == undefined) delete obj.variables[args[0]]; else obj.setVar(args[0], argsval[1]);
|
||||
break;
|
||||
case 3: // print(message)
|
||||
if (obj.onConsole) { obj.onConsole(obj.toString(argsval[0]), obj); } else { console.log(obj.toString(argsval[0])); }
|
||||
// Q(obj.consoleid).value += () + '\n'); Q(obj.console).scrollTop = Q(obj.console).scrollHeight;
|
||||
break;
|
||||
case 4: // dialog(title, content, buttons)
|
||||
obj.state = 2;
|
||||
obj.dialog = true;
|
||||
setDialogMode(11, argsval[0], argsval[2], obj.xxStepDialogOk, argsval[1], obj);
|
||||
break;
|
||||
case 5: // getitem(a, b, c)
|
||||
for (var i in argsval[1]) { if (argsval[1][i][argsval[2]] == argsval[3]) { storeInArg0 = i; } };
|
||||
break;
|
||||
case 6: // substr(variable_dest, variable_src, index, len)
|
||||
storeInArg0 = argsval[1].substr(argsval[2], argsval[3]);
|
||||
break;
|
||||
case 7: // indexOf(variable_dest, variable_src, index, len)
|
||||
storeInArg0 = argsval[1].indexOf(argsval[2]);
|
||||
break;
|
||||
case 8: // split(variable_dest, variable_src, separator)
|
||||
storeInArg0 = argsval[1].split(argsval[2]);
|
||||
break;
|
||||
case 9: // join(variable_dest, variable_src, separator)
|
||||
storeInArg0 = argsval[1].join(argsval[2]);
|
||||
break;
|
||||
case 10: // length(variable_dest, variable_src)
|
||||
storeInArg0 = argsval[1].length;
|
||||
break;
|
||||
case 11: // jsonparse(variable_dest, json)
|
||||
storeInArg0 = JSON.parse(argsval[1]);
|
||||
break;
|
||||
case 12: // jsonstr(variable_dest, variable_src)
|
||||
storeInArg0 = JSON.stringify(argsval[1]);
|
||||
break;
|
||||
case 13: // add(variable_dest, variable_src, value)
|
||||
storeInArg0 = (argsval[1] + argsval[2]);
|
||||
break;
|
||||
case 14: // substract(variable_dest, variable_src, value)
|
||||
storeInArg0 = (argsval[1] - argsval[2]);
|
||||
break;
|
||||
case 15: // parseInt(variable_dest, variable_src)
|
||||
storeInArg0 = parseInt(argsval[1]);
|
||||
break;
|
||||
case 16: // wsbatchenum(name, objectList)
|
||||
obj.state = 2;
|
||||
obj.amtstack.BatchEnum(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
|
||||
break;
|
||||
case 17: // wsput(name, args)
|
||||
obj.state = 2;
|
||||
obj.amtstack.Put(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
|
||||
break;
|
||||
case 18: // wscreate(name, args)
|
||||
obj.state = 2;
|
||||
obj.amtstack.Create(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
|
||||
break;
|
||||
case 19: // wsdelete(name, args)
|
||||
obj.state = 2;
|
||||
obj.amtstack.Delete(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
|
||||
break;
|
||||
case 20: // wsexec(name, method, args, selectors)
|
||||
obj.state = 2;
|
||||
obj.amtstack.Exec(argsval[0], argsval[1], argsval[2], obj.xxWsmanReturn, obj, 0, argsval[3]);
|
||||
break;
|
||||
case 21: // Script Speed
|
||||
obj.stepspeed = argsval[0];
|
||||
if (obj.timer != null) { clearInterval(obj.timer); obj.timer = setInterval(function () { obj.step() }, obj.stepspeed); }
|
||||
break;
|
||||
case 22: // wssubscribe(name, delivery, url, selectors, opaque, user, pass)
|
||||
obj.state = 2;
|
||||
obj.amtstack.Subscribe(argsval[0], argsval[1], argsval[2], obj.xxWsmanReturn, obj, 0, argsval[3], argsval[4], argsval[5], argsval[6]);
|
||||
break;
|
||||
case 23: // wsunsubscribe(name, selectors)
|
||||
obj.state = 2;
|
||||
obj.amtstack.UnSubscribe(argsval[0], obj.xxWsmanReturn, obj, 0, argsval[1]);
|
||||
break;
|
||||
case 24: // readchar(str, pos)
|
||||
console.log(argsval[1], argsval[2], argsval[1].charCodeAt(argsval[2]));
|
||||
storeInArg0 = argsval[1].charCodeAt(argsval[2]);
|
||||
break;
|
||||
case 25: // signWithDummyCa
|
||||
// ###BEGIN###{Certificates}
|
||||
obj.state = 2;
|
||||
// DERKey, xxCaPrivateKey, certattributes, issuerattributes
|
||||
amtcert_signWithCaKey(argsval[0], null, argsval[1], { 'CN': 'Untrusted Root Certificate' }, obj.xxSignWithDummyCaReturn);
|
||||
// ###END###{Certificates}
|
||||
break;
|
||||
default: {
|
||||
obj.state = 9;
|
||||
console.error("Script Error, unknown command: " + cmdid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cmdid < 20000) {
|
||||
// functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
storeInArg0 = script_functionTableX2[cmdid - 10000](argsval[1], argsval[2], argsval[3], argsval[4], argsval[5], argsval[6]);
|
||||
} else {
|
||||
// Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
if (script_functionTableX3 && script_functionTableX3[cmdid - 20000]) {
|
||||
storeInArg0 = script_functionTableX3[cmdid - 20000](obj, argsval[1], argsval[2], argsval[3], argsval[4], argsval[5], argsval[6]); // Note that optional calls start with "obj" as first argument.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (storeInArg0 != undefined) obj.setVar(args[0], storeInArg0);
|
||||
} catch (e) {
|
||||
if (typeof e == 'object') { e = e.message; }
|
||||
obj.setVar('_exception', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.state == 1 && obj.ip >= obj.script.length) { obj.state = 0; obj.stop(); }
|
||||
if (obj.onStep) obj.onStep(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj.xxStepDialogOk = function (button) {
|
||||
obj.variables['DialogSelect'] = button;
|
||||
obj.state = 1;
|
||||
obj.dialog = false;
|
||||
if (obj.onStep) obj.onStep(obj);
|
||||
}
|
||||
|
||||
// ###BEGIN###{**ClosureAdvancedMode}
|
||||
obj.xxWsmanReturnFix = function (x) {
|
||||
if (!x || x == null) return;
|
||||
if (x.Header) { x['Header'] = x.Header; delete x.Header; }
|
||||
if (x.Body) { x['Body'] = x.Body; delete x.Body; }
|
||||
if (x.Responses) { x['Responses'] = x.Responses; delete x.Responses; }
|
||||
if (x.Response) { x['Response'] = x.Response; delete x.Response; }
|
||||
if (x.ReturnValueStr) { x['ReturnValueStr'] = x.ReturnValueStr; delete x.ReturnValueStr; }
|
||||
}
|
||||
// ###END###{**ClosureAdvancedMode}
|
||||
|
||||
obj.xxWsmanReturn = function (stack, name, responses, status) {
|
||||
// ###BEGIN###{**ClosureAdvancedMode}
|
||||
// This is required when Google Closure is used
|
||||
if (responses) {
|
||||
obj.xxWsmanReturnFix(responses);
|
||||
for (var i in responses) {
|
||||
obj.xxWsmanReturnFix(responses[i]);
|
||||
for (var j in responses[i]) { obj.xxWsmanReturnFix(responses[i][j]); }
|
||||
}
|
||||
}
|
||||
// ###END###{**ClosureAdvancedMode}
|
||||
obj.setVar(name, responses);
|
||||
obj.setVar('wsman_result', status);
|
||||
obj.setVar('wsman_result_str', ((httpErrorTable[status]) ? (httpErrorTable[status]) : ('Error #' + status)));
|
||||
obj.state = 1;
|
||||
if (obj.onStep) obj.onStep(obj);
|
||||
}
|
||||
|
||||
// ###BEGIN###{Certificates}
|
||||
obj.xxSignWithDummyCaReturn = function (cert) {
|
||||
obj.setVar('signed_cert', btoa(_arrayBufferToString(cert)));
|
||||
obj.state = 1;
|
||||
if (obj.onStep) obj.onStep(obj);
|
||||
}
|
||||
// ###END###{Certificates}
|
||||
|
||||
obj.toString = function (x) { if (typeof x == 'object') return JSON.stringify(x); return x; }
|
||||
|
||||
obj.reset();
|
||||
return obj;
|
||||
}
|
||||
*/
|
||||
|
||||
ReadShort = function (v, p) { return (v.charCodeAt(p) << 8) + v.charCodeAt(p + 1); }
|
||||
ReadShortX = function (v, p) { return (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }
|
||||
ReadInt = function (v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); } // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
|
||||
ReadIntX = function (v, p) { return (v.charCodeAt(p + 3) * 0x1000000) + (v.charCodeAt(p + 2) << 16) + (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }
|
||||
ShortToStr = function (v) { return String.fromCharCode((v >> 8) & 0xFF, v & 0xFF); }
|
||||
ShortToStrX = function (v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); }
|
||||
IntToStr = function (v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); }
|
||||
IntToStrX = function (v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); }
|
||||
|
||||
// Argument types: 0 = Variable, 1 = String, 2 = Integer, 3 = Label
|
||||
o.script_compile = function(script, onmsg) {
|
||||
var r = '', scriptlines = script.split('\n'), labels = {}, labelswap = [], swaps = [];
|
||||
// Go thru each script line and encode it
|
||||
for (var i in scriptlines) {
|
||||
var scriptline = scriptlines[i];
|
||||
if (scriptline.startsWith('##SWAP ')) { var x = scriptline.split(' '); if (x.length == 3) { swaps[x[1]] = x[2]; } } // Add a swap instance
|
||||
if (scriptline[0] == '#' || scriptline.length == 0) continue; // Skip comments & blank lines
|
||||
for (var x in swaps) { scriptline = scriptline.split(x).join(swaps[x]); } // Apply all swaps
|
||||
var keywords = scriptline.match(/"[^"]*"|[^\s"]+/g);
|
||||
if ((keywords == null) || (keywords.length == 0)) continue; // Skip blank lines
|
||||
if (scriptline[0] == ':') { labels[keywords[0].toUpperCase()] = r.length; continue; } // Mark a label position
|
||||
var funcIndex = script_functionTable1.indexOf(keywords[0].toLowerCase());
|
||||
if (funcIndex == -1) { funcIndex = script_functionTable2.indexOf(keywords[0].toLowerCase()); if (funcIndex >= 0) funcIndex += 10000; }
|
||||
if (funcIndex == -1) { funcIndex = script_functionTable3.indexOf(keywords[0].toLowerCase()); if (funcIndex >= 0) funcIndex += 20000; } // Optional methods
|
||||
if (funcIndex == -1) { if (onmsg) { onmsg("Unabled to compile, unknown command: " + keywords[0]); } return ''; }
|
||||
// Encode CommandId, CmdSize, ArgCount, Arg1Len, Arg1, Arg2Len, Arg2...
|
||||
var cmd = ShortToStr(keywords.length - 1);
|
||||
for (var j in keywords) {
|
||||
if (j == 0) continue;
|
||||
if (keywords[j][0] == ':') {
|
||||
labelswap.push([keywords[j], r.length + cmd.length + 7]); // Add a label swap
|
||||
cmd += ShortToStr(5) + String.fromCharCode(3) + IntToStr(0xFFFFFFFF); // Put an empty label
|
||||
} else {
|
||||
var argint = parseInt(keywords[j]);
|
||||
if (argint == keywords[j]) {
|
||||
cmd += ShortToStr(5) + String.fromCharCode(2) + IntToStr(argint);
|
||||
} else {
|
||||
if (keywords[j][0] == '"' && keywords[j][keywords[j].length - 1] == '"') {
|
||||
cmd += ShortToStr(keywords[j].length - 1) + String.fromCharCode(1) + keywords[j].substring(1, keywords[j].length - 1);
|
||||
} else {
|
||||
cmd += ShortToStr(keywords[j].length + 1) + String.fromCharCode(0) + keywords[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cmd = ShortToStr(funcIndex) + ShortToStr(cmd.length + 4) + cmd;
|
||||
r += cmd;
|
||||
}
|
||||
// Perform all the needed label swaps
|
||||
for (i in labelswap) {
|
||||
var label = labelswap[i][0].toUpperCase(), position = labelswap[i][1], target = labels[label];
|
||||
if (target == undefined) { if (onmsg) { onmsg("Unabled to compile, unknown label: " + label); } return ''; }
|
||||
r = r.substr(0, position) + IntToStr(target) + r.substr(position + 4);
|
||||
}
|
||||
return IntToStr(0x247D2945) + ShortToStr(1) + r;
|
||||
}
|
||||
|
||||
// Decompile the script, intended for debugging only
|
||||
o.script_decompile = function(binary, onecmd) {
|
||||
var r = '', ptr = 6, labelcount = 0, labels = {};
|
||||
if (onecmd >= 0) {
|
||||
ptr = onecmd; // If we are decompiling just one command, set the ptr to that command.
|
||||
} else {
|
||||
if (binary.length < 6) { return '# Invalid script length'; }
|
||||
var magic = ReadInt(binary, 0);
|
||||
var version = ReadShort(binary, 4);
|
||||
if (magic != 0x247D2945) { return '# Invalid binary script: ' + magic; }
|
||||
if (version != 1) { return '# Invalid script version'; }
|
||||
}
|
||||
// Loop on each command, moving forward by the command length each time.
|
||||
while (ptr < binary.length) {
|
||||
var cmdid = ReadShort(binary, ptr);
|
||||
var cmdlen = ReadShort(binary, ptr + 2);
|
||||
var argcount = ReadShort(binary, ptr + 4);
|
||||
var argptr = ptr + 6;
|
||||
var argstr = '';
|
||||
if (!(onecmd >= 0)) r += ":label" + (ptr - 6) + "\n";
|
||||
// Loop on each argument, moving forward by the argument length each time
|
||||
for (var i = 0; i < argcount; i++) {
|
||||
var arglen = ReadShort(binary, argptr);
|
||||
var argval = binary.substring(argptr + 2, argptr + 2 + arglen);
|
||||
var argtyp = argval.charCodeAt(0);
|
||||
if (argtyp == 0) { argstr += ' ' + argval.substring(1); } // Variable
|
||||
else if (argtyp == 1) { argstr += ' \"' + argval.substring(1) + '\"'; } // String
|
||||
else if (argtyp == 2) { argstr += ' ' + ReadInt(argval, 1); } // Integer
|
||||
else if (argtyp == 3) { // Label
|
||||
var target = ReadInt(argval, 1);
|
||||
var label = labels[target];
|
||||
if (!label) { label = ":label" + target; labels[label] = target; }
|
||||
argstr += ' ' + label;
|
||||
}
|
||||
argptr += (2 + arglen);
|
||||
}
|
||||
// Go in the script function table to decode the function
|
||||
if (cmdid < 10000) {
|
||||
r += script_functionTable1[cmdid] + argstr + "\n";
|
||||
} else {
|
||||
if (cmdid >= 20000) {
|
||||
r += script_functionTable3[cmdid - 20000] + argstr + "\n"; // Optional methods
|
||||
} else {
|
||||
r += script_functionTable2[cmdid - 10000] + argstr + "\n";
|
||||
}
|
||||
}
|
||||
ptr += cmdlen;
|
||||
if (onecmd >= 0) return r; // If we are decompiling just one command, exit now
|
||||
}
|
||||
// Remove all unused labels
|
||||
var scriptlines = r.split('\n');
|
||||
r = '';
|
||||
for (var i in scriptlines) {
|
||||
var line = scriptlines[i];
|
||||
if (line[0] != ':') { r += line + '\n'; } else { if (labels[line]) { r += line + '\n'; } }
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Convert the list of blocks into a script that can be compiled
|
||||
o.script_blocksToScript = function (script_BuildingBlocks, script_BlockScript) {
|
||||
var script = '';
|
||||
if (script_BuildingBlocks) {
|
||||
if (script_BuildingBlocks['_start']) { script += '##### Starting Block #####\r\n' + script_BuildingBlocks['_start']['code'] + '\r\n\r\n'; }
|
||||
for (var i in script_BlockScript) {
|
||||
var code = script_BlockScript[i]['code'];
|
||||
code = code.split("%%%~%%%").join(i);
|
||||
for (var j in script_BlockScript[i]['vars']) { code = code.split("%%%" + j + "%%%").join(script_BlockScript[i]['vars'][j]['value']); }
|
||||
script += '##### Block: ' + script_BlockScript[i]['name'] + ' #####\r\nHighlightBlock __t ' + i + '\r\n' + code + '\r\n\r\n';
|
||||
}
|
||||
if (script_BuildingBlocks['_end']) { script += '##### Ending Block #####\r\n' + script_BuildingBlocks['_end']['code'] + '\r\nHighlightBlock\r\n'; }
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/**
|
||||
* @description Certificate generator
|
||||
* @author Joko Sastriawan / Ylian Saint-Hilaire
|
||||
* @version v0.0.1
|
||||
*/
|
||||
module.exports.CertificateOperations = function () {
|
||||
var obj = {};
|
||||
|
||||
obj.fs = require('fs');
|
||||
obj.forge = require('node-forge');
|
||||
obj.pki = obj.forge.pki;
|
||||
obj.dirExists = function (filePath) { try { return obj.fs.statSync(filePath).isDirectory(); } catch (err) { return false; } }
|
||||
obj.getFilesizeInBytes = function(filename) { try { return obj.fs.statSync(filename)["size"]; } catch (err) { return -1; } }
|
||||
obj.fileExists = function(filePath) { try { return obj.fs.statSync(filePath).isFile(); } catch (err) { return false; } }
|
||||
|
||||
// Return the SHA256 hash of the certificate public key
|
||||
obj.getPublicKeyHash = function(cert) {
|
||||
var publickey = obj.pki.certificateFromPem(cert).publicKey;
|
||||
return obj.pki.getPublicKeyFingerprint(publickey, { encoding: 'hex', md: obj.forge.md.sha256.create() });
|
||||
}
|
||||
|
||||
// Create a self-signed certificate
|
||||
obj.GenerateRootCertificate = function (addThumbPrintToName, commonName, country, organization) {
|
||||
var keys = obj.pki.rsa.generateKeyPair(2048);
|
||||
var cert = obj.pki.createCertificate();
|
||||
cert.publicKey = keys.publicKey;
|
||||
cert.serialNumber = '' + Math.floor((Math.random() * 100000) + 1); ;
|
||||
cert.validity.notBefore = new Date();
|
||||
cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 1); // Create a certificate that is valid one year before, to make sure out-of-sync clocks don't reject this cert.
|
||||
cert.validity.notAfter = new Date();
|
||||
cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 30);
|
||||
if (addThumbPrintToName == true) { commonName += '-' + obj.pki.getPublicKeyFingerprint(cert.publicKey, { encoding: 'hex' }).substring(0, 6); }
|
||||
var attrs = [ { name: 'commonName', value: commonName } ];
|
||||
if (country != undefined) attrs.push({ name: 'countryName', value: country });
|
||||
if (organization != undefined) attrs.push({ name: 'organizationName', value: organization });
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(attrs);
|
||||
cert.setExtensions([
|
||||
{ name: 'basicConstraints', cA: true },
|
||||
{
|
||||
name: 'nsCertType',
|
||||
client: false,
|
||||
server: false,
|
||||
email: false,
|
||||
objsign: false,
|
||||
sslCA: true,
|
||||
emailCA: false,
|
||||
objCA: true
|
||||
}
|
||||
]);
|
||||
cert.sign(keys.privateKey, obj.forge.md.sha256.create());
|
||||
|
||||
return { cert: cert, key: keys.privateKey };
|
||||
}
|
||||
|
||||
// Issue a certificate from a root
|
||||
obj.IssueWebServerCertificate = function (rootcert, addThumbPrintToName, commonName, country, organization) {
|
||||
var keys = obj.pki.rsa.generateKeyPair(2048);
|
||||
var cert = obj.pki.createCertificate();
|
||||
cert.publicKey = keys.publicKey;
|
||||
cert.serialNumber = '' + Math.floor((Math.random() * 100000) + 1); ;
|
||||
cert.validity.notBefore = new Date();
|
||||
cert.validity.notBefore.setFullYear(cert.validity.notAfter.getFullYear() - 1); // Create a certificate that is valid one year before, to make sure out-of-sync clocks don't reject this cert.
|
||||
cert.validity.notAfter = new Date();
|
||||
cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 30);
|
||||
if (addThumbPrintToName == true) { commonName += '-' + obj.pki.getPublicKeyFingerprint(cert.publicKey, { encoding: 'hex' }).substring(0, 6); }
|
||||
var attrs = [ { name: 'commonName', value: commonName }];
|
||||
if (country != undefined) attrs.push({ name: 'countryName', value: country });
|
||||
if (organization != undefined) attrs.push({ name: 'organizationName', value: organization });
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(rootcert.cert.subject.attributes);
|
||||
|
||||
cert.setExtensions([{
|
||||
name: 'basicConstraints',
|
||||
cA: false
|
||||
}, {
|
||||
name: 'keyUsage',
|
||||
keyCertSign: true,
|
||||
digitalSignature: true,
|
||||
nonRepudiation: true,
|
||||
keyEncipherment: true,
|
||||
dataEncipherment: true
|
||||
}, {
|
||||
name: 'extKeyUsage',
|
||||
serverAuth: true,
|
||||
clientAuth: false,
|
||||
codeSigning: false,
|
||||
emailProtection: false,
|
||||
timeStamping: false
|
||||
}, {
|
||||
name: 'nsCertType',
|
||||
client: false,
|
||||
server: true,
|
||||
email: false,
|
||||
objsign: false,
|
||||
sslCA: false,
|
||||
emailCA: false,
|
||||
objCA: false
|
||||
}, {
|
||||
name: 'subjectAltName',
|
||||
altNames: [{
|
||||
type: 6, // URI
|
||||
value: 'http://' + commonName + '/'
|
||||
}, {
|
||||
type: 6, // URL
|
||||
value: 'http://localhost/'
|
||||
}]
|
||||
}, {
|
||||
name: 'subjectKeyIdentifier'
|
||||
}]);
|
||||
|
||||
cert.sign(rootcert.key, obj.forge.md.sha256.create());
|
||||
|
||||
return { cert: cert, key: keys.privateKey };
|
||||
}
|
||||
|
||||
// Returns the web server TLS certificate and private key, if not present, create demonstration ones.
|
||||
obj.GetMeshServerCertificate = function (directory, certargs, func) {
|
||||
// commonName, country, organization
|
||||
|
||||
// If the certificates directory does not exist, create it.
|
||||
if (!obj.dirExists(directory)) { obj.fs.mkdirSync(directory); }
|
||||
|
||||
var r = {}, rcount = 0;
|
||||
|
||||
// If the root certificate already exist, load it
|
||||
if (obj.fileExists(directory + '/root-cert-public.crt') && obj.fileExists(directory + '/root-cert-private.key')) {
|
||||
var rootCertificate = obj.fs.readFileSync(directory + '/root-cert-public.crt', 'utf8');
|
||||
var rootPrivateKey = obj.fs.readFileSync(directory + '/root-cert-private.key', 'utf8');
|
||||
r.root = { cert: rootCertificate, key: rootPrivateKey };
|
||||
rcount++;
|
||||
}
|
||||
|
||||
// If the web certificate already exist, load it
|
||||
if (obj.fileExists(directory + '/webserver-cert-public.crt') && obj.fileExists(directory + '/webserver-cert-private.key')) {
|
||||
var webCertificate = obj.fs.readFileSync(directory + '/webserver-cert-public.crt', 'utf8');
|
||||
var webPrivateKey = obj.fs.readFileSync(directory + '/webserver-cert-private.key', 'utf8');
|
||||
r.web = { cert: webCertificate, key: webPrivateKey };
|
||||
rcount++;
|
||||
}
|
||||
|
||||
// If the bin certificate already exist, load it
|
||||
if (obj.fileExists(directory + '/mpsserver-cert-public.crt') && obj.fileExists(directory + '/mpsserver-cert-private.key')) {
|
||||
var mpsCertificate = obj.fs.readFileSync(directory + '/mpsserver-cert-public.crt', 'utf8');
|
||||
var mpsPrivateKey = obj.fs.readFileSync(directory + '/mpsserver-cert-private.key', 'utf8');
|
||||
r.mps = { cert: mpsCertificate, key: mpsPrivateKey };
|
||||
rcount++;
|
||||
}
|
||||
|
||||
// If the bin certificate already exist, load it
|
||||
if (obj.fileExists(directory + '/agentserver-cert-public.crt') && obj.fileExists(directory + '/agentserver-cert-private.key')) {
|
||||
var agentCertificate = obj.fs.readFileSync(directory + '/agentserver-cert-public.crt', 'utf8');
|
||||
var agentPrivateKey = obj.fs.readFileSync(directory + '/agentserver-cert-private.key', 'utf8');
|
||||
r.agent = { cert: agentCertificate, key: agentPrivateKey };
|
||||
rcount++;
|
||||
}
|
||||
|
||||
// Decode certificate arguments
|
||||
var commonName = 'un-configured', country, organization;
|
||||
if (certargs != undefined) {
|
||||
var args = certargs.split(',');
|
||||
if (args.length > 0) commonName = args[0];
|
||||
if (args.length > 1) country = args[1];
|
||||
if (args.length > 2) organization = args[2];
|
||||
}
|
||||
|
||||
if (rcount == 4) {
|
||||
// Fetch the name of the server
|
||||
var webCertificate = obj.pki.certificateFromPem(r.web.cert);
|
||||
r.CommonName = webCertificate.subject.getField('CN').value;
|
||||
var rootCertificate = obj.pki.certificateFromPem(r.root.cert);
|
||||
r.RootName = rootCertificate.subject.getField('CN').value;
|
||||
if (certargs == undefined) { if (func != undefined) { func(r); } return r }; // If no certificate arguments are given, keep the certificate
|
||||
var xcountry, xcountryField = webCertificate.subject.getField('C');
|
||||
if (xcountryField != null) { xcountry = xcountryField.value; }
|
||||
var xorganization, xorganizationField = webCertificate.subject.getField('O');
|
||||
if (xorganizationField != null) { xorganization = xorganizationField.value; }
|
||||
if ((r.CommonName == commonName) && (xcountry == country) && (xorganization == organization)) { if (func != undefined) { func(r); } return r; } // If the certificate matches what we want, keep it.
|
||||
}
|
||||
console.log('Generating certificates...');
|
||||
|
||||
var rootCertAndKey, rootCertificate, rootPrivateKey, rootName;
|
||||
if (r.root == undefined) {
|
||||
// If the root certificate does not exist, create one
|
||||
rootCertAndKey = obj.GenerateRootCertificate(true, 'MeshCentralRoot');
|
||||
rootCertificate = obj.pki.certificateToPem(rootCertAndKey.cert);
|
||||
rootPrivateKey = obj.pki.privateKeyToPem(rootCertAndKey.key);
|
||||
obj.fs.writeFileSync(directory + '/root-cert-public.crt', rootCertificate);
|
||||
obj.fs.writeFileSync(directory + '/root-cert-private.key', rootPrivateKey);
|
||||
} else {
|
||||
// Keep the root certificate we have
|
||||
rootCertAndKey = { cert: obj.pki.certificateFromPem(r.root.cert), key: obj.pki.privateKeyFromPem(r.root.key) };
|
||||
rootCertificate = r.root.cert
|
||||
rootPrivateKey = r.root.key
|
||||
}
|
||||
var rootName = rootCertAndKey.cert.subject.getField('CN').value;
|
||||
|
||||
// If the web certificate does not exist, create one
|
||||
var webCertAndKey, webCertificate, webPrivateKey;
|
||||
webCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, false, commonName, country, organization);
|
||||
webCertificate = obj.pki.certificateToPem(webCertAndKey.cert);
|
||||
webPrivateKey = obj.pki.privateKeyToPem(webCertAndKey.key);
|
||||
obj.fs.writeFileSync(directory + '/webserver-cert-public.crt', webCertificate);
|
||||
obj.fs.writeFileSync(directory + '/webserver-cert-private.key', webPrivateKey);
|
||||
|
||||
// If the Intel AMT MPS certificate does not exist, create one
|
||||
var mpsCertAndKey, mpsCertificate, mpsPrivateKey;
|
||||
mpsCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, false, commonName, country, organization);
|
||||
mpsCertificate = obj.pki.certificateToPem(mpsCertAndKey.cert);
|
||||
mpsPrivateKey = obj.pki.privateKeyToPem(mpsCertAndKey.key);
|
||||
obj.fs.writeFileSync(directory + '/mpsserver-cert-public.crt', mpsCertificate);
|
||||
obj.fs.writeFileSync(directory + '/mpsserver-cert-private.key', mpsPrivateKey);
|
||||
|
||||
// If the mesh agent server certificate does not exist, create one
|
||||
var agentCertAndKey, agentCertificate, agentPrivateKey;
|
||||
if (r.agent == undefined) {
|
||||
agentCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, true, 'MeshCentralAgentServer');
|
||||
agentCertificate = obj.pki.certificateToPem(agentCertAndKey.cert);
|
||||
agentPrivateKey = obj.pki.privateKeyToPem(agentCertAndKey.key);
|
||||
obj.fs.writeFileSync(directory + '/agentserver-cert-public.crt', agentCertificate);
|
||||
obj.fs.writeFileSync(directory + '/agentserver-cert-private.key', agentPrivateKey);
|
||||
} else {
|
||||
// Keep the mesh agent server certificate we have
|
||||
agentCertAndKey = { cert: obj.pki.certificateFromPem(r.agent.cert), key: obj.pki.privateKeyFromPem(r.agent.key) };
|
||||
agentCertificate = r.agent.cert
|
||||
agentPrivateKey = r.agent.key
|
||||
}
|
||||
|
||||
var r = { root: { cert: rootCertificate, key: rootPrivateKey }, web: { cert: webCertificate, key: webPrivateKey }, mps: { cert: mpsCertificate, key: mpsPrivateKey }, agent: { cert: agentCertificate, key: agentPrivateKey }, CommonName: commonName, RootName: rootName };
|
||||
if (func != undefined) { func(r); }
|
||||
return r;
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
|
@ -0,0 +1,112 @@
|
|||
|
||||
var crypto = require('crypto');
|
||||
|
||||
// Binary encoding and decoding functions
|
||||
module.exports.ReadShort = function(v, p) { return (v.charCodeAt(p) << 8) + v.charCodeAt(p + 1); }
|
||||
module.exports.ReadShortX = function(v, p) { return (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }
|
||||
module.exports.ReadInt = function(v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); } // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
|
||||
module.exports.ReadIntX = function(v, p) { return (v.charCodeAt(p + 3) * 0x1000000) + (v.charCodeAt(p + 2) << 16) + (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }
|
||||
module.exports.ShortToStr = function(v) { return String.fromCharCode((v >> 8) & 0xFF, v & 0xFF); }
|
||||
module.exports.ShortToStrX = function(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); }
|
||||
module.exports.IntToStr = function(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); }
|
||||
module.exports.IntToStrX = function(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); }
|
||||
module.exports.MakeToArray = function(v) { if (!v || v == null || typeof v == 'object') return v; return [v]; }
|
||||
module.exports.SplitArray = function(v) { return v.split(','); }
|
||||
module.exports.Clone = function(v) { return JSON.parse(JSON.stringify(v)); }
|
||||
module.exports.IsFilenameValid = (function () { var x1 = /^[^\\/:\*\?"<>\|]+$/, x2 = /^\./, x3 = /^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname) { return x1.test(fname) && !x2.test(fname) && !x3.test(fname) && (fname[0] != '.'); } })();
|
||||
|
||||
// Move an element from one position in an array to a new position
|
||||
module.exports.ArrayElementMove = function(arr, from, to) { arr.splice(to, 0, arr.splice(from, 1)[0]); };
|
||||
|
||||
// Print object for HTML
|
||||
module.exports.ObjectToStringEx = function(x, c) {
|
||||
var r = "";
|
||||
if (x != 0 && (!x || x == null)) return "(Null)";
|
||||
if (x instanceof Array) { for (var i in x) { r += '<br />' + gap(c) + "Item #" + i + ": " + module.exports.ObjectToStringEx(x[i], c + 1); } }
|
||||
else if (x instanceof Object) { for (var i in x) { r += '<br />' + gap(c) + i + " = " + module.exports.ObjectToStringEx(x[i], c + 1); } }
|
||||
else { r += x; }
|
||||
return r;
|
||||
}
|
||||
|
||||
// Print object for console
|
||||
module.exports.ObjectToStringEx2 = function(x, c) {
|
||||
var r = "";
|
||||
if (x != 0 && (!x || x == null)) return "(Null)";
|
||||
if (x instanceof Array) { for (var i in x) { r += '\r\n' + gap2(c) + "Item #" + i + ": " + module.exports.ObjectToStringEx2(x[i], c + 1); } }
|
||||
else if (x instanceof Object) { for (var i in x) { r += '\r\n' + gap2(c) + i + " = " + module.exports.ObjectToStringEx2(x[i], c + 1); } }
|
||||
else { r += x; }
|
||||
return r;
|
||||
}
|
||||
|
||||
// Create an ident gap
|
||||
module.exports.gap = function(c) { var x = ''; for (var i = 0; i < (c * 4) ; i++) { x += ' '; } return x; }
|
||||
module.exports.gap2 = function(c) { var x = ''; for (var i = 0; i < (c * 4) ; i++) { x += ' '; } return x; }
|
||||
|
||||
// Print an object in html
|
||||
module.exports.ObjectToString = function(x) { return module.exports.ObjectToStringEx(x, 0); }
|
||||
module.exports.ObjectToString2 = function(x) { return module.exports.ObjectToStringEx2(x, 0); }
|
||||
|
||||
// Convert a hex string to a raw string
|
||||
module.exports.hex2rstr = function(d) {
|
||||
var r = '', m = ('' + d).match(/../g), t;
|
||||
while (t = m.shift()) r += String.fromCharCode('0x' + t);
|
||||
return r
|
||||
}
|
||||
|
||||
// Convert decimal to hex
|
||||
module.exports.char2hex = function(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
|
||||
|
||||
// Convert a raw string to a hex string
|
||||
module.exports.rstr2hex = function(input) {
|
||||
var r = '', i;
|
||||
for (i = 0; i < input.length; i++) { r += module.exports.char2hex(input.charCodeAt(i)); }
|
||||
return r;
|
||||
}
|
||||
|
||||
// UTF-8 encoding & decoding functions
|
||||
module.exports.encode_utf8 = function(s) { return unescape(encodeURIComponent(s)); }
|
||||
module.exports.decode_utf8 = function(s) { return decodeURIComponent(escape(s)); }
|
||||
|
||||
// Convert a string into a blob
|
||||
module.exports.data2blob = function(data) {
|
||||
var bytes = new Array(data.length);
|
||||
for (var i = 0; i < data.length; i++) bytes[i] = data.charCodeAt(i);
|
||||
var blob = new Blob([new Uint8Array(bytes)]);
|
||||
return blob;
|
||||
}
|
||||
|
||||
// Generate random numbers
|
||||
module.exports.random = function (max) { return Math.floor(Math.random() * max); }
|
||||
|
||||
// Split a comma seperated string, ignoring commas in quotes.
|
||||
module.exports.quoteSplit = function (str) {
|
||||
var tmp = '', quote = 0, result = [];
|
||||
for (var i in str) { if (str[i] == '"') { quote = (quote + 1) % 2; } if ((str[i] == ',') && (quote == 0)) { tmp = tmp.trim(); result.push(tmp); tmp = ''; } else { tmp += str[i]; } }
|
||||
if (tmp.length > 0) result.push(tmp.trim());
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert list of "name = value" into object
|
||||
module.exports.parseNameValueList = function (list) {
|
||||
var result = [];
|
||||
for (var i in list) {
|
||||
var j = list[i].indexOf('=');
|
||||
if (j > 0) {
|
||||
var v = list[i].substring(j + 1).trim();
|
||||
if ((v[0] == '"') && (v[v.length - 1] == '"')) { v = v.substring(1, v.length - 1); }
|
||||
result[list[i].substring(0, j).trim()] = v;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Compute the MD5 digest hash for a set of values
|
||||
module.exports.ComputeDigesthash = function (username, password, realm, method, path, qop, nonce, nc, cnonce) {
|
||||
var ha1 = crypto.createHash('md5').update(username + ":" + realm + ":" + password).digest("hex");
|
||||
var ha2 = crypto.createHash('md5').update(method + ":" + path).digest("hex");
|
||||
return crypto.createHash('md5').update(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2).digest("hex");
|
||||
}
|
||||
|
||||
module.exports.toNumber = function (str) { var x = parseInt(str); if (x == str) return x; return str; }
|
||||
module.exports.escapeHtml = function (string) { return String(string).replace(/[&<>"'`=\/]/g, function (s) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/', '`': '`', '=': '=' }[s]; }); }
|
||||
module.exports.escapeHtmlBreaks = function (string) { return String(string).replace(/[&<>"'`=\/]/g, function (s) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/', '`': '`', '=': '=', '\r': '<br />', '\n': '' }[s]; }); }
|
|
@ -0,0 +1,168 @@
|
|||
/**
|
||||
* @description Meshcentral database
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.2
|
||||
*/
|
||||
|
||||
//
|
||||
// Construct Meshcentral database object
|
||||
//
|
||||
// The default database is NeDB
|
||||
// https://github.com/louischatriot/nedb
|
||||
//
|
||||
// Alternativety, MongoDB can be used
|
||||
// https://www.mongodb.com/
|
||||
// Just run with --mongodb [connectionstring], where the connection string is documented here: https://docs.mongodb.com/manual/reference/connection-string/
|
||||
// The default collection is "meshcentral", but you can override it using --mongodbcol [collection]
|
||||
//
|
||||
module.exports.CreateDB = function (args, datapath) {
|
||||
var obj = {};
|
||||
obj.path = require('path');
|
||||
if (args.mongodb) {
|
||||
// Use MongoDB
|
||||
obj.databaseType = 2;
|
||||
var Datastore = require("mongojs");
|
||||
var db = Datastore(args.mongodb);
|
||||
var dbcollection = 'meshcentral';
|
||||
if (args.mongodbcol) { dbcollection = args.mongodbcol; }
|
||||
obj.file = db.collection(dbcollection);
|
||||
} else {
|
||||
// Use NeDB (The default)
|
||||
obj.databaseType = 1;
|
||||
var Datastore = require('nedb');
|
||||
obj.file = new Datastore({ filename: obj.path.join(datapath, 'meshcentral.db'), autoload: true });
|
||||
obj.file.persistence.setAutocompactionInterval(3600);
|
||||
}
|
||||
|
||||
obj.SetupDatabase = function (func) {
|
||||
// Load database schema version and check if we need to update
|
||||
obj.Get('SchemaVersion', function (err, docs) {
|
||||
var ver = 0;
|
||||
if (docs && docs.length == 1) { ver = docs[0].value; }
|
||||
|
||||
// Upgrade schema 0 to schema 1
|
||||
if (ver == 0) {
|
||||
// Add the default domain to all users
|
||||
obj.GetAllType('user', function (err, docs) {
|
||||
for (var id in docs) {
|
||||
var oldid, changed = false;
|
||||
if (docs[id].subscriptions) { delete docs[id].subscriptions; changed = true; }
|
||||
if (docs[id].domain == undefined) {
|
||||
docs[id].domain = '';
|
||||
oldid = docs[id]._id;
|
||||
docs[id]._id = 'user//' + docs[id]._id.substring(5);
|
||||
changed = true;
|
||||
}
|
||||
if (docs[id].links) {
|
||||
for (var linkid in docs[id].links) {
|
||||
var linkid2 = 'mesh//' + linkid.substring(5);
|
||||
docs[id].links[linkid2] = docs[id].links[linkid];
|
||||
delete docs[id].links[linkid];
|
||||
}
|
||||
}
|
||||
if (changed == true) {
|
||||
if (oldid) obj.Remove(oldid);
|
||||
obj.Set(docs[id]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the default domain to all nodes
|
||||
obj.GetAllType('node', function (err, docs) {
|
||||
for (var id in docs) {
|
||||
var oldid, changed = false;
|
||||
if (docs[id].domain == undefined) {
|
||||
docs[id].domain = '';
|
||||
oldid = docs[id]._id;
|
||||
docs[id]._id = 'node//' + docs[id]._id.substring(5);
|
||||
docs[id].meshid = 'mesh//' + docs[id].meshid.substring(5);
|
||||
changed = true;
|
||||
}
|
||||
if (changed == true) {
|
||||
if (oldid) obj.Remove(oldid);
|
||||
obj.Set(docs[id]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add the default domain to all meshes
|
||||
obj.GetAllType('mesh', function (err, docs) {
|
||||
for (var id in docs) {
|
||||
var oldid, changed = false;
|
||||
if (docs[id].domain == undefined) {
|
||||
docs[id].domain = '';
|
||||
oldid = docs[id]._id;
|
||||
docs[id]._id = 'mesh//' + docs[id]._id.substring(5);
|
||||
if (docs[id].links) {
|
||||
for (var linkid in docs[id].links) {
|
||||
var linkid2 = 'user//' + linkid.substring(5);
|
||||
docs[id].links[linkid2] = docs[id].links[linkid];
|
||||
delete docs[id].links[linkid];
|
||||
}
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
if (changed == true) {
|
||||
if (oldid) obj.Remove(oldid);
|
||||
obj.Set(docs[id]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add the default domain to all events
|
||||
obj.GetAllType('event', function (err, docs) {
|
||||
var changed = false;
|
||||
for (var id in docs) {
|
||||
var oldid;
|
||||
changed = true;
|
||||
if (docs[id].domain == undefined) {
|
||||
docs[id].domain = '';
|
||||
obj.Set(docs[id]);
|
||||
}
|
||||
}
|
||||
|
||||
obj.Set({ _id: 'SchemaVersion', value: 1 });
|
||||
ver = 1;
|
||||
if (changed == true) { console.log('Upgraded database to version 1.'); }
|
||||
func(ver);
|
||||
});
|
||||
});
|
||||
|
||||
} else { func(ver); }
|
||||
});
|
||||
}
|
||||
|
||||
obj.cleanup = function () {
|
||||
// TODO: Remove all mesh links to invalid users
|
||||
// TODO: Remove all meshes that dont have any links
|
||||
|
||||
// Remove all objects that have a "meshid" that no longer points to a valid mesh.
|
||||
obj.GetAllType('mesh', function (err, docs) {
|
||||
var meshlist = [];
|
||||
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 });
|
||||
});
|
||||
}
|
||||
|
||||
obj.Set = function (data) { obj.file.update({ _id: data._id }, data, { upsert: true }); }
|
||||
obj.Get = function (id, func) { obj.file.find({ _id: id }, func); }
|
||||
obj.GetAllTypeNoTypeField = function (type, domain, func) { obj.file.find({ type: type, domain: domain }, { type : 0 }, func); }
|
||||
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.Remove = function (id) { obj.file.remove({ _id: id }); }
|
||||
obj.StoreEvent = function (ids, source, event) { obj.file.insert(event); }
|
||||
obj.GetEvents = function (ids, domain, func) { if (obj.databaseType == 1) { obj.file.find({ type: 'event', domain: domain, ids: { $in: ids } }, { type: 0, _id: 0 }).sort({ time: -1 }).exec(func); } else { obj.file.find({ type: 'event', domain: domain, ids: { $in: ids } }, { type: 0, _id: 0 }).sort({ time: -1 }, func) } }
|
||||
obj.RemoveMesh = function (id) { obj.file.remove({ mesh: id }, { multi: true }); obj.file.remove({ _id: id }); }
|
||||
obj.RemoveAllEvents = function (domain) { obj.file.remove({ type: 'event', domain: domain }, { multi: true }); }
|
||||
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if (docs.length == 1) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); }
|
||||
obj.DeleteDomain = function (domain, func) { obj.file.remove({ domain: domain }, { multi: true }, func); }
|
||||
obj.SetUser = function(user) { var u = Clone(user); if (u.subscriptions) { delete u.subscriptions; } obj.Set(u); }
|
||||
obj.dispose = function () { for (var x in obj) { if (obj[x].close) { obj[x].close(); } delete obj[x]; } }
|
||||
obj.clearOldEntries = function (type, days, domain) { var cutoff = Date.now() - (1000 * 60 * 60 * 24 * days); obj.file.remove({ type: type, time: { $lt: cutoff } }, { multi: true }); }
|
||||
obj.getPowerTimeline = function (nodeid, func) { if (obj.databaseType == 1) { obj.file.find({ type: 'power', node: { $in: ['*', nodeid] } }).sort({ time: 1 }).exec(func); } else { obj.file.find({ type: 'power', node: { $in: ['*', nodeid] } }).sort({ time: 1 }, func); } }
|
||||
obj.getLocalAmtNodes = function (func) { obj.file.find({ type: 'node', host: { $exists: true, $ne: null }, intelamt: { $exists: true } }, func); }
|
||||
|
||||
function Clone(v) { return JSON.parse(JSON.stringify(v)); }
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,424 @@
|
|||
/**
|
||||
* @description Intel AMT Interceptor
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.3
|
||||
*/
|
||||
|
||||
var crypto = require('crypto');
|
||||
var common = require('./common.js');
|
||||
|
||||
var HttpInterceptorAuthentications = {};
|
||||
var RedirInterceptorAuthentications = {};
|
||||
|
||||
// Construct a HTTP interceptor object
|
||||
module.exports.CreateHttpInterceptor = function (args) {
|
||||
var obj = {};
|
||||
|
||||
// Create a random hex string of a given length
|
||||
obj.randomValueHex = function (len) { return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); }
|
||||
|
||||
obj.args = args;
|
||||
obj.amt = { acc: "", mode: 0, count: 0, error: false }; // mode: 0:Header, 1:LengthBody, 2:ChunkedBody, 3:UntilClose
|
||||
obj.ws = { acc: "", mode: 0, count: 0, error: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 };
|
||||
|
||||
// Private method
|
||||
obj.Debug = function (msg) { console.log(msg); }
|
||||
|
||||
// Process data coming from Intel AMT
|
||||
obj.processAmtData = function (data) {
|
||||
obj.amt.acc += data; // Add data to accumulator
|
||||
data = "";
|
||||
var datalen = 0;
|
||||
do {
|
||||
datalen = data.length;
|
||||
data += obj.processAmtDataEx();
|
||||
} while (datalen != data.length); // Process as much data as possible
|
||||
return data;
|
||||
}
|
||||
|
||||
// Process data coming from AMT in the accumulator
|
||||
obj.processAmtDataEx = function () {
|
||||
if (obj.amt.mode == 0) { // Header Mode
|
||||
// Decode the HTTP header
|
||||
var headerend = obj.amt.acc.indexOf('\r\n\r\n');
|
||||
if (headerend < 0) return "";
|
||||
var headerlines = obj.amt.acc.substring(0, headerend).split('\r\n');
|
||||
obj.amt.acc = obj.amt.acc.substring(headerend + 4);
|
||||
obj.amt.directive = headerlines[0].split(' ');
|
||||
var headers = headerlines.slice(1);
|
||||
obj.amt.headers = {};
|
||||
obj.amt.mode = 3; // UntilClose
|
||||
for (var i in headers) {
|
||||
var j = headers[i].indexOf(':');
|
||||
if (j > 0) {
|
||||
var v1 = headers[i].substring(0, j).trim().toLowerCase();
|
||||
var v2 = headers[i].substring(j + 1).trim();
|
||||
obj.amt.headers[v1] = v2;
|
||||
if (v1.toLowerCase() == 'www-authenticate') {
|
||||
HttpInterceptorAuthentications[obj.args.host + ':' + obj.args.port] = v2;
|
||||
} else if (v1.toLowerCase() == 'content-length') {
|
||||
obj.amt.count = parseInt(v2);
|
||||
if (obj.amt.count > 0) {
|
||||
obj.amt.mode = 1; // LengthBody
|
||||
} else {
|
||||
obj.amt.mode = 0; // Header
|
||||
}
|
||||
} else if (v1.toLowerCase() == 'transfer-encoding' && v2.toLowerCase() == 'chunked') {
|
||||
obj.amt.mode = 2; // ChunkedBody
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reform the HTTP header
|
||||
var r = obj.amt.directive.join(' ') + '\r\n';
|
||||
for (var i in obj.amt.headers) { r += (i + ': ' + obj.amt.headers[i] + '\r\n'); }
|
||||
r += '\r\n';
|
||||
return r;
|
||||
} else if (obj.amt.mode == 1) { // Length Body Mode
|
||||
// Send the body of content-length size
|
||||
var rl = obj.amt.count;
|
||||
if (rl < obj.amt.acc.length) rl = obj.amt.acc.length;
|
||||
var r = obj.amt.acc.substring(0, rl);
|
||||
obj.amt.acc = obj.amt.acc.substring(rl);
|
||||
obj.amt.count -= rl;
|
||||
if (obj.amt.count == 0) { obj.amt.mode = 0; }
|
||||
return r;
|
||||
} else if (obj.amt.mode == 2) { // Chunked Body Mode
|
||||
// Send data one chunk at a time
|
||||
var headerend = obj.amt.acc.indexOf('\r\n');
|
||||
if (headerend < 0) return "";
|
||||
var chunksize = parseInt(obj.amt.acc.substring(0, headerend), 16);
|
||||
if ((chunksize == 0) && (obj.amt.acc.length >= headerend + 4)) {
|
||||
// Send the ending chunk (NOTE: We do not support trailing headers)
|
||||
var r = obj.amt.acc.substring(0, headerend + 4);
|
||||
obj.amt.acc = obj.amt.acc.substring(headerend + 4);
|
||||
obj.amt.mode = 0;
|
||||
return r;
|
||||
} else if ((chunksize > 0) && (obj.amt.acc.length >= (headerend + 4 + chunksize))) {
|
||||
// Send a chunk
|
||||
var r = obj.amt.acc.substring(0, headerend + chunksize + 4);
|
||||
obj.amt.acc = obj.amt.acc.substring(headerend + chunksize + 4);
|
||||
return r;
|
||||
}
|
||||
} else if (obj.amt.mode == 3) { // Until Close Mode
|
||||
var r = obj.amt.acc;
|
||||
obj.amt.acc = "";
|
||||
return r;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Process data coming from the Browser
|
||||
obj.processBrowserData = function (data) {
|
||||
obj.ws.acc += data; // Add data to accumulator
|
||||
data = "";
|
||||
var datalen = 0;
|
||||
do {
|
||||
datalen = data.length;
|
||||
data += obj.processBrowserDataEx();
|
||||
} while (datalen != data.length); // Process as much data as possible
|
||||
return data;
|
||||
}
|
||||
|
||||
// Process data coming from the Browser in the accumulator
|
||||
obj.processBrowserDataEx = function () {
|
||||
if (obj.ws.mode == 0) { // Header Mode
|
||||
// Decode the HTTP header
|
||||
var headerend = obj.ws.acc.indexOf('\r\n\r\n');
|
||||
if (headerend < 0) return "";
|
||||
var headerlines = obj.ws.acc.substring(0, headerend).split('\r\n');
|
||||
obj.ws.acc = obj.ws.acc.substring(headerend + 4);
|
||||
obj.ws.directive = headerlines[0].split(' ');
|
||||
var headers = headerlines.slice(1);
|
||||
obj.ws.headers = {};
|
||||
obj.ws.mode = 3; // UntilClose
|
||||
for (var i in headers) {
|
||||
var j = headers[i].indexOf(':');
|
||||
if (j > 0) {
|
||||
var v1 = headers[i].substring(0, j).trim().toLowerCase();
|
||||
var v2 = headers[i].substring(j + 1).trim();
|
||||
obj.ws.headers[v1] = v2;
|
||||
if (v1.toLowerCase() == 'www-authenticate') {
|
||||
HttpInterceptorAuthentications[obj.args.host + ':' + obj.args.port] = v2;
|
||||
} else if (v1.toLowerCase() == 'content-length') {
|
||||
obj.ws.count = parseInt(v2);
|
||||
if (obj.ws.count > 0) {
|
||||
obj.ws.mode = 1; // LengthBody
|
||||
} else {
|
||||
obj.ws.mode = 0; // Header
|
||||
}
|
||||
} else if (v1.toLowerCase() == 'transfer-encoding' && v2.toLowerCase() == 'chunked') {
|
||||
obj.ws.mode = 2; // ChunkedBody
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert authentication
|
||||
if (obj.args.user && obj.args.pass && HttpInterceptorAuthentications[obj.args.host + ':' + obj.args.port]) {
|
||||
// We have authentication data, lets use it.
|
||||
var AuthArgs = obj.GetAuthArgs(HttpInterceptorAuthentications[obj.args.host + ':' + obj.args.port]);
|
||||
var hash = obj.ComputeDigesthash(obj.args.user, obj.args.pass, AuthArgs.realm, obj.ws.directive[0], obj.ws.directive[1], AuthArgs.qop, AuthArgs.nonce, obj.ws.authCNonceCount, obj.ws.authCNonce);
|
||||
var authstr = 'Digest username="' + obj.args.user + '",realm="' + AuthArgs.realm + '",nonce="' + AuthArgs.nonce + '",uri="' + obj.ws.directive[1] + '",qop=' + AuthArgs.qop + ',nc=' + obj.ws.authCNonceCount + ',cnonce="' + obj.ws.authCNonce + '",response="' + hash + '"';
|
||||
if (AuthArgs.opaque) { authstr += ',opaque="' + AuthArgs.opaque + '"'}
|
||||
obj.ws.headers.authorization = authstr;
|
||||
obj.ws.authCNonceCount++;
|
||||
} else {
|
||||
// We don't have authentication, clear it out of the header if needed.
|
||||
if (obj.ws.headers.authorization) { delete obj.ws.headers.authorization; }
|
||||
}
|
||||
|
||||
// Reform the HTTP header
|
||||
var r = obj.ws.directive.join(' ') + '\r\n';
|
||||
for (var i in obj.ws.headers) { r += (i + ': ' + obj.ws.headers[i] + '\r\n'); }
|
||||
r += '\r\n';
|
||||
return r;
|
||||
} else if (obj.ws.mode == 1) { // Length Body Mode
|
||||
// Send the body of content-length size
|
||||
var rl = obj.ws.count;
|
||||
if (rl < obj.ws.acc.length) rl = obj.ws.acc.length;
|
||||
var r = obj.ws.acc.substring(0, rl);
|
||||
obj.ws.acc = obj.ws.acc.substring(rl);
|
||||
obj.ws.count -= rl;
|
||||
if (obj.ws.count == 0) { obj.ws.mode = 0; }
|
||||
return r;
|
||||
} else if (obj.ws.mode == 2) { // Chunked Body Mode
|
||||
// Send data one chunk at a time
|
||||
var headerend = obj.ws.acc.indexOf('\r\n');
|
||||
if (headerend < 0) return "";
|
||||
var chunksize = parseInt(obj.ws.acc.substring(0, headerend), 16);
|
||||
if (chunksize == 0 && obj.ws.acc.length >= headerend + 4) {
|
||||
// Send the ending chunk (NOTE: We do not support trailing headers)
|
||||
var r = obj.ws.acc.substring(0, headerend + 4);
|
||||
obj.ws.acc = obj.ws.acc.substring(headerend + 4);
|
||||
obj.ws.mode = 0;
|
||||
return r;
|
||||
} else if (chunksize > 0 && obj.ws.acc.length >= headerend + 4) {
|
||||
// Send a chunk
|
||||
var r = obj.ws.acc.substring(0, headerend + chunksize + 4);
|
||||
obj.ws.acc = obj.ws.acc.substring(headerend + chunksize + 4);
|
||||
return r;
|
||||
}
|
||||
} else if (obj.ws.mode == 3) { // Until Close Mode
|
||||
var r = obj.ws.acc;
|
||||
obj.ws.acc = "";
|
||||
return r;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Parse authentication values from the HTTP header
|
||||
obj.GetAuthArgs = function (authheader) {
|
||||
var authargs = {};
|
||||
var authargsstr = authheader.substring(7).split(',');
|
||||
for (var j in authargsstr) {
|
||||
var argstr = authargsstr[j];
|
||||
var i = argstr.indexOf('=');
|
||||
var k = argstr.substring(0, i).trim().toLowerCase();
|
||||
var v = argstr.substring(i + 1).trim();
|
||||
if (v.substring(0,1) == '\"') { v = v.substring(1, v.length - 1); }
|
||||
if (i > 0) authargs[k] = v;
|
||||
}
|
||||
return authargs;
|
||||
}
|
||||
|
||||
// Compute the MD5 digest hash for a set of values
|
||||
obj.ComputeDigesthash = function (username, password, realm, method, path, qop, nonce, nc, cnonce) {
|
||||
var ha1 = crypto.createHash('md5').update(username + ":" + realm + ":" + password).digest("hex");
|
||||
var ha2 = crypto.createHash('md5').update(method + ":" + path).digest("hex");
|
||||
return crypto.createHash('md5').update(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2).digest("hex");
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
// Construct a redirection interceptor object
|
||||
module.exports.CreateRedirInterceptor = function (args) {
|
||||
var obj = {};
|
||||
|
||||
// Create a random hex string of a given length
|
||||
obj.randomValueHex = function (len) { return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); }
|
||||
|
||||
obj.args = args;
|
||||
obj.amt = { acc: "", mode: 0, count: 0, error: false, direct: false};
|
||||
obj.ws = { acc: "", mode: 0, count: 0, error: false, direct: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 };
|
||||
|
||||
obj.RedirectCommands = { StartRedirectionSession: 0x10, StartRedirectionSessionReply: 0x11, EndRedirectionSession: 0x12, AuthenticateSession: 0x13, AuthenticateSessionReply: 0x14 };
|
||||
obj.StartRedirectionSessionReplyStatus = { SUCCESS: 0, TYPE_UNKNOWN: 1, BUSY: 2, UNSUPPORTED: 3, ERROR: 0xFF };
|
||||
obj.AuthenticationStatus = { SUCCESS: 0, FALIURE: 1, NOTSUPPORTED: 2 };
|
||||
obj.AuthenticationType = { QUERY: 0, USERPASS: 1, KERBEROS: 2, BADDIGEST: 3, DIGEST: 4 };
|
||||
|
||||
// Private method
|
||||
obj.Debug = function (msg) { console.log(msg); }
|
||||
|
||||
// Process data coming from Intel AMT
|
||||
obj.processAmtData = function (data) {
|
||||
obj.amt.acc += data; // Add data to accumulator
|
||||
data = "";
|
||||
var datalen = 0;
|
||||
do { datalen = data.length; data += obj.processAmtDataEx(); } while (datalen != data.length); // Process as much data as possible
|
||||
return data;
|
||||
}
|
||||
|
||||
// Process data coming from AMT in the accumulator
|
||||
obj.processAmtDataEx = function () {
|
||||
if (obj.amt.acc.length == 0) return "";
|
||||
if (obj.amt.direct == true) {
|
||||
var data = obj.amt.acc;
|
||||
obj.amt.acc = "";
|
||||
return data;
|
||||
} else {
|
||||
//console.log(obj.amt.acc.charCodeAt(0));
|
||||
switch (obj.amt.acc.charCodeAt(0)) {
|
||||
case obj.RedirectCommands.StartRedirectionSessionReply: {
|
||||
if (obj.amt.acc.length < 4) return "";
|
||||
if (obj.amt.acc.charCodeAt(1) == obj.StartRedirectionSessionReplyStatus.SUCCESS) {
|
||||
if (obj.amt.acc.length < 13) return "";
|
||||
var oemlen = obj.amt.acc.charCodeAt(12);
|
||||
if (obj.amt.acc.length < 13 + oemlen) return "";
|
||||
var r = obj.amt.acc.substring(0, 13 + oemlen);
|
||||
obj.amt.acc = obj.amt.acc.substring(13 + oemlen);
|
||||
return r;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case obj.RedirectCommands.AuthenticateSessionReply: {
|
||||
if (obj.amt.acc.length < 9) return "";
|
||||
var l = common.ReadIntX(obj.amt.acc, 5);
|
||||
if (obj.amt.acc.length < 9 + l) return "";
|
||||
var authstatus = obj.amt.acc.charCodeAt(1);
|
||||
var authType = obj.amt.acc.charCodeAt(4);
|
||||
|
||||
if (authType == obj.AuthenticationType.DIGEST && authstatus == obj.AuthenticationStatus.FALIURE) {
|
||||
// Grab and keep all authentication parameters
|
||||
var realmlen = obj.amt.acc.charCodeAt(9);
|
||||
obj.amt.digestRealm = obj.amt.acc.substring(10, 10 + realmlen);
|
||||
var noncelen = obj.amt.acc.charCodeAt(10 + realmlen);
|
||||
obj.amt.digestNonce = obj.amt.acc.substring(11 + realmlen, 11 + realmlen + noncelen);
|
||||
var qoplen = obj.amt.acc.charCodeAt(11 + realmlen + noncelen);
|
||||
obj.amt.digestQOP = obj.amt.acc.substring(12 + realmlen + noncelen, 12 + realmlen + noncelen + qoplen);
|
||||
}
|
||||
else if (authType != obj.AuthenticationType.QUERY && authstatus == obj.AuthenticationStatus.SUCCESS) {
|
||||
// Intel AMT relayed that authentication was succesful, go to direct relay mode in both directions.
|
||||
obj.ws.direct = true;
|
||||
obj.amt.direct = true;
|
||||
}
|
||||
|
||||
var r = obj.amt.acc.substring(0, 9 + l);
|
||||
obj.amt.acc = obj.amt.acc.substring(9 + l);
|
||||
return r;
|
||||
}
|
||||
default: {
|
||||
obj.amt.error = true;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Process data coming from the Browser
|
||||
obj.processBrowserData = function (data) {
|
||||
obj.ws.acc += data; // Add data to accumulator
|
||||
data = "";
|
||||
var datalen = 0;
|
||||
do { datalen = data.length; data += obj.processBrowserDataEx(); } while (datalen != data.length); // Process as much data as possible
|
||||
return data;
|
||||
}
|
||||
|
||||
// Process data coming from the Browser in the accumulator
|
||||
obj.processBrowserDataEx = function () {
|
||||
if (obj.ws.acc.length == 0) return "";
|
||||
if (obj.ws.direct == true) {
|
||||
var data = obj.ws.acc;
|
||||
obj.ws.acc = "";
|
||||
return data;
|
||||
} else {
|
||||
switch (obj.ws.acc.charCodeAt(0)) {
|
||||
case obj.RedirectCommands.StartRedirectionSession: {
|
||||
if (obj.ws.acc.length < 8) return "";
|
||||
var r = obj.ws.acc.substring(0, 8);
|
||||
obj.ws.acc = obj.ws.acc.substring(8);
|
||||
return r;
|
||||
}
|
||||
case obj.RedirectCommands.EndRedirectionSession: {
|
||||
if (obj.ws.acc.length < 4) return "";
|
||||
var r = obj.ws.acc.substring(0, 4);
|
||||
obj.ws.acc = obj.ws.acc.substring(4);
|
||||
return r;
|
||||
}
|
||||
case obj.RedirectCommands.AuthenticateSession: {
|
||||
if (obj.ws.acc.length < 9) return "";
|
||||
var l = common.ReadIntX(obj.ws.acc, 5);
|
||||
if (obj.ws.acc.length < 9 + l) return "";
|
||||
|
||||
var authType = obj.ws.acc.charCodeAt(4);
|
||||
if (authType == obj.AuthenticationType.DIGEST && obj.args.user && obj.args.pass) {
|
||||
var authurl = "/RedirectionService";
|
||||
if (obj.amt.digestRealm) {
|
||||
// Replace this authentication digest with a server created one
|
||||
// We have everything we need to authenticate
|
||||
var nc = obj.ws.authCNonceCount;
|
||||
obj.ws.authCNonceCount++;
|
||||
var digest = obj.ComputeDigesthash(obj.args.user, obj.args.pass, obj.amt.digestRealm, "POST", authurl, obj.amt.digestQOP, obj.amt.digestNonce, nc, obj.ws.authCNonce);
|
||||
|
||||
// Replace this authentication digest with a server created one
|
||||
// We have everything we need to authenticate
|
||||
var r = String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x04);
|
||||
r += common.IntToStrX(obj.args.user.length + obj.amt.digestRealm.length + obj.amt.digestNonce.length + authurl.length + obj.ws.authCNonce.length + nc.toString().length + digest.length + obj.amt.digestQOP.length + 8);
|
||||
r += String.fromCharCode(obj.args.user.length); // Username Length
|
||||
r += obj.args.user; // Username
|
||||
r += String.fromCharCode(obj.amt.digestRealm.length); // Realm Length
|
||||
r += obj.amt.digestRealm; // Realm
|
||||
r += String.fromCharCode(obj.amt.digestNonce.length); // Nonce Length
|
||||
r += obj.amt.digestNonce; // Nonce
|
||||
r += String.fromCharCode(authurl.length); // Authentication URL "/RedirectionService" Length
|
||||
r += authurl; // Authentication URL
|
||||
r += String.fromCharCode(obj.ws.authCNonce.length); // CNonce Length
|
||||
r += obj.ws.authCNonce; // CNonce
|
||||
r += String.fromCharCode(nc.toString().length); // NonceCount Length
|
||||
r += nc.toString(); // NonceCount
|
||||
r += String.fromCharCode(digest.length); // Response Length
|
||||
r += digest; // Response
|
||||
r += String.fromCharCode(obj.amt.digestQOP.length); // QOP Length
|
||||
r += obj.amt.digestQOP; // QOP
|
||||
|
||||
obj.ws.acc = obj.ws.acc.substring(9 + l); // Don't relay the original message
|
||||
return r;
|
||||
} else {
|
||||
// Replace this authentication digest with a server created one
|
||||
// Since we don't have authentication parameters, fill them in with blanks to get an error back what that info.
|
||||
var r = String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x04);
|
||||
r += common.IntToStrX(obj.args.user.length + authurl.length + 8);
|
||||
r += String.fromCharCode(obj.args.user.length);
|
||||
r += obj.args.user;
|
||||
r += String.fromCharCode(0x00, 0x00, authurl.length);
|
||||
r += authurl;
|
||||
r += String.fromCharCode(0x00, 0x00, 0x00, 0x00);
|
||||
obj.ws.acc = obj.ws.acc.substring(9 + l); // Don't relay the original message
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
var r = obj.ws.acc.substring(0, 9 + l);
|
||||
obj.ws.acc = obj.ws.acc.substring(9 + l);
|
||||
return r;
|
||||
}
|
||||
default: {
|
||||
obj.ws.error = true;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Compute the MD5 digest hash for a set of values
|
||||
obj.ComputeDigesthash = function (username, password, realm, method, path, qop, nonce, nc, cnonce) {
|
||||
var ha1 = crypto.createHash('md5').update(username + ":" + realm + ":" + password).digest("hex");
|
||||
var ha2 = crypto.createHash('md5').update(method + ":" + path).digest("hex");
|
||||
return crypto.createHash('md5').update(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2).digest("hex");
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2017 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -0,0 +1,472 @@
|
|||
/**
|
||||
* @description Meshcentral MeshAgent
|
||||
* @author Ylian Saint-Hilaire & Bryan Roe
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
// Construct a MeshAgent object, called upon connection
|
||||
module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||
var obj = {};
|
||||
obj.parent = parent;
|
||||
obj.db = db;
|
||||
obj.ws = ws;
|
||||
obj.fs = parent.fs;
|
||||
obj.args = args;
|
||||
obj.nodeid = null;
|
||||
obj.meshid = null;
|
||||
obj.dbNodeKey = null;
|
||||
obj.dbMeshKey = null;
|
||||
obj.forge = parent.parent.certificateOperations.forge;
|
||||
obj.common = parent.parent.common;
|
||||
obj.authenticated = 0;
|
||||
obj.domain = domain;
|
||||
obj.receivedCommands = 0;
|
||||
obj.connectTime = null;
|
||||
obj.agentCoreCheck = 0;
|
||||
obj.agentInfo;
|
||||
obj.agentUpdate = null;
|
||||
var agentUpdateBlockSize = 65520;
|
||||
|
||||
// Send a message to the mesh agent
|
||||
obj.send = function (data) { if (typeof data == 'string') { obj.ws.send(new Buffer(data, 'binary')); } else { obj.ws.send(data); } }
|
||||
|
||||
// Disconnect this agent
|
||||
obj.close = function (arg) {
|
||||
//console.log('MeshAgent.close(' + arg + ')');
|
||||
if (arg !== 1) { try { obj.ws.close(); } catch (e) { } }
|
||||
if (obj.parent.wsagents[obj.dbNodeKey] == obj) {
|
||||
delete obj.parent.wsagents[obj.dbNodeKey];
|
||||
obj.parent.parent.ClearConnectivityState(obj.dbMeshKey, obj.dbNodeKey, 1);
|
||||
}
|
||||
// Other clean up may be needed here
|
||||
if (obj.unauth) { delete obj.unauth; }
|
||||
if (obj.agentUpdate != null) { obj.fs.close(obj.agentUpdate.fd); obj.agentUpdate = null; }
|
||||
}
|
||||
|
||||
// When data is received from the mesh agent web socket
|
||||
ws.on('message', function (msg) {
|
||||
if (msg.length < 2) return;
|
||||
if (typeof msg == 'object') {
|
||||
// Convert the buffer into a string
|
||||
var msg2 = "";
|
||||
for (var i = 0; i < msg.length; i++) { msg2 += String.fromCharCode(msg[i]); }
|
||||
msg = msg2;
|
||||
}
|
||||
|
||||
if (obj.authenticated == 2) { // We are authenticated
|
||||
if (msg.charCodeAt(0) == 123) { processAgentData(msg); }
|
||||
if (msg.length < 2) return;
|
||||
var cmdid = obj.common.ReadShort(msg, 0);
|
||||
if (cmdid == 11) { // MeshCommand_CoreModuleHash
|
||||
if (msg.length == 4) { ChangeAgentCoreInfo({ caps: 0 }); } // If the agent indicated that no core is running, clear the core information string.
|
||||
// Mesh core hash, sent by agent with the hash of the current mesh core.
|
||||
if (obj.agentCoreCheck == 1000) return; // If we are using a custom core, don't try to update it.
|
||||
// We need to check if the core is current.
|
||||
// TODO: Check if we have a mesh specific core. If so, use that.
|
||||
var agentMeshCoreHash = null;
|
||||
if (msg.length == 36) { agentMeshCoreHash = msg.substring(4, 36); }
|
||||
if (agentMeshCoreHash != obj.parent.parent.defaultMeshCoreHash) {
|
||||
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
|
||||
}
|
||||
obj.agentCoreCheck++;
|
||||
}
|
||||
} else {
|
||||
obj.agentCoreCheck = 0;
|
||||
}
|
||||
}
|
||||
else if (cmdid == 12) { // MeshCommand_AgentHash
|
||||
if ((msg.length == 36) && (obj.agentInfo != undefined) && (obj.agentInfo.update == true)) {
|
||||
var agenthash = obj.common.rstr2hex(msg.substring(4)).toLowerCase();
|
||||
if (agenthash != obj.agentInfo.hash) {
|
||||
// Mesh agent update required
|
||||
console.log('Agent update required, NodeID=0x' + obj.nodeid.substring(0, 16) + ', ' + obj.agentInfo.desc);
|
||||
obj.fs.open(obj.agentInfo.path, 'r', function (err, fd) {
|
||||
if (err) { return console.error(err); }
|
||||
obj.agentUpdate = { oldHash: agenthash, ptr: 0, buf: new Buffer(agentUpdateBlockSize + 4), fd: fd };
|
||||
|
||||
// We got the agent file open ont he server side, tell the agent we are sending an update starting with the SHA256 hash of the result
|
||||
//console.log("Agent update file open.");
|
||||
obj.send(obj.common.ShortToStr(13) + obj.common.ShortToStr(0)); // Command 13, start mesh agent download
|
||||
|
||||
// Send the first mesh agent update data block
|
||||
obj.agentUpdate.buf[0] = 0;
|
||||
obj.agentUpdate.buf[1] = 14;
|
||||
obj.agentUpdate.buf[2] = 0;
|
||||
obj.agentUpdate.buf[3] = 1;
|
||||
var len = -1;
|
||||
try { len = obj.fs.readSync(obj.agentUpdate.fd, obj.agentUpdate.buf, 4, agentUpdateBlockSize, obj.agentUpdate.ptr); } catch (e) { }
|
||||
if (len == -1) {
|
||||
// Error reading the agent file, stop here.
|
||||
obj.fs.close(obj.agentUpdate.fd);
|
||||
obj.agentUpdate = null;
|
||||
} else {
|
||||
// Send the first block to the agent
|
||||
obj.agentUpdate.ptr += len;
|
||||
//console.log("Agent update send first block: " + len);
|
||||
obj.send(obj.agentUpdate.buf); // Command 14, mesh agent first data block
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cmdid == 14) { // MeshCommand_AgentBinaryBlock
|
||||
if ((msg.length == 4) && (obj.agentUpdate != null)) {
|
||||
var status = obj.common.ReadShort(msg, 2);
|
||||
if (status == 1) {
|
||||
var len = -1;
|
||||
try { len = obj.fs.readSync(obj.agentUpdate.fd, obj.agentUpdate.buf, 4, agentUpdateBlockSize, obj.agentUpdate.ptr); } catch (e) { }
|
||||
if (len == -1) {
|
||||
// Error reading the agent file, stop here.
|
||||
obj.fs.close(obj.agentUpdate.fd);
|
||||
obj.agentUpdate = null;
|
||||
} else {
|
||||
// Send the next block to the agent
|
||||
obj.agentUpdate.ptr += len;
|
||||
//console.log("Agent update send next block: " + len);
|
||||
if (len == agentUpdateBlockSize) { obj.ws.send(obj.agentUpdate.buf); } else { obj.ws.send(obj.agentUpdate.buf.slice(0, len + 4)); } // Command 14, mesh agent next data block
|
||||
|
||||
if (len < agentUpdateBlockSize) {
|
||||
//console.log("Agent update sent");
|
||||
obj.send(obj.common.ShortToStr(13) + obj.common.ShortToStr(0) + obj.common.hex2rstr(obj.agentInfo.hash)); // Command 13, end mesh agent download, send agent SHA256 hash
|
||||
obj.fs.close(obj.agentUpdate.fd);
|
||||
obj.agentUpdate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cmdid == 15) { // MeshCommand_AgentTag
|
||||
ChangeAgentTag(msg.substring(2));
|
||||
}
|
||||
}
|
||||
else if (obj.authenticated < 2) { // We are not authenticated
|
||||
var cmd = obj.common.ReadShort(msg, 0);
|
||||
if (cmd == 1) {
|
||||
// Agent authentication request
|
||||
if ((msg.length != 66) || ((obj.receivedCommands & 1) != 0)) return;
|
||||
obj.receivedCommands += 1; // Agent can't send the same command twice on the same connection ever. Block DOS attack path.
|
||||
|
||||
// Check that the server hash matches out own web certificate hash
|
||||
if (obj.parent.webCertificatHash != msg.substring(2, 34)) { obj.close(); return; }
|
||||
|
||||
// Use our server private key to sign the ServerHash + AgentNonce + ServerNonce
|
||||
var privateKey = obj.forge.pki.privateKeyFromPem(obj.parent.certificates.agent.key);
|
||||
var md = obj.forge.md.sha256.create();
|
||||
md.update(msg.substring(2), 'binary');
|
||||
md.update(obj.nonce, 'binary');
|
||||
obj.agentnonce = msg.substring(34);
|
||||
|
||||
// Send back our certificate + signature
|
||||
obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(parent.agentCertificatAsn1.length) + parent.agentCertificatAsn1 + privateKey.sign(md)); // Command 2, certificate + signature
|
||||
|
||||
// Check the agent signature if we can
|
||||
if (obj.unauthsign != undefined) {
|
||||
if (processAgentSignature(obj.unauthsign) == false) { disonnect(); return; } else { completeAgentConnection(); }
|
||||
}
|
||||
}
|
||||
else if (cmd == 2) {
|
||||
// Agent certificate
|
||||
if ((msg.length < 4) || ((obj.receivedCommands & 2) != 0)) return;
|
||||
obj.receivedCommands += 2; // Agent can't send the same command twice on the same connection ever. Block DOS attack path.
|
||||
|
||||
// Decode the certificate
|
||||
var certlen = obj.common.ReadShort(msg, 2);
|
||||
obj.unauth = {};
|
||||
obj.unauth.nodeCert = null;
|
||||
try { obj.unauth.nodeCert = obj.forge.pki.certificateFromAsn1(obj.forge.asn1.fromDer(msg.substring(4, 4 + certlen))); } catch (e) { return; }
|
||||
obj.unauth.nodeid = obj.forge.pki.getPublicKeyFingerprint(obj.unauth.nodeCert.publicKey, { encoding: 'hex', md: obj.forge.md.sha256.create() });
|
||||
|
||||
// Check the agent signature if we can
|
||||
if (obj.agentnonce == undefined) { obj.unauthsign = msg.substring(4 + certlen); } else { if (processAgentSignature(msg.substring(4 + certlen)) == false) { disonnect(); return; } }
|
||||
completeAgentConnection();
|
||||
}
|
||||
else if (cmd == 3) {
|
||||
// Agent meshid
|
||||
if ((msg.length < 56) || ((obj.receivedCommands & 4) != 0)) return;
|
||||
obj.receivedCommands += 4; // Agent can't send the same command twice on the same connection ever. Block DOS attack path.
|
||||
|
||||
// Set the meshid
|
||||
obj.agentInfo = {};
|
||||
obj.agentInfo.infoVersion = obj.common.ReadInt(msg, 2);
|
||||
obj.agentInfo.agentId = obj.common.ReadInt(msg, 6);
|
||||
obj.agentInfo.agentVersion = obj.common.ReadInt(msg, 10);
|
||||
obj.agentInfo.platformType = obj.common.ReadInt(msg, 14);
|
||||
obj.meshid = obj.common.rstr2hex(msg.substring(18, 50)).toUpperCase();
|
||||
obj.agentInfo.capabilities = obj.common.ReadInt(msg, 50);
|
||||
var computerNameLen = obj.common.ReadShort(msg, 54);
|
||||
obj.agentInfo.computerName = msg.substring(56, 56 + computerNameLen);
|
||||
obj.dbMeshKey = 'mesh/' + obj.domain.id + '/' + obj.meshid;
|
||||
completeAgentConnection();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If error, do nothing
|
||||
ws.on('error', function (err) { console.log(err); });
|
||||
|
||||
// If the mesh agent web socket is closed, clean up.
|
||||
ws.on('close', function (req) { obj.close(1); });
|
||||
|
||||
// Start authenticate the mesh agent by sending a auth nonce & server TLS cert hash.
|
||||
// Send 256 bits SHA256 hash of TLS cert public key + 256 bits nonce
|
||||
obj.nonce = obj.forge.random.getBytesSync(32);
|
||||
obj.send(obj.common.ShortToStr(1) + parent.webCertificatHash + obj.nonce); // Command 1, hash + nonce
|
||||
|
||||
// Once we get all the information about an agent, run this to hook everything up to the server
|
||||
function completeAgentConnection() {
|
||||
if (obj.authenticated =! 1 || obj.meshid == null) return;
|
||||
// Check that the mesh exists
|
||||
obj.db.Get(obj.dbMeshKey, function (err, meshes) {
|
||||
if (meshes.length == 0) { console.log('Agent connected with invalid domain/mesh, holding connection.'); return; } // If we disconnect, the agnet will just reconnect. We need to log this or tell agent to connect in a few hours.
|
||||
var mesh = meshes[0];
|
||||
if (mesh.mtype != 2) { console.log('Agent connected with invalid mesh type, holding connection.'); return; } // If we disconnect, the agnet will just reconnect. We need to log this or tell agent to connect in a few hours.
|
||||
|
||||
// Check that the node exists
|
||||
obj.db.Get(obj.dbNodeKey, function (err, nodes) {
|
||||
// Mark when we connected to this agent
|
||||
obj.connectTime = Date.now();
|
||||
|
||||
if (nodes.length == 0) {
|
||||
// This node does not exist, create it.
|
||||
var device = { type: 'node', mtype: mesh.mtype, _id: obj.dbNodeKey, icon: obj.agentInfo.platformType, meshid: obj.dbMeshKey, name: obj.agentInfo.computerName, domain: domain.id, agent: { ver: obj.agentInfo.agentVersion, id: obj.agentInfo.agentId, caps: obj.agentInfo.capabilities }, host: null };
|
||||
obj.db.Set(device);
|
||||
|
||||
// Event the new node
|
||||
var change = 'Added device ' + obj.agentInfo.computerName + ' to mesh ' + mesh.name;
|
||||
obj.parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, { etype: 'node', action: 'addnode', node: device, msg: change, domain: domain.id })
|
||||
} else {
|
||||
// Device already exists, look if changes has occured
|
||||
var device = nodes[0];
|
||||
if (device.agent == undefined) {
|
||||
device.agent = { ver: obj.agentInfo.agentVersion, id: obj.agentInfo.agentId, caps: obj.agentInfo.capabilities }; change = 1;
|
||||
} else {
|
||||
var changes = [], change = 0;
|
||||
if (device.agent.ver != obj.agentInfo.agentVersion) { device.agent.ver = obj.agentInfo.agentVersion; change = 1; changes.push('agent version'); }
|
||||
if (device.agent.id != obj.agentInfo.agentId) { device.agent.id = obj.agentInfo.agentId; change = 1; changes.push('agent type'); }
|
||||
if ((device.agent.caps & 24) != (obj.agentInfo.capabilities & 24)) { device.agent.caps = obj.agentInfo.capabilities; change = 1; changes.push('agent capabilities'); } // If agent console or javascript support changes, update capabilities
|
||||
if (device.meshid != obj.dbMeshKey) { device.meshid = obj.dbMeshKey; change = 1; changes.push('agent meshid'); } // TODO: If the meshid changes, we need to event a device add/remove on both meshes
|
||||
if (change == 1) {
|
||||
obj.db.Set(device);
|
||||
|
||||
// Event the node change
|
||||
var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id, msg: 'Changed device ' + device.name + ' from mesh ' + mesh.name + ': ' + changes.join(', ') };
|
||||
var device2 = obj.common.Clone(device);
|
||||
if (device2.intelamt && device2.intelamt.pass) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
|
||||
event.node = device;
|
||||
obj.parent.parent.DispatchEvent(['*', device.meshid], obj, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this agent is already connected
|
||||
var dupAgent = obj.parent.wsagents[obj.dbNodeKey];
|
||||
obj.parent.wsagents[obj.dbNodeKey] = obj;
|
||||
if (dupAgent) {
|
||||
// Close the duplicate agent
|
||||
dupAgent.close();
|
||||
} else {
|
||||
// Indicate the agent is connected
|
||||
obj.parent.parent.SetConnectivityState(obj.dbMeshKey, obj.dbNodeKey, obj.connectTime, 1, 1);
|
||||
}
|
||||
|
||||
// We are done, ready to communicate with this agent
|
||||
obj.authenticated = 2;
|
||||
|
||||
// Command 4, inform mesh agent that it's authenticated.
|
||||
obj.send(obj.common.ShortToStr(4));
|
||||
|
||||
// Check the mesh core, if the agent is capable of running one
|
||||
if ((obj.agentInfo.capabilities & 16) != 0) { obj.send(obj.common.ShortToStr(11) + obj.common.ShortToStr(0)); } // Command 11, ask for mesh core hash.
|
||||
|
||||
// Check if we need to make an native update check
|
||||
obj.agentInfo = obj.parent.parent.meshAgentBinaries[obj.agentInfo.agentId];
|
||||
if ((obj.agentInfo != undefined) && (obj.agentInfo.update == true)) { obj.send(obj.common.ShortToStr(12) + obj.common.ShortToStr(0)); } // Ask the agent for it's executable binary hash
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Verify the agent signature
|
||||
function processAgentSignature(msg) {
|
||||
var md = obj.forge.md.sha256.create(); // TODO: Switch this to SHA256 on node instead of forge.
|
||||
md.update(obj.parent.webCertificatHash, 'binary');
|
||||
md.update(obj.nonce, 'binary');
|
||||
md.update(obj.agentnonce, 'binary');
|
||||
if (obj.unauth.nodeCert.publicKey.verify(md.digest().bytes(), msg) == false) return false;
|
||||
|
||||
// Connection is a success, clean up
|
||||
obj.nodeid = obj.unauth.nodeid.toUpperCase();
|
||||
obj.dbNodeKey = 'node/' + domain.id + '/' + obj.nodeid;
|
||||
delete obj.nonce;
|
||||
delete obj.agentnonce;
|
||||
delete obj.unauth;
|
||||
if (obj.unauthsign) delete obj.unauthsign;
|
||||
obj.parent.parent.debug(1, 'Verified agent connection to ' + obj.nodeid);
|
||||
obj.authenticated = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Process incoming agent JSON data
|
||||
function processAgentData(msg) {
|
||||
var str = msg.toString('utf8');
|
||||
if (str[0] == '{') {
|
||||
try { command = JSON.parse(str) } catch (e) { console.log('Unable to parse JSON'); return; } // If the command can't be parsed, ignore it.
|
||||
switch (command.action) {
|
||||
case 'msg':
|
||||
{
|
||||
// Route a message.
|
||||
// If this command has a sessionid, that is the target.
|
||||
if (command.sessionid != undefined) {
|
||||
var splitsessionid = command.sessionid.split('/');
|
||||
// Check that we are in the same domain and the user has rights over this node.
|
||||
if ((splitsessionid[0] == 'user') && (splitsessionid[1] == domain.id)) {
|
||||
// Check if this user has rights to get this message
|
||||
//if (mesh.links[user._id] == undefined || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// See if the session is connected
|
||||
var ws = obj.parent.wssessions2[command.sessionid];
|
||||
|
||||
// Go ahead and send this message to the target node
|
||||
if (ws != undefined) {
|
||||
command.nodeid = obj.dbNodeKey; // Set the nodeid, required for responses.
|
||||
delete command.sessionid; // Remove the sessionid, since we are sending to that sessionid, so it's implyed.
|
||||
ws.send(JSON.stringify(command));
|
||||
}
|
||||
}
|
||||
} else if (command.userid != undefined) { // If this command has a userid, that is the target.
|
||||
var splituserid = command.userid.split('/');
|
||||
// Check that we are in the same domain and the user has rights over this node.
|
||||
if ((splituserid[0] == 'user') && (splituserid[1] == domain.id)) {
|
||||
// Check if this user has rights to get this message
|
||||
//if (mesh.links[user._id] == undefined || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// See if the session is connected
|
||||
var sessions = obj.parent.wssessions[command.userid];
|
||||
|
||||
// Go ahead and send this message to the target node
|
||||
if (sessions != undefined) {
|
||||
command.nodeid = obj.dbNodeKey; // Set the nodeid, required for responses.
|
||||
delete command.userid; // Remove the userid, since we are sending to that userid, so it's implyed.
|
||||
for (var i in sessions) { sessions[i].send(JSON.stringify(command)); }
|
||||
}
|
||||
}
|
||||
} else { // Route this command to the mesh
|
||||
for (var userid in obj.parent.wssessions) { // Find all connected users for this mesh and send the message
|
||||
var user = obj.parent.users[userid];
|
||||
if (user) {
|
||||
var rights = user.links[obj.dbMeshKey];
|
||||
if (rights != undefined) { // TODO: Look at what rights are needed for message routing
|
||||
command.nodeid = obj.dbNodeKey;
|
||||
var sessions = obj.parent.wssessions[userid];
|
||||
for (var i in sessions) { sessions[i].send(JSON.stringify(command)); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'coreinfo':
|
||||
{
|
||||
// Sent by the agent to update agent information
|
||||
ChangeAgentCoreInfo(command);
|
||||
break;
|
||||
}
|
||||
case 'netinfo':
|
||||
{
|
||||
// Sent by the agent to update agent network interface information
|
||||
delete command.action;
|
||||
command.updateTime = Date.now();
|
||||
command._id = 'if' + obj.dbNodeKey;
|
||||
command.type = 'ifinfo';
|
||||
obj.db.Set(command);
|
||||
|
||||
// Event the node interface information change
|
||||
obj.parent.parent.DispatchEvent(['*', obj.meshid], obj, { action: 'ifchange', nodeid: obj.dbNodeKey, domain: domain.id, nolog: 1 });
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Change the current core information string and event it
|
||||
function ChangeAgentCoreInfo(command) {
|
||||
if ((command == undefined) || (command == null)) return; // Safety, should never happen.
|
||||
|
||||
// Check capabilities value
|
||||
if (command.caps == undefined || command.caps == null) { command.caps = 0; } else { if (typeof command.caps != 'number') command.caps = 0; }
|
||||
|
||||
// Check that the mesh exists
|
||||
obj.db.Get(obj.dbMeshKey, function (err, meshes) {
|
||||
if (meshes.length != 1) return;
|
||||
var mesh = meshes[0];
|
||||
// Get the node and change it if needed
|
||||
obj.db.Get(obj.dbNodeKey, function (err, nodes) {
|
||||
if (nodes.length != 1) return;
|
||||
var device = nodes[0];
|
||||
if (device.agent) {
|
||||
var changes = [], change = 0;
|
||||
|
||||
// Check if anything changes
|
||||
if (device.agent.core != command.value) { if ((command.value == null) && (device.agent.core != undefined)) { delete device.agent.core; } else { device.agent.core = command.value; } change = 1; changes.push('agent core'); }
|
||||
if ((device.agent.caps & 0xFFFFFFE7) != (command.caps & 0xFFFFFFE7)) { device.agent.caps = ((device.agent.caps & 24) + (command.caps & 0xFFFFFFE7)); change = 1; changes.push('agent capabilities'); } // Allow Javascript on the agent to change all capabilities except console and javascript support
|
||||
if (command.intelamt) {
|
||||
if (!device.intelamt) { device.intelamt = {}; }
|
||||
if (device.intelamt.ver != command.intelamt.ver) { device.intelamt.ver = command.intelamt.ver; change = 1; changes.push('AMT version'); }
|
||||
if (device.intelamt.state != command.intelamt.state) { device.intelamt.state = command.intelamt.state; change = 1; changes.push('AMT state'); }
|
||||
if (device.intelamt.flags != command.intelamt.flags) { device.intelamt.flags = command.intelamt.flags; change = 1; changes.push('AMT flags'); }
|
||||
if (device.intelamt.host != command.intelamt.host) { device.intelamt.host = command.intelamt.host; change = 1; changes.push('AMT host'); }
|
||||
}
|
||||
if (mesh.mtype == 2) {
|
||||
var remoteaddr = obj.ws._socket.remoteAddress;
|
||||
if (remoteaddr.startsWith('::ffff:')) { remoteaddr = remoteaddr.substring(7); }
|
||||
if (device.host != remoteaddr) { device.host = remoteaddr; change = 1; changes.push('host'); }
|
||||
// TODO: Check that the agent has an interface that is the same as the one we got this websocket connection on. Only set if we have a match.
|
||||
}
|
||||
|
||||
// If there are changes, save and event
|
||||
if (change == 1) {
|
||||
obj.db.Set(device);
|
||||
|
||||
// Event the node change
|
||||
var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id, msg: 'Changed device ' + device.name + ' from mesh ' + mesh.name + ': ' + changes.join(', ') };
|
||||
var device2 = obj.common.Clone(device);
|
||||
if (device2.intelamt && device2.intelamt.pass) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
|
||||
event.node = device;
|
||||
obj.parent.parent.DispatchEvent(['*', device.meshid], obj, event);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Update the mesh agent tab in the database
|
||||
function ChangeAgentTag(tag) {
|
||||
if (tag.length == 0) { tag = undefined; }
|
||||
// Get the node and change it if needed
|
||||
obj.db.Get(obj.dbNodeKey, function (err, nodes) {
|
||||
if (nodes.length != 1) return;
|
||||
var device = nodes[0];
|
||||
if (device.agent) {
|
||||
if (device.agent.tag != tag) {
|
||||
device.agent.tag = tag;
|
||||
obj.db.Set(device);
|
||||
|
||||
// Event the node change
|
||||
var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id, nolog: 1 };
|
||||
var device2 = obj.common.Clone(device);
|
||||
if (device2.intelamt && device2.intelamt.pass) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
|
||||
event.node = device;
|
||||
obj.parent.parent.DispatchEvent(['*', device.meshid], obj, event);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,642 @@
|
|||
/**
|
||||
* @description Meshcentral
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
function CreateMeshCentralServer() {
|
||||
var obj = {};
|
||||
obj.db;
|
||||
obj.webserver;
|
||||
obj.redirserver;
|
||||
obj.mpsserver;
|
||||
obj.amtEventHandler;
|
||||
obj.amtScanner;
|
||||
obj.meshScanner;
|
||||
obj.eventsDispatch = {};
|
||||
obj.fs = require('fs');
|
||||
obj.path = require('path');
|
||||
obj.crypto = require('crypto');
|
||||
obj.platform = require('os').platform();
|
||||
obj.args = require('minimist')(process.argv.slice(2));
|
||||
obj.common = require('./common.js');
|
||||
obj.certificates = null;
|
||||
obj.connectivityByMesh = {}; // This object keeps a list of all connected CIRA and agents, by meshid->nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect)
|
||||
obj.connectivityByNode = {}; // This object keeps a list of all connected CIRA and agents, by nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect)
|
||||
obj.debugLevel = 0;
|
||||
obj.config = {}; // Configuration file
|
||||
obj.dbconfig = {}; // Persistance values, loaded from database
|
||||
obj.datapath = obj.path.join(__dirname, '../.meshcentral-data');
|
||||
obj.filespath = obj.path.join(__dirname, '../.meshcentral-files');
|
||||
obj.certificateOperations = require('./certoperations.js').CertificateOperations();
|
||||
obj.defaultMeshCore = null;
|
||||
obj.defaultMeshCoreHash = null;
|
||||
obj.meshAgentBinaries = {}; // Mesh Agent Binaries, Architecture type --> { hash:(sha256 hash), size:(binary size), path:(binary path) }
|
||||
obj.meshAgentInstallScripts = {}; // Mesh Install Scripts, Script ID -- { hash:(sha256 hash), size:(binary size), path:(binary path) }
|
||||
obj.multiServer = null;
|
||||
|
||||
// Create data and files folders if needed
|
||||
try { obj.fs.mkdirSync(obj.datapath); } catch (e) { }
|
||||
try { obj.fs.mkdirSync(obj.filespath); } catch (e) { }
|
||||
|
||||
// Windows Specific Code, setup service and event log
|
||||
obj.service = null;
|
||||
obj.servicelog = null;
|
||||
if (obj.platform == 'win32') {
|
||||
var nodewindows = require('node-windows');
|
||||
obj.service = nodewindows.Service;
|
||||
var eventlogger = nodewindows.EventLogger;
|
||||
obj.servicelog = new eventlogger('MeshCentral');
|
||||
}
|
||||
|
||||
// Start the Meshcentral server
|
||||
obj.Start = function () {
|
||||
try { require('./pass').hash('test', function () { }); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not.
|
||||
|
||||
// Check for invalid arguments
|
||||
var validArguments = ['_', 'notls', 'user', 'port', 'mpsport', 'redirport', 'cert', 'deletedomain', 'deletedefaultdomain', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn'];
|
||||
for (var arg in obj.args) { if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } }
|
||||
if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; }
|
||||
|
||||
if ((obj.args.help == true) || (obj.args['?'] == true)) {
|
||||
console.log('MeshCentral2 Beta 1, a web-based remote computer management web portal.\r\n');
|
||||
if (obj.platform == 'win32') {
|
||||
console.log('Run as a Windows Service');
|
||||
console.log(' --install/uninstall Install Meshcentral as a background service.');
|
||||
console.log(' --start/stop/restart Control Meshcentral background service.');
|
||||
console.log('Run standalone, console application');
|
||||
}
|
||||
console.log(' --notls Use HTTP instead of HTTPS for the main web server.');
|
||||
console.log(' --user [username] Always login as [username] if account exists.');
|
||||
console.log(' --port [number] Web server port number.');
|
||||
console.log(' --mpsport [number] Intel AMT server port number.');
|
||||
console.log(' --redirport [number] Creates an additional HTTP server to redirect users to the HTTPS server.');
|
||||
console.log(' --exactports Server must run with correct ports or exit.');
|
||||
console.log(' --noagentupdate Server will not update mesh agent native binaries.');
|
||||
console.log(' --cert [name], (country), (org) Create a web server certificate with [name]server name.');
|
||||
console.log(' country and organization can optionaly be set.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we need to install, start, stop, remove ourself as a background service
|
||||
if ((obj.service != null) && ((obj.args.install == true) || (obj.args.uninstall == true) || (obj.args.start == true) || (obj.args.stop == true) || (obj.args.restart == true))) {
|
||||
var env = [], xenv = ['user', 'port', 'mpsport', 'redirport', 'exactport', 'debug'];
|
||||
for (var i in xenv) { if (obj.args[xenv[i]] != undefined) { env.push({ name: 'mesh' + xenv[i], value: obj.args[xenv[i]] }); } } // Set some args as service environement variables.
|
||||
var svc = new obj.service({ name: 'MeshCentral', description: 'MeshCentral Remote Management Server', script: process.argv[1] + '.js', env: env, wait: 2, grow: .5 });
|
||||
svc.on('install', function () { console.log('MeshCentral service installed.'); svc.start(); });
|
||||
svc.on('uninstall', function () { console.log('MeshCentral service uninstalled.'); process.exit(); });
|
||||
svc.on('start', function () { console.log('MeshCentral service started.'); process.exit(); });
|
||||
svc.on('stop', function () { console.log('MeshCentral service stopped.'); if (obj.args.stop) { process.exit(); } if (obj.args.restart) { console.log('Holding 5 seconds...'); setTimeout(function () { svc.start(); }, 5000); } });
|
||||
svc.on('alreadyinstalled', function () { console.log('MeshCentral service already installed.'); process.exit(); });
|
||||
svc.on('invalidinstallation', function () { console.log('Invalid MeshCentral service installation.'); process.exit(); });
|
||||
try {
|
||||
if (obj.args.install == true) { svc.install(); return; }
|
||||
else if (obj.args.uninstall == true) { svc.uninstall(); return; }
|
||||
else if (obj.args.start == true) { svc.start(); return; }
|
||||
else if (obj.args.stop == true || obj.args.restart == true) { svc.stop(); return; }
|
||||
} catch (e) { logException(e); }
|
||||
}
|
||||
|
||||
// If "--launch" is in the arguments, launch now
|
||||
if (obj.args.launch == 1) {
|
||||
obj.StartEx();
|
||||
} else {
|
||||
// if "--launch" is not specified, launch the server as a child process.
|
||||
var startLine = '';
|
||||
for (var i in process.argv) {
|
||||
var arg = process.argv[i];
|
||||
if (arg.length > 0) {
|
||||
if (startLine.length > 0) startLine += ' ';
|
||||
if (arg.indexOf(' ') >= 0) { startLine += '"' + arg + '"'; } else { startLine += arg; }
|
||||
}
|
||||
}
|
||||
obj.launchChildServer(startLine);
|
||||
}
|
||||
}
|
||||
|
||||
// Launch MeshCentral as a child server and monitor it.
|
||||
obj.launchChildServer = function (startLine) {
|
||||
var child_process = require('child_process');
|
||||
var xprocess = child_process.exec(startLine + ' --launch', function (error, stdout, stderr) {
|
||||
if (xprocess.xrestart == true) {
|
||||
setTimeout(function () { obj.launchChildServer(startLine); }, 500); // If exit with restart requested, restart the server.
|
||||
} else {
|
||||
if (error != null) { console.log('ERROR: Unable to start MeshCentral: ' + error); process.exit(); }
|
||||
}
|
||||
});
|
||||
xprocess.stdout.on('data', function (data) { if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); } if (data.indexOf('Updating settings folder...') >= 0) { xprocess.xrestart = true; } console.log(data); });
|
||||
xprocess.stderr.on('data', function (data) { if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); } console.error(data); });
|
||||
xprocess.on('close', function (code) { if ((code != 0) && (code != 123)) { console.log("Exited with code " + code); } });
|
||||
}
|
||||
|
||||
// Get current and latest MeshCentral server versions using NPM
|
||||
obj.getLatestServerVersion = function (callback) {
|
||||
if (callback == undefined) return;
|
||||
var child_process = require('child_process');
|
||||
var xprocess = child_process.exec('npm view meshcentral dist-tags.latest', function (error, stdout, stderr) {
|
||||
if (xprocess.xrestart == true) {
|
||||
setTimeout(function () { obj.launchChildServer(startLine); }, 500); // If exit with restart requested, restart the server.
|
||||
} else {
|
||||
if (error != null) { console.log('ERROR: Unable to start MeshCentral: ' + error); process.exit(); }
|
||||
}
|
||||
});
|
||||
xprocess.data = '';
|
||||
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('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);
|
||||
});
|
||||
}
|
||||
|
||||
obj.StartEx = function () {
|
||||
// Look to see if data and/or file path is specified
|
||||
if (obj.args.datapath) { obj.datapath = obj.args.datapath; }
|
||||
if (obj.args.filespath) { obj.filespath = obj.args.filespath; }
|
||||
|
||||
// Read configuration file if present and change arguments.
|
||||
if (require('fs').existsSync(obj.path.join(obj.datapath, 'config.json'))) {
|
||||
// Load and validate the configuration file
|
||||
try { obj.config = require(obj.path.join(obj.datapath, 'config.json')); } catch (e) { console.log('ERROR: Unable to parse ./data/config.json.'); return; }
|
||||
if (obj.config.domains == undefined) { obj.config.domains = {}; }
|
||||
for (var i in obj.config.domains) { if ((i.split('/').length > 1) || (i.split(' ').length > 1)) { console.log("ERROR: Error in config.json, domain names can't have spaces or /."); return; } }
|
||||
|
||||
// Set the command line arguments to the config file if they are not present
|
||||
if (obj.config.settings) { for (var i in obj.config.settings) { if (obj.args[i] == undefined) obj.args[i] = obj.config.settings[i]; } }
|
||||
}
|
||||
|
||||
// Read environment variables. For a subset of arguments, we allow them to be read from environment variables.
|
||||
var xenv = ['user', 'port', 'mpsport', 'redirport', 'exactport', 'debug'];
|
||||
for (var i in xenv) { if ((obj.args[xenv[i]] == undefined) && (process.env['mesh' + xenv[i]])) { obj.args[xenv[i]] = obj.common.toNumber(process.env['mesh' + xenv[i]]); } }
|
||||
|
||||
// Validate the domains, this is used for multi-hosting
|
||||
if (obj.config.domains == undefined) { obj.config.domains = {}; }
|
||||
if (obj.config.domains[''] == undefined) { obj.config.domains[''] = { }; }
|
||||
var xdomains = {}; for (var i in obj.config.domains) { if (!obj.config.domains[i].title) { obj.config.domains[i].title = 'MeshCentral'; } if (!obj.config.domains[i].title2) { obj.config.domains[i].title2 = '2.0 Beta 1'; } xdomains[i.toLowerCase()] = obj.config.domains[i]; } obj.config.domains = xdomains;
|
||||
var bannedDomains = ['public', 'private', 'images', 'scripts', 'styles', 'views']; // List of banned domains
|
||||
for (var i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in ./data/config.json."); return; } } }
|
||||
for (var i in obj.config.domains) { obj.config.domains[i].url = (i == '')?'/':('/' + i + '/'); obj.config.domains[i].id = i; }
|
||||
|
||||
// Log passed arguments into Windows Service Log
|
||||
//if (obj.servicelog != null) { var s = ''; for (var i in obj.args) { if (i != '_') { if (s.length > 0) { s += ', '; } s += i + "=" + obj.args[i]; } } logInfoEvent('MeshServer started with arguments: ' + s); }
|
||||
|
||||
// Look at passed in arguments
|
||||
if ((obj.args.ciralocalfqdn != undefined) && ((obj.args.lanonly == true) || (obj.args.wanonly == true))) { console.log("WARNING: CIRA local FQDN's ignored when server in LAN-only or WAN-only mode."); }
|
||||
if ((obj.args.ciralocalfqdn != undefined) && (obj.args.ciralocalfqdn.split(',').length > 4)) { console.log("WARNING: Can't have more than 4 CIRA local FQDN's. Ignoring value."); obj.args.ciralocalfqdn = undefined; }
|
||||
if (obj.args.port == undefined || typeof obj.args.port != 'number') { if (obj.args.notls == undefined) { obj.args.port = 443; } else { obj.args.port = 80; } }
|
||||
if (obj.args.mpsport == undefined || typeof obj.args.mpsport != 'number') obj.args.mpsport = 4433;
|
||||
if (obj.args.notls == undefined && obj.args.redirport == undefined) obj.args.redirport = 80;
|
||||
if (typeof obj.args.debug == 'number') obj.debugLevel = obj.args.debug;
|
||||
if (obj.args.debug == true) obj.debugLevel = 1;
|
||||
obj.db = require('./db.js').CreateDB(obj.args, obj.datapath);
|
||||
obj.db.SetupDatabase(function (dbversion) {
|
||||
// See if any database operations needs to be completed
|
||||
if (obj.args.deletedomain) { obj.db.DeleteDomain(obj.args.deletedomain, function () { console.log('Deleted domain ' + obj.args.deletedomain + '.'); process.exit(); }); return; }
|
||||
if (obj.args.deletedefaultdomain) { obj.db.DeleteDomain('', function () { console.log('Deleted default domain.'); process.exit(); }); return; }
|
||||
if (obj.args.showusers) { obj.db.GetAllType('user', function (err, docs) { console.log(docs); process.exit(); }); return; }
|
||||
if (obj.args.shownodes) { obj.db.GetAllType('node', function (err, docs) { console.log(docs); process.exit(); }); return; }
|
||||
if (obj.args.showmeshes) { obj.db.GetAllType('mesh', function (err, docs) { console.log(docs); process.exit(); }); return; }
|
||||
if (obj.args.showevents) { obj.db.GetAllType('event', function (err, docs) { console.log(docs); process.exit(); }); return; }
|
||||
if (obj.args.showpower) { obj.db.GetAllType('power', function (err, docs) { console.log(docs); process.exit(); }); return; }
|
||||
|
||||
// Clear old event entries and power entires
|
||||
obj.db.clearOldEntries('event', 30); // Clear all event entires that are older than 30 days.
|
||||
obj.db.clearOldEntries('power', 10); // Clear all event entires that are older than 10 days. If a node is connected longer than 10 days, current power state will be used for everything.
|
||||
|
||||
// Perform other database cleanup
|
||||
obj.db.cleanup();
|
||||
|
||||
// Set all nodes to power state of unknown (0)
|
||||
// TODO: This time for this message can be earlier: When server closed or last time did an update to the db.
|
||||
obj.db.file.insert({ type: 'power', time: Date.now(), node: '*', power: 0 });
|
||||
|
||||
// Read or setup database configuration values
|
||||
obj.db.Get('dbconfig', function (err, dbconfig) {
|
||||
if (dbconfig.length == 1) { obj.dbconfig = dbconfig[0]; } else { obj.dbconfig = { _id: 'dbconfig', version: 1 }; }
|
||||
if (obj.dbconfig.amtWsEventSecret == undefined) { require('crypto').randomBytes(32, function (err, buf) { obj.dbconfig.amtWsEventSecret = buf.toString('hex'); obj.db.Set(obj.dbconfig); }); }
|
||||
|
||||
// This is used by the user to create a username/password for a Intel AMT WSMAN event subscription
|
||||
if (obj.args.getwspass) {
|
||||
if (obj.args.getwspass.length == 64) {
|
||||
require('crypto').randomBytes(6, function (err, buf) {
|
||||
while (obj.dbconfig.amtWsEventSecret == undefined) { process.nextTick(); }
|
||||
var username = buf.toString('hex');
|
||||
var nodeid = obj.args.getwspass;
|
||||
var pass = require('crypto').createHash('sha256').update(username.toLowerCase() + ":" + nodeid.toUpperCase() + ":" + obj.dbconfig.amtWsEventSecret).digest("base64").substring(0, 12).split("/").join("x").split("\\").join("x");
|
||||
console.log('--- Intel(r) AMT WSMAN eventing credentials ---');
|
||||
console.log('Username: ' + username);
|
||||
console.log('Password: ' + pass);
|
||||
console.log('Argument: ' + nodeid.toLowerCase());
|
||||
process.exit();
|
||||
});
|
||||
} else {
|
||||
console.log('Invalid NodeID.');
|
||||
process.exit();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the default mesh core
|
||||
obj.updateMeshCore();
|
||||
|
||||
// Load server certificates
|
||||
obj.certificateOperations.GetMeshServerCertificate(obj.datapath, obj.args.cert, function (certs) {
|
||||
obj.certificates = certs;
|
||||
|
||||
// If the certificate is un-configured, force LAN-only mode
|
||||
if (obj.certificates.CommonName == 'un-configured') { console.log('Server name not configured, running in LAN-only mode.'); obj.args.lanonly = true; }
|
||||
|
||||
// Load the list of mesh agents and install scripts
|
||||
if (obj.args.noagentupdate == 1) { for (var i in meshAgentsArchitectureNumbers) { meshAgentsArchitectureNumbers[i].update = false; } }
|
||||
obj.updateMeshAgentsTable();
|
||||
obj.updateMeshAgentInstallScripts();
|
||||
|
||||
// Setup and start the web server
|
||||
require('crypto').randomBytes(32, function (err, buf) {
|
||||
// Setup Mesh Multi-Server if needed
|
||||
obj.multiServer = require('./multiserver.js').CreateMultiServer(obj, obj.args);
|
||||
|
||||
if (obj.args.secret) {
|
||||
// This secret is used to encrypt HTTP session information, if specified, user it.
|
||||
obj.webserver = require('./webserver.js').CreateWebServer(obj, obj.db, obj.args, obj.args.secret, obj.certificates);
|
||||
} else {
|
||||
// If the secret is not specified, generate a random number.
|
||||
obj.webserver = require('./webserver.js').CreateWebServer(obj, obj.db, obj.args, buf.toString('hex').toUpperCase(), obj.certificates);
|
||||
}
|
||||
|
||||
// Setup and start the redirection server if needed
|
||||
if (obj.args.redirport != undefined && typeof obj.args.redirport == 'number') {
|
||||
obj.redirserver = require('./redirserver.js').CreateRedirServer(obj, obj.db, obj.args, obj.certificates);
|
||||
}
|
||||
|
||||
// Setup the Intel AMT event handler
|
||||
obj.amtEventHandler = require('./amtevents.js').CreateAmtEventsHandler(obj);
|
||||
|
||||
// Setup the Intel AMT local network scanner
|
||||
if (obj.args.wanonly != true) {
|
||||
obj.amtScanner = require('./amtscanner.js').CreateAmtScanner(obj).start();
|
||||
obj.meshScanner = require('./meshscanner.js').CreateMeshScanner(obj).start();
|
||||
}
|
||||
|
||||
// Setup and start the MPS server
|
||||
if (obj.args.lanonly != true) {
|
||||
obj.mpsserver = require('./mpsserver.js').CreateMpsServer(obj, obj.db, obj.args, obj.certificates);
|
||||
}
|
||||
|
||||
// Dispatch an event that the server is now running
|
||||
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' })
|
||||
|
||||
obj.debug(1, 'Server started');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Stop the Meshcentral server
|
||||
obj.Stop = function (restoreFile) {
|
||||
// If the database is not setup, exit now.
|
||||
if (!obj.db) return;
|
||||
|
||||
// Dispatch an event saying the server is now stopping
|
||||
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'stopped', msg: 'Server stopped' })
|
||||
|
||||
// Set all nodes to power state of unknown (0)
|
||||
obj.db.file.insert({ type: 'power', time: Date.now(), node: '*', power: 0 }, function () {
|
||||
if (restoreFile) {
|
||||
obj.debug(1, 'Server stopped, updating settings: ' + restoreFile);
|
||||
console.log('Updating settings folder...');
|
||||
var fs = require('fs');
|
||||
var unzip = require('unzip');
|
||||
var rs = fs.createReadStream(restoreFile);
|
||||
rs.on('end', () => { setTimeout(function () { fs.unlinkSync(restoreFile); process.exit(123); }, 500); });
|
||||
rs.pipe(unzip.Extract({ path: obj.datapath }));
|
||||
} else {
|
||||
obj.debug(1, 'Server stopped');
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Event Dispatch
|
||||
obj.AddEventDispatch = function (ids, target) {
|
||||
obj.debug(3, 'AddEventDispatch', ids);
|
||||
for (var i in ids) { var id = ids[i]; if (!obj.eventsDispatch[id]) { obj.eventsDispatch[id] = [target]; } else { obj.eventsDispatch[id].push(target); } }
|
||||
}
|
||||
obj.RemoveEventDispatch = function (ids, target) {
|
||||
obj.debug(3, 'RemoveEventDispatch', id);
|
||||
for (var i in ids) { var id = ids[i]; if (obj.eventsDispatch[id]) { var j = obj.eventsDispatch[id].indexOf(target); if (j >= 0) { array.splice(j, 1); } } }
|
||||
}
|
||||
obj.RemoveEventDispatchId = function (id) {
|
||||
obj.debug(3, 'RemoveEventDispatchId', id);
|
||||
if (obj.eventsDispatch[id] != undefined) { delete obj.eventsDispatch[id]; }
|
||||
}
|
||||
obj.RemoveAllEventDispatch = function (target) {
|
||||
obj.debug(3, 'RemoveAllEventDispatch');
|
||||
for (var i in obj.eventsDispatch) { var j = obj.eventsDispatch[i].indexOf(target); if (j >= 0) { obj.eventsDispatch[i].splice(j, 1); } }
|
||||
}
|
||||
obj.DispatchEvent = function (ids, source, event, fromPeerServer) {
|
||||
// If the database is not setup, exit now.
|
||||
if (!obj.db) return;
|
||||
|
||||
obj.debug(3, 'DispatchEvent', ids);
|
||||
event.type = 'event';
|
||||
event.time = Date.now();
|
||||
event.ids = ids;
|
||||
if (!event.nolog) { obj.db.StoreEvent(ids, source, event); }
|
||||
var targets = []; // List of targets we dispatched the event to, we don't want to dispatch to the same target twice.
|
||||
for (var j in ids) {
|
||||
var id = ids[j];
|
||||
if (obj.eventsDispatch[id]) {
|
||||
for (var i in obj.eventsDispatch[id]) {
|
||||
if (targets.indexOf(obj.eventsDispatch[id][i]) == -1) { // Check if we already displatched to this target
|
||||
targets.push(obj.eventsDispatch[id][i]);
|
||||
obj.eventsDispatch[id][i].HandleEvent(source, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((fromPeerServer == undefined) && (obj.multiServer != null)) { obj.multiServer.DispatchEvent(ids, source, event); }
|
||||
delete targets;
|
||||
}
|
||||
|
||||
|
||||
// Set the connectivity state of a node and setup the server so that messages can be routed correctly.
|
||||
// meshId: mesh identifier of format mesh/domain/meshidhex
|
||||
// nodeId: node identifier of format node/domain/nodeidhex
|
||||
// connectTime: time of connection, milliseconds elapsed since the UNIX epoch.
|
||||
// connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local.
|
||||
// powerState: Value, 0 = Unknown, 1 = S0 power on, 2 = S1 Sleep, 3 = S2 Sleep, 4 = S3 Sleep, 5 = S4 Hibernate, 6 = S5 Soft-Off, 7 = Present
|
||||
var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local'];
|
||||
var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present'];
|
||||
obj.SetConnectivityState = function (meshid, nodeid, connectTime, connectType, powerState) {
|
||||
//console.log('SetConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + ', Power: ' + powerStateStrings[powerState]);
|
||||
|
||||
// Change the node connection state
|
||||
var eventConnectChange = 0;
|
||||
var state = obj.connectivityByNode[nodeid];
|
||||
if (state) {
|
||||
// Change the connection in the node and mesh state lists
|
||||
if ((state.connectivity & connectType) == 0) {
|
||||
state.connectivity |= connectType;
|
||||
eventConnectChange = 1;
|
||||
}
|
||||
} else {
|
||||
// Add the connection to the node and mesh state list
|
||||
obj.connectivityByNode[nodeid] = state = { connectivity: connectType };
|
||||
if (!obj.connectivityByMesh[meshid]) { obj.connectivityByMesh[meshid] = {}; }
|
||||
obj.connectivityByMesh[meshid][nodeid] = state;
|
||||
eventConnectChange = 1;
|
||||
}
|
||||
|
||||
// Set node power state
|
||||
if (connectType == 1) { state.agentPower = powerState; } else if (connectType == 2) { state.ciraPower = powerState; } else if (connectType == 4) { state.amtPower = powerState; }
|
||||
var powerState = 0, oldPowerState = state.powerState;
|
||||
if ((state.connectivity & 1) != 0) { powerState = state.agentPower; } else if ((state.connectivity & 2) != 0) { powerState = state.ciraPower; } else if ((state.connectivity & 4) != 0) { powerState = state.amtPower; }
|
||||
if ((state.powerState == undefined) || (state.powerState != powerState)) {
|
||||
state.powerState = powerState;
|
||||
eventConnectChange = 1;
|
||||
|
||||
// Set new power state in database
|
||||
obj.db.file.insert({ type: 'power', time: connectTime, node: nodeid, power: powerState, oldPower: oldPowerState });
|
||||
}
|
||||
|
||||
// Event the node connection change
|
||||
if (eventConnectChange == 1) { obj.DispatchEvent(['*', meshid], obj, { action: 'nodeconnect', meshid: meshid, nodeid: nodeid, conn: state.connectivity, pwr: state.powerState, ct: connectTime, nolog: 1 }); }
|
||||
}
|
||||
|
||||
// Clear the connectivity state of a node and setup the server so that messages can be routed correctly.
|
||||
// meshId: mesh identifier of format mesh/domain/meshidhex
|
||||
// nodeId: node identifier of format node/domain/nodeidhex
|
||||
// connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 3 = Intel AMT local.
|
||||
obj.ClearConnectivityState = function (meshid, nodeid, connectType) {
|
||||
//console.log('ClearConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType]);
|
||||
|
||||
// Remove the agent connection from the nodes connection list
|
||||
var state = obj.connectivityByNode[nodeid];
|
||||
if (state == undefined) return;
|
||||
|
||||
if ((state.connectivity & connectType) != 0) {
|
||||
state.connectivity -= connectType;
|
||||
|
||||
// If the node is completely disconnected, clean it up completely
|
||||
if (state.connectivity == 0) {
|
||||
delete obj.connectivityByNode[nodeid];
|
||||
delete obj.connectivityByMesh[meshid][nodeid];
|
||||
state.powerState = 0;
|
||||
}
|
||||
eventConnectChange = 1;
|
||||
}
|
||||
|
||||
// Clear node power state
|
||||
if (connectType == 1) { state.agentPower = 0; } else if (connectType == 2) { state.ciraPower = 0; } else if (connectType == 4) { state.amtPower = 0; }
|
||||
var powerState = 0, oldPowerState = state.powerState;
|
||||
if ((state.connectivity & 1) != 0) { powerState = state.agentPower; } else if ((state.connectivity & 2) != 0) { powerState = state.ciraPower; } else if ((state.connectivity & 4) != 0) { powerState = state.amtPower; }
|
||||
if ((state.powerState == undefined) || (state.powerState != powerState)) {
|
||||
state.powerState = powerState;
|
||||
eventConnectChange = 1;
|
||||
|
||||
// Set new power state in database
|
||||
obj.db.file.insert({ type: 'power', time: Date.now(), node: nodeid, power: powerState, oldPower: oldPowerState });
|
||||
}
|
||||
|
||||
// Event the node connection change
|
||||
if (eventConnectChange == 1) { obj.DispatchEvent(['*', meshid], obj, { action: 'nodeconnect', meshid: meshid, nodeid: nodeid, conn: state.connectivity, pwr: state.powerState, nolog: 1 }); }
|
||||
}
|
||||
|
||||
// Update the default mesh core
|
||||
obj.updateMeshCore = function (func) {
|
||||
var altCorePath = obj.path.join(obj.datapath, 'meshcore.js');
|
||||
if (require('fs').existsSync(altCorePath)) {
|
||||
// Load default mesh agent core from data path if present
|
||||
readEntireTextFile(altCorePath, function (data) {
|
||||
if (data != null) {
|
||||
data = obj.common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw)
|
||||
obj.defaultMeshCore = data;
|
||||
obj.defaultMeshCoreHash = obj.crypto.createHash('sha256').update(data).digest("binary");
|
||||
} else {
|
||||
obj.parent.defaultMeshCore = null;
|
||||
obj.parent.defaultMeshCoreHash = null;
|
||||
}
|
||||
if (func != undefined) { func(); }
|
||||
});
|
||||
} else {
|
||||
// Load default mesh agent core from meshcentral path if present
|
||||
readEntireTextFile(obj.path.join(__dirname, 'agents', 'meshcore.js'), function (data) {
|
||||
if (data != null) {
|
||||
data = obj.common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw)
|
||||
obj.defaultMeshCore = data;
|
||||
obj.defaultMeshCoreHash = obj.crypto.createHash('sha256').update(data).digest("binary");
|
||||
} else {
|
||||
obj.parent.defaultMeshCore = null;
|
||||
obj.parent.defaultMeshCoreHash = null;
|
||||
}
|
||||
if (func != undefined) { func(); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// List of possible mesh agent install scripts
|
||||
var meshAgentsInstallScriptList = {
|
||||
1: { id: 1, localname: 'meshinstall-linux.sh', rname: 'meshinstall.sh' }
|
||||
};
|
||||
|
||||
// Update the list of available mesh agents
|
||||
obj.updateMeshAgentInstallScripts = function () {
|
||||
for (var scriptid in meshAgentsInstallScriptList) {
|
||||
var scriptpath = obj.path.join(__dirname, 'agents', meshAgentsInstallScriptList[scriptid].localname);
|
||||
var stream = null;
|
||||
try {
|
||||
stream = obj.fs.createReadStream(scriptpath);
|
||||
stream.on('data', function (data) { this.hash.update(data, 'binary') });
|
||||
stream.on('error', function (data) {
|
||||
// If there is an error reading this file, make sure this agent is not in the agent table
|
||||
if (obj.meshAgentInstallScripts[this.info.id] != undefined) { delete obj.meshAgentInstallScripts[this.info.id]; }
|
||||
});
|
||||
stream.on('end', function () {
|
||||
// Add the agent to the agent table with all information and the hash
|
||||
obj.meshAgentInstallScripts[this.info.id] = obj.common.Clone(this.info);
|
||||
obj.meshAgentInstallScripts[this.info.id].hash = this.hash.digest('hex');
|
||||
obj.meshAgentInstallScripts[this.info.id].path = this.agentpath;
|
||||
obj.meshAgentInstallScripts[this.info.id].url = ((obj.args.notls == true) ? 'http://' : 'https://') + obj.certificates.CommonName + ':' + obj.args.port + '/meshagents?script=' + this.info.id;
|
||||
var stats = null;
|
||||
try { stats = obj.fs.statSync(this.agentpath) } catch (e) { }
|
||||
if (stats != null) { obj.meshAgentInstallScripts[this.info.id].size = stats.size; }
|
||||
});
|
||||
stream.info = meshAgentsInstallScriptList[scriptid];
|
||||
stream.agentpath = scriptpath;
|
||||
stream.hash = obj.crypto.createHash('sha256', stream);
|
||||
} catch (e) { }
|
||||
}
|
||||
}
|
||||
|
||||
// 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_pi2', rname: 'meshagent', desc: 'Linux ARMv7 - Raspberry Pi 2/3', update: true } // armv7l
|
||||
};
|
||||
|
||||
// Update the list of available mesh agents
|
||||
obj.updateMeshAgentsTable = function () {
|
||||
for (var archid in meshAgentsArchitectureNumbers) {
|
||||
var agentpath = obj.path.join(__dirname, 'agents', meshAgentsArchitectureNumbers[archid].localname);
|
||||
var stream = null;
|
||||
try {
|
||||
stream = obj.fs.createReadStream(agentpath);
|
||||
stream.on('data', function (data) { this.hash.update(data, 'binary') });
|
||||
stream.on('error', function (data) {
|
||||
// If there is an error reading this file, make sure this agent is not in the agent table
|
||||
if (obj.meshAgentBinaries[this.info.id] != undefined) { delete obj.meshAgentBinaries[this.info.id]; }
|
||||
});
|
||||
stream.on('end', function () {
|
||||
// Add the agent to the agent table with all information and the hash
|
||||
obj.meshAgentBinaries[this.info.id] = obj.common.Clone(this.info);
|
||||
obj.meshAgentBinaries[this.info.id].hash = this.hash.digest('hex');
|
||||
obj.meshAgentBinaries[this.info.id].path = this.agentpath;
|
||||
obj.meshAgentBinaries[this.info.id].url = ((obj.args.notls == true) ? 'http://' : 'https://') + obj.certificates.CommonName + ':' + obj.args.port + '/meshagents?id=' + this.info.id;
|
||||
var stats = null;
|
||||
try { stats = obj.fs.statSync(this.agentpath) } catch (e) { }
|
||||
if (stats != null) { obj.meshAgentBinaries[this.info.id].size = stats.size; }
|
||||
});
|
||||
stream.info = meshAgentsArchitectureNumbers[archid];
|
||||
stream.agentpath = agentpath;
|
||||
stream.hash = obj.crypto.createHash('sha256', stream);
|
||||
} catch (e) { }
|
||||
}
|
||||
}
|
||||
|
||||
// Debug
|
||||
obj.debug = function (lvl) {
|
||||
if (lvl > obj.debugLevel) return;
|
||||
if (arguments.length == 2) { console.log(arguments[1]); }
|
||||
else if (arguments.length == 3) { console.log(arguments[1], arguments[2]); }
|
||||
else if (arguments.length == 4) { console.log(arguments[1], arguments[2], arguments[3]); }
|
||||
else if (arguments.length == 5) { console.log(arguments[1], arguments[2], arguments[3], arguments[4]); }
|
||||
}
|
||||
|
||||
// Logging funtions
|
||||
function logException(e) { e += ''; logErrorEvent(e); }
|
||||
function logInfoEvent(msg) { if (obj.servicelog != null) { obj.servicelog.info(msg); } console.log(msg); }
|
||||
function logWarnEvent(msg) { if (obj.servicelog != null) { obj.servicelog.warn(msg); } console.log(msg); }
|
||||
function logErrorEvent(msg) { if (obj.servicelog != null) { obj.servicelog.error(msg); } console.error(msg); }
|
||||
|
||||
// Read entire file and return it in callback function
|
||||
function readEntireTextFile(filepath, func) {
|
||||
var called = false;
|
||||
try {
|
||||
obj.fs.open(filepath, 'r', function (err, fd) {
|
||||
obj.fs.fstat(fd, function (err, stats) {
|
||||
var bufferSize = stats.size, chunkSize = 512, buffer = new Buffer(bufferSize), bytesRead = 0;
|
||||
while (bytesRead < bufferSize) {
|
||||
if ((bytesRead + chunkSize) > bufferSize) { chunkSize = (bufferSize - bytesRead); }
|
||||
obj.fs.readSync(fd, buffer, bytesRead, chunkSize, bytesRead);
|
||||
bytesRead += chunkSize;
|
||||
}
|
||||
obj.fs.close(fd);
|
||||
called = true;
|
||||
func(buffer.toString('utf8', 0, bufferSize));
|
||||
});
|
||||
});
|
||||
} catch (e) { console.log(e); if (called == false) { func(null); } }
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function InstallModules(modules, func) {
|
||||
if (modules.length > 0) { InstallModule(modules.shift(), InstallModules, modules, func); } else { func(); }
|
||||
}
|
||||
|
||||
function InstallModule(modulename, func, tag1, tag2) {
|
||||
try {
|
||||
var module = require(modulename);
|
||||
delete module;
|
||||
} catch (e) {
|
||||
console.log('Installing ' + modulename + '...');
|
||||
var child_process = require('child_process');
|
||||
child_process.exec('npm install ' + modulename + ' --save', function (error, stdout, stderr) {
|
||||
if (error != null) { console.log('ERROR: Unable to install missing package \'' + modulename + '\', make sure npm is installed.'); process.exit(); return; }
|
||||
func(tag1, tag2);
|
||||
return;
|
||||
});
|
||||
return;
|
||||
}
|
||||
func(tag1, tag2);
|
||||
}
|
||||
|
||||
// Detect CTRL-C on Linux and stop nicely
|
||||
process.on('SIGINT', function () { if (meshserver != null) { meshserver.Stop(); meshserver = null; } });
|
||||
|
||||
// Build the list of required modules
|
||||
var modules = ['nedb', 'https', 'unzip', 'xmldom', 'express', 'archiver', 'mongojs', 'minimist', '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.
|
||||
var meshserver = null;
|
||||
InstallModules(modules, function () { meshserver = CreateMeshCentralServer(); meshserver.Start(); });
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* @description Meshcentral MeshRelay
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
// Construct a MeshRelay object, called upon connection
|
||||
module.exports.CreateMeshRelayKey = function (parent, func) {
|
||||
parent.crypto.randomBytes(16, function (err, buf) {
|
||||
var key = buf.toString('hex').toUpperCase() + ':' + Date.now();
|
||||
key += ':' + parent.crypto.createHmac('SHA256', parent.relayRandom).update(key).digest('hex');
|
||||
func(key);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.CreateMeshRelay = function (parent, ws, req) {
|
||||
var obj = {};
|
||||
obj.ws = ws;
|
||||
obj.peer = null;
|
||||
obj.id = req.query['id'];
|
||||
|
||||
//console.log('Got relay connection for: ' + obj.id);
|
||||
if (obj.id == undefined) { obj.ws.close(); obj.id = null; return null; } // Attempt to connect without id, drop this.
|
||||
|
||||
// Validate that the id is valid, we only need to do this on non-authenticated sessions.
|
||||
// TODO: Figure out when this needs to be done.
|
||||
/*
|
||||
if (!parent.args.notls) {
|
||||
// Check the identifier, if running without TLS, skip this.
|
||||
var ids = obj.id.split(':');
|
||||
if (ids.length != 3) { obj.ws.close(); obj.id = null; return null; } // Invalid ID, drop this.
|
||||
if (parent.crypto.createHmac('SHA256', parent.relayRandom).update(ids[0] + ':' + ids[1]).digest('hex') != ids[2]) { obj.ws.close(); obj.id = null; return null; } // Invalid HMAC, drop this.
|
||||
if ((Date.now() - parseInt(ids[1])) > 120000) { obj.ws.close(); obj.id = null; return null; } // Expired time, drop this.
|
||||
obj.id = ids[0];
|
||||
}
|
||||
*/
|
||||
|
||||
// Check the peer connection status
|
||||
{
|
||||
var relayinfo = parent.wsrelays[obj.id];
|
||||
if (relayinfo) {
|
||||
if (relayinfo.state == 1) {
|
||||
// Connect to peer
|
||||
obj.peer = relayinfo.peer1;
|
||||
obj.peer.peer = obj;
|
||||
relayinfo.peer2 = obj;
|
||||
relayinfo.state = 2;
|
||||
obj.ws.send('c'); // Send connect to both peers
|
||||
relayinfo.peer1.ws.send('c');
|
||||
|
||||
relayinfo.peer1.ws.peer = relayinfo.peer2.ws;
|
||||
relayinfo.peer2.ws.peer = relayinfo.peer1.ws;
|
||||
|
||||
} else {
|
||||
// Connected already, drop (TODO: maybe we should re-connect?)
|
||||
obj.id = null;
|
||||
obj.ws.close();
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// Setup the connection, wait for peer
|
||||
parent.wsrelays[obj.id] = { peer1 : obj, state : 1 };
|
||||
}
|
||||
}
|
||||
|
||||
ws.flushSink = function () {
|
||||
try { ws.resume(); } catch (e) { }
|
||||
};
|
||||
|
||||
// When data is received from the mesh relay web socket
|
||||
ws.on('message', function (data)
|
||||
{
|
||||
if (this.peer != null) { try { this.pause(); this.peer.send(data, ws.flushSink); } catch (e) { } }
|
||||
});
|
||||
|
||||
// If error, do nothing
|
||||
ws.on('error', function (err) { console.log(err); });
|
||||
|
||||
// If the mesh relay web socket is closed
|
||||
ws.on('close', function (req) {
|
||||
//console.log('Got relay disconnection for: ' + obj.id);
|
||||
if (obj.id != null) {
|
||||
var relayinfo = parent.wsrelays[obj.id];
|
||||
if (relayinfo.state == 2) {
|
||||
// Disconnect the peer
|
||||
var peer = (relayinfo.peer1 == obj)?relayinfo.peer2:relayinfo.peer1;
|
||||
peer.id = null;
|
||||
peer.ws._socket.end();
|
||||
}
|
||||
delete parent.wsrelays[obj.id];
|
||||
obj.peer = null;
|
||||
obj.id = null;
|
||||
}
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
* @description Meshcentral Mesh Agent Local Scanner
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
// Construct a Mesh Scanner object
|
||||
// TODO: We need once "server4" and "server6" per interface, or change the default multicast interface as we send.
|
||||
module.exports.CreateMeshScanner = function (parent) {
|
||||
var obj = {};
|
||||
obj.parent = parent;
|
||||
obj.dgram = require('dgram');
|
||||
obj.common = require('./common.js');
|
||||
obj.server4 = null;
|
||||
obj.server6 = null;
|
||||
obj.mainTimer = null;
|
||||
var periodicScanTime = (60000 * 20); // Interval between scans, 20 minutes.
|
||||
var membershipIPv4 = '239.255.255.235';
|
||||
var membershipIPv6 = 'FF02:0:0:0:0:0:0:FE';
|
||||
obj.agentCertificatHashHex = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha256.create(), encoding: 'hex' });
|
||||
obj.error = 0;
|
||||
|
||||
// Start scanning for local network Mesh Agents
|
||||
obj.start = function () {
|
||||
if (obj.server4 != null) return;
|
||||
var url = (parent.args.notls ? 'ws' : 'wss') + '://%s:' + parent.args.port + '/agent.ashx';
|
||||
obj.multicastPacket4 = Buffer.from("MeshCentral2|" + obj.agentCertificatHashHex.toUpperCase() + '|' + url, 'ascii');
|
||||
url = (parent.args.notls ? 'ws' : 'wss') + '://[%s]:' + parent.args.port + '/agent.ashx';
|
||||
obj.multicastPacket6 = Buffer.from("MeshCentral2|" + obj.agentCertificatHashHex.toUpperCase() + '|' + url, 'ascii');
|
||||
obj.server4 = obj.dgram.createSocket("udp4");
|
||||
obj.server4.on('error', function(err) { if (obj.error++ == 0) { console.log("ERROR: Server port 16989 not available, check if server is running twice."); } obj.server4.close(); obj.server4 = null; });
|
||||
obj.server4.bind(16989, function () {
|
||||
obj.server4.setBroadcast(true)
|
||||
obj.server4.setMulticastTTL(128);
|
||||
obj.server4.addMembership(membershipIPv4);
|
||||
obj.server4.on('error', function (error) { console.log('Error: ' + error); });
|
||||
obj.server4.on('message', onUdpPacket); // TODO!!! We can't use this server for receive, instead we have to bind a seperate UDP server for each of the network interfaces.
|
||||
obj.performScan(4);
|
||||
obj.performScan(4);
|
||||
});
|
||||
obj.server6 = obj.dgram.createSocket("udp6");
|
||||
obj.server6.on('error', function(err) { obj.server6.close(); obj.server6 = null; }); // IPv6 may not be supported.
|
||||
obj.server6.bind(16989, function () {
|
||||
obj.server6.setBroadcast(true)
|
||||
obj.server6.setMulticastTTL(128);
|
||||
obj.server6.addMembership(membershipIPv6);
|
||||
obj.server6.on('error', function (error) { console.log('Error: ' + error); });
|
||||
obj.server6.on('message', onUdpPacket); // TODO!!! We can't use this server for receive, instead we have to bind a seperate UDP server for each of the network interfaces.
|
||||
obj.performScan(6);
|
||||
obj.performScan(6);
|
||||
});
|
||||
obj.mainTimer = setInterval(obj.performScan, periodicScanTime);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Stop scanning for local network Mesh Agents
|
||||
obj.stop = function () {
|
||||
if (obj.mainTimer != null) { clearInterval(obj.mainTimer); obj.mainTimer = null; }
|
||||
if (obj.server4 != null) { obj.server4.close(); obj.server4 = null; }
|
||||
if (obj.server6 != null) { obj.server6.close(); obj.server6 = null; }
|
||||
}
|
||||
|
||||
// Look for all Mesh Agents that may be locally reachable, indicating the presense of this server.
|
||||
obj.performScan = function (mode) {
|
||||
if ((mode != 6) && (obj.server4 != null)) { obj.server4.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); }
|
||||
if ((mode != 4) && (obj.server6 != null)) { obj.server6.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); }
|
||||
}
|
||||
|
||||
// Called when a UDP packet is received from an agent.
|
||||
function onUdpPacket(msg, info) {
|
||||
//console.log('Received ' + msg.length + ' bytes from ' + info.address + ':' + info.port + '\n');
|
||||
if ((msg.length == 64) && (msg.toString('ascii') == obj.agentCertificatHashHex.toUpperCase())) {
|
||||
if (info.family == 'IPv4') { obj.server4.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, info.port, info.address); }
|
||||
if (info.family == 'IPv6') { obj.server6.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, info.port, info.address); }
|
||||
}
|
||||
}
|
||||
|
||||
// As a side job, we also send server wake-on-lan packets
|
||||
obj.wakeOnLan = function (macs) {
|
||||
for (var i in macs) {
|
||||
var mac = macs[i];
|
||||
var hexpacket = 'FFFFFFFFFFFF';
|
||||
for (var i = 0; i < 16; i++) { hexpacket += mac; }
|
||||
var wakepacket = Buffer.from(hexpacket, 'hex');
|
||||
//console.log(wakepacket.toString('hex'));
|
||||
|
||||
// Send the wake packet 3 times with small time intervals
|
||||
if (obj.server4) { obj.server4.send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.server4.send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); }
|
||||
if (obj.server6) { obj.server6.send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
|
||||
setTimeout(function () {
|
||||
if (obj.server4) { obj.server4.send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.server4.send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); }
|
||||
if (obj.server6) { obj.server6.send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
|
||||
}, 200);
|
||||
setTimeout(function () {
|
||||
if (obj.server4) { obj.server4.send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.server4.send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); }
|
||||
if (obj.server6) { obj.server6.send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); }
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,659 @@
|
|||
/**
|
||||
* @description Meshcentral Intel AMT MPS server
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
// Construct a Intel AMT MPS server object
|
||||
module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
var obj = {};
|
||||
obj.parent = parent;
|
||||
obj.db = db;
|
||||
obj.certificates = certificates;
|
||||
obj.ciraConnections = {};
|
||||
var common = require('./common.js');
|
||||
var net = require('net');
|
||||
var tls = require('tls');
|
||||
|
||||
obj.server = tls.createServer({ key: certificates.mps.key, cert: certificates.mps.cert, requestCert: true }, onConnection);
|
||||
|
||||
obj.server.listen(args.mpsport, function () { console.log('MeshCentral Intel(R) AMT server running on ' + certificates.CommonName + ':' + args.mpsport + '.'); }).on('error', function (err) { console.error('ERROR: MeshCentral Intel(R) AMT server port ' + args.mpsport + ' is not available.'); if (args.exactports) { process.exit(); } });
|
||||
|
||||
var APFProtocol = {
|
||||
UNKNOWN: 0,
|
||||
DISCONNECT: 1,
|
||||
SERVICE_REQUEST: 5,
|
||||
SERVICE_ACCEPT: 6,
|
||||
USERAUTH_REQUEST: 50,
|
||||
USERAUTH_FAILURE: 51,
|
||||
USERAUTH_SUCCESS: 52,
|
||||
GLOBAL_REQUEST: 80,
|
||||
REQUEST_SUCCESS: 81,
|
||||
REQUEST_FAILURE: 82,
|
||||
CHANNEL_OPEN: 90,
|
||||
CHANNEL_OPEN_CONFIRMATION: 91,
|
||||
CHANNEL_OPEN_FAILURE: 92,
|
||||
CHANNEL_WINDOW_ADJUST: 93,
|
||||
CHANNEL_DATA: 94,
|
||||
CHANNEL_CLOSE: 97,
|
||||
PROTOCOLVERSION: 192,
|
||||
KEEPALIVE_REQUEST: 208,
|
||||
KEEPALIVE_REPLY: 209,
|
||||
KEEPALIVE_OPTIONS_REQUEST: 210,
|
||||
KEEPALIVE_OPTIONS_REPLY: 211
|
||||
}
|
||||
|
||||
var APFDisconnectCode = {
|
||||
HOST_NOT_ALLOWED_TO_CONNECT: 1,
|
||||
PROTOCOL_ERROR: 2,
|
||||
KEY_EXCHANGE_FAILED: 3,
|
||||
RESERVED: 4,
|
||||
MAC_ERROR: 5,
|
||||
COMPRESSION_ERROR: 6,
|
||||
SERVICE_NOT_AVAILABLE: 7,
|
||||
PROTOCOL_VERSION_NOT_SUPPORTED: 8,
|
||||
HOST_KEY_NOT_VERIFIABLE: 9,
|
||||
CONNECTION_LOST: 10,
|
||||
BY_APPLICATION: 11,
|
||||
TOO_MANY_CONNECTIONS: 12,
|
||||
AUTH_CANCELLED_BY_USER: 13,
|
||||
NO_MORE_AUTH_METHODS_AVAILABLE: 14,
|
||||
INVALID_CREDENTIALS: 15,
|
||||
CONNECTION_TIMED_OUT: 16,
|
||||
BY_POLICY: 17,
|
||||
TEMPORARILY_UNAVAILABLE: 18
|
||||
}
|
||||
|
||||
var APFChannelOpenFailCodes = {
|
||||
ADMINISTRATIVELY_PROHIBITED: 1,
|
||||
CONNECT_FAILED: 2,
|
||||
UNKNOWN_CHANNEL_TYPE: 3,
|
||||
RESOURCE_SHORTAGE: 4,
|
||||
}
|
||||
|
||||
var APFChannelOpenFailureReasonCode = {
|
||||
AdministrativelyProhibited: 1,
|
||||
ConnectFailed: 2,
|
||||
UnknownChannelType: 3,
|
||||
ResourceShortage: 4,
|
||||
}
|
||||
|
||||
function onConnection(socket) {
|
||||
socket.tag = { first: true, clientCert: socket.getPeerCertificate(true), accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
|
||||
socket.setEncoding('binary');
|
||||
Debug(1, 'MPS:New CIRA connection');
|
||||
|
||||
socket.addListener("data", function (data) {
|
||||
if (args.mpsdebug) { var buf = new Buffer(data, "binary"); console.log('MPS <-- (' + buf.length + '):' + buf.toString('hex')); } // Print out received bytes
|
||||
socket.tag.accumulator += data;
|
||||
|
||||
// Detect if this is an HTTPS request, if it is, return a simple answer and disconnect. This is useful for debugging access to the MPS port.
|
||||
if (socket.tag.first == true) {
|
||||
if (socket.tag.accumulator.length < 3) return;
|
||||
//if (!socket.tag.clientCert.subject) { console.log("MPS 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 MPS server.\r\nNo client certificate given.'); socket.end(); return; }
|
||||
if (socket.tag.accumulator.substring(0, 3) == 'GET') { console.log("MPS Connection, HTTP GET detected: " + socket.remoteAddress); socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nMeshCentral2 MPS server.\r\nIntel(R) AMT computers should connect here.'); socket.end(); return; }
|
||||
socket.tag.first = false;
|
||||
|
||||
// Setup this node with certificate authentication
|
||||
if (socket.tag.clientCert && socket.tag.clientCert.subject && socket.tag.clientCert.subject.O && socket.tag.clientCert.subject.O.length == 64) {
|
||||
// This is a node where the MeshID is indicated within the CIRA certificate
|
||||
var domainid = '', meshid;
|
||||
var xx = socket.tag.clientCert.subject.O.split('/');
|
||||
if (xx.length == 1) { meshid = xx[0].toUpperCase(); } else { domainid = xx[0].toLowerCase(); meshid = xx[1].toUpperCase(); }
|
||||
|
||||
socket.tag.domainid = domainid;
|
||||
socket.tag.meshid = 'mesh/' + domainid + '/' + meshid;
|
||||
socket.tag.nodeid = 'node/' + domainid + '/' + require('crypto').createHash('sha256').update(common.hex2rstr(socket.tag.clientCert.modulus, 'binary')).digest('hex').toUpperCase();
|
||||
socket.tag.name = socket.tag.clientCert.subject.CN;
|
||||
socket.tag.connectTime = Date.now();
|
||||
socket.tag.host = '';
|
||||
|
||||
// Fetch the mesh
|
||||
obj.db.Get(socket.tag.meshid, function (err, meshes) {
|
||||
if (meshes.length == 1) {
|
||||
var mesh = meshes[0];
|
||||
obj.db.Get(socket.tag.nodeid, function (err, nodes) {
|
||||
if (nodes.length == 0) {
|
||||
if (mesh.mtype == 1) {
|
||||
// Node is not in the database, add it. Credentials will be empty until added by the user.
|
||||
var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: domainid, intelamt: { user: '', pass: '', tls: 0 } };
|
||||
obj.db.Set(device);
|
||||
|
||||
// Event the new node
|
||||
var device2 = common.Clone(device);
|
||||
if (device2.intelamt.pass != undefined) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
|
||||
var change = 'CIRA added device ' + socket.tag.name + ' to mesh ' + mesh.name;
|
||||
obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: device2, msg: change, domain: domainid })
|
||||
} else {
|
||||
// New CIRA connection for unknown node, disconnect.
|
||||
console.log('CIRA connection for unknown node with incorrect mesh type. meshid: ' + socket.tag.meshid);
|
||||
socket.end();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Node is already present
|
||||
var node = nodes[0];
|
||||
if (node.intelamt != undefined) { socket.tag.host = node.intelamt.host; }
|
||||
}
|
||||
|
||||
// Add the connection to the MPS connection list
|
||||
obj.ciraConnections[socket.tag.nodeid] = socket;
|
||||
obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 2, 7); // TODO: Right now report power state as "present" (7) until we can poll.
|
||||
});
|
||||
} else {
|
||||
console.log('ERROR: Intel AMT CIRA connected with unknown meshid: ' + socket.tag.meshid);
|
||||
socket.end();
|
||||
return;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// This node connected without certificate authentication, use password auth
|
||||
//console.log('Intel AMT CIRA connected without certificate authentication');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Parse all of the APF data we can
|
||||
var l = 0;
|
||||
do { l = ProcessCommand(socket); if (l > 0) { socket.tag.accumulator = socket.tag.accumulator.substring(l); } } while (l > 0);
|
||||
if (l < 0) { socket.end(); }
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
|
||||
// Process one AFP command
|
||||
function ProcessCommand(socket) {
|
||||
var cmd = socket.tag.accumulator.charCodeAt(0);
|
||||
var len = socket.tag.accumulator.length;
|
||||
var data = socket.tag.accumulator;
|
||||
if (len == 0) { return 0; }
|
||||
|
||||
switch (cmd) {
|
||||
case APFProtocol.KEEPALIVE_REQUEST: {
|
||||
if (len < 5) return 0;
|
||||
Debug(3, 'MPS:KEEPALIVE_REQUEST');
|
||||
SendKeepAliveReply(socket, common.ReadInt(data, 1));
|
||||
return 5;
|
||||
}
|
||||
case APFProtocol.KEEPALIVE_REPLY: {
|
||||
if (len < 5) return 0;
|
||||
Debug(3, 'MPS:KEEPALIVE_REPLY');
|
||||
return 5;
|
||||
}
|
||||
case APFProtocol.PROTOCOLVERSION: {
|
||||
if (len < 93) return 0;
|
||||
socket.tag.MajorVersion = common.ReadInt(data, 1);
|
||||
socket.tag.MinorVersion = common.ReadInt(data, 5);
|
||||
socket.tag.SystemId = guidToStr(common.rstr2hex(data.substring(13, 29))).toLowerCase();
|
||||
Debug(3, 'MPS:PROTOCOLVERSION', socket.tag.MajorVersion, socket.tag.MinorVersion, socket.tag.SystemId);
|
||||
return 93;
|
||||
}
|
||||
case APFProtocol.USERAUTH_REQUEST: {
|
||||
if (len < 13) return 0;
|
||||
var usernameLen = common.ReadInt(data, 1);
|
||||
var username = data.substring(5, 5 + usernameLen);
|
||||
var serviceNameLen = common.ReadInt(data, 5 + usernameLen);
|
||||
var serviceName = data.substring(9 + usernameLen, 9 + usernameLen + serviceNameLen);
|
||||
var methodNameLen = common.ReadInt(data, 9 + usernameLen + serviceNameLen);
|
||||
var methodName = data.substring(13 + usernameLen + serviceNameLen, 13 + usernameLen + serviceNameLen + methodNameLen);
|
||||
var passwordLen = 0, password = null;
|
||||
if (methodName == 'password') {
|
||||
passwordLen = common.ReadInt(data, 14 + usernameLen + serviceNameLen + methodNameLen);
|
||||
password = data.substring(18 + usernameLen + serviceNameLen + methodNameLen, 18 + usernameLen + serviceNameLen + methodNameLen + passwordLen);
|
||||
}
|
||||
Debug(3, 'MPS:USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password);
|
||||
|
||||
// Check the CIRA password
|
||||
if ((args.mpspass != undefined) && (password != args.mpspass)) { Debug(1, 'MPS:Incorrect password', username, password); SendUserAuthFail(socket); return -1; }
|
||||
|
||||
// Check the CIRA username, which should be the HEX start of the MeshID.
|
||||
if (usernameLen != 16) { SendUserAuthFail(socket); return -1; }
|
||||
var meshIdStart = '/' + username;
|
||||
meshIdStart = meshIdStart.toUpperCase();
|
||||
obj.db.GetAllType('mesh', function (err, docs) {
|
||||
var mesh = null;
|
||||
for (var i in docs) { if (docs[i]._id.indexOf(meshIdStart) > 0) { mesh = docs[i]; break; } }
|
||||
if (mesh == null) { SendUserAuthFail(socket); return -1; }
|
||||
|
||||
// Intel AMT GUID (socket.tag.SystemId) will be used at NodeID
|
||||
var systemid = socket.tag.SystemId.split('-').join('').toUpperCase();
|
||||
socket.tag.name = '';
|
||||
socket.tag.nodeid = 'node/' + mesh.domain + '/' + systemid + systemid;
|
||||
socket.tag.meshid = mesh._id;
|
||||
|
||||
obj.db.Get(socket.tag.nodeid, function (err, nodes) {
|
||||
if (nodes.length == 0) {
|
||||
if (mesh.mtype == 1) {
|
||||
// Node is not in the database, add it. Credentials will be empty until added by the user.
|
||||
var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: mesh.domain, intelamt: { user: '', pass: '', tls: 0 } };
|
||||
obj.db.Set(device);
|
||||
|
||||
// Event the new node
|
||||
var device2 = common.Clone(device);
|
||||
if (device2.intelamt.pass != undefined) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
|
||||
var change = 'CIRA added device ' + socket.tag.name + ' to mesh ' + mesh.name;
|
||||
obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: device2, msg: change, domain: mesh.domain })
|
||||
} else {
|
||||
// New CIRA connection for unknown node, disconnect.
|
||||
console.log('CIRA connection for unknown node with incorrect mesh type. meshid: ' + socket.tag.meshid);
|
||||
socket.end();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Node is already present
|
||||
var node = nodes[0];
|
||||
if (node.intelamt != undefined) { socket.tag.host = node.intelamt.host; }
|
||||
}
|
||||
|
||||
// Add the connection to the MPS connection list
|
||||
obj.ciraConnections[socket.tag.nodeid] = socket;
|
||||
obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 2, 7); // TODO: Right now report power state as "present" (7) until we can poll.
|
||||
SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection
|
||||
});
|
||||
});
|
||||
return 18 + usernameLen + serviceNameLen + methodNameLen + passwordLen;
|
||||
}
|
||||
case APFProtocol.SERVICE_REQUEST: {
|
||||
if (len < 5) return 0;
|
||||
var serviceNameLen = common.ReadInt(data, 1);
|
||||
if (len < 5 + serviceNameLen) return 0;
|
||||
var serviceName = data.substring(5, 5 + serviceNameLen);
|
||||
Debug(3, 'MPS:SERVICE_REQUEST', serviceName);
|
||||
if (serviceName == "pfwd@amt.intel.com") { SendServiceAccept(socket, "pfwd@amt.intel.com"); }
|
||||
if (serviceName == "auth@amt.intel.com") { SendServiceAccept(socket, "auth@amt.intel.com"); }
|
||||
return 5 + serviceNameLen;
|
||||
}
|
||||
case APFProtocol.GLOBAL_REQUEST: {
|
||||
if (len < 14) return 0;
|
||||
var requestLen = common.ReadInt(data, 1);
|
||||
if (len < 14 + requestLen) return 0;
|
||||
var request = data.substring(5, 5 + requestLen);
|
||||
var wantResponse = data.charCodeAt(5 + requestLen);
|
||||
|
||||
if (request == "tcpip-forward") {
|
||||
var addrLen = common.ReadInt(data, 6 + requestLen);
|
||||
if (len < 14 + requestLen + addrLen) return 0;
|
||||
var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen);
|
||||
var port = common.ReadInt(data, 10 + requestLen + addrLen);
|
||||
if (addr == '') addr = undefined;
|
||||
Debug(2, 'MPS:GLOBAL_REQUEST', request, addr + ':' + port);
|
||||
ChangeHostname(socket, addr);
|
||||
if (socket.tag.boundPorts.indexOf(port) == -1) { socket.tag.boundPorts.push(port); }
|
||||
SendTcpForwardSuccessReply(socket, port);
|
||||
return 14 + requestLen + addrLen;
|
||||
}
|
||||
|
||||
if (request == "cancel-tcpip-forward") {
|
||||
var addrLen = common.ReadInt(data, 6 + requestLen);
|
||||
if (len < 14 + requestLen + addrLen) return 0;
|
||||
var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen);
|
||||
var port = common.ReadInt(data, 10 + requestLen + addrLen);
|
||||
Debug(2, 'MPS:GLOBAL_REQUEST', request, addr + ':' + port);
|
||||
var portindex = socket.tag.boundPorts.indexOf(port);
|
||||
if (portindex >= 0) { socket.tag.boundPorts.splice(portindex, 1); }
|
||||
SendTcpForwardCancelReply(socket);
|
||||
return 14 + requestLen + addrLen;
|
||||
}
|
||||
|
||||
if (request == "udp-send-to@amt.intel.com") {
|
||||
var addrLen = common.ReadInt(data, 6 + requestLen);
|
||||
if (len < 26 + requestLen + addrLen) return 0;
|
||||
var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen);
|
||||
var port = common.ReadInt(data, 10 + requestLen + addrLen);
|
||||
var oaddrLen = common.ReadInt(data, 14 + requestLen + addrLen);
|
||||
if (len < 26 + requestLen + addrLen + oaddrLen) return 0;
|
||||
var oaddr = data.substring(18 + requestLen, 18 + requestLen + addrLen);
|
||||
var oport = common.ReadInt(data, 18 + requestLen + addrLen + oaddrLen);
|
||||
var datalen = common.ReadInt(data, 22 + requestLen + addrLen + oaddrLen);
|
||||
if (len < 26 + requestLen + addrLen + oaddrLen + datalen) return 0;
|
||||
Debug(2, 'MPS:GLOBAL_REQUEST', request, addr + ':' + port, oaddr + ':' + oport, datalen);
|
||||
// TODO
|
||||
return ptr + 26 + requestLen + addrLen + oaddrLen + datalen;
|
||||
}
|
||||
|
||||
return 6 + requestLen;
|
||||
}
|
||||
case APFProtocol.CHANNEL_OPEN: {
|
||||
if (len < 33) return 0;
|
||||
var ChannelTypeLength = common.ReadInt(data, 1);
|
||||
if (len < (33 + ChannelTypeLength)) return 0;
|
||||
|
||||
// Decode channel identifiers and window size
|
||||
var ChannelType = data.substring(5, 5 + ChannelTypeLength);
|
||||
var SenderChannel = common.ReadInt(data, 5 + ChannelTypeLength);
|
||||
var WindowSize = common.ReadInt(data, 9 + ChannelTypeLength);
|
||||
|
||||
// Decode the target
|
||||
var TargetLen = common.ReadInt(data, 17 + ChannelTypeLength);
|
||||
if (len < (33 + ChannelTypeLength + TargetLen)) return 0;
|
||||
var Target = data.substring(21 + ChannelTypeLength, 21 + ChannelTypeLength + TargetLen);
|
||||
var TargetPort = common.ReadInt(data, 21 + ChannelTypeLength + TargetLen);
|
||||
|
||||
// Decode the source
|
||||
var SourceLen = common.ReadInt(data, 25 + ChannelTypeLength + TargetLen);
|
||||
if (len < (33 + ChannelTypeLength + TargetLen + SourceLen)) return 0;
|
||||
var Source = data.substring(29 + ChannelTypeLength + TargetLen, 29 + ChannelTypeLength + TargetLen + SourceLen);
|
||||
var SourcePort = common.ReadInt(data, 29 + ChannelTypeLength + TargetLen + SourceLen);
|
||||
|
||||
Debug(3, 'MPS:CHANNEL_OPEN', ChannelType, SenderChannel, WindowSize, Target + ':' + TargetPort, Source + ':' + SourcePort);
|
||||
|
||||
// Check if we understand this channel type
|
||||
//if (ChannelType.toLowerCase() == "direct-tcpip")
|
||||
{
|
||||
// We don't understand this channel type, send an error back
|
||||
SendChannelOpenFailure(socket, SenderChannel, APFChannelOpenFailureReasonCode.UnknownChannelType);
|
||||
return 33 + ChannelTypeLength + TargetLen + SourceLen;
|
||||
}
|
||||
|
||||
/*
|
||||
// This is a correct connection. Lets get it setup
|
||||
var MeshAmtEventEndpoint = { ServerChannel: GetNextBindId(), AmtChannel: SenderChannel, MaxWindowSize: 2048, CurrentWindowSize:2048, SendWindow: WindowSize, InfoHeader: "Target: " + Target + ":" + TargetPort + ", Source: " + Source + ":" + SourcePort};
|
||||
// TODO: Connect this socket for a WSMAN event
|
||||
SendChannelOpenConfirmation(socket, SenderChannel, MeshAmtEventEndpoint.ServerChannel, MeshAmtEventEndpoint.MaxWindowSize);
|
||||
*/
|
||||
|
||||
return 33 + ChannelTypeLength + TargetLen + SourceLen;
|
||||
}
|
||||
case APFProtocol.CHANNEL_OPEN_CONFIRMATION:
|
||||
{
|
||||
if (len < 17) return 0;
|
||||
var RecipientChannel = common.ReadInt(data, 1);
|
||||
var SenderChannel = common.ReadInt(data, 5);
|
||||
var WindowSize = common.ReadInt(data, 9);
|
||||
socket.tag.activetunnels++;
|
||||
var cirachannel = socket.tag.channels[RecipientChannel];
|
||||
if (cirachannel == undefined) { console.log("MPS Error in CHANNEL_OPEN_CONFIRMATION: Unable to find channelid " + RecipientChannel); return; }
|
||||
cirachannel.amtchannelid = SenderChannel;
|
||||
cirachannel.sendcredits = cirachannel.amtCiraWindow = WindowSize;
|
||||
Debug(3, 'MPS:CHANNEL_OPEN_CONFIRMATION', RecipientChannel, SenderChannel, WindowSize);
|
||||
if (cirachannel.closing == 1) {
|
||||
// Close this channel
|
||||
SendChannelClose(cirachannel.socket, cirachannel.amtchannelid);
|
||||
} else {
|
||||
cirachannel.state = 2;
|
||||
// Send any pending data
|
||||
if (cirachannel.sendBuffer != undefined) {
|
||||
if (cirachannel.sendBuffer.length <= cirachannel.sendcredits) {
|
||||
// Send the entire pending buffer
|
||||
SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer);
|
||||
cirachannel.sendcredits -= cirachannel.sendBuffer.length;
|
||||
delete cirachannel.sendBuffer;
|
||||
if (cirachannel.onSendOk) { cirachannel.onSendOk(cirachannel); }
|
||||
} else {
|
||||
// Send a part of the pending buffer
|
||||
SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer.substring(0, cirachannel.sendcredits));
|
||||
cirachannel.sendBuffer = cirachannel.sendBuffer.substring(cirachannel.sendcredits);
|
||||
cirachannel.sendcredits = 0;
|
||||
}
|
||||
}
|
||||
// Indicate the channel is open
|
||||
if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); }
|
||||
}
|
||||
return 17;
|
||||
}
|
||||
case APFProtocol.CHANNEL_OPEN_FAILURE:
|
||||
{
|
||||
if (len < 17) return 0;
|
||||
var RecipientChannel = common.ReadInt(data, 1);
|
||||
var ReasonCode = common.ReadInt(data, 5);
|
||||
Debug(3, 'MPS:CHANNEL_OPEN_FAILURE', RecipientChannel, ReasonCode);
|
||||
var cirachannel = socket.tag.channels[RecipientChannel];
|
||||
if (cirachannel == undefined) { console.log("MPS Error in CHANNEL_OPEN_FAILURE: Unable to find channelid " + RecipientChannel); return; }
|
||||
if (cirachannel.state > 0) {
|
||||
cirachannel.state = 0;
|
||||
if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); }
|
||||
delete socket.tag.channels[RecipientChannel];
|
||||
}
|
||||
return 17;
|
||||
}
|
||||
case APFProtocol.CHANNEL_CLOSE:
|
||||
{
|
||||
if (len < 5) return 0;
|
||||
var RecipientChannel = common.ReadInt(data, 1);
|
||||
Debug(3, 'MPS:CHANNEL_CLOSE', RecipientChannel);
|
||||
var cirachannel = socket.tag.channels[RecipientChannel];
|
||||
if (cirachannel == undefined) { console.log("MPS Error in CHANNEL_CLOSE: Unable to find channelid " + RecipientChannel); return; }
|
||||
socket.tag.activetunnels--;
|
||||
if (cirachannel.state > 0) {
|
||||
cirachannel.state = 0;
|
||||
if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); }
|
||||
delete socket.tag.channels[RecipientChannel];
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
case APFProtocol.CHANNEL_WINDOW_ADJUST:
|
||||
{
|
||||
if (len < 9) return 0;
|
||||
var RecipientChannel = common.ReadInt(data, 1);
|
||||
var ByteToAdd = common.ReadInt(data, 5);
|
||||
Debug(3, 'MPS:CHANNEL_WINDOW_ADJUST', RecipientChannel, ByteToAdd);
|
||||
var cirachannel = socket.tag.channels[RecipientChannel];
|
||||
if (cirachannel == undefined) { console.log("MPS Error in CHANNEL_WINDOW_ADJUST: Unable to find channelid " + RecipientChannel); return; }
|
||||
cirachannel.sendcredits += ByteToAdd;
|
||||
if (cirachannel.state == 2 && cirachannel.sendBuffer != undefined) {
|
||||
// Compute how much data we can send
|
||||
if (cirachannel.sendBuffer.length <= cirachannel.sendcredits) {
|
||||
// Send the entire pending buffer
|
||||
SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer);
|
||||
cirachannel.sendcredits -= cirachannel.sendBuffer.length;
|
||||
delete cirachannel.sendBuffer;
|
||||
if (cirachannel.onSendOk) { cirachannel.onSendOk(cirachannel); }
|
||||
} else {
|
||||
// Send a part of the pending buffer
|
||||
SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer.substring(0, cirachannel.sendcredits));
|
||||
cirachannel.sendBuffer = cirachannel.sendBuffer.substring(cirachannel.sendcredits);
|
||||
cirachannel.sendcredits = 0;
|
||||
}
|
||||
}
|
||||
return 9;
|
||||
}
|
||||
case APFProtocol.CHANNEL_DATA:
|
||||
{
|
||||
if (len < 9) return 0;
|
||||
var RecipientChannel = common.ReadInt(data, 1);
|
||||
var LengthOfData = common.ReadInt(data, 5);
|
||||
if (len < (9 + LengthOfData)) return 0;
|
||||
Debug(3, 'MPS:CHANNEL_DATA', RecipientChannel, LengthOfData);
|
||||
var cirachannel = socket.tag.channels[RecipientChannel];
|
||||
if (cirachannel == undefined) { console.log("MPS Error in CHANNEL_DATA: Unable to find channelid " + RecipientChannel); return; }
|
||||
cirachannel.amtpendingcredits += LengthOfData;
|
||||
if (cirachannel.onData) cirachannel.onData(cirachannel, data.substring(9, 9 + LengthOfData));
|
||||
if (cirachannel.amtpendingcredits > (cirachannel.ciraWindow / 2)) {
|
||||
SendChannelWindowAdjust(cirachannel.socket, cirachannel.amtchannelid, cirachannel.amtpendingcredits); // Adjust the buffer window
|
||||
cirachannel.amtpendingcredits = 0;
|
||||
}
|
||||
return 9 + LengthOfData;
|
||||
}
|
||||
case APFProtocol.DISCONNECT:
|
||||
{
|
||||
if (len < 7) return 0;
|
||||
var ReasonCode = common.ReadInt(data, 1);
|
||||
Debug(3, 'MPS:DISCONNECT', ReasonCode);
|
||||
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
|
||||
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
|
||||
return 7;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Debug(1, 'MPS:Unknown CIRA command: ' + cmd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
socket.addListener("close", function () {
|
||||
Debug(1, 'MPS:CIRA connection closed');
|
||||
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
|
||||
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
|
||||
});
|
||||
|
||||
socket.addListener("error", function () {
|
||||
//console.log("MPS Error: " + socket.remoteAddress);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Disconnect CIRA tunnel
|
||||
obj.close = function (socket) {
|
||||
try { socket.close(); } catch (e) { }
|
||||
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
|
||||
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
|
||||
}
|
||||
|
||||
function SendServiceAccept(socket, service) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.SERVICE_ACCEPT) + common.IntToStr(service.length) + service);
|
||||
}
|
||||
|
||||
function SendTcpForwardSuccessReply(socket, port) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.REQUEST_SUCCESS) + common.IntToStr(port));
|
||||
}
|
||||
|
||||
function SendTcpForwardCancelReply(socket) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.REQUEST_SUCCESS));
|
||||
}
|
||||
|
||||
function SendKeepAliveRequest(socket, cookie) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.KEEPALIVE_REQUEST) + common.IntToStr(cookie));
|
||||
}
|
||||
|
||||
function SendKeepAliveReply(socket, cookie) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.KEEPALIVE_REPLY) + common.IntToStr(cookie));
|
||||
}
|
||||
|
||||
function SendChannelOpenFailure(socket, senderChannel, reasonCode) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.CHANNEL_OPEN_FAILURE) + common.IntToStr(senderChannel) + common.IntToStr(reasonCode) + common.IntToStr(0) + common.IntToStr(0));
|
||||
}
|
||||
|
||||
function SendChannelOpenConfirmation(socket, recipientChannelId, senderChannelId, initialWindowSize) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.CHANNEL_OPEN_CONFIRMATION) + common.IntToStr(recipientChannelId) + common.IntToStr(senderChannelId) + common.IntToStr(initialWindowSize) + common.IntToStr(-1));
|
||||
}
|
||||
|
||||
function SendChannelOpen(socket, direct, channelid, windowsize, target, targetport, source, sourceport) {
|
||||
var connectionType = ((direct == true) ? "direct-tcpip" : "forwarded-tcpip");
|
||||
if ((target == null) || (target == undefined)) target = ''; // TODO: Reports of target being undefined that causes target.length to fail. This is a hack.
|
||||
Write(socket, String.fromCharCode(APFProtocol.CHANNEL_OPEN) + common.IntToStr(connectionType.length) + connectionType + common.IntToStr(channelid) + common.IntToStr(windowsize) + common.IntToStr(-1) + common.IntToStr(target.length) + target + common.IntToStr(targetport) + common.IntToStr(source.length) + source + common.IntToStr(sourceport));
|
||||
}
|
||||
|
||||
function SendChannelClose(socket, channelid) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.CHANNEL_CLOSE) + common.IntToStr(channelid));
|
||||
}
|
||||
|
||||
function SendChannelData(socket, channelid, data) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.CHANNEL_DATA) + common.IntToStr(channelid) + common.IntToStr(data.length) + data);
|
||||
}
|
||||
|
||||
function SendChannelWindowAdjust(socket, channelid, bytestoadd) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.CHANNEL_WINDOW_ADJUST) + common.IntToStr(channelid) + common.IntToStr(bytestoadd));
|
||||
}
|
||||
|
||||
function SendDisconnect(socket, reasonCode) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.DISCONNECT) + common.IntToStr(ReasonCode) + common.ShortToStr(0));
|
||||
}
|
||||
|
||||
function SendUserAuthFail(socket) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.USERAUTH_FAILURE) + common.IntToStr(8) + 'password' + common.ShortToStr(0));
|
||||
}
|
||||
|
||||
function SendUserAuthSuccess(socket) {
|
||||
Write(socket, String.fromCharCode(APFProtocol.USERAUTH_SUCCESS));
|
||||
}
|
||||
|
||||
function Write(socket, data) {
|
||||
if (args.mpsdebug) {
|
||||
// Print out sent bytes
|
||||
var buf = new Buffer(data, "binary");
|
||||
console.log('MPS --> (' + buf.length + '):' + buf.toString('hex'));
|
||||
socket.write(buf);
|
||||
} else {
|
||||
socket.write(new Buffer(data, "binary"));
|
||||
}
|
||||
}
|
||||
|
||||
obj.SetupCiraChannel = function (socket, targetport) {
|
||||
var sourceport = (socket.tag.nextsourceport++ % 30000) + 1024;
|
||||
var cirachannel = { targetport: targetport, channelid: socket.tag.nextchannelid++, socket: socket, state: 1, sendcredits: 0, amtpendingcredits: 0, amtCiraWindow: 0, ciraWindow: 32768 };
|
||||
SendChannelOpen(socket, false, cirachannel.channelid, cirachannel.ciraWindow, socket.tag.host, targetport, "1.2.3.4", sourceport);
|
||||
|
||||
// This function writes data to this CIRA channel
|
||||
cirachannel.write = function (data) {
|
||||
if (cirachannel.state == 0) return false;
|
||||
if (cirachannel.state == 1 || cirachannel.sendcredits == 0 || cirachannel.sendBuffer != undefined) { if (cirachannel.sendBuffer == undefined) { cirachannel.sendBuffer = data; } else { cirachannel.sendBuffer += data; } return; }
|
||||
// Compute how much data we can send
|
||||
if (data.length <= cirachannel.sendcredits) {
|
||||
// Send the entire message
|
||||
SendChannelData(cirachannel.socket, cirachannel.amtchannelid, data);
|
||||
cirachannel.sendcredits -= data.length;
|
||||
return true;
|
||||
}
|
||||
// Send a part of the message
|
||||
cirachannel.sendBuffer = data.substring(cirachannel.sendcredits);
|
||||
SendChannelData(cirachannel.socket, cirachannel.amtchannelid, data.substring(0, cirachannel.sendcredits));
|
||||
cirachannel.sendcredits = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// This function closes this CIRA channel
|
||||
cirachannel.close = function () {
|
||||
if (cirachannel.state == 0 || cirachannel.closing == 1) return;
|
||||
if (cirachannel.state == 1) { cirachannel.closing = 1; cirachannel.state = 0; if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } return; }
|
||||
cirachannel.state = 0;
|
||||
cirachannel.closing = 1;
|
||||
SendChannelClose(cirachannel.socket, cirachannel.amtchannelid);
|
||||
if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); }
|
||||
}
|
||||
|
||||
socket.tag.channels[cirachannel.channelid] = cirachannel;
|
||||
return cirachannel;
|
||||
}
|
||||
|
||||
function ChangeHostname(socket, host) {
|
||||
if (socket.tag.host == host) return; // Nothing to change
|
||||
socket.tag.host = host;
|
||||
|
||||
// Change the device
|
||||
obj.db.Get(socket.tag.nodeid, function (err, nodes) {
|
||||
if (nodes.length != 1) return;
|
||||
var node = nodes[0];
|
||||
if ((node.intelamt != undefined) && (node.intelamt.host == host)) return;
|
||||
|
||||
// Get the mesh for this device
|
||||
obj.db.Get(node.meshid, function (err, meshes) {
|
||||
if (meshes.length != 1) return;
|
||||
var mesh = meshes[0];
|
||||
|
||||
// Ready the node change event
|
||||
var changes = ['host'], event = { etype: 'node', action: 'changenode', nodeid: node._id };
|
||||
event.msg = +": ";
|
||||
|
||||
// Make the change & save
|
||||
if (node.intelamt == undefined) node.intelamt = {};
|
||||
node.intelamt.host = host;
|
||||
if (node.name == '') { node.name = host.split('.')[0]; }
|
||||
obj.db.Set(node);
|
||||
|
||||
// Event the node change
|
||||
event.msg = 'CIRA changed device ' + node.name + ' from mesh ' + mesh.name + ': ' + changes.join(', ');
|
||||
var node2 = common.Clone(node);
|
||||
if (node2.intelamt && node2.intelamt.pass) delete node2.intelamt.pass; // Remove the Intel AMT password before eventing this.
|
||||
event.node = node2;
|
||||
obj.parent.DispatchEvent(['*', node.meshid], obj, event);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + "-" + g.substring(10, 12) + g.substring(8, 10) + "-" + g.substring(14, 16) + g.substring(12, 14) + "-" + g.substring(16, 20) + "-" + g.substring(20); }
|
||||
|
||||
// Debug
|
||||
function Debug(lvl) {
|
||||
if (lvl > obj.parent.debugLevel) return;
|
||||
if (arguments.length == 2) { console.log(arguments[1]); }
|
||||
else if (arguments.length == 3) { console.log(arguments[1], arguments[2]); }
|
||||
else if (arguments.length == 4) { console.log(arguments[1], arguments[2], arguments[3]); }
|
||||
else if (arguments.length == 5) { console.log(arguments[1], arguments[2], arguments[3], arguments[4]); }
|
||||
else if (arguments.length == 6) { console.log(arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]); }
|
||||
else if (arguments.length == 7) { console.log(arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]); }
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* @description Meshcentral Multi-Server Support
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
// Construct a Mesh Multi-Server object. This is used for MeshCentral-to-MeshCentral communication.
|
||||
module.exports.CreateMultiServer = function (parent, args) {
|
||||
var obj = {};
|
||||
obj.parent = parent;
|
||||
obj.crypto = require('crypto');
|
||||
|
||||
// Generate a cryptographic key used to encode and decode cookies
|
||||
obj.generateCookieKey = function () {
|
||||
return new Buffer(obj.crypto.randomBytes(32), 'binary').toString('ascii');
|
||||
}
|
||||
|
||||
// Encode an object as a cookie using a key
|
||||
obj.encodeCookie = function (o, key) {
|
||||
try {
|
||||
if (key == undefined) { key = obj.serverKey; }
|
||||
o.time = Math.floor(Date.now() / 1000); // Add the cookie creation time
|
||||
var msg = JSON.stringify(o);
|
||||
msg = obj.crypto.createHmac('sha256', key.substring(16)).update(msg, 'binary', 'binary').digest('binary') + msg;
|
||||
var iv = new Buffer(obj.crypto.randomBytes(16), 'binary');
|
||||
var cipher = obj.crypto.createCipheriv('aes-128-cbc', key.substring(0, 16), iv);
|
||||
crypted = cipher.update(msg, 'binary', 'binary');
|
||||
crypted += cipher.final('binary');
|
||||
var total = new Buffer(iv, 'binary').toString('hex') + new Buffer(crypted, 'binary').toString('hex'); // HEX: This is not an efficient concat, but it's very compatible.
|
||||
var cookie = new Buffer(total, 'hex').toString('base64');
|
||||
return cookie.replace(/\+/g, '@').replace(/\//g, '$');
|
||||
} catch (e) { return null; }
|
||||
}
|
||||
|
||||
// Decode a cookie back into an object using a key. Return null if it's not a valid cookie.
|
||||
obj.decodeCookie = function (cookie, key) {
|
||||
try {
|
||||
if (key == undefined) { key = obj.serverKey; }
|
||||
cookie = new Buffer(cookie.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64').toString('hex'); // HEX: This is not an efficient split, but it's very compatible.
|
||||
var iv = new Buffer(cookie.substring(0, 32), 'hex');
|
||||
var msg = new Buffer(cookie.substring(32), 'hex');
|
||||
var decipher = obj.crypto.createDecipheriv('aes-128-cbc', key.substring(0, 16), iv)
|
||||
var dec = decipher.update(msg, 'binary', 'binary')
|
||||
dec += decipher.final('binary');
|
||||
var msg = dec.substring(32);
|
||||
var hash1 = dec.substring(0, 32);
|
||||
var hash2 = obj.crypto.createHmac('sha256', key.substring(16)).update(msg, 'binary', 'binary').digest('binary');
|
||||
if (hash1 !== hash2) { return null; }
|
||||
var o = JSON.parse(msg);
|
||||
if ((o.time == null) || (o.time == undefined) || (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
|
||||
return o;
|
||||
} catch (e) { return null; }
|
||||
}
|
||||
|
||||
// Dispatch an event to other MeshCentral2 peer servers
|
||||
obj.DispatchEvent = function (ids, source, event) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// Handle websocket requests on "/meshserver.ashx" from other MeshCentral2 peer servers.
|
||||
obj.handleServerWebSocket = function (ws, req) {
|
||||
Debug(1, 'MeshServer connection open.');
|
||||
|
||||
// Handle data from another mesh server
|
||||
ws.on('message', function (msg) {
|
||||
Debug(1, 'MeshServer data of length ' + msg.length);
|
||||
// TODO
|
||||
});
|
||||
|
||||
// If error, do nothing
|
||||
ws.on('error', function (err) { console.log(err); });
|
||||
|
||||
// Another mesh server connection has closed
|
||||
ws.on('close', function (req) {
|
||||
Debug(1, 'MeshServer connection closed.');
|
||||
// TODO
|
||||
});
|
||||
}
|
||||
|
||||
obj.serverKey = obj.generateCookieKey();
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.0.6-p",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
"Active Management",
|
||||
"Remote Desktop"
|
||||
],
|
||||
"homepage": "http://meshcommander.com",
|
||||
"description": "Web based remote computer management and file server",
|
||||
"author": "Ylian Saint-Hilaire <ysainthilaire@hotmail.com>",
|
||||
"main": "meshcentral.js",
|
||||
"bin": {
|
||||
"meshcentral": "./bin/meshcentral"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"files": [
|
||||
"*.js",
|
||||
"license.txt",
|
||||
"readme.txt",
|
||||
"agents",
|
||||
"public",
|
||||
"views",
|
||||
"bin"
|
||||
],
|
||||
"dependencies": {
|
||||
"meshcentral": "*",
|
||||
"archiver": "^1.3.0",
|
||||
"body-parser": "^1.17.1",
|
||||
"compression": "^1.6.2",
|
||||
"connect-redis": "^3.2.0",
|
||||
"express": "^4.15.2",
|
||||
"express-handlebars": "^3.0.0",
|
||||
"express-session": "^1.15.1",
|
||||
"express-ws": "^2.0.0",
|
||||
"minimist": "^1.2.0",
|
||||
"multiparty": "^4.1.3",
|
||||
"nedb": "^1.8.0",
|
||||
"node-forge": "^0.6.49",
|
||||
"unzip": "^0.1.11",
|
||||
"xmldom": "^0.1.27"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"node-windows": "^0.1.14",
|
||||
"mongojs": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"readme": "readme.txt"
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// check out https://github.com/tj/node-pwd
|
||||
|
||||
// Module dependencies.
|
||||
var crypto = require('crypto');
|
||||
|
||||
// Bytesize.
|
||||
var len = 128;
|
||||
|
||||
// Iterations. ~300ms
|
||||
var iterations = 12000;
|
||||
|
||||
/**
|
||||
* Hashes a password with optional `salt`, otherwise
|
||||
* generate a salt for `pass` and invoke `fn(err, salt, hash)`.
|
||||
*
|
||||
* @param {String} password to hash
|
||||
* @param {String} optional salt
|
||||
* @param {Function} callback
|
||||
* @api public
|
||||
*/
|
||||
exports.hash = function (pwd, salt, fn) {
|
||||
if (3 == arguments.length) {
|
||||
try {
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, 'sha256', function (err, hash) { fn(err, hash.toString('base64')); });
|
||||
} catch (e) {
|
||||
// If this previous call fails, it's probably because older pbkdf2 did not specify the hashing function, just use the default.
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, function (err, hash) { fn(err, hash.toString('base64')); });
|
||||
}
|
||||
} else {
|
||||
fn = salt;
|
||||
crypto.randomBytes(len, function (err, salt) {
|
||||
if (err) return fn(err);
|
||||
salt = salt.toString('base64');
|
||||
try {
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, 'sha256', function (err, hash) { if (err) { return fn(err); } fn(null, salt, hash.toString('base64')); });
|
||||
} catch (e) {
|
||||
// If this previous call fails, it's probably because older pbkdf2 did not specify the hashing function, just use the default.
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, function (err, hash) { if (err) { return fn(err); } fn(null, salt, hash.toString('base64')); });
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 185 B |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 189 B |
After Width: | Height: | Size: 180 B |
After Width: | Height: | Size: 229 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 165 B |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 142 B |
After Width: | Height: | Size: 131 B |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="relay.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
var relay = createMeshConnection('0D49FDEFFB778D40C062DD34A3E96113:1485387591408:b2970207326684b4de5375d097db23c85e64497eaa72013b56a8ecd8063fb9b3').connect();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* @fileoverview Dynamic interface to MeshCentral2
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
var createMeshConnection = function (connectionId) {
|
||||
var obj = {};
|
||||
obj.connectionId = connectionId;
|
||||
obj.state = 0;
|
||||
obj.websocket = null;
|
||||
obj.onStateChanged = null;
|
||||
obj.onData = null;
|
||||
|
||||
obj.connect = function () {
|
||||
if (obj.state == 0) {
|
||||
obj.websocket = new WebSocket(window.location.protocol.replace('http', 'ws') + '//' + window.location.host + '/meshrelay.ashx?id=' + obj.connectionId);
|
||||
obj.websocket.binaryType = "arraybuffer";
|
||||
obj.websocket.onopen = function (e) { console.log('WebSocket Connected', e); };
|
||||
obj.websocket.onmessage = function (e) {
|
||||
console.log('WebSocket Message', e);
|
||||
if ((obj.state = 1) && (e.data == 'c')) {
|
||||
obj.state = 2;
|
||||
if (obj.onStateChanged) { onStateChanged(obj, 2); }
|
||||
console.log('WebSocket Peer Connection', e);
|
||||
obj.send('bob');
|
||||
} else {
|
||||
if (obj.onData != null) { obj.onData(obj, e.data); }
|
||||
}
|
||||
};
|
||||
obj.websocket.onclose = function (e) {
|
||||
console.log('WebSocket Closed', e);
|
||||
obj.state = 0;
|
||||
if (obj.onStateChanged) { onStateChanged(obj, 0); }
|
||||
};
|
||||
obj.websocket.onerror = function (e) { console.log('WebSocket Error', e); };
|
||||
obj.state = 1;
|
||||
if (obj.onStateChanged) { onStateChanged(obj, 1); }
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
obj.send = function (data) {
|
||||
if ((obj.state == 2) && (obj.websocket != null)) { obj.websocket.send(data); }
|
||||
};
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,591 @@
|
|||
/**
|
||||
* @description Remote Desktop
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.2g
|
||||
*/
|
||||
|
||||
// Construct a MeshServer object
|
||||
var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
|
||||
var obj = {}
|
||||
obj.CanvasId = canvasid;
|
||||
if (typeof canvasid === 'string') obj.CanvasId = Q(canvasid);
|
||||
obj.Canvas = obj.CanvasId.getContext("2d");
|
||||
obj.scrolldiv = scrolldiv;
|
||||
obj.State = 0;
|
||||
obj.PendingOperations = [];
|
||||
obj.tilesReceived = 0;
|
||||
obj.TilesDrawn = 0;
|
||||
obj.KillDraw = 0;
|
||||
obj.ipad = false;
|
||||
obj.tabletKeyboardVisible = false;
|
||||
obj.LastX = 0;
|
||||
obj.LastY = 0;
|
||||
obj.touchenabled = 0;
|
||||
obj.submenuoffset = 0;
|
||||
obj.touchtimer = null;
|
||||
obj.TouchArray = {};
|
||||
obj.connectmode = 0; // 0 = HTTP, 1 = WebSocket, 2 = WebRTC
|
||||
obj.connectioncount = 0;
|
||||
obj.rotation = 0;
|
||||
obj.protocol = 2; // KVM
|
||||
|
||||
obj.sessionid = 0;
|
||||
obj.username;
|
||||
obj.oldie = false;
|
||||
obj.CompressionLevel = 50;
|
||||
obj.ScalingLevel = 1024;
|
||||
obj.FrameRateTimer = 50;
|
||||
obj.FirstDraw = false;
|
||||
|
||||
obj.ScreenWidth = 960;
|
||||
obj.ScreenHeight = 700;
|
||||
obj.width = 960;
|
||||
obj.height = 960;
|
||||
|
||||
obj.onScreenResize = null;
|
||||
obj.onMessage = null;
|
||||
obj.onConnectCountChanged = null;
|
||||
obj.onDebugMessage = null;
|
||||
obj.onTouchEnabledChanged = null;
|
||||
|
||||
obj.Start = function () {
|
||||
obj.State = 0;
|
||||
}
|
||||
|
||||
obj.Stop = function () {
|
||||
obj.setRotation(0);
|
||||
obj.UnGrabKeyInput();
|
||||
obj.UnGrabMouseInput();
|
||||
obj.touchenabled = 0;
|
||||
if (obj.onScreenResize != null) obj.onScreenResize(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId);
|
||||
obj.Canvas.clearRect(0, 0, obj.CanvasId.width, obj.CanvasId.height);
|
||||
}
|
||||
|
||||
obj.xxStateChange = function (newstate) {
|
||||
if (obj.State == newstate) return;
|
||||
obj.State = newstate;
|
||||
//console.log('xxStateChange', newstate);
|
||||
switch (newstate) {
|
||||
case 0: {
|
||||
// Disconnect
|
||||
obj.Stop();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
// Websocket connected
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.Send = function (x) {
|
||||
//console.log("KSend(" + x.length + "): " + rstr2hex(x));
|
||||
obj.parent.Send(x);
|
||||
}
|
||||
|
||||
// KVM Control.
|
||||
// Routines for processing incoming packets from the AJAX server, and handling individual messages.
|
||||
obj.ProcessPictureMsg = function (str, X, Y) {
|
||||
//if (obj.targetnode != null) obj.Debug("ProcessPictureMsg " + X + "," + Y + " - " + obj.targetnode.substring(0, 8));
|
||||
var tile = new Image();
|
||||
obj.tilesReceived++;
|
||||
var r = obj.tilesReceived;
|
||||
tile.src = "data:image/jpeg;base64," + btoa(str.substring(4, str.length));
|
||||
tile.onload = function () {
|
||||
if (obj.Canvas != null && obj.KillDraw < r && obj.State != 0) {
|
||||
obj.PendingOperations.push([r, 2, tile, X, Y]);
|
||||
while (obj.DoPendingOperations()) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.DoPendingOperations = function () {
|
||||
if (obj.PendingOperations.length == 0) return false;
|
||||
for (var i = 0; i < obj.PendingOperations.length; i++) { // && KillDraw < tilesDrawn
|
||||
var Msg = obj.PendingOperations[i];
|
||||
if (Msg[0] == (obj.TilesDrawn + 1)) {
|
||||
if (Msg[1] == 1) { obj.ProcessCopyRectMsg(Msg[2]); }
|
||||
else if (Msg[1] == 2) { obj.Canvas.drawImage(Msg[2], obj.rotX(Msg[3], Msg[4]), obj.rotY(Msg[3], Msg[4])); delete Msg[2]; }
|
||||
obj.PendingOperations.splice(i, 1);
|
||||
delete Msg;
|
||||
obj.TilesDrawn++;
|
||||
if (obj.TilesDrawn == obj.tilesReceived && obj.KillDraw < obj.TilesDrawn) { obj.KillDraw = obj.TilesDrawn = obj.tilesReceived = 0; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (obj.oldie && obj.PendingOperations.length > 0) { obj.TilesDrawn++; }
|
||||
return false;
|
||||
}
|
||||
|
||||
obj.ProcessCopyRectMsg = function (str) {
|
||||
var SX = ((str.charCodeAt(0) & 0xFF) << 8) + (str.charCodeAt(1) & 0xFF);
|
||||
var SY = ((str.charCodeAt(2) & 0xFF) << 8) + (str.charCodeAt(3) & 0xFF);
|
||||
var DX = ((str.charCodeAt(4) & 0xFF) << 8) + (str.charCodeAt(5) & 0xFF);
|
||||
var DY = ((str.charCodeAt(6) & 0xFF) << 8) + (str.charCodeAt(7) & 0xFF);
|
||||
var WIDTH = ((str.charCodeAt(8) & 0xFF) << 8) + (str.charCodeAt(9) & 0xFF);
|
||||
var HEIGHT = ((str.charCodeAt(10) & 0xFF) << 8) + (str.charCodeAt(11) & 0xFF);
|
||||
obj.Canvas.drawImage(Canvas.canvas, SX, SY, WIDTH, HEIGHT, DX, DY, WIDTH, HEIGHT);
|
||||
}
|
||||
|
||||
obj.SendUnPause = function () {
|
||||
//obj.Debug("SendUnPause");
|
||||
//obj.xxStateChange(3);
|
||||
obj.Send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x00));
|
||||
}
|
||||
|
||||
obj.SendPause = function () {
|
||||
//obj.Debug("SendPause");
|
||||
//obj.xxStateChange(2);
|
||||
obj.Send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x01));
|
||||
}
|
||||
|
||||
obj.SendCompressionLevel = function (type, level, scaling, frametimer) {
|
||||
if (level) { obj.CompressionLevel = level; }
|
||||
if (scaling) { obj.ScalingLevel = scaling; }
|
||||
if (frametimer) { obj.FrameRateTimer = frametimer; }
|
||||
obj.Send(String.fromCharCode(0x00, 0x05, 0x00, 0x0A, type, obj.CompressionLevel) + obj.shortToStr(obj.ScalingLevel) + obj.shortToStr(obj.FrameRateTimer));
|
||||
}
|
||||
|
||||
obj.SendRefresh = function () {
|
||||
obj.Send(String.fromCharCode(0x00, 0x06, 0x00, 0x04));
|
||||
}
|
||||
|
||||
obj.ProcessScreenMsg = function (width, height) {
|
||||
//obj.Debug("ProcessScreenMsg: " + width + " x " + height);
|
||||
obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
|
||||
obj.rotation = 0;
|
||||
obj.FirstDraw = true;
|
||||
obj.ScreenWidth = obj.width = width;
|
||||
obj.ScreenHeight = obj.height = height;
|
||||
obj.KillDraw = obj.tilesReceived;
|
||||
while (obj.PendingOperations.length > 0) { obj.PendingOperations.shift(); }
|
||||
obj.SendCompressionLevel(1);
|
||||
obj.SendUnPause();
|
||||
}
|
||||
|
||||
obj.ProcessData = function (str) {
|
||||
if (str.length < 4) return;
|
||||
var cmdmsg = null, X = 0, Y = 0, command = ReadShort(str, 0), cmdsize = ReadShort(str, 2);
|
||||
if (command >= 18) { console.error("Invalid KVM command " + command + " of size " + cmdsize); obj.parent.Stop(); return; }
|
||||
if (cmdsize > str.length) return;
|
||||
//meshOnDebug("KVM Command: " + command + " Len:" + cmdsize);
|
||||
|
||||
if (command == 3 || command == 4 || command == 7) {
|
||||
cmdmsg = str.substring(4, cmdsize);
|
||||
X = ((cmdmsg.charCodeAt(0) & 0xFF) << 8) + (cmdmsg.charCodeAt(1) & 0xFF);
|
||||
Y = ((cmdmsg.charCodeAt(2) & 0xFF) << 8) + (cmdmsg.charCodeAt(3) & 0xFF);
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case 3: // Tile
|
||||
if (obj.FirstDraw) obj.onResize();
|
||||
obj.ProcessPictureMsg(cmdmsg, X, Y);
|
||||
break;
|
||||
case 4: // Tile Copy
|
||||
if (obj.FirstDraw) obj.onResize();
|
||||
if (obj.TilesDrawn == obj.tilesReceived) {
|
||||
obj.ProcessCopyRectMsg(cmdmsg);
|
||||
} else {
|
||||
obj.PendingOperations.push([ ++tilesReceived, 1, cmdmsg ]);
|
||||
}
|
||||
break;
|
||||
case 7: // Screen size
|
||||
obj.ProcessScreenMsg(X, Y);
|
||||
obj.SendKeyMsgKC(obj.KeyAction.UP, 16); // Shift
|
||||
obj.SendKeyMsgKC(obj.KeyAction.UP, 17); // Ctrl
|
||||
obj.SendKeyMsgKC(obj.KeyAction.UP, 18); // Alt
|
||||
obj.SendKeyMsgKC(obj.KeyAction.UP, 91); // Left-Windows
|
||||
obj.SendKeyMsgKC(obj.KeyAction.UP, 92); // Right-Windows
|
||||
obj.Send(String.fromCharCode(0x00, 0x0E, 0x00, 0x04));
|
||||
break;
|
||||
case 11: // GetDisplays
|
||||
var dcount = ((str.charCodeAt(4) & 0xFF) << 8) + (str.charCodeAt(5) & 0xFF);
|
||||
if (dcount == 0) {
|
||||
// One display present
|
||||
if (document.getElementById('termdisplays') != null) document.getElementById('termdisplays').style.display = 'none';
|
||||
if (document.getElementById('termdisplays2') != null) document.getElementById('termdisplays2').style.display = 'none';
|
||||
} else {
|
||||
// Many displays present
|
||||
var seldisp = ((str.charCodeAt(6 + (dcount * 2)) & 0xFF) << 8) + (str.charCodeAt(7 + (dcount * 2)) & 0xFF);
|
||||
var selitem = 0;
|
||||
var myOptions = [];
|
||||
for (var i = 0; i < dcount; i++) {
|
||||
var disp = ((str.charCodeAt(6 + (i * 2)) & 0xFF) << 8) + (str.charCodeAt(7 + (i * 2)) & 0xFF);
|
||||
if (disp == 65535) {
|
||||
myOptions.push('All Displays');
|
||||
} else {
|
||||
myOptions.push('Display ' + disp);
|
||||
}
|
||||
if (disp == seldisp) selitem = i;
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
break;
|
||||
case 12: // SetDisplay
|
||||
break;
|
||||
case 14: // KVM_INIT_TOUCH
|
||||
obj.touchenabled = 1;
|
||||
obj.TouchArray = {};
|
||||
if (obj.onTouchEnabledChanged != null) obj.onTouchEnabledChanged(obj.touchenabled);
|
||||
break;
|
||||
case 15: // KVM_TOUCH
|
||||
obj.TouchArray = {};
|
||||
break;
|
||||
case 16: // MNG_KVM_CONNECTCOUNT
|
||||
obj.connectioncount = ReadInt(str, 4);
|
||||
//obj.Debug("Got KVM Connect Count: " + obj.connectioncount);
|
||||
if (obj.onConnectCountChanged != null) obj.onConnectCountChanged(obj.connectioncount, obj);
|
||||
break;
|
||||
case 17: // MNG_KVM_MESSAGE
|
||||
//obj.Debug("Got KVM Message: " + str.substring(4, cmdsize));
|
||||
if (obj.onMessage != null) obj.onMessage(str.substring(4, cmdsize), obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard and Mouse I/O.
|
||||
obj.MouseButton = { "NONE": 0x00, "LEFT": 0x02, "RIGHT": 0x08, "MIDDLE": 0x20 };
|
||||
obj.KeyAction = { "NONE": 0, "DOWN": 1, "UP": 2, "SCROLL": 3, "EXUP": 4, "EXDOWN": 5 };
|
||||
obj.InputType = { "KEY": 1, "MOUSE": 2, "CTRLALTDEL": 10, "TOUCH": 15 };
|
||||
obj.Alternate = 0;
|
||||
|
||||
obj.SendKeyMsg = function (action, event) {
|
||||
if (action == null) return;
|
||||
if (!event) { var event = window.event; }
|
||||
var kc = event.keyCode;
|
||||
if (kc == 0x3B) kc = 0xBA; // ';' key
|
||||
obj.SendKeyMsgKC(action, kc);
|
||||
}
|
||||
|
||||
obj.SendMessage = function (msg) {
|
||||
if (obj.State == 3) obj.Send(String.fromCharCode(0x00, 0x11) + obj.shortToStr(4 + msg.length) + msg); // 0x11 = 17 MNG_KVM_MESSAGE
|
||||
}
|
||||
|
||||
obj.SendKeyMsgKC = function (action, kc) {
|
||||
if (obj.State == 3) obj.Send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, (action - 1), kc));
|
||||
}
|
||||
|
||||
obj.sendcad = function() { obj.SendCtrlAltDelMsg(); }
|
||||
|
||||
obj.SendCtrlAltDelMsg = function () {
|
||||
if (obj.State == 3) { obj.Send(String.fromCharCode(0x00, obj.InputType.CTRLALTDEL, 0x00, 0x04)); }
|
||||
}
|
||||
|
||||
obj.SendEscKey = function () {
|
||||
if (obj.State == 3) obj.Send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, 0x00, 0x1B, 0x00, obj.InputType.KEY, 0x00, 0x06, 0x01, 0x1B));
|
||||
}
|
||||
|
||||
obj.SendStartMsg = function () {
|
||||
obj.SendKeyMsgKC(obj.KeyAction.EXDOWN, 0x5B); // L-Windows
|
||||
obj.SendKeyMsgKC(obj.KeyAction.EXUP, 0x5B); // L-Windows
|
||||
}
|
||||
|
||||
obj.SendCharmsMsg = function () {
|
||||
obj.SendKeyMsgKC(obj.KeyAction.EXDOWN, 0x5B); // L-Windows
|
||||
obj.SendKeyMsgKC(obj.KeyAction.DOWN, 67); // C
|
||||
obj.SendKeyMsgKC(obj.KeyAction.UP, 67); // C
|
||||
obj.SendKeyMsgKC(obj.KeyAction.EXUP, 0x5B); // L-Windows
|
||||
}
|
||||
|
||||
obj.SendTouchMsg1 = function (id, flags, x, y) {
|
||||
if (obj.State == 3) obj.Send(String.fromCharCode(0x00, obj.InputType.TOUCH) + obj.shortToStr(14) + String.fromCharCode(0x01, id) + obj.intToStr(flags) + obj.shortToStr(x) + obj.shortToStr(y));
|
||||
}
|
||||
|
||||
obj.SendTouchMsg2 = function (id, flags) {
|
||||
var msg = '';
|
||||
var flags2;
|
||||
var str = "TOUCHSEND: ";
|
||||
for (var k in obj.TouchArray) {
|
||||
if (k == id) { flags2 = flags; } else {
|
||||
if (obj.TouchArray[k].f == 1) { flags2 = 0x00010000 | 0x00000002 | 0x00000004; obj.TouchArray[k].f = 3; str += "START" + k; } // POINTER_FLAG_DOWN
|
||||
else if (obj.TouchArray[k].f == 2) { flags2 = 0x00040000; str += "STOP" + k; } // POINTER_FLAG_UP
|
||||
else flags2 = 0x00000002 | 0x00000004 | 0x00020000; // POINTER_FLAG_UPDATE
|
||||
}
|
||||
msg += String.fromCharCode(k) + obj.intToStr(flags2) + obj.shortToStr(obj.TouchArray[k].x) + obj.shortToStr(obj.TouchArray[k].y);
|
||||
if (obj.TouchArray[k].f == 2) delete obj.TouchArray[k];
|
||||
}
|
||||
if (obj.State == 3) obj.Send(String.fromCharCode(0x00, obj.InputType.TOUCH) + obj.shortToStr(5 + msg.length) + String.fromCharCode(0x02) + msg);
|
||||
if (Object.keys(obj.TouchArray).length == 0 && obj.touchtimer != null) { clearInterval(obj.touchtimer); obj.touchtimer = null; }
|
||||
}
|
||||
|
||||
obj.SendMouseMsg = function (Action, event) {
|
||||
if (obj.State != 3) return;
|
||||
if (Action != null && obj.Canvas != null) {
|
||||
if (!event) { var event = window.event; }
|
||||
var ScaleFactorHeight = (obj.Canvas.canvas.height / obj.CanvasId.clientHeight);
|
||||
var ScaleFactorWidth = (obj.Canvas.canvas.width / obj.CanvasId.clientWidth);
|
||||
var Offsets = obj.GetPositionOfControl(obj.Canvas.canvas);
|
||||
var X = ((event.pageX - Offsets[0]) * ScaleFactorWidth);
|
||||
var Y = ((event.pageY - Offsets[1]) * ScaleFactorHeight);
|
||||
|
||||
if (X >= 0 && X <= obj.Canvas.canvas.width && Y >= 0 && Y <= obj.Canvas.canvas.height) {
|
||||
var Button = 0;
|
||||
var Delta = 0;
|
||||
if (Action == obj.KeyAction.UP || Action == obj.KeyAction.DOWN) {
|
||||
if (event.which) { ((event.which == 1) ? (Button = obj.MouseButton.LEFT) : ((event.which == 2) ? (Button = obj.MouseButton.MIDDLE) : (Button = obj.MouseButton.RIGHT))); }
|
||||
else if (event.button) { ((event.button == 0) ? (Button = obj.MouseButton.LEFT) : ((event.button == 1) ? (Button = obj.MouseButton.MIDDLE) : (Button = obj.MouseButton.RIGHT))); }
|
||||
}
|
||||
else if (Action == obj.KeyAction.SCROLL) {
|
||||
if (event.detail) { Delta = (-1 * (event.detail * 120)); } else if (event.wheelDelta) { Delta = (event.wheelDelta * 3); }
|
||||
}
|
||||
|
||||
var MouseMsg = "";
|
||||
if (Action == obj.KeyAction.SCROLL) MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0C, 0x00, ((Action == obj.KeyAction.DOWN) ? Button : ((Button * 2) & 0xFF)), ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF), ((Delta / 256) & 0xFF), (Delta & 0xFF));
|
||||
else MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0A, 0x00, ((Action == obj.KeyAction.DOWN) ? Button : ((Button * 2) & 0xFF)), ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF));
|
||||
if (obj.Action == obj.KeyAction.NONE) { if (obj.Alternate == 0 || obj.ipad) { obj.Send(MouseMsg); obj.Alternate = 1; } else { obj.Alternate = 0; } } else { obj.Send(MouseMsg); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.GetDisplayNumbers = function () { obj.Send(String.fromCharCode(0x00, 0x0B, 0x00, 0x04)); } // Get Terminal display
|
||||
obj.SetDisplay = function (number) { obj.Send(String.fromCharCode(0x00, 0x0C, 0x00, 0x06, number >> 8, number & 0xFF)); } // Set Terminal display
|
||||
obj.intToStr = function (x) { return String.fromCharCode((x >> 24) & 0xFF, (x >> 16) & 0xFF, (x >> 8) & 0xFF, x & 0xFF); }
|
||||
obj.shortToStr = function (x) { return String.fromCharCode((x >> 8) & 0xFF, x & 0xFF); }
|
||||
|
||||
obj.onResize = function () {
|
||||
if (obj.ScreenWidth == 0 || obj.ScreenHeight == 0) return;
|
||||
if (obj.Canvas.canvas.width == obj.ScreenWidth && obj.Canvas.canvas.height == obj.ScreenHeight) return;
|
||||
if (obj.FirstDraw) {
|
||||
obj.Canvas.canvas.width = obj.ScreenWidth;
|
||||
obj.Canvas.canvas.height = obj.ScreenHeight;
|
||||
obj.Canvas.fillRect(0, 0, obj.ScreenWidth, obj.ScreenHeight);
|
||||
if (obj.onScreenResize != null) obj.onScreenResize(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId);
|
||||
}
|
||||
obj.FirstDraw = false;
|
||||
//obj.Debug("onResize: " + obj.ScreenWidth + " x " + obj.ScreenHeight);
|
||||
}
|
||||
|
||||
obj.xxMouseInputGrab = false;
|
||||
obj.xxKeyInputGrab = false;
|
||||
obj.xxMouseMove = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.NONE, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||||
obj.xxMouseUp = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.UP, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||||
obj.xxMouseDown = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.DOWN, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||||
obj.xxDOMMouseScroll = function (e) { if (obj.State == 3) { obj.SendMouseMsg(obj.KeyAction.SCROLL, e); return false; } return true; }
|
||||
obj.xxMouseWheel = function (e) { if (obj.State == 3) { obj.SendMouseMsg(obj.KeyAction.SCROLL, e); return false; } return true; }
|
||||
obj.xxKeyUp = function (e) { if (obj.State == 3) { obj.SendKeyMsg(obj.KeyAction.UP, e); } if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||||
obj.xxKeyDown = function (e) { if (obj.State == 3) { obj.SendKeyMsg(obj.KeyAction.DOWN, e); } if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||||
obj.xxKeyPress = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||||
|
||||
// Key handlers
|
||||
obj.handleKeys = function (e) { return obj.xxKeyPress(e); }
|
||||
obj.handleKeyUp = function (e) { return obj.xxKeyUp(e); }
|
||||
obj.handleKeyDown = function (e) { return obj.xxKeyDown(e); }
|
||||
|
||||
// Mouse handlers
|
||||
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.xxMsTouchEvent = function (evt) {
|
||||
if (evt.originalEvent.pointerType == 4) return; // If this is a mouse pointer, ignore this event. Touch & pen are ok.
|
||||
if (evt.preventDefault) evt.preventDefault();
|
||||
if (evt.stopPropagation) evt.stopPropagation();
|
||||
if (evt.type == 'MSPointerDown' || evt.type == 'MSPointerMove' || evt.type == 'MSPointerUp') {
|
||||
var flags = 0;
|
||||
var id = evt.originalEvent.pointerId % 256;
|
||||
var X = evt.offsetX * (Canvas.canvas.width / obj.CanvasId.clientWidth);
|
||||
var Y = evt.offsetY * (Canvas.canvas.height / obj.CanvasId.clientHeight);
|
||||
|
||||
if (evt.type == 'MSPointerDown') flags = 0x00010000 | 0x00000002 | 0x00000004; // POINTER_FLAG_DOWN
|
||||
else if (evt.type == 'MSPointerMove') {
|
||||
//if (obj.TouchArray[id] && MuchTheSame(obj.TouchArray[id].x, X) && MuchTheSame(obj.TouchArray[id].y, Y)) return;
|
||||
flags = 0x00020000 | 0x00000002 | 0x00000004; // POINTER_FLAG_UPDATE
|
||||
}
|
||||
else if (evt.type == 'MSPointerUp') flags = 0x00040000; // POINTER_FLAG_UP
|
||||
|
||||
if (!obj.TouchArray[id]) obj.TouchArray[id] = { x: X, y : Y };
|
||||
obj.SendTouchMsg2(id, flags)
|
||||
if (evt.type == 'MSPointerUp') delete obj.TouchArray[id];
|
||||
} else {
|
||||
alert(evt.type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
obj.xxTouchStart = function (e) {
|
||||
if (obj.State != 3) return;
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
if (obj.touchenabled == 0 || obj.touchenabled == 1) {
|
||||
if (e.originalEvent.touches.length > 1) return;
|
||||
var t = e.originalEvent.touches[0];
|
||||
e.which = 1;
|
||||
obj.LastX = e.pageX = t.pageX;
|
||||
obj.LastY = e.pageY = t.pageY;
|
||||
obj.SendMouseMsg(KeyAction.DOWN, e);
|
||||
} else {
|
||||
var Offsets = obj.GetPositionOfControl(Canvas.canvas);
|
||||
for (var i in e.originalEvent.changedTouches) {
|
||||
if (!e.originalEvent.changedTouches[i].identifier) continue;
|
||||
var id = e.originalEvent.changedTouches[i].identifier % 256;
|
||||
if (!obj.TouchArray[id]) { obj.TouchArray[id] = { x: (e.originalEvent.touches[i].pageX - Offsets[0]) * (Canvas.canvas.width / obj.CanvasId.clientWidth), y: (e.originalEvent.touches[i].pageY - Offsets[1]) * (Canvas.canvas.height / obj.CanvasId.clientHeight), f: 1 }; }
|
||||
}
|
||||
if (Object.keys(obj.TouchArray).length > 0 && touchtimer == null) { obj.touchtimer = setInterval(function () { obj.SendTouchMsg2(256, 0); }, 50); }
|
||||
}
|
||||
}
|
||||
|
||||
obj.xxTouchMove = function (e) {
|
||||
if (obj.State != 3) return;
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
if (obj.touchenabled == 0 || obj.touchenabled == 1) {
|
||||
if (e.originalEvent.touches.length > 1) return;
|
||||
var t = e.originalEvent.touches[0];
|
||||
e.which = 1;
|
||||
obj.LastX = e.pageX = t.pageX;
|
||||
obj.LastY = e.pageY = t.pageY;
|
||||
obj.SendMouseMsg(obj.KeyAction.NONE, e);
|
||||
} else {
|
||||
var Offsets = obj.GetPositionOfControl(Canvas.canvas);
|
||||
for (var i in e.originalEvent.changedTouches) {
|
||||
if (!e.originalEvent.changedTouches[i].identifier) continue;
|
||||
var id = e.originalEvent.changedTouches[i].identifier % 256;
|
||||
if (obj.TouchArray[id]) {
|
||||
obj.TouchArray[id].x = (e.originalEvent.touches[i].pageX - Offsets[0]) * (obj.Canvas.canvas.width / obj.CanvasId.clientWidth);
|
||||
obj.TouchArray[id].y = (e.originalEvent.touches[i].pageY - Offsets[1]) * (obj.Canvas.canvas.height / obj.CanvasId.clientHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.xxTouchEnd = function (e) {
|
||||
if (obj.State != 3) return;
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
if (obj.touchenabled == 0 || obj.touchenabled == 1) {
|
||||
if (e.originalEvent.touches.length > 1) return;
|
||||
e.which = 1;
|
||||
e.pageX = LastX;
|
||||
e.pageY = LastY;
|
||||
obj.SendMouseMsg(KeyAction.UP, e);
|
||||
} else {
|
||||
for (var i in e.originalEvent.changedTouches) {
|
||||
if (!e.originalEvent.changedTouches[i].identifier) continue;
|
||||
var id = e.originalEvent.changedTouches[i].identifier % 256;
|
||||
if (obj.TouchArray[id]) obj.TouchArray[id].f = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.GrabMouseInput = function () {
|
||||
if (obj.xxMouseInputGrab == true) return;
|
||||
var c = obj.CanvasId;
|
||||
c.onmousemove = obj.xxMouseMove;
|
||||
c.onmouseup = obj.xxMouseUp;
|
||||
c.onmousedown = obj.xxMouseDown;
|
||||
c.touchstart = obj.xxTouchStart;
|
||||
c.touchmove = obj.xxTouchMove;
|
||||
c.touchend = obj.xxTouchEnd;
|
||||
c.MSPointerDown = obj.xxMsTouchEvent;
|
||||
c.MSPointerMove = obj.xxMsTouchEvent;
|
||||
c.MSPointerUp = obj.xxMsTouchEvent;
|
||||
if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = obj.xxDOMMouseScroll; else c.onmousewheel = obj.xxMouseWheel;
|
||||
obj.xxMouseInputGrab = true;
|
||||
}
|
||||
|
||||
obj.UnGrabMouseInput = function () {
|
||||
if (obj.xxMouseInputGrab == false) return;
|
||||
var c = obj.CanvasId;
|
||||
c.onmousemove = null;
|
||||
c.onmouseup = null;
|
||||
c.onmousedown = null;
|
||||
c.touchstart = null;
|
||||
c.touchmove = null;
|
||||
c.touchend = null;
|
||||
c.MSPointerDown = null;
|
||||
c.MSPointerMove = null;
|
||||
c.MSPointerUp = null;
|
||||
if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = null; else c.onmousewheel = null;
|
||||
obj.xxMouseInputGrab = false;
|
||||
}
|
||||
|
||||
obj.GrabKeyInput = function () {
|
||||
if (obj.xxKeyInputGrab == true) return;
|
||||
document.onkeyup = obj.xxKeyUp;
|
||||
document.onkeydown = obj.xxKeyDown;
|
||||
document.onkeypress = obj.xxKeyPress;
|
||||
obj.xxKeyInputGrab = true;
|
||||
}
|
||||
|
||||
obj.UnGrabKeyInput = function () {
|
||||
if (obj.xxKeyInputGrab == false) return;
|
||||
document.onkeyup = null;
|
||||
document.onkeydown = null;
|
||||
document.onkeypress = null;
|
||||
obj.xxKeyInputGrab = false;
|
||||
}
|
||||
|
||||
obj.GetPositionOfControl = function (Control) {
|
||||
var Position = Array(2);
|
||||
Position[0] = Position[1] = 0;
|
||||
while (Control) { Position[0] += Control.offsetLeft; Position[1] += Control.offsetTop; Control = Control.offsetParent; }
|
||||
return Position;
|
||||
}
|
||||
|
||||
obj.crotX = function (x, y) {
|
||||
if (obj.rotation == 0) return x;
|
||||
if (obj.rotation == 1) return y;
|
||||
if (obj.rotation == 2) return obj.Canvas.canvas.width - x;
|
||||
if (obj.rotation == 3) return obj.Canvas.canvas.height - y;
|
||||
}
|
||||
|
||||
obj.crotY = function (x, y) {
|
||||
if (obj.rotation == 0) return y;
|
||||
if (obj.rotation == 1) return obj.Canvas.canvas.width - x;
|
||||
if (obj.rotation == 2) return obj.Canvas.canvas.height - y;
|
||||
if (obj.rotation == 3) return x;
|
||||
}
|
||||
|
||||
obj.rotX = function (x, y) {
|
||||
if (obj.rotation == 0 || obj.rotation == 1) return x;
|
||||
if (obj.rotation == 2) return x - obj.Canvas.canvas.width;
|
||||
if (obj.rotation == 3) return x - obj.Canvas.canvas.height;
|
||||
}
|
||||
|
||||
obj.rotY = function (x, y) {
|
||||
if (obj.rotation == 0 || obj.rotation == 3) return y;
|
||||
if (obj.rotation == 1) return y - obj.Canvas.canvas.width;
|
||||
if (obj.rotation == 2) return y - obj.Canvas.canvas.height;
|
||||
}
|
||||
|
||||
obj.tcanvas = null;
|
||||
obj.setRotation = function (x) {
|
||||
while (x < 0) { x += 4; }
|
||||
var newrotation = x % 4;
|
||||
if (newrotation == obj.rotation) return true;
|
||||
var rw = obj.Canvas.canvas.width;
|
||||
var rh = obj.Canvas.canvas.height;
|
||||
if (obj.rotation == 1 || obj.rotation == 3) { rw = obj.Canvas.canvas.height; rh = obj.Canvas.canvas.width; }
|
||||
|
||||
// Copy the canvas, put it back in the correct direction
|
||||
if (obj.tcanvas == null) obj.tcanvas = document.createElement('canvas');
|
||||
var tcanvasctx = obj.tcanvas.getContext('2d');
|
||||
tcanvasctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
tcanvasctx.canvas.width = rw;
|
||||
tcanvasctx.canvas.height = rh;
|
||||
tcanvasctx.rotate((obj.rotation * -90) * Math.PI / 180);
|
||||
if (obj.rotation == 0) tcanvasctx.drawImage(obj.Canvas.canvas, 0, 0);
|
||||
if (obj.rotation == 1) tcanvasctx.drawImage(obj.Canvas.canvas, -obj.Canvas.canvas.width, 0);
|
||||
if (obj.rotation == 2) tcanvasctx.drawImage(obj.Canvas.canvas, -obj.Canvas.canvas.width, -obj.Canvas.canvas.height);
|
||||
if (obj.rotation == 3) tcanvasctx.drawImage(obj.Canvas.canvas, 0, -obj.Canvas.canvas.height);
|
||||
|
||||
// Change the size and orientation and copy the canvas back into the rotation
|
||||
if (obj.rotation == 0 || obj.rotation == 2) { obj.Canvas.canvas.height = rw; obj.Canvas.canvas.width = rh; }
|
||||
if (obj.rotation == 1 || obj.rotation == 3) { obj.Canvas.canvas.height = rh; obj.Canvas.canvas.width = rw; }
|
||||
obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
|
||||
obj.Canvas.rotate((newrotation * 90) * Math.PI / 180);
|
||||
obj.rotation = newrotation;
|
||||
obj.Canvas.drawImage(obj.tcanvas, obj.rotX(0, 0), obj.rotY(0, 0));
|
||||
|
||||
obj.ScreenWidth = obj.Canvas.canvas.width;
|
||||
obj.ScreenHeight = obj.Canvas.canvas.height;
|
||||
if (obj.onScreenResize != null) obj.onScreenResize(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Private method
|
||||
obj.MuchTheSame = function (a, b) { return (Math.abs(a - b) < 4); }
|
||||
obj.Debug = function (msg) { console.log(msg); }
|
||||
obj.getIEVersion = function () { var r = -1; if (navigator.appName == 'Microsoft Internet Explorer') { var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) r = parseFloat(RegExp.$1); } return r; }
|
||||
obj.haltEvent = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/**
|
||||
* @description Mesh Agent Transport Module - using websocket relay
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.1f
|
||||
*/
|
||||
|
||||
// Construct a MeshServer agent direction object
|
||||
var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
|
||||
var obj = {};
|
||||
obj.m = module; // This is the inner module (Terminal or Desktop)
|
||||
module.parent = obj;
|
||||
obj.meshserver = meshserver;
|
||||
obj.nodeid = null;
|
||||
obj.State = 0;
|
||||
obj.socket = null;
|
||||
obj.connectstate = -1;
|
||||
obj.tunnelid = Math.random().toString(36).substring(2); // Generate a random client tunnel id
|
||||
obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER, 4 = Files, 5 = FileTransfer
|
||||
|
||||
obj.onStateChanged = null;
|
||||
|
||||
// Private method
|
||||
//obj.debug = function (msg) { console.log(msg); }
|
||||
|
||||
obj.Start = function (nodeid) {
|
||||
var url2, url = window.location.protocol.replace("http", "ws") + "//" + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/meshrelay.ashx?id=" + obj.tunnelid;
|
||||
if (serverPublicNamePort) { url2 = window.location.protocol.replace("http", "ws") + "//" + serverPublicNamePort + "/meshrelay.ashx?id=" + obj.tunnelid; } else { url2 = url; }
|
||||
obj.nodeid = nodeid;
|
||||
obj.connectstate = 0;
|
||||
obj.socket = new WebSocket(url);
|
||||
obj.socket.onopen = obj.xxOnSocketConnected;
|
||||
obj.socket.onmessage = obj.xxOnMessage;
|
||||
obj.socket.onclose = obj.xxOnSocketClosed;
|
||||
obj.xxStateChange(1);
|
||||
obj.meshserver.Send({ action: 'msg', type: 'tunnel', nodeid: obj.nodeid, value: url2 });
|
||||
//obj.debug("Agent Redir Start: " + url);
|
||||
}
|
||||
|
||||
obj.xxOnSocketConnected = function () {
|
||||
//obj.debug("Agent Redir Socket Connected");
|
||||
obj.xxStateChange(2);
|
||||
}
|
||||
|
||||
obj.xxOnMessage = function (e) {
|
||||
if (obj.State < 3) { if (e.data == 'c') { obj.socket.send(obj.protocol); obj.xxStateChange(3); return; } }
|
||||
if (typeof e.data == 'object') {
|
||||
var f = new FileReader();
|
||||
if (f.readAsBinaryString) {
|
||||
// Chrome & Firefox (Draft)
|
||||
f.onload = function (e) { obj.xxOnSocketData(e.target.result); }
|
||||
f.readAsBinaryString(new Blob([e.data]));
|
||||
} else if (f.readAsArrayBuffer) {
|
||||
// Chrome & Firefox (Spec)
|
||||
f.onloadend = function (e) { obj.xxOnSocketData(e.target.result); }
|
||||
f.readAsArrayBuffer(e.data);
|
||||
} else {
|
||||
// IE10, readAsBinaryString does not exist, use an alternative.
|
||||
var binary = "";
|
||||
var bytes = new Uint8Array(e.data);
|
||||
var length = bytes.byteLength;
|
||||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||||
obj.xxOnSocketData(binary);
|
||||
}
|
||||
} else {
|
||||
// If we get a string object, it maybe the WebRTC confirm. Ignore it.
|
||||
//obj.debug("Agent Redir Relay - OnData - " + typeof e.data + " - " + e.data.length);
|
||||
obj.xxOnSocketData(e.data);
|
||||
}
|
||||
};
|
||||
|
||||
obj.xxOnSocketData = function (data) {
|
||||
if (!data || obj.connectstate == -1) return;
|
||||
|
||||
if (typeof data === 'object') {
|
||||
// This is an ArrayBuffer, convert it to a string array (used in IE)
|
||||
var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
|
||||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||||
data = binary;
|
||||
}
|
||||
else if (typeof data !== 'string') return;
|
||||
|
||||
//console.log("xxOnSocketData", rstr2hex(data));
|
||||
|
||||
return obj.m.ProcessData(data);
|
||||
}
|
||||
|
||||
obj.Send = function (x) {
|
||||
//obj.debug("Agent Redir Send(" + x.length + "): " + rstr2hex(x));
|
||||
if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
|
||||
if (typeof x == 'string') {
|
||||
var b = new Uint8Array(x.length);
|
||||
for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
|
||||
obj.socket.send(b.buffer);
|
||||
} else {
|
||||
obj.socket.send(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.xxOnSocketClosed = function () {
|
||||
//obj.debug("Agent Redir Socket Closed");
|
||||
obj.Stop();
|
||||
}
|
||||
|
||||
obj.xxStateChange = function(newstate) {
|
||||
if (obj.State == newstate) return;
|
||||
obj.State = newstate;
|
||||
obj.m.xxStateChange(obj.State);
|
||||
if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
|
||||
}
|
||||
|
||||
obj.Stop = function () {
|
||||
//obj.debug("Agent Redir Socket Stopped");
|
||||
obj.xxStateChange(0);
|
||||
obj.connectstate = -1;
|
||||
if (obj.socket != null) { obj.socket.close(); obj.socket = null; }
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,976 @@
|
|||
/**
|
||||
* @fileoverview Intel(r) AMT Communication StackXX
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.2.0b
|
||||
*/
|
||||
|
||||
/**
|
||||
* Construct a AmtStackCreateService object, this ia the main Intel AMT communication stack.
|
||||
* @constructor
|
||||
*/
|
||||
function AmtStackCreateService(wsmanStack) {
|
||||
var obj = new Object();
|
||||
obj.wsman = wsmanStack;
|
||||
obj.pfx = ["http://intel.com/wbem/wscim/1/amt-schema/1/", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/", "http://intel.com/wbem/wscim/1/ips-schema/1/"];
|
||||
obj.PendingEnums = [];
|
||||
obj.PendingBatchOperations = 0;
|
||||
obj.ActiveEnumsCount = 0;
|
||||
obj.MaxActiveEnumsCount = 1; // Maximum number of enumerations that can be done at the same time.
|
||||
obj.onProcessChanged = null;
|
||||
var _MaxProcess = 0;
|
||||
var _LastProcess = 0;
|
||||
|
||||
// Return the number of pending actions
|
||||
obj.GetPendingActions = function () { return (obj.PendingEnums.length * 2) + (obj.ActiveEnumsCount) + obj.wsman.comm.PendingAjax.length + obj.wsman.comm.ActiveAjaxCount + obj.PendingBatchOperations; }
|
||||
|
||||
// Private Method, Update the current processing status, this gives the application an idea of what progress is being done by the WSMAN stack
|
||||
function _up() {
|
||||
var x = obj.GetPendingActions();
|
||||
if (_MaxProcess < x) _MaxProcess = x;
|
||||
if (obj.onProcessChanged != null && _LastProcess != x) {
|
||||
//console.log("Process Old=" + _LastProcess + ", New=" + x + ", PEnums=" + obj.PendingEnums.length + ", AEnums=" + obj.ActiveEnumsCount + ", PAjax=" + obj.wsman.comm.PendingAjax.length + ", AAjax=" + obj.wsman.comm.ActiveAjaxCount + ", PBatch=" + obj.PendingBatchOperations);
|
||||
_LastProcess = x;
|
||||
obj.onProcessChanged(x, _MaxProcess);
|
||||
}
|
||||
if (x == 0) _MaxProcess = 0;
|
||||
}
|
||||
|
||||
// Perform a WSMAN "SUBSCRIBE" operation.
|
||||
obj.Subscribe = function (name, delivery, url, callback, tag, pri, selectors, opaque, user, pass) { obj.wsman.ExecSubscribe(obj.CompleteName(name), delivery, url, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri, selectors, opaque, user, pass); _up(); }
|
||||
|
||||
// Perform a WSMAN "UNSUBSCRIBE" operation.
|
||||
obj.UnSubscribe = function (name, callback, tag, pri, selectors) { obj.wsman.ExecUnSubscribe(obj.CompleteName(name), function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri, selectors); _up(); }
|
||||
|
||||
// Perform a WSMAN "GET" operation.
|
||||
obj.Get = function (name, callback, tag, pri) { obj.wsman.ExecGet(obj.CompleteName(name), function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri); _up(); }
|
||||
|
||||
// Perform a WSMAN "PUT" operation.
|
||||
obj.Put = function (name, putobj, callback, tag, pri, selectors) { obj.wsman.ExecPut(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri, selectors); _up(); }
|
||||
|
||||
// Perform a WSMAN "CREATE" operation.
|
||||
obj.Create = function (name, putobj, callback, tag, pri) { obj.wsman.ExecCreate(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri); _up(); }
|
||||
|
||||
// Perform a WSMAN "DELETE" operation.
|
||||
obj.Delete = function (name, putobj, callback, tag, pri) { obj.wsman.ExecDelete(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri); _up(); }
|
||||
|
||||
// Perform a WSMAN method call operation.
|
||||
obj.Exec = function (name, method, args, callback, tag, pri, selectors) { obj.wsman.ExecMethod(obj.CompleteName(name), method, args, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, obj.CompleteExecResponse(response), xstatus, tag); }, 0, pri, selectors); _up(); }
|
||||
|
||||
// Perform a WSMAN method call operation.
|
||||
obj.ExecWithXml = function (name, method, args, callback, tag, pri, selectors) { obj.wsman.ExecMethodXml(obj.CompleteName(name), method, execArgumentsToXml(args), function (ws, resuri, response, xstatus) { _up(); callback(obj, name, obj.CompleteExecResponse(response), xstatus, tag); }, 0, pri, selectors); _up(); }
|
||||
|
||||
// Perform a WSMAN "ENUMERATE" operation.
|
||||
obj.Enum = function (name, callback, tag, pri) {
|
||||
if (obj.ActiveEnumsCount < obj.MaxActiveEnumsCount) {
|
||||
obj.ActiveEnumsCount++; obj.wsman.ExecEnum(obj.CompleteName(name), function (ws, resuri, response, xstatus, tag0) { _up(); _EnumStartSink(name, response, callback, resuri, xstatus, tag0); }, tag, pri);
|
||||
} else {
|
||||
obj.PendingEnums.push([name, callback, tag, pri]);
|
||||
}
|
||||
_up();
|
||||
}
|
||||
|
||||
// Private method
|
||||
function _EnumStartSink(name, response, callback, resuri, status, tag, pri) {
|
||||
if (status != 200) { callback(obj, name, null, status, tag); _EnumDoNext(1); return; }
|
||||
if (response == null || response.Header["Method"] != "EnumerateResponse" || !response.Body["EnumerationContext"]) { callback(obj, name, null, 603, tag); _EnumDoNext(1); return; }
|
||||
var enumctx = response.Body["EnumerationContext"];
|
||||
obj.wsman.ExecPull(resuri, enumctx, function (ws, resuri, response, xstatus) { _EnumContinueSink(name, response, callback, resuri, [], xstatus, tag, pri); });
|
||||
}
|
||||
|
||||
// Private method
|
||||
function _EnumContinueSink(name, response, callback, resuri, items, status, tag, pri) {
|
||||
if (status != 200) { callback(obj, name, null, status, tag); _EnumDoNext(1); return; }
|
||||
if (response == null || response.Header["Method"] != "PullResponse") { callback(obj, name, null, 604, tag); _EnumDoNext(1); return; }
|
||||
for (var i in response.Body["Items"]) {
|
||||
if (response.Body["Items"][i] instanceof Array) {
|
||||
for (var j in response.Body["Items"][i]) { items.push(response.Body["Items"][i][j]); }
|
||||
} else {
|
||||
items.push(response.Body["Items"][i]);
|
||||
}
|
||||
}
|
||||
if (response.Body["EnumerationContext"]) {
|
||||
var enumctx = response.Body["EnumerationContext"];
|
||||
obj.wsman.ExecPull(resuri, enumctx, function (ws, resuri, response, xstatus) { _EnumContinueSink(name, response, callback, resuri, items, xstatus, tag, 1); });
|
||||
} else {
|
||||
_EnumDoNext(1);
|
||||
callback(obj, name, items, status, tag);
|
||||
_up();
|
||||
}
|
||||
}
|
||||
|
||||
// Private method
|
||||
function _EnumDoNext(dec) {
|
||||
obj.ActiveEnumsCount -= dec;
|
||||
if (obj.ActiveEnumsCount >= obj.MaxActiveEnumsCount || obj.PendingEnums.length == 0) return;
|
||||
var x = obj.PendingEnums.shift();
|
||||
obj.Enum(x[0], x[1], x[2]);
|
||||
_EnumDoNext(0);
|
||||
}
|
||||
|
||||
// Perform a batch of WSMAN "ENUM" operations.
|
||||
obj.BatchEnum = function (batchname, names, callback, tag, continueOnError, pri) {
|
||||
obj.PendingBatchOperations += (names.length * 2);
|
||||
_BatchNextEnum(batchname, Clone(names), callback, tag, {}, continueOnError, pri); _up();
|
||||
}
|
||||
|
||||
// Request each enum in the batch, stopping if something does not return status 200
|
||||
function _BatchNextEnum(batchname, names, callback, tag, results, continueOnError, pri) {
|
||||
obj.PendingBatchOperations -= 2;
|
||||
var n = names.shift(), f = obj.Enum;
|
||||
if (n[0] == '*') { f = obj.Get; n = n.substring(1); } // If the name starts with a star, do a GET instead of an ENUM. This will reduce round trips.
|
||||
//console.log((f == obj.Get?'Get ':'Enum ') + n);
|
||||
// Perform a GET/ENUM action
|
||||
f(n, function (stack, name, responses, status, tag0) {
|
||||
tag0[2][name] = { response: (responses==null?null:responses.Body), responses: responses, status: status };
|
||||
if (tag0[1].length == 0 || status == 401 || (continueOnError != true && status != 200 && status != 400)) { obj.PendingBatchOperations -= (names.length * 2); _up(); callback(obj, batchname, tag0[2], status, tag); }
|
||||
else { _up(); _BatchNextEnum(batchname, names, callback, tag, tag0[2], pri); }
|
||||
}, [batchname, names, results], pri);
|
||||
_up();
|
||||
}
|
||||
|
||||
// Perform a batch of WSMAN "GET" operations.
|
||||
obj.BatchGet = function (batchname, names, callback, tag, pri) {
|
||||
_FetchNext({ name: batchname, names: names, callback: callback, current: 0, responses: {}, tag: tag, pri: pri }); _up();
|
||||
}
|
||||
|
||||
// Private method
|
||||
function _FetchNext(batch) {
|
||||
if (batch.names.length <= batch.current) {
|
||||
batch.callback(obj, batch.name, batch.responses, 200, batch.tag);
|
||||
} else {
|
||||
obj.wsman.ExecGet(obj.CompleteName(batch.names[batch.current]), function (ws, resuri, response, xstatus) { _Fetched(batch, response, xstatus); }, batch.pri);
|
||||
batch.current++;
|
||||
}
|
||||
_up();
|
||||
}
|
||||
|
||||
// Private method
|
||||
function _Fetched(batch, response, status) {
|
||||
if (response == null || status != 200) {
|
||||
batch.callback(obj, batch.name, null, status, batch.tag);
|
||||
} else {
|
||||
batch.responses[response.Header["Method"]] = response;
|
||||
_FetchNext(batch);
|
||||
}
|
||||
}
|
||||
|
||||
// Private method
|
||||
obj.CompleteName = function(name) {
|
||||
if (name.indexOf("AMT_") == 0) return obj.pfx[0] + name;
|
||||
if (name.indexOf("CIM_") == 0) return obj.pfx[1] + name;
|
||||
if (name.indexOf("IPS_") == 0) return obj.pfx[2] + name;
|
||||
}
|
||||
|
||||
obj.CompleteExecResponse = function (resp) {
|
||||
if (resp && resp != null && resp.Body && resp.Body["ReturnValue"]) resp.Body.ReturnValueStr = obj.AmtStatusToStr(resp.Body["ReturnValue"]);
|
||||
return resp;
|
||||
}
|
||||
|
||||
obj.RequestPowerStateChange = function (PowerState, callback_func) {
|
||||
obj.CIM_PowerManagementService_RequestPowerStateChange(PowerState, "<Address xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"><ResourceURI xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</ResourceURI><SelectorSet xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\"><Selector Name=\"CreationClassName\">CIM_ComputerSystem</Selector><Selector Name=\"Name\">ManagedSystem</Selector></SelectorSet></ReferenceParameters>", null, null, callback_func);
|
||||
}
|
||||
|
||||
obj.SetBootConfigRole = function (Role, callback_func) {
|
||||
obj.CIM_BootService_SetBootConfigRole("<Address xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"><ResourceURI xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootConfigSetting</ResourceURI><SelectorSet xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\"><Selector Name=\"InstanceID\">Intel(r) AMT: Boot Configuration 0</Selector></SelectorSet></ReferenceParameters>", Role, callback_func);
|
||||
}
|
||||
|
||||
// Cancel all pending queries with given status
|
||||
obj.CancelAllQueries = function (s) {
|
||||
obj.wsman.CancelAllQueries(s);
|
||||
}
|
||||
|
||||
// Auto generated methods
|
||||
obj.AMT_AgentPresenceWatchdog_RegisterAgent = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "RegisterAgent", {}, callback_func); }
|
||||
obj.AMT_AgentPresenceWatchdog_AssertPresence = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertPresence", { "SequenceNumber": SequenceNumber }, callback_func); }
|
||||
obj.AMT_AgentPresenceWatchdog_AssertShutdown = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertShutdown", { "SequenceNumber": SequenceNumber }, callback_func); }
|
||||
obj.AMT_AgentPresenceWatchdog_AddAction = function (OldState, NewState, EventOnTransition, ActionSd, ActionEac, callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "AddAction", { "OldState": OldState, "NewState": NewState, "EventOnTransition": EventOnTransition, "ActionSd": ActionSd, "ActionEac": ActionEac }, callback_func, tag, pri, selectors); }
|
||||
obj.AMT_AgentPresenceWatchdog_DeleteAllActions = function (callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "DeleteAllActions", {}, callback_func, tag, pri, selectors); }
|
||||
obj.AMT_AgentPresenceWatchdogAction_GetActionEac = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdogAction", "GetActionEac", {}, callback_func); }
|
||||
obj.AMT_AgentPresenceWatchdogVA_RegisterAgent = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "RegisterAgent", {}, callback_func); }
|
||||
obj.AMT_AgentPresenceWatchdogVA_AssertPresence = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AssertPresence", { "SequenceNumber": SequenceNumber }, callback_func); }
|
||||
obj.AMT_AgentPresenceWatchdogVA_AssertShutdown = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AssertShutdown", { "SequenceNumber": SequenceNumber }, callback_func); }
|
||||
obj.AMT_AgentPresenceWatchdogVA_AddAction = function (OldState, NewState, EventOnTransition, ActionSd, ActionEac, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AddAction", { "OldState": OldState, "NewState": NewState, "EventOnTransition": EventOnTransition, "ActionSd": ActionSd, "ActionEac": ActionEac }, callback_func); }
|
||||
obj.AMT_AgentPresenceWatchdogVA_DeleteAllActions = function (_method_dummy, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "DeleteAllActions", { "_method_dummy": _method_dummy }, callback_func); }
|
||||
obj.AMT_AuditLog_ClearLog = function (callback_func) { obj.Exec("AMT_AuditLog", "ClearLog", {}, callback_func); }
|
||||
obj.AMT_AuditLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_AuditLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.AMT_AuditLog_ReadRecords = function (StartIndex, callback_func, tag) { obj.Exec("AMT_AuditLog", "ReadRecords", { "StartIndex": StartIndex }, callback_func, tag); }
|
||||
obj.AMT_AuditLog_SetAuditLock = function (LockTimeoutInSeconds, Flag, Handle, callback_func) { obj.Exec("AMT_AuditLog", "SetAuditLock", { "LockTimeoutInSeconds": LockTimeoutInSeconds, "Flag": Flag, "Handle": Handle }, callback_func); }
|
||||
obj.AMT_AuditLog_ExportAuditLogSignature = function (SigningMechanism, callback_func) { obj.Exec("AMT_AuditLog", "ExportAuditLogSignature", { "SigningMechanism": SigningMechanism }, callback_func); }
|
||||
obj.AMT_AuditLog_SetSigningKeyMaterial = function (SigningMechanismType, SigningKey, LengthOfCertificates, Certificates, callback_func) { obj.Exec("AMT_AuditLog", "SetSigningKeyMaterial", { "SigningMechanismType": SigningMechanismType, "SigningKey": SigningKey, "LengthOfCertificates": LengthOfCertificates, "Certificates": Certificates }, callback_func); }
|
||||
obj.AMT_AuditPolicyRule_SetAuditPolicy = function (Enable, AuditedAppID, EventID, PolicyType, callback_func) { obj.Exec("AMT_AuditPolicyRule", "SetAuditPolicy", { "Enable": Enable, "AuditedAppID": AuditedAppID, "EventID": EventID, "PolicyType": PolicyType }, callback_func); }
|
||||
obj.AMT_AuditPolicyRule_SetAuditPolicyBulk = function (Enable, AuditedAppID, EventID, PolicyType, callback_func) { obj.Exec("AMT_AuditPolicyRule", "SetAuditPolicyBulk", { "Enable": Enable, "AuditedAppID": AuditedAppID, "EventID": EventID, "PolicyType": PolicyType }, callback_func); }
|
||||
obj.AMT_AuthorizationService_AddUserAclEntryEx = function (DigestUsername, DigestPassword, KerberosUserSid, AccessPermission, Realms, callback_func) { obj.Exec("AMT_AuthorizationService", "AddUserAclEntryEx", { "DigestUsername": DigestUsername, "DigestPassword": DigestPassword, "KerberosUserSid": KerberosUserSid, "AccessPermission": AccessPermission, "Realms": Realms }, callback_func); }
|
||||
obj.AMT_AuthorizationService_EnumerateUserAclEntries = function (StartIndex, callback_func) { obj.Exec("AMT_AuthorizationService", "EnumerateUserAclEntries", { "StartIndex": StartIndex }, callback_func); }
|
||||
obj.AMT_AuthorizationService_GetUserAclEntryEx = function (Handle, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "GetUserAclEntryEx", { "Handle": Handle }, callback_func, tag); }
|
||||
obj.AMT_AuthorizationService_UpdateUserAclEntryEx = function (Handle, DigestUsername, DigestPassword, KerberosUserSid, AccessPermission, Realms, callback_func) { obj.Exec("AMT_AuthorizationService", "UpdateUserAclEntryEx", { "Handle": Handle, "DigestUsername": DigestUsername, "DigestPassword": DigestPassword, "KerberosUserSid": KerberosUserSid, "AccessPermission": AccessPermission, "Realms": Realms }, callback_func); }
|
||||
obj.AMT_AuthorizationService_RemoveUserAclEntry = function (Handle, callback_func) { obj.Exec("AMT_AuthorizationService", "RemoveUserAclEntry", { "Handle": Handle }, callback_func); }
|
||||
obj.AMT_AuthorizationService_SetAdminAclEntryEx = function (Username, DigestPassword, callback_func) { obj.Exec("AMT_AuthorizationService", "SetAdminAclEntryEx", { "Username": Username, "DigestPassword": DigestPassword }, callback_func); }
|
||||
obj.AMT_AuthorizationService_GetAdminAclEntry = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminAclEntry", {}, callback_func); }
|
||||
obj.AMT_AuthorizationService_GetAdminAclEntryStatus = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminAclEntryStatus", {}, callback_func); }
|
||||
obj.AMT_AuthorizationService_GetAdminNetAclEntryStatus = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminNetAclEntryStatus", {}, callback_func); }
|
||||
obj.AMT_AuthorizationService_SetAclEnabledState = function (Handle, Enabled, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "SetAclEnabledState", { "Handle": Handle, "Enabled": Enabled }, callback_func, tag); }
|
||||
obj.AMT_AuthorizationService_GetAclEnabledState = function (Handle, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "GetAclEnabledState", { "Handle": Handle }, callback_func, tag); }
|
||||
obj.AMT_EndpointAccessControlService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.AMT_EndpointAccessControlService_GetPosture = function (PostureType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetPosture", { "PostureType": PostureType }, callback_func); }
|
||||
obj.AMT_EndpointAccessControlService_GetPostureHash = function (PostureType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetPostureHash", { "PostureType": PostureType }, callback_func); }
|
||||
obj.AMT_EndpointAccessControlService_UpdatePostureState = function (UpdateType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "UpdatePostureState", { "UpdateType": UpdateType }, callback_func); }
|
||||
obj.AMT_EndpointAccessControlService_GetEacOptions = function (callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetEacOptions", {}, callback_func); }
|
||||
obj.AMT_EndpointAccessControlService_SetEacOptions = function (EacVendors, PostureHashAlgorithm, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "SetEacOptions", { "EacVendors": EacVendors, "PostureHashAlgorithm": PostureHashAlgorithm }, callback_func); }
|
||||
obj.AMT_EnvironmentDetectionSettingData_SetSystemDefensePolicy = function (Policy, callback_func) { obj.Exec("AMT_EnvironmentDetectionSettingData", "SetSystemDefensePolicy", { "Policy": Policy }, callback_func); }
|
||||
obj.AMT_EnvironmentDetectionSettingData_EnableVpnRouting = function (Enable, callback_func) { obj.Exec("AMT_EnvironmentDetectionSettingData", "EnableVpnRouting", { "Enable": Enable }, callback_func); }
|
||||
obj.AMT_EthernetPortSettings_SetLinkPreference = function (LinkPreference, Timeout, callback_func) { obj.Exec("AMT_EthernetPortSettings", "SetLinkPreference", { "LinkPreference": LinkPreference, "Timeout": Timeout }, callback_func); }
|
||||
obj.AMT_HeuristicPacketFilterStatistics_ResetSelectedStats = function (SelectedStatistics, callback_func) { obj.Exec("AMT_HeuristicPacketFilterStatistics", "ResetSelectedStats", { "SelectedStatistics": SelectedStatistics }, callback_func); }
|
||||
obj.AMT_KerberosSettingData_GetCredentialCacheState = function (callback_func) { obj.Exec("AMT_KerberosSettingData", "GetCredentialCacheState", {}, callback_func); }
|
||||
obj.AMT_KerberosSettingData_SetCredentialCacheState = function (Enable, callback_func) { obj.Exec("AMT_KerberosSettingData", "SetCredentialCacheState", { "Enable": Enable }, callback_func); }
|
||||
obj.AMT_MessageLog_CancelIteration = function (IterationIdentifier, callback_func) { obj.Exec("AMT_MessageLog", "CancelIteration", { "IterationIdentifier": IterationIdentifier }, callback_func); }
|
||||
obj.AMT_MessageLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_MessageLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.AMT_MessageLog_ClearLog = function (callback_func) { obj.Exec("AMT_MessageLog", "ClearLog", { }, callback_func); }
|
||||
obj.AMT_MessageLog_GetRecords = function (IterationIdentifier, MaxReadRecords, callback_func, tag) { obj.Exec("AMT_MessageLog", "GetRecords", { "IterationIdentifier": IterationIdentifier, "MaxReadRecords": MaxReadRecords }, callback_func, tag); }
|
||||
obj.AMT_MessageLog_GetRecord = function (IterationIdentifier, PositionToNext, callback_func) { obj.Exec("AMT_MessageLog", "GetRecord", { "IterationIdentifier": IterationIdentifier, "PositionToNext": PositionToNext }, callback_func); }
|
||||
obj.AMT_MessageLog_PositionAtRecord = function (IterationIdentifier, MoveAbsolute, RecordNumber, callback_func) { obj.Exec("AMT_MessageLog", "PositionAtRecord", { "IterationIdentifier": IterationIdentifier, "MoveAbsolute": MoveAbsolute, "RecordNumber": RecordNumber }, callback_func); }
|
||||
obj.AMT_MessageLog_PositionToFirstRecord = function (callback_func, tag) { obj.Exec("AMT_MessageLog", "PositionToFirstRecord", {}, callback_func, tag); }
|
||||
obj.AMT_MessageLog_FreezeLog = function (Freeze, callback_func) { obj.Exec("AMT_MessageLog", "FreezeLog", { "Freeze": Freeze }, callback_func); }
|
||||
obj.AMT_PublicKeyManagementService_AddCRL = function (Url, SerialNumbers, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddCRL", { "Url": Url, "SerialNumbers": SerialNumbers }, callback_func); }
|
||||
obj.AMT_PublicKeyManagementService_ResetCRLList = function (_method_dummy, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "ResetCRLList", { "_method_dummy": _method_dummy }, callback_func); }
|
||||
obj.AMT_PublicKeyManagementService_AddCertificate = function (CertificateBlob, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddCertificate", { "CertificateBlob": CertificateBlob }, callback_func); }
|
||||
obj.AMT_PublicKeyManagementService_AddTrustedRootCertificate = function (CertificateBlob, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddTrustedRootCertificate", { "CertificateBlob": CertificateBlob }, callback_func); }
|
||||
obj.AMT_PublicKeyManagementService_AddKey = function (KeyBlob, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddKey", { "KeyBlob": KeyBlob }, callback_func); }
|
||||
obj.AMT_PublicKeyManagementService_GeneratePKCS10Request = function (KeyPair, DNName, Usage, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GeneratePKCS10Request", { "KeyPair": KeyPair, "DNName": DNName, "Usage": Usage }, callback_func); }
|
||||
obj.AMT_PublicKeyManagementService_GeneratePKCS10RequestEx = function (KeyPair, SigningAlgorithm, NullSignedCertificateRequest, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GeneratePKCS10RequestEx", { "KeyPair": KeyPair, "SigningAlgorithm": SigningAlgorithm, "NullSignedCertificateRequest": NullSignedCertificateRequest }, callback_func); }
|
||||
obj.AMT_PublicKeyManagementService_GenerateKeyPair = function (KeyAlgorithm, KeyLength, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GenerateKeyPair", { "KeyAlgorithm": KeyAlgorithm, "KeyLength": KeyLength }, callback_func); }
|
||||
obj.AMT_RedirectionService_RequestStateChange = function (RequestedState, callback_func) { obj.Exec("AMT_RedirectionService", "RequestStateChange", { "RequestedState": RequestedState }, callback_func); }
|
||||
obj.AMT_RedirectionService_TerminateSession = function (SessionType, callback_func) { obj.Exec("AMT_RedirectionService", "TerminateSession", { "SessionType": SessionType }, callback_func); }
|
||||
obj.AMT_RemoteAccessService_AddMpServer = function (AccessInfo, InfoFormat, Port, AuthMethod, Certificate, Username, Password, CN, callback_func) { obj.Exec("AMT_RemoteAccessService", "AddMpServer", { "AccessInfo": AccessInfo, "InfoFormat": InfoFormat, "Port": Port, "AuthMethod": AuthMethod, "Certificate": Certificate, "Username": Username, "Password": Password, "CN": CN }, callback_func); }
|
||||
obj.AMT_RemoteAccessService_AddRemoteAccessPolicyRule = function (Trigger, TunnelLifeTime, ExtendedData, MpServer, callback_func) { obj.Exec("AMT_RemoteAccessService", "AddRemoteAccessPolicyRule", { "Trigger": Trigger, "TunnelLifeTime": TunnelLifeTime, "ExtendedData": ExtendedData, "MpServer": MpServer }, callback_func); }
|
||||
obj.AMT_RemoteAccessService_CloseRemoteAccessConnection = function (_method_dummy, callback_func) { obj.Exec("AMT_RemoteAccessService", "CloseRemoteAccessConnection", { "_method_dummy": _method_dummy }, callback_func); }
|
||||
obj.AMT_SetupAndConfigurationService_CommitChanges = function (_method_dummy, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "CommitChanges", { "_method_dummy": _method_dummy }, callback_func); }
|
||||
obj.AMT_SetupAndConfigurationService_Unprovision = function (ProvisioningMode, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "Unprovision", { "ProvisioningMode": ProvisioningMode }, callback_func); }
|
||||
obj.AMT_SetupAndConfigurationService_PartialUnprovision = function (_method_dummy, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "PartialUnprovision", { "_method_dummy": _method_dummy }, callback_func); }
|
||||
obj.AMT_SetupAndConfigurationService_ResetFlashWearOutProtection = function (_method_dummy, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "ResetFlashWearOutProtection", { "_method_dummy": _method_dummy }, callback_func); }
|
||||
obj.AMT_SetupAndConfigurationService_ExtendProvisioningPeriod = function (Duration, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "ExtendProvisioningPeriod", { "Duration": Duration }, callback_func); }
|
||||
obj.AMT_SetupAndConfigurationService_SetMEBxPassword = function (Password, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "SetMEBxPassword", { "Password": Password }, callback_func); }
|
||||
obj.AMT_SetupAndConfigurationService_SetTLSPSK = function (PID, PPS, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "SetTLSPSK", { "PID": PID, "PPS": PPS }, callback_func); }
|
||||
obj.AMT_SetupAndConfigurationService_GetProvisioningAuditRecord = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetProvisioningAuditRecord", {}, callback_func); }
|
||||
obj.AMT_SetupAndConfigurationService_GetUuid = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetUuid", {}, callback_func); }
|
||||
obj.AMT_SetupAndConfigurationService_GetUnprovisionBlockingComponents = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetUnprovisionBlockingComponents", {}, callback_func); }
|
||||
obj.AMT_SetupAndConfigurationService_GetProvisioningAuditRecordV2 = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetProvisioningAuditRecordV2", {}, callback_func); }
|
||||
obj.AMT_SystemDefensePolicy_GetTimeout = function (callback_func) { obj.Exec("AMT_SystemDefensePolicy", "GetTimeout", {}, callback_func); }
|
||||
obj.AMT_SystemDefensePolicy_SetTimeout = function (Timeout, callback_func) { obj.Exec("AMT_SystemDefensePolicy", "SetTimeout", { "Timeout": Timeout }, callback_func); }
|
||||
obj.AMT_SystemDefensePolicy_UpdateStatistics = function (NetworkInterface, ResetOnRead, callback_func, tag, pri, selectors) { obj.Exec("AMT_SystemDefensePolicy", "UpdateStatistics", { "NetworkInterface": NetworkInterface, "ResetOnRead": ResetOnRead }, callback_func, tag, pri, selectors); }
|
||||
obj.AMT_SystemPowerScheme_SetPowerScheme = function (callback_func, schemeInstanceId, tag) { obj.Exec("AMT_SystemPowerScheme", "SetPowerScheme", {}, callback_func, tag, 0, { "InstanceID": schemeInstanceId }); }
|
||||
obj.AMT_TimeSynchronizationService_GetLowAccuracyTimeSynch = function (callback_func, tag) { obj.Exec("AMT_TimeSynchronizationService", "GetLowAccuracyTimeSynch", {}, callback_func, tag); }
|
||||
obj.AMT_TimeSynchronizationService_SetHighAccuracyTimeSynch = function (Ta0, Tm1, Tm2, callback_func, tag) { obj.Exec("AMT_TimeSynchronizationService", "SetHighAccuracyTimeSynch", { "Ta0": Ta0, "Tm1": Tm1, "Tm2": Tm2 }, callback_func, tag); }
|
||||
obj.AMT_UserInitiatedConnectionService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_UserInitiatedConnectionService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.AMT_WebUIService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_WebUIService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.AMT_WiFiPortConfigurationService_AddWiFiSettings = function (WiFiEndpoint, WiFiEndpointSettingsInput, IEEE8021xSettingsInput, ClientCredential, CACredential, callback_func) { obj.ExecWithXml("AMT_WiFiPortConfigurationService", "AddWiFiSettings", { "WiFiEndpoint": WiFiEndpoint, "WiFiEndpointSettingsInput": WiFiEndpointSettingsInput, "IEEE8021xSettingsInput": IEEE8021xSettingsInput, "ClientCredential": ClientCredential, "CACredential": CACredential }, callback_func); }
|
||||
obj.AMT_WiFiPortConfigurationService_UpdateWiFiSettings = function (WiFiEndpointSettings, WiFiEndpointSettingsInput, IEEE8021xSettingsInput, ClientCredential, CACredential, callback_func) { obj.ExecWithXml("AMT_WiFiPortConfigurationService", "UpdateWiFiSettings", { "WiFiEndpointSettings": WiFiEndpointSettings, "WiFiEndpointSettingsInput": WiFiEndpointSettingsInput, "IEEE8021xSettingsInput": IEEE8021xSettingsInput, "ClientCredential": ClientCredential, "CACredential": CACredential }, callback_func); }
|
||||
obj.AMT_WiFiPortConfigurationService_DeleteAllITProfiles = function (_method_dummy, callback_func) { obj.Exec("AMT_WiFiPortConfigurationService", "DeleteAllITProfiles", { "_method_dummy": _method_dummy }, callback_func); }
|
||||
obj.AMT_WiFiPortConfigurationService_DeleteAllUserProfiles = function (_method_dummy, callback_func) { obj.Exec("AMT_WiFiPortConfigurationService", "DeleteAllUserProfiles", { "_method_dummy": _method_dummy }, callback_func); }
|
||||
obj.CIM_Account_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Account", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.CIM_AccountManagementService_CreateAccount = function (System, AccountTemplate, callback_func) { obj.Exec("CIM_AccountManagementService", "CreateAccount", { "System": System, "AccountTemplate": AccountTemplate }, callback_func); }
|
||||
obj.CIM_BootConfigSetting_ChangeBootOrder = function (Source, callback_func) { obj.Exec("CIM_BootConfigSetting", "ChangeBootOrder", { "Source": Source }, callback_func); }
|
||||
obj.CIM_BootService_SetBootConfigRole = function (BootConfigSetting, Role, callback_func) { obj.Exec("CIM_BootService", "SetBootConfigRole", { "BootConfigSetting": BootConfigSetting, "Role": Role }, callback_func, 0, 1); }
|
||||
obj.CIM_Card_ConnectorPower = function (Connector, PoweredOn, callback_func) { obj.Exec("CIM_Card", "ConnectorPower", { "Connector": Connector, "PoweredOn": PoweredOn }, callback_func); }
|
||||
obj.CIM_Card_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_Card", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
|
||||
obj.CIM_Chassis_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_Chassis", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
|
||||
obj.CIM_Fan_SetSpeed = function (DesiredSpeed, callback_func) { obj.Exec("CIM_Fan", "SetSpeed", { "DesiredSpeed": DesiredSpeed }, callback_func); }
|
||||
obj.CIM_KVMRedirectionSAP_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_KVMRedirectionSAP", "RequestStateChange", { "RequestedState": RequestedState/*, "TimeoutPeriod": TimeoutPeriod */}, callback_func); }
|
||||
obj.CIM_MediaAccessDevice_LockMedia = function (Lock, callback_func) { obj.Exec("CIM_MediaAccessDevice", "LockMedia", { "Lock": Lock }, callback_func); }
|
||||
obj.CIM_MediaAccessDevice_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_MediaAccessDevice", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||||
obj.CIM_MediaAccessDevice_Reset = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "Reset", {}, callback_func); }
|
||||
obj.CIM_MediaAccessDevice_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_MediaAccessDevice", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||||
obj.CIM_MediaAccessDevice_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_MediaAccessDevice", "OnlineDevice", { "Online": Online }, callback_func); }
|
||||
obj.CIM_MediaAccessDevice_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_MediaAccessDevice", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||||
obj.CIM_MediaAccessDevice_SaveProperties = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "SaveProperties", {}, callback_func); }
|
||||
obj.CIM_MediaAccessDevice_RestoreProperties = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "RestoreProperties", {}, callback_func); }
|
||||
obj.CIM_MediaAccessDevice_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_MediaAccessDevice", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.CIM_PhysicalFrame_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_PhysicalFrame", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
|
||||
obj.CIM_PhysicalPackage_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_PhysicalPackage", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
|
||||
obj.CIM_PowerManagementService_RequestPowerStateChange = function (PowerState, ManagedElement, Time, TimeoutPeriod, callback_func) { obj.Exec("CIM_PowerManagementService", "RequestPowerStateChange", { "PowerState": PowerState, "ManagedElement": ManagedElement, "Time": Time, "TimeoutPeriod": TimeoutPeriod }, callback_func, 0, 1); }
|
||||
obj.CIM_PowerSupply_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_PowerSupply", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||||
obj.CIM_PowerSupply_Reset = function (callback_func) { obj.Exec("CIM_PowerSupply", "Reset", {}, callback_func); }
|
||||
obj.CIM_PowerSupply_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_PowerSupply", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||||
obj.CIM_PowerSupply_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_PowerSupply", "OnlineDevice", { "Online": Online }, callback_func); }
|
||||
obj.CIM_PowerSupply_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_PowerSupply", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||||
obj.CIM_PowerSupply_SaveProperties = function (callback_func) { obj.Exec("CIM_PowerSupply", "SaveProperties", {}, callback_func); }
|
||||
obj.CIM_PowerSupply_RestoreProperties = function (callback_func) { obj.Exec("CIM_PowerSupply", "RestoreProperties", {}, callback_func); }
|
||||
obj.CIM_PowerSupply_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_PowerSupply", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.CIM_Processor_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Processor", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||||
obj.CIM_Processor_Reset = function (callback_func) { obj.Exec("CIM_Processor", "Reset", {}, callback_func); }
|
||||
obj.CIM_Processor_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Processor", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||||
obj.CIM_Processor_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Processor", "OnlineDevice", { "Online": Online }, callback_func); }
|
||||
obj.CIM_Processor_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Processor", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||||
obj.CIM_Processor_SaveProperties = function (callback_func) { obj.Exec("CIM_Processor", "SaveProperties", {}, callback_func); }
|
||||
obj.CIM_Processor_RestoreProperties = function (callback_func) { obj.Exec("CIM_Processor", "RestoreProperties", {}, callback_func); }
|
||||
obj.CIM_Processor_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Processor", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.CIM_RecordLog_ClearLog = function (callback_func) { obj.Exec("CIM_RecordLog", "ClearLog", {}, callback_func); }
|
||||
obj.CIM_RecordLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_RecordLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.CIM_RedirectionService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_RedirectionService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.CIM_Sensor_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Sensor", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||||
obj.CIM_Sensor_Reset = function (callback_func) { obj.Exec("CIM_Sensor", "Reset", {}, callback_func); }
|
||||
obj.CIM_Sensor_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Sensor", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||||
obj.CIM_Sensor_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Sensor", "OnlineDevice", { "Online": Online }, callback_func); }
|
||||
obj.CIM_Sensor_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Sensor", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||||
obj.CIM_Sensor_SaveProperties = function (callback_func) { obj.Exec("CIM_Sensor", "SaveProperties", {}, callback_func); }
|
||||
obj.CIM_Sensor_RestoreProperties = function (callback_func) { obj.Exec("CIM_Sensor", "RestoreProperties", {}, callback_func); }
|
||||
obj.CIM_Sensor_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Sensor", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.CIM_StatisticalData_ResetSelectedStats = function (SelectedStatistics, callback_func) { obj.Exec("CIM_StatisticalData", "ResetSelectedStats", { "SelectedStatistics": SelectedStatistics }, callback_func); }
|
||||
obj.CIM_Watchdog_KeepAlive = function (callback_func) { obj.Exec("CIM_Watchdog", "KeepAlive", {}, callback_func); }
|
||||
obj.CIM_Watchdog_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Watchdog", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||||
obj.CIM_Watchdog_Reset = function (callback_func) { obj.Exec("CIM_Watchdog", "Reset", {}, callback_func); }
|
||||
obj.CIM_Watchdog_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Watchdog", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||||
obj.CIM_Watchdog_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Watchdog", "OnlineDevice", { "Online": Online }, callback_func); }
|
||||
obj.CIM_Watchdog_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Watchdog", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||||
obj.CIM_Watchdog_SaveProperties = function (callback_func) { obj.Exec("CIM_Watchdog", "SaveProperties", {}, callback_func); }
|
||||
obj.CIM_Watchdog_RestoreProperties = function (callback_func) { obj.Exec("CIM_Watchdog", "RestoreProperties", {}, callback_func); }
|
||||
obj.CIM_Watchdog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Watchdog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.CIM_WiFiPort_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_WiFiPort", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||||
obj.CIM_WiFiPort_Reset = function (callback_func) { obj.Exec("CIM_WiFiPort", "Reset", {}, callback_func); }
|
||||
obj.CIM_WiFiPort_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_WiFiPort", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||||
obj.CIM_WiFiPort_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_WiFiPort", "OnlineDevice", { "Online": Online }, callback_func); }
|
||||
obj.CIM_WiFiPort_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_WiFiPort", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||||
obj.CIM_WiFiPort_SaveProperties = function (callback_func) { obj.Exec("CIM_WiFiPort", "SaveProperties", {}, callback_func); }
|
||||
obj.CIM_WiFiPort_RestoreProperties = function (callback_func) { obj.Exec("CIM_WiFiPort", "RestoreProperties", {}, callback_func); }
|
||||
obj.CIM_WiFiPort_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_WiFiPort", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.IPS_HostBasedSetupService_Setup = function (NetAdminPassEncryptionType, NetworkAdminPassword, McNonce, Certificate, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "Setup", { "NetAdminPassEncryptionType": NetAdminPassEncryptionType, "NetworkAdminPassword": NetworkAdminPassword, "McNonce": McNonce, "Certificate": Certificate, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
|
||||
obj.IPS_HostBasedSetupService_AddNextCertInChain = function (NextCertificate, IsLeafCertificate, IsRootCertificate, callback_func) { obj.Exec("IPS_HostBasedSetupService", "AddNextCertInChain", { "NextCertificate": NextCertificate, "IsLeafCertificate": IsLeafCertificate, "IsRootCertificate": IsRootCertificate }, callback_func); }
|
||||
obj.IPS_HostBasedSetupService_AdminSetup = function (NetAdminPassEncryptionType, NetworkAdminPassword, McNonce, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "AdminSetup", { "NetAdminPassEncryptionType": NetAdminPassEncryptionType, "NetworkAdminPassword": NetworkAdminPassword, "McNonce": McNonce, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
|
||||
obj.IPS_HostBasedSetupService_UpgradeClientToAdmin = function (McNonce, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "UpgradeClientToAdmin", { "McNonce": McNonce, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
|
||||
obj.IPS_HostBasedSetupService_DisableClientControlMode = function (_method_dummy, callback_func) { obj.Exec("IPS_HostBasedSetupService", "DisableClientControlMode", { "_method_dummy": _method_dummy }, callback_func); }
|
||||
obj.IPS_KVMRedirectionSettingData_TerminateSession = function (callback_func) { obj.Exec("IPS_KVMRedirectionSettingData", "TerminateSession", {}, callback_func); }
|
||||
obj.IPS_OptInService_StartOptIn = function (callback_func) { obj.Exec("IPS_OptInService", "StartOptIn", {}, callback_func); }
|
||||
obj.IPS_OptInService_CancelOptIn = function (callback_func) { obj.Exec("IPS_OptInService", "CancelOptIn", {}, callback_func); }
|
||||
obj.IPS_OptInService_SendOptInCode = function (OptInCode, callback_func) { obj.Exec("IPS_OptInService", "SendOptInCode", { "OptInCode": OptInCode }, callback_func); }
|
||||
obj.IPS_OptInService_StartService = function (callback_func) { obj.Exec("IPS_OptInService", "StartService", {}, callback_func); }
|
||||
obj.IPS_OptInService_StopService = function (callback_func) { obj.Exec("IPS_OptInService", "StopService", {}, callback_func); }
|
||||
obj.IPS_OptInService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_OptInService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.IPS_ProvisioningRecordLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_ProvisioningRecordLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
obj.IPS_ProvisioningRecordLog_ClearLog = function (_method_dummy, callback_func) { obj.Exec("IPS_ProvisioningRecordLog", "ClearLog", { "_method_dummy": _method_dummy }, callback_func); }
|
||||
obj.IPS_SecIOService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_SecIOService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||||
|
||||
obj.AmtStatusToStr = function (code) { if (obj.AmtStatusCodes[code]) return obj.AmtStatusCodes[code]; else return "UNKNOWN_ERROR" }
|
||||
obj.AmtStatusCodes = {
|
||||
0x0000: "SUCCESS",
|
||||
0x0001: "INTERNAL_ERROR",
|
||||
0x0002: "NOT_READY",
|
||||
0x0003: "INVALID_PT_MODE",
|
||||
0x0004: "INVALID_MESSAGE_LENGTH",
|
||||
0x0005: "TABLE_FINGERPRINT_NOT_AVAILABLE",
|
||||
0x0006: "INTEGRITY_CHECK_FAILED",
|
||||
0x0007: "UNSUPPORTED_ISVS_VERSION",
|
||||
0x0008: "APPLICATION_NOT_REGISTERED",
|
||||
0x0009: "INVALID_REGISTRATION_DATA",
|
||||
0x000A: "APPLICATION_DOES_NOT_EXIST",
|
||||
0x000B: "NOT_ENOUGH_STORAGE",
|
||||
0x000C: "INVALID_NAME",
|
||||
0x000D: "BLOCK_DOES_NOT_EXIST",
|
||||
0x000E: "INVALID_BYTE_OFFSET",
|
||||
0x000F: "INVALID_BYTE_COUNT",
|
||||
0x0010: "NOT_PERMITTED",
|
||||
0x0011: "NOT_OWNER",
|
||||
0x0012: "BLOCK_LOCKED_BY_OTHER",
|
||||
0x0013: "BLOCK_NOT_LOCKED",
|
||||
0x0014: "INVALID_GROUP_PERMISSIONS",
|
||||
0x0015: "GROUP_DOES_NOT_EXIST",
|
||||
0x0016: "INVALID_MEMBER_COUNT",
|
||||
0x0017: "MAX_LIMIT_REACHED",
|
||||
0x0018: "INVALID_AUTH_TYPE",
|
||||
0x0019: "AUTHENTICATION_FAILED",
|
||||
0x001A: "INVALID_DHCP_MODE",
|
||||
0x001B: "INVALID_IP_ADDRESS",
|
||||
0x001C: "INVALID_DOMAIN_NAME",
|
||||
0x001D: "UNSUPPORTED_VERSION",
|
||||
0x001E: "REQUEST_UNEXPECTED",
|
||||
0x001F: "INVALID_TABLE_TYPE",
|
||||
0x0020: "INVALID_PROVISIONING_STATE",
|
||||
0x0021: "UNSUPPORTED_OBJECT",
|
||||
0x0022: "INVALID_TIME",
|
||||
0x0023: "INVALID_INDEX",
|
||||
0x0024: "INVALID_PARAMETER",
|
||||
0x0025: "INVALID_NETMASK",
|
||||
0x0026: "FLASH_WRITE_LIMIT_EXCEEDED",
|
||||
0x0027: "INVALID_IMAGE_LENGTH",
|
||||
0x0028: "INVALID_IMAGE_SIGNATURE",
|
||||
0x0029: "PROPOSE_ANOTHER_VERSION",
|
||||
0x002A: "INVALID_PID_FORMAT",
|
||||
0x002B: "INVALID_PPS_FORMAT",
|
||||
0x002C: "BIST_COMMAND_BLOCKED",
|
||||
0x002D: "CONNECTION_FAILED",
|
||||
0x002E: "CONNECTION_TOO_MANY",
|
||||
0x002F: "RNG_GENERATION_IN_PROGRESS",
|
||||
0x0030: "RNG_NOT_READY",
|
||||
0x0031: "CERTIFICATE_NOT_READY",
|
||||
0x0400: "DISABLED_BY_POLICY",
|
||||
0x0800: "NETWORK_IF_ERROR_BASE",
|
||||
0x0801: "UNSUPPORTED_OEM_NUMBER",
|
||||
0x0802: "UNSUPPORTED_BOOT_OPTION",
|
||||
0x0803: "INVALID_COMMAND",
|
||||
0x0804: "INVALID_SPECIAL_COMMAND",
|
||||
0x0805: "INVALID_HANDLE",
|
||||
0x0806: "INVALID_PASSWORD",
|
||||
0x0807: "INVALID_REALM",
|
||||
0x0808: "STORAGE_ACL_ENTRY_IN_USE",
|
||||
0x0809: "DATA_MISSING",
|
||||
0x080A: "DUPLICATE",
|
||||
0x080B: "EVENTLOG_FROZEN",
|
||||
0x080C: "PKI_MISSING_KEYS",
|
||||
0x080D: "PKI_GENERATING_KEYS",
|
||||
0x080E: "INVALID_KEY",
|
||||
0x080F: "INVALID_CERT",
|
||||
0x0810: "CERT_KEY_NOT_MATCH",
|
||||
0x0811: "MAX_KERB_DOMAIN_REACHED",
|
||||
0x0812: "UNSUPPORTED",
|
||||
0x0813: "INVALID_PRIORITY",
|
||||
0x0814: "NOT_FOUND",
|
||||
0x0815: "INVALID_CREDENTIALS",
|
||||
0x0816: "INVALID_PASSPHRASE",
|
||||
0x0818: "NO_ASSOCIATION",
|
||||
0x081B: "AUDIT_FAIL",
|
||||
0x081C: "BLOCKING_COMPONENT",
|
||||
0x0821: "USER_CONSENT_REQUIRED",
|
||||
0x1000: "APP_INTERNAL_ERROR",
|
||||
0x1001: "NOT_INITIALIZED",
|
||||
0x1002: "LIB_VERSION_UNSUPPORTED",
|
||||
0x1003: "INVALID_PARAM",
|
||||
0x1004: "RESOURCES",
|
||||
0x1005: "HARDWARE_ACCESS_ERROR",
|
||||
0x1006: "REQUESTOR_NOT_REGISTERED",
|
||||
0x1007: "NETWORK_ERROR",
|
||||
0x1008: "PARAM_BUFFER_TOO_SHORT",
|
||||
0x1009: "COM_NOT_INITIALIZED_IN_THREAD",
|
||||
0x100A: "URL_REQUIRED"
|
||||
}
|
||||
|
||||
//
|
||||
// Methods used for getting the event log
|
||||
//
|
||||
|
||||
obj.GetMessageLog = function (func, tag) {
|
||||
obj.AMT_MessageLog_PositionToFirstRecord(_GetMessageLog0, [func, tag, []]);
|
||||
}
|
||||
function _GetMessageLog0(stack, name, responses, status, tag) {
|
||||
if (status != 200 || responses.Body["ReturnValue"] != '0') { tag[0](obj, null, tag[2]); return; }
|
||||
obj.AMT_MessageLog_GetRecords(responses.Body["IterationIdentifier"], 390, _GetMessageLog1, tag);
|
||||
}
|
||||
function _GetMessageLog1(stack, name, responses, status, tag) {
|
||||
if (status != 200 || responses.Body["ReturnValue"] != '0') { tag[0](obj, null, tag[2]); return; }
|
||||
var i, j, x, e, AmtMessages = tag[2], t = new Date(), TimeStamp, ra = responses.Body["RecordArray"];
|
||||
if (typeof ra === 'string') { responses.Body["RecordArray"] = [responses.Body["RecordArray"]]; }
|
||||
|
||||
for (i in ra) {
|
||||
e = null;
|
||||
try { e = window.atob(ra[i]); } catch (ex) { }
|
||||
if (e != null) {
|
||||
TimeStamp = ReadIntX(e, 0);
|
||||
if ((TimeStamp > 0) && (TimeStamp < 0xFFFFFFFF)) {
|
||||
x = { 'DeviceAddress': e.charCodeAt(4), 'EventSensorType': e.charCodeAt(5), 'EventType': e.charCodeAt(6), 'EventOffset': e.charCodeAt(7), 'EventSourceType': e.charCodeAt(8), 'EventSeverity': e.charCodeAt(9), 'SensorNumber': e.charCodeAt(10), 'Entity': e.charCodeAt(11), 'EntityInstance': e.charCodeAt(12), 'EventData': [], 'Time': new Date((TimeStamp + (t.getTimezoneOffset() * 60)) * 1000) };
|
||||
for (j = 13; j < 21; j++) { x['EventData'].push(e.charCodeAt(j)); }
|
||||
x['EntityStr'] = _SystemEntityTypes[x['Entity']];
|
||||
x['Desc'] = _GetEventDetailStr(x['EventSensorType'], x['EventOffset'], x['EventData'], x['Entity']);
|
||||
if (!x['EntityStr']) x['EntityStr'] = "Unknown";
|
||||
AmtMessages.push(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (responses.Body["NoMoreRecords"] != true) { obj.AMT_MessageLog_GetRecords(responses.Body["IterationIdentifier"], 390, _GetMessageLog1, [tag[0], AmtMessages, tag[2]]); } else { tag[0](obj, AmtMessages, tag[2]); }
|
||||
}
|
||||
|
||||
var _EventTrapSourceTypes = "Platform firmware (e.g. BIOS)|SMI handler|ISV system management software|Alert ASIC|IPMI|BIOS vendor|System board set vendor|System integrator|Third party add-in|OSV|NIC|System management card".split('|');
|
||||
var _SystemFirmwareError = "Unspecified.|No system memory is physically installed in the system.|No usable system memory, all installed memory has experienced an unrecoverable failure.|Unrecoverable hard-disk/ATAPI/IDE device failure.|Unrecoverable system-board failure.|Unrecoverable diskette subsystem failure.|Unrecoverable hard-disk controller failure.|Unrecoverable PS/2 or USB keyboard failure.|Removable boot media not found.|Unrecoverable video controller failure.|No video device detected.|Firmware (BIOS) ROM corruption detected.|CPU voltage mismatch (processors that share same supply have mismatched voltage requirements)|CPU speed matching failure".split('|');
|
||||
var _SystemFirmwareProgress = "Unspecified.|Memory initialization.|Starting hard-disk initialization and test|Secondary processor(s) initialization|User authentication|User-initiated system setup|USB resource configuration|PCI resource configuration|Option ROM initialization|Video initialization|Cache initialization|SM Bus initialization|Keyboard controller initialization|Embedded controller/management controller initialization|Docking station attachment|Enabling docking station|Docking station ejection|Disabling docking station|Calling operating system wake-up vector|Starting operating system boot process|Baseboard or motherboard initialization|reserved|Floppy initialization|Keyboard test|Pointing device test|Primary processor initialization".split('|');
|
||||
var _SystemEntityTypes = "Unspecified|Other|Unknown|Processor|Disk|Peripheral|System management module|System board|Memory module|Processor module|Power supply|Add in card|Front panel board|Back panel board|Power system board|Drive backplane|System internal expansion board|Other system board|Processor board|Power unit|Power module|Power management board|Chassis back panel board|System chassis|Sub chassis|Other chassis board|Disk drive bay|Peripheral bay|Device bay|Fan cooling|Cooling unit|Cable interconnect|Memory device|System management software|BIOS|Intel(r) ME|System bus|Group|Intel(r) ME|External environment|Battery|Processing blade|Connectivity switch|Processor/memory module|I/O module|Processor I/O module|Management controller firmware|IPMI channel|PCI bus|PCI express bus|SCSI bus|SATA/SAS bus|Processor front side bus".split('|');
|
||||
obj.RealmNames = "||Redirection|PT Administration|Hardware Asset|Remote Control|Storage|Event Manager|Storage Admin|Agent Presence Local|Agent Presence Remote|Circuit Breaker|Network Time|General Information|Firmware Update|EIT|LocalUN|Endpoint Access Control|Endpoint Access Control Admin|Event Log Reader|Audit Log|ACL Realm|||Local System".split('|');
|
||||
obj.WatchdogCurrentStates = { 1: 'Not Started', 2: 'Stopped', 4: 'Running', 8: 'Expired', 16: 'Suspended' };
|
||||
|
||||
function _GetEventDetailStr(eventSensorType, eventOffset, eventDataField, entity) {
|
||||
|
||||
if (eventSensorType == 15)
|
||||
{
|
||||
if (eventDataField[0] == 235) return "Invalid Data";
|
||||
if (eventOffset == 0) return _SystemFirmwareError[eventDataField[1]];
|
||||
return _SystemFirmwareProgress[eventDataField[1]];
|
||||
}
|
||||
|
||||
if (eventSensorType == 18 && eventDataField[0] == 170) // System watchdog event
|
||||
{
|
||||
return "Agent watchdog " + char2hex(eventDataField[4]) + char2hex(eventDataField[3]) + char2hex(eventDataField[2]) + char2hex(eventDataField[1]) + "-" + char2hex(eventDataField[6]) + char2hex(eventDataField[5]) + "-... changed to " + obj.WatchdogCurrentStates[eventDataField[7]];
|
||||
}
|
||||
|
||||
/*
|
||||
if (eventSensorType == 5 && eventOffset == 0) // System chassis
|
||||
{
|
||||
return "Case intrusion";
|
||||
}
|
||||
|
||||
if (eventSensorType == 192 && eventOffset == 0 && eventDataField[0] == 170 && eventDataField[1] == 48)
|
||||
{
|
||||
if (eventDataField[2] == 0) return "A remote Serial Over LAN session was established.";
|
||||
if (eventDataField[2] == 1) return "Remote Serial Over LAN session finished. User control was restored.";
|
||||
if (eventDataField[2] == 2) return "A remote IDE-Redirection session was established.";
|
||||
if (eventDataField[2] == 3) return "Remote IDE-Redirection session finished. User control was restored.";
|
||||
}
|
||||
|
||||
if (eventSensorType == 36)
|
||||
{
|
||||
long handle = ((long)(eventDataField[1]) << 24) + ((long)(eventDataField[2]) << 16) + ((long)(eventDataField[3]) << 8) + (long)(eventDataField[4]);
|
||||
string nic = string.Format("#{0}", eventDataField[0]);
|
||||
if (eventDataField[0] == 0xAA) nic = "wired"; // TODO: Add wireless *****
|
||||
//if (eventDataField[0] == 0xAA) nic = "wireless";
|
||||
|
||||
if (handle == 4294967293) { return string.Format("All received packet filter was matched on {0} interface.", nic); }
|
||||
if (handle == 4294967292) { return string.Format("All outbound packet filter was matched on {0} interface.", nic); }
|
||||
if (handle == 4294967290) { return string.Format("Spoofed packet filter was matched on {0} interface.", nic); }
|
||||
return string.Format("Filter {0} was matched on {1} interface.", handle, nic);
|
||||
}
|
||||
|
||||
if (eventSensorType == 192)
|
||||
{
|
||||
if (eventDataField[2] == 0) return "Security policy invoked. Some or all network traffic (TX) was stopped.";
|
||||
if (eventDataField[2] == 2) return "Security policy invoked. Some or all network traffic (RX) was stopped.";
|
||||
return "Security policy invoked.";
|
||||
}
|
||||
|
||||
if (eventSensorType == 193)
|
||||
{
|
||||
if (eventDataField[0] == 0xAA && eventDataField[1] == 0x30 && eventDataField[2] == 0x00 && eventDataField[3] == 0x00) { return "User request for remote connection."; }
|
||||
if (eventDataField[0] == 0xAA && eventDataField[1] == 0x20 && eventDataField[2] == 0x03 && eventDataField[3] == 0x01) { return "EAC error: attempt to get posture while NAC in Intel® AMT is disabled."; // eventDataField = 0xAA20030100000000 }
|
||||
if (eventDataField[0] == 0xAA && eventDataField[1] == 0x20 && eventDataField[2] == 0x04 && eventDataField[3] == 0x00) { return "Certificate revoked. "; }
|
||||
}
|
||||
*/
|
||||
|
||||
if (eventSensorType == 6) return "Authentication failed " + (eventDataField[1] + (eventDataField[2] << 8)) + " times. The system may be under attack.";
|
||||
if (eventSensorType == 30) return "No bootable media";
|
||||
if (eventSensorType == 32) return "Operating system lockup or power interrupt";
|
||||
if (eventSensorType == 35) return "System boot failure";
|
||||
if (eventSensorType == 37) return "System firmware started (at least one CPU is properly executing).";
|
||||
return "Unknown Sensor Type #" + eventSensorType;
|
||||
}
|
||||
|
||||
// ###BEGIN###{AuditLog}
|
||||
|
||||
// Useful link: https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm?turl=WordDocuments%2Fsecurityadminevents.htm
|
||||
|
||||
var _AmtAuditStringTable =
|
||||
{
|
||||
16: 'Security Admin',
|
||||
17: 'RCO',
|
||||
18: 'Redirection Manager',
|
||||
19: 'Firmware Update Manager',
|
||||
20: 'Security Audit Log',
|
||||
21: 'Network Time',
|
||||
22: 'Network Administration',
|
||||
23: 'Storage Administration',
|
||||
24: 'Event Manager',
|
||||
25: 'Circuit Breaker Manager',
|
||||
26: 'Agent Presence Manager',
|
||||
27: 'Wireless Configuration',
|
||||
28: 'EAC',
|
||||
29: 'KVM',
|
||||
30: 'User Opt-In Events',
|
||||
32: 'Screen Blanking',
|
||||
33: 'Watchdog Events',
|
||||
1600: 'Provisioning Started',
|
||||
1601: 'Provisioning Completed',
|
||||
1602: 'ACL Entry Added',
|
||||
1603: 'ACL Entry Modified',
|
||||
1604: 'ACL Entry Removed',
|
||||
1605: 'ACL Access with Invalid Credentials',
|
||||
1606: 'ACL Entry State',
|
||||
1607: 'TLS State Changed',
|
||||
1608: 'TLS Server Certificate Set',
|
||||
1609: 'TLS Server Certificate Remove',
|
||||
1610: 'TLS Trusted Root Certificate Added',
|
||||
1611: 'TLS Trusted Root Certificate Removed',
|
||||
1612: 'TLS Preshared Key Set',
|
||||
1613: 'Kerberos Settings Modified',
|
||||
1614: 'Kerberos Master Key Modified',
|
||||
1615: 'Flash Wear out Counters Reset',
|
||||
1616: 'Power Package Modified',
|
||||
1617: 'Set Realm Authentication Mode',
|
||||
1618: 'Upgrade Client to Admin Control Mode',
|
||||
1619: 'Unprovisioning Started',
|
||||
1700: 'Performed Power Up',
|
||||
1701: 'Performed Power Down',
|
||||
1702: 'Performed Power Cycle',
|
||||
1703: 'Performed Reset',
|
||||
1704: 'Set Boot Options',
|
||||
1800: 'IDER Session Opened',
|
||||
1801: 'IDER Session Closed',
|
||||
1802: 'IDER Enabled',
|
||||
1803: 'IDER Disabled',
|
||||
1804: 'SoL Session Opened',
|
||||
1805: 'SoL Session Closed',
|
||||
1806: 'SoL Enabled',
|
||||
1807: 'SoL Disabled',
|
||||
1808: 'KVM Session Started',
|
||||
1809: 'KVM Session Ended',
|
||||
1810: 'KVM Enabled',
|
||||
1811: 'KVM Disabled',
|
||||
1812: 'VNC Password Failed 3 Times',
|
||||
1900: 'Firmware Updated',
|
||||
1901: 'Firmware Update Failed',
|
||||
2000: 'Security Audit Log Cleared',
|
||||
2001: 'Security Audit Policy Modified',
|
||||
2002: 'Security Audit Log Disabled',
|
||||
2003: 'Security Audit Log Enabled',
|
||||
2004: 'Security Audit Log Exported',
|
||||
2005: 'Security Audit Log Recovered',
|
||||
2100: 'Intel® ME Time Set',
|
||||
2200: 'TCPIP Parameters Set',
|
||||
2201: 'Host Name Set',
|
||||
2202: 'Domain Name Set',
|
||||
2203: 'VLAN Parameters Set',
|
||||
2204: 'Link Policy Set',
|
||||
2205: 'IPv6 Parameters Set',
|
||||
2300: 'Global Storage Attributes Set',
|
||||
2301: 'Storage EACL Modified',
|
||||
2302: 'Storage FPACL Modified',
|
||||
2303: 'Storage Write Operation',
|
||||
2400: 'Alert Subscribed',
|
||||
2401: 'Alert Unsubscribed',
|
||||
2402: 'Event Log Cleared',
|
||||
2403: 'Event Log Frozen',
|
||||
2500: 'CB Filter Added',
|
||||
2501: 'CB Filter Removed',
|
||||
2502: 'CB Policy Added',
|
||||
2503: 'CB Policy Removed',
|
||||
2504: 'CB Default Policy Set',
|
||||
2505: 'CB Heuristics Option Set',
|
||||
2506: 'CB Heuristics State Cleared',
|
||||
2600: 'Agent Watchdog Added',
|
||||
2601: 'Agent Watchdog Removed',
|
||||
2602: 'Agent Watchdog Action Set',
|
||||
2700: 'Wireless Profile Added',
|
||||
2701: 'Wireless Profile Removed',
|
||||
2702: 'Wireless Profile Updated',
|
||||
2800: 'EAC Posture Signer SET',
|
||||
2801: 'EAC Enabled',
|
||||
2802: 'EAC Disabled',
|
||||
2803: 'EAC Posture State',
|
||||
2804: 'EAC Set Options',
|
||||
2900: 'KVM Opt-in Enabled',
|
||||
2901: 'KVM Opt-in Disabled',
|
||||
2902: 'KVM Password Changed',
|
||||
2903: 'KVM Consent Succeeded',
|
||||
2904: 'KVM Consent Failed',
|
||||
3000: 'Opt-In Policy Change',
|
||||
3001: 'Send Consent Code Event',
|
||||
3002: 'Start Opt-In Blocked Event'
|
||||
}
|
||||
|
||||
// Return human readable extended audit log data
|
||||
// TODO: Just put some of them here, but many more still need to be added, helpful link here:
|
||||
// https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm?turl=WordDocuments%2Fsecurityadminevents.htm
|
||||
obj.GetAuditLogExtendedDataStr = function (id, data) {
|
||||
if ((id == 1602 || id == 1604) && data.charCodeAt(0) == 0) { return data.substring(2, 2 + data.charCodeAt(1)); } // ACL Entry Added/Removed (Digest)
|
||||
if (id == 1603) { if (data.charCodeAt(1) == 0) { return data.substring(3); } return null; } // ACL Entry Modified
|
||||
if (id == 1605) { return ["Invalid ME access", "Invalid MEBx access"][data.charCodeAt(0)]; } // ACL Access with Invalid Credentials
|
||||
if (id == 1606) { var r = ["Disabled", "Enabled"][data.charCodeAt(0)]; if (data.charCodeAt(1) == 0) { r += ", " + data.substring(3); } return r;} // ACL Entry State
|
||||
if (id == 1607) { return "Remote " + ["NoAuth", "ServerAuth", "MutualAuth"][data.charCodeAt(0)] + ", Local " + ["NoAuth", "ServerAuth", "MutualAuth"][data.charCodeAt(1)]; } // TLS State Changed
|
||||
if (id == 1617) { return obj.RealmNames[ReadInt(data, 0)] + ", " + ["NoAuth", "Auth", "Disabled"][data.charCodeAt(4)]; } // Set Realm Authentication Mode
|
||||
if (id == 1619) { return ["BIOS", "MEBx", "Local MEI", "Local WSMAN", "Remote WSAMN"][data.charCodeAt(0)]; } // Intel AMT Unprovisioning Started
|
||||
if (id == 1900) { return "From " + ReadShort(data, 0) + "." + ReadShort(data, 2) + "." + ReadShort(data, 4) + "." + ReadShort(data, 6) + " to " + ReadShort(data, 8) + "." + ReadShort(data, 10) + "." + ReadShort(data, 12) + "." + ReadShort(data, 14); } // Firmware Updated
|
||||
if (id == 2100) { var t4 = new Date(); t4.setTime(ReadInt(data, 0) * 1000 + (new Date().getTimezoneOffset() * 60000)); return t4.toLocaleString(); } // Intel AMT Time Set
|
||||
if (id == 3000) { return "From " + ["None", "KVM", "All"][data.charCodeAt(0)] + " to " + ["None", "KVM", "All"][data.charCodeAt(1)]; } // Opt-In Policy Change
|
||||
if (id == 3001) { return ["Success", "Failed 3 times"][data.charCodeAt(0)]; } // Send Consent Code Event
|
||||
return null;
|
||||
}
|
||||
|
||||
obj.GetAuditLog = function (func) {
|
||||
obj.AMT_AuditLog_ReadRecords(1, _GetAuditLog0, [func, []]);
|
||||
}
|
||||
|
||||
function _GetAuditLog0(stack, name, responses, status, tag) {
|
||||
if (status != 200) { tag[0](obj, [], status); return; }
|
||||
var ptr, i, e, x, r = tag[1], t = new Date(), TimeStamp;
|
||||
|
||||
if (responses.Body['RecordsReturned'] > 0) {
|
||||
responses.Body['EventRecords'] = MakeToArray(responses.Body['EventRecords']);
|
||||
|
||||
for (i in responses.Body['EventRecords']) {
|
||||
e = null;
|
||||
try {
|
||||
e = window.atob(responses.Body['EventRecords'][i]);
|
||||
} catch (e) {
|
||||
console.log(e + " " + responses.Body['EventRecords'][i])
|
||||
}
|
||||
x = { 'AuditAppID': ReadShort(e, 0), 'EventID': ReadShort(e, 2), 'InitiatorType': e.charCodeAt(4) };
|
||||
x['AuditApp'] = _AmtAuditStringTable[x['AuditAppID']];
|
||||
x['Event'] = _AmtAuditStringTable[(x['AuditAppID'] * 100) + x['EventID']];
|
||||
if (!x['Event']) x['Event'] = '#' + x['EventID'];
|
||||
|
||||
// Read and process the initiator
|
||||
if (x['InitiatorType'] == 0) {
|
||||
// HTTP digest
|
||||
var userlen = e.charCodeAt(5);
|
||||
x['Initiator'] = e.substring(6, 6 + userlen);
|
||||
ptr = 6 + userlen;
|
||||
}
|
||||
if (x['InitiatorType'] == 1) {
|
||||
// Kerberos
|
||||
x['KerberosUserInDomain'] = ReadInt(e, 5);
|
||||
var userlen = e.charCodeAt(9);
|
||||
x['Initiator'] = GetSidString(e.substring(10, 10 + userlen));
|
||||
ptr = 10 + userlen;
|
||||
}
|
||||
if (x['InitiatorType'] == 2) {
|
||||
// Local
|
||||
x['Initiator'] = '<i>Local</i>';
|
||||
ptr = 5;
|
||||
}
|
||||
if (x['InitiatorType'] == 3) {
|
||||
// KVM Default Port
|
||||
x['Initiator'] = '<i>KVM Default Port</i>';
|
||||
ptr = 5;
|
||||
}
|
||||
|
||||
// Read timestamp
|
||||
TimeStamp = ReadInt(e, ptr);
|
||||
x['Time'] = new Date((TimeStamp + (t.getTimezoneOffset() * 60)) * 1000);
|
||||
ptr += 4;
|
||||
|
||||
// Read network access
|
||||
x['MCLocationType'] = e.charCodeAt(ptr++);
|
||||
var netlen = e.charCodeAt(ptr++);
|
||||
x['NetAddress'] = e.substring(ptr, ptr + netlen);
|
||||
|
||||
// Read extended data
|
||||
ptr += netlen;
|
||||
var exlen = e.charCodeAt(ptr++);
|
||||
x['Ex'] = e.substring(ptr, ptr + exlen);
|
||||
x['ExStr'] = obj.GetAuditLogExtendedDataStr((x['AuditAppID'] * 100) + x['EventID'], x['Ex']);
|
||||
|
||||
r.push(x);
|
||||
}
|
||||
}
|
||||
if (responses.Body['TotalRecordCount'] > r.length) {
|
||||
obj.AMT_AuditLog_ReadRecords(r.length + 1, _GetAuditLog0, [tag[0], r]);
|
||||
} else {
|
||||
tag[0](obj, r, status);
|
||||
}
|
||||
}
|
||||
|
||||
// ###END###{AuditLog}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
// ###BEGIN###{Certificates}
|
||||
|
||||
// Forge MD5
|
||||
function hex_md5(str) { return forge.md.md5.create().update(str).digest().toHex(); }
|
||||
|
||||
// ###END###{Certificates}
|
||||
|
||||
// ###BEGIN###{!Certificates}
|
||||
|
||||
// TinyMD5 from https://github.com/jbt/js-crypto
|
||||
|
||||
// Perform MD5 setup
|
||||
var md5_k = [];
|
||||
for (var i = 0; i < 64;) { md5_k[i] = 0 | (Math.abs(Math.sin(++i)) * 4294967296); }
|
||||
|
||||
// Perform MD5 on raw string and return hex
|
||||
function hex_md5(str) {
|
||||
var b, c, d, j,
|
||||
x = [],
|
||||
str2 = unescape(encodeURI(str)),
|
||||
a = str2.length,
|
||||
h = [b = 1732584193, c = -271733879, ~b, ~c],
|
||||
i = 0;
|
||||
|
||||
for (; i <= a;) x[i >> 2] |= (str2.charCodeAt(i) || 128) << 8 * (i++ % 4);
|
||||
|
||||
x[str = (a + 8 >> 6) * 16 + 14] = a * 8;
|
||||
i = 0;
|
||||
|
||||
for (; i < str; i += 16) {
|
||||
a = h; j = 0;
|
||||
for (; j < 64;) {
|
||||
a = [
|
||||
d = a[3],
|
||||
((b = a[1] | 0) +
|
||||
((d = (
|
||||
(a[0] +
|
||||
[
|
||||
b & (c = a[2]) | ~b & d,
|
||||
d & b | ~d & c,
|
||||
b ^ c ^ d,
|
||||
c ^ (b | ~d)
|
||||
][a = j >> 4]
|
||||
) +
|
||||
(md5_k[j] +
|
||||
(x[[
|
||||
j,
|
||||
5 * j + 1,
|
||||
3 * j + 5,
|
||||
7 * j
|
||||
][a] % 16 + i] | 0)
|
||||
)
|
||||
)) << (a = [
|
||||
7, 12, 17, 22,
|
||||
5, 9, 14, 20,
|
||||
4, 11, 16, 23,
|
||||
6, 10, 15, 21
|
||||
][4 * a + j++ % 4]) | d >>> 32 - a)
|
||||
),
|
||||
b,
|
||||
c
|
||||
];
|
||||
}
|
||||
for (j = 4; j;) h[--j] = h[j] + a[j];
|
||||
}
|
||||
|
||||
str = '';
|
||||
for (; j < 32;) str += ((h[j >> 3] >> ((1 ^ j++ & 7) * 4)) & 15).toString(16);
|
||||
return str;
|
||||
}
|
||||
|
||||
// ###END###{!Certificates}
|
||||
|
||||
// Perform MD5 on raw string and return raw string result
|
||||
function rstr_md5(str) { return hex2rstr(hex_md5(str)); }
|
||||
|
||||
/*
|
||||
Convert arguments into selector set and body XML. Used by AMT_WiFiPortConfigurationService_UpdateWiFiSettings.
|
||||
args = {
|
||||
"WiFiEndpoint": {
|
||||
__parameterType: 'reference',
|
||||
__resourceUri: 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpoint',
|
||||
Name: 'WiFi Endpoint 0'
|
||||
},
|
||||
"WiFiEndpointSettingsInput":
|
||||
{
|
||||
__parameterType: 'instance',
|
||||
__namespace: 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpointSettings',
|
||||
ElementName: document.querySelector('#editProfile-profileName').value,
|
||||
InstanceID: 'Intel(r) AMT:WiFi Endpoint Settings ' + document.querySelector('#editProfile-profileName').value,
|
||||
AuthenticationMethod: document.querySelector('#editProfile-networkAuthentication').value,
|
||||
//BSSType: 3, // Intel(r) AMT supports only infrastructure networks
|
||||
EncryptionMethod: document.querySelector('#editProfile-encryption').value,
|
||||
SSID: document.querySelector('#editProfile-networkName').value,
|
||||
Priority: 100,
|
||||
PSKPassPhrase: document.querySelector('#editProfile-passPhrase').value
|
||||
},
|
||||
"IEEE8021xSettingsInput": null,
|
||||
"ClientCredential": null,
|
||||
"CACredential": null
|
||||
},
|
||||
*/
|
||||
function execArgumentsToXml(args) {
|
||||
if(args === undefined || args === null) return null;
|
||||
|
||||
var result = '';
|
||||
for(var argName in args) {
|
||||
var arg = args[argName];
|
||||
if(!arg) continue;
|
||||
if(arg['__parameterType'] === 'reference') result += referenceToXml(argName, arg);
|
||||
else result += instanceToXml(argName, arg);
|
||||
//if(arg['__isInstance']) result += instanceToXml(argName, arg);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert JavaScript object into XML
|
||||
|
||||
<r:WiFiEndpointSettingsInput xmlns:q="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpointSettings">
|
||||
<q:ElementName>Wireless-Profile-Admin</q:ElementName>
|
||||
<q:InstanceID>Intel(r) AMT:WiFi Endpoint Settings Wireless-Profile-Admin</q:InstanceID>
|
||||
<q:AuthenticationMethod>6</q:AuthenticationMethod>
|
||||
<q:EncryptionMethod>4</q:EncryptionMethod>
|
||||
<q:Priority>100</q:Priority>
|
||||
<q:PSKPassPhrase>P@ssw0rd</q:PSKPassPhrase>
|
||||
</r:WiFiEndpointSettingsInput>
|
||||
*/
|
||||
function instanceToXml(instanceName, inInstance) {
|
||||
if(inInstance === undefined || inInstance === null) return null;
|
||||
|
||||
var hasNamespace = !!inInstance['__namespace'];
|
||||
var startTag = hasNamespace ? '<q:' : '<';
|
||||
var endTag = hasNamespace ? '</q:' : '</';
|
||||
var namespaceDef = hasNamespace ? (' xmlns:q="' + inInstance['__namespace'] + '"' ): '';
|
||||
var result = '<r:' + instanceName + namespaceDef + '>';
|
||||
for(var prop in inInstance) {
|
||||
if (!inInstance.hasOwnProperty(prop) || prop.indexOf('__') === 0) continue;
|
||||
|
||||
if (typeof inInstance[prop] === 'function' || Array.isArray(inInstance[prop]) ) continue;
|
||||
|
||||
if (typeof inInstance[prop] === 'object') {
|
||||
//result += startTag + prop +'>' + instanceToXml('prop', inInstance[prop]) + endTag + prop +'>';
|
||||
console.error('only convert one level down...');
|
||||
}
|
||||
else {
|
||||
result += startTag + prop +'>' + inInstance[prop].toString() + endTag + prop +'>';
|
||||
}
|
||||
}
|
||||
result += '</r:' + instanceName + '>';
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a selector set into XML. Expect no nesting.
|
||||
* {
|
||||
* selectorName : selectorValue,
|
||||
* selectorName : selectorValue,
|
||||
* ... ...
|
||||
* }
|
||||
|
||||
<r:WiFiEndpoint>
|
||||
<a:Address>http://192.168.1.103:16992/wsman</a:Address>
|
||||
<a:ReferenceParameters>
|
||||
<w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpoint</w:ResourceURI>
|
||||
<w:SelectorSet>
|
||||
<w:Selector Name="Name">WiFi Endpoint 0</w:Selector>
|
||||
</w:SelectorSet>
|
||||
</a:ReferenceParameters>
|
||||
</r:WiFiEndpoint>
|
||||
|
||||
*/
|
||||
function referenceToXml(referenceName, inReference) {
|
||||
if(inReference === undefined || inReference === null ) return null;
|
||||
|
||||
var result = '<r:' + referenceName + '><a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>'+ inReference['__resourceUri']+'</w:ResourceURI><w:SelectorSet>';
|
||||
for(var selectorName in inReference) {
|
||||
if (!inReference.hasOwnProperty(selectorName) || selectorName.indexOf('__') === 0) continue;
|
||||
|
||||
if (typeof inReference[selectorName] === 'function' ||
|
||||
typeof inReference[selectorName] === 'object' ||
|
||||
Array.isArray(inReference[selectorName]) )
|
||||
continue;
|
||||
|
||||
result += '<w:Selector Name="' + selectorName +'">' + inReference[selectorName].toString() + '</w:Selector>';
|
||||
}
|
||||
|
||||
result += '</w:SelectorSet></a:ReferenceParameters></r:' + referenceName + '>';
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert a byte array of SID into string
|
||||
function GetSidString(sid) {
|
||||
var r = "S-" + sid.charCodeAt(0) + "-" + sid.charCodeAt(7);
|
||||
for (var i = 2; i < (sid.length / 4) ; i++) r += "-" + ReadIntX(sid, i * 4);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Convert a SID readable string into bytes
|
||||
function GetSidByteArray(sidString) {
|
||||
if (!sidString || sidString == null) return null;
|
||||
var sidParts = sidString.split('-');
|
||||
|
||||
// Make sure the SID has at least 4 parts and starts with 'S'
|
||||
if (sidParts.length < 4 || (sidParts[0] != 's' && sidParts[0] != 'S')) return null;
|
||||
|
||||
// Check that each part of the SID is really an integer
|
||||
for (var i = 1; i < sidParts.length; i++) { var y = parseInt(sidParts[i]); if (y != sidParts[i]) return null; sidParts[i] = y; }
|
||||
|
||||
// Version (8 bit) + Id count (8 bit) + 48 bit in big endian -- DO NOT use bitwise right shift operator. JavaScript converts the number into a 32 bit integer before shifting. In real world, it's highly likely this part is always 0.
|
||||
var r = String.fromCharCode(sidParts[1]) + String.fromCharCode(sidParts.length - 3) + ShortToStr(Math.floor(sidParts[2] / Math.pow(2, 32))) + IntToStr((sidParts[2]) & 0xFFFF);
|
||||
|
||||
// the rest are in 32 bit in little endian
|
||||
for (var i = 3; i < sidParts.length; i++) r += IntToStrX(sidParts[i]);
|
||||
return r;
|
||||
}
|
|
@ -0,0 +1,646 @@
|
|||
/**
|
||||
* @description Remote Desktop
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.2g
|
||||
*/
|
||||
|
||||
// Construct a MeshServer object
|
||||
var CreateAmtRemoteDesktop = function (divid, scrolldiv) {
|
||||
var obj = {};
|
||||
obj.canvasid = divid;
|
||||
obj.scrolldiv = scrolldiv;
|
||||
obj.canvas = Q(divid).getContext("2d");
|
||||
obj.protocol = 2; // KVM
|
||||
obj.state = 0;
|
||||
obj.acc = "";
|
||||
obj.ScreenWidth = 960;
|
||||
obj.ScreenHeight = 700;
|
||||
obj.width = 0;
|
||||
obj.height = 0;
|
||||
obj.rwidth = 0;
|
||||
obj.rheight = 0;
|
||||
obj.bpp = 2; // Bytes per pixel (1 or 2 supported)
|
||||
obj.useZRLE = true;
|
||||
obj.showmouse = true;
|
||||
obj.buttonmask = 0;
|
||||
obj.spare = null;
|
||||
obj.sparew = 0;
|
||||
obj.spareh = 0;
|
||||
obj.sparew2 = 0;
|
||||
obj.spareh2 = 0;
|
||||
obj.sparecache = {};
|
||||
obj.ZRLEfirst = 1;
|
||||
obj.onScreenSizeChange = null;
|
||||
obj.frameRateDelay = 0;
|
||||
// ###BEGIN###{DesktopRotation}
|
||||
obj.rotation = 0;
|
||||
// ###END###{DesktopRotation}
|
||||
|
||||
// ###BEGIN###{DesktopFocus}
|
||||
obj.mx = 0; // Last mouse x position
|
||||
obj.my = 0; // Last mouse y position
|
||||
obj.ox = -1; // Old mouse x position
|
||||
obj.oy = -1; // Old mouse y position
|
||||
obj.focusmode = 0;
|
||||
// ###END###{DesktopFocus}
|
||||
|
||||
// Private method
|
||||
obj.Debug = function (msg) { console.log(msg); }
|
||||
|
||||
obj.xxStateChange = function (newstate) {
|
||||
if (newstate == 0) {
|
||||
obj.canvas.fillStyle = '#000000';
|
||||
obj.canvas.fillRect(0, 0, obj.width, obj.height);
|
||||
obj.canvas.canvas.width = obj.rwidth = obj.width = 640;
|
||||
obj.canvas.canvas.height = obj.rheight = obj.height = 400;
|
||||
QS(obj.canvasid).cursor = 'auto';
|
||||
} else {
|
||||
if (!obj.showmouse) { QS(obj.canvasid).cursor = 'none'; }
|
||||
}
|
||||
}
|
||||
|
||||
obj.ProcessData = function (data) {
|
||||
if (!data) return;
|
||||
// obj.Debug("KRecv(" + data.length + "): " + rstr2hex(data));
|
||||
obj.acc += data;
|
||||
while (obj.acc.length > 0) {
|
||||
//obj.Debug("KAcc(" + obj.acc.length + "): " + rstr2hex(obj.acc));
|
||||
var cmdsize = 0;
|
||||
if (obj.state == 0 && obj.acc.length >= 12) {
|
||||
// Getting handshake & version
|
||||
cmdsize = 12;
|
||||
//if (obj.acc.substring(0, 4) != "RFB ") { return obj.Stop(); }
|
||||
//var version = parseFloat(obj.acc.substring(4, 11));
|
||||
//obj.Debug("KVersion: " + version);
|
||||
obj.state = 1;
|
||||
obj.Send("RFB 003.008\n");
|
||||
}
|
||||
else if (obj.state == 1 && obj.acc.length >= 1) {
|
||||
// Getting security options
|
||||
cmdsize = obj.acc.charCodeAt(0) + 1;
|
||||
obj.Send(String.fromCharCode(1)); // Send the "None" security type. Since we already authenticated using redirection digest auth, we don't need to do this again.
|
||||
obj.state = 2;
|
||||
}
|
||||
else if (obj.state == 2 && obj.acc.length >= 4) {
|
||||
// Getting security response
|
||||
cmdsize = 4;
|
||||
if (ReadInt(obj.acc, 0) != 0) { return obj.Stop(); }
|
||||
obj.Send(String.fromCharCode(1)); // Send share desktop flag
|
||||
obj.state = 3;
|
||||
}
|
||||
else if (obj.state == 3 && obj.acc.length >= 24) {
|
||||
// Getting server init
|
||||
var namelen = ReadInt(obj.acc, 20);
|
||||
if (obj.acc.length < 24 + namelen) return;
|
||||
cmdsize = 24 + namelen;
|
||||
obj.canvas.canvas.width = obj.rwidth = obj.width = obj.ScreenWidth = ReadShort(obj.acc, 0);
|
||||
obj.canvas.canvas.height = obj.rheight = obj.height = obj.ScreenHeight = ReadShort(obj.acc, 2);
|
||||
|
||||
// These are all values we don't really need, we are going to only run in RGB565 or RGB332 and not use the flexibility provided by these settings.
|
||||
// Makes the javascript code smaller and maybe a bit faster.
|
||||
/*
|
||||
obj.xbpp = obj.acc.charCodeAt(4);
|
||||
obj.depth = obj.acc.charCodeAt(5);
|
||||
obj.bigend = obj.acc.charCodeAt(6);
|
||||
obj.truecolor = obj.acc.charCodeAt(7);
|
||||
obj.rmax = ReadShort(obj.acc, 8);
|
||||
obj.gmax = ReadShort(obj.acc, 10);
|
||||
obj.bmax = ReadShort(obj.acc, 12);
|
||||
obj.rsh = obj.acc.charCodeAt(14);
|
||||
obj.gsh = obj.acc.charCodeAt(15);
|
||||
obj.bsh = obj.acc.charCodeAt(16);
|
||||
var name = obj.acc.substring(24, 24 + namelen);
|
||||
obj.Debug("name: " + name);
|
||||
obj.Debug("width: " + obj.width + ", height: " + obj.height);
|
||||
obj.Debug("bits-per-pixel: " + obj.xbpp);
|
||||
obj.Debug("depth: " + obj.depth);
|
||||
obj.Debug("big-endian-flag: " + obj.bigend);
|
||||
obj.Debug("true-colour-flag: " + obj.truecolor);
|
||||
obj.Debug("rgb max: " + obj.rmax + "," + obj.gmax + "," + obj.bmax);
|
||||
obj.Debug("rgb shift: " + obj.rsh + "," + obj.gsh + "," + obj.bsh);
|
||||
*/
|
||||
|
||||
// SetEncodings, with AMT we can't omit RAW, must be specified.
|
||||
// Intel AMT supports encodings: RAW (0), ZRLE (16), Desktop Size (0xFFFFFF21, -223)
|
||||
|
||||
var supportedEncodings = '';
|
||||
if (obj.useZRLE) supportedEncodings += IntToStr(16);
|
||||
supportedEncodings += IntToStr(0);
|
||||
|
||||
obj.Send(String.fromCharCode(2, 0) + ShortToStr((supportedEncodings.length / 4) + 1) + supportedEncodings + IntToStr(-223)); // Supported Encodings + Desktop Size
|
||||
|
||||
// Set the pixel encoding to something much smaller
|
||||
// obj.Send(String.fromCharCode(0, 0, 0, 0, 16, 16, 0, 1) + ShortToStr(31) + ShortToStr(63) + ShortToStr(31) + String.fromCharCode(11, 5, 0, 0, 0, 0)); // Setup 16 bit color RGB565 (This is the default, so we don't need to set it)
|
||||
if (obj.bpp == 1) obj.Send(String.fromCharCode(0, 0, 0, 0, 8, 8, 0, 1) + ShortToStr(7) + ShortToStr(7) + ShortToStr(3) + String.fromCharCode(5, 2, 0, 0, 0, 0)); // Setup 8 bit color RGB332
|
||||
|
||||
obj.state = 4;
|
||||
obj.parent.xxStateChange(3);
|
||||
_SendRefresh();
|
||||
//obj.timer = setInterval(obj.xxOnTimer, 50);
|
||||
|
||||
// ###BEGIN###{DesktopFocus}
|
||||
obj.ox = -1; // Old mouse x position
|
||||
// ###END###{DesktopFocus}
|
||||
|
||||
if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); }
|
||||
}
|
||||
else if (obj.state == 4) {
|
||||
var c = obj.acc.charCodeAt(0);
|
||||
if (c == 2) {
|
||||
cmdsize = 1; // This is the bell, do nothing.
|
||||
} else if (c == 0) {
|
||||
if (obj.acc.length < 4) return;
|
||||
obj.state = 100 + ReadShort(obj.acc, 2); // Read the number of tiles that are going to be sent, add 100 and use that as our protocol state.
|
||||
cmdsize = 4;
|
||||
}
|
||||
}
|
||||
else if (obj.state > 100 && obj.acc.length >= 12) {
|
||||
var x = ReadShort(obj.acc, 0),
|
||||
y = ReadShort(obj.acc, 2),
|
||||
width = ReadShort(obj.acc, 4),
|
||||
height = ReadShort(obj.acc, 6),
|
||||
s = width * height,
|
||||
encoding = ReadInt(obj.acc, 8);
|
||||
|
||||
if (encoding < 17) {
|
||||
if (width < 1 || width > 64 || height < 1 || height > 64) { console.log("Invalid tile size (" + width + "," + height + "), disconnecting."); return obj.Stop(); }
|
||||
|
||||
// Set the spare bitmap to the rigth size if it's not already. This allows us to recycle the spare most if not all the time.
|
||||
if (obj.sparew != width || obj.spareh != height) {
|
||||
obj.sparew = obj.sparew2 = width;
|
||||
obj.spareh = obj.spareh2 = height;
|
||||
// ###BEGIN###{DesktopRotation}
|
||||
if (obj.rotation == 1 || obj.rotation == 3) { obj.sparew2 = height, obj.spareh2 = width; }
|
||||
// ###END###{DesktopRotation}
|
||||
var xspacecachename = obj.sparew2 + 'x' + obj.spareh2;
|
||||
obj.spare = obj.sparecache[xspacecachename];
|
||||
if (!obj.spare) { obj.sparecache[xspacecachename] = obj.spare = obj.canvas.createImageData(obj.sparew2, obj.spareh2); }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (encoding == 0xFFFFFF21) {
|
||||
// Desktop Size (0xFFFFFF21, -223)
|
||||
obj.canvas.canvas.width = obj.rwidth = obj.width = width;
|
||||
obj.canvas.canvas.height = obj.rheight = obj.height = height;
|
||||
obj.Send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest
|
||||
cmdsize = 12;
|
||||
if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); }
|
||||
// obj.Debug("New desktop width: " + obj.width + ", height: " + obj.height);
|
||||
}
|
||||
else if (encoding == 0) {
|
||||
// RAW encoding
|
||||
var ptr = 12, cs = 12 + (s * obj.bpp);
|
||||
if (obj.acc.length < cs) return; // Check we have all the data needed and we can only draw 64x64 tiles.
|
||||
cmdsize = cs;
|
||||
|
||||
// CRITICAL LOOP, optimize this as much as possible
|
||||
for (var i = 0; i < s; i++) { _setPixel(obj.acc.charCodeAt(ptr++) + ((obj.bpp == 2) ? (obj.acc.charCodeAt(ptr++) << 8) : 0), i); }
|
||||
_putImage(obj.spare, x, y);
|
||||
}
|
||||
else if (encoding == 16) {
|
||||
// ZRLE encoding
|
||||
if (obj.acc.length < 16) return;
|
||||
var datalen = ReadInt(obj.acc, 12);
|
||||
if (obj.acc.length < (16 + datalen)) return;
|
||||
//obj.Debug("RECT ZRLE (" + x + "," + y + "," + width + "," + height + ") LEN = " + datalen);
|
||||
//obj.Debug("RECT ZRLE LEN: " + ReadShortX(obj.acc, 17) + ", DATA: " + rstr2hex(obj.acc.substring(16)));
|
||||
|
||||
// Process the ZLib header if this is the first block
|
||||
var ptr = 16, delta = 5, dx = 0;
|
||||
if (obj.ZRLEfirst == 1) { obj.ZRLEfirst = 0; ptr += 2; delta = 7; dx = 2; } // Skip the ZLib header
|
||||
|
||||
if (datalen > 5 && obj.acc.charCodeAt(ptr) == 0 && ReadShortX(obj.acc, ptr + 1) == (datalen - delta)) {
|
||||
// This is an uncompressed ZLib data block
|
||||
_decodeLRE(obj.acc, ptr + 5, x, y, width, height, s, datalen);
|
||||
}
|
||||
// ###BEGIN###{Inflate}
|
||||
else {
|
||||
// This is compressed ZLib data, decompress and process it.
|
||||
var arr = inflate(obj.acc.substring(ptr, ptr + datalen - dx));
|
||||
if (arr.length > 0) { _decodeLRE(String.fromCharCode.apply(null, new Uint8Array(arr)), 0, x, y, width, height, s, arr.length); } else { obj.Debug("Invalid deflate data"); }
|
||||
}
|
||||
// ###END###{Inflate}
|
||||
|
||||
cmdsize = 16 + datalen;
|
||||
}
|
||||
else {
|
||||
obj.Debug("Unknown Encoding: " + encoding);
|
||||
return obj.Stop();
|
||||
}
|
||||
if (--obj.state == 100) {
|
||||
obj.state = 4;
|
||||
if (obj.frameRateDelay == 0) {
|
||||
_SendRefresh(); // Ask for new frame
|
||||
} else {
|
||||
setTimeout(_SendRefresh, obj.frameRateDelay); // Hold x miliseconds before asking for a new frame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cmdsize == 0) return;
|
||||
obj.acc = obj.acc.substring(cmdsize);
|
||||
}
|
||||
}
|
||||
|
||||
function _decodeLRE(data, ptr, x, y, width, height, s, datalen) {
|
||||
var subencoding = data.charCodeAt(ptr++), index, v, runlengthdecode, palette = {}, rlecount = 0, runlength = 0, i;
|
||||
// obj.Debug("RECT RLE (" + (datalen - 5) + ", " + subencoding + "):" + rstr2hex(data.substring(21, 21 + (datalen - 5))));
|
||||
if (subencoding == 0) {
|
||||
// RAW encoding
|
||||
for (i = 0; i < s; i++) { _setPixel(data.charCodeAt(ptr++) + ((obj.bpp == 2) ? (data.charCodeAt(ptr++) << 8) : 0), i); }
|
||||
_putImage(obj.spare, x, y);
|
||||
}
|
||||
else if (subencoding == 1) {
|
||||
// Solid color tile
|
||||
v = data.charCodeAt(ptr++) + ((obj.bpp == 2) ? (data.charCodeAt(ptr++) << 8) : 0);
|
||||
obj.canvas.fillStyle = 'rgb(' + ((obj.bpp == 1) ? ((v & 224) + ',' + ((v & 28) << 3) + ',' + _fixColor((v & 3) << 6)) : (((v >> 8) & 248) + ',' + ((v >> 3) & 252) + ',' + ((v & 31) << 3))) + ')';
|
||||
|
||||
// ###BEGIN###{DesktopRotation}
|
||||
var xx = _rotX(x, y);
|
||||
y = _rotY(x, y);
|
||||
x = xx;
|
||||
// ###END###{DesktopRotation}
|
||||
|
||||
obj.canvas.fillRect(x, y, width, height);
|
||||
}
|
||||
else if (subencoding > 1 && subencoding < 17) { // Packed palette encoded tile
|
||||
// Read the palette
|
||||
var br = 4, bm = 15; // br is BitRead and bm is BitMask. By adjusting these two we can support all the variations in this encoding.
|
||||
for (i = 0; i < subencoding; i++) { palette[i] = data.charCodeAt(ptr++) + ((obj.bpp == 2) ? (data.charCodeAt(ptr++) << 8) : 0); }
|
||||
|
||||
// Compute bits to read & bit mark
|
||||
if (subencoding == 2) { br = 1; bm = 1; } else if (subencoding <= 4) { br = 2; bm = 3; }
|
||||
|
||||
// Display all the bits
|
||||
while (rlecount < s && ptr < data.length) { v = data.charCodeAt(ptr++); for (i = (8 - br) ; i >= 0; i -= br) { _setPixel(palette[(v >> i) & bm], rlecount++); } }
|
||||
_putImage(obj.spare, x, y);
|
||||
}
|
||||
else if (subencoding == 128) { // RLE encoded tile
|
||||
while (rlecount < s && ptr < data.length) {
|
||||
// Get the run color
|
||||
v = data.charCodeAt(ptr++) + ((obj.bpp == 2) ? (data.charCodeAt(ptr++) << 8) : 0);
|
||||
|
||||
// Decode the run length. This is the fastest and most compact way I found to do this.
|
||||
runlength = 1; do { runlength += (runlengthdecode = data.charCodeAt(ptr++)); } while (runlengthdecode == 255);
|
||||
|
||||
// Draw a run
|
||||
while (--runlength >= 0) { _setPixel(v, rlecount++); }
|
||||
}
|
||||
_putImage(obj.spare, x, y);
|
||||
}
|
||||
else if (subencoding > 129) { // Palette RLE encoded tile
|
||||
// Read the palette
|
||||
for (i = 0; i < (subencoding - 128) ; i++) { palette[i] = data.charCodeAt(ptr++) + ((obj.bpp == 2) ? (data.charCodeAt(ptr++) << 8) : 0); }
|
||||
|
||||
// Decode RLE on palette
|
||||
while (rlecount < s && ptr < data.length) {
|
||||
// Setup the run, get the color index and get the color from the palette.
|
||||
runlength = 1; index = data.charCodeAt(ptr++); v = palette[index % 128];
|
||||
|
||||
// If the index starts with high order bit 1, this is a run and decode the run length.
|
||||
if (index > 127) { do { runlength += (runlengthdecode = data.charCodeAt(ptr++)); } while (runlengthdecode == 255); }
|
||||
|
||||
// Draw a run
|
||||
while (--runlength >= 0) { _setPixel(v, rlecount++); }
|
||||
}
|
||||
_putImage(obj.spare, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
function _putImage(i, x, y) {
|
||||
// ###BEGIN###{DesktopRotation}
|
||||
var xx = _arotX(x, y);
|
||||
y = _arotY(x, y);
|
||||
x = xx;
|
||||
// ###END###{DesktopRotation}
|
||||
obj.canvas.putImageData(i, x, y);
|
||||
}
|
||||
|
||||
function _setPixel(v, p) {
|
||||
var pp = p * 4;
|
||||
|
||||
// ###BEGIN###{DesktopRotation}
|
||||
if (obj.rotation > 0) {
|
||||
if (obj.rotation == 1) {
|
||||
var x = p % obj.sparew;
|
||||
var y = Math.floor(p / obj.sparew);
|
||||
p = (x * obj.sparew2) + (obj.sparew2 - 1 - y);
|
||||
pp = p * 4;
|
||||
}
|
||||
else if (obj.rotation == 2) { pp = (obj.sparew * obj.spareh * 4) - 4 - pp; }
|
||||
else if (obj.rotation == 3) {
|
||||
var x = p % obj.sparew;
|
||||
var y = Math.floor(p / obj.sparew);
|
||||
p = ((obj.sparew2 - 1 - x) * obj.sparew2) + (y);
|
||||
pp = p * 4;
|
||||
}
|
||||
}
|
||||
// ###END###{DesktopRotation}
|
||||
|
||||
if (obj.bpp == 1) {
|
||||
// Set 8bit color RGB332
|
||||
obj.spare.data[pp++] = v & 224;
|
||||
obj.spare.data[pp++] = (v & 28) << 3;
|
||||
obj.spare.data[pp++] = _fixColor((v & 3) << 6);
|
||||
} else {
|
||||
// Set 16bit color RGB565
|
||||
obj.spare.data[pp++] = (v >> 8) & 248;
|
||||
obj.spare.data[pp++] = (v >> 3) & 252;
|
||||
obj.spare.data[pp++] = (v & 31) << 3;
|
||||
}
|
||||
obj.spare.data[pp] = 0xFF; // Set alpha channel to opaque.
|
||||
}
|
||||
|
||||
// ###BEGIN###{DesktopRotation}
|
||||
function _arotX(x, y) {
|
||||
if (obj.rotation == 0) return x;
|
||||
if (obj.rotation == 1) return obj.canvas.canvas.width - obj.sparew2 - y;
|
||||
if (obj.rotation == 2) return obj.canvas.canvas.width - obj.sparew2 - x;
|
||||
if (obj.rotation == 3) return y;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _arotY(x, y) {
|
||||
if (obj.rotation == 0) return y;
|
||||
if (obj.rotation == 1) return x;
|
||||
if (obj.rotation == 2) return obj.canvas.canvas.height - obj.spareh2 - y;
|
||||
if (obj.rotation == 3) return obj.canvas.canvas.height - obj.spareh - x;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _crotX(x, y) {
|
||||
if (obj.rotation == 0) return x;
|
||||
if (obj.rotation == 1) return y;
|
||||
if (obj.rotation == 2) return obj.canvas.canvas.width - x;
|
||||
if (obj.rotation == 3) return obj.canvas.canvas.height - y;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _crotY(x, y) {
|
||||
if (obj.rotation == 0) return y;
|
||||
if (obj.rotation == 1) return obj.canvas.canvas.width - x;
|
||||
if (obj.rotation == 2) return obj.canvas.canvas.height - y;
|
||||
if (obj.rotation == 3) return x;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _rotX(x, y) {
|
||||
if (obj.rotation == 0) return x;
|
||||
if (obj.rotation == 1) return x;
|
||||
if (obj.rotation == 2) return x - obj.canvas.canvas.width;
|
||||
if (obj.rotation == 3) return x - obj.canvas.canvas.height;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _rotY(x, y) {
|
||||
if (obj.rotation == 0) return y;
|
||||
if (obj.rotation == 1) return y - obj.canvas.canvas.width;
|
||||
if (obj.rotation == 2) return y - obj.canvas.canvas.height;
|
||||
if (obj.rotation == 3) return y;
|
||||
return 0;
|
||||
}
|
||||
|
||||
obj.tcanvas = null;
|
||||
obj.setRotation = function (x) {
|
||||
while (x < 0) { x += 4; }
|
||||
var newrotation = x % 4;
|
||||
if (newrotation == obj.rotation) return true;
|
||||
var rw = obj.canvas.canvas.width;
|
||||
var rh = obj.canvas.canvas.height;
|
||||
if (obj.rotation == 1 || obj.rotation == 3) { rw = obj.canvas.canvas.height; rh = obj.canvas.canvas.width; }
|
||||
|
||||
// Copy the canvas, put it back in the correct direction
|
||||
if (obj.tcanvas == null) obj.tcanvas = document.createElement('canvas');
|
||||
var tcanvasctx = obj.tcanvas.getContext('2d');
|
||||
tcanvasctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
tcanvasctx.canvas.width = rw;
|
||||
tcanvasctx.canvas.height = rh;
|
||||
tcanvasctx.rotate((obj.rotation * -90) * Math.PI / 180);
|
||||
if (obj.rotation == 0) tcanvasctx.drawImage(obj.canvas.canvas, 0, 0);
|
||||
if (obj.rotation == 1) tcanvasctx.drawImage(obj.canvas.canvas, -obj.canvas.canvas.width, 0);
|
||||
if (obj.rotation == 2) tcanvasctx.drawImage(obj.canvas.canvas, -obj.canvas.canvas.width, -obj.canvas.canvas.height);
|
||||
if (obj.rotation == 3) tcanvasctx.drawImage(obj.canvas.canvas, 0, -obj.canvas.canvas.height);
|
||||
|
||||
// Change the size and orientation and copy the canvas back into the rotation
|
||||
if (obj.rotation == 0 || obj.rotation == 2) { obj.canvas.canvas.height = rw; obj.canvas.canvas.width = rh; }
|
||||
if (obj.rotation == 1 || obj.rotation == 3) { obj.canvas.canvas.height = rh; obj.canvas.canvas.width = rw; }
|
||||
obj.canvas.setTransform(1, 0, 0, 1, 0, 0);
|
||||
obj.canvas.rotate((newrotation * 90) * Math.PI / 180);
|
||||
obj.rotation = newrotation;
|
||||
obj.canvas.drawImage(obj.tcanvas, _rotX(0, 0), _rotY(0, 0));
|
||||
|
||||
obj.width = obj.canvas.canvas.width;
|
||||
obj.height = obj.canvas.canvas.height;
|
||||
if (obj.onScreenResize != null) obj.onScreenResize(obj, obj.width, obj.height, obj.CanvasId);
|
||||
return true;
|
||||
}
|
||||
// ###END###{DesktopRotation}
|
||||
|
||||
function _fixColor(c) { return (c > 127) ? (c + 32) : c; }
|
||||
|
||||
function _SendRefresh() {
|
||||
// ###BEGIN###{DesktopFocus}
|
||||
if (obj.focusmode > 0) {
|
||||
// Request only pixels around the last mouse position
|
||||
var df = obj.focusmode * 2;
|
||||
obj.Send(String.fromCharCode(3, 1) + ShortToStr(Math.max(Math.min(obj.ox, obj.mx) - obj.focusmode, 0)) + ShortToStr(Math.max(Math.min(obj.oy, obj.my) - obj.focusmode, 0)) + ShortToStr(df + Math.abs(obj.ox - obj.mx)) + ShortToStr(df + Math.abs(obj.oy - obj.my))); // FramebufferUpdateRequest
|
||||
obj.ox = obj.mx;
|
||||
obj.oy = obj.my;
|
||||
} else
|
||||
// ###END###{DesktopFocus} {
|
||||
// Request the entire screen
|
||||
obj.Send(String.fromCharCode(3, 1, 0, 0, 0, 0) + ShortToStr(obj.rwidth) + ShortToStr(obj.rheight)); // FramebufferUpdateRequest
|
||||
}
|
||||
|
||||
obj.Start = function () {
|
||||
//obj.Debug("KVM-Start");
|
||||
obj.state = 0;
|
||||
obj.acc = "";
|
||||
obj.ZRLEfirst = 1;
|
||||
// ###BEGIN###{Inflate}
|
||||
inflate_start();
|
||||
// ###END###{Inflate}
|
||||
for (var i in obj.sparecache) { delete obj.sparecache[i]; }
|
||||
}
|
||||
|
||||
obj.Stop = function () {
|
||||
obj.UnGrabMouseInput();
|
||||
obj.UnGrabKeyInput();
|
||||
obj.parent.Stop();
|
||||
}
|
||||
|
||||
obj.Send = function (x) {
|
||||
//obj.Debug("KSend(" + x.length + "): " + rstr2hex(x));
|
||||
obj.parent.Send(x);
|
||||
}
|
||||
|
||||
/*
|
||||
Intel AMT only recognizes a small subset of keysym characters defined in the keysymdef.h so you don’t need to
|
||||
implement all the languages (this is taken care by the USB Scancode Extension in RFB4.0 protocol).
|
||||
The only subset recognized by the FW is the defined by the following sets : XK_LATIN1 , XK_MISCELLANY, XK_3270, XK_XKB_KEYS, XK_KATAKANA.
|
||||
In addition to keysymdef.h symbols there are 6 japanese extra keys that we do support:
|
||||
|
||||
#define XK_Intel_EU_102kbd_backslash_pipe_45 0x17170056 // European 102-key: 45 (backslash/pipe), usb Usage: 0x64
|
||||
#define XK_Intel_JP_106kbd_yen_pipe 0x1717007d // Japanese 106-key: 14 (Yen/pipe), usb Usage: 0x89
|
||||
#define XK_Intel_JP_106kbd_backslash_underbar 0x17170073 // Japanese 106-key: 56 (backslash/underbar), usb Usage: 0x87
|
||||
#define XK_Intel_JP_106kbd_NoConvert 0x1717007b // Japanese 106-key: 131 (NoConvert), usb Usage: 0x8b
|
||||
#define XK_Intel_JP_106kbd_Convert 0x17170079 // Japanese 106-key: 132 (Convert), usb Usage: 0x8a
|
||||
#define XK_Intel_JP_106kbd_Hirigana_Katakana 0x17170070 // Japanese 106-key: 133 (Hirigana/Katakana), usb Usage: 0x88
|
||||
*/
|
||||
|
||||
function _keyevent(d, e) {
|
||||
if (!e) { e = window.event; }
|
||||
var k = e.keyCode;
|
||||
if (k == 173) k = 189; // '-' key (Firefox)
|
||||
if (k == 61) k = 187; // '=' key (Firefox)
|
||||
var kk = k;
|
||||
if (e.shiftKey == false && k >= 65 && k <= 90) kk = k + 32;
|
||||
if (k >= 112 && k <= 124) kk = k + 0xFF4E;
|
||||
if (k == 8) kk = 0xff08; // Backspace
|
||||
if (k == 9) kk = 0xff09; // Tab
|
||||
if (k == 13) kk = 0xff0d; // Return
|
||||
if (k == 16) kk = 0xffe1; // Shift (Left)
|
||||
if (k == 17) kk = 0xffe3; // Ctrl (Left)
|
||||
if (k == 18) kk = 0xffe9; // Alt (Left)
|
||||
if (k == 27) kk = 0xff1b; // ESC
|
||||
if (k == 33) kk = 0xff55; // PageUp
|
||||
if (k == 34) kk = 0xff56; // PageDown
|
||||
if (k == 35) kk = 0xff57; // End
|
||||
if (k == 36) kk = 0xff50; // Home
|
||||
if (k == 37) kk = 0xff51; // Left
|
||||
if (k == 38) kk = 0xff52; // Up
|
||||
if (k == 39) kk = 0xff53; // Right
|
||||
if (k == 40) kk = 0xff54; // Down
|
||||
if (k == 45) kk = 0xff63; // Insert
|
||||
if (k == 46) kk = 0xffff; // Delete
|
||||
if (k >= 96 && k <= 105) kk = k - 48; // Key pad numbers
|
||||
if (k == 106) kk = 42; // Pad *
|
||||
if (k == 107) kk = 43; // Pad +
|
||||
if (k == 109) kk = 45; // Pad -
|
||||
if (k == 110) kk = 46; // Pad .
|
||||
if (k == 111) kk = 47; // Pad /
|
||||
if (k == 186) kk = 59; // ;
|
||||
if (k == 187) kk = 61; // =
|
||||
if (k == 188) kk = 44; // ,
|
||||
if (k == 189) kk = 45; // -
|
||||
if (k == 190) kk = 46; // .
|
||||
if (k == 191) kk = 47; // /
|
||||
if (k == 192) kk = 96; // `
|
||||
if (k == 219) kk = 91; // [
|
||||
if (k == 220) kk = 92; // \
|
||||
if (k == 221) kk = 93; // ]t
|
||||
if (k == 222) kk = 39; // '
|
||||
//console.log('Key' + d + ": " + k + " = " + kk);
|
||||
obj.sendkey(kk, d);
|
||||
return obj.haltEvent(e);
|
||||
}
|
||||
|
||||
obj.sendkey = function (k, d) { obj.Send(String.fromCharCode(4, d, 0, 0) + IntToStr(k)); }
|
||||
|
||||
obj.SendCtrlAltDelMsg = function () { obj.sendcad(); }
|
||||
obj.sendcad = function () {
|
||||
obj.sendkey(0xFFE3, 1); // Control
|
||||
obj.sendkey(0xFFE9, 1); // Alt
|
||||
obj.sendkey(0xFFFF, 1); // Delete
|
||||
obj.sendkey(0xFFFF, 0); // Delete
|
||||
obj.sendkey(0xFFE9, 0); // Alt
|
||||
obj.sendkey(0xFFE3, 0); // Control
|
||||
}
|
||||
|
||||
var _MouseInputGrab = false;
|
||||
var _KeyInputGrab = false;
|
||||
|
||||
obj.GrabMouseInput = function () {
|
||||
if (_MouseInputGrab == true) return;
|
||||
var c = obj.canvas.canvas;
|
||||
c.onmouseup = obj.mouseup;
|
||||
c.onmousedown = obj.mousedown;
|
||||
c.onmousemove = obj.mousemove;
|
||||
//if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = obj.xxDOMMouseScroll; else c.onmousewheel = obj.xxMouseWheel;
|
||||
_MouseInputGrab = true;
|
||||
}
|
||||
|
||||
obj.UnGrabMouseInput = function () {
|
||||
if (_MouseInputGrab == false) return;
|
||||
var c = obj.canvas.canvas;
|
||||
c.onmousemove = null;
|
||||
c.onmouseup = null;
|
||||
c.onmousedown = null;
|
||||
//if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = null; else c.onmousewheel = null;
|
||||
_MouseInputGrab = false;
|
||||
}
|
||||
|
||||
obj.GrabKeyInput = function () {
|
||||
if (_KeyInputGrab == true) return;
|
||||
document.onkeyup = obj.handleKeyUp;
|
||||
document.onkeydown = obj.handleKeyDown;
|
||||
document.onkeypress = obj.handleKeys;
|
||||
_KeyInputGrab = true;
|
||||
}
|
||||
|
||||
obj.UnGrabKeyInput = function () {
|
||||
if (_KeyInputGrab == false) return;
|
||||
document.onkeyup = null;
|
||||
document.onkeydown = null;
|
||||
document.onkeypress = null;
|
||||
_KeyInputGrab = false;
|
||||
}
|
||||
|
||||
obj.handleKeys = function (e) { return obj.haltEvent(e); }
|
||||
obj.handleKeyUp = function (e) { return _keyevent(0, e); }
|
||||
obj.handleKeyDown = function (e) { return _keyevent(1, e); }
|
||||
obj.haltEvent = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||||
|
||||
// RFB "PointerEvent" and mouse handlers
|
||||
obj.mousedown = function (e) { obj.buttonmask |= (1 << e.button); return obj.mousemove(e); }
|
||||
obj.mouseup = function (e) { obj.buttonmask &= (0xFFFF - (1 << e.button)); return obj.mousemove(e); }
|
||||
obj.mousemove = function (e) {
|
||||
if (obj.state != 4) return true;
|
||||
var pos = obj.getPositionOfControl(Q(obj.canvasid));
|
||||
obj.mx = (e.pageX - pos[0]) * (obj.canvas.canvas.height / Q(obj.canvasid).offsetHeight);
|
||||
obj.my = ((e.pageY - pos[1] + (scrolldiv ? scrolldiv.scrollTop : 0)) * (obj.canvas.canvas.width / Q(obj.canvasid).offsetWidth));
|
||||
|
||||
// ###BEGIN###{DesktopRotation}
|
||||
obj.mx2 = _crotX(obj.mx, obj.my);
|
||||
obj.my = _crotY(obj.mx, obj.my);
|
||||
obj.mx = obj.mx2;
|
||||
// ###END###{DesktopRotation}
|
||||
|
||||
obj.Send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my));
|
||||
|
||||
// ###BEGIN###{DesktopFocus}
|
||||
// Update focus area if we are in focus mode
|
||||
QV('DeskFocus', obj.focusmode);
|
||||
if (obj.focusmode != 0) {
|
||||
var x = Math.min(obj.mx, obj.canvas.canvas.width - obj.focusmode),
|
||||
y = Math.min(obj.my, obj.canvas.canvas.height - obj.focusmode),
|
||||
df = obj.focusmode * 2,
|
||||
c = Q(obj.canvasid),
|
||||
qx = c.offsetHeight / obj.canvas.canvas.height,
|
||||
qy = c.offsetWidth / obj.canvas.canvas.width,
|
||||
q = QS('DeskFocus'),
|
||||
ppos = obj.getPositionOfControl(Q(obj.canvasid).parentElement);
|
||||
q.left = (Math.max(((x - obj.focusmode) * qx), 0) + (pos[0] - ppos[0])) + 'px';
|
||||
q.top = (Math.max(((y - obj.focusmode) * qy), 0) + (pos[1] - ppos[1])) + 'px';
|
||||
q.width = ((df * qx) - 6) + 'px';
|
||||
q.height = ((df * qx) - 6) + 'px';
|
||||
}
|
||||
// ###END###{DesktopFocus}
|
||||
|
||||
return obj.haltEvent(e);
|
||||
}
|
||||
|
||||
obj.getPositionOfControl = function (Control) {
|
||||
var Position = Array(2);
|
||||
Position[0] = Position[1] = 0;
|
||||
while (Control) {
|
||||
Position[0] += Control.offsetLeft;
|
||||
Position[1] += Control.offsetTop;
|
||||
Control = Control.offsetParent;
|
||||
}
|
||||
return Position;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* @description Remote Desktop
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.2
|
||||
*/
|
||||
|
||||
// Construct a Intel AMT IDER object
|
||||
var CreateAmtRemoteIder = function (serverurl) {
|
||||
var obj = {};
|
||||
obj.protocol = 3; // IDER
|
||||
obj.state = 0;
|
||||
obj.socket = null;
|
||||
obj.serverurl = serverurl;
|
||||
obj.bytesToAmt = 0;
|
||||
obj.bytesFromAmt = 0;
|
||||
|
||||
// Private method
|
||||
obj.Debug = function (msg) { console.log(msg); }
|
||||
|
||||
// Private method, called by parent when it change state
|
||||
obj.xxStateChange = function (newstate) {
|
||||
//console.log("STATE: " + newstate);
|
||||
if (newstate == 0) { obj.Stop(); } // Tell Storage Server to stop IDER
|
||||
if (newstate == 3) { obj.StateChange(3); _Send('D'); } // Tell Storage Server to start IDER
|
||||
}
|
||||
|
||||
// Private method
|
||||
obj.StateChange = function (newstate) { obj.state = newstate; }
|
||||
|
||||
// Private method
|
||||
obj.ProcessData = function (data) { _Send('F' + data); obj.bytesFromAmt += data.length; }
|
||||
|
||||
obj.Start = function (host, port, user, pass, tls) {
|
||||
//obj.Debug("IDER-Start");
|
||||
obj.host = host;
|
||||
obj.port = port;
|
||||
obj.user = user;
|
||||
obj.pass = pass;
|
||||
obj.tls = tls;
|
||||
obj.bytesToAmt = 0;
|
||||
obj.bytesFromAmt = 0;
|
||||
obj.socket = new WebSocket(serverurl);
|
||||
obj.socket.onopen = _OnSocketConnected;
|
||||
obj.socket.onmessage = _OnMessage;
|
||||
obj.socket.onclose = _OnSocketClosed;
|
||||
obj.StateChange(1);
|
||||
}
|
||||
|
||||
obj.Stop = function () {
|
||||
if (obj.socket != null) { _Send('G'); obj.socket.close(); obj.socket = null; }
|
||||
obj.StateChange(0);
|
||||
obj.parent.Stop();
|
||||
}
|
||||
|
||||
function _OnSocketConnected() {
|
||||
obj.Debug("Socket Connected");
|
||||
obj.StateChange(2);
|
||||
_Send('C');
|
||||
}
|
||||
|
||||
function _OnMessage(e) {
|
||||
if (typeof e.data == 'object') {
|
||||
var f = new FileReader();
|
||||
if (f.readAsBinaryString) {
|
||||
// Chrome & Firefox (Draft)
|
||||
f.onload = function (e) { _OnSocketData(e.target.result); }
|
||||
f.readAsBinaryString(new Blob([e.data]));
|
||||
} else if (f.readAsArrayBuffer) {
|
||||
// Chrome & Firefox (Spec)
|
||||
f.onloadend = function (e) { _OnSocketData(e.target.result); }
|
||||
f.readAsArrayBuffer(e.data);
|
||||
} else {
|
||||
// IE10, readAsBinaryString does not exist, use an alternative.
|
||||
var binary = "";
|
||||
var bytes = new Uint8Array(e.data);
|
||||
var length = bytes.byteLength;
|
||||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||||
_OnSocketData(binary);
|
||||
}
|
||||
} else {
|
||||
_OnSocketData(e.data);
|
||||
}
|
||||
};
|
||||
|
||||
function _OnSocketData(data) {
|
||||
if (!data) return;
|
||||
|
||||
if (typeof data === 'object') {
|
||||
// This is an ArrayBuffer, convert it to a string array (used in IE)
|
||||
var binary = "";
|
||||
var bytes = new Uint8Array(data);
|
||||
var length = bytes.byteLength;
|
||||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||||
data = binary;
|
||||
}
|
||||
else if (typeof data !== 'string') { return; }
|
||||
|
||||
// console.log("CMD: " + data.substring(0, 1));
|
||||
|
||||
// Handle commands
|
||||
switch (data.substring(0, 1)) {
|
||||
case 'A': { data[0] = 'B'; _Send(data); break; } // Echo
|
||||
case 'C': { obj.parent.Start(obj.host, obj.port, obj.user, obj.pass, obj.tls); break; } // Session Start
|
||||
case 'E': { obj.Stop(); break; } // Stop IDER
|
||||
case 'F': { obj.parent.xxSend(data.substring(1)); obj.bytesToAmt += (data.length - 1); break; } // IDER Data
|
||||
case 'H': { if (obj.onDialogPrompt) obj.onDialogPrompt(obj, JSON.parse(data.substring(1))); } // IDER Dialog Prompt
|
||||
}
|
||||
}
|
||||
|
||||
function _OnSocketClosed() {
|
||||
// obj.Debug("Socket Closed");
|
||||
obj.Stop();
|
||||
}
|
||||
|
||||
obj.dialogPrompt = function(x) { _Send('H' + JSON.stringify(x)); }
|
||||
|
||||
function _Send(x) {
|
||||
// obj.Debug("Send(" + x.length + "): " + rstr2hex(x));
|
||||
if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
|
||||
var b = new Uint8Array(x.length);
|
||||
for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
|
||||
obj.socket.send(b.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
/**
|
||||
* @description Intel AMT Redirection Transport Module - using websocket relay
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.1f
|
||||
*/
|
||||
|
||||
// Construct a MeshServer object
|
||||
var CreateAmtRedirect = function (module) {
|
||||
var obj = {};
|
||||
obj.m = module; // This is the inner module (Terminal or Desktop)
|
||||
module.parent = obj;
|
||||
obj.State = 0;
|
||||
obj.socket = null;
|
||||
// ###BEGIN###{!Mode-Firmware}
|
||||
obj.host = null;
|
||||
obj.port = 0;
|
||||
obj.user = null;
|
||||
obj.pass = null;
|
||||
obj.authuri = "/RedirectionService";
|
||||
// ###END###{!Mode-Firmware}
|
||||
obj.connectstate = 0;
|
||||
obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER
|
||||
|
||||
obj.amtaccumulator = "";
|
||||
obj.amtsequence = 1;
|
||||
obj.amtkeepalivetimer = null;
|
||||
|
||||
obj.onStateChanged = null;
|
||||
|
||||
// Private method
|
||||
//obj.Debug = function (msg) { console.log(msg); }
|
||||
|
||||
obj.Start = function (host, port, user, pass, tls) {
|
||||
obj.host = host;
|
||||
obj.port = port;
|
||||
obj.user = user;
|
||||
obj.pass = pass;
|
||||
obj.connectstate = 0;
|
||||
obj.socket = new WebSocket(window.location.protocol.replace("http", "ws") + "//" + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/webrelay.ashx?p=2&host=" + host + "&port=" + port + "&tls=" + tls + ((user == '*') ? "&serverauth=1" : "") + ((typeof pass === "undefined") ? ("&serverauth=1&user=" + user) : "")); // The "p=2" indicates to the relay that this is a REDIRECTION session
|
||||
obj.socket.onopen = obj.xxOnSocketConnected;
|
||||
obj.socket.onmessage = obj.xxOnMessage;
|
||||
obj.socket.onclose = obj.xxOnSocketClosed;
|
||||
obj.xxStateChange(1);
|
||||
}
|
||||
|
||||
obj.xxOnSocketConnected = function () {
|
||||
//obj.Debug("Redir Socket Connected");
|
||||
obj.xxStateChange(2);
|
||||
if (obj.protocol == 1) obj.xxSend(obj.RedirectStartSol); // TODO: Put these strings in higher level module to tighten code
|
||||
if (obj.protocol == 2) obj.xxSend(obj.RedirectStartKvm); // Don't need these is the feature is not compiled-in.
|
||||
if (obj.protocol == 3) obj.xxSend(obj.RedirectStartIder);
|
||||
}
|
||||
|
||||
obj.xxOnMessage = function (e) {
|
||||
if (typeof e.data == 'object') {
|
||||
var f = new FileReader();
|
||||
if (f.readAsBinaryString) {
|
||||
// Chrome & Firefox (Draft)
|
||||
f.onload = function (e) { obj.xxOnSocketData(e.target.result); }
|
||||
f.readAsBinaryString(new Blob([e.data]));
|
||||
} else if (f.readAsArrayBuffer) {
|
||||
// Chrome & Firefox (Spec)
|
||||
f.onloadend = function (e) { obj.xxOnSocketData(e.target.result); }
|
||||
f.readAsArrayBuffer(e.data);
|
||||
} else {
|
||||
// IE10, readAsBinaryString does not exist, use an alternative.
|
||||
var binary = "";
|
||||
var bytes = new Uint8Array(e.data);
|
||||
var length = bytes.byteLength;
|
||||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||||
obj.xxOnSocketData(binary);
|
||||
}
|
||||
} else {
|
||||
// If we get a string object, it maybe the WebRTC confirm. Ignore it.
|
||||
// obj.debug("MeshDataChannel - OnData - " + typeof e.data + " - " + e.data.length);
|
||||
obj.xxOnSocketData(e.data);
|
||||
}
|
||||
};
|
||||
|
||||
obj.xxOnSocketData = function (data) {
|
||||
if (!data || obj.connectstate == -1) return;
|
||||
|
||||
if (typeof data === 'object') {
|
||||
// This is an ArrayBuffer, convert it to a string array (used in IE)
|
||||
var binary = "";
|
||||
var bytes = new Uint8Array(data);
|
||||
var length = bytes.byteLength;
|
||||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||||
data = binary;
|
||||
}
|
||||
else if (typeof data !== 'string') { return; }
|
||||
|
||||
if ((obj.protocol == 2 || obj.protocol == 3) && obj.connectstate == 1) { return obj.m.ProcessData(data); } // KVM traffic, forward it directly.
|
||||
obj.amtaccumulator += data;
|
||||
//obj.Debug("Redir Recv(" + obj.amtaccumulator.length + "): " + rstr2hex(obj.amtaccumulator));
|
||||
while (obj.amtaccumulator.length >= 1) {
|
||||
var cmdsize = 0;
|
||||
switch (obj.amtaccumulator.charCodeAt(0)) {
|
||||
case 0x11: // StartRedirectionSessionReply (17)
|
||||
if (obj.amtaccumulator.length < 4) return;
|
||||
var statuscode = obj.amtaccumulator.charCodeAt(1);
|
||||
switch (statuscode) {
|
||||
case 0: // STATUS_SUCCESS
|
||||
if (obj.amtaccumulator.length < 13) return;
|
||||
var oemlen = obj.amtaccumulator.charCodeAt(12);
|
||||
if (obj.amtaccumulator.length < 13 + oemlen) return;
|
||||
// Query for available authentication
|
||||
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); // Query authentication support
|
||||
cmdsize = (13 + oemlen);
|
||||
break;
|
||||
default:
|
||||
obj.Stop();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x14: // AuthenticateSessionReply (20)
|
||||
if (obj.amtaccumulator.length < 9) return;
|
||||
var authDataLen = ReadIntX(obj.amtaccumulator, 5);
|
||||
if (obj.amtaccumulator.length < 9 + authDataLen) return;
|
||||
var status = obj.amtaccumulator.charCodeAt(1);
|
||||
var authType = obj.amtaccumulator.charCodeAt(4);
|
||||
var authData = [];
|
||||
for (i = 0; i < authDataLen; i++) { authData.push(obj.amtaccumulator.charCodeAt(9 + i)); }
|
||||
var authDataBuf = obj.amtaccumulator.substring(9, 9 + authDataLen);
|
||||
cmdsize = 9 + authDataLen;
|
||||
if (authType == 0) {
|
||||
// Query
|
||||
if (authData.indexOf(4) >= 0) {
|
||||
// Good Digest Auth (With cnonce and all)
|
||||
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x04) + IntToStrX(obj.user.length + obj.authuri.length + 8) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00, 0x00));
|
||||
}
|
||||
else if (authData.indexOf(3) >= 0) {
|
||||
// Bad Digest Auth (Not sure why this is supported, cnonce is not used!)
|
||||
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x03) + IntToStrX(obj.user.length + obj.authuri.length + 7) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00));
|
||||
}
|
||||
else if (authData.indexOf(1) >= 0) {
|
||||
// Basic Auth (Probably a good idea to not support this unless this is an old version of Intel AMT)
|
||||
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x01) + IntToStrX(obj.user.length + obj.pass.length + 2) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(obj.pass.length) + obj.pass);
|
||||
}
|
||||
else obj.Stop();
|
||||
}
|
||||
else if ((authType == 3 || authType == 4) && status == 1) {
|
||||
var curptr = 0;
|
||||
|
||||
// Realm
|
||||
var realmlen = authDataBuf.charCodeAt(curptr);
|
||||
var realm = authDataBuf.substring(curptr + 1, curptr + 1 + realmlen);
|
||||
curptr += (realmlen + 1);
|
||||
|
||||
// Nonce
|
||||
var noncelen = authDataBuf.charCodeAt(curptr);
|
||||
var nonce = authDataBuf.substring(curptr + 1, curptr + 1 + noncelen);
|
||||
curptr += (noncelen + 1);
|
||||
|
||||
// QOP
|
||||
var qoplen = 0;
|
||||
var qop = null;
|
||||
var cnonce = obj.xxRandomNonce(32);
|
||||
var snc = '00000002';
|
||||
var extra = '';
|
||||
if (authType == 4) {
|
||||
qoplen = authDataBuf.charCodeAt(curptr);
|
||||
qop = authDataBuf.substring(curptr + 1, curptr + 1 + qoplen);
|
||||
curptr += (qoplen + 1);
|
||||
extra = snc + ":" + cnonce + ":" + qop + ":";
|
||||
}
|
||||
|
||||
var digest = hex_md5(hex_md5(obj.user + ":" + realm + ":" + obj.pass) + ":" + nonce + ":" + extra + hex_md5("POST:" + obj.authuri));
|
||||
var totallen = obj.user.length + realm.length + nonce.length + obj.authuri.length + cnonce.length + snc.length + digest.length + 7;
|
||||
if (authType == 4) totallen += (qop.length + 1);
|
||||
var buf = String.fromCharCode(0x13, 0x00, 0x00, 0x00, authType) + IntToStrX(totallen) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(realm.length) + realm + String.fromCharCode(nonce.length) + nonce + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(cnonce.length) + cnonce + String.fromCharCode(snc.length) + snc + String.fromCharCode(digest.length) + digest;
|
||||
if (authType == 4) buf += (String.fromCharCode(qop.length) + qop);
|
||||
obj.xxSend(buf);
|
||||
}
|
||||
else
|
||||
if (status == 0) { // Success
|
||||
if (obj.protocol == 1) {
|
||||
// Serial-over-LAN: Send Intel AMT serial settings...
|
||||
var MaxTxBuffer = 10000;
|
||||
var TxTimeout = 100;
|
||||
var TxOverflowTimeout = 0;
|
||||
var RxTimeout = 10000;
|
||||
var RxFlushTimeout = 100;
|
||||
var Heartbeat = 0;//5000;
|
||||
obj.xxSend(String.fromCharCode(0x20, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++) + ShortToStrX(MaxTxBuffer) + ShortToStrX(TxTimeout) + ShortToStrX(TxOverflowTimeout) + ShortToStrX(RxTimeout) + ShortToStrX(RxFlushTimeout) + ShortToStrX(Heartbeat) + IntToStrX(0));
|
||||
}
|
||||
if (obj.protocol == 2) {
|
||||
// Remote Desktop: Send traffic directly...
|
||||
obj.xxSend(String.fromCharCode(0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
|
||||
}
|
||||
if (obj.protocol == 3) {
|
||||
// Remote IDER: Send traffic directly...
|
||||
obj.connectstate = 1;
|
||||
obj.xxStateChange(3);
|
||||
}
|
||||
} else obj.Stop();
|
||||
break;
|
||||
case 0x21: // Response to settings (33)
|
||||
if (obj.amtaccumulator.length < 23) break;
|
||||
cmdsize = 23;
|
||||
obj.xxSend(String.fromCharCode(0x27, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++) + String.fromCharCode(0x00, 0x00, 0x1B, 0x00, 0x00, 0x00));
|
||||
if (obj.protocol == 1) { obj.amtkeepalivetimer = setInterval(obj.xxSendAmtKeepAlive, 2000); }
|
||||
obj.connectstate = 1;
|
||||
obj.xxStateChange(3);
|
||||
break;
|
||||
case 0x29: // Serial Settings (41)
|
||||
if (obj.amtaccumulator.length < 10) break;
|
||||
cmdsize = 10;
|
||||
break;
|
||||
case 0x2A: // Incoming display data (42)
|
||||
if (obj.amtaccumulator.length < 10) break;
|
||||
var cs = (10 + ((obj.amtaccumulator.charCodeAt(9) & 0xFF) << 8) + (obj.amtaccumulator.charCodeAt(8) & 0xFF));
|
||||
if (obj.amtaccumulator.length < cs) break;
|
||||
obj.m.ProcessData(obj.amtaccumulator.substring(10, cs));
|
||||
cmdsize = cs;
|
||||
break;
|
||||
case 0x2B: // Keep alive message (43)
|
||||
if (obj.amtaccumulator.length < 8) break;
|
||||
cmdsize = 8;
|
||||
break;
|
||||
case 0x41:
|
||||
if (obj.amtaccumulator.length < 8) break;
|
||||
obj.connectstate = 1;
|
||||
obj.m.Start();
|
||||
// KVM traffic, forward rest of accumulator directly.
|
||||
if (obj.amtaccumulator.length > 8) { obj.m.ProcessData(obj.amtaccumulator.substring(8)); }
|
||||
cmdsize = obj.amtaccumulator.length;
|
||||
break;
|
||||
default:
|
||||
console.log("Unknown Intel AMT command: " + obj.amtaccumulator.charCodeAt(0) + " acclen=" + obj.amtaccumulator.length);
|
||||
obj.Stop();
|
||||
return;
|
||||
}
|
||||
if (cmdsize == 0) return;
|
||||
obj.amtaccumulator = obj.amtaccumulator.substring(cmdsize);
|
||||
}
|
||||
}
|
||||
|
||||
obj.xxSend = function (x) {
|
||||
//obj.Debug("Redir Send(" + x.length + "): " + rstr2hex(x));
|
||||
if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
|
||||
var b = new Uint8Array(x.length);
|
||||
for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
|
||||
obj.socket.send(b.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
obj.Send = function (x) {
|
||||
if (obj.socket == null || obj.connectstate != 1) return;
|
||||
if (obj.protocol == 1) { obj.xxSend(String.fromCharCode(0x28, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++) + ShortToStrX(x.length) + x); } else { obj.xxSend(x); }
|
||||
}
|
||||
|
||||
obj.xxSendAmtKeepAlive = function () {
|
||||
if (obj.socket == null) return;
|
||||
obj.xxSend(String.fromCharCode(0x2B, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++));
|
||||
}
|
||||
|
||||
obj.xxRandomNonceX = "abcdef0123456789";
|
||||
obj.xxRandomNonce = function (length) {
|
||||
var r = "";
|
||||
for (var i = 0; i < length; i++) { r += obj.xxRandomNonceX.charAt(Math.floor(Math.random() * obj.xxRandomNonceX.length)); }
|
||||
return r;
|
||||
}
|
||||
|
||||
obj.xxOnSocketClosed = function () {
|
||||
//obj.Debug("Redir Socket Closed");
|
||||
obj.Stop();
|
||||
}
|
||||
|
||||
obj.xxStateChange = function(newstate) {
|
||||
if (obj.State == newstate) return;
|
||||
obj.State = newstate;
|
||||
obj.m.xxStateChange(obj.State);
|
||||
if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
|
||||
}
|
||||
|
||||
obj.Stop = function () {
|
||||
//obj.Debug("Redir Socket Stopped");
|
||||
obj.xxStateChange(0);
|
||||
obj.connectstate = -1;
|
||||
obj.amtaccumulator = "";
|
||||
if (obj.socket != null) { obj.socket.close(); obj.socket = null; }
|
||||
if (obj.amtkeepalivetimer != null) { clearInterval(obj.amtkeepalivetimer); obj.amtkeepalivetimer = null; }
|
||||
}
|
||||
|
||||
obj.RedirectStartSol = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20);
|
||||
obj.RedirectStartKvm = String.fromCharCode(0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52);
|
||||
obj.RedirectStartIder = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52);
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,436 @@
|
|||
/**
|
||||
* @fileoverview Script Compiler / Decompiler / Runner
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.1.0e
|
||||
*/
|
||||
|
||||
// Core functions
|
||||
script_functionTable1 = ['nop', 'jump', 'set', 'print', 'dialog', 'getitem', 'substr', 'indexof', 'split', 'join', 'length', 'jsonparse', 'jsonstr', 'add', 'substract', 'parseint', 'wsbatchenum', 'wsput', 'wscreate', 'wsdelete', 'wsexec', 'scriptspeed', 'wssubscribe', 'wsunsubscribe', 'readchar', 'signwithdummyca'];
|
||||
|
||||
// functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
script_functionTable2 = ['encodeuri', 'decodeuri', 'passwordcheck', 'atob', 'btoa', 'hex2str', 'str2hex', 'random', 'md5', 'maketoarray', 'readshort', 'readshortx', 'readint', 'readsint', 'readintx', 'shorttostr', 'shorttostrx', 'inttostr', 'inttostrx'];
|
||||
|
||||
// functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
script_functionTableX2 = [encodeURI, decodeURI, passwordcheck, window.atob.bind(window), window.btoa.bind(window), hex2rstr, rstr2hex, random, rstr_md5, MakeToArray, ReadShort, ReadShortX, ReadInt, ReadSInt, ReadIntX, ShortToStr, ShortToStrX, IntToStr, IntToStrX];
|
||||
|
||||
// Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
script_functionTable3 = ['pullsystemstatus', 'pulleventlog', 'pullauditlog', 'pullcertificates', 'pullwatchdog', 'pullsystemdefense', 'pullhardware', 'pulluserinfo', 'pullremoteaccess', 'highlightblock', 'disconnect', 'getsidstring', 'getsidbytearray'];
|
||||
|
||||
// Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
script_functionTableX3 = [
|
||||
PullSystemStatus
|
||||
,
|
||||
// ###BEGIN###{EventLog}
|
||||
PullEventLog
|
||||
// ###END###{EventLog}
|
||||
,
|
||||
// ###BEGIN###{AuditLog}
|
||||
PullAuditLog
|
||||
// ###END###{AuditLog}
|
||||
,
|
||||
// ###BEGIN###{Certificates}
|
||||
PullCertificates
|
||||
// ###END###{Certificates}
|
||||
,
|
||||
// ###BEGIN###{AgentPresence}
|
||||
PullWatchdog
|
||||
// ###END###{AgentPresence}
|
||||
,
|
||||
// ###BEGIN###{SystemDefense}
|
||||
PullSystemDefense
|
||||
// ###END###{SystemDefense}
|
||||
,
|
||||
// ###BEGIN###{HardwareInfo}
|
||||
PullHardware
|
||||
// ###END###{HardwareInfo}
|
||||
,
|
||||
PullUserInfo
|
||||
,
|
||||
// ###BEGIN###{RemoteAccess}
|
||||
PullRemoteAccess
|
||||
// ###END###{RemoteAccess}
|
||||
,
|
||||
// ###BEGIN###{Scripting-Editor}
|
||||
script_HighlightBlock
|
||||
// ###END###{Scripting-Editor}
|
||||
,
|
||||
// ###BEGIN###{ComputerSelector}
|
||||
disconnect
|
||||
// ###END###{ComputerSelector}
|
||||
,
|
||||
function (runner, x) { return GetSidString(x); }
|
||||
,
|
||||
function (runner, x) { return GetSidByteArray(x); }
|
||||
];
|
||||
|
||||
// Setup the script state
|
||||
function script_setup(binary, startvars) {
|
||||
var obj = { startvars:startvars };
|
||||
if (binary.length < 6) { console.error('Invalid script length'); return null; } // Script must have at least 6 byte header
|
||||
if (ReadInt(binary, 0) != 0x247D2945) { console.error('Invalid binary script'); return null; } // Check the script magic header
|
||||
if (ReadShort(binary, 4) > 1) { console.error('Unsupported script version'); return null; } // Check the script version
|
||||
obj.script = binary.substring(6);
|
||||
// obj.onStep;
|
||||
// obj.onConsole;
|
||||
|
||||
// Reset the script to the start
|
||||
obj.reset = function (stepspeed) {
|
||||
obj.stop();
|
||||
obj.ip = 0;
|
||||
obj.variables = startvars;
|
||||
obj.state = 1;
|
||||
}
|
||||
|
||||
// Start the script
|
||||
obj.start = function (stepspeed) {
|
||||
obj.stop();
|
||||
obj.stepspeed = stepspeed;
|
||||
if (stepspeed > 0) { obj.timer = setInterval(function () { obj.step() }, stepspeed); }
|
||||
}
|
||||
|
||||
// Stop the script
|
||||
obj.stop = function () {
|
||||
if (obj.timer != null) { clearInterval(obj.timer); }
|
||||
obj.timer = null;
|
||||
obj.stepspeed = 0;
|
||||
}
|
||||
|
||||
// function used to load and store variable values
|
||||
obj.getVar = function (name) { if (name == undefined) return undefined; return obj.getVarEx(name.split('.'), obj.variables); }
|
||||
obj.getVarEx = function (name, val) { try { if (name == undefined) return undefined; if (name.length == 0) return val; return obj.getVarEx(name.slice(1), val[name[0]]); } catch (e) { return null; } }
|
||||
obj.setVar = function (name, val) { obj.setVarEx(name.split('.'), obj.variables, val); }
|
||||
obj.setVarEx = function (name, vars, val) { if (name.length == 1) { vars[name[0]] = val; } else { obj.setVarEx(name.slice(1), vars[name[0]], val); } }
|
||||
|
||||
// Run the script one step forward
|
||||
obj.step = function () {
|
||||
if (obj.state != 1) return;
|
||||
if (obj.ip < obj.script.length) {
|
||||
var cmdid = ReadShort(obj.script, obj.ip);
|
||||
var cmdlen = ReadShort(obj.script, obj.ip + 2);
|
||||
var argcount = ReadShort(obj.script, obj.ip + 4);
|
||||
var argptr = obj.ip + 6;
|
||||
var args = [];
|
||||
|
||||
// Clear all temp variables (This is optional)
|
||||
for (var i in obj.variables) { if (i.startsWith('__')) { delete obj.variables[i]; } }
|
||||
|
||||
// Loop on each argument, moving forward by the argument length each time
|
||||
for (var i = 0; i < argcount; i++) {
|
||||
var arglen = ReadShort(obj.script, argptr);
|
||||
var argval = obj.script.substring(argptr + 2, argptr + 2 + arglen);
|
||||
var argtyp = argval.charCodeAt(0);
|
||||
argval = argval.substring(1);
|
||||
if (argtyp < 2) {
|
||||
// Get the value and replace all {var} with variable values
|
||||
while (argval.split("{").length > 1) { var t = argval.split("{").pop().split("}").shift(); argval = argval.replace('{' + t + '}', obj.getVar(t)); }
|
||||
if (argtyp == 1) { obj.variables['__' + i] = decodeURI(argval); argval = '__' + i; } // If argtyp is 1, this is a literal. Store in temp variable.
|
||||
args.push(argval);
|
||||
}
|
||||
if (argtyp == 2 || argtyp == 3) {
|
||||
obj.variables['__' + i] = ReadSInt(argval, 0);
|
||||
args.push('__' + i);
|
||||
}
|
||||
argptr += (2 + arglen);
|
||||
}
|
||||
|
||||
// Move instruction pointer forward by command size
|
||||
obj.ip += cmdlen;
|
||||
|
||||
// Get all variable values
|
||||
var argsval = [];
|
||||
for (var i = 0; i < 10; i++) { argsval.push(obj.getVar(args[i])); }
|
||||
var storeInArg0;
|
||||
|
||||
try {
|
||||
if (cmdid < 10000) {
|
||||
// Lets run the actual command
|
||||
switch (cmdid) {
|
||||
case 0: // nop
|
||||
break;
|
||||
case 1: // jump(label) or jump(label, a, compare, b)
|
||||
if (argsval[2]) {
|
||||
if (
|
||||
(argsval[2] == '<' && argsval[1] < argsval[3]) ||
|
||||
(argsval[2] == '<=' && argsval[1] <= argsval[3]) ||
|
||||
(argsval[2] == '!=' && argsval[1] != argsval[3]) ||
|
||||
(argsval[2] == '=' && argsval[1] == argsval[3]) ||
|
||||
(argsval[2] == '>=' && argsval[1] >= argsval[3]) ||
|
||||
(argsval[2] == '>' && argsval[1] > argsval[3])
|
||||
) { obj.ip = argsval[0]; }
|
||||
} else {
|
||||
obj.ip = argsval[0]; // Set the instruction pointer to the new location in the script
|
||||
}
|
||||
break;
|
||||
case 2: // set(variable, value)
|
||||
if (args[1] == undefined) delete obj.variables[args[0]]; else obj.setVar(args[0], argsval[1]);
|
||||
break;
|
||||
case 3: // print(message)
|
||||
if (obj.onConsole) { obj.onConsole(obj.toString(argsval[0]), obj); } else { console.log(obj.toString(argsval[0])); }
|
||||
// Q(obj.consoleid).value += () + '\n'); Q(obj.console).scrollTop = Q(obj.console).scrollHeight;
|
||||
break;
|
||||
case 4: // dialog(title, content, buttons)
|
||||
obj.state = 2;
|
||||
obj.dialog = true;
|
||||
setDialogMode(11, argsval[0], argsval[2], obj.xxStepDialogOk, argsval[1], obj);
|
||||
break;
|
||||
case 5: // getitem(a, b, c)
|
||||
for (var i in argsval[1]) { if (argsval[1][i][argsval[2]] == argsval[3]) { storeInArg0 = i; } };
|
||||
break;
|
||||
case 6: // substr(variable_dest, variable_src, index, len)
|
||||
storeInArg0 = argsval[1].substr(argsval[2], argsval[3]);
|
||||
break;
|
||||
case 7: // indexOf(variable_dest, variable_src, index, len)
|
||||
storeInArg0 = argsval[1].indexOf(argsval[2]);
|
||||
break;
|
||||
case 8: // split(variable_dest, variable_src, separator)
|
||||
storeInArg0 = argsval[1].split(argsval[2]);
|
||||
break;
|
||||
case 9: // join(variable_dest, variable_src, separator)
|
||||
storeInArg0 = argsval[1].join(argsval[2]);
|
||||
break;
|
||||
case 10: // length(variable_dest, variable_src)
|
||||
storeInArg0 = argsval[1].length;
|
||||
break;
|
||||
case 11: // jsonparse(variable_dest, json)
|
||||
storeInArg0 = JSON.parse(argsval[1]);
|
||||
break;
|
||||
case 12: // jsonstr(variable_dest, variable_src)
|
||||
storeInArg0 = JSON.stringify(argsval[1]);
|
||||
break;
|
||||
case 13: // add(variable_dest, variable_src, value)
|
||||
storeInArg0 = (argsval[1] + argsval[2]);
|
||||
break;
|
||||
case 14: // substract(variable_dest, variable_src, value)
|
||||
storeInArg0 = (argsval[1] - argsval[2]);
|
||||
break;
|
||||
case 15: // parseInt(variable_dest, variable_src)
|
||||
storeInArg0 = parseInt(argsval[1]);
|
||||
break;
|
||||
case 16: // wsbatchenum(name, objectList)
|
||||
obj.state = 2;
|
||||
obj.amtstack.BatchEnum(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
|
||||
break;
|
||||
case 17: // wsput(name, args)
|
||||
obj.state = 2;
|
||||
obj.amtstack.Put(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
|
||||
break;
|
||||
case 18: // wscreate(name, args)
|
||||
obj.state = 2;
|
||||
obj.amtstack.Create(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
|
||||
break;
|
||||
case 19: // wsdelete(name, args)
|
||||
obj.state = 2;
|
||||
obj.amtstack.Delete(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
|
||||
break;
|
||||
case 20: // wsexec(name, method, args, selectors)
|
||||
obj.state = 2;
|
||||
obj.amtstack.Exec(argsval[0], argsval[1], argsval[2], obj.xxWsmanReturn, obj, 0, argsval[3]);
|
||||
break;
|
||||
case 21: // Script Speed
|
||||
obj.stepspeed = argsval[0];
|
||||
if (obj.timer != null) { clearInterval(obj.timer); obj.timer = setInterval(function () { obj.step() }, obj.stepspeed); }
|
||||
break;
|
||||
case 22: // wssubscribe(name, delivery, url, selectors, opaque, user, pass)
|
||||
obj.state = 2;
|
||||
obj.amtstack.Subscribe(argsval[0], argsval[1], argsval[2], obj.xxWsmanReturn, obj, 0, argsval[3], argsval[4], argsval[5], argsval[6]);
|
||||
break;
|
||||
case 23: // wsunsubscribe(name, selectors)
|
||||
obj.state = 2;
|
||||
obj.amtstack.UnSubscribe(argsval[0], obj.xxWsmanReturn, obj, 0, argsval[1]);
|
||||
break;
|
||||
case 24: // readchar(str, pos)
|
||||
console.log(argsval[1], argsval[2], argsval[1].charCodeAt(argsval[2]));
|
||||
storeInArg0 = argsval[1].charCodeAt(argsval[2]);
|
||||
break;
|
||||
case 25: // signWithDummyCa
|
||||
// ###BEGIN###{Certificates}
|
||||
obj.state = 2;
|
||||
// DERKey, xxCaPrivateKey, certattributes, issuerattributes
|
||||
amtcert_signWithCaKey(argsval[0], null, argsval[1], { 'CN': 'Untrusted Root Certificate' }, obj.xxSignWithDummyCaReturn);
|
||||
// ###END###{Certificates}
|
||||
break;
|
||||
default: {
|
||||
obj.state = 9;
|
||||
console.error("Script Error, unknown command: " + cmdid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cmdid < 20000) {
|
||||
// functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
storeInArg0 = script_functionTableX2[cmdid - 10000](argsval[1], argsval[2], argsval[3], argsval[4], argsval[5], argsval[6]);
|
||||
} else {
|
||||
// Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
|
||||
if (script_functionTableX3 && script_functionTableX3[cmdid - 20000]) {
|
||||
storeInArg0 = script_functionTableX3[cmdid - 20000](obj, argsval[1], argsval[2], argsval[3], argsval[4], argsval[5], argsval[6]); // Note that optional calls start with "obj" as first argument.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (storeInArg0 != undefined) obj.setVar(args[0], storeInArg0);
|
||||
} catch (e) {
|
||||
if (typeof e == 'object') { e = e.message; }
|
||||
obj.setVar('_exception', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.state == 1 && obj.ip >= obj.script.length) { obj.state = 0; obj.stop(); }
|
||||
if (obj.onStep) obj.onStep(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj.xxStepDialogOk = function (button) {
|
||||
obj.variables['DialogSelect'] = button;
|
||||
obj.state = 1;
|
||||
obj.dialog = false;
|
||||
if (obj.onStep) obj.onStep(obj);
|
||||
}
|
||||
|
||||
// ###BEGIN###{**ClosureAdvancedMode}
|
||||
obj.xxWsmanReturnFix = function (x) {
|
||||
if (!x || x == null) return;
|
||||
if (x.Header) { x['Header'] = x.Header; delete x.Header; }
|
||||
if (x.Body) { x['Body'] = x.Body; delete x.Body; }
|
||||
if (x.Responses) { x['Responses'] = x.Responses; delete x.Responses; }
|
||||
if (x.Response) { x['Response'] = x.Response; delete x.Response; }
|
||||
if (x.ReturnValueStr) { x['ReturnValueStr'] = x.ReturnValueStr; delete x.ReturnValueStr; }
|
||||
}
|
||||
// ###END###{**ClosureAdvancedMode}
|
||||
|
||||
obj.xxWsmanReturn = function (stack, name, responses, status) {
|
||||
// ###BEGIN###{**ClosureAdvancedMode}
|
||||
// This is required when Google Closure is used
|
||||
if (responses) {
|
||||
obj.xxWsmanReturnFix(responses);
|
||||
for (var i in responses) {
|
||||
obj.xxWsmanReturnFix(responses[i]);
|
||||
for (var j in responses[i]) { obj.xxWsmanReturnFix(responses[i][j]); }
|
||||
}
|
||||
}
|
||||
// ###END###{**ClosureAdvancedMode}
|
||||
obj.setVar(name, responses);
|
||||
obj.setVar('wsman_result', status);
|
||||
obj.setVar('wsman_result_str', ((httpErrorTable[status]) ? (httpErrorTable[status]) : ('Error #' + status)));
|
||||
obj.state = 1;
|
||||
if (obj.onStep) obj.onStep(obj);
|
||||
}
|
||||
|
||||
// ###BEGIN###{Certificates}
|
||||
obj.xxSignWithDummyCaReturn = function (cert) {
|
||||
obj.setVar('signed_cert', btoa(_arrayBufferToString(cert)));
|
||||
obj.state = 1;
|
||||
if (obj.onStep) obj.onStep(obj);
|
||||
}
|
||||
// ###END###{Certificates}
|
||||
|
||||
obj.toString = function (x) { if (typeof x == 'object') return JSON.stringify(x); return x; }
|
||||
|
||||
obj.reset();
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Argument types: 0 = Variable, 1 = String, 2 = Integer, 3 = Label
|
||||
function script_compile(script, onmsg) {
|
||||
var r = '', scriptlines = script.split('\n'), labels = {}, labelswap = [], swaps = [];
|
||||
// Go thru each script line and encode it
|
||||
for (var i in scriptlines) {
|
||||
var scriptline = scriptlines[i];
|
||||
if (scriptline.startsWith('##SWAP ')) { var x = scriptline.split(' '); if (x.length == 3) { swaps[x[1]] = x[2]; } } // Add a swap instance
|
||||
if (scriptline[0] == '#' || scriptline.length == 0) continue; // Skip comments & blank lines
|
||||
for (var x in swaps) { scriptline = scriptline.split(x).join(swaps[x]); } // Apply all swaps
|
||||
var keywords = scriptline.match(/"[^"]*"|[^\s"]+/g);
|
||||
if (keywords.length == 0) continue; // Skip blank lines
|
||||
if (scriptline[0] == ':') { labels[keywords[0].toUpperCase()] = r.length; continue; } // Mark a label position
|
||||
var funcIndex = script_functionTable1.indexOf(keywords[0].toLowerCase());
|
||||
if (funcIndex == -1) { funcIndex = script_functionTable2.indexOf(keywords[0].toLowerCase()); if (funcIndex >= 0) funcIndex += 10000; }
|
||||
if (funcIndex == -1) { funcIndex = script_functionTable3.indexOf(keywords[0].toLowerCase()); if (funcIndex >= 0) funcIndex += 20000; } // Optional methods
|
||||
if (funcIndex == -1) { if (onmsg) { onmsg("Unabled to compile, unknown command: " + keywords[0]); } return ''; }
|
||||
// Encode CommandId, CmdSize, ArgCount, Arg1Len, Arg1, Arg2Len, Arg2...
|
||||
var cmd = ShortToStr(keywords.length - 1);
|
||||
for (var j in keywords) {
|
||||
if (j == 0) continue;
|
||||
if (keywords[j][0] == ':') {
|
||||
labelswap.push([keywords[j], r.length + cmd.length + 7]); // Add a label swap
|
||||
cmd += ShortToStr(5) + String.fromCharCode(3) + IntToStr(0xFFFFFFFF); // Put an empty label
|
||||
} else {
|
||||
var argint = parseInt(keywords[j]);
|
||||
if (argint == keywords[j]) {
|
||||
cmd += ShortToStr(5) + String.fromCharCode(2) + IntToStr(argint);
|
||||
} else {
|
||||
if (keywords[j][0] == '"' && keywords[j][keywords[j].length - 1] == '"') {
|
||||
cmd += ShortToStr(keywords[j].length - 1) + String.fromCharCode(1) + keywords[j].substring(1, keywords[j].length - 1);
|
||||
} else {
|
||||
cmd += ShortToStr(keywords[j].length + 1) + String.fromCharCode(0) + keywords[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cmd = ShortToStr(funcIndex) + ShortToStr(cmd.length + 4) + cmd;
|
||||
r += cmd;
|
||||
}
|
||||
// Perform all the needed label swaps
|
||||
for (i in labelswap) {
|
||||
var label = labelswap[i][0].toUpperCase(), position = labelswap[i][1], target = labels[label];
|
||||
if (target == undefined) { if (onmsg) { onmsg("Unabled to compile, unknown label: " + label); } return ''; }
|
||||
r = r.substr(0, position) + IntToStr(target) + r.substr(position + 4);
|
||||
}
|
||||
return IntToStr(0x247D2945) + ShortToStr(1) + r;
|
||||
}
|
||||
|
||||
// Decompile the script, intended for debugging only
|
||||
function script_decompile(binary, onecmd) {
|
||||
var r = '', ptr = 6, labelcount = 0, labels = {};
|
||||
if (onecmd >= 0) {
|
||||
ptr = onecmd; // If we are decompiling just one command, set the ptr to that command.
|
||||
} else {
|
||||
if (binary.length < 6) { return '# Invalid script length'; }
|
||||
var magic = ReadInt(binary, 0);
|
||||
var version = ReadShort(binary, 4);
|
||||
if (magic != 0x247D2945) { return '# Invalid binary script: ' + magic; }
|
||||
if (version != 1) { return '# Invalid script version'; }
|
||||
}
|
||||
// Loop on each command, moving forward by the command length each time.
|
||||
while (ptr < binary.length) {
|
||||
var cmdid = ReadShort(binary, ptr);
|
||||
var cmdlen = ReadShort(binary, ptr + 2);
|
||||
var argcount = ReadShort(binary, ptr + 4);
|
||||
var argptr = ptr + 6;
|
||||
var argstr = '';
|
||||
if (!(onecmd >= 0)) r += ":label" + (ptr - 6) + "\n";
|
||||
// Loop on each argument, moving forward by the argument length each time
|
||||
for (var i = 0; i < argcount; i++) {
|
||||
var arglen = ReadShort(binary, argptr);
|
||||
var argval = binary.substring(argptr + 2, argptr + 2 + arglen);
|
||||
var argtyp = argval.charCodeAt(0);
|
||||
if (argtyp == 0) { argstr += ' ' + argval.substring(1); } // Variable
|
||||
else if (argtyp == 1) { argstr += ' \"' + argval.substring(1) + '\"'; } // String
|
||||
else if (argtyp == 2) { argstr += ' ' + ReadInt(argval, 1); } // Integer
|
||||
else if (argtyp == 3) { // Label
|
||||
var target = ReadInt(argval, 1);
|
||||
var label = labels[target];
|
||||
if (!label) { label = ":label" + target; labels[label] = target; }
|
||||
argstr += ' ' + label;
|
||||
}
|
||||
argptr += (2 + arglen);
|
||||
}
|
||||
// Go in the script function table to decode the function
|
||||
if (cmdid < 10000) {
|
||||
r += script_functionTable1[cmdid] + argstr + "\n";
|
||||
} else {
|
||||
if (cmdid >= 20000) {
|
||||
r += script_functionTable3[cmdid - 20000] + argstr + "\n"; // Optional methods
|
||||
} else {
|
||||
r += script_functionTable2[cmdid - 10000] + argstr + "\n";
|
||||
}
|
||||
}
|
||||
ptr += cmdlen;
|
||||
if (onecmd >= 0) return r; // If we are decompiling just one command, exit now
|
||||
}
|
||||
// Remove all unused labels
|
||||
var scriptlines = r.split('\n');
|
||||
r = '';
|
||||
for (var i in scriptlines) {
|
||||
var line = scriptlines[i];
|
||||
if (line[0] != ':') { r += line + '\n'; } else { if (labels[line]) { r += line + '\n'; } }
|
||||
}
|
||||
return r;
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
/**
|
||||
* @description Intel(R) AMT Setup.bin Parser
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.1.0
|
||||
*/
|
||||
|
||||
// Intel(R) AMT Setup.bin GUID's
|
||||
var AmtSetupBinSetupGuids = [
|
||||
"\xb5\x16\xfb\x71\x87\xcb\xf9\x4a\xb4\x41\xca\x7b\x38\x35\x78\xf9", // Version 1
|
||||
"\x96\xb2\x81\x58\xcf\x6b\x72\x4c\x8b\x91\xa1\x5e\x51\x2e\x99\xc4", // Version 2
|
||||
"\xa7\xf7\xf6\xc6\x89\xc4\xf6\x47\x93\xed\xe2\xe5\x02\x0d\xa5\x1d", // Version 3
|
||||
"\xaa\xa9\x34\x52\xe1\x29\xa9\x44\x8d\x4d\x08\x1c\x07\xb9\x63\x53" // Version 4
|
||||
];
|
||||
|
||||
// Notes about version 2 of setup.bin:
|
||||
// - Default "admin" must be followed by a new MEBx password
|
||||
// - ME_VARIABLE_IDENTIFIER_MANAGEABILITY_FEATURE_SELECTION may not appear after any CM settings
|
||||
// - CM_VARIABLE_IDENTIFIER_USER_DEFINED_CERT_ADD must be preceded by setting CM_VARIABLE_IDENTIFIER_USER_DEFINED_CERTS_CONFIG to (TODO!)
|
||||
|
||||
// General notes:
|
||||
// - Setup.bin should always start with "CurrentMEBx Pwd", "newMebx Pwd", "manageability selection" (if present).
|
||||
|
||||
// Intel(R) AMT variable identifiers
|
||||
// Type: 0 = Binar Stringy, 1 = Char, 2 = Short, 3 = Int
|
||||
var AmtSetupBinVarIds =
|
||||
{
|
||||
1: {
|
||||
1: [0, "Current MEBx Password"],
|
||||
2: [0, "New MEBx Password"],
|
||||
3: [1, "Manageability Feature Selection"],
|
||||
4: [1, "Firmware Local Update", // 0 = Disabled, 1 = Enabled, 2 = Password Protected
|
||||
{ 0: "Disabled", 1: "Enabled", 2: "Password Protected" }],
|
||||
5: [1, "Firmware Update Qualifier", // 0 = Always, 1 = Never, 2 = Restricted
|
||||
{ 0: "Always", 1: "Never", 2: "Restricted" }],
|
||||
6: [0, "Power Package"] // GUID Length (16 bytes), Intel AMT version 2.1, 3 and 4
|
||||
},
|
||||
2: {
|
||||
1: [0, "Provisioning Preshared Key ID (PID)"],
|
||||
2: [0, "Provisioning Preshared Key (PPS)"],
|
||||
3: [0, "PKI DNS Suffix"], // 255 bytes max length
|
||||
4: [0, "Configuration Server FQDN"], // 255 bytes max length
|
||||
5: [1, "Remote Configuration Enabled (RCFG)", // 0 = Off, 1 = On
|
||||
{ 0: "Off", 1: "On" }],
|
||||
6: [1, "Pre-Installed Certificates Enabled", // 0 = Off, 1 = On
|
||||
{ 0: "Off", 1: "On" }],
|
||||
7: [1, "User Defined Certificate Configuration", // 0 = Disabled, 1 = Enabled, 2 = Delete
|
||||
{ 0: "Disabled", 1: "Enabled", 2: "Delete" }],
|
||||
8: [0, "User Defined Certificate Addition"], // 1 byte hash algo, 20 to 48 bytes hash, 1 byte name length, up to 32 bytes friendly name, 1 = SHA1 (20 bytes), 2 = SHA256 (32 bytes), 3 = SHA384 (48 bytes). Algo 2 & 3 are for version 3 and up.
|
||||
10: [1, "SOL/IDER Redirection Configuration"],
|
||||
11: [0, "Hostname"], // 63 bytes max length
|
||||
12: [0, "Domain Name"], // 255 bytes max length
|
||||
13: [0, "DHCP"],
|
||||
14: [1, "Secure Firmware Update (SFWU)", // 0 = Disabled, 1 = Enabled
|
||||
{ 0: "Disabled", 1: "Enabled" }],
|
||||
15: [0, "ITO"],
|
||||
16: [1, "Provisioning Mode (PM)", // 1 = Enterprise, 2 = Small Buisness (SMB)
|
||||
{ 0: "Enterprise", 1: "Small Buisness"}],
|
||||
17: [0, "Provisioning Server Address"],
|
||||
18: [2, "Provision Server Port Number (PSPO)"],
|
||||
19: [0, "Static PV4 Parameters"],
|
||||
20: [0, "VLAN"],
|
||||
21: [0, "PASS Policy Flag"],
|
||||
22: [0, "IPv6"], // Length is 204 bytes old format, 84 bytes new format, Version 3+ only
|
||||
23: [1, "Shared/Dedicated FQDN", // 0 = Dedicated, 1 = Shared. This option is valid only if configuring the hostname as well
|
||||
{ 0: "Dedicated", 1: "Shared" }],
|
||||
24: [1, "Dynamic DNS Update", // 0 = Disabled, 1 = Enabled
|
||||
{ 0: "Disabled", 1: "Enabled" }],
|
||||
25: [1, "Remote Desktop (KVM) State", // 0 = Disabled, 1 = Enabled
|
||||
{ 0: "Disabled", 1: "Enabled" }],
|
||||
26: [1, "Opt-in User Consent Option", // 0 = Disabled, 1 = KVM, 0xFF = ALL
|
||||
{ 0 : "Disabled", 1 : "KVM", 255 : "All" }],
|
||||
27: [1, "Opt-in Remote IT Consent Policy", // 0 = Disabled, 1 = Enabled. Allows user consent to be configured remotely.
|
||||
{ 0 : "Disabled", 1 : "Enabled"} ],
|
||||
28: [1, "ME Provision Halt Active", // 0 = Stop, 1 = Start. The "ME provisioning Halt/Activate" command must appear in the file only after "PKIDNSSuffix", "ConfigServerFQDN" and "Provisioning Server Address"
|
||||
{ 0 : "Stop", 1 : "Start"}],
|
||||
29: [1, "Manual Setup and Configuration", // 0 = Automated, 1 = Manual
|
||||
{ 0 : "Automated", 1 : "Manual"}],
|
||||
30: [3, "Support Channel Identifier"], // 4 bytes length. Support channel identifier (valid values: 1-65535)
|
||||
31: [0, "Support Channel Description"], // 60 bytes max. Friendly name used to describe the party representedby the support channel identifier.
|
||||
32: [0, "Service Account Number"], // 32 bytes max. Unique string identifier given to the end user by the service provider.
|
||||
33: [0, "Enrollement Passcode"], // 32 bytes max
|
||||
34: [3, "Service Type"], // 4 bytes length. 1 = Reactive, 2 = Proactive, 4 = One Time Session
|
||||
35: [0, "Service Provider Identifier"] // GUID Length (16 bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Parse the Setup.bin file
|
||||
var AmtSetupBinCreate = function (version, flags) {
|
||||
var obj = {};
|
||||
obj.fileType = version;
|
||||
obj.recordChunkCount = 0;
|
||||
obj.recordHeaderByteCount = 0;
|
||||
obj.recordNumber = 0;
|
||||
obj.majorVersion = version;
|
||||
obj.minorVersion = 0;
|
||||
obj.flags = flags;
|
||||
obj.dataRecordsConsumed = 0;
|
||||
obj.dataRecordChunkCount = 0;
|
||||
obj.records = [];
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
// Parse the Setup.bin file
|
||||
var AmtSetupBinDecode = function (file) {
|
||||
|
||||
// Format of the setup file header:
|
||||
// FileTypeUUID(16) - uniquely identifies the file type. This identifier will remain valid and constant across all versions of the file type.
|
||||
// RecordChunkCount(2) - indicates the number of 512-byte chunks occupied by this record, including all header, body, and reserved fields.
|
||||
// RecordHeaderBytes(2) - indicates the length of the record header in bytes.
|
||||
// RecordNumber(4) - uniquely identifies the record among all records in the file. The field contains a non-negative ordinal value. The value of this field is always zero in the Local Provisioning File Header Record.
|
||||
// MajorVersion(1) - identifies the major version of the file format specification. This is a positive integer that is greater than or equal to 1. The Major Version number is incremented to indicate that changes have been introduced that will cause code written against a lower Major Version number to fail.
|
||||
// MinorVersion(1) - identifies the minor version of the file format specification. This is an integer that is greater than or equal to 0. The Minor Version number is incremented to indicate that changes have been introduced that will not cause code written against the same Major Version and a lower Minor Version number to fail. The purpose of this behavior is to allow a single local provisioning file to be used for multiple generations of Intel® AMT platform.
|
||||
// DataRecordCount(4) - indicates the total number of data records written in the file when it was created.
|
||||
// DataRecordsConsumed(4) - is a counter value that begins at 0 and is incremented by 1 by each platform BIOS when it consumes a data record from the file. This value is used to determine the offset of the next data record in the file.
|
||||
// DataRecordChunkCount(2) - contains the number of 512-byte chunks in each data record. All data records are the same length.
|
||||
// ModuleList - contains a list of module identifiers. A module’s identifier appears in the list if and only if the data records contain entries for that module. Each module identifier is two bytes in length. The list is terminated by an identifier value of 0.
|
||||
|
||||
var obj = {}, UUID = file.substring(0, 16);
|
||||
obj.fileType = 0;
|
||||
for (var i in AmtSetupBinSetupGuids) { if (UUID == AmtSetupBinSetupGuids[i]) obj.fileType = (+i + 1); }
|
||||
if (obj.fileType == 0) return; // Bad header
|
||||
obj.recordChunkCount = ReadShortX(file, 16);
|
||||
obj.recordHeaderByteCount = ReadShortX(file, 18);
|
||||
obj.recordNumber = ReadIntX(file, 20);
|
||||
obj.majorVersion = file.charCodeAt(24);
|
||||
obj.minorVersion = file.charCodeAt(25);
|
||||
obj.flags = ReadShortX(file, 26); // Flags: 1 = Do not consume records
|
||||
var dataRecordCount = ReadIntX(file, 28);
|
||||
obj.dataRecordsConsumed = ReadIntX(file, 32);
|
||||
obj.dataRecordChunkCount = ReadShortX(file, 36);
|
||||
obj.records = [];
|
||||
|
||||
var ptr = 512;
|
||||
while (ptr + 512 <= file.length) {
|
||||
|
||||
// Format of a data record header:
|
||||
// RecordTypeIdentifier(4) - identifies the type of record (in this case a data record). Record Identifiers: Invalid - 0, Data Record - 1
|
||||
// RecordFlags(4) - contains a set of bit flags that characterize the record.
|
||||
// RecordChunkCount(2) - contains the number of 512-byte chunks occupied by the record including all header, body, and reserved fields.
|
||||
// RecordHeaderByteCount(2) - indicates the length of the record header in bytes.
|
||||
// RecordNumber(4) - uniquely identifies the record among all records in the file, including invalid as well as valid records. The identifier is a non-negative integer.
|
||||
|
||||
var r = {};
|
||||
r.typeIdentifier = ReadIntX(file, ptr);
|
||||
r.flags = ReadIntX(file, ptr + 4); // Flags: 1 = Valid, 2 = Scrambled
|
||||
r.chunkCount = ReadShortX(file, ptr + 8);
|
||||
r.headerByteCount = ReadShortX(file, ptr + 10);
|
||||
r.number = ReadIntX(file, ptr + 12);
|
||||
r.variables = [];
|
||||
|
||||
var ptr2 = 0, recbin = file.substring(ptr + 24, ptr + 512);
|
||||
if ((r.flags & 2) != 0) { recbin = AmtSetupBinDescrambleRecordData(recbin); } // De-Scramble the record
|
||||
while (1) {
|
||||
|
||||
// Format of a data record entry:
|
||||
// ModuleIdentifier(2) - identifies the target ME module for the entry.
|
||||
// VariableIdentifier(2) - an enumeration value that identifies the variable. Variable identifiers are unique to each ModuleIdentifier.
|
||||
// VariableLength(2) - is the length of the variable value in bytes.
|
||||
// VariableValue - is the value to be assigned to the variable.
|
||||
|
||||
var v = {};
|
||||
v.moduleid = ReadShortX(recbin, ptr2);
|
||||
v.varid = ReadShortX(recbin, ptr2 + 2);
|
||||
if (v.moduleid == 0 || v.varid == 0) break;
|
||||
if (AmtSetupBinVarIds[v.moduleid][v.varid]) {
|
||||
v.length = ReadShortX(recbin, ptr2 + 4);
|
||||
v.type = AmtSetupBinVarIds[v.moduleid][v.varid][0];
|
||||
v.desc = AmtSetupBinVarIds[v.moduleid][v.varid][1];
|
||||
v.value = recbin.substring(ptr2 + 8, ptr2 + 8 + v.length);
|
||||
if (v.type == 1 && v.length == 1) v.value = v.value.charCodeAt(0);
|
||||
else if (v.type == 2 && v.length == 2) v.value = ReadShortX(v.value, 0);
|
||||
else if (v.type == 3 && v.length == 4) v.value = ReadIntX(v.value, 0);
|
||||
r.variables.push(v);
|
||||
}
|
||||
ptr2 += (8 + (Math.floor((v.length + 3) / 4) * 4));
|
||||
}
|
||||
|
||||
// Sort the variables
|
||||
r.variables.sort(AmtSetupBinVariableCompare);
|
||||
|
||||
obj.records.push(r);
|
||||
ptr += 512;
|
||||
}
|
||||
|
||||
if (dataRecordCount != obj.records.length) return; // Mismatch record count
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Construct a Setup.bin file
|
||||
var AmtSetupBinEncode = function (obj) {
|
||||
if (obj.fileType < 1 && obj.fileType > AmtSetupBinSetupGuids.length) return null;
|
||||
var out = [], r = AmtSetupBinSetupGuids[obj.fileType - 1], reccount = 0;
|
||||
r += ShortToStrX(obj.recordChunkCount);
|
||||
r += ShortToStrX(obj.recordHeaderByteCount);
|
||||
r += IntToStrX(obj.recordNumber);
|
||||
r += String.fromCharCode(obj.majorVersion, obj.minorVersion);
|
||||
r += ShortToStrX(obj.flags); // Flags: 1 = Do not consume records
|
||||
r += IntToStrX(obj.records.length);
|
||||
r += IntToStrX(obj.dataRecordsConsumed);
|
||||
r += ShortToStrX(obj.dataRecordChunkCount);
|
||||
while (r.length < 512) { r += "\0"; } // Pad the header
|
||||
out.push(r);
|
||||
|
||||
// Write each record
|
||||
for (var i in obj.records) {
|
||||
var r2 = "", rec = obj.records[i];
|
||||
r2 += IntToStrX(rec.typeIdentifier);
|
||||
r2 += IntToStrX(rec.flags);
|
||||
r2 += IntToStrX(0); // Reserved
|
||||
r2 += IntToStrX(0); // Reserved
|
||||
r2 += ShortToStrX(1); // rec.chunkCount
|
||||
r2 += ShortToStrX(24); // rec.headerByteCount
|
||||
r2 += IntToStrX(++reccount);
|
||||
|
||||
// Sort the variables
|
||||
rec.variables.sort(AmtSetupBinVariableCompare);
|
||||
|
||||
/*
|
||||
// Change variable priority
|
||||
AmtSetupBinMoveToTop(r.variables, 1, 3); // Manageability Feature Selection
|
||||
AmtSetupBinMoveToTop(r.variables, 1, 2); // New MEBx password
|
||||
AmtSetupBinMoveToTop(r.variables, 1, 1); // Current MEBx password
|
||||
*/
|
||||
|
||||
// Write each variable
|
||||
for (var j in rec.variables) {
|
||||
var r3 = "", v = rec.variables[j], data = v.value;
|
||||
v.type = AmtSetupBinVarIds[v.moduleid][v.varid][0]; // Set the correct type if not alreay connect
|
||||
if (v.type > 0) { // If this is a numeric value, encode it correctly
|
||||
data = parseInt(data);
|
||||
if (v.type == 1) data = String.fromCharCode(data);
|
||||
if (v.type == 2) data = ShortToStrX(data);
|
||||
if (v.type == 3) data = IntToStrX(data);
|
||||
}
|
||||
r3 += ShortToStrX(v.moduleid); // Module Identifier
|
||||
r3 += ShortToStrX(v.varid); // Variable Identifier
|
||||
r3 += ShortToStrX(data.length); // Variable Length
|
||||
r3 += ShortToStrX(0); // Reserved
|
||||
r3 += data; // Variable Data
|
||||
while (r3.length % 4 != 0) { r3 += "\0"; } // Pad the variable
|
||||
r2 += r3;
|
||||
}
|
||||
|
||||
while (r2.length < 512) { r2 += "\0"; } // Pad the record
|
||||
if ((rec.flags & 2) != 0) { r2 = r2.substring(0, 24) + AmtSetupBinScrambleRecordData(r2.substring(24)); } // Scramble the record starting at byte 24, after the header
|
||||
out.push(r2);
|
||||
}
|
||||
return out.join('');
|
||||
}
|
||||
|
||||
// Used to sort variables
|
||||
function AmtSetupBinVariableCompare(a, b) {
|
||||
if (a.moduleid > b.moduleid) return 1;
|
||||
if (a.moduleid < b.moduleid) return -1;
|
||||
if (a.varid > b.varid) return 1;
|
||||
if (a.varid < b.varid) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Scramble and un-scramble records
|
||||
function AmtSetupBinScrambleRecordData(data) { var out = ""; for (var i = 0; i < data.length; i++) { out += String.fromCharCode((data.charCodeAt(i) + 17) & 0xFF); } return out; }
|
||||
function AmtSetupBinDescrambleRecordData(data) { var out = ""; for (var i = 0; i < data.length; i++) { out += String.fromCharCode((data.charCodeAt(i) + 0xEF) & 0xFF); } return out; }
|
||||
|
||||
// Find a moduleid/varid in the variable list, if found, move it to the top
|
||||
//function AmtSetupBinMoveToTop(variables, moduleid, varid) { var i = -1; for (var j in variables) { if ((variables[j].moduleid == moduleid) && (variables[j].varid == varid)) { i = j; } } if (i > 1) { ArrayElementMove(variables, i, 0); } }
|
|
@ -0,0 +1,679 @@
|
|||
/**
|
||||
* @description Remote Terminal
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.2c
|
||||
*/
|
||||
|
||||
// Construct a MeshServer object
|
||||
var CreateAmtRemoteTerminal = function (divid) {
|
||||
var obj = {};
|
||||
obj.DivId = divid;
|
||||
obj.DivElement = document.getElementById(divid);
|
||||
obj.protocol = 1; // SOL
|
||||
// ###BEGIN###{Terminal-Enumation-All}
|
||||
obj.terminalEmulation = 1;
|
||||
// ###END###{Terminal-Enumation-All}
|
||||
obj.fxEmulation = 0;
|
||||
|
||||
obj.width = 80; // 80 or 100
|
||||
obj.height = 25; // 25 or 30
|
||||
var _Terminal_CellHeight = 21;
|
||||
var _Terminal_CellWidth = 13;
|
||||
var _TermColors = ['000000', 'BB0000', '00BB00', 'BBBB00', '0000BB', 'BB00BB', '00BBBB', 'BBBBBB', '555555', 'FF5555', '55FF55', 'FFFF55', '5555FF', 'FF55FF', '55FFFF', 'FFFFFF'];
|
||||
var _TermCurrentReverse = 0;
|
||||
var _TermCurrentFColor = 7;
|
||||
var _TermCurrentBColor = 0;
|
||||
var _TermLineWrap = true;
|
||||
var _termx = 0;
|
||||
var _termy = 0;
|
||||
var _termstate = 0;
|
||||
var _escNumber = [];
|
||||
var _escNumberPtr = 0;
|
||||
var _scratt = [];
|
||||
var _tscreen = [];
|
||||
var _VTUNDERLINE = 1;
|
||||
var _VTREVERSE = 2;
|
||||
// ###BEGIN###{Terminal-Enumation-All}
|
||||
var _utf8accumulator = 0;
|
||||
var _utf8accumulatorCount = 0;
|
||||
// ###END###{Terminal-Enumation-All}
|
||||
// ###BEGIN###{Terminal-Enumation-UTF8}
|
||||
var _utf8accumulator = 0;
|
||||
var _utf8accumulatorCount = 0;
|
||||
// ###END###{Terminal-Enumation-UTF8}
|
||||
|
||||
obj.Start = function () { }
|
||||
|
||||
obj.Init = function (width, height) {
|
||||
obj.width = width ? width : 80;
|
||||
obj.height = height ? height : 25;
|
||||
for (var y = 0; y < obj.height; y++) {
|
||||
_tscreen[y] = [];
|
||||
_scratt[y] = [];
|
||||
for (var x = 0; x < obj.width; x++) { _tscreen[y][x] = ' '; _scratt[y][x] = (7 << 6); }
|
||||
}
|
||||
obj.TermInit();
|
||||
obj.TermDraw();
|
||||
}
|
||||
|
||||
obj.xxStateChange = function(newstate) { }
|
||||
|
||||
obj.ProcessData = function (str) { if (obj.capture != null) obj.capture += str; _ProcessVt100EscString(str); obj.TermDraw(); }
|
||||
|
||||
function _ProcessVt100EscString(str) { for (var i = 0; i < str.length; i++) _ProcessVt100EscChar(String.fromCharCode(str.charCodeAt(i)), str.charCodeAt(i)); }
|
||||
|
||||
function _ProcessVt100EscChar(b, c) {
|
||||
switch (_termstate) {
|
||||
case 0: // Normal Term State
|
||||
switch (c) {
|
||||
case 27: // ESC
|
||||
_termstate = 1;
|
||||
break;
|
||||
default:
|
||||
// Process a single char
|
||||
_ProcessVt100Char(b);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (b) {
|
||||
case '[':
|
||||
_escNumberPtr = 0;
|
||||
_escNumber = [];
|
||||
_termstate = 2;
|
||||
break;
|
||||
case '(':
|
||||
_termstate = 4;
|
||||
break;
|
||||
case ')':
|
||||
_termstate = 5;
|
||||
break;
|
||||
default:
|
||||
_termstate = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (b >= '0' && b <= '9') {
|
||||
// This is a number
|
||||
if (!_escNumber[_escNumberPtr]) {
|
||||
_escNumber[_escNumberPtr] = (b - '0');
|
||||
}
|
||||
else {
|
||||
_escNumber[_escNumberPtr] = ((_escNumber[_escNumberPtr] * 10) + (b - '0'));
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (b == ';') {
|
||||
// New number
|
||||
_escNumberPtr++;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// Process Escape Sequence
|
||||
if (!_escNumber[0]) _escNumber[0] = 0;
|
||||
_ProcessEscapeHandler(b, _escNumber, _escNumberPtr + 1);
|
||||
_termstate = 0;
|
||||
}
|
||||
break;
|
||||
case 4: // '(' Code
|
||||
_termstate = 0;
|
||||
break;
|
||||
case 5: // ')' Code
|
||||
_termstate = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function _ProcessEscapeHandler(code, args, argslen) {
|
||||
var i;
|
||||
switch (code) {
|
||||
case 'c': // ResetDevice
|
||||
// Reset
|
||||
obj.TermResetScreen();
|
||||
break;
|
||||
case 'A': // Move cursor up n lines
|
||||
if (argslen == 1) {
|
||||
_termy -= args[0];
|
||||
if (_termy < 0) _termy = 0;
|
||||
}
|
||||
break;
|
||||
case 'B': // Move cursor down n lines
|
||||
if (argslen == 1) {
|
||||
_termy += args[0];
|
||||
if (_termy > obj.height) _termy = obj.height;
|
||||
}
|
||||
break;
|
||||
case 'C': // Move cursor right n lines
|
||||
if (argslen == 1) {
|
||||
_termx += args[0];
|
||||
if (_termx > obj.width) _termx = obj.width;
|
||||
}
|
||||
break;
|
||||
case 'D': // Move cursor left n lines
|
||||
if (argslen == 1) {
|
||||
_termx -= args[0];
|
||||
if (_termx < 0) _termx = 0;
|
||||
}
|
||||
break;
|
||||
case 'd': // Set cursor to line n
|
||||
if (argslen == 1) {
|
||||
_termy = args[0] - 1;
|
||||
if (_termy > obj.height) _termy = obj.height;
|
||||
if (_termy < 0) _termy = 0;
|
||||
}
|
||||
break;
|
||||
case 'G': // Set cursor to col n
|
||||
if (argslen == 1) {
|
||||
_termx = args[0] - 1;
|
||||
if (_termx < 0) _termx = 0;
|
||||
if (_termx > 79) _termx = 79;
|
||||
}
|
||||
break;
|
||||
case 'J': // ClearScreen:
|
||||
if (argslen == 1 && args[0] == 2) {
|
||||
obj.TermClear((_TermCurrentBColor << 12) + (_TermCurrentFColor << 6)); // Erase entire screen
|
||||
_termx = 0;
|
||||
_termy = 0;
|
||||
}
|
||||
else if (argslen == 0 || argslen == 1 && args[0] == 0) // Erase cursor down
|
||||
{
|
||||
_EraseCursorToEol();
|
||||
for (i = _termy + 1; i < obj.height; i++) _EraseLine(i);
|
||||
}
|
||||
else if (argslen == 1 && args[0] == 1) // Erase cursor up
|
||||
{
|
||||
_EraseCursorToEol();
|
||||
for (i = 0; i < _termy - 1; i++) _EraseLine(i);
|
||||
}
|
||||
break;
|
||||
case 'H': // MoveCursor:
|
||||
if (argslen == 2) {
|
||||
if (args[0] < 1) args[0] = 1;
|
||||
if (args[1] < 1) args[1] = 1;
|
||||
if (args[0] > obj.height) args[0] = obj.height;
|
||||
if (args[1] > obj.width) args[1] = obj.width;
|
||||
_termy = args[0] - 1;
|
||||
_termx = args[1] - 1;
|
||||
}
|
||||
else {
|
||||
_termy = 0;
|
||||
_termx = 0;
|
||||
}
|
||||
break;
|
||||
case 'm': // ScreenAttribs:
|
||||
// Change attributes
|
||||
for (i = 0; i < argslen; i++) {
|
||||
if (!args[i] || args[i] == 0) {
|
||||
// Reset Attributes
|
||||
_TermCurrentBColor = 0;
|
||||
_TermCurrentFColor = 7;
|
||||
_TermCurrentReverse = 0;
|
||||
}
|
||||
else if (args[i] == 1) {
|
||||
// Bright
|
||||
if (_TermCurrentFColor < 8) _TermCurrentFColor += 8;
|
||||
}
|
||||
else if (args[i] == 2 || args[i] == 22) {
|
||||
// Dim
|
||||
if (_TermCurrentFColor >= 8) _TermCurrentFColor -= 8;
|
||||
}
|
||||
else if (args[i] == 7) {
|
||||
// Set Reverse attribute true
|
||||
_TermCurrentReverse = 2;
|
||||
}
|
||||
else if (args[i] == 27) {
|
||||
// Set Reverse attribute false
|
||||
_TermCurrentReverse = 0;
|
||||
}
|
||||
else if (args[i] >= 30 && args[i] <= 37) {
|
||||
// Set Foreground Color
|
||||
var bright = (_TermCurrentFColor >= 8);
|
||||
_TermCurrentFColor = (args[i] - 30);
|
||||
if (bright && _TermCurrentFColor <= 8) _TermCurrentFColor += 8;
|
||||
}
|
||||
else if (args[i] >= 40 && args[i] <= 47) {
|
||||
// Set Background Color
|
||||
_TermCurrentBColor = (args[i] - 40);
|
||||
}
|
||||
else if (args[i] >= 90 && args[i] <= 99) {
|
||||
// Set Bright Foreground Color
|
||||
_TermCurrentFColor = (args[i] - 82);
|
||||
}
|
||||
else if (args[i] >= 100 && args[i] <= 109) {
|
||||
// Set Bright Background Color
|
||||
_TermCurrentBColor = (args[i] - 92);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'K': // EraseLine:
|
||||
if (argslen == 0 || (argslen == 1 && (!args[0] || args[0] == 0))) {
|
||||
_EraseCursorToEol(); // Erase from the cursor to the end of the line
|
||||
}
|
||||
else if (argslen == 1) {
|
||||
if (args[0] == 1) // Erase from the beginning of the line to the cursor
|
||||
{
|
||||
_EraseBolToCursor();
|
||||
}
|
||||
else if (args[0] == 2) // Erase the line with the cursor
|
||||
{
|
||||
_EraseLine(_termy);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'h': // EnableLineWrap:
|
||||
_TermLineWrap = true;
|
||||
break;
|
||||
case 'l': // DisableLineWrap:
|
||||
_TermLineWrap = false;
|
||||
break;
|
||||
default:
|
||||
//if (code != '@') alert(code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
obj.ProcessVt100String = function (str) {
|
||||
for (var i = 0; i < str.length; i++) _ProcessVt100Char(String.fromCharCode(str.charCodeAt(i)));
|
||||
}
|
||||
|
||||
// ###BEGIN###{Terminal-Enumation-All}
|
||||
var AsciiToUnicode = [
|
||||
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
||||
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
||||
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
||||
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
||||
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
|
||||
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
|
||||
0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
||||
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
||||
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
|
||||
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
|
||||
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
||||
0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
|
||||
0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
|
||||
0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
|
||||
];
|
||||
|
||||
var AsciiToUnicodeIntel = [
|
||||
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
||||
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
||||
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
||||
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
||||
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
|
||||
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ae, 0x00bb,
|
||||
0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
||||
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
||||
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
|
||||
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
|
||||
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
||||
0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
|
||||
0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
|
||||
0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
|
||||
];
|
||||
// ###END###{Terminal-Enumation-All}
|
||||
|
||||
// ###BEGIN###{Terminal-Enumation-ASCII}
|
||||
var AsciiToUnicode = [
|
||||
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
||||
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
||||
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
||||
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
||||
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
|
||||
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
|
||||
0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
||||
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
||||
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
|
||||
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
|
||||
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
||||
0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
|
||||
0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
|
||||
0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
|
||||
];
|
||||
// ###END###{Terminal-Enumation-ASCII}
|
||||
|
||||
// ###BEGIN###{Terminal-Enumation-Intel}
|
||||
var AsciiToUnicodeIntel = [
|
||||
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
||||
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
||||
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
||||
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
||||
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
|
||||
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ae, 0x00bb,
|
||||
0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
||||
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
||||
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
|
||||
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
|
||||
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
||||
0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
|
||||
0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
|
||||
0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
|
||||
];
|
||||
// ###END###{Terminal-Enumation-Intel}
|
||||
|
||||
function _ProcessVt100Char(c) {
|
||||
if (c == '\0' || c.charCodeAt() == 7) return; // Ignore null & bell
|
||||
|
||||
var ch = c.charCodeAt();
|
||||
|
||||
// ###BEGIN###{Terminal-Enumation-All}
|
||||
// UTF8 Terminal
|
||||
if (obj.terminalEmulation == 0) {
|
||||
// VT100 - UTF-8 emulation
|
||||
var fallout = true;
|
||||
if ((ch & 0x80) == 0) {
|
||||
// Sub 127 char.
|
||||
_utf8accumulator = ch;
|
||||
_utf8accumulatorCount = 0;
|
||||
fallout = false;
|
||||
}
|
||||
else if ((ch & 0xE0) == 0xC0) {
|
||||
// 2 byte char
|
||||
_utf8accumulator = (ch & 0x1F);
|
||||
_utf8accumulatorCount = 1;
|
||||
fallout = true;
|
||||
}
|
||||
else if ((ch & 0xF0) == 0xE0) {
|
||||
// 3 byte char
|
||||
_utf8accumulator = (ch & 0x0F);
|
||||
_utf8accumulatorCount = 2;
|
||||
fallout = true;
|
||||
}
|
||||
else if ((ch & 0xC0) == 0x80) {
|
||||
if (_utf8accumulatorCount > 0) {
|
||||
_utf8accumulator = (_utf8accumulator << 6);
|
||||
_utf8accumulator += (ch & 0x3F);
|
||||
_utf8accumulatorCount--;
|
||||
fallout = (_utf8accumulatorCount != 0);
|
||||
}
|
||||
else {
|
||||
_utf8accumulator = 0;
|
||||
_utf8accumulatorCount = 0;
|
||||
fallout = true;
|
||||
}
|
||||
}
|
||||
if (fallout == true) return;
|
||||
c = String.fromCharCode(_utf8accumulator);
|
||||
} else if (obj.terminalEmulation == 1) {
|
||||
// ANSI - Extended ASCII emulation.
|
||||
if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicode[ch & 0x7F]); }
|
||||
} else if (obj.terminalEmulation == 2) {
|
||||
// ANSI - Intel Extended ASCII emulation.
|
||||
if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicodeIntel[ch & 0x7F]); }
|
||||
}
|
||||
// ###END###{Terminal-Enumation-All}
|
||||
|
||||
// ###BEGIN###{Terminal-Enumation-UTF8}
|
||||
// VT100 - UTF-8 emulation
|
||||
var fallout = true;
|
||||
if ((ch & 0x80) == 0) {
|
||||
// Sub 127 char.
|
||||
_utf8accumulator = ch;
|
||||
_utf8accumulatorCount = 0;
|
||||
fallout = false;
|
||||
}
|
||||
else if ((ch & 0xE0) == 0xC0) {
|
||||
// 2 byte char
|
||||
_utf8accumulator = (ch & 0x1F);
|
||||
_utf8accumulatorCount = 1;
|
||||
fallout = true;
|
||||
}
|
||||
else if ((ch & 0xF0) == 0xE0) {
|
||||
// 3 byte char
|
||||
_utf8accumulator = (ch & 0x0F);
|
||||
_utf8accumulatorCount = 2;
|
||||
fallout = true;
|
||||
}
|
||||
else if ((ch & 0xC0) == 0x80) {
|
||||
if (_utf8accumulatorCount > 0) {
|
||||
_utf8accumulator = (_utf8accumulator << 6);
|
||||
_utf8accumulator += (ch & 0x3F);
|
||||
_utf8accumulatorCount--;
|
||||
fallout = (_utf8accumulatorCount != 0);
|
||||
}
|
||||
else {
|
||||
_utf8accumulator = 0;
|
||||
_utf8accumulatorCount = 0;
|
||||
fallout = true;
|
||||
}
|
||||
}
|
||||
if (fallout == true) return;
|
||||
c = String.fromCharCode(_utf8accumulator);
|
||||
// ###END###{Terminal-Enumation-UTF8}
|
||||
|
||||
// ###BEGIN###{Terminal-Enumation-ASCII}
|
||||
// ANSI - Extended ASCII emulation.
|
||||
if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicode[ch & 0x7F]); }
|
||||
// ###END###{Terminal-Enumation-ASCII}
|
||||
|
||||
// ###BEGIN###{Terminal-Enumation-Intel}
|
||||
// ANSI - Intel Extended ASCII emulation.
|
||||
if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicodeIntel[ch & 0x7F]); }
|
||||
// ###END###{Terminal-Enumation-Intel}
|
||||
|
||||
//if (ch < 32 && ch != 10 && ch != 13) alert(ch);
|
||||
switch (ch) {
|
||||
case 16: { c = ' '; break; } // This is an odd char that show up on Intel BIOS's.
|
||||
case 24: { c = '↑'; break; }
|
||||
case 25: { c = '↓'; break; }
|
||||
}
|
||||
|
||||
if (_termx > obj.width) _termx = obj.width;
|
||||
if (_termy > (obj.height - 1)) _termy = (obj.height - 1);
|
||||
|
||||
switch (c) {
|
||||
case '\b': // Backspace
|
||||
if (_termx > 0) {
|
||||
_termx = _termx - 1;
|
||||
_TermDrawChar(' ');
|
||||
}
|
||||
break;
|
||||
case '\t': // tab
|
||||
var tab = 8 - (_termx % 8)
|
||||
for (var x = 0; x < tab; x++) _ProcessVt100Char(" ");
|
||||
break;
|
||||
case '\n': // Linefeed
|
||||
_termy++;
|
||||
if (_termy > (obj.height - 1)) {
|
||||
// Move everything up one line
|
||||
_TermMoveUp(1);
|
||||
_termy = (obj.height - 1);
|
||||
}
|
||||
break;
|
||||
case '\r': // Carriage Return
|
||||
_termx = 0;
|
||||
break;
|
||||
default:
|
||||
if (_termx >= obj.width) {
|
||||
_termx = 0;
|
||||
if (_TermLineWrap) { _termy++; }
|
||||
if (_termy >= (obj.height - 1)) { _TermMoveUp(1); _termy = (obj.height - 1); }
|
||||
}
|
||||
_TermDrawChar(c);
|
||||
_termx++;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _TermDrawChar(c) {
|
||||
_tscreen[_termy][_termx] = c;
|
||||
_scratt[_termy][_termx] = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
|
||||
}
|
||||
|
||||
obj.TermClear = function(TermColor) {
|
||||
for (var y = 0; y < obj.height; y++) {
|
||||
for (var x = 0; x < obj.width; x++) {
|
||||
_tscreen[y][x] = ' ';
|
||||
_scratt[y][x] = TermColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.TermResetScreen = function () {
|
||||
_TermCurrentReverse = 0;
|
||||
_TermCurrentFColor = 7;
|
||||
_TermCurrentBColor = 0;
|
||||
_TermLineWrap = true;
|
||||
_termx = 0;
|
||||
_termy = 0;
|
||||
obj.TermClear(7 << 6);
|
||||
}
|
||||
|
||||
function _EraseCursorToEol() {
|
||||
var t = (_TermCurrentBColor << 12);
|
||||
for (var x = _termx; x < obj.width; x++) {
|
||||
_tscreen[_termy][x] = ' ';
|
||||
_scratt[_termy][x] = t;
|
||||
}
|
||||
}
|
||||
|
||||
function _EraseBolToCursor() {
|
||||
var t = (_TermCurrentBColor << 12);
|
||||
for (var x = 0; x < _termx; x++) {
|
||||
_tscreen[_termy][x] = ' ';
|
||||
_scratt[_termy][x] = t;
|
||||
}
|
||||
}
|
||||
|
||||
function _EraseLine(line) {
|
||||
var t = (_TermCurrentBColor << 12);
|
||||
for (var x = 0; x < obj.width; x++) {
|
||||
_tscreen[line][x] = ' ';
|
||||
_scratt[line][x] = t;
|
||||
}
|
||||
}
|
||||
|
||||
obj.TermSendKeys = function(keys) { obj.parent.Send(keys); }
|
||||
obj.TermSendKey = function(key) { obj.parent.Send(String.fromCharCode(key)); }
|
||||
|
||||
function _TermMoveUp(linecount) {
|
||||
var x, y;
|
||||
for (y = 0; y < obj.height - linecount; y++) {
|
||||
_tscreen[y] = _tscreen[y + linecount];
|
||||
_scratt[y] = _scratt[y + linecount];
|
||||
}
|
||||
for (y = obj.height - linecount; y < obj.height; y++) {
|
||||
_tscreen[y] = [];
|
||||
_scratt[y] = [];
|
||||
for (x = 0; x < obj.width; x++) {
|
||||
_tscreen[y][x] = ' ';
|
||||
_scratt[y][x] = (7 << 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.TermHandleKeys = function (e) {
|
||||
if (!e.ctrlKey) {
|
||||
if (e.which == 127) obj.TermSendKey(8);
|
||||
else if (e.which == 13) obj.TermSendKeys("\r\n");
|
||||
else if (e.which != 0) obj.TermSendKey(e.which);
|
||||
return false;
|
||||
}
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
if (e.stopPropagation) e.stopPropagation();
|
||||
}
|
||||
|
||||
obj.TermHandleKeyUp = function (e) {
|
||||
if ((e.which != 8) && (e.which != 32) && (e.which != 9)) return true;
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
if (e.stopPropagation) e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
obj.TermHandleKeyDown = function (e) {
|
||||
if ((e.which >= 65) && (e.which <= 90) && (e.ctrlKey == true)) {
|
||||
obj.TermSendKey(e.which - 64);
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
if (e.stopPropagation) e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
if (e.which == 27) { obj.TermSendKeys(String.fromCharCode(27)); return true; }; // ESC
|
||||
if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 91, 68)); return true; }; // Left
|
||||
if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 91, 65)); return true; }; // Up
|
||||
if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 91, 67)); return true; }; // Right
|
||||
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 91, 66)); return true; }; // Down
|
||||
if (e.which == 9) { obj.TermSendKeys("\t"); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return true; }; // TAB
|
||||
|
||||
// F1 to F12 keys
|
||||
// ###BEGIN###{Terminal-FxEnumation-All}
|
||||
var fx0 = [80, 81, 119, 120, 116, 117, 113, 114, 112, 77];
|
||||
var fx1 = [49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 33, 64];
|
||||
var fx2 = [80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91];
|
||||
if (e.which > 111 & e.which < 124 && e.repeat == false) { // F1 to F12 keys
|
||||
if (obj.fxEmulation == 0 && e.which < 122) { obj.TermSendKeys(String.fromCharCode(27, 91, 79, fx0[e.which - 112])); return true; } // 'Intel (F10 = ESC+[OM)'
|
||||
if (obj.fxEmulation == 1) { obj.TermSendKeys(String.fromCharCode(27, fx1[e.which - 112])); return true; } // 'Alternate (F10 = ESC+0)'
|
||||
if (obj.fxEmulation == 2) { obj.TermSendKeys(String.fromCharCode(27, 79, fx2[e.which - 112])); return true; } // 'VT100+ (F10 = ESC+[OY)'
|
||||
}
|
||||
// ###END###{Terminal-FxEnumation-All}
|
||||
// ###BEGIN###{Terminal-FxEnumation-Intel}
|
||||
var fx0 = [80, 81, 119, 120, 116, 117, 113, 114, 112, 77];
|
||||
if (e.which > 111 & e.which < 122 && e.repeat == false) { obj.TermSendKeys(String.fromCharCode(27, 91, 79, fx0[e.which - 112])); return true; } // 'Intel (F10 = ESC+[OM)'
|
||||
// ###END###{Terminal-FxEnumation-Intel}
|
||||
// ###BEGIN###{Terminal-FxEnumation-Alternate}
|
||||
var fx1 = [49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 33, 64];
|
||||
if (e.which > 111 & e.which < 124 && e.repeat == false) { obj.TermSendKeys(String.fromCharCode(27, fx1[e.which - 112])); return true; } // 'Alternate (F10 = ESC+0)'
|
||||
// ###END###{Terminal-FxEnumation-Alternate}
|
||||
// ###BEGIN###{Terminal-FxEnumation-VT100Plus}
|
||||
var fx2 = [80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91];
|
||||
if (e.which > 111 & e.which < 124 && e.repeat == false) { obj.TermSendKeys(String.fromCharCode(27, 79, fx2[e.which - 112])); return true; } // 'VT100+ (F10 = ESC+[OY)'
|
||||
// ###END###{Terminal-FxEnumation-VT100Plus}
|
||||
|
||||
if (e.which != 8 && e.which != 32 && e.which != 9) return true;
|
||||
obj.TermSendKey(e.which);
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
if (e.stopPropagation) e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
obj.TermDraw = function() {
|
||||
var c, buf = '', closetag = '', newat, oldat = 1, x1, x2;
|
||||
for (var y = 0; y < obj.height; ++y) {
|
||||
for (var x = 0; x < obj.width; ++x) {
|
||||
newat = _scratt[y][x];
|
||||
if (_termx == x && _termy == y) { newat |= _VTREVERSE; } // If this is the cursor location, reverse the color.
|
||||
if (newat != oldat) {
|
||||
buf += closetag;
|
||||
closetag = '';
|
||||
x1 = 6; x2 = 12;
|
||||
if (newat & _VTREVERSE) { x1 = 12; x2 = 6;}
|
||||
buf += '<span style="color:#' + _TermColors[(newat >> x1) & 0x3F] + ';background-color:#' + _TermColors[(newat >> x2) & 0x3F];
|
||||
if (newat & _VTUNDERLINE) buf += ';text-decoration:underline';
|
||||
buf += ';">';
|
||||
closetag = "</span>" + closetag;
|
||||
oldat = newat;
|
||||
}
|
||||
|
||||
c = _tscreen[y][x];
|
||||
switch (c) {
|
||||
case '&':
|
||||
buf += '&'; break;
|
||||
case '<':
|
||||
buf += '<'; break;
|
||||
case '>':
|
||||
buf += '>'; break;
|
||||
case ' ':
|
||||
buf += ' '; break;
|
||||
default:
|
||||
buf += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (y != (obj.height - 1)) buf += '<br>';
|
||||
}
|
||||
obj.DivElement.innerHTML = "<font size='4'><b>" + buf + closetag + "</b></font>";
|
||||
}
|
||||
|
||||
obj.TermInit = function () { obj.TermResetScreen(); }
|
||||
|
||||
obj.Init();
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
/**
|
||||
* @description Intel(r) AMT WSMAN Stack
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.2.0
|
||||
*/
|
||||
|
||||
// Construct a MeshServer object
|
||||
var WsmanStackCreateService = function (host, port, user, pass, tls, extra) {
|
||||
var obj = {};
|
||||
//obj.onDebugMessage = null; // Set to a function if you want to get debug messages.
|
||||
obj.NextMessageId = 1; // Next message number, used to label WSMAN calls.
|
||||
obj.Address = '/wsman';
|
||||
obj.comm = CreateWsmanComm(host, port, user, pass, tls, extra);
|
||||
|
||||
obj.PerformAjax = function (postdata, callback, tag, pri, namespaces) {
|
||||
if (namespaces == undefined) namespaces = '';
|
||||
obj.comm.PerformAjax('<?xml version=\"1.0\" encoding=\"utf-8\"?><Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns=\"http://www.w3.org/2003/05/soap-envelope\" ' + namespaces + '><Header><a:Action>' + postdata, function (data, status, tag) {
|
||||
if (status != 200) { callback(obj, null, { Header: { HttpError: status } }, status, tag); return; }
|
||||
var wsresponse = obj.ParseWsman(data);
|
||||
if (!wsresponse || wsresponse == null) { callback(obj, null, { Header: { HttpError: status } }, 601, tag); } else { callback(obj, wsresponse.Header["ResourceURI"], wsresponse, 200, tag); }
|
||||
}, tag, pri);
|
||||
}
|
||||
|
||||
// Private method
|
||||
//obj.Debug = function (msg) { /*console.log(msg);*/ }
|
||||
|
||||
// Cancel all pending queries with given status
|
||||
obj.CancelAllQueries = function (s) { obj.comm.CancelAllQueries(s); }
|
||||
|
||||
// Get the last element of a URI string
|
||||
obj.GetNameFromUrl = function (resuri) {
|
||||
var x = resuri.lastIndexOf("/");
|
||||
return (x == -1)?resuri:resuri.substring(x + 1);
|
||||
}
|
||||
|
||||
// Perform a WSMAN Subscribe operation
|
||||
obj.ExecSubscribe = function (resuri, delivery, url, callback, tag, pri, selectors, opaque, user, pass) {
|
||||
var digest = "", digest2 = "";
|
||||
if (user != undefined && pass != undefined) { digest = '<t:IssuedTokens><t:RequestSecurityTokenResponse><t:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken</t:TokenType><t:RequestedSecurityToken><se:UsernameToken><se:Username>' + user + '</se:Username><se:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#PasswordText">' + pass + '</se:Password></se:UsernameToken></t:RequestedSecurityToken></t:RequestSecurityTokenResponse></t:IssuedTokens>'; digest2 = '<Auth Profile="http://schemas.xmlsoap.org/ws/2004/08/eventing/DeliveryModes/secprofile/http/digest"/>'; }
|
||||
if (opaque != undefined && opaque != null) { opaque = '<a:ReferenceParameters>' + opaque + '</a:ReferenceParameters>'; } else { opaque = ""; }
|
||||
var data = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" + _PutObjToSelectorsXml(selectors) + digest + '</Header><Body><e:Subscribe><e:Delivery Mode="http://schemas.dmtf.org/wbem/wsman/1/wsman/' + delivery + '"><e:NotifyTo><a:Address>' + url + '</a:Address></e:NotifyTo>' + digest2 + '</e:Delivery><e:Expires>PT0.000000S</e:Expires></e:Subscribe>';
|
||||
obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri, 'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:se="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:m="http://x.com"');
|
||||
}
|
||||
|
||||
// Perform a WSMAN UnSubscribe operation
|
||||
obj.ExecUnSubscribe = function (resuri, callback, tag, pri, selectors) {
|
||||
var data = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Unsubscribe</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" + _PutObjToSelectorsXml(selectors) + '</Header><Body><e:Unsubscribe/>';
|
||||
obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri, 'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing"');
|
||||
}
|
||||
|
||||
// Perform a WSMAN PUT operation
|
||||
obj.ExecPut = function (resuri, putobj, callback, tag, pri, selectors) {
|
||||
var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Put</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60.000S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + '</Header><Body>' + _PutObjToBodyXml(resuri, putobj);
|
||||
obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri);
|
||||
}
|
||||
|
||||
// Perform a WSMAN CREATE operation
|
||||
obj.ExecCreate = function (resuri, putobj, callback, tag, pri, selectors) {
|
||||
var objname = obj.GetNameFromUrl(resuri);
|
||||
var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + "</Header><Body><g:" + objname + " xmlns:g=\"" + resuri + "\">";
|
||||
for (var n in putobj) { data += "<g:" + n + ">" + putobj[n] + "</g:" + n + ">" }
|
||||
obj.PerformAjax(data + "</g:" + objname + "></Body></Envelope>", callback, tag, pri);
|
||||
}
|
||||
|
||||
// Perform a WSMAN CREATE operation
|
||||
obj.ExecCreateXml = function (resuri, argsxml, callback, tag, pri) {
|
||||
var objname = obj.GetNameFromUrl(resuri), selector = "";
|
||||
obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60.000S</w:OperationTimeout></Header><Body><r:" + objname + " xmlns:r=\"" + resuri + "\">" + argsxml + "</r:" + objname + "></Body></Envelope>", callback, tag, pri);
|
||||
}
|
||||
|
||||
// Perform a WSMAN DELETE operation
|
||||
obj.ExecDelete = function (resuri, putobj, callback, tag, pri) {
|
||||
var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(putobj) + "</Header><Body /></Envelope>";
|
||||
obj.PerformAjax(data, callback, tag, pri);
|
||||
}
|
||||
|
||||
// Perform a WSMAN GET operation
|
||||
obj.ExecGet = function (resuri, callback, tag, pri) {
|
||||
obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Get</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body /></Envelope>", callback, tag, pri);
|
||||
}
|
||||
|
||||
// Perform a WSMAN method call operation
|
||||
obj.ExecMethod = function (resuri, method, args, callback, tag, pri, selectors) {
|
||||
var argsxml = "";
|
||||
for (var i in args) { if (args[i] != null) { if (Array.isArray(args[i])) { for (var x in args[i]) { argsxml += "<r:" + i + ">" + args[i][x] + "</r:" + i + ">"; } } else { argsxml += "<r:" + i + ">" + args[i] + "</r:" + i + ">"; } } }
|
||||
obj.ExecMethodXml(resuri, method, argsxml, callback, tag, pri, selectors);
|
||||
}
|
||||
|
||||
// Perform a WSMAN method call operation. The arguments are already formatted in XML.
|
||||
obj.ExecMethodXml = function (resuri, method, argsxml, callback, tag, pri, selectors) {
|
||||
obj.PerformAjax(resuri + "/" + method + "</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + "</Header><Body><r:" + method + '_INPUT' + " xmlns:r=\"" + resuri + "\">" + argsxml + "</r:" + method + "_INPUT></Body></Envelope>", callback, tag, pri);
|
||||
}
|
||||
|
||||
// Perform a WSMAN ENUM operation
|
||||
obj.ExecEnum = function (resuri, callback, tag, pri) {
|
||||
obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body><Enumerate xmlns=\"http://schemas.xmlsoap.org/ws/2004/09/enumeration\" /></Body></Envelope>", callback, tag, pri);
|
||||
}
|
||||
|
||||
// Perform a WSMAN PULL operation
|
||||
obj.ExecPull = function (resuri, enumctx, callback, tag, pri) {
|
||||
obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body><Pull xmlns=\"http://schemas.xmlsoap.org/ws/2004/09/enumeration\"><EnumerationContext>" + enumctx + "</EnumerationContext><MaxElements>999</MaxElements><MaxCharacters>99999</MaxCharacters></Pull></Body></Envelope>", callback, tag, pri);
|
||||
}
|
||||
|
||||
// Private method
|
||||
obj.ParseWsman = function (xml) {
|
||||
try {
|
||||
if (!xml.childNodes) xml = _turnToXml(xml);
|
||||
var r = { Header:{} }, header = xml.getElementsByTagName("Header")[0], t;
|
||||
if (!header) header = xml.getElementsByTagName("a:Header")[0];
|
||||
if (!header) return null;
|
||||
for (var i = 0; i < header.childNodes.length; i++) {
|
||||
var child = header.childNodes[i];
|
||||
r.Header[child.localName] = child.textContent;
|
||||
}
|
||||
var body = xml.getElementsByTagName("Body")[0];
|
||||
if (!body) body = xml.getElementsByTagName("a:Body")[0];
|
||||
if (!body) return null;
|
||||
if (body.childNodes.length > 0) {
|
||||
t = body.childNodes[0].localName;
|
||||
if (t.indexOf("_OUTPUT") == t.length - 7) { t = t.substring(0, t.length - 7); }
|
||||
r.Header['Method'] = t;
|
||||
r.Body = _ParseWsmanRec(body.childNodes[0]);
|
||||
}
|
||||
return r;
|
||||
} catch (e) {
|
||||
console.log("Unable to parse XML: " + xml);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Private method
|
||||
function _ParseWsmanRec(node) {
|
||||
var data, r = {};
|
||||
for (var i = 0; i < node.childNodes.length; i++) {
|
||||
var child = node.childNodes[i];
|
||||
if (child.childElementCount == 0) { data = child.textContent; } else { data = _ParseWsmanRec(child); }
|
||||
if (data == 'true') data = true; // Convert 'true' into true
|
||||
if (data == 'false') data = false; // Convert 'false' into false
|
||||
|
||||
var childObj = data;
|
||||
if (child.attributes.length > 0) {
|
||||
childObj = {'Value': data };
|
||||
for(var j = 0; j < child.attributes.length; j++) {
|
||||
childObj['@' + child.attributes[j].name] = child.attributes[j].value;
|
||||
}
|
||||
}
|
||||
|
||||
if (r[child.localName] instanceof Array) { r[child.localName].push(childObj); }
|
||||
else if (r[child.localName] == undefined) { r[child.localName] = childObj; }
|
||||
else { r[child.localName] = [r[child.localName], childObj]; }
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
function _PutObjToBodyXml(resuri, putObj) {
|
||||
if(!resuri || putObj === undefined || putObj === null) return '';
|
||||
var objname = obj.GetNameFromUrl(resuri);
|
||||
var result = '<r:' + objname + ' xmlns:r="' + resuri + '">';
|
||||
|
||||
for (var prop in putObj) {
|
||||
if (!putObj.hasOwnProperty(prop) || prop.indexOf('__') === 0 || prop.indexOf('@') === 0) continue;
|
||||
if (putObj[prop] === undefined || putObj[prop] === null || typeof putObj[prop] === 'function') continue;
|
||||
if (typeof putObj[prop] === 'object' && putObj[prop]['ReferenceParameters']) {
|
||||
result += '<r:' + prop + '><a:Address>' + putObj[prop].Address + '</a:Address><a:ReferenceParameters><w:ResourceURI>' + putObj[prop]['ReferenceParameters']["ResourceURI"] + '</w:ResourceURI><w:SelectorSet>';
|
||||
var selectorArray = putObj[prop]['ReferenceParameters']['SelectorSet']['Selector'];
|
||||
if (Array.isArray(selectorArray)) {
|
||||
for (var i=0; i< selectorArray.length; i++) {
|
||||
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray[i]) + '>' + selectorArray[i]['Value'] + '</w:Selector>';
|
||||
}
|
||||
}
|
||||
else {
|
||||
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray) + '>' + selectorArray['Value'] + '</w:Selector>';
|
||||
}
|
||||
result += '</w:SelectorSet></a:ReferenceParameters></r:' + prop + '>';
|
||||
}
|
||||
else {
|
||||
if (Array.isArray(putObj[prop])) {
|
||||
for (var i = 0; i < putObj[prop].length; i++) {
|
||||
result += '<r:' + prop + '>' + putObj[prop][i].toString() + '</r:' + prop + '>';
|
||||
}
|
||||
} else {
|
||||
result += '<r:' + prop + '>' + putObj[prop].toString() + '</r:' + prop + '>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result += '</r:' + objname + '>';
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
convert
|
||||
{ @Name: 'InstanceID', @AttrName: 'Attribute Value'}
|
||||
into
|
||||
' Name="InstanceID" AttrName="Attribute Value" '
|
||||
*/
|
||||
function _ObjectToXmlAttributes(objWithAttributes) {
|
||||
if(!objWithAttributes) return '';
|
||||
var result = ' ';
|
||||
for (var propName in objWithAttributes) {
|
||||
if (!objWithAttributes.hasOwnProperty(propName) || propName.indexOf('@') !== 0) continue;
|
||||
result += propName.substring(1) + '="' + objWithAttributes[propName] + '" ';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function _PutObjToSelectorsXml(selectorSet) {
|
||||
if (!selectorSet) return '';
|
||||
if (typeof selectorSet == 'string') return selectorSet;
|
||||
if (selectorSet['InstanceID']) return "<w:SelectorSet><w:Selector Name=\"InstanceID\">" + selectorSet['InstanceID'] + "</w:Selector></w:SelectorSet>";
|
||||
var result = '<w:SelectorSet>';
|
||||
for(var propName in selectorSet) {
|
||||
if (!selectorSet.hasOwnProperty(propName)) continue;
|
||||
result += '<w:Selector Name="' + propName + '">';
|
||||
if (selectorSet[propName]['ReferenceParameters']) {
|
||||
result += '<a:EndpointReference>';
|
||||
result += '<a:Address>' + selectorSet[propName]['Address'] + '</a:Address><a:ReferenceParameters><w:ResourceURI>' + selectorSet[propName]['ReferenceParameters']['ResourceURI'] + '</w:ResourceURI><w:SelectorSet>';
|
||||
var selectorArray = selectorSet[propName]['ReferenceParameters']['SelectorSet']['Selector'];
|
||||
if (Array.isArray(selectorArray)) {
|
||||
for (var i = 0; i < selectorArray.length; i++) {
|
||||
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray[i]) + '>' + selectorArray[i]['Value'] + '</w:Selector>';
|
||||
}
|
||||
}
|
||||
else {
|
||||
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray) + '>' + selectorArray['Value'] + '</w:Selector>';
|
||||
}
|
||||
result += '</w:SelectorSet></a:ReferenceParameters></a:EndpointReference>';
|
||||
} else {
|
||||
result += selectorSet[propName];
|
||||
}
|
||||
result += '</w:Selector>';
|
||||
}
|
||||
result += '</w:SelectorSet>';
|
||||
return result;
|
||||
}
|
||||
|
||||
function _turnToXml(text) {
|
||||
if (window.DOMParser) {
|
||||
return new DOMParser().parseFromString(text, "text/xml");
|
||||
}
|
||||
else // Internet Explorer
|
||||
{
|
||||
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
|
||||
xmlDoc.async = false;
|
||||
xmlDoc.loadXML(text);
|
||||
return xmlDoc;
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,287 @@
|
|||
/**
|
||||
* @description WSMAN communication using websocket
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.2.0c
|
||||
*/
|
||||
|
||||
// Construct a WSMAN communication object
|
||||
var CreateWsmanComm = function (host, port, user, pass, tls) {
|
||||
var obj = {};
|
||||
obj.PendingAjax = []; // List of pending AJAX calls. When one frees up, another will start.
|
||||
obj.ActiveAjaxCount = 0; // Number of currently active AJAX calls
|
||||
obj.MaxActiveAjaxCount = 1; // Maximum number of activate AJAX calls at the same time.
|
||||
obj.FailAllError = 0; // Set this to non-zero to fail all AJAX calls with that error status, 999 causes responses to be silent.
|
||||
obj.challengeParams = null;
|
||||
obj.noncecounter = 1;
|
||||
obj.authcounter = 0;
|
||||
obj.socket = null;
|
||||
obj.socketState = 0;
|
||||
obj.host = host;
|
||||
obj.port = port;
|
||||
obj.user = user;
|
||||
obj.pass = pass;
|
||||
obj.tls = tls;
|
||||
obj.cnonce = Math.random().toString(36).substring(7); // Generate a random client nonce
|
||||
|
||||
// Private method
|
||||
//obj.Debug = function (msg) { console.log(msg); }
|
||||
|
||||
// Private method
|
||||
// pri = priority, if set to 1, the call is high priority and put on top of the stack.
|
||||
obj.PerformAjax = function (postdata, callback, tag, pri, url, action) {
|
||||
if (obj.ActiveAjaxCount < obj.MaxActiveAjaxCount && obj.PendingAjax.length == 0) {
|
||||
// There are no pending AJAX calls, perform the call now.
|
||||
obj.PerformAjaxEx(postdata, callback, tag, url, action);
|
||||
} else {
|
||||
// If this is a high priority call, put this call in front of the array, otherwise put it in the back.
|
||||
if (pri == 1) { obj.PendingAjax.unshift([postdata, callback, tag, url, action]); } else { obj.PendingAjax.push([postdata, callback, tag, url, action]); }
|
||||
}
|
||||
}
|
||||
|
||||
// Private method
|
||||
obj.PerformNextAjax = function () {
|
||||
if (obj.ActiveAjaxCount >= obj.MaxActiveAjaxCount || obj.PendingAjax.length == 0) return;
|
||||
var x = obj.PendingAjax.shift();
|
||||
obj.PerformAjaxEx(x[0], x[1], x[2], x[3], x[4]);
|
||||
obj.PerformNextAjax();
|
||||
}
|
||||
|
||||
// Private method
|
||||
obj.PerformAjaxEx = function (postdata, callback, tag, url, action) {
|
||||
if (obj.FailAllError != 0) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag, url, action]); return; }
|
||||
if (!postdata) postdata = "";
|
||||
//console.log("SEND: " + postdata); // DEBUG
|
||||
|
||||
// We are in a websocket relay environment
|
||||
obj.ActiveAjaxCount++;
|
||||
return obj.PerformAjaxExNodeJS(postdata, callback, tag, url, action);
|
||||
}
|
||||
|
||||
// Websocket relay specific private method
|
||||
obj.pendingAjaxCall = [];
|
||||
|
||||
// Websocket relay specific private method
|
||||
obj.PerformAjaxExNodeJS = function (postdata, callback, tag, url, action) { obj.PerformAjaxExNodeJS2(postdata, callback, tag, url, action, 3); }
|
||||
|
||||
// Websocket relay specific private method
|
||||
obj.PerformAjaxExNodeJS2 = function (postdata, callback, tag, url, action, retry) {
|
||||
if (retry <= 0 || obj.FailAllError != 0) {
|
||||
// Too many retry, fail here.
|
||||
obj.ActiveAjaxCount--;
|
||||
if (obj.FailAllError != 999) obj.gotNextMessages(null, 'error', { status: ((obj.FailAllError == 0) ? 408 : obj.FailAllError) }, [postdata, callback, tag, url, action]); // 408 is timeout error
|
||||
obj.PerformNextAjax();
|
||||
return;
|
||||
}
|
||||
obj.pendingAjaxCall.push([postdata, callback, tag, url, action, retry]);
|
||||
if (obj.socketState == 0) { obj.xxConnectHttpSocket(); }
|
||||
else if (obj.socketState == 2) { obj.sendRequest(postdata, url, action); }
|
||||
}
|
||||
|
||||
// Websocket relay specific private method (Content Length Encoding)
|
||||
obj.sendRequest = function (postdata, url, action) {
|
||||
url = url ? url : "/wsman";
|
||||
action = action ? action : "POST";
|
||||
var h = action + " " + url + " HTTP/1.1\r\n";
|
||||
if (obj.challengeParams != null) {
|
||||
var response = hex_md5(hex_md5(obj.user + ':' + obj.challengeParams["realm"] + ':' + obj.pass) + ':' + obj.challengeParams["nonce"] + ':' + obj.noncecounter + ':' + obj.cnonce + ':' + obj.challengeParams["qop"] + ':' + hex_md5(action + ':' + url));
|
||||
h += 'Authorization: ' + obj.renderDigest({ "username": obj.user, "realm": obj.challengeParams["realm"], "nonce": obj.challengeParams["nonce"], "uri": url, "qop": obj.challengeParams["qop"], "response": response, "nc": obj.noncecounter++, "cnonce": obj.cnonce }) + '\r\n';
|
||||
}
|
||||
//h += 'Host: ' + obj.host + ':' + obj.port + '\r\nContent-Length: ' + postdata.length + '\r\n\r\n' + postdata; // Use Content-Length
|
||||
h += 'Host: ' + obj.host + ':' + obj.port + '\r\nTransfer-Encoding: chunked\r\n\r\n' + postdata.length.toString(16).toUpperCase() + '\r\n' + postdata + '\r\n0\r\n\r\n'; // Use Chunked-Encoding
|
||||
_Send(h);
|
||||
//obj.Debug("SEND: " + h); // Display send packet
|
||||
}
|
||||
|
||||
// Websocket relay specific private method
|
||||
obj.parseDigest = function (header) {
|
||||
var t = header.substring(7).split(',');
|
||||
for (i in t) t[i] = t[i].trim();
|
||||
return t.reduce(function (obj, s) { var parts = s.split('='); obj[parts[0]] = parts[1].replace(/"/g, ''); return obj; }, {})
|
||||
}
|
||||
|
||||
// Websocket relay specific private method
|
||||
obj.renderDigest = function (params) {
|
||||
var paramsnames = [];
|
||||
for (i in params) { paramsnames.push(i); }
|
||||
return 'Digest ' + paramsnames.reduce(function (s1, ii) { return s1 + ',' + ii + '="' + params[ii] + '"' }, '').substring(1);
|
||||
}
|
||||
|
||||
// Websocket relay specific private method
|
||||
obj.xxConnectHttpSocket = function () {
|
||||
//obj.Debug("xxConnectHttpSocket");
|
||||
obj.socketParseState = 0;
|
||||
obj.socketAccumulator = '';
|
||||
obj.socketHeader = null;
|
||||
obj.socketData = '';
|
||||
obj.socketState = 1;
|
||||
|
||||
obj.socket = new WebSocket(window.location.protocol.replace("http", "ws") + "//" + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/webrelay.ashx?p=1&host=" + obj.host + "&port=" + obj.port + "&tls=" + obj.tls + ((user == '*') ? "&serverauth=1" : "") + ((typeof pass === "undefined") ? ("&serverauth=1&user=" + user) : "")); // The "p=1" indicates to the relay that this is a WSMAN session
|
||||
obj.socket.onopen = _OnSocketConnected;
|
||||
obj.socket.onmessage = _OnMessage;
|
||||
obj.socket.onclose = _OnSocketClosed;
|
||||
}
|
||||
|
||||
// Websocket relay specific private method
|
||||
function _OnSocketConnected() {
|
||||
//obj.Debug("xxOnSocketConnected");
|
||||
obj.socketState = 2;
|
||||
for (i in obj.pendingAjaxCall) { obj.sendRequest(obj.pendingAjaxCall[i][0], obj.pendingAjaxCall[i][3], obj.pendingAjaxCall[i][4]); }
|
||||
}
|
||||
|
||||
function _OnMessage(e) {
|
||||
if (typeof e.data == 'object') {
|
||||
var f = new FileReader();
|
||||
if (f.readAsBinaryString) {
|
||||
// Chrome & Firefox (Draft)
|
||||
f.onload = function (e) { _OnSocketData(e.target.result); }
|
||||
f.readAsBinaryString(new Blob([e.data]));
|
||||
} else if (f.readAsArrayBuffer) {
|
||||
// Chrome & Firefox (Spec)
|
||||
f.onloadend = function (e) { _OnSocketData(e.target.result); }
|
||||
f.readAsArrayBuffer(e.data);
|
||||
} else {
|
||||
// IE10, readAsBinaryString does not exist, use an alternative.
|
||||
var binary = "";
|
||||
var bytes = new Uint8Array(e.data);
|
||||
var length = bytes.byteLength;
|
||||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||||
_OnSocketData(binary);
|
||||
}
|
||||
} else if (typeof e.data == 'string') {
|
||||
// We got a string object
|
||||
_OnSocketData(e.data);
|
||||
}
|
||||
};
|
||||
|
||||
// Websocket relay specific private method
|
||||
function _OnSocketData(data) {
|
||||
//obj.Debug("_OnSocketData (" + data.length + "): " + data);
|
||||
|
||||
if (typeof data === 'object') {
|
||||
// This is an ArrayBuffer, convert it to a string array (used in IE)
|
||||
var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
|
||||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||||
data = binary;
|
||||
}
|
||||
else if (typeof data !== 'string') return;
|
||||
|
||||
//console.log("RECV: " + data); // DEBUG
|
||||
|
||||
obj.socketAccumulator += data;
|
||||
while (true) {
|
||||
if (obj.socketParseState == 0) {
|
||||
var headersize = obj.socketAccumulator.indexOf("\r\n\r\n");
|
||||
if (headersize < 0) return;
|
||||
//obj.Debug(obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header
|
||||
obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split("\r\n");
|
||||
obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4);
|
||||
obj.socketParseState = 1;
|
||||
obj.socketData = '';
|
||||
obj.socketXHeader = { Directive: obj.socketHeader[0].split(' ') };
|
||||
for (i in obj.socketHeader) {
|
||||
if (i != 0) {
|
||||
var x2 = obj.socketHeader[i].indexOf(':');
|
||||
obj.socketXHeader[obj.socketHeader[i].substring(0, x2).toLowerCase()] = obj.socketHeader[i].substring(x2 + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (obj.socketParseState == 1) {
|
||||
var csize = -1;
|
||||
if ((obj.socketXHeader["connection"] != undefined) && (obj.socketXHeader["connection"].toLowerCase() == 'close') && ((obj.socketXHeader["transfer-encoding"] == undefined) || (obj.socketXHeader["transfer-encoding"].toLowerCase() != 'chunked'))) {
|
||||
// The body ends with a close, in this case, we will only process the header
|
||||
csize = 0;
|
||||
} else if (obj.socketXHeader["content-length"] != undefined) {
|
||||
// The body length is specified by the content-length
|
||||
csize = parseInt(obj.socketXHeader["content-length"]);
|
||||
if (obj.socketAccumulator.length < csize) return;
|
||||
var data = obj.socketAccumulator.substring(0, csize);
|
||||
obj.socketAccumulator = obj.socketAccumulator.substring(csize);
|
||||
obj.socketData = data;
|
||||
csize = 0;
|
||||
} else {
|
||||
// The body is chunked
|
||||
var clen = obj.socketAccumulator.indexOf("\r\n");
|
||||
if (clen < 0) return; // Chunk length not found, exit now and get more data.
|
||||
// Chunk length if found, lets see if we can get the data.
|
||||
csize = parseInt(obj.socketAccumulator.substring(0, clen), 16);
|
||||
if (isNaN(csize)) { if (obj.websocket) { obj.websocket.close(); } return; } // Critical error, close the socket and exit.
|
||||
if (obj.socketAccumulator.length < clen + 2 + csize + 2) return;
|
||||
// We got a chunk with all of the data, handle the chunck now.
|
||||
var data = obj.socketAccumulator.substring(clen + 2, clen + 2 + csize);
|
||||
obj.socketAccumulator = obj.socketAccumulator.substring(clen + 2 + csize + 2);
|
||||
obj.socketData += data;
|
||||
}
|
||||
if (csize == 0) {
|
||||
//obj.Debug("_OnSocketData DONE: (" + obj.socketData.length + "): " + obj.socketData);
|
||||
_ProcessHttpResponse(obj.socketXHeader, obj.socketData);
|
||||
obj.socketParseState = 0;
|
||||
obj.socketHeader = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Websocket relay specific private method
|
||||
function _ProcessHttpResponse(header, data) {
|
||||
//obj.Debug("_ProcessHttpResponse: " + header.Directive[1]);
|
||||
|
||||
var s = parseInt(header.Directive[1]);
|
||||
if (isNaN(s)) s = 602;
|
||||
if (s == 401 && ++(obj.authcounter) < 3) {
|
||||
obj.challengeParams = obj.parseDigest(header['www-authenticate']); // Set the digest parameters, after this, the socket will close and we will auto-retry
|
||||
} else {
|
||||
var r = obj.pendingAjaxCall.shift();
|
||||
// if (s != 200) { obj.Debug("Error, status=" + s + "\r\n\r\nreq=" + r[0] + "\r\n\r\nresp=" + data); } // Debug: Display the request & response if something did not work.
|
||||
obj.authcounter = 0;
|
||||
obj.ActiveAjaxCount--;
|
||||
obj.gotNextMessages(data, 'success', { status: s }, r);
|
||||
obj.PerformNextAjax();
|
||||
}
|
||||
}
|
||||
|
||||
// Websocket relay specific private method
|
||||
function _OnSocketClosed(data) {
|
||||
//obj.Debug("_OnSocketClosed");
|
||||
obj.socketState = 0;
|
||||
if (obj.socket != null) { obj.socket.close(); obj.socket = null; }
|
||||
if (obj.pendingAjaxCall.length > 0) {
|
||||
var r = obj.pendingAjaxCall.shift();
|
||||
var retry = r[5];
|
||||
obj.PerformAjaxExNodeJS2(r[0], r[1], r[2], r[3], r[4], --retry);
|
||||
}
|
||||
}
|
||||
|
||||
// Websocket relay specific private method
|
||||
function _Send(x) {
|
||||
//console.log("SEND: " + x); // DEBUG
|
||||
if (obj.socketState == 2 && obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
|
||||
var b = new Uint8Array(x.length);
|
||||
for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
|
||||
try { obj.socket.send(b.buffer); } catch (e) { }
|
||||
}
|
||||
}
|
||||
|
||||
// Private method
|
||||
obj.gotNextMessages = function (data, status, request, callArgs) {
|
||||
if (obj.FailAllError == 999) return;
|
||||
if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
|
||||
if (request.status != 200) { callArgs[1](null, request.status, callArgs[2]); return; }
|
||||
callArgs[1](data, 200, callArgs[2]);
|
||||
}
|
||||
|
||||
// Private method
|
||||
obj.gotNextMessagesError = function (request, status, errorThrown, callArgs) {
|
||||
if (obj.FailAllError == 999) return;
|
||||
if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
|
||||
callArgs[1](obj, null, { Header: { HttpError: request.status } }, request.status, callArgs[2]);
|
||||
}
|
||||
|
||||
// Cancel all pending queries with given status
|
||||
obj.CancelAllQueries = function (s) {
|
||||
while (obj.PendingAjax.length > 0) { var x = obj.PendingAjax.shift(); x[1](null, s, x[2]); }
|
||||
if (obj.websocket != null) { obj.websocket.close(); obj.websocket = null; obj.socketState = 0; }
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* @description Set of short commonly used methods for handling HTML elements
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.1b
|
||||
*/
|
||||
|
||||
// Add startsWith for IE browser
|
||||
if (!String.prototype.startsWith) { String.prototype.startsWith = function (str) { return this.lastIndexOf(str, 0) === 0; }; }
|
||||
if (!String.prototype.endsWith) { String.prototype.endsWith = function (str) { return this.indexOf(str, this.length - str.length) !== -1; }; }
|
||||
|
||||
// Quick UI functions, a bit of a replacement for jQuery
|
||||
//function Q(x) { if (document.getElementById(x) == null) { console.log('Invalid element: ' + x); } return document.getElementById(x); } // "Q"
|
||||
function Q(x) { return document.getElementById(x); } // "Q"
|
||||
function QS(x) { try { return Q(x).style; } catch (x) { } } // "Q" style
|
||||
function QE(x, y) { try { Q(x).disabled = !y; } catch (x) { } } // "Q" enable
|
||||
function QV(x, y) { try { QS(x).display = (y ? '' : 'none'); } catch (x) { } } // "Q" visible
|
||||
function QA(x, y) { Q(x).innerHTML += y; } // "Q" append
|
||||
function QH(x, y) { Q(x).innerHTML = y; } // "Q" html
|
||||
|
||||
// Move cursor to end of input box
|
||||
function inputBoxFocus(x) { Q(x).focus(); var v = Q(x).value; Q(x).value = ''; Q(x).value = v; }
|
||||
|
||||
// Binary encoding and decoding functions
|
||||
function ReadShort(v, p) { return (v.charCodeAt(p) << 8) + v.charCodeAt(p + 1); }
|
||||
function ReadShortX(v, p) { return (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }
|
||||
function ReadInt(v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); } // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
|
||||
function ReadSInt(v, p) { return (v.charCodeAt(p) << 24) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); }
|
||||
function ReadIntX(v, p) { return (v.charCodeAt(p + 3) * 0x1000000) + (v.charCodeAt(p + 2) << 16) + (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }
|
||||
function ShortToStr(v) { return String.fromCharCode((v >> 8) & 0xFF, v & 0xFF); }
|
||||
function ShortToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); }
|
||||
function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); }
|
||||
function IntToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); }
|
||||
function MakeToArray(v) { if (!v || v == null || typeof v == 'object') return v; return [v]; }
|
||||
function SplitArray(v) { return v.split(','); }
|
||||
function Clone(v) { return JSON.parse(JSON.stringify(v)); }
|
||||
function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, '''); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
|
||||
function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, ''').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, ' '); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
|
||||
|
||||
// Move an element from one position in an array to a new position
|
||||
function ArrayElementMove(arr, from, to) { arr.splice(to, 0, arr.splice(from, 1)[0]); };
|
||||
|
||||
// Print object for HTML
|
||||
function ObjectToStringEx(x, c) {
|
||||
var r = "";
|
||||
if (x != 0 && (!x || x == null)) return "(Null)";
|
||||
if (x instanceof Array) { for (var i in x) { r += '<br />' + gap(c) + "Item #" + i + ": " + ObjectToStringEx(x[i], c + 1); } }
|
||||
else if (x instanceof Object) { for (var i in x) { r += '<br />' + gap(c) + i + " = " + ObjectToStringEx(x[i], c + 1); } }
|
||||
else { r += EscapeHtml(x); }
|
||||
return r;
|
||||
}
|
||||
|
||||
// Print object for console
|
||||
function ObjectToStringEx2(x, c) {
|
||||
var r = "";
|
||||
if (x != 0 && (!x || x == null)) return "(Null)";
|
||||
if (x instanceof Array) { for (var i in x) { r += '\r\n' + gap2(c) + "Item #" + i + ": " + ObjectToStringEx2(x[i], c + 1); } }
|
||||
else if (x instanceof Object) { for (var i in x) { r += '\r\n' + gap2(c) + i + " = " + ObjectToStringEx2(x[i], c + 1); } }
|
||||
else { r += EscapeHtml(x); }
|
||||
return r;
|
||||
}
|
||||
|
||||
// Create an ident gap
|
||||
function gap(c) { var x = ''; for (var i = 0; i < (c * 4) ; i++) { x += ' '; } return x; }
|
||||
function gap2(c) { var x = ''; for (var i = 0; i < (c * 4) ; i++) { x += ' '; } return x; }
|
||||
|
||||
// Print an object in html
|
||||
function ObjectToString(x) { return ObjectToStringEx(x, 0); }
|
||||
function ObjectToString2(x) { return ObjectToStringEx2(x, 0); }
|
||||
|
||||
// Convert a hex string to a raw string
|
||||
function hex2rstr(d) {
|
||||
if (typeof d != "string" || d.length == 0) return '';
|
||||
var r = '', m = ('' + d).match(/../g), t;
|
||||
while (t = m.shift()) r += String.fromCharCode('0x' + t);
|
||||
return r
|
||||
}
|
||||
|
||||
// Convert decimal to hex
|
||||
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
|
||||
|
||||
// Convert a raw string to a hex string
|
||||
function rstr2hex(input) {
|
||||
var r = '', i;
|
||||
for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); }
|
||||
return r;
|
||||
}
|
||||
|
||||
// UTF-8 encoding & decoding functions
|
||||
function encode_utf8(s) { return unescape(encodeURIComponent(s)); }
|
||||
function decode_utf8(s) { return decodeURIComponent(escape(s)); }
|
||||
|
||||
// Convert a string into a blob
|
||||
function data2blob(data) {
|
||||
var bytes = new Array(data.length);
|
||||
for (var i = 0; i < data.length; i++) bytes[i] = data.charCodeAt(i);
|
||||
var blob = new Blob([new Uint8Array(bytes)]);
|
||||
return blob;
|
||||
}
|
||||
|
||||
// Generate random numbers
|
||||
function random(max) { return Math.floor(Math.random() * max); }
|
||||
|
||||
// Trademarks
|
||||
function trademarks(x) { return x.replace(/\(R\)/g, '®').replace(/\(TM\)/g, '™'); }
|
|
@ -0,0 +1,270 @@
|
|||
/* FileSaver.js
|
||||
* A saveAs() FileSaver implementation.
|
||||
* 1.1.20151003
|
||||
*
|
||||
* By Eli Grey, http://eligrey.com
|
||||
* License: MIT
|
||||
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/*global self */
|
||||
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
|
||||
|
||||
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
|
||||
|
||||
var saveAs = saveAs || (function (view) {
|
||||
"use strict";
|
||||
// IE <10 is explicitly unsupported
|
||||
if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
|
||||
return;
|
||||
}
|
||||
var
|
||||
doc = view.document
|
||||
// only get URL when necessary in case Blob.js hasn't overridden it yet
|
||||
, get_URL = function () {
|
||||
return view.URL || view.webkitURL || view;
|
||||
}
|
||||
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
|
||||
, can_use_save_link = "download" in save_link
|
||||
, click = function (node) {
|
||||
var event = new MouseEvent("click");
|
||||
node.dispatchEvent(event);
|
||||
}
|
||||
, is_safari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent)
|
||||
, webkit_req_fs = view.webkitRequestFileSystem
|
||||
, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
|
||||
, throw_outside = function (ex) {
|
||||
(view.setImmediate || view.setTimeout)(function () {
|
||||
throw ex;
|
||||
}, 0);
|
||||
}
|
||||
, force_saveable_type = "application/octet-stream"
|
||||
, fs_min_size = 0
|
||||
// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and
|
||||
// https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047
|
||||
// for the reasoning behind the timeout and revocation flow
|
||||
, arbitrary_revoke_timeout = 500 // in ms
|
||||
, revoke = function (file) {
|
||||
var revoker = function () {
|
||||
if (typeof file === "string") { // file is an object URL
|
||||
get_URL().revokeObjectURL(file);
|
||||
} else { // file is a File
|
||||
file.remove();
|
||||
}
|
||||
};
|
||||
if (view.chrome) {
|
||||
revoker();
|
||||
} else {
|
||||
setTimeout(revoker, arbitrary_revoke_timeout);
|
||||
}
|
||||
}
|
||||
, dispatch = function (filesaver, event_types, event) {
|
||||
event_types = [].concat(event_types);
|
||||
var i = event_types.length;
|
||||
while (i--) {
|
||||
var listener = filesaver["on" + event_types[i]];
|
||||
if (typeof listener === "function") {
|
||||
try {
|
||||
listener.call(filesaver, event || filesaver);
|
||||
} catch (ex) {
|
||||
throw_outside(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
, auto_bom = function (blob) {
|
||||
// prepend BOM for UTF-8 XML and text types (including HTML)
|
||||
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
|
||||
return new Blob(["\ufeff", blob], { type: blob.type });
|
||||
}
|
||||
return blob;
|
||||
}
|
||||
, FileSaver = function (blob, name, no_auto_bom) {
|
||||
if (!no_auto_bom) {
|
||||
blob = auto_bom(blob);
|
||||
}
|
||||
// First try a.download, then web filesystem, then object URLs
|
||||
var
|
||||
filesaver = this
|
||||
, type = blob.type
|
||||
, blob_changed = false
|
||||
, object_url
|
||||
, target_view
|
||||
, dispatch_all = function () {
|
||||
dispatch(filesaver, "writestart progress write writeend".split(" "));
|
||||
}
|
||||
// on any filesys errors revert to saving with object URLs
|
||||
, fs_error = function () {
|
||||
if (target_view && is_safari && typeof FileReader !== "undefined") {
|
||||
// Safari doesn't allow downloading of blob urls
|
||||
var reader = new FileReader();
|
||||
reader.onloadend = function () {
|
||||
var base64Data = reader.result;
|
||||
target_view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/));
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch_all();
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
filesaver.readyState = filesaver.INIT;
|
||||
return;
|
||||
}
|
||||
// don't create more object URLs than needed
|
||||
if (blob_changed || !object_url) {
|
||||
object_url = get_URL().createObjectURL(blob);
|
||||
}
|
||||
if (target_view) {
|
||||
target_view.location.href = object_url;
|
||||
} else {
|
||||
var new_tab = view.open(object_url, "_blank");
|
||||
if (new_tab == undefined && is_safari) {
|
||||
//Apple do not allow window.open, see http://bit.ly/1kZffRI
|
||||
view.location.href = object_url
|
||||
}
|
||||
}
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch_all();
|
||||
revoke(object_url);
|
||||
}
|
||||
, abortable = function (func) {
|
||||
return function () {
|
||||
if (filesaver.readyState !== filesaver.DONE) {
|
||||
return func.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
, create_if_not_found = { create: true, exclusive: false }
|
||||
, slice
|
||||
;
|
||||
filesaver.readyState = filesaver.INIT;
|
||||
if (!name) {
|
||||
name = "download";
|
||||
}
|
||||
if (can_use_save_link) {
|
||||
object_url = get_URL().createObjectURL(blob);
|
||||
save_link.href = object_url;
|
||||
save_link.download = name;
|
||||
setTimeout(function () {
|
||||
click(save_link);
|
||||
dispatch_all();
|
||||
revoke(object_url);
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Object and web filesystem URLs have a problem saving in Google Chrome when
|
||||
// viewed in a tab, so I force save with application/octet-stream
|
||||
// http://code.google.com/p/chromium/issues/detail?id=91158
|
||||
// Update: Google errantly closed 91158, I submitted it again:
|
||||
// https://code.google.com/p/chromium/issues/detail?id=389642
|
||||
if (view.chrome && type && type !== force_saveable_type) {
|
||||
slice = blob.slice || blob.webkitSlice;
|
||||
blob = slice.call(blob, 0, blob.size, force_saveable_type);
|
||||
blob_changed = true;
|
||||
}
|
||||
// Since I can't be sure that the guessed media type will trigger a download
|
||||
// in WebKit, I append .download to the filename.
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=65440
|
||||
if (webkit_req_fs && name !== "download") {
|
||||
name += ".download";
|
||||
}
|
||||
if (type === force_saveable_type || webkit_req_fs) {
|
||||
target_view = view;
|
||||
}
|
||||
if (!req_fs) {
|
||||
fs_error();
|
||||
return;
|
||||
}
|
||||
fs_min_size += blob.size;
|
||||
req_fs(view.TEMPORARY, fs_min_size, abortable(function (fs) {
|
||||
fs.root.getDirectory("saved", create_if_not_found, abortable(function (dir) {
|
||||
var save = function () {
|
||||
dir.getFile(name, create_if_not_found, abortable(function (file) {
|
||||
file.createWriter(abortable(function (writer) {
|
||||
writer.onwriteend = function (event) {
|
||||
target_view.location.href = file.toURL();
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch(filesaver, "writeend", event);
|
||||
revoke(file);
|
||||
};
|
||||
writer.onerror = function () {
|
||||
var error = writer.error;
|
||||
if (error.code !== error.ABORT_ERR) {
|
||||
fs_error();
|
||||
}
|
||||
};
|
||||
"writestart progress write abort".split(" ").forEach(function (event) {
|
||||
writer["on" + event] = filesaver["on" + event];
|
||||
});
|
||||
writer.write(blob);
|
||||
filesaver.abort = function () {
|
||||
writer.abort();
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
};
|
||||
filesaver.readyState = filesaver.WRITING;
|
||||
}), fs_error);
|
||||
}), fs_error);
|
||||
};
|
||||
dir.getFile(name, { create: false }, abortable(function (file) {
|
||||
// delete file if it already exists
|
||||
file.remove();
|
||||
save();
|
||||
}), abortable(function (ex) {
|
||||
if (ex.code === ex.NOT_FOUND_ERR) {
|
||||
save();
|
||||
} else {
|
||||
fs_error();
|
||||
}
|
||||
}));
|
||||
}), fs_error);
|
||||
}), fs_error);
|
||||
}
|
||||
, FS_proto = FileSaver.prototype
|
||||
, saveAs = function (blob, name, no_auto_bom) {
|
||||
return new FileSaver(blob, name, no_auto_bom);
|
||||
}
|
||||
;
|
||||
// IE 10+ (native saveAs)
|
||||
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
|
||||
return function (blob, name, no_auto_bom) {
|
||||
if (!no_auto_bom) {
|
||||
blob = auto_bom(blob);
|
||||
}
|
||||
return navigator.msSaveOrOpenBlob(blob, name || "download");
|
||||
};
|
||||
}
|
||||
|
||||
FS_proto.abort = function () {
|
||||
var filesaver = this;
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch(filesaver, "abort");
|
||||
};
|
||||
FS_proto.readyState = FS_proto.INIT = 0;
|
||||
FS_proto.WRITING = 1;
|
||||
FS_proto.DONE = 2;
|
||||
|
||||
FS_proto.error =
|
||||
FS_proto.onwritestart =
|
||||
FS_proto.onprogress =
|
||||
FS_proto.onwrite =
|
||||
FS_proto.onabort =
|
||||
FS_proto.onerror =
|
||||
FS_proto.onwriteend =
|
||||
null;
|
||||
|
||||
return saveAs;
|
||||
}(
|
||||
typeof self !== "undefined" && self
|
||||
|| typeof window !== "undefined" && window
|
||||
|| this.content
|
||||
));
|
||||
// `self` is undefined in Firefox for Android content script context
|
||||
// while `this` is nsIContentFrameMessageManager
|
||||
// with an attribute `content` that corresponds to the window
|
||||
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports.saveAs = saveAs;
|
||||
} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) {
|
||||
define([], function () {
|
||||
return saveAs;
|
||||
});
|
||||
}
|
|
@ -0,0 +1,732 @@
|
|||
/*
|
||||
* Port of script by Masanao Izumo.
|
||||
*
|
||||
* Wrapped all the variables in a function, created a
|
||||
* constructor for interacting with the lib. Everything
|
||||
* else was written by M. Izumo.
|
||||
*
|
||||
* Original code can be found here: http://www.onicos.com/staff/iz/amuse/javascript/expert/inflate.txt
|
||||
*
|
||||
*/
|
||||
|
||||
var zip_WSIZE = 32768; // Sliding Window size
|
||||
var zip_STORED_BLOCK = 0;
|
||||
var zip_STATIC_TREES = 1;
|
||||
var zip_DYN_TREES = 2;
|
||||
|
||||
/* for inflate */
|
||||
var zip_lbits = 9; // bits in base literal/length lookup table
|
||||
var zip_dbits = 6; // bits in base distance lookup table
|
||||
var zip_INBUFSIZ = 32768; // Input buffer size
|
||||
var zip_INBUF_EXTRA = 64; // Extra buffer
|
||||
|
||||
/* variables (inflate) */
|
||||
var zip_slide;
|
||||
var zip_wp; // current position in slide
|
||||
var zip_fixed_tl = null; // inflate static
|
||||
var zip_fixed_td; // inflate static
|
||||
var zip_fixed_bl, fixed_bd; // inflate static
|
||||
var zip_bit_buf; // bit buffer
|
||||
var zip_bit_len; // bits in bit buffer
|
||||
var zip_method;
|
||||
var zip_eof;
|
||||
var zip_copy_leng;
|
||||
var zip_copy_dist;
|
||||
var zip_tl, zip_td; // literal/length and distance decoder tables
|
||||
var zip_bl, zip_bd; // number of bits decoded by tl and td
|
||||
|
||||
var zip_inflate_data;
|
||||
var zip_inflate_pos;
|
||||
|
||||
|
||||
/* constant tables (inflate) */
|
||||
var zip_MASK_BITS = new Array(
|
||||
0x0000,
|
||||
0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
|
||||
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff);
|
||||
// Tables for deflate from PKZIP's appnote.txt.
|
||||
var zip_cplens = new Array( // Copy lengths for literal codes 257..285
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
|
||||
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0);
|
||||
/* note: see note #13 above about the 258 in this list. */
|
||||
var zip_cplext = new Array( // Extra bits for literal codes 257..285
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99); // 99==invalid
|
||||
var zip_cpdist = new Array( // Copy offsets for distance codes 0..29
|
||||
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
||||
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
||||
8193, 12289, 16385, 24577);
|
||||
var zip_cpdext = new Array( // Extra bits for distance codes
|
||||
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
|
||||
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
|
||||
12, 12, 13, 13);
|
||||
var zip_border = new Array( // Order of the bit length code lengths
|
||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15);
|
||||
/* objects (inflate) */
|
||||
|
||||
function zip_HuftList() {
|
||||
this.next = null;
|
||||
this.list = null;
|
||||
}
|
||||
|
||||
function zip_HuftNode() {
|
||||
this.e = 0; // number of extra bits or operation
|
||||
this.b = 0; // number of bits in this code or subcode
|
||||
|
||||
// union
|
||||
this.n = 0; // literal, length base, or distance base
|
||||
this.t = null; // (zip_HuftNode) pointer to next level of table
|
||||
}
|
||||
|
||||
function zip_HuftBuild(b, // code lengths in bits (all assumed <= BMAX)
|
||||
n, // number of codes (assumed <= N_MAX)
|
||||
s, // number of simple-valued codes (0..s-1)
|
||||
d, // list of base values for non-simple codes
|
||||
e, // list of extra bits for non-simple codes
|
||||
mm // maximum lookup bits
|
||||
) {
|
||||
this.BMAX = 16; // maximum bit length of any code
|
||||
this.N_MAX = 288; // maximum number of codes in any set
|
||||
this.status = 0; // 0: success, 1: incomplete table, 2: bad input
|
||||
this.root = null; // (zip_HuftList) starting table
|
||||
this.m = 0; // maximum lookup bits, returns actual
|
||||
|
||||
/* Given a list of code lengths and a maximum table size, make a set of
|
||||
tables to decode that set of codes. Return zero on success, one if
|
||||
the given code set is incomplete (the tables are still built in this
|
||||
case), two if the input is invalid (all zero length codes or an
|
||||
oversubscribed set of lengths), and three if not enough memory.
|
||||
The code with value 256 is special, and the tables are constructed
|
||||
so that no bits beyond that code are fetched when that code is
|
||||
decoded. */
|
||||
{
|
||||
var a; // counter for codes of length k
|
||||
var c = new Array(this.BMAX + 1); // bit length count table
|
||||
var el; // length of EOB code (value 256)
|
||||
var f; // i repeats in table every f entries
|
||||
var g; // maximum code length
|
||||
var h; // table level
|
||||
var i; // counter, current code
|
||||
var j; // counter
|
||||
var k; // number of bits in current code
|
||||
var lx = new Array(this.BMAX + 1); // stack of bits per table
|
||||
var p; // pointer into c[], b[], or v[]
|
||||
var pidx; // index of p
|
||||
var q; // (zip_HuftNode) points to current table
|
||||
var r = new zip_HuftNode(); // table entry for structure assignment
|
||||
var u = new Array(this.BMAX); // zip_HuftNode[BMAX][] table stack
|
||||
var v = new Array(this.N_MAX); // values in order of bit length
|
||||
var w;
|
||||
var x = new Array(this.BMAX + 1);// bit offsets, then code stack
|
||||
var xp; // pointer into x or c
|
||||
var y; // number of dummy codes added
|
||||
var z; // number of entries in current table
|
||||
var o;
|
||||
var tail; // (zip_HuftList)
|
||||
|
||||
tail = this.root = null;
|
||||
for (i = 0; i < c.length; i++)
|
||||
c[i] = 0;
|
||||
for (i = 0; i < lx.length; i++)
|
||||
lx[i] = 0;
|
||||
for (i = 0; i < u.length; i++)
|
||||
u[i] = null;
|
||||
for (i = 0; i < v.length; i++)
|
||||
v[i] = 0;
|
||||
for (i = 0; i < x.length; i++)
|
||||
x[i] = 0;
|
||||
|
||||
// Generate counts for each bit length
|
||||
el = n > 256 ? b[256] : this.BMAX; // set length of EOB code, if any
|
||||
p = b; pidx = 0;
|
||||
i = n;
|
||||
do {
|
||||
c[p[pidx]]++; // assume all entries <= BMAX
|
||||
pidx++;
|
||||
} while (--i > 0);
|
||||
if (c[0] == n) { // null input--all zero length codes
|
||||
this.root = null;
|
||||
this.m = 0;
|
||||
this.status = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find minimum and maximum length, bound *m by those
|
||||
for (j = 1; j <= this.BMAX; j++)
|
||||
if (c[j] != 0)
|
||||
break;
|
||||
k = j; // minimum code length
|
||||
if (mm < j)
|
||||
mm = j;
|
||||
for (i = this.BMAX; i != 0; i--)
|
||||
if (c[i] != 0)
|
||||
break;
|
||||
g = i; // maximum code length
|
||||
if (mm > i)
|
||||
mm = i;
|
||||
|
||||
// Adjust last length count to fill out codes, if needed
|
||||
for (y = 1 << j; j < i; j++, y <<= 1)
|
||||
if ((y -= c[j]) < 0) {
|
||||
this.status = 2; // bad input: more codes than bits
|
||||
this.m = mm;
|
||||
return;
|
||||
}
|
||||
if ((y -= c[i]) < 0) {
|
||||
this.status = 2;
|
||||
this.m = mm;
|
||||
return;
|
||||
}
|
||||
c[i] += y;
|
||||
|
||||
// Generate starting offsets into the value table for each length
|
||||
x[1] = j = 0;
|
||||
p = c;
|
||||
pidx = 1;
|
||||
xp = 2;
|
||||
while (--i > 0) // note that i == g from above
|
||||
x[xp++] = (j += p[pidx++]);
|
||||
|
||||
// Make a table of values in order of bit lengths
|
||||
p = b; pidx = 0;
|
||||
i = 0;
|
||||
do {
|
||||
if ((j = p[pidx++]) != 0)
|
||||
v[x[j]++] = i;
|
||||
} while (++i < n);
|
||||
n = x[g]; // set n to length of v
|
||||
|
||||
// Generate the Huffman codes and for each, make the table entries
|
||||
x[0] = i = 0; // first Huffman code is zero
|
||||
p = v; pidx = 0; // grab values in bit order
|
||||
h = -1; // no tables yet--level -1
|
||||
w = lx[0] = 0; // no bits decoded yet
|
||||
q = null; // ditto
|
||||
z = 0; // ditto
|
||||
|
||||
// go through the bit lengths (k already is bits in shortest code)
|
||||
for (; k <= g; k++) {
|
||||
a = c[k];
|
||||
while (a-- > 0) {
|
||||
// here i is the Huffman code of length k bits for value p[pidx]
|
||||
// make tables up to required level
|
||||
while (k > w + lx[1 + h]) {
|
||||
w += lx[1 + h]; // add bits already decoded
|
||||
h++;
|
||||
|
||||
// compute minimum size table less than or equal to *m bits
|
||||
z = (z = g - w) > mm ? mm : z; // upper limit
|
||||
if ((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table
|
||||
// too few codes for k-w bit table
|
||||
f -= a + 1; // deduct codes from patterns left
|
||||
xp = k;
|
||||
while (++j < z) { // try smaller tables up to z bits
|
||||
if ((f <<= 1) <= c[++xp])
|
||||
break; // enough codes to use up j bits
|
||||
f -= c[xp]; // else deduct codes from patterns
|
||||
}
|
||||
}
|
||||
if (w + j > el && w < el)
|
||||
j = el - w; // make EOB code end at table
|
||||
z = 1 << j; // table entries for j-bit table
|
||||
lx[1 + h] = j; // set table size in stack
|
||||
|
||||
// allocate and link in new table
|
||||
q = new Array(z);
|
||||
for (o = 0; o < z; o++) {
|
||||
q[o] = new zip_HuftNode();
|
||||
}
|
||||
|
||||
if (tail == null)
|
||||
tail = this.root = new zip_HuftList();
|
||||
else
|
||||
tail = tail.next = new zip_HuftList();
|
||||
tail.next = null;
|
||||
tail.list = q;
|
||||
u[h] = q; // table starts after link
|
||||
|
||||
/* connect to last table, if there is one */
|
||||
if (h > 0) {
|
||||
x[h] = i; // save pattern for backing up
|
||||
r.b = lx[h]; // bits to dump before this table
|
||||
r.e = 16 + j; // bits in this table
|
||||
r.t = q; // pointer to this table
|
||||
j = (i & ((1 << w) - 1)) >> (w - lx[h]);
|
||||
u[h - 1][j].e = r.e;
|
||||
u[h - 1][j].b = r.b;
|
||||
u[h - 1][j].n = r.n;
|
||||
u[h - 1][j].t = r.t;
|
||||
}
|
||||
}
|
||||
|
||||
// set up table entry in r
|
||||
r.b = k - w;
|
||||
if (pidx >= n)
|
||||
r.e = 99; // out of values--invalid code
|
||||
else if (p[pidx] < s) {
|
||||
r.e = (p[pidx] < 256 ? 16 : 15); // 256 is end-of-block code
|
||||
r.n = p[pidx++]; // simple code is just the value
|
||||
} else {
|
||||
r.e = e[p[pidx] - s]; // non-simple--look up in lists
|
||||
r.n = d[p[pidx++] - s];
|
||||
}
|
||||
|
||||
// fill code-like entries with r //
|
||||
f = 1 << (k - w);
|
||||
for (j = i >> w; j < z; j += f) {
|
||||
q[j].e = r.e;
|
||||
q[j].b = r.b;
|
||||
q[j].n = r.n;
|
||||
q[j].t = r.t;
|
||||
}
|
||||
|
||||
// backwards increment the k-bit code i
|
||||
for (j = 1 << (k - 1) ; (i & j) != 0; j >>= 1)
|
||||
i ^= j;
|
||||
i ^= j;
|
||||
|
||||
// backup over finished tables
|
||||
while ((i & ((1 << w) - 1)) != x[h]) {
|
||||
w -= lx[h]; // don't need to update q
|
||||
h--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return actual size of base table */
|
||||
this.m = lx[1];
|
||||
|
||||
/* Return true (1) if we were given an incomplete table */
|
||||
this.status = ((y != 0 && g != 1) ? 1 : 0);
|
||||
} /* end of constructor */
|
||||
}
|
||||
|
||||
|
||||
/* routines (inflate) */
|
||||
|
||||
function zip_GET_BYTE() {
|
||||
if (zip_inflate_data.length == zip_inflate_pos)
|
||||
return -1;
|
||||
return zip_inflate_data.charCodeAt(zip_inflate_pos++) & 0xff;
|
||||
}
|
||||
|
||||
function zip_NEEDBITS(n) {
|
||||
while (zip_bit_len < n) {
|
||||
zip_bit_buf |= zip_GET_BYTE() << zip_bit_len;
|
||||
zip_bit_len += 8;
|
||||
}
|
||||
}
|
||||
|
||||
function zip_GETBITS(n) {
|
||||
return zip_bit_buf & zip_MASK_BITS[n];
|
||||
}
|
||||
|
||||
function zip_DUMPBITS(n) {
|
||||
zip_bit_buf >>= n;
|
||||
zip_bit_len -= n;
|
||||
}
|
||||
|
||||
function zip_inflate_codes(buff, off, size) {
|
||||
/* inflate (decompress) the codes in a deflated (compressed) block.
|
||||
Return an error code or zero if it all goes ok. */
|
||||
var e; // table entry flag/number of extra bits
|
||||
var t; // (zip_HuftNode) pointer to table entry
|
||||
var n;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
// inflate the coded data
|
||||
n = 0;
|
||||
for (; ;) { // do until end of block
|
||||
zip_NEEDBITS(zip_bl);
|
||||
t = zip_tl.list[zip_GETBITS(zip_bl)];
|
||||
e = t.e;
|
||||
while (e > 16) {
|
||||
if (e == 99)
|
||||
return -1;
|
||||
zip_DUMPBITS(t.b);
|
||||
e -= 16;
|
||||
zip_NEEDBITS(e);
|
||||
t = t.t[zip_GETBITS(e)];
|
||||
e = t.e;
|
||||
}
|
||||
zip_DUMPBITS(t.b);
|
||||
|
||||
if (e == 16) { // then it's a literal
|
||||
zip_wp &= zip_WSIZE - 1;
|
||||
buff[off + n++] = zip_slide[zip_wp++] = t.n;
|
||||
if (n == size)
|
||||
return size;
|
||||
continue;
|
||||
}
|
||||
|
||||
// exit if end of block
|
||||
if (e == 15)
|
||||
break;
|
||||
|
||||
// it's an EOB or a length
|
||||
|
||||
// get length of block to copy
|
||||
zip_NEEDBITS(e);
|
||||
zip_copy_leng = t.n + zip_GETBITS(e);
|
||||
zip_DUMPBITS(e);
|
||||
|
||||
// decode distance of block to copy
|
||||
zip_NEEDBITS(zip_bd);
|
||||
t = zip_td.list[zip_GETBITS(zip_bd)];
|
||||
e = t.e;
|
||||
|
||||
while (e > 16) {
|
||||
if (e == 99)
|
||||
return -1;
|
||||
zip_DUMPBITS(t.b);
|
||||
e -= 16;
|
||||
zip_NEEDBITS(e);
|
||||
t = t.t[zip_GETBITS(e)];
|
||||
e = t.e;
|
||||
}
|
||||
zip_DUMPBITS(t.b);
|
||||
zip_NEEDBITS(e);
|
||||
zip_copy_dist = zip_wp - t.n - zip_GETBITS(e);
|
||||
zip_DUMPBITS(e);
|
||||
|
||||
// do the copy
|
||||
while (zip_copy_leng > 0 && n < size) {
|
||||
zip_copy_leng--;
|
||||
zip_copy_dist &= zip_WSIZE - 1;
|
||||
zip_wp &= zip_WSIZE - 1;
|
||||
buff[off + n++] = zip_slide[zip_wp++]
|
||||
= zip_slide[zip_copy_dist++];
|
||||
}
|
||||
|
||||
if (n == size)
|
||||
return size;
|
||||
}
|
||||
|
||||
zip_method = -1; // done
|
||||
return n;
|
||||
}
|
||||
|
||||
function zip_inflate_stored(buff, off, size) {
|
||||
/* "decompress" an inflated type 0 (stored) block. */
|
||||
var n;
|
||||
|
||||
// go to byte boundary
|
||||
n = zip_bit_len & 7;
|
||||
zip_DUMPBITS(n);
|
||||
|
||||
// get the length and its complement
|
||||
zip_NEEDBITS(16);
|
||||
n = zip_GETBITS(16);
|
||||
zip_DUMPBITS(16);
|
||||
zip_NEEDBITS(16);
|
||||
if (n != ((~zip_bit_buf) & 0xffff))
|
||||
return -1; // error in compressed data
|
||||
zip_DUMPBITS(16);
|
||||
|
||||
// read and output the compressed data
|
||||
zip_copy_leng = n;
|
||||
|
||||
n = 0;
|
||||
while (zip_copy_leng > 0 && n < size) {
|
||||
zip_copy_leng--;
|
||||
zip_wp &= zip_WSIZE - 1;
|
||||
zip_NEEDBITS(8);
|
||||
buff[off + n++] = zip_slide[zip_wp++] =
|
||||
zip_GETBITS(8);
|
||||
zip_DUMPBITS(8);
|
||||
}
|
||||
|
||||
if (zip_copy_leng == 0)
|
||||
zip_method = -1; // done
|
||||
return n;
|
||||
}
|
||||
|
||||
function zip_inflate_fixed(buff, off, size) {
|
||||
/* decompress an inflated type 1 (fixed Huffman codes) block. We should
|
||||
either replace this with a custom decoder, or at least precompute the
|
||||
Huffman tables. */
|
||||
|
||||
// if first time, set up tables for fixed blocks
|
||||
if (zip_fixed_tl == null) {
|
||||
var i; // temporary variable
|
||||
var l = new Array(288); // length list for huft_build
|
||||
var h; // zip_HuftBuild
|
||||
|
||||
// literal table
|
||||
for (i = 0; i < 144; i++)
|
||||
l[i] = 8;
|
||||
for (; i < 256; i++)
|
||||
l[i] = 9;
|
||||
for (; i < 280; i++)
|
||||
l[i] = 7;
|
||||
for (; i < 288; i++) // make a complete, but wrong code set
|
||||
l[i] = 8;
|
||||
zip_fixed_bl = 7;
|
||||
|
||||
h = new zip_HuftBuild(l, 288, 257, zip_cplens, zip_cplext,
|
||||
zip_fixed_bl);
|
||||
if (h.status != 0) {
|
||||
alert("HufBuild error: " + h.status);
|
||||
return -1;
|
||||
}
|
||||
zip_fixed_tl = h.root;
|
||||
zip_fixed_bl = h.m;
|
||||
|
||||
// distance table
|
||||
for (i = 0; i < 30; i++) // make an incomplete code set
|
||||
l[i] = 5;
|
||||
zip_fixed_bd = 5;
|
||||
|
||||
h = new zip_HuftBuild(l, 30, 0, zip_cpdist, zip_cpdext, zip_fixed_bd);
|
||||
if (h.status > 1) {
|
||||
zip_fixed_tl = null;
|
||||
alert("HufBuild error: " + h.status);
|
||||
return -1;
|
||||
}
|
||||
zip_fixed_td = h.root;
|
||||
zip_fixed_bd = h.m;
|
||||
}
|
||||
|
||||
zip_tl = zip_fixed_tl;
|
||||
zip_td = zip_fixed_td;
|
||||
zip_bl = zip_fixed_bl;
|
||||
zip_bd = zip_fixed_bd;
|
||||
return zip_inflate_codes(buff, off, size);
|
||||
}
|
||||
|
||||
function zip_inflate_dynamic(buff, off, size) {
|
||||
// decompress an inflated type 2 (dynamic Huffman codes) block.
|
||||
var i; // temporary variables
|
||||
var j;
|
||||
var l; // last length
|
||||
var n; // number of lengths to get
|
||||
var t; // (zip_HuftNode) literal/length code table
|
||||
var nb; // number of bit length codes
|
||||
var nl; // number of literal/length codes
|
||||
var nd; // number of distance codes
|
||||
var ll = new Array(286 + 30); // literal/length and distance code lengths
|
||||
var h; // (zip_HuftBuild)
|
||||
|
||||
for (i = 0; i < ll.length; i++)
|
||||
ll[i] = 0;
|
||||
|
||||
// read in table lengths
|
||||
zip_NEEDBITS(5);
|
||||
nl = 257 + zip_GETBITS(5); // number of literal/length codes
|
||||
zip_DUMPBITS(5);
|
||||
zip_NEEDBITS(5);
|
||||
nd = 1 + zip_GETBITS(5); // number of distance codes
|
||||
zip_DUMPBITS(5);
|
||||
zip_NEEDBITS(4);
|
||||
nb = 4 + zip_GETBITS(4); // number of bit length codes
|
||||
zip_DUMPBITS(4);
|
||||
if (nl > 286 || nd > 30)
|
||||
return -1; // bad lengths
|
||||
|
||||
// read in bit-length-code lengths
|
||||
for (j = 0; j < nb; j++) {
|
||||
zip_NEEDBITS(3);
|
||||
ll[zip_border[j]] = zip_GETBITS(3);
|
||||
zip_DUMPBITS(3);
|
||||
}
|
||||
for (; j < 19; j++)
|
||||
ll[zip_border[j]] = 0;
|
||||
|
||||
// build decoding table for trees--single level, 7 bit lookup
|
||||
zip_bl = 7;
|
||||
h = new zip_HuftBuild(ll, 19, 19, null, null, zip_bl);
|
||||
if (h.status != 0)
|
||||
return -1; // incomplete code set
|
||||
|
||||
zip_tl = h.root;
|
||||
zip_bl = h.m;
|
||||
|
||||
// read in literal and distance code lengths
|
||||
n = nl + nd;
|
||||
i = l = 0;
|
||||
while (i < n) {
|
||||
zip_NEEDBITS(zip_bl);
|
||||
t = zip_tl.list[zip_GETBITS(zip_bl)];
|
||||
j = t.b;
|
||||
zip_DUMPBITS(j);
|
||||
j = t.n;
|
||||
if (j < 16) // length of code in bits (0..15)
|
||||
ll[i++] = l = j; // save last length in l
|
||||
else if (j == 16) { // repeat last length 3 to 6 times
|
||||
zip_NEEDBITS(2);
|
||||
j = 3 + zip_GETBITS(2);
|
||||
zip_DUMPBITS(2);
|
||||
if (i + j > n)
|
||||
return -1;
|
||||
while (j-- > 0)
|
||||
ll[i++] = l;
|
||||
} else if (j == 17) { // 3 to 10 zero length codes
|
||||
zip_NEEDBITS(3);
|
||||
j = 3 + zip_GETBITS(3);
|
||||
zip_DUMPBITS(3);
|
||||
if (i + j > n)
|
||||
return -1;
|
||||
while (j-- > 0)
|
||||
ll[i++] = 0;
|
||||
l = 0;
|
||||
} else { // j == 18: 11 to 138 zero length codes
|
||||
zip_NEEDBITS(7);
|
||||
j = 11 + zip_GETBITS(7);
|
||||
zip_DUMPBITS(7);
|
||||
if (i + j > n)
|
||||
return -1;
|
||||
while (j-- > 0)
|
||||
ll[i++] = 0;
|
||||
l = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// build the decoding tables for literal/length and distance codes
|
||||
zip_bl = zip_lbits;
|
||||
h = new zip_HuftBuild(ll, nl, 257, zip_cplens, zip_cplext, zip_bl);
|
||||
if (zip_bl == 0) // no literals or lengths
|
||||
h.status = 1;
|
||||
if (h.status != 0) {
|
||||
if (h.status == 1)
|
||||
;// **incomplete literal tree**
|
||||
return -1; // incomplete code set
|
||||
}
|
||||
zip_tl = h.root;
|
||||
zip_bl = h.m;
|
||||
|
||||
for (i = 0; i < nd; i++)
|
||||
ll[i] = ll[i + nl];
|
||||
zip_bd = zip_dbits;
|
||||
h = new zip_HuftBuild(ll, nd, 0, zip_cpdist, zip_cpdext, zip_bd);
|
||||
zip_td = h.root;
|
||||
zip_bd = h.m;
|
||||
|
||||
if (zip_bd == 0 && nl > 257) { // lengths but no distances
|
||||
// **incomplete distance tree**
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (h.status == 1) {
|
||||
;// **incomplete distance tree**
|
||||
}
|
||||
if (h.status != 0)
|
||||
return -1;
|
||||
|
||||
// decompress until an end-of-block code
|
||||
return zip_inflate_codes(buff, off, size);
|
||||
}
|
||||
|
||||
function zip_inflate_internal(buff, off, size) {
|
||||
// decompress an inflated entry
|
||||
var n, i;
|
||||
|
||||
n = 0;
|
||||
while (n < size) {
|
||||
if (zip_eof && zip_method == -1)
|
||||
return n;
|
||||
|
||||
if (zip_copy_leng > 0) {
|
||||
if (zip_method != zip_STORED_BLOCK) {
|
||||
// STATIC_TREES or DYN_TREES
|
||||
while (zip_copy_leng > 0 && n < size) {
|
||||
zip_copy_leng--;
|
||||
zip_copy_dist &= zip_WSIZE - 1;
|
||||
zip_wp &= zip_WSIZE - 1;
|
||||
buff[off + n++] = zip_slide[zip_wp++] =
|
||||
zip_slide[zip_copy_dist++];
|
||||
}
|
||||
} else {
|
||||
while (zip_copy_leng > 0 && n < size) {
|
||||
zip_copy_leng--;
|
||||
zip_wp &= zip_WSIZE - 1;
|
||||
zip_NEEDBITS(8);
|
||||
buff[off + n++] = zip_slide[zip_wp++] = zip_GETBITS(8);
|
||||
zip_DUMPBITS(8);
|
||||
}
|
||||
if (zip_copy_leng == 0)
|
||||
zip_method = -1; // done
|
||||
}
|
||||
if (n == size)
|
||||
return n;
|
||||
}
|
||||
|
||||
if (zip_method == -1) {
|
||||
if (zip_eof)
|
||||
break;
|
||||
|
||||
// read in last block bit
|
||||
zip_NEEDBITS(1);
|
||||
if (zip_GETBITS(1) != 0)
|
||||
zip_eof = true;
|
||||
zip_DUMPBITS(1);
|
||||
|
||||
// read in block type
|
||||
zip_NEEDBITS(2);
|
||||
zip_method = zip_GETBITS(2);
|
||||
zip_DUMPBITS(2);
|
||||
zip_tl = null;
|
||||
zip_copy_leng = 0;
|
||||
}
|
||||
|
||||
switch (zip_method) {
|
||||
case 0: // zip_STORED_BLOCK
|
||||
i = zip_inflate_stored(buff, off + n, size - n);
|
||||
break;
|
||||
|
||||
case 1: // zip_STATIC_TREES
|
||||
if (zip_tl != null)
|
||||
i = zip_inflate_codes(buff, off + n, size - n);
|
||||
else
|
||||
i = zip_inflate_fixed(buff, off + n, size - n);
|
||||
break;
|
||||
|
||||
case 2: // zip_DYN_TREES
|
||||
if (zip_tl != null)
|
||||
i = zip_inflate_codes(buff, off + n, size - n);
|
||||
else
|
||||
i = zip_inflate_dynamic(buff, off + n, size - n);
|
||||
break;
|
||||
|
||||
default: // error
|
||||
i = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == -1) {
|
||||
if (zip_eof)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
n += i;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
function zip_inflate_start() {
|
||||
if (zip_slide == null) zip_slide = new Array(2 * zip_WSIZE);
|
||||
zip_wp = 0;
|
||||
zip_bit_buf = 0;
|
||||
zip_bit_len = 0;
|
||||
zip_method = -1;
|
||||
zip_eof = false;
|
||||
zip_copy_leng = zip_copy_dist = 0;
|
||||
zip_tl = null;
|
||||
}
|
||||
|
||||
inflate_start = function () { zip_inflate_start(); };
|
||||
inflate = function (data) {
|
||||
var out = "", buff = [], i, j;
|
||||
zip_bit_buf = 0;
|
||||
zip_bit_len = 0;
|
||||
zip_method = -1;
|
||||
zip_eof = false;
|
||||
zip_inflate_data = data;
|
||||
zip_inflate_pos = 0;
|
||||
|
||||
do { i = zip_inflate_internal(buff, buff.length, 1024); } while (i > 0);
|
||||
zip_inflate_data = null; // G.C.
|
||||
|
||||
return buff;
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* @fileoverview Meshcentral.js
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
var MeshServerCreateControl = function (domain) {
|
||||
var obj = {};
|
||||
obj.State = 0;
|
||||
obj.connectstate = 0;
|
||||
|
||||
obj.xxStateChange = function (newstate) {
|
||||
if (obj.State == newstate) return;
|
||||
obj.State = newstate;
|
||||
if (obj.onStateChanged) obj.onStateChanged(obj, obj.State);
|
||||
}
|
||||
|
||||
obj.Start = function () {
|
||||
obj.connectstate = 0;
|
||||
obj.socket = new WebSocket(window.location.protocol.replace("http", "ws") + "//" + window.location.host + domain + "control.ashx");
|
||||
obj.socket.onopen = function () { obj.connectstate = 1; obj.xxStateChange(2); }
|
||||
obj.socket.onmessage = obj.xxOnMessage;
|
||||
obj.socket.onclose = function () { obj.Stop(); }
|
||||
obj.xxStateChange(1);
|
||||
}
|
||||
|
||||
obj.Stop = function () {
|
||||
obj.connectstate = 0;
|
||||
if (obj.socket) { obj.socket.close(); delete obj.socket; }
|
||||
obj.xxStateChange(0);
|
||||
}
|
||||
|
||||
obj.xxOnMessage = function (e) {
|
||||
// console.log('xxOnMessage', e.data);
|
||||
var message;
|
||||
try { message = JSON.parse(e.data); } catch (e) { return; }
|
||||
if (obj.onMessage) obj.onMessage(obj, message);
|
||||
};
|
||||
|
||||
obj.Send = function (x) { if (obj.socket != null && obj.connectstate == 1) { obj.socket.send(JSON.stringify(x)); } }
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
[LocalizedFileNames]
|
||||
chimes.wav=@%windir%\system32\mmres.dll,-700
|
|
@ -0,0 +1,515 @@
|
|||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
color: black;
|
||||
font-size: 13px;
|
||||
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
|
||||
background-color: #d3d9d6;
|
||||
}
|
||||
|
||||
#container {
|
||||
background-color: #fff;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
border-top: 0;
|
||||
border-right: 1px solid #b7b7b7;
|
||||
border-bottom: 0;
|
||||
border-left: 1px solid #b7b7b7;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#masthead {
|
||||
width: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
text-align: right;
|
||||
background-color: #036;
|
||||
width: 960px;
|
||||
}
|
||||
|
||||
#column_l {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 930px;
|
||||
margin: 0;
|
||||
padding: 0 15px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#footer {
|
||||
clear: both;
|
||||
overflow: auto;
|
||||
width: 960px;
|
||||
text-align: center;
|
||||
background-color: #113962;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
#masthead img {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#masthead p {
|
||||
font-size: 11px;
|
||||
color: #fff;
|
||||
margin: 10px 10px 0;
|
||||
}
|
||||
|
||||
#footer a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#footer a:hover {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #036;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.i1 {
|
||||
background: url(../images/icons50.png) 0px 0px;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.i2 {
|
||||
background: url(../images/icons50.png) -50px 0px;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.i3 {
|
||||
background: url(../images/icons50.png) -100px 0px;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.i4 {
|
||||
background: url(../images/icons50.png) -150px 0px;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.i5 {
|
||||
background: url(../images/icons50.png) -200px 0px;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.i6 {
|
||||
background: url(../images/icons50.png) -250px 0px;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.j1 {
|
||||
background: url(../images/icons16.png) 0px 0px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.j2 {
|
||||
background: url(../images/icons16.png) -16px 0px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.j3 {
|
||||
background: url(../images/icons16.png) -32px 0px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.j4 {
|
||||
background: url(../images/icons16.png) -48px 0px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.j5 {
|
||||
background: url(../images/icons16.png) -64px 0px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.j6 {
|
||||
background: url(../images/icons16.png) -80px 0px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.m0 { background : url(../images/images16.png) -32px 0px; height : 16px; width : 16px; border:none; float:left }
|
||||
.m1 { background : url(../images/images16.png) -16px 0px; height : 16px; width : 16px; border:none; float:left }
|
||||
.m2 { background : url(../images/images16.png) -96px 0px; height : 16px; width : 16px; border:none; float:left }
|
||||
.m3 { background : url(../images/images16.png) -112px 0px; height : 16px; width : 16px; border:none; float:left }
|
||||
.si0 { background : url(../images/icons16.png) 0px 0px; height : 16px; width : 16px; border:none; float:left }
|
||||
.si1 { background : url(../images/icons16.png) -16px 0px; height : 16px; width : 16px; border:none; float:left }
|
||||
.si2 { background : url(../images/icons16.png) -32px 0px; height : 16px; width : 16px; border:none; float:left }
|
||||
.si3 { background : url(../images/icons16.png) -48px 0px; height : 16px; width : 16px; border:none; float:left }
|
||||
.si4 { background : url(../images/icons16.png) -64px 0px; height : 16px; width : 16px; border:none; float:left }
|
||||
|
||||
.mi { background : url(../images/meshicon50.png) 0px 0px; height: 50px; width: 50px; cursor:pointer; border:none }
|
||||
|
||||
#floatframe {
|
||||
position: fixed;
|
||||
top: 200px;
|
||||
height: 300px;
|
||||
z-index: 200;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.style1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.style2 {
|
||||
text-align: center;
|
||||
background-color: #808080;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.style3 {
|
||||
text-align: center;
|
||||
color: white;
|
||||
background-color: #808080;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.style4 {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.style5 {
|
||||
text-align: center;
|
||||
background-color: #808080;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.style6 {
|
||||
text-align: center;
|
||||
background-color: #D3D9D6;
|
||||
}
|
||||
|
||||
.style7 {
|
||||
font-size: large;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.style10 {
|
||||
background-color: #C9C9C9;
|
||||
}
|
||||
|
||||
.style11 {
|
||||
font-size: large;
|
||||
background-color: #C9C9C9;
|
||||
}
|
||||
|
||||
.style14 {
|
||||
text-align: left;
|
||||
background-color: #D3D9D6;
|
||||
}
|
||||
|
||||
.auto-style1 {
|
||||
text-align: right;
|
||||
background-color: #D3D9D6;
|
||||
}
|
||||
|
||||
|
||||
.fileIcon1 {
|
||||
background: url(data:image/gif;base64,R0lGODlhEAAQAJEDAPb49Y2Sj9LT2f///yH5BAEAAAMALAAAAAAQABAAAAImnI+py+1vhJwyUYAzHTL4D3qdlJWaIFJqmKod607sDKIiDUP63hQAOw==);
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
float: left;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.fileIcon2 {
|
||||
background: url(data:image/gif;base64,R0lGODlhEAAQAJEDAM2xV/Xur+XPgP///yH5BAEAAAMALAAAAAAQABAAAAJD3ISZIGHWUGihznesYDYATFVM+D2hJ4lgN1olxALAtAlmPCJvuMmJd6PJckDYwicrHhTD5o7plJmg0Uc0asNMkphHAQA7);
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
float: left;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.fileIcon3 {
|
||||
background: url(data:image/gif;base64,R0lGODlhEAAQAJEDAPb19IGBgbq6uv///yH5BAEAAAMALAAAAAAQABAAAAIy3ISpxgcPH2ouQgFEw1YmxnUXKEaaEZZnVWZk66JwzKpvuwZzwOgwb/C1gIOA8Yg8DgoAOw==);
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
float: left;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.filelist {
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-o-user-select: none;
|
||||
cursor: default;
|
||||
-khtml-user-drag: element;
|
||||
background-color: white;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.noselect {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.fsize {
|
||||
float: right;
|
||||
text-align: right;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.g1 {
|
||||
background-position: 0% 0%;
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
float: left;
|
||||
/* fallback (Opera) */
|
||||
/* Mozilla: */
|
||||
/* Chrome, Safari:*/
|
||||
background-image: linear-gradient(to right, #ffffff 0%, #c9c9c9 100%);
|
||||
background-color: #c9c9c9;
|
||||
background-repeat: repeat;
|
||||
background-attachment: scroll;
|
||||
}
|
||||
|
||||
.g2 {
|
||||
background-position: 0% 0%;
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
float: right;
|
||||
/* fallback (Opera) */
|
||||
/* Mozilla: */
|
||||
/* Chrome, Safari:*/
|
||||
background-image: linear-gradient(to right, #c9c9c9 0%, #ffffff 100%);
|
||||
background-color: #c9c9c9;
|
||||
background-repeat: repeat;
|
||||
background-attachment: scroll;
|
||||
}
|
||||
|
||||
.h1 {
|
||||
background-position: 0% 0%;
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
/* fallback (Opera) */
|
||||
/* Mozilla: */
|
||||
/* Chrome, Safari:*/
|
||||
background-image: linear-gradient(to right, #ffffff 0%, #d3d9d6 100%);
|
||||
background-color: #d3d9d6;
|
||||
background-repeat: repeat;
|
||||
background-attachment: scroll;
|
||||
}
|
||||
|
||||
.h2 {
|
||||
background-position: 0% 0%;
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
/* fallback (Opera) */
|
||||
/* Mozilla: */
|
||||
/* Chrome, Safari:*/
|
||||
background-image: linear-gradient(to right, #d3d9d6 0%, #ffffff 100%);
|
||||
background-color: #d3d9d6;
|
||||
background-repeat: repeat;
|
||||
background-attachment: scroll;
|
||||
}
|
||||
|
||||
.e1 {
|
||||
font-size: large;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 3px;
|
||||
overflow: hidden;
|
||||
word-wrap: hyphenate;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.e2 {
|
||||
float: left;
|
||||
height: 100%;
|
||||
width: 201px;
|
||||
background-color: #c9c9c9;
|
||||
}
|
||||
|
||||
.bar {
|
||||
font-size: large;
|
||||
background-color: #C9C9C9;
|
||||
height: 24px;
|
||||
float: left;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.bar2 {
|
||||
font-size: large;
|
||||
height: 24px;
|
||||
float: left;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.bar18 {
|
||||
font-size: large;
|
||||
background-color: #C9C9C9;
|
||||
height: 18px;
|
||||
float: left;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.bar182 {
|
||||
font-size: large;
|
||||
height: 18px;
|
||||
float: left;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.devHeaderx {
|
||||
color: lightgray;
|
||||
}
|
||||
|
||||
.DevSt {
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: #DDDDDD;
|
||||
}
|
||||
|
||||
.contextMenu {
|
||||
background: #F9F9F9;
|
||||
box-shadow: 0 0 12px rgba( 0, 0, 0, .3 );
|
||||
border: 1px solid #ccc;
|
||||
/*border-radius: 4px;*/
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
min-width: 100px;
|
||||
max-width: 150px;
|
||||
z-index: 500;
|
||||
}
|
||||
|
||||
.cmtext {
|
||||
color: #444;
|
||||
display: inline-block;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
text-decoration: none;
|
||||
width: 85%;
|
||||
cursor: default;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cmtext:hover {
|
||||
color: #f9f9f9;
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.gray {
|
||||
/*filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale");*/ /* Firefox 10+, Firefox on Android */
|
||||
filter: gray; /* IE6-9 */
|
||||
-webkit-filter: grayscale(100%) opacity(60%); /* Chrome 19+, Safari 6+, Safari 6+ iOS */
|
||||
}
|
||||
|
||||
.unselectable {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.notifiyBox {
|
||||
position: absolute;
|
||||
z-index:1000;
|
||||
top: 50px;
|
||||
right: 26px;
|
||||
width: 300px;
|
||||
text-align: left;
|
||||
background-color: #F0ECCD;
|
||||
border: 4px solid #666;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
-webkit-box-shadow: 2px 2px 4px #888;
|
||||
-moz-box-shadow: 2px 2px 4px #888;
|
||||
box-shadow: 2px 2px 4px #888;
|
||||
max-height:200px;
|
||||
}
|
||||
|
||||
.notifiyBox:before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
right: 5px;
|
||||
top: -30px;
|
||||
border: 15px solid;
|
||||
border-color: transparent #666 #666 transparent;
|
||||
}
|
||||
|
||||
.notifiyBox:after {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
right: 7px;
|
||||
top: -24px;
|
||||
border: 12px solid;
|
||||
border-color: transparent #F0ECCD #F0ECCD transparent;
|
||||
}
|
||||
|
||||
.notification {
|
||||
width:100%;
|
||||
min-height:30px;
|
||||
}
|
||||
|
||||
.notification:hover {
|
||||
background-color: #EFE8B6;
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
MeshCentral
|
||||
===========
|
||||
|
||||
For more information, [visit MeshCommander.com/MeshCentral2](http://www.meshcommander.com/meshcentral2).
|
||||
|
||||
This is a full computer management web site. With MeshCentral, you can run your own web server and it to remotely manage and control computers on a local network or anywhere on the internet. Once you get the server started, will create a mesh (a group of computers) and then download and install a mesh agent on each computer you want to manage. A minute later, the new computer will show up on the web site and you can take control of it, etc. MeshCentral includes full web-based remote desktop, terminal and file management capability.
|
||||
|
||||
This version of MeshCentral that is completely rebuild of the original MeshCentral coded in C#. It's simpler and includes many other design improvements over the original. At some point in the future, [MeshCentral.com](http://meshcentral.com) that is still running the older code will switch to using this code base. For now, this is early software, preview quality at best.
|
||||
|
||||
Note that in this version, **the Mesh Agent works only on Windows**. This version is BETA and should not be used in production.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Make sure you have NodeJS and npm installed. If you are behind a proxy, setup npm to use the proxy:
|
||||
|
||||
```
|
||||
npm config set proxy http://proxy.com:88
|
||||
npm config set https-proxy http://proxy.com:88
|
||||
```
|
||||
|
||||
Then, install MeshCentral by creating an empty folder and using npm to download the module:
|
||||
|
||||
```
|
||||
mkdir meshcentral
|
||||
cd meshcentral
|
||||
npm install meshcentral
|
||||
```
|
||||
|
||||
To run MeshCentral you may need to use "nodejs" instead of "node" on Linux.
|
||||
|
||||
```
|
||||
cd ./node_modules/meshcentral
|
||||
node meshcentral [arguments]
|
||||
```
|
||||
|
||||
One of the first things you will want to do is set a server name or IP address. This will be used by mesh agents to connect back to the server. So, make sure you set **a name that will resolve back to your server**. MeshCentral will not register this name for you. You must make sure to setup the DNS name yourself first, or use the right IP address. If you are just taking a quick look at MeshCentral, you can skip this step and do it at later time.
|
||||
|
||||
```
|
||||
node meshcentral --cert servername.domain.com
|
||||
node meshcentral --cert 1.2.3.4
|
||||
```
|
||||
|
||||
On Windows, you can install MeshCentral to run as a background service, just run it using "--install". Once running, open a browser and enter the server url. By default, a TLS self-signed certificate is created so you will need to ignore the security warning given by your browser. A link to the root certificate you need to load in your browser is provided on the web site if you want to make warnings go away. You can run without TLS security using --notls, but this is not recommended.
|
||||
|
||||
|
||||
Update and uninstall
|
||||
--------------------
|
||||
|
||||
Upgrading or uninstalling MeshCentral is super easy, just use npm as usual. From the parent folder of node_module, enter ether:
|
||||
|
||||
```
|
||||
npm upgrade meshcentral
|
||||
npm uninstall meshcentral
|
||||
```
|
||||
|
||||
Command Line
|
||||
------------
|
||||
|
||||
Command line arguments on Windows only:
|
||||
|
||||
| Arguments | Description
|
||||
| ------------------------------------- | -----------
|
||||
| --install | Install MeshCentral as a background service.
|
||||
| --uninstall | Uninstall MeshCentral background service.
|
||||
| --start | Start MeshCentral as a background service.
|
||||
| --stop | Stop MeshCentral background service.
|
||||
|
||||
|
||||
Command line arguments on any platform:
|
||||
|
||||
| Arguments | Description
|
||||
| ------------------------------------- | -----------
|
||||
| --notls | Use HTTP instead of HTTPS for the main web server.
|
||||
| --user [username] | Always login as [username] if the account exists.
|
||||
| --port [number] | Web server port number (default to 443).
|
||||
| --mpsport [number] | Intel AMT server port number (default to 4433).
|
||||
| --redirport [number] | Redirection web server, redirects users to the HTTPS server (default to 80).
|
||||
| --exactports | Server must run with correct ports or exit.
|
||||
| --cert [name], (country), (org) | Create a web server certificate with a server name. Country and organization can optionaly be set.
|
||||
|
||||
|
||||
Configuration File
|
||||
------------------
|
||||
|
||||
As an alternative to using command line arguments, you can create a ./node-module/meshcentral-data/config.json file, for example:
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": {
|
||||
"port": 8080,
|
||||
"redirport": 81
|
||||
},
|
||||
"domains": {
|
||||
"": {
|
||||
"title": "MyServer",
|
||||
"title2": "Servername",
|
||||
"userQuota": 1048576,
|
||||
"meshQuota": 248576,
|
||||
"newAccounts" : 1
|
||||
},
|
||||
"Customer1": {
|
||||
"title": "Customer1",
|
||||
"title2": "Extra String",
|
||||
"newAccounts" : 0
|
||||
},
|
||||
"Customer2": {
|
||||
"title": "Customer2",
|
||||
"title2": "Other String"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The "settings" part are for command line arguments. For example, instead of running with "--port 8080", you can put "port: 8080" in the settings portion of the config.json file. In addition, you can use the config.json file to create multi-tenancy servers. In the domains section, you can set options for the default domain ("") in addition to creating new domains.
|
||||
|
||||
For the configuration above, the root domain and two other domains will be accessible like this:
|
||||
|
||||
```
|
||||
https://servername:8080/
|
||||
https://servername:8080/customer1
|
||||
https://servername:8080/customer2
|
||||
```
|
||||
|
||||
When you setup many domains, the server considers each domain seperatly. Each domain has seperate user accounts, administrators, etc. Within each domain, you can put a "title" and "title2" as strings that will show up at the top of the web site. "userQuota" indicates the default maximum amount of data a user can have in it's "My Files" folder. "meshQuota" is the maximum total size of files in each mesh folder. "newAccounts" indicates if new accounts can be created from the login page, 0 if not allowed, 1 if allowed. Note that if a web site has no accounts, the new account option will be available until an account is created and the first account will be the site administrator.
|
||||
|
||||
|
||||
Other Notes
|
||||
-----------
|
||||
|
||||
For Windows users, if you install MeshCentral globally using "npm install meshcentral -g", it will not be able to run correctly as a Windows Service. It will immidiatly stop each time you start it.
|
||||
|
||||
For more information on MeshCentral or other tools, visit [MeshCommander.com](http://meshcommander.com).
|
||||
|
||||
|
||||
Tutorials
|
||||
---------
|
||||
|
||||
How to install MeshCentral2 in a few minutes.
|
||||
|
||||
[![MeshCentral2 - Installation](http://img.youtube.com/vi/LSiWuu71k_U/mqdefault.jpg)](http://www.youtube.com/watch?v=LSiWuu71k_U)
|
||||
|
||||
|
||||
Demonstration of MeshCentral2 usages and more tips & tricks.
|
||||
|
||||
[![MeshCentral2 - Usages](http://img.youtube.com/vi/1E3NqGJzYds/mqdefault.jpg)](http://www.youtube.com/watch?v=1E3NqGJzYds)
|
||||
|
||||
|
||||
How to setup Intel® AMT client initiated remote access (CIRA) to connect to MeshCentral2.
|
||||
|
||||
[![MeshCentral2 - Intel AMT CIRA](http://img.youtube.com/vi/rA2KHa2jkO0/mqdefault.jpg)](http://www.youtube.com/watch?v=rA2KHa2jkO0)
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
This software is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0).
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* @description Meshcentral web server
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
// ExpressJS login sample
|
||||
// https://github.com/expressjs/express/blob/master/examples/auth/index.js
|
||||
|
||||
// Construct a HTTP redirection web server object
|
||||
module.exports.CreateRedirServer = function (parent, db, args, certificates) {
|
||||
var obj = {};
|
||||
obj.parent = parent;
|
||||
obj.db = db;
|
||||
obj.args = args;
|
||||
obj.certificates = certificates;
|
||||
obj.express = require('express');
|
||||
obj.net = require('net');
|
||||
obj.app = obj.express();
|
||||
obj.tcpServer;
|
||||
|
||||
// Perform an HTTP to HTTPS redirection
|
||||
function performRedirection(req, res) {
|
||||
var host = certificates.CommonName;
|
||||
if (certificates.CommonName == 'sample.org') { host = req.headers.host; }
|
||||
if (req.headers && req.headers.host && (req.headers.host.split(':')[0].toLowerCase() == 'localhost')) { res.redirect('https://localhost:' + args.port + req.url); } else { res.redirect('https://' + host + ':' + args.port + req.url); }
|
||||
}
|
||||
|
||||
// Return the current domain of the request
|
||||
function getDomain(req) {
|
||||
var x = req.url.split('/');
|
||||
if (x.length < 2) return parent.config.domains[''];
|
||||
if (parent.config.domains[x[1].toLowerCase()]) return parent.config.domains[x[1].toLowerCase()];
|
||||
return parent.config.domains[''];
|
||||
}
|
||||
|
||||
// Renter the terms of service.
|
||||
obj.app.get('/MeshServerRootCert.cer', function (req, res) {
|
||||
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=' + certificates.RootName + '.cer' });
|
||||
var rootcert = obj.certificates.root.cert;
|
||||
var i = rootcert.indexOf("-----BEGIN CERTIFICATE-----\r\n");
|
||||
if (i >= 0) { rootcert = rootcert.substring(i + 29); }
|
||||
i = rootcert.indexOf("-----END CERTIFICATE-----");
|
||||
if (i >= 0) { rootcert = rootcert.substring(i, 0); }
|
||||
res.send(new Buffer(rootcert, 'base64'));
|
||||
});
|
||||
|
||||
// Add HTTP security headers to all responses
|
||||
obj.app.use(function (req, res, next) {
|
||||
res.removeHeader("X-Powered-By");
|
||||
res.set({ 'strict-transport-security': 'max-age=60000; includeSubDomains', 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src http: ws: 'self' 'unsafe-inline'" });
|
||||
return next();
|
||||
});
|
||||
|
||||
// Setup all HTTP redirection handlers
|
||||
//obj.app.set('etag', false);
|
||||
for (var i in parent.config.domains) {
|
||||
var url = parent.config.domains[i].url;
|
||||
obj.app.get(url, performRedirection);
|
||||
obj.app.post(url + 'amtevents.ashx', obj.parent.webserver.handleAmtEventRequest);
|
||||
obj.app.get(url + 'meshsettings', obj.parent.webserver.handleMeshSettingsRequest);
|
||||
obj.app.get(url + 'meshagents', obj.parent.webserver.handleMeshAgentRequest);
|
||||
}
|
||||
|
||||
// Find a free port starting with the specified one and going up.
|
||||
function CheckListenPort(port, func) {
|
||||
var s = obj.net.createServer(function (socket) { });
|
||||
obj.tcpServer = s.listen(port, function () { s.close(function () { if (func) { func(port); } }); }).on('error', function (err) {
|
||||
if (args.exactports) { console.error('ERROR: MeshCentral HTTP web server port ' + port + ' not available.'); process.exit(); }
|
||||
else { if (port < 65535) { CheckListenPort(port + 1, func); } else { if (func) { func(0); } } }
|
||||
});
|
||||
}
|
||||
|
||||
// Start the ExpressJS web server, if the port is busy try the next one.
|
||||
function StartRedirServer(port) {
|
||||
if (port == 0 || port == 65535) return;
|
||||
obj.args.redirport = port;
|
||||
obj.tcpServer = obj.app.listen(port, function () { console.log('MeshCentral HTTP redirection web server running on port ' + port + '.'); }).on('error', function (err) { if ((err.code == 'EACCES') && (port < 65535)) { StartRedirServer(port + 1); } else { console.log(err); } });
|
||||
}
|
||||
|
||||
CheckListenPort(args.redirport, StartRedirServer);
|
||||
|
||||
return obj;
|
||||
}
|