EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
amt-mei.js
Go to the documentation of this file.
1/*
2Copyright 2018-2021 Intel Corporation
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17var Q = require('queue');
18var g_internal = null;
19
20function retry_pthi_later()
21{
22 if (++g_internal.errorCount < 20)
23 {
24 g_internal.timeout = setTimeout(function (p)
25 {
26 p.connect(require('heci').GUIDS.AMT, { noPipeline: 1 });
27 }, 250, this);
28 }
29 else
30 {
31 this.Parent.emit('error', 'PTHI Connection could not be established');
32 }
33}
34
35function amt_heci()
36{
37 var emitterUtils = require('events').inherits(this);
38 emitterUtils.createEvent('error');
39
40 var heci = require('heci');
41 var sendConsole = function (msg) { try { require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": msg }); } catch (ex) { } }
42
43 this._ObjectID = "pthi";
44 var that = this;
45 if (g_internal == null)
46 {
47 g_internal = { _rq: new Q(), _amt: null, errorCount: 0 };
48 g_internal._setupPTHI = function _g_setupPTHI()
49 {
50 console.info1('setupPTHI()');
51 this._amt = heci.create();
52 this._amt.descriptorMetadata = "amt-pthi";
53 this._amt.BiosVersionLen = 65;
54 this._amt.UnicodeStringLen = 20;
55 this._amt.Parent = that;
56
57 this._amt.on('error', function _amtOnError(e)
58 {
59 console.info1('PTHIError: ' + e);
60 if (g_internal._rq.isEmpty())
61 {
62 console.info1(' Queue is empty');
63 this.Parent.emit('error', e); // No pending requests, so propagate the error up
64 }
65 else
66 {
67 console.info1(' Queue is NOT empty');
68
69 // Try again
70 retry_pthi_later.call(this);
71 }
72 });
73 this._amt.on('connect', function _amtOnConnect()
74 {
75 g_internal.errorCount = 0;
76 this.on('data', function _amtOnData(chunk)
77 {
78 //console.log("Received: " + chunk.length + " bytes");
79 var header = this.Parent.getCommand(chunk);
80 console.info1("CMD = " + header.Command + " (Status: " + header.Status + ") Response = " + header.IsResponse);
81
82 var user = g_internal._rq.deQueue();
83 var params = user.optional;
84 var callback = user.func;
85
86 params.unshift(header);
87 callback.apply(this.Parent, params);
88
89 if (g_internal._rq.isEmpty())
90 {
91 console.info1('No more requests, disconnecting');
92
93 // No More Requests, we can close PTHI
94 g_internal._amt.disconnect();
95 g_internal._amt = null;
96 }
97 else
98 {
99 // Send the next request
100 console.info1('Sending Next Request');
101 this.write(g_internal._rq.peekQueue().send);
102 }
103 });
104
105 // Start sending requests
106 this.write(g_internal._rq.peekQueue().send);
107 });
108
109
110
111 };
112 }
113
114
115 function trim(x) { var y = x.indexOf('\0'); if (y >= 0) { return x.substring(0, y); } else { return x; } }
116 this.getCommand = function getCommand(chunk) {
117 var command = chunk.length == 0 ? (g_internal._rq.peekQueue().cmd | 0x800000) : chunk.readUInt32LE(4);
118 var ret = { IsResponse: (command & 0x800000) == 0x800000 ? true : false, Command: (command & 0x7FFFFF), Status: chunk.length != 0 ? chunk.readUInt32LE(12) : -1, Data: chunk.length != 0 ? chunk.slice(16) : null };
119 return (ret);
120 };
121
122 this.sendCommand = function sendCommand()
123 {
124 if (arguments.length < 3 || typeof (arguments[0]) != 'number' || typeof (arguments[1]) != 'object' || typeof (arguments[2]) != 'function') { throw ('invalid parameters'); }
125 var args = [];
126 for (var i = 3; i < arguments.length; ++i) { args.push(arguments[i]); }
127
128 console.info1('sendCommand(' + arguments[0] + ')', this._hashCode());
129
130 var header = Buffer.from('010100000000000000000000', 'hex');
131 header.writeUInt32LE(arguments[0] | 0x04000000, 4);
132 header.writeUInt32LE(arguments[1] == null ? 0 : arguments[1].length, 8);
133
134 g_internal._rq.enQueue({ cmd: arguments[0], func: arguments[2], optional: args, send: (arguments[1] == null ? header : Buffer.concat([header, arguments[1]])) });
135 if (!g_internal._amt)
136 {
137 g_internal._setupPTHI();
138 g_internal._amt.connect(heci.GUIDS.AMT, { noPipeline: 1 });
139 }
140 }
141
142 this.getVersion = function getVersion(callback) {
143 var optional = [];
144 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
145 this.sendCommand(26, null, function (header, fn, opt) {
146 if (header.Status == 0) {
147 var i, CodeVersion = header.Data, val = { BiosVersion: CodeVersion.slice(0, g_internal._amt.BiosVersionLen).toString(), Versions: [] }, v = CodeVersion.slice(g_internal._amt.BiosVersionLen + 4);
148 for (i = 0; i < CodeVersion.readUInt32LE(g_internal._amt.BiosVersionLen) ; ++i)
149 {
150 val.Versions[i] = { Description: v.slice(2, v.readUInt16LE(0) + 2).toString(), Version: v.slice(4 + g_internal._amt.UnicodeStringLen, 4 + g_internal._amt.UnicodeStringLen + v.readUInt16LE(2 + g_internal._amt.UnicodeStringLen)).toString() };
151 v = v.slice(4 + (2 * g_internal._amt.UnicodeStringLen));
152 }
153 if (val.BiosVersion.indexOf('\0') > 0) { val.BiosVersion = val.BiosVersion.substring(0, val.BiosVersion.indexOf('\0')); }
154 opt.unshift(val);
155 } else {
156 opt.unshift(null);
157 }
158 fn.apply(this, opt);
159 }, callback, optional);
160 };
161
162 // Fill the left with zeros until the string is of a given length
163 function zeroLeftPad(str, len) {
164 if ((len == null) && (typeof (len) != 'number')) { return null; }
165 if (str == null) str = ''; // If null, this is to generate zero leftpad string
166 var zlp = '';
167 for (var i = 0; i < len - str.length; i++) { zlp += '0'; }
168 return zlp + str;
169 }
170
171 this.getUuid = function getUuid(callback) {
172 var optional = [];
173 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
174 this.sendCommand(0x5c, null, function (header, fn, opt) {
175 if (header.Status == 0) {
176 var result = {};
177 result.uuid = [zeroLeftPad(header.Data.readUInt32LE(0).toString(16), 8),
178 zeroLeftPad(header.Data.readUInt16LE(4).toString(16), 4),
179 zeroLeftPad(header.Data.readUInt16LE(6).toString(16), 4),
180 zeroLeftPad(header.Data.readUInt16BE(8).toString(16), 4),
181 zeroLeftPad(header.Data.slice(10).toString('hex').toLowerCase(), 12)].join('-');
182 opt.unshift(result);
183 } else {
184 opt.unshift(null);
185 }
186 fn.apply(this, opt);
187 }, callback, optional);
188 };
189
190 this.getProvisioningState = function getProvisioningState(callback) {
191 var optional = [];
192 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
193 this.sendCommand(17, null, function (header, fn, opt) {
194 if (header.Status == 0) {
195 var result = {};
196 result.state = header.Data.readUInt32LE(0);
197 if (result.state < 3) { result.stateStr = ["PRE", "IN", "POST"][result.state]; }
198 opt.unshift(result);
199 } else {
200 opt.unshift(null);
201 }
202 fn.apply(this, opt);
203 }, callback, optional);
204 };
205 this.getProvisioningMode = function getProvisioningMode(callback) {
206 var optional = [];
207 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
208 this.sendCommand(8, null, function (header, fn, opt) {
209 if (header.Status == 0) {
210 var result = {};
211 result.mode = header.Data.readUInt32LE(0);
212 if (result.mode < 4) { result.modeStr = ["NONE", "ENTERPRISE", "SMALL_BUSINESS", "REMOTE_ASSISTANCE"][result.mode]; }
213 result.legacy = header.Data.readUInt32LE(4) == 0 ? false : true;
214 opt.unshift(result);
215 } else {
216 opt.unshift(null);
217 }
218 fn.apply(this, opt);
219 }, callback, optional);
220 };
221 this.getEHBCState = function getEHBCState(callback) {
222 var optional = [];
223 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
224 this.sendCommand(132, null, function (header, fn, opt) {
225 if (header.Status == 0) {
226 opt.unshift({ EHBC: header.Data.readUInt32LE(0) != 0 });
227 } else {
228 opt.unshift(null);
229 }
230 fn.apply(this, opt);
231 }, callback, optional);
232 };
233 this.getControlMode = function getControlMode(callback) {
234 var optional = [];
235 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
236 this.sendCommand(107, null, function (header, fn, opt) {
237 if (header.Status == 0) {
238 var result = {};
239 result.controlMode = header.Data.readUInt32LE(0);
240 if (result.controlMode < 3) { result.controlModeStr = ["NONE_RPAT", "CLIENT", "ADMIN", "REMOTE_ASSISTANCE"][result.controlMode]; }
241 opt.unshift(result);
242 } else {
243 opt.unshift(null);
244 }
245 fn.apply(this, opt);
246 }, callback, optional);
247 };
248 this.getMACAddresses = function getMACAddresses(callback) {
249 var optional = [];
250 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
251 this.sendCommand(37, null, function (header, fn, opt) {
252 if (header.Status == 0) {
253 opt.unshift({ DedicatedMAC: header.Data.slice(0, 6).toString('hex:'), HostMAC: header.Data.slice(6, 12).toString('hex:') });
254 } else { opt.unshift({ DedicatedMAC: null, HostMAC: null }); }
255 fn.apply(this, opt);
256 }, callback, optional);
257 };
258 this.getDnsSuffix = function getDnsSuffix(callback) {
259 var optional = [];
260 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
261 this.sendCommand(54, null, function (header, fn, opt) {
262 if (header.Status == 0) {
263 var resultLen = header.Data.readUInt16LE(0);
264 if (resultLen > 0) { opt.unshift(header.Data.slice(2, 2 + resultLen).toString()); } else { opt.unshift(null); }
265 } else {
266 opt.unshift(null);
267 }
268 fn.apply(this, opt);
269 }, callback, optional);
270 };
271 this.getHashHandles = function getHashHandles(callback) {
272 var optional = [];
273 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
274 this.sendCommand(0x2C, null, function (header, fn, opt) {
275 var result = [];
276 if (header.Status == 0) {
277 var resultLen = header.Data.readUInt32LE(0);
278 for (var i = 0; i < resultLen; ++i) {
279 result.push(header.Data.readUInt32LE(4 + (4 * i)));
280 }
281 }
282 opt.unshift(result);
283 fn.apply(this, opt);
284 }, callback, optional);
285 };
286 this.getCertHashEntry = function getCertHashEntry(handle, callback) {
287 var optional = [];
288 for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
289
290 var data = Buffer.alloc(4);
291 data.writeUInt32LE(handle, 0);
292
293 this.sendCommand(0x2D, data, function (header, fn, opt) {
294 if (header.Status == 0) {
295 var result = {};
296 result.isDefault = header.Data.readUInt32LE(0);
297 result.isActive = header.Data.readUInt32LE(4);
298 result.hashAlgorithm = header.Data.readUInt8(72);
299 if (result.hashAlgorithm < 4) {
300 result.hashAlgorithmStr = ["MD5", "SHA1", "SHA256", "SHA512"][result.hashAlgorithm];
301 result.hashAlgorithmSize = [16, 20, 32, 64][result.hashAlgorithm];
302 result.certificateHash = header.Data.slice(8, 8 + result.hashAlgorithmSize).toString('hex');
303 }
304 result.name = header.Data.slice(73 + 2, 73 + 2 + header.Data.readUInt16LE(73)).toString();
305 opt.unshift(result);
306 } else {
307 opt.unshift(null);
308 }
309 fn.apply(this, opt);
310 }, callback, optional);
311 };
312 this.getCertHashEntries = function getCertHashEntries(callback) {
313 var optional = [];
314 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
315
316 this.getHashHandles(function (handles, fn, opt) {
317 var entries = [];
318 this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles);
319 }, callback, optional);
320 };
321
322 this._getHashEntrySink = function _getHashEntrySink(result, fn, opt, entries, handles) {
323 entries.push(result);
324 if (handles.length > 0) {
325 this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles);
326 } else {
327 opt.unshift(entries);
328 fn.apply(this, opt);
329 }
330 }
331 this.getLocalSystemAccount = function getLocalSystemAccount(callback) {
332 var optional = [];
333 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
334 this.sendCommand(103, Buffer.alloc(40), function (header, fn, opt) {
335 if (header.Status == 0 && header.Data.length == 68) {
336 opt.unshift({ user: trim(header.Data.slice(0, 33).toString()), pass: trim(header.Data.slice(33, 67).toString()), raw: header.Data });
337 }
338 else {
339 opt.unshift(null);
340 }
341 fn.apply(this, opt);
342 }, callback, optional);
343 }
344 this.getLanInterfaceSettings = function getLanInterfaceSettings(index, callback) {
345 var optional = [];
346 for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
347 var ifx = Buffer.alloc(4);
348 ifx.writeUInt32LE(index);
349 this.sendCommand(0x48, ifx, function onGetLanInterfaceSettings(header, fn, opt) {
350 if (header.Status == 0) {
351 var info = {};
352 info.enabled = header.Data.readUInt32LE(0);
353 info.dhcpEnabled = header.Data.readUInt32LE(8);
354 switch (header.Data[12]) {
355 case 1:
356 info.dhcpMode = 'ACTIVE'
357 break;
358 case 2:
359 info.dhcpMode = 'PASSIVE'
360 break;
361 default:
362 info.dhcpMode = 'UNKNOWN';
363 break;
364 }
365 info.mac = header.Data.slice(14).toString('hex:');
366
367 var addr = header.Data.readUInt32LE(4);
368 info.address = ((addr >> 24) & 255) + '.' + ((addr >> 16) & 255) + '.' + ((addr >> 8) & 255) + '.' + (addr & 255);
369 opt.unshift(info);
370 fn.apply(this, opt);
371 }
372 else {
373 opt.unshift(null);
374 fn.apply(this, opt);
375 }
376 }, callback, optional);
377
378 };
379 this.unprovision = function unprovision(mode, callback) {
380 var optional = [];
381 for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
382 var data = Buffer.alloc(4);
383 data.writeUInt32LE(mode, 0);
384 this.sendCommand(16, data, function (header, fn, opt) {
385 opt.unshift(header.Status);
386 fn.apply(this, opt);
387 }, callback, optional);
388 }
389 this.startConfiguration = function startConfiguration(callback) {
390 var optional = [];
391 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
392 this.sendCommand(0x29, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
393 }
394 this.stopConfiguration = function stopConfiguration(callback) {
395 var optional = [];
396 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
397 this.sendCommand(0x5E, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
398 }
399 this.openUserInitiatedConnection = function openUserInitiatedConnection(callback) {
400 var optional = [];
401 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
402 this.sendCommand(0x44, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
403 }
404 this.closeUserInitiatedConnection = function closeUnserInitiatedConnected(callback) {
405 var optional = [];
406 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
407 this.sendCommand(0x45, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
408 }
409 this.getRemoteAccessConnectionStatus = function getRemoteAccessConnectionStatus(callback) {
410 var optional = [];
411 for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
412 this.sendCommand(0x46, null, function (header, fn, opt) {
413 if (header.Status == 0) {
414 var hostname = header.Data.slice(14, header.Data.readUInt16LE(12) + 14).toString()
415 opt.unshift({ status: header.Status, networkStatus: header.Data.readUInt32LE(0), remoteAccessStatus: header.Data.readUInt32LE(4), remoteAccessTrigger: header.Data.readUInt32LE(8), mpsHostname: hostname, raw: header.Data });
416 } else {
417 opt.unshift({ status: header.Status });
418 }
419 fn.apply(this, opt);
420 }, callback, optional);
421 }
422 this.getProtocolVersion = function getProtocolVersion(callback) {
423 var optional = [];
424 for (var i = 1; i < arguments.length; ++i) { opt.push(arguments[i]); }
425
426 if (!this._tmpSession) { this._tmpSession = heci.create(); this._tmpSession.parent = this; }
427 this._tmpSession.doIoctl(heci.IOCTL.HECI_VERSION, Buffer.alloc(5), Buffer.alloc(5), function (status, buffer, self, fn, opt) {
428 if (status == 0) {
429 var result = buffer.readUInt8(0).toString() + '.' + buffer.readUInt8(1).toString() + '.' + buffer.readUInt8(2).toString() + '.' + buffer.readUInt16BE(3).toString();
430 opt.unshift(result);
431 fn.apply(self, opt);
432 }
433 else {
434 opt.unshift(null);
435 fn.apply(self, opt);
436 }
437
438 }, this, callback, optional);
439 }
440 this.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
441 if ((certHash == null) || ((certHash.length != 32) && (certHash.length != 48))) { func({ status: -101 }); }
442 this.stopConfiguration(function (status) {
443 if (status == 0) {
444 // We stopped the configuration, wait 20 seconds before starting up again.
445 var f = function tf() { delete tf.parent.xtimeout; tf.parent.startConfigurationHBasedEx(certHash, hostVpn, dnsSuffixList, func); }
446 f.parent = this;
447 this.xtimeout = setTimeout(f, 20000);
448 } else {
449 // We are not in the connect mode, this is good, start configuration right away.
450 this.startConfigurationHBasedEx(certHash, hostVpn, dnsSuffixList, func);
451 }
452 })
453 }
454 this.startConfigurationHBasedEx = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
455 var optional = [];
456 for (var i = 4; i < arguments.length; ++i) { optional.push(arguments[i]); }
457
458 // Format the command
459 var data = Buffer.alloc(1 + 64 + 4 + 4 + ((dnsSuffixList != null) ? 320 : 0));
460 data[0] = (certHash.length == 48) ? 3 : 2 // Write certificate hash type: SHA256 = 2, SHA384 = 3
461 certHash.copy(data, 1); // Write the hash
462 data.writeUInt32LE(hostVpn ? 1 : 0, 65); // Write is HostVPN is enabled
463 if (dnsSuffixList != null) {
464 data.writeUInt32LE(dnsSuffixList.length, 69); // Write the number of DNS Suffix, from 0 to 4
465 var ptr = 73;
466 for (var i = 0; i < dnsSuffixList.length; i++) { ptr += data.write(dnsSuffixList[i], ptr) + 1; } // Write up to 4 DNS Suffix with null seperation.
467 }
468
469 // Send the command
470 this.sendCommand(139, data, function (header, fn, opt) {
471 if (header.Status == 0) {
472 var amtHash = null;
473 if (header.Data[0] == 2) { amtHash = header.Data.slice(1, 33); } // SHA256
474 if (header.Data[0] == 3) { amtHash = header.Data.slice(1, 49); } // SHA384
475 opt.unshift({ status: header.Status, hash: amtHash.toString('hex') });
476 } else {
477 opt.unshift({ status: header.Status });
478 }
479 fn.apply(this, opt);
480 }, func, optional);
481 }
482}
483
484module.exports = amt_heci;
485
486
487/*
488AMT_STATUS_SUCCESS = 0,
489AMT_STATUS_INTERNAL_ERROR = 1,
490AMT_STATUS_INVALID_AMT_MODE = 3,
491AMT_STATUS_INVALID_MESSAGE_LENGTH = 4,
492AMT_STATUS_MAX_LIMIT_REACHED = 23,
493AMT_STATUS_INVALID_PARAMETER = 36,
494AMT_STATUS_RNG_GENERATION_IN_PROGRESS = 47,
495AMT_STATUS_RNG_NOT_READY = 48,
496AMT_STATUS_CERTIFICATE_NOT_READY = 49,
497AMT_STATUS_INVALID_HANDLE = 2053
498AMT_STATUS_NOT_FOUND = 2068,
499*/