feat: implement mail-login directadmin plugin
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
final class ClientIp
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed> $server
|
||||
*/
|
||||
public static function resolve(array $server, Settings $settings): string
|
||||
{
|
||||
$remote = self::validIp((string)($server['REMOTE_ADDR'] ?? ''));
|
||||
if ($settings->clientIpMode() === 'direct') {
|
||||
return $remote;
|
||||
}
|
||||
if (!self::ipInList($remote, $settings->trustedProxies())) {
|
||||
throw new RuntimeException('REMOTE_ADDR is not a trusted proxy');
|
||||
}
|
||||
$xff = (string)($server['HTTP_X_FORWARDED_FOR'] ?? '');
|
||||
foreach (explode(',', $xff) as $candidate) {
|
||||
$candidate = trim($candidate);
|
||||
if (filter_var($candidate, FILTER_VALIDATE_IP)) {
|
||||
return $candidate;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException('Missing valid forwarded client IP');
|
||||
}
|
||||
|
||||
private static function validIp(string $ip): string
|
||||
{
|
||||
$ip = trim($ip);
|
||||
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
throw new InvalidArgumentException('Invalid client IP');
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $allowed
|
||||
*/
|
||||
private static function ipInList(string $ip, array $allowed): bool
|
||||
{
|
||||
foreach ($allowed as $entry) {
|
||||
if ($entry === $ip) {
|
||||
return true;
|
||||
}
|
||||
if (str_contains($entry, '/') && self::cidrContains($entry, $ip)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function cidrContains(string $cidr, string $ip): bool
|
||||
{
|
||||
[$network, $prefix] = array_pad(explode('/', $cidr, 2), 2, '');
|
||||
if (!filter_var($network, FILTER_VALIDATE_IP) || !preg_match('/^[0-9]+$/', $prefix)) {
|
||||
return false;
|
||||
}
|
||||
$networkBin = inet_pton($network);
|
||||
$ipBin = inet_pton($ip);
|
||||
if ($networkBin === false || $ipBin === false || strlen($networkBin) !== strlen($ipBin)) {
|
||||
return false;
|
||||
}
|
||||
$bits = (int)$prefix;
|
||||
$bytes = intdiv($bits, 8);
|
||||
$remainder = $bits % 8;
|
||||
if ($bytes > 0 && substr($networkBin, 0, $bytes) !== substr($ipBin, 0, $bytes)) {
|
||||
return false;
|
||||
}
|
||||
if ($remainder === 0) {
|
||||
return true;
|
||||
}
|
||||
$mask = chr((0xff << (8 - $remainder)) & 0xff);
|
||||
return (ord($networkBin[$bytes]) & ord($mask)) === (ord($ipBin[$bytes]) & ord($mask));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user