(hostData)
| 4766 | } |
| 4767 | } |
| 4768 | |
| 4769 | async function runDns() { |
| 4770 | const input = document.getElementById('dns-target'); |
| 4771 | const out = document.getElementById('dns-results'); |
| 4772 | const name = (input.value || '').trim(); |
| 4773 | if (!name) { input.focus(); return; } |
| 4774 | const btn = event && event.target ? event.target : null; |
| 4775 | _ndBusy(btn, true, 'Resolving…'); |
| 4776 | out.classList.remove('hidden'); |
| 4777 | out.innerHTML = '<p class="text-sm text-gray-400">Querying resolvers…</p>'; |
| 4778 | try { |
| 4779 | const d = await postAPI('/api/net/dns', { name: name }); |
| 4780 | if (!d.success) { |
| 4781 | out.innerHTML = d.missing_tool ? _ndMissingTool(d, 'runDns') |
| 4782 | : '<p class="text-sm text-red-400">Error: ' + escapeHtml(d.error || 'failed') + '</p>'; |
| 4783 | return; |
| 4784 | } |
| 4785 | const bogonSet = {}; |
| 4786 | (d.results || []).forEach(r => { (r.bogon_answers || []).forEach(ip => { bogonSet[r.resolver + '|' + ip] = 1; }); }); |
| 4787 | const rows = (d.results || []).map(r => { |
| 4788 | const ans = (r.answers && r.answers.length) |
| 4789 | ? r.answers.map(ip => bogonSet[r.resolver + '|' + ip] |
| 4790 | ? '<span class="text-red-400" title="private/bogon address for a public name">' + escapeHtml(ip) + ' ⚠</span>' |
| 4791 | : escapeHtml(ip)).join('<br>') |
| 4792 | : '<span class="text-red-400">' + escapeHtml(r.error || (r.status || 'no answer')) + '</span>'; |
| 4793 | const flags = []; |
| 4794 | if (r.nxdomain_rewrite) flags.push('<span class="text-red-400" title="synthesized an answer for a non-existent name">NXDOMAIN-rewrite</span>'); |
| 4795 | if (r.status === 'SERVFAIL') flags.push('<span class="text-amber-300" title="possible DNSSEC validation failure">SERVFAIL</span>'); |
| 4796 | const flagCell = flags.length ? '<div class="text-xs mt-0.5">' + flags.join(' · ') + '</div>' : ''; |
| 4797 | return `<tr class="border-t border-slate-800"> |
| 4798 | <td class="px-3 py-1.5 font-mono">${escapeHtml(r.resolver)}</td> |
| 4799 | <td class="px-3 py-1.5 text-gray-500">${escapeHtml(r.kind)}</td> |
| 4800 | <td class="px-3 py-1.5 font-mono">${ans}${flagCell}</td> |
| 4801 | <td class="px-3 py-1.5 text-right">${r.query_ms != null ? r.query_ms + ' ms' : '—'}</td> |
| 4802 | <td class="px-3 py-1.5 text-center">${r.ad ? '<span class="text-green-400">✓</span>' : '<span class="text-gray-500">—</span>'}</td> |
| 4803 | </tr>`; |
| 4804 | }).join(''); |
| 4805 | const p = d.poison || {}; |
| 4806 | let poisonBanner = ''; |
| 4807 | if (p.verdict === 'hijacked') { |
| 4808 | poisonBanner = '<div class="mb-2 px-3 py-2 rounded bg-red-950/60 border border-red-800 text-red-300 text-sm">🛑 <strong>DNS poisoning / hijack detected</strong></div>'; |
no test coverage detected