<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Uncategorized &#8211; PawaOps</title>
	<atom:link href="https://pawaops.com/category/uncategorized-en/feed/" rel="self" type="application/rss+xml" />
	<link>https://pawaops.com</link>
	<description></description>
	<lastBuildDate>Wed, 17 Jun 2026 21:34:12 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://pawaops.com/wp-content/uploads/2026/01/cropped-Pawaops_icon-32x32.png</url>
	<title>Uncategorized &#8211; PawaOps</title>
	<link>https://pawaops.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>PawaDNS — DNS Helper</title>
		<link>https://pawaops.com/pawa-dns/</link>
					<comments>https://pawaops.com/pawa-dns/#respond</comments>
		
		<dc:creator><![CDATA[Admin Pawaops]]></dc:creator>
		<pubDate>Wed, 17 Jun 2026 20:49:20 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://pawaops.com/?p=253</guid>

					<description><![CDATA[Analysez rapidement les enregistrements DNS d’un domaine et obtenez des pistes de correction exploitables. PawaDNS interroge les principaux enregistrements DNS d’un domaine, résume les résultats, vérifie les bases web et email, puis génère des “Fix Cards” utiles pour administrateurs systèmes, DevOps, freelances web et équipes support. Analyze DNS records quickly and get practical fix cards. ... <a title="PawaDNS — DNS Helper" class="read-more" href="https://pawaops.com/pawa-dns/" aria-label="Read more about PawaDNS — DNS Helper">Read more</a>]]></description>
										<content:encoded><![CDATA[
<article class="pawa-tool pawa-dns-tool">

  <p>
    <strong>Analysez rapidement les enregistrements DNS d’un domaine et obtenez des pistes de correction exploitables.</strong>
  </p>

  <p>
    PawaDNS interroge les principaux enregistrements DNS d’un domaine, résume les résultats, vérifie les bases web
    et email, puis génère des “Fix Cards” utiles pour administrateurs systèmes, DevOps, freelances web et équipes support.
  </p>

  <p lang="en">
    <strong>Analyze DNS records quickly and get practical fix cards.</strong>
  </p>

  <p lang="en">
    PawaDNS queries common DNS records for a domain, summarizes results, checks web and email basics, and generates
    practical fix cards for sysadmins, DevOps engineers, web freelancers and support teams.
  </p>

  <hr>

  <section id="pawa-dns-app">
    <h2>Outil / Tool</h2>

&#8220;`
<p>
  <label for="pawaDnsDomain"><strong>Domain</strong></label><br>
  <input id="pawaDnsDomain" type="text" placeholder="example.com">
</p>

<p>
  <label for="pawaDnsSelectors"><strong>DKIM selectors to test, optional</strong></label><br>
  <input id="pawaDnsSelectors" type="text" value="default,google,selector1,selector2,k1" placeholder="default,google,selector1">
</p>

<p>
  <button type="button" id="pawaDnsAnalyze">Analyze DNS</button>
  <button type="button" id="pawaDnsCopyReport">Copy report</button>
  <button type="button" id="pawaDnsCopyCommands">Copy dig commands</button>
  <button type="button" id="pawaDnsReset">Reset</button>
</p>

<div id="pawaDnsError" role="alert"></div>

<h3>Summary</h3>
<pre id="pawaDnsSummary">Enter a domain and click “Analyze DNS”.</pre>

<h3>Records overview</h3>
<div id="pawaDnsOverview"></div>

<h3>Answers</h3>
<div id="pawaDnsAnswers"></div>

<h3>Mail security</h3>
<div id="pawaDnsMail"></div>

<h3>Fix Cards</h3>
<div id="pawaDnsFixCards"></div>

<h3>Useful commands</h3>
<pre><code id="pawaDnsCommands"></code></pre>

<h3>Raw DoH data</h3>
<details>
  <summary>Show raw JSON</summary>
  <pre><code id="pawaDnsRaw"></code></pre>
</details>
&#8220;`

  </section>

  <hr>

  <section class="pawa-article-content">
    <h2>Pourquoi PawaDNS est utile</h2>

&#8220;`
<p>
  Le DNS est une configuration critique. Lorsqu’il est incomplet ou incohérent, les symptômes sont très variés :
  site inaccessible, email qui tombe en spam, domaine qui pointe au mauvais serveur, certificat impossible à émettre,
  migration confuse ou propagation mal comprise.
</p>

<p>
  PawaDNS est conçu comme un outil opérationnel. Il ne se contente pas d’afficher des enregistrements bruts :
  il cherche à transformer les signaux importants en informations lisibles et en actions vérifiables.
</p>

<h2>Ce que l’outil vérifie</h2>

<ul>
  <li><code>A</code> : adresse IPv4 du domaine.</li>
  <li><code>AAAA</code> : adresse IPv6 du domaine.</li>
  <li><code>CNAME</code> : alias DNS.</li>
  <li><code>MX</code> : serveurs de réception email.</li>
  <li><code>TXT</code> : SPF, vérifications de domaine et autres données texte.</li>
  <li><code>NS</code> : serveurs de noms autoritaires.</li>
  <li><code>SOA</code> : informations de zone DNS.</li>
  <li><code>CAA</code> : autorités de certification autorisées.</li>
  <li><code>HTTPS</code> : informations modernes de service HTTPS quand présentes.</li>
  <li><code>_dmarc</code> : politique DMARC du domaine.</li>
  <li><code>DKIM</code> : test de sélecteurs DKIM courants ou fournis.</li>
</ul>

<h2>DNS web : A, AAAA et CNAME</h2>

<p>
  Pour qu’un domaine pointe vers un site, il a généralement besoin d’un enregistrement <code>A</code>, d’un
  enregistrement <code>AAAA</code>, d’un <code>CNAME</code>, ou d’une combinaison adaptée à l’hébergeur.
</p>

<p>
  Une erreur fréquente consiste à mettre à jour <code>www</code> mais pas le domaine racine, ou inversement.
  Une autre erreur fréquente est de laisser une vieille adresse IPv6 active alors que le serveur ne répond plus
  correctement dessus.
</p>

<h2>DNS email : MX, SPF, DKIM et DMARC</h2>

<p>
  Les problèmes email viennent souvent d’une configuration DNS incomplète.
</p>

<h3>MX</h3>

<p>
  Les enregistrements <code>MX</code> indiquent quels serveurs reçoivent les emails pour le domaine.
</p>

<h3>SPF</h3>

<p>
  SPF indique quels serveurs sont autorisés à envoyer des emails pour le domaine.
</p>

<pre><code>v=spf1 include:_spf.example.net -all</code></pre>

<p>
  Un domaine ne doit normalement pas avoir plusieurs enregistrements SPF séparés. C’est une erreur fréquente
  lorsqu’on ajoute plusieurs prestataires email.
</p>

<h3>DKIM</h3>

<p>
  DKIM publie une clé dans le DNS pour permettre la vérification cryptographique des emails signés.
  Le sélecteur dépend du fournisseur email. C’est pour cela que PawaDNS permet de tester plusieurs sélecteurs.
</p>

<pre><code>selector._domainkey.example.com TXT "v=DKIM1; k=rsa; p=..."</code></pre>

<h3>DMARC</h3>

<p>
  DMARC définit ce qu’il faut faire lorsqu’un email échoue aux contrôles SPF ou DKIM alignés.
</p>

<pre><code>_dmarc.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"</code></pre>

<p>
  Une politique <code>p=none</code> permet d’observer. <code>quarantine</code> et <code>reject</code> sont plus
  strictes et doivent être déployées progressivement.
</p>

<h2>CAA et certificats TLS</h2>

<p>
  L’enregistrement <code>CAA</code> indique quelles autorités de certification sont autorisées à émettre des
  certificats pour le domaine.
</p>

<pre><code>example.com CAA 0 issue "letsencrypt.org"</code></pre>

<p>
  L’absence de CAA n’est pas toujours une erreur, mais sa présence peut aider à mieux contrôler l’émission
  de certificats.
</p>

<h2>TTL et propagation DNS</h2>

<p>
  Le TTL indique combien de temps une réponse DNS peut être mise en cache. Lors d’une migration, il est souvent
  utile de réduire le TTL avant la bascule, puis de le remonter après stabilisation.
</p>

<p>
  Beaucoup de “problèmes de propagation” sont en réalité des effets normaux de cache DNS.
</p>

<h2>Fix Cards</h2>

<p>
  La partie la plus utile de PawaDNS est la génération de cartes d’action. Une Fix Card doit répondre à quatre
  questions :
</p>

<ul>
  <li>Quel est le problème ?</li>
  <li>Pourquoi c’est important ?</li>
  <li>Quelle est la prochaine vérification ?</li>
  <li>Quelle correction de départ peut être envisagée ?</li>
</ul>

<p>
  Exemple :
</p>

<pre><code>Problem:
```

DMARC record is missing.

Impact:
The domain has weaker protection against email spoofing.

Suggested start:
_dmarc.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"

Priority:
Medium to high if the domain sends email.</code></pre>

&#8220;`
<h2>Commandes utiles</h2>

<p>
  PawaDNS génère aussi des commandes <code>dig</code> pour reproduire les vérifications en terminal :
</p>

<pre><code>dig example.com A
```

dig example.com AAAA
dig example.com MX
dig example.com TXT
dig _dmarc.example.com TXT
dig example.com CAA</code></pre>

&#8220;`
<p>
  Pour comparer les réponses de résolveurs publics :
</p>

<pre><code>dig @1.1.1.1 example.com A
```

dig @8.8.8.8 example.com A</code></pre>

&#8220;`
<h2>Position d’administrateur systèmes</h2>

<p>
  Le DNS doit être traité comme une configuration de production. Un domaine peut sembler fonctionner tout en ayant
  des signaux faibles : IPv6 cassé, SPF incomplet, DMARC absent, ancien MX, CAA manquant, mauvais TTL ou enregistrement
  hérité d’un ancien prestataire.
</p>

<p>
  PawaDNS aide à lire ces signaux et à produire des vérifications concrètes. La correction finale dépend toujours
  du fournisseur DNS, de l’hébergeur, du prestataire email et du contexte de production.
</p>

<p lang="en">
  DNS should be treated as production configuration. PawaDNS helps read DNS signals and turn them into practical checks.
</p>
&#8220;`

  </section>
</article>

<script>
(function () {
  const domainEl = document.getElementById('pawaDnsDomain');
  const selectorsEl = document.getElementById('pawaDnsSelectors');
  const analyzeBtn = document.getElementById('pawaDnsAnalyze');
  const copyReportBtn = document.getElementById('pawaDnsCopyReport');
  const copyCommandsBtn = document.getElementById('pawaDnsCopyCommands');
  const resetBtn = document.getElementById('pawaDnsReset');

  const errorEl = document.getElementById('pawaDnsError');
  const summaryEl = document.getElementById('pawaDnsSummary');
  const overviewEl = document.getElementById('pawaDnsOverview');
  const answersEl = document.getElementById('pawaDnsAnswers');
  const mailEl = document.getElementById('pawaDnsMail');
  const fixCardsEl = document.getElementById('pawaDnsFixCards');
  const commandsEl = document.getElementById('pawaDnsCommands');
  const rawEl = document.getElementById('pawaDnsRaw');

  let lastReport = '';
  let lastCommands = '';

  const recordTypes = ['A', 'AAAA', 'CNAME', 'MX', 'TXT', 'NS', 'SOA', 'CAA', 'HTTPS'];

  const statusMap = {
    0: 'NOERROR',
    1: 'FORMERR',
    2: 'SERVFAIL',
    3: 'NXDOMAIN',
    4: 'NOTIMP',
    5: 'REFUSED'
  };

  function escapeHtml(value) {
    return String(value ?? '')
      .replaceAll('&', '&amp;')
      .replaceAll('<', '&lt;')
      .replaceAll('>', '&gt;')
      .replaceAll('"', '&quot;')
      .replaceAll("'", '&#039;');
  }

  function clearError() {
    errorEl.textContent = '';
  }

  function showError(message) {
    errorEl.textContent = message;
  }

  function normalizeDomain(value) {
    let domain = String(value || '').trim().toLowerCase();

    domain = domain.replace(/^https?:\/\//, '');
    domain = domain.split('/')[0];
    domain = domain.split('?')[0];
    domain = domain.split('#')[0];
    domain = domain.replace(/\.$/, '');

    if (!domain) {
      throw new Error('Enter a domain.');
    }

    if (!/^[a-z0-9.-]+\.[a-z]{2,}$/i.test(domain)) {
      throw new Error('Invalid domain format.');
    }

    if (domain.includes('..')) {
      throw new Error('Invalid domain format.');
    }

    return domain;
  }

  function normalizeTxt(value) {
    return String(value || '')
      .replace(/^"/, '')
      .replace(/"$/, '')
      .replace(/"\s+"/g, '');
  }

  async function dnsQuery(name, type) {
    const url = 'https://cloudflare-dns.com/dns-query?name=' +
      encodeURIComponent(name) +
      '&type=' +
      encodeURIComponent(type);

    const response = await fetch(url, {
      headers: {
        'Accept': 'application/dns-json'
      }
    });

    if (!response.ok) {
      throw new Error('DNS query failed for ' + name + ' ' + type + ': HTTP ' + response.status);
    }

    return response.json();
  }

  function getAnswers(result) {
    return result && Array.isArray(result.Answer) ? result.Answer : [];
  }

  function statusText(result) {
    const code = result && typeof result.Status === 'number' ? result.Status : -1;
    return statusMap[code] || ('STATUS_' + code);
  }

  function hasAnswers(result) {
    return getAnswers(result).length > 0;
  }

  function answersFor(results, type) {
    return getAnswers(results[type] || {});
  }

  function txtRecords(result) {
    return getAnswers(result).map(function (answer) {
      return normalizeTxt(answer.data);
    });
  }

  function findSpf(txts) {
    return txts.filter(function (txt) {
      return /^v=spf1/i.test(txt);
    });
  }

  function findDmarc(txts) {
    return txts.filter(function (txt) {
      return /^v=DMARC1/i.test(txt);
    });
  }

  function parseSelectors(value) {
    return String(value || '')
      .split(',')
      .map(function (v) { return v.trim(); })
      .filter(Boolean)
      .slice(0, 10);
  }

  function buildCommands(domain, selectors) {
    const lines = [
      'dig ' + domain + ' A',
      'dig ' + domain + ' AAAA',
      'dig ' + domain + ' CNAME',
      'dig ' + domain + ' MX',
      'dig ' + domain + ' TXT',
      'dig ' + domain + ' NS',
      'dig ' + domain + ' SOA',
      'dig ' + domain + ' CAA',
      'dig ' + domain + ' HTTPS',
      'dig _dmarc.' + domain + ' TXT'
    ];

    selectors.forEach(function (selector) {
      lines.push('dig ' + selector + '._domainkey.' + domain + ' TXT');
    });

    lines.push('');
    lines.push('# Compare public resolvers');
    lines.push('dig @1.1.1.1 ' + domain + ' A');
    lines.push('dig @8.8.8.8 ' + domain + ' A');

    return lines.join('\n');
  }

  function renderOverview(results) {
    let html = '<table><thead><tr><th>Type</th><th>Status</th><th>Answers</th><th>Min TTL</th></tr></thead><tbody>';

    recordTypes.forEach(function (type) {
      const result = results[type];
      const answers = getAnswers(result);
      const ttlValues = answers.map(function (a) { return a.TTL; }).filter(function (v) { return typeof v === 'number'; });
      const minTtl = ttlValues.length ? Math.min.apply(null, ttlValues) : '-';

      html += '<tr>';
      html += '<td><code>' + escapeHtml(type) + '</code></td>';
      html += '<td>' + escapeHtml(statusText(result)) + '</td>';
      html += '<td>' + escapeHtml(answers.length) + '</td>';
      html += '<td>' + escapeHtml(minTtl) + '</td>';
      html += '</tr>';
    });

    html += '</tbody></table>';
    overviewEl.innerHTML = html;
  }

  function renderAnswers(results, dmarcResult, dkimResults) {
    let html = '';

    recordTypes.forEach(function (type) {
      const answers = answersFor(results, type);

      html += '<h4>' + escapeHtml(type) + '</h4>';

      if (!answers.length) {
        html += '<p>No answer.</p>';
        return;
      }

      html += '<table><thead><tr><th>Name</th><th>TTL</th><th>Data</th></tr></thead><tbody>';

      answers.forEach(function (answer) {
        html += '<tr>';
        html += '<td><code>' + escapeHtml(answer.name) + '</code></td>';
        html += '<td>' + escapeHtml(answer.TTL) + '</td>';
        html += '<td><code>' + escapeHtml(answer.data) + '</code></td>';
        html += '</tr>';
      });

      html += '</tbody></table>';
    });

    html += '<h4>DMARC</h4>';
    const dmarcAnswers = getAnswers(dmarcResult);

    if (!dmarcAnswers.length) {
      html += '<p>No DMARC answer.</p>';
    } else {
      html += '<table><thead><tr><th>Name</th><th>TTL</th><th>Data</th></tr></thead><tbody>';
      dmarcAnswers.forEach(function (answer) {
        html += '<tr><td><code>' + escapeHtml(answer.name) + '</code></td><td>' + escapeHtml(answer.TTL) + '</td><td><code>' + escapeHtml(answer.data) + '</code></td></tr>';
      });
      html += '</tbody></table>';
    }

    html += '<h4>DKIM selectors tested</h4>';

    if (!Object.keys(dkimResults).length) {
      html += '<p>No DKIM selector tested.</p>';
    } else {
      html += '<table><thead><tr><th>Selector</th><th>Status</th><th>Answers</th></tr></thead><tbody>';

      Object.keys(dkimResults).forEach(function (selector) {
        const result = dkimResults[selector];
        html += '<tr>';
        html += '<td><code>' + escapeHtml(selector) + '</code></td>';
        html += '<td>' + escapeHtml(statusText(result)) + '</td>';
        html += '<td>' + escapeHtml(getAnswers(result).length) + '</td>';
        html += '</tr>';
      });

      html += '</tbody></table>';
    }

    answersEl.innerHTML = html;
  }

  function buildMailSecurity(results, dmarcResult, dkimResults) {
    const txts = txtRecords(results.TXT || {});
    const spf = findSpf(txts);
    const dmarc = findDmarc(txtRecords(dmarcResult || {}));

    const dkimFound = Object.keys(dkimResults).filter(function (selector) {
      return getAnswers(dkimResults[selector]).some(function (answer) {
        return /v=DKIM1/i.test(normalizeTxt(answer.data));
      });
    });

    let html = '<table><tbody>';

    html += '<tr><th>MX</th><td>' + escapeHtml(hasAnswers(results.MX) ? 'found' : 'missing') + '</td></tr>';
    html += '<tr><th>SPF</th><td>' + escapeHtml(spf.length ? spf.join(' | ') : 'missing') + '</td></tr>';
    html += '<tr><th>DMARC</th><td>' + escapeHtml(dmarc.length ? dmarc.join(' | ') : 'missing') + '</td></tr>';
    html += '<tr><th>DKIM selectors found</th><td>' + escapeHtml(dkimFound.length ? dkimFound.join(', ') : 'none found among tested selectors') + '</td></tr>';

    html += '</tbody></table>';

    if (!dkimFound.length) {
      html += '<p>DKIM cannot be fully checked without knowing the real selector used by the email provider.</p>';
    }

    mailEl.innerHTML = html;

    return {
      spf: spf,
      dmarc: dmarc,
      dkimFound: dkimFound
    };
  }

  function addCard(cards, title, impact, next, priority) {
    cards.push({
      title: title,
      impact: impact,
      next: next,
      priority: priority
    });
  }

  function buildFixCards(domain, results, mailInfo, dmarcResult) {
    const cards = [];

    if ((results.A && results.A.Status === 3) || (results.NS && results.NS.Status === 3)) {
      addCard(
        cards,
        'Domain appears to return NXDOMAIN',
        'The domain may not exist publicly or may be misspelled.',
        'Check the domain spelling, registration status and authoritative nameservers.',
        'High'
      );
    }

    if (!hasAnswers(results.A) && !hasAnswers(results.AAAA) && !hasAnswers(results.CNAME)) {
      addCard(
        cards,
        'No web address record found',
        'The domain may not point to a web server.',
        'Add an A, AAAA or CNAME record depending on the hosting provider.',
        'High if the domain should host a website'
      );
    }

    if (hasAnswers(results.A) && !hasAnswers(results.AAAA)) {
      addCard(
        cards,
        'No IPv6 record found',
        'This is not always an error, but IPv6 clients will use IPv4 only.',
        'Add an AAAA record only if the hosting stack supports IPv6 correctly.',
        'Low to medium'
      );
    }

    if (!hasAnswers(results.MX)) {
      addCard(
        cards,
        'No MX record found',
        'The domain may not receive email properly.',
        'Add MX records from the email provider if the domain must receive mail.',
        'High if the domain uses email'
      );
    }

    if (!mailInfo.spf.length) {
      addCard(
        cards,
        'SPF record is missing',
        'The domain has weaker protection against unauthorized email sending.',
        'Add a TXT record starting with v=spf1 based on the real email providers.',
        'Medium to high'
      );
    }

    if (mailInfo.spf.length > 1) {
      addCard(
        cards,
        'Multiple SPF records found',
        'Multiple SPF records can break SPF validation.',
        'Merge all SPF mechanisms into a single TXT record.',
        'High'
      );
    }

    if (mailInfo.spf.some(function (record) { return /\+all/i.test(record); })) {
      addCard(
        cards,
        'SPF uses +all',
        '+all authorizes everything and defeats the purpose of SPF.',
        'Replace +all with a stricter mechanism such as ~all or -all after validation.',
        'High'
      );
    }

    if (!mailInfo.dmarc.length) {
      addCard(
        cards,
        'DMARC record is missing',
        'The domain has weaker protection against spoofing and poor visibility into email abuse.',
        '_dmarc.' + domain + ' TXT "v=DMARC1; p=none; rua=mailto:dmarc@' + domain + '"',
        'Medium to high if the domain sends email'
      );
    }

    if (mailInfo.dmarc.some(function (record) { return /p=none/i.test(record); })) {
      addCard(
        cards,
        'DMARC is in monitoring mode',
        'p=none collects data but does not ask receivers to quarantine or reject failing mail.',
        'Review reports, fix legitimate senders, then consider quarantine or reject progressively.',
        'Medium'
      );
    }

    if (mailInfo.dmarc.length && !mailInfo.dmarc.some(function (record) { return /rua=/i.test(record); })) {
      addCard(
        cards,
        'DMARC has no aggregate report address',
        'Without rua, you lose visibility into DMARC aggregate reports.',
        'Add rua=mailto:... if you have a mailbox or report processor ready.',
        'Low to medium'
      );
    }

    if (!hasAnswers(results.CAA)) {
      addCard(
        cards,
        'No CAA record found',
        'This is not always an error, but CAA can restrict which certificate authorities may issue certificates.',
        'Consider adding CAA records for the certificate authorities you actually use.',
        'Low to medium'
      );
    }

    if (!mailInfo.dkimFound.length) {
      addCard(
        cards,
        'No DKIM record found for tested selectors',
        'This does not prove DKIM is missing, but none of the tested selectors returned a DKIM key.',
        'Check the real selector from the email provider and test selector._domainkey.' + domain + '.',
        'Medium if the domain sends email'
      );
    }

    if (!cards.length) {
      addCard(
        cards,
        'No obvious issue detected by basic checks',
        'The main records queried returned usable signals.',
        'Still verify provider-specific requirements, mail alignment, TTL and production behavior.',
        'Informational'
      );
    }

    return cards;
  }

  function renderFixCards(cards) {
    let html = '';

    cards.forEach(function (card) {
      html += '<section>';
      html += '<h4>' + escapeHtml(card.title) + '</h4>';
      html += '<p><strong>Impact:</strong> ' + escapeHtml(card.impact) + '</p>';
      html += '<p><strong>Next step:</strong> ' + escapeHtml(card.next) + '</p>';
      html += '<p><strong>Priority:</strong> ' + escapeHtml(card.priority) + '</p>';
      html += '</section>';
    });

    fixCardsEl.innerHTML = html;
  }

  function buildReport(domain, results, mailInfo, cards, commands) {
    const lines = [
      'PawaDNS report',
      '',
      'Domain: ' + domain,
      '',
      'Records overview:'
    ];

    recordTypes.forEach(function (type) {
      const result = results[type];
      lines.push('- ' + type + ': ' + statusText(result) + ', answers=' + getAnswers(result).length);
    });

    lines.push('');
    lines.push('Mail security:');
    lines.push('- MX: ' + (hasAnswers(results.MX) ? 'found' : 'missing'));
    lines.push('- SPF: ' + (mailInfo.spf.length ? mailInfo.spf.join(' | ') : 'missing'));
    lines.push('- DMARC: ' + (mailInfo.dmarc.length ? mailInfo.dmarc.join(' | ') : 'missing'));
    lines.push('- DKIM found among tested selectors: ' + (mailInfo.dkimFound.length ? mailInfo.dkimFound.join(', ') : 'none'));

    lines.push('');
    lines.push('Fix Cards:');

    cards.forEach(function (card, index) {
      lines.push(String(index + 1) + '. ' + card.title);
      lines.push('   Impact: ' + card.impact);
      lines.push('   Next: ' + card.next);
      lines.push('   Priority: ' + card.priority);
    });

    lines.push('');
    lines.push('Commands:');
    lines.push(commands);

    return lines.join('\n');
  }

  async function analyzeDns() {
    clearError();

    try {
      const domain = normalizeDomain(domainEl.value);
      const selectors = parseSelectors(selectorsEl.value);

      summaryEl.textContent = 'Querying DNS records...';
      overviewEl.innerHTML = '';
      answersEl.innerHTML = '';
      mailEl.innerHTML = '';
      fixCardsEl.innerHTML = '';
      rawEl.textContent = '';

      const results = {};
      await Promise.all(recordTypes.map(async function (type) {
        try {
          results[type] = await dnsQuery(domain, type);
        } catch (error) {
          results[type] = { Status: -1, error: error.message, Answer: [] };
        }
      }));

      let dmarcResult;
      try {
        dmarcResult = await dnsQuery('_dmarc.' + domain, 'TXT');
      } catch (error) {
        dmarcResult = { Status: -1, error: error.message, Answer: [] };
      }

      const dkimResults = {};
      await Promise.all(selectors.map(async function (selector) {
        try {
          dkimResults[selector] = await dnsQuery(selector + '._domainkey.' + domain, 'TXT');
        } catch (error) {
          dkimResults[selector] = { Status: -1, error: error.message, Answer: [] };
        }
      }));

      renderOverview(results);
      renderAnswers(results, dmarcResult, dkimResults);

      const mailInfo = buildMailSecurity(results, dmarcResult, dkimResults);
      const cards = buildFixCards(domain, results, mailInfo, dmarcResult);
      renderFixCards(cards);

      lastCommands = buildCommands(domain, selectors);
      commandsEl.textContent = lastCommands;

      lastReport = buildReport(domain, results, mailInfo, cards, lastCommands);

      summaryEl.textContent = [
        'Domain: ' + domain,
        'A records: ' + getAnswers(results.A).length,
        'AAAA records: ' + getAnswers(results.AAAA).length,
        'MX records: ' + getAnswers(results.MX).length,
        'SPF: ' + (mailInfo.spf.length ? 'found' : 'missing'),
        'DMARC: ' + (mailInfo.dmarc.length ? 'found' : 'missing'),
        'DKIM selectors found: ' + (mailInfo.dkimFound.length ? mailInfo.dkimFound.join(', ') : 'none among tested selectors'),
        'Fix Cards: ' + cards.length
      ].join('\n');

      rawEl.textContent = JSON.stringify({
        domain: domain,
        records: results,
        dmarc: dmarcResult,
        dkim: dkimResults
      }, null, 2);
    } catch (error) {
      showError(error.message);
      summaryEl.textContent = 'DNS analysis failed.';
    }
  }

  analyzeBtn.addEventListener('click', analyzeDns);

  domainEl.addEventListener('keydown', function (event) {
    if (event.key === 'Enter') {
      analyzeDns();
    }
  });

  copyReportBtn.addEventListener('click', function () {
    navigator.clipboard.writeText(lastReport || summaryEl.textContent).then(function () {
      copyReportBtn.textContent = 'Copied';
      setTimeout(function () {
        copyReportBtn.textContent = 'Copy report';
      }, 1200);
    });
  });

  copyCommandsBtn.addEventListener('click', function () {
    navigator.clipboard.writeText(lastCommands || commandsEl.textContent).then(function () {
      copyCommandsBtn.textContent = 'Copied';
      setTimeout(function () {
        copyCommandsBtn.textContent = 'Copy dig commands';
      }, 1200);
    });
  });

  resetBtn.addEventListener('click', function () {
    domainEl.value = '';
    selectorsEl.value = 'default,google,selector1,selector2,k1';
    errorEl.textContent = '';
    summaryEl.textContent = 'Enter a domain and click “Analyze DNS”.';
    overviewEl.innerHTML = '';
    answersEl.innerHTML = '';
    mailEl.innerHTML = '';
    fixCardsEl.innerHTML = '';
    commandsEl.textContent = '';
    rawEl.textContent = '';
    lastReport = '';
    lastCommands = '';
  });
})();
</script>
]]></content:encoded>
					
					<wfw:commentRss>https://pawaops.com/pawa-dns/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
