Files
utools/frontend/app/asn-lookup.html
2026-03-05 21:04:57 +01:00

471 lines
19 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ASN / AS Lookup - uTools</title>
<meta name="description"
content="Look up any Autonomous System Number (ASN) to see peering connections, network graph, prefixes and IXP information.">
<script src="https://cdn.tailwindcss.com"></script>
<!-- D3.js v7 for network graph -->
<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
<style>
.loader {
border: 4px solid rgba(168, 85, 247, 0.1);
border-left-color: #d8b4fe;
border-radius: 50%;
width: 24px;
height: 24px;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.glass-panel {
background: rgba(17, 24, 39, 0.7);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.05);
}
.glass-card {
background: rgba(31, 41, 55, 0.6);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.05);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.glass-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.5);
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.5s ease-out forwards;
}
.text-gradient {
background: linear-gradient(to right, #c084fc, #e879f9);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.hidden {
display: none !important;
}
nav ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
nav a {
color: #d1d5db;
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
transition: all 0.2s ease;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.05);
}
nav a:hover {
color: #fff;
background: rgba(168, 85, 247, 0.2);
border-color: rgba(168, 85, 247, 0.4);
}
nav a.active-link {
background: rgba(168, 85, 247, 0.3);
color: #fff;
border-color: #a855f7;
}
header {
background: rgba(31, 41, 55, 0.4);
backdrop-filter: blur(10px);
padding: 1.5rem;
margin-bottom: 2rem;
border-radius: 1rem;
border: 1px solid rgba(255, 255, 255, 0.05);
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
}
@media (min-width: 768px) {
header {
flex-direction: row;
justify-content: space-between;
}
}
/* ── Network Graph ─────────────────────────────────────── */
#graph-container {
width: 100%;
height: 600px;
background: rgba(0, 0, 0, 0.3);
border-radius: 0.75rem;
border: 1px solid rgba(255, 255, 255, 0.06);
overflow: hidden;
position: relative;
}
#graph-svg {
width: 100%;
height: 100%;
cursor: grab;
}
#graph-svg:active {
cursor: grabbing;
}
.node-center circle {
fill: #a855f7;
stroke: #d8b4fe;
stroke-width: 2.5;
}
.node-upstream circle {
fill: #3b82f6;
stroke: #93c5fd;
stroke-width: 1.5;
}
.node-downstream circle {
fill: #10b981;
stroke: #6ee7b7;
stroke-width: 1.5;
}
.node-tier1 circle {
fill: #6b7280;
stroke: #9ca3af;
stroke-width: 1.5;
}
.node text {
fill: #e5e7eb;
font-size: 11px;
font-family: 'Courier New', monospace;
pointer-events: none;
text-anchor: middle;
}
.node:hover circle {
filter: brightness(1.4);
cursor: pointer;
}
.link {
stroke: rgba(255, 255, 255, 0.12);
stroke-linecap: round;
}
.link-upstream {
stroke: rgba(59, 130, 246, 0.35);
}
.link-tier1 {
stroke: rgba(107, 114, 128, 0.3);
stroke-dasharray: 4 3;
}
.link-downstream {
stroke: rgba(16, 185, 129, 0.35);
}
/* Tooltip */
#graph-tooltip {
position: absolute;
pointer-events: none;
background: rgba(17, 24, 39, 0.95);
backdrop-filter: blur(8px);
border: 1px solid rgba(168, 85, 247, 0.4);
border-radius: 0.5rem;
padding: 0.6rem 0.9rem;
font-size: 12px;
color: #e5e7eb;
max-width: 220px;
z-index: 50;
opacity: 0;
transition: opacity 0.15s;
}
/* Prefix list */
.prefix-tag {
display: inline-block;
font-family: monospace;
font-size: 11px;
background: rgba(168, 85, 247, 0.15);
color: #c084fc;
border: 1px solid rgba(168, 85, 247, 0.3);
border-radius: 4px;
padding: 2px 6px;
margin: 2px;
}
/* IXP table */
.ixp-row {
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.ixp-row:last-child {
border-bottom: none;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(31, 41, 55, 0.5);
}
::-webkit-scrollbar-thumb {
background: #4b5563;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #6b7280;
}
</style>
</head>
<body
class="bg-gray-950 text-gray-100 font-sans p-4 md:p-8 min-h-screen bg-[url('https://tailwindcss.com/_next/static/media/hero-dark.939eb757.png')] bg-cover bg-center bg-fixed selection:bg-purple-500 selection:text-white">
<header class="glass-panel">
<h1>uTools <span class="text-sm font-normal text-gray-400 opacity-75 tracking-wider uppercase ml-2">Network
Suite</span></h1>
<nav>
<ul>
<li><a href="/">IP Info &amp; Tools</a></li>
<li><a href="/subnet">Subnetz Rechner</a></li>
<li><a href="/dns">DNS Lookup</a></li>
<li><a href="/whois">WHOIS Lookup</a></li>
<li><a href="/mac">MAC Lookup</a></li>
<li><a href="/asn" class="active-link">ASN Lookup</a></li>
</ul>
</nav>
</header>
<div
class="container mx-auto max-w-6xl glass-panel rounded-xl shadow-2xl p-6 md:p-8 backdrop-blur-xl border border-gray-800/50">
<h1 class="text-3xl font-bold mb-2 text-center text-gradient">AS / ASN Lookup</h1>
<p class="text-center text-gray-400 text-sm mb-8">Peering graph, prefixes &amp; IXP connections for any
Autonomous System</p>
<!-- Search -->
<div class="flex flex-col sm:flex-row gap-3 mb-6 max-w-2xl mx-auto">
<input type="text" id="asn-input" placeholder="Enter ASN (e.g. 15169 or AS3320)"
class="flex-grow px-4 py-3 bg-gray-900/50 border border-gray-700/50 rounded-lg text-gray-200 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent font-mono transition-all placeholder-gray-600">
<button id="lookup-button"
class="bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-500 hover:to-pink-500 text-white font-bold py-3 px-6 rounded-lg shadow-lg hover:shadow-purple-500/25 transition-all duration-200 ease-in-out transform hover:-translate-y-0.5">
Lookup
</button>
</div>
<!-- Error -->
<div id="error-box"
class="hidden max-w-2xl mx-auto mb-6 p-4 bg-red-900/30 border border-red-500/40 text-red-300 rounded-lg text-sm">
</div>
<!-- Loading -->
<div id="loading-section" class="hidden flex flex-col items-center gap-4 py-16">
<div class="loader" style="width:40px;height:40px;border-width:5px;"></div>
<p class="text-gray-400 text-sm" id="loading-msg">Querying RIPE Stat &amp; PeeringDB…</p>
<!-- Shown after 3s for slow lookups -->
<div id="loading-hint" class="hidden mt-2 max-w-sm text-center">
<p class="text-xs text-amber-400/80 bg-amber-400/10 border border-amber-400/20 rounded-lg px-4 py-2">
⏳ Large ASes (like Cloudflare, Google, Tier-1 carriers) can take up to 15 seconds on the first
lookup — subsequent lookups are cached for 7 days.
</p>
</div>
</div>
<!-- Results -->
<div id="results-section" class="hidden fade-in">
<!-- AS Info Header -->
<div class="glass-card rounded-xl p-6 mb-6">
<div class="flex flex-col md:flex-row md:items-center gap-4">
<div class="flex-1">
<div class="flex items-center gap-3 mb-1">
<span id="res-asn" class="font-mono text-2xl font-bold text-purple-400"></span>
<span id="res-announced-badge"
class="hidden text-xs px-2 py-0.5 bg-green-500/20 border border-green-500/40 text-green-400 rounded-full">Announced</span>
<span id="res-type-badge"
class="text-xs px-2 py-0.5 bg-blue-500/20 border border-blue-500/40 text-blue-300 rounded-full"></span>
</div>
<h2 id="res-name" class="text-xl font-semibold text-white mb-1"></h2>
<div class="flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-gray-400 mb-2">
<span id="res-policy-container" class="hidden">Peering Policy: <span id="res-policy"
class="text-gray-200"></span></span>
<span id="res-website-container" class="hidden">Website: <a id="res-website" href="#"
target="_blank"
class="text-purple-400 hover:text-purple-300 transition-colors"></a></span>
</div>
<!-- Rich Info Grid -->
<div id="res-rich-info"
class="grid grid-cols-2 sm:grid-cols-4 gap-3 mt-4 pt-4 border-t border-gray-700/50 hidden">
<div id="res-info-type-container" class="hidden">
<div class="text-xs text-gray-500 uppercase tracking-widest">Type</div>
<div id="res-info-type" class="text-sm text-gray-200 capitalize mt-0.5"></div>
</div>
<div id="res-info-scope-container" class="hidden">
<div class="text-xs text-gray-500 uppercase tracking-widest">Scope</div>
<div id="res-info-scope" class="text-sm text-gray-200 capitalize mt-0.5"></div>
</div>
<div id="res-info-traffic-container" class="hidden">
<div class="text-xs text-gray-500 uppercase tracking-widest">Traffic</div>
<div id="res-info-traffic" class="text-sm text-gray-200 capitalize mt-0.5"></div>
</div>
<div id="res-info-ratio-container" class="hidden">
<div class="text-xs text-gray-500 uppercase tracking-widest">Ratio</div>
<div id="res-info-ratio" class="text-sm text-gray-200 capitalize mt-0.5"></div>
</div>
</div>
</div>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3 text-center">
<div class="bg-blue-500/10 border border-blue-500/20 rounded-lg p-3">
<div id="res-upstream-count" class="text-xl font-bold text-blue-400"></div>
<div class="text-xs text-gray-400 mt-0.5">Upstreams</div>
</div>
<div class="bg-green-500/10 border border-green-500/20 rounded-lg p-3">
<div id="res-downstream-count" class="text-xl font-bold text-green-400"></div>
<div class="text-xs text-gray-400 mt-0.5">Downstreams</div>
</div>
<div
class="bg-purple-500/10 border border-purple-500/20 rounded-lg p-3 col-span-2 sm:col-span-1">
<div id="res-prefix-count" class="text-xl font-bold text-purple-400"></div>
<div class="text-xs text-gray-400 mt-0.5">Prefixes</div>
</div>
</div>
</div>
</div>
<!-- Network Map -->
<div class="glass-card rounded-xl p-6 mb-6">
<div class="flex items-center gap-3 mb-4">
<h3 class="text-lg font-bold text-purple-300">Network Map</h3>
<div class="flex gap-3 text-xs text-gray-400">
<span class="flex items-center gap-1"><span
class="inline-block w-3 h-3 rounded-full bg-gray-500"></span>Tier-1 / Transit</span>
<span class="flex items-center gap-1"><span
class="inline-block w-3 h-3 rounded-full bg-blue-500"></span>Upstream</span>
<span class="flex items-center gap-1"><span
class="inline-block w-3 h-3 rounded-full bg-purple-500"></span>This AS</span>
<span class="flex items-center gap-1"><span
class="inline-block w-3 h-3 rounded-full bg-green-500"></span>Downstream</span>
</div>
<span class="ml-auto text-xs text-gray-500">Scroll to zoom · Drag to pan · Click node to open</span>
</div>
<div id="graph-container">
<svg id="graph-svg"></svg>
<div id="graph-tooltip"></div>
</div>
</div>
<!-- Prefixes + IXPs side by side -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<!-- Prefixes -->
<div class="glass-card rounded-xl p-5">
<div class="flex items-center justify-between mb-3">
<h3 class="text-sm font-bold text-gray-400 uppercase tracking-widest">Announced Prefixes</h3>
<button id="prefix-toggle"
class="text-xs text-purple-400 hover:text-purple-300 transition-colors">Show all</button>
</div>
<div id="prefix-list" class="max-h-48 overflow-y-auto"></div>
<p id="prefix-empty" class="hidden text-sm text-gray-500 italic">No prefix data available.</p>
</div>
<!-- IXPs -->
<div class="glass-card rounded-xl p-5">
<h3 class="text-sm font-bold text-gray-400 uppercase tracking-widest mb-3">IXP Presence <span
class="text-xs font-normal text-gray-500">(via PeeringDB)</span></h3>
<div id="ixp-list" class="space-y-1 text-sm max-h-48 overflow-y-auto">
<p id="ixp-empty" class="text-gray-500 italic text-sm">Not listed on PeeringDB.</p>
</div>
</div>
</div>
<!-- Direct Peers Table -->
<div class="glass-card rounded-xl p-5">
<h3 class="text-sm font-bold text-gray-400 uppercase tracking-widest mb-3">Direct Neighbours <span
class="text-xs font-normal text-gray-500">(Level 2 · via RIPE Stat)</span></h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Upstreams -->
<div>
<h4 class="text-xs font-semibold text-blue-400 mb-2 flex items-center gap-1">
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M10 17a.75.75 0 01-.75-.75V5.56l-2.47 2.47a.75.75 0 01-1.06-1.06l3.75-3.75a.75.75 0 011.06 0l3.75 3.75a.75.75 0 11-1.06 1.06L10.75 5.56v10.69A.75.75 0 0110 17z"
clip-rule="evenodd" />
</svg>
Upstreams (Transit Providers)
</h4>
<div id="upstream-table" class="space-y-1 text-xs font-mono max-h-52 overflow-y-auto"></div>
</div>
<!-- Downstreams -->
<div>
<h4 class="text-xs font-semibold text-green-400 mb-2 flex items-center gap-1">
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M10 3a.75.75 0 01.75.75v10.69l2.47-2.47a.75.75 0 111.06 1.06l-3.75 3.75a.75.75 0 01-1.06 0l-3.75-3.75a.75.75 0 111.06-1.06L9.25 14.44V3.75A.75.75 0 0110 3z"
clip-rule="evenodd" />
</svg>
Downstreams (Customers)
</h4>
<div id="downstream-table" class="space-y-1 text-xs font-mono max-h-52 overflow-y-auto"></div>
</div>
</div>
</div>
</div><!-- /results -->
<footer class="mt-12 pt-6 border-t border-gray-700/30 text-center text-xs text-gray-500">
<p>Data: <a href="https://stat.ripe.net" target="_blank"
class="text-purple-400 hover:text-purple-300 transition-colors">RIPE Stat</a> &amp; <a
href="https://www.peeringdb.com" target="_blank"
class="text-purple-400 hover:text-purple-300 transition-colors">PeeringDB</a> · Cache: 7 days</p>
<p class="mt-1">&copy; 2025 <a href="https://mrunk.de"
class="text-purple-400 hover:text-purple-300 transition-colors">MrUnknownDE</a></p>
</footer>
</div>
<script src="asn-lookup.js"></script>
</body>
</html>