digd.js
=======

| [dns-suite.js](https://git.coolaj86.com/coolaj86/dns-suite.js)
| [dig.js](https://git.coolaj86.com/coolaj86/dig.js)
| [mdig.js](https://git.coolaj86.com/coolaj86/mdig.js)
| **digd.js**
| Sponsored by [Daplie](https://daplie.com).

A lightweight DNS / mDNS daemon (server) in node.js.

Although originally created for testing dns-suite.js
by creating and capturing to disk DNS and mDNS query
and response packets (as binary and/or JSON), digd.js
has grown into a full-blown nameserver.

Options are similar to the Unix dig command.

Install
-------

### systemd service

```bash
curl -L https://git.coolaj86.com/coolaj86/digd.js/raw/v1.2/install.sh | bash
```

### with git

```bash
# Install the latest of v1.x
npm install -g 'git+https://git.coolaj86.com/coolaj86/digd.js.git#v1'
```

```bash
# Install exactly v1.2.0
npm install -g 'git+https://git.coolaj86.com/coolaj86/digd.js.git#v1.2.0'
```

### without git

Don't have git? Well, you can also bow down to the gods of the centralized, monopolized, concentrated, *dictator*net
(as we like to call it here at Daplie Labs), if that's how you roll:

```bash
npm install -g digd.js
```

Usage
-----

```bash
digd.js --input <path/to/dns.json>
```

**Example**:

```bash
digd.js --input ./samples/db.json
```

### Testing

```bash
# unix dig
dig @localhost example.com

# dns-suite's dig.js
dig.js @localhost example.com

# unix netcat
netcat -u 127.0.0.1 53 < ./samples/example.com.a.query.bin
```

Options
-------

```
--output <path/to/file>     write query and response(s) to disk with this path prefix (ex: ./samples/dns)
--input <path/to/file>      input file to use for authoritative responses (ex: ./samples/db.json)

--mdns                      Use mDNS port (5353) and nameserver address (224.0.0.251)

-p <port>                   default 53 (mdns default: 5353) (listener is random for DNS and 5353 for mDNS)
--nameserver <ns>           alias of @<nameserver>
--timeout <ms>              alias of +time=<seconds>, but in milliseconds

@<nameserver>               specify the nameserver to use for recursive DNS resolutions (defaults to system defaults)
+time=<seconds>             Sets the timeout for a query in seconds.
+norecurse                  Set `ra` flag to 0. Do not perform recursion.
+aaonly                     Set `aa` flag to 1. Do not respond with non-authoritative responses.
+notcp                      Disable TCP server (default in v1.2)
+tcp                        Enable TCP server (default in v1.3)

--debug                     verbose output
```

JSON Database File
------------------

This DNS server is being created for use in the wild.
Although there will be a true database adapter later,
this JSON representation gives us an easy way to experiment with serving DNS and various record types.

There are 4 types of information in the file:

* Primary Nameservers `primaryNameservers`
* SOA Records `domains`
* devices
* All other records (A, AAAA, CAA, CNAME, MX, NS, PTR, SPF, SRV, TXT)

```js
module.exports = {
  primaryNameservers: [ 'ns1.example.com', 'ns2.example.com' ]

  // SOA records
, domains: [
    // `primary` is chosen at random from `primaryNameservers` or `vanityNs`
    // `serial` is generated from `updatedAt`

    { id: "publicsuffix.net", updatedAt: 1507594095118, ttl: 60
    , admin: 'admin.publicsuffix.net', refresh: 1800, retry: 600
    , expiration: 2419200, minimum: 5 }

  , { id: "doe.publicsuffix.net", updatedAt: 1507594095118, ttl: 60
    , admin: 'admin.doe.publicsuffix.net', refresh: 1800, retry: 600
    , expiration: 2419200, minimum: 5 }


    // default values will be used when left undefined
  , { id: "doefam.net", updatedAt: 1507594095118
    , vanityNs: [ 'ns1.awesome.com', 'ns2.awesome.com' ] }
  ]
, records: [
    //
    // Plain old boring A Records
    //
    { name: "publicsuffix.net", zone: "publicsuffix.net"
    , tld: "net", sld: "publicsuffix", sub: ""
    , type: 'A', ttl: 300, address: '127.0.0.1' }

    { name: "www.publicsuffix.net", zone: "publicsuffix.net"
    , tld: "net", sld: "publicsuffix", sub: "www"
    , type: 'A', ttl: 300, address: '127.0.0.1' }

    //
    // Subdomain Delegation of a public suffix (treated as TLD)
    //
    { name: "jane.doe.publicsuffix.net", zone: "doe.publicsuffix.net"
    , tld: "publicsuffix.net", sld: "doe", sub: "john"
    , type: 'NS', ttl: 300, data: 'ns1.other-dns.net'
    }

    //
    // Example of all other record types
    //
    { name: "john.doe.publicsuffix.net"

      // The zone / SOA it belongs to (keep in mind that subdomains can be delegated to other users and/or nameservers)
    , zone: "doe.publicsuffix.net"

      // For indexing (note that we can treat delegated subdomains as if they were TLDs for delegation and resale)
    , tld: "publicsuffix.net"
    , sld: "doe"
    , sub: "john"

    , type: 'A'           // for this example we specify a type even though we show all of the record data
    , class: 'IN'         // (default)
    , ttl: 300

      // A, AAAA
    , address: '127.0.0.1'
    , aname: 'some-device.example.com' // See "A Note on ANAMEs" below

      // CAA
    , flag: 0
    , tag: 'issue'
    , value: 'letsencrypt.org'

      // CNAME, NS, PTR put 'name' here
      // TXT puts an array here
    , data: 'a.example.com'

      // MX, SRV
    , priority: 10

      // MX
    , exchange: 'mxa.example.org'

      // SRV
    , weight: 20
    , port: 65065
    , target: 'laptop1.devices.example.com'
    }
  ]
};
```

The **Primary Nameservers** should be all of the nameservers that are in sync for these collections of records.

The **SOA** records represent that a domain or subdomain has be registered to or delegated to these nameservers.
The SOA records are separate from other record types because they are automatically generated as part of registering
a domain or updating its records.

The **other records** are in their own table for easy and fast lookup.

The **devices** are an abstraction that will be used in the future for ANAMEs and Dynamic DNS.

Note: Because it's possible to that delegated subdomains could have delegated subdomains that go right back to the
original nameserver, **NS** records will be replaced with an SOA record if any of the NS records match any of
the server's primary nameservers or if vanity nameservers are used.

### A Note on ANAMES

ANAMEs serve two purposes in this system:

1. Traditional ANAME. Just a CNAME that is automatically resolved to an A record for the "bare domain" problem, and efficiency.
2. Dynamic DNS. When a record on the system is updated, any records that match it by ANAME are also updated

TODO: use dns0x20 for ANAME resolutions

Other Resources
---------------

You may also be interested in Unbound (https://unboundtest.com), which is an entirely different project by someone else
which is much more complete, written in go, and may be very useful for debugging and linting.

LICENSE
=======

You may, at your option, use this software under the MIT and/or Apache-2.0 licenses.
