lanscape 1.2.7a1__tar.gz → 1.2.7a2__tar.gz
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-1.2.7a1/src/lanscape.egg-info → lanscape-1.2.7a2}/PKG-INFO +1 -1
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/pyproject.toml +1 -1
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/libraries/mac_lookup.py +12 -9
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/libraries/net_tools.py +55 -9
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/libraries/subnet_scan.py +2 -3
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/tests/test_library.py +2 -2
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/webviewer.py +2 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2/src/lanscape.egg-info}/PKG-INFO +1 -1
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/LICENSE +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/MANIFEST.in +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/README.md +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/setup.cfg +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/__init__.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/__main__.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/libraries/app_scope.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/libraries/decorators.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/libraries/errors.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/libraries/ip_parser.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/libraries/logger.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/libraries/port_manager.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/libraries/runtime_args.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/libraries/version_manager.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/resources/mac_addresses/convert_csv.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/resources/mac_addresses/mac_db.json +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/resources/ports/convert_csv.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/resources/ports/full.json +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/resources/ports/large.json +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/resources/ports/medium.json +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/resources/ports/small.json +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/tests/__init__.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/tests/_helpers.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/tests/test_api.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/tests/test_env.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/tests/test_webview.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/app.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/blueprints/__init__.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/blueprints/api/__init__.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/blueprints/api/port.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/blueprints/api/scan.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/blueprints/api/tools.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/blueprints/web/__init__.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/blueprints/web/routes.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/main.py +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/css/style.css +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/img/ico/android-chrome-192x192.png +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/img/ico/android-chrome-512x512.png +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/img/ico/apple-touch-icon.png +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/img/ico/favicon-16x16.png +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/img/ico/favicon-32x32.png +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/img/ico/favicon.ico +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/img/ico/site.webmanifest +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/img/readme1.png +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/js/core.js +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/js/layout-sizing.js +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/js/main.js +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/js/quietReload.js +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/js/shutdown-server.js +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/js/subnet-info.js +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/js/subnet-selector.js +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/lanscape.webmanifest +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/base.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/core/head.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/core/scripts.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/error.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/info.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/main.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/scan/export.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/scan/ip-table-row.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/scan/ip-table.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/scan/overview.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/scan/scan-error.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/scan.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/templates/shutdown.html +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape.egg-info/SOURCES.txt +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape.egg-info/dependency_links.txt +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape.egg-info/requires.txt +0 -0
- {lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape.egg-info/top_level.txt +0 -0
|
@@ -3,7 +3,7 @@ import json
|
|
|
3
3
|
import logging
|
|
4
4
|
import platform
|
|
5
5
|
import subprocess
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import List
|
|
7
7
|
|
|
8
8
|
from .app_scope import ResourceManager
|
|
9
9
|
|
|
@@ -22,7 +22,7 @@ def lookup_mac(mac: str) -> str:
|
|
|
22
22
|
return DB[m]
|
|
23
23
|
return None
|
|
24
24
|
|
|
25
|
-
def
|
|
25
|
+
def get_macs(ip: str) -> List[str]:
|
|
26
26
|
"""Try to get the MAC address using Scapy, fallback to ARP if it fails."""
|
|
27
27
|
if mac := get_mac_by_scapy(ip):
|
|
28
28
|
log.debug(f"Used Scapy to resolve ip {ip} to mac {mac}")
|
|
@@ -31,7 +31,8 @@ def get_mac(ip: str) -> Optional[str]:
|
|
|
31
31
|
log.debug(f"Used ARP to resolve ip {ip} to mac {arp}")
|
|
32
32
|
return arp
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
def get_mac_by_arp(ip: str) -> List[str]:
|
|
35
36
|
"""Retrieve the last MAC address instance using the ARP command."""
|
|
36
37
|
try:
|
|
37
38
|
# Use the appropriate ARP command based on the platform
|
|
@@ -44,11 +45,11 @@ def get_mac_by_arp(ip: str) -> Optional[str]:
|
|
|
44
45
|
|
|
45
46
|
macs = re.findall(r'..:..:..:..:..:..', output)
|
|
46
47
|
# found that typically last mac is the correct one
|
|
47
|
-
return macs
|
|
48
|
+
return macs
|
|
48
49
|
except:
|
|
49
|
-
return
|
|
50
|
+
return []
|
|
50
51
|
|
|
51
|
-
def get_mac_by_scapy(ip: str) ->
|
|
52
|
+
def get_mac_by_scapy(ip: str) -> List[str]:
|
|
52
53
|
"""Retrieve the MAC address using the Scapy library."""
|
|
53
54
|
try:
|
|
54
55
|
from scapy.all import ARP, Ether, srp
|
|
@@ -61,7 +62,9 @@ def get_mac_by_scapy(ip: str) -> Optional[str]:
|
|
|
61
62
|
# Send the packet and wait for a response
|
|
62
63
|
result = srp(packet, timeout=1, verbose=0)[0]
|
|
63
64
|
|
|
64
|
-
# Extract the MAC
|
|
65
|
-
return
|
|
65
|
+
# Extract the MAC addresses from the response
|
|
66
|
+
return [res[1].hwsrc for res in result]
|
|
67
|
+
# return result[0][1].hwsrc if result else None
|
|
66
68
|
except:
|
|
67
|
-
return None
|
|
69
|
+
return None
|
|
70
|
+
|
|
@@ -11,7 +11,7 @@ from time import sleep
|
|
|
11
11
|
from typing import List
|
|
12
12
|
from scapy.all import ARP, Ether, srp
|
|
13
13
|
|
|
14
|
-
from .mac_lookup import lookup_mac,
|
|
14
|
+
from .mac_lookup import lookup_mac, get_macs
|
|
15
15
|
from .ip_parser import get_address_count, MAX_IPS_ALLOWED
|
|
16
16
|
|
|
17
17
|
log = logging.getLogger('NetTools')
|
|
@@ -61,13 +61,15 @@ class IPAlive:
|
|
|
61
61
|
pass # Ping failed
|
|
62
62
|
sleep(retry_delay)
|
|
63
63
|
return False
|
|
64
|
+
|
|
65
|
+
|
|
64
66
|
|
|
65
67
|
class Device(IPAlive):
|
|
66
68
|
def __init__(self,ip:str):
|
|
67
69
|
self.ip: str = ip
|
|
68
70
|
self.alive: bool = None
|
|
69
71
|
self.hostname: str = None
|
|
70
|
-
self.
|
|
72
|
+
self.macs: List[str] = []
|
|
71
73
|
self.manufacturer: str = None
|
|
72
74
|
self.ports: List[int] = []
|
|
73
75
|
self.stage: str = 'found'
|
|
@@ -76,8 +78,16 @@ class Device(IPAlive):
|
|
|
76
78
|
def get_metadata(self):
|
|
77
79
|
if self.alive:
|
|
78
80
|
self.hostname = self._get_hostname()
|
|
79
|
-
self.
|
|
80
|
-
|
|
81
|
+
self.macs = self._get_mac_addresses()
|
|
82
|
+
|
|
83
|
+
def dict(self) -> dict:
|
|
84
|
+
obj = vars(self).copy()
|
|
85
|
+
obj.pop('log')
|
|
86
|
+
primary_mac = self.get_mac()
|
|
87
|
+
obj['mac_addr'] = primary_mac
|
|
88
|
+
obj['manufacturer'] = self._get_manufacturer(primary_mac)
|
|
89
|
+
|
|
90
|
+
return obj
|
|
81
91
|
|
|
82
92
|
|
|
83
93
|
def test_port(self,port:int) -> bool:
|
|
@@ -89,12 +99,19 @@ class Device(IPAlive):
|
|
|
89
99
|
self.ports.append(port)
|
|
90
100
|
return True
|
|
91
101
|
return False
|
|
102
|
+
|
|
103
|
+
def get_mac(self):
|
|
104
|
+
if not self.macs:
|
|
105
|
+
self.macs = self._get_mac_addresses()
|
|
106
|
+
return mac_selector.choose_mac(self.macs)
|
|
92
107
|
|
|
93
|
-
def
|
|
108
|
+
def _get_mac_addresses(self):
|
|
94
109
|
"""
|
|
95
110
|
Get the MAC address of a network device given its IP address.
|
|
96
111
|
"""
|
|
97
|
-
|
|
112
|
+
macs = get_macs(self.ip)
|
|
113
|
+
mac_selector.import_macs(macs)
|
|
114
|
+
return macs
|
|
98
115
|
|
|
99
116
|
def _get_hostname(self):
|
|
100
117
|
"""
|
|
@@ -106,11 +123,38 @@ class Device(IPAlive):
|
|
|
106
123
|
except socket.herror:
|
|
107
124
|
return None
|
|
108
125
|
|
|
109
|
-
def _get_manufacturer(self):
|
|
126
|
+
def _get_manufacturer(self,mac_addr=None):
|
|
110
127
|
"""
|
|
111
128
|
Get the manufacturer of a network device given its MAC address.
|
|
112
129
|
"""
|
|
113
|
-
return lookup_mac(
|
|
130
|
+
return lookup_mac(mac_addr) if mac_addr else None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class MacSelector:
|
|
134
|
+
def __init__(self):
|
|
135
|
+
self.macs = {}
|
|
136
|
+
|
|
137
|
+
def choose_mac(self,macs:List[str]) -> str:
|
|
138
|
+
if len(macs) == 1:
|
|
139
|
+
return macs[0]
|
|
140
|
+
lowest = 9999
|
|
141
|
+
lowest_i = -1
|
|
142
|
+
|
|
143
|
+
for mac in macs:
|
|
144
|
+
if self.macs[mac] < lowest:
|
|
145
|
+
lowest = self.macs[mac]
|
|
146
|
+
lowest_i = macs.index(mac)
|
|
147
|
+
return macs[lowest_i] if lowest_i != -1 else None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def import_macs(self,macs:List[str]):
|
|
151
|
+
for mac in macs:
|
|
152
|
+
self.macs[mac] = self.macs.get(mac,0) + 1
|
|
153
|
+
|
|
154
|
+
def clear(self):
|
|
155
|
+
self.macs = {}
|
|
156
|
+
|
|
157
|
+
mac_selector = MacSelector()
|
|
114
158
|
|
|
115
159
|
|
|
116
160
|
def get_ip_address(interface: str):
|
|
@@ -262,4 +306,6 @@ def smart_select_primary_subnet(subnets: List[dict]=get_all_network_subnets()) -
|
|
|
262
306
|
selected = subnet
|
|
263
307
|
if not selected and len(subnets):
|
|
264
308
|
selected = subnets[0]
|
|
265
|
-
return selected['subnet']
|
|
309
|
+
return selected['subnet']
|
|
310
|
+
|
|
311
|
+
|
|
@@ -250,10 +250,9 @@ class ScannerResults:
|
|
|
250
250
|
out.pop('scan')
|
|
251
251
|
out.pop('log')
|
|
252
252
|
|
|
253
|
-
devices: Device = out.pop('devices')
|
|
253
|
+
devices: List[Device] = out.pop('devices')
|
|
254
254
|
sortedDevices = sorted(devices, key=lambda obj: ipaddress.IPv4Address(obj.ip))
|
|
255
|
-
out['devices'] = [
|
|
256
|
-
for device in out['devices']: device.pop('log')
|
|
255
|
+
out['devices'] = [device.dict() for device in sortedDevices]
|
|
257
256
|
|
|
258
257
|
if out_type == str:
|
|
259
258
|
return json.dumps(out, indent=2)
|
|
@@ -30,8 +30,8 @@ class LibraryTestCase(unittest.TestCase):
|
|
|
30
30
|
for d in scan.results.devices:
|
|
31
31
|
if d.hostname: cnt_with_hostname += 1
|
|
32
32
|
# ensure there arent dupe mac addresses
|
|
33
|
-
self.assertNotIn(d.
|
|
34
|
-
macs.append(d.
|
|
33
|
+
self.assertNotIn(d.get_mac(), macs)
|
|
34
|
+
macs.append(d.get_mac())
|
|
35
35
|
|
|
36
36
|
# ensure there arent dupe ips
|
|
37
37
|
self.assertNotIn(d.ip, ips)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import webview
|
|
2
2
|
from .app import start_webserver_dameon
|
|
3
3
|
from ..libraries.runtime_args import RuntimeArgs
|
|
4
|
+
from time import sleep
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
|
|
@@ -10,6 +11,7 @@ def start_webview(args: RuntimeArgs) -> None:
|
|
|
10
11
|
|
|
11
12
|
# Start the Pywebview window
|
|
12
13
|
webview.create_window('LANscape', f'http://127.0.0.1:{args.port}')
|
|
14
|
+
sleep(1)
|
|
13
15
|
webview.start()
|
|
14
16
|
|
|
15
17
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/img/ico/android-chrome-192x192.png
RENAMED
|
File without changes
|
{lanscape-1.2.7a1 → lanscape-1.2.7a2}/src/lanscape/ui/static/img/ico/android-chrome-512x512.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|