commit 3deafe34efca00b5b7100a93fcf0cd6bd99bb7b6 Author: Ylian Saint-Hilaire Date: Mon Mar 9 11:11:06 2020 -0700 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..197fccb --- /dev/null +++ b/.gitignore @@ -0,0 +1,293 @@ +## Ignore nodejs things +[Ii]mages-branded/ +[Ii]mages-brandedcommander/ +[Ii]mages-isdu + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4667732 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + 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-2020 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. diff --git a/VSSolution.sln b/VSSolution.sln new file mode 100644 index 0000000..84015b8 --- /dev/null +++ b/VSSolution.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Source", ".", "{431BAB2D-8D6E-43E6-B9E9-A7A286AA1CF8}" + ProjectSection(WebsiteProperties) = preProject + TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0" + Debug.AspNetCompiler.VirtualPath = "/localhost_56807" + Debug.AspNetCompiler.PhysicalPath = "..\Source\" + Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_56807\" + Debug.AspNetCompiler.Updateable = "true" + Debug.AspNetCompiler.ForceOverwrite = "true" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.VirtualPath = "/localhost_56807" + Release.AspNetCompiler.PhysicalPath = "..\Source\" + Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_56807\" + Release.AspNetCompiler.Updateable = "true" + Release.AspNetCompiler.ForceOverwrite = "true" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.Debug = "False" + VWDPort = "56807" + SlnRelativePath = "..\Source\" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {431BAB2D-8D6E-43E6-B9E9-A7A286AA1CF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {431BAB2D-8D6E-43E6-B9E9-A7A286AA1CF8}.Debug|Any CPU.Build.0 = Debug|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/agent-desktop-0.0.2.js b/agent-desktop-0.0.2.js new file mode 100644 index 0000000..2e9f148 --- /dev/null +++ b/agent-desktop-0.0.2.js @@ -0,0 +1,781 @@ +/** +* @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.debugmode = 0; + obj.firstUpKeys = []; + obj.stopInput = false; + obj.localKeyMap = true; + obj.altPressed = false; + obj.ctrlPressed = false; + obj.shiftPressed = false; + + 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.onScreenSizeChange = null; + obj.onMessage = null; + obj.onConnectCountChanged = null; + obj.onDebugMessage = null; + obj.onTouchEnabledChanged = null; + obj.onDisplayinfo = null; + obj.accumulator = null; + + obj.Start = function () { + obj.State = 0; + obj.accumulator = null; + } + + obj.Stop = function () { + obj.setRotation(0); + obj.UnGrabKeyInput(); + obj.UnGrabMouseInput(); + obj.touchenabled = 0; + if (obj.onScreenSizeChange != null) obj.onScreenSizeChange(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; + obj.CanvasId.style.cursor = 'default'; + //console.log('xxStateChange', newstate); + switch (newstate) { + case 0: { + // Disconnect + obj.Stop(); + break; + } + case 3: { + // Websocket connected + + break; + } + } + } + + obj.send = function (x) { + if (obj.debugmode > 1) { console.log("KSend(" + x.length + "): " + rstr2hex(x)); } + if (obj.parent != null) { 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(); + tile.xcount = obj.tilesReceived++; + //console.log('Tile #' + tile.xcount); + var r = obj.tilesReceived; + tile.src = "data:image/jpeg;base64," + btoa(str.substring(4, str.length)); + tile.onload = function () { + //console.log('DecodeTile #' + this.xcount); + if (obj.Canvas != null && obj.KillDraw < r && obj.State != 0) { + obj.PendingOperations.push([r, 2, tile, X, Y]); + while (obj.DoPendingOperations()) { } + } + } + tile.error = function () { console.log('DecodeTileError'); } + } + + 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) { + if (obj.debugmode > 0) { console.log("ScreenSize: " + 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(); + if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); } + } + + obj.ProcessData = function (str) { + var ptr = 0; + //console.log('x0', str.length); + while (ptr < str.length) { + //console.log('x1', ptr, str.length); + ptr += obj.ProcessDataEx(str.substring(ptr)); + //console.log('x2', ptr, str.length); + } + } + + obj.ProcessDataEx = function (str) { + if (obj.accumulator != null) { + str = obj.accumulator + str; + //console.log('KVM using accumulated data, total size is now ' + str.length + ' bytes.'); + obj.accumulator = null; + } + if (obj.debugmode > 1) { console.log("KRecv(" + str.length + "): " + rstr2hex(str.substring(0, Math.min(str.length, 40)))); } + if (str.length < 4) return; + var cmdmsg = null, X = 0, Y = 0, command = ReadShort(str, 0), cmdsize = ReadShort(str, 2), jumboAdd = 0; + if ((command == 27) && (cmdsize == 8)) { + // Jumbo packet + if (str.length < 12) return; + command = ReadShort(str, 8) + cmdsize = ReadInt(str, 4); + //console.log('JUMBO cmd=' + command + ', cmdsize=' + cmdsize + ', data received=' + str.length); + if ((cmdsize + 8) > str.length) { + //console.log('KVM accumulator set to ' + str.length + ' bytes, need ' + cmdsize + ' bytes.'); + obj.accumulator = str; + return; + } + str = str.substring(8); + jumboAdd = 8; + } + if ((cmdsize != str.length) && (obj.debugmode > 0)) { console.log(cmdsize, str.length, cmdsize == str.length); } + if ((command >= 18) && (command != 65)) { console.error("Invalid KVM command " + command + " of size " + cmdsize); console.log("Invalid KVM data", str.length, rstr2hex(str.substring(0, 40)) + '...'); return; } + if (cmdsize > str.length) { + //console.log('KVM accumulator set to ' + str.length + ' bytes, need ' + cmdsize + ' bytes.'); + obj.accumulator = str; + return; + } + //console.log("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); + if (obj.debugmode > 0) { console.log("CMD" + command + " at X=" + X + " Y=" + Y); } + } + + 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.SendKeyMsgKC(obj.KeyAction.UP, 16); // Shift + obj.send(String.fromCharCode(0x00, 0x0E, 0x00, 0x04)); + break; + case 11: // GetDisplays + var selectedDisplay = 0, displays = { }, dcount = ((str.charCodeAt(4) & 0xFF) << 8) + (str.charCodeAt(5) & 0xFF); + if (dcount > 0) { + // Many displays present + selectedDisplay = ((str.charCodeAt(6 + (dcount * 2)) & 0xFF) << 8) + (str.charCodeAt(7 + (dcount * 2)) & 0xFF); + 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) { displays[disp] = 'All Displays'; } else { displays[disp] = 'Display ' + disp; } + } + } + //console.log('Get Displays', displays, selectedDisplay, rstr2hex(str)); + if (obj.onDisplayinfo != null) { obj.onDisplayinfo(obj, displays, selectedDisplay); } + break; + case 12: // SetDisplay + //console.log('SetDisplayConfirmed'); + 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; + case 65: // Alert + str = str.substring(4); + if (str[0] != '.') { + console.log(str); //alert('KVM: ' + str); + if (obj.parent != null) { + obj.parent.consoleMessage = str; + if (obj.parent.onConsoleMessageChange) { obj.parent.onConsoleMessageChange(obj.parent, str); } + } + } else { + console.log('KVM: ' + str.substring(1)); + } + break; + } + return cmdsize + jumboAdd; + } + + // 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, "DBLCLICK": 6 }; + obj.InputType = { "KEY": 1, "MOUSE": 2, "CTRLALTDEL": 10, "TOUCH": 15 }; + obj.Alternate = 0; + + var convertKeyCodeTable = { + "Pause": 19, + "CapsLock": 20, + "Space": 32, + "Quote": 222, + "Minus": 189, + "NumpadMultiply": 106, + "NumpadAdd": 107, + "PrintScreen": 44, + "Comma": 188, + "NumpadSubtract": 109, + "NumpadDecimal": 110, + "Period": 190, + "Slash": 191, + "NumpadDivide": 111, + "Semicolon": 186, + "Equal": 187, + "OSLeft": 91, + "BracketLeft": 219, + "OSRight": 91, + "Backslash": 220, + "BracketRight": 221, + "ContextMenu": 93, + "Backquote": 192, + "NumLock": 144, + "ScrollLock": 145, + "Backspace": 8, + "Tab": 9, + "Enter": 13, + "NumpadEnter": 13, + "Escape": 27, + "Delete": 46, + "Home": 36, + "PageUp": 33, + "PageDown": 34, + "ArrowLeft": 37, + "ArrowUp": 38, + "ArrowRight": 39, + "ArrowDown": 40, + "End": 35, + "Insert": 45, + "F1": 112, + "F2": 113, + "F3": 114, + "F4": 115, + "F5": 116, + "F6": 117, + "F7": 118, + "F8": 119, + "F9": 120, + "F10": 121, + "F11": 122, + "F12": 123, + "ShiftLeft": 16, + "ShiftRight": 16, + "ControlLeft": 17, + "ControlRight": 17, + "AltLeft": 18, + "AltRight": 18, + "MetaLeft": 91, + "MetaRight": 92, + "VolumeMute": 181 + //"LaunchMail": + //"LaunchApp1": + //"LaunchApp2": + //"BrowserStop": + //"MediaStop": + //"MediaTrackPrevious": + //"MediaTrackNext": + //"MediaPlayPause": + //"MediaSelect": + } + + function convertKeyCode(e) { + if (e.code.startsWith('Key') && e.code.length == 4) { return e.code.charCodeAt(3); } + if (e.code.startsWith('Digit') && e.code.length == 6) { return e.code.charCodeAt(5); } + if (e.code.startsWith('Numpad') && e.code.length == 7) { return e.code.charCodeAt(6) + 48; } + return convertKeyCodeTable[e.code]; + } + + obj.SendKeyMsg = function (action, event) { + if (action == null) return; + if (!event) { event = window.event; } + if (event.code && (obj.localKeyMap == false)) { + // Convert "event.code" into a scancode. This works the same regardless of the keyboard language. + // Older browsers will not support this. + var kc = convertKeyCode(event); + if (kc != null) { obj.SendKeyMsgKC(action, kc); } + } else { + // Use this keycode, this works best with "US-EN" keyboards. + // Older browser support this. + var kc = event.keyCode; + if (kc == 0x3B) { kc = 0xBA; } // Fix the ';' key + else if (kc == 173) { kc = 189; } // Fix the '-' key for Firefox + else if (kc == 61) { kc = 187; } // Fix the '=' key for Firefox + 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) { + //console.log('SendKeyMsgKC', action, kc); + if (obj.State != 3) return; + if (typeof action == 'object') { for (var i in action) { obj.SendKeyMsgKC(action[i][0], action[i][1]); } } + else { 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 (event.addx) { X += event.addx; } + if (event.addy) { Y += event.addy; } + + 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.DBLCLICK) { + MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0A, 0x00, 0x88, ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF)); + } else if (Action == obj.KeyAction.SCROLL) { + MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0C, 0x00, 0x00, ((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) { console.log('Set display', 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.onScreenSizeChange != null) obj.onScreenSizeChange(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.xxMouseDblClick = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.DBLCLICK, 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) { if (obj.stopInput == true || desktop.State != 3) return false; return obj.xxKeyPress(e); } + obj.handleKeyUp = function (e) { + if (obj.stopInput == true || desktop.State != 3) return false; + if (obj.firstUpKeys.length < 5) { + obj.firstUpKeys.push(e.keyCode); + if ((obj.firstUpKeys.length == 5)) { var j = obj.firstUpKeys.join(','); if ((j == '16,17,91,91,16') || (j == '16,17,18,91,92')) { obj.stopInput = true; } } + } + if (e.keyCode == 16) { obj.shiftPressed = false; } + if (e.keyCode == 17) { obj.ctrlPressed = false; } + if (e.keyCode == 18) { obj.altPressed = false; } + return obj.xxKeyUp(e); + } + obj.handleKeyDown = function (e) { + if (obj.stopInput == true || desktop.State != 3) return false; + if (e.keyCode == 16) { obj.shiftPressed = true; } + if (e.keyCode == 17) { obj.ctrlPressed = true; } + if (e.keyCode == 18) { obj.altPressed = true; } + return obj.xxKeyDown(e); + } + + // Release the CTRL, ALT, SHIFT keys if they are pressed. + obj.handleReleaseKeys = function () { + if (obj.shiftPressed) { obj.SendKeyMsgKC(obj.KeyAction.UP, 16); } // Shift + if (obj.ctrlPressed) { obj.SendKeyMsgKC(obj.KeyAction.UP, 17); } // Ctrl + if (obj.altPressed) { obj.SendKeyMsgKC(obj.KeyAction.UP, 18); } // Alt + obj.shiftPressed = obj.ctrlPressed = obj.altPressed = false; + } + + // Mouse handlers + obj.mousedblclick = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseDblClick(e); } + obj.mousedown = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseDown(e); } + obj.mouseup = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseUp(e); } + obj.mousemove = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseMove(e); } + obj.mousewheel = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseWheel(e); } + + obj.xxMsTouchEvent = function (evt) { + if (evt.originalEvent.pointerType == 4) return; // If this is a mouse pointer, ignore this event. Touch & pen are ok. + 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.onScreenSizeChange != null) obj.onScreenSizeChange(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; +} diff --git a/agent-redir-rtc-0.1.0.js b/agent-redir-rtc-0.1.0.js new file mode 100644 index 0000000..fd60b27 --- /dev/null +++ b/agent-redir-rtc-0.1.0.js @@ -0,0 +1,145 @@ +/** +* @description Mesh Agent Transport Module - using websocket relay +* @author Ylian Saint-Hilaire +* @version v0.0.1 +*/ + +// Construct a MeshServer agent direction object +var CreateKvmDataChannel = function (webchannel, module, keepalive) { + var obj = {}; + obj.m = module; // This is the inner module (Terminal or Desktop) + module.parent = obj; + obj.webchannel = webchannel; + obj.State = 0; + obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER, 4 = Files, 5 = FileTransfer + obj.onStateChanged = null; + obj.onControlMsg = null; + obj.debugmode = 0; + obj.keepalive = keepalive; + obj.rtcKeepAlive = null; + + // Private method + //obj.debug = function (msg) { console.log(msg); } + + obj.Start = function () { + if (obj.debugmode == 1) { console.log('start'); } + obj.xxStateChange(3); + obj.webchannel.onmessage = obj.xxOnMessage; + obj.rtcKeepAlive = setInterval(obj.xxSendRtcKeepAlive, 30000); + } + + // Setup the file reader + var fileReader = new FileReader(); + var fileReaderInuse = false, fileReaderAcc = []; + if (fileReader.readAsBinaryString) { + // Chrome & Firefox (Draft) + fileReader.onload = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } } + } else if (fileReader.readAsArrayBuffer) { + // Chrome & Firefox (Spec) + fileReader.onloadend = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } } + } + + obj.xxOnMessage = function (e) { + //if (obj.debugmode == 1) { console.log('Recv', e.data); } + //if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Recv(' + obj.State + '): ', typeof e.data, e.data); } + if (typeof e.data == 'string') { if (obj.onControlMsg != null) { obj.onControlMsg(e.data); } return; } // If this is a control message, handle it here. + if (typeof e.data == 'object') { + if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; } + if (fileReader.readAsBinaryString) { + // Chrome & Firefox (Draft) + fileReaderInuse = true; + fileReader.readAsBinaryString(new Blob([e.data])); + } else if (f.readAsArrayBuffer) { + // Chrome & Firefox (Spec) + fileReaderInuse = true; + fileReader.readAsArrayBuffer(e.data); + } else { + // IE10, readAsBinaryString does not exist, use an alternative. + var binary = "", bytes = new Uint8Array(e.data), 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.xxOnMessage = function (e) { + //if (obj.debugmode == 1) { console.log('Recv', e.data); } + if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Recv(' + obj.State + '): ', typeof e.data, e.data); } + if (typeof e.data == 'string') { if (obj.onControlMsg != null) { obj.onControlMsg(e.data); } return; } // If this is a control message, handle it here. + 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 = '', bytes = new Uint8Array(e.data), 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) 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); + } + + // Send a control message over the WebRTC data channel + obj.sendCtrlMsg = function (x) { + if (typeof x == 'string') { + obj.webchannel.send(x); + if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Send(' + obj.State + '): ', typeof x, x); } + if (obj.keepalive != null) obj.keepalive.sendKeepAlive(); + } + } + + // Send a binary message over the WebRTC data channel + obj.send = function (x) { + if (typeof x == 'string') { var b = new Uint8Array(x.length); for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); } x = b; } + if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Send(' + obj.State + '): ', typeof x, x); } + obj.webchannel.send(x); + } + + 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 () { + if (obj.debugmode == 1) { console.log('stop'); } + if (obj.rtcKeepAlive != null) { clearInterval(obj.rtcKeepAlive); obj.rtcKeepAlive = null; } + obj.xxStateChange(0); + } + + obj.xxSendRtcKeepAlive = function () { + if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-SendKeepAlive()'); } + obj.sendCtrlMsg(JSON.stringify({ action: 'ping' })); + } + + return obj; +} diff --git a/amt-0.2.0.js b/amt-0.2.0.js new file mode 100644 index 0000000..76e9443 --- /dev/null +++ b/amt-0.2.0.js @@ -0,0 +1,987 @@ +/** +* @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]) { if (typeof response.Body["Items"][i][j] != 'function') { items.push(response.Body["Items"][i][j]); } } + } else { + if (typeof response.Body["Items"][i] != 'function') { 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) { _up(); 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"] != undefined)) { resp.Body.ReturnValueStr = obj.AmtStatusToStr(resp.Body["ReturnValue"]); } + return resp; + } + + obj.RequestPowerStateChange = function (PowerState, callback_func) { + obj.CIM_PowerManagementService_RequestPowerStateChange(PowerState, "
http://schemas.xmlsoap.org/ws/2004/08/addressing
http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystemCIM_ComputerSystemManagedSystem", null, null, callback_func); + } + + obj.RequestOSPowerStateChange = function (PowerState, callback_func) { + obj.IPS_PowerManagementService_RequestOSPowerSavingStateChange(PowerState, "
http://schemas.xmlsoap.org/ws/2004/08/addressing
http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystemCIM_ComputerSystemManagedSystem", null, null, callback_func); + } + + obj.SetBootConfigRole = function (Role, callback_func) { + obj.CIM_BootService_SetBootConfigRole("
http://schemas.xmlsoap.org/ws/2004/08/addressing
http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootConfigSettingIntel(r) AMT: Boot Configuration 0", 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, InternalMpServer, callback_func) { obj.Exec("AMT_RemoteAccessService", "AddRemoteAccessPolicyRule", { "Trigger": Trigger, "TunnelLifeTime": TunnelLifeTime, "ExtendedData": ExtendedData, "MpServer": MpServer, "InternalMpServer": InternalMpServer }, 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_KVMRedirectionSettingData_DataChannelRead = function (callback_func) { obj.Exec("IPS_KVMRedirectionSettingData", "DataChannelRead", {}, callback_func); } + obj.IPS_KVMRedirectionSettingData_DataChannelWrite = function (Data, callback_func) { obj.Exec("IPS_KVMRedirectionSettingData", "DataChannelWrite", { "DataMessage": Data }, 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_PowerManagementService_RequestOSPowerSavingStateChange = function (PowerState, ManagedElement, Time, TimeoutPeriod, callback_func) { obj.Exec("IPS_PowerManagementService", "RequestOSPowerSavingStateChange", { "OSPowerSavingState": PowerState, "ManagedElement": ManagedElement, "Time": Time, "TimeoutPeriod": TimeoutPeriod }, callback_func, 0, 1); } + 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_ScreenConfigurationService_SetSessionState = function (SessionState, ConsecutiveRebootsNum, callback_func) { obj.Exec("IPS_ScreenConfigurationService", "SetSessionState", { "SessionState": SessionState, "ConsecutiveRebootsNum": ConsecutiveRebootsNum }, callback_func); } + obj.IPS_SecIOService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_SecIOService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); } + obj.IPS_HTTPProxyService_AddProxyAccessPoint = function (AccessInfo, InfoFormat, Port, NetworkDnsSuffix, callback_func) { obj.Exec("IPS_HTTPProxyService", "AddProxyAccessPoint", { "AccessInfo": AccessInfo, "InfoFormat": InfoFormat, "Port": Port, "NetworkDnsSuffix": NetworkDnsSuffix }, 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 { + // ###BEGIN###{!Mode-MeshCentral2} + // NodeJS detection + var isNode = new Function('try {return this===global;}catch(e){return false;}'); + if (isNode()) { e = require('atob')(ra[i]); } else { e = window.atob(ra[i]); } + // ###END###{!Mode-MeshCentral2} + // ###BEGIN###{Mode-MeshCentral2} + e = window.atob(ra[i]); + // ###END###{Mode-MeshCentral2} + } 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||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) { + var handle = (eventDataField[1] << 24) + (eventDataField[2] << 16) + (eventDataField[3] << 8) + eventDataField[4]; + var nic = "#" + eventDataField[0]; + if (eventDataField[0] == 0xAA) nic = "wired"; // TODO: Add wireless ***** + //if (eventDataField[0] == 0xAA) nic = "wireless"; + + if (handle == 4294967293) { return "All received packet filter was matched on " + nic + " interface."; } + if (handle == 4294967292) { return "All outbound packet filter was matched on " + nic + " interface."; } + if (handle == 4294967290) { return "Spoofed packet filter was matched on " + nic + " interface."; } + return "Filter " + handle + " was matched on " + nic + " interface."; + } + + 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 "HWA Error: general error"; } // Used to be "Certificate revoked." but don't know the source of this. + } + + 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'] = 'Local'; + ptr = 5; + } + if (x['InitiatorType'] == 3) { + // KVM Default Port + x['Initiator'] = 'KVM Default Port'; + 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) { if (str == null) { 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) { + if (str == null) { 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 + + + Wireless-Profile-Admin + Intel(r) AMT:WiFi Endpoint Settings Wireless-Profile-Admin + 6 + 4 + 100 + xxxxxxxx + + */ +function instanceToXml(instanceName, inInstance) { + if(inInstance === undefined || inInstance === null) return null; + + var hasNamespace = !!inInstance['__namespace']; + var startTag = hasNamespace ? ''; + 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 += ''; + return result; +} + + +/** + * Convert a selector set into XML. Expect no nesting. + * { + * selectorName : selectorValue, + * selectorName : selectorValue, + * ... ... + * } + + + http://192.168.1.103:16992/wsman + + http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpoint + + WiFi Endpoint 0 + + + + + */ +function referenceToXml(referenceName, inReference) { + if(inReference === undefined || inReference === null ) return null; + + var result = '/wsman'+ inReference['__resourceUri']+''; + 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 += '' + inReference[selectorName].toString() + ''; + } + + result += ''; + 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; +} diff --git a/amt-certificates-0.0.1.js b/amt-certificates-0.0.1.js new file mode 100644 index 0000000..b5b767d --- /dev/null +++ b/amt-certificates-0.0.1.js @@ -0,0 +1,181 @@ +/** +* @fileoverview Intel(r) AMT Certificate functions +* @author Ylian Saint-Hilaire +* @version v0.2.0b +*/ + +// Check which key pair matches the public key in the certificate +function amtcert_linkCertPrivateKey(certs, keys) { + for (var i in certs) { + var cert = certs[i]; + try { + if (xxCertPrivateKeys.length == 0) return; + var publicKeyPEM = forge.pki.publicKeyToPem(forge.pki.certificateFromAsn1(forge.asn1.fromDer(cert.X509Certificate)).publicKey).substring(28 + 32).replace(/(\r\n|\n|\r)/gm, ""); + for (var j = 0; j < keys.length; j++) { + if (publicKeyPEM === (keys[j]['DERKey'] + '-----END PUBLIC KEY-----')) { + keys[j].XCert = cert; // Link the key pair to the certificate + cert.XPrivateKey = keys[j]; // Link the certificate to the key pair + } + } + } catch (e) { console.log(e); } + } +} + +// Load a P12 file, decodes it using the password and returns the private key handle +function amtcert_loadP12File(file, password, func) { + try { + // Encode in Base64 so Forge API can parse it. + var p12Der = window.forge.util.decode64(btoa(file)); + var p12Asn1 = window.forge.asn1.fromDer(p12Der); + var p12 = window.forge.pkcs12.pkcs12FromAsn1(p12Asn1, password); + + // Private key is stored in a shrouded key bag + var bags = p12.getBags({ bagType: window.forge.pki.oids.pkcs8ShroudedKeyBag }); + console.assert(bags[window.forge.pki.oids.pkcs8ShroudedKeyBag] && bags[window.forge.pki.oids.pkcs8ShroudedKeyBag].length > 0); + + // Import the Forge private key structure into Web Crypto + var privateKey = bags[window.forge.pki.oids.pkcs8ShroudedKeyBag][0].key; + var rsaPrivateKey = window.forge.pki.privateKeyToAsn1(privateKey); + var privateKeyInfo = window.forge.pki.wrapRsaPrivateKey(rsaPrivateKey); + var pkcs8 = window.forge.asn1.toDer(privateKeyInfo).getBytes(); + + // Get the issuer attributes + var certBags = p12.getBags({ bagType: window.forge.pki.oids.certBag }); + var issuerAttributes = certBags[window.forge.pki.oids.certBag][0].cert.subject.attributes; + + var bags1 = p12.getBags({ bagType: forge.pki.oids.certBag }); + var cert = bags1[forge.pki.oids.certBag][0].cert; + + func(privateKey, issuerAttributes, cert); + return true; + } catch (ex) { } + return false; +} + +function amtcert_signWithCaKey(DERKey, caPrivateKey, certAttributes, issuerAttributes, extKeyUsage) { + if (!caPrivateKey || caPrivateKey == null) { + var certAndKey = amtcert_createCertificate(issuerAttributes); + caPrivateKey = certAndKey.key; + } + return amtcert_createCertificate(certAttributes, caPrivateKey, DERKey, issuerAttributes, extKeyUsage); +} + +// --- Extended Key Usage OID's --- +// 1.3.6.1.5.5.7.3.1 = TLS Server certificate +// 1.3.6.1.5.5.7.3.2 = TLS Client certificate +// 2.16.840.1.113741.1.2.1 = Intel AMT Remote Console +// 2.16.840.1.113741.1.2.2 = Intel AMT Local Console +// 2.16.840.1.113741.1.2.3 = Intel AMT Client Setup Certificate (Zero-Touch) + +// Generate a certificate with a set of attributes signed by a rootCert. If the rootCert is obmitted, the generated certificate is self-signed. +function amtcert_createCertificate(certAttributes, caPrivateKey, DERKey, issuerAttributes, extKeyUsage) { + // Generate a keypair and create an X.509v3 certificate + var keys, cert = forge.pki.createCertificate(); + if (!DERKey) { + keys = forge.pki.rsa.generateKeyPair(2048); + cert.publicKey = keys.publicKey; + } else { + cert.publicKey = forge.pki.publicKeyFromPem('-----BEGIN PUBLIC KEY-----' + DERKey + '-----END PUBLIC KEY-----'); + } + cert.serialNumber = '' + Math.floor((Math.random() * 100000) + 1); + cert.validity.notBefore = new Date(2018, 0, 1); + //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(2049, 11, 31); + //cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 20); + var attrs = []; + if (certAttributes['CN']) attrs.push({ name: 'commonName', value: certAttributes['CN'] }); + if (certAttributes['C']) attrs.push({ name: 'countryName', value: certAttributes['C'] }); + if (certAttributes['ST']) attrs.push({ shortName: 'ST', value: certAttributes['ST'] }); + if (certAttributes['O']) attrs.push({ name: 'organizationName', value: certAttributes['O'] }); + cert.setSubject(attrs); + + if (caPrivateKey) { + // Use root attributes + var rootattrs = []; + if (issuerAttributes['CN']) rootattrs.push({ name: 'commonName', value: issuerAttributes['CN'] }); + if (issuerAttributes['C']) rootattrs.push({ name: 'countryName', value: issuerAttributes['C'] }); + if (issuerAttributes['ST']) rootattrs.push({ shortName: 'ST', value: issuerAttributes['ST'] }); + if (issuerAttributes['O']) rootattrs.push({ name: 'organizationName', value: issuerAttributes['O'] }); + cert.setIssuer(rootattrs); + } else { + // Use our own attributes + cert.setIssuer(attrs); + } + + if (caPrivateKey == undefined) { + // Create a root certificate + cert.setExtensions([{ + name: 'basicConstraints', + cA: true + }, { + name: 'nsCertType', + sslCA: true, + emailCA: true, + objCA: true + }, { + name: 'subjectKeyIdentifier' + }]); + } else { + if (extKeyUsage == null) { extKeyUsage = { name: 'extKeyUsage', serverAuth: true, } } else { extKeyUsage.name = 'extKeyUsage'; } + + /* + { + name: 'extKeyUsage', + serverAuth: true, + clientAuth: true, + codeSigning: true, + emailProtection: true, + timeStamping: true, + '2.16.840.1.113741.1.2.1': true + } + */ + + // Create a leaf certificate + cert.setExtensions([{ + name: 'basicConstraints' + }, { + name: 'keyUsage', + keyCertSign: true, + digitalSignature: true, + nonRepudiation: true, + keyEncipherment: true, + dataEncipherment: true + }, extKeyUsage, { + name: 'nsCertType', + client: true, + server: true, + email: true, + objsign: true, + }, { + name: 'subjectKeyIdentifier' + }]); + } + + // Self-sign certificate + if (caPrivateKey) { + cert.sign(caPrivateKey, forge.md.sha256.create()); + } else { + cert.sign(keys.privateKey, forge.md.sha256.create()); + } + + if (DERKey) { + return cert; + } else { + return { 'cert': cert, 'key': keys.privateKey }; + } +} + +function _stringToArrayBuffer(str) { + var buf = new ArrayBuffer(str.length); + var bufView = new Uint8Array(buf); + for (var i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } + return buf; +} + +function _arrayBufferToString(buffer) { + var binary = ''; + var bytes = new Uint8Array(buffer); + var len = bytes.byteLength; + for (var i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } + return binary; +} diff --git a/amt-desktop-0.0.2.js b/amt-desktop-0.0.2.js new file mode 100644 index 0000000..42aefa8 --- /dev/null +++ b/amt-desktop-0.0.2.js @@ -0,0 +1,923 @@ +/** +* @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.graymode = 0; // 0 = Color, 1 = 256 grays, 2 = 16 grays. obj.bbp must be 1 in gray modes. + obj.useZRLE = true; + obj.showmouse = true; + obj.buttonmask = 0; + //obj.inbytes = 0; + //obj.outbytes = 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.noMouseRotate = false; + obj.rotation = 0; + // ###END###{DesktopRotation} + // ###BEGIN###{DesktopInband} + obj.kvmDataSupported = false; + obj.onKvmData = null; + obj.onKvmDataPending = []; + obj.onKvmDataAck = -1; + obj.holding = false; + obj.lastKeepAlive = Date.now(); + // ###END###{DesktopInband} + + obj.mNagleTimer = null; // Mouse motion slowdown timer + obj.mx = 0; // Last mouse x position + obj.my = 0; // Last mouse y position + // ###BEGIN###{DesktopFocus} + obj.ox = -1; // Old mouse x position + obj.oy = -1; // Old mouse y position + obj.focusmode = 0; + // ###END###{DesktopFocus} + // ###BEGIN###{Inflate} + obj.inflate = ZLIB.inflateInit(-15); + // ###END###{Inflate} + + // 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'; + // ###BEGIN###{Inflate} + obj.inflate = ZLIB.inflateInit(-15); // Reset inflate + // ###END###{Inflate} + } else { + if (!obj.showmouse) { QS(obj.canvasid).cursor = 'none'; } + } + } + + obj.ProcessData = function (data) { + if (!data) return; + // obj.Debug("KRecv(" + data.length + "): " + rstr2hex(data)); + //obj.inbytes += data.length; + //obj.Debug("KRecv(" + obj.inbytes + ")"); + 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 + // ###BEGIN###{DesktopRotation} + obj.rotation = 0; // We don't currently support screen init while rotated. + // ###END###{DesktopRotation} + 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), KVM Data Channel (1092) + + var supportedEncodings = ''; + if (obj.useZRLE) supportedEncodings += IntToStr(16); + supportedEncodings += IntToStr(0); + // ###BEGIN###{DesktopInband} + supportedEncodings += IntToStr(1092); + // ###END###{DesktopInband} + + obj.Send(String.fromCharCode(2, 0) + ShortToStr((supportedEncodings.length / 4) + 1) + supportedEncodings + IntToStr(-223)); // Supported Encodings + Desktop Size + + if (obj.graymode == 0) { + // 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 + } else { + obj.bpp = 1; + if (obj.graymode == 1) { obj.Send(String.fromCharCode(0, 0, 0, 0, 8, 8, 0, 1) + ShortToStr(255) + ShortToStr(0) + ShortToStr(0) + String.fromCharCode(0, 0, 0, 0, 0, 0)); } // Setup 8 bit gray RGB800 + if (obj.graymode == 2) { obj.Send(String.fromCharCode(0, 0, 0, 0, 8, 8, 0, 1) + ShortToStr(15) + ShortToStr(0) + ShortToStr(0) + String.fromCharCode(0, 0, 0, 0, 0, 0)); } // Setup 4 bit gray RGB400 + } + + obj.state = 4; + if (obj.parent) { 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) { + switch (obj.acc.charCodeAt(0)) { + case 0: // FramebufferUpdate + 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; + break; + case 2: // This is the bell, do nothing. + cmdsize = 1; + break; + case 3: // This is ServerCutText + if (obj.acc.length < 8) return; + var len = ReadInt(obj.acc, 4) + 8; + if (obj.acc.length < len) return; + cmdsize = handleServerCutText(obj.acc); + break; + } + } + 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 right 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); + var j = (obj.sparew2 * obj.spareh2) << 2; + for (var i = 3; i < j; i += 4) { obj.spare.data[i] = 0xFF; } // Set alpha channel to opaque. + } + } + + } + + if (encoding == 0xFFFFFF21) { + // Desktop Size (0xFFFFFF21, -223) + obj.canvas.canvas.width = obj.ScreenWidth = obj.rwidth = obj.width = width; + obj.canvas.canvas.height = obj.ScreenHeight = 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 + if (obj.bpp == 2) { + for (var i = 0; i < s; i++) { _setPixel16(obj.acc.charCodeAt(ptr++) + (obj.acc.charCodeAt(ptr++) << 8), i); } + } else { + for (var i = 0; i < s; i++) { _setPixel8(obj.acc.charCodeAt(ptr++), 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 (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 = obj.inflate.inflate(obj.acc.substring(ptr, ptr + datalen - dx)); + if (arr.length > 0) { _decodeLRE(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 + ', HEX: ' + rstr2hex(obj.acc)); + 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 + if (obj.bpp == 2) { + for (i = 0; i < s; i++) { _setPixel16(data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8), i); } + } else { + for (i = 0; i < s; i++) { _setPixel8(data.charCodeAt(ptr++), 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. + if (obj.bpp == 2) { + for (i = 0; i < subencoding; i++) { palette[i] = data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8); } + if (subencoding == 2) { br = 1; bm = 1; } else if (subencoding <= 4) { br = 2; bm = 3; } // Compute bits to read & bit mark + while (rlecount < s && ptr < data.length) { v = data.charCodeAt(ptr++); for (i = (8 - br) ; i >= 0; i -= br) { _setPixel16(palette[(v >> i) & bm], rlecount++); } } // Display all the bits + } else { + for (i = 0; i < subencoding; i++) { palette[i] = data.charCodeAt(ptr++); } + if (subencoding == 2) { br = 1; bm = 1; } else if (subencoding <= 4) { br = 2; bm = 3; } // Compute bits to read & bit mark + while (rlecount < s && ptr < data.length) { v = data.charCodeAt(ptr++); for (i = (8 - br) ; i >= 0; i -= br) { _setPixel8(palette[(v >> i) & bm], rlecount++); } } // Display all the bits + } + _putImage(obj.spare, x, y); + } + else if (subencoding == 128) { // RLE encoded tile + if (obj.bpp == 2) { + while (rlecount < s && ptr < data.length) { + // Get the run color + v = data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8); + + // 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 + if (obj.rotation == 0) { + _setPixel16run(v, rlecount, runlength); rlecount += runlength; + } else { + while (--runlength >= 0) { _setPixel16(v, rlecount++); } + } + } + } else { + while (rlecount < s && ptr < data.length) { + // Get the run color + v = data.charCodeAt(ptr++); + + // 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 + if (obj.rotation == 0) { + _setPixel8run(v, rlecount, runlength); rlecount += runlength; + } else { + while (--runlength >= 0) { _setPixel8(v, rlecount++); } + } + } + } + _putImage(obj.spare, x, y); + } + else if (subencoding > 129) { // Palette RLE encoded tile + // Read the palette + if (obj.bpp == 2) { + for (i = 0; i < (subencoding - 128) ; i++) { palette[i] = data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8); } + } else { + for (i = 0; i < (subencoding - 128) ; i++) { palette[i] = data.charCodeAt(ptr++); } + } + + // 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 + if (obj.rotation == 0) { + if (obj.bpp == 2) { + _setPixel16run(v, rlecount, runlength); rlecount += runlength; + } else { + _setPixel8run(v, rlecount, runlength); rlecount += runlength; + } + } else { + if (obj.bpp == 2) { + while (--runlength >= 0) { _setPixel16(v, rlecount++); } + } else { + while (--runlength >= 0) { _setPixel8(v, rlecount++); } + } + } + } + _putImage(obj.spare, x, y); + } + } + + // ###BEGIN###{DesktopInband} + obj.hold = function (holding) { + if (obj.holding == holding) return; + obj.holding = holding; + obj.canvas.fillStyle = '#000000'; + obj.canvas.fillRect(0, 0, obj.width, obj.height); // Paint black + if (obj.holding == false) { + // Go back to normal operations + // Set canvas size and ask for full screen refresh + if ((obj.canvas.canvas.width != obj.width) || (obj.canvas.canvas.height != obj.height)) { + obj.canvas.canvas.width = obj.width; obj.canvas.canvas.height = obj.height; + if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); } // ??? + } + obj.Send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest + } else { + obj.UnGrabMouseInput(); + obj.UnGrabKeyInput(); + } + } + // ###END###{DesktopInband} + + function _putImage(i, x, y) { + // ###BEGIN###{DesktopInband} + if (obj.holding == true) return; + // ###END###{DesktopInband} + // ###BEGIN###{DesktopRotation} + var xx = _arotX(x, y); + y = _arotY(x, y); + x = xx; + // ###END###{DesktopRotation} + obj.canvas.putImageData(i, x, y); + } + + // Set 8bit color RGB332 + function _setPixel8(v, p) { + var pp = p << 2; + + // ###BEGIN###{DesktopRotation} + if (obj.rotation > 0) { + if (obj.rotation == 1) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = (x * obj.sparew2) + (obj.sparew2 - 1 - y); pp = p << 2; } + else if (obj.rotation == 2) { pp = (obj.sparew * obj.spareh * 4) - 4 - pp; } + else if (obj.rotation == 3) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = ((obj.sparew2 - 1 - x) * obj.sparew2) + (y); pp = p << 2; } + } + // ###END###{DesktopRotation} + + if (obj.graymode == 0) { + obj.spare.data[pp] = v & 224; + obj.spare.data[pp + 1] = (v & 28) << 3; + obj.spare.data[pp + 2] = _fixColor((v & 3) << 6); + } else { + obj.spare.data[pp] = obj.spare.data[pp + 1] = obj.spare.data[pp + 2] = v; + } + } + + // Set 16bit color RGB565 + function _setPixel16(v, p) { + var pp = p << 2; + + // ###BEGIN###{DesktopRotation} + if (obj.rotation > 0) { + if (obj.rotation == 1) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = (x * obj.sparew2) + (obj.sparew2 - 1 - y); pp = p << 2; } + else if (obj.rotation == 2) { pp = (obj.sparew * obj.spareh * 4) - 4 - pp; } + else if (obj.rotation == 3) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = ((obj.sparew2 - 1 - x) * obj.sparew2) + (y); pp = p << 2; } + } + // ###END###{DesktopRotation} + + obj.spare.data[pp] = (v >> 8) & 248; + obj.spare.data[pp + 1] = (v >> 3) & 252; + obj.spare.data[pp + 2] = (v & 31) << 3; + } + + // Set a run of 8bit color RGB332 + function _setPixel8run(v, p, run) { + var pp = (p << 2), r = (v & 224), g = ((v & 28) << 3), b = (_fixColor((v & 3) << 6)); + while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp+1] = g; obj.spare.data[pp+2] = b; pp += 4; } + } + + // Set a run of 16bit color RGB565 + function _setPixel16run(v, p, run) { + var pp = (p << 2), r = ((v >> 8) & 248), g = ((v >> 3) & 252), b = ((v & 31) << 3); + while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp+1] = g; obj.spare.data[pp+2] = b; pp += 4; } + } + + // ###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; + //console.log('hard-rot: ' + newrotation); + // ###BEGIN###{DesktopInband} + if (obj.holding == true) { obj.rotation = newrotation; return; } + // ###END###{DesktopInband} + + 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###{DesktopInband} + if (obj.holding == true) return; + // ###END###{DesktopInband} + // ###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 + // ###BEGIN###{DesktopFocus} + } + // ###END###{DesktopFocus} + } + + obj.Start = function () { + //obj.Debug("KVM-Start"); + obj.state = 0; + obj.acc = ""; + obj.ZRLEfirst = 1; + //obj.inbytes = 0; + //obj.outbytes = 0; + // ###BEGIN###{Inflate} + obj.inflate.inflateReset(); + // ###END###{Inflate} + // ###BEGIN###{DesktopInband} + obj.onKvmDataPending = []; + obj.onKvmDataAck = -1; + obj.kvmDataSupported = false; + // ###END###{DesktopInband} + for (var i in obj.sparecache) { delete obj.sparecache[i]; } + } + + obj.Stop = function () { + obj.UnGrabMouseInput(); + obj.UnGrabKeyInput(); + if (obj.parent) { obj.parent.Stop(); } + } + + obj.Send = function (x) { + //obj.Debug("KSend(" + x.length + "): " + rstr2hex(x)); + //obj.outbytes += x.length; + if (obj.parent) { obj.parent.Send(x); } + } + + var convertAmtKeyCodeTable = { + "Pause": 19, + "CapsLock": 20, + "Space": 32, + "Quote": 39, + "Minus": 45, + "NumpadMultiply": 42, + "NumpadAdd": 43, + "PrintScreen": 44, + "Comma": 44, + "NumpadSubtract": 45, + "NumpadDecimal": 46, + "Period": 46, + "Slash": 47, + "NumpadDivide": 47, + "Semicolon": 59, + "Equal": 61, + "OSLeft": 91, + "BracketLeft": 91, + "OSRight": 91, + "Backslash": 92, + "BracketRight": 93, + "ContextMenu": 93, + "Backquote": 96, + "NumLock": 144, + "ScrollLock": 145, + "Backspace": 0xff08, + "Tab": 0xff09, + "Enter": 0xff0d, + "NumpadEnter": 0xff0d, + "Escape": 0xff1b, + "Delete": 0xffff, + "Home": 0xff50, + "PageUp": 0xff55, + "PageDown": 0xff56, + "ArrowLeft": 0xff51, + "ArrowUp": 0xff52, + "ArrowRight": 0xff53, + "ArrowDown": 0xff54, + "End": 0xff57, + "Insert": 0xff63, + "F1": 0xffbe, + "F2": 0xffbf, + "F3": 0xffc0, + "F4": 0xffc1, + "F5": 0xffc2, + "F6": 0xffc3, + "F7": 0xffc4, + "F8": 0xffc5, + "F9": 0xffc6, + "F10": 0xffc7, + "F11": 0xffc8, + "F12": 0xffc9, + "ShiftLeft": 0xffe1, + "ShiftRight": 0xffe2, + "ControlLeft": 0xffe3, + "ControlRight": 0xffe4, + "AltLeft": 0xffe9, + "AltRight": 0xffea, + "MetaLeft": 0xffe7, + "MetaRight": 0xffe8 + } + function convertAmtKeyCode(e) { + if (e.code.startsWith('Key') && e.code.length == 4) { return e.code.charCodeAt(3) + ((e.shiftKey == false) ? 32 : 0); } + if (e.code.startsWith('Digit') && e.code.length == 6) { return e.code.charCodeAt(5); } + if (e.code.startsWith('Numpad') && e.code.length == 7) { return e.code.charCodeAt(6); } + return convertAmtKeyCodeTable[e.code]; + } + + /* + 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; } + + if (e.code) { + // For new browsers, this mapping is keyboard language independent + var k = convertAmtKeyCode(e); + if (k != null) { obj.sendkey(k, d); } + } else { + 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) { + if (typeof k == 'object') { for (var i in k) { obj.sendkey(k[i][0], k[i][1]); } } + else { obj.Send(String.fromCharCode(4, d, 0, 0) + IntToStr(k)); } + } + + function handleServerCutText(acc) { + if (acc.length < 8) return 0; + var len = ReadInt(obj.acc, 4) + 8; + if (acc.length < len) return 0; + // ###BEGIN###{DesktopInband} + if (obj.onKvmData != null) { + var d = acc.substring(8, len); + if ((d.length >= 16) && (d.substring(0, 15) == '\0KvmDataChannel')) { + if (obj.kvmDataSupported == false) { obj.kvmDataSupported = true; console.log('KVM Data Channel Supported.'); } + if (((obj.onKvmDataAck == -1) && (d.length == 16)) || (d.charCodeAt(15) != 0)) { obj.onKvmDataAck = true; } + if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-Recv(' + (d.length - 16) + '): ' + d.substring(16)); } + if (d.length > 16) { obj.onKvmData(d.substring(16)); } // Event the data and ack + if ((obj.onKvmDataAck == true) && (obj.onKvmDataPending.length > 0)) { obj.sendKvmData(obj.onKvmDataPending.shift()); } // Send pending data + } + } + // ###END###{DesktopInband} + return len; + } + + // ###BEGIN###{DesktopInband} + obj.sendKvmData = function (x) { + if (obj.onKvmDataAck !== true) { + obj.onKvmDataPending.push(x); + } else { + if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-Send(' + x.length + '): ' + x); } + x = '\0KvmDataChannel\0' + x; + obj.Send(String.fromCharCode(6, 0, 0, 0) + IntToStr(x.length) + x); + obj.onKvmDataAck = false; + } + } + + // Send a HWKVM keep alive if it's not been sent in the last 5 seconds. + obj.sendKeepAlive = function () { + if (obj.lastKeepAlive < Date.now() - 5000) { obj.lastKeepAlive = Date.now(); obj.Send(String.fromCharCode(6, 0, 0, 0) + IntToStr(16) + '\0KvmDataChannel\0'); } + } + // ###END###{DesktopInband} + + 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, 1); } + obj.mouseup = function (e) { obj.buttonmask &= (0xFFFF - (1 << e.button)); return obj.mousemove(e, 1); } + obj.mousemove = function (e, force) { + 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} + if (obj.noMouseRotate != true) { + obj.mx2 = _crotX(obj.mx, obj.my); + obj.my = _crotY(obj.mx, obj.my); + obj.mx = obj.mx2; + } + // ###END###{DesktopRotation} + + // This is the mouse motion nagle timer. Slow down the mouse motion event rate. + if (force == 1) { + obj.Send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my)); + if (obj.mNagleTimer != null) { clearTimeout(obj.mNagleTimer); obj.mNagleTimer = null; } + } else { + if (obj.mNagleTimer == null) { + obj.mNagleTimer = setTimeout(function () { + obj.Send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my)); + obj.mNagleTimer = null; + }, 50); + } + } + + // ###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; +} diff --git a/amt-ider-node-0.0.1.js b/amt-ider-node-0.0.1.js new file mode 100644 index 0000000..ac72f0a --- /dev/null +++ b/amt-ider-node-0.0.1.js @@ -0,0 +1,509 @@ +/** +* @description Meshcentral +* @author Ylian Saint-Hilaire +* @version v0.0.1 +*/ + +function CreateIMRSDKWrapper() { + var obj = {}; + + var _IMRSDK; + var _ImrSdkVersion; + var _ref = require("ref"); + var _ffi = require("ffi"); + var _struct = require('ref-struct'); + var _arrayType = require('ref-array'); + obj.pendingData = {}; + + // Callback from the native lib back into js (StdCall) + var uintPtr = _ref.refType('uint'); + var OpenHandlerCallBack = _ffi.Callback('int', ['uint', 'uint'], _ffi.FFI_STDCALL, function (clientID, conID) { obj.pendingData[conID] = ''; return 0; }); + var CloseHandlerCallBack = _ffi.Callback('int', ['uint'], _ffi.FFI_STDCALL, function (conID) { obj.pendingData[conID] = ''; return 0; }); + var ReceiveHandlerCallBack = _ffi.Callback('int', ['pointer', uintPtr, 'uint'], _ffi.FFI_STDCALL, function (bufferPtr, lengthPtr, conID) { + try { + var bufferLen = lengthPtr.readUInt32LE(0), buffer = _ref.reinterpret(bufferPtr, bufferLen); + lengthPtr.writeUInt32LE(obj.pendingData[conID].length, 0); + for (var i = 0; i < obj.pendingData[conID].length; i++) { buffer[i] = obj.pendingData[conID].charCodeAt(i); } + //console.log("ReceiveHandlerCallBack(conID: " + conID + ", Len: " + obj.pendingData[conID].length + ")"); + obj.pendingData[conID] = ''; + } catch (e) { console.log(e); } + return 0; + }); + var SendHandlerCallBack = _ffi.Callback('int', ['pointer', 'uint', 'uint'], _ffi.FFI_STDCALL, function (ptr, length, conID) { + try { + var buffer = _ref.reinterpret(ptr, length), str = ''; + for (var i = 0; i < length; i++) { str += String.fromCharCode(buffer[i]); } + //console.log("SendHandlerCallBack(conID: " + conID + ", Len: " + length + ")"); + obj.client.write(str, 'binary'); + } catch (e) { console.log(e); } + return 0; + }); + + var _IMRVersion = _struct({ 'major': 'ushort', 'minor': 'ushort' }); + var _IMRVersionPtr = _ref.refType(_IMRVersion); + var _IMRClientInfo = _struct({ 'type': 'int', 'ip': _arrayType('char', 128), 'guid': _arrayType('char', 16)}); + var _IMRClientInfoPtr = _ref.refType(_IMRClientInfo); + var _ProxySettings = _struct({ 'type': 'int', 'server': _arrayType('char', 128), 'port': 'int', 'user': _arrayType('char', 128), 'pass': _arrayType('char', 128) }); + var _TCPSessionParams = _struct({ 'user': _arrayType('char', 128), 'pass': _arrayType('char', 128) }); + var _TCPSessionParamsPtr = _ref.refType(_TCPSessionParams); + var _TCPSessionParamsEx = _struct({ 'version': 'int', 'user': _arrayType('char', 128), 'pass': _arrayType('char', 128), 'proxy': _ProxySettings }); + var _TCPSessionParamsExPtr = _ref.refType(_TCPSessionParamsEx); + var _TCPSessionParamsEx2 = _struct({ 'version': 'int', 'user': _arrayType('char', 128), 'pass': _arrayType('char', 128), 'domain': _arrayType('char', 254), 'proxy': _ProxySettings }); + var _TCPSessionParamsEx2Ptr = _ref.refType(_TCPSessionParamsEx2); + var _FeaturesSupported = _struct({ 'ider_dev_pri': 'int', 'ider_dev_sec': 'int', 'reserved': _arrayType('char', 30) }); + var _FeaturesSupportedPtr = _ref.refType(_FeaturesSupported); + var _IDERTout = _struct({ 'rx_timeout': 'ushort', 'tx_timeout': 'ushort', 'hb_timeout': 'ushort' }); + var _IDERToutPtr = _ref.refType(_IDERTout); + var _IDERStatistics = _struct({ + 'error_state': 'int', + 'data_transfer': 'int', + 'num_reopen': 'ushort', + 'num_error': 'int', + 'num_reset': 'int', + 'last_cmd_length': 'int', + 'data_sent': 'int', + 'data_received': 'int', + 'packets_sent': 'int', + 'packets_received': 'int' + }); + var _IDERStatisticsPtr = _ref.refType(_IDERStatistics); + var _IDERDeviceState = _struct({ 'pri_default': 'int', 'pri_current': 'int', 'sec_default': 'int', 'sec_current': 'int' }); + var _IDERDeviceStatePtr = _ref.refType(_IDERDeviceState); + var _IDERDeviceCmd = _struct({ 'pri_op': 'int', 'pri_timing': 'int', 'sec_op': 'int', 'sec_timing': 'int' }); + var _IDERDeviceCmdPtr = _ref.refType(_IDERDeviceCmd); + var _IDERDeviceResult = _struct({ 'pri_res': 'int', 'sec_res': 'int' }); + var _IDERDeviceResultPtr = _ref.refType(_IDERDeviceResult); + var _SockCallBacks = _struct({ 'OpenHandler': 'pointer', 'CloseHandler': 'pointer', 'ReceiveHandler': 'pointer', 'SendHandler': 'pointer' }); + var _SockCallBacksPtr = _ref.refType(_SockCallBacks); + var _intPtr = _ref.refType('int'); + + function _Setup(lib) { + try { + _IMRSDK = _ffi.Library(lib, { + "IMR_Init": ['uint', [_IMRVersionPtr, 'string']], // IMRResult IMR_Init(IMRVersion *version, char *ini_file); + "IMR_InitEx": ['uint', [_IMRVersionPtr, 'string', _SockCallBacksPtr]], // IMRResult IMR_Init(IMRVersion *version, char *ini_file, void *funcPtrs); + "IMR_ReadyReadSock": ['uint', ['uint']], // IMRResult IMR_ReadyReadSock(uint conid); + "IMR_Close": ['uint', []], // IMRResult IMR_Close(); + "IMR_GetErrorStringLen": ['uint', ['uint', _intPtr]], // IMRResult IMR_GetErrorStringLen(IMRResult, int * str_len); + "IMR_GetErrorString": ['uint', ['uint', 'pointer']], // IMRResult IMR_GetErrorString(IMRResult, char * str); + "IMR_SetCertificateInfo": ['uint', ['string', 'string', 'string']], // IMRResult IMR_SetCertificateInfo(const char *root_cert, const char *private_cert, const char *cert_pass); + "IMR_SetClientCertificate": ['uint', ['string']], // IMRResult IMR_SetClientCertificate(const char *common_name); + "IMR_AddClient": ['uint', ['int', 'string', 'pointer', _intPtr]], // IMRResult IMR_AddClient(ClientType new_client_type, char * client_ip, GUIDType client_guid, ClientID * new_client_id); + "IMR_RemoveClient": ['uint', ['int']], // IMRResult IMR_RemoveClient(ClientID client_id); + "IMR_RemoveAllClients": ['uint', []], // IMRResult IMR_RemoveAllClients(); + "IMR_GetAllClients": ['uint', [_arrayType('int'), _intPtr]], // IMRResult IMR_GetAllClients(ClientID * client_list, int * client_list_size); + "IMR_GetClientInfo": ['uint', ['int', _IMRClientInfoPtr]], // IMRResult IMR_GetClientInfo(ClientID client_id, ClientInfo *); + "IMR_IDEROpenTCPSession": ['uint', ['int', _TCPSessionParamsPtr, _IDERToutPtr, 'string', 'string']], // IMRResult IMR_IDEROpenTCPSession(ClientID client_id, TCPSessionParams * params, IDERTout * touts, char * drive0, char * drive1); + "IMR_IDEROpenTCPSessionEx": ['uint', ['int', _TCPSessionParamsExPtr, _IDERToutPtr, 'string', 'string']], // IMRResult IMR_IDEROpenTCPSessionEx(ClientID client_id, TCPSessionParamsEx * params, IDERTout * touts, char * drive0, char * drive1); + "IMR_IDEROpenTCPSessionEx2": ['uint', ['int', _TCPSessionParamsEx2Ptr, _IDERToutPtr, 'string', 'string']], // IMRResult IMR_IDEROpenTCPSessionEx2(ClientID client_id, TCPSessionParamsEx2 * params, IDERTout * touts, char * drive0, char * drive1); + "IMR_IDERCloseSession": ['uint', ['int']], // IMRResult IMR_IDERCloseSession(ClientID client_id); + "IMR_IDERClientFeatureSupported" : ['uint', ['int', _FeaturesSupportedPtr]], // IMRResult IMR_IDERClientFeatureSupported(ClientID client_id, FeaturesSupported * supported); + "IMR_IDERGetDeviceState" : ['uint', ['int', _IDERDeviceStatePtr]], // IMRResult IMR_IDERGetDeviceState(ClientID client_id, IDERDeviceState * state); + "IMR_IDERSetDeviceState" : ['uint', ['int', _IDERDeviceCmdPtr, _IDERDeviceResultPtr]], // IMRResult IMR_IDERSetDeviceState(ClientID client_id, IDERDeviceCmd * cmd, IDERDeviceResult * result); + "IMR_IDERGetSessionStatistics": ['uint', ['int', _IDERStatisticsPtr]], // IMRResult IMR_IDERGetSessionStatistics(ClientID client_id, IDERStatistics * stat); + "IMR_SetOpt": ['uint', ['int', 'string', 'string', 'int']], // IMRResult IMR_SetOpt(/*IN*/ClientID id, /*IN*/int optname, /*IN*/ const char *optval, /*IN*/ int optlen); + "IMR_GetOpt": ['uint', ['int', 'string', 'pointer', _intPtr]], // IMRResult IMR_GetOpt(/*IN*/ClientID id, /*IN*/int optname, /*OUT*/ char *optval, /*OUT*/ int* optlen); + }); + } catch (e) { return false; } + return true; + } + + // Try to link to both 32bit and 64bit IMRSDK.dll's + if (_Setup('imrsdk') == false) { if (_Setup('imrsdk_x64') == false) { return null; } } + + // IMR_Init + obj.Init = function() { + var version = new _IMRVersion(); + var error = _IMRSDK.IMR_Init(version.ref(), "imrsdk.ini"); + if (error == 4) return _ImrSdkVersion; // If already initialized, return previous version information. + if (error != 0) { throw obj.GetErrorString(error); } + _ImrSdkVersion = { major: version.major, minor: version.minor }; + return _ImrSdkVersion; + } + + // IMR_InitEx + obj.InitEx = function(client) { + var version = new _IMRVersion(); + var callbacks = new _SockCallBacks(); + callbacks.OpenHandler = OpenHandlerCallBack; + callbacks.CloseHandler = CloseHandlerCallBack; + callbacks.ReceiveHandler = ReceiveHandlerCallBack; + callbacks.SendHandler = SendHandlerCallBack; + obj.client = client; + + var error = _IMRSDK.IMR_InitEx(version.ref(), "imrsdk.ini", callbacks.ref()); + if (error == 4) return _ImrSdkVersion; // If already initialized, return previous version information. + if (error != 0) { throw obj.GetErrorString(error); } + _ImrSdkVersion = { major: version.major, minor: version.minor }; + return _ImrSdkVersion; + } + + // IMR_ReadyReadSock + obj.ReadyReadSock = function (connid, func) { _IMRSDK.IMR_ReadyReadSock.async(connid, func); } + + // IMR_Close + obj.Close = function (func) { _IMRSDK.IMR_Close.async(func); } + + // IMR_GetErrorString + obj.GetErrorString = function (v) { + var errorStringLen = _ref.alloc('int'); + var error = _IMRSDK.IMR_GetErrorStringLen(v, errorStringLen); if (error != 0) return 'Unknown IMRSDK Error ' + v; + var errorStringLenEx = errorStringLen.deref(); + var errorString = new Buffer(errorStringLenEx); + error = _IMRSDK.IMR_GetErrorString(v, errorString); if (error != 0) return 'Unknown IMRSDK Error ' + v; + //console.log('IMRSDK ' + errorString.toString().substring(0, errorStringLenEx)); + return 'IMRSDK ' + errorString.toString().substring(0, errorStringLenEx); + } + + // IMR_SetCertificateInfo + obj.SetCertificateInfo = function (root_cert, private_cert, cert_pass) { + var error = _IMRSDK.IMR_SetCertificateInfo(root_cert, private_cert, cert_pass); if (error != 0) { throw obj.GetErrorString(error); } + } + + // IMR_SetClientCertificate + obj.SetClientCertificate = function (common_name) { + var error = _IMRSDK.IMR_SetClientCertificate(common_name); if (error != 0) { throw obj.GetErrorString(error); } + } + + // IMR_AddClient + // Type: 1=TCP, 2=TLS + obj.AddClient = function (type, ip) { + var guid = new Buffer(16); + for (var i = 0; i < 16; i++) { guid[i] = Math.floor(Math.random() * 255); } + var clientid = _ref.alloc('int'); + var error = _IMRSDK.IMR_AddClient(type, ip, guid, clientid); if (error != 0) { throw obj.GetErrorString(error); } + return clientid.deref(); + } + + // IMR_RemoveClient + obj.RemoveClient = function (client_id) { + var error = _IMRSDK.IMR_RemoveClient(client_id); if (error != 0) { throw obj.GetErrorString(error); } + } + + // IMR_RemoveAllClients + obj.RemoveAllClients = function () { + var error = _IMRSDK.IMR_RemoveAllClients(); if (error != 0) { throw obj.GetErrorString(error); } + } + + // IMR_GetAllClients + obj.GetAllClients = function () { + var clientListLength = _ref.alloc('int'); + var error = _IMRSDK.IMR_GetAllClients(null, clientListLength); if (error != 0) { throw obj.GetErrorString(error); } + var clientListLengthEx = clientListLength.deref(); + var IntArray = _arrayType(_ref.types.int) + var buf = new Buffer(4 * clientListLengthEx); + var error = _IMRSDK.IMR_GetAllClients(buf, clientListLength); if (error != 0) { throw obj.GetErrorString(error); } + clientListLengthEx = clientListLength.deref(); + var array = IntArray.untilZeros(buf) + var clientids = []; + for (var i = 0; i < clientListLengthEx; i++) { clientids.push(array[i]); } + return clientids; + } + + // IMR_GetClientInfo + obj.GetClientInfo = function (client_id) { + var client = new obj.IMRClientInfo(); + var error = _IMRSDK.IMR_GetClientInfo(client_id, client.ref()); if (error != 0) { throw obj.GetErrorString(error); } + var ip = '', i = 0; + while (client.ip[i] != 0) { ip += String.fromCharCode(client.ip[i++]); } + return { type: client.type, ip: ip }; + } + + // IMR_IDEROpenTCPSession + obj.IDEROpenTCPSessionAsync = function (client_id, user, pass, driveimg, driveiso, func) { + //console.log('IDEROpenTCPSessionAsync', client_id, user, pass, driveimg, driveiso); + // Setup parameters + var params = new _TCPSessionParams(); + var user2 = obj.decode_utf8(user); + var pass2 = obj.decode_utf8(pass); + for (var i = 0; i < 128; i++) { params.user[i] = 0; } + for (var i = 0; i < user2.length; i++) { params.user[i] = user2.charCodeAt(i); } + for (var i = 0; i < 128; i++) { params.pass[i] = 0; } + for (var i = 0; i < pass2.length; i++) { params.pass[i] = pass2.charCodeAt(i); } + + // Setup + var IDERTout = new _IDERTout(); + IDERTout.rx_timeout = 30000; // Default is 10000 + IDERTout.tx_timeout = 0; // Default is 0 + IDERTout.hb_timeout = 20000; // Default is 5000 + + // Make the call + _IMRSDK.IMR_IDEROpenTCPSession.async(client_id, params.ref(), IDERTout.ref(), driveimg, driveiso, function (x, error) { + if (error == 38) { return error; } // User consent required + //if (error != 0) { throw obj.GetErrorString(error); } + if (func != null) { func(error); } + }); + } + + // IMR_IDERCloseSession + obj.IDERCloseSessionAsync = function (client_id, func) { _IMRSDK.IMR_IDERCloseSession.async(client_id, func); } + + // IMR_IDERClientFeatureSupported + obj.IDERClientFeatureSupported = function (client_id) { + var features = new _FeaturesSupported(); + var error = _IMRSDK.IMR_IDERClientFeatureSupported(client_id, features.ref()); if (error != 0) { throw obj.GetErrorString(error); } + return { ider_dev_pri: features.ider_dev_pri, ider_dev_sec: features.ider_dev_sec } + } + + // IMR_IDERGetDeviceState + obj.IDERGetDeviceState = function (client_id) { + var state = new _IDERDeviceState(); + var error = _IMRSDK.IMR_IDERGetDeviceState(client_id, state.ref()); if (error != 0) { throw obj.GetErrorString(error); } + return { pri_default: state.pri_default, pri_current: state.pri_current, sec_default: state.sec_default, sec_current: state.sec_current } + } + + // IMR_IDERSetDeviceState Async + obj.IDERSetDeviceStateAsync = function (client_id, pri_op, pri_timing, sec_op, sec_timing, func) { + var cmd = new _IDERDeviceCmd(); + cmd.pri_op = pri_op; + cmd.pri_timing = pri_timing; + cmd.sec_op = sec_op; + cmd.sec_timing = sec_timing; + var result = new _IDERDeviceResult(); + _IMRSDK.IMR_IDERSetDeviceState.async(client_id, cmd.ref(), result.ref(), function (x, error) { + if (func != null) func(error, { pri_res: result.pri_res, sec_res: result.sec_res }); + }); + } + + // IMR_IDERGetSessionStatistics + obj.IDERGetSessionStatistics = function (client_id) { + var stats = new _IDERStatistics(); + var error = _IMRSDK.IMR_IDERGetSessionStatistics(client_id, stats.ref()); if (error != 0) { throw obj.GetErrorString(error); } + return { error_state: stats.error_state, data_transfer: stats.data_transfer, num_reopen: stats.num_reopen, num_error: stats.num_error, num_reset: stats.num_reset, last_cmd_length: stats.last_cmd_length, data_sent: stats.data_sent, data_received: stats.data_received, packets_sent: stats.packets_sent, packets_received: stats.packets_received }; + } + + // IMR_SetOpt + obj.SetOpt = function (client_id, optname, optval) { + var error = _IMRSDK.IMR_SetOpt(client_id, optname, optval, optval.length); if (error != 0) { throw obj.GetErrorString(error); } + } + + // IMR_GetOpt + obj.GetOpt = function (client_id, optname) { + var len = _ref.alloc('int'); + var error = _IMRSDK.IMR_GetOpt(client_id, optname, null, len); if (error != 0) { throw obj.GetErrorString(error); } + var buf = new Buffer(len.deref() + 1); + var error = _IMRSDK.IMR_GetOpt(client_id, optname, buf, len); if (error != 0) { throw obj.GetErrorString(error); } + // console.log(buf); + return buf; // TODO: Return something correct after testing this call + } + + // UTF-8 encoding & decoding functions + obj.encode_utf8 = function (s) { return unescape(encodeURIComponent(s)); } + obj.decode_utf8 = function (s) { return decodeURIComponent(escape(s)); } + + return obj; +} + +globalIderPendingCalls = 0; + +// Construct a Intel AMT IDER object +var CreateAmtRemoteIderIMR = function () { + if (globalIderPendingCalls != 0) { console.log('Incomplete IDER cleanup (' + globalIderPendingCalls + ').'); return null; } // IDER is not ready. + var _net = require('net'); + var _tls = require('tls'); + + var obj = {}; + obj.onStateChanged = null; + obj.state = 0; + + obj.m = {}; + obj.m.protocol = 3; // IDER + obj.m.state = 0; + obj.m.bytesToAmt = 0; + obj.m.bytesFromAmt = 0; + obj.m.imrsdk = null; + obj.receivedCount = 0; + + // Private method + obj.StateChange = function (newstate) { if (obj.state != newstate) { obj.state = newstate; obj.onStateChanged(obj, obj.state); } } + + obj.m.Start = function (host, port, user, pass, tls, wsmanCert, tlsOptions) { + obj.constants = require('constants'); + obj.m.host = host; + obj.m.port = port; + obj.m.user = user; + obj.m.pass = pass; + obj.m.tls = tls; + obj.m.xtlsoptions = tlsOptions; + obj.m.wsmanCert = wsmanCert; + obj.m.bytesToAmt = 0; + obj.m.bytesFromAmt = 0; + + if (obj.m.onDialogPrompt) { + if (require('os').platform() == 'win32') { + obj.m.onDialogPrompt(obj.m, { 'html': '
Select a CDROM and Floppy disk image to start the disk redirection.


