2025-07-10 13:34:45 +05:00
|
|
|
|
from fastapi import Depends
|
2025-06-09 10:17:12 +05:00
|
|
|
|
from scapy.layers.l2 import ARP, Ether
|
|
|
|
|
|
from scapy.sendrecv import srp
|
|
|
|
|
|
import netifaces
|
|
|
|
|
|
from netaddr import IPNetwork
|
2025-07-08 15:57:42 +05:00
|
|
|
|
from utils.i18n import get_translations, _
|
|
|
|
|
|
from babel.support import Translations
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-06-09 10:17:12 +05:00
|
|
|
|
class NetworkScanner:
|
2025-06-23 12:47:05 +05:00
|
|
|
|
"""Инициализация сетевого сканера.
|
|
|
|
|
|
|
|
|
|
|
|
Предоставляет методы для сканирования сети с использованием ARP-запросов и
|
|
|
|
|
|
вывода информации о клиентах.
|
|
|
|
|
|
"""
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-07-08 15:57:42 +05:00
|
|
|
|
def __init__(self, interface=None, translations: Translations = Depends(get_translations("services"))):
|
2025-06-23 12:47:05 +05:00
|
|
|
|
"""Инициализация параметров сетевого сканера.
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
interface (str, optional): Название сетевого интерфейса. По умолчанию
|
|
|
|
|
|
используется интерфейс по умолчанию из netifaces.gateways().
|
2025-07-08 15:57:42 +05:00
|
|
|
|
translations: Объект переводов для текущего языка.
|
2025-06-23 12:47:05 +05:00
|
|
|
|
|
|
|
|
|
|
Инициализирует интерфейс, информацию об IP-адресах и локальный MAC-адрес.
|
|
|
|
|
|
"""
|
2025-06-12 16:32:05 +05:00
|
|
|
|
self.interface = (
|
2025-06-12 16:47:27 +05:00
|
|
|
|
interface or netifaces.gateways()["default"][netifaces.AF_INET][1])
|
|
|
|
|
|
self.ip_info = netifaces.ifaddresses(self.interface).get(
|
|
|
|
|
|
netifaces.AF_INET, [])
|
2025-06-12 16:32:05 +05:00
|
|
|
|
self.networks = [
|
|
|
|
|
|
IPNetwork(f"{ip['addr']}/{ip['netmask']}") for ip in self.ip_info
|
|
|
|
|
|
]
|
2025-06-12 16:47:27 +05:00
|
|
|
|
self.local_mac = netifaces.ifaddresses(
|
|
|
|
|
|
self.interface)[netifaces.AF_LINK][0]["addr"]
|
2025-07-08 15:57:42 +05:00
|
|
|
|
self.translations = translations
|
2025-06-09 10:17:12 +05:00
|
|
|
|
|
|
|
|
|
|
def _create_arp_packet(self, target_ip):
|
2025-06-23 12:47:05 +05:00
|
|
|
|
"""Создаёт ARP-пакет для отправки на указанный IP-адрес.
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
target_ip (str): Целевой IP-адрес или подсеть для сканирования.
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
scapy.packet.Packet: Сформированный ARP-пакет с Ethernet-заголовком.
|
|
|
|
|
|
"""
|
2025-06-09 10:17:12 +05:00
|
|
|
|
arp = ARP(pdst=target_ip)
|
|
|
|
|
|
ether = Ether(dst="ff:ff:ff:ff:ff:ff")
|
2025-06-12 16:32:05 +05:00
|
|
|
|
return ether / arp
|
2025-06-09 10:17:12 +05:00
|
|
|
|
|
|
|
|
|
|
def scan_network(self, attempts=3, timeout=2):
|
2025-06-23 12:47:05 +05:00
|
|
|
|
"""Сканирует сеть для обнаружения активных клиентов.
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
attempts (int, optional): Количество попыток отправки ARP-запросов.
|
|
|
|
|
|
По умолчанию 3.
|
|
|
|
|
|
timeout (int, optional): Таймаут ожидания ответа в секундах.
|
|
|
|
|
|
По умолчанию 2.
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
List[dict]: Список словарей с ключами "ip" и "mac", представляющих
|
|
|
|
|
|
обнаруженных клиентов, включая локальные IP-адреса.
|
|
|
|
|
|
|
|
|
|
|
|
Note:
|
|
|
|
|
|
Учитывает локальные IP-адреса интерфейса и удаляет дубликаты.
|
|
|
|
|
|
"""
|
2025-06-09 10:17:12 +05:00
|
|
|
|
all_clients = []
|
|
|
|
|
|
|
|
|
|
|
|
for network in self.networks:
|
|
|
|
|
|
target_ip = f"{network.network}/{network.prefixlen}"
|
|
|
|
|
|
packet = self._create_arp_packet(target_ip)
|
|
|
|
|
|
clients = []
|
|
|
|
|
|
|
|
|
|
|
|
for _ in range(attempts):
|
2025-06-12 16:47:27 +05:00
|
|
|
|
result = srp(packet,
|
|
|
|
|
|
timeout=timeout,
|
|
|
|
|
|
iface=self.interface,
|
|
|
|
|
|
verbose=0)[0]
|
2025-06-09 10:17:12 +05:00
|
|
|
|
for _, received in result:
|
2025-06-12 16:32:05 +05:00
|
|
|
|
client = {"ip": received.psrc, "mac": received.hwsrc}
|
2025-06-23 12:47:05 +05:00
|
|
|
|
if client not in clients:
|
2025-06-09 10:17:12 +05:00
|
|
|
|
clients.append(client)
|
|
|
|
|
|
|
|
|
|
|
|
all_clients.extend(clients)
|
|
|
|
|
|
|
|
|
|
|
|
for ip_info in self.ip_info:
|
2025-06-12 16:32:05 +05:00
|
|
|
|
local_client = {"ip": ip_info["addr"], "mac": self.local_mac}
|
2025-06-09 10:17:12 +05:00
|
|
|
|
if local_client not in all_clients:
|
|
|
|
|
|
all_clients.append(local_client)
|
|
|
|
|
|
|
|
|
|
|
|
unique_clients = {tuple(client.items()) for client in all_clients}
|
|
|
|
|
|
return [dict(client) for client in unique_clients]
|
|
|
|
|
|
|
|
|
|
|
|
def print_clients(self, clients):
|
2025-06-23 12:47:05 +05:00
|
|
|
|
"""Выводит список адресов клиентов в формате таблицы.
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
clients (List[dict]): Список словарей с ключами "ip" и "mac",
|
|
|
|
|
|
представляющих клиентов.
|
|
|
|
|
|
|
|
|
|
|
|
Выводит IP-адреса и MAC-адреса в отсортированном по IP порядке.
|
|
|
|
|
|
"""
|
2025-07-08 15:57:42 +05:00
|
|
|
|
print(self.translations.gettext("Доступные устройства в сети:"))
|
|
|
|
|
|
print(self.translations.gettext("IP") + " " * 18 + self.translations.gettext("MAC"))
|
2025-06-12 16:32:05 +05:00
|
|
|
|
for client in sorted(clients, key=lambda x: x["ip"]):
|
2025-06-09 10:17:12 +05:00
|
|
|
|
print(f"{client['ip']:<16} {client['mac']}")
|
|
|
|
|
|
|
2025-06-23 12:47:05 +05:00
|
|
|
|
# Оставил нативный запуск сервиса для тестов
|
2025-06-09 10:17:12 +05:00
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
scanner = NetworkScanner()
|
|
|
|
|
|
clients = scanner.scan_network()
|
2025-06-23 12:47:05 +05:00
|
|
|
|
scanner.print_clients(clients)
|