msgbyte / msgbyte/tianji

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

Open
#175 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
3.1k
Forks
194
Avg merge
18m
Merged PRs (30d)
3

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
}

Image

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The payload names the referenced dnslookup main.go, the dig command, and a JavaScript DoH example; start by comparing those approaches and locating Tianji's existing monitoring entry point. Done means a settled scope for AdGuard-compatible DoH/DoT checks, including the optional path and IP-result validation, with coverage for supported variants.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, javascript, typescript
Domain
networking, observability-sre
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.