dns2 Documentation

repository·master·Indexed 20 days ago

https://github.com/lsongdev/node-dns

A pure JavaScript implementation of a DNS Server and Client with no external dependencies. It supports UDP, TCP, and DNS-over-HTTPS (DoH). The library provides a high-level DNS class for resolving common record types (A, AAAA, MX, CNAME, PTR, DNSKEY, RRSIG, SOA) and factory methods to create specialized servers and clients. It includes features for handling packet decoding errors, managing server concurrency via maxConcurrent, and built-in UDP throughput benchmarking.

Tokens
6.4K
Snippets
27
Records
34
Agent score
68%

What's inside dns2

  1. Handle packet decoding errors

    master

    When parsing packets, two types of errors can occur:

    1. Packet.DecodeError: Thrown by Packet.parse(buffer) if the message is fundamentally unreadable (e.g., shorter than the 12-octet header).
    2. Malformed Records: If the header is valid but specific records are malformed, Packet.parse does not throw. Instead, it drops the malformed records and populates packet.errors.

    For each error in packet.errors, you can inspect:

    • err.section: 'questions' | 'answers' | 'authorities' | 'additionals'
    • err.index: position within the section
    • err.offset: octet offset in the message
    • err.recovered: true if decoding continued after the error; false if the error caused misalignment and decoding stopped.
    try {
      Packet.parse(buffer);
    } catch (err) {
      // Handles undecodable headers
      console.error(err.message);
    }
    
    const packet = Packet.parse(buffer);
    if (packet.errors.length > 0) {
      for (const err of packet.errors) {
        console.error(err.message);
        console.log(err.section, err.index, err.offset, err.recovered);
      }
    }
  2. Run UDP throughput benchmarks

    master

    You can run a built-in UDP throughput benchmark that starts an in-process echo server, fires queries, and reports Requests Per Second (RPS) and latency percentiles.

    Use the following environment variables to tune the benchmark:

    • TOTAL: The total number of queries to send.
    • CONCURRENCY: The number of concurrent requests.
    • DNS: The address of an external DNS server to target (format IP:PORT).
    # Standard benchmark
    node benchmark/udp.js
    
    # Tune total queries and concurrency
    TOTAL=50000 CONCURRENCY=200 node benchmark/udp.js
    
    # Target an external server
    DNS=127.0.0.1:5353 node benchmark/udp.js
  3. Optimize Node.js performance for dns2

    master

    To achieve higher performance and stability under high DNS load, consider the following Node.js tuning strategies:

    • Multi-core utilization: Use the built-in cluster module to run multiple worker processes across all available CPU cores.
    • V8 Memory Tuning: Increase the V8 new-space size to reduce the frequency of minor Garbage Collection (GC) cycles: node --max-semi-space-size=64 server.js.
    • Process Management: In production environments, use a process manager like PM2 or systemd to handle automatic restarts on failure and manage multi-instance clustering.
  4. Use the UDPClient helper

    master

    A shorthand way to instantiate a UDP client is using the UDPClient factory function.

    const { UDPClient } = require('dns2');
    
    const resolve = UDPClient();
    
    (async () => {
      const response = await resolve('google.com');
      console.log(response.answers);
    })();
  5. Create a DNS Server

    master

    You can create a DNS server using dns2.createServer(). You can specify udp and tcp configurations (port and address) and provide a handle function to process requests. The handle function receives (request, send, rinfo).

    const dns2 = require('dns2');
    const { Packet } = dns2;
    
    const server = dns2.createServer({
      udp: true,
      handle: (request, send, rinfo) => {
        const response = Packet.createResponseFromRequest(request);
        const [question] = request.questions;
        const { name } = question;
        
        response.answers.push({
          name,
          type: Packet.TYPE.A,
          class: Packet.CLASS.IN,
          ttl: 300,
          address: '8.8.8.8',
        });
        send(response);
      },
    });
    
    server.listen({
      udp: { port: 5333, address: '127.0.0.1' },
      tcp: { port: 5333, address: '127.0.0.1' },
    });
  6. Respond with DNS Error Codes (RCODE)

    master

    When building a server, use Packet.RCODE to signal errors in your response. For error codes above 15 (like BADVERS or BADSIG), you must use Packet.createErrorResponseFromRequest to ensure the high byte is correctly carried in an OPT record.

    // Example: Refusing internal domains
    const response = Packet.createResponseFromRequest(request);
    if (question.name.endsWith('.internal')) {
      response.header.rcode = Packet.RCODE.REFUSED;
      return send(response);
    }
  7. Limit server concurrency with `maxConcurrent`

    master

    When using dns2.createServer, you can use the maxConcurrent option to cap the number of handler invocations in flight simultaneously.

    Behavior:

    • If the maxConcurrent limit is reached, incoming requests receive an immediate SERVFAIL response instead of being queued.
    • Critical Requirement: Handlers must always call the send() function. The active-request counter only decrements when send() is invoked. If a handler fails to call send(), it will permanently occupy a concurrency slot, eventually leading to a denial of service as slots are exhausted.
    const server = dns2.createServer({
      udp: true,
      maxConcurrent: 500, // at most 500 handler calls in flight at once
      handle(request, send) {
        // async work here...
      },
    });
  8. Use the DNS Client (UDP)

    master

    You can use the dns2 class to perform DNS lookups. By default, the client uses UDP. You can configure nameServers (array of IPs), port, recursive (boolean), and timeout (ms). Every name server is queried in parallel, and the first successful reply wins.

    const dns2 = require('dns2');
    
    const options = {
      // nameServers: ['8.8.8.8']
      // port: 53
      // recursive: true
      // timeout: 3000
    };
    const dns = new dns2(options);
    
    (async () => {
      const result = await dns.resolveA('google.com');
      console.log(result.answers);
    })();
  9. Lookup common DNS records with convenience methods

    master

    The DNS class provides high-level methods for common record types. For any record type not listed, use dns.resolve(domain, 'TYPE').

    // Available convenience methods:
    // resolveA(domain)      -> A records (field: address)
    // resolveAAAA(domain)  -> AAAA records (field: address)
    // resolveMX(domain)    -> MX records (fields: exchange, priority)
    // resolveCNAME(domain) -> CNAME records (field: domain)
    // resolveSOA(domain)   -> SOA records (fields: primary, admin, serial, refresh, retry, expiration, minimum)
    // resolvePTR(domain)   -> PTR records (field: domain)
    // resolveDNSKEY(domain)-> DNSKEY records (fields: publicKey, algorithm)
    // resolveRRSIG(domain) -> RRSIG records (fields: varies)
    
    // Generic lookup:
    // await dns.resolve('example.com', 'TXT');
  10. Use the TCPClient for DNS over TCP

    master

    To perform lookups over TCP, use the TCPClient factory. You can optionally specify a custom DNS server via the dns option.

    const { TCPClient } = require('dns2');
    
    // Default TCP client
    const resolve = TCPClient();
    
    // TCP client with custom DNS server
    const resolveCustom = TCPClient({
      dns: '1.1.1.1',
    });
    
    (async () => {
      try {
        const response = await resolve('lsong.org');
        console.log(response.answers);
      } catch (error) {
        console.log(error);
      }
    })();
  11. Reference: DNS RCODE constants

    master

    Standard DNS error codes available via Packet.RCODE.

    Packet.RCODE.NOERROR    // 0
    Packet.RCODE.FORMERR    // 1
    Packet.RCODE.SERVFAIL   // 2
    Packet.RCODE.NXDOMAIN   // 3
    Packet.RCODE.NOTIMP    // 4
    Packet.RCODE.REFUSED    // 5
    Packet.RCODE.YXDOMAIN   // 6
    Packet.RCODE.YXRRSET   // 7
    Packet.RCODE.NXRRSET   // 8
    Packet.RCODE.NOTAUTH   // 9
    Packet.RCODE.NOTZONE   // 10
    Packet.RCODE.DSOTYPENI // 11
    Packet.RCODE.BADVERS   // 16
    Packet.RCODE.BADSIG    // 16
    Packet.RCODE.BADKEY    // 17
    Packet.RCODE.BADTIME   // 18
    Packet.RCODE.BADMODE   // 19
    Packet.RCODE.BADNAME   // 20
    Packet.RCODE.BADALG    // 21
    Packet.RCODE.BADTRUNC  // 22
    Packet.RCODE.BADCOOKIE // 23