Features implemented: - Modular JavaScript architecture (theme.js, dns-tools.js, whois.js, punycode.js, ip-tools.js, main.js) - Responsive design with dark/light theme toggle - DNS Lookup and Reverse DNS Lookup tools - Whois Lookup functionality - IDN Punycode Converter with full Unicode support - Comprehensive IP Address Tools (validation, IPv4-to-IPv6 mapping, IPv6 compression/expansion) - Dynamic tab descriptions that change based on active tool - Mobile-responsive horizontal scrollable tabs - Copy-to-clipboard functionality for all results - Clean footer with dynamic year - IPv4-mapped IPv6 address explanation with clear warnings Technical improvements: - Separated concerns with modular JS files - Fixed browser compatibility issues with punycode library - Implemented proper error handling and user feedback - Added comprehensive input validation - Optimized for mobile devices with touch-friendly UI
197 lines
9.4 KiB
JavaScript
197 lines
9.4 KiB
JavaScript
// DNS Things - Whois Module
|
|
|
|
// Initialize Whois lookup form
|
|
function initWhoisForm() {
|
|
const form = document.getElementById('whois-form');
|
|
const input = document.getElementById('whois-input');
|
|
const results = document.getElementById('whois-results');
|
|
const loader = document.getElementById('whois-loader');
|
|
|
|
if (!form || !input || !results || !loader) return;
|
|
|
|
form.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
|
|
const domain = input.value.trim();
|
|
if (!domain) return;
|
|
|
|
// Show loader
|
|
loader.classList.remove('hidden');
|
|
results.innerHTML = '';
|
|
|
|
try {
|
|
// Use DNS-based domain information lookup as a free alternative
|
|
const recordTypes = ['A', 'AAAA', 'MX', 'TXT', 'NS', 'SOA'];
|
|
|
|
const queries = recordTypes.map(type =>
|
|
fetch(`https://dns.google/resolve?name=${encodeURIComponent(domain)}&type=${type}`)
|
|
.then(response => response.json())
|
|
.then(data => ({ type, data }))
|
|
.catch(error => ({ type, error }))
|
|
);
|
|
|
|
const responses = await Promise.all(queries);
|
|
|
|
// Process DNS data
|
|
const dnsData = {};
|
|
const ipAddresses = { ipv4: [], ipv6: [] };
|
|
|
|
responses.forEach(({ type, data, error }) => {
|
|
if (!error && data.Answer && data.Answer.length > 0) {
|
|
dnsData[type] = data.Answer.map(record => record.data);
|
|
|
|
if (type === 'A') {
|
|
ipAddresses.ipv4.push(...data.Answer.map(record => record.data));
|
|
} else if (type === 'AAAA') {
|
|
ipAddresses.ipv6.push(...data.Answer.map(record => record.data));
|
|
}
|
|
}
|
|
});
|
|
|
|
// Determine TLD registry
|
|
const tld = domain.split('.').pop().toLowerCase();
|
|
const tldRegistries = {
|
|
'com': 'Verisign',
|
|
'net': 'Verisign',
|
|
'org': 'Public Interest Registry (PIR)',
|
|
'info': 'Afilias',
|
|
'biz': 'NeuStar',
|
|
'name': 'Verisign',
|
|
'mobi': 'Afilias',
|
|
'travel': 'Tralliance',
|
|
'museum': 'MuseDoma',
|
|
'coop': 'DotCooperation',
|
|
'aero': 'SITA',
|
|
'pro': 'RegistryPro',
|
|
'edu': 'Educause',
|
|
'gov': 'General Services Administration',
|
|
'mil': 'DoD Network Information Center',
|
|
'int': 'IANA'
|
|
};
|
|
|
|
// Build results
|
|
let resultsHTML = '<div class="space-y-4">';
|
|
|
|
// Domain Information
|
|
resultsHTML += `
|
|
<div class="p-4 bg-blue-50 dark:bg-blue-900/30 rounded-lg">
|
|
<h4 class="font-semibold text-blue-800 dark:text-blue-200 mb-3">Domain Information</h4>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
|
<div>
|
|
<div><strong>Domain:</strong> ${domain}</div>
|
|
<div><strong>TLD Registry:</strong> ${tldRegistries[tld] || 'Unknown'}</div>
|
|
<div><strong>Website:</strong> ${dnsData.A || dnsData.AAAA ? '✅ Available' : '❌ Not available'}</div>
|
|
<div><strong>Email:</strong> ${dnsData.MX ? '✅ Supported' : '❌ Not supported'}</div>
|
|
</div>
|
|
<div class="text-xs text-blue-600 dark:text-blue-400">
|
|
<p><strong>Note:</strong> This is a DNS-based lookup providing practical domain information. For detailed registration data, use official whois services.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Name Servers
|
|
if (dnsData.NS) {
|
|
resultsHTML += `
|
|
<div class="p-4 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
|
|
<h4 class="font-semibold text-gray-800 dark:text-white mb-3">Name Servers (${dnsData.NS.length})</h4>
|
|
<ul class="space-y-1">
|
|
`;
|
|
dnsData.NS.forEach(ns => {
|
|
const cleanNS = ns.replace(/\.$/, '');
|
|
resultsHTML += `
|
|
<li class="flex justify-between items-center p-2 bg-gray-50 dark:bg-gray-700 rounded">
|
|
<span class="font-mono text-sm">${cleanNS}</span>
|
|
<button onclick="copyToClipboard('${cleanNS}', this)" class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200" title="Copy">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
|
</svg>
|
|
</button>
|
|
</li>
|
|
`;
|
|
});
|
|
resultsHTML += '</ul></div>';
|
|
}
|
|
|
|
// DNS Records Summary
|
|
resultsHTML += `
|
|
<div class="p-4 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
|
|
<h4 class="font-semibold text-gray-800 dark:text-white mb-3">DNS Records Summary</h4>
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-2">
|
|
`;
|
|
|
|
recordTypes.forEach(type => {
|
|
const hasRecord = dnsData[type] && dnsData[type].length > 0;
|
|
resultsHTML += `
|
|
<div class="text-center p-2 rounded ${hasRecord ? 'bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-200' : 'bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400'}">
|
|
<div class="font-semibold">${type}</div>
|
|
<div class="text-sm">${hasRecord ? '✅' : '❌'}</div>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
resultsHTML += '</div></div>';
|
|
|
|
// IP Addresses
|
|
if (ipAddresses.ipv4.length > 0 || ipAddresses.ipv6.length > 0) {
|
|
resultsHTML += `
|
|
<div class="p-4 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
|
|
<h4 class="font-semibold text-gray-800 dark:text-white mb-3">IP Addresses</h4>
|
|
<div class="space-y-2">
|
|
`;
|
|
|
|
ipAddresses.ipv4.forEach(ip => {
|
|
resultsHTML += `
|
|
<div class="flex justify-between items-center">
|
|
<span class="whois-badge ipv4">${ip}</span>
|
|
<button onclick="copyToClipboard('${ip}', this)" class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200" title="Copy">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
ipAddresses.ipv6.forEach(ip => {
|
|
resultsHTML += `
|
|
<div class="flex justify-between items-center">
|
|
<span class="whois-badge ipv6">${ip}</span>
|
|
<button onclick="copyToClipboard('${ip}', this)" class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200" title="Copy">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
resultsHTML += '</div></div>';
|
|
}
|
|
|
|
resultsHTML += '</div>';
|
|
results.innerHTML = resultsHTML;
|
|
|
|
} catch (error) {
|
|
console.error('Whois lookup error:', error);
|
|
results.innerHTML = `
|
|
<div class="p-4 bg-red-50 dark:bg-red-900/30 text-red-700 dark:text-red-300 rounded-lg">
|
|
<h3 class="font-medium mb-1">Error</h3>
|
|
<p class="text-sm">Failed to lookup domain information. Please try again.</p>
|
|
</div>
|
|
`;
|
|
} finally {
|
|
loader.classList.add('hidden');
|
|
}
|
|
});
|
|
|
|
// Auto-submit on paste
|
|
input.addEventListener('paste', () => {
|
|
setTimeout(() => {
|
|
if (input.value.trim()) {
|
|
form.dispatchEvent(new Event('submit'));
|
|
}
|
|
}, 100);
|
|
});
|
|
}
|