{"id":4187,"date":"2025-08-12T15:18:00","date_gmt":"2025-08-12T13:18:00","guid":{"rendered":"https:\/\/ba.be\/ongecategoriseerd\/dnspector-2\/"},"modified":"2026-10-08T07:38:07","modified_gmt":"2026-10-08T05:38:07","slug":"dnspector","status":"publish","type":"post","link":"https:\/\/ba.be\/en\/front-en\/dnspector\/","title":{"rendered":"DNSpector"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"4187\" class=\"elementor elementor-4187 elementor-3858\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-043862c e-flex e-con-boxed e-con e-parent\" data-id=\"043862c\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-fcae26d elementor-widget elementor-widget-text-editor\" data-id=\"fcae26d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h2>CHECK YOUR DOMAIN NAME FOR COMMON FLAWS<\/h2><p>A correctly configured domain name offers numerous possibilities to significantly increase security and prevent abuse, such as phishing and email spoofing.<\/p><p>It is therefore striking that many organisations often overlook the configuration of their domain name \u2013 their digital identity. To help you with this, we have developed a test that checks the basic configuration of your domain on 10 crucial points and provides concrete tips for improvement. <\/p><p>Test the quality of your domain name now and tackle the outstanding points immediately.<br>Are you having trouble solving these problems, or do you want to take the security of your domain to a higher level? Then please contact us. We not only help you with the basics, but also carry out 15 additional, specialised checks to optimally secure your company&#8217;s online identity.  <\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5166d81 elementor-widget__width-auto elementor-widget elementor-widget-html\" data-id=\"5166d81\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<head>\n    <style>\n     .bla-container{ background-color: #fff; padding: 5px; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }\n        input[type=\"text\"], input[type=\"email\"] { width: calc(100% - 20px); padding: 10px; margin-bottom: 15px; border: 1px solid #ccc; border-radius: 0px; }\n        button { background-color: #2E82B5; color: white; padding: 12px 10px; border: none; border-radius: 0px; cursor: pointer; font-size: 16px; width: 100%; }\n        button:hover { background-color: #204359; }\n        button:disabled { background-color: #a0a0a0; cursor: not-allowed; }\n        #results { margin-top: 25px; }\n        .result-item { padding: 15px; margin-bottom: 10px; border-left-width: 5px; border-left-style: solid; background-color: #f8f9fa; }\n        .pass { border-color: #28a745; }\n        .fail { border-color: #dc3545; }\n        .info { border-color: #17a2b8; }\n        .warn { border-color: #ffc107; }\n         code { background-color: #e9ecef; padding: 2px 2E82B5px; border-radius: 4px; font-family: \"Courier New\", Courier, monospace; display: block; white-space: pre-wrap; word-break: break-all; margin-top: 5px; } \n     .spinner { border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; width: 30px; height: 30px; animation: spin 1s linear infinite; margin: 20px auto; display: none; }\n        @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } \n    <\/style>\n\n<\/head>\n<body>\n\n    <div class=\"bla-container\">\n        <h2>Domain & Email Verification Tool<\/h2>\n        <p>Enter a domain name and, optionally, an email address to test the configuration.<\/p>\n\n        <label for=\"domain\">Domain name:<\/label>\n        <input type=\"text\" id=\"domain\" name=\"domain\" placeholder=\"e.g. google.com\" required>\n\n        <label for=\"email\">Email address (optional):<\/label>\n        <input type=\"email\" id=\"email\" name=\"email\" placeholder=\"e.g. info@google.com\">\n\n        <div style=\"margin-top: 15px; margin-bottom: 15px;\">\n            <input type=\"checkbox\" id=\"contactUsCheckbox\" name=\"contactUsCheckbox\">\n            <label for=\"contactUsCheckbox\">Contact me to help secure our domain names even better.<\/label>\n        <\/div>\n\n        <button id=\"checkBtn\" onclick=\"runChecks()\">Start Analysis<\/button>\n        \n        <div id=\"spinner\" class=\"spinner\"><\/div>\n        <div id=\"results\"><\/div>\n    <\/div>\n\n    <script>\n        const resultsDiv = document.getElementById('results');\n        const checkBtn = document.getElementById('checkBtn');\n        const spinner = document.getElementById('spinner');\n        const COMMON_DKIM_SELECTORS = ['google', 'selector1', 'selector2', 'default', 'k1', 'dkim', 'default', 'dkim', 'mail', 'selector1', 'selector2', 'selector3', 'selector4','key1', 'key2', 'key3', 'k1', 'k2', 'k3', 'm1', 'm2', 'm3', 's1', 's2', 'mandril', 'sm', 'smtp','amazonses', 'cm', 'constantcontact', 'everlytickey1', 'everlytickey2','hubspot', 'mailjet', 'mxvault', 'protonmail', 'sendinblue', 'sig1', 'zendesk1', 'zendesk2'];\n        const MAX_SPF_LOOKUPS = 10;\n        const CAA_RR_TYPE = 257;\n        const CAA_KNOWN_TAGS = ['issue', 'issuewild', 'iodef', 'contactemail', 'contactphone', 'issuemail', 'issuevmc'];\n        const NUMBER_LOCALE = 'en-GB';\n\n        const lang = {\n            TOOL_TITLE: 'Domain & Email Verification Tool',\n            TOOL_DESCRIPTION: 'Enter a domain name and, optionally, an email address to test the configuration.',\n            LABEL_DOMAIN: 'Domain name:',\n            PLACEHOLDER_DOMAIN: 'e.g. google.com',\n            LABEL_EMAIL: 'Email address (optional):',\n            PLACEHOLDER_EMAIL: 'e.g. info@google.com',\n            BUTTON_START_ANALYSIS: 'Start Analysis',\n            ALERT_ENTER_DOMAIN: 'Please enter a domain name.',\n\n            A_RECORD_PASS_TITLE: '\u2705 Domain Exists (A Record)',\n            A_RECORD_PASS_TECHDATA_IPS: 'IP addresses found:',\n            A_RECORD_PASS_TIP: 'The domain correctly points to one or more servers.',\n            A_RECORD_FAIL_TITLE: '\u274c Domain Does Not Exist (A Record)',\n            A_RECORD_FAIL_TECHDATA: 'No A records found.',\n            A_RECORD_FAIL_TIP: 'Make sure the domain name is correct and that an A record is configured in your DNS settings.',\n\n            AAAA_RECORD_PASS_TITLE: '\u2705 Domain Exists (AAAA Record)',\n            AAAA_RECORD_PASS_TECHDATA_IPS: 'IPv6 addresses found:',\n            AAAA_RECORD_PASS_TIP: 'The domain correctly points to one or more IPv6 servers.',\n            AAAA_RECORD_INFO_TITLE: '\u2139\ufe0f Domain Exists (AAAA Record)',\n            AAAA_RECORD_INFO_TECHDATA: 'No AAAA records found.',\n            AAAA_RECORD_INFO_TIP: 'The domain has no IPv6 address. Consider adding IPv6 support for the future.',\n\n            SOA_RECORD_PASS_TITLE: '\u2705 SOA Record',\n            SOA_RECORD_PASS_TIP: 'SOA record found.',\n            SOA_RECORD_FAIL_TITLE: '\u274c SOA Record',\n            SOA_RECORD_FAIL_TECHDATA: 'No SOA record found.',\n            SOA_RECORD_FAIL_TIP: 'Every domain must have an SOA (Start of Authority) record.',\n\n            NS_REDUNDANCY_PASS_TITLE: '\u2705 Nameserver Redundancy',\n            NS_REDUNDANCY_PASS_TECHDATA_FOUND: 'Found:',\n            NS_REDUNDANCY_PASS_TECHDATA_RANGES: 'nameservers. IPs in {uniqueRanges} different \/24 ranges.',\n            NS_REDUNDANCY_PASS_TIP: 'Excellent redundancy: nameservers on different networks.',\n            NS_REDUNDANCY_WARN_TITLE: '\u26a0\ufe0f Nameserver Redundancy',\n            NS_REDUNDANCY_WARN_TIP: 'Consider a secondary DNS provider for better fault tolerance.',\n            NS_REDUNDANCY_FAIL_TITLE: '\u274c Nameserver Redundancy',\n            NS_REDUNDANCY_FAIL_TECHDATA_COUNT: 'Number of nameservers:',\n            NS_REDUNDANCY_FAIL_TIP: 'Add at least a second nameserver for redundancy.',\n\n            DNSSEC_PASS_TITLE: '\u2705 DNSSEC',\n            DNSSEC_PASS_TECHDATA: 'DNSKEY records found.',\n            DNSSEC_PASS_TIP: 'DNSSEC is configured correctly.',\n            DNSSEC_WARN_TITLE: '\u26a0\ufe0f DNSSEC',\n            DNSSEC_WARN_TECHDATA: 'No DNSKEY records found.',\n            DNSSEC_WARN_TIP: 'Enable DNSSEC at your registrar to protect against DNS spoofing.',\n\n            CAA_RECORD_PASS_TITLE: '\u2705 CAA Record',\n            CAA_RECORD_WARN_TITLE: '\u26a0\ufe0f CAA Record',\n            CAA_RECORD_FAIL_TITLE: '\u274c CAA Record',\n            CAA_RECORD_TECHDATA_RECORDS: 'Records:',\n            CAA_RECORD_TECHDATA_INHERITED: 'No CAA records of its own; inherited from parent domain: {name}',\n            CAA_RECORD_NONE_TECHDATA: 'No CAA records found (not on parent domains either).',\n            CAA_RECORD_NONE_TIP: 'Without a CAA record, any certificate authority (CA) may issue a TLS certificate for your domain. Add a CAA record for the CA you actually use, e.g. <strong>0 issue \"letsencrypt.org\"<\/strong>.',\n            CAA_RECORD_SERVFAIL_TECHDATA: 'DNS error (SERVFAIL) when querying CAA for {name}.',\n            CAA_RECORD_SERVFAIL_TIP: 'CAs refuse to issue certificates while the CAA lookup fails. Check your DNS and DNSSEC configuration.',\n            CAA_RECORD_TIP_ISSUE: 'Only these CAs may issue certificates: <strong>{cas}<\/strong>.',\n            CAA_RECORD_TIP_ISSUE_NONE: 'Certificate issuance is completely forbidden (issue \";\"). If that is not intended, you will no longer be able to obtain a TLS certificate.',\n            CAA_RECORD_TIP_NO_ISSUE: 'There is no \\'issue\\' record: any CA may still issue regular certificates. Add an \\'issue\\' record for your own CA.',\n            CAA_RECORD_TIP_WILD: 'Wildcard certificates: <strong>{cas}<\/strong>.',\n            CAA_RECORD_TIP_WILD_NONE: 'none (forbidden)',\n            CAA_RECORD_TIP_IODEF: 'Notifications of refused requests (iodef): {iodef}.',\n            CAA_RECORD_TIP_NO_IODEF: 'Consider an \\'iodef\\' record so you are notified when a CA refuses a request, e.g. <strong>0 iodef \"mailto:security@{domain}\"<\/strong>.',\n            CAA_RECORD_TIP_CRITICAL: '<strong>CRITICAL:<\/strong> unknown tag(s) with the critical flag set ({tags}). CAs will then refuse to issue any certificate.',\n\n            MX_RECORD_PASS_TITLE: '\u2705 MX Record',\n            MX_RECORD_PASS_TECHDATA_MAILSERVERS: 'Mail servers:',\n            MX_RECORD_PASS_TECHDATA_PRIORITY: '(Priority:',\n            MX_RECORD_PASS_TIP: 'MX records are configured correctly for receiving email.',\n            MX_RECORD_FAIL_TITLE: '\u274c MX Record',\n            MX_RECORD_FAIL_TECHDATA: 'No MX records found.',\n            MX_RECORD_FAIL_TIP: 'Configure MX records if you want to receive email on this domain.',\n\n            SPF_RECORD_INVALID_TITLE: '\u274c SPF Record (INVALID)',\n            SPF_RECORD_STRICT_TITLE: '\u2705 SPF Record (Strict)',\n            SPF_RECORD_SOFTFAIL_TITLE: '\u26a0\ufe0f SPF Record (Softfail)',\n            SPF_RECORD_TECHDATA_RECORD: 'Record:',\n            SPF_RECORD_TECHDATA_IPS: 'Calculated number of IPs:',\n            SPF_RECORD_TECHDATA_LOOKUPS: 'DNS lookups:',\n            SPF_RECORD_TIP_AUTH_IPS: 'The record authorises approximately {ipCount} IP addresses. The include chain is: {chain}.',\n            SPF_RECORD_TIP_CRITICAL_LOOKUPS: '**CRITICAL:** The record is invalid because it exceeds the limit of {maxLookups} DNS lookups. Mail servers will ignore this record. Simplify it by removing unnecessary \\'includes\\' or by \\'flattening\\' IP addresses.',\n            SPF_RECORD_TIP_SOFTFAIL: 'The record uses \\'~all\\' (softfail). Consider \\'-all\\' (fail) for a stricter policy.',\n            SPF_RECORD_TIP_STRICT: 'The record uses \\'-all\\' (fail), which is the recommended strict configuration.',\n            SPF_RECORD_FAIL_TITLE: '\u274c SPF Record',\n            SPF_RECORD_FAIL_TECHDATA: 'No SPF record found.',\n            SPF_RECORD_FAIL_TIP: 'Create an SPF record to prevent email spoofing.',\n\n            DKIM_RECORD_PASS_TITLE: '\u2705 DKIM Record Found',\n            DKIM_RECORD_PASS_TECHDATA_SELECTOR: 'Selector \\'{selector}\\' found.',\n            DKIM_RECORD_PASS_TIP: 'DKIM was found and helps verify the authenticity of your emails.',\n            DKIM_RECORD_FAIL_TITLE: '\u274c DKIM Record',\n            DKIM_RECORD_FAIL_TECHDATA: 'No DKIM records found with common selectors.',\n            DKIM_RECORD_FAIL_TIP: 'Configure DKIM with your email provider.',\n\n            DMARC_RECORD_PASS_TITLE: '\u2705 DMARC Record',\n            DMARC_RECORD_TIP_FOUND: 'DMARC record found with policy: <strong>{policy}<\/strong> (subdomains: <strong>{subdomainPolicy}<\/strong>).',\n            DMARC_RECORD_TIP_MONITORING: 'The policy only monitors. Consider \\'quarantine\\' or \\'reject\\' for better protection.',\n            DMARC_RECORD_TIP_QUARANTINE: 'The policy quarantines unauthenticated emails. Good for a gradual rollout.',\n            DMARC_RECORD_TIP_REJECT: 'The policy rejects unauthenticated emails. This is the strongest level of protection.',\n            DMARC_RECORD_TIP_RUA: 'Aggregate reporting (RUA): {rua}.',\n            DMARC_RECORD_TIP_RUF: 'Forensic reporting (RUF): {ruf}.',\n            DMARC_RECORD_FAIL_TITLE: '\u274c DMARC Record',\n            DMARC_RECORD_FAIL_TECHDATA: 'No DMARC record found at {dmarcDomain}',\n            DMARC_RECORD_FAIL_TIP: 'Publish a DMARC record to protect your domain against phishing and spoofing.',\n\n            WEBHOOK_WARN_NOT_CONFIGURED: 'Webhook URL is not configured. Skipping webhook post.',\n            WEBHOOK_SUCCESS: 'Webhook post successful!',\n            WEBHOOK_FAILED: 'Webhook post failed:',\n            WEBHOOK_ERROR_SENDING: 'Error sending to webhook:',\n            CHECKBOX_CONTACT_US: 'Contact me to help secure our domain names even better.'\n        };\n\n        async function queryDns(name, type) {\n            const url = `https:\/\/dns.google\/resolve?name=${encodeURIComponent(name)}&type=${type}`;\n            try {\n                const response = await fetch(url);\n                if (!response.ok) { throw new Error(`Network error for ${name}\/${type}`); }\n                return await response.json();\n            } catch (error) {\n                console.error('DNS Query Error:', error);\n                return { Answer: [] };\n            }\n        }\n\n        function renderResult(status, title, techData, tip) {\n            const item = document.createElement('div');\n            item.className = `result-item ${status}`;\n            item.innerHTML = `<strong>${title}<\/strong><code>${techData || 'N\/A'}<\/code><p>${tip}<\/p>`;\n            resultsDiv.appendChild(item);\n        }\n\n        \/\/ CAA values come from DNS and are rendered via innerHTML: escape them first.\n        function escapeHtml(str) {\n            return String(str).replace(\/[&<>\"']\/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\n        }\n\n        \/\/ Parse a single CAA record: presentation format (0 issue \"ca.org\") or RFC 3597 hex (\\# 22 00 05 ...).\n        function parseCaaRecord(data) {\n            data = data.trim();\n            if (data.startsWith('\\\\#')) {\n                const hex = data.split(\/\\s+\/).slice(2).join('');\n                const bytes = (hex.match(\/..\/g) || []).map(h => parseInt(h, 16));\n                if (bytes.length < 2) return null;\n                const tagLen = bytes[1];\n                return {\n                    flags: bytes[0],\n                    tag: String.fromCharCode(...bytes.slice(2, 2 + tagLen)).toLowerCase(),\n                    value: String.fromCharCode(...bytes.slice(2 + tagLen))\n                };\n            }\n            const m = data.match(\/^(\\d+)\\s+(\\S+)\\s+(.*)$\/);\n            if (!m) return null;\n            return { flags: Number(m[1]), tag: m[2].toLowerCase(), value: m[3].replace(\/^\"|\"$\/g, '') };\n        }\n\n        \/\/ RFC 8659: find the relevant CAA set by climbing from the domain up the tree.\n        async function findRelevantCaa(domain) {\n            let labels = domain.toLowerCase().replace(\/\\.$\/, '').split('.');\n            while (labels.length >= 2) {\n                const name = labels.join('.');\n                const caaData = await queryDns(name, 'CAA');\n                if (caaData.Status === 2) return { name, servfail: true, records: [] };\n                const records = (caaData.Answer || [])\n                    .filter(ans => ans.type === CAA_RR_TYPE)\n                    .map(ans => ({ raw: ans.data, ...parseCaaRecord(ans.data) }))\n                    .filter(r => r.tag);\n                if (records.length > 0) return { name, servfail: false, records };\n                labels = labels.slice(1);\n            }\n            return { name: null, servfail: false, records: [] };\n        }\n\n        async function checkCaa(domain) {\n            const caa = await findRelevantCaa(domain);\n\n            if (caa.servfail) {\n                renderResult('fail', lang.CAA_RECORD_FAIL_TITLE, lang.CAA_RECORD_SERVFAIL_TECHDATA.replace('{name}', escapeHtml(caa.name)), lang.CAA_RECORD_SERVFAIL_TIP);\n                return;\n            }\n\n            if (caa.records.length === 0) {\n                renderResult('warn', lang.CAA_RECORD_WARN_TITLE, lang.CAA_RECORD_NONE_TECHDATA, lang.CAA_RECORD_NONE_TIP);\n                return;\n            }\n\n            const recs = caa.records;\n            const caNames = list => list.map(r => r.value.split(';')[0].trim()).filter(v => v);\n            const issue = recs.filter(r => r.tag === 'issue');\n            const wild = recs.filter(r => r.tag === 'issuewild');\n            const iodef = recs.filter(r => r.tag === 'iodef');\n            const criticalUnknown = recs.filter(r => (r.flags & 128) && !CAA_KNOWN_TAGS.includes(r.tag));\n\n            let techInfo = `${lang.CAA_RECORD_TECHDATA_RECORDS}\\n${recs.map(r => escapeHtml(r.raw)).join('\\n')}`;\n            if (caa.name !== domain.toLowerCase().replace(\/\\.$\/, '')) {\n                techInfo += `\\n${lang.CAA_RECORD_TECHDATA_INHERITED.replace('{name}', escapeHtml(caa.name))}`;\n            }\n\n            let status = 'pass';\n            let tipParts = [];\n\n            if (issue.length > 0) {\n                const cas = caNames(issue);\n                tipParts.push(cas.length > 0\n                    ? lang.CAA_RECORD_TIP_ISSUE.replace('{cas}', escapeHtml(cas.join(', ')))\n                    : lang.CAA_RECORD_TIP_ISSUE_NONE);\n            } else {\n                status = 'warn';\n                tipParts.push(lang.CAA_RECORD_TIP_NO_ISSUE);\n            }\n\n            if (wild.length > 0) {\n                const wildCas = caNames(wild);\n                tipParts.push(lang.CAA_RECORD_TIP_WILD.replace('{cas}', escapeHtml(wildCas.length > 0 ? wildCas.join(', ') : lang.CAA_RECORD_TIP_WILD_NONE)));\n            }\n\n            if (iodef.length > 0) {\n                tipParts.push(lang.CAA_RECORD_TIP_IODEF.replace('{iodef}', escapeHtml(iodef.map(r => r.value).join(', '))));\n            } else {\n                tipParts.push(lang.CAA_RECORD_TIP_NO_IODEF.replace('{domain}', escapeHtml(domain)));\n            }\n\n            if (criticalUnknown.length > 0) {\n                status = 'fail';\n                tipParts.unshift(lang.CAA_RECORD_TIP_CRITICAL.replace('{tags}', escapeHtml(criticalUnknown.map(r => r.tag).join(', '))));\n            }\n\n            const title = status === 'pass' ? lang.CAA_RECORD_PASS_TITLE\n                        : status === 'warn' ? lang.CAA_RECORD_WARN_TITLE\n                        : lang.CAA_RECORD_FAIL_TITLE;\n\n            renderResult(status, title, techInfo, tipParts.join(' '));\n        }\n        \n        async function analyzeSpf(domain) {\n            let dnsLookups = 0;\n            let ipCount = 0;\n            let processedIncludes = new Set();\n            let spfChain = [];\n\n            async function getIpCountForMechanism(mechanism, currentDomain) {\n                dnsLookups++;\n                if (dnsLookups > MAX_SPF_LOOKUPS) return 0;\n                \n                let targetDomain = currentDomain;\n                if (mechanism.includes(':')) {\n                    targetDomain = mechanism.split(':')[1];\n                }\n                \n                const aData = await queryDns(targetDomain, 'A');\n                const aaaaData = await queryDns(targetDomain, 'AAAA');\n                return (aData.Answer?.length || 0) + (aaaaData.Answer?.length || 0);\n            }\n            \n            async function getIpCountForMx(mechanism, currentDomain) {\n                 dnsLookups++;\n                 if (dnsLookups > MAX_SPF_LOOKUPS) return 0;\n\n                 let targetDomain = currentDomain;\n                 if (mechanism.includes(':')) {\n                    targetDomain = mechanism.split(':')[1];\n                 }\n\n                 const mxData = await queryDns(targetDomain, 'MX');\n                 let mxIpCount = 0;\n                 if (mxData.Answer) {\n                     for(const mx of mxData.Answer) {\n                         const exchangeDomain = mx.data.split(' ')[1];\n                         mxIpCount += await getIpCountForMechanism('a:' + exchangeDomain, exchangeDomain);\n                     }\n                 }\n                 return mxIpCount;\n            }\n\n            function countIpsInCidr(cidr) {\n                if (!cidr.includes('\/')) return 1;\n                const [_, prefixStr] = cidr.split('\/');\n                const prefix = parseInt(prefixStr, 10);\n                if (isNaN(prefix)) return 1;\n                \n                if (cidr.includes('.')) {\n                    if (prefix < 0 || prefix > 32) return 1;\n                    return Math.pow(2, 32 - prefix);\n                }\n                if (cidr.includes(':')) {\n                   return 1;\n                }\n                return 1;\n            }\n\n            async function parseSpf(currentDomain) {\n                if (processedIncludes.has(currentDomain)) return;\n                processedIncludes.add(currentDomain);\n                spfChain.push(currentDomain);\n\n                const txtData = await queryDns(currentDomain, 'TXT');\n                const spfRecord = txtData.Answer?.find(ans => ans.data.toLowerCase().includes(\"v=spf1\"));\n\n                if (!spfRecord) return; \n                \n                let recordText = spfRecord.data.replace(\/\"\/g, '');\n                const parts = recordText.split(\/\\s+\/);\n\n                for (const part of parts) {\n                    const mechanism = part.toLowerCase();\n\n                    if(dnsLookups > MAX_SPF_LOOKUPS) break;\n\n                    if (mechanism.startsWith('include:')) {\n                        dnsLookups++;\n                        const includeDomain = mechanism.substring(8);\n                        await parseSpf(includeDomain);\n                    } else if (mechanism.startsWith('redirect=')) {\n                        dnsLookups++;\n                        const redirectDomain = mechanism.substring(9);\n                        spfChain.push(`-> REDIRECT to ${redirectDomain}`);\n                        await parseSpf(redirectDomain);\n                        break;\n                    } else if (mechanism.startsWith('ip4:')) {\n                        ipCount += countIpsInCidr(mechanism.substring(4));\n                    } else if (mechanism.startsWith('ip6:')) {\n                        ipCount += countIpsInCidr(mechanism.substring(4));\n                    }\n                    else if (mechanism === 'a' || mechanism.startsWith('a:')) {\n                        ipCount += await getIpCountForMechanism(mechanism, currentDomain);\n                    } else if (mechanism === 'mx' || mechanism.startsWith('mx:')) {\n                         ipCount += await getIpCountForMx(mechanism, currentDomain);\n                    }\n                }\n            }\n\n            await parseSpf(domain);\n\n            return {\n                ipCount: ipCount,\n                dnsLookups: dnsLookups,\n                isTooManyLookups: dnsLookups > MAX_SPF_LOOKUPS,\n                chain: spfChain.join(' -> ')\n            };\n        }\n\n        async function sendToWebhook(domain, email, contactUs) {\n            const WEBHOOK_URL = 'https:\/\/apipost.ba.be\/webhook\/9fbe371b-502d-4b55-b68c-241206bcda15';\n\n            if (WEBHOOK_URL === 'YOUR_WEBHOOK_URL_HERE') {\n                console.warn(lang.WEBHOOK_WARN_NOT_CONFIGURED);\n                return;\n            }\n\n            const payload = {\n                domain: domain,\n                email: email,\n                contactUs: contactUs\n            };\n\n            try {\n                const response = await fetch(WEBHOOK_URL, {\n                    method: 'POST',\n                    headers: {\n                        'Content-Type': 'application\/json',\n                    },\n                    body: JSON.stringify(payload),\n                });\n\n                if (response.ok) {\n                    console.log(lang.WEBHOOK_SUCCESS);\n                } else {\n                    console.error(`${lang.WEBHOOK_FAILED} ${response.status}, ${response.statusText}`);\n                }\n            } catch (error) {\n                console.error(`${lang.WEBHOOK_ERROR_SENDING} ${error}`);\n            }\n        }\n\n        async function runChecks() {\n            const domain = document.getElementById('domain').value.trim();\n            const email = document.getElementById('email').value.trim();\n            const contactUs = document.getElementById('contactUsCheckbox').checked;\n\n            if (!domain) {\n                alert(lang.ALERT_ENTER_DOMAIN);\n                return;\n            }\n\n            resultsDiv.innerHTML = '';\n            spinner.style.display = 'block';\n            checkBtn.disabled = true;\n\n            const aData = await queryDns(domain, 'A');\n            if (aData.Answer?.length > 0) {\n                renderResult('pass', lang.A_RECORD_PASS_TITLE, `${lang.A_RECORD_PASS_TECHDATA_IPS} ${aData.Answer.map(ans => ans.data).join(', ')}`, lang.A_RECORD_PASS_TIP);\n            } else {\n                renderResult('fail', lang.A_RECORD_FAIL_TITLE, lang.A_RECORD_FAIL_TECHDATA, lang.A_RECORD_FAIL_TIP);\n            }\n\n            const aaaaData = await queryDns(domain, 'AAAA');\n            if (aaaaData.Answer?.length > 0) {\n                renderResult('pass', lang.AAAA_RECORD_PASS_TITLE, `${lang.AAAA_RECORD_PASS_TECHDATA_IPS} ${aaaaData.Answer.map(ans => ans.data).join(', ')}`, lang.AAAA_RECORD_PASS_TIP);\n            } else {\n                renderResult('info', lang.AAAA_RECORD_INFO_TITLE, lang.AAAA_RECORD_INFO_TECHDATA, lang.AAAA_RECORD_INFO_TIP);\n            }\n            \n            const soaData = await queryDns(domain, 'SOA');\n            if (soaData.Answer?.length > 0) {\n                renderResult('pass', lang.SOA_RECORD_PASS_TITLE, soaData.Answer[0].data, lang.SOA_RECORD_PASS_TIP);\n            } else {\n                renderResult('fail', lang.SOA_RECORD_FAIL_TITLE, lang.SOA_RECORD_FAIL_TECHDATA, lang.SOA_RECORD_FAIL_TIP);\n            }\n\n            const nsData = await queryDns(domain, 'NS');\n            if (nsData.Answer?.length > 1) {\n                const nameservers = nsData.Answer.map(ans => ans.data);\n                let ips = [];\n                for (const ns of nameservers) {\n                    const nsIpData = await queryDns(ns, 'A');\n                    if(nsIpData.Answer?.length > 0) ips.push(nsIpData.Answer[0].data);\n                }\n                const uniqueRanges = new Set(ips.map(ip => ip.split('.').slice(0, 3).join('.'))).size;\n                const techInfo = `${lang.NS_REDUNDANCY_PASS_TECHDATA_FOUND} ${nameservers.length} ${lang.NS_REDUNDANCY_PASS_TECHDATA_RANGES.replace('{uniqueRanges}', uniqueRanges)}`;\n                if (uniqueRanges > 1) {\n                    renderResult('pass', lang.NS_REDUNDANCY_PASS_TITLE, techInfo, lang.NS_REDUNDANCY_PASS_TIP);\n                } else {\n                    renderResult('warn', lang.NS_REDUNDANCY_WARN_TITLE, techInfo, lang.NS_REDUNDANCY_WARN_TIP);\n                }\n            } else {\n                 renderResult('fail', lang.NS_REDUNDANCY_FAIL_TITLE, `${lang.NS_REDUNDANCY_FAIL_TECHDATA_COUNT} ${nsData.Answer?.length || 0}`, lang.NS_REDUNDANCY_FAIL_TIP);\n            }\n\n            const dnskeyData = await queryDns(domain, 'DNSKEY');\n            if (dnskeyData.Answer?.length > 0) {\n                renderResult('pass', lang.DNSSEC_PASS_TITLE, lang.DNSSEC_PASS_TECHDATA, lang.DNSSEC_PASS_TIP);\n            } else {\n                renderResult('warn', lang.DNSSEC_WARN_TITLE, lang.DNSSEC_WARN_TECHDATA, lang.DNSSEC_WARN_TIP);\n            }\n\n            await checkCaa(domain);\n\n            const mxData = await queryDns(domain, 'MX');\n            if (mxData.Answer?.length > 0) {\n                const mailServers = mxData.Answer\n                    .map(ans => {\n                        const parts = ans.data.split(' ');\n                        return `${parts[1]} (${lang.MX_RECORD_PASS_TECHDATA_PRIORITY} ${parts[0]})`;\n                    })\n                    .join(', ');\n                renderResult('pass', lang.MX_RECORD_PASS_TITLE, `${lang.MX_RECORD_PASS_TECHDATA_MAILSERVERS} ${mailServers}`, lang.MX_RECORD_PASS_TIP);\n            } else {\n                renderResult('fail', lang.MX_RECORD_FAIL_TITLE, lang.MX_RECORD_FAIL_TECHDATA, lang.MX_RECORD_FAIL_TIP);\n            }\n\n            const txtDataSpf = await queryDns(domain, 'TXT');\n            const spfRecord = txtDataSpf.Answer?.find(ans => ans.data.toLowerCase().includes(\"v=spf1\"));\n            if (spfRecord) {\n                const recordText = spfRecord.data.replace(\/\"\/g, '');\n                \n                const spfAnalysis = await analyzeSpf(domain);\n                \n                let status = 'pass';\n                if (spfAnalysis.isTooManyLookups) status = 'fail';\n                else if (recordText.includes(\"~all\")) status = 'warn';\n\n                let title = '';\n                if (spfAnalysis.isTooManyLookups) title = lang.SPF_RECORD_INVALID_TITLE;\n                else if (status === 'pass') title = lang.SPF_RECORD_STRICT_TITLE;\n                else title = lang.SPF_RECORD_SOFTFAIL_TITLE;\n\n                const techInfo = `${lang.SPF_RECORD_TECHDATA_RECORD} ${recordText}\\n${lang.SPF_RECORD_TECHDATA_IPS} ${spfAnalysis.ipCount.toLocaleString(NUMBER_LOCALE)}\\n${lang.SPF_RECORD_TECHDATA_LOOKUPS} ${spfAnalysis.dnsLookups} \/ ${MAX_SPF_LOOKUPS}`;\n                \n                let tip = lang.SPF_RECORD_TIP_AUTH_IPS.replace('{ipCount}', spfAnalysis.ipCount.toLocaleString(NUMBER_LOCALE)).replace('{chain}', spfAnalysis.chain);\n                if(spfAnalysis.isTooManyLookups) {\n                    tip += ` \\n\\n${lang.SPF_RECORD_TIP_CRITICAL_LOOKUPS.replace('{maxLookups}', MAX_SPF_LOOKUPS)}`;\n                } else if (recordText.includes(\"~all\")) {\n                    tip += ` ${lang.SPF_RECORD_TIP_SOFTFAIL}`;\n                } else {\n                     tip += ` ${lang.SPF_RECORD_TIP_STRICT}`;\n                }\n\n                renderResult(status, title, techInfo, tip);\n\n            } else {\n                renderResult('fail', lang.SPF_RECORD_FAIL_TITLE, lang.SPF_RECORD_FAIL_TECHDATA, lang.SPF_RECORD_FAIL_TIP);\n            }\n\n            let dkimFound = false;\n            for (const selector of COMMON_DKIM_SELECTORS) {\n                 const dkimDomain = `${selector}._domainkey.${domain}`;\n                 const dkimData = await queryDns(dkimDomain, 'TXT');\n                 if(dkimData.Answer?.length > 0) {\n                     renderResult('pass', lang.DKIM_RECORD_PASS_TITLE, lang.DKIM_RECORD_PASS_TECHDATA_SELECTOR.replace('{selector}', selector), lang.DKIM_RECORD_PASS_TIP);\n                     dkimFound = true;\n                     break;\n                 }\n            }\n             if (!dkimFound) {\n                 renderResult('fail', lang.DKIM_RECORD_FAIL_TITLE, lang.DKIM_RECORD_FAIL_TECHDATA, lang.DKIM_RECORD_FAIL_TIP);\n             }\n\n            const dmarcDomain = `_dmarc.${domain}`;\n            const dmarcData = await queryDns(dmarcDomain, 'TXT');\n             const dmarcRecord = dmarcData.Answer?.find(ans => ans.data.toLowerCase().includes(\"v=dmarc1\"));\n             if (dmarcRecord) {\n                const recordText = dmarcRecord.data.replace(\/\"\/g, '');\n                const dmarcParts = recordText.split(';').map(s => s.trim()).filter(s => s);\n                let policy = 'none';\n                let subdomainPolicy = 'none';\n                let rua = '';\n                let ruf = '';\n\n                dmarcParts.forEach(part => {\n                    if (part.startsWith('p=')) policy = part.substring(2);\n                    if (part.startsWith('sp=')) subdomainPolicy = part.substring(3);\n                    if (part.startsWith('rua=')) rua = part.substring(4);\n                    if (part.startsWith('ruf=')) ruf = part.substring(4);\n                });\n\n                let status = 'pass';\n                let tip = lang.DMARC_RECORD_TIP_FOUND.replace('{policy}', policy).replace('{subdomainPolicy}', subdomainPolicy);\n\n                if (policy === 'none') {\n                    status = 'warn';\n                    tip += ` ${lang.DMARC_RECORD_TIP_MONITORING}`;\n                } else if (policy === 'quarantine') {\n                    tip += ` ${lang.DMARC_RECORD_TIP_QUARANTINE}`;\n                } else if (policy === 'reject') {\n                    tip += ` ${lang.DMARC_RECORD_TIP_REJECT}`;\n                }\n\n                if (rua) tip += ` ${lang.DMARC_RECORD_TIP_RUA.replace('{rua}', rua)}`;\n                if (ruf) tip += ` ${lang.DMARC_RECORD_TIP_RUF.replace('{ruf}', ruf)}`;\n\n                renderResult(status, lang.DMARC_RECORD_PASS_TITLE, recordText, tip);\n            } else {\n                 renderResult('fail', lang.DMARC_RECORD_FAIL_TITLE, lang.DMARC_RECORD_FAIL_TECHDATA.replace('{dmarcDomain}', dmarcDomain), lang.DMARC_RECORD_FAIL_TIP);\n             }\n\n            spinner.style.display = 'none';\n            checkBtn.disabled = false;\n\n            sendToWebhook(domain, email, contactUs);\n        }\n\n        document.addEventListener('DOMContentLoaded', () => {\n            \/\/ No language loading needed, as lang object is defined inline\n        });\n    <\/script>\n<\/body>\n<\/html>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-a0f233f elementor-widget elementor-widget-text-editor\" data-id=\"a0f233f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Disclaimer: We perform these tests from within your browser. The results are therefore entirely based on the DNS settings of your device. Inaccurate findings may occur due to factors such as network variations or browser limitations. Therefore, always verify the results with us or your DNS agent for confirmation.   <\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-10233c4 e-con-full e-flex e-con e-parent\" data-id=\"10233c4\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Your domain name is the foundation of your online presence. It is the first thing customers see and ensures that they can find your website and that your emails are delivered correctly. When your domain is not set up correctly, this has direct consequences for your accessibility and reliability.  <\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[44,75,74],"tags":[],"class_list":["post-4187","post","type-post","status-publish","format-standard","hentry","category-front","category-lab","category-product"],"_links":{"self":[{"href":"https:\/\/ba.be\/en\/wp-json\/wp\/v2\/posts\/4187","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ba.be\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ba.be\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ba.be\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/ba.be\/en\/wp-json\/wp\/v2\/comments?post=4187"}],"version-history":[{"count":17,"href":"https:\/\/ba.be\/en\/wp-json\/wp\/v2\/posts\/4187\/revisions"}],"predecessor-version":[{"id":5180,"href":"https:\/\/ba.be\/en\/wp-json\/wp\/v2\/posts\/4187\/revisions\/5180"}],"wp:attachment":[{"href":"https:\/\/ba.be\/en\/wp-json\/wp\/v2\/media?parent=4187"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ba.be\/en\/wp-json\/wp\/v2\/categories?post=4187"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ba.be\/en\/wp-json\/wp\/v2\/tags?post=4187"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}