181 lines
5.2 KiB
Python
181 lines
5.2 KiB
Python
import socket
|
|
import threading
|
|
import ipaddress
|
|
import time
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
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(subnet, manual_trigger=False):
|
|
"""
|
|
Scan a subnet for active hosts
|
|
|
|
Args:
|
|
subnet: The subnet object to scan
|
|
manual_trigger: If False, only scan if the subnet hasn't been scanned recently
|
|
"""
|
|
# Skip if not auto scan and not manually triggered
|
|
if not subnet.auto_scan and not manual_trigger:
|
|
return False
|
|
|
|
active_hosts = []
|
|
|
|
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"""
|
|
for ip in ip_list:
|
|
if ping(ip):
|
|
hostname = get_hostname(ip)
|
|
results[index].append(
|
|
{
|
|
"ip": str(ip),
|
|
"hostname": hostname if hostname else str(ip),
|
|
"status": "up",
|
|
}
|
|
)
|
|
|
|
|
|
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:
|
|
# 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):
|
|
"""Try to get the hostname for an IP address"""
|
|
try:
|
|
hostname = socket.gethostbyaddr(str(ip))[0]
|
|
return hostname
|
|
except (socket.herror, socket.gaierror):
|
|
return None
|
|
|
|
|
|
def is_host_active_ping(ip):
|
|
"""Simple ICMP ping test (platform dependent)"""
|
|
import platform
|
|
import subprocess
|
|
|
|
param = "-n" if platform.system().lower() == "windows" else "-c"
|
|
command = ["ping", param, "1", "-w", "1", ip]
|
|
|
|
try:
|
|
return (
|
|
subprocess.call(
|
|
command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
|
)
|
|
== 0
|
|
)
|
|
except:
|
|
return False
|
|
|
|
|
|
def save_scan_results(cidr, results):
|
|
"""Save scan results to the database"""
|
|
from flask import current_app
|
|
|
|
# Need to be in application context
|
|
if not hasattr(current_app, "app_context"):
|
|
print("Not in Flask application context, cannot save results")
|
|
return
|
|
|
|
try:
|
|
# Find subnet by CIDR
|
|
subnet = Subnet.query.filter_by(cidr=cidr).first()
|
|
if not subnet:
|
|
print(f"Subnet {cidr} not found in database")
|
|
return
|
|
|
|
# Get existing servers in this subnet
|
|
existing_servers = {
|
|
server.ip_address: server
|
|
for server in Server.query.filter_by(subnet_id=subnet.id).all()
|
|
}
|
|
|
|
# Process scan results
|
|
for host in results:
|
|
ip = host["ip"]
|
|
hostname = host["hostname"]
|
|
|
|
# Check if server already exists
|
|
if ip in existing_servers:
|
|
# Update hostname if it was previously unknown
|
|
if existing_servers[ip].hostname == ip and hostname != ip:
|
|
existing_servers[ip].hostname = hostname
|
|
db.session.add(existing_servers[ip])
|
|
else:
|
|
# Create new server
|
|
server = Server(
|
|
hostname=hostname,
|
|
ip_address=ip,
|
|
subnet_id=subnet.id,
|
|
documentation=f"# {hostname}\n\nAutomatically discovered by network scan on {time.strftime('%Y-%m-%d %H:%M:%S')}",
|
|
)
|
|
db.session.add(server)
|
|
|
|
db.session.commit()
|
|
print(f"Saved scan results for {cidr}")
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
print(f"Error saving scan results: {e}")
|
|
|
|
|
|
def schedule_subnet_scans():
|
|
"""Schedule automatic scans for subnets marked as auto_scan"""
|
|
from flask import current_app
|
|
|
|
with current_app.app_context():
|
|
try:
|
|
# Find all subnets with auto_scan enabled
|
|
subnets = Subnet.query.filter_by(auto_scan=True).all()
|
|
|
|
for subnet in subnets:
|
|
# Start a thread for each subnet
|
|
thread = threading.Thread(target=scan, args=(subnet,), daemon=True)
|
|
thread.start()
|
|
|
|
# Sleep briefly to avoid overloading
|
|
time.sleep(1)
|
|
|
|
except Exception as e:
|
|
print(f"Error scheduling subnet scans: {e}")
|