IDER improvements.

Ylian Saint-Hilaire committed Apr 20, 2019 at 20:10 UTC 07f75ccb4f2b0ca46a9874ef9f5eb9687f6adf4e
2 files changed +518 -94
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.3.2-o",
3 + "version": "0.3.2-q",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
public/scripts/amt-ider-ws-0.0.1.js
+517 -93
@@ -1,136 +1,560 @@
1 /**
2 -* @description Remote Desktop
2 +* @description IDER Handling Module
3 * @author Ylian Saint-Hilaire
4 * @version v0.0.2
5 */
6
7 // Construct a Intel AMT IDER object
8 -var CreateAmtRemoteIder = function (serverurl) {
8 +var CreateAmtRemoteIder = function () {
9 var obj = {};
10 obj.protocol = 3; // IDER
11 - obj.state = 0;
12 - obj.socket = null;
13 - obj.serverurl = serverurl;
11 obj.bytesToAmt = 0;
12 obj.bytesFromAmt = 0;
13 + obj.rx_timeout = 30000; // Default 30000
14 + obj.tx_timeout = 0; // Default 0
15 + obj.heartbeat = 20000; // Default 20000
16 + obj.version = 1;
17 + obj.acc = "";
18 + obj.inSequence = 0;
19 + obj.outSequence = 0;
20 + obj.iderinfo = null;
21 + obj.enabled = false;
22 + obj.iderStart = 0; // OnReboot = 0, Graceful = 1, Now = 2
23 + obj.floppy = null;
24 + obj.cdrom = null;
25 + obj.floppyReady = false;
26 + obj.cdromReady = false;
27 + obj.pingTimer = null;
28
29 // Private method
18 - obj.Debug = function (msg) { console.log(msg); }
30 + function debug() { if (urlvars && urlvars['idertrace']) { console.log(...arguments); } }
31 +
32 + // Mode Sense
33 + 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);
34 + 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);
35 + 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);
36 + 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);
37 + 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);
38 + //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);
39 + 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);
40 + 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);
41 + //var IDE_ModeSence_CD_01_Array = String.fromCharCode(0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00);
42 + 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);
43 +
44 + // 0x46 constant data
45 + var IDE_CD_ConfigArrayHeader = String.fromCharCode(0x00, 0x00,0x00, 0x28, 0x00, 0x00, 0x00, 0x08);
46 + var IDE_CD_ConfigArrayProfileList = String.fromCharCode(0x00, 0x00, 0x03, 0x04, 0x00, 0x08, 0x01, 0x00);
47 + var IDE_CD_ConfigArrayCore = String.fromCharCode(0x00, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00, 0x02);
48 + var IDE_CD_Morphing = String.fromCharCode(0x00, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00);
49 + var IDE_CD_ConfigArrayRemovable = String.fromCharCode(0x00, 0x03, 0x03, 0x04, 0x29, 0x00, 0x00, 0x02);
50 + var IDE_CD_ConfigArrayRandom = String.fromCharCode(0x00, 0x10, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00);
51 + var IDE_CD_Read = String.fromCharCode(0x00, 0x1E, 0x03, 0x00);
52 + var IDE_CD_PowerManagement = String.fromCharCode(0x01, 0x00, 0x03, 0x00);
53 + var IDE_CD_Timeout = String.fromCharCode(0x01, 0x05, 0x03, 0x00);
54 +
55 + // 0x01 constant data
56 + 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);
57 + 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);
58 + var IDE_ModeSence_CDError_Recovery_Array = String.fromCharCode(0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00);
59 +
60
61 // Private method, called by parent when it change state
62 obj.xxStateChange = function (newstate) {
22 - //console.log("STATE: " + newstate);
23 - if (newstate == 0) { obj.Stop(); } // Tell Storage Server to stop IDER
24 - if (newstate == 3) { obj.StateChange(3); _Send('D'); } // Tell Storage Server to start IDER
63 + debug("IDER-StateChange", newstate);
64 + if (newstate == 0) { obj.Stop(); }
65 + if (newstate == 3) { obj.Start(); }
66 }
67
27 - // Private method
28 - obj.StateChange = function (newstate) { obj.state = newstate; }
29 -
30 - // Private method
31 - obj.ProcessData = function (data) { _Send('F' + data); obj.bytesFromAmt += data.length; }
32 -
33 - obj.Start = function (host, port, user, pass, tls) {
34 - //obj.Debug("IDER-Start");
35 - obj.host = host;
36 - obj.port = port;
37 - obj.user = user;
38 - obj.pass = pass;
39 - obj.tls = tls;
68 + obj.Start = function () {
69 + debug("IDER-Start");
70 + debug(obj.floppy, obj.cdrom);
71 obj.bytesToAmt = 0;
72 obj.bytesFromAmt = 0;
42 - obj.socket = new WebSocket(serverurl);
43 - obj.socket.onopen = _OnSocketConnected;
44 - obj.socket.onmessage = _OnMessage;
45 - obj.socket.onclose = _OnSocketClosed;
46 - obj.StateChange(1);
73 + obj.inSequence = 0;
74 + obj.outSequence = 0;
75 +
76 + // Send first command, OPEN_SESSION
77 + obj.SendCommand(0x40, ShortToStrX(obj.rx_timeout) + ShortToStrX(obj.tx_timeout) + ShortToStrX(obj.heartbeat) + IntToStrX(obj.version));
78 +
79 + // Setup the ping timer
80 + //obj.pingTimer = setInterval(function () { obj.SendCommand(0x44); }, 5000);
81 }
82
83 obj.Stop = function () {
50 - if (obj.socket != null) { _Send('G'); obj.socket.close(); obj.socket = null; }
51 - obj.StateChange(0);
84 + debug("IDER-Stop");
85 + if (obj.pingTimer) { clearInterval(obj.pingTimer); obj.pingTimer = null; }
86 obj.parent.Stop();
87 }
88
55 - function _OnSocketConnected() {
56 - obj.Debug("Socket Connected");
57 - obj.StateChange(2);
58 - _Send('C');
89 + // Private method
90 + obj.ProcessData = function (data) {
91 + obj.bytesFromAmt += data.length;
92 + obj.acc += data;
93 + debug('IDER-ProcessData', obj.acc.length, rstr2hex(obj.acc));
94 +
95 + // Process as many commands as possible
96 + while (true) {
97 + var len = obj.ProcessDataEx();
98 + if (len == 0) return;
99 + if (obj.inSequence != ReadIntX(obj.acc, 4)) { debug('ERROR: Out of sequence', obj.inSequence, ReadIntX(obj.acc, 4)); obj.Stop(); return; }
100 + obj.inSequence++;
101 + obj.acc = obj.acc.substring(len);
102 + }
103 }
104
61 - // Setup the file reader
62 - var fileReader = new FileReader();
63 - var fileReaderInuse = false, fileReaderAcc = [];
64 - if (fileReader.readAsBinaryString) {
65 - // Chrome & Firefox (Draft)
66 - fileReader.onload = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } }
67 - } else if (fileReader.readAsArrayBuffer) {
68 - // Chrome & Firefox (Spec)
69 - fileReader.onloadend = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } }
105 + // Private method
106 + obj.SendCommand = function (cmdid, data, completed, dma) {
107 + if (data == null) { data = ''; }
108 + var attributes = ((cmdid > 50) && (completed == true)) ? 2 : 0;
109 + if (dma) { attributes += 1; }
110 + var x = String.fromCharCode(cmdid, 0, 0, attributes) + IntToStrX(obj.outSequence++) + data;
111 + obj.parent.xxSend(x);
112 + obj.bytesToAmt += x.length;
113 + if (cmdid != 0x4B) { debug('IDER-SendData', x.length, rstr2hex(x)); }
114 }
115
72 - function _OnMessage(e) {
73 - if (typeof e.data == 'object') {
74 - if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; }
75 - if (fileReader.readAsBinaryString) {
76 - // Chrome & Firefox (Draft)
77 - fileReaderInuse = true;
78 - fileReader.readAsBinaryString(new Blob([e.data]));
79 - } else if (fileReader.readAsArrayBuffer) {
80 - // Chrome & Firefox (Spec)
81 - fileReaderInuse = true;
82 - fileReader.readAsArrayBuffer(e.data);
83 - } else {
84 - // IE10, readAsBinaryString does not exist, use an alternative.
85 - var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength;
86 - for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
87 - _OnSocketData(binary);
88 - }
116 + // CommandEndResponse (SCSI_SENSE)
117 + obj.SendCommandEndResponse = function (error, sense, device, asc, asq) {
118 + 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); }
119 + 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); }
120 + }
121 +
122 + //54 00 00 03 71 00 00 00 00 00 0c 00 b4 00 02 00 00 00 a0 58 85 00 03 00 00 00 a0 50 00 00
123 + //54 00 00 02 26 00 00 00 00 00 0c 00 b5 00 02 00 00 0c a0 58 85 00 03 00 00 00 a0 50 00 00
124 +
125 + // DataToHost (SCSI_READ)
126 + obj.SendDataToHost = function (device, completed, data, dma) {
127 + var dmalen = (dma) ? 0 : data.length;
128 + if (completed == true) {
129 + 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);
130 } else {
90 - _OnSocketData(e.data);
91 - }
92 - };
93 -
94 - function _OnSocketData(data) {
95 - if (!data) return;
96 -
97 - if (typeof data === 'object') {
98 - // This is an ArrayBuffer, convert it to a string array (used in IE)
99 - var binary = "";
100 - var bytes = new Uint8Array(data);
101 - var length = bytes.byteLength;
102 - for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
103 - data = binary;
131 + 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);
132 }
105 - else if (typeof data !== 'string') { return; }
133 + }
134 +
135 + // GetDataFromHost (SCSI_CHUNK)
136 + obj.SendGetDataFromHost = function (device, chunksize) {
137 + 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);
138 + }
139 +
140 + // DisableEnableFeatures (STATUS_DATA)
141 + // If type is REGS_TOGGLE (3), 4 bytes of data must be provided.
142 + obj.SendDisableEnableFeatures = function (type, data) { if (data == null) { data = ''; } obj.SendCommand(0x48, String.fromCharCode(type) + data); }
143 +
144 + // Private method
145 + obj.ProcessDataEx = function () {
146 + if (obj.acc.length < 8) return 0;
147
107 - // console.log("CMD: " + data.substring(0, 1));
148 + // First 8 bytes are the header
149 + // CommandID + 0x000000 + Sequence Number
150
109 - // Handle commands
110 - switch (data.substring(0, 1)) {
111 - case 'A': { data[0] = 'B'; _Send(data); break; } // Echo
112 - case 'C': { obj.parent.Start(obj.host, obj.port, obj.user, obj.pass, obj.tls); break; } // Session Start
113 - case 'E': { obj.Stop(); break; } // Stop IDER
114 - case 'F': { obj.parent.xxSend(data.substring(1)); obj.bytesToAmt += (data.length - 1); break; } // IDER Data
115 - case 'H': { if (obj.onDialogPrompt) obj.onDialogPrompt(obj, JSON.parse(data.substring(1))); break; } // IDER Dialog Prompt
151 + switch(obj.acc.charCodeAt(0)) {
152 + case 0x41: // OPEN_SESSION
153 + if (obj.acc.length < 30) return 0;
154 + var len = obj.acc.charCodeAt(29);
155 + if (obj.acc.length < (30 + len)) return 0;
156 + obj.iderinfo = {};
157 + obj.iderinfo.major = obj.acc.charCodeAt(8);
158 + obj.iderinfo.minor = obj.acc.charCodeAt(9);
159 + obj.iderinfo.fwmajor = obj.acc.charCodeAt(10);
160 + obj.iderinfo.fwminor = obj.acc.charCodeAt(11);
161 + obj.iderinfo.readbfr = ReadShortX(obj.acc, 16);
162 + obj.iderinfo.writebfr = ReadShortX(obj.acc, 18);
163 + obj.iderinfo.proto = obj.acc.charCodeAt(21);
164 + obj.iderinfo.iana = ReadIntX(obj.acc, 25);
165 + debug(obj.iderinfo);
166 +
167 + if (obj.iderinfo.proto != 0) { debug("Unknown proto", obj.iderinfo.proto); obj.Stop(); }
168 + if (obj.iderinfo.readbfr > 8192) { debug("Illegal read buffer size", obj.iderinfo.readbfr); obj.Stop(); }
169 + if (obj.iderinfo.writebfr > 8192) { debug("Illegal write buffer size", obj.iderinfo.writebfr); obj.Stop(); }
170 +
171 + if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
172 + else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
173 + else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
174 + //obj.SendDisableEnableFeatures(1); // GetSupportedFeatures
175 + return 30 + len;
176 + case 0x43: // CLOSE
177 + debug('CLOSE');
178 + obj.Stop();
179 + return 8;
180 + case 0x44: // KEEPALIVEPING
181 + obj.SendCommand(0x45); // Send PONG back
182 + return 8;
183 + case 0x45: // KEEPALIVEPONG
184 + debug('PONG');
185 + return 8;
186 + case 0x46: // RESETOCCURED
187 + if (obj.acc.length < 9) return 0;
188 + var resetMask = obj.acc.charCodeAt(8);
189 + if (g_media === null) {
190 + // No operations are pending
191 + obj.SendCommand(0x47); // Send ResetOccuredResponse
192 + debug('RESETOCCURED1', resetMask);
193 + } else {
194 + // Operations are being done, sent the reset once completed.
195 + g_reset = true;
196 + debug('RESETOCCURED2', resetMask);
197 + }
198 + return 9;
199 + case 0x49: // STATUS_DATA - DisableEnableFeaturesReply
200 + if (obj.acc.length < 13) return 0;
201 + var type = obj.acc.charCodeAt(8);
202 + var value = ReadIntX(obj.acc, 9);
203 + debug('STATUS_DATA', type, value);
204 + switch (type)
205 + {
206 + case 1: // REGS_AVAIL
207 + if (value & 1) {
208 + if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
209 + else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
210 + else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
211 + }
212 + break;
213 + case 2: // REGS_STATUS
214 + obj.enabled = (value & 2) ? true : false;
215 + debug("IDER Status: " + obj.enabled);
216 + break;
217 + case 3: // REGS_TOGGLE
218 + if (value != 1) { debug("Register toggle failure"); } //else { obj.SendDisableEnableFeatures(2); }
219 + break;
220 + }
221 + return 13;
222 + case 0x4A: // ERROR OCCURED
223 + if (obj.acc.length < 11) return 0;
224 + debug('IDER: ABORT', obj.acc.charCodeAt(8));
225 + //obj.Stop();
226 + return 11;
227 + case 0x4B: // HEARTBEAT
228 + //debug('HEARTBEAT');
229 + return 8;
230 + case 0x50: // COMMAND WRITTEN
231 + if (obj.acc.length < 28) return 0;
232 + var device = (obj.acc.charCodeAt(14) & 0x10) ? 0xB0 : 0xA0;
233 + var deviceFlags = obj.acc.charCodeAt(14);
234 + var cdb = obj.acc.substring(16, 28);
235 + var featureRegister = obj.acc.charCodeAt(9);
236 + debug('SCSI_CMD', device, rstr2hex(cdb), featureRegister, deviceFlags);
237 + handleSCSI(device, cdb, featureRegister, deviceFlags);
238 + return 28;
239 + case 0x53: // DATA FROM HOST
240 + if (obj.acc.length < 14) return 0;
241 + var len = ReadShortX(obj.acc, 9);
242 + if (obj.acc.length < (14 + len)) return 0;
243 + debug('SCSI_WRITE, len = ' + (14 + len));
244 + 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);
245 + return 14 + len;
246 + default:
247 + debug('Unknown IDER command', obj.acc[0]);
248 + obj.Stop();
249 + break;
250 }
251 + return 0;
252 }
253
119 - function _OnSocketClosed() {
120 - // obj.Debug("Socket Closed");
121 - obj.Stop();
122 - }
254 + function handleSCSI(dev, cdb, featureRegister, deviceFlags)
255 + {
256 + var lba;
257 + var len;
258 +
259 + switch(cdb.charCodeAt(0))
260 + {
261 + case 0x00: // TEST_UNIT_READY:
262 + debug("SCSI: TEST_UNIT_READY", dev);
263 + switch (dev) {
264 + case 0xA0: // DEV_FLOPPY
265 + if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
266 + if (obj.floppyReady == false) { obj.floppyReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
267 + break;
268 + case 0xB0: // DEV_CDDVD
269 + if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
270 + if (obj.cdromReady == false) { obj.cdromReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
271 + break;
272 + default:
273 + debug("SCSI Internal error 3", dev);
274 + return -1;
275 + }
276 + obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); // Indicate ready
277 + break;
278 + case 0x08: // READ_6
279 + lba = ((cdb.charCodeAt(1) & 0x1f) << 16) + (cdb.charCodeAt(2) << 8) + cdb.charCodeAt(3);
280 + len = cdb.charCodeAt(4);
281 + if (len == 0) { len = 256; }
282 + debug("SCSI: READ_6", dev, lba, len);
283 + sendDiskData(dev, lba, len, featureRegister);
284 + break;
285 + case 0x0a: // WRITE_6
286 + lba = ((cdb.charCodeAt(1) & 0x1f) << 16) + (cdb.charCodeAt(2) << 8) + cdb.charCodeAt(3);
287 + len = cdb.charCodeAt(4);
288 + if (len == 0) { len = 256; }
289 + debug("SCSI: WRITE_6", dev, lba, len);
290 + obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); // Write is not supported, remote no medium.
291 + return -1;
292 + /*
293 + case 0x15: // MODE_SELECT_6:
294 + debug("SCSI ERROR: MODE_SELECT_6", dev);
295 + obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
296 + return -1;
297 + */
298 + case 0x1a: // MODE_SENSE_6
299 + debug("SCSI: MODE_SENSE_6", dev);
300 + if ((cdb.charCodeAt(2) == 0x3f) && (cdb.charCodeAt(3) == 0x00)) {
301 + var a = 0, b = 0;
302 + switch (dev) {
303 + case 0xA0: // DEV_FLOPPY
304 + if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
305 + a = 0x00;
306 + b = 0x80; // Read only = 0x80, Read write = 0x00
307 + break;
308 + case 0xB0: // DEV_CDDVD
309 + if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
310 + a = 0x05;
311 + b = 0x80;
312 + break;
313 + default:
314 + debug("SCSI Internal error 6", dev);
315 + return -1;
316 + }
317 + obj.SendDataToHost(dev, true, String.fromCharCode(0, a, b, 0), featureRegister & 1);
318 + return;
319 + }
320 + obj.SendCommandEndResponse(1, 0x05, dev, 0x24, 0x00);
321 + break;
322 + case 0x1b: // START_STOP (Called when you eject the CDROM)
323 + //var immediate = cdb.charCodeAt(1) & 0x01;
324 + //var loej = cdb.charCodeAt(4) & 0x02;
325 + //var start = cdb.charCodeAt(4) & 0x01;
326 + obj.SendCommandEndResponse(1, 0, dev);
327 + break;
328 + case 0x1e: // LOCK_UNLOCK - ALLOW_MEDIUM_REMOVAL
329 + debug("SCSI: ALLOW_MEDIUM_REMOVAL", dev);
330 + if ((dev == 0xA0) && (obj.floppy == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
331 + if ((dev == 0xB0) && (obj.cdrom == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
332 + obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00);
333 + break;
334 + case 0x23: // READ_FORMAT_CAPACITIES (Floppy only)
335 + debug("SCSI: READ_FORMAT_CAPACITIES", dev);
336 + var buflen = ReadShort(cdb, 7);
337 + var mediaStatus = 0, sectors;
338 + var mcSize = buflen / 8; // Capacity descriptor size is 8
339 +
340 + switch (dev) {
341 + case 0xA0: // DEV_FLOPPY
342 + if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
343 + sectors = (obj.floppy.size >> 9) - 1;
344 + break;
345 + case 0xB0: // DEV_CDDVD
346 + if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
347 + sectors = (obj.cdrom.size >> 11) - 1; // Number 2048 byte blocks
348 + break;
349 + default:
350 + debug("SCSI Internal error 4", dev);
351 + return -1;
352 + }
353 +
354 + obj.SendDataToHost(dev, true, IntToStr(8) + String.fromCharCode(0x00, 0x00, 0x0b, 0x40, 0x02, 0x00, 0x02, 0x00), featureRegister & 1);
355 + break;
356 + case 0x25: // READ_CAPACITY
357 + debug("SCSI: READ_CAPACITY", dev);
358 + var len = 0;
359 + switch(dev)
360 + {
361 + case 0xA0: // DEV_FLOPPY
362 + if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
363 + if (obj.floppy != null) { len = (obj.floppy.size >> 9) - 1; }
364 + debug('DEV_FLOPPY', len); // Number 512 byte blocks
365 + break;
366 + case 0xB0: // DEV_CDDVD
367 + if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
368 + if (obj.cdrom != null) { len = (obj.cdrom.size >> 11) - 1; } // Number 2048 byte blocks
369 + debug('DEV_CDDVD', len);
370 + break;
371 + default:
372 + debug("SCSI Internal error 4", dev);
373 + return -1;
374 + }
375 + //if (dev == 0xA0) { dev = 0x00; } else { dev = 0x10; } // Weird but seems to work.
376 + debug("SCSI: READ_CAPACITY2", dev, deviceFlags);
377 + obj.SendDataToHost(deviceFlags, true, IntToStr(len) + String.fromCharCode(0, 0, ((dev == 0xB0) ? 0x08 : 0x02), 0), featureRegister & 1);
378 + break;
379 + case 0x28: // READ_10
380 + lba = ReadInt(cdb, 2);
381 + len = ReadShort(cdb, 7);
382 + debug("SCSI: READ_10", dev, lba, len);
383 + sendDiskData(dev, lba, len, featureRegister);
384 + break;
385 + case 0x2a: // WRITE_10 (Floppy only)
386 + case 0x2e: // WRITE_AND_VERIFY (Floppy only)
387 + lba = ReadInt(cdb, 2);
388 + len = ReadShort(cdb, 7);
389 + debug("SCSI: WRITE_10", dev, lba, len);
390 + obj.SendGetDataFromHost(dev, 512 * len); // Floppy writes only, accept sectors of 512 bytes
391 + break;
392 + case 0x43: // READ_TOC (CD Audio only)
393 + var buflen = ReadShort(cdb, 7);
394 + var msf = cdb.charCodeAt(1) & 0x02;
395 + var format = cdb.charCodeAt(2) & 0x07;
396 + if (format == 0) { format = cdb.charCodeAt(9) >> 6; }
397 + debug("SCSI: READ_TOC, dev=" + dev + ", buflen=" + buflen + ", msf=" + msf + ", format=" + format);
398 +
399 + switch (dev) {
400 + case 0xA0: // DEV_FLOPPY
401 + obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00); // Not implemented
402 + return -1;
403 + case 0xB0: // DEV_CDDVD
404 + // NOP
405 + break;
406 + default:
407 + debug("SCSI Internal error 9", dev);
408 + return -1;
409 + }
410 +
411 + if (format == 1) { obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x0a, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00), featureRegister & 1); }
412 + else if (format == 0) {
413 + if (msf) {
414 + 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);
415 + } else {
416 + 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);
417 + }
418 + }
419 + break;
420 + case 0x46: // GET_CONFIGURATION
421 + var sendall = (cdb.charCodeAt(1) != 2);
422 + var firstcode = ReadShort(cdb, 2);
423 + var buflen = ReadShort(cdb, 7);
424 +
425 + debug("SCSI: GET_CONFIGURATION", dev, sendall, firstcode, buflen);
426 +
427 + if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
428 +
429 + // Set the header
430 + var r = IntToStr(0x0008);
431 +
432 + // Add the data
433 + if (firstcode == 0) { r += IDE_CD_ConfigArrayProfileList; }
434 + if ((firstcode == 0x1) || (sendall && (firstcode < 0x1))) { r += IDE_CD_ConfigArrayCore; }
435 + if ((firstcode == 0x2) || (sendall && (firstcode < 0x2))) { r += IDE_CD_Morphing; }
436 + if ((firstcode == 0x3) || (sendall && (firstcode < 0x3))) { r += IDE_CD_ConfigArrayRemovable; }
437 + if ((firstcode == 0x10) || (sendall && (firstcode < 0x10))) { r += IDE_CD_ConfigArrayRandom; }
438 + if ((firstcode == 0x1E) || (sendall && (firstcode < 0x1E))) { r += IDE_CD_Read; }
439 + if ((firstcode == 0x100) || (sendall && (firstcode < 0x100))) { r += IDE_CD_PowerManagement; }
440 + if ((firstcode == 0x105) || (sendall && (firstcode < 0x105))) { r += IDE_CD_Timeout; }
441 +
442 + // Set the length
443 + r = IntToStr(r.length) + r;
444
124 - obj.dialogPrompt = function(x) { _Send('H' + JSON.stringify(x)); }
445 + // Cut the length to buflen if needed
446 + if (r.length > buflen) { r = r.substring(0, buflen); }
447
126 - function _Send(x) {
127 - // obj.Debug("Send(" + x.length + "): " + rstr2hex(x));
128 - if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
129 - var b = new Uint8Array(x.length);
130 - for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
131 - obj.socket.send(b.buffer);
448 + obj.SendDataToHost(dev, true, r, featureRegister & 1);
449 + return -1;
450 + case 0x4a: // GET_EV_STATUS - GET_EVENT_STATUS_NOTIFICATION
451 + //var buflen = (cdb.charCodeAt(7) << 8) + cdb.charCodeAt(8);
452 + //if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
453 + debug("SCSI: GET_EVENT_STATUS_NOTIFICATION", dev, cdb.charCodeAt(1), cdb.charCodeAt(4), cdb.charCodeAt(9));
454 + if ((cdb.charCodeAt(1) != 0x01) && (cdb.charCodeAt(4) != 0x10)) { debug('SCSI ERROR'); obj.SendCommandEndResponse(1, 0x05, dev, 0x26, 0x01); break; }
455 + var present = 0x00;
456 + if ((dev == 0xA0) && (obj.floppy != null)) { present = 0x02; }
457 + else if ((dev == 0xB0) && (obj.cdrom != null)) { present = 0x02; }
458 + obj.SendDataToHost(dev, true, String.fromCharCode(0x00, present, 0x80, 0x00), featureRegister & 1); // This is the original version, 4 bytes long
459 + break;
460 + case 0x4c:
461 + obj.SendCommand(0x51, IntToStrX(0) + IntToStrX(0) + IntToStrX(0) + String.fromCharCode(0x87, 0x50, 0x03, 0x00, 0x00, 0x00, 0xb0, 0x51, 0x05, 0x20, 0x00), true);
462 + break;
463 + case 0x51: // READ_DISC_INFO
464 + debug("SCSI READ_DISC_INFO", dev);
465 + obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // Correct
466 + return -1;
467 + case 0x55: // MODE_SELECT_10:
468 + debug("SCSI ERROR: MODE_SELECT_10", dev);
469 + obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
470 + return -1;
471 + case 0x5a: // MODE_SENSE_10
472 + debug("SCSI: MODE_SENSE_10", dev, cdb.charCodeAt(2) & 0x3f);
473 + var buflen = ReadShort(cdb, 7);
474 + //var pc = cdb.charCodeAt(2) & 0xc0;
475 + var r = null;
476 +
477 + if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
478 +
479 + // 1.44 mb floppy or LS120 (sectorCount == 0x3c300)
480 + var sectorCount = 0;
481 + if (dev == 0xA0) {
482 + if (obj.floppy != null) { sectorCount = (obj.floppy.size >> 9); }
483 + } else {
484 + if (obj.cdrom != null) { sectorCount = (obj.cdrom.size >> 11); }
485 + }
486 +
487 + switch (cdb.charCodeAt(2) & 0x3f) {
488 + 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;
489 + case 0x05: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_FloppyDisk_Page_Array:IDE_ModeSence_LS120Disk_Page_Array; } break;
490 + 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;
491 + case 0x1A: if (dev == 0xB0) { r = IDE_ModeSence_CD_1A_Array; } break;
492 + case 0x1D: if (dev == 0xB0) { r = IDE_ModeSence_CD_1D_Array; } break;
493 + case 0x2A: if (dev == 0xB0) { r = IDE_ModeSence_CD_2A_Array; } break;
494 + }
495 +
496 + if (r == null) {
497 + obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // TODO: Send proper error!!!
498 + } else {
499 + // Set disk to read only (we don't support write).
500 + //ms_data[3] = ms_data[3] | 0x80;
501 + obj.SendDataToHost(dev, true, r, featureRegister & 1);
502 + }
503 + break;
504 + default: // UNKNOWN COMMAND
505 + debug("IDER: Unknown SCSI command", cdb.charCodeAt(0));
506 + obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00);
507 + return -1;
508 + }
509 + return 0;
510 + }
511 +
512 + function sendDiskData(dev, lba, len, featureRegister) {
513 + var media = null;
514 + var mediaBlocks = 0;
515 + if (dev == 0xA0) { media = obj.floppy; if (obj.floppy != null) { mediaBlocks = (obj.floppy.size >> 9); } }
516 + if (dev == 0xB0) { media = obj.cdrom; if (obj.cdrom != null) { mediaBlocks = (obj.cdrom.size >> 11); } }
517 + if ((len < 0) || (lba + len > mediaBlocks)) { obj.SendCommandEndResponse(1, 0x05, dev, 0x21, 0x00); return 0; }
518 + if (len == 0) { obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); return 0; }
519 + if (media != null) {
520 + if (dev == 0xA0) { lba <<= 9; len <<= 9; } else { lba <<= 11; len <<= 11; }
521 + if (g_media !== null) {
522 + console.log('IDERERROR: Read while performing read');
523 + obj.Stop();
524 + } else {
525 + // obj.iderinfo.readbfr // TODO: MaxRead
526 + g_media = media;
527 + g_dev = dev;
528 + g_lba = lba;
529 + g_len = len;
530 + sendDiskDataEx(featureRegister);
531 + }
532 }
533 }
534
535 + var g_reset = false;
536 + var g_media = null;
537 + var g_dev;
538 + var g_lba;
539 + var g_len;
540 + function sendDiskDataEx(featureRegister) {
541 + var len = g_len, lba = g_lba;
542 + if (g_len > obj.iderinfo.readbfr) { len = obj.iderinfo.readbfr; }
543 + g_len -= len;
544 + g_lba += len;
545 + var fr = new FileReader();
546 + fr.onload = function () {
547 + obj.SendDataToHost(g_dev, (g_len == 0), this.result, featureRegister & 1);
548 + if ((g_len > 0) && (g_reset == false)) {
549 + sendDiskDataEx(featureRegister);
550 + } else {
551 + g_media = null;
552 + if (g_reset) { obj.SendCommand(0x47); g_reset = false; } // Send ResetOccuredResponse
553 + }
554 + };
555 + //console.log('Read from ' + lba + ' to ' + (lba + len) + ', total of ' + len);
556 + fr.readAsBinaryString(g_media.slice(lba, lba + len));
557 + }
558 +
559 return obj;
560 }