Skip to content

Can be added DNS check for Adguard DoT,DoH.... (other self hosted DNS projects) or allow use dig - simple bash? #175

Open
@ghostersk

Description

@ghostersk

Hello, as mentioned on Discord, I was trying to test a Adguard DNS if it is resolving using Dns over HTTPS

  • i could not find any mentions how i can test Adguard DoT with curl, as curl what works on Google DNS is not available for adguard.
  • On the end I made long Javascript what does request via https and just checks if response contains domain name ( cannot get the IP lookup work in the hex output)

Could be added function to query DNS servers to see if the resolution works?
Not just simple DNS but encrypted DNS too.
I found some simple GO project what claim to do it, as you use GO it may be easier to add then working with JS or adding Curl to the monitoring as option.
github.com/ameshkov/dnslookup/blob/master/main.go

Just I am not sure if this Go project would work with something like adguard, what probably people would test as self hosted solution. Possibly there are more variants of selfhosted DoT and DoH servers what may act differently.

I was able to test the dns using dig
dig +short @adguardhome.example.com +https=/dns-query/client-id231 google.com A
This way it could be possible to check also if the IP changed, if there is check for the ip value

  • the /dns-query/client-id231 is specific to Adguard Home - if clients are set
  • otherwise it would be ommited or just /dns-query

This is example of code I use for checking if my DoH Adguard server works:

const url_link = "https://adguardhome.example.com/dns-query/client-id231";

const domain_name = "bbc.com";

function encodeDomainToDNSQuery(domain) {
    function domainToDNSFormat(domain) {
        let parts = domain.split('.');
        let buffer = [];

        parts.forEach(part => {
            buffer.push(part.length);
            buffer.push(...part.split('').map(c => c.charCodeAt(0)));
        });

        buffer.push(0); // Null terminator for the domain name

        return new Uint8Array(buffer);
    }

    function buildDNSQuery(domainBuffer) {
        let header = new Uint8Array([
            0xAB, 0xCD, // Transaction ID (randomized)
            0x01, 0x00, // Standard query with recursion
            0x00, 0x01, // One question
            0x00, 0x00, // Zero answer records
            0x00, 0x00, // Zero authority records
            0x00, 0x00  // Zero additional records
        ]);

        let questionTypeClass = new Uint8Array([
            0x00, 0x01, // Query Type: A (IPv4 Address)
            0x00, 0x01  // Query Class: IN (Internet)
        ]);

        return new Uint8Array([...header, ...domainBuffer, ...questionTypeClass]);
    }

    function base64Encode(arrayBuffer) {
        const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
        let result = '';
        let i = 0;

        while (i < arrayBuffer.length) {
            const byte1 = arrayBuffer[i] || 0;
            const byte2 = arrayBuffer[i + 1] || 0;
            const byte3 = arrayBuffer[i + 2] || 0;

            const group = (byte1 << 16) + (byte2 << 8) + byte3;
            result += base64Chars[(group >> 18) & 0x3F];
            result += base64Chars[(group >> 12) & 0x3F];
            result += (i + 1 < arrayBuffer.length) ? base64Chars[(group >> 6) & 0x3F] : '';
            result += (i + 2 < arrayBuffer.length) ? base64Chars[group & 0x3F] : '';

            i += 3;
        }

        return result;
    }

    let domainBuffer = domainToDNSFormat(domain);
    let dnsQuery = buildDNSQuery(domainBuffer);
    return base64Encode(dnsQuery);
}

const url_check = `${url_link}?dns=${encodeDomainToDNSQuery(domain_name)}`;
const res = await request({
    url: url_check,
    method: 'GET',
    headers: { 'accept': 'application/dns-message' }
});

// Convert res.data to hexdump
let hexDump = '';
let asciiDump = '';
for (let i = 0; i < res.data.length; i++) {
    const byte = res.data.charCodeAt(i) & 0xFF;
    const hex = byte.toString(16).padStart(2, '0');
    hexDump += hex + ' ';
    asciiDump += (byte >= 32 && byte <= 126) ? String.fromCharCode(byte) : '.';
    if ((i + 1) % 16 === 0) {
        hexDump = '';
        asciiDump = '';
    }
}

// Extract domain from question section (starting at byte 12)
let pos = 12;
let domain = '';
while (pos < res.data.length && res.data.charCodeAt(pos) !== 0) {
    const len = res.data.charCodeAt(pos);
    pos++;
    for (let i = 0; i < len; i++) {
        domain += res.data[pos];
        pos++;
    }
    domain += '.';
}
domain = domain.slice(0, -1);

// Skip QTYPE (2 bytes) and QCLASS (2 bytes) after domain
pos += 4;

// Check if it’s working (contains domain and has an IP)
let fullText = '';
for (let i = 0; i < res.data.length; i++) {
    const byte = res.data.charCodeAt(i) & 0xFF;
    fullText += (byte >= 32 && byte <= 126) ? String.fromCharCode(byte) : '.';
}
const containsDomain = fullText.includes(domain_name);
if (containsDomain) {
    console.log('Contains domain:', containsDomain);
    return 1
} else {
    return -1
}

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions