lanscape 1.3.2a7__py3-none-any.whl → 1.3.2a9__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.
- lanscape/libraries/__init__.py +0 -0
- lanscape/libraries/app_scope.py +75 -0
- lanscape/libraries/decorators.py +153 -0
- lanscape/libraries/errors.py +32 -0
- lanscape/libraries/ip_parser.py +69 -0
- lanscape/libraries/logger.py +45 -0
- lanscape/libraries/mac_lookup.py +102 -0
- lanscape/libraries/net_tools.py +516 -0
- lanscape/libraries/port_manager.py +67 -0
- lanscape/libraries/runtime_args.py +54 -0
- lanscape/libraries/scan_config.py +97 -0
- lanscape/libraries/service_scan.py +50 -0
- lanscape/libraries/subnet_scan.py +338 -0
- lanscape/libraries/version_manager.py +56 -0
- lanscape/libraries/web_browser.py +142 -0
- lanscape/resources/mac_addresses/convert_csv.py +30 -0
- lanscape/resources/ports/convert_csv.py +30 -0
- lanscape/ui/app.py +128 -0
- lanscape/ui/blueprints/__init__.py +7 -0
- lanscape/ui/blueprints/api/__init__.py +3 -0
- lanscape/ui/blueprints/api/port.py +33 -0
- lanscape/ui/blueprints/api/scan.py +75 -0
- lanscape/ui/blueprints/api/tools.py +36 -0
- lanscape/ui/blueprints/web/__init__.py +3 -0
- lanscape/ui/blueprints/web/routes.py +78 -0
- lanscape/ui/main.py +137 -0
- {lanscape-1.3.2a7.dist-info → lanscape-1.3.2a9.dist-info}/METADATA +1 -1
- {lanscape-1.3.2a7.dist-info → lanscape-1.3.2a9.dist-info}/RECORD +31 -5
- {lanscape-1.3.2a7.dist-info → lanscape-1.3.2a9.dist-info}/WHEEL +0 -0
- {lanscape-1.3.2a7.dist-info → lanscape-1.3.2a9.dist-info}/licenses/LICENSE +0 -0
- {lanscape-1.3.2a7.dist-info → lanscape-1.3.2a9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Only used to import csv data - not during runtime
|
|
2
|
+
|
|
3
|
+
import csv
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
ans = {}
|
|
9
|
+
with open('mac-vendors-export.csv', 'r', encoding='utf-8') as f:
|
|
10
|
+
data = csv.reader(f)
|
|
11
|
+
services = csv_to_dict(data)
|
|
12
|
+
for service in services:
|
|
13
|
+
if service['Vendor Name'] and service['Mac Prefix']:
|
|
14
|
+
try:
|
|
15
|
+
ans[service['Mac Prefix']] = service['Vendor Name']
|
|
16
|
+
except BaseException:
|
|
17
|
+
pass
|
|
18
|
+
with open('mac_db.json', 'w') as f:
|
|
19
|
+
json.dump(ans, f, indent=2)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def csv_to_dict(data):
|
|
23
|
+
"""
|
|
24
|
+
Convert a CSV file to a dictionary.
|
|
25
|
+
"""
|
|
26
|
+
header = next(data)
|
|
27
|
+
return [dict(zip(header, row)) for row in data]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
main()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Only used to import csv data - not during runtime
|
|
2
|
+
|
|
3
|
+
import csv
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
ans = {}
|
|
9
|
+
with open('service-names-port-numbers.csv', 'r') as f:
|
|
10
|
+
data = csv.reader(f)
|
|
11
|
+
services = csv_to_dict(data)
|
|
12
|
+
for service in services:
|
|
13
|
+
if service['Service Name'] and service['Port Number']:
|
|
14
|
+
try:
|
|
15
|
+
ans[service['Port Number']] = service['Service Name']
|
|
16
|
+
except BaseException:
|
|
17
|
+
pass
|
|
18
|
+
with open('valid_ports.json', 'w') as f:
|
|
19
|
+
json.dump(ans, f, indent=2)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def csv_to_dict(data):
|
|
23
|
+
"""
|
|
24
|
+
Convert a CSV file to a dictionary.
|
|
25
|
+
"""
|
|
26
|
+
header = next(data)
|
|
27
|
+
return [dict(zip(header, row)) for row in data]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
main()
|
lanscape/ui/app.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from flask import Flask, render_template, request
|
|
2
|
+
import traceback
|
|
3
|
+
import threading
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from lanscape.libraries.runtime_args import RuntimeArgs, parse_args
|
|
8
|
+
from lanscape.libraries.version_manager import is_update_available, get_installed_version, lookup_latest_version
|
|
9
|
+
from lanscape.libraries.app_scope import is_local_run
|
|
10
|
+
from lanscape.libraries.net_tools import is_arp_supported
|
|
11
|
+
|
|
12
|
+
app = Flask(
|
|
13
|
+
__name__
|
|
14
|
+
)
|
|
15
|
+
log = logging.getLogger('flask')
|
|
16
|
+
|
|
17
|
+
# Import and register BPs
|
|
18
|
+
################################
|
|
19
|
+
from lanscape.ui.blueprints.api import api_bp, tools, port, scan
|
|
20
|
+
from lanscape.ui.blueprints.web import web_bp, routes
|
|
21
|
+
|
|
22
|
+
app.register_blueprint(api_bp)
|
|
23
|
+
app.register_blueprint(web_bp)
|
|
24
|
+
|
|
25
|
+
# Define global jinja filters
|
|
26
|
+
################################
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def is_substring_in_values(results: dict, substring: str) -> bool:
|
|
30
|
+
return any(substring.lower() in str(v).lower() for v in results.values()) if substring else True
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
app.jinja_env.filters['is_substring_in_values'] = is_substring_in_values
|
|
34
|
+
|
|
35
|
+
# Define global jinja vars
|
|
36
|
+
################################
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def set_global_safe(key: str, value):
|
|
40
|
+
""" Safely set global vars without worrying about an exception """
|
|
41
|
+
app_globals = app.jinja_env.globals
|
|
42
|
+
try:
|
|
43
|
+
if callable(value):
|
|
44
|
+
value = value()
|
|
45
|
+
|
|
46
|
+
app_globals[key] = value
|
|
47
|
+
log.debug(f'jinja_globals[{key}] = {value}')
|
|
48
|
+
except BaseException:
|
|
49
|
+
default = app_globals.get(key)
|
|
50
|
+
log.debug(traceback.format_exc())
|
|
51
|
+
log.info(
|
|
52
|
+
f"Unable to set app global var '{key}'" +
|
|
53
|
+
f"defaulting to '{default}'"
|
|
54
|
+
)
|
|
55
|
+
app_globals[key] = default
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
set_global_safe('app_version', get_installed_version)
|
|
59
|
+
set_global_safe('update_available', is_update_available)
|
|
60
|
+
set_global_safe('latest_version', lookup_latest_version)
|
|
61
|
+
set_global_safe('runtime_args', vars(parse_args()))
|
|
62
|
+
set_global_safe('is_local', is_local_run)
|
|
63
|
+
set_global_safe('is_arp_supported', is_arp_supported)
|
|
64
|
+
|
|
65
|
+
# External hook to kill flask server
|
|
66
|
+
################################
|
|
67
|
+
|
|
68
|
+
exiting = False
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@app.route("/shutdown", methods=['GET', 'POST'])
|
|
72
|
+
def exit_app():
|
|
73
|
+
|
|
74
|
+
req_type = request.args.get('type')
|
|
75
|
+
if req_type == 'browser-close':
|
|
76
|
+
args = parse_args()
|
|
77
|
+
if args.persistent:
|
|
78
|
+
log.info('Dectected browser close, not exiting flask.')
|
|
79
|
+
return "Ignored"
|
|
80
|
+
log.info('Web browser closed, terminating flask. (disable with --peristent)')
|
|
81
|
+
elif req_type == 'core':
|
|
82
|
+
log.info('Core requested exit, terminating flask.')
|
|
83
|
+
else:
|
|
84
|
+
log.info('Received external exit request. Terminating flask.')
|
|
85
|
+
global exiting
|
|
86
|
+
exiting = True
|
|
87
|
+
return "Done"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@app.teardown_request
|
|
91
|
+
def teardown(exception):
|
|
92
|
+
if exiting:
|
|
93
|
+
os._exit(0)
|
|
94
|
+
|
|
95
|
+
# Generalized error handling
|
|
96
|
+
################################
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@app.errorhandler(500)
|
|
100
|
+
def internal_error(e):
|
|
101
|
+
"""
|
|
102
|
+
handle internal errors nicely
|
|
103
|
+
"""
|
|
104
|
+
tb = traceback.format_exc()
|
|
105
|
+
return render_template('error.html',
|
|
106
|
+
error=None,
|
|
107
|
+
traceback=tb), 500
|
|
108
|
+
|
|
109
|
+
# Webserver creation functions
|
|
110
|
+
################################
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def start_webserver_daemon(args: RuntimeArgs) -> threading.Thread:
|
|
114
|
+
proc = threading.Thread(target=start_webserver, args=(args,))
|
|
115
|
+
proc.daemon = True # Kill thread when main thread exits
|
|
116
|
+
proc.start()
|
|
117
|
+
log.info('Flask server initializing as dameon')
|
|
118
|
+
return proc
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def start_webserver(args: RuntimeArgs) -> int:
|
|
122
|
+
run_args = {
|
|
123
|
+
'host': '0.0.0.0',
|
|
124
|
+
'port': args.port,
|
|
125
|
+
'debug': args.reloader,
|
|
126
|
+
'use_reloader': args.reloader
|
|
127
|
+
}
|
|
128
|
+
app.run(**run_args)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from flask import request, jsonify
|
|
2
|
+
from lanscape.ui.blueprints.api import api_bp
|
|
3
|
+
from lanscape.libraries.port_manager import PortManager
|
|
4
|
+
|
|
5
|
+
# Port Manager API
|
|
6
|
+
############################################
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@api_bp.route('/api/port/list', methods=['GET'])
|
|
10
|
+
def get_port_lists():
|
|
11
|
+
return jsonify(PortManager().get_port_lists())
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@api_bp.route('/api/port/list/<port_list>', methods=['GET'])
|
|
15
|
+
def get_port_list(port_list):
|
|
16
|
+
return jsonify(PortManager().get_port_list(port_list))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@api_bp.route('/api/port/list/<port_list>', methods=['POST'])
|
|
20
|
+
def create_port_list(port_list):
|
|
21
|
+
data = request.get_json()
|
|
22
|
+
return jsonify(PortManager().create_port_list(port_list, data))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@api_bp.route('/api/port/list/<port_list>', methods=['PUT'])
|
|
26
|
+
def update_port_list(port_list):
|
|
27
|
+
data = request.get_json()
|
|
28
|
+
return jsonify(PortManager().update_port_list(port_list, data))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@api_bp.route('/api/port/list/<port_list>', methods=['DELETE'])
|
|
32
|
+
def delete_port_list(port_list):
|
|
33
|
+
return jsonify(PortManager().delete_port_list(port_list))
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from lanscape.ui.blueprints.api import api_bp
|
|
2
|
+
from lanscape.libraries.subnet_scan import ScanConfig
|
|
3
|
+
from lanscape.ui.blueprints import scan_manager
|
|
4
|
+
|
|
5
|
+
from flask import request, jsonify
|
|
6
|
+
import json
|
|
7
|
+
import traceback
|
|
8
|
+
|
|
9
|
+
# Subnet Scanner API
|
|
10
|
+
############################################
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@api_bp.route('/api/scan', methods=['POST'])
|
|
14
|
+
@api_bp.route('/api/scan/threaded', methods=['POST'])
|
|
15
|
+
def scan_subnet_threaded():
|
|
16
|
+
try:
|
|
17
|
+
config = get_scan_config()
|
|
18
|
+
scan = scan_manager.new_scan(config)
|
|
19
|
+
|
|
20
|
+
return jsonify({'status': 'running', 'scan_id': scan.uid})
|
|
21
|
+
except BaseException:
|
|
22
|
+
return jsonify({'status': 'error', 'traceback': traceback.format_exc()}), 500
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@api_bp.route('/api/scan/async', methods=['POST'])
|
|
26
|
+
def scan_subnet_async():
|
|
27
|
+
config = get_scan_config()
|
|
28
|
+
scan = scan_manager.new_scan(config)
|
|
29
|
+
scan_manager.wait_until_complete(scan.uid)
|
|
30
|
+
|
|
31
|
+
return jsonify({'status': 'complete', 'scan_id': scan.uid})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@api_bp.route('/api/scan/<scan_id>', methods=['GET'])
|
|
35
|
+
def get_scan(scan_id):
|
|
36
|
+
scan = scan_manager.get_scan(scan_id)
|
|
37
|
+
# cast to str and back to handle custom JSON serialization
|
|
38
|
+
return jsonify(json.loads(scan.results.export(str)))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@api_bp.route('/api/scan/<scan_id>/summary', methods=['GET'])
|
|
42
|
+
def get_scan_summary(scan_id):
|
|
43
|
+
scan = scan_manager.get_scan(scan_id)
|
|
44
|
+
if not scan:
|
|
45
|
+
return jsonify({'error': 'scan not found'}), 404
|
|
46
|
+
return jsonify({
|
|
47
|
+
'running': scan.running,
|
|
48
|
+
'percent_complete': scan.calc_percent_complete(),
|
|
49
|
+
'stage': scan.results.stage,
|
|
50
|
+
'runtime': scan.results.get_runtime(),
|
|
51
|
+
'devices': {
|
|
52
|
+
'scanned': scan.results.devices_scanned,
|
|
53
|
+
'alive': len(scan.results.devices),
|
|
54
|
+
'total': scan.results.devices_total
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@api_bp.route('/api/scan/<scan_id>/terminate', methods=['GET'])
|
|
60
|
+
def terminate_scan(scan_id):
|
|
61
|
+
scan = scan_manager.get_scan(scan_id)
|
|
62
|
+
scan.terminate()
|
|
63
|
+
return jsonify({'success': True})
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_scan_config():
|
|
67
|
+
"""
|
|
68
|
+
pulls config from the request body
|
|
69
|
+
"""
|
|
70
|
+
data = request.get_json()
|
|
71
|
+
return ScanConfig(
|
|
72
|
+
subnet=data['subnet'],
|
|
73
|
+
port_list=data['port_list'],
|
|
74
|
+
t_multiplier=data.get('parallelism', 1.0)
|
|
75
|
+
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from flask import request, jsonify
|
|
2
|
+
from lanscape.ui.blueprints.api import api_bp
|
|
3
|
+
from lanscape.libraries.net_tools import get_all_network_subnets
|
|
4
|
+
from lanscape.libraries.ip_parser import parse_ip_input
|
|
5
|
+
from lanscape.libraries.errors import SubnetTooLargeError
|
|
6
|
+
import traceback
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@api_bp.route('/api/tools/subnet/test')
|
|
10
|
+
def test_subnet():
|
|
11
|
+
subnet = request.args.get('subnet')
|
|
12
|
+
if not subnet:
|
|
13
|
+
return jsonify({'valid': False, 'msg': 'Subnet cannot be blank', 'count': -1})
|
|
14
|
+
try:
|
|
15
|
+
ips = parse_ip_input(subnet)
|
|
16
|
+
length = len(ips)
|
|
17
|
+
return jsonify({'valid': True,
|
|
18
|
+
'msg': f"{length} IP{'s' if length > 1 else ''}",
|
|
19
|
+
'count': length})
|
|
20
|
+
except SubnetTooLargeError:
|
|
21
|
+
return jsonify({'valid': False, 'msg': 'subnet too large',
|
|
22
|
+
'error': traceback.format_exc(), 'count': -1})
|
|
23
|
+
except BaseException:
|
|
24
|
+
return jsonify({'valid': False, 'msg': 'invalid subnet',
|
|
25
|
+
'error': traceback.format_exc(), 'count': -1})
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@api_bp.route('/api/tools/subnet/list')
|
|
29
|
+
def list_subnet():
|
|
30
|
+
"""
|
|
31
|
+
list all interface subnets
|
|
32
|
+
"""
|
|
33
|
+
try:
|
|
34
|
+
return jsonify(get_all_network_subnets())
|
|
35
|
+
except BaseException:
|
|
36
|
+
return jsonify({'error': traceback.format_exc()})
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from flask import render_template, request, redirect
|
|
2
|
+
from lanscape.ui.blueprints.web import web_bp
|
|
3
|
+
from lanscape.libraries.net_tools import (
|
|
4
|
+
get_all_network_subnets,
|
|
5
|
+
smart_select_primary_subnet
|
|
6
|
+
)
|
|
7
|
+
from lanscape.ui.blueprints import scan_manager, log
|
|
8
|
+
|
|
9
|
+
# Template Renderer
|
|
10
|
+
############################################
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@web_bp.route('/', methods=['GET'])
|
|
14
|
+
def index():
|
|
15
|
+
subnets = get_all_network_subnets()
|
|
16
|
+
subnet = smart_select_primary_subnet(subnets)
|
|
17
|
+
|
|
18
|
+
port_list = 'medium'
|
|
19
|
+
parallelism = 1
|
|
20
|
+
if scan_id := request.args.get('scan_id'):
|
|
21
|
+
if scan := scan_manager.get_scan(scan_id):
|
|
22
|
+
subnet = scan.cfg.subnet
|
|
23
|
+
port_list = scan.cfg.port_list
|
|
24
|
+
parallelism = scan.cfg.t_multiplier
|
|
25
|
+
|
|
26
|
+
else:
|
|
27
|
+
log.debug(f'Redirecting, scan {scan_id} doesnt exist in memory')
|
|
28
|
+
return redirect('/')
|
|
29
|
+
return render_template(
|
|
30
|
+
'main.html',
|
|
31
|
+
subnet=subnet,
|
|
32
|
+
port_list=port_list,
|
|
33
|
+
parallelism=parallelism,
|
|
34
|
+
alternate_subnets=subnets
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@web_bp.route('/scan/<scan_id>', methods=['GET'])
|
|
39
|
+
@web_bp.route('/scan/<scan_id>/<section>', methods=['GET'])
|
|
40
|
+
def render_scan(scan_id, section='all'):
|
|
41
|
+
if scanner := scan_manager.get_scan(scan_id):
|
|
42
|
+
data = scanner.results.export()
|
|
43
|
+
filter = request.args.get('filter')
|
|
44
|
+
return render_template('scan.html', data=data, section=section, filter=filter)
|
|
45
|
+
log.debug(f'Redirecting, scan {scan_id} doesnt exist in memory')
|
|
46
|
+
return redirect('/')
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@web_bp.route('/errors/<scan_id>')
|
|
50
|
+
def view_errors(scan_id):
|
|
51
|
+
if scanner := scan_manager.get_scan(scan_id):
|
|
52
|
+
data = scanner.results.export()
|
|
53
|
+
return render_template('scan/scan-error.html', data=data)
|
|
54
|
+
log.debug(f'Redirecting, scan {scan_id} doesnt exist in memory')
|
|
55
|
+
return redirect('/')
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@web_bp.route('/export/<scan_id>')
|
|
59
|
+
def export_scan(scan_id):
|
|
60
|
+
if scanner := scan_manager.get_scan(scan_id):
|
|
61
|
+
export_json = scanner.results.export(str)
|
|
62
|
+
return render_template(
|
|
63
|
+
'scan/export.html',
|
|
64
|
+
scan=scanner,
|
|
65
|
+
export_json=export_json
|
|
66
|
+
)
|
|
67
|
+
log.debug(f'Redirecting, scan {scan_id} doesnt exist in memory')
|
|
68
|
+
return redirect('/')
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@web_bp.route('/shutdown-ui')
|
|
72
|
+
def shutdown_ui():
|
|
73
|
+
return render_template('shutdown.html')
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@web_bp.route('/info')
|
|
77
|
+
def app_info():
|
|
78
|
+
return render_template('info.html')
|
lanscape/ui/main.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
|
|
2
|
+
import socket
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
import logging
|
|
8
|
+
import traceback
|
|
9
|
+
import os
|
|
10
|
+
from lanscape.libraries.logger import configure_logging
|
|
11
|
+
from lanscape.libraries.runtime_args import parse_args, RuntimeArgs
|
|
12
|
+
from lanscape.libraries.web_browser import open_webapp
|
|
13
|
+
from lanscape.libraries.net_tools import is_arp_supported
|
|
14
|
+
from lanscape.libraries.version_manager import get_installed_version, is_update_available
|
|
15
|
+
from lanscape.ui.app import start_webserver_daemon, start_webserver
|
|
16
|
+
# do this so any logs generated on import are displayed
|
|
17
|
+
args = parse_args()
|
|
18
|
+
configure_logging(args.loglevel, args.logfile, args.flask_logging)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
log = logging.getLogger('core')
|
|
22
|
+
# determine if the execution is an instance of a flask reload
|
|
23
|
+
# happens on file change with reloader enabled
|
|
24
|
+
IS_FLASK_RELOAD = os.environ.get("WERKZEUG_RUN_MAIN")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def main():
|
|
28
|
+
try:
|
|
29
|
+
_main()
|
|
30
|
+
except KeyboardInterrupt:
|
|
31
|
+
log.info('Keyboard interrupt received, terminating...')
|
|
32
|
+
terminate()
|
|
33
|
+
except Exception as e:
|
|
34
|
+
log.critical(f'Unexpected error: {e}')
|
|
35
|
+
log.debug(traceback.format_exc())
|
|
36
|
+
terminate()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _main():
|
|
40
|
+
if not IS_FLASK_RELOAD:
|
|
41
|
+
log.info(f'LANscape v{get_installed_version()}')
|
|
42
|
+
try_check_update()
|
|
43
|
+
|
|
44
|
+
else:
|
|
45
|
+
log.info('Flask reloaded app.')
|
|
46
|
+
|
|
47
|
+
args.port = get_valid_port(args.port)
|
|
48
|
+
|
|
49
|
+
if not is_arp_supported():
|
|
50
|
+
log.warning('ARP is not supported, device discovery is degraded. For more information, see the help guide: https://github.com/mdennis281/LANscape/blob/main/support/arp-issues.md')
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
start_webserver_ui(args)
|
|
54
|
+
log.info('Exiting...')
|
|
55
|
+
except Exception as e:
|
|
56
|
+
# showing error in debug only because this is handled gracefully
|
|
57
|
+
log.critical(f'Failed to start app. Error: {e}')
|
|
58
|
+
log.debug('Failed to start. Traceback below')
|
|
59
|
+
log.debug(traceback.format_exc())
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def try_check_update():
|
|
63
|
+
try:
|
|
64
|
+
if is_update_available():
|
|
65
|
+
log.info('An update is available!')
|
|
66
|
+
log.info(
|
|
67
|
+
'Run "pip install --upgrade lanscape --no-cache" to suppress this message.')
|
|
68
|
+
except BaseException:
|
|
69
|
+
log.debug(traceback.format_exc())
|
|
70
|
+
log.warning('Unable to check for updates.')
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def open_browser(url: str, wait=2) -> bool:
|
|
74
|
+
"""
|
|
75
|
+
Open a browser window to the specified
|
|
76
|
+
url after waiting for the server to start
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
time.sleep(wait)
|
|
80
|
+
log.info(f'Starting UI - http://127.0.0.1:{args.port}')
|
|
81
|
+
return open_webapp(url)
|
|
82
|
+
|
|
83
|
+
except BaseException:
|
|
84
|
+
log.debug(traceback.format_exc())
|
|
85
|
+
log.info(f'Unable to open web browser, server running on {url}')
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def start_webserver_ui(args: RuntimeArgs):
|
|
90
|
+
uri = f'http://127.0.0.1:{args.port}'
|
|
91
|
+
|
|
92
|
+
# running reloader requires flask to run in main thread
|
|
93
|
+
# this decouples UI from main process
|
|
94
|
+
if args.reloader:
|
|
95
|
+
# determine if it was reloaded by flask debug reloader
|
|
96
|
+
# if it was, dont open the browser again
|
|
97
|
+
log.info('Opening UI as daemon')
|
|
98
|
+
if not IS_FLASK_RELOAD:
|
|
99
|
+
threading.Thread(
|
|
100
|
+
target=open_browser,
|
|
101
|
+
args=(uri,),
|
|
102
|
+
daemon=True
|
|
103
|
+
).start()
|
|
104
|
+
start_webserver(args)
|
|
105
|
+
else:
|
|
106
|
+
flask_thread = start_webserver_daemon(args)
|
|
107
|
+
app_closed = open_browser(uri)
|
|
108
|
+
|
|
109
|
+
# depending on env, open_browser may or
|
|
110
|
+
# may not be coupled with the closure of UI
|
|
111
|
+
# (if in browser tab, it's uncoupled)
|
|
112
|
+
if not app_closed or args.persistent:
|
|
113
|
+
# not doing a direct join so i can still
|
|
114
|
+
# terminate the app with ctrl+c
|
|
115
|
+
while flask_thread.is_alive():
|
|
116
|
+
time.sleep(1)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_valid_port(port: int):
|
|
120
|
+
"""
|
|
121
|
+
Get the first available port starting from the specified port
|
|
122
|
+
"""
|
|
123
|
+
while True:
|
|
124
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
125
|
+
if s.connect_ex(('localhost', port)) != 0:
|
|
126
|
+
return port
|
|
127
|
+
port += 1
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def terminate():
|
|
131
|
+
import requests
|
|
132
|
+
log.info('Attempting flask shutdown')
|
|
133
|
+
requests.get(f'http://127.0.0.1:{args.port}/shutdown?type=core')
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
main()
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
lanscape/__init__.py,sha256=CpU5C1IdJ9mkGAwJakvnpaFNiQhnTYoOwtSJI-1xUG4,239
|
|
2
2
|
lanscape/__main__.py,sha256=mxgOewoYiNlUCb-QNFKBUqXoPVjMyMIVuLrqg4tNBJk,179
|
|
3
|
+
lanscape/libraries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
lanscape/libraries/app_scope.py,sha256=LTvCD60NFBDqZTAMWFDDyBFNuilli52cWj-Q2K6KezQ,2628
|
|
5
|
+
lanscape/libraries/decorators.py,sha256=wVOC7Wv6mEQIkLlpPJbbxFk_dS3RkLWfYq1ep6oPeYI,5217
|
|
6
|
+
lanscape/libraries/errors.py,sha256=oBMcgPVWL5_O7OgpOf3NkUObCubA6xNkusM3AqIrbX8,972
|
|
7
|
+
lanscape/libraries/ip_parser.py,sha256=sMiACqmIy-uvWoCNhTCYxZ37521nFAeoihRftR1_5DU,2220
|
|
8
|
+
lanscape/libraries/logger.py,sha256=WlUg1y88iqjNgUarbyEUWxsc5pgm4KU5lnfTqK-kNWc,1451
|
|
9
|
+
lanscape/libraries/mac_lookup.py,sha256=m0uxVPxLR8-vkWnqd3ZoauUXYWRBlUntzltfWjiRN5w,3481
|
|
10
|
+
lanscape/libraries/net_tools.py,sha256=M-ZfCCHvS4ABNlmVBHLVSNvrFMQ3TaBVHmgVZUhGd30,18324
|
|
11
|
+
lanscape/libraries/port_manager.py,sha256=CStw-xgwCi9a7dmYL_Ox78K5vUCExjOZyUg3LEf6Afc,2085
|
|
12
|
+
lanscape/libraries/runtime_args.py,sha256=vqspmExhqnyT_vs7FIM3Fe1I8iQUGe2v9Qz0jmyMWjU,2122
|
|
13
|
+
lanscape/libraries/scan_config.py,sha256=KcBLsxo5RK8M5tyId9WfF5MvKb1Nr9TCCYLnAXrmnZI,2660
|
|
14
|
+
lanscape/libraries/service_scan.py,sha256=KVbw2pjie6hrGl092KRM-FtduB9Id8fg1g3SqP-PsfE,1920
|
|
15
|
+
lanscape/libraries/subnet_scan.py,sha256=N5Db3zUdzlF0OyGlYv0Z5YSIItTxw0NxzmbcfF5vtCc,12075
|
|
16
|
+
lanscape/libraries/version_manager.py,sha256=drVagoN9KcxDl4I0Y8JnurBQZlW0u2ViPz6Pmk5cl7w,1756
|
|
17
|
+
lanscape/libraries/web_browser.py,sha256=CIztmodX8o8Is-0ZiM7JQ3vf4olKhCo9VjpQkzJleVM,5007
|
|
18
|
+
lanscape/resources/mac_addresses/convert_csv.py,sha256=35yBKtRPlUK4O1hjHqfRTAa7CgEz4t49sUYovSh88Rs,740
|
|
3
19
|
lanscape/resources/mac_addresses/mac_db.json,sha256=ygtFSwNwJzDlg6hmAujdgCyzUjxt9Di75J8SO4xYIs8,2187804
|
|
20
|
+
lanscape/resources/ports/convert_csv.py,sha256=YB189V2vcbCYIloCHq9A1hXeSTtEYmJT30g66zezrBI,739
|
|
4
21
|
lanscape/resources/ports/full.json,sha256=Abfbi-b5yZF4jR5NS6CT6QpIDfx4Vk04gIC1fKH2ws0,1506980
|
|
5
22
|
lanscape/resources/ports/large.json,sha256=jK4gkzlPT74uv4y0TvFjsaOaUcX7Cy8Zxe2bh5FYcc0,144496
|
|
6
23
|
lanscape/resources/ports/medium.json,sha256=3yHAca0_pEWXK4k1wmda1eNVM6ftzcpKn5VspVwmkRs,3667
|
|
7
24
|
lanscape/resources/ports/small.json,sha256=Mj3zGVG1F2eqZx2YkrLpTL8STeLcqB8_65IR67MS1Tg,397
|
|
8
25
|
lanscape/resources/services/definitions.jsonc,sha256=71w9Q7r4RoBYiIMkzzO2KdEJXaSIchNccYQueqAhD4E,8842
|
|
26
|
+
lanscape/ui/app.py,sha256=AVSfsKCipukx22cCOkK_9bUdSIrLtBknu0znJHJM7lI,3758
|
|
27
|
+
lanscape/ui/main.py,sha256=qerJS6NQt6r6zM34j3saY6Ek57bKf3W11BBxDqgZ7ho,4341
|
|
28
|
+
lanscape/ui/blueprints/__init__.py,sha256=fBrihVUCYryi-f5QpR73mQBFcgEjr40S4EcCVUuRXGs,214
|
|
29
|
+
lanscape/ui/blueprints/api/__init__.py,sha256=wPhNBIriIz9SCfMuDQsPeuv2dUh6N40GD81-J1sHYwY,68
|
|
30
|
+
lanscape/ui/blueprints/api/port.py,sha256=0FU6UNH8z7mfR8JwZHrPV-Vi6nuL2As9AU0NzYPyvlQ,1061
|
|
31
|
+
lanscape/ui/blueprints/api/scan.py,sha256=sYu_-6nykCcEkNmWNQaBW_b1L34d9KigOlQALw2vJZc,2303
|
|
32
|
+
lanscape/ui/blueprints/api/tools.py,sha256=NSSijS2F_kNlQ1NNjWQ3-OBpe2NlUdgg3ho6ist3hcE,1328
|
|
33
|
+
lanscape/ui/blueprints/web/__init__.py,sha256=cz1cHN0FOtTlewVbMN9VsmPWAfqAZPT8YybTszZpTpY,68
|
|
34
|
+
lanscape/ui/blueprints/web/routes.py,sha256=ndKcraNQ2cFRm_NRK5vjL5d0SDNOOUVG0hPKZmgDmAk,2464
|
|
9
35
|
lanscape/ui/static/lanscape.webmanifest,sha256=0aauJk_Bybd0B2iwzJfvPcs7AX43kVHs0dtpV6_jSWk,459
|
|
10
36
|
lanscape/ui/static/css/style.css,sha256=lu9078sZKrMBmT_tM6084_DDnEEMXVMziO6E-gVYkhk,16372
|
|
11
37
|
lanscape/ui/static/img/ico/android-chrome-192x192.png,sha256=JmFT6KBCCuoyxMV-mLNtF9_QJbVBvfWPUizKN700fi8,18255
|
|
@@ -36,8 +62,8 @@ lanscape/ui/templates/scan/ip-table-row.html,sha256=ptY24rxJRaA4PEEQRDncaq6Q0ql5
|
|
|
36
62
|
lanscape/ui/templates/scan/ip-table.html,sha256=ds__UP9JiTKf5IxCmTMzw--eN_yg1Pvn3Nj1KvQxeZg,940
|
|
37
63
|
lanscape/ui/templates/scan/overview.html,sha256=FsX-jSFhGKwCxZGKE8AMKk328UuawN6O9RNTzYvIOts,1205
|
|
38
64
|
lanscape/ui/templates/scan/scan-error.html,sha256=Q4eZM5ThrxnFaWOSTUpK8hA2ksHwhxOBTaVUCLALhyA,1032
|
|
39
|
-
lanscape-1.3.
|
|
40
|
-
lanscape-1.3.
|
|
41
|
-
lanscape-1.3.
|
|
42
|
-
lanscape-1.3.
|
|
43
|
-
lanscape-1.3.
|
|
65
|
+
lanscape-1.3.2a9.dist-info/licenses/LICENSE,sha256=cCO-NbS01Ilwc6djHjZ7LIgPFRkRmWdr0fH2ysXKioA,1090
|
|
66
|
+
lanscape-1.3.2a9.dist-info/METADATA,sha256=zff6Z0fOwHfLVn5uEUoYzwZvx-MAaCNVBs5DhAa6stc,2586
|
|
67
|
+
lanscape-1.3.2a9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
68
|
+
lanscape-1.3.2a9.dist-info/top_level.txt,sha256=E9D4sjPz_6H7c85Ycy_pOS2xuv1Wm-ilKhxEprln2ps,9
|
|
69
|
+
lanscape-1.3.2a9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|