Skip to content

NicTool/dns-resource-record

Repository files navigation

Module Tests Test Coverage

dns-resource-record

DNS resource record parser, validator, importer, and exporter for node.js & browsers

Quick Links: Getting Started | API Reference | Browser/CDN | Supported Records | Contributing

Overview

This module validates, imports, and exports DNS resource records (RRs) with rigorous RFC compliance and robust test coverage.

What you can do:

  • Validate DNS records for RFC compliance and well-formedness
  • Parse/Import records from BIND zone files, tinydns data files, or JS objects
  • Export records to BIND, tinydns, MaraDNS, JSON, or wire format
  • Manipulate validated records with type-safe setters
  • Convert between formats seamlessly (e.g., tinydns → BIND -> wire)

Scope: This package handles individual Resource Records. For zone file operations, see dns-zone.

Quality: RFC compliance is a first-class concern. If you encounter a valid RR that fails to parse or an invalid RR that passes validation, please open an issue.

Supported Records

This module supports all current DNS RRs in active use on the internet.

Usage Resource Record Types
Common A, AAAA, CNAME, HTTPS, MX, NS, PTR, SOA, SRV, TXT
Less Common APL, CERT, DHCID, DNAME, HIP, KX, LOC, NAPTR, SVCB, TSIG, URI
Security CAA, IPSECKEY, OPENPGPKEY, SMIMEA, SSHFP, TLSA
DNSSEC DNSKEY, DS, NSEC, NSEC3, NSEC3PARAM, RRSIG
Obsolete HINFO, KEY, NXT, RP, SIG, SPF, WKS

Getting Started

Installation

npm install @nictool/dns-resource-record

Validation

Validate an A record:

import * as RR from '@nictool/dns-resource-record'

const validA = new RR.A({
  owner: 'example.com.',
  address: '192.0.2.1',
  ttl: 3600,
})

console.log(validA.toBind())
// example.com          3600    IN  A   192.0.2.1

Invalid records throw immediately:

try {
  new RR.A({ owner: 'example.com.', address: 'not-an-ip' })
} catch (err) {
  console.error(err.message) // Error: A address must be IPv4
}

This library enforces RFC-compliance during construction, catching errors early.


API Reference

Understanding Record Structure

Every DNS record has these standard fields:

  • owner — The domain name this record belongs to (always fully qualified, e.g., example.com.)
  • type — The record type (A, AAAA, MX, TXT, etc.)
  • ttl — Time to live in seconds
  • class — Almost always IN (Internet); omitted in examples

Plus rdata fields specific to each record type (e.g., address for A records, preference/exchange for MX records).

Core Workflows

1. Validate & Create — Use the Constructor

When you have data in JavaScript object form:

new RR.SOA({
  owner: 'example.com.',
  mname: 'ns1.example.com.',
  rname: 'hostmaster.example.com.',
  serial: 2024042201,
  refresh: 7200,
  retry: 3600,
  expire: 1209600,
  minimum: 3600,
  ttl: 3600,
})

When to use: Building records programmatically, validating user input, working with API responses.


2. Parse BIND Format — Use fromBind()

When you have a line from a BIND zone file:

const rr = RR.A.fromBind('www.example.com.  3600  IN  A  192.0.2.1\n')
console.log(rr.get('address')) // '192.0.2.1'

When to use: Processing BIND zone files, DNS configs, or standard zone file exports.


3. Parse Tinydns Format — Use fromTinydns()

When you have a line from a tinydns data file:

const rr = RR.A.fromTinydns('+www.example.com:192.0.2.1:3600::\n')
console.log(rr.get('address')) // '192.0.2.1'

When to use: Working with djbdns/tinydns configurations, or when parsing tinydns data files.


4. Export to BIND — Use toBind()

Convert any validated record to BIND zone file format:

new RR.MX({
  owner: 'example.com.',
  preference: 10,
  exchange: 'mail.example.com.',
  ttl: 3600,
}).toBind()
// example.com          3600    IN  MX  10 mail.example.com.

When to use: Generating zone files, displaying records for editing, exporting to BIND nameservers.


5. Export to Tinydns — Use toTinydns()

Convert to tinydns data file format:

new RR.A({
  owner: 'example.com.',
  address: '192.0.2.1',
  ttl: 3600,
}).toTinydns()
// +example.com:192.0.2.1:3600::

When to use: Generating tinydns data files, migrating to djbdns, or integrating with tinydns tooling.


6. Round-Trip Conversion

Parse from one format, export to another:

// tinydns → BIND
const fromTiny = RR.CAA.fromTinydns(
  ':ns1.example.com:257:\\000\\005issue"http\\072\\057\\057letsencrypt.org":3600::\n',
)
console.log(fromTiny.toBind())
// ns1.example.com 3600    IN  CAA 0   issue   "http://letsencrypt.org"

