world.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. /**
  2. * TraCINg-Server - Gathering and visualizing cyber incidents on the world
  3. *
  4. * Copyright 2013 Matthias Gazzari, Annemarie Mattmann, André Wolski
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. /**
  19. * Creates a world object containing a 2d map, 2d streetmap and a 3d globe
  20. */
  21. var world = new function() {
  22. // constants
  23. var view = {
  24. MAP: 0,
  25. STREETMAP: 1,
  26. GLOBE: 2,
  27. NULL: 3
  28. };
  29. var currentView;
  30. var globeObject; // the 3d globe
  31. var mapObject; // the 2d map
  32. var streetmapObject; // the street map
  33. var timeout = 300; // timeout interval
  34. var timer = null; // timeout id
  35. var key = 0; // current key for delayed database request
  36. var dataset = null; // database data
  37. var requestPaused = "."; // text to show in speed information if loading is currently paused
  38. var attackNumberHash = {}; // hashmap containing the number of attacks per Lat/Lng position
  39. var controller = new Controller();
  40. /**
  41. * Toggle whether the key control is enabled or not
  42. */
  43. this.toggleEnabled = controller.toggleEnabled;
  44. /**
  45. * Leave maps
  46. */
  47. this.leaveMap = function() {
  48. currentView = view.NULL;
  49. controller.unregisterCallbacks();
  50. }
  51. /**
  52. * Show 2d map
  53. */
  54. this.showMap = function() {
  55. if (mapObject === undefined) {
  56. mapObject = new map($('#map'), 'world_mill_en', 'navy');
  57. }
  58. currentView = view.MAP;
  59. controller.registerCallbacks({
  60. zoom: function(dir) {
  61. if (dir == controller.args.IN)
  62. mapObject.zoom(1.6);
  63. if (dir == controller.args.OUT)
  64. mapObject.zoom(1/1.6);
  65. },
  66. move: function(dir) {
  67. var speed = 120;
  68. if (dir == controller.args.LEFT)
  69. mapObject.move(speed,0);
  70. if (dir == controller.args.RIGHT)
  71. mapObject.move(-speed, 0);
  72. if (dir == controller.args.UP)
  73. mapObject.move(0, speed);
  74. if (dir == controller.args.DOWN)
  75. mapObject.move(0, -speed);
  76. },
  77. toggle: undefined
  78. });
  79. }
  80. /**
  81. * Show streetmap
  82. */
  83. this.showStreetmap = function() {
  84. if (streetmapObject === undefined) {
  85. streetmapObject = new streetmap('streetmap');
  86. }
  87. currentView = view.STREETMAP;
  88. controller.registerCallbacks({
  89. zoom: function(dir) {
  90. if (dir == controller.args.IN)
  91. streetmapObject.zoom(1);
  92. if (dir == controller.args.OUT)
  93. streetmapObject.zoom(-1);
  94. },
  95. move: function(dir) {
  96. var speed = 120;
  97. if (dir == controller.args.LEFT)
  98. streetmapObject.move(speed,0);
  99. if (dir == controller.args.RIGHT)
  100. streetmapObject.move(-speed, 0);
  101. if (dir == controller.args.UP)
  102. streetmapObject.move(0, speed);
  103. if (dir == controller.args.DOWN)
  104. streetmapObject.move(0, -speed);
  105. },
  106. toggle: undefined
  107. });
  108. }
  109. /**
  110. * Show 3d globe
  111. */
  112. this.showGlobe = function() {
  113. if (globeObject === undefined) {
  114. // create globeObject
  115. var container = document.getElementById('globe');
  116. // set modifyMarkerLabel function for globe
  117. var modifyMarkerLabel = function(label) {
  118. if (!advInfo) {
  119. var splittedLabel = label.split(";");
  120. label = splittedLabel[0];
  121. }
  122. return label;
  123. };
  124. // set setCountryLabel function for globe
  125. var setCountryLabel = function(cc, markers, allMarkers) {
  126. var country = countryName[cc];
  127. return country + " (" + markers + " attacks of " + allMarkers + " total)";
  128. };
  129. globeObject = new GLOBE.main(container, "extern/globe/images/", {
  130. 'modifyMarkerLabel': modifyMarkerLabel,
  131. 'setCountryLabel': setCountryLabel
  132. });
  133. }
  134. else
  135. globeObject.resize();
  136. currentView = view.GLOBE;
  137. controller.registerCallbacks({
  138. zoom: function(dir) {
  139. if (dir == controller.args.IN)
  140. globeObject.zoom(100);
  141. if (dir == controller.args.OUT)
  142. globeObject.zoom(-100);
  143. },
  144. move: function(dir) {
  145. if (dir == controller.args.LEFT)
  146. globeObject.rotate(-0.000001, 0);
  147. if (dir == controller.args.RIGHT)
  148. globeObject.rotate(0.000001, 0);
  149. if (dir == controller.args.UP)
  150. globeObject.rotate(0, 0.0000005);
  151. if (dir == controller.args.DOWN)
  152. globeObject.rotate(0, -0.0000005);
  153. },
  154. toggle: function() {
  155. globeObject.toggleView();
  156. }
  157. });
  158. }
  159. /**
  160. * Delay database markings
  161. */
  162. this.delayedMarking = function delayedMarking(data, live) {
  163. var total_attacks = 0;
  164. for(var i=0; i<data.length; i++){
  165. var d = data[i];
  166. var llHash = d.location[0] + d.location[1];
  167. if (attackNumberHash[llHash] != undefined) {
  168. attackNumberHash[llHash] += d.count;
  169. } else {
  170. attackNumberHash[llHash] = d.count;
  171. }
  172. total_attacks += d.count;
  173. // define source color and label
  174. var sourceColor = "red";
  175. var sourceLabel = getLabel(d, false);
  176. // each view has it own marker key
  177. var mapKey;
  178. var streetmapKey;
  179. var globeKey;
  180. // mark on 2d map and try to animate
  181. if (mapObject != undefined) {
  182. mapKey = mapObject.addMarker(d._id.cc, d.location, sourceColor, sourceLabel, d.count);
  183. if (currentView == view.MAP) {
  184. var pos = mapObject.getPosition(d.location[0], d.location[1]);
  185. animateMarker(pos.x, pos.y, sourceColor, "#map", mapKey);
  186. }
  187. }
  188. // mark on streetmap and try to animate
  189. if (streetmapObject != undefined) {
  190. streetmapKey = streetmapObject.addMarker(d.location, sourceColor, sourceLabel);
  191. if (currentView == view.STREETMAP) {
  192. var pos = streetmapObject.getPosition(d.location[0], d.location[1]);
  193. animateMarker(pos.x, pos.y, sourceColor, "#streetmap", streetmapKey);
  194. }
  195. }
  196. // mark on 3d map
  197. if (globeObject != undefined && globeObject.addMarker != undefined) {
  198. globeKey = globeObject.addMarker(d._id.cc, d.location[0], d.location[1], sourceLabel);
  199. // globe has its own mechanism to animate a marker
  200. }
  201. }
  202. if (data.length > 0) {
  203. world.finishLoading(true);
  204. } else {
  205. world.finishLoading(false);
  206. }
  207. // show number of attacks loaded
  208. if (data.length > 0) {
  209. $('#requestInfo').text('Successfully loaded ' + total_attacks + ' attacks.');
  210. }
  211. }
  212. /**
  213. * Stop the timeout of the database delay timer, reset all values, the progress bar and "Get Incidents" Button
  214. */
  215. this.finishLoading = function finishLoading(showState) {
  216. // stop the timer
  217. clearTimeout(timer);
  218. // reset timer
  219. timer = null;
  220. dataset = null;
  221. key = 0;
  222. timeout = 500;
  223. if (showState) {
  224. // update progress bar
  225. $('.bar').css('width', '100%');
  226. } else {
  227. // reset progress bar
  228. $('.bar').css('width', 0);
  229. }
  230. // reset play button to pause if necessary
  231. if ($('#playButton i').hasClass('icon-play')) {
  232. $('#playButton i').addClass('icon-pause');
  233. $('#playButton i').removeClass('icon-play');
  234. }
  235. // reset "Get Incidents" button (stop showing "Loading..."), disable control buttons
  236. $('#getIncidents').text('Get Incidents');
  237. $('#getIncidents').removeClass("disabled");
  238. disableRequestControl();
  239. // reset info on loading progress and speed
  240. $('#requestInfo').text('');
  241. $('#requestSpeedInfo').text('');
  242. }
  243. /*
  244. * Change the timeout intervals of the database delay timer
  245. */
  246. this.changeTimer = function(value) {
  247. // do not speed up too fast
  248. if (timeout + value <= 0)
  249. value = -100;
  250. // and slow down in the same interval as the speedup
  251. if (timeout - value < 0)
  252. value = 100;
  253. // do not speed up to "no time"
  254. if (timeout + value <= 0)
  255. value = 0;
  256. // increase or decrease the timeout interval depending on the given value
  257. timeout += value;
  258. // do not speed up "in negative time"
  259. if (timeout < 0)
  260. timeout = 0;
  261. $('#requestSpeedInfo').text('Current speed: ' + Math.round(100*(1000/timeout))/100 + ' attacks/second' + requestPaused);
  262. }
  263. /**
  264. * Reset the timeout of the database delay timer to 0
  265. */
  266. this.resetTimer = function() {
  267. timeout = 0;
  268. $('#requestSpeedInfo').text('');
  269. }
  270. /**
  271. * Stop the timeout of the database delay timer
  272. */
  273. this.stopTimer = function() {
  274. clearTimeout(timer);
  275. requestPaused = " (currently paused).";
  276. $('#requestSpeedInfo').text('Current speed: ' + Math.round(100*(1000/timeout))/100 + ' attacks/second' + requestPaused);
  277. }
  278. /**
  279. * Restart the timeout of the database delay timer
  280. */
  281. this.restartTimer = function() {
  282. this.delayedMarking(dataset, live);
  283. requestPaused = ".";
  284. $('#requestSpeedInfo').text('Current speed: ' + Math.round(100*(1000/timeout))/100 + ' attacks/second' + requestPaused);
  285. }
  286. /**
  287. * Mark an incident on the 2d map, the street map and the 3d globe
  288. */
  289. this.markIncident = function(data, live, noAnimation) {
  290. // remove alert saying "Waiting for attacks..."
  291. $("#tableWaitingAlert").remove();
  292. // update hashmap for displaying number of attacks per LatLng
  293. llHash = new String(data.src.ll ? data.src.ll[0] : "") + new String(data.src.ll ? data.src.ll[1] : "");
  294. if (attackNumberHash[llHash] != undefined) {
  295. attackNumberHash[llHash]++;
  296. } else {
  297. attackNumberHash[llHash] = 1;
  298. }
  299. // define source color and label
  300. var sourceColor = "red";
  301. var sourceLabel = getLabel(data, live);
  302. // each view has it own marker key
  303. var mapKey;
  304. var streetmapKey;
  305. var globeKey;
  306. // mark on 2d map and try to animate
  307. if (mapObject != undefined) {
  308. mapKey = mapObject.addMarker(data.src.cc, data.src.ll, sourceColor, sourceLabel);
  309. if (currentView == view.MAP && !noAnimation) {
  310. var pos = mapObject.getPosition(data.src.ll[0], data.src.ll[1]);
  311. animateMarker(pos.x, pos.y, sourceColor, "#map", mapKey);
  312. }
  313. }
  314. // mark on streetmap and try to animate
  315. if (streetmapObject != undefined) {
  316. streetmapKey = streetmapObject.addMarker(data.src.ll, sourceColor, sourceLabel);
  317. if (currentView == view.STREETMAP && !noAnimation) {
  318. var pos = streetmapObject.getPosition(data.src.ll[0], data.src.ll[1]);
  319. animateMarker(pos.x, pos.y, sourceColor, "#streetmap", streetmapKey);
  320. }
  321. }
  322. // mark on 3d map
  323. if (globeObject != undefined && globeObject.addMarker != undefined) {
  324. globeKey = globeObject.addMarker(data.src.cc, data.src.ll[0], data.src.ll[1], sourceLabel);
  325. // globe has its own mechanism to animate a marker
  326. }
  327. // set timeout to remove marker if in live view
  328. if (live) {
  329. var expireTime = 300000;
  330. setTimeout(
  331. function() {
  332. // update hashmap for displaying number of attacks per LatLng
  333. llHash = new String(data.src.ll[0]) + new String(data.src.ll[1]);
  334. if (attackNumberHash[llHash] > 0) {
  335. attackNumberHash[llHash]--;
  336. } else {
  337. attackNumberHash[llHash] = undefined;
  338. }
  339. // remove marker on 2d maps and globe
  340. if (mapKey != undefined && mapObject != undefined)
  341. mapObject.removeMarker(mapKey);
  342. if (streetmapKey != undefined && streetmapObject != undefined)
  343. streetmapObject.removeMarker(streetmapKey);
  344. if (globeKey != undefined && globeObject != undefined)
  345. globeObject.removeMarker(globeKey);
  346. },
  347. expireTime
  348. );
  349. }
  350. }
  351. /**
  352. * State whether there is a marker on the jVectorMap
  353. */
  354. this.jvmHasMarker = function() {
  355. if (mapObject != undefined) {
  356. return mapObject.hasMarker();
  357. }
  358. return false;
  359. }
  360. /**
  361. * State whether there is a marker on the streetmap
  362. */
  363. this.stMapHasMarker = function() {
  364. if (streetmapObject != undefined) {
  365. return streetmapObject.hasMarker();
  366. }
  367. return false;
  368. }
  369. /**
  370. * State whether there is a marker on the globe
  371. */
  372. this.globeHasMarker = function() {
  373. if (globeObject != undefined && globeObject.hasMarker != undefined) {
  374. return globeObject.hasMarker();
  375. }
  376. return false;
  377. }
  378. /**
  379. * Reset every marker on the 2d map, the streetmap and the 3d globe
  380. */
  381. this.reset = function() {
  382. // reset 2d maps abd globe
  383. mapObject && mapObject.reset();
  384. streetmapObject && streetmapObject.reset();
  385. globeObject && globeObject.reset();
  386. attackNumberHash = {};
  387. // reset progress bar
  388. $('.bar').css('width', 0);
  389. // reset progress information
  390. $('#requestInfo').text('');
  391. //TODO also influence x/y entries loaded and successfully loaded y items?
  392. }
  393. /**
  394. * Reset the table
  395. */
  396. this.resetTable = function() {
  397. // reset table
  398. $("#attackTable").dataTable().fnClearTable();
  399. }
  400. /**
  401. * Determine label shown on both the 2d map and the 3d globe.
  402. */
  403. function getLabel(data, live) {
  404. var label = "";
  405. // standard information label
  406. if (live)
  407. label += "Live data";
  408. else
  409. label += "Database data";
  410. var llKey = !live ? data.location[0] + data.location[1] : new String(data.src.ll ? data.src.ll[0] : "") + new String(data.src.ll ? data.src.ll[1] : "");
  411. var num = attackNumberHash[llKey];
  412. if (num != null) {
  413. if (num == 1)
  414. label += ": " + num + " attack";
  415. else
  416. label += ": " + num + " attacks";
  417. }
  418. label += "<br />Attack source (marked here): ";
  419. var city = live ? data.src.city : data._id.city;
  420. if(city){
  421. label += city + ",";
  422. }
  423. label += live ? data.src.country : data._id.country;
  424. // advanced information
  425. label += ";<br />Destination: ";
  426. var destination = "";
  427. if(live){
  428. if (data.dst.city != "" && data.dst.city != undefined) {
  429. destination += data.dst.city + ", ";
  430. }
  431. if (data.dst.country != "" && data.dst.country != undefined) {
  432. destination += data.dst.country;
  433. }
  434. } else {
  435. destination = data.destination;
  436. }
  437. if (destination)
  438. label += destination;
  439. else
  440. label += "Unknown";
  441. label += ";<br />Date: " + formatDate(data);
  442. var type = typeid2str(data.type)
  443. if (type != "" && type != undefined);
  444. label += ";<br />Type: " + type;
  445. if (data.authorized)
  446. label += ";<br />Authorized Sensor";
  447. else
  448. label += ";<br />Unauthorized Sensor";
  449. return label;
  450. }
  451. /**
  452. * Format the date to a more readable representation
  453. */
  454. function formatDate(incident) {
  455. var date = incident.date && new Date(incident.date) || new Date();
  456. var day = date.getDate();
  457. if (date.getDate() < 10)
  458. day = "0" + date.getDate();
  459. var month = (date.getMonth() + 1);
  460. if ((date.getMonth() + 1) < 10)
  461. month = "0" + (date.getMonth() + 1);
  462. var hour = date.getHours();
  463. if (date.getHours() < 10)
  464. hour = "0" + date.getHours();
  465. var minute = date.getMinutes();
  466. if (date.getMinutes() < 10)
  467. minute = "0" + date.getMinutes();
  468. var dateFormat = day + "." + month + "." + date.getFullYear() + " " + hour + ":" + minute;
  469. return dateFormat;
  470. }
  471. /**
  472. * Generate one table entry and return it as a string to be inserted
  473. */
  474. function generateTableEntry(incident) {
  475. // set city and country names
  476. if (incident.src.city == undefined)
  477. incident.src.city = "";
  478. if (incident.dst.country == undefined)
  479. incident.dst.country = "";
  480. if (incident.dst.city == undefined)
  481. incident.dst.city = "";
  482. if (incident.src.port == 0)
  483. incident.src.port = "";
  484. if (incident.dst.port == 0)
  485. incident.dst.port = "";
  486. //format date
  487. dateFormat = formatDate(incident);
  488. var log = '';
  489. if (incident.log && incident.log != ''){
  490. log = '<a href="#showLog" data-toggle="modal" onclick="javascript:showLog(\'' + incident._id + '\');">show log</a>';
  491. }
  492. var type = typeid2str(incident.type);
  493. var typeDescr = typeid2descr(incident.type);
  494. // popup for md5sum so it does not take so much space in the table
  495. var md5 = "";
  496. if (incident.md5sum && incident.md5sum != '') {
  497. var virustotalLink = "https://www.virustotal.com/en/search/?query=" + incident.md5sum;
  498. var popoverContent = "Md5sum of malware hash: " + incident.md5sum + "<br \\> Get more information about this malware from virustotal: <a href=\'" + virustotalLink + "\' target='_blank'>Click here</a> (by doing so you will open a different website)!";
  499. var url = "\"./extern/bootstrap/images/glyphicons-halflings.png\"";
  500. md5 = "<a class='btn' rel='popover' data-html='true' data-content=\"" + popoverContent + "\" data-animation='false' data-placement='left'><i class='icon-info-sign' style='background-image: url("+ url +");'></i></a>";
  501. }
  502. var authorized = "";
  503. if (incident.authorized) {
  504. authorized = "<p class='text-success'>Yes</p>";
  505. } else {
  506. authorized = "<p class='text-error'>No</p>";
  507. }
  508. //make entry
  509. var attackTableEntry = [incident.sensortype, incident.sensorname, '<span title="' + typeDescr + '">' + type + '</span>', dateFormat, incident.src.country, incident.src.city, incident.src.port, incident.dst.country, incident.dst.city, incident.dst.port, authorized, md5, log];
  510. return attackTableEntry;
  511. }
  512. this.generateTableEntry = generateTableEntry;
  513. /**
  514. * Make table entries (includes time formatting)
  515. */
  516. function makeTableEntry(attackTableEntries) {
  517. $("#attackTable").dataTable().fnAddData(attackTableEntries);
  518. makePopovers();
  519. }
  520. this.makeTableEntry = makeTableEntry;
  521. /**
  522. * Make popovers in the table
  523. */
  524. function makePopovers() {
  525. console.log("makePopovers");
  526. $('a[rel=popover]').popover({});
  527. }
  528. this.makePopovers = makePopovers;
  529. /**
  530. * animate new marker using jquery animate()
  531. */
  532. function animateMarker(x, y, color, container, key) {
  533. // define style of the animation div
  534. var style = {
  535. 'background-color': color,
  536. 'position': 'absolute',
  537. 'border-radius': '100px',
  538. 'height': '40px',
  539. 'width': '40px',
  540. 'margin-top': '-20px',
  541. 'margin-left': '-20px',
  542. 'left': x + 'px',
  543. 'top': y + 'px',
  544. };
  545. // create a marker animation
  546. $(container).append('<div class="markerAnimation" id=' + key + '></div>');
  547. // add the css style to the marker animation
  548. $("#" + key + ".markerAnimation").css(style);
  549. // animate the marker and delete it afterwards
  550. $("#" + key + ".markerAnimation").fadeOut(
  551. 1000,
  552. $("#" + key + ".markerAnimation").remove
  553. );
  554. }
  555. function updateAttackTableHeight() {
  556. var attackTable = $("#attackTable").dataTable();
  557. var height = $(window).height();
  558. height -= $("#table .dataTables_scrollBody").offset().top;
  559. height -= $("#table .dataTables_scroll + div").height();
  560. console.log("attackTableHeight: " + height);
  561. $("#table .dataTables_scrollBody").css({"max-height": height});
  562. }
  563. /**
  564. * resize table
  565. */
  566. function resize() {
  567. updateAttackTableHeight();
  568. $("#attackTable").dataTable().fnDraw();
  569. }
  570. this.resize = resize;
  571. }
  572. function showLog(id){
  573. console.log("showLog " + id);
  574. socket.emit("getLog", id, function(content){
  575. console.log("showLog content: " + content);
  576. $('#showLogLabel').text("Log for id " + id);
  577. $('#showLogBody').html(content);
  578. $('#showLog').modal();
  579. });
  580. }
  581. $(function(){
  582. $("#attackTable").dataTable({
  583. "aaSorting": [[ 3, "desc" ]], // order by date, new items first
  584. "sScrollY": "100%",
  585. bServerSide: false,
  586. "ajax": function(request, callback, settings){
  587. if(!currentTableFilter) return;
  588. var attackFilter = currentTableFilter;
  589. attackFilter["page"] = request.start;
  590. attackFilter["perpage"] = request.length;
  591. attackFilter["query"] = request.search.value;
  592. socket.emit("requestAttacksForTable", attackFilter);
  593. console.log(arguments);
  594. },
  595. "fnDrawCallback": function() {
  596. world.makePopovers();
  597. }
  598. });
  599. setTimeout(function() {
  600. world.resize();
  601. }, 10);
  602. });
  603. $(window).resize($.throttle(250,function() {
  604. world.resize();
  605. }));