Итерация 2: сканирование сети

- Phinx миграции: network_ranges, scan_jobs, discovered_hosts, audit_log
- Models: NetworkRange, ScanJob, DiscoveredHost
- Repositories с CRUD для всех сущностей
- NetworkScanner: ping sweep + TCP connect + ARP table + reverse DNS
- PingScanner, TcpPortScanner (curl_multi для параллелизма), ArpTableReader
- HostFingerprintService: vendor detection + confidence scoring
- ScanJobRunner + CLI worker (bin/run-scan-worker.php)
- Discovery UI: управление диапазонами, запуск скана, таблица находок
- NetworkRangeController с валидацией CIDR
- Обновлён public/index.php с DI конфигурацией для новых сервисов
This commit is contained in:
2026-05-26 07:41:23 +08:00
parent e7732f5cee
commit 177e44f015
22 changed files with 1477 additions and 1 deletions
@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
namespace Domovoy\Controllers;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class NetworkRangeController
{
private \Domovoy\Repositories\NetworkRangeRepository $repository;
public function __construct(\Domovoy\Repositories\NetworkRangeRepository $repository)
{
$this->repository = $repository;
}
public function create(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$data = $request->getParsedBody();
$name = trim($data['name'] ?? '');
$cidr = trim($data['cidr'] ?? '');
if ($name === '' || $cidr === '') {
$response->getBody()->write('Name and CIDR are required');
return $response->withStatus(400);
}
// Basic CIDR validation
if (!$this->isValidCidr($cidr)) {
$response->getBody()->write('Invalid CIDR format');
return $response->withStatus(400);
}
$range = new \Domovoy\Models\NetworkRange();
$range->name = $name;
$range->cidr = $cidr;
$range->enabled = true;
$this->repository->save($range);
return $response
->withHeader('Location', '/discovery')
->withStatus(302);
}
public function toggle(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$data = $request->getParsedBody();
$id = $data['id'] ?? null;
if ($id !== null) {
$range = $this->repository->findById((int)$id);
if ($range !== null) {
$range->enabled = !$range->enabled;
$this->repository->save($range);
}
}
return $response
->withHeader('Location', '/discovery')
->withStatus(302);
}
public function delete(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$data = $request->getParsedBody();
$id = $data['id'] ?? null;
if ($id !== null) {
$this->repository->delete((int)$id);
}
return $response
->withHeader('Location', '/discovery')
->withStatus(302);
}
private function isValidCidr(string $cidr): bool
{
if (!str_contains($cidr, '/')) {
return false;
}
[$ip, $mask] = explode('/', $cidr, 2);
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return false;
}
$maskInt = (int)$mask;
return $maskInt >= 8 && $maskInt <= 32;
}
}