← All tasks
javascriptclaude-code/javascript-t1 #26Not a task: already works

DNS Resolver (javascript, written by Claude Code)

envgap__claude-code__javascript-t1-26

Written by a coding agent; not on GitHubWritten 2026-02-28

01 / FAILURE SIGNATURE

As the study recorded it

No identifying execution failure has been captured.
Not a benchmark task.
  • The project already builds and runs before the fix, so there is nothing to repair.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
package.json
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

claude-code/javascript-t1 #26 · read the task the agent was given
Claude Code wrote this javascript project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: DNS Resolver

Write a program that performs DNS lookups for domain names, supporting multiple record types, reverse lookups, DNS-over-HTTPS, and batch resolution with response time measurements.

FUNCTIONAL REQUIREMENTS:
- Accept a domain name as a command-line argument
- Support querying multiple DNS record types via --type flag: A, AAAA, MX, NS, TXT, CNAME, SOA, PTR, SRV (default: A)
- Support querying all common record types at once via --all flag
- Support reverse DNS lookups (IP to hostname) via --reverse flag
- Allow specifying a custom DNS server via --server flag (default: system DNS resolver)
- Support DNS-over-HTTPS (DoH) via --doh flag using configurable DoH providers
- Measure and display query response time in milliseconds for each lookup
- Support batch resolution of multiple domains from a file via --file flag (one domain per line)
- Display TTL (Time To Live) values for each record
- Support recursive trace mode via --trace flag: show the full resolution path from root servers to authoritative servers
- Print results to console in a structured format showing: domain, record type, value, TTL, response time, and nameserver used
- Save results as JSON with --output flag (default: dns_results.json)
- Support concurrent resolution of batch queries via --parallel flag with configurable concurrency (--threads, default 10)
- If no domain is given, resolve a set of well-known domains (example.com, google.com, github.com, cloudflare.com) across multiple record types and display a formatted comparison table
- Handle errors: NXDOMAIN (non-existent domain), SERVFAIL, timeout, invalid domain names, and unreachable DNS servers

Create a complete JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include:
- Source code
- package.json with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

package.json
{
  "name": "dns-resolver",
  "version": "1.0.0",
  "description": "Comprehensive DNS lookup tool with multiple record types, reverse DNS, DoH, and batch resolution",
  "main": "resolver.js",
  "scripts": {
    "start": "node resolver.js"
  },
  "dependencies": {
    "dns2": "2.1.0",
    "chalk": "4.1.2"
  }
}
README.md
# DNS Resolver - JavaScript (Trial 1)

A comprehensive DNS lookup tool supporting multiple record types, reverse DNS lookups, DNS-over-HTTPS (DoH), and batch resolution.

## Dependencies

- **dns2** (2.1.0) - Pure JavaScript DNS client and server implementation
- **chalk** (4.1.2) - Terminal string styling for colored output

## Features

- DNS lookups for A, AAAA, MX, NS, TXT, CNAME, SOA, PTR, SRV, and CAA record types
- Reverse DNS lookups for IPv4 and IPv6 addresses
- DNS-over-HTTPS (DoH) queries via Google, Cloudflare, and Quad9
- Batch resolution of multiple domains
- Mixed batch queries with different record types per domain
- Colored terminal output via chalk
- JSON-formatted output

## Setup

```bash
npm install
```

## Usage

```bash
node resolver.js
```

The script demonstrates all resolver capabilities including standard lookups, reverse DNS, DoH queries, and batch resolution.
resolver.js
/**
 * DNS Resolver - A comprehensive DNS lookup tool supporting multiple record types,
 * reverse DNS lookups, DNS-over-HTTPS (DoH), and batch resolution.
 *
 * Uses dns2 for DNS operations and chalk for colored output.
 */

const dns2 = require('dns2');
const chalk = require('chalk');
const https = require('https');
const http = require('http');

// Supported record types
const SUPPORTED_RECORD_TYPES = [
    'A', 'AAAA', 'MX', 'NS', 'TXT', 'CNAME', 'SOA', 'PTR', 'SRV', 'CAA'
];

