Added public IP location information to the mesh agent

Ylian Saint-Hilaire committed Aug 28, 2017 at 12:48 UTC 888e8dedefc26919dc0df237c090b24f10712f47
4 files changed +122 -32
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
agents/meshcore.js
+116 -32
@@ -24,6 +24,7 @@ function createMeshCore(agent) {
24 var tunnels = {};
25 var lastSelfInfo = null;
26 var lastNetworkInfo = null;
27 + var lastPublicLocationInfo = null;
28 var selfInfoUpdateTimer = null;
29
30 var http = require('http');
@@ -44,6 +45,67 @@ function createMeshCore(agent) {
45 var mesh = agent.getMeshApi();
46 }
47
48 + // Get our location (lat/long) using our public IP address
49 + function getIpLocationDataEx(func) {
50 + try {
51 + http.request({
52 + host: 'ipinfo.io', // TODO: Use a HTTP proxy if needed!!!!
53 + port: 80,
54 + path: 'http://ipinfo.io/json', // Use this service to get our geolocation
55 + headers: { Host: "ipinfo.io" }
56 + },
57 + function (resp) {
58 + var geoData = '';
59 + resp.data = function (geoipdata) { geoData += geoipdata; };
60 + resp.end = function () {
61 + var location = null;
62 + try { if (typeof geoData == 'string') { var result = JSON.parse(geoData); if (result.ip && result.loc) { location = result; } } } catch (e) { }
63 + if (func) { func(location); }
64 + }
65 + }).end();
66 + }
67 + catch (e) { }
68 + }
69 +
70 + // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
71 + function clearGatewayMac(str) {
72 + if (str == null) return null;
73 + var x = JSON.parse(str);
74 + for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
75 + return JSON.stringify(x);
76 + }
77 +
78 + function getIpLocationData(func) {
79 + // Get the location information for the cache if possible
80 + var publicLocationInfo = db.Get('publicLocationInfo');
81 + if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
82 + if (publicLocationInfo == null) {
83 + // Nothing in the cache, fetch the data
84 + getIpLocationDataEx(function (locationData) {
85 + publicLocationInfo = {};
86 + publicLocationInfo.netInfoStr = lastNetworkInfo;
87 + publicLocationInfo.locationData = locationData;
88 + var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
89 + if (func) func(locationData);
90 + });
91 + } else {
92 + // Check the cache
93 + if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) {
94 + // Cache match
95 + if (func) func(publicLocationInfo.locationData);
96 + } else {
97 + // Cache mismatch
98 + getIpLocationDataEx(function (locationData) {
99 + publicLocationInfo = {};
100 + publicLocationInfo.netInfoStr = lastNetworkInfo;
101 + publicLocationInfo.locationData = locationData;
102 + var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
103 + if (func) func(locationData);
104 + });
105 + }
106 + }
107 + }
108 +
109 // Polyfill String.endsWith
110 if (!String.prototype.endsWith) {
111 String.prototype.endsWith = function (searchString, position) {
@@ -149,38 +211,47 @@ function createMeshCore(agent) {
211 function handleServerCommand(data) {
212 if (typeof data == 'object') {
213 // If this is a console command, parse it and call the console handler
152 - if (data.action == 'msg') {
153 - if (data.type == 'console') { // Process a console command
154 - if (data.value && data.sessionid) {
155 - var args = splitArgs(data.value);
156 - processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
214 + switch (data.action) {
215 + case 'msg': {
216 + if (data.type == 'console') { // Process a console command
217 + if (data.value && data.sessionid) {
218 + var args = splitArgs(data.value);
219 + processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
220 + }
221 }
158 - }
159 - else if (data.type == 'tunnel') { // Process a new tunnel connection request
160 - if (data.value && data.sessionid) {
161 - // Create a new tunnel object
162 - var tunnel = http.request(parseUrl(data.value));
163 - tunnel.upgrade = onTunnelUpgrade;
164 - tunnel.sessionid = data.sessionid;
165 - tunnel.rights = data.rights;
166 - tunnel.state = 0;
167 - tunnel.url = data.value;
168 - tunnel.protocol = 0;
169 -
170 - // Put the tunnel in the tunnels list
171 - var index = 1;
172 - while (tunnels[index]) { index++; }
173 - tunnel.index = index;
174 - tunnels[index] = tunnel;
175 -
176 - sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
222 + else if (data.type == 'tunnel') { // Process a new tunnel connection request
223 + if (data.value && data.sessionid) {
224 + // Create a new tunnel object
225 + var tunnel = http.request(parseUrl(data.value));
226 + tunnel.upgrade = onTunnelUpgrade;
227 + tunnel.sessionid = data.sessionid;
228 + tunnel.rights = data.rights;
229 + tunnel.state = 0;
230 + tunnel.url = data.value;
231 + tunnel.protocol = 0;
232 +
233 + // Put the tunnel in the tunnels list
234 + var index = 1;
235 + while (tunnels[index]) { index++; }
236 + tunnel.index = index;
237 + tunnels[index] = tunnel;
238 +
239 + sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
240 + }
241 }
242 + break;
243 + }
244 + case 'wakeonlan': {
245 + // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
246 + sendConsoleText('Server requesting wake-on-lan for: ' + data.macs.join(', '));
247 + // TODO!!!!
248 + break;
249 + }
250 + case 'location': {
251 + // Update the location information of this node
252 + getIpLocationData(function (location) { mesh.SendCommand({ "action": "location", "type": "publicip", "value": location }); });
253 + break;
254 }
179 - }
180 - else if (data.action == 'wakeonlan') {
181 - // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
182 - sendConsoleText('Server requesting wake-on-lan for: ' + data.macs.join(', '));
183 - // TODO!!!!
255 }
256 }
257 }
@@ -449,7 +520,7 @@ function createMeshCore(agent) {
520 var response = null;
521 switch (cmd) {
522 case 'help': { // Displays available commands
452 - response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, parseurl, httpget, wsconnect, wssend, wsclose, notify, ls, amt, netinfo.';
523 + response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, parseurl, httpget, wsconnect, wssend, wsclose, notify, ls, amt, netinfo, location.';
524 break;
525 }
526 case 'notify': { // Send a notification message to the mesh
@@ -656,6 +727,10 @@ function createMeshCore(agent) {
727 sendConsoleText(args['_'].join(' '));
728 break;
729 }
730 + case 'location': {
731 + getIpLocationData(function (location) { sendConsoleText("Public IP location:\r\n" + objToString(location, 0, '.'), sessionid); }, args['_'][0]);
732 + break;
733 + }
734 default: { // This is an unknown command, return an error message
735 response = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
736 break;
@@ -666,7 +741,10 @@ function createMeshCore(agent) {
741 }
742
743 // Send a mesh agent console command
669 - function sendConsoleText(text, sessionid) { mesh.SendCommand({ "action": "msg", "type": "console", "value": text, "sessionid": sessionid }); }
744 + function sendConsoleText(text, sessionid) {
745 + if (typeof text == 'object') { text = JSON.stringify(text); }
746 + mesh.SendCommand({ "action": "msg", "type": "console", "value": text, "sessionid": sessionid });
747 + }
748
749 // Called before the process exits
750 //process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
@@ -712,7 +790,13 @@ function createMeshCore(agent) {
790 var netInfo = mesh.NetInfo;
791 netInfo.action = 'netinfo';
792 var netInfoStr = JSON.stringify(netInfo);
715 - if ((force == true) || (netInfoStr != lastNetworkInfo)) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
793 + if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
794 +
795 + // Update public IP location information, location caching will be used
796 + getIpLocationData(function (location) {
797 + var locationStr = JSON.stringify(location);
798 + if ((force == true) || (locationStr != lastPublicLocationInfo)) { mesh.SendCommand({ "action": "location", "type": "publicip", "value": location }); lastPublicLocationInfo = locationStr; }
799 + });
800 }
801
802 // Called on MicroLMS Intel AMT user notification
meshagent.js
+6
@@ -388,6 +388,12 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
388 // Event the node interface information change
389 obj.parent.parent.DispatchEvent(['*', obj.meshid], obj, { action: 'ifchange', nodeid: obj.dbNodeKey, domain: domain.id, nolog: 1 });
390
391 + break;
392 + }
393 + case 'location':
394 + {
395 + // Sent by the agent to update location information
396 + console.log(JSON.stringify(command));
397 break;
398 }
399 }