730 lines
26 KiB
Python
730 lines
26 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Netzwerk IP-Scanner (Ultra-Zuverlässig)
|
|
Scannt angegebene Netzwerke nach belegten und freien IP-Adressen
|
|
Mehrschichtige Erkennung für 100% Zuverlässigkeit
|
|
"""
|
|
|
|
import argparse
|
|
import ipaddress
|
|
import subprocess
|
|
import sys
|
|
import socket
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
from dataclasses import dataclass
|
|
from typing import List, Set, Dict, Tuple, Optional
|
|
import time
|
|
import shutil
|
|
import re
|
|
|
|
# Globale Debug-Variable
|
|
DEBUG = False
|
|
|
|
# ANSI Farben
|
|
class Color:
|
|
RED = '\033[91m'
|
|
GREEN = '\033[92m'
|
|
YELLOW = '\033[93m'
|
|
BLUE = '\033[94m'
|
|
MAGENTA = '\033[95m'
|
|
CYAN = '\033[96m'
|
|
WHITE = '\033[97m'
|
|
BOLD = '\033[1m'
|
|
UNDERLINE = '\033[4m'
|
|
RESET = '\033[0m'
|
|
|
|
def debug_print(message: str):
|
|
"""Gibt Debug-Nachrichten aus wenn DEBUG aktiviert ist"""
|
|
if DEBUG:
|
|
print(f"{Color.YELLOW}[DEBUG] {message}{Color.RESET}")
|
|
|
|
@dataclass
|
|
class HostInfo:
|
|
ip: str
|
|
hostname: Optional[str] = None
|
|
mac: Optional[str] = None
|
|
responding: bool = True
|
|
method: str = "unknown"
|
|
|
|
@dataclass
|
|
class ScanResult:
|
|
network: str
|
|
used_hosts: Dict[str, HostInfo]
|
|
free_ips: Set[str]
|
|
total: int
|
|
scan_time: float
|
|
method: str
|
|
|
|
def print_banner():
|
|
"""Zeigt einen Banner"""
|
|
banner = f"""
|
|
{Color.CYAN}{Color.BOLD}╔═══════════════════════════════════════════════════════╗
|
|
║ Netzwerk IP-Scanner v3.0 (Ultra-Zuverlässig) ║
|
|
╚═══════════════════════════════════════════════════════╝{Color.RESET}
|
|
"""
|
|
print(banner)
|
|
|
|
def check_tool(tool: str) -> bool:
|
|
"""Prüft ob ein Tool verfügbar ist"""
|
|
is_available = shutil.which(tool) is not None
|
|
debug_print(f"Tool '{tool}' verfügbar: {is_available}")
|
|
return is_available
|
|
|
|
def get_hostname(ip: str, timeout: float = 0.3) -> Optional[str]:
|
|
"""Versucht den Hostnamen einer IP-Adresse aufzulösen"""
|
|
try:
|
|
socket.setdefaulttimeout(timeout)
|
|
hostname = socket.gethostbyaddr(ip)[0]
|
|
result = hostname if hostname != ip else None
|
|
debug_print(f"Hostname für {ip}: {result}")
|
|
return result
|
|
except (socket.herror, socket.gaierror, socket.timeout, OSError) as e:
|
|
debug_print(f"Hostname-Auflösung für {ip} fehlgeschlagen: {e}")
|
|
return None
|
|
|
|
def get_network_interface(network_str: str) -> Optional[str]:
|
|
"""Findet automatisch das passende Netzwerk-Interface für ein Netzwerk"""
|
|
try:
|
|
debug_print(f"Suche Interface für Netzwerk {network_str}")
|
|
network = ipaddress.ip_network(network_str, strict=False)
|
|
|
|
# ip route get um das Interface zu finden
|
|
result = subprocess.run(
|
|
['ip', 'route', 'get', str(list(network.hosts())[0])],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
timeout=5
|
|
)
|
|
|
|
if result.returncode == 0:
|
|
# Parse die Ausgabe nach "dev <interface>"
|
|
match = re.search(r'dev\s+(\S+)', result.stdout)
|
|
if match:
|
|
interface = match.group(1)
|
|
debug_print(f"Gefundenes Interface: {interface}")
|
|
return interface
|
|
|
|
debug_print(f"Kein Interface gefunden via ip route")
|
|
return None
|
|
|
|
except Exception as e:
|
|
debug_print(f"Fehler bei Interface-Erkennung: {e}")
|
|
return None
|
|
|
|
def arp_scan_network(network_str: str, interface: Optional[str] = None) -> Tuple[Dict[str, HostInfo], Set[str]]:
|
|
"""
|
|
Scannt Netzwerk mit arp-scan (Layer 2 - sehr zuverlässig)
|
|
Benötigt root/sudo!
|
|
"""
|
|
try:
|
|
network = ipaddress.ip_network(network_str, strict=False)
|
|
|
|
# Interface bestimmen
|
|
if interface:
|
|
debug_print(f"Verwende manuell angegebenes Interface: {interface}")
|
|
selected_interface = interface
|
|
else:
|
|
debug_print(f"Automatische Interface-Erkennung für {network_str}")
|
|
selected_interface = get_network_interface(network_str)
|
|
if not selected_interface:
|
|
debug_print("Keine automatische Interface-Erkennung möglich, verwende --localnet")
|
|
selected_interface = None
|
|
|
|
# arp-scan Kommando aufbauen
|
|
if selected_interface:
|
|
cmd = ['arp-scan', '--interface', selected_interface, '--numeric', '--quiet', '--ignoredups', str(network)]
|
|
debug_print(f"ARP-Scan Befehl: {' '.join(cmd)}")
|
|
else:
|
|
cmd = ['arp-scan', '--localnet', '--numeric', '--quiet', '--ignoredups', str(network)]
|
|
debug_print(f"ARP-Scan Befehl (localnet): {' '.join(cmd)}")
|
|
|
|
result = subprocess.run(
|
|
cmd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
timeout=90,
|
|
text=True
|
|
)
|
|
|
|
if result.returncode != 0:
|
|
debug_print(f"ARP-Scan stderr: {result.stderr}")
|
|
debug_print(f"ARP-Scan returncode: {result.returncode}")
|
|
|
|
debug_print(f"ARP-Scan stdout:\n{result.stdout}")
|
|
|
|
used_hosts = {}
|
|
all_ips = set(str(ip) for ip in network.hosts())
|
|
|
|
# Parse arp-scan output
|
|
# Format: IP<TAB>MAC<TAB>Vendor
|
|
lines_parsed = 0
|
|
for line in result.stdout.split('\n'):
|
|
line = line.strip()
|
|
if not line or line.startswith('#'):
|
|
continue
|
|
|
|
# Split by tabs oder whitespace
|
|
parts = re.split(r'\s+', line)
|
|
if len(parts) >= 2:
|
|
ip = parts[0].strip()
|
|
mac = parts[1].strip()
|
|
|
|
# Prüfe ob IP gültig ist und im Netzwerk liegt
|
|
try:
|
|
ip_obj = ipaddress.ip_address(ip)
|
|
if str(ip_obj) in all_ips:
|
|
hostname = get_hostname(ip)
|
|
used_hosts[ip] = HostInfo(ip=ip, hostname=hostname, mac=mac, method="arp-scan")
|
|
lines_parsed += 1
|
|
debug_print(f"Gefunden: {ip} ({mac})")
|
|
except ValueError as e:
|
|
debug_print(f"Ungültige IP in arp-scan Ausgabe: {ip} - {e}")
|
|
continue
|
|
|
|
debug_print(f"ARP-Scan: {lines_parsed} Zeilen geparst, {len(used_hosts)} Hosts gefunden")
|
|
|
|
free_ips = all_ips - set(used_hosts.keys())
|
|
return used_hosts, free_ips
|
|
|
|
except (subprocess.TimeoutExpired, FileNotFoundError, subprocess.CalledProcessError, Exception) as e:
|
|
debug_print(f"ARP-Scan Exception: {type(e).__name__}: {e}")
|
|
print(f" {Color.YELLOW}⚠ ARP-Scan Fehler: {e}{Color.RESET}")
|
|
return None, None
|
|
|
|
def nmap_aggressive_scan(network_str: str, scan_ports: bool = False) -> Tuple[Dict[str, HostInfo], Set[str]]:
|
|
"""
|
|
Scannt Netzwerk mit nmap - sehr aggressiv und zuverlässig
|
|
"""
|
|
try:
|
|
network = ipaddress.ip_network(network_str, strict=False)
|
|
|
|
if scan_ports:
|
|
# Mit Port-Scan für maximale Zuverlässigkeit
|
|
cmd = [
|
|
'nmap',
|
|
'-sS', # TCP SYN Scan
|
|
'-p', '21,22,23,25,80,111,135,139,443,445,993,995,1723,3306,3389,5900,8080,8443', # Häufige Ports
|
|
'--min-rate', '1000', # Schneller
|
|
'-T4', # Aggressives Timing
|
|
'-n', # Keine DNS-Auflösung durch nmap
|
|
str(network)
|
|
]
|
|
else:
|
|
# Nur Host-Discovery
|
|
cmd = [
|
|
'nmap',
|
|
'-sn', # Ping Scan
|
|
'-PS21,22,23,25,80,443,3389,8080', # TCP SYN Ping
|
|
'-PA80,443', # TCP ACK Ping
|
|
'-PU53,67,68,161', # UDP Ping
|
|
'-PE', # ICMP Echo
|
|
'-PP', # ICMP Timestamp
|
|
'--min-rate', '500',
|
|
'-T4',
|
|
'-n',
|
|
str(network)
|
|
]
|
|
|
|
debug_print(f"Nmap Befehl: {' '.join(cmd)}")
|
|
|
|
result = subprocess.run(
|
|
cmd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
timeout=300,
|
|
text=True
|
|
)
|
|
|
|
if result.returncode != 0:
|
|
debug_print(f"Nmap stderr: {result.stderr}")
|
|
|
|
debug_print(f"Nmap stdout (erste 500 Zeichen):\n{result.stdout[:500]}")
|
|
|
|
used_hosts = {}
|
|
all_ips = set(str(ip) for ip in network.hosts())
|
|
|
|
# Parse nmap output
|
|
current_ip = None
|
|
hosts_found = 0
|
|
for line in result.stdout.split('\n'):
|
|
# Suche nach "Nmap scan report for"
|
|
if 'Nmap scan report for' in line:
|
|
match = re.search(r'(\d+\.\d+\.\d+\.\d+)', line)
|
|
if match:
|
|
current_ip = match.group(1)
|
|
debug_print(f"Nmap: Gefunden IP {current_ip}")
|
|
|
|
# Prüfe ob Host up ist
|
|
if current_ip and ('Host is up' in line or 'open' in line):
|
|
if current_ip in all_ips and current_ip not in used_hosts:
|
|
hostname = get_hostname(current_ip)
|
|
used_hosts[current_ip] = HostInfo(ip=current_ip, hostname=hostname, method="nmap")
|
|
hosts_found += 1
|
|
debug_print(f"Nmap: Host {current_ip} ist up")
|
|
|
|
debug_print(f"Nmap: {hosts_found} Hosts gefunden")
|
|
|
|
free_ips = all_ips - set(used_hosts.keys())
|
|
return used_hosts, free_ips
|
|
|
|
except (subprocess.TimeoutExpired, FileNotFoundError, Exception) as e:
|
|
debug_print(f"Nmap Exception: {type(e).__name__}: {e}")
|
|
print(f" {Color.YELLOW}⚠ Nmap Fehler: {e}{Color.RESET}")
|
|
return None, None
|
|
|
|
def check_tcp_ports(ip: str, ports: List[int]) -> bool:
|
|
"""Prüft ob TCP-Ports offen sind"""
|
|
for port in ports:
|
|
try:
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(0.3)
|
|
result = sock.connect_ex((ip, port))
|
|
sock.close()
|
|
if result == 0:
|
|
debug_print(f"TCP Port {port} auf {ip} ist offen")
|
|
return True
|
|
except Exception as e:
|
|
debug_print(f"TCP Port Check Fehler {ip}:{port}: {e}")
|
|
continue
|
|
return False
|
|
|
|
def ping_check(ip: str) -> bool:
|
|
"""ICMP Ping"""
|
|
try:
|
|
result = subprocess.run(
|
|
['ping', '-c', '1', '-W', '1', str(ip)],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
timeout=2
|
|
)
|
|
success = result.returncode == 0
|
|
if success:
|
|
debug_print(f"Ping erfolgreich: {ip}")
|
|
return success
|
|
except Exception as e:
|
|
debug_print(f"Ping Fehler {ip}: {e}")
|
|
return False
|
|
|
|
def comprehensive_check(ip: str, tcp_ports: List[int]) -> bool:
|
|
"""Kombinierte Prüfung: Ping + TCP-Ports"""
|
|
if ping_check(ip):
|
|
return True
|
|
return check_tcp_ports(ip, tcp_ports)
|
|
|
|
def scan_network(network_str: str, method: str = 'auto', max_workers: int = 100,
|
|
tcp_ports: List[int] = None, aggressive: bool = False,
|
|
interface: Optional[str] = None) -> ScanResult:
|
|
"""Scannt ein Netzwerk nach aktiven IPs"""
|
|
|
|
if tcp_ports is None:
|
|
tcp_ports = [22, 80, 443, 3389, 8080, 8443, 3306, 5432]
|
|
|
|
try:
|
|
network = ipaddress.ip_network(network_str, strict=False)
|
|
except ValueError as e:
|
|
print(f"{Color.RED}✗ Fehler beim Parsen von {network_str}: {e}{Color.RESET}")
|
|
return None
|
|
|
|
all_ips = list(network.hosts())
|
|
total_ips = len(all_ips)
|
|
|
|
print(f"\n{Color.YELLOW}► Scanne Netzwerk: {Color.BOLD}{network_str}{Color.RESET}")
|
|
print(f" {Color.WHITE}IPs zu scannen: {total_ips}{Color.RESET}")
|
|
|
|
if interface:
|
|
print(f" {Color.WHITE}Interface: {interface}{Color.RESET}")
|
|
|
|
start_time = time.time()
|
|
used_hosts = {}
|
|
free_ips = set()
|
|
scan_method = method
|
|
|
|
# Automatische Methodenwahl
|
|
if method == 'auto':
|
|
if check_tool('arp-scan'):
|
|
scan_method = 'arp-scan'
|
|
debug_print("Auto-Methode: Verwende arp-scan")
|
|
elif check_tool('nmap'):
|
|
scan_method = 'nmap'
|
|
debug_print("Auto-Methode: Verwende nmap")
|
|
else:
|
|
scan_method = 'comprehensive'
|
|
debug_print("Auto-Methode: Verwende comprehensive")
|
|
|
|
print(f" {Color.CYAN}Methode: {scan_method}{Color.RESET}")
|
|
|
|
# ARP-Scan (beste Option)
|
|
if scan_method == 'arp-scan':
|
|
print(f" {Color.CYAN}Führe ARP-Scan durch...{Color.RESET}")
|
|
used_hosts, free_ips = arp_scan_network(network_str, interface=interface)
|
|
|
|
if used_hosts is None or len(used_hosts) == 0:
|
|
print(f" {Color.YELLOW}⚠ ARP-Scan lieferte keine Ergebnisse, fallback zu nmap{Color.RESET}")
|
|
debug_print("ARP-Scan Fallback zu nmap")
|
|
scan_method = 'nmap'
|
|
|
|
# Nmap-Scan
|
|
if scan_method == 'nmap' and not used_hosts:
|
|
print(f" {Color.CYAN}Führe Nmap-Scan durch...{Color.RESET}")
|
|
used_hosts, free_ips = nmap_aggressive_scan(network_str, scan_ports=aggressive)
|
|
|
|
if used_hosts is None:
|
|
print(f" {Color.YELLOW}⚠ Nmap fehlgeschlagen, fallback zu comprehensive{Color.RESET}")
|
|
debug_print("Nmap Fallback zu comprehensive")
|
|
scan_method = 'comprehensive'
|
|
|
|
# Comprehensive Scan (Fallback)
|
|
if scan_method == 'comprehensive' or not used_hosts:
|
|
print(f" {Color.CYAN}Führe umfassenden Scan durch (Ping + TCP)...{Color.RESET}")
|
|
|
|
scanned = 0
|
|
def print_progress():
|
|
progress = (scanned / total_ips) * 100
|
|
bar_length = 40
|
|
filled = int(bar_length * scanned / total_ips)
|
|
bar = '█' * filled + '░' * (bar_length - filled)
|
|
print(f"\r {Color.CYAN}[{bar}] {progress:.1f}% ({scanned}/{total_ips}){Color.RESET}", end='', flush=True)
|
|
|
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
futures = {executor.submit(comprehensive_check, str(ip), tcp_ports): ip for ip in all_ips}
|
|
|
|
for future in as_completed(futures):
|
|
ip = str(futures[future])
|
|
try:
|
|
is_alive = future.result()
|
|
if is_alive:
|
|
hostname = get_hostname(ip)
|
|
used_hosts[ip] = HostInfo(ip=ip, hostname=hostname, method="comprehensive")
|
|
else:
|
|
free_ips.add(ip)
|
|
except Exception:
|
|
free_ips.add(ip)
|
|
|
|
scanned += 1
|
|
print_progress()
|
|
|
|
print()
|
|
|
|
# Sicherstellen dass free_ips korrekt ist
|
|
all_ips_set = set(str(ip) for ip in all_ips)
|
|
free_ips = all_ips_set - set(used_hosts.keys())
|
|
|
|
scan_time = time.time() - start_time
|
|
|
|
debug_print(f"Scan abgeschlossen: {len(used_hosts)} belegt, {len(free_ips)} frei, Dauer: {scan_time:.2f}s")
|
|
|
|
return ScanResult(
|
|
network=network_str,
|
|
used_hosts=used_hosts,
|
|
free_ips=free_ips,
|
|
total=total_ips,
|
|
scan_time=scan_time,
|
|
method=scan_method
|
|
)
|
|
|
|
def print_result(result: ScanResult, show_all: bool = False, show_free: bool = True):
|
|
"""Gibt das Scan-Ergebnis formatiert aus"""
|
|
if not result:
|
|
return
|
|
|
|
used_count = len(result.used_hosts)
|
|
free_count = len(result.free_ips)
|
|
usage_percent = (used_count / result.total) * 100 if result.total > 0 else 0
|
|
|
|
print(f"\n{Color.BLUE}{Color.BOLD}╔═══ Ergebnis für {result.network} ═══╗{Color.RESET}")
|
|
print(f"{Color.WHITE} Scan-Methode: {result.method}{Color.RESET}")
|
|
print(f"{Color.WHITE} Scan-Dauer: {result.scan_time:.2f}s{Color.RESET}")
|
|
print(f"{Color.GREEN} ✓ Belegte IPs: {used_count} ({usage_percent:.1f}%){Color.RESET}")
|
|
print(f"{Color.RED} ✗ Freie IPs: {free_count} ({100-usage_percent:.1f}%){Color.RESET}")
|
|
|
|
# Belegte IPs mit Hostnamen ausgeben
|
|
if used_count > 0:
|
|
print(f"\n{Color.GREEN}{Color.BOLD} Belegte IP-Adressen:{Color.RESET}")
|
|
sorted_hosts = sorted(result.used_hosts.values(), key=lambda x: ipaddress.ip_address(x.ip))
|
|
|
|
display_hosts = sorted_hosts if (show_all or used_count <= 20) else sorted_hosts[:10] + [None] + sorted_hosts[-10:]
|
|
|
|
for host in display_hosts:
|
|
if host is None:
|
|
print(f" {Color.YELLOW}... ({used_count - 20} weitere) ...{Color.RESET}")
|
|
continue
|
|
|
|
hostname_str = f" → {Color.CYAN}{host.hostname}{Color.RESET}" if host.hostname else ""
|
|
mac_str = f" [{host.mac}]" if host.mac else ""
|
|
print(f" {Color.GREEN}{host.ip:15}{Color.RESET}{hostname_str}{mac_str}")
|
|
|
|
# Freie IPs ausgeben
|
|
if show_free and free_count > 0:
|
|
print(f"\n{Color.RED}{Color.BOLD} Freie IP-Adressen:{Color.RESET}")
|
|
sorted_free = sorted(result.free_ips, key=lambda x: ipaddress.ip_address(x))
|
|
|
|
if free_count <= 20 or show_all:
|
|
for i, ip in enumerate(sorted_free, 1):
|
|
if i % 4 == 1:
|
|
print(f" {Color.RED}", end='')
|
|
print(f"{ip:15}", end=' ')
|
|
if i % 4 == 0:
|
|
print(Color.RESET)
|
|
if free_count % 4 != 0:
|
|
print(Color.RESET)
|
|
else:
|
|
for i, ip in enumerate(sorted_free[:10], 1):
|
|
if i % 4 == 1:
|
|
print(f" {Color.RED}", end='')
|
|
print(f"{ip:15}", end=' ')
|
|
if i % 4 == 0:
|
|
print(Color.RESET)
|
|
if len(sorted_free[:10]) % 4 != 0:
|
|
print(Color.RESET)
|
|
print(f"\n {Color.YELLOW} ... ({free_count - 20} weitere freie IPs) ...{Color.RESET}\n")
|
|
remaining = sorted_free[-10:]
|
|
for i, ip in enumerate(remaining, 1):
|
|
if i % 4 == 1:
|
|
print(f" {Color.RED}", end='')
|
|
print(f"{ip:15}", end=' ')
|
|
if i % 4 == 0:
|
|
print(Color.RESET)
|
|
if len(remaining) % 4 != 0:
|
|
print(Color.RESET)
|
|
|
|
print(f"{Color.BLUE}{Color.BOLD}╚{'═' * 50}╝{Color.RESET}\n")
|
|
|
|
def print_common_free_hosts(results: List[ScanResult], show_all: bool = False):
|
|
"""
|
|
Zeigt Host-IDs an, die in ALLEN gescannten Netzwerken gleichzeitig frei sind
|
|
"""
|
|
if len(results) < 2:
|
|
return
|
|
|
|
free_host_ids_per_network = []
|
|
|
|
for result in results:
|
|
host_ids = set()
|
|
for ip_str in result.free_ips:
|
|
try:
|
|
ip_parts = ip_str.split('.')
|
|
if len(ip_parts) == 4:
|
|
host_id = int(ip_parts[3])
|
|
host_ids.add(host_id)
|
|
except (ValueError, IndexError):
|
|
continue
|
|
free_host_ids_per_network.append(host_ids)
|
|
|
|
common_free = set.intersection(*free_host_ids_per_network) if free_host_ids_per_network else set()
|
|
|
|
if not common_free:
|
|
print(f"{Color.YELLOW}⚠ Keine gemeinsam freien Host-IDs in allen Netzwerken gefunden{Color.RESET}\n")
|
|
return
|
|
|
|
common_count = len(common_free)
|
|
sorted_common = sorted(common_free)
|
|
|
|
networks_str = ", ".join([r.network for r in results])
|
|
|
|
print(f"{Color.MAGENTA}{Color.BOLD}╔═══ Gemeinsam freie Host-IDs ═══╗{Color.RESET}")
|
|
print(f"{Color.WHITE} Netzwerke: {networks_str}{Color.RESET}")
|
|
print(f"{Color.MAGENTA} Anzahl gemeinsam frei: {common_count}{Color.RESET}")
|
|
print(f"\n{Color.MAGENTA}{Color.BOLD} Verfügbare Host-IDs (letztes Oktett):{Color.RESET}")
|
|
|
|
print(f"\n{Color.CYAN} Host-ID ", end='')
|
|
for result in results:
|
|
network_prefix = '.'.join(result.network.split('.')[:3])
|
|
print(f"│ {network_prefix:15}", end=' ')
|
|
print(Color.RESET)
|
|
print(f"{Color.CYAN} ─────────{'─' * (len(results) * 19)}{Color.RESET}")
|
|
|
|
display_limit = None if show_all else 30
|
|
|
|
if display_limit and common_count > display_limit:
|
|
display_ids = sorted_common[:15] + [None] + sorted_common[-15:]
|
|
else:
|
|
display_ids = sorted_common
|
|
|
|
for host_id in display_ids:
|
|
if host_id is None:
|
|
print(f"{Color.YELLOW} ... {'│' * len(results)} ({common_count - 30} weitere){Color.RESET}")
|
|
continue
|
|
|
|
print(f" {Color.MAGENTA}{host_id:>3} {Color.RESET}", end='')
|
|
for result in results:
|
|
network_prefix = '.'.join(result.network.split('.')[:3])
|
|
full_ip = f"{network_prefix}.{host_id}"
|
|
print(f"│ {Color.GREEN}{full_ip:15}{Color.RESET}", end=' ')
|
|
print()
|
|
|
|
print(f"\n{Color.MAGENTA}{Color.BOLD}╚{'═' * 50}╝{Color.RESET}\n")
|
|
|
|
if common_count > 0:
|
|
example_id = sorted_common[0]
|
|
example_ips = []
|
|
for result in results:
|
|
network_prefix = '.'.join(result.network.split('.')[:3])
|
|
example_ips.append(f"{network_prefix}.{example_id}")
|
|
|
|
print(f"{Color.CYAN}💡 Tipp: Für eine VM mit mehreren Interfaces kannst du z.B. verwenden:{Color.RESET}")
|
|
for i, ip in enumerate(example_ips, 1):
|
|
print(f" Interface {i}: {Color.GREEN}{ip}{Color.RESET}")
|
|
print()
|
|
|
|
def main():
|
|
global DEBUG
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description='Scannt Netzwerke nach belegten und freien IP-Adressen (100%% zuverlässig)',
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Scan-Methoden:
|
|
auto - Automatisch beste verfügbare Methode (Standard)
|
|
arp-scan - ARP-Scan (Layer 2, am zuverlässigsten, benötigt sudo!)
|
|
nmap - Nmap Scan (sehr zuverlässig)
|
|
comprehensive - Ping + TCP Port-Check (Fallback)
|
|
|
|
Wichtig für maximale Zuverlässigkeit:
|
|
sudo ./ip.py 10.150.8.0/24 --method arp-scan
|
|
|
|
Beispiele:
|
|
sudo %(prog)s 10.150.8.0/24 10.150.56.0/24
|
|
sudo %(prog)s 192.168.0.0/24 --method arp-scan -a
|
|
sudo %(prog)s 10.0.0.0/24 --method arp-scan --interface vmbr0
|
|
%(prog)s 10.0.0.0/24 --aggressive
|
|
%(prog)s 192.168.1.0/24 --no-free --debug
|
|
"""
|
|
)
|
|
|
|
parser.add_argument(
|
|
'networks',
|
|
nargs='+',
|
|
help='Netzwerke im CIDR-Format (z.B. 192.168.0.0/24)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-a', '--show-all',
|
|
action='store_true',
|
|
help='Zeige alle IPs (auch bei großen Listen)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-m', '--method',
|
|
choices=['auto', 'arp-scan', 'nmap', 'comprehensive'],
|
|
default='auto',
|
|
help='Scan-Methode (Standard: auto)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-t', '--threads',
|
|
type=int,
|
|
default=100,
|
|
help='Anzahl paralleler Threads (Standard: 100)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-p', '--ports',
|
|
type=str,
|
|
default='22,80,443,3389,8080,8443,3306,5432',
|
|
help='TCP-Ports für Port-Check (Standard: 22,80,443,3389,8080,8443,3306,5432)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--aggressive',
|
|
action='store_true',
|
|
help='Aggressiver Nmap-Scan mit Port-Scan (langsamer aber zuverlässiger)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--no-free',
|
|
action='store_true',
|
|
help='Zeige keine freien IPs'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-i', '--interface',
|
|
type=str,
|
|
default=None,
|
|
help='Netzwerk-Interface für ARP-Scan (z.B. eth0, vmbr0, enp67s0)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--debug',
|
|
action='store_true',
|
|
help='Aktiviert Debug-Modus mit detaillierter Ausgabe'
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Debug-Modus aktivieren
|
|
if args.debug:
|
|
DEBUG = True
|
|
debug_print("Debug-Modus aktiviert")
|
|
|
|
# Parse TCP-Ports
|
|
try:
|
|
tcp_ports = [int(p.strip()) for p in args.ports.split(',')]
|
|
debug_print(f"TCP-Ports: {tcp_ports}")
|
|
except ValueError:
|
|
print(f"{Color.RED}✗ Ungültiges Port-Format{Color.RESET}")
|
|
return
|
|
|
|
print_banner()
|
|
|
|
# Warnung wenn nicht als root
|
|
if args.method == 'arp-scan':
|
|
try:
|
|
uid_result = subprocess.run(['id', '-u'], capture_output=True, text=True)
|
|
if uid_result.stdout.strip() != '0':
|
|
print(f"{Color.RED}⚠ WARNUNG: arp-scan benötigt root-Rechte! Führe mit 'sudo' aus.{Color.RESET}\n")
|
|
debug_print("Nicht als root ausgeführt")
|
|
except:
|
|
pass
|
|
|
|
# Verfügbare Tools anzeigen
|
|
tools_available = []
|
|
if check_tool('arp-scan'):
|
|
tools_available.append('arp-scan')
|
|
if check_tool('nmap'):
|
|
tools_available.append('nmap')
|
|
tools_available.append('ping+tcp')
|
|
|
|
print(f"{Color.CYAN}Verfügbare Scan-Methoden: {', '.join(tools_available)}{Color.RESET}\n")
|
|
|
|
if args.interface:
|
|
print(f"{Color.CYAN}Manuelles Interface: {args.interface}{Color.RESET}\n")
|
|
debug_print(f"Verwende manuell angegebenes Interface: {args.interface}")
|
|
|
|
results = []
|
|
total_start = time.time()
|
|
|
|
for network in args.networks:
|
|
result = scan_network(
|
|
network,
|
|
method=args.method,
|
|
max_workers=args.threads,
|
|
tcp_ports=tcp_ports,
|
|
aggressive=args.aggressive,
|
|
interface=args.interface
|
|
)
|
|
if result:
|
|
results.append(result)
|
|
print_result(result, args.show_all, not args.no_free)
|
|
|
|
total_time = time.time() - total_start
|
|
|
|
# Zusammenfassung
|
|
if len(results) > 1:
|
|
total_used = sum(len(r.used_hosts) for r in results)
|
|
total_free = sum(len(r.free_ips) for r in results)
|
|
total_ips = sum(r.total for r in results)
|
|
|
|
print(f"\n{Color.CYAN}{Color.BOLD}╔═══ Gesamtzusammenfassung ═══╗{Color.RESET}")
|
|
print(f"{Color.WHITE} Gescannte Netzwerke: {len(results)}{Color.RESET}")
|
|
print(f"{Color.WHITE} Gesamte IPs: {total_ips}{Color.RESET}")
|
|
print(f"{Color.GREEN} Belegte IPs: {total_used}{Color.RESET}")
|
|
print(f"{Color.RED} Freie IPs: {total_free}{Color.RESET}")
|
|
print(f"{Color.WHITE} Gesamtdauer: {total_time:.2f}s{Color.RESET}")
|
|
print(f"{Color.CYAN}{Color.BOLD}╚{'═' * 30}╝{Color.RESET}\n")
|
|
|
|
# Gemeinsam freie Host-IDs
|
|
print_common_free_hosts(results, args.show_all)
|
|
|
|
if __name__ == '__main__':
|
|
main() |