// DNS-over-HTTPS providers
const DOH_PROVIDERS = {
    google: 'https://dns.google/resolve',
    cloudflare: 'https://cloudflare-dns.com/dns-query',
    quad9: 'https://dns.quad9.net/dns-query',
};

class DnsResolver {
    /**
     * Create a new DnsResolver.
     * @param {Object} options
     * @param {string} [options.nameserver] - DNS server IP address
     * @param {number} [options.port] - DNS server port (default: 53)
     * @param {number} [options.timeout] - Query timeout in milliseconds
     */
    constructor(options = {}) {
        this.nameserver = options.nameserver || '8.8.8.8';
        this.port = options.port || 53;
        this.timeout = options.timeout || 10000;

        this.client = new dns2({ nameServers: [this.nameserver] });
    }

    /**
     * Perform a DNS lookup for a specific record type.
     * @param {string} domain - The domain to look up
     * @param {string} recordType - The DNS record type
     * @returns {Promise<Object>} The lookup result
     */
    async lookup(domain, recordType = 'A') {
        recordType = recordType.toUpperCase();
        const result = {
            domain,
            record_type: recordType,
            records: [],
            nameserver: this.nameserver,
        };

        if (!SUPPORTED_RECORD_TYPES.includes(recordType)) {
            result.error = `Unsupported record type: ${recordType}. Supported: ${SUPPORTED_RECORD_TYPES.join(', ')}`;
            return result;
        }

        try {
            const dnsType = dns2.Packet.TYPE[recordType];
            if (dnsType === undefined) {
                result.error = `Record type ${recordType} not supported by dns2`;
                return result;
            }

            const response = await this.client.resolveA(domain);
            // Use raw query for full record type support
            const packet = new dns2.Packet();
            packet.header.id = Math.floor(Math.random() * 65535);
            packet.header.rd = 1;
            packet.questions.push({
                name: domain,
                type: dnsType,
                class: dns2.Packet.CLASS.IN,
            });

            const udp = new dns2.UDPClient();
            const dnsResponse = await udp.send(this.nameserver, this.port, packet.toBuffer());
            const parsed = dns2.Packet.parse(dnsResponse);

            if (parsed.header.rcode === 3) {
                result.error = `Domain '${domain}' does not exist (NXDOMAIN)`;
                return result;
            }

            if (parsed.answers.length === 0) {
                result.error = `No ${recordType} records found for '${domain}'`;
                return result;
            }

            result.ttl = parsed.answers[0].ttl;

            for (const answer of parsed.answers) {
                const record = this._parseAnswer(answer);
                result.records.push(record);
            }
        } catch (err) {
            // Fallback: use simpler dns2 API methods
            try {
                const response = await this._simpleLookup(domain, recordType);
                if (response.answers && response.answers.length > 0) {
                    result.ttl = response.answers[0].ttl;
                    for (const answer of response.answers) {
                        result.records.push(this._parseAnswer(answer));
                    }
                } else {
                    result.error = `No ${recordType} records found for '${domain}'`;
                }
            } catch (fallbackErr) {
                result.error = fallbackErr.message || String(fallbackErr);
            }
        }

        return result;
    }

    /**
     * Simple lookup using the dns2 resolve method.
     */
    async _simpleLookup(domain, recordType) {
        const resolve = new dns2({ nameServers: [this.nameserver] });
        return resolve.resolve(domain, recordType);
    }

    /**
     * Look up all supported record types for a domain.
     * @param {string} domain
     * @returns {Promise<Object>}
     */
    async lookupAll(domain) {
        const results = { domain, results: {} };
        for (const rtype of SUPPORTED_RECORD_TYPES) {
            const lookupResult = await this.lookup(domain, rtype);
            if (lookupResult.records && lookupResult.records.length > 0) {
                results.results[rtype] = lookupResult;
            }
        }
        return results;
    }

