This commit is contained in:
pika 2025-03-30 19:57:41 +02:00
parent 6dd38036e7
commit 097b3dbf09
34 changed files with 1719 additions and 520 deletions

View file

@ -7,63 +7,41 @@ from app.core.extensions import db
from app.core.models import Subnet, Server
import json
import subprocess
import concurrent.futures
from datetime import datetime
import platform
def scan(cidr, max_threads=10, save_results=False):
def scan(subnet, manual_trigger=False):
"""
Scan a subnet for active hosts
Args:
cidr: The subnet in CIDR notation (e.g. "192.168.1.0/24")
max_threads: Maximum number of threads to use
save_results: Whether to save results to the database
Returns:
A list of dictionaries with IP, hostname, and status
subnet: The subnet object to scan
manual_trigger: If False, only scan if the subnet hasn't been scanned recently
"""
print(f"Starting scan of {cidr}")
network = ipaddress.ip_network(cidr)
# Skip if not auto scan and not manually triggered
if not subnet.auto_scan and not manual_trigger:
return False
# Skip network and broadcast addresses for IPv4
if network.version == 4:
hosts = list(network.hosts())
else:
# For IPv6, just take the first 100 addresses to avoid scanning too many
hosts = list(network.hosts())[:100]
active_hosts = []
# Split the hosts into chunks for multithreading
chunks = [[] for _ in range(max_threads)]
for i, host in enumerate(hosts):
chunks[i % max_threads].append(host)
# Initialize results
results = [[] for _ in range(max_threads)]
# Create and start threads
threads = []
for i in range(max_threads):
if chunks[i]: # Only start a thread if there are IPs to scan
t = threading.Thread(target=scan_worker, args=(chunks[i], results, i))
threads.append(t)
t.start()
# Wait for all threads to complete
for t in threads:
t.join()
# Combine results
all_results = []
for r in results:
all_results.extend(r)
# Save results to database if requested
if save_results:
try:
save_scan_results(cidr, all_results)
except Exception as e:
print(f"Error saving scan results: {e}")
print(f"Scan completed. Found {len(all_results)} active hosts.")
return all_results
try:
# Parse the CIDR notation
network = ipaddress.ip_network(subnet.cidr, strict=False)
# For each address in this network, ping it
for ip in network.hosts():
if ping(str(ip)):
active_hosts.append(str(ip))
# Update subnet with scan results
subnet.active_hosts = json.dumps(active_hosts)
subnet.last_scanned = datetime.utcnow()
return True
except Exception as e:
print(f"Error scanning subnet {subnet.cidr}: {str(e)}")
return False
def scan_worker(ip_list, results, index):
"""Worker function for threading"""
@ -76,13 +54,23 @@ def scan_worker(ip_list, results, index):
'status': 'up'
})
def ping(ip):
"""Ping an IP address and return True if it responds"""
def ping(host):
"""
Returns True if host responds to a ping request
"""
# Ping parameters based on OS
param = '-n' if platform.system().lower() == 'windows' else '-c'
# Build the command
command = ['ping', param, '1', '-w', '1', host]
try:
# Faster timeout (1 second)
subprocess.check_output(['ping', '-c', '1', '-W', '1', str(ip)], stderr=subprocess.STDOUT)
return True
except subprocess.CalledProcessError:
# Run the command and capture output
output = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=2)
# Return True if ping was successful
return output.returncode == 0
except subprocess.TimeoutExpired:
return False
except Exception:
return False
def get_hostname(ip):
@ -166,7 +154,7 @@ def schedule_subnet_scans():
# Start a thread for each subnet
thread = threading.Thread(
target=scan,
args=(subnet.cidr,),
args=(subnet,),
daemon=True
)
thread.start()