73 lines
3.0 KiB
PHP
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);
|
|
}
|
|
}
|
|
}
|