Description
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
}