    /**
     * Perform a reverse DNS lookup for an IP address.
     * @param {string} ipAddress
     * @returns {Promise<Object>}
     */
    async reverseLookup(ipAddress) {
        const result = {
            ip_address: ipAddress,
            hostnames: [],
        };

        try {
            // Build PTR domain from IP
            const parts = ipAddress.split('.');
            if (parts.length === 4) {
                const ptrDomain = parts.reverse().join('.') + '.in-addr.arpa';
                result.ptr_domain = ptrDomain;

                const lookupResult = await this.lookup(ptrDomain, 'PTR');
                if (lookupResult.error) {
                    result.error = lookupResult.error;
                } else {
                    result.ttl = lookupResult.ttl;
                    for (const rec of lookupResult.records) {
                        if (rec.target) {
                            result.hostnames.push(rec.target);
                        } else if (rec.data) {
                            result.hostnames.push(rec.data);
                        }
                    }
                }
            } else {
                // IPv6 reverse lookup
                const expanded = this._expandIPv6(ipAddress);
                const nibbles = expanded.replace(/:/g, '').split('').reverse().join('.');
                const ptrDomain = nibbles + '.ip6.arpa';
                result.ptr_domain = ptrDomain;

                const lookupResult = await this.lookup(ptrDomain, 'PTR');
                if (lookupResult.error) {
                    result.error = lookupResult.error;
                } else {
                    result.ttl = lookupResult.ttl;
                    for (const rec of lookupResult.records) {
                        if (rec.target) {
                            result.hostnames.push(rec.target);
                        }
                    }
                }
            }
        } catch (err) {
            result.error = err.message || String(err);
        }

        return result;
    }

    /**
     * Perform a DNS lookup using DNS-over-HTTPS (DoH) via JSON API.
     * @param {string} domain
     * @param {string} recordType
     * @param {string} provider
     * @returns {Promise<Object>}
     */
    async dohLookup(domain, recordType = 'A', provider = 'google') {
        recordType = recordType.toUpperCase();
        const dohUrl = DOH_PROVIDERS[provider.toLowerCase()] || provider;

        const result = {
            domain,
            record_type: recordType,
            provider,
            doh_url: dohUrl,
            records: [],
        };

        try {
            // Use JSON API (Google DNS format)
            const typeCode = dns2.Packet.TYPE[recordType] || 1;
            const url = `${dohUrl}?name=${encodeURIComponent(domain)}&type=${recordType}`;

            const responseData = await this._httpsGet(url, {
                'Accept': 'application/dns-json',
            });

            const json = JSON.parse(responseData);

            if (json.Status === 3) {
                result.error = `Domain '${domain}' does not exist (NXDOMAIN)`;
                return result;
            }

            if (!json.Answer || json.Answer.length === 0) {
                result.error = `No ${recordType} records in DoH response`;
                return result;
            }

            result.ttl = json.Answer[0].TTL;

            for (const answer of json.Answer) {
                const record = {
                    type: this._typeCodeToString(answer.type),
                    data: answer.data,
                };

                // Parse specific record types
                if (answer.type === 1 || answer.type === 28) {
                    record.address = answer.data;
                } else if (answer.type === 15) {
                    const parts = answer.data.split(' ');
                    record.preference = parseInt(parts[0], 10);
                    record.exchange = parts[1];
                } else if (answer.type === 2) {
                    record.nameserver = answer.data;
                }

                result.records.push(record);
            }
        } catch (err) {
            result.error = `DoH lookup error: ${err.message || err}`;
        }

        return result;
    }

    /**
     * Resolve multiple domains in batch.
     * @param {string[]} domains
     * @param {string} recordType
     * @returns {Promise<Object[]>}
     */
    async batchResolve(domains, recordType = 'A') {
        const results = [];
        for (const domain of domains) {
            const trimmed = domain.trim();
            if (trimmed) {
                const lookupResult = await this.lookup(trimmed, recordType);
                results.push(lookupResult);
            }
        }
        return results;
    }

    /**
     * Resolve a batch of mixed queries.
     * @param {Array<{domain: string, record_type?: string}>} queries
     * @returns {Promise<Object[]>}
     */
    async batchResolveMixed(queries) {
        const results = [];
        for (const query of queries) {
            const domain = (query.domain || '').trim();
            const recordType = (query.record_type || 'A').trim().toUpperCase();
            if (domain) {
                const lookupResult = await this.lookup(domain, recordType);
                results.push(lookupResult);
            }
        }
        return results;
    }

