Files
JNC-Scan-Windows/src/core/network_manager_win.py
T
joaquin.navajas 7add14d06f JNC Windows
2026-05-11 07:56:47 +02:00

174 lines
6.6 KiB
Python

"""
Implementación de NetworkManager para Windows.
Usa psutil + netsh; no requiere pyroute2 ni nmcli.
"""
import socket
import subprocess
import re
from typing import Tuple, List, Dict, Any
import psutil
def _parse_netsh_config() -> Dict[str, Dict[str, str]]:
"""Ejecuta netsh interface ipv4 show config y devuelve dict por interfaz."""
out: Dict[str, Dict[str, str]] = {}
try:
r = subprocess.run(
["netsh", "interface", "ipv4", "show", "config"],
capture_output=True,
text=True,
timeout=15,
creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, "CREATE_NO_WINDOW") else 0,
)
if r.returncode != 0:
return out
current = None
for line in r.stdout.splitlines():
# "Configuration for interface "Ethernet""
m = re.match(r'Configuration for interface "([^"]+)"', line)
if m:
current = m.group(1)
out[current] = {"method": "unknown", "ip": "", "prefix": "", "gateway": "", "dns": ""}
continue
if current is None:
continue
line = line.strip()
if line.startswith("DHCP enabled:"):
val = line.split(":", 1)[1].strip().lower()
out[current]["method"] = "auto" if val == "yes" else "manual"
elif line.startswith("IP Address:"):
out[current]["ip"] = line.split(":", 1)[1].strip()
elif line.startswith("Subnet Prefix:"):
val = line.split(":", 1)[1].strip()
if "/" in val:
ip_part, prefix_part = val.split("/", 1)
out[current]["ip"] = out[current]["ip"] or ip_part.strip()
out[current]["prefix"] = prefix_part.strip()
elif line.startswith("Default Gateway:"):
out[current]["gateway"] = line.split(":", 1)[1].strip()
elif line.startswith("DNS Servers:"):
out[current]["dns"] = line.split(":", 1)[1].strip()
except Exception:
pass
return out
def _is_mac_address(addr: str) -> bool:
if not addr or len(addr) != 17:
return False
parts = addr.split(":")
return len(parts) == 6 and all(len(p) == 2 and p.isalnum() for p in parts)
class NetworkManager:
"""NetworkManager para Windows: list_interfaces y get_interface_config con psutil + netsh."""
def __init__(self):
pass
def list_interfaces(self) -> List[Dict[str, Any]]:
interfaces = []
stats = psutil.net_if_stats()
addrs = psutil.net_if_addrs()
netsh_config = _parse_netsh_config()
for if_name, if_addrs in addrs.items():
if if_name == "lo" or "Loopback" in if_name:
continue
is_up = False
if if_name in stats:
is_up = stats[if_name].isup
ipv4 = []
ipv6 = []
mac = ""
for addr in if_addrs:
try:
af = addr.family
addr_str = addr.address
except AttributeError:
af = addr[0]
addr_str = addr[1]
if af == socket.AF_INET:
ipv4.append(addr_str)
elif af == socket.AF_INET6 and not addr_str.startswith("fe80") and addr_str != "::1":
ipv6.append(addr_str)
elif _is_mac_address(addr_str):
mac = addr_str
if not mac:
for addr in if_addrs:
try:
a = getattr(addr, "address", addr[1])
except (IndexError, TypeError):
continue
if _is_mac_address(a):
mac = a
break
config = netsh_config.get(if_name, {
"method": "unknown", "ip": "", "prefix": "", "gateway": "", "dns": ""
})
if ipv4 and not config.get("ip"):
config = dict(config)
config["ip"] = ipv4[0].split("/")[0] if "/" in str(ipv4[0]) else str(ipv4[0])
# Formato ip/prefix como en Linux cuando hay prefix en netsh
if config.get("prefix") and config.get("ip"):
ipv4_formatted = [f"{config['ip']}/{config['prefix']}"]
elif ipv4:
ipv4_formatted = [a if "/" in str(a) else str(a) for a in ipv4]
else:
ipv4_formatted = []
interfaces.append({
"name": if_name,
"mac": mac or "N/A",
"status": "UP" if is_up else "DOWN",
"ipv4": ipv4_formatted if ipv4 else [],
"ipv6": ipv6,
"type": "Wired",
"is_up": is_up,
"config": config,
})
return interfaces
def get_interface_config(self, iface_name: str) -> Dict[str, str]:
configs = _parse_netsh_config()
return configs.get(iface_name, {
"method": "unknown", "ip": "", "prefix": "", "gateway": "", "dns": ""
})
def apply_interface_config(
self, iface_name: str, method: str,
ip: str = None, prefix: str = None, gateway: str = None, dns: str = None
) -> Tuple[bool, str]:
return False, "Configuración IP no disponible en Windows desde esta aplicación."
def set_ip_config(self, iface_name: str, ip_address: str, prefix_len: str, gateway: str = None) -> Tuple[bool, str]:
return False, "No disponible en Windows."
def set_dhcp(self, iface_name: str) -> Tuple[bool, str]:
return False, "No disponible en Windows."
def create_vlan_bridge(self, trunk_iface: str, vlan_id: int, access_iface: str) -> Tuple[bool, str]:
return False, "VLAN/Bridge no disponible en Windows."
def create_simple_bridge(self, iface1: str, iface2: str) -> Tuple[bool, str]:
return False, "Bridge no disponible en Windows."
def start_dhcp_server(
self, interface: str, ip: str, range_start: str, range_end: str
) -> Tuple[bool, str]:
return False, "DHCP no disponible en Windows."
def delete_bridge(self, bridge_name: str) -> Tuple[bool, str]:
return False, "No disponible en Windows."
def setup_wifi_ap(
self, wifi_iface: str, ssid: str, password: str, bridge_name: str
) -> Tuple[bool, str]:
return False, "Wi-Fi AP no disponible en Windows."
def reset_interface(self, iface_name: str) -> Tuple[bool, str]:
return False, "Reinicio de interfaz no disponible en Windows."
def close(self) -> None:
pass