diff --git a/scan.py b/scan.py index ba03bb6..b2eebcc 100644 --- a/scan.py +++ b/scan.py @@ -1,21 +1,23 @@ import ipaddress import subprocess import argparse +import csv +import json from concurrent.futures import ThreadPoolExecutor, as_completed -# Lista OID-ów do sprawdzenia (etykieta => OID) -oids = { +# Domyślny OID tylko sysName +default_oids = { "sysName": "1.3.6.1.2.1.1.5.0", - "sysDescr": "1.3.6.1.2.1.1.1.0", - "sysLocation": "1.3.6.1.2.1.1.6.0", - "sysContact": "1.3.6.1.2.1.1.4.0" + #"sysDescr": "1.3.6.1.2.1.1.1.0", + #"sysLocation": "1.3.6.1.2.1.1.6.0", + #"sysContact": "1.3.6.1.2.1.1.4.0" } -def get_oid_value(ip, community, oid): +def get_oid_value(ip, community, oid, timeout=1): try: result = subprocess.run( ["snmpget", "-v2c", "-c", community, str(ip), oid], - capture_output=True, text=True, timeout=2 + capture_output=True, text=True, timeout=timeout ) if result.returncode == 0 and "STRING" in result.stdout: return result.stdout.split("STRING:")[-1].strip() @@ -23,36 +25,91 @@ def get_oid_value(ip, community, oid): return "Timeout" return "Brak danych" -def query_all_oids(ip, community): - values = {name: get_oid_value(ip, community, oid) for name, oid in oids.items()} +def query_all_oids(ip, community, oids, timeout=1): + values = {name: get_oid_value(ip, community, oid, timeout) for name, oid in oids.items()} has_data = any(val not in ("Timeout", "Brak danych") for val in values.values()) if has_data: return (str(ip), values) return None +def scan_subnet(subnet, community, oids): + results = [] + for ip in subnet.hosts(): + result = query_all_oids(ip, community, oids, timeout=1) + if result: + results.append(result) + return results + +def split_subnet(supernet, new_prefix=24): + try: + network = ipaddress.IPv4Network(supernet) + return list(network.subnets(new_prefix=new_prefix)) + except ValueError as e: + print(f"Błąd podsieci: {e}") + return [] + +def read_subnets_from_file(filename): + subnets = [] + try: + with open(filename, "r", encoding="utf-8") as f: + for line in f: + line = line.strip() + if line: + subnets.append(line) + except Exception as e: + print(f"Błąd podczas wczytywania pliku podsieci: {e}") + return subnets + +def save_to_csv(results, oids, filename="wyniki.csv"): + with open(filename, "w", newline="", encoding="utf-8") as f: + writer = csv.writer(f) + headers = ["IP"] + list(oids.keys()) + writer.writerow(headers) + for ip, values in results: + row = [ip] + [values.get(key, "") for key in oids.keys()] + writer.writerow(row) + def main(): - parser = argparse.ArgumentParser(description="Skaner SNMP dla wielu podsieci.") - parser.add_argument("subnets", nargs="+", help="Podsieci w formacie CIDR (np. 10.1.1.0/24 10.2.2.0/24)") + parser = argparse.ArgumentParser(description="Skaner SNMP z podziałem podsieci i eksportem do CSV.") + parser.add_argument("subnets", nargs="*", help="Podsieci w formacie CIDR (np. 172.16.0.0/16)") + parser.add_argument("-s", "--subnet-file", help="Plik tekstowy z listą podsieci CIDR (jedna na linię)") parser.add_argument("-c", "--community", default="public", help="SNMP community (domyślnie: public)") + parser.add_argument("-o", "--oids", help="OID-y w formacie JSON, np. '{\"sysDescr\":\"1.3.6.1.2.1.1.1.0\"}'") + parser.add_argument("-w", "--workers", type=int, default=10, help="Liczba równoległych wątków (domyślnie: 10)") + parser.add_argument("-p", "--prefix", type=int, default=24, help="Wielkość podsieci do podziału (domyślnie: 24)") + parser.add_argument("-f", "--file", default="wyniki.csv", help="Nazwa pliku CSV do zapisu (domyślnie: wyniki.csv)") args = parser.parse_args() - ips = [] - for subnet in args.subnets: - try: - ips.extend(ipaddress.IPv4Network(subnet)) - except ValueError: - print(f"Błędny format podsieci: {subnet}") + oids = json.loads(args.oids) if args.oids else default_oids - print(f"{'IP':<15} {'sysName':<25} {'sysDescr':<40} {'sysLocation':<25} {'sysContact'}") - print("-" * 130) + all_input_subnets = list(args.subnets) + if args.subnet_file: + all_input_subnets.extend(read_subnets_from_file(args.subnet_file)) - with ThreadPoolExecutor(max_workers=6) as executor: - futures = {executor.submit(query_all_oids, ip, args.community): ip for ip in ips} + if not all_input_subnets: + print("Brak podsieci do przeskanowania (ani z linii poleceń, ani z pliku).") + return + + subnets_to_scan = [] + for subnet in all_input_subnets: + subnets_to_scan.extend(split_subnet(subnet, new_prefix=args.prefix)) + + print(f"{'IP':<15} " + " ".join([f"{name:<25}" for name in oids.keys()])) + print("-" * (15 + 26 * len(oids))) + + all_results = [] + with ThreadPoolExecutor(max_workers=args.workers) as executor: + futures = { + executor.submit(scan_subnet, subnet, args.community, oids): subnet + for subnet in subnets_to_scan + } for future in as_completed(futures): - result = future.result() - if result: - ip_str, values = result - print(f"{ip_str:<15} {values['sysName']:<25} {values['sysDescr']:<40} {values['sysLocation']:<25} {values['sysContact']}") + subnet_results = future.result() + for ip_str, values in subnet_results: + all_results.append((ip_str, values)) + print(f"{ip_str:<15} " + " ".join([f"{values[name]:<25}" for name in oids.keys()])) + + save_to_csv(all_results, oids, filename=args.file) if __name__ == "__main__": main() \ No newline at end of file