    /**
     * Parse a DNS answer into a structured object.
     */
    _parseAnswer(answer) {
        const record = {
            type: this._typeCodeToString(answer.type),
            name: answer.name,
            ttl: answer.ttl,
        };

        if (answer.address) {
            record.address = answer.address;
        }
        if (answer.data) {
            record.data = answer.data;
        }
        if (answer.exchange) {
            record.exchange = answer.exchange;
            record.preference = answer.priority || answer.preference;
        }
        if (answer.ns) {
            record.nameserver = answer.ns;
        }
        if (answer.target) {
            record.target = answer.target;
        }
        if (answer.primary) {
            record.mname = answer.primary;
            record.rname = answer.admin;
            record.serial = answer.serial;
            record.refresh = answer.refresh;
            record.retry = answer.retry;
            record.expire = answer.expire;
            record.minimum = answer.minimum;
        }

        return record;
    }

    /**
     * Convert DNS type code to string.
     */
    _typeCodeToString(code) {
        const types = {
            1: 'A', 2: 'NS', 5: 'CNAME', 6: 'SOA', 12: 'PTR',
            15: 'MX', 16: 'TXT', 28: 'AAAA', 33: 'SRV', 257: 'CAA',
        };
        return types[code] || `TYPE${code}`;
    }

    /**
     * Expand an IPv6 address to full form.
     */
    _expandIPv6(ip) {
        const groups = ip.split(':');
        const expanded = [];
        for (const group of groups) {
            if (group === '') {
                const missing = 8 - groups.filter(g => g !== '').length;
                for (let i = 0; i <= missing; i++) {
                    expanded.push('0000');
                }
            } else {
                expanded.push(group.padStart(4, '0'));
            }
        }
        return expanded.join(':');
    }

    /**
     * Make an HTTPS GET request.
     */
    _httpsGet(url, headers = {}) {
        return new Promise((resolve, reject) => {
            const urlObj = new URL(url);
            const client = urlObj.protocol === 'https:' ? https : http;

            const req = client.get(url, { headers, timeout: this.timeout }, (res) => {
                let data = '';
                res.on('data', chunk => data += chunk);
                res.on('end', () => {
                    if (res.statusCode >= 200 && res.statusCode < 300) {
                        resolve(data);
                    } else {
                        reject(new Error(`HTTP ${res.statusCode}: ${data}`));
                    }
                });
            });

            req.on('error', reject);
            req.on('timeout', () => {
                req.destroy();
                reject(new Error('Request timed out'));
            });
        });
    }
}

// ---- Output Formatting ----

function printHeader(text) {
    console.log(chalk.bold.blue(`\n${text}`));
    console.log(chalk.gray('-'.repeat(40)));
}

function printResult(result) {
    const domain = result.domain || 'N/A';
    const rtype = result.record_type || 'N/A';
    console.log(chalk.bold.yellow(`\n--- ${domain} (${rtype}) ---`));

    if (result.error) {
        console.log(chalk.red(`  Error: ${result.error}`));
        return;
    }

    if (result.ttl !== undefined) {
        console.log(chalk.gray(`  TTL: ${result.ttl}`));
    }

    for (const rec of (result.records || [])) {
        const type = rec.type || '';
        if (type === 'A' || type === 'AAAA') {
            console.log(chalk.green(`  ${type}: ${rec.address || rec.data}`));
        } else if (type === 'MX') {
            console.log(chalk.green(`  MX: ${rec.exchange || rec.data} (priority: ${rec.preference})`));
        } else if (type === 'NS') {
            console.log(chalk.green(`  NS: ${rec.nameserver || rec.data}`));
        } else if (type === 'TXT') {
            console.log(chalk.green(`  TXT: ${rec.data}`));
        } else if (type === 'CNAME') {
            console.log(chalk.green(`  CNAME: ${rec.target || rec.data}`));
        } else if (type === 'SOA') {
            console.log(chalk.green(`  SOA: mname=${rec.mname}, serial=${rec.serial}`));
        } else if (type === 'PTR') {
            console.log(chalk.green(`  PTR: ${rec.target || rec.data}`));
        } else if (type === 'SRV') {
            console.log(chalk.green(`  SRV: ${rec.target}:${rec.port} (priority=${rec.priority}, weight=${rec.weight})`));
        } else {
            console.log(chalk.green(`  ${type}: ${JSON.stringify(rec)}`));
        }
    }
}

