lanscape 1.3.1a7__py3-none-any.whl → 1.3.1a8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of lanscape might be problematic. Click here for more details.

@@ -9,7 +9,9 @@ import traceback
9
9
  import subprocess
10
10
  from time import sleep
11
11
  from typing import List, Dict
12
- from scapy.all import ARP, Ether, srp
12
+ from scapy.sendrecv import srp
13
+ from scapy.layers.l2 import ARP, Ether
14
+ from scapy.error import Scapy_Exception
13
15
  from concurrent.futures import ThreadPoolExecutor, as_completed
14
16
 
15
17
  from .service_scan import scan_service
@@ -66,7 +68,7 @@ class IPAlive:
66
68
 
67
69
  def _ping_lookup(
68
70
  self, host: str,
69
- retries: int = 1,
71
+ retries: int = 2,
70
72
  retry_delay: int = .25,
71
73
  ping_count: int = 2,
72
74
  timeout: int = 2
@@ -80,16 +82,31 @@ class IPAlive:
80
82
  # -c count, -W timeout in s
81
83
  cmd = ['ping', '-c', str(ping_count), '-W', str(timeout)]
82
84
 
85
+ cmd = cmd + [host]
86
+
83
87
  for r in range(retries):
84
88
  try:
85
- output = subprocess.check_output(
86
- cmd + [host],
87
- stderr=subprocess.STDOUT,
88
- universal_newlines=True
89
+ proc = subprocess.run(
90
+ cmd,
91
+ text=True,
92
+ stdout=subprocess.PIPE,
93
+ stderr=subprocess.PIPE
89
94
  )
90
- # Windows/Linux both include “TTL” on a successful reply
91
- if 'TTL' in output.upper():
92
- return True
95
+
96
+ if proc.returncode == 0:
97
+ output = proc.stdout.lower()
98
+
99
+ # Windows/Linux both include “TTL” on a successful reply
100
+ if psutil.WINDOWS or psutil.LINUX:
101
+ if 'ttl' in output:
102
+ return True
103
+ # some distributions of Linux and macOS
104
+ if psutil.MACOS or psutil.LINUX:
105
+ bad = '100.0% packet loss'
106
+ good = 'ping statistics'
107
+ # mac doesnt include TTL, so we check good is there, and bad is not
108
+ if good in output and bad not in output:
109
+ return True
93
110
  except subprocess.CalledProcessError as e:
94
111
  self.caught_errors.append(DeviceError(e))
95
112
  pass
@@ -283,20 +300,65 @@ def get_cidr_from_netmask(netmask: str):
283
300
 
284
301
  def get_primary_interface():
285
302
  """
286
- Get the primary network interface based on the default gateway.
303
+ Get the primary network interface that is likely handling internet traffic.
304
+ Uses heuristics to identify the most probable interface.
287
305
  """
288
- # Get the default gateway information
289
- gateways = psutil.net_if_addrs()
290
- default_gw = psutil.net_if_stats()
291
-
292
- # Iterate over the default gateways
293
- for interface, addrs in gateways.items():
294
- if default_gw[interface].isup: # Ensure the interface is up
295
- for addr in addrs:
296
- if addr.family == socket.AF_INET: # Look for IPv4 addresses
297
- return interface
298
-
299
- return None
306
+ # Try to find the interface with the default gateway
307
+ try:
308
+ if psutil.WINDOWS:
309
+ # On Windows, parse route print output
310
+ output = subprocess.check_output("route print 0.0.0.0", shell=True, text=True)
311
+ lines = output.strip().split('\n')
312
+ for line in lines:
313
+ if '0.0.0.0' in line and 'Gateway' not in line: # Skip header
314
+ parts = [p for p in line.split() if p]
315
+ if len(parts) >= 4:
316
+ interface_idx = parts[3]
317
+ # Find interface name in the output
318
+ for iface_name, addrs in psutil.net_if_addrs().items():
319
+ if str(interface_idx) in iface_name:
320
+ return iface_name
321
+ else:
322
+ # Linux/Unix/Mac - use ip route or netstat
323
+ try:
324
+ output = subprocess.check_output("ip route show default 2>/dev/null || netstat -rn | grep default",
325
+ shell=True, text=True)
326
+ for line in output.split('\n'):
327
+ if 'default via' in line and 'dev' in line:
328
+ return line.split('dev')[1].split()[0]
329
+ elif 'default' in line:
330
+ parts = line.split()
331
+ if len(parts) > 3:
332
+ return parts[-1] # Interface is usually the last column
333
+ except (subprocess.SubprocessError, IndexError, FileNotFoundError):
334
+ pass
335
+ except Exception as e:
336
+ log.debug(f"Error determining primary interface: {e}")
337
+
338
+ # Fallback: Identify likely candidates based on heuristics
339
+ candidates = []
340
+
341
+ for interface, addrs in psutil.net_if_addrs().items():
342
+ stats = psutil.net_if_stats().get(interface)
343
+ if stats and stats.isup:
344
+ ipv4_addrs = [addr for addr in addrs if addr.family == socket.AF_INET]
345
+ if ipv4_addrs:
346
+ # Skip loopback and common virtual interfaces
347
+ is_loopback = any(addr.address.startswith('127.') for addr in ipv4_addrs)
348
+ is_virtual = any(name in interface.lower() for name in
349
+ ['loop', 'vmnet', 'vbox', 'docker', 'virtual', 'veth'])
350
+
351
+ if not is_loopback and not is_virtual:
352
+ candidates.append(interface)
353
+
354
+ # Prioritize interfaces with names typically used for physical connections
355
+ for prefix in ['eth', 'en', 'wlan', 'wifi', 'wl', 'wi']:
356
+ for interface in candidates:
357
+ if interface.lower().startswith(prefix):
358
+ return interface
359
+
360
+ # Otherwise return the first candidate or None
361
+ return candidates[0] if candidates else None
300
362
 
301
363
  def get_host_ip_mask(ip_with_cidr: str):
302
364
  """
@@ -306,25 +368,26 @@ def get_host_ip_mask(ip_with_cidr: str):
306
368
  network = ipaddress.ip_network(ip_with_cidr, strict=False)
307
369
  return f'{network.network_address}/{cidr}'
308
370
 
309
- def get_network_subnet(interface = get_primary_interface()):
371
+ def get_network_subnet(interface = None):
310
372
  """
311
- Get the network interface and subnet.
312
- Default is primary interface
313
- """
373
+ Get the network subnet for a given interface.
374
+ Uses network_from_snicaddr for conversion.
375
+ Default is primary interface.
376
+ """
377
+ interface = interface or get_primary_interface()
378
+
314
379
  try:
315
- ip_address = get_ip_address(interface)
316
- netmask = get_netmask(interface)
317
- # is valid interface?
318
- if ip_address and netmask:
319
- cidr = get_cidr_from_netmask(netmask)
320
-
321
- ip_mask = f'{ip_address}/{cidr}'
322
-
323
- return get_host_ip_mask(ip_mask)
324
- except:
380
+ addrs = psutil.net_if_addrs()
381
+ if interface in addrs:
382
+ for snicaddr in addrs[interface]:
383
+ if snicaddr.family == socket.AF_INET and snicaddr.address and snicaddr.netmask:
384
+ subnet = network_from_snicaddr(snicaddr)
385
+ if subnet:
386
+ return subnet
387
+ except Exception:
325
388
  log.info(f'Unable to parse subnet for interface: {interface}')
326
389
  log.debug(traceback.format_exc())
327
- return
390
+ return None
328
391
 
329
392
  def get_all_network_subnets():
330
393
  """
@@ -337,7 +400,9 @@ def get_all_network_subnets():
337
400
  for interface, snicaddrs in addrs.items():
338
401
  for snicaddr in snicaddrs:
339
402
  if snicaddr.family == socket.AF_INET and gateways[interface].isup:
340
- subnet = get_network_subnet(interface)
403
+
404
+ subnet = network_from_snicaddr(snicaddr)
405
+
341
406
  if subnet:
342
407
  subnets.append({
343
408
  'subnet': subnet,
@@ -346,24 +411,70 @@ def get_all_network_subnets():
346
411
 
347
412
  return subnets
348
413
 
414
+ def network_from_snicaddr(snicaddr: psutil._common.snicaddr) -> str:
415
+ """
416
+ Convert a psutil snicaddr object to a human-readable string.
417
+ """
418
+ if not snicaddr.address or not snicaddr.netmask:
419
+ return None
420
+ elif snicaddr.family == socket.AF_INET:
421
+ addr = f"{snicaddr.address}/{get_cidr_from_netmask(snicaddr.netmask)}"
422
+ elif snicaddr.family == socket.AF_INET6:
423
+ addr = f"{snicaddr.address}/{snicaddr.netmask}"
424
+ else:
425
+ return f"{snicaddr.address}"
426
+ return get_host_ip_mask(addr)
427
+
349
428
  def smart_select_primary_subnet(subnets: List[dict] | None = None) -> str:
350
429
  """
351
- Finds the largest subnet within max ip range. If no subnets are
352
- available, returns an empty string instead of raising ``KeyError``.
430
+ Intelligently select the primary subnet that is most likely handling internet traffic.
431
+
432
+ Selection priority:
433
+ 1. Subnet associated with the primary interface (with default gateway)
434
+ 2. Largest subnet within maximum allowed IP range
435
+ 3. First subnet in the list as fallback
436
+
437
+ Returns an empty string if no subnets are available.
353
438
  """
354
439
  subnets = subnets or get_all_network_subnets()
355
440
 
356
441
  if not subnets:
357
442
  return ""
358
-
443
+
444
+ # First priority: Get subnet for the primary interface
445
+ primary_if = get_primary_interface()
446
+ if primary_if:
447
+ primary_subnet = get_network_subnet(primary_if)
448
+ if primary_subnet:
449
+ # Return this subnet if it's within our list
450
+ for subnet in subnets:
451
+ if subnet["subnet"] == primary_subnet:
452
+ return primary_subnet
453
+
454
+ # Second priority: Find a reasonable sized subnet (existing logic)
359
455
  selected = {}
360
456
  for subnet in subnets:
361
457
  if selected.get("address_cnt", 0) < subnet["address_cnt"] < MAX_IPS_ALLOWED:
362
458
  selected = subnet
363
459
 
364
- if not selected:
460
+ # Third priority: Just take the first subnet if nothing else matched
461
+ if not selected and subnets:
365
462
  selected = subnets[0]
366
463
 
367
464
  return selected.get("subnet", "")
368
465
 
466
+ def is_arp_supported():
467
+ """
468
+ Check if ARP requests are supported on the current platform.
469
+ """
470
+ try:
471
+ arp_request = ARP(pdst='0.0.0.0')
472
+ broadcast = Ether(dst="ff:ff:ff:ff:ff:ff")
473
+ packet = broadcast / arp_request
474
+
475
+ srp(packet, timeout=0, verbose=False)
476
+ return True
477
+ except Scapy_Exception:
478
+ return False
479
+
369
480
 
@@ -12,7 +12,7 @@ from tabulate import tabulate
12
12
  from dataclasses import dataclass
13
13
  from concurrent.futures import ThreadPoolExecutor, as_completed
14
14
 
15
- from .net_tools import Device
15
+ from .net_tools import Device, is_arp_supported
16
16
  from .ip_parser import parse_ip_input
17
17
  from .port_manager import PortManager
18
18
  from.errors import SubnetScanTerminationFailure
@@ -80,6 +80,8 @@ class SubnetScanner:
80
80
  self.uid = str(uuid.uuid4())
81
81
  self.results = ScannerResults(self)
82
82
  self.log: logging.Logger = logging.getLogger('SubnetScanner')
83
+ if not is_arp_supported():
84
+ self.log.warning('ARP is not supported with the active runtime context. Device discovery will be limited to ping responses.')
83
85
  self.log.debug(f'Instantiated with uid: {self.uid}')
84
86
  self.log.debug(f'Port Count: {len(self.ports)} | Device Count: {len(self.subnet)}')
85
87
 
@@ -2,6 +2,7 @@ import unittest
2
2
 
3
3
  from ..libraries.version_manager import lookup_latest_version
4
4
  from ..libraries.app_scope import ResourceManager, is_local_run
5
+ from ..libraries.net_tools import is_arp_supported
5
6
 
6
7
 
7
8
 
@@ -20,5 +21,10 @@ class EnvTestCase(unittest.TestCase):
20
21
  def test_local_version(self):
21
22
  self.assertTrue(is_local_run())
22
23
 
24
+ def test_arp_support(self):
25
+ arp_supported = is_arp_supported()
26
+ self.assertIn(arp_supported, [True, False],
27
+ f"ARP support should be either True or False, not {arp_supported}"
28
+ )
23
29
 
24
30
 
lanscape/ui/app.py CHANGED
@@ -9,6 +9,7 @@ import os
9
9
  from ..libraries.runtime_args import RuntimeArgs, parse_args
10
10
  from ..libraries.version_manager import is_update_available, get_installed_version, lookup_latest_version
11
11
  from ..libraries.app_scope import is_local_run
12
+ from ..libraries.net_tools import is_arp_supported
12
13
 
13
14
  app = Flask(
14
15
  __name__
@@ -57,6 +58,7 @@ set_global_safe('update_available', is_update_available)
57
58
  set_global_safe('latest_version',lookup_latest_version)
58
59
  set_global_safe('runtime_args', vars(parse_args()))
59
60
  set_global_safe('is_local',is_local_run)
61
+ set_global_safe('is_arp_supported', is_arp_supported)
60
62
 
61
63
  ## External hook to kill flask server
62
64
  ################################
lanscape/ui/main.py CHANGED
@@ -7,6 +7,7 @@ import os
7
7
  from ..libraries.logger import configure_logging
8
8
  from ..libraries.runtime_args import parse_args, RuntimeArgs
9
9
  from ..libraries.web_browser import open_webapp
10
+ from ..libraries.net_tools import is_arp_supported
10
11
  # do this so any logs generated on import are displayed
11
12
  args = parse_args()
12
13
  configure_logging(args.loglevel, args.logfile, args.flask_logging)
@@ -43,8 +44,11 @@ def _main():
43
44
  log.info('Flask reloaded app.')
44
45
 
45
46
  args.port = get_valid_port(args.port)
46
-
47
-
47
+
48
+ if not is_arp_supported():
49
+ log.warning('ARP is not supported, device discovery is degraded.')
50
+ log.warning('Help guide: https://github.com/mdennis281/LANscape/blob/main/support/arp-issues.md')
51
+
48
52
  try:
49
53
  start_webserver_ui(args)
50
54
  log.info('Exiting...')
@@ -11,6 +11,8 @@
11
11
  --danger-accent: #ff5252; /* Bright red for warnings */
12
12
  --danger-accent-hover: #e64545;
13
13
 
14
+ --danger-accent-transparent: rgba(255, 82, 82, 0.3);/* Light red for subtle danger indication */
15
+
14
16
  --warning-accent: #cc8801; /* Vibrant amber for warnings */
15
17
  --warning-accent-hover: #d08802;
16
18
 
@@ -56,6 +58,7 @@ body:has(.submodule) footer {
56
58
  box-shadow: 0 0 10px var(--box-shadow);
57
59
  width: 90%;
58
60
  margin-top: 10px;
61
+ overflow: hidden;
59
62
  }
60
63
 
61
64
  #header {
@@ -255,18 +258,7 @@ details {
255
258
 
256
259
 
257
260
 
258
- @media screen and (max-width: 681px) {
259
- #power-button {
260
- left: auto;
261
- right: 0;
262
- border-width: 0 0 1px 1px;
263
- border-radius: 0 0 0 5px;
264
- }
265
- .container-fluid {
266
- width:98%;
267
- padding: 8px;
268
- }
269
- }
261
+
270
262
 
271
263
  /* Card Styles */
272
264
  .card {
@@ -692,7 +684,36 @@ html {
692
684
  background-color: var(--warning-accent);
693
685
  }
694
686
 
695
- /* width of iFrame, not page */
687
+ #arp-error {
688
+ width: calc(100% + 40px);
689
+ position: relative;
690
+ display: flex;
691
+ justify-content: center;
692
+ background-color: var(--danger-accent-transparent);
693
+ color: var(--text-color);
694
+ transform: translate3d(-20px, -20px, 0);
695
+ font-size: small;
696
+ }
697
+ #arp-error span {
698
+ text-align: center;
699
+ }
700
+
701
+ @media screen and (max-width: 681px) {
702
+ #power-button {
703
+ left: auto;
704
+ right: 0;
705
+ border-width: 0 0 1px 1px;
706
+ border-radius: 0 0 0 5px;
707
+ }
708
+ .container-fluid {
709
+ width:98%;
710
+ padding: 8px;
711
+ }
712
+ #arp-error {
713
+ width: calc(100% + 16px);
714
+ transform: translate3d(-8px, -8px, 0);
715
+ }
716
+ }
696
717
 
697
718
  @media screen and (max-width: 885px) {
698
719
  #overview-container .col-4 {
@@ -3,4 +3,7 @@
3
3
  <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
4
4
  <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"></script>
5
5
  <script src="{{ url_for('static', filename='js/core.js') }}"></script>
6
+
7
+ {% if not section is defined %}
6
8
  <script src="{{ url_for('static', filename='js/on-tab-close.js') }}"></script>
9
+ {% endif %}
@@ -49,6 +49,13 @@
49
49
  </div>
50
50
  <div id="content">
51
51
  <div class="container-fluid my-4">
52
+ <!-- ARP Error -->
53
+ <div id="arp-error" class="{{ 'div-hide' if is_arp_supported else '' }}">
54
+ <span>
55
+ Unable to discover devices using ARP. Device discovery is degraded.
56
+ <a target="_blank" href="https://github.com/mdennis281/LANscape/blob/main/support/arp-issues.md">Steps to fix</a>
57
+ </span>
58
+ </div>
52
59
  <!-- Scan Results -->
53
60
  <div id="scan-results" class="div-hide">
54
61
  <div class="d-flex justify-content-between">
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lanscape
3
- Version: 1.3.1a7
3
+ Version: 1.3.1a8
4
4
  Summary: A python based local network scanner
5
5
  Author-email: Michael Dennis <michael@dipduo.com>
6
6
  Project-URL: Homepage, https://github.com/mdennis281/py-lanscape
@@ -6,11 +6,11 @@ lanscape/libraries/errors.py,sha256=DaercNEZD_tUuXF7KsNk3SD6AqAwT-S7fvzpEybVn08,
6
6
  lanscape/libraries/ip_parser.py,sha256=ElXz3LU5CUYWqKOHEyrj5Y4Iv6OBtoSlbCcxhCsibfQ,2226
7
7
  lanscape/libraries/logger.py,sha256=doD8KKb4TNWDwVXc1VR7NK4UdharrAoRHl8vZnSAupI,1407
8
8
  lanscape/libraries/mac_lookup.py,sha256=-dRV0ygtjjh3JkgL3GTi_5-w7pcZ1oj4XVH4chjsmRs,2121
9
- lanscape/libraries/net_tools.py,sha256=_XWdZjboJ76dIoi90gDkk2fUzvEKHPsy2TQ7URj7QlE,12040
9
+ lanscape/libraries/net_tools.py,sha256=dUGPwiOudr6I3flMrTyUumpve4pJrt_lST3l8fOOFxs,17117
10
10
  lanscape/libraries/port_manager.py,sha256=fNext3FNfGnGYRZK9RhTEwQ2K0e0YmmMlhK4zVAvoCw,1977
11
11
  lanscape/libraries/runtime_args.py,sha256=ICX_JkOmqDQdewZNfRxJb9jMggDw1XqF5CxM9zXZE_Q,1947
12
12
  lanscape/libraries/service_scan.py,sha256=jLU84ZoJnqSQbE30Zly2lm2zHrCGutNXjla1sEvp1hE,1949
13
- lanscape/libraries/subnet_scan.py,sha256=0LW_xdoL-PRp59rJr6r6pSL3LiXEO_SJnjdrgEF_pO8,13120
13
+ lanscape/libraries/subnet_scan.py,sha256=nT1-P9UkIneRcgfPNwZveiYYB4T-qnS6fv21tYRhCGQ,13313
14
14
  lanscape/libraries/version_manager.py,sha256=3n0PV_EdW9Fgg2yxD2uzA4WYvzk6H6hMf_1gfvtZbp0,1730
15
15
  lanscape/libraries/web_browser.py,sha256=EuKJG3bmBZUochDGm9a0qrB6JkIkOkvx4njZBe4eLIQ,5028
16
16
  lanscape/resources/mac_addresses/convert_csv.py,sha256=w3Heed5z2mHYDEZNBep3_hNg4dbrp_N6J54MGxnrq4s,721
@@ -24,10 +24,10 @@ lanscape/resources/services/definitions.jsonc,sha256=71w9Q7r4RoBYiIMkzzO2KdEJXaS
24
24
  lanscape/tests/__init__.py,sha256=xYPeceOF-ppTS0wnq7CkVYQMwupmeSHxbYLbGj_imZ0,113
25
25
  lanscape/tests/_helpers.py,sha256=wXJfUwzL3Fq4XBsC3dValCbXsf0U8FisuM_yo1de4QQ,371
26
26
  lanscape/tests/test_api.py,sha256=sjcjt3b1ZwvQiALOSfXoQxTlN1Z1_TRPC0EEy-bRIgE,7313
27
- lanscape/tests/test_env.py,sha256=ivFhCcemJ9vbe0_KtUkbqDY4r9nsDB8rVLUVjV-sNj8,673
27
+ lanscape/tests/test_env.py,sha256=7gfekAAfzlQxHmYG3MwDQpABDDkRomKWBhmIHFPq0jw,946
28
28
  lanscape/tests/test_library.py,sha256=OPcTsUoR5IureSNDbePxid2BG98mfNNIJmCIY0BVz3w,1553
29
- lanscape/ui/app.py,sha256=jMr72omFHOJJWxb9vwfpC7Lg-YIpw-4sZFAbWHnx_D4,3594
30
- lanscape/ui/main.py,sha256=KObgvdOlIhx4VQgcaNLcLrnkFXmOhfVpUyI8tZDQmJo,4027
29
+ lanscape/ui/app.py,sha256=WfoWmNMkt9_58Dn8rX4vs59rchQ8E2d_6vGOvaINYbg,3701
30
+ lanscape/ui/main.py,sha256=h-iV9gvMmMyXDtB_w5HdWEEuL68nmJavZNshDKqXpBQ,4278
31
31
  lanscape/ui/blueprints/__init__.py,sha256=agvgPOSVbrxddaw6EY64ZZr1CQi1Qzwcs1t0lZMv5oY,206
32
32
  lanscape/ui/blueprints/api/__init__.py,sha256=t0QOq3vHFWmlZm_3YFPQbQzCn1a_a5cmRchtIxwy4eY,103
33
33
  lanscape/ui/blueprints/api/port.py,sha256=2UA38umzXE8pMitx1E-_wJHyL1dYYbtM6Kg5zVtfj6A,1019
@@ -36,7 +36,7 @@ lanscape/ui/blueprints/api/tools.py,sha256=CD0NDSX8kN6_lpl0jEw-ULLsDx7pKODCMFQia
36
36
  lanscape/ui/blueprints/web/__init__.py,sha256=-WRjENG8D99NfaiSDk9uAa8OX6XJq9Zmq1ck29ARL-w,92
37
37
  lanscape/ui/blueprints/web/routes.py,sha256=hl89T5_oRbTlA9Cde_xh9zAQDGRRH-Dpwm74B1_NM1Y,2511
38
38
  lanscape/ui/static/lanscape.webmanifest,sha256=0aauJk_Bybd0B2iwzJfvPcs7AX43kVHs0dtpV6_jSWk,459
39
- lanscape/ui/static/css/style.css,sha256=ZoROzQmBIZlMdz0i9YHkx0Eomo87nQcDLJnGiiC0S3Y,15838
39
+ lanscape/ui/static/css/style.css,sha256=DJKGnsv7jpjXemVQRPNIWE_THTqNfjzPVs49ysRbBGM,16372
40
40
  lanscape/ui/static/img/ico/android-chrome-192x192.png,sha256=JmFT6KBCCuoyxMV-mLNtF9_QJbVBvfWPUizKN700fi8,18255
41
41
  lanscape/ui/static/img/ico/android-chrome-512x512.png,sha256=88Jjx_1-4XAnZYz64KP6FdTl_kYkNG2_kQIKteQwSh4,138055
42
42
  lanscape/ui/static/img/ico/apple-touch-icon.png,sha256=tEJlLwBZtF4v-NC90YCfRJQ2prTsF4i3VQLK_hnv2Mw,16523
@@ -55,18 +55,18 @@ lanscape/ui/static/js/subnet-selector.js,sha256=OG01pDaSOPLq3Ial0aO0CqPcob9tPZA1
55
55
  lanscape/ui/templates/base.html,sha256=P5xnMlvDXYkYSXdSZUWaRfhsszNuZPP7A56hemBrAFs,1498
56
56
  lanscape/ui/templates/error.html,sha256=zXFO0zPIfQORWq1ZMiSZ8G7FjfhVVr-aaYC0HeBl4Rs,1068
57
57
  lanscape/ui/templates/info.html,sha256=ZRx0Q92viaIsD0H8Rd1oGMdFIsZtBal7-tcKvt9WJIY,2586
58
- lanscape/ui/templates/main.html,sha256=M12xJSN6Ga565vIPhdCiqcr1tYgDrqzuQTeuXtk-8yo,3759
58
+ lanscape/ui/templates/main.html,sha256=Xu3SDFXlnW6qkH-nqzt4b4Wq9Q6LG5FT5zeKiYXmpks,4143
59
59
  lanscape/ui/templates/scan.html,sha256=Fz1Q4CzRq5qpKgszTAQLhaLVV0A6gBraT33mNDmpYRE,390
60
60
  lanscape/ui/templates/shutdown.html,sha256=v0cGT5CJWi-V8b5sUN3l-QIDNUmHTvKGi2gDlhmRlrs,724
61
61
  lanscape/ui/templates/core/head.html,sha256=6XyoDIz-IMPIRG-ti2LFY9FFX39UTIf_K6-6USni_ek,788
62
- lanscape/ui/templates/core/scripts.html,sha256=5dg0KsPnBPKXIuCah3CKCJRE7aijBE3dhUXMUzj15-U,601
62
+ lanscape/ui/templates/core/scripts.html,sha256=31e62wrNUs8pQLp9unx3iSLT4YuPXgZoHUrGYTdt-8w,649
63
63
  lanscape/ui/templates/scan/export.html,sha256=Qi0m2xJPbC5I2rxzekXjvQ6q9gm2Lr4VJW6riLhIaU0,776
64
64
  lanscape/ui/templates/scan/ip-table-row.html,sha256=ptY24rxJRaA4PEEQRDncaq6Q0ql5RJ87Kn0zKRCzOHw,4842
65
65
  lanscape/ui/templates/scan/ip-table.html,sha256=ds__UP9JiTKf5IxCmTMzw--eN_yg1Pvn3Nj1KvQxeZg,940
66
66
  lanscape/ui/templates/scan/overview.html,sha256=FsX-jSFhGKwCxZGKE8AMKk328UuawN6O9RNTzYvIOts,1205
67
67
  lanscape/ui/templates/scan/scan-error.html,sha256=Q4eZM5ThrxnFaWOSTUpK8hA2ksHwhxOBTaVUCLALhyA,1032
68
- lanscape-1.3.1a7.dist-info/licenses/LICENSE,sha256=cCO-NbS01Ilwc6djHjZ7LIgPFRkRmWdr0fH2ysXKioA,1090
69
- lanscape-1.3.1a7.dist-info/METADATA,sha256=c2V2KRicArHyY67oW6gSl1jLq9jsag_noE8RY-veqFE,2567
70
- lanscape-1.3.1a7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
71
- lanscape-1.3.1a7.dist-info/top_level.txt,sha256=E9D4sjPz_6H7c85Ycy_pOS2xuv1Wm-ilKhxEprln2ps,9
72
- lanscape-1.3.1a7.dist-info/RECORD,,
68
+ lanscape-1.3.1a8.dist-info/licenses/LICENSE,sha256=cCO-NbS01Ilwc6djHjZ7LIgPFRkRmWdr0fH2ysXKioA,1090
69
+ lanscape-1.3.1a8.dist-info/METADATA,sha256=TbyhZCx4ZrzNChJDFmmF05B5smHwT1QripthSE5lPqA,2567
70
+ lanscape-1.3.1a8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
71
+ lanscape-1.3.1a8.dist-info/top_level.txt,sha256=E9D4sjPz_6H7c85Ycy_pOS2xuv1Wm-ilKhxEprln2ps,9
72
+ lanscape-1.3.1a8.dist-info/RECORD,,