When to use: DNS migrations, format conversions, tool interoperability.


Manipulating Records

Use setter methods to modify validated records. Setter names follow the pattern set + FieldName (camelCased):

const a = new RR.A({
  owner: 'example.com.',
  address: '192.0.2.1',
  ttl: 3600,
})

a.setAddress('192.0.2.2')
a.setTtl(7200)

console.log(a.toBind())
// example.com          7200    IN  A   192.0.2.2

Setters include validation. Invalid values throw with helpful error messages.

For a list of available setters, check getFields('rdata') for your record type:

new RR.SSHFP(null).getFields('rdata')
// ['algorithm', 'fptype', 'fingerprint']
// So use: setSshfp(), setFptype(), setFingerprint()

Metadata Methods

Get information about record types:

getFields([section])

Get field names for a record type. Pass 'rdata' to get only the custom fields:

new RR.A(null).getFields() // ['owner', 'ttl', 'class', 'type', 'address']
new RR.A(null).getFields('rdata') // ['address']
new RR.MX(null).getFields('rdata') // ['preference', 'exchange']

getRFCs()

Get RFC references for a record type:

new RR.A(null).getRFCs() // [1035]
new RR.MX(null).getRFCs() // [1035, 2181, 7505]
new RR.SRV(null).getRFCs() // [2782]

getCanonical()

Get a record with all fields populated with default/canonical values:

new RR.AAAA(null).getCanonical()
// {
//   owner: 'host.example.com.',
//   address: '2001:0db8:0020:000a:0000:0000:0000:0004',
//   class: 'IN',
//   ttl: 3600,
//   type: 'AAAA'
// }

toJSON() / JSON.stringify()

Serialize a record to a plain object:

const a = new RR.A({ owner: 'example.com.', address: '192.0.2.1', ttl: 3600 })
JSON.stringify(a)
// {"owner":"example.com.","ttl":3600,"class":"IN","type":"A","address":"192.0.2.1"}

Advanced: Bypassing Validation

The set() method bypasses validation (use carefully):

record.set('address', 'invalid-value')
// Works, but now the record is malformed — useful only for testing or low-level manipulation

Note: Validation is there to protect data integrity. Use at your own risk.


Browser & CDN

Use this module directly in browsers via CDN:

<!DOCTYPE html>
<html>
  <head>
    <title>DNS Record Validator</title>
  </head>
  <body>
    <h1>DNS Record Validator</h1>
    <pre id="output"></pre>

    <script type="module">
      // Import from unpkg (or jsdelivr, esm.sh)
      import * as RR from 'https://unpkg.com/@nictool/dns-resource-record@latest/dist/dns-rr.min.js'

      // Use just like Node.js
      const record = new RR.A({
        owner: 'example.com.',
        address: '192.0.2.1',
        ttl: 3600,
      })

      document.getElementById('output').textContent = record.toBind()
    </script>
  </body>
</html>

Available CDNs:

  • unpkg: https://unpkg.com/@nictool/dns-resource-record/dist/dns-rr.min.js
  • jsdelivr: https://cdn.jsdelivr.net/npm/@nictool/dns-resource-record/dist/dns-rr.min.js
  • esm.sh: https://esm.sh/@nictool/dns-resource-record

Live example: See nictool.github.io/builder/ for an interactive DNS record editor using this library.


Important Concepts

Domain Name Normalization

Domain owner names are:

  • Stored fully qualified (absolute), e.g., example.com. not example.com
  • Normalized to lowercase because:
    • DNS is case-insensitive (RFC 4343, RFC 1035)
    • This library enforces automatic duplicate suppression
    • DNSSEC canonicalization requires it (RFC 4034)
    • Wire format for most RRs requires lowercase

Example:

new RR.A({ owner: 'EXAMPLE.COM', address: '192.0.2.1', ttl: 3600 })
// Automatically normalized to: 'example.com.'

Relative vs Absolute Names

Master zone file expansions (relative domain names) are handled by dns-zone. This library works only with fully qualified names.

Export Options

The toBind() and toMaraDNS() methods accept an options object to customize output:

record.toBind({ suppressTtl: true, suppressClass: true, relativeName: true })

See dns-zone for full options documentation.

Development

No external dependencies. Runs on node.js and modern browsers.

Key scripts:

  • npm test — Run all tests with node's built-in test runner
  • npm run watch — Run tests in watch mode during development
  • npm run lint — Check code with ESLint
  • npm run format — Auto-format with Prettier and fix linting issues
  • npm run build — Regenerate browser bundle and README
  • npm run test:coverage — Generate test coverage report

Architecture:

  • rr.js — Base class with shared DNS logic and validation
  • rr/*.js — Individual RR type implementations
  • test/*.js — Comprehensive test suite for each RR type

References


Sponsor this project

 

Contributors