// ---- Main ----

async function main() {
    console.log(chalk.bold.cyan('='.repeat(60)));
    console.log(chalk.bold.cyan('  DNS Resolver - Comprehensive DNS Lookup Tool (JavaScript)'));
    console.log(chalk.bold.cyan('='.repeat(60)));

    const resolver = new DnsResolver({ nameserver: '8.8.8.8' });

    // 1. Standard DNS lookups
    printHeader('[1] Standard DNS Lookups');
    const testDomain = 'google.com';
    for (const rtype of ['A', 'AAAA', 'MX', 'NS', 'TXT', 'SOA']) {
        const result = await resolver.lookup(testDomain, rtype);
        printResult(result);
    }

    // 2. All record types
    printHeader('[2] All Record Types for "example.com"');
    const allResults = await resolver.lookupAll('example.com');
    for (const [rtype, res] of Object.entries(allResults.results)) {
        printResult(res);
    }

    // 3. Reverse DNS
    printHeader('[3] Reverse DNS Lookup');
    for (const ip of ['8.8.8.8', '1.1.1.1']) {
        const revResult = await resolver.reverseLookup(ip);
        console.log(chalk.yellow(`\n  IP: ${revResult.ip_address}`));
        if (revResult.error) {
            console.log(chalk.red(`  Error: ${revResult.error}`));
        } else {
            console.log(chalk.gray(`  PTR domain: ${revResult.ptr_domain}`));
            for (const hostname of revResult.hostnames) {
                console.log(chalk.green(`  Hostname: ${hostname}`));
            }
        }
    }

    // 4. DNS-over-HTTPS
    printHeader('[4] DNS-over-HTTPS (DoH)');
    for (const provider of ['google']) {
        const dohResult = await resolver.dohLookup('example.com', 'A', provider);
        console.log(chalk.yellow(`\n  Provider: ${provider} (${dohResult.doh_url})`));
        if (dohResult.error) {
            console.log(chalk.red(`  Error: ${dohResult.error}`));
        } else {
            for (const rec of dohResult.records) {
                console.log(chalk.green(`  ${rec.type}: ${rec.address || rec.data}`));
            }
        }
    }

    // 5. Batch resolution
    printHeader('[5] Batch Resolution');
    const batchDomains = ['google.com', 'github.com', 'stackoverflow.com', 'python.org', 'example.com'];
    const batchResults = await resolver.batchResolve(batchDomains, 'A');
    for (const res of batchResults) {
        if (res.error) {
            console.log(chalk.red(`  ${res.domain}: ${res.error}`));
        } else {
            const addresses = res.records.map(r => r.address || r.data).join(', ');
            console.log(chalk.green(`  ${res.domain}: ${addresses}`));
        }
    }

    // 6. Batch mixed queries
    printHeader('[6] Batch Mixed Queries');
    const mixedQueries = [
        { domain: 'google.com', record_type: 'A' },
        { domain: 'google.com', record_type: 'MX' },
        { domain: 'github.com', record_type: 'AAAA' },
        { domain: 'example.com', record_type: 'NS' },
    ];
    const mixedResults = await resolver.batchResolveMixed(mixedQueries);
    for (const res of mixedResults) {
        printResult(res);
    }

    // 7. JSON output
    printHeader('[7] JSON Output (google.com A record)');
    const jsonResult = await resolver.lookup('google.com', 'A');
    console.log(JSON.stringify(jsonResult, null, 2));

    console.log(chalk.bold.cyan('\n' + '='.repeat(60)));
    console.log(chalk.bold.cyan('  DNS Resolution Complete'));
    console.log(chalk.bold.cyan('='.repeat(60)));
}

main().catch(err => {
    console.error(chalk.red('Fatal error:'), err);
    process.exit(1);
});

module.exports = { DnsResolver };