Source
.ISO file

.IMG file

Start
' }); + } else { + obj.m.onDialogPrompt(obj.m, { 'html': '
Select a CDROM and Floppy disk image to start the disk redirection.


.ISO file

.IMG file

Start
' }); + } + } + } + + obj.m.Stop = function () { + if (obj.m.client != null) { + //console.log('obj.m.Stop - client'); + try { obj.m.client.destroy(); } catch (e) { console.log(e); } + delete obj.m.client; + } + if (obj.m.imrsdk) { + //console.log('obj.m.Stop - imrsdk', obj.m.clientid); + try + { + if (obj.m.clientid !== undefined) { + try { obj.m.imrsdk.IDERCloseSessionAsync(obj.m.clientid, function (error) { }); } catch (e) { } + delete obj.m.clientid; + } + obj.m.imrsdk.Close(function (error) { }); + } catch (e) { } + delete obj.m.imrsdk; + } + obj.StateChange(0); + } + + obj.m.Update = function () { + if ((obj.m.imrsdk != undefined) && (obj.m.clientid !== undefined)) { + try { + var stats = obj.m.imrsdk.IDERGetSessionStatistics(obj.m.clientid); + obj.m.bytesToAmt = stats.data_sent; + obj.m.bytesFromAmt = stats.data_received; + } catch (e) { + obj.m.bytesToAmt = -1; + obj.m.bytesFromAmt = -1; + } + } else { + obj.m.bytesToAmt = 0; + obj.m.bytesFromAmt = 0; + } + } + + obj.m.dialogPrompt = function (userConsentFunc) { + try { + obj.m.Stop(); + if (Q('storageserveroption')) { + var sel = 0; + if (require('os').platform() == 'win32') { sel = Q('storagesourceoption').value; } + if ((sel & 1) == 0) { + var x = Q('iderisofile'); + if (!x || x.files.length != 1) { obj.m.isopath = 'empty.iso'; } else { obj.m.isopath = x.files[0].path; } + } else { + obj.m.isopath = Q('iderisodrive').value; + } + if ((sel & 2) == 0) { + var x = Q('iderimgfile'); + if (!x || x.files.length != 1) { obj.m.imgpath = 'empty.img'; } else { obj.m.imgpath = x.files[0].path; } + } else { + obj.m.imgpath = Q('iderimgdrive').value; + } + obj.m.startoption = Q('storageserveroption').value; + startIderSession(userConsentFunc); + } + } catch (e) { + //console.log(e); + obj.m.onDialogPrompt(obj.m, { 'html': e }, 1); + } + } + + function startIderSession(userConsentFunc) { + if (globalIderPendingCalls != 0) { console.log('Incomplete IDER cleanup (' + globalIderPendingCalls + ').'); return; } + try { + //console.log("IDER-Start"); + if (obj.m.xtlsoptions && obj.m.xtlsoptions.meshServerConnect) { + // Open thru MeshCentral websocket wrapper + obj.m.client = CreateWebSocketWrapper(obj.m.xtlsoptions.host, obj.m.xtlsoptions.port, '/webrelay.ashx?user=' + encodeURIComponent(obj.m.xtlsoptions.username) + '&pass=' + encodeURIComponent(obj.m.xtlsoptions.password) + '&host=' + encodeURIComponent(obj.m.host) + '&p=2', obj.m.xtlsoptions.xtlsFingerprint); + obj.m.client.connect(function () { + //console.log('IDER Connected WS, ' + obj.m.xtlsoptions.host + ':' + obj.m.xtlsoptions.port + ' to ' + obj.m.host); + startIderSessionEx(userConsentFunc); + }); + } else if (obj.m.tls == 0) { + // Open connection without TLS + obj.m.client = new _net.Socket(); + obj.m.client.connect(obj.m.port, obj.m.host, function () { + //console.log('IDER Connected TCP, ' + obj.m.host + ':' + obj.m.port); + startIderSessionEx(userConsentFunc); + }); + } else { + // Open connection with TLS + if (obj.m.xtlsoptions == null) { obj.m.xtlsoptions = { secureProtocol: 'TLSv1_method', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false }; } + obj.m.client = _tls.connect(obj.m.port, obj.m.host, obj.m.xtlsoptions, function () { + //console.log('IDER Connected TLS, ' + obj.m.host + ':' + obj.m.port); + + // Check the Intel AMT certificate, it must be the same as the one used for WSMAN. If not, disconnect. + var iderTlsCertificate = obj.m.client.getPeerCertificate(); + if ((iderTlsCertificate == null) || (obj.m.wsmanCert == null) || (iderTlsCertificate.fingerprint != obj.m.wsmanCert.fingerprint)) { + console.log("Invalid IDER certificate, disconnecting.", iderTlsCertificate); + obj.m.Stop(); + } else { + startIderSessionEx(userConsentFunc) + } + }); + } + + obj.m.client.setEncoding('binary'); + + obj.m.client.on('data', function (data) { + //console.log("IDER-RECV(" + data.length + ", " + obj.receivedCount + "): " + rstr2hex(data)); + + if (obj.m.imrsdk == null) { return; } + + if ((obj.receivedCount == 0) && (data.charCodeAt(0) == 0x11) && (data.charCodeAt(1) == 0x05)) { + // We got a user consent error, handle it now before IMRSDK.dll can get it. + console.log('IDER user consent required.'); + obj.m.imrsdk.pendingData[obj.m.clientid] = String.fromCharCode(0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); // Try to cause a fault. + obj.m.imrsdk.ReadyReadSock(0, function (x, error) { }); + setTimeout(function () { obj.m.userConsentFunc(startIderSession); }, 500); + } else { + if (obj.m.imrsdk.pendingData[obj.m.clientid] == null) { obj.m.imrsdk.pendingData[obj.m.clientid] = data; } else { obj.m.imrsdk.pendingData[obj.m.clientid] += data; } + obj.m.imrsdk.ReadyReadSock(0, function (x, error) { }); + } + obj.receivedCount += data.length; + }); + + obj.m.client.on('close', function () { obj.m.Stop(); }); + + // If the TCP connection causes an error, disconnect the associated web socket. + obj.m.client.on('error', function (err) { obj.m.Stop(); }); + } catch (e) { + //console.log(e); + obj.m.Stop(); + obj.m.onDialogPrompt(obj.m, { 'html': e }, 1); + } + } + + function startIderSessionEx(userConsentFunc) { + try { + //console.log("IDER-StartEx"); + obj.m.userConsentFunc = userConsentFunc; + obj.m.imrsdk = CreateIMRSDKWrapper(); + obj.m.imrsdk.InitEx(obj.m.client); + obj.m.imrsdk.RemoveAllClients(); + if (obj.m.amtcertpath) { obj.m.imrsdk.SetCertificateInfo(obj.m.amtcertpath, null, null); } + obj.m.clientid = obj.m.imrsdk.AddClient(obj.m.tls + 1, obj.m.host); + globalIderPendingCalls++; + //console.log('IDEROpenTCPSessionAsync-call'); + var error = obj.m.imrsdk.IDEROpenTCPSessionAsync(obj.m.clientid, obj.m.user, obj.m.pass, obj.m.imgpath, obj.m.isopath, function (error) { + //console.log('IDEROpenTCPSessionAsync-callback(' + error + ')'); + globalIderPendingCalls--; + if (obj.m.imrsdk == null) return; // We closed already, exit now. + if ((error == 38) && (userConsentFunc != undefined)) { obj.m.Stop(); userConsentFunc(startIderSession); return; } + if (error != 0) { console.log('IDER error ' + error); obj.m.Stop(); } + if (error == 0) { + globalIderPendingCalls++; + obj.m.imrsdk.IDERSetDeviceStateAsync(obj.m.clientid, 0, obj.m.startoption, 0, obj.m.startoption, function (error, result) { + globalIderPendingCalls--; + if (error != 0) { + obj.m.Stop(); + obj.m.onDialogPrompt(obj.m, { 'html': e }, 1); + } else { + obj.StateChange(3); + } + }); + } + }); + } catch (e) { + //console.log(e); + obj.m.Stop(); + obj.m.onDialogPrompt(obj.m, { 'html': e }, 1); + } + } + + return obj; +} diff --git a/amt-ider-server-ws-0.0.1.js b/amt-ider-server-ws-0.0.1.js new file mode 100644 index 0000000..d2ff62e --- /dev/null +++ b/amt-ider-server-ws-0.0.1.js @@ -0,0 +1,100 @@ +/** +* @description IDER Handling Module +* @author Ylian Saint-Hilaire +* @version v0.0.2 +*/ + +// Construct a Intel AMT Server IDER object +var CreateAmtRemoteServerIder = function () { + var obj = {}; + obj.protocol = 4; // IDER-Server + + obj.iderStart = 0; // OnReboot = 0, Graceful = 1, Now = 2 + obj.floppy = null; + obj.cdrom = null; + obj.state = 0; + obj.onStateChanged = null; + obj.m = { + sectorStats: null, + onDialogPrompt: null, + dialogPrompt: function (data) { obj.socket.send(JSON.stringify({ action: 'dialogResponse', args: data })); }, + bytesToAmt: 0, + bytesFromAmt: 0, + server: true, + Stop: function () { obj.Stop(); } + }; + + // Private method + function debug() { if (urlvars && urlvars['idertrace']) { console.log(...arguments); } } + + // Private method, called by parent when it change state + obj.xxStateChange = function (newstate) { + if (obj.state == newstate) return; + debug("SIDER-StateChange", newstate); + obj.state = newstate; + if (obj.onStateChanged != null) { obj.onStateChanged(obj, obj.state); } + } + + obj.Start = function (host, port, user, pass, tls) { + debug("SIDER-Start", 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('/')) + "/webider.ashx?host=" + host + "&port=" + port + "&tls=" + tls + ((user == '*') ? "&serverauth=1" : "") + ((typeof pass === "undefined") ? ("&serverauth=1&user=" + user) : "") + "&tls1only=" + obj.tlsv1only); + obj.socket.onopen = obj.xxOnSocketConnected; + obj.socket.onmessage = obj.xxOnMessage; + obj.socket.onclose = obj.xxOnSocketClosed; + obj.xxStateChange(1); + } + + obj.Stop = function () { + debug("SIDER-Stop"); + if (obj.socket != null) { obj.socket.close(); obj.socket = null; } + obj.xxStateChange(0); + } + + obj.xxOnSocketConnected = function () { + obj.xxStateChange(2); + obj.socket.send(JSON.stringify({ action: 'start' })); + } + + obj.xxOnMessage = function (data) { + var msg = null; + try { msg = JSON.parse(data.data); } catch (ex) { } + if ((msg == null) || (typeof msg.action != 'string')) return; + + switch (msg.action) { + case 'dialog': { + if (obj.m.onDialogPrompt != null) { obj.m.onDialogPrompt(obj, msg.args, msg.buttons); } + break; + } + case 'state': { + if (msg.state == 2) { obj.xxStateChange(3); } + break; + } + case 'stats': { + obj.m.bytesToAmt = msg.toAmt; + obj.m.bytesFromAmt = msg.fromAmt; + if (obj.m.sectorStats) { obj.m.sectorStats(msg.mode, msg.dev, msg.total, msg.start, msg.len); } + break; + } + case 'error': { + var iderErrorStrings = ["", "Floppy disk image does not exist", "Invalid floppy disk image", "Unable to open floppy disk image", "CDROM disk image does not exist", "Invalid CDROM disk image", "Unable to open CDROM disk image", "Can't perform IDER with no disk images"]; + console.log('IDER Error: ' + iderErrorStrings[msg.code]); + // TODO: Display dialog box this error. + break; + } + default: { + console.log('Unknown Server IDER action: ' + msg.action); + breal; + } + } + + } + + obj.xxOnSocketClosed = function () { obj.Stop(); } + + return obj; +} diff --git a/amt-ider-ws-0.0.1.js b/amt-ider-ws-0.0.1.js new file mode 100644 index 0000000..77084ae --- /dev/null +++ b/amt-ider-ws-0.0.1.js @@ -0,0 +1,692 @@ +/** +* @description IDER Handling Module +* @author Ylian Saint-Hilaire +* @version v0.0.2 +*/ + +// Construct a Intel AMT IDER object +var CreateAmtRemoteIder = function () { + var obj = {}; + obj.protocol = 3; // IDER + obj.bytesToAmt = 0; + obj.bytesFromAmt = 0; + obj.rx_timeout = 30000; // Default 30000 + obj.tx_timeout = 0; // Default 0 + obj.heartbeat = 20000; // Default 20000 + obj.version = 1; + obj.acc = ""; + obj.inSequence = 0; + obj.outSequence = 0; + obj.iderinfo = null; + obj.enabled = false; + obj.iderStart = 0; // OnReboot = 0, Graceful = 1, Now = 2 + obj.floppy = null; + obj.cdrom = null; + obj.floppyReady = false; + obj.cdromReady = false; + //obj.pingTimer = null; + // ###BEGIN###{IDERStats} + obj.sectorStats = null; + // ###END###{IDERStats} + + // Private method + // ###BEGIN###{IDERDebug} + function debug() { if (urlvars && urlvars['idertrace']) { console.log(...arguments); } } + // ###END###{IDERDebug} + + // Mode Sense + var IDE_ModeSence_LS120Disk_Page_Array = String.fromCharCode(0x00, 0x26, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00); + var IDE_ModeSence_3F_LS120_Array = String.fromCharCode(0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31); + var IDE_ModeSence_FloppyDisk_Page_Array = String.fromCharCode(0x00, 0x26, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x04, 0xB0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00); + var IDE_ModeSence_3F_Floppy_Array = String.fromCharCode(0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1e, 0x04, 0xb0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31); + var IDE_ModeSence_CD_1A_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + //var IDE_ModeSence_CD_1B_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + var IDE_ModeSence_CD_1D_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + var IDE_ModeSence_CD_2A_Array = String.fromCharCode(0x00, 0x20, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + //var IDE_ModeSence_CD_01_Array = String.fromCharCode(0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00); + var IDE_ModeSence_3F_CD_Array = String.fromCharCode(0x00, 0x28, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + + // 0x46 constant data + var IDE_CD_ConfigArrayHeader = String.fromCharCode(0x00, 0x00,0x00, 0x28, 0x00, 0x00, 0x00, 0x08); + var IDE_CD_ConfigArrayProfileList = String.fromCharCode(0x00, 0x00, 0x03, 0x04, 0x00, 0x08, 0x01, 0x00); + var IDE_CD_ConfigArrayCore = String.fromCharCode(0x00, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00, 0x02); + var IDE_CD_Morphing = String.fromCharCode(0x00, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00); + var IDE_CD_ConfigArrayRemovable = String.fromCharCode(0x00, 0x03, 0x03, 0x04, 0x29, 0x00, 0x00, 0x02); + var IDE_CD_ConfigArrayRandom = String.fromCharCode(0x00, 0x10, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00); + var IDE_CD_Read = String.fromCharCode(0x00, 0x1E, 0x03, 0x00); + var IDE_CD_PowerManagement = String.fromCharCode(0x01, 0x00, 0x03, 0x00); + var IDE_CD_Timeout = String.fromCharCode(0x01, 0x05, 0x03, 0x00); + + // 0x01 constant data + var IDE_ModeSence_FloppyError_Recovery_Array = String.fromCharCode(0x00, 0x12, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00); + var IDE_ModeSence_Ls120Error_Recovery_Array = String.fromCharCode(0x00, 0x12, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00); + var IDE_ModeSence_CDError_Recovery_Array = String.fromCharCode(0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00); + + + // Private method, called by parent when it change state + obj.xxStateChange = function (newstate) { + // ###BEGIN###{IDERDebug} + debug("IDER-StateChange", newstate); + // ###END###{IDERDebug} + if (newstate == 0) { obj.Stop(); } + if (newstate == 3) { obj.Start(); } + } + + obj.Start = function () { + // ###BEGIN###{IDERDebug} + debug("IDER-Start"); + debug(obj.floppy, obj.cdrom); + // ###END###{IDERDebug} + obj.bytesToAmt = 0; + obj.bytesFromAmt = 0; + obj.inSequence = 0; + obj.outSequence = 0; + g_readQueue = []; + + // Send first command, OPEN_SESSION + obj.SendCommand(0x40, ShortToStrX(obj.rx_timeout) + ShortToStrX(obj.tx_timeout) + ShortToStrX(obj.heartbeat) + IntToStrX(obj.version)); + + // Send sector stats + // ###BEGIN###{IDERStats} + if (obj.sectorStats) { + obj.sectorStats(0, 0, obj.floppy?(obj.floppy.size >> 9):0); + obj.sectorStats(0, 1, obj.cdrom ? (obj.cdrom.size >> 11) : 0); + } + // ###END###{IDERStats} + + // Setup the ping timer + //obj.pingTimer = setInterval(function () { obj.SendCommand(0x44); }, 5000); + } + + obj.Stop = function () { + // ###BEGIN###{IDERDebug} + debug("IDER-Stop"); + // ###END###{IDERDebug} + //if (obj.pingTimer) { clearInterval(obj.pingTimer); obj.pingTimer = null; } + obj.parent.Stop(); + } + + // Private method + obj.ProcessData = function (data) { + obj.bytesFromAmt += data.length; + obj.acc += data; + // ###BEGIN###{IDERDebug} + debug('IDER-ProcessData', obj.acc.length, rstr2hex(obj.acc)); + // ###END###{IDERDebug} + + // Process as many commands as possible + while (true) { + var len = obj.ProcessDataEx(); + if (len == 0) return; + if (obj.inSequence != ReadIntX(obj.acc, 4)) { + // ###BEGIN###{IDERDebug} + debug('ERROR: Out of sequence', obj.inSequence, ReadIntX(obj.acc, 4)); + // ###END###{IDERDebug} + obj.Stop(); + return; + } + obj.inSequence++; + obj.acc = obj.acc.substring(len); + } + } + + // Private method + obj.SendCommand = function (cmdid, data, completed, dma) { + if (data == null) { data = ''; } + var attributes = ((cmdid > 50) && (completed == true)) ? 2 : 0; + if (dma) { attributes += 1; } + var x = String.fromCharCode(cmdid, 0, 0, attributes) + IntToStrX(obj.outSequence++) + data; + obj.parent.xxSend(x); + obj.bytesToAmt += x.length; + // ###BEGIN###{IDERDebug} + if (cmdid != 0x4B) { debug('IDER-SendData', x.length, rstr2hex(x)); } + // ###END###{IDERDebug} + } + + // CommandEndResponse (SCSI_SENSE) + obj.SendCommandEndResponse = function (error, sense, device, asc, asq) { + if (error) { obj.SendCommand(0x51, String.fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc5, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0), true); } + else { obj.SendCommand(0x51, String.fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x87, (sense << 4), 3, 0, 0, 0, device, 0x51, sense, asc, asq), true); } + } + + // DataToHost (SCSI_READ) + obj.SendDataToHost = function (device, completed, data, dma) { + var dmalen = (dma) ? 0 : data.length; + if (completed == true) { + obj.SendCommand(0x54, String.fromCharCode(0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0x85, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0, 0, 0, 0) + data, completed, dma); + } else { + obj.SendCommand(0x54, String.fromCharCode(0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + data, completed, dma); + } + } + + // GetDataFromHost (SCSI_CHUNK) + obj.SendGetDataFromHost = function (device, chunksize) { + obj.SendCommand(0x52, String.fromCharCode(0, (chunksize & 0xff), (chunksize >> 8), 0, 0xb5, 0, 0, 0, (chunksize & 0xff), (chunksize >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), false); + } + + // DisableEnableFeatures (STATUS_DATA) + // If type is REGS_TOGGLE (3), 4 bytes of data must be provided. + obj.SendDisableEnableFeatures = function (type, data) { if (data == null) { data = ''; } obj.SendCommand(0x48, String.fromCharCode(type) + data); } + + // Private method + obj.ProcessDataEx = function () { + if (obj.acc.length < 8) return 0; + + // First 8 bytes are the header + // CommandID + 0x000000 + Sequence Number + + switch(obj.acc.charCodeAt(0)) { + case 0x41: // OPEN_SESSION + if (obj.acc.length < 30) return 0; + var len = obj.acc.charCodeAt(29); + if (obj.acc.length < (30 + len)) return 0; + obj.iderinfo = {}; + obj.iderinfo.major = obj.acc.charCodeAt(8); + obj.iderinfo.minor = obj.acc.charCodeAt(9); + obj.iderinfo.fwmajor = obj.acc.charCodeAt(10); + obj.iderinfo.fwminor = obj.acc.charCodeAt(11); + obj.iderinfo.readbfr = ReadShortX(obj.acc, 16); + obj.iderinfo.writebfr = ReadShortX(obj.acc, 18); + obj.iderinfo.proto = obj.acc.charCodeAt(21); + obj.iderinfo.iana = ReadIntX(obj.acc, 25); + // ###BEGIN###{IDERDebug} + debug(obj.iderinfo); + // ###END###{IDERDebug} + + if (obj.iderinfo.proto != 0) { + // ###BEGIN###{IDERDebug} + debug("Unknown proto", obj.iderinfo.proto); + // ###END###{IDERDebug} + obj.Stop(); + } + if (obj.iderinfo.readbfr > 8192) { + // ###BEGIN###{IDERDebug} + debug("Illegal read buffer size", obj.iderinfo.readbfr); + // ###END###{IDERDebug} + obj.Stop(); + } + if (obj.iderinfo.writebfr > 8192) { + // ###BEGIN###{IDERDebug} + debug("Illegal write buffer size", obj.iderinfo.writebfr); + // ###END###{IDERDebug} + obj.Stop(); + } + + if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot + else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful + else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now + //obj.SendDisableEnableFeatures(1); // GetSupportedFeatures + return 30 + len; + case 0x43: // CLOSE + // ###BEGIN###{IDERDebug} + debug('CLOSE'); + // ###END###{IDERDebug} + obj.Stop(); + return 8; + case 0x44: // KEEPALIVEPING + obj.SendCommand(0x45); // Send PONG back + return 8; + case 0x45: // KEEPALIVEPONG + // ###BEGIN###{IDERDebug} + debug('PONG'); + // ###END###{IDERDebug} + return 8; + case 0x46: // RESETOCCURED + if (obj.acc.length < 9) return 0; + var resetMask = obj.acc.charCodeAt(8); + if (g_media === null) { + // No operations are pending + obj.SendCommand(0x47); // Send ResetOccuredResponse + // ###BEGIN###{IDERDebug} + debug('RESETOCCURED1', resetMask); + // ###END###{IDERDebug} + } else { + // Operations are being done, sent the reset once completed. + g_reset = true; + // ###BEGIN###{IDERDebug} + debug('RESETOCCURED2', resetMask); + // ###END###{IDERDebug} + } + return 9; + case 0x49: // STATUS_DATA - DisableEnableFeaturesReply + if (obj.acc.length < 13) return 0; + var type = obj.acc.charCodeAt(8); + var value = ReadIntX(obj.acc, 9); + // ###BEGIN###{IDERDebug} + debug('STATUS_DATA', type, value); + // ###END###{IDERDebug} + switch (type) + { + case 1: // REGS_AVAIL + if (value & 1) { + if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot + else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful + else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now + } + break; + case 2: // REGS_STATUS + obj.enabled = (value & 2) ? true : false; + // ###BEGIN###{IDERDebug} + debug("IDER Status: " + obj.enabled); + // ###END###{IDERDebug} + break; + case 3: // REGS_TOGGLE + if (value != 1) { + // ###BEGIN###{IDERDebug} + debug("Register toggle failure"); + // ###END###{IDERDebug} + } //else { obj.SendDisableEnableFeatures(2); } + break; + } + return 13; + case 0x4A: // ERROR OCCURED + if (obj.acc.length < 11) return 0; + // ###BEGIN###{IDERDebug} + debug('IDER: ABORT', obj.acc.charCodeAt(8)); + // ###END###{IDERDebug} + //obj.Stop(); + return 11; + case 0x4B: // HEARTBEAT + // ###BEGIN###{IDERDebug} + //debug('HEARTBEAT'); + // ###END###{IDERDebug} + return 8; + case 0x50: // COMMAND WRITTEN + if (obj.acc.length < 28) return 0; + var device = (obj.acc.charCodeAt(14) & 0x10) ? 0xB0 : 0xA0; + var deviceFlags = obj.acc.charCodeAt(14); + var cdb = obj.acc.substring(16, 28); + var featureRegister = obj.acc.charCodeAt(9); + // ###BEGIN###{IDERDebug} + debug('SCSI_CMD', device, rstr2hex(cdb), featureRegister, deviceFlags); + // ###END###{IDERDebug} + handleSCSI(device, cdb, featureRegister, deviceFlags); + return 28; + case 0x53: // DATA FROM HOST + if (obj.acc.length < 14) return 0; + var len = ReadShortX(obj.acc, 9); + if (obj.acc.length < (14 + len)) return 0; + // ###BEGIN###{IDERDebug} + debug('SCSI_WRITE, len = ' + (14 + len)); + // ###END###{IDERDebug} + obj.SendCommand(0x51, String.fromCharCode(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x70, 0x03, 0x00, 0x00, 0x00, 0xa0, 0x51, 0x07, 0x27, 0x00), true); + return 14 + len; + default: + // ###BEGIN###{IDERDebug} + debug('Unknown IDER command', obj.acc[0]); + // ###END###{IDERDebug} + obj.Stop(); + break; + } + return 0; + } + + function handleSCSI(dev, cdb, featureRegister, deviceFlags) + { + var lba; + var len; + + switch(cdb.charCodeAt(0)) + { + case 0x00: // TEST_UNIT_READY: + // ###BEGIN###{IDERDebug} + debug("SCSI: TEST_UNIT_READY", dev); + // ###END###{IDERDebug} + switch (dev) { + case 0xA0: // DEV_FLOPPY + if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; } + if (obj.floppyReady == false) { obj.floppyReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready + break; + case 0xB0: // DEV_CDDVD + if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; } + if (obj.cdromReady == false) { obj.cdromReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready + break; + default: + // ###BEGIN###{IDERDebug} + debug("SCSI Internal error 3", dev); + // ###END###{IDERDebug} + return -1; + } + obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); // Indicate ready + break; + case 0x08: // READ_6 + lba = ((cdb.charCodeAt(1) & 0x1f) << 16) + (cdb.charCodeAt(2) << 8) + cdb.charCodeAt(3); + len = cdb.charCodeAt(4); + if (len == 0) { len = 256; } + // ###BEGIN###{IDERDebug} + debug("SCSI: READ_6", dev, lba, len); + // ###END###{IDERDebug} + sendDiskData(dev, lba, len, featureRegister); + break; + case 0x0a: // WRITE_6 + lba = ((cdb.charCodeAt(1) & 0x1f) << 16) + (cdb.charCodeAt(2) << 8) + cdb.charCodeAt(3); + len = cdb.charCodeAt(4); + if (len == 0) { len = 256; } + // ###BEGIN###{IDERDebug} + debug("SCSI: WRITE_6", dev, lba, len); + // ###END###{IDERDebug} + obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); // Write is not supported, remote no medium. + return -1; + /* + case 0x15: // MODE_SELECT_6: + // ###BEGIN###{IDERDebug} + debug("SCSI ERROR: MODE_SELECT_6", dev); + // ###END###{IDERDebug} + obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00); + return -1; + */ + case 0x1a: // MODE_SENSE_6 + // ###BEGIN###{IDERDebug} + debug("SCSI: MODE_SENSE_6", dev); + // ###END###{IDERDebug} + if ((cdb.charCodeAt(2) == 0x3f) && (cdb.charCodeAt(3) == 0x00)) { + var a = 0, b = 0; + switch (dev) { + case 0xA0: // DEV_FLOPPY + if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; } + a = 0x00; + b = 0x80; // Read only = 0x80, Read write = 0x00 + break; + case 0xB0: // DEV_CDDVD + if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; } + a = 0x05; + b = 0x80; + break; + default: + // ###BEGIN###{IDERDebug} + debug("SCSI Internal error 6", dev); + // ###END###{IDERDebug} + return -1; + } + obj.SendDataToHost(dev, true, String.fromCharCode(0, a, b, 0), featureRegister & 1); + return; + } + obj.SendCommandEndResponse(1, 0x05, dev, 0x24, 0x00); + break; + case 0x1b: // START_STOP (Called when you eject the CDROM) + //var immediate = cdb.charCodeAt(1) & 0x01; + //var loej = cdb.charCodeAt(4) & 0x02; + //var start = cdb.charCodeAt(4) & 0x01; + obj.SendCommandEndResponse(1, 0, dev); + break; + case 0x1e: // LOCK_UNLOCK - ALLOW_MEDIUM_REMOVAL + // ###BEGIN###{IDERDebug} + debug("SCSI: ALLOW_MEDIUM_REMOVAL", dev); + // ###END###{IDERDebug} + if ((dev == 0xA0) && (obj.floppy == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; } + if ((dev == 0xB0) && (obj.cdrom == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; } + obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); + break; + case 0x23: // READ_FORMAT_CAPACITIES (Floppy only) + // ###BEGIN###{IDERDebug} + debug("SCSI: READ_FORMAT_CAPACITIES", dev); + // ###END###{IDERDebug} + var buflen = ReadShort(cdb, 7); + var mediaStatus = 0, sectors; + var mcSize = buflen / 8; // Capacity descriptor size is 8 + + switch (dev) { + case 0xA0: // DEV_FLOPPY + if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; } + sectors = (obj.floppy.size >> 9) - 1; + break; + case 0xB0: // DEV_CDDVD + if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; } + sectors = (obj.cdrom.size >> 11) - 1; // Number 2048 byte blocks + break; + default: + // ###BEGIN###{IDERDebug} + debug("SCSI Internal error 4", dev); + // ###END###{IDERDebug} + return -1; + } + + obj.SendDataToHost(dev, true, IntToStr(8) + String.fromCharCode(0x00, 0x00, 0x0b, 0x40, 0x02, 0x00, 0x02, 0x00), featureRegister & 1); + break; + case 0x25: // READ_CAPACITY + // ###BEGIN###{IDERDebug} + debug("SCSI: READ_CAPACITY", dev); + // ###END###{IDERDebug} + var len = 0; + switch(dev) + { + case 0xA0: // DEV_FLOPPY + if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; } + if (obj.floppy != null) { len = (obj.floppy.size >> 9) - 1; } + // ###BEGIN###{IDERDebug} + debug('DEV_FLOPPY', len); // Number 512 byte blocks + // ###END###{IDERDebug} + break; + case 0xB0: // DEV_CDDVD + if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; } + if (obj.cdrom != null) { len = (obj.cdrom.size >> 11) - 1; } // Number 2048 byte blocks + // ###BEGIN###{IDERDebug} + debug('DEV_CDDVD', len); + // ###END###{IDERDebug} + break; + default: + // ###BEGIN###{IDERDebug} + debug("SCSI Internal error 4", dev); + // ###END###{IDERDebug} + return -1; + } + //if (dev == 0xA0) { dev = 0x00; } else { dev = 0x10; } // Weird but seems to work. + // ###BEGIN###{IDERDebug} + debug("SCSI: READ_CAPACITY2", dev, deviceFlags); + // ###END###{IDERDebug} + obj.SendDataToHost(deviceFlags, true, IntToStr(len) + String.fromCharCode(0, 0, ((dev == 0xB0) ? 0x08 : 0x02), 0), featureRegister & 1); + break; + case 0x28: // READ_10 + lba = ReadInt(cdb, 2); + len = ReadShort(cdb, 7); + // ###BEGIN###{IDERDebug} + debug("SCSI: READ_10", dev, lba, len); + // ###END###{IDERDebug} + sendDiskData(dev, lba, len, featureRegister); + break; + case 0x2a: // WRITE_10 (Floppy only) + case 0x2e: // WRITE_AND_VERIFY (Floppy only) + lba = ReadInt(cdb, 2); + len = ReadShort(cdb, 7); + // ###BEGIN###{IDERDebug} + debug("SCSI: WRITE_10", dev, lba, len); + // ###END###{IDERDebug} + obj.SendGetDataFromHost(dev, 512 * len); // Floppy writes only, accept sectors of 512 bytes + break; + case 0x43: // READ_TOC (CD Audio only) + var buflen = ReadShort(cdb, 7); + var msf = cdb.charCodeAt(1) & 0x02; + var format = cdb.charCodeAt(2) & 0x07; + if (format == 0) { format = cdb.charCodeAt(9) >> 6; } + // ###BEGIN###{IDERDebug} + debug("SCSI: READ_TOC, dev=" + dev + ", buflen=" + buflen + ", msf=" + msf + ", format=" + format); + // ###END###{IDERDebug} + + switch (dev) { + case 0xA0: // DEV_FLOPPY + obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00); // Not implemented + return -1; + case 0xB0: // DEV_CDDVD + // NOP + break; + default: + // ###BEGIN###{IDERDebug} + debug("SCSI Internal error 9", dev); + // ###END###{IDERDebug} + return -1; + } + + if (format == 1) { obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x0a, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00), featureRegister & 1); } + else if (format == 0) { + if (msf) { + obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x34, 0x13), featureRegister & 1); + } else { + obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00), featureRegister & 1); + } + } + break; + case 0x46: // GET_CONFIGURATION + var sendall = (cdb.charCodeAt(1) != 2); + var firstcode = ReadShort(cdb, 2); + var buflen = ReadShort(cdb, 7); + + // ###BEGIN###{IDERDebug} + debug("SCSI: GET_CONFIGURATION", dev, sendall, firstcode, buflen); + // ###END###{IDERDebug} + + if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct. + + // Set the header + var r = IntToStr(0x0008); + + // Add the data + if (firstcode == 0) { r += IDE_CD_ConfigArrayProfileList; } + if ((firstcode == 0x1) || (sendall && (firstcode < 0x1))) { r += IDE_CD_ConfigArrayCore; } + if ((firstcode == 0x2) || (sendall && (firstcode < 0x2))) { r += IDE_CD_Morphing; } + if ((firstcode == 0x3) || (sendall && (firstcode < 0x3))) { r += IDE_CD_ConfigArrayRemovable; } + if ((firstcode == 0x10) || (sendall && (firstcode < 0x10))) { r += IDE_CD_ConfigArrayRandom; } + if ((firstcode == 0x1E) || (sendall && (firstcode < 0x1E))) { r += IDE_CD_Read; } + if ((firstcode == 0x100) || (sendall && (firstcode < 0x100))) { r += IDE_CD_PowerManagement; } + if ((firstcode == 0x105) || (sendall && (firstcode < 0x105))) { r += IDE_CD_Timeout; } + + // Set the length + r = IntToStr(r.length) + r; + + // Cut the length to buflen if needed + if (r.length > buflen) { r = r.substring(0, buflen); } + + obj.SendDataToHost(dev, true, r, featureRegister & 1); + return -1; + case 0x4a: // GET_EV_STATUS - GET_EVENT_STATUS_NOTIFICATION + //var buflen = (cdb.charCodeAt(7) << 8) + cdb.charCodeAt(8); + //if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct. + // ###BEGIN###{IDERDebug} + debug("SCSI: GET_EVENT_STATUS_NOTIFICATION", dev, cdb.charCodeAt(1), cdb.charCodeAt(4), cdb.charCodeAt(9)); + // ###END###{IDERDebug} + if ((cdb.charCodeAt(1) != 0x01) && (cdb.charCodeAt(4) != 0x10)) { + // ###BEGIN###{IDERDebug} + debug('SCSI ERROR'); + // ###END###{IDERDebug} + obj.SendCommandEndResponse(1, 0x05, dev, 0x26, 0x01); + break; + } + var present = 0x00; + if ((dev == 0xA0) && (obj.floppy != null)) { present = 0x02; } + else if ((dev == 0xB0) && (obj.cdrom != null)) { present = 0x02; } + obj.SendDataToHost(dev, true, String.fromCharCode(0x00, present, 0x80, 0x00), featureRegister & 1); // This is the original version, 4 bytes long + break; + case 0x4c: + obj.SendCommand(0x51, IntToStrX(0) + IntToStrX(0) + IntToStrX(0) + String.fromCharCode(0x87, 0x50, 0x03, 0x00, 0x00, 0x00, 0xb0, 0x51, 0x05, 0x20, 0x00), true); + break; + case 0x51: // READ_DISC_INFO + // ###BEGIN###{IDERDebug} + debug("SCSI READ_DISC_INFO", dev); + // ###END###{IDERDebug} + obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // Correct + return -1; + case 0x55: // MODE_SELECT_10: + // ###BEGIN###{IDERDebug} + debug("SCSI ERROR: MODE_SELECT_10", dev); + // ###END###{IDERDebug} + obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00); + return -1; + case 0x5a: // MODE_SENSE_10 + // ###BEGIN###{IDERDebug} + debug("SCSI: MODE_SENSE_10", dev, cdb.charCodeAt(2) & 0x3f); + // ###END###{IDERDebug} + var buflen = ReadShort(cdb, 7); + //var pc = cdb.charCodeAt(2) & 0xc0; + var r = null; + + if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct. + + // 1.44 mb floppy or LS120 (sectorCount == 0x3c300) + var sectorCount = 0; + if (dev == 0xA0) { + if (obj.floppy != null) { sectorCount = (obj.floppy.size >> 9); } + } else { + if (obj.cdrom != null) { sectorCount = (obj.cdrom.size >> 11); } + } + + switch (cdb.charCodeAt(2) & 0x3f) { + case 0x01: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_FloppyError_Recovery_Array:IDE_ModeSence_Ls120Error_Recovery_Array; } else { r = IDE_ModeSence_CDError_Recovery_Array; } break; + case 0x05: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_FloppyDisk_Page_Array:IDE_ModeSence_LS120Disk_Page_Array; } break; + case 0x3f: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_3F_Floppy_Array:IDE_ModeSence_3F_LS120_Array; } else { r = IDE_ModeSence_3F_CD_Array; } break; + case 0x1A: if (dev == 0xB0) { r = IDE_ModeSence_CD_1A_Array; } break; + case 0x1D: if (dev == 0xB0) { r = IDE_ModeSence_CD_1D_Array; } break; + case 0x2A: if (dev == 0xB0) { r = IDE_ModeSence_CD_2A_Array; } break; + } + + if (r == null) { + obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // TODO: Send proper error!!! + } else { + // Set disk to read only (we don't support write). + //ms_data[3] = ms_data[3] | 0x80; + obj.SendDataToHost(dev, true, r, featureRegister & 1); + } + break; + default: // UNKNOWN COMMAND + // ###BEGIN###{IDERDebug} + debug("IDER: Unknown SCSI command", cdb.charCodeAt(0)); + // ###END###{IDERDebug} + obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); + return -1; + } + return 0; + } + + function sendDiskData(dev, lba, len, featureRegister) { + var media = null; + var mediaBlocks = 0; + if (dev == 0xA0) { media = obj.floppy; if (obj.floppy != null) { mediaBlocks = (obj.floppy.size >> 9); } } + if (dev == 0xB0) { media = obj.cdrom; if (obj.cdrom != null) { mediaBlocks = (obj.cdrom.size >> 11); } } + if ((len < 0) || (lba + len > mediaBlocks)) { obj.SendCommandEndResponse(1, 0x05, dev, 0x21, 0x00); return 0; } + if (len == 0) { obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); return 0; } + if (media != null) { + // Send sector stats + // ###BEGIN###{IDERStats} + if (obj.sectorStats) { obj.sectorStats(1, (dev == 0xA0) ? 0 : 1, mediaBlocks, lba, len); } + // ###END###{IDERStats} + if (dev == 0xA0) { lba <<= 9; len <<= 9; } else { lba <<= 11; len <<= 11; } + if (g_media !== null) { + // Queue read operation + g_readQueue.push({ media: media, dev: dev, lba: lba, len: len, fr: featureRegister }); + } else { + // obj.iderinfo.readbfr // TODO: MaxRead + g_media = media; + g_dev = dev; + g_lba = lba; + g_len = len; + sendDiskDataEx(featureRegister); + } + } + } + + var g_readQueue = []; + var g_reset = false; + var g_media = null; + var g_dev; + var g_lba; + var g_len; + function sendDiskDataEx(featureRegister) { + var len = g_len, lba = g_lba; + if (g_len > obj.iderinfo.readbfr) { len = obj.iderinfo.readbfr; } + g_len -= len; + g_lba += len; + var fr = new FileReader(); + fr.onload = function () { + obj.SendDataToHost(g_dev, (g_len == 0), this.result, featureRegister & 1); + if ((g_len > 0) && (g_reset == false)) { + sendDiskDataEx(featureRegister); + } else { + g_media = null; + if (g_reset) { obj.SendCommand(0x47); g_readQueue = []; g_reset = false; } // Send ResetOccuredResponse + else if (g_readQueue.length > 0) { var op = g_readQueue.shift(); g_media = op.media; g_dev = op.dev; g_lba = op.lba; g_len = op.len; sendDiskDataEx(op.fr); } // Un-queue read operation + } + }; + //console.log('Read from ' + lba + ' to ' + (lba + len) + ', total of ' + len); + fr.readAsBinaryString(g_media.slice(lba, lba + len)); + } + + return obj; +} diff --git a/amt-lms-0.0.1.js b/amt-lms-0.0.1.js new file mode 100644 index 0000000..be22e97 --- /dev/null +++ b/amt-lms-0.0.1.js @@ -0,0 +1,104 @@ +/** +* @description Intel AMT LMS control module - using websocket +* @author Ylian Saint-Hilaire +* @version v0.0.1 +*/ + +// Construct a LMS control object +var CreateLmsControl = function () { + var obj = {}; + var socket = null; + obj.State = 0; + obj.onStateChanged = null; + obj.onData = null; + + // Private method + obj.Start = function () { + socket = new WebSocket(window.location.protocol.replace("http", "ws") + "//" + window.location.host + "/lms.ashx"); + socket.onopen = _OnSocketConnected; + socket.onmessage = _OnMessage; + socket.onclose = obj.Stop; + _StateChange(1); + } + + function _OnSocketConnected() { + _StateChange(2); + } + + // Setup the file reader + var fileReader = new FileReader(); + var fileReaderInuse = false, fileReaderAcc = []; + if (fileReader.readAsBinaryString) { + // Chrome & Firefox (Draft) + fileReader.onload = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } } + } else if (fileReader.readAsArrayBuffer) { + // Chrome & Firefox (Spec) + fileReader.onloadend = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } } + } + + function _OnMessage(e) { + if (typeof e.data == 'object') { + if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; } + if (fileReader.readAsBinaryString) { + // Chrome & Firefox (Draft) + fileReaderInuse = true; + fileReader.readAsBinaryString(new Blob([e.data])); + } else if (fileReader.readAsArrayBuffer) { + // Chrome & Firefox (Spec) + fileReaderInuse = true; + fileReader.readAsArrayBuffer(e.data); + } else { + // IE10, readAsBinaryString does not exist, use an alternative. + var binary = "", bytes = new Uint8Array(e.data), 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; } + + // Send the data up + if (obj.onData != null) obj.onData(obj, data); + } + + function _Send(x) { + if (socket != null && socket.readyState == WebSocket.OPEN) { + var b = new Uint8Array(x.length); + for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); } + socket.send(b.buffer); + } + } + + obj.SendCmd = function (cmdid, data) { + if (socket == null || obj.State != 2) return; + if (!data || data == null) data = ""; + _Send(ShortToStrX(cmdid) + data); + } + + function _StateChange(newstate) { + if (obj.State == newstate) return; + obj.State = newstate; + if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State); + } + + obj.Stop = function () { + _StateChange(0); + if (socket != null) { socket.close(); socket = null; } + } + + return obj; +} \ No newline at end of file diff --git a/amt-redir-node-0.1.0.js b/amt-redir-node-0.1.0.js new file mode 100644 index 0000000..fba1952 --- /dev/null +++ b/amt-redir-node-0.1.0.js @@ -0,0 +1,323 @@ +/** +* @description Intel AMT Redirection Transport Module - using Node +* @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.net = require('net'); + obj.tls = require('tls'); + obj.crypto = require('crypto'); + obj.constants = require('constants'); + obj.socket = null; + obj.host = null; + obj.port = 0; + obj.user = null; + obj.pass = null; + obj.connectstate = 0; + obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER + obj.xtlsoptions = null; + + obj.amtaccumulator = ''; + obj.amtsequence = 1; + obj.amtkeepalivetimer = null; + obj.authuri = '/RedirectionService'; + obj.digestRealmMatch = null; + + obj.onStateChanged = null; + + // Private method + //obj.Debug = function (msg) { console.log(msg); } + + obj.Start = function (host, port, user, pass, tls, tlsFingerprint, tlsoptions) { + obj.host = host; + obj.port = port; + obj.user = user; + obj.pass = pass; + obj.xtls = tls; + obj.xtlsoptions = tlsoptions; + obj.xtlsFingerprint = tlsFingerprint; + obj.connectstate = 0; + + if (obj.xtlsoptions && obj.xtlsoptions.meshServerConnect) { + // Use the websocket wrapper to connect to MeshServer server + obj.socket = CreateWebSocketWrapper(obj.xtlsoptions.host, obj.xtlsoptions.port, '/webrelay.ashx?user=' + encodeURIComponent(obj.xtlsoptions.username) + '&pass=' + encodeURIComponent(obj.xtlsoptions.password) + '&host=' + encodeURIComponent(obj.host) + '&p=2&tls=' + tlsFingerprint.xtls + '&tls1only=' + tlsFingerprint.xtlsMethod, obj.xtlsoptions.xtlsFingerprint); + obj.socket.setEncoding('binary'); + obj.socket.ondata = obj.xxOnSocketData; + obj.socket.onclose = obj.xxOnSocketClosed; + obj.socket.connect(obj.xxOnSocketConnected); + } else if (tls == false) { + obj.socket = new obj.net.Socket(); + obj.socket.setEncoding('binary'); + obj.socket.connect(port, host, obj.xxOnSocketConnected); + obj.socket.on('data', obj.xxOnSocketData); + obj.socket.on('close', obj.xxOnSocketClosed); + obj.socket.on('error', obj.xxOnSocketClosed); + } else { + if (obj.xtlsoptions == null) { obj.xtlsoptions = { secureProtocol: 'TLSv1_method', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false }; } + obj.socket = obj.tls.connect(port, host, obj.xtlsoptions, obj.xxOnSocketConnected); + obj.socket.setEncoding('binary'); + obj.socket.on('data', obj.xxOnSocketData); + obj.socket.on('close', obj.xxOnSocketClosed); + obj.socket.on('error', obj.xxOnSocketClosed); + } + obj.xxStateChange(1); + } + + // Get the certificate of Intel AMT + obj.getPeerCertificate = function () { if (obj.xtls == true) { return obj.socket.getPeerCertificate(); } return null; } + + obj.xxOnSocketConnected = function () { + if (obj.socket == null) return; + if (!obj.xtlsoptions || !obj.xtlsoptions.meshServerConnect) { + if (obj.xtls == true) { + obj.xtlsCertificate = obj.socket.getPeerCertificate(); + if ((obj.xtlsFingerprint != 0) && (obj.xtlsCertificate.fingerprint.split(':').join('').toLowerCase() != obj.xtlsFingerprint)) { obj.Stop(); return; } + } + } + + if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CONNECTED'); } + //obj.Debug("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 if not compiled-in. + if (obj.protocol == 3) obj.xxSend(obj.RedirectStartIder); + } + + obj.xxOnSocketData = function (data) { + if (!data || obj.connectstate == -1) return; + if (urlvars && urlvars['redirtrace']) { console.log('REDIR-RECV(' + data.length + '): ' + rstr2hex(data)); } + //obj.Debug("Recv(" + data.length + "): " + rstr2hex(data)); + 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("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; + 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) { + // ###BEGIN###{Mode-NodeWebkit} + if (obj.user == '*') { + if (authData.indexOf(2) >= 0) { + // Kerberos Auth + var ticket; + if (kerberos && kerberos != null) { + var ticketReturn = kerberos.getTicket('HTTP' + ((obj.tls == 1)?'S':'') + '/' + ((obj.pass == '') ? (obj.host + ':' + obj.port) : obj.pass)); + if (ticketReturn.returnCode == 0 || ticketReturn.returnCode == 0x90312) { + ticket = ticketReturn.ticket; + if (process.platform.indexOf('win') >= 0) { + // Clear kerberos tickets on both 32 and 64bit Windows platforms + try { require('child_process').exec('%windir%\\system32\\klist purge', function (error, stdout, stderr) { if (error) { require('child_process').exec('%windir%\\sysnative\\klist purge', function (error, stdout, stderr) { if (error) { console.error('Unable to purge kerberos tickets'); } }); } }); } catch (e) { console.log(e); } + } + } else { + console.error('Unexpected Kerberos error code: ' + ticketReturn.returnCode); + } + } + if (ticket) { + obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x02) + IntToStrX(ticket.length) + ticket); + } else { + obj.Stop(); + } + } + else obj.Stop(); + } else { + // ###END###{Mode-NodeWebkit} + // 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(); + // ###BEGIN###{Mode-NodeWebkit} + } + // ###END###{Mode-NodeWebkit} + } + 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); + + // Check the digest realm. If it does not match, close the connection. + if (obj.digestRealmMatch && (obj.digestRealmMatch != realm)) { obj.Stop(); return; } + + // 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.xxRandomValueHex(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) + ToIntStr(obj.amtsequence++) + ToShortStr(MaxTxBuffer) + ToShortStr(TxTimeout) + ToShortStr(TxOverflowTimeout) + ToShortStr(RxTimeout) + ToShortStr(RxFlushTimeout) + ToShortStr(Heartbeat) + ToIntStr(0)); + } + if (obj.protocol == 2) { + // Remote Desktop: Send traffic directly... + obj.xxSend(String.fromCharCode(0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + } + // ###BEGIN###{IDER} + if (obj.protocol == 3) { + // Remote IDER: Send traffic directly... + obj.connectstate = 1; + obj.xxStateChange(3); + } + // ###END###{IDER} + } 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) + ToIntStr(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) { + if (urlvars && urlvars['redirtrace']) { console.log('REDIR-SEND(' + x.length + '): ' + rstr2hex(x)); } + //obj.Debug("Send(" + x.length + "): " + rstr2hex(x)); + obj.socket.write(new Buffer(x, 'binary')); + } + + obj.Send = function (x) { + if (obj.socket == null || obj.connectstate != 1) return; + if (obj.protocol == 1) { obj.xxSend(String.fromCharCode(0x28, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + ToShortStr(x.length) + x); } else { obj.xxSend(x); } + } + + obj.xxSendAmtKeepAlive = function () { + if (obj.socket == null) return; + obj.xxSend(String.fromCharCode(0x2B, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++)); + } + + obj.xxRandomValueHex = function(len) { return obj.crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); } + + obj.xxOnSocketClosed = function () { + if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CLOSED'); } + //obj.Debug("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 () { + if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CLOSED'); } + //obj.Debug("Socket Stopped"); + obj.xxStateChange(0); + obj.connectstate = -1; + obj.amtaccumulator = ''; + if (obj.socket != null) { obj.socket.destroy(); 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; +} + +function ToIntStr(v) { return String.fromCharCode((v & 0xFF), ((v >> 8) & 0xFF), ((v >> 16) & 0xFF), ((v >> 24) & 0xFF)); } +function ToShortStr(v) { return String.fromCharCode((v & 0xFF), ((v >> 8) & 0xFF)); } diff --git a/amt-redir-ws-0.1.0.js b/amt-redir-ws-0.1.0.js new file mode 100644 index 0000000..9d13596 --- /dev/null +++ b/amt-redir-ws-0.1.0.js @@ -0,0 +1,335 @@ +/** +* @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'; + obj.tlsv1only = 0; + // ###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.digestRealmMatch = null; + + obj.onStateChanged = null; + + // Private method + //obj.Debug = function (msg) { console.log(msg); } + + // ###BEGIN###{!Mode-Firmware} + 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) : '') + '&tls1only=' + obj.tlsv1only); // 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); + } + // ###END###{!Mode-Firmware} + + // ###BEGIN###{Mode-Firmware} + obj.Start = function () { + obj.connectstate = 0; + obj.socket = new WebSocket(window.location.protocol.replace('http', 'ws') + '//' + window.location.host + '/ws-redirection'); + obj.socket.onopen = obj.xxOnSocketConnected; + obj.socket.onmessage = obj.xxOnMessage; + obj.socket.onclose = obj.xxOnSocketClosed; + obj.xxStateChange(1); + } + // ###END###{Mode-Firmware} + + obj.xxOnSocketConnected = function() { + if (urlvars && urlvars['redirtrace']) console.log('REDIR-CONNECT'); + 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); + } + + // Setup the file reader + var fileReader = new FileReader(); + var fileReaderInuse = false, fileReaderAcc = []; + if (fileReader.readAsBinaryString) { + // Chrome & Firefox (Draft) + fileReader.onload = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } } + } else if (fileReader.readAsArrayBuffer) { + // Chrome & Firefox (Spec) + fileReader.onloadend = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } } + } + + obj.xxOnMessage = function (e) { + //if (obj.debugmode == 1) { console.log('Recv', e.data); } + obj.inDataCount++; + if (typeof e.data == 'object') { + if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; } + if (fileReader.readAsBinaryString) { + // Chrome & Firefox (Draft) + fileReaderInuse = true; + fileReader.readAsBinaryString(new Blob([e.data])); + } else if (fileReader.readAsArrayBuffer) { + // Chrome & Firefox (Spec) + fileReaderInuse = true; + fileReader.readAsArrayBuffer(e.data); + } else { + // IE10, readAsBinaryString does not exist, use an alternative. + var binary = "", bytes = new Uint8Array(e.data), 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; + if (urlvars && urlvars['redirtrace']) console.log("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; + // ###BEGIN###{!Mode-Firmware} + // Query for available authentication + obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); // Query authentication support + // ###END###{!Mode-Firmware} + // ###BEGIN###{Mode-Firmware} + // When using websocket, we are already authenticated. Send empty basic auth. + obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00)); + // ###END###{Mode-Firmware} + 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; + // ###BEGIN###{!Mode-Firmware} + 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); + + // Check the digest realm. If it does not match, close the connection. + if (obj.digestRealmMatch && (obj.digestRealmMatch != realm)) { obj.Stop(); return; } + + // 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 + // ###END###{!Mode-Firmware} + 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; + case 0xF0: + // console.log('Session is being recorded'); + obj.serverIsRecording = true; + cmdsize = 1; + 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) { + if (urlvars && urlvars['redirtrace']) { console.log('REDIR-SEND(' + x.length + '): ' + rstr2hex(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 () { + if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CLOSED'); } + //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; +} diff --git a/amt-scanner-0.1.0.js b/amt-scanner-0.1.0.js new file mode 100644 index 0000000..1090b73 --- /dev/null +++ b/amt-scanner-0.1.0.js @@ -0,0 +1,235 @@ +/** +* @description Meshcentral Intel AMT Local Scanner +* @author Ylian Saint-Hilaire +* @version v0.0.1 +*/ + +// Construct a Intel AMT Scanner object +var CreateAmtScanner = function (func) { + var obj = {}; + obj.active = false; + obj.dgram = require('dgram'); + obj.servers = {}; + obj.rserver = {}; + obj.rpacket = null; + obj.tagToId = {}; // Tag --> { lastpong: time, id: NodeId } + obj.scanTable = {}; // Handle --> 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; + obj.computerPresenceFunc = func; + 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(hex2rstr('06000006000011BE80000000'), 'ascii'); + packet[9] = tag; + return packet; + } + + // Start scanning for local network Intel AMT computers + obj.start = function () { + if (obj.active == false) { + obj.active = true; + obj.performScan(); + obj.mainTimer = setInterval(obj.performScan, PeriodicScanTime); + } + } + + // 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 (id, rangestr, func) { + 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: id, range: rangestr, min: range.min, max: range.max, results: {}, resultCount: 0 }; + obj.rserver[id] = rangeinfo; + rangeinfo.server = obj.dgram.createSocket("udp4"); + rangeinfo.server.bind(0); + rangeinfo.server.on('error', function(err) { console.log(err); }); + rangeinfo.server.on('message', function(data, rinfo) { obj.parseRmcpPacket(data, rinfo, 0, func, rangeinfo); }); + rangeinfo.server.on('listening', function () { + for (var i = rangeinfo.min; i <= rangeinfo.max; i++) { + rangeinfo.server.send(obj.rpacket, 0, obj.rpacket.length, 623, obj.IPv4NumToStr(i)); + rangeinfo.server.send(obj.rpacket, 0, obj.rpacket.length, 623, obj.IPv4NumToStr(i)); + } + }); + rangeinfo.timer = setTimeout(function () { + if (rangeinfo.resultCount == 0) { func(obj, null); } + rangeinfo.server.close(); + delete rangeinfo.server; + delete rangeinfo; + }, 5000); + 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) return null; + for (var i = 0; i < (32 - 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); + } + + // Scan the list of all computers + obj.performScan = function () { + if (obj.active == false || computerlist == undefined) return; + for (var i in obj.scanTable) { obj.scanTable[i].present = false; } + if (computerlist.length > 0) { + for (var i in computerlist) { + var computer = computerlist[i]; + var host = computer.host.toLowerCase(); + if ((host != '127.0.0.1') && (host != '::1') && (host != 'localhost') && (host.split(':').length == 1)) { + var scaninfo = obj.scanTable[computer.h]; + if (scaninfo == undefined) { + var tag = obj.nextTag++; + obj.scanTableTags[tag] = obj.scanTable[computer.h] = scaninfo = { computer: computer, present: true, tag: tag, scanstate: -1, lastpong: Date.now() }; + } else { + scaninfo.present = true; + var delta = Date.now() - scaninfo.lastpong; + if ((delta > PeriodicScanTimeout) && (scaninfo.scanstate != 0)) { // More than the timeout without a response, mark the node as unknown state + scaninfo.scanstate = 0; + if (obj.computerPresenceFunc) { obj.computerPresenceFunc(scaninfo.computer.h, false); } + } + } + // Start scanning this node + scaninfo.lastping = Date.now(); + obj.checkAmtPresence(computer.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]; + } + } + } + + // 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', function(err) { }); + server.on('message', function (data, rinfo) { obj.parseRmcpPacket(data, rinfo, serverid, obj.changeConnectState, null); }); + server.on('listening', function() { 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], 0, p[1].length, 623, p[2]); + p[0].send(p[1], 0, p[1].length, 623, p[2]); + } else { + clearInterval(obj.pendingSendTimer); + obj.pendingSendTimer = null; + } + } catch (e) { } + } + + // Parse RMCP packet + obj.parseRmcpPacket = function (data, rinfo, serverid, func, rangeinfo) { + if (data == null || data.length < 20) return; + if (((data[12] == 0) || (data[13] != 0) || (data[14] != 1) || (data[15] != 0x57)) && (data[21] & 32)) { + var result = { tag: (serverid * 255) + data[9], ver: ((data[18] >> 4) & 0x0F) + '.' + (data[18] & 0x0F), state: (data[19] & 0x03), port: ((data[16] * 256) + data[17]), dualport: (((data[19] & 0x04) != 0) ? true : false), rinfo: rinfo }; + result.tls = (((result.openPort == 16993) || (result.dualPorts == true)) ? 1 : 0) + if (rangeinfo != null) { + if ((result.state <= 2) && (!rangeinfo.results[rinfo.address])) { + rangeinfo.results[rinfo.address] = result; + rangeinfo.resultCount++; + obj.reverseLookup(rinfo.address, function (err, domains) { + result.domains = domains; + func(obj, result, rangeinfo); + }); + } + } else { + func(obj, result, rangeinfo); + } + } + } + + // Use the RMCP packet to change the computer state + obj.changeConnectState = function (obj, result, rangeinfo) { + var scaninfo = obj.scanTableTags[result.tag]; + if (scaninfo) { + scaninfo.lastpong = Date.now(); + if (scaninfo.scanstate != 1) { + scaninfo.scanstate = 1; + if (obj.computerPresenceFunc) obj.computerPresenceFunc(scaninfo.computer.h, true); + } + } + } + + // Perform DNS reverse lookup + obj.reverseLookup = function(ip, callback) { + var callbackCalled = false, timer = null; + var doCallback = function (err, domains) { + if (err) domains = [ip]; + if (callbackCalled) return; + callbackCalled = true; + if (timer != null) { + // Cancelling timer to safe few seconds + clearTimeout(timer); + timer = null; + } + callback(err, domains); + } + // throw failed upon timeout + timer = setTimeout(function () { + timer = null; // Ensure timer is nullified + doCallback(new Error("Timeout exceeded"), null); + }, 3000); + + require("dns").reverse(ip, doCallback); + } + + return obj; +} \ No newline at end of file diff --git a/amt-script-0.2.0.js b/amt-script-0.2.0.js new file mode 100644 index 0000000..3ebfc70 --- /dev/null +++ b/amt-script-0.2.0.js @@ -0,0 +1,440 @@ +/** +* @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', 'pulleventsubscriptions']; + +// 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); } + , + // ###BEGIN###{EventSubscriptions} + PullEventSubscriptions + // ###END###{EventSubscriptions} +]; + +// 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; +} diff --git a/amt-setupbin-0.1.0.js b/amt-setupbin-0.1.0.js new file mode 100644 index 0000000..1797bae --- /dev/null +++ b/amt-setupbin-0.1.0.js @@ -0,0 +1,273 @@ +/** +* @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", + { 0: "None", 1: "Intel AMT" }], + 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: [4, "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", { + 0: "None", 1: "SOL only - User/Pass Disabled", 2: "IDER only - User/Pass Disabled", 3: "SOL+IDER - User/Pass Disabled", + 4: "None - User/Pass Enabled", 5: "SOL only - User/Pass Enabled", 6: "IDER only - User/Pass Enabled", 7: "SOL+IDER - User/Pass Enabled" + }], + 11: [0, "Hostname"], // 63 bytes max length + 12: [0, "Domain Name"], // 255 bytes max length + 13: [1, "DHCP", { 1: "Disabled", 2: "Enabled" }], + 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 IPv4 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); // 1 byte number + else if (v.type == 2 && v.length == 2) v.value = ReadShortX(v.value, 0); // 2 byte number + else if (v.type == 3 && v.length == 4) v.value = ReadIntX(v.value, 0); // 4 byte number + else if (v.type == 4) v.value = guidToStr(rstr2hex(v.value)); // GUID + 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) && (v.type < 4)) { // 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); + } + if (v.type == 4) { data = hex2rstr(guidToStr(data.split('-').join('')).split('-').join('')); } + 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); } } diff --git a/amt-terminal-0.0.2.js b/amt-terminal-0.0.2.js new file mode 100644 index 0000000..fb2ee4a --- /dev/null +++ b/amt-terminal-0.0.2.js @@ -0,0 +1,813 @@ +/** +* @description Remote Terminal +* @author Ylian Saint-Hilaire +* @version v0.0.2c +*/ + +// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +// https://www.x.org/docs/xterm/ctlseqs.pdf + +// Construct a MeshServer object +var CreateAmtRemoteTerminal = function (divid, options) { + var obj = {}; + obj.DivId = divid; + obj.DivElement = document.getElementById(divid); + obj.protocol = 1; // SOL + if (options && options.protocol) { obj.protocol = options.protocol; } // 1 = Normal, 6 = PowerShell + // ###BEGIN###{Terminal-Enumation-All} + obj.terminalEmulation = 1; + // ###END###{Terminal-Enumation-All} + obj.fxEmulation = 0; + obj.lineFeed = '\r\n'; + obj.debugmode = 0; + + obj.width = 80; // 80 or 100 + obj.height = 25; // 25 or 30 + obj.heightLock = 0; + + 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 _termsavex = 0; + var _termsavey = 0; + var _termstate = 0; + var _escNumber = []; + var _escNumberPtr = 0; + var _escNumberMode = 0; + var _scratt = []; + var _tscreen = []; + var _VTUNDERLINE = 1; + var _VTREVERSE = 2; + var _backSpaceErase = false; + var _cursorVisible = true; + var _scrollRegion; + var _altKeypadMode = false; + var scrollBackBuffer = []; + // ###BEGIN###{Terminal-Enumation-UTF8} + var utf8decodeBuffer = ''; + // ###END###{Terminal-Enumation-UTF8} + // ###BEGIN###{Terminal-Enumation-All} + var utf8decodeBuffer = ''; + // ###END###{Terminal-Enumation-All} + obj.title = null; + obj.onTitleChange = null; + + 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) { + if ((newstate == 3) && (options != null) && (options.xterm == true)) { obj.TermSendKeys('stty rows ' + obj.height + ' cols ' + obj.width + '\nclear\n'); } + } + + obj.ProcessData = function (str) { + if (obj.debugmode == 2) { console.log("TRecv(" + str.length + "): " + rstr2hex(str)); } + if (obj.capture != null) obj.capture += str; + // ###BEGIN###{Terminal-Enumation-UTF8} + try { str = decode_utf8(utf8decodeBuffer + str); } catch (ex) { utf8decodeBuffer += str; return; } // If we get data in the middle of a UTF-8 code, buffer it for next time. + utf8decodeBuffer = ''; + // ###END###{Terminal-Enumation-UTF8} + // ###BEGIN###{Terminal-Enumation-All} + if (obj.terminalEmulation == 0) { try { str = decode_utf8(utf8decodeBuffer + str); } catch (ex) { utf8decodeBuffer += str; return; } } // If we get data in the middle of a UTF-8 code, buffer it for next time. + utf8decodeBuffer = ''; + // ###END###{Terminal-Enumation-All} + _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; + _escNumber = []; + _escNumberPtr = 0; + _escNumberMode = 0; + break; + default: + // Process a single char + _ProcessVt100Char(b); + break; + } + break; + case 1: + switch (b) { + case '[': + _termstate = 2; + break; + case '(': + _termstate = 4; + break; + case ')': + _termstate = 5; + break; + case ']': + _termstate = 6; // xterm strings + break; + case '=': + // Set alternate keypad mode + _altKeypadMode = true; + _termstate = 0; + break; + case '>': + // Set numeric keypad mode + _altKeypadMode = false; + _termstate = 0; + break; + case '7': + // Save Cursor + _termsavex = _termx; + _termsavey = _termy; + _termstate = 0; + break; + case '8': + // Restore Cursor + _termx = _termsavex; + _termy = _termsavey; + _termstate = 0; + break; + case 'M': + // Scroll down one + var x = 1; + for (var y = _scrollRegion[1]; y >= _scrollRegion[0] + x; y--) { + for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y - x][z]; _scratt[y][z] = _scratt[y - x][z]; } + } + for (var y = _scrollRegion[0] + x - 1; y > _scrollRegion[0] - 1; y--) { + for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); } + } + _termstate = 0; + break; + default: + console.log('unknown terminal short code', b); + _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 if (b == '?') { + _escNumberMode = 1; + break; + } else { + // Process Escape Sequence + if (!_escNumber[0]) _escNumber[0] = 0; + _ProcessEscapeHandler(b, _escNumber, _escNumberPtr + 1, _escNumberMode); + _termstate = 0; + } + break; + case 4: // '(' Code + _termstate = 0; + break; + case 5: // ')' Code + _termstate = 0; + break; + case 6: // ']' Code, xterm + var bx = b.charCodeAt(0); + if (b == ';') { + _escNumberPtr++; + } else if (bx == 7) { + _ProcessXTermHandler(_escNumber); + _termstate = 0; + } else { + if (!_escNumber[_escNumberPtr]) { _escNumber[_escNumberPtr] = b; } + else { _escNumber[_escNumberPtr] += b; } + } + break; + } + } + + function _ProcessXTermHandler(_escNumber) { + if (_escNumber.length == 0) return; + var cmd = parseInt(_escNumber[0]); + if ((cmd == 0 || cmd == 2) && (_escNumber.length > 1) && (_escNumber[1] != '?')) { + if (obj.onTitleChange) { obj.onTitleChange(obj, obj.title = _escNumber[1]); } + } + } + + function _ProcessEscapeHandler(code, args, argslen, mode) { + //console.log('process', code, args, mode); + if (mode == 1) { + switch (code) { + case 'l': // Hide the cursor + if (args[0] == 25) { _cursorVisible = false; } + break; + case 'h': // Show the cursor + if (args[0] == 25) { _cursorVisible = true; } + break; + } + } else if (mode == 0) { + var i; + switch (code) { + case 'c': // ResetDevice + // Reset + obj.TermResetScreen(); + break; + case 'A': // Move cursor up n lines + if (argslen == 1) { + if (args[0] == 0) { _termy--; } else { _termy -= args[0]; } + if (_termy < 0) _termy = 0; + } + break; + case 'B': // Move cursor down n lines + if (argslen == 1) { + if (args[0] == 0) { _termy++; } else { _termy += args[0]; } + if (_termy > obj.height) _termy = obj.height; + } + break; + case 'C': // Move cursor right n lines + if (argslen == 1) { + if (args[0] == 0) { _termx++; } else { _termx += args[0]; } + if (_termx > obj.width) _termx = obj.width; + } + break; + case 'D': // Move cursor left n lines + if (argslen == 1) { + if (args[0] == 0) { _termx--; } else { _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 > (obj.width - 1)) _termx = (obj.width - 1); + } + break; + case 'P': // Delete X Character(s), default 1 char + var x = 1; + if (argslen == 1) { x = args[0]; } + for (i = _termx; i < obj.width - x; i++) { _tscreen[_termy][i] = _tscreen[_termy][i + x]; _scratt[_termy][i] = _scratt[_termy][i + x]; } + for (i = (obj.width - x); i < obj.width; i++) { _tscreen[_termy][i] = ' '; _scratt[_termy][i] = (7 << 6); } + break; + case 'L': // Insert X Line(s), default 1 char + var linecount = 1; + if (argslen == 1) { linecount = args[0]; } + if (linecount == 0) { linecount = 1; } + for (y = _scrollRegion[1]; y >= _termy + linecount; y--) { + _tscreen[y] = _tscreen[y - linecount]; + _scratt[y] = _scratt[y - linecount]; + } + for (y = _termy; y < _termy + linecount; y++) { + _tscreen[y] = []; + _scratt[y] = []; + for (x = 0; x < obj.width; x++) { _tscreen[y][x] = ' '; _scratt[y][x] = (7 << 6); } + } + break; + case 'J': // ClearScreen: + if (argslen == 1 && args[0] == 2) { + obj.TermClear((_TermCurrentBColor << 12) + (_TermCurrentFColor << 6)); // Erase entire screen + _termx = 0; + _termy = 0; + scrollBackBuffer = []; + } + 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; + case 'r': // Set the scroll region + if (argslen == 2) { _scrollRegion = [args[0] - 1, args[1] - 1]; } + if (_scrollRegion[0] < 0) { _scrollRegion[0] = 0; } + if (_scrollRegion[0] > (obj.height - 1)) { _scrollRegion[0] = (obj.height - 1); } + if (_scrollRegion[1] < 0) { _scrollRegion[1] = 0; } + if (_scrollRegion[1] > (obj.height - 1)) { _scrollRegion[1] = (obj.height - 1); } + if (_scrollRegion[0] > _scrollRegion[1]) { _scrollRegion[0] = _scrollRegion[1]; } + break; + case 'S': // Scroll up the scroll region X lines, default 1 + var x = 1; + if (argslen == 1) { x = args[0] } + for (var y = _scrollRegion[0]; y <= _scrollRegion[1] - x; y++) { + for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y + x][z]; _scratt[y][z] = _scratt[y + x][z]; } + } + for (var y = _scrollRegion[1] - x + 1; y < _scrollRegion[1]; y++) { + for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); } + } + break; + case 'M': // Delete X lines, default 1 + var x = 1; + if (argslen == 1) { x = args[0] } + for (var y = _termy; y <= _scrollRegion[1] - x; y++) { + for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y + x][z]; _scratt[y][z] = _scratt[y + x][z]; } + } + for (var y = _scrollRegion[1] - x + 1; y < _scrollRegion[1]; y++) { + for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); } + } + break; + case 'T': // Scroll down the scroll region X lines, default 1 + var x = 1; + if (argslen == 1) { x = args[0] } + for (var y = _scrollRegion[1]; y > _scrollRegion[0] + x; y--) { + for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y - x][z]; _scratt[y][z] = _scratt[y - x][z]; } + } + for (var y = _scrollRegion[0] + x; y > _scrollRegion[0]; y--) { + for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); } + } + break; + case 'X': // Erase X characters, default 1 + var x = 1, xx = _termx, yy = _termy; + if (argslen == 1) { x = args[0] } + while ((x > 0) && (yy < obj.height)) { _tscreen[yy][xx] = ' '; xx++; x--; if (xx >= obj.width) { xx = 0; yy++; } } + break; + default: + //if (code != '@') alert(code); + console.log('unknown terminal code', code, args, mode); + 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(); + //console.log('_ProcessVt100Char', ch, c); + + // ###BEGIN###{Terminal-Enumation-All} + // UTF8 Terminal + 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-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--; + if (_backSpaceErase) { _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 > _scrollRegion[1]) { + // Move everything up one line + obj.recordLineTobackBuffer(0); + _TermMoveUp(1); + _termy = _scrollRegion[1]; + } + if (obj.lineFeed = '\r') { _termx = 0; } // *** If we are in Linux mode, \n will also return the cursor to the first col + 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; + } + } + scrollBackBuffer = []; + } + + obj.TermResetScreen = function () { + _TermCurrentReverse = 0; + _TermCurrentFColor = 7; + _TermCurrentBColor = 0; + _TermLineWrap = _cursorVisible = true; + _termx = _termy = 0; + _backSpaceErase = false; + _scrollRegion = [0, (obj.height - 1)]; + _altKeypadMode = false; + obj.TermClear(7 << 6); + // ###BEGIN###{Terminal-Enumation-UTF8} + //utf8decodeBuffer = ''; + // ###END###{Terminal-Enumation-UTF8} + // ###BEGIN###{Terminal-Enumation-All} + utf8decodeBuffer = ''; + // ###END###{Terminal-Enumation-All} + } + + function _EraseCursorToEol() { + var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse; + for (var x = _termx; x < obj.width; x++) { + _tscreen[_termy][x] = ' '; + _scratt[_termy][x] = t; + } + } + + function _EraseBolToCursor() { + var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse; + for (var x = 0; x < _termx; x++) { + _tscreen[_termy][x] = ' '; + _scratt[_termy][x] = t; + } + } + + function _EraseLine(line) { + var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse; + for (var x = 0; x < obj.width; x++) { + _tscreen[line][x] = ' '; + _scratt[line][x] = t; + } + } + + obj.TermSendKeys = function (keys) { if (obj.debugmode == 2) { console.log("TSend(" + keys.length + "): " + rstr2hex(keys), keys); } if (obj.parent) { obj.parent.Send(keys); } } + obj.TermSendKey = function (key) { if (obj.debugmode == 2) { console.log("TSend(1): " + rstr2hex(String.fromCharCode(key)), key); } if (obj.parent) { obj.parent.Send(String.fromCharCode(key)); } } + + function _TermMoveUp(linecount) { + var x, y; + for (y = _scrollRegion[0]; y <= _scrollRegion[1] - linecount; y++) { + _tscreen[y] = _tscreen[y + linecount]; + _scratt[y] = _scratt[y + linecount]; + } + for (y = _scrollRegion[1] - linecount + 1; y <= _scrollRegion[1]; 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(obj.lineFeed); } + 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 (_altKeypadMode == true) { + if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 79, 68)); return true; }; // Left + if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 79, 65)); return true; }; // Up + if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 79, 67)); return true; }; // Right + if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 79, 66)); return true; }; // Down + } else { + 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 == 33) { obj.TermSendKeys(String.fromCharCode(27, 91, 53, 126)); return true; }; // PageUp + if (e.which == 34) { obj.TermSendKeys(String.fromCharCode(27, 91, 54, 126)); return true; }; // PageDown + if (e.which == 35) { obj.TermSendKeys(String.fromCharCode(27, 91, 70)); return true; }; // End + if (e.which == 36) { obj.TermSendKeys(String.fromCharCode(27, 91, 72)); return true; }; // Home + if (e.which == 45) { obj.TermSendKeys(String.fromCharCode(27, 91, 50, 126)); return true; }; // Insert + if (e.which == 46) { obj.TermSendKeys(String.fromCharCode(27, 91, 51, 126)); return true; }; // Delete + + 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.recordLineTobackBuffer = function(y) { + var closetag = '', buf = ''; + var r = obj.TermDrawLine(buf, y, closetag); + buf = r[0]; + closetag = r[1]; + scrollBackBuffer.push(buf + closetag + '
'); + } + + obj.TermDrawLine = function (buf, y, closetag) { + var newat, c, oldat = 1, x1, x2; + for (var x = 0; x < obj.width; ++x) { + newat = _scratt[y][x]; + if (_termx == x && _termy == y && _cursorVisible) { 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 += ''; + closetag = "" + 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; + } + } + return [buf, closetag]; + } + + obj.TermDraw = function() { + var closetag = '', buf = ''; + for (var y = 0; y < obj.height; ++y) { + var r = obj.TermDrawLine(buf, y, closetag); + buf = r[0]; + closetag = r[1]; + if (y != (obj.height - 1)) buf += '
'; + } + + if (scrollBackBuffer.length > 800) { scrollBackBuffer = scrollBackBuffer.slice(scrollBackBuffer.length - 800); } + var backbuffer = scrollBackBuffer.join(''); + obj.DivElement.innerHTML = "" + backbuffer + buf + closetag + ""; + obj.DivElement.scrollTop = obj.DivElement.scrollHeight; + //if (obj.heightLock == 0) { setTimeout(obj.TermLockHeight, 10); } + } + + /* + obj.TermLockHeight = function () { + obj.heightLock = obj.DivElement.clientHeight; + obj.DivElement.style['height'] = obj.DivElement.parentNode.style['height'] = obj.heightLock + 'px'; + obj.DivElement.style['overflow-y'] = 'scroll'; + } + */ + + obj.TermInit = function () { obj.TermResetScreen(); } + + //obj.heightLock = 0; + //obj.DivElement.style['height'] = ''; + if ((options != null) && (options.width != null) && (options.height != null)) { obj.Init(options.width, options.height); } else { obj.Init(); } + return obj; +} \ No newline at end of file diff --git a/amt-wsman-0.2.0.js b/amt-wsman-0.2.0.js new file mode 100644 index 0000000..7426aa9 --- /dev/null +++ b/amt-wsman-0.2.0.js @@ -0,0 +1,336 @@ +/** +* @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 == null) namespaces = ''; + obj.comm.PerformAjax('
' + 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 = "", opaque = ""; + if (user != null && pass != null) { digest = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken' + user + '' + pass + ''; digest2 = ''; } + if (opaque != null) { opaque = '' + opaque + ''; } + if (delivery == 'PushWithAck') { delivery = 'dmtf.org/wbem/wsman/1/wsman/PushWithAck'; } else if (delivery == 'Push') { delivery = 'xmlsoap.org/ws/2004/08/eventing/DeliveryModes/Push'; } + var data = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous" + _PutObjToSelectorsXml(selectors) + digest + '
' + url + '' + opaque + '' + digest2 + ''; + obj.PerformAjax(data + "
", callback, tag, pri, 'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" 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" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous" + _PutObjToSelectorsXml(selectors) + ''; + obj.PerformAjax(data + "", 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" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60.000S" + _PutObjToSelectorsXml(selectors) + '' + _PutObjToBodyXml(resuri, putobj); + obj.PerformAjax(data + "", 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" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S" + _PutObjToSelectorsXml(selectors) + ""; + for (var n in putobj) { data += "" + putobj[n] + "" } + obj.PerformAjax(data + "", 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" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S" + _PutObjToSelectorsXml(putobj) + ""; + 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" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S", 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 += "" + args[i][x] + ""; } } else { argsxml += "" + args[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 + "" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S" + _PutObjToSelectorsXml(selectors) + "" + argsxml + "", 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" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S", 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" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S" + enumctx + "", callback, tag, pri); // --99999999-- + } + + // 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 == null) || (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 + if ((parseInt(data) + '') === data) data = parseInt(data); // Convert integers + + var childObj = data; + if ((child.attributes != null) && (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] == null) { r[child.localName] = childObj; } + else { r[child.localName] = [r[child.localName], childObj]; } + } + return r; + } + + function _PutObjToBodyXml(resuri, putObj) { + if (!resuri || putObj == null) return ''; + var objname = obj.GetNameFromUrl(resuri); + var result = ''; + + for (var prop in putObj) { + if (!putObj.hasOwnProperty(prop) || prop.indexOf('__') === 0 || prop.indexOf('@') === 0) continue; + if (putObj[prop] == null || typeof putObj[prop] === 'function') continue; + if (typeof putObj[prop] === 'object' && putObj[prop]['ReferenceParameters']) { + result += '' + putObj[prop].Address + '' + putObj[prop]['ReferenceParameters']["ResourceURI"] + ''; + var selectorArray = putObj[prop]['ReferenceParameters']['SelectorSet']['Selector']; + if (Array.isArray(selectorArray)) { + for (var i=0; i< selectorArray.length; i++) { + result += '' + selectorArray[i]['Value'] + ''; + } + } + else { + result += '' + selectorArray['Value'] + ''; + } + result += ''; + } + else { + if (Array.isArray(putObj[prop])) { + for (var i = 0; i < putObj[prop].length; i++) { + result += '' + putObj[prop][i].toString() + ''; + } + } else { + result += '' + putObj[prop].toString() + ''; + } + } + } + + result += ''; + 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 "" + selectorSet['InstanceID'] + ""; + var result = ''; + for(var propName in selectorSet) { + if (!selectorSet.hasOwnProperty(propName)) continue; + result += ''; + if (selectorSet[propName]['ReferenceParameters']) { + result += ''; + result += '' + selectorSet[propName]['Address'] + '' + selectorSet[propName]['ReferenceParameters']['ResourceURI'] + ''; + var selectorArray = selectorSet[propName]['ReferenceParameters']['SelectorSet']['Selector']; + if (Array.isArray(selectorArray)) { + for (var i = 0; i < selectorArray.length; i++) { + result += '' + selectorArray[i]['Value'] + ''; + } + } + else { + result += '' + selectorArray['Value'] + ''; + } + result += ''; + } else { + result += selectorSet[propName]; + } + result += ''; + } + result += ''; + return result; + } + + function _turnToXml(text) { + // ###BEGIN###{!Mode-MeshCentral2} + // NodeJS detection + var isNode = new Function("try {return this===global;}catch(e){return false;}"); + if (isNode()) { + var XDOMParser = require('xmldom').DOMParser; + return new XDOMParser().parseFromString(text, "text/xml"); + } + // ###END###{!Mode-MeshCentral2} + 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; + } + } + + /* + // This is a drop-in replacement to _turnToXml() that works without xml parser dependency. + Object.defineProperty(Array.prototype, "peek", { value: function () { return (this.length > 0 ? this[this.length - 1] : null); } }); + function _treeBuilder() { + this.tree = []; + this.push = function (element) { this.tree.push(element); }; + this.pop = function () { var element = this.tree.pop(); if (this.tree.length > 0) { var x = this.tree.peek(); x.childNodes.push(element); x.childElementCount = x.childNodes.length; } return (element); }; + this.peek = function () { return (this.tree.peek()); } + this.addNamespace = function (prefix, namespace) { this.tree.peek().nsTable[prefix] = namespace; if (this.tree.peek().attributes.length > 0) { for (var i = 0; i < this.tree.peek().attributes; ++i) { var a = this.tree.peek().attributes[i]; if (prefix == '*' && a.name == a.localName) { a.namespace = namespace; } else if (prefix != '*' && a.name != a.localName) { var pfx = a.name.split(':')[0]; if (pfx == prefix) { a.namespace = namespace; } } } } } + this.getNamespace = function (prefix) { for (var i = this.tree.length - 1; i >= 0; --i) { if (this.tree[i].nsTable[prefix] != null) { return (this.tree[i].nsTable[prefix]); } } return null; } + } + function _turnToXml(text) { if (text == null) return null; return ({ childNodes: [_turnToXmlRec(text)], getElementsByTagName: _getElementsByTagName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS }); } + function _getElementsByTagNameNS(ns, name) { var ret = []; _xmlTraverseAllRec(this.childNodes, function (node) { if (node.localName == name && (node.namespace == ns || ns == '*')) { ret.push(node); } }); return ret; } + function _getElementsByTagName(name) { var ret = []; _xmlTraverseAllRec(this.childNodes, function (node) { if (node.localName == name) { ret.push(node); } }); return ret; } + function _getChildElementsByTagName(name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name) { ret.push(this.childNodes[node]); } } } return (ret); } + function _getChildElementsByTagNameNS(ns, name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } } } return (ret); } + function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } } + function _turnToXmlRec(text) { + var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null; + for (var i in x1) { + var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0]; + if ((elementName.length > 0) && (elementName[0] != '?')) { + if (elementName[0] != '/') { + var attributes = [], localName, localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0]; + Object.defineProperty(attributes, "get", + { + value: function () { + if (arguments.length == 1) { + for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } } + } + else if (arguments.length == 2) { + for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } } + } + else { + throw ('attributes.get(): Invalid number of parameters'); + } + } + }); + elementStack.push({ name: elementName, localName: localName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS, getChildElementsByTagNameNS: _getChildElementsByTagNameNS, attributes: attributes, childNodes: [], nsTable: {} }); + // Parse Attributes + if (x3.length > 0) { + var skip = false; + for (var j in x3) { + if (x3[j] == '/') { + // This is an empty Element + elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':'))); + elementStack.peek().textContent = ''; + lastElement = elementStack.pop(); + skip = true; + break; + } + var k = x3[j].indexOf('='); + if (k > 0) { + var attrName = x3[j].substring(0, k); + var attrValue = x3[j].substring(k + 2, x3[j].length - 1); + var attrNS = elementStack.getNamespace('*'); + + if (attrName == 'xmlns') { + elementStack.addNamespace('*', attrValue); + attrNS = attrValue; + } else if (attrName.startsWith('xmlns:')) { + elementStack.addNamespace(attrName.substring(6), attrValue); + } else { + var ax = attrName.split(':'); + if (ax.length == 2) { attrName = ax[1]; attrNS = elementStack.getNamespace(ax[0]); } + } + var x = { name: attrName, value: attrValue } + if (attrNS != null) x.namespace = attrNS; + elementStack.peek().attributes.push(x); + } + } + if (skip) { continue; } + } + elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':'))); + if (x2[1]) { elementStack.peek().textContent = x2[1]; } + } else { lastElement = elementStack.pop(); } + } + } + return lastElement; + } + */ + + return obj; +} diff --git a/amt-wsman-ajax-0.2.0.js b/amt-wsman-ajax-0.2.0.js new file mode 100644 index 0000000..7cca880 --- /dev/null +++ b/amt-wsman-ajax-0.2.0.js @@ -0,0 +1,95 @@ +/** +* @description WSMAN communication using browser AJAX +* @author Ylian Saint-Hilaire +* @version v0.2.0c +*/ + +// Construct a WSMAN communication object +var CreateWsmanComm = function (url) { + 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.Url = url; + + // 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 == 0 || ((obj.ActiveAjaxCount < obj.MaxActiveAjaxCount) && (obj.challengeParams != null))) && 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) { if (obj.FailAllError != 999) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag]); } return; } + // console.log("SEND: " + postdata); // DEBUG + + // We are in a AJAX browser environment + obj.ActiveAjaxCount++; + if (!postdata) { postdata = ''; } + var xdr = null; // TODO: See if we should re-use this object and if we do, when should we do it. + try { xdr = new XDomainRequest(); } catch (e) { } + if (!xdr) xdr = new XMLHttpRequest(); + xdr.open(action ? action : "POST", url ? url : obj.Url); + xdr.timeout = 15000; + xdr.onload = function () { obj.gotNextMessages(xdr.responseText, 'success', xdr, [postdata, callback, tag]); }; + xdr.onerror = function () { obj.gotNextMessagesError(xdr, 'error', null, [postdata, callback, tag]); }; + xdr.ontimeout = function () { obj.gotNextMessagesError(xdr, 'error', null, [postdata, callback, tag]); }; + //xdr.send(postdata); // Works for text only, no binary. + + // Send POST body, this work with binary. + if (urlvars && urlvars['wsmantrace']) { console.log("WSMAN-SEND(" + postdata.length + "): " + postdata); } + var b = new Uint8Array(postdata.length); + for (var i = 0; i < postdata.length; ++i) { b[i] = postdata.charCodeAt(i); } + xdr.send(b); + + return xdr; + } + + // AJAX specific private method + obj.pendingAjaxCall = []; + + // Private method + obj.gotNextMessages = function (data, status, request, callArgs) { + if (urlvars && urlvars['wsmantrace']) { console.log("WSMAN-RECV(" + data.length + "): " + data); } + obj.ActiveAjaxCount--; + if (obj.FailAllError == 999) return; + // console.log("RECV: " + data); // DEBUG + if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; } + if (request.status != 200) { callArgs[1](null, request.status, callArgs[2]); obj.PerformNextAjax(); return; } + callArgs[1](data, 200, callArgs[2]); + obj.PerformNextAjax(); + } + + // Private method + obj.gotNextMessagesError = function (request, status, errorThrown, callArgs) { + obj.ActiveAjaxCount--; + if (obj.FailAllError == 999) return; + if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; } + // if (s != 200) { console.log("ERROR, status=" + status + "\r\n\r\nreq=" + callArgs[0]); } // Debug: Display the request & response if something did not work. + if (obj.FailAllError != 999) { callArgs[1]({ Header: { HttpError: request.status } }, request.status, callArgs[2]); } + obj.PerformNextAjax(); + } + + // 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]); } + } + + return obj; +} + diff --git a/amt-wsman-node-0.2.0.js b/amt-wsman-node-0.2.0.js new file mode 100644 index 0000000..4a633cb --- /dev/null +++ b/amt-wsman-node-0.2.0.js @@ -0,0 +1,392 @@ +/** +* @description Intel(r) AMT WSMAN communication using Node.js TLS +* @author Ylian Saint-Hilaire +* @version v0.2.0b +*/ + +// Construct a MeshServer object +var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions) { + //console.log('CreateWsmanComm', host, port, user, pass, tls, tlsoptions); + + 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.Address = '/wsman'; + obj.challengeParams = null; + obj.noncecounter = 1; + obj.authcounter = 0; + obj.cnonce = Math.random().toString(36).substring(7); // Generate a random client nonce + + obj.net = require('net'); + obj.tls = require('tls'); + obj.crypto = require('crypto'); + obj.constants = require('constants'); + obj.socket = null; + obj.socketState = 0; + obj.kerberosDone = 0; + obj.amtVersion = null; + + obj.host = host; + obj.port = port; + obj.user = user; + obj.pass = pass; + obj.xtls = tls; + obj.xtlsoptions = tlsoptions; + obj.xtlsFingerprint; + obj.xtlsCertificate = null; + obj.xtlsCheck = 0; // 0 = No TLS, 1 = CA Checked, 2 = Pinned, 3 = Untrusted + obj.xtlsSkipHostCheck = 0; + obj.xtlsMethod = 0; + obj.xtlsDataReceived = false; + obj.digestRealmMatch = null; + obj.digestRealm = null; + + // 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 == 0 || ((obj.ActiveAjaxCount < obj.MaxActiveAjaxCount) && (obj.challengeParams != null))) && 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 = ''; + //obj.Debug("SEND: " + postdata); // DEBUG + + obj.ActiveAjaxCount++; + return obj.PerformAjaxExNodeJS(postdata, callback, tag, url, action); + } + + // NODE.js specific private method + obj.pendingAjaxCall = []; + + // NODE.js specific private method + obj.PerformAjaxExNodeJS = function (postdata, callback, tag, url, action) { obj.PerformAjaxExNodeJS2(postdata, callback, tag, url, action, 5); } + + // NODE.js 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); } + } + + // NODE.js specific private method + 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) { + obj.digestRealm = obj.challengeParams['realm']; + if (obj.digestRealmMatch && (obj.digestRealm != obj.digestRealmMatch)) { + obj.FailAllError = 997; // Cause all new responses to be silent. 997 = Digest Realm check error + obj.CancelAllQueries(997); + return; + } + } + if ((obj.user == '*') && (kerberos != null)) { + // Kerberos Auth + if (obj.kerberosDone == 0) { + var ticketName = 'HTTP' + ((obj.tls == 1) ? 'S' : '') + '/' + ((obj.pass == '') ? (obj.host + ':' + obj.port) : obj.pass); + // Ask for the new Kerberos ticket + //console.log('kerberos.getTicket', ticketName); + var ticketReturn = kerberos.getTicket(ticketName); + if (ticketReturn.returnCode == 0 || ticketReturn.returnCode == 0x90312) { + h += 'Authorization: Negotiate ' + ticketReturn.ticket + '\r\n'; + if (process.platform.indexOf('win') >= 0) { + // Clear kerberos tickets on both 32 and 64bit Windows platforms + try { require('child_process').exec('%windir%\\system32\\klist purge', function (error, stdout, stderr) { if (error) { require('child_process').exec('%windir%\\sysnative\\klist purge', function (error, stdout, stderr) { if (error) { console.error('Unable to purge kerberos tickets'); } }); } }); } catch (e) { console.log(e); } + } + } else { + console.log('Unexpected Kerberos error code: ' + ticketReturn.returnCode); + } + obj.kerberosDone = 1; + } + } else 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 + ((obj.challengeParams['qop'] == 'auth-int')?(':' + hex_md5(postdata)):''))); + 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 + obj.xxSend(h); + //console.log("SEND: " + h); // Display send packet + } + + // Parse the HTTP digest header and return a list of key & values. + obj.parseDigest = function (header) { return correctedQuoteSplit(header.substring(7)).reduce(function (obj, s) { var parts = s.trim().split('='); obj[parts[0]] = parts[1].replace(new RegExp('\"', 'g'), ''); return obj; }, {}) } + + // Split a string on quotes but do not do it when in quotes + function correctedQuoteSplit(str) { return str.split(',').reduce(function (a, c) { if (a.ic) { a.st[a.st.length - 1] += ',' + c } else { a.st.push(c) } if (c.split('"').length % 2 == 0) { a.ic = !a.ic } return a; }, { st: [], ic: false }).st } + + // NODE.js 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); + } + + // NODE.js specific private method + obj.xxConnectHttpSocket = function () { + //obj.Debug("xxConnectHttpSocket"); + obj.socketParseState = 0; + obj.socketAccumulator = ''; + obj.socketHeader = null; + obj.socketData = ''; + obj.socketState = 1; + obj.kerberosDone = 0; + + if (obj.xtlsoptions && obj.xtlsoptions.meshServerConnect) { + // Use the websocket wrapper to connect to MeshServer server + obj.socket = CreateWebSocketWrapper(obj.xtlsoptions.host, obj.xtlsoptions.port, '/webrelay.ashx?user=' + encodeURIComponent(obj.xtlsoptions.username) + '&pass=' + encodeURIComponent(obj.xtlsoptions.password) + '&host=' + encodeURIComponent(obj.host) + '&p=1&tls1only=' + obj.xtlsMethod, obj.xtlsoptions.xtlsFingerprint); + obj.socket.setEncoding('binary'); + obj.socket.setTimeout(6000); // Set socket idle timeout + obj.socket.ondata = obj.xxOnSocketData; + obj.socket.onclose = function () { if (obj.xtlsDataReceived == false) { obj.xtlsMethod = 1 - obj.xtlsMethod; } obj.xxOnSocketClosed(); } + obj.socket.ontimeout = function () { if (obj.xtlsDataReceived == false) { obj.xtlsMethod = 1 - obj.xtlsMethod; } obj.xxOnSocketClosed(); } + obj.socket.connect(obj.xxOnSocketConnected); + } else if (obj.xtls != 1) { + // Connect without TLS + obj.socket = new obj.net.Socket(); + obj.socket.setEncoding('binary'); + obj.socket.setTimeout(6000); // Set socket idle timeout + obj.socket.on('data', obj.xxOnSocketData); + obj.socket.on('close', obj.xxOnSocketClosed); + obj.socket.on('timeout', obj.xxOnSocketClosed); + obj.socket.connect(obj.port, obj.host, obj.xxOnSocketConnected); + } else { + // Connect with TLS + var options = { secureProtocol: ((obj.xtlsMethod == 0) ? 'SSLv23_method' : 'TLSv1_method'), ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false }; + if (obj.xtlsoptions) { + if (obj.xtlsoptions.ca) options.ca = obj.xtlsoptions.ca; + if (obj.xtlsoptions.cert) options.cert = obj.xtlsoptions.cert; + if (obj.xtlsoptions.key) options.key = obj.xtlsoptions.key; + obj.xtlsoptions = options; + } + obj.socket = obj.tls.connect(obj.port, obj.host, obj.xtlsoptions, obj.xxOnSocketConnected); + obj.socket.setEncoding('binary'); + obj.socket.setTimeout(6000); // Set socket idle timeout + obj.socket.on('data', obj.xxOnSocketData); + obj.socket.on('close', obj.xxOnSocketClosed); + obj.socket.on('timeout', obj.xxOnSocketClosed); + obj.socket.on('error', function (e) { if (e.message && e.message.indexOf('sslv3 alert bad record mac') >= 0) { obj.xtlsMethod = 1 - obj.xtlsMethod; } }); + } + obj.socket.setNoDelay(true); // Disable nagle. We will encode each WSMAN request as a single send block and want to send it at once. This may help Intel AMT handle pipelining? + } + + // Get the certificate of Intel AMT + obj.getPeerCertificate = function () { if (obj.xtls == 1) { return obj.socket.getPeerCertificate(); } return null; } + obj.getPeerCertificateFingerprint = function () { if (obj.xtls == 1) { return obj.socket.getPeerCertificate().fingerprint.split(':').join('').toLowerCase(); } return null; } + + // NODE.js specific private method + obj.xxOnSocketConnected = function () { + if (obj.socket == null) return; + if (obj.xtls == 1) { + obj.xtlsCertificate = obj.socket.getPeerCertificate(); + + // ###BEGIN###{Certificates} + // Setup the forge certificate check + var camatch = 0; + if (obj.xtlsoptions.ca) { + var forgeCert = forge.pki.certificateFromAsn1(forge.asn1.fromDer(atob(obj.xtlsCertificate.raw.toString('base64')))); + var caStore = forge.pki.createCaStore(obj.xtlsoptions.ca); + // Got thru all certificates in the store and look for a match. + for (var i in caStore.certs) { + if (camatch == 0) { + var c = caStore.certs[i], verified = false; + try { verified = c.verify(forgeCert); } catch (e) { } + if (verified == true) { camatch = c; } + } + } + // We found a match, check that the CommonName matches the hostname + if ((obj.xtlsSkipHostCheck == 0) && (camatch != 0)) { + amtcertname = forgeCert.subject.getField('CN').value; + if (amtcertname.toLowerCase() != obj.host.toLowerCase()) { camatch = 0; } + } + } + if ((camatch == 0) && (obj.xtlsFingerprint != 0) && (obj.xtlsCertificate.fingerprint.split(':').join('').toLowerCase() != obj.xtlsFingerprint)) { + obj.FailAllError = 998; // Cause all new responses to be silent. 998 = TLS Certificate check error + obj.CancelAllQueries(998); + return; + } + if ((obj.xtlsFingerprint == 0) && (camatch == 0)) { obj.xtlsCheck = 3; } else { obj.xtlsCheck = (camatch == 0) ? 2 : 1; } + // ###END###{Certificates} + // ###BEGIN###{!Certificates} + if ((obj.xtlsFingerprint != 0) && (obj.xtlsCertificate.fingerprint.split(':').join('').toLowerCase() != obj.xtlsFingerprint)) { + obj.FailAllError = 998; // Cause all new responses to be silent. 998 = TLS Certificate check error + obj.CancelAllQueries(998); + return; + } + obj.xtlsCheck = 2; + // ###END###{!Certificates} + } else { obj.xtlsCheck = 0; } + obj.socketState = 2; + obj.socketParseState = 0; + for (i in obj.pendingAjaxCall) { obj.sendRequest(obj.pendingAjaxCall[i][0], obj.pendingAjaxCall[i][3], obj.pendingAjaxCall[i][4]); } + } + + // NODE.js specific private method + obj.xxOnSocketData = function (data) { + obj.xtlsDataReceived = true; + if (urlvars && urlvars['wsmantrace']) { console.log("WSMAN-RECV(" + 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; + + obj.socketAccumulator += data; + while (true) { + //console.log('ACC(' + obj.socketAccumulator + '): ' + obj.socketAccumulator); + 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'); + if (obj.amtVersion == null) { for (var i in obj.socketHeader) { if (obj.socketHeader[i].indexOf('Server: Intel(R) Active Management Technology ') == 0) { obj.amtVersion = obj.socketHeader[i].substring(46); } } } + 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 (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("xxOnSocketData DONE: (" + obj.socketData.length + "): " + obj.socketData); + obj.xxProcessHttpResponse(obj.socketXHeader, obj.socketData); + obj.socketParseState = 0; + obj.socketHeader = null; + } + } + } + } + + // NODE.js specific private method + obj.xxProcessHttpResponse = function (header, data) { + //obj.Debug("xxProcessHttpResponse: " + header.Directive[1]); + + var s = parseInt(header.Directive[1]); + if (isNaN(s)) s = 500; + 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 + if (obj.challengeParams['qop'] != null) { + var qopList = obj.challengeParams['qop'].split(','); + for (var i in qopList) { qopList[i] = qopList[i].trim(); } + if (qopList.indexOf('auth-int') >= 0) { obj.challengeParams['qop'] = 'auth-int'; } else { obj.challengeParams['qop'] = 'auth'; } + } + obj.socket.end(); + } else { + var r = obj.pendingAjaxCall.shift(); + if (r == null || r.length < 1) { console.log("pendingAjaxCall error, " + r); return; } + //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(); + } + } + + // NODE.js specific private method + obj.xxOnSocketClosed = function (data) { + //obj.Debug("xxOnSocketClosed"); + obj.socketState = 0; + if (obj.socket != null) { obj.socket.destroy(); obj.socket = null; } + if (obj.pendingAjaxCall.length > 0) { + var r = obj.pendingAjaxCall.shift(); + var retry = r[5]; + setTimeout(function () { obj.PerformAjaxExNodeJS2(r[0], r[1], r[2], r[3], r[4], --retry) }, 500); // Wait half a second and try again + } + } + + // NODE.js specific private method + obj.xxSend = function (x) { + if (obj.socketState == 2) { + if (urlvars && urlvars['wsmantrace']) { console.log("WSMAN-SEND(" + x.length + "): " + x); } + obj.socket.write(new Buffer(x, 'binary')); + } + } + + // Cancel all pending queries with given status + obj.CancelAllQueries = function (s) { + obj.FailAllError = s; + while (obj.PendingAjax.length > 0) { var x = obj.PendingAjax.shift(); x[1](null, s, x[2]); } + if (obj.socket != null) { obj.socket.end(); obj.socket = null; obj.socketState = 0; } + } + + // Private method + obj.gotNextMessages = function (data, status, request, callArgs) { + if (obj.FailAllError == 999) return; + if (obj.FailAllError != 0) { try { callArgs[1](null, obj.FailAllError, callArgs[2]); } catch (ex) { console.error(ex); } return; } + if (request.status != 200) { try { callArgs[1](null, request.status, callArgs[2]); } catch (ex) { console.error(ex); } return; } + try { callArgs[1](data, 200, callArgs[2]); } catch (ex) { console.error(ex); } + } + + // Private method + obj.gotNextMessagesError = function (request, status, errorThrown, callArgs) { + if (obj.FailAllError == 999) return; + if (obj.FailAllError != 0) { try { callArgs[1](null, obj.FailAllError, callArgs[2]); } catch (ex) { console.error(ex); } return; } + try { callArgs[1](obj, null, { Header: { HttpError: request.status } }, request.status, callArgs[2]); } catch (ex) { console.error(ex); } + } + + return obj; +} + diff --git a/amt-wsman-ws-0.2.0.js b/amt-wsman-ws-0.2.0.js new file mode 100644 index 0000000..da69d05 --- /dev/null +++ b/amt-wsman-ws-0.2.0.js @@ -0,0 +1,315 @@ +/** +* @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.tlsv1only = 0; + obj.cnonce = Math.random().toString(36).substring(7); // Generate a random client nonce + obj.inDataCount = 0; + obj.amtVersion = null; + obj.digestRealmMatch = null; + obj.digestRealm = null; + + // 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, 5); } + + // Websocket relay specific private method + obj.PerformAjaxExNodeJS2 = function (postdata, callback, tag, url, action, retry) { + //console.log('PerformAjaxExNodeJS2', postdata, 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) { + obj.digestRealm = obj.challengeParams["realm"]; + if (obj.digestRealmMatch && (obj.digestRealm != obj.digestRealmMatch)) { + obj.FailAllError = 997; // Cause all new responses to be silent. 997 = Digest Realm check error + obj.CancelAllQueries(997); + return; + } + 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 + ((obj.challengeParams['qop'] == 'auth-int') ? (':' + hex_md5(postdata)) : ''))); + 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); + } + + // Parse the HTTP digest header and return a list of key & values. + obj.parseDigest = function (header) { return correctedQuoteSplit(header.substring(7)).reduce(function (obj, s) { var parts = s.trim().split('='); obj[parts[0]] = parts[1].replace(new RegExp('\"', 'g'), ''); return obj; }, {}) } + + // Split a string on quotes but do not do it when in quotes + function correctedQuoteSplit(str) { return str.split(',').reduce(function (a, c) { if (a.ic) { a.st[a.st.length - 1] += ',' + c } else { a.st.push(c) } if (c.split('"').length % 2 == 0) { a.ic = !a.ic } return a; }, { st: [], ic: false }).st } + + // 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.inDataCount = 0; + 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 + "&tls1only=" + obj.tlsv1only + ((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.socketState = 2; + obj.socketParseState = 0; + obj.socketAccumulator = ''; + obj.socketHeader = null; + obj.socketData = ''; + //console.log("xxOnSocketConnected"); + for (i in obj.pendingAjaxCall) { obj.sendRequest(obj.pendingAjaxCall[i][0], obj.pendingAjaxCall[i][3], obj.pendingAjaxCall[i][4]); } + } + + // Setup the file reader + var fileReader = new FileReader(); + var fileReaderInuse = false, fileReaderAcc = []; + if (fileReader.readAsBinaryString) { + // Chrome & Firefox (Draft) + fileReader.onload = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } } + } else if (fileReader.readAsArrayBuffer) { + // Chrome & Firefox (Spec) + fileReader.onloadend = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } } + } + + function _OnMessage(e) { + if (typeof e.data == 'object') { + if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; } + if (fileReader.readAsBinaryString) { + // Chrome & Firefox (Draft) + fileReaderInuse = true; + fileReader.readAsBinaryString(new Blob([e.data])); + } else if (fileReader.readAsArrayBuffer) { + // Chrome & Firefox (Spec) + fileReaderInuse = true; + fileReader.readAsArrayBuffer(e.data); + } else { + // IE10, readAsBinaryString does not exist, use an alternative. + var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength; + for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); } + _OnSocketData(binary); + } + } else { + _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(" + obj.socketParseState + "): " + 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"); + if (obj.amtVersion == null) { for (var i in obj.socketHeader) { if (obj.socketHeader[i].indexOf('Server: Intel(R) Active Management Technology ') == 0) { obj.amtVersion = obj.socketHeader[i].substring(46); } } } + obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4); + obj.socketParseState = 1; + obj.socketData = ''; + obj.socketXHeader = { Directive: obj.socketHeader[0].split(' ') }; + //console.log("Header", obj.socketXHeader); + 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 + if (obj.challengeParams['qop'] != null) { + var qopList = obj.challengeParams['qop'].split(','); + for (var i in qopList) { qopList[i] = qopList[i].trim(); } + if (qopList.indexOf('auth-int') >= 0) { obj.challengeParams['qop'] = 'auth-int'; } else { obj.challengeParams['qop'] = 'auth'; } + } + } 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) { + //console.log("_OnSocketClosed"); + if (obj.inDataCount == 0) { obj.tlsv1only = (1 - obj.tlsv1only); } + 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; +} + diff --git a/certificate.png b/certificate.png new file mode 100644 index 0000000..3de9ee5 Binary files /dev/null and b/certificate.png differ diff --git a/closure-externs/crypto.js b/closure-externs/crypto.js new file mode 100644 index 0000000..64ff6b0 --- /dev/null +++ b/closure-externs/crypto.js @@ -0,0 +1,578 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * 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. + */ + +/** + * @fileoverview Definitions for node's crypto module. Depends on the buffer module. + * @see http://nodejs.org/api/crypto.html + * @see https://github.com/joyent/node/blob/master/lib/crypto.js + * @externs + * @author Daniel Wirtz + */ + +/** + BEGIN_NODE_INCLUDE + var crypto = require('crypto'); + END_NODE_INCLUDE + */ + +/** + * @type {Object.} + */ +var crypto = {}; + +/** + * @type {string} + */ +crypto.DEFAULT_ENCODING; + +/** + * @typedef {{pfx: (string|buffer.Buffer), key: (string|buffer.Buffer), passphrase: string, cert: (string|buffer.Buffer), ca: Array., crl: (string|Array.), ciphers: string}} + */ +crypto.Credentials; + +/** + * @param {Object.=} details + * @return {crypto.Credentials} + */ +crypto.createCredentials = +function(details) {}; + +/** + * @param {string} algorithm + * @return {crypto.Hash} + */ +crypto.createHash = +function(algorithm) {}; + +/** + * @param {string} algorithm + * @param {Object=} options + * @constructor + * @extends stream.Transform + */ +crypto.Hash = +function(algorithm, options) {}; + +/** + * @param {string|buffer.Buffer} data + * @param {string=} input_encoding + */ +crypto.Hash.prototype.update = +function(data, input_encoding) {}; + +/** + * @param {string=} encoding + * @return {string} + */ +crypto.Hash.prototype.digest = +function(encoding) {}; + +/** + * @param {string} algorithm + * @param {string|buffer.Buffer} key + * @return {crypto.Hmac} + */ +crypto.createHmac = +function(algorithm, key) {}; + +/** + * @param {string} hmac + * @param {string|buffer.Buffer} key + * @param {Object=} options + * @constructor + * @extends stream.Transform + */ +crypto.Hmac = +function(hmac, key, options) {}; + +/** + * @param {string|buffer.Buffer} data + */ +crypto.Hmac.prototype.update = +function(data) {}; + +/** + * @param {string} encoding + */ +crypto.Hmac.prototype.digest = +function(encoding) {}; + +/** + * @param {string} algorithm + * @param {string|buffer.Buffer} password + * @return {crypto.Cipher} + */ +crypto.createCipher = +function(algorithm, password) {}; + +/** + * @param {string} algorithm + * @param {string|buffer.Buffer} key + * @param {string|buffer.Buffer} iv + * @return {crypto.Cipheriv} + */ +crypto.createCipheriv = +function(algorithm, key, iv) {}; + +/** + * @param {string|buffer.Buffer} cipher + * @param {string} password + * @param {Object=} options + * @constructor + * @extends stream.Transform + */ +crypto.Cipher = +function(cipher, password, options) {}; + +/** + * @param {string|buffer.Buffer} data + * @param {string=} input_encoding + * @param {string=} output_encoding + * @return {string|buffer.Buffer} + */ +crypto.Cipher.prototype.update = +function(data, input_encoding, output_encoding) {}; + +/** + * @name crypto.Cipher.prototype.final + * @param {string} output_encoding + * @return {string|buffer.Buffer} + */ +crypto.Cipher.prototype['final'] = +function(output_encoding) {}; + +/** + * @param {boolean=} auto_padding + */ +crypto.Cipher.prototype.setAutoPadding = +function(auto_padding) {}; + +/** + * Note: Cipheriv mixes update, final, and setAutoPadding from Cipher but + * doesn't inherit directly from Cipher. + * + * @param {string} cipher + * @param {string|buffer.Buffer} key + * @param {string|buffer.Buffer} iv + * @constructor + * @extends stream.Transform + */ +crypto.Cipheriv = +function(cipher, key, iv) {}; + +/** + * @param {string|buffer.Buffer} data + * @param {string=} input_encoding + * @param {string=} output_encoding + * @return {string|buffer.Buffer} + */ +crypto.Cipheriv.prototype.update = +function(data, input_encoding, output_encoding) {}; + +/** + * @name crypto.Cipheriv.prototype.final + * @param {string} output_encoding + * @return {string|buffer.Buffer} + */ +crypto.Cipheriv.prototype['final'] = +function(output_encoding) {}; + +/** + * @param {boolean=} auto_padding + */ +crypto.Cipheriv.prototype.setAutoPadding = +function(auto_padding) {}; + +/** + * @param {string} algorithm + * @param {string|buffer.Buffer} password + * @return {crypto.Decipher} + */ +crypto.createDecipher = +function(algorithm, password) {}; + +/** + * @param {string} algorithm + * @param {string|buffer.Buffer} key + * @param {string|buffer.Buffer} iv + * @return {crypto.Decipheriv} + */ +crypto.createDecipheriv = +function(algorithm, key, iv) {}; + +/** + * Note: Decipher mixes update, final, and setAutoPadding from Cipher but + * doesn't inherit directly from Cipher. + * + * @param {string|buffer.Buffer} cipher + * @param {string|buffer.Buffer} password + * @param {Object=} options + * @constructor + * @extends stream.Transform + */ +crypto.Decipher = +function(cipher, password, options) {} + +/** + * @param {string|buffer.Buffer} data + * @param {string=} input_encoding + * @param {string=} output_encoding + * @return {string|buffer.Buffer} + */ +crypto.Decipher.prototype.update = +function(data, input_encoding, output_encoding) {}; + +/** + * @name crypto.Decipher.prototype.final + * @param {string} output_encoding + * @return {string|buffer.Buffer} + */ +crypto.Decipher.prototype['final'] = +function(output_encoding) {}; + +/** + * @param {string} output_encoding + * @return {string|buffer.Buffer} + */ +crypto.Decipher.prototype.finaltol = +function(output_encoding) {}; + +/** + * @param {boolean=} auto_padding + */ +crypto.Decipher.prototype.setAutoPadding = +function(auto_padding) {}; + +/** + * Note: Decipheriv mixes update, final, and setAutoPadding from Cipher but + * doesn't inherit directly from Cipher. + * + * @param {string|buffer.Buffer|crypto.Decipheriv} cipher + * @param {string|buffer.Buffer} key + * @param {string|buffer.Buffer} iv + * @param {Object=} options + * @constructor + * @extends stream.Transform + */ +crypto.Decipheriv = +function(cipher, key, iv, options) {}; + +/** + * @param {string|buffer.Buffer} data + * @param {string=} input_encoding + * @param {string=} output_encoding + * @return {string|buffer.Buffer} + */ +crypto.Decipheriv.prototype.update = +function(data, input_encoding, output_encoding) {}; + +/** + * @name crypto.Decipheriv.prototype.final + * @param {string} output_encoding + * @return {string|buffer.Buffer} + */ +crypto.Decipheriv.prototype['final'] = +function(output_encoding) {}; + +/** + * @param {string} output_encoding + * @return {string|buffer.Buffer} + */ +crypto.Decipheriv.prototype.finaltol = +function(output_encoding) {}; + +/** + * @param {boolean=} auto_padding + */ +crypto.Decipheriv.prototype.setAutoPadding = +function(auto_padding) {}; + +/** + * @param {string} algorithm + * @return {crypto.Sign} + */ +crypto.createSign = +function(algorithm) {}; + +/** + * @param {string} algorithm + * @param {Object=} options + * @constructor + * @extends stream.Writable + */ +crypto.Sign = +function(algorithm, options) {}; + +/** + * @param {string|buffer.Buffer} data + */ +crypto.Sign.prototype.update = +function(data) {}; + +/** + * @param {string} private_key + * @param {string=} output_format + * @return {string|buffer.Buffer} + */ +crypto.Sign.prototype.sign = +function(private_key, output_format) {}; + +/** + * @param {string} algorithm + * @return crypto.Verify + */ +crypto.createVerify = +function(algorithm) {}; + +/** + * @param {string} algorithm + * @param {Object=} options + * @constructor + * @extends stream.Writable + */ +crypto.Verify = +function(algorithm, options) {}; + +/** + * @param {string|buffer.Buffer} data + */ +crypto.Verify.prototype.update = +function(data) {}; + +/** + * @param {string} object + * @param {string|buffer.Buffer} signature + * @param {string=} signature_format + * @return {boolean} + */ +crypto.Verify.prototype.verify = +function(object, signature, signature_format) {}; + +/** + * @param {number} prime + * @param {string=} encoding + * @return {crypto.DiffieHellman} + */ +crypto.createDiffieHellman = +function(prime, encoding) {}; + +/** + * @param {number} sizeOrKey + * @param {string=} encoding + * @constructor + */ +crypto.DiffieHellman = +function(sizeOrKey, encoding) {}; + +/** + * @param {string=} encoding + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellman.prototype.generateKeys = +function(encoding) {}; + +/** + * @param {string|buffer.Buffer} key + * @param {string=} inEnc + * @param {string=} outEnc + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellman.prototype.computeSecret = +function(key, inEnc, outEnc) {}; + +/** + * @param {string=} encoding + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellman.prototype.getPrime = +function(encoding) {}; + +/** + * @param {string=} encoding + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellman.prototype.getGenerator = +function(encoding) {}; + +/** + * @param {string=} encoding + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellman.prototype.getPublicKey = +function(encoding) {}; + +/** + * @param {string} encoding + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellman.prototype.getPrivateKey = +function(encoding) {} + +/** + * @param {string|buffer.Buffer} key + * @param {string=} encoding + * @return {crypto.DiffieHellman} + */ +crypto.DiffieHellman.prototype.setPublicKey = +function(key, encoding) {}; + +/** + * @param {string|buffer.Buffer} key + * @param {string=} encoding + * @return {crypto.DiffieHellman} + */ +crypto.DiffieHellman.prototype.setPrivateKey = +function(key, encoding) {}; + +/** + * Note: DiffieHellmanGroup mixes DiffieHellman but doesn't inherit directly. + * + * @param {string} name + * @constructor + */ +crypto.DiffieHellmanGroup = +function(name) {}; + +/** + * @param {string=} encoding + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellmanGroup.prototype.generateKeys = +function(encoding) {}; + +/** + * @param {string|buffer.Buffer} key + * @param {string=} inEnc + * @param {string=} outEnc + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellmanGroup.prototype.computeSecret = +function(key, inEnc, outEnc) {}; + +/** + * @param {string=} encoding + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellmanGroup.prototype.getPrime = +function(encoding) {}; + +/** + * @param {string=} encoding + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellmanGroup.prototype.getGenerator = +function(encoding) {}; + +/** + * @param {string=} encoding + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellmanGroup.prototype.getPublicKey = +function(encoding) {}; + +/** + * @param {string} encoding + * @return {string|buffer.Buffer} + */ +crypto.DiffieHellmanGroup.prototype.getPrivateKey = +function(encoding) {} + +/** + * @param {string|buffer.Buffer} key + * @param {string=} encoding + * @return {crypto.DiffieHellmanGroup} + */ +crypto.DiffieHellmanGroup.prototype.setPublicKey = +function(key, encoding) {}; + +/** + * @param {string|buffer.Buffer} key + * @param {string=} encoding + * @return {crypto.DiffieHellmanGroup} + */ +crypto.DiffieHellmanGroup.prototype.setPrivateKey = +function(key, encoding) {}; + +/** + * @param {string} group_name + * @return {crypto.DiffieHellmanGroup} + */ +crypto.getDiffieHellman = +function(group_name) {}; + +/** + * @param {string|buffer.Buffer} password + * @param {string|buffer.Buffer} salt + * @param {number} iterations + * @param {number} keylen + * @param { +function(*, string)} callback + */ +crypto.pbkdf2 = +function(password, salt, iterations, keylen, callback) {}; + +/** + * @param {string|buffer.Buffer} password + * @param {string|buffer.Buffer} salt + * @param {number} iterations + * @param {number} keylen + */ +crypto.pbkdf2Sync = +function(password, salt, iterations, keylen) {}; + +/** + * @param {number} size + * @param { +function(Error, buffer.Buffer)=} callback + */ +crypto.randomBytes = +function(size, callback) {}; + +/** + * @param {number} size + * @param { +function(Error, buffer.Buffer)=} callback + */ +crypto.pseudoRandomBytes = +function(size, callback) {}; + +/** + * @param {number} size + * @param { +function(Error, buffer.Buffer)=} callback + */ +crypto.rng = +function(size, callback) {}; + +/** + * @param {number} size + * @param { +function(Error, buffer.Buffer)=} callback + */ +crypto.prng = +function(size, callback) {}; + +/** + * @return {Array.} + */ +crypto.getCiphers = +function() {}; + +/** + * @return {Array.} + */ +crypto.getHashes = +function() {}; diff --git a/closure-externs/net.js b/closure-externs/net.js new file mode 100644 index 0000000..3bfcce8 --- /dev/null +++ b/closure-externs/net.js @@ -0,0 +1,249 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * 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. + */ + +/** + * @fileoverview Definitions for node's net module. Depends on the events and buffer modules. + * @see http://nodejs.org/api/net.html + * @see https://github.com/joyent/node/blob/master/lib/net.js + * @externs + * @author Daniel Wirtz + */ + +/** + BEGIN_NODE_INCLUDE + var net = require('net'); + END_NODE_INCLUDE + */ + +/** + * @type {Object.} + */ +var net = {}; + +/** + * @typedef {{allowHalfOpen: ?boolean}} + */ +net.CreateOptions; + +/** + * @param {(net.CreateOptions| +function(...))=} options + * @param { +function(...)=} connectionListener + * @return {net.Server} + */ +net.createServer = +function(options, connectionListener) {}; + +/** + * @typedef {{port: ?number, host: ?string, localAddress: ?string, path: ?string, allowHalfOpen: ?boolean}} + */ +net.ConnectOptions; + +/** + * @param {net.ConnectOptions|number|string} arg1 + * @param {( +function(...)|string)=} arg2 + * @param { +function(...)=} arg3 + */ +net.connect = +function(arg1, arg2, arg3) {}; + +/** + * @param {net.ConnectOptions|number|string} arg1 + * @param {( +function(...)|string)=} arg2 + * @param { +function(...)=} arg3 + */ +net.createConnection = +function(arg1, arg2, arg3) {}; + +/** + * @constructor + * @extends events.EventEmitter + */ +net.Server = +function() {}; + +/** + * + * @param {number|*} port + * @param {(string|number| +function(...))=} host + * @param {(number| +function(...))=} backlog + * @param { +function(...)=} callback + */ +net.Server.prototype.listen = +function(port, host, backlog, callback) {}; + +/** + * @param { +function(...)=} callback + */ +net.Server.prototype.close = +function(callback) {}; + +/** + * @return {{port: number, family: string, address: string}} + */ +net.Server.prototype.address = +function() {}; + +/** + * @type {number} + */ +net.Server.prototype.maxConnectinos; + +/** + * @type {number} + */ +net.Server.prototype.connections; + +/** + * @constructor + * @param {{fd: ?*, type: ?string, allowHalfOpen: ?boolean}=} options + * @extends events.EventEmitter + */ +net.Socket = +function(options) {}; + +/** + * @param {number|string| +function(...)} port + * @param {(string| +function(...))=} host + * @param { +function(...)=} connectListener + */ +net.Socket.prototype.connect = +function(port, host, connectListener) {}; + +/** + * @type {number} + */ +net.Socket.prototype.bufferSize; + +/** + * @param {?string=} encoding + */ +net.Socket.prototype.setEncoding = +function (encoding) { }; +net.Socket.prototype.on = +function (str, func) { }; + +/** + * @param {string|buffer.Buffer} data + * @param {(string| +function(...))=}encoding + * @param { +function(...)=} callback + */ +net.Socket.prototype.write = +function(data, encoding, callback) {}; + +/** + * @param {(string|buffer.Buffer)=}data + * @param {string=} encoding + */ +net.Socket.prototype.end = +function(data, encoding) {}; + +/** + */ +net.Socket.prototype.destroy = +function() {}; + +/** + */ +net.Socket.prototype.pause = +function() {}; + +/** + */ +net.Socket.prototype.resume = +function() {}; + +/** + * @param {number} timeout + * @param { +function(...)=} callback + */ +net.Socket.prototype.setTimeout = +function(timeout, callback) {}; + +/** + * @param {boolean=} noDelay + */ +net.Socket.prototype.setNoDelay = +function(noDelay) {}; + +/** + * @param {(boolean|number)=} enable + * @param {number=} initialDelay + */ +net.Socket.prototype.setKeepAlive = +function(enable, initialDelay) {}; + +/** + * @return {string} + */ +net.Socket.prototype.address = +function() {}; + +/** + * @type {?string} + */ +net.Socket.prototype.remoteAddress; + +/** + * @type {?number} + */ +net.Socket.prototype.remotePort; + +/** + * @type {number} + */ +net.Socket.prototype.bytesRead; + +/** + * @type {number} + */ +net.Socket.prototype.bytesWritten; + +/** + * @param {*} input + * @return {number} + */ +net.isIP = +function(input) {}; + +/** + * @param {*} input + * @return {boolean} + */ +net.isIPv4 = +function(input) {}; + +/** + * @param {*} input + * @return {boolean} + */ +net.isIPv6 = +function(input) {}; diff --git a/closure-externs/tls.js b/closure-externs/tls.js new file mode 100644 index 0000000..9ff4345 --- /dev/null +++ b/closure-externs/tls.js @@ -0,0 +1,144 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * 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. + */ + +/** + * @fileoverview Definitions for node's tls module. Depends on the stream module. + * @see http://nodejs.org/api/tls.html + * @see https://github.com/joyent/node/blob/master/lib/tls.js + * @externs + * @author Daniel Wirtz + */ + +/** + BEGIN_NODE_INCLUDE + var tls = require('tls'); + END_NODE_INCLUDE + */ + +/** + * @type {Object.} + */ +var tls = {}; + +/** + * @typedef {{pfx: (string|buffer.Buffer), key: (string|buffer.Buffer), passphrase: string, cert: (string|buffer.Buffer), ca: Array., crl: (string|Array.), ciphers: string, honorCipherOrder: boolean, requestCert: boolean, rejectUnauthorized: boolean, NPNProtocols: (Array|buffer.Buffer), SNICallback: +function(string), sessionIdContext: string}} + */ +tls.CreateOptions; + +/** + * + * @param {tls.CreateOptions} options + * @param { +function(...)=} secureConnectionListener + * @return {tls.Server} + */ +tls.createServer = +function(options, secureConnectionListener) {}; + +/** + * @typedef {{host: string, port: number, socket: *, pfx: (string|buffer.Buffer), key: (string|buffer.Buffer), passphrase: string, cert: (string|buffer.Buffer), ca: Array., rejectUnauthorized: boolean, NPNProtocols: Array., servername: string}} + */ +tls.ConnectOptions; + +/** + * + * @param {number|tls.ConnectOptions} port + * @param {(string|tls.ConnectOptions| +function(...))=} host + * @param {(tls.ConnectOptions| +function(...))=} options + * @param { +function(...)=} callback + */ +tls.connect = +function(port, host, options, callback) {}; + +/** + * @param {crypto.Credentials=} credentials + * @param {boolean=} isServer + * @param {boolean=} requestCert + * @param {boolean=} rejectUnauthorized + * @return {tls.SecurePair} + */ +tls.createSecurePair = +function(credentials, isServer, requestCert, rejectUnauthorized) {}; + +/** + * @constructor + * @extends events.EventEmitter + */ +tls.SecurePair = +function() {}; + +/** + * @constructor + * @extends net.Server + */ +tls.Server = +function() {}; + +/** + * @param {string} hostname + * @param {string|buffer.Buffer} credentials + */ +tls.Server.prototype.addContext = +function(hostname, credentials) {}; + +/** + * @constructor + * @extends stream.Duplex + */ +tls.CleartextStream = +function() {}; + +/** + * @type {boolean} + */ +tls.CleartextStream.prototype.authorized; + +/** + * @type {?string} + */ +tls.CleartextStream.prototype.authorizationError; + +/** + * @return {Object.)>} + */ +tls.CleartextStream.prototype.getPeerCertificate = +function() {}; + +/** + * @return {{name: string, version: string}} + */ +tls.CleartextStream.prototype.getCipher = +function() {}; + +/** + * @return {{port: number, family: string, address: string}} + */ +tls.CleartextStream.prototype.address = +function() {}; + +/** + * @type {string} + */ +tls.CleartextStream.prototype.remoteAddress; + +/** + * @type {number} + */ +tls.CleartextStream.prototype.remotePort; diff --git a/common-0.0.1.js b/common-0.0.1.js new file mode 100644 index 0000000..b228e3b --- /dev/null +++ b/common-0.0.1.js @@ -0,0 +1,105 @@ +/** +* @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(/', key); + * cipher.start({iv: iv}); + * + * Creates an AES cipher object to encrypt data using the given symmetric key. + * The output will be stored in the 'output' member of the returned cipher. + * + * The key and iv may be given as a string of bytes, an array of bytes, + * a byte buffer, or an array of 32-bit words. + * + * @param key the symmetric key to use. + * @param iv the initialization vector to use. + * @param output the buffer to write to, null to create one. + * @param mode the cipher mode to use (default: 'CBC'). + * + * @return the cipher. + */ +forge.aes.startEncrypting = function(key, iv, output, mode) { + var cipher = _createCipher({ + key: key, + output: output, + decrypt: false, + mode: mode + }); + cipher.start(iv); + return cipher; +}; + +/** + * Deprecated. Instead, use: + * + * var cipher = forge.cipher.createCipher('AES-', key); + * + * Creates an AES cipher object to encrypt data using the given symmetric key. + * + * The key may be given as a string of bytes, an array of bytes, a + * byte buffer, or an array of 32-bit words. + * + * @param key the symmetric key to use. + * @param mode the cipher mode to use (default: 'CBC'). + * + * @return the cipher. + */ +forge.aes.createEncryptionCipher = function(key, mode) { + return _createCipher({ + key: key, + output: null, + decrypt: false, + mode: mode + }); +}; + +/** + * Deprecated. Instead, use: + * + * var decipher = forge.cipher.createDecipher('AES-', key); + * decipher.start({iv: iv}); + * + * Creates an AES cipher object to decrypt data using the given symmetric key. + * The output will be stored in the 'output' member of the returned cipher. + * + * The key and iv may be given as a string of bytes, an array of bytes, + * a byte buffer, or an array of 32-bit words. + * + * @param key the symmetric key to use. + * @param iv the initialization vector to use. + * @param output the buffer to write to, null to create one. + * @param mode the cipher mode to use (default: 'CBC'). + * + * @return the cipher. + */ +forge.aes.startDecrypting = function(key, iv, output, mode) { + var cipher = _createCipher({ + key: key, + output: output, + decrypt: true, + mode: mode + }); + cipher.start(iv); + return cipher; +}; + +/** + * Deprecated. Instead, use: + * + * var decipher = forge.cipher.createDecipher('AES-', key); + * + * Creates an AES cipher object to decrypt data using the given symmetric key. + * + * The key may be given as a string of bytes, an array of bytes, a + * byte buffer, or an array of 32-bit words. + * + * @param key the symmetric key to use. + * @param mode the cipher mode to use (default: 'CBC'). + * + * @return the cipher. + */ +forge.aes.createDecryptionCipher = function(key, mode) { + return _createCipher({ + key: key, + output: null, + decrypt: true, + mode: mode + }); +}; + +/** + * Creates a new AES cipher algorithm object. + * + * @param name the name of the algorithm. + * @param mode the mode factory function. + * + * @return the AES algorithm object. + */ +forge.aes.Algorithm = function(name, mode) { + if(!init) { + initialize(); + } + var self = this; + self.name = name; + self.mode = new mode({ + blockSize: 16, + cipher: { + encrypt: function(inBlock, outBlock) { + return _updateBlock(self._w, inBlock, outBlock, false); + }, + decrypt: function(inBlock, outBlock) { + return _updateBlock(self._w, inBlock, outBlock, true); + } + } + }); + self._init = false; +}; + +/** + * Initializes this AES algorithm by expanding its key. + * + * @param options the options to use. + * key the key to use with this algorithm. + * decrypt true if the algorithm should be initialized for decryption, + * false for encryption. + */ +forge.aes.Algorithm.prototype.initialize = function(options) { + if(this._init) { + return; + } + + var key = options.key; + var tmp; + + /* Note: The key may be a string of bytes, an array of bytes, a byte + buffer, or an array of 32-bit integers. If the key is in bytes, then + it must be 16, 24, or 32 bytes in length. If it is in 32-bit + integers, it must be 4, 6, or 8 integers long. */ + + if(typeof key === 'string' && + (key.length === 16 || key.length === 24 || key.length === 32)) { + // convert key string into byte buffer + key = forge.util.createBuffer(key); + } else if(forge.util.isArray(key) && + (key.length === 16 || key.length === 24 || key.length === 32)) { + // convert key integer array into byte buffer + tmp = key; + key = forge.util.createBuffer(); + for(var i = 0; i < tmp.length; ++i) { + key.putByte(tmp[i]); + } + } + + // convert key byte buffer into 32-bit integer array + if(!forge.util.isArray(key)) { + tmp = key; + key = []; + + // key lengths of 16, 24, 32 bytes allowed + var len = tmp.length(); + if(len === 16 || len === 24 || len === 32) { + len = len >>> 2; + for(var i = 0; i < len; ++i) { + key.push(tmp.getInt32()); + } + } + } + + // key must be an array of 32-bit integers by now + if(!forge.util.isArray(key) || + !(key.length === 4 || key.length === 6 || key.length === 8)) { + throw new Error('Invalid key parameter.'); + } + + // encryption operation is always used for these modes + var mode = this.mode.name; + var encryptOp = (['CFB', 'OFB', 'CTR', 'GCM'].indexOf(mode) !== -1); + + // do key expansion + this._w = _expandKey(key, options.decrypt && !encryptOp); + this._init = true; +}; + +/** + * Expands a key. Typically only used for testing. + * + * @param key the symmetric key to expand, as an array of 32-bit words. + * @param decrypt true to expand for decryption, false for encryption. + * + * @return the expanded key. + */ +forge.aes._expandKey = function(key, decrypt) { + if(!init) { + initialize(); + } + return _expandKey(key, decrypt); +}; + +/** + * Updates a single block. Typically only used for testing. + * + * @param w the expanded key to use. + * @param input an array of block-size 32-bit words. + * @param output an array of block-size 32-bit words. + * @param decrypt true to decrypt, false to encrypt. + */ +forge.aes._updateBlock = _updateBlock; + + +/** Register AES algorithms **/ + +registerAlgorithm('AES-ECB', forge.cipher.modes.ecb); +registerAlgorithm('AES-CBC', forge.cipher.modes.cbc); +registerAlgorithm('AES-CFB', forge.cipher.modes.cfb); +registerAlgorithm('AES-OFB', forge.cipher.modes.ofb); +registerAlgorithm('AES-CTR', forge.cipher.modes.ctr); +registerAlgorithm('AES-GCM', forge.cipher.modes.gcm); + +function registerAlgorithm(name, mode) { + var factory = function() { + return new forge.aes.Algorithm(name, mode); + }; + forge.cipher.registerAlgorithm(name, factory); +} + + +/** AES implementation **/ + +var init = false; // not yet initialized +var Nb = 4; // number of words comprising the state (AES = 4) +var sbox; // non-linear substitution table used in key expansion +var isbox; // inversion of sbox +var rcon; // round constant word array +var mix; // mix-columns table +var imix; // inverse mix-columns table + +/** + * Performs initialization, ie: precomputes tables to optimize for speed. + * + * One way to understand how AES works is to imagine that 'addition' and + * 'multiplication' are interfaces that require certain mathematical + * properties to hold true (ie: they are associative) but they might have + * different implementations and produce different kinds of results ... + * provided that their mathematical properties remain true. AES defines + * its own methods of addition and multiplication but keeps some important + * properties the same, ie: associativity and distributivity. The + * explanation below tries to shed some light on how AES defines addition + * and multiplication of bytes and 32-bit words in order to perform its + * encryption and decryption algorithms. + * + * The basics: + * + * The AES algorithm views bytes as binary representations of polynomials + * that have either 1 or 0 as the coefficients. It defines the addition + * or subtraction of two bytes as the XOR operation. It also defines the + * multiplication of two bytes as a finite field referred to as GF(2^8) + * (Note: 'GF' means "Galois Field" which is a field that contains a finite + * number of elements so GF(2^8) has 256 elements). + * + * This means that any two bytes can be represented as binary polynomials; + * when they multiplied together and modularly reduced by an irreducible + * polynomial of the 8th degree, the results are the field GF(2^8). The + * specific irreducible polynomial that AES uses in hexadecimal is 0x11b. + * This multiplication is associative with 0x01 as the identity: + * + * (b * 0x01 = GF(b, 0x01) = b). + * + * The operation GF(b, 0x02) can be performed at the byte level by left + * shifting b once and then XOR'ing it (to perform the modular reduction) + * with 0x11b if b is >= 128. Repeated application of the multiplication + * of 0x02 can be used to implement the multiplication of any two bytes. + * + * For instance, multiplying 0x57 and 0x13, denoted as GF(0x57, 0x13), can + * be performed by factoring 0x13 into 0x01, 0x02, and 0x10. Then these + * factors can each be multiplied by 0x57 and then added together. To do + * the multiplication, values for 0x57 multiplied by each of these 3 factors + * can be precomputed and stored in a table. To add them, the values from + * the table are XOR'd together. + * + * AES also defines addition and multiplication of words, that is 4-byte + * numbers represented as polynomials of 3 degrees where the coefficients + * are the values of the bytes. + * + * The word [a0, a1, a2, a3] is a polynomial a3x^3 + a2x^2 + a1x + a0. + * + * Addition is performed by XOR'ing like powers of x. Multiplication + * is performed in two steps, the first is an algebriac expansion as + * you would do normally (where addition is XOR). But the result is + * a polynomial larger than 3 degrees and thus it cannot fit in a word. So + * next the result is modularly reduced by an AES-specific polynomial of + * degree 4 which will always produce a polynomial of less than 4 degrees + * such that it will fit in a word. In AES, this polynomial is x^4 + 1. + * + * The modular product of two polynomials 'a' and 'b' is thus: + * + * d(x) = d3x^3 + d2x^2 + d1x + d0 + * with + * d0 = GF(a0, b0) ^ GF(a3, b1) ^ GF(a2, b2) ^ GF(a1, b3) + * d1 = GF(a1, b0) ^ GF(a0, b1) ^ GF(a3, b2) ^ GF(a2, b3) + * d2 = GF(a2, b0) ^ GF(a1, b1) ^ GF(a0, b2) ^ GF(a3, b3) + * d3 = GF(a3, b0) ^ GF(a2, b1) ^ GF(a1, b2) ^ GF(a0, b3) + * + * As a matrix: + * + * [d0] = [a0 a3 a2 a1][b0] + * [d1] [a1 a0 a3 a2][b1] + * [d2] [a2 a1 a0 a3][b2] + * [d3] [a3 a2 a1 a0][b3] + * + * Special polynomials defined by AES (0x02 == {02}): + * a(x) = {03}x^3 + {01}x^2 + {01}x + {02} + * a^-1(x) = {0b}x^3 + {0d}x^2 + {09}x + {0e}. + * + * These polynomials are used in the MixColumns() and InverseMixColumns() + * operations, respectively, to cause each element in the state to affect + * the output (referred to as diffusing). + * + * RotWord() uses: a0 = a1 = a2 = {00} and a3 = {01}, which is the + * polynomial x3. + * + * The ShiftRows() method modifies the last 3 rows in the state (where + * the state is 4 words with 4 bytes per word) by shifting bytes cyclically. + * The 1st byte in the second row is moved to the end of the row. The 1st + * and 2nd bytes in the third row are moved to the end of the row. The 1st, + * 2nd, and 3rd bytes are moved in the fourth row. + * + * More details on how AES arithmetic works: + * + * In the polynomial representation of binary numbers, XOR performs addition + * and subtraction and multiplication in GF(2^8) denoted as GF(a, b) + * corresponds with the multiplication of polynomials modulo an irreducible + * polynomial of degree 8. In other words, for AES, GF(a, b) will multiply + * polynomial 'a' with polynomial 'b' and then do a modular reduction by + * an AES-specific irreducible polynomial of degree 8. + * + * A polynomial is irreducible if its only divisors are one and itself. For + * the AES algorithm, this irreducible polynomial is: + * + * m(x) = x^8 + x^4 + x^3 + x + 1, + * + * or {01}{1b} in hexadecimal notation, where each coefficient is a bit: + * 100011011 = 283 = 0x11b. + * + * For example, GF(0x57, 0x83) = 0xc1 because + * + * 0x57 = 87 = 01010111 = x^6 + x^4 + x^2 + x + 1 + * 0x85 = 131 = 10000101 = x^7 + x + 1 + * + * (x^6 + x^4 + x^2 + x + 1) * (x^7 + x + 1) + * = x^13 + x^11 + x^9 + x^8 + x^7 + + * x^7 + x^5 + x^3 + x^2 + x + + * x^6 + x^4 + x^2 + x + 1 + * = x^13 + x^11 + x^9 + x^8 + x^6 + x^5 + x^4 + x^3 + 1 = y + * y modulo (x^8 + x^4 + x^3 + x + 1) + * = x^7 + x^6 + 1. + * + * The modular reduction by m(x) guarantees the result will be a binary + * polynomial of less than degree 8, so that it can fit in a byte. + * + * The operation to multiply a binary polynomial b with x (the polynomial + * x in binary representation is 00000010) is: + * + * b_7x^8 + b_6x^7 + b_5x^6 + b_4x^5 + b_3x^4 + b_2x^3 + b_1x^2 + b_0x^1 + * + * To get GF(b, x) we must reduce that by m(x). If b_7 is 0 (that is the + * most significant bit is 0 in b) then the result is already reduced. If + * it is 1, then we can reduce it by subtracting m(x) via an XOR. + * + * It follows that multiplication by x (00000010 or 0x02) can be implemented + * by performing a left shift followed by a conditional bitwise XOR with + * 0x1b. This operation on bytes is denoted by xtime(). Multiplication by + * higher powers of x can be implemented by repeated application of xtime(). + * + * By adding intermediate results, multiplication by any constant can be + * implemented. For instance: + * + * GF(0x57, 0x13) = 0xfe because: + * + * xtime(b) = (b & 128) ? (b << 1 ^ 0x11b) : (b << 1) + * + * Note: We XOR with 0x11b instead of 0x1b because in javascript our + * datatype for b can be larger than 1 byte, so a left shift will not + * automatically eliminate bits that overflow a byte ... by XOR'ing the + * overflow bit with 1 (the extra one from 0x11b) we zero it out. + * + * GF(0x57, 0x02) = xtime(0x57) = 0xae + * GF(0x57, 0x04) = xtime(0xae) = 0x47 + * GF(0x57, 0x08) = xtime(0x47) = 0x8e + * GF(0x57, 0x10) = xtime(0x8e) = 0x07 + * + * GF(0x57, 0x13) = GF(0x57, (0x01 ^ 0x02 ^ 0x10)) + * + * And by the distributive property (since XOR is addition and GF() is + * multiplication): + * + * = GF(0x57, 0x01) ^ GF(0x57, 0x02) ^ GF(0x57, 0x10) + * = 0x57 ^ 0xae ^ 0x07 + * = 0xfe. + */ +function initialize() { + init = true; + + /* Populate the Rcon table. These are the values given by + [x^(i-1),{00},{00},{00}] where x^(i-1) are powers of x (and x = 0x02) + in the field of GF(2^8), where i starts at 1. + + rcon[0] = [0x00, 0x00, 0x00, 0x00] + rcon[1] = [0x01, 0x00, 0x00, 0x00] 2^(1-1) = 2^0 = 1 + rcon[2] = [0x02, 0x00, 0x00, 0x00] 2^(2-1) = 2^1 = 2 + ... + rcon[9] = [0x1B, 0x00, 0x00, 0x00] 2^(9-1) = 2^8 = 0x1B + rcon[10] = [0x36, 0x00, 0x00, 0x00] 2^(10-1) = 2^9 = 0x36 + + We only store the first byte because it is the only one used. + */ + rcon = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36]; + + // compute xtime table which maps i onto GF(i, 0x02) + var xtime = new Array(256); + for(var i = 0; i < 128; ++i) { + xtime[i] = i << 1; + xtime[i + 128] = (i + 128) << 1 ^ 0x11B; + } + + // compute all other tables + sbox = new Array(256); + isbox = new Array(256); + mix = new Array(4); + imix = new Array(4); + for(var i = 0; i < 4; ++i) { + mix[i] = new Array(256); + imix[i] = new Array(256); + } + var e = 0, ei = 0, e2, e4, e8, sx, sx2, me, ime; + for(var i = 0; i < 256; ++i) { + /* We need to generate the SubBytes() sbox and isbox tables so that + we can perform byte substitutions. This requires us to traverse + all of the elements in GF, find their multiplicative inverses, + and apply to each the following affine transformation: + + bi' = bi ^ b(i + 4) mod 8 ^ b(i + 5) mod 8 ^ b(i + 6) mod 8 ^ + b(i + 7) mod 8 ^ ci + for 0 <= i < 8, where bi is the ith bit of the byte, and ci is the + ith bit of a byte c with the value {63} or {01100011}. + + It is possible to traverse every possible value in a Galois field + using what is referred to as a 'generator'. There are many + generators (128 out of 256): 3,5,6,9,11,82 to name a few. To fully + traverse GF we iterate 255 times, multiplying by our generator + each time. + + On each iteration we can determine the multiplicative inverse for + the current element. + + Suppose there is an element in GF 'e'. For a given generator 'g', + e = g^x. The multiplicative inverse of e is g^(255 - x). It turns + out that if use the inverse of a generator as another generator + it will produce all of the corresponding multiplicative inverses + at the same time. For this reason, we choose 5 as our inverse + generator because it only requires 2 multiplies and 1 add and its + inverse, 82, requires relatively few operations as well. + + In order to apply the affine transformation, the multiplicative + inverse 'ei' of 'e' can be repeatedly XOR'd (4 times) with a + bit-cycling of 'ei'. To do this 'ei' is first stored in 's' and + 'x'. Then 's' is left shifted and the high bit of 's' is made the + low bit. The resulting value is stored in 's'. Then 'x' is XOR'd + with 's' and stored in 'x'. On each subsequent iteration the same + operation is performed. When 4 iterations are complete, 'x' is + XOR'd with 'c' (0x63) and the transformed value is stored in 'x'. + For example: + + s = 01000001 + x = 01000001 + + iteration 1: s = 10000010, x ^= s + iteration 2: s = 00000101, x ^= s + iteration 3: s = 00001010, x ^= s + iteration 4: s = 00010100, x ^= s + x ^= 0x63 + + This can be done with a loop where s = (s << 1) | (s >> 7). However, + it can also be done by using a single 16-bit (in this case 32-bit) + number 'sx'. Since XOR is an associative operation, we can set 'sx' + to 'ei' and then XOR it with 'sx' left-shifted 1,2,3, and 4 times. + The most significant bits will flow into the high 8 bit positions + and be correctly XOR'd with one another. All that remains will be + to cycle the high 8 bits by XOR'ing them all with the lower 8 bits + afterwards. + + At the same time we're populating sbox and isbox we can precompute + the multiplication we'll need to do to do MixColumns() later. + */ + + // apply affine transformation + sx = ei ^ (ei << 1) ^ (ei << 2) ^ (ei << 3) ^ (ei << 4); + sx = (sx >> 8) ^ (sx & 255) ^ 0x63; + + // update tables + sbox[e] = sx; + isbox[sx] = e; + + /* Mixing columns is done using matrix multiplication. The columns + that are to be mixed are each a single word in the current state. + The state has Nb columns (4 columns). Therefore each column is a + 4 byte word. So to mix the columns in a single column 'c' where + its rows are r0, r1, r2, and r3, we use the following matrix + multiplication: + + [2 3 1 1]*[r0,c]=[r'0,c] + [1 2 3 1] [r1,c] [r'1,c] + [1 1 2 3] [r2,c] [r'2,c] + [3 1 1 2] [r3,c] [r'3,c] + + r0, r1, r2, and r3 are each 1 byte of one of the words in the + state (a column). To do matrix multiplication for each mixed + column c' we multiply the corresponding row from the left matrix + with the corresponding column from the right matrix. In total, we + get 4 equations: + + r0,c' = 2*r0,c + 3*r1,c + 1*r2,c + 1*r3,c + r1,c' = 1*r0,c + 2*r1,c + 3*r2,c + 1*r3,c + r2,c' = 1*r0,c + 1*r1,c + 2*r2,c + 3*r3,c + r3,c' = 3*r0,c + 1*r1,c + 1*r2,c + 2*r3,c + + As usual, the multiplication is as previously defined and the + addition is XOR. In order to optimize mixing columns we can store + the multiplication results in tables. If you think of the whole + column as a word (it might help to visualize by mentally rotating + the equations above by counterclockwise 90 degrees) then you can + see that it would be useful to map the multiplications performed on + each byte (r0, r1, r2, r3) onto a word as well. For instance, we + could map 2*r0,1*r0,1*r0,3*r0 onto a word by storing 2*r0 in the + highest 8 bits and 3*r0 in the lowest 8 bits (with the other two + respectively in the middle). This means that a table can be + constructed that uses r0 as an index to the word. We can do the + same with r1, r2, and r3, creating a total of 4 tables. + + To construct a full c', we can just look up each byte of c in + their respective tables and XOR the results together. + + Also, to build each table we only have to calculate the word + for 2,1,1,3 for every byte ... which we can do on each iteration + of this loop since we will iterate over every byte. After we have + calculated 2,1,1,3 we can get the results for the other tables + by cycling the byte at the end to the beginning. For instance + we can take the result of table 2,1,1,3 and produce table 3,2,1,1 + by moving the right most byte to the left most position just like + how you can imagine the 3 moved out of 2,1,1,3 and to the front + to produce 3,2,1,1. + + There is another optimization in that the same multiples of + the current element we need in order to advance our generator + to the next iteration can be reused in performing the 2,1,1,3 + calculation. We also calculate the inverse mix column tables, + with e,9,d,b being the inverse of 2,1,1,3. + + When we're done, and we need to actually mix columns, the first + byte of each state word should be put through mix[0] (2,1,1,3), + the second through mix[1] (3,2,1,1) and so forth. Then they should + be XOR'd together to produce the fully mixed column. + */ + + // calculate mix and imix table values + sx2 = xtime[sx]; + e2 = xtime[e]; + e4 = xtime[e2]; + e8 = xtime[e4]; + me = + (sx2 << 24) ^ // 2 + (sx << 16) ^ // 1 + (sx << 8) ^ // 1 + (sx ^ sx2); // 3 + ime = + (e2 ^ e4 ^ e8) << 24 ^ // E (14) + (e ^ e8) << 16 ^ // 9 + (e ^ e4 ^ e8) << 8 ^ // D (13) + (e ^ e2 ^ e8); // B (11) + // produce each of the mix tables by rotating the 2,1,1,3 value + for(var n = 0; n < 4; ++n) { + mix[n][e] = me; + imix[n][sx] = ime; + // cycle the right most byte to the left most position + // ie: 2,1,1,3 becomes 3,2,1,1 + me = me << 24 | me >>> 8; + ime = ime << 24 | ime >>> 8; + } + + // get next element and inverse + if(e === 0) { + // 1 is the inverse of 1 + e = ei = 1; + } else { + // e = 2e + 2*2*2*(10e)) = multiply e by 82 (chosen generator) + // ei = ei + 2*2*ei = multiply ei by 5 (inverse generator) + e = e2 ^ xtime[xtime[xtime[e2 ^ e8]]]; + ei ^= xtime[xtime[ei]]; + } + } +} + +/** + * Generates a key schedule using the AES key expansion algorithm. + * + * The AES algorithm takes the Cipher Key, K, and performs a Key Expansion + * routine to generate a key schedule. The Key Expansion generates a total + * of Nb*(Nr + 1) words: the algorithm requires an initial set of Nb words, + * and each of the Nr rounds requires Nb words of key data. The resulting + * key schedule consists of a linear array of 4-byte words, denoted [wi ], + * with i in the range 0 ≤ i < Nb(Nr + 1). + * + * KeyExpansion(byte key[4*Nk], word w[Nb*(Nr+1)], Nk) + * AES-128 (Nb=4, Nk=4, Nr=10) + * AES-192 (Nb=4, Nk=6, Nr=12) + * AES-256 (Nb=4, Nk=8, Nr=14) + * Note: Nr=Nk+6. + * + * Nb is the number of columns (32-bit words) comprising the State (or + * number of bytes in a block). For AES, Nb=4. + * + * @param key the key to schedule (as an array of 32-bit words). + * @param decrypt true to modify the key schedule to decrypt, false not to. + * + * @return the generated key schedule. + */ +function _expandKey(key, decrypt) { + // copy the key's words to initialize the key schedule + var w = key.slice(0); + + /* RotWord() will rotate a word, moving the first byte to the last + byte's position (shifting the other bytes left). + + We will be getting the value of Rcon at i / Nk. 'i' will iterate + from Nk to (Nb * Nr+1). Nk = 4 (4 byte key), Nb = 4 (4 words in + a block), Nr = Nk + 6 (10). Therefore 'i' will iterate from + 4 to 44 (exclusive). Each time we iterate 4 times, i / Nk will + increase by 1. We use a counter iNk to keep track of this. + */ + + // go through the rounds expanding the key + var temp, iNk = 1; + var Nk = w.length; + var Nr1 = Nk + 6 + 1; + var end = Nb * Nr1; + for(var i = Nk; i < end; ++i) { + temp = w[i - 1]; + if(i % Nk === 0) { + // temp = SubWord(RotWord(temp)) ^ Rcon[i / Nk] + temp = + sbox[temp >>> 16 & 255] << 24 ^ + sbox[temp >>> 8 & 255] << 16 ^ + sbox[temp & 255] << 8 ^ + sbox[temp >>> 24] ^ (rcon[iNk] << 24); + iNk++; + } else if(Nk > 6 && (i % Nk === 4)) { + // temp = SubWord(temp) + temp = + sbox[temp >>> 24] << 24 ^ + sbox[temp >>> 16 & 255] << 16 ^ + sbox[temp >>> 8 & 255] << 8 ^ + sbox[temp & 255]; + } + w[i] = w[i - Nk] ^ temp; + } + + /* When we are updating a cipher block we always use the code path for + encryption whether we are decrypting or not (to shorten code and + simplify the generation of look up tables). However, because there + are differences in the decryption algorithm, other than just swapping + in different look up tables, we must transform our key schedule to + account for these changes: + + 1. The decryption algorithm gets its key rounds in reverse order. + 2. The decryption algorithm adds the round key before mixing columns + instead of afterwards. + + We don't need to modify our key schedule to handle the first case, + we can just traverse the key schedule in reverse order when decrypting. + + The second case requires a little work. + + The tables we built for performing rounds will take an input and then + perform SubBytes() and MixColumns() or, for the decrypt version, + InvSubBytes() and InvMixColumns(). But the decrypt algorithm requires + us to AddRoundKey() before InvMixColumns(). This means we'll need to + apply some transformations to the round key to inverse-mix its columns + so they'll be correct for moving AddRoundKey() to after the state has + had its columns inverse-mixed. + + To inverse-mix the columns of the state when we're decrypting we use a + lookup table that will apply InvSubBytes() and InvMixColumns() at the + same time. However, the round key's bytes are not inverse-substituted + in the decryption algorithm. To get around this problem, we can first + substitute the bytes in the round key so that when we apply the + transformation via the InvSubBytes()+InvMixColumns() table, it will + undo our substitution leaving us with the original value that we + want -- and then inverse-mix that value. + + This change will correctly alter our key schedule so that we can XOR + each round key with our already transformed decryption state. This + allows us to use the same code path as the encryption algorithm. + + We make one more change to the decryption key. Since the decryption + algorithm runs in reverse from the encryption algorithm, we reverse + the order of the round keys to avoid having to iterate over the key + schedule backwards when running the encryption algorithm later in + decryption mode. In addition to reversing the order of the round keys, + we also swap each round key's 2nd and 4th rows. See the comments + section where rounds are performed for more details about why this is + done. These changes are done inline with the other substitution + described above. + */ + if(decrypt) { + var tmp; + var m0 = imix[0]; + var m1 = imix[1]; + var m2 = imix[2]; + var m3 = imix[3]; + var wnew = w.slice(0); + end = w.length; + for(var i = 0, wi = end - Nb; i < end; i += Nb, wi -= Nb) { + // do not sub the first or last round key (round keys are Nb + // words) as no column mixing is performed before they are added, + // but do change the key order + if(i === 0 || i === (end - Nb)) { + wnew[i] = w[wi]; + wnew[i + 1] = w[wi + 3]; + wnew[i + 2] = w[wi + 2]; + wnew[i + 3] = w[wi + 1]; + } else { + // substitute each round key byte because the inverse-mix + // table will inverse-substitute it (effectively cancel the + // substitution because round key bytes aren't sub'd in + // decryption mode) and swap indexes 3 and 1 + for(var n = 0; n < Nb; ++n) { + tmp = w[wi + n]; + wnew[i + (3&-n)] = + m0[sbox[tmp >>> 24]] ^ + m1[sbox[tmp >>> 16 & 255]] ^ + m2[sbox[tmp >>> 8 & 255]] ^ + m3[sbox[tmp & 255]]; + } + } + } + w = wnew; + } + + return w; +} + +/** + * Updates a single block (16 bytes) using AES. The update will either + * encrypt or decrypt the block. + * + * @param w the key schedule. + * @param input the input block (an array of 32-bit words). + * @param output the updated output block. + * @param decrypt true to decrypt the block, false to encrypt it. + */ +function _updateBlock(w, input, output, decrypt) { + /* + Cipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)]) + begin + byte state[4,Nb] + state = in + AddRoundKey(state, w[0, Nb-1]) + for round = 1 step 1 to Nr–1 + SubBytes(state) + ShiftRows(state) + MixColumns(state) + AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) + end for + SubBytes(state) + ShiftRows(state) + AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + out = state + end + + InvCipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)]) + begin + byte state[4,Nb] + state = in + AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + for round = Nr-1 step -1 downto 1 + InvShiftRows(state) + InvSubBytes(state) + AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) + InvMixColumns(state) + end for + InvShiftRows(state) + InvSubBytes(state) + AddRoundKey(state, w[0, Nb-1]) + out = state + end + */ + + // Encrypt: AddRoundKey(state, w[0, Nb-1]) + // Decrypt: AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + var Nr = w.length / 4 - 1; + var m0, m1, m2, m3, sub; + if(decrypt) { + m0 = imix[0]; + m1 = imix[1]; + m2 = imix[2]; + m3 = imix[3]; + sub = isbox; + } else { + m0 = mix[0]; + m1 = mix[1]; + m2 = mix[2]; + m3 = mix[3]; + sub = sbox; + } + var a, b, c, d, a2, b2, c2; + a = input[0] ^ w[0]; + b = input[decrypt ? 3 : 1] ^ w[1]; + c = input[2] ^ w[2]; + d = input[decrypt ? 1 : 3] ^ w[3]; + var i = 3; + + /* In order to share code we follow the encryption algorithm when both + encrypting and decrypting. To account for the changes required in the + decryption algorithm, we use different lookup tables when decrypting + and use a modified key schedule to account for the difference in the + order of transformations applied when performing rounds. We also get + key rounds in reverse order (relative to encryption). */ + for(var round = 1; round < Nr; ++round) { + /* As described above, we'll be using table lookups to perform the + column mixing. Each column is stored as a word in the state (the + array 'input' has one column as a word at each index). In order to + mix a column, we perform these transformations on each row in c, + which is 1 byte in each word. The new column for c0 is c'0: + + m0 m1 m2 m3 + r0,c'0 = 2*r0,c0 + 3*r1,c0 + 1*r2,c0 + 1*r3,c0 + r1,c'0 = 1*r0,c0 + 2*r1,c0 + 3*r2,c0 + 1*r3,c0 + r2,c'0 = 1*r0,c0 + 1*r1,c0 + 2*r2,c0 + 3*r3,c0 + r3,c'0 = 3*r0,c0 + 1*r1,c0 + 1*r2,c0 + 2*r3,c0 + + So using mix tables where c0 is a word with r0 being its upper + 8 bits and r3 being its lower 8 bits: + + m0[c0 >> 24] will yield this word: [2*r0,1*r0,1*r0,3*r0] + ... + m3[c0 & 255] will yield this word: [1*r3,1*r3,3*r3,2*r3] + + Therefore to mix the columns in each word in the state we + do the following (& 255 omitted for brevity): + c'0,r0 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3] + c'0,r1 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3] + c'0,r2 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3] + c'0,r3 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3] + + However, before mixing, the algorithm requires us to perform + ShiftRows(). The ShiftRows() transformation cyclically shifts the + last 3 rows of the state over different offsets. The first row + (r = 0) is not shifted. + + s'_r,c = s_r,(c + shift(r, Nb) mod Nb + for 0 < r < 4 and 0 <= c < Nb and + shift(1, 4) = 1 + shift(2, 4) = 2 + shift(3, 4) = 3. + + This causes the first byte in r = 1 to be moved to the end of + the row, the first 2 bytes in r = 2 to be moved to the end of + the row, the first 3 bytes in r = 3 to be moved to the end of + the row: + + r1: [c0 c1 c2 c3] => [c1 c2 c3 c0] + r2: [c0 c1 c2 c3] [c2 c3 c0 c1] + r3: [c0 c1 c2 c3] [c3 c0 c1 c2] + + We can make these substitutions inline with our column mixing to + generate an updated set of equations to produce each word in the + state (note the columns have changed positions): + + c0 c1 c2 c3 => c0 c1 c2 c3 + c0 c1 c2 c3 c1 c2 c3 c0 (cycled 1 byte) + c0 c1 c2 c3 c2 c3 c0 c1 (cycled 2 bytes) + c0 c1 c2 c3 c3 c0 c1 c2 (cycled 3 bytes) + + Therefore: + + c'0 = 2*r0,c0 + 3*r1,c1 + 1*r2,c2 + 1*r3,c3 + c'0 = 1*r0,c0 + 2*r1,c1 + 3*r2,c2 + 1*r3,c3 + c'0 = 1*r0,c0 + 1*r1,c1 + 2*r2,c2 + 3*r3,c3 + c'0 = 3*r0,c0 + 1*r1,c1 + 1*r2,c2 + 2*r3,c3 + + c'1 = 2*r0,c1 + 3*r1,c2 + 1*r2,c3 + 1*r3,c0 + c'1 = 1*r0,c1 + 2*r1,c2 + 3*r2,c3 + 1*r3,c0 + c'1 = 1*r0,c1 + 1*r1,c2 + 2*r2,c3 + 3*r3,c0 + c'1 = 3*r0,c1 + 1*r1,c2 + 1*r2,c3 + 2*r3,c0 + + ... and so forth for c'2 and c'3. The important distinction is + that the columns are cycling, with c0 being used with the m0 + map when calculating c0, but c1 being used with the m0 map when + calculating c1 ... and so forth. + + When performing the inverse we transform the mirror image and + skip the bottom row, instead of the top one, and move upwards: + + c3 c2 c1 c0 => c0 c3 c2 c1 (cycled 3 bytes) *same as encryption + c3 c2 c1 c0 c1 c0 c3 c2 (cycled 2 bytes) + c3 c2 c1 c0 c2 c1 c0 c3 (cycled 1 byte) *same as encryption + c3 c2 c1 c0 c3 c2 c1 c0 + + If you compare the resulting matrices for ShiftRows()+MixColumns() + and for InvShiftRows()+InvMixColumns() the 2nd and 4th columns are + different (in encrypt mode vs. decrypt mode). So in order to use + the same code to handle both encryption and decryption, we will + need to do some mapping. + + If in encryption mode we let a=c0, b=c1, c=c2, d=c3, and r be + a row number in the state, then the resulting matrix in encryption + mode for applying the above transformations would be: + + r1: a b c d + r2: b c d a + r3: c d a b + r4: d a b c + + If we did the same in decryption mode we would get: + + r1: a d c b + r2: b a d c + r3: c b a d + r4: d c b a + + If instead we swap d and b (set b=c3 and d=c1), then we get: + + r1: a b c d + r2: d a b c + r3: c d a b + r4: b c d a + + Now the 1st and 3rd rows are the same as the encryption matrix. All + we need to do then to make the mapping exactly the same is to swap + the 2nd and 4th rows when in decryption mode. To do this without + having to do it on each iteration, we swapped the 2nd and 4th rows + in the decryption key schedule. We also have to do the swap above + when we first pull in the input and when we set the final output. */ + a2 = + m0[a >>> 24] ^ + m1[b >>> 16 & 255] ^ + m2[c >>> 8 & 255] ^ + m3[d & 255] ^ w[++i]; + b2 = + m0[b >>> 24] ^ + m1[c >>> 16 & 255] ^ + m2[d >>> 8 & 255] ^ + m3[a & 255] ^ w[++i]; + c2 = + m0[c >>> 24] ^ + m1[d >>> 16 & 255] ^ + m2[a >>> 8 & 255] ^ + m3[b & 255] ^ w[++i]; + d = + m0[d >>> 24] ^ + m1[a >>> 16 & 255] ^ + m2[b >>> 8 & 255] ^ + m3[c & 255] ^ w[++i]; + a = a2; + b = b2; + c = c2; + } + + /* + Encrypt: + SubBytes(state) + ShiftRows(state) + AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + + Decrypt: + InvShiftRows(state) + InvSubBytes(state) + AddRoundKey(state, w[0, Nb-1]) + */ + // Note: rows are shifted inline + output[0] = + (sub[a >>> 24] << 24) ^ + (sub[b >>> 16 & 255] << 16) ^ + (sub[c >>> 8 & 255] << 8) ^ + (sub[d & 255]) ^ w[++i]; + output[decrypt ? 3 : 1] = + (sub[b >>> 24] << 24) ^ + (sub[c >>> 16 & 255] << 16) ^ + (sub[d >>> 8 & 255] << 8) ^ + (sub[a & 255]) ^ w[++i]; + output[2] = + (sub[c >>> 24] << 24) ^ + (sub[d >>> 16 & 255] << 16) ^ + (sub[a >>> 8 & 255] << 8) ^ + (sub[b & 255]) ^ w[++i]; + output[decrypt ? 1 : 3] = + (sub[d >>> 24] << 24) ^ + (sub[a >>> 16 & 255] << 16) ^ + (sub[b >>> 8 & 255] << 8) ^ + (sub[c & 255]) ^ w[++i]; +} + +/** + * Deprecated. Instead, use: + * + * forge.cipher.createCipher('AES-', key); + * forge.cipher.createDecipher('AES-', key); + * + * Creates a deprecated AES cipher object. This object's mode will default to + * CBC (cipher-block-chaining). + * + * The key and iv may be given as a string of bytes, an array of bytes, a + * byte buffer, or an array of 32-bit words. + * + * @param options the options to use. + * key the symmetric key to use. + * output the buffer to write to. + * decrypt true for decryption, false for encryption. + * mode the cipher mode to use (default: 'CBC'). + * + * @return the cipher. + */ +function _createCipher(options) { + options = options || {}; + var mode = (options.mode || 'CBC').toUpperCase(); + var algorithm = 'AES-' + mode; + + var cipher; + if(options.decrypt) { + cipher = forge.cipher.createDecipher(algorithm, options.key); + } else { + cipher = forge.cipher.createCipher(algorithm, options.key); + } + + // backwards compatible start API + var start = cipher.start; + cipher.start = function(iv, options) { + // backwards compatibility: support second arg as output buffer + var output = null; + if(options instanceof forge.util.ByteBuffer) { + output = options; + options = {}; + } + options = options || {}; + options.output = output; + options.iv = iv; + start.call(cipher, options); + }; + + return cipher; +} + +} // end module implementation + +/* ########## Begin module wrapper ########## */ +var name = 'aes'; +if(typeof define !== 'function') { + // NodeJS -> AMD + if(typeof module === 'object' && module.exports) { + var nodeJS = true; + define = function(ids, factory) { + factory(require, module); + }; + } else { + // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MeshCommander + + + LMS MeshCommander + + + + ScriptCommander + + + + Intel® Active Management Technology + + + Intel® System Defense Utility + + + Intel® Manageability Commander + + + Intel® Unite - Hardware Management + + + + + + + + +
+ + + + + + + +
+

Intel® Active Management Technology

+

+
+ +
+ + +
+
+
+ +
+ + + +
+
+
+ +
+ + + + + + + +
+

Intel® Manageability Commander

+

+
+ +
+ + + + + + + + + +
+ + +

Manageability Switchbox

+ + + + + +

MeshCommander

+ + +

LMS MeshCommander

+ + + +

ScriptCommander

+ + + + +

Intel® Active Management Technology

+ + +

Intel® Standard Manageability

+ + +

Intel® Small Buisness Technology

+ +

+
+ + + + + + + + + + +
+ + + + + + + + +
+
+   +   +
+
+ +
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+
+
+ + Disconnected + + + Loading... + + +

+ + +

+ +
+ + + + + + + +