lanscape 2.1.0b1__py3-none-any.whl → 2.1.2__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.
- lanscape/core/device_alive.py +80 -13
- lanscape/core/net_tools.py +52 -2
- lanscape/core/subnet_scan.py +13 -4
- {lanscape-2.1.0b1.dist-info → lanscape-2.1.2.dist-info}/METADATA +3 -1
- {lanscape-2.1.0b1.dist-info → lanscape-2.1.2.dist-info}/RECORD +9 -9
- {lanscape-2.1.0b1.dist-info → lanscape-2.1.2.dist-info}/WHEEL +0 -0
- {lanscape-2.1.0b1.dist-info → lanscape-2.1.2.dist-info}/entry_points.txt +0 -0
- {lanscape-2.1.0b1.dist-info → lanscape-2.1.2.dist-info}/licenses/LICENSE +0 -0
- {lanscape-2.1.0b1.dist-info → lanscape-2.1.2.dist-info}/top_level.txt +0 -0
lanscape/core/device_alive.py
CHANGED
|
@@ -12,8 +12,9 @@ import psutil
|
|
|
12
12
|
from scapy.sendrecv import srp
|
|
13
13
|
from scapy.layers.l2 import ARP, Ether
|
|
14
14
|
from icmplib import ping
|
|
15
|
+
from icmplib.exceptions import SocketPermissionError
|
|
15
16
|
|
|
16
|
-
from lanscape.core.net_tools import Device
|
|
17
|
+
from lanscape.core.net_tools import Device, DeviceError
|
|
17
18
|
from lanscape.core.scan_config import (
|
|
18
19
|
ScanConfig, ScanType, PingConfig,
|
|
19
20
|
ArpConfig, PokeConfig, ArpCacheConfig
|
|
@@ -72,18 +73,84 @@ class IcmpLookup():
|
|
|
72
73
|
Returns:
|
|
73
74
|
bool: True if the device is reachable via ICMP, False otherwise.
|
|
74
75
|
"""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
try:
|
|
77
|
+
# Try using icmplib first
|
|
78
|
+
for _ in range(cfg.attempts):
|
|
79
|
+
result = ping(
|
|
80
|
+
device.ip,
|
|
81
|
+
count=cfg.ping_count,
|
|
82
|
+
interval=cfg.retry_delay,
|
|
83
|
+
timeout=cfg.timeout,
|
|
84
|
+
privileged=psutil.WINDOWS # Use privileged mode on Windows
|
|
85
|
+
)
|
|
86
|
+
if result.is_alive:
|
|
87
|
+
device.alive = True
|
|
88
|
+
break
|
|
89
|
+
return device.alive is True
|
|
90
|
+
except SocketPermissionError:
|
|
91
|
+
# Fallback to system ping command when raw sockets aren't available
|
|
92
|
+
return cls._ping_fallback(device, cfg)
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def _ping_fallback(cls, device: Device, cfg: PingConfig) -> bool:
|
|
96
|
+
"""Fallback ping using system ping command via subprocess.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
device (Device): The device to ping.
|
|
100
|
+
cfg (PingConfig): The ping configuration.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
bool: True if the device responds to ping, False otherwise.
|
|
104
|
+
"""
|
|
105
|
+
cmd = []
|
|
106
|
+
|
|
107
|
+
if psutil.WINDOWS:
|
|
108
|
+
# -n count, -w timeout in ms
|
|
109
|
+
cmd = ['ping', '-n', str(cfg.ping_count), '-w', str(int(cfg.timeout * 1000)), device.ip]
|
|
110
|
+
else: # Linux, macOS, and other Unix-like systems
|
|
111
|
+
# -c count, -W timeout in s
|
|
112
|
+
cmd = ['ping', '-c', str(cfg.ping_count), '-W', str(int(cfg.timeout)), device.ip]
|
|
113
|
+
|
|
114
|
+
for r in range(cfg.attempts):
|
|
115
|
+
try:
|
|
116
|
+
# Remove check=True to handle return codes manually
|
|
117
|
+
# Add timeout to prevent hanging
|
|
118
|
+
timeout_val = cfg.timeout * cfg.ping_count + 5
|
|
119
|
+
proc = subprocess.run(
|
|
120
|
+
cmd,
|
|
121
|
+
text=True,
|
|
122
|
+
stdout=subprocess.PIPE,
|
|
123
|
+
stderr=subprocess.PIPE,
|
|
124
|
+
timeout=timeout_val,
|
|
125
|
+
check=False # Handle return codes manually
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Check if ping was successful
|
|
129
|
+
if proc.returncode == 0:
|
|
130
|
+
output = proc.stdout.lower()
|
|
131
|
+
|
|
132
|
+
# Windows/Linux both include "TTL" on a successful reply
|
|
133
|
+
if psutil.WINDOWS or psutil.LINUX:
|
|
134
|
+
if 'ttl' in output:
|
|
135
|
+
device.alive = True
|
|
136
|
+
return True # Early return on success
|
|
137
|
+
|
|
138
|
+
# some distributions of Linux and macOS
|
|
139
|
+
if psutil.MACOS or psutil.LINUX:
|
|
140
|
+
bad = '100.0% packet loss'
|
|
141
|
+
good = 'ping statistics'
|
|
142
|
+
# mac doesnt include TTL, so we check good is there, and bad is not
|
|
143
|
+
if good in output and bad not in output:
|
|
144
|
+
device.alive = True
|
|
145
|
+
return True # Early return on success
|
|
146
|
+
|
|
147
|
+
except (subprocess.CalledProcessError, subprocess.TimeoutExpired,
|
|
148
|
+
FileNotFoundError) as e:
|
|
149
|
+
device.caught_errors.append(DeviceError(e))
|
|
150
|
+
|
|
151
|
+
if r < cfg.attempts - 1:
|
|
152
|
+
time.sleep(cfg.retry_delay)
|
|
153
|
+
|
|
87
154
|
return device.alive is True
|
|
88
155
|
|
|
89
156
|
|
lanscape/core/net_tools.py
CHANGED
|
@@ -31,10 +31,10 @@ else:
|
|
|
31
31
|
|
|
32
32
|
from lanscape.core.service_scan import scan_service
|
|
33
33
|
from lanscape.core.mac_lookup import MacLookup, get_macs
|
|
34
|
-
from lanscape.core.ip_parser import get_address_count, MAX_IPS_ALLOWED
|
|
34
|
+
from lanscape.core.ip_parser import get_address_count, MAX_IPS_ALLOWED, parse_ip_input
|
|
35
35
|
from lanscape.core.errors import DeviceError
|
|
36
36
|
from lanscape.core.decorators import job_tracker, run_once, timeout_enforcer
|
|
37
|
-
from lanscape.core.scan_config import ServiceScanConfig, PortScanConfig
|
|
37
|
+
from lanscape.core.scan_config import ServiceScanConfig, PortScanConfig, ScanType
|
|
38
38
|
|
|
39
39
|
log = logging.getLogger('NetTools')
|
|
40
40
|
mac_lookup = MacLookup()
|
|
@@ -552,6 +552,56 @@ def smart_select_primary_subnet(subnets: List[dict] = None) -> str:
|
|
|
552
552
|
return selected.get("subnet", "")
|
|
553
553
|
|
|
554
554
|
|
|
555
|
+
def is_internal_block(subnet: str) -> bool:
|
|
556
|
+
"""
|
|
557
|
+
Check if a subnet contains only internal/private IP addresses.
|
|
558
|
+
|
|
559
|
+
Supports CIDR notation, IP ranges, comma-separated lists, and single IPs.
|
|
560
|
+
For ranges and complex inputs, samples representative IPs for efficiency.
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
subnet: IP subnet string in various formats
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
bool: True if all sampled IPs are private/internal, False otherwise
|
|
567
|
+
"""
|
|
568
|
+
try:
|
|
569
|
+
# Handle comma-separated subnets recursively
|
|
570
|
+
if ',' in subnet:
|
|
571
|
+
return all(is_internal_block(part.strip()) for part in subnet.split(','))
|
|
572
|
+
|
|
573
|
+
# Handle CIDR notation directly
|
|
574
|
+
if '/' in subnet:
|
|
575
|
+
return ipaddress.IPv4Network(subnet, strict=False).is_private
|
|
576
|
+
|
|
577
|
+
# Handle ranges and single IPs by parsing and sampling
|
|
578
|
+
ip_list = parse_ip_input(subnet)
|
|
579
|
+
sample_ips = ([ip_list[0], ip_list[-1]] if len(ip_list) > 1 else ip_list)
|
|
580
|
+
return all(ipaddress.IPv4Address(ip).is_private for ip in sample_ips)
|
|
581
|
+
|
|
582
|
+
except (ValueError, ipaddress.AddressValueError):
|
|
583
|
+
return False # Assume external for unparseable input
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def scan_config_uses_arp(config) -> bool:
|
|
587
|
+
"""
|
|
588
|
+
Check if a scan configuration uses ARP-based scanning methods.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
config: ScanConfig instance
|
|
592
|
+
|
|
593
|
+
Returns:
|
|
594
|
+
bool: True if the configuration uses ARP scanning, False otherwise
|
|
595
|
+
"""
|
|
596
|
+
arp_scan_types = {
|
|
597
|
+
ScanType.ARP_LOOKUP,
|
|
598
|
+
ScanType.POKE_THEN_ARP,
|
|
599
|
+
ScanType.ICMP_THEN_ARP
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return any(scan_type in arp_scan_types for scan_type in config.lookup_type)
|
|
603
|
+
|
|
604
|
+
|
|
555
605
|
@run_once
|
|
556
606
|
def is_arp_supported():
|
|
557
607
|
"""
|
lanscape/core/subnet_scan.py
CHANGED
|
@@ -22,7 +22,9 @@ from tabulate import tabulate
|
|
|
22
22
|
# Local imports
|
|
23
23
|
from lanscape.core.scan_config import ScanConfig
|
|
24
24
|
from lanscape.core.decorators import job_tracker, terminator, JobStats
|
|
25
|
-
from lanscape.core.net_tools import
|
|
25
|
+
from lanscape.core.net_tools import (
|
|
26
|
+
Device, is_internal_block, scan_config_uses_arp
|
|
27
|
+
)
|
|
26
28
|
from lanscape.core.errors import SubnetScanTerminationFailure
|
|
27
29
|
from lanscape.core.device_alive import is_device_alive
|
|
28
30
|
|
|
@@ -301,11 +303,11 @@ class ScannerResults:
|
|
|
301
303
|
Calculate the runtime of the scan in seconds.
|
|
302
304
|
|
|
303
305
|
Returns:
|
|
304
|
-
|
|
306
|
+
float: Runtime in seconds
|
|
305
307
|
"""
|
|
306
308
|
if self.scan.running:
|
|
307
|
-
return
|
|
308
|
-
return
|
|
309
|
+
return time() - self.start_time
|
|
310
|
+
return self.end_time - self.start_time
|
|
309
311
|
|
|
310
312
|
def export(self, out_type=dict) -> Union[str, dict]:
|
|
311
313
|
"""
|
|
@@ -385,6 +387,13 @@ class ScanManager:
|
|
|
385
387
|
Returns:
|
|
386
388
|
SubnetScanner: The newly created scan instance
|
|
387
389
|
"""
|
|
390
|
+
if not is_internal_block(config.subnet) and scan_config_uses_arp(config):
|
|
391
|
+
self.log.warning(
|
|
392
|
+
f"ARP scanning detected for external subnet '{config.subnet}'. "
|
|
393
|
+
"ARP requests typically only work within the local network segment. "
|
|
394
|
+
"Consider using ICMP scanning for external IP ranges."
|
|
395
|
+
)
|
|
396
|
+
|
|
388
397
|
scan = SubnetScanner(config)
|
|
389
398
|
self._start(scan)
|
|
390
399
|
self.log.info(f'Scan started - {config}')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lanscape
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: A python based local network scanner
|
|
5
5
|
Author-email: Michael Dennis <michael@dipduo.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -28,6 +28,8 @@ Requires-Dist: icmplib
|
|
|
28
28
|
Provides-Extra: dev
|
|
29
29
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
30
30
|
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-xdist>=3.0; extra == "dev"
|
|
32
|
+
Requires-Dist: openai>=1.0.0; extra == "dev"
|
|
31
33
|
Dynamic: license-file
|
|
32
34
|
|
|
33
35
|
# LANscape
|
|
@@ -3,17 +3,17 @@ lanscape/__main__.py,sha256=PuY42yuCLAwHrOREJ6u2DgVyGX5hZKRQeoE9pajkNfM,170
|
|
|
3
3
|
lanscape/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
lanscape/core/app_scope.py,sha256=qfzX8Ed4bFdxHMGjgnLlWuLZDTCBKObermz91KbGVn0,3298
|
|
5
5
|
lanscape/core/decorators.py,sha256=CZbPEfnLS1OF-uejQweetadzqf0pVo736jKko4Xs-g4,7264
|
|
6
|
-
lanscape/core/device_alive.py,sha256=
|
|
6
|
+
lanscape/core/device_alive.py,sha256=VY2dsoy6_MWUxcysZEFcsSoCtDkLMYzwqy0U_sbVWE0,9609
|
|
7
7
|
lanscape/core/errors.py,sha256=QTf42UzR9Zxj1t1mdwfLvZIp0c9a5EItELOdCR7kTmE,1322
|
|
8
8
|
lanscape/core/ip_parser.py,sha256=kn5H4ERitLnreRAqifWphwbxdjItGqwu50lsMCPDMcA,3474
|
|
9
9
|
lanscape/core/logger.py,sha256=nzo6J8UdlMdhRkOJEDOIHKztoE3Du8PQZad7ixvNgeM,2534
|
|
10
10
|
lanscape/core/mac_lookup.py,sha256=PxBSMe3wEVDtivCsh5NclSAguZz9rqdAS7QshBiuWvM,3519
|
|
11
|
-
lanscape/core/net_tools.py,sha256=
|
|
11
|
+
lanscape/core/net_tools.py,sha256=Ht8TkLnKf-f6E1_AkTCde-yuiwUNI57TCXke0nIFi0o,21303
|
|
12
12
|
lanscape/core/port_manager.py,sha256=3_ROOb6JEiB0NByZVtADuGcldFkgZwn1RKtvwgs9AIk,4479
|
|
13
13
|
lanscape/core/runtime_args.py,sha256=2vIqRrcWr-NHRSBlZGrxh1PdkPY0ytkPguu8KZqy2L8,2543
|
|
14
14
|
lanscape/core/scan_config.py,sha256=A2ZKXqXKW9nrP6yLb7b9b3XqSY_cQB3LZ5K0LVCSebE,11114
|
|
15
15
|
lanscape/core/service_scan.py,sha256=wTDxOdazOsbI0hwCBR__4UCmB2RIbl2pw3F2YUW9aaE,6428
|
|
16
|
-
lanscape/core/subnet_scan.py,sha256=
|
|
16
|
+
lanscape/core/subnet_scan.py,sha256=PtSOk92dK05-reyr8LBkOXaI15qpYnar5nDqALCX1tQ,14850
|
|
17
17
|
lanscape/core/version_manager.py,sha256=eGjyKgsv31QO0W26se9pPQ1TwmEN8qn37dHULtoocqc,2841
|
|
18
18
|
lanscape/core/web_browser.py,sha256=23MuGIrBYdGhw6ejj6OWxwReeKIlWhtWukc1dKV_3_0,6736
|
|
19
19
|
lanscape/resources/mac_addresses/convert_csv.py,sha256=hvlyLs0XmuuhBuvXBNRGP1cKJzYVRSf8VfUJ1VqROms,1189
|
|
@@ -69,9 +69,9 @@ lanscape/ui/templates/scan/ip-table-row.html,sha256=iSW3PYev3_k7pxTZUJUboqDUgdhs
|
|
|
69
69
|
lanscape/ui/templates/scan/ip-table.html,sha256=AT2ZvCPYdKl-XJiAkEAawPOVuQw-w0MXumGQTr3zyKM,926
|
|
70
70
|
lanscape/ui/templates/scan/overview.html,sha256=xWj9jWDPg2KcPLvS8fnSins23_UXjKCdb2NJwNG2U2Q,1176
|
|
71
71
|
lanscape/ui/templates/scan/scan-error.html,sha256=wmAYQ13IJHUoO8fAGNDjMvNml7tu4rsIU3Vav71ETlA,999
|
|
72
|
-
lanscape-2.1.
|
|
73
|
-
lanscape-2.1.
|
|
74
|
-
lanscape-2.1.
|
|
75
|
-
lanscape-2.1.
|
|
76
|
-
lanscape-2.1.
|
|
77
|
-
lanscape-2.1.
|
|
72
|
+
lanscape-2.1.2.dist-info/licenses/LICENSE,sha256=VLoE0IrNTIc09dFm7hMN0qzk4T3q8V0NaPcFQqMemDs,1070
|
|
73
|
+
lanscape-2.1.2.dist-info/METADATA,sha256=oxHxoHbAYnhTnSIgsMyzzgqevSrWmF5Fzti1bqjLAao,3578
|
|
74
|
+
lanscape-2.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
75
|
+
lanscape-2.1.2.dist-info/entry_points.txt,sha256=evxSxUikFa1OEd4e0Boky9sLH87HdgM0YqB_AbB2HYc,51
|
|
76
|
+
lanscape-2.1.2.dist-info/top_level.txt,sha256=E9D4sjPz_6H7c85Ycy_pOS2xuv1Wm-ilKhxEprln2ps,9
|
|
77
|
+
lanscape-2.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|