Files
Atomcms-edit/app/Services/IpLookupService.php
T
2026-05-09 17:32:17 +02:00

185 lines
5.8 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace App\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
readonly class IpLookupService
{
private const array BLOCKLISTS = [
// FireHol - All Levels (werkt)
'firehol_level1' => 'https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset',
'firehol_level2' => 'https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level2.netset',
'firehol_level3' => 'https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level3.netset',
'firehol_level4' => 'https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level4.netset',
// Spamhaus (werkt)
'spamhaus_drop' => 'https://www.spamhaus.org/drop/drop.txt',
'spamhaus_edrop' => 'https://www.spamhaus.org/drop/edrop.txt',
// Threat Intelligence (werkt)
'dshield' => 'https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/dshield.netset',
'emerging_threats' => 'https://rules.emergingthreats.net/blockrules/compromised-ips.txt',
// Blocklist.de (werkt)
'blocklist_de_all' => 'https://lists.blocklist.de/lists/all.txt',
'blocklist_de_mail' => 'https://lists.blocklist.de/lists/mail.txt',
'blocklist_de_ssh' => 'https://lists.blocklist.de/lists/ssh.txt',
'blocklist_de_ftp' => 'https://lists.blocklist.de/lists/ftp.txt',
'blocklist_de_sip' => 'https://lists.blocklist.de/lists/sip.txt',
];
private const int CACHE_TIME = 43200;
public function getCountryInfo(string $ip): array
{
$cacheKey = "ip_country_{$ip}";
return Cache::remember($cacheKey, now()->addDays(7), function () use ($ip) {
try {
$response = Http::timeout(10)->get("http://ip-api.com/json/{$ip}");
if ($response->ok()) {
$data = $response->json();
return [
'country_code' => $data['countryCode'] ?? null,
'country_name' => $data['country'] ?? null,
'region' => $data['regionName'] ?? null,
'city' => $data['city'] ?? null,
'isp' => $data['isp'] ?? null,
'org' => $data['org'] ?? null,
'as' => $data['as'] ?? null,
];
}
} catch (\Exception $e) {
return ['error' => $e->getMessage()];
}
return ['error' => 'Failed to fetch'];
});
}
public function checkVpnProxyTor(string $ip): array
{
$cacheKey = "ip_check_{$ip}";
return Cache::remember($cacheKey, now()->addHours(6), function () use ($ip) {
$isBlocked = $this->isIpInAnyBlocklist($ip);
return [
'is_vpn' => $isBlocked,
'is_proxy' => $isBlocked,
'is_tor' => $isBlocked,
'is_malicious' => $isBlocked,
'source' => 'multi_blocklist',
];
});
}
private function isIpInAnyBlocklist(string $ip): bool
{
foreach (self::BLOCKLISTS as $name => $url) {
$cidrs = $this->getBlocklistCidrs($name, $url);
foreach ($cidrs as $cidr) {
if ($this->ipInCidr($ip, $cidr)) {
return true;
}
}
}
return false;
}
private function getBlocklistCidrs(string $name, string $url): array
{
$cacheKey = "cidr_blocklist_{$name}";
return Cache::remember($cacheKey, now()->addHours(self::CACHE_TIME), function () use ($url) {
try {
$response = Http::timeout(120)->get($url);
if (! $response->ok()) {
return [];
}
$lines = explode("\n", $response->body());
$cidrs = [];
foreach ($lines as $line) {
$line = trim($line);
if ($line === '' || $line === '0' || str_starts_with($line, '#') || str_starts_with($line, ';')) {
continue;
}
if (str_contains($line, '/')) {
$cidrs[] = $line;
} elseif (filter_var($line, FILTER_VALIDATE_IP)) {
$cidrs[] = $line . '/32';
}
}
return $cidrs;
} catch (\Exception) {
return [];
}
});
}
private function ipInCidr(string $ip, string $cidr): bool
{
if (! str_contains($cidr, '/')) {
return $ip === $cidr;
}
try {
[$subnet, $mask] = explode('/', $cidr);
$mask = (int) $mask;
if ($mask < 0 || $mask > 32) {
return false;
}
$ipLong = ip2long($ip);
$subnetLong = ip2long($subnet);
if ($ipLong === false || $subnetLong === false) {
return false;
}
$maskLong = -1 << (32 - $mask);
return ($ipLong & $maskLong) === ($subnetLong & $maskLong);
} catch (\Exception) {
return false;
}
}
public function refreshBlocklists(): void
{
foreach (array_keys(self::BLOCKLISTS) as $name) {
Cache::forget("cidr_blocklist_{$name}");
}
}
public function getBlocklistStats(): array
{
$totalCidrs = 0;
foreach (self::BLOCKLISTS as $name => $url) {
$cidrs = $this->getBlocklistCidrs($name, $url);
$totalCidrs += count($cidrs);
}
return [
'total' => $totalCidrs,
'source' => 'multi_blocklist',
];
}
}