mirror of
https://github.com/Tizian-Maxime-Weigt/IP-to-ASN-and-Whois-API.git
synced 2026-03-18 09:41:08 +00:00
Add IP to ASN lookup functionality
This PHP script handles IP address input, validates it, caches results, and performs a binary search on a database to find ASN information. It also includes a class for ASN configuration and a function to display country names based on locale.
This commit is contained in:
parent
f0e11c3372
commit
a8bb077a9c
231
index.php
Normal file
231
index.php
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
<?php
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
|
||||||
|
// 1. INPUT HANDLING
|
||||||
|
if (isset($_GET['ip'])) {
|
||||||
|
$ip = trim($_GET['ip']);
|
||||||
|
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||||
|
$ipList = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
||||||
|
$ip = trim($ipList[0]);
|
||||||
|
} else {
|
||||||
|
$ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
|
||||||
|
}
|
||||||
|
|
||||||
|
$locale = isset($_GET['locale']) ? strtolower($_GET['locale']) : 'both';
|
||||||
|
if (!in_array($locale, ['de','en','both'])) {
|
||||||
|
$locale = 'both';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. VALIDATION & BINARY CONVERSION
|
||||||
|
$targetBin = @inet_pton($ip);
|
||||||
|
if ($targetBin === false) {
|
||||||
|
echo json_encode(["error" => "Invalid IP address"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$isIPv4 = (strlen($targetBin) === 4);
|
||||||
|
|
||||||
|
// 3. CACHING
|
||||||
|
$cacheDir = __DIR__ . '/cache';
|
||||||
|
if (!is_dir($cacheDir)) {
|
||||||
|
@mkdir($cacheDir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheKey = ($isIPv4 ? 'v4_' : 'v6_') . md5($ip) . '_' . $locale;
|
||||||
|
$cacheFile = $cacheDir . '/' . $cacheKey . '.json';
|
||||||
|
$cacheTtl = 8 * 3600;
|
||||||
|
|
||||||
|
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) <= $cacheTtl)) {
|
||||||
|
readfile($cacheFile);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. DATABASE LOOKUP (BINARY SEARCH)
|
||||||
|
$dbFile = __DIR__ . "/iptoasn.tsv";
|
||||||
|
$handle = @fopen($dbFile, "r");
|
||||||
|
|
||||||
|
if (!$handle) {
|
||||||
|
echo json_encode(["error" => "Could not open ASN database"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = null;
|
||||||
|
$fileSize = filesize($dbFile);
|
||||||
|
$low = 0;
|
||||||
|
$high = $fileSize;
|
||||||
|
|
||||||
|
while ($low <= $high) {
|
||||||
|
$mid = (int)(($low + $high) / 2);
|
||||||
|
fseek($handle, $mid);
|
||||||
|
|
||||||
|
if ($mid != 0) {
|
||||||
|
fgets($handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
$line = fgets($handle);
|
||||||
|
|
||||||
|
if (!$line) {
|
||||||
|
$high = $mid - 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = explode("\t", $line);
|
||||||
|
if (count($parts) < 5) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$startBin = @inet_pton($parts[0]);
|
||||||
|
$endBin = @inet_pton($parts[1]);
|
||||||
|
$rowIsV4 = (strlen($startBin) === 4);
|
||||||
|
|
||||||
|
if ($isIPv4 && !$rowIsV4) {
|
||||||
|
$high = $mid - 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$isIPv4 && $rowIsV4) {
|
||||||
|
$low = $mid + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($targetBin < $startBin) {
|
||||||
|
$high = $mid - 1;
|
||||||
|
} elseif ($targetBin > $endBin) {
|
||||||
|
$low = $mid + 1;
|
||||||
|
} else {
|
||||||
|
$asn = trim($parts[2]);
|
||||||
|
$cc = trim($parts[3]);
|
||||||
|
$desc = trim($parts[4]);
|
||||||
|
|
||||||
|
$readable = AsnConfig::getDescription($asn, $desc);
|
||||||
|
|
||||||
|
$result = [
|
||||||
|
"ip" => $ip,
|
||||||
|
"asn" => $asn,
|
||||||
|
"country" => $cc,
|
||||||
|
"country_name" => getCountryDisplay($cc, $locale),
|
||||||
|
"description" => $readable,
|
||||||
|
"logo" => AsnConfig::getLogoUrl($asn, $readable)
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($handle);
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
$json = json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||||
|
} else {
|
||||||
|
$json = json_encode(["error" => "ASN not found for given IP"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($cacheFile, $json, LOCK_EX);
|
||||||
|
echo $json;
|
||||||
|
|
||||||
|
|
||||||
|
class AsnConfig
|
||||||
|
{
|
||||||
|
private static array $asnMap = [
|
||||||
|
"3320" => "Deutsche Telekom", "45090" => "Tencent", "132203" => "Tencent",
|
||||||
|
"12876" => "Scaleway", "17012" => "PayPal", "39832" => "Opera",
|
||||||
|
"31898" => "Oracle", "14593" => "Starlink", "216246" => "Aeza",
|
||||||
|
"62041" => "Telegram", "26141" => "CubePath", "60781" => "Leaseweb",
|
||||||
|
"262287" => "Latitude", "206996" => "ZAP-Hosting", "8767" => "M-net",
|
||||||
|
"6805" => "o2", "213200" => "Tube Hosting", "50436" => "Pyur", "42652" => "Deutsche Glasfaser", "60294" => "Deutsche Glasfaser", "8220" => "COLT", "29802" => "HIVELOCITY"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static array $textMap = [
|
||||||
|
"CLOUDFLARENET" => "Cloudflare, Inc.", "AKAMAI-AS" => "Akamai",
|
||||||
|
"GOOGLE" => "Google", "META" => "Meta", "FACEBOOK" => "Meta",
|
||||||
|
"AMAZON" => "Amazon", "APPLE" => "Apple", "MICROSOFT" => "Microsoft",
|
||||||
|
"DIGITALOCEAN" => "DigitalOcean", "HETZNER" => "Hetzner",
|
||||||
|
"OVH" => "OVHcloud", "LINODE" => "Linode", "QUAD9-AS-1" => "Quad9",
|
||||||
|
"BAHN-AS-BLN" => "Deutsche Bahn", "BAHN-WIFI-AS" => "Deutsche Bahn Public Wifi",
|
||||||
|
"VERSATEL" => "1&1 Deutschland", "IONOS" => "IONOS", "Clouvider" => "Clouvider",
|
||||||
|
"HUAWEI CLOUDS" => "Huawei Cloud", "LEASEWEB" => "Leaseweb",
|
||||||
|
"EXTRAVM-LLC" => "Extra VM", "TORSERVERS-NET" => "TORServers.net",
|
||||||
|
"GO-DADDY-COM-LLC" => "GoDaddy", "CONTABO" => "Contabo GmbH",
|
||||||
|
"ONE-NETWORK" => "dogado GmbH", "TELEHOUSE-AS" => "Telehouse",
|
||||||
|
"LATITUDE-SH" => "Latitude", "FERDINANDZINK" => "Tube Hosting",
|
||||||
|
"Akamai Connected Cloud" => "Akamai", "VULTR" => "Vultr",
|
||||||
|
"Vodafone" => "Vodafone", "RELIABLESITE" => "ReliableSite",
|
||||||
|
"TELESYSTEM-AS" => "Telesystem", "NETCOLOGNE" => "NetCologne",
|
||||||
|
"DATAFOREST" => "Dataforest", "TMW-GLOBAL-NETWORKS" => "TMW Global Networks"
|
||||||
|
];
|
||||||
|
|
||||||
|
public static function getDescription(string $asn, string $desc): string
|
||||||
|
{
|
||||||
|
if (isset(self::$asnMap[$asn])) {
|
||||||
|
return self::$asnMap[$asn];
|
||||||
|
}
|
||||||
|
|
||||||
|
$descClean = strpos($desc, ' ') !== false
|
||||||
|
? implode(" ", array_slice(preg_split('/\s+/', trim($desc)), 1))
|
||||||
|
: trim($desc);
|
||||||
|
|
||||||
|
foreach (self::$textMap as $needle => $replacement) {
|
||||||
|
if (stripos($descClean, $needle) !== false) {
|
||||||
|
return $replacement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $descClean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getLogoUrl(string $asn, string $readableDesc): string
|
||||||
|
{
|
||||||
|
$base = "https://cdn.t-w.dev/img/%s.webp";
|
||||||
|
|
||||||
|
if (isset(self::$asnMap[$asn])) {
|
||||||
|
return sprintf($base, rawurlencode(self::$asnMap[$asn]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static $logoMatchMap = null;
|
||||||
|
if ($logoMatchMap === null) {
|
||||||
|
$logoMatchMap = array_flip([
|
||||||
|
"NETCOLOGNE" => "NetCologne", "CLOUDFLARE" => "Cloudflare",
|
||||||
|
"AKAMAI" => "Akamai", "FASTLY" => "Fastly", "GOOGLE" => "Google",
|
||||||
|
"META" => "Meta", "FACEBOOK" => "Meta", "AMAZON" => "Amazon",
|
||||||
|
"AWS" => "Amazon", "APPLE" => "Apple", "MICROSOFT" => "Microsoft",
|
||||||
|
"DIGITALOCEAN" => "DigitalOcean", "HETZNER" => "Hetzner",
|
||||||
|
"OVH" => "OVHcloud", "LINODE" => "Linode", "VULTR" => "Vultr",
|
||||||
|
"LEASEWEB" => "Leaseweb", "CONTABO" => "Contabo", "IONOS" => "IONOS",
|
||||||
|
"GO-DADDY" => "GoDaddy", "QUAD9" => "Quad9", "DEUTSCHE TELEKOM" => "Deutsche Telekom",
|
||||||
|
"TELEKOM" => "Deutsche Telekom", "VODAFONE" => "Vodafone",
|
||||||
|
"TORSERVERS" => "TORServers", "RELIABLESITE" => "ReliableSite",
|
||||||
|
"CLOUVIDER" => "Clouvider", "HUAWEI" => "Huawei Cloud",
|
||||||
|
"DOGADO" => "dogado", "TELEHOUSE" => "Telehouse",
|
||||||
|
"DATAFOREST" => "Dataforest", "TMW-GLOBAL-NETWORKS" => "TMW Global Networks"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$descNorm = trim($readableDesc);
|
||||||
|
foreach ($logoMatchMap as $needle => $canonical) {
|
||||||
|
if (mb_stripos($descNorm, $needle) !== false) {
|
||||||
|
return sprintf($base, rawurlencode($canonical));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf($base, rawurlencode(mb_substr($descNorm ?: "Unknown", 0, 64, 'UTF-8')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCountryDisplay($cc, $locale = 'both') {
|
||||||
|
static $countryMap = [
|
||||||
|
"DE" => ["de" => "Deutschland", "en" => "Germany"],
|
||||||
|
"US" => ["de" => "Vereinigte Staaten", "en" => "United States"],
|
||||||
|
"GB" => ["de" => "Vereinigtes Königreich", "en" => "United Kingdom"],
|
||||||
|
"FR" => ["de" => "Frankreich", "en" => "France"],
|
||||||
|
"NL" => ["de" => "Niederlande", "en" => "Netherlands"],
|
||||||
|
"IT" => ["de" => "Italien", "en" => "Italy"],
|
||||||
|
"ES" => ["de" => "Spanien", "en" => "Spain"]
|
||||||
|
];
|
||||||
|
|
||||||
|
$ccUp = strtoupper(trim($cc));
|
||||||
|
|
||||||
|
if (isset($countryMap[$ccUp])) {
|
||||||
|
$entry = $countryMap[$ccUp];
|
||||||
|
if ($locale === 'de') return $entry['de'] ?? $ccUp;
|
||||||
|
if ($locale === 'en') return $entry['en'] ?? $ccUp;
|
||||||
|
return ($entry['de'] ?? $ccUp) . " / " . ($entry['en'] ?? $ccUp);
|
||||||
|
}
|
||||||
|
return $ccUp;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user