function parseMaltegoClientsData(xml) { var data = []; var entities = xml.documentElement.getElementsByTagName("Entity"); console.log(entities.length + ' entities') for(var i = 0; i < entities.length; i++) { var el = entities.item(i); var dataFieldMapping = { 'start_time': 'startTime', 'end_time': 'endTime', 'vendor': 'vendor', 'num_ssids': 'numSSIDs' } var obj = namedFieldsToObj(el.getElementsByTagName("Field"), dataFieldMapping); obj.startTime = obj.startTime ? maltegoParseDate(obj.startTime) : null; obj.endTime = obj.endTime ? maltegoParseDate(obj.endTime) : null; obj.duration = 0; if(obj.startTime) { obj.date = obj.startTime; obj.durationMs = obj.endTime ? obj.endTime.getTime() - obj.startTime.getTime() : NaN; delete obj.startTime; delete obj.endTime; if(obj.durationMs > 5 * 60 * 1000 || obj.durationMs === 0) // Ignore outliers obj.durationMs = NaN; data.push(obj); }else { console.log('Entity without a start_time Attribute found ', el) } } return data; } /** Calculate a rank for the best closest active client */ function closestActiveRank(client, stats) { var rank = (client.lastProbe.getTime() - stats.oldestLastProbe.getTime()) / (stats.newestLastProbe - stats.oldestLastProbe); if(client.lastProbe.getTime() < (new Date().getTime() - 5 * 60 * 1000)) rank = 0.1 * rank; // De-rank old data rank += 1.0 + (-(client.value - stats.minValue) / (stats.maxValue - stats.minValue)); // rank += 4.0 * (client.numSSIDs / stats.maxNumSSIDs); rank += 0.2 * (client.numSSIDs / stats.maxNumSSIDs); if(client.numSSIDs === 0) rank -= 0.2; // Penalty when having no probes with SSIDs, as there's not much to display then return rank; } function parseMaltegoClientsActiveData(xml) { var data = []; var entities = xml.documentElement.getElementsByTagName("Entity"); console.log(entities.length + ' active entities') for(var i = 0; i < entities.length; i++) { var el = entities.item(i); var dataFieldMapping = { 'mac': 'mac', 'hostname': 'hostname', 'last_probe': 'lastProbe', 'signal_db': 'signalDb', 'vendor': 'vendor', 'num_ssids': 'numSSIDs' }; var obj = namedFieldsToObj(el.getElementsByTagName("Field"), dataFieldMapping); obj.lastProbe = obj.lastProbe ? maltegoParseDate(obj.lastProbe) : null; obj.numSSIDs = Number.parseInt(obj.numSSIDs); // We need to remember the axis of the same client as observed previously // TODO re-activate once we actually pull up-to-date data from the backend // obj.axis = activeMacToAxis[obj.mac] = activeMacToAxis[obj.mac] || radarAxis[Math.round(Math.random() * 7)]; obj.axis = radarAxis[Math.round(Math.random() * 7)]; // random axis obj.value = -obj.signalDb; if(!obj.lastProbe) { console.log('Entity without a start_time Attribute found ', el) continue; } data.push(obj); } // Figure out the stats for the active clients activeClientStats = { oldestLastProbe: d3.min(data, function(d) { return d.lastProbe; }), newestLastProbe: d3.max(data, function(d) { return d.lastProbe; }), minValue: d3.min(data, function(d) { return d.value; }), maxValue: d3.max(data, function(d) { return d.value; }), maxNumSSIDs: d3.max(data, function(d) { return d.numSSIDs; }), }; data.sort(function (a,b) { return d3.descending(closestActiveRank(a, activeClientStats), closestActiveRank(b, activeClientStats)); }); closestActiveClient = data[0]; data = d3.nest().key(function(d) { var vendor = 'Other'; if(d.vendor === 'Apple') vendor = 'Apple'; else if(androidVendors.indexOf(d.vendor) !== -1) vendor = 'Android'; return vendor; }).entries(data); console.log(data.length + ' active clients found'); return data; } function parseMaltegoClosestActiveSSIDs(xml, chartObj) { var data = []; var entities = xml.documentElement.getElementsByTagName("Entity"); console.log(entities.length + ' ssid entities') for(var i = 0; i < entities.length; i++) { var els = entities.item(i).getElementsByTagName("Value"); if(els.length <= 0) continue; var name = decodeURIComponent(escape(els[0].textContent)); // Decode stuff like FREEWIFI Milja und Sch\xc3\xa4fa data.push({ id: els[0].textContent.hashCode(), name: name.length > activeClientMaxSSIDStrLen ? (name.substring(0, activeClientMaxSSIDStrLen - 3) + '...') : name, // Shorten, we have limited screen space fullName: name, }); } return data; } function maltegoTransformToXmlDOM(responseText) { // XXX The backend is a bit of a hack, does not have an xml header, fix that var xmlStr = '\n' + responseText; var parser = new DOMParser(); return parser.parseFromString(xmlStr, "application/xml"); } /** Filters out uninteresting SSIDs */ function uninterestingSsidsPartitionFilter(ssidObj) { var keep = true; for(var i = 0; i < uninterestingSsidsREx.length; i++) { if(ssidObj.name.match(uninterestingSsidsREx[i])) return 0; // remove / uninteresting } return 1; // keep / interesting }