From 98796edcbbde9b498c822967095fbf387f88c7ac Mon Sep 17 00:00:00 2001 From: MrUnknownDE Date: Sat, 29 Mar 2025 17:07:49 +0100 Subject: [PATCH] ip-lookup: resolv dns --- frontend/app/script.js | 124 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 10 deletions(-) diff --git a/frontend/app/script.js b/frontend/app/script.js index f69ac95..d1f8339 100644 --- a/frontend/app/script.js +++ b/frontend/app/script.js @@ -33,6 +33,7 @@ document.addEventListener('DOMContentLoaded', () => { const lookupIpInput = document.getElementById('lookup-ip-input'); const lookupButton = document.getElementById('lookup-button'); const lookupErrorEl = document.getElementById('lookup-error'); + const lookupStatusEl = document.getElementById('lookup-status'); // Optional: für Statusmeldungen wie "Resolving..." const lookupResultsSection = document.getElementById('lookup-results-section'); const lookupIpAddressEl = document.getElementById('lookup-ip-address'); const lookupResultLoader = document.getElementById('lookup-result-loader'); @@ -94,6 +95,23 @@ document.addEventListener('DOMContentLoaded', () => { globalErrorEl.classList.add('hidden'); } + /** + * Prüft, ob der String eine gültige IPv4 oder IPv6 Adresse ist. + * @param {string} input - Der zu prüfende String. + * @returns {boolean} True, wenn es eine gültige IP ist, sonst false. + */ + function isValidIpAddress(input) { + if (!input || typeof input !== 'string') return false; + const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + // Sehr einfache IPv6 Regex (erkennt gültige Zeichen, aber nicht alle komplexen Formate perfekt) + const ipv6Regex = /^[a-fA-F0-9:]+$/; + // Komplexere IPv6 Regex (versucht mehr Fälle abzudecken, aber immer noch nicht perfekt) + const complexIpv6Regex = /^(?:(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}|(?:[a-fA-F0-9]{1,4}:){1,7}:|(?:[a-fA-F0-9]{1,4}:){1,6}:[a-fA-F0-9]{1,4}|(?:[a-fA-F0-9]{1,4}:){1,5}(?::[a-fA-F0-9]{1,4}){1,2}|(?:[a-fA-F0-9]{1,4}:){1,4}(?::[a-fA-F0-9]{1,4}){1,3}|(?:[a-fA-F0-9]{1,4}:){1,3}(?::[a-fA-F0-9]{1,4}){1,4}|(?:[a-fA-F0-9]{1,4}:){1,2}(?::[a-fA-F0-9]{1,4}){1,5}|[a-fA-F0-9]{1,4}:(?:(?::[a-fA-F0-9]{1,4}){1,6})|:(?:(?::[a-fA-F0-9]{1,4}){1,7}|:)|fe80:(?::[a-fA-F0-9]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[a-fA-F0-9]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; + + return ipv4Regex.test(input) || complexIpv6Regex.test(input); + } + + /** * Aktualisiert ein Info-Feld und versteckt optional einen Loader. * @param {HTMLElement} valueElement - Das Element, das den Wert anzeigt. @@ -306,6 +324,8 @@ document.addEventListener('DOMContentLoaded', () => { if (!lookupErrorEl) return; lookupErrorEl.textContent = `Error: ${message}`; lookupErrorEl.classList.remove('hidden'); + // Hide status message if error occurs + if (lookupStatusEl) lookupStatusEl.classList.add('hidden'); } /** Versteckt Fehler im Lookup-Bereich */ @@ -314,6 +334,21 @@ document.addEventListener('DOMContentLoaded', () => { lookupErrorEl.classList.add('hidden'); } + /** Zeigt eine Statusmeldung im Lookup-Bereich an */ + function showLookupStatus(message) { + if (!lookupStatusEl) return; + lookupStatusEl.textContent = message; + lookupStatusEl.classList.remove('hidden'); + hideLookupError(); // Hide errors when showing status + } + + /** Versteckt die Statusmeldung im Lookup-Bereich */ + function hideLookupStatus() { + if (!lookupStatusEl) return; + lookupStatusEl.classList.add('hidden'); + } + + /** Setzt den Lookup-Ergebnisbereich zurück */ function resetLookupResults() { if (!lookupResultsSection) return; @@ -326,6 +361,7 @@ document.addEventListener('DOMContentLoaded', () => { if (lookupPingLoader) lookupPingLoader.classList.add('hidden'); if (lookupPingOutputEl) lookupPingOutputEl.textContent = ''; if (lookupPingErrorEl) lookupPingErrorEl.textContent = ''; + hideLookupStatus(); // Hide status on reset const fieldsToClear = [ lookupIpAddressEl, lookupCountryEl, lookupRegionEl, lookupCityEl, @@ -348,7 +384,7 @@ document.addEventListener('DOMContentLoaded', () => { /** Ruft Informationen für eine spezifische IP ab */ async function fetchLookupInfo(ipToLookup) { - resetLookupResults(); + resetLookupResults(); // Reset before showing new results/loaders hideLookupError(); hideGlobalError(); if (!lookupResultsSection || !lookupResultLoader || !lookupMapLoader) return; // Exit if elements missing @@ -356,6 +392,7 @@ document.addEventListener('DOMContentLoaded', () => { lookupResultsSection.classList.remove('hidden'); lookupResultLoader.classList.remove('hidden'); lookupMapLoader.classList.remove('hidden'); // Show map loader initially + hideLookupStatus(); // Hide status like "Resolving..." try { const response = await fetch(`${API_BASE_URL}/lookup?targetIp=${encodeURIComponent(ipToLookup)}`); // Use targetIp parameter @@ -366,9 +403,9 @@ document.addEventListener('DOMContentLoaded', () => { } console.log(`Received Lookup Info for ${ipToLookup}:`, data); - currentLookupIp = data.ip; + currentLookupIp = data.ip; // Store the IP that was actually looked up - updateField(lookupIpAddressEl, data.ip); + updateField(lookupIpAddressEl, data.ip); // Display the looked-up IP updateField(lookupCountryEl, data.geo?.countryName ? `${data.geo.countryName} (${data.geo.country})` : null, null, lookupGeoErrorEl); updateField(lookupRegionEl, data.geo?.region); updateField(lookupCityEl, data.geo?.city); @@ -388,13 +425,14 @@ document.addEventListener('DOMContentLoaded', () => { } catch (error) { console.error(`Failed to fetch lookup info for ${ipToLookup}:`, error); - showLookupError(`${error.message}`); + showLookupError(`Lookup failed: ${error.message}`); if (lookupMapMessageEl) { lookupMapMessageEl.textContent = 'Map could not be loaded due to an error.'; lookupMapMessageEl.classList.remove('hidden'); } if (lookupMapEl) lookupMapEl.classList.add('hidden'); if (lookupMapLoader) lookupMapLoader.classList.add('hidden'); // Hide loader on error + resetLookupResults(); // Hide the section again on error } finally { if (lookupResultLoader) lookupResultLoader.classList.add('hidden'); // Hide main loader @@ -402,6 +440,44 @@ document.addEventListener('DOMContentLoaded', () => { } } + /** + * Löst einen Domainnamen zu einer IP-Adresse auf (bevorzugt IPv4). + * @param {string} domain - Der aufzulösende Domainname. + * @returns {Promise} Eine Promise, die mit der IP-Adresse oder null aufgelöst wird. + */ + async function resolveDomainToIp(domain) { + console.log(`Attempting to resolve domain: ${domain}`); + try { + // 1. Versuche A Record (IPv4) + let response = await fetch(`${API_BASE_URL}/dns-lookup?domain=${encodeURIComponent(domain)}&type=A`); + let data = await response.json(); + + if (response.ok && data.success && data.records && Array.isArray(data.records) && data.records.length > 0) { + console.log(`Resolved ${domain} to IPv4: ${data.records[0]}`); + return data.records[0]; // Nimm die erste IPv4-Adresse + } + + // 2. Wenn kein A-Record, versuche AAAA Record (IPv6) + console.log(`No A record found for ${domain}, trying AAAA.`); + response = await fetch(`${API_BASE_URL}/dns-lookup?domain=${encodeURIComponent(domain)}&type=AAAA`); + data = await response.json(); + + if (response.ok && data.success && data.records && Array.isArray(data.records) && data.records.length > 0) { + console.log(`Resolved ${domain} to IPv6: ${data.records[0]}`); + return data.records[0]; // Nimm die erste IPv6-Adresse + } + + // 3. Wenn beides fehlschlägt oder keine Records gefunden wurden + console.warn(`Could not resolve domain ${domain} to an IP address.`); + throw new Error(data.error || 'No A or AAAA records found.'); + + } catch (error) { + console.error(`DNS resolution failed for ${domain}:`, error); + throw new Error(`Could not resolve domain: ${error.message}`); + } + } + + // --- Ping Function (for Lookup) --- async function runLookupPing(ip) { if (!ip || !lookupPingResultsEl || !lookupPingLoader || !lookupPingOutputEl || !lookupPingErrorEl) return; @@ -589,17 +665,45 @@ document.addEventListener('DOMContentLoaded', () => { } } - function handleLookupClick() { + async function handleLookupClick() { if (!lookupIpInput) return; - const ipToLookup = lookupIpInput.value.trim(); - if (!ipToLookup) { - showLookupError('Please enter an IP address.'); + const query = lookupIpInput.value.trim(); + if (!query) { + showLookupError('Please enter an IP address or domain name.'); return; } - console.log(`Lookup button clicked for IP: ${ipToLookup}`); - fetchLookupInfo(ipToLookup); + + resetLookupResults(); // Reset results before starting + hideLookupError(); + + if (isValidIpAddress(query)) { + // Input is an IP address + console.log(`Lookup button clicked for IP: ${query}`); + fetchLookupInfo(query); + } else { + // Input is likely a domain name + console.log(`Lookup button clicked for domain: ${query}`); + showLookupStatus(`Resolving domain ${query}...`); // Show status + try { + const resolvedIp = await resolveDomainToIp(query); + if (resolvedIp) { + console.log(`Domain ${query} resolved to ${resolvedIp}. Fetching lookup info...`); + // Optional: Update input field with resolved IP? Maybe not, keep original query. + // lookupIpInput.value = resolvedIp; + fetchLookupInfo(resolvedIp); // Fetch info for the resolved IP + } else { + // Should be caught by the error in resolveDomainToIp, but as a fallback: + showLookupError(`Could not resolve domain ${query} to an IP address.`); + } + } catch (error) { + showLookupError(error.message); // Display resolution error + } finally { + hideLookupStatus(); // Hide status message regardless of outcome + } + } } + function handleLookupPingClick() { if (currentLookupIp) { console.log(`Starting ping for looked-up IP: ${currentLookupIp}`);