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 +