SMB2: dissect new signing capability negotiate context
[wireshark-sm.git] / ipmap.html
blobd31140c8399b860fc03e062b4e5af8e88c5f8b27
1 <!doctype html>
2 <meta charset="utf-8">
3 <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
4 <title>Wireshark: IP Location Map</title>
5 <link rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css"
6 integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA=="
7 crossorigin="">
8 <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.Default.css"
9 integrity="sha512-BBToHPBStgMiw0lD4AtkRIZmdndhB6aQbXpX7omcrXeG2PauGBl2lzq2xUZTxaLxYz5IDHlmneCZ1IJ+P3kYtQ=="
10 crossorigin="">
11 <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.css"
12 integrity="sha512-RLEjtaFGdC4iQMJDbMzim/dOvAu+8Qp9sw7QE4wIMYcg2goVoivzwgSZq9CsIxp4xKAZPKh5J2f2lOko2Ze6FQ=="
13 crossorigin="">
14 <!--
15 <link rel="stylesheet" href="https://unpkg.com/leaflet-measure@3.1.0/dist/leaflet-measure.css"
16 integrity="sha512-wgiKVjb46JxgnGNL6xagIy2+vpqLQmmHH7fWD/BnPzouddSmbRTf6xatWIRbH2Rgr2F+tLtCZKbxnhm5Xz0BcA=="
17 crossorigin="">
18 -->
19 <style>
20 html, body {
21 margin: 0;
22 padding: 0;
23 height: 100%;
25 #map {
26 height: 100%;
28 .file-picker-enabled #map, #file-picker-container {
29 display: none;
31 .file-picker-enabled #file-picker-container {
32 display: block;
33 margin: 2em;
35 .range-control {
36 padding: 3px 5px;
37 color: #333;
38 background: #fff;
39 opacity: .5;
41 .range-control:hover { opacity: 1; }
42 .range-control-label { padding-right: 3px; }
43 .range-control-input { padding: 0; width: 130px; }
44 .range-control-input, .range-control-label { vertical-align: middle; }
45 </style>
46 <script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js"
47 integrity="sha512-QVftwZFqvtRNi0ZyCtsznlKSWOStnDORoefr1enyq5mVL4tmKB3S/EnC3rRJcxCPavG10IcrVGSmPh6Qw5lwrg=="
48 crossorigin=""></script>
49 <script src="https://unpkg.com/leaflet.markercluster@1.4.1/dist/leaflet.markercluster.js"
50 integrity="sha512-MQlyPV+ol2lp4KodaU/Xmrn+txc1TP15pOBF/2Sfre7MRsA/pB4Vy58bEqe9u7a7DczMLtU5wT8n7OblJepKbg=="
51 crossorigin=""></script>
52 <!--
53 <script src="https://unpkg.com/leaflet-measure@3.1.0/dist/leaflet-measure.js"
54 integrity="sha512-ovh6EqS7MUI3QjLWBM7CY8Gu8cSM5x6vQofUMwKGbHVDPSAS2lmNv6Wq5es5WCz1muyojQxcc8rA3CvVjD2Z+A=="
55 crossorigin=""></script>
56 -->
57 <script>
58 var map;
60 function sortIpKey(v) {
61 if (/\./.test(v)) {
62 // Assume IPv4. Convert 192.0.2.34 -> 192.000.002.034 for alpha sort.
63 return v.replace(/\b\d\b/g, '00$&').replace(/\b\d{2}\b/g, '0$&');
64 } else {
65 // Assume IPv6. We won't handle :: correctly. Hope for the best.
66 return v;
70 function escapeHtml(text) {
71 if (!text) return '';
72 return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
74 function sanitizeHtml(text) {
75 // Handle legacy data containing <div class="geoip_property">...</div>
76 // (since Wireshark 2.0) or <br/> (before v1.99.0-rc1-1781-g7e63805708).
77 text = text
78 .replace(/<div[^>]*>/g, '')
79 .replace(/<\/div>|<br\/>/g, '\n')
80 .replace(/&#39;/g, "'");
81 return escapeHtml(text).replace(/\n/g, '<br>');
84 var RangeControl = L.Control.extend({
85 options: {
86 // @option label: String = 'Speed:'
87 // The HTML text to be displayed next to the slider.
88 label: '',
89 title: '',
91 min: 0,
92 max: 100,
93 value: 0,
95 // @option onChange: Function = *
96 // A `Function` that is called on slider value changes.
97 // Called with two arguments, the new and previous range value.
99 onAdd: function(map) {
100 var className = 'range-control';
101 var container = L.DomUtil.create('div', className + ' leaflet-bar');
102 L.DomEvent.disableClickPropagation(container);
103 var label = L.DomUtil.create('label', className + '-label', container);
104 var labelText = L.DomUtil.create('span', className + '-label', label);
105 labelText.title = this.options.title;
106 labelText.innerHTML = this.options.label;
107 var input = L.DomUtil.create('input', className + '-input', label);
108 this._input = input;
109 input.type = 'range';
110 input.min = this.options.min;
111 input.max = this.options.max;
112 this._lastValue = input.valueAsNumber = this.options.value;
113 L.DomEvent.on(input, 'change', this._onInputChange, this);
114 return container;
116 _onInputChange: function(ev) {
117 var value = this._input.valueAsNumber;
118 if (value !== this._lastValue) {
119 if (this.options.onChange) {
120 this.options.onChange(value, this._lastValue);
122 this._lastValue = value;
127 var rangeControl = function(options) {
128 return new RangeControl(options);
131 function loadGeoJSON(obj) {
132 'use strict';
133 if (map) map.remove();
134 map = L.map('map');
135 var tileServer = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png';
136 L.tileLayer(tileServer, {
137 minZoom: 2,
138 maxZoom: 16,
139 subdomains: 'abcd',
140 attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>'
141 }).addTo(map);
142 L.control.scale().addTo(map);
144 // Measurement tool, useful for investigating accuracy-related issues.
145 if (L.control.measure) {
146 L.control.measure({
147 primaryLengthUnit: 'kilometers',
148 secondaryLengthUnit: 'miles'
149 }).addTo(map);
152 var geoJson = L.geoJSON(obj, {
153 pointToLayer: function(feature, latlng) {
154 // MaxMind databases use km for accuracy, but they always use
155 // 50, 100, 200 or 1000. That is too course, so ignore it and use a
156 // fixed 1km radius.
157 // See https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=14693#c12
158 return L.circle(latlng, {radius: 1e3});
160 onEachFeature: function(feature, layer) {
161 var props = feature.properties;
162 var title, lines = [];
163 if (props.title && props.description) {
164 title = escapeHtml(props.title);
165 lines.push(sanitizeHtml(props.description));
166 } else {
167 title = escapeHtml(props.ip);
168 if (props.autonomous_system_number) {
169 var line = 'AS: ' + props.autonomous_system_number;
170 line += ' (' + props.autonomous_system_organization + ')';
171 lines.push(escapeHtml(line));
173 if (props.city) {
174 lines.push(escapeHtml('City: ' + props.city));
176 if (props.country) {
177 lines.push(escapeHtml('Country: ' + props.country));
179 if ('packets' in props) {
180 lines.push(escapeHtml('Packets: ' + props.packets));
182 if ('bytes' in props) {
183 lines.push(escapeHtml('Bytes: ' + props.bytes));
186 if (title) {
187 layer.bindTooltip(title, {
188 offset: [10, 0],
189 direction: 'right',
190 sticky: true
193 if (title && lines.length) {
194 layer.bindPopup('<b>' + title + '</b><br>' + lines.join('<br>'));
199 map.on('zoomend', function() {
200 // Ensure that the circles are clearly visible even when zoomed out.
201 // Larger values will increase the size of the circle.
202 var visibleZoomLevel = 9;
203 var radius = 1e3;
204 if (map.getZoom() < visibleZoomLevel) {
205 // Enlarge radius to ensure it is easy to select.
206 radius *= map.getZoomScale(visibleZoomLevel, map.getZoom());
208 geoJson.eachLayer(function(layer) {
209 layer.setRadius(radius);
213 // Cluster nearby/overlapping nodes by default.
214 var clusterGroup = L.markerClusterGroup({
215 zoomToBoundsOnClick: false,
216 spiderfyOnMaxZoom: false,
217 maxClusterRadius: 10
219 clusterGroup.addTo(map).addLayer(geoJson);
220 map.fitWorld().fitBounds(clusterGroup.getBounds());
222 // Summarize nodes within the cluster.
223 clusterGroup.on('clustermouseover', function(ev) {
224 // More addresses will be stripped.
225 var cutoff = 30;
226 var cluster = ev.propagatedFrom;
227 var addresses = cluster.getAllChildMarkers().map(function(marker) {
228 return marker.getTooltip().getContent();
230 addresses.sort(function(a, b) {
231 a = sortIpKey(a);
232 b = sortIpKey(b);
233 return a === b ? 0 : (a < b ? -1 : 1);
235 var deleted = addresses.splice(cutoff).length;
236 var title = addresses.join('<br>');
237 if (deleted) {
238 title += '<br>(and ' + deleted + ' more)';
240 cluster.bindTooltip(title, {
241 offset: [10, 0],
242 direction: 'right',
243 sticky: true,
244 opacity: 0.8
245 }).openTooltip();
246 }).on('clustermouseout', function(ev) {
247 ev.propagatedFrom.unbindTooltip();
248 }).on('clusterclick', function(ev) {
249 ev.propagatedFrom.spiderfy();
252 // Provide an option to disable clustering
253 rangeControl({
254 label: 'Cluster radius:',
255 title: 'Control merging of nearby nodes. Set to the minimum to disable merges.',
256 min: 0,
257 max: 100,
258 value: clusterGroup.options.maxClusterRadius,
259 onChange: function(value, oldValue) {
260 // Apply new radius: remove map, clear markers and finally add new.
261 clusterGroup.options.maxClusterRadius = value;
262 clusterGroup.remove().clearLayers().addTo(map);
263 // Value 0: clustering is disabled, the map is directly used.
264 geoJson.remove().addTo(value === 0 ? map : clusterGroup);
266 }).addTo(map);
269 function showError(msg) {
270 document.getElementById('error-message').textContent = msg;
271 document.body.classList.add('file-picker-enabled');
274 function loadData(data) {
275 'use strict';
276 var html_match, what, error;
277 var reOldHtml = /^ *var endpoints = (\{[\s\S]+? *\});$/m;
278 // Complicated regex to support html-minifier.
279 var reNewHtml = /<script[^>]+id="?ipmap-data"?(?: [^>]*)?>\s*(\{[\S\s]+?\})\s*<\/script>/;
280 if ((html_match = reNewHtml.exec(data))) {
281 // Match new ipmap.html file.
282 what = 'new ipmap.html';
283 data = html_match[1];
284 } else if ((html_match = reOldHtml.exec(data))) {
285 // Match old ipmap.html file
286 what = 'old ipmap.html';
287 var text = html_match[1].replace(/'/g, '"');
288 text = text.replace(/ class="geoip_property"/g, '');
289 data = text.replace(/\/\/ Start endpoint list.*/, '');
290 } else if (/^\s*\{[\s\S]+\}\s*$/.test(data)) {
291 // Assume GeoJSON (.json) file.
292 what = 'GeoJSON file';
293 } else {
294 what = 'unknown file';
295 error = 'Unrecognized file contents';
297 if (!error) {
298 try {
299 loadGeoJSON(JSON.parse(data));
300 return true;
301 } catch (e) {
302 error = e;
305 var msg = 'Failed to load map data from ' + what + ': ' + error;
306 msg += '; data was: ' + data.substring(0, 120);
307 if (data.length > 100) msg += '... (' + data.length + ' bytes)';
308 showError(msg);
311 (function() {
312 'use strict';
313 function loadFromUrl(url) {
314 var xhr = new XMLHttpRequest();
315 xhr.open('GET', url, true);
316 xhr.onload = function() {
317 if (xhr.status !== 200) {
318 showError('Failed to retrieve ' + url + ': ' + xhr.status + ' ' + xhr.statusText);
319 return;
321 loadData(xhr.responseText);
323 xhr.onerror = function() {
324 showError('Failed to retrieve ' + url + ': ' + xhr.status + ' ' + xhr.statusText);
326 xhr.send(null);
329 addEventListener('load', function() {
330 // Note: FileReader and classList do not work with IE9 or older.
331 var fileSelector = document.getElementById('file-picker');
332 fileSelector.addEventListener('change', function() {
333 if (!fileSelector.files.length) {
334 return;
336 document.body.classList.remove('file-picker-enabled');
337 var reader = new FileReader();
338 reader.onload = function() {
339 if (!loadData(reader.result)) {
340 document.body.classList.add('file-picker-enabled');
343 reader.onerror = function() {
344 showError('Failed to read file.');
346 reader.readAsText(fileSelector.files[0]);
349 // Force file picker when the "file" URL is given.
350 var url = location.search.match(/[?&]url=([^&]*)/);
351 if (url) {
352 url = decodeURIComponent(url[1]);
353 if (url) {
354 loadFromUrl(url);
355 } else {
356 showError('');
358 return;
361 var data = document.getElementById('ipmap-data');
362 if (data) {
363 loadData(data.textContent);
364 } else {
365 showError('');
368 }());
369 </script>
370 <div id="file-picker-container">
371 <label>Select an ipmap.html or GeoJSON .json file as created by Wireshark.<br>
372 <input type="file" id="file-picker" accept=".json,.html"></label>
373 <p id="error-message"></p>
374 </div>
375 <div id="map"></div>
376 <!--
377 Wireshark will append a script tag (id="ipmap-data" type="application/json")
378 below, containing a GeoJSON object. If missing, then a file picker will be
379 displayed which can be useful during development.