Files
services_core/app/Services/Nas/RadiusHealthService.php
T
2026-08-07 09:16:13 +07:00

73 lines
3.0 KiB
PHP

<?php
namespace App\Services\Nas;
use App\Models\NasMikrotik;
use Illuminate\Validation\ValidationException;
class RadiusHealthService
{
public function testConnection(NasMikrotik $radius): array
{
if (! $radius->host || ! $radius->radius_secret) {
throw ValidationException::withMessages(['radius' => 'Host dan shared secret RADIUS wajib lengkap.']);
}
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) {
throw ValidationException::withMessages(['radius' => 'Socket UDP untuk RADIUS tidak dapat dibuat.']);
}
$timeout = max(1, min((int) ($radius->timeout ?: 5), 10));
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => $timeout, 'usec' => 0]);
$identifier = random_int(0, 255);
$requestAuthenticator = random_bytes(16);
$request = pack('CCn', 12, $identifier, 20).$requestAuthenticator;
$startedAt = hrtime(true);
try {
$sent = @socket_sendto(
$socket,
$request,
strlen($request),
0,
$radius->host,
(int) ($radius->radius_auth_port ?: 1812),
);
if ($sent === false) {
throw ValidationException::withMessages(['radius' => 'Status-Server RADIUS gagal dikirim.']);
}
$response = '';
$from = '';
$port = 0;
$received = @socket_recvfrom($socket, $response, 4096, 0, $from, $port);
if ($received === false || $received < 20) {
throw ValidationException::withMessages([
'radius' => 'RADIUS tidak merespons Status-Server. Pastikan Status-Server diizinkan oleh server.',
]);
}
['code' => $code, 'identifier' => $responseIdentifier, 'length' => $length] = unpack('Ccode/Cidentifier/nlength', substr($response, 0, 4));
if ($responseIdentifier !== $identifier || $length > strlen($response)) {
throw ValidationException::withMessages(['radius' => 'Respons RADIUS tidak valid.']);
}
$attributes = substr($response, 20, $length - 20);
$expected = md5(substr($response, 0, 4).$requestAuthenticator.$attributes.$radius->radius_secret, true);
if (! hash_equals($expected, substr($response, 4, 16))) {
throw ValidationException::withMessages(['radius' => 'Authenticator respons RADIUS tidak valid.']);
}
return [
'connected' => true,
'latency_ms' => (int) round((hrtime(true) - $startedAt) / 1_000_000),
'host' => $radius->host,
'auth_port' => (int) ($radius->radius_auth_port ?: 1812),
'accounting_port' => (int) ($radius->radius_accounting_port ?: 1813),
'response_code' => $code,
'probe' => 'radius_status_server',
];
} finally {
socket_close($socket);
}
}
}