dhcp.js is not only on Linux.
Ylian Saint-Hilaire committed
Jul 24, 2021 at 09:28 UTC
345f3c12b4b053e0f78dfeb3a6dcdb21da99c7aa
3 files changed
+376
-109
agents/meshcore.js
+53
-109
@@ -46,11 +46,9 @@ var MESHRIGHT_CHATNOTIFY = 16384;
46
var MESHRIGHT_UNINSTALL = 32768;
47
var MESHRIGHT_NODESKTOP = 65536;
48
49
-if (require('MeshAgent').ARCHID == null)
50
-{
49
+if (require('MeshAgent').ARCHID == null) {
50
var id = null;
52
- switch (process.platform)
53
- {
51
+ switch (process.platform) {
52
case 'win32':
53
id = require('_GenericMarshal').PointerSize == 4 ? 3 : 4;
54
break;
@@ -58,12 +56,10 @@ if (require('MeshAgent').ARCHID == null)
56
id = require('_GenericMarshal').PointerSize == 4 ? 31 : 30;
57
break;
58
case 'darwin':
61
- try
62
- {
59
+ try {
60
id = require('os').arch() == 'x64' ? 16 : 29;
61
}
65
- catch (xx)
66
- {
62
+ catch (xx) {
63
id = 16;
64
}
65
break;
@@ -280,11 +276,9 @@ obj.DAIPC.on('connection', function (c) {
276
});
277
278
// Send current sessions to registered apps
283
-function broadcastSessionsToRegisteredApps(x)
284
-{
279
+function broadcastSessionsToRegisteredApps(x) {
280
var p = {}, i;
286
- for (i = 0; sendAgentMessage.messages != null && i < sendAgentMessage.messages.length; ++i)
287
- {
281
+ for (i = 0; sendAgentMessage.messages != null && i < sendAgentMessage.messages.length; ++i) {
282
p[i] = sendAgentMessage.messages[i];
283
}
284
tunnelUserCount.msg = p;
@@ -849,8 +843,7 @@ function handleServerCommand(data) {
843
844
// Perform manual server TLS certificate checking based on the certificate hash given by the server.
845
woptions.rejectUnauthorized = 0;
852
- woptions.checkServerIdentity = function checkServerIdentity(certs)
853
- {
846
+ woptions.checkServerIdentity = function checkServerIdentity(certs) {
847
/*
848
try { sendConsoleText("certs[0].digest: " + certs[0].digest); } catch (ex) { sendConsoleText(ex); }
849
try { sendConsoleText("certs[0].fingerprint: " + certs[0].fingerprint); } catch (ex) { sendConsoleText(ex); }
@@ -1057,17 +1050,13 @@ function handleServerCommand(data) {
1050
}
1051
case 'setclip': {
1052
// Set the load clipboard to a user value
1060
- if (typeof data.data == 'string')
1061
- {
1053
+ if (typeof data.data == 'string') {
1054
MeshServerLogEx(22, [data.data.length], "Setting clipboard content, " + data.data.length + " byte(s)", data);
1063
- if (require('MeshAgent').isService)
1064
- {
1065
- if (process.platform != 'win32')
1066
- {
1055
+ if (require('MeshAgent').isService) {
1056
+ if (process.platform != 'win32') {
1057
require('clipboard').dispatchWrite(data.data);
1058
}
1069
- else
1070
- {
1059
+ else {
1060
var clipargs = data.data;
1061
var uid = require('user-sessions').consoleUid();
1062
var user = require('user-sessions').getUsername(uid);
@@ -1077,20 +1066,17 @@ function handleServerCommand(data) {
1066
this._dispatcher = require('win-dispatcher').dispatch({ user: user, modules: [{ name: 'clip-dispatch', script: "module.exports = { dispatch: function dispatch(val) { require('clipboard')(val); process.exit(); } };" }], launch: { module: 'clip-dispatch', method: 'dispatch', args: [clipargs] } });
1067
this._dispatcher.parent = this;
1068
//require('events').setFinalizerMetadata.call(this._dispatcher, 'clip-dispatch');
1080
- this._dispatcher.on('connection', function (c)
1081
- {
1069
+ this._dispatcher.on('connection', function (c) {
1070
this._c = c;
1071
this._c.root = this.parent;
1084
- this._c.on('end', function ()
1085
- {
1072
+ this._c.on('end', function () {
1073
this.root._dispatcher = null;
1074
this.root = null;
1075
});
1076
});
1077
}
1078
}
1092
- else
1093
- {
1079
+ else {
1080
require("clipboard")(data.data);
1081
} // Set the clipboard
1082
mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
@@ -1324,7 +1310,7 @@ function handleServerCommand(data) {
1310
if (data.tag == 'info') { sendConsoleText(JSON.stringify(data, null, 2)); }
1311
if (data.tag == 'install') {
1312
data.func = function (options, success) {
1327
- sendConsoleText('Download of MeshCentral Assistant ' + (success?'succeed':'failed'));
1313
+ sendConsoleText('Download of MeshCentral Assistant ' + (success ? 'succeed' : 'failed'));
1314
if (success) {
1315
// TODO: Install & Run
1316
}
@@ -2885,7 +2871,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
2871
case 'help': { // Displays available commands
2872
var fin = '', f = '', availcommands = 'agentupdate,errorlog,msh,timerinfo,coreinfo,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,wslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,openurl,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,wallpaper,agentmsg';
2873
if (require('os').dns != null) { availcommands += ',dnsinfo'; }
2888
- try { require('dhcp'); availcommands += ',dhcp'; } catch (e) { }
2874
+ try { require('linux-dhcp'); availcommands += ',dhcp'; } catch (e) { }
2875
if (process.platform == 'win32') { availcommands += ',cs,safemode,wpfhwacceleration,uac'; }
2876
if (amt != null) { availcommands += ',amt,amtconfig,amtevents'; }
2877
if (process.platform != 'freebsd') { availcommands += ',vm'; }
@@ -2901,27 +2887,15 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
2887
response = "Available commands: \r\n" + fin + ".";
2888
break;
2889
}
2904
- case 'dhcp':
2890
+ case 'dhcp': // This command is only supported on Linux, this is because Linux does not give us the DNS suffix for each network adapter independently so we have to ask the DHCP server.
2891
{
2906
- try
2907
- {
2908
- require('dhcp');
2909
- }
2910
- catch(e)
2911
- {
2912
- response = 'Unknown command "dhcp", type "help" for list of avaialble commands.';
2913
- break;
2914
- }
2915
- if(args['_'].length==0)
2916
- {
2892
+ try { require('linux-dhcp'); } catch (e) { response = 'Unknown command "dhcp", type "help" for list of avaialble commands.'; break; }
2893
+ if (args['_'].length == 0) {
2894
var j = require('os').networkInterfaces();
2895
var ifcs = [];
2919
- for(var i in j)
2920
- {
2921
- for (var z in j[i])
2922
- {
2923
- if (j[i][z].status == 'up' && j[i][z].type != 'loopback' && j[i][z].address != null)
2924
- {
2896
+ for (var i in j) {
2897
+ for (var z in j[i]) {
2898
+ if (j[i][z].status == 'up' && j[i][z].type != 'loopback' && j[i][z].address != null) {
2899
ifcs.push('"' + i + '"');
2900
break;
2901
}
@@ -2929,15 +2903,12 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
2903
}
2904
response = 'Proper usage: dhcp [' + ifcs.join(' | ') + ']';
2905
}
2932
- else
2933
- {
2934
- require('dhcp').client.info(args['_'][0]).
2935
- then(function (d)
2936
- {
2906
+ else {
2907
+ require('linux-dhcp').client.info(args['_'][0]).
2908
+ then(function (d) {
2909
sendConsoleText(JSON.stringify(d, null, 1), sessionid);
2910
},
2939
- function (e)
2940
- {
2911
+ function (e) {
2912
sendConsoleText(e, sessionid);
2913
});
2914
}
@@ -3064,24 +3035,19 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
3035
break;
3036
}
3037
case 'agentmsg': {
3067
- if (args['_'].length == 0)
3068
- {
3038
+ if (args['_'].length == 0) {
3039
response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [index]\r\n agentmsg list"; // Display usage
3070
- } else
3071
- {
3072
- if ((args['_'][0] == 'add') && (args['_'].length > 1))
3073
- {
3040
+ } else {
3041
+ if ((args['_'][0] == 'add') && (args['_'].length > 1)) {
3042
var msgID, iconIndex = 0;
3043
if (args['_'].length >= 3) { try { iconIndex = parseInt(args['_'][2]); } catch (e) { } }
3044
if (typeof iconIndex != 'number') { iconIndex = 0; }
3045
msgID = sendAgentMessage(args['_'][1], iconIndex);
3046
response = 'Agent message: ' + msgID + ' added.';
3079
- } else if ((args['_'][0] == 'remove') && (args['_'].length > 1))
3080
- {
3047
+ } else if ((args['_'][0] == 'remove') && (args['_'].length > 1)) {
3048
var r = removeAgentMessage(args['_'][1]);
3049
response = 'Message ' + (r ? 'removed' : 'NOT FOUND');
3083
- } else if (args['_'][0] == 'list')
3084
- {
3050
+ } else if (args['_'][0] == 'list') {
3051
response = JSON.stringify(sendAgentMessage(), null, 2);
3052
}
3053
broadcastSessionsToRegisteredApps();
@@ -3518,8 +3484,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
3484
if (process.platform != 'win32') {
3485
require('clipboard').dispatchWrite(args['_'][0]);
3486
}
3521
- else
3522
- {
3487
+ else {
3488
var clipargs = args['_'][0];
3489
var uid = require('user-sessions').consoleUid();
3490
var user = require('user-sessions').getUsername(uid);
@@ -3529,12 +3494,10 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
3494
this._dispatcher = require('win-dispatcher').dispatch({ user: user, modules: [{ name: 'clip-dispatch', script: "module.exports = { dispatch: function dispatch(val) { require('clipboard')(val); process.exit(); } };" }], launch: { module: 'clip-dispatch', method: 'dispatch', args: [clipargs] } });
3495
this._dispatcher.parent = this;
3496
//require('events').setFinalizerMetadata.call(this._dispatcher, 'clip-dispatch');
3532
- this._dispatcher.on('connection', function (c)
3533
- {
3497
+ this._dispatcher.on('connection', function (c) {
3498
this._c = c;
3499
this._c.root = this.parent;
3536
- this._c.on('end', function ()
3537
- {
3500
+ this._c.on('end', function () {
3501
this.root._dispatcher = null;
3502
this.root = null;
3503
});
@@ -3542,8 +3505,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
3505
}
3506
response = 'Setting clipboard to: "' + args['_'][0] + '"';
3507
}
3545
- else
3546
- {
3508
+ else {
3509
require("clipboard")(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"';
3510
}
3511
}
@@ -4108,20 +4070,16 @@ function sendConsoleText(text, sessionid) {
4070
if (sessionid != 'pipe') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: text, sessionid: sessionid }); }
4071
}
4072
4111
-function removeAgentMessage(msgid)
4112
-{
4073
+function removeAgentMessage(msgid) {
4074
var ret = false;
4114
- if (msgid == null)
4115
- {
4075
+ if (msgid == null) {
4076
// Delete all messages
4077
sendAgentMessage.messages = [];
4078
ret = true;
4079
}
4120
- else
4121
- {
4080
+ else {
4081
var i = sendAgentMessage.messages.findIndex(function (v) { return (v.id == msgid); });
4123
- if (i >= 0)
4124
- {
4082
+ if (i >= 0) {
4083
sendAgentMessage.messages.splice(i, 1);
4084
ret = true;
4085
}
@@ -4131,33 +4089,26 @@ function removeAgentMessage(msgid)
4089
}
4090
4091
// Send a mesh agent message to server, placing a bubble/badge on the agent device
4134
-function sendAgentMessage(msg, icon, serverid, first)
4135
-{
4136
- if (sendAgentMessage.messages == null)
4137
- {
4092
+function sendAgentMessage(msg, icon, serverid, first) {
4093
+ if (sendAgentMessage.messages == null) {
4094
sendAgentMessage.messages = [];
4095
}
4096
4141
- if (arguments.length > 0)
4142
- {
4143
- if (first == null || (serverid && first && sendAgentMessage.messages.findIndex(function (v) { return (v.msgid == serverid); }) < 0))
4144
- {
4097
+ if (arguments.length > 0) {
4098
+ if (first == null || (serverid && first && sendAgentMessage.messages.findIndex(function (v) { return (v.msgid == serverid); }) < 0)) {
4099
sendAgentMessage.messages.push({ msg: msg, icon: icon, msgid: serverid });
4100
sendAgentMessage.messages.peek().id = sendAgentMessage.messages.peek()._hashCode();
4101
}
4102
}
4103
4104
var p = {}, i;
4151
- for (i = 0; i < sendAgentMessage.messages.length; ++i)
4152
- {
4105
+ for (i = 0; i < sendAgentMessage.messages.length; ++i) {
4106
p[i] = sendAgentMessage.messages[i];
4107
}
4155
- try
4156
- {
4108
+ try {
4109
require('MeshAgent').SendCommand({ action: 'sessions', type: 'msg', value: p });
4110
}
4159
- catch(ex)
4160
- {
4111
+ catch (ex) {
4112
}
4113
return (arguments.length > 0 ? sendAgentMessage.messages.peek().id : sendAgentMessage.messages);
4114
}
@@ -4557,24 +4508,19 @@ function agentUpdate_Start(updateurl, updateoptions) {
4508
//process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
4509
4510
// Called when the server connection state changes
4560
-function handleServerConnection(state)
4561
-{
4511
+function handleServerConnection(state) {
4512
meshServerConnectionState = state;
4563
- if (meshServerConnectionState == 0)
4564
- {
4513
+ if (meshServerConnectionState == 0) {
4514
// Server disconnected
4515
if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
4516
lastSelfInfo = null;
4568
- } else
4569
- {
4517
+ } else {
4518
// Server connected, send mesh core information
4571
- if (require('MeshAgent').ServerInfo == null || require('MeshAgent').ServerInfo.ControlChannelCertificate == null)
4572
- {
4519
+ if (require('MeshAgent').ServerInfo == null || require('MeshAgent').ServerInfo.ControlChannelCertificate == null) {
4520
// Outdated Agent, will have insecure tunnels
4521
sendAgentMessage("This agent has an outdated certificate validation mechanism, consider updating.", 3, 118);
4522
}
4576
- else if (global._MSH == null)
4577
- {
4523
+ else if (global._MSH == null) {
4524
sendAgentMessage("This is an old agent version, consider updating.", 3, 117);
4525
}
4526
@@ -4587,15 +4533,13 @@ function handleServerConnection(state)
4533
// Update the server on with basic info, logged in users and more advanced stuff, like Intel ME and Network Settings
4534
meInfoStr = null;
4535
sendPeriodicServerUpdate(null, true);
4590
- if (selfInfoUpdateTimer == null)
4591
- {
4536
+ if (selfInfoUpdateTimer == null) {
4537
selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); // 20 minutes
4538
selfInfoUpdateTimer.metadata = 'meshcore (InfoUpdate Timer)';
4539
}
4540
4541
// Send any state messages
4597
- if (Object.keys(tunnelUserCount.msg).length > 0)
4598
- {
4542
+ if (Object.keys(tunnelUserCount.msg).length > 0) {
4543
sendAgentMessage();
4544
broadcastSessionsToRegisteredApps();
4545
}
agents/modules_meshcmd/linux-dhcp.js
renamed
agents/modules_meshcore/linux-dhcp.js
new
+323
@@ -0,0 +1,323 @@
1
+/*
2
+Copyright 2021 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+
16
+* @description Mini DHCP Client Module, to fetch configuration data
17
+* @author Bryan Roe & Ylian Saint-Hilaire
18
+*/
19
+
20
+// DHCP Information
21
+if (Function.prototype.internal == null) { Object.defineProperty(Function.prototype, 'internal', { get: function () { return (this); } }); }
22
+if (global._hide == null)
23
+{
24
+ global._hide = function _hide(v)
25
+ {
26
+ if(v==null || (v!=null && typeof(v)=='boolean'))
27
+ {
28
+ var ret = _hide.currentObject;
29
+ if (v) { _hide.currentObject = null; }
30
+ return (ret);
31
+ }
32
+ else
33
+ {
34
+ _hide.currentObject = v;
35
+ }
36
+ }
37
+}
38
+addModule('promise2', Buffer.from('LyoNCkNvcHlyaWdodCAyMDE4IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgcmVmVGFibGUgPSB7fTsNCg0KZnVuY3Rpb24gcHJvbWlzZUluaXRpYWxpemVyKHIsaikNCnsNCiAgICB0aGlzLl9yZXMgPSByOw0KICAgIHRoaXMuX3JlaiA9IGo7DQp9DQoNCmZ1bmN0aW9uIGdldFJvb3RQcm9taXNlKG9iaikNCnsNCiAgICB3aGlsZShvYmoucGFyZW50UHJvbWlzZSkNCiAgICB7DQogICAgICAgIG9iaiA9IG9iai5wYXJlbnRQcm9taXNlOw0KICAgIH0NCiAgICByZXR1cm4gKG9iaik7DQp9DQoNCmZ1bmN0aW9uIGV2ZW50X3N3aXRjaGVyKGRlc2lyZWRfY2FsbGVlLCB0YXJnZXQpDQp7DQogICAgcmV0dXJuICh7IF9PYmplY3RJRDogJ2V2ZW50X3N3aXRjaGVyJywgZnVuYzogdGFyZ2V0LmJpbmQoZGVzaXJlZF9jYWxsZWUpIH0pOw0KfQ0KDQpmdW5jdGlvbiBldmVudF9mb3J3YXJkZXIoc291cmNlT2JqLCBzb3VyY2VOYW1lLCB0YXJnZXRPYmosIHRhcmdldE5hbWUpDQp7DQogICAgc291cmNlT2JqLm9uKHNvdXJjZU5hbWUsIHRhcmdldE9iai5lbWl0LmJpbmQodGFyZ2V0T2JqKSk7DQp9DQoNCg0KZnVuY3Rpb24gcmV0dXJuX3Jlc29sdmVkKCkNCnsNCiAgICB2YXIgcGFybXMgPSBbJ3Jlc29sdmVkJ107DQogICAgZm9yICh2YXIgYWkgaW4gYXJndW1lbnRzKQ0KICAgIHsNCiAgICAgICAgcGFybXMucHVzaChhcmd1bWVudHNbYWldKTsNCiAgICB9DQogICAgdGhpcy5fWFNMRi5lbWl0LmFwcGx5KHRoaXMuX1hTTEYsIHBhcm1zKTsNCn0NCmZ1bmN0aW9uIHJldHVybl9yZWplY3RlZCgpDQp7DQogICAgdGhpcy5fWFNMRi5wcm9taXNlLl9fY2hpbGRQcm9taXNlLl9yZWooZSk7DQp9DQpmdW5jdGlvbiBlbWl0cmVqZWN0KGEpDQp7DQogICAgcHJvY2Vzcy5lbWl0KCd1bmNhdWdodEV4Y2VwdGlvbicsICdwcm9taXNlLnVuY2F1Z2h0UmVqZWN0aW9uOiAnICsgSlNPTi5zdHJpbmdpZnkoYSkpOw0KfQ0KZnVuY3Rpb24gUHJvbWlzZShwcm9taXNlRnVuYykNCnsNCiAgICB0aGlzLl9PYmplY3RJRCA9ICdwcm9taXNlJzsNCiAgICB0aGlzLnByb21pc2UgPSB0aGlzOw0KICAgIHRoaXMuX2ludGVybmFsID0geyBfT2JqZWN0SUQ6ICdwcm9taXNlLmludGVybmFsJywgcHJvbWlzZTogdGhpcywgY29tcGxldGVkOiBmYWxzZSwgZXJyb3JzOiBmYWxzZSwgY29tcGxldGVkQXJnczogW10sIGludGVybmFsQ291bnQ6IDAsIF91cDogbnVsbCB9Ow0KICAgIHJlcXVpcmUoJ2V2ZW50cycpLkV2ZW50RW1pdHRlci5jYWxsKHRoaXMuX2ludGVybmFsKTsNCiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkodGhpcywgInBhcmVudFByb21pc2UiLA0KICAgICAgICB7DQogICAgICAgICAgICBnZXQ6IGZ1bmN0aW9uICgpIHsgcmV0dXJuICh0aGlzLl91cCk7IH0sDQogICAgICAgICAgICBzZXQ6IGZ1bmN0aW9uICh2YWx1ZSkNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBpZiAodmFsdWUgIT0gbnVsbCAmJiB0aGlzLl91cCA9PSBudWxsKQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgLy8gV2UgYXJlIG5vIGxvbmdlciBhbiBvcnBoYW4NCiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuX2ludGVybmFsLnVuY2F1Z2h0ICE9IG51bGwpDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgIGNsZWFySW1tZWRpYXRlKHRoaXMuX2ludGVybmFsLnVuY2F1Z2h0KTsNCiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX2ludGVybmFsLnVuY2F1Z2h0ID0gbnVsbDsNCiAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICB0aGlzLl91cCA9IHZhbHVlOw0KICAgICAgICAgICAgfQ0KICAgICAgICB9KTsNCiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkodGhpcywgImRlc2NyaXB0b3JNZXRhZGF0YSIsDQogICAgICAgIHsNCiAgICAgICAgICAgIGdldDogZnVuY3Rpb24gKCkNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICByZXR1cm4gKHJlcXVpcmUoJ2V2ZW50cycpLmdldFByb3BlcnR5LmNhbGwodGhpcy5faW50ZXJuYWwsICc/X0ZpbmFsaXplckRlYnVnTWVzc2FnZScpKTsNCiAgICAgICAgICAgIH0sDQogICAgICAgICAgICBzZXQ6IGZ1bmN0aW9uICh2YWx1ZSkNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICByZXF1aXJlKCdldmVudHMnKS5zZXRQcm9wZXJ0eS5jYWxsKHRoaXMuX2ludGVybmFsLCAnP19GaW5hbGl6ZXJEZWJ1Z01lc3NhZ2UnLCB2YWx1ZSk7DQogICAgICAgICAgICB9DQogICAgICAgIH0pOw0KICAgIHRoaXMuX2ludGVybmFsLm9uKCd+JywgZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHRoaXMuY29tcGxldGVkQXJncyA9IFtdOw0KICAgIH0pOw0KICAgIHRoaXMuX2ludGVybmFsLm9uKCduZXdMaXN0ZW5lcjInLCAoZnVuY3Rpb24gKGV2ZW50TmFtZSwgZXZlbnRDYWxsYmFjaykNCiAgICB7DQogICAgICAgIC8vY29uc29sZS5sb2coJ25ld0xpc3RlbmVyJywgZXZlbnROYW1lLCAnZXJyb3JzLycgKyB0aGlzLmVycm9ycyArICcgY29tcGxldGVkLycgKyB0aGlzLmNvbXBsZXRlZCk7DQogICAgICAgIHZhciByID0gbnVsbDsNCg0KICAgICAgICBpZiAoZXZlbnROYW1lID09ICdyZXNvbHZlZCcgJiYgIXRoaXMuZXJyb3JzICYmIHRoaXMuY29tcGxldGVkKQ0KICAgICAgICB7DQogICAgICAgICAgICByID0gZXZlbnRDYWxsYmFjay5hcHBseSh0aGlzLCB0aGlzLmNvbXBsZXRlZEFyZ3MpOw0KICAgICAgICAgICAgaWYociE9bnVsbCkNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICB0aGlzLmVtaXRfcmV0dXJuVmFsdWUoJ3Jlc29sdmVkJywgcik7DQogICAgICAgICAgICB9DQogICAgICAgICAgICB0cnkgeyB0aGlzLnJlbW92ZUFsbExpc3RlbmVycygncmVzb2x2ZWQnKTsgfSBjYXRjaCAoeCkgeyB9DQogICAgICAgICAgICB0cnkgeyB0aGlzLnJlbW92ZUFsbExpc3RlbmVycygncmVqZWN0ZWQnKTsgfSBjYXRjaCAoeCkgeyB9DQogICAgICAgIH0NCg0KICAgICAgICAvL2lmIChldmVudE5hbWUgPT0gJ3JlamVjdGVkJyAmJiAoZXZlbnRDYWxsYmFjay5pbnRlcm5hbCA9PSBudWxsIHx8IGV2ZW50Q2FsbGJhY2suaW50ZXJuYWwgPT0gZmFsc2UpKQ0KICAgICAgICBpZiAoZXZlbnROYW1lID09ICdyZWplY3RlZCcpDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmICh0aGlzLnVuY2F1Z2h0ICE9IG51bGwpDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgY2xlYXJJbW1lZGlhdGUodGhpcy51bmNhdWdodCk7DQogICAgICAgICAgICAgICAgdGhpcy51bmNhdWdodCA9IG51bGw7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBpZiAodGhpcy5wcm9taXNlKQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIHZhciBycCA9IGdldFJvb3RQcm9taXNlKHRoaXMucHJvbWlzZSk7DQogICAgICAgICAgICAgICAgcnAuX2ludGVybmFsLmV4dGVybmFsID0gdHJ1ZTsNCiAgICAgICAgICAgICAgICBpZiAocnAuX2ludGVybmFsLnVuY2F1Z2h0ICE9IG51bGwpDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICBjbGVhckltbWVkaWF0ZShycC5faW50ZXJuYWwudW5jYXVnaHQpOw0KICAgICAgICAgICAgICAgICAgICBycC5faW50ZXJuYWwudW5jYXVnaHQgPSBudWxsOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgfQ0KDQogICAgICAgIGlmIChldmVudE5hbWUgPT0gJ3JlamVjdGVkJyAmJiB0aGlzLmVycm9ycyAmJiB0aGlzLmNvbXBsZXRlZCkNCiAgICAgICAgew0KICAgICAgICAgICAgZXZlbnRDYWxsYmFjay5hcHBseSh0aGlzLCB0aGlzLmNvbXBsZXRlZEFyZ3MpOw0KICAgICAgICAgICAgdHJ5IHsgdGhpcy5yZW1vdmVBbGxMaXN0ZW5lcnMoJ3Jlc29sdmVkJyk7IH0gY2F0Y2ggKHgpIHsgfQ0KICAgICAgICAgICAgdHJ5IHsgdGhpcy5yZW1vdmVBbGxMaXN0ZW5lcnMoJ3JlamVjdGVkJyk7IH0gY2F0Y2ggKHgpIHsgfQ0KICAgICAgICB9DQogICAgICAgIGlmIChldmVudE5hbWUgPT0gJ3NldHRsZWQnICYmIHRoaXMuY29tcGxldGVkKQ0KICAgICAgICB7DQogICAgICAgICAgICBldmVudENhbGxiYWNrLmFwcGx5KHRoaXMsIFtdKTsNCiAgICAgICAgfQ0KICAgIH0pLmludGVybmFsKTsNCiAgICB0aGlzLl9pbnRlcm5hbC5yZXNvbHZlciA9IGZ1bmN0aW9uIF9yZXNvbHZlcigpDQogICAgew0KICAgICAgICBpZiAodGhpcy5jb21wbGV0ZWQpIHsgcmV0dXJuOyB9DQogICAgICAgIHRoaXMuZXJyb3JzID0gZmFsc2U7DQogICAgICAgIHRoaXMuY29tcGxldGVkID0gdHJ1ZTsNCiAgICAgICAgdGhpcy5jb21wbGV0ZWRBcmdzID0gW107DQogICAgICAgIHZhciBhcmdzID0gWydyZXNvbHZlZCddOw0KICAgICAgICBpZiAodGhpcy5lbWl0X3JldHVyblZhbHVlICYmIHRoaXMuZW1pdF9yZXR1cm5WYWx1ZSgncmVzb2x2ZWQnKSAhPSBudWxsKQ0KICAgICAgICB7DQogICAgICAgICAgICB0aGlzLmNvbXBsZXRlZEFyZ3MucHVzaCh0aGlzLmVtaXRfcmV0dXJuVmFsdWUoJ3Jlc29sdmVkJykpOw0KICAgICAgICAgICAgYXJncy5wdXNoKHRoaXMuZW1pdF9yZXR1cm5WYWx1ZSgncmVzb2x2ZWQnKSk7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICBmb3IgKHZhciBhIGluIGFyZ3VtZW50cykNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICB0aGlzLmNvbXBsZXRlZEFyZ3MucHVzaChhcmd1bWVudHNbYV0pOw0KICAgICAgICAgICAgICAgIGFyZ3MucHVzaChhcmd1bWVudHNbYV0pOw0KICAgICAgICAgICAgfQ0KICAgICAgICB9DQogICAgICAgIGlmIChhcmdzLmxlbmd0aCA9PSAyICYmIGFyZ3NbMV0hPW51bGwgJiYgdHlwZW9mKGFyZ3NbMV0pID09ICdvYmplY3QnICYmIGFyZ3NbMV0uX09iamVjdElEID09ICdwcm9taXNlJykNCiAgICAgICAgew0KICAgICAgICAgICAgdmFyIHByID0gZ2V0Um9vdFByb21pc2UodGhpcy5wcm9taXNlKTsNCiAgICAgICAgICAgIGFyZ3NbMV0uX1hTTEYgPSB0aGlzOw0KICAgICAgICAgICAgYXJnc1sxXS50aGVuKHJldHVybl9yZXNvbHZlZCwgcmV0dXJuX3JlamVjdGVkKTsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgIHRoaXMuZW1pdC5hcHBseSh0aGlzLCBhcmdzKTsNCiAgICAgICAgICAgIHRoaXMuZW1pdCgnc2V0dGxlZCcpOw0KICAgICAgICB9DQogICAgfTsNCg0KICAgIHRoaXMuX2ludGVybmFsLnJlamVjdG9yID0gZnVuY3Rpb24gX3JlamVjdG9yKCkNCiAgICB7DQogICAgICAgIGlmICh0aGlzLmNvbXBsZXRlZCkgeyByZXR1cm47IH0NCiAgICAgICAgdGhpcy5lcnJvcnMgPSB0cnVlOw0KICAgICAgICB0aGlzLmNvbXBsZXRlZCA9IHRydWU7DQogICAgICAgIHRoaXMuY29tcGxldGVkQXJncyA9IFtdOw0KICAgICAgICB2YXIgYXJncyA9IFsncmVqZWN0ZWQnXTsNCiAgICAgICAgZm9yICh2YXIgYSBpbiBhcmd1bWVudHMpDQogICAgICAgIHsNCiAgICAgICAgICAgIHRoaXMuY29tcGxldGVkQXJncy5wdXNoKGFyZ3VtZW50c1thXSk7DQogICAgICAgICAgICBhcmdzLnB1c2goYXJndW1lbnRzW2FdKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHZhciByID0gZ2V0Um9vdFByb21pc2UodGhpcy5wcm9taXNlKTsNCiAgICAgICAgaWYgKChyLl9pbnRlcm5hbC5leHRlcm5hbCA9PSBudWxsIHx8IHIuX2ludGVybmFsLmV4dGVybmFsID09IGZhbHNlKSAmJiByLl9pbnRlcm5hbC51bmNhdWdodCA9PSBudWxsKQ0KICAgICAgICB7DQogICAgICAgICAgICByLl9pbnRlcm5hbC51bmNhdWdodCA9IHNldEltbWVkaWF0ZShlbWl0cmVqZWN0LCBhcmd1bWVudHNbMF0pOw0KICAgICAgICB9DQoNCiAgICAgICAgdGhpcy5lbWl0LmFwcGx5KHRoaXMsIGFyZ3MpOw0KICAgICAgICB0aGlzLmVtaXQoJ3NldHRsZWQnKTsNCiAgICB9Ow0KDQogICAgdGhpcy5jYXRjaCA9IGZ1bmN0aW9uKGZ1bmMpDQogICAgew0KICAgICAgICB2YXIgcnQgPSBnZXRSb290UHJvbWlzZSh0aGlzKTsNCiAgICAgICAgaWYgKHJ0Ll9pbnRlcm5hbC51bmNhdWdodCAhPSBudWxsKSB7IGNsZWFySW1tZWRpYXRlKHJ0Ll9pbnRlcm5hbC51bmNhdWdodCk7IH0NCiAgICAgICAgdGhpcy5faW50ZXJuYWwub25jZSgncmVqZWN0ZWQnLCBldmVudF9zd2l0Y2hlcih0aGlzLCBmdW5jKS5mdW5jLmludGVybmFsKTsNCiAgICB9DQogICAgdGhpcy5maW5hbGx5ID0gZnVuY3Rpb24gKGZ1bmMpDQogICAgew0KICAgICAgICB0aGlzLl9pbnRlcm5hbC5vbmNlKCdzZXR0bGVkJywgZXZlbnRfc3dpdGNoZXIodGhpcywgZnVuYykuZnVuYy5pbnRlcm5hbCk7DQogICAgfTsNCiAgICB0aGlzLnRoZW4gPSBmdW5jdGlvbiAocmVzb2x2ZWQsIHJlamVjdGVkKQ0KICAgIHsNCiAgICAgICAgaWYgKHJlc29sdmVkKQ0KICAgICAgICB7DQogICAgICAgICAgICB0aGlzLl9pbnRlcm5hbC5vbmNlKCdyZXNvbHZlZCcsIGV2ZW50X3N3aXRjaGVyKHRoaXMsIHJlc29sdmVkKS5mdW5jLmludGVybmFsKTsNCiAgICAgICAgfQ0KICAgICAgICBpZiAocmVqZWN0ZWQpDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmICh0aGlzLl9pbnRlcm5hbC5jb21wbGV0ZWQpDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgdmFyIHIgPSBnZXRSb290UHJvbWlzZSh0aGlzKTsNCiAgICAgICAgICAgICAgICBpZihyLl9pbnRlcm5hbC51bmNhdWdodCAhPSBudWxsKQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgY2xlYXJJbW1lZGlhdGUoci5faW50ZXJuYWwudW5jYXVnaHQpOw0KICAgICAgICAgICAgICAgIH0gICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgdGhpcy5faW50ZXJuYWwub25jZSgncmVqZWN0ZWQnLCBldmVudF9zd2l0Y2hlcih0aGlzLCByZWplY3RlZCkuZnVuYy5pbnRlcm5hbCk7DQogICAgICAgIH0NCiAgICAgICAgICANCiAgICAgICAgdmFyIHJldFZhbCA9IG5ldyBQcm9taXNlKHByb21pc2VJbml0aWFsaXplcik7DQogICAgICAgIHJldFZhbC5wYXJlbnRQcm9taXNlID0gdGhpczsNCg0KICAgICAgICBpZiAodGhpcy5faW50ZXJuYWwuY29tcGxldGVkKQ0KICAgICAgICB7DQogICAgICAgICAgICAvLyBUaGlzIHByb21pc2Ugd2FzIGFscmVhZHkgcmVzb2x2ZWQsIHNvIGxldHMgY2hlY2sgaWYgdGhlIGhhbmRsZXIgcmV0dXJuZWQgYSBwcm9taXNlDQogICAgICAgICAgICB2YXIgcnYgPSB0aGlzLl9pbnRlcm5hbC5lbWl0X3JldHVyblZhbHVlKCdyZXNvbHZlZCcpOw0KICAgICAgICAgICAgaWYocnYhPW51bGwpDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYocnYuX09iamVjdElEID09ICdwcm9taXNlJykNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgIHJ2LnBhcmVudFByb21pc2UgPSB0aGlzOw0KICAgICAgICAgICAgICAgICAgICBydi5faW50ZXJuYWwub25jZSgncmVzb2x2ZWQnLCByZXRWYWwuX2ludGVybmFsLnJlc29sdmVyLmJpbmQocmV0VmFsLl9pbnRlcm5hbCkuaW50ZXJuYWwpOw0KICAgICAgICAgICAgICAgICAgICBydi5faW50ZXJuYWwub25jZSgncmVqZWN0ZWQnLCByZXRWYWwuX2ludGVybmFsLnJlamVjdG9yLmJpbmQocmV0VmFsLl9pbnRlcm5hbCkuaW50ZXJuYWwpOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICByZXRWYWwuX2ludGVybmFsLnJlc29sdmVyLmNhbGwocmV0VmFsLl9pbnRlcm5hbCwgcnYpOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICB0aGlzLl9pbnRlcm5hbC5vbmNlKCdyZXNvbHZlZCcsIHJldFZhbC5faW50ZXJuYWwucmVzb2x2ZXIuYmluZChyZXRWYWwuX2ludGVybmFsKS5pbnRlcm5hbCk7DQogICAgICAgICAgICAgICAgdGhpcy5faW50ZXJuYWwub25jZSgncmVqZWN0ZWQnLCByZXRWYWwuX2ludGVybmFsLnJlamVjdG9yLmJpbmQocmV0VmFsLl9pbnRlcm5hbCkuaW50ZXJuYWwpOw0KICAgICAgICAgICAgfQ0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgdGhpcy5faW50ZXJuYWwub25jZSgncmVzb2x2ZWQnLCByZXRWYWwuX2ludGVybmFsLnJlc29sdmVyLmJpbmQocmV0VmFsLl9pbnRlcm5hbCkuaW50ZXJuYWwpOw0KICAgICAgICAgICAgdGhpcy5faW50ZXJuYWwub25jZSgncmVqZWN0ZWQnLCByZXRWYWwuX2ludGVybmFsLnJlamVjdG9yLmJpbmQocmV0VmFsLl9pbnRlcm5hbCkuaW50ZXJuYWwpOw0KICAgICAgICB9DQoNCiAgICAgICAgdGhpcy5fX2NoaWxkUHJvbWlzZSA9IHJldFZhbDsNCiAgICAgICAgcmV0dXJuKHJldFZhbCk7DQogICAgfTsNCg0KICAgIHRyeQ0KICAgIHsNCiAgICAgICAgcHJvbWlzZUZ1bmMuY2FsbCh0aGlzLCB0aGlzLl9pbnRlcm5hbC5yZXNvbHZlci5iaW5kKHRoaXMuX2ludGVybmFsKSwgdGhpcy5faW50ZXJuYWwucmVqZWN0b3IuYmluZCh0aGlzLl9pbnRlcm5hbCkpOw0KICAgIH0NCiAgICBjYXRjaCAoZSkNCiAgICB7DQogICAgICAgIHRoaXMuX2ludGVybmFsLmVycm9ycyA9IHRydWU7DQogICAgICAgIHRoaXMuX2ludGVybmFsLmNvbXBsZXRlZCA9IHRydWU7DQogICAgICAgIHRoaXMuX2ludGVybmFsLmNvbXBsZXRlZEFyZ3MgPSBbZV07DQogICAgICAgIHRoaXMuX2ludGVybmFsLmVtaXQoJ3JlamVjdGVkJywgZSk7DQogICAgICAgIHRoaXMuX2ludGVybmFsLmVtaXQoJ3NldHRsZWQnKTsNCiAgICB9DQoNCiAgICBpZighdGhpcy5faW50ZXJuYWwuY29tcGxldGVkKQ0KICAgIHsNCiAgICAgICAgLy8gU2F2ZSByZWZlcmVuY2Ugb2YgdGhpcyBvYmplY3QNCiAgICAgICAgcmVmVGFibGVbdGhpcy5faW50ZXJuYWwuX2hhc2hDb2RlKCldID0gdGhpcy5faW50ZXJuYWw7DQogICAgICAgIHRoaXMuX2ludGVybmFsLm9uY2UoJ3NldHRsZWQnLCBmdW5jdGlvbiAoKQ0KICAgICAgICB7DQogICAgICAgICAgICBkZWxldGUgcmVmVGFibGVbdGhpcy5faGFzaENvZGUoKV07DQogICAgICAgIH0pOw0KICAgIH0NCiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkodGhpcywgImNvbXBsZXRlZCIsIHsNCiAgICAgICAgZ2V0OiBmdW5jdGlvbiAoKQ0KICAgICAgICB7DQogICAgICAgICAgICByZXR1cm4gKHRoaXMuX2ludGVybmFsLmNvbXBsZXRlZCk7DQogICAgICAgIH0NCiAgICB9KTsNCg0KICAgIHRoaXMuX2ludGVybmFsLm9uY2UoJ3NldHRsZWQnLCAoZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIGlmICh0aGlzLnVuY2F1Z2h0ICE9IG51bGwpDQogICAgICAgIHsNCiAgICAgICAgICAgIGNsZWFySW1tZWRpYXRlKHRoaXMudW5jYXVnaHQpOw0KICAgICAgICAgICAgdGhpcy51bmNhdWdodCA9IG51bGw7DQogICAgICAgIH0NCg0KICAgICAgICB2YXIgcnAgPSBnZXRSb290UHJvbWlzZSh0aGlzLnByb21pc2UpOw0KICAgICAgICBpZiAocnAgJiYgcnAuX2ludGVybmFsLnVuY2F1Z2h0KQ0KICAgICAgICB7DQogICAgICAgICAgICBjbGVhckltbWVkaWF0ZShycC5faW50ZXJuYWwudW5jYXVnaHQpOw0KICAgICAgICAgICAgcnAuX2ludGVybmFsLnVuY2F1Z2h0ID0gbnVsbDsNCiAgICAgICAgfQ0KDQogICAgICAgIGRlbGV0ZSB0aGlzLnByb21pc2UuX3VwOw0KICAgICAgICBkZWxldGUgdGhpcy5wcm9taXNlLl9fY2hpbGRQcm9taXNlOw0KICAgICAgICBkZWxldGUgdGhpcy5wcm9taXNlLnByb21pc2U7DQoNCiAgICAgICAgZGVsZXRlIHRoaXMuX3VwOw0KICAgICAgICBkZWxldGUgdGhpcy5fX2NoaWxkUHJvbWlzZTsNCiAgICAgICAgZGVsZXRlIHRoaXMucHJvbWlzZTsNCiAgICAgICAgdHJ5IHsgdGhpcy5yZW1vdmVBbGxMaXN0ZW5lcnMoJ3Jlc29sdmVkJyk7IH0gY2F0Y2ggKHgpIHsgfQ0KICAgICAgICB0cnkgeyB0aGlzLnJlbW92ZUFsbExpc3RlbmVycygncmVqZWN0ZWQnKTsgfSBjYXRjaCAoeCkgeyB9DQogICAgfSkuaW50ZXJuYWwpOw0KfQ0KDQpQcm9taXNlLnJlc29sdmUgPSBmdW5jdGlvbiByZXNvbHZlKCkNCnsNCiAgICB2YXIgcmV0VmFsID0gbmV3IFByb21pc2UoZnVuY3Rpb24gKHIsIGopIHsgfSk7DQogICAgdmFyIGFyZ3MgPSBbXTsNCiAgICBmb3IgKHZhciBpIGluIGFyZ3VtZW50cykNCiAgICB7DQogICAgICAgIGFyZ3MucHVzaChhcmd1bWVudHNbaV0pOw0KICAgIH0NCiAgICByZXRWYWwuX2ludGVybmFsLnJlc29sdmVyLmFwcGx5KHJldFZhbC5faW50ZXJuYWwsIGFyZ3MpOw0KICAgIHJldHVybiAocmV0VmFsKTsNCn07DQpQcm9taXNlLnJlamVjdCA9IGZ1bmN0aW9uIHJlamVjdCgpIHsNCiAgICB2YXIgcmV0VmFsID0gbmV3IFByb21pc2UoZnVuY3Rpb24gKHIsIGopIHsgfSk7DQogICAgdmFyIGFyZ3MgPSBbXTsNCiAgICBmb3IgKHZhciBpIGluIGFyZ3VtZW50cykgew0KICAgICAgICBhcmdzLnB1c2goYXJndW1lbnRzW2ldKTsNCiAgICB9DQogICAgcmV0VmFsLl9pbnRlcm5hbC5yZWplY3Rvci5hcHBseShyZXRWYWwuX2ludGVybmFsLCBhcmdzKTsNCiAgICByZXR1cm4gKHJldFZhbCk7DQp9Ow0KUHJvbWlzZS5hbGwgPSBmdW5jdGlvbiBhbGwocHJvbWlzZUxpc3QpDQp7DQogICAgdmFyIHJldCA9IG5ldyBQcm9taXNlKGZ1bmN0aW9uIChyZXMsIHJlaikNCiAgICB7DQogICAgICAgIHRoaXMuX19yZWplY3RvciA9IHJlajsNCiAgICAgICAgdGhpcy5fX3Jlc29sdmVyID0gcmVzOw0KICAgICAgICB0aGlzLl9fcHJvbWlzZUxpc3QgPSBwcm9taXNlTGlzdDsNCiAgICAgICAgdGhpcy5fX2RvbmUgPSBmYWxzZTsNCiAgICAgICAgdGhpcy5fX2NvdW50ID0gMDsNCiAgICB9KTsNCg0KICAgIGZvciAodmFyIGkgaW4gcHJvbWlzZUxpc3QpDQogICAgew0KICAgICAgICBwcm9taXNlTGlzdFtpXS50aGVuKGZ1bmN0aW9uICgpDQogICAgICAgIHsNCiAgICAgICAgICAgIC8vIFN1Y2Nlc3MNCiAgICAgICAgICAgIGlmKCsrcmV0Ll9fY291bnQgPT0gcmV0Ll9fcHJvbWlzZUxpc3QubGVuZ3RoKQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIHJldC5fX2RvbmUgPSB0cnVlOw0KICAgICAgICAgICAgICAgIHJldC5fX3Jlc29sdmVyKHJldC5fX3Byb21pc2VMaXN0KTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgfSwgZnVuY3Rpb24gKGFyZykNCiAgICAgICAgew0KICAgICAgICAgICAgLy8gRmFpbHVyZQ0KICAgICAgICAgICAgaWYoIXJldC5fX2RvbmUpDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgcmV0Ll9fZG9uZSA9IHRydWU7DQogICAgICAgICAgICAgICAgcmV0Ll9fcmVqZWN0b3IoYXJnKTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgfSk7DQogICAgfQ0KICAgIGlmIChwcm9taXNlTGlzdC5sZW5ndGggPT0gMCkNCiAgICB7DQogICAgICAgIHJldC5fX3Jlc29sdmVyKHByb21pc2VMaXN0KTsNCiAgICB9DQogICAgcmV0dXJuIChyZXQpOw0KfTsNCg0KbW9kdWxlLmV4cG9ydHMgPSBQcm9taXNlOw0KbW9kdWxlLmV4cG9ydHMuZXZlbnRfc3dpdGNoZXIgPSBldmVudF9zd2l0Y2hlcjsNCm1vZHVsZS5leHBvcnRzLmV2ZW50X2ZvcndhcmRlciA9IGV2ZW50X2ZvcndhcmRlcjsNCm1vZHVsZS5leHBvcnRzLmRlZmF1bHRJbml0ID0gZnVuY3Rpb24gZGVmYXVsdEluaXQocmVzLCByZWopIHsgdGhpcy5yZXNvbHZlID0gcmVzOyB0aGlzLnJlamVjdCA9IHJlajsgfQ==', 'base64').toString());
39
+var promise = require('promise2');
40
+function promise_default(res, rej)
41
+{
42
+ this._res = res;
43
+ this._rej = rej;
44
+}
45
+
46
+
47
+function buf2addr(buf)
48
+{
49
+ return (buf[0] + '.' + buf[1] + '.' + buf[2] + '.' + buf[3]);
50
+}
51
+function parseDHCP(buffer)
52
+{
53
+ var i;
54
+ var packet = Buffer.alloc(buffer.length);
55
+ for (i = 0; i < buffer.length; ++i) { packet[i] = buffer[i]; }
56
+
57
+ var ret = { op: packet[0] == 0 ? 'REQ' : 'RES', hlen: packet[2] }; // OP Code
58
+ ret.xid = packet.readUInt32BE(4); // Transaction ID
59
+ ret.ciaddr = buf2addr(packet.slice(12, 16));
60
+ ret.yiaddr = buf2addr(packet.slice(16, 20));
61
+ ret.siaddr = buf2addr(packet.slice(20, 24));
62
+ ret.giaddr = buf2addr(packet.slice(24, 28));
63
+ ret.chaddr = packet.slice(28, 28 + ret.hlen).toString('hex:');
64
+ if (packet[236] == 99 && packet[237] == 130 && packet[238] == 83 && packet[239] == 99)
65
+ {
66
+ // Magic Cookie Validated
67
+ ret.magic = true;
68
+ ret.options = {};
69
+
70
+ i = 240;
71
+ while(i<packet.length)
72
+ {
73
+ switch(packet[i])
74
+ {
75
+ case 0:
76
+ i += 1;
77
+ break;
78
+ case 255:
79
+ ret.options[255] = true;
80
+ i += 2;
81
+ break;
82
+ default:
83
+ ret.options[packet[i]] = packet.slice(i + 2, i + 2 + packet[i + 1]);
84
+ switch(packet[i])
85
+ {
86
+ case 1: // Subnet Mask
87
+ ret.options.subnetmask = buf2addr(ret.options[1]);
88
+ delete ret.options[1];
89
+ break;
90
+ case 3: // Router
91
+ ret.options.router = [];
92
+ var ti = 0;
93
+ while (ti < ret.options[3].length)
94
+ {
95
+ ret.options.router.push(buf2addr(ret.options[3].slice(ti, ti + 4)));
96
+ ti += 4;
97
+ }
98
+ delete ret.options[3];
99
+ break;
100
+ case 6: // DNS
101
+ ret.options.dns = buf2addr(ret.options[6]);
102
+ delete ret.options[6];
103
+ break;
104
+ case 15: // Domain Name
105
+ ret.options.domainname = ret.options[15].toString();
106
+ delete ret.options[15];
107
+ break;
108
+ case 28: // Broadcast Address
109
+ ret.options.broadcastaddr = buf2addr(ret.options[28]);
110
+ delete ret.options[28];
111
+ break;
112
+ case 51: // Lease Time
113
+ ret.options.lease = { raw: ret.options[51].readInt32BE() };
114
+ delete ret.options[51];
115
+ ret.options.lease.hours = Math.floor(ret.options.lease.raw / 3600);
116
+ ret.options.lease.minutes = Math.floor((ret.options.lease.raw % 3600) / 60);
117
+ ret.options.lease.seconds = (ret.options.lease.raw % 3600) % 60;
118
+ break;
119
+ case 53: // Message Type
120
+ ret.options.messageType = ret.options[53][0];
121
+ delete ret.options[53];
122
+ break;
123
+ case 54: // Server
124
+ ret.options.server = buf2addr(ret.options[54]);
125
+ delete ret.options[54];
126
+ break;
127
+ }
128
+ i += (2 + packet[i + 1]);
129
+ break;
130
+ }
131
+ }
132
+ }
133
+
134
+
135
+ return (ret);
136
+}
137
+
138
+function createPacket(messageType, data)
139
+{
140
+ var b = Buffer.alloc(245);
141
+
142
+ switch(messageType)
143
+ {
144
+ //case 0x02:
145
+ //case 0x04:
146
+ //case 0x05:
147
+ //case 0x06:
148
+ // b[0] = 0x00; // Reply
149
+ // break;
150
+ //case 0x01:
151
+ //case 0x03:
152
+ //case 0x07:
153
+ case 0x08:
154
+ b[0] = 0x01; // Request
155
+ break;
156
+ default:
157
+ throw ('DHCP(' + messageType + ') NOT SUPPORTED');
158
+ break;
159
+ }
160
+
161
+ // Headers
162
+ b[1] = 0x01; // Ethernet
163
+ b[2] = 0x06; // HW Address Length
164
+ b[3] = 0x00; // HOPS
165
+
166
+ // Transaction ID
167
+ var r = Buffer.alloc(4); r.randomFill();
168
+ b.writeUInt32BE(r.readUInt32BE(), 4);
169
+ b.writeUInt16BE(0x8000, 10);
170
+
171
+ // Magic Cookie
172
+ b[236] = 99;
173
+ b[237] = 130;
174
+ b[238] = 83;
175
+ b[239] = 99;
176
+
177
+ // DHCP Message Type
178
+ b[240] = 53;
179
+ b[241] = 1;
180
+ b[242] = messageType;
181
+ b[243] = 255;
182
+
183
+ switch(messageType)
184
+ {
185
+ case 0x08:
186
+ if (data.ciaddress == null) { throw ('ciadress missing'); }
187
+ if (data.chaddress == null) { throw ('chaddress missing'); }
188
+
189
+ // ciaddress
190
+ var a = data.ciaddress.split('.');
191
+ var ci = parseInt(a[0]);
192
+ ci = ci << 8;
193
+ ci = ci | parseInt(a[1]);
194
+ ci = ci << 8;
195
+ ci = ci | parseInt(a[2]);
196
+ ci = ci << 8;
197
+ ci = ci | parseInt(a[3]);
198
+ b.writeInt32BE(ci, 12);
199
+
200
+ // chaddress
201
+ var y = data.chaddress.split(':').join('');
202
+ y = Buffer.from(y, 'hex');
203
+ y.copy(b, 28);
204
+
205
+ break;
206
+ }
207
+
208
+ return (b);
209
+}
210
+
211
+function raw(localAddress, port, buffer, handler)
212
+{
213
+ var ret = new promise(promise_default);
214
+ ret.socket = require('dgram').createSocket({ type: 'udp4' });
215
+ try
216
+ {
217
+ ret.socket.bind({ address: localAddress, port: (port != null && port != 0) ? port : null });
218
+ }
219
+ catch (e)
220
+ {
221
+ ret._rej('Unable to bind to ' + localAddress);
222
+ return (ret);
223
+ }
224
+
225
+ ret.socket.setBroadcast(true);
226
+ ret.socket.setMulticastInterface(localAddress);
227
+ ret.socket.setMulticastTTL(1);
228
+ ret.socket.descriptorMetadata = 'DHCP (' + localAddress + ')';
229
+ ret.socket.on('message', handler.bind(ret));
230
+ ret.socket.send(buffer, 67, '255.255.255.255');
231
+ return (ret);
232
+}
233
+
234
+function info(interfaceName, port)
235
+{
236
+ var f = require('os').networkInterfaces();
237
+ if (interfaceName.split(':').length == 6)
238
+ {
239
+ var newname = null;
240
+ for(var n in f)
241
+ {
242
+ for (var nx in f[n])
243
+ {
244
+ if(f[n][nx].mac.toUpperCase() == interfaceName.toUpperCase())
245
+ {
246
+ newname = n;
247
+ break;
248
+ }
249
+ }
250
+ if(newname)
251
+ {
252
+ interfaceName = newname;
253
+ break;
254
+ }
255
+ }
256
+ }
257
+
258
+
259
+ if (f[interfaceName] != null)
260
+ {
261
+ var i;
262
+ for(i=0;i<f[interfaceName].length;++i)
263
+ {
264
+ if(f[interfaceName][i].family == 'IPv4' && f[interfaceName][i].mac != '00:00:00:00:00:00')
265
+ {
266
+ try
267
+ {
268
+ var b = createPacket(8, { ciaddress: f[interfaceName][i].address, chaddress: f[interfaceName][i].mac });
269
+ _hide(raw(f[interfaceName][i].address, port, b, function infoHandler(msg)
270
+ {
271
+ try
272
+ {
273
+ var res = parseDHCP(msg);
274
+ if (res.chaddr.toUpperCase() == this.hwaddr.toUpperCase() && res.options != null && res.options.lease != null)
275
+ {
276
+ clearTimeout(this.timeout);
277
+ setImmediate(function (s) { try { s.removeAllListeners('message'); } catch (x) { } }, this.socket); // Works around bug in older dgram.js
278
+ this._res(res);
279
+ }
280
+ }
281
+ catch(z)
282
+ {
283
+ }
284
+ }));
285
+ _hide().hwaddr = f[interfaceName][i].mac;
286
+ _hide().timeout = setTimeout(function (x)
287
+ {
288
+ x.socket.removeAllListeners('message');
289
+ x._rej('timeout');
290
+ }, 2000, _hide());
291
+ return (_hide(true));
292
+ }
293
+ catch(e)
294
+ {
295
+ var ret = new promise(promise_default);
296
+ ret._rej(e);
297
+ return (ret);
298
+ }
299
+ }
300
+ }
301
+ }
302
+
303
+ var ret = new promise(promise_default);
304
+ ret._rej('interface (' + interfaceName + ') not found');
305
+ return (ret);
306
+}
307
+
308
+module.exports =
309
+ {
310
+ client: { info: info, raw: raw },
311
+ MESSAGE_TYPES:
312
+ {
313
+ DISCOVER: 1,
314
+ OFFER: 2,
315
+ REQUEST: 3,
316
+ DECLINE: 4,
317
+ ACK: 5,
318
+ NACK: 6,
319
+ RELEASE: 7,
320
+ INFO: 8
321
+ }
322
+ };
323
+