From 950a09d18bf7cd53f447b29b2440e22c6fd39ffb Mon Sep 17 00:00:00 2001 From: "s.galanskiy" Date: Thu, 19 Jun 2025 16:14:59 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9A=D0=BE=D0=BD=D0=BA=D1=80=D0=B5=D1=82?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20hostname?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SRV/services/subnet_service.py | 87 +++++++++++++++++++++------------- SRV/services/wifi_service.py | 2 +- 2 files changed, 55 insertions(+), 34 deletions(-) diff --git a/SRV/services/subnet_service.py b/SRV/services/subnet_service.py index 689d97c..e25fcb6 100644 --- a/SRV/services/subnet_service.py +++ b/SRV/services/subnet_service.py @@ -12,6 +12,7 @@ import json from pathlib import Path from datetime import datetime, timedelta from zoneinfo import ZoneInfo +import subprocess from models.subnet import SubnetScanResult, Device from config.ports import port_config @@ -24,9 +25,7 @@ logging.basicConfig( format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") logger = logging.getLogger(__name__) - class NetworkScanner: - def __init__(self, interface: str): self.interface = interface conf.iface = interface @@ -46,9 +45,7 @@ class NetworkScanner: logger.error(f"Ошибка ARP-сканирования подсети {subnet}: {str(e)}") return [] - class PortScanner: - def __init__(self, interface: str): self.interface = interface @@ -61,7 +58,6 @@ class PortScanner: IP(dst=ip) / TCP(dport=port, flags="S"), timeout=2, verbose=False, - iface=self.interface, ) if pkt and pkt.haslayer(TCP) and pkt[TCP].flags == 0x12: return port_config.get_port_name(port) @@ -83,11 +79,11 @@ class PortScanner: f"Ошибка при сканировании портов для IP {ip}: {str(e)}") return [] - class DeviceInfoCollector: - def __init__(self): self.mac_lookup = MacLookup() + self.nbtscan_cache = {} + self.nbtscan_results = {} @lru_cache(maxsize=1000) def get_vendor(self, mac: str) -> str: @@ -98,16 +94,51 @@ class DeviceInfoCollector: f"Не удалось определить производителя для MAC {mac}: {str(e)}") return "Unknown" + def scan_nbtscan(self, subnet: str) -> None: + if subnet not in self.nbtscan_results: + try: + logger.debug(f"Запуск nbtscan для подсети {subnet}") + result = subprocess.run(["sudo", "nbtscan", "-r", str(subnet)], + capture_output=True, + text=True, + check=False, + timeout=10) + logger.debug(f"Полный вывод nbtscan: {result.stdout}") + if result.returncode == 0: + for line in result.stdout.splitlines(): + logger.debug(f"Обработка строки: {line}") + if not line.startswith(("IP address", "-", "Doing NBT")): + parts = [p for p in line.split() if p] + if len(parts) >= 2: + ip_addr, hostname = parts[0], parts[1] + if hostname != "": + self.nbtscan_cache[ip_addr] = hostname + logger.info(f"NetBIOS: Найден хостнейм {hostname} для IP {ip_addr}") + self.nbtscan_results[subnet] = True + except subprocess.TimeoutExpired: + logger.error(f"NetBIOS: Таймаут для подсети {subnet}") + except Exception as e: + logger.error(f"NetBIOS: Ошибка для подсети {subnet}: {str(e)}") + def get_hostname(self, ip: str) -> Optional[str]: try: + socket.setdefaulttimeout(2) return socket.gethostbyaddr(ip)[0] - except: - return None + except (socket.herror, socket.gaierror, socket.timeout): + logger.debug(f"DNS: Не удалось разрешить имя для IP {ip}") + subnet = ipaddress.ip_network(f"{ip}/24", strict=False) + if subnet not in self.nbtscan_results: + self.scan_nbtscan(str(subnet)) + + if ip in self.nbtscan_cache: + return self.nbtscan_cache[ip] + + logger.debug(f"Не удалось определить хостнейм для IP {ip} через DNS или NetBIOS") + return None class DeviceStorage: - - def __init__(self, storage_path: str = "storage/devices/devices.json"): + def __init__(self, storage_path: str = "etc/gonets/SRV/storage/devices/devices.json"): self.storage_file = Path(storage_path) self.storage_file.parent.mkdir(parents=True, exist_ok=True) self.offline_timeout = timedelta(days=7) @@ -128,8 +159,7 @@ class DeviceStorage: logger.error(f"Ошибка при загрузке устройств: {str(e)}") return {} - def _clean_old_devices(self, devices: Dict[str, - Device]) -> Dict[str, Device]: + def _clean_old_devices(self, devices: Dict[str, Device]) -> Dict[str, Device]: now = datetime.now() filtered = {} for mac, device in devices.items(): @@ -139,11 +169,9 @@ class DeviceStorage: if now - last_seen <= self.offline_timeout: filtered[mac] = device else: - logger.info( - f"Удалено устройство {mac} (offline > 7 дней)") + logger.info(f"Удалено устройство {mac} (offline > 7 дней)") except ValueError: - logger.warning( - f"Неверный формат last_seen для устройства {mac}") + logger.warning(f"Неверный формат last_seen для устройства {mac}") else: filtered[mac] = device return filtered @@ -153,14 +181,11 @@ class DeviceStorage: data = SubnetScanResult(devices=devices).model_dump() with open(self.storage_file, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) - logger.info( - f"Сохранено {len(devices)} устройств в {self.storage_file}") + logger.info(f"Сохранено {len(devices)} устройств в {self.storage_file}") except Exception as e: logger.error(f"Ошибка при сохранении устройств: {str(e)}") - class SubnetService: - def __init__(self): self.interface = self._get_default_interface() self.network_scanner = NetworkScanner(self.interface) @@ -171,7 +196,7 @@ class SubnetService: def _get_default_interface(self) -> str: try: gateways = netifaces.gateways() - default_gateway = gateways.get("default",{}).get(netifaces.AF_INET) + default_gateway = gateways.get("default", {}).get(netifaces.AF_INET) if default_gateway: return default_gateway[1] interfaces = netifaces.interfaces() @@ -183,9 +208,7 @@ class SubnetService: logger.warning(f"Не удалось определить интерфейс: {str(e)}") return "eth0" - def scan_subnet(self, - subnet: str, - ports: List[int] = None) -> SubnetScanResult: + def scan_subnet(self, subnet: str, ports: List[int] = None) -> SubnetScanResult: start_time = time.time() devices = [] now = datetime.now().astimezone() @@ -202,6 +225,7 @@ class SubnetService: saved_devices[local_device.mac] = local_device discovered_devices = self.network_scanner.scan(subnet) + self.device_info.scan_nbtscan(subnet) def process_device(ip_mac: Tuple[str, str]) -> Optional[Device]: ip, mac = ip_mac @@ -213,11 +237,9 @@ class SubnetService: return Device( ip=ip, mac=mac, - hostname=self.device_info.get_hostname(ip) - or existing_device.hostname, + hostname=self.device_info.get_hostname(ip) or existing_device.hostname, vendor=self.device_info.get_vendor(mac), - open_ports=self.port_scanner.scan(ip) - or existing_device.open_ports, + open_ports=self.port_scanner.scan(ip) or existing_device.open_ports, status="online", last_seen=formatted_now, note=existing_device.note, @@ -237,8 +259,7 @@ class SubnetService: ) with ThreadPoolExecutor(max_workers=10) as executor: - device_results = executor.map(process_device, - discovered_devices) + device_results = executor.map(process_device, discovered_devices) devices.extend([d for d in device_results if d is not None]) active_macs = {d.mac for d in devices if d.mac} @@ -282,7 +303,7 @@ class SubnetService: hostname = self.device_info.get_hostname(ip) open_ports = self.port_scanner.scan(ip) logger.debug( - f"Локальное устройство: IP={ip}, MAC={mac}, Ports={open_ports}" + f"Локальное устройство: IP={ip}, MAC={mac}, Ports={open_ports}, Hostname={hostname}" ) return Device( ip=ip, @@ -400,4 +421,4 @@ class SubnetService: except Exception as e: logger.error( f"Ошибка при удалении заметки для MAC {mac}: {str(e)}") - return False, f"Внутренняя ошибка сервера: {str(e)}" + return False, f"Внутренняя ошибка сервера: {str(e)}" \ No newline at end of file diff --git a/SRV/services/wifi_service.py b/SRV/services/wifi_service.py index 0289cc7..30cc458 100644 --- a/SRV/services/wifi_service.py +++ b/SRV/services/wifi_service.py @@ -20,7 +20,7 @@ logger = logging.getLogger(__name__) class WifiStorage: """Класс для хранения данных о Wi-Fi сетях и их заметках по SSID.""" - def __init__(self, storage_path: str = "storage/wifi/wifi.json"): + def __init__(self, storage_path: str = "etc/gonets/SRV/storage/wifi/wifi.json"): self.storage_file = Path(storage_path) self.storage_file.parent.mkdir(parents=True, exist_ok=True)