NLnetLabs / dnsi

A tool to investigate the DNS.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

JSON output

paulehoffman opened this issue · comments

It would be really useful for the output to be in a JSON format, probably triggered with a command-line option.

Agreed. Especially since dig cannot produce json, but only yaml output format.

We agree that this should be added and I've started working on it. The problem is that there are many possible ways of representing DNS messages as JSON. Of course, there's RFC 8427, but that's not used by a lot of tools at the moment and it might not be what everyone expects.

The RFC is really good at representing malformed and incomplete messages and has the advantage of being standardized, but is in my opinion not very ergonomic. Therefore, I think we should settle on 2 formats: one based on the RFC for compatibility and one closer to tools like dog, which allows for easier querying with jq and other tools that can read JSON.

If you have ideas about what this second format should include, I'd love to hear it! Also, I'd love to hear about other applications that use RFC 8427, because I was only able to find one: kdig.

Overview of tools (warning: it's a lot of tools)

All outputs have been formatted by prettier to make them easier to read.

The arguments are all chosen to give roughly the same amount of information.

dog

https://github.com/ogham/dog

> dog google.com --json
{
  "responses": [
    {
      "queries": [{ "name": "nlnetlabs.nl.", "class": "IN", "type": "A" }],
      "answers": [
        {
          "name": "nlnetlabs.nl.",
          "class": "IN",
          "ttl": 240,
          "type": "A",
          "data": { "address": "185.49.140.10" }
        }
      ],
      "authorities": [],
      "additionals": []
    }
  ]
}

doggo

https://github.com/mr-karan/doggo

> doggo google.com A --json --strategy=first
[
  {
    "answers": [
      {
        "name": "nlnetlabs.nl.",
        "type": "A",
        "class": "IN",
        "ttl": "240s",
        "address": "185.49.140.10",
        "status": "",
        "rtt": "32ms",
        "nameserver": "84.116.46.21:53"
      }
    ],
    "authorities": null,
    "questions": [
      {
        "name": "nlnetlabs.nl.",
        "type": "A",
        "class": "IN"
      }
    ]
  }
]  

zdns

https://github.com/zmap/zdns

> echo "nlnetlabs.nl" | zdns A
{
  "data": {
    "additionals": [
      { "flags": "", "type": "EDNS0", "udpsize": 512, "version": 0 }
    ],
    "answers": [
      {
        "answer": "185.49.140.10",
        "class": "IN",
        "name": "nlnetlabs.nl",
        "ttl": 92,
        "type": "A"
      }
    ],
    "protocol": "udp",
    "resolver": "84.116.46.21:53"
  },
  "name": "nlnetlabs.nl",
  "status": "NOERROR",
  "timestamp": "2024-05-24T21:29:37+02:00"
}

q

https://github.com/natesales/q

> q nlnetlabs.nl A --format=json
[
  {
    "queries": [
      {
        "id": 26016,
        "response": false,
        "opcode": 0,
        "authoritative": false,
        "truncated": false,
        "recursiondesired": true,
        "recursionavailable": false,
        "zero": false,
        "authenticateddata": false,
        "checkingdisabled": false,
        "rcode": 0,
        "question": [{ "name": "nlnetlabs.nl.", "qtype": 1, "qclass": 1 }],
        "answer": null,
        "ns": null,
        "extra": null
      }
    ],
    "replies": [
      {
        "id": 26016,
        "response": true,
        "opcode": 0,
        "authoritative": false,
        "truncated": false,
        "recursiondesired": true,
        "recursionavailable": true,
        "zero": false,
        "authenticateddata": false,
        "checkingdisabled": false,
        "rcode": 0,
        "question": [{ "name": "nlnetlabs.nl.", "qtype": 1, "qclass": 1 }],
        "answer": [
          {
            "hdr": {
              "name": "nlnetlabs.nl.",
              "rrtype": 1,
              "class": 1,
              "ttl": 28,
              "rdlength": 4
            },
            "a": "185.49.140.10"
          }
        ],
        "ns": null,
        "extra": null
      }
    ],
    "server": "84.116.46.21:53",
    "time": 29517116
  }
]

dnsx

https://github.com/projectdiscovery/dnsx

> echo "nlnetlabs.nl" | dnsx --json
{
  "host": "nlnetlabs.nl",
  "ttl": 240,
  "resolver": ["1.0.0.1:53"],
  "a": ["185.49.140.10"],
  "all": [
    "nlnetlabs.nl.\t240\tIN\tA\t185.49.140.10",
    "\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 1232"
  ],
  "status_code": "NOERROR",
  "timestamp": "2024-05-24T21:34:57.37107916+02:00"
}

nu_plugin_dns (based on hickory)

https://github.com/dead10ck/nu_plugin_dns

> dns query nlnetlabs.nl A | to json
[
  {
    "header": {
      "id": 56629,
      "message_type": "RESPONSE",
      "op_code": "QUERY",
      "authoritative": false,
      "truncated": false,
      "recursion_desired": true,
      "recursion_available": true,
      "authentic_data": false,
      "response_code": "No Error",
      "query_count": 1,
      "answer_count": 2,
      "name_server_count": 0,
      "additional_count": 1
    },
    "question": {
      "name": "nlnetlabs.nl.",
      "type": "A",
      "class": "IN"
    },
    "answer": [
      {
        "name": "nlnetlabs.nl.",
        "type": "A",
        "class": "IN",
        "ttl": 240000000000,
        "rdata": "185.49.140.10"
      }
    ],
    "authority": [],
    "additional": [],
    "edns": {
      "rcode_high": 0,
      "version": 0,
      "dnssec_ok": true,
      "max_payload": 512,
      "opts": {}
    },
    "size": 57
  },
  {
    "header": {
      "id": 43255,
      "message_type": "RESPONSE",
      "op_code": "QUERY",
      "authoritative": false,
      "truncated": false,
      "recursion_desired": true,
      "recursion_available": true,
      "authentic_data": false,
      "response_code": "No Error",
      "query_count": 1,
      "answer_count": 2,
      "name_server_count": 0,
      "additional_count": 1
    },
    "question": {
      "name": "nlnetlabs.nl.",
      "type": "AAAA",
      "class": "IN"
    },
    "answer": [
      {
        "name": "nlnetlabs.nl.",
        "type": "AAAA",
        "class": "IN",
        "ttl": 224000000000,
        "rdata": "2a04:b900::1:0:0:10"
      }
    ],
    "authority": [],
    "additional": [],
    "edns": {
      "rcode_high": 0,
      "version": 0,
      "dnssec_ok": true,
      "max_payload": 512,
      "opts": {}
    },
    "size": 69
  }
]

kdig

This is an implementation of RFC 8427.

> kdig nlnetlabs.nl +json
{
  "dateString": "2024-05-25T14:32:48+0200",
  "dateSeconds": 1716640368,
  "msgLength": 46,
  "ID": 5142,
  "QR": 1,
  "Opcode": 0,
  "AA": 0,
  "TC": 0,
  "RD": 1,
  "RA": 1,
  "AD": 0,
  "CD": 0,
  "RCODE": 0,
  "QDCOUNT": 1,
  "ANCOUNT": 1,
  "NSCOUNT": 0,
  "ARCOUNT": 0,
  "QNAME": "nlnetlabs.nl.",
  "QTYPE": 1,
  "QTYPEname": "A",
  "QCLASS": 1,
  "QCLASSname": "IN",
  "answerRRs": [
    {
      "NAME": "nlnetlabs.nl.",
      "TYPE": 1,
      "TYPEname": "A",
      "CLASS": 1,
      "CLASSname": "IN",
      "TTL": 162,
      "rdataA": "185.49.140.10",
      "RDLENGTH": 4,
      "RDATAHEX": "B9318C0A"
    }
  ]
}

Cloudflare DNS

> curl -H 'Accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=nlnetlabs.nl&type=A'
{
  "Status": 0,
  "TC": false,
  "RD": true,
  "RA": true,
  "AD": true,
  "CD": false,
  "Question": [{ "name": "nlnetlabs.nl", "type": 1 }],
  "Answer": [
    { "name": "nlnetlabs.nl", "type": 1, "TTL": 240, "data": "185.49.140.10" }
  ]
}

Google DNS

> curl -H 'Accept: application/dns-json' 'https://dns.google.com/resolve?name=nlnetlabs.nl&type=A'
{
  "Status": 0,
  "TC": false,
  "RD": true,
  "RA": true,
  "AD": true,
  "CD": false,
  "Question": [{ "name": "nlnetlabs.nl.", "type": 1 }],
  "Answer": [
    { "name": "nlnetlabs.nl.", "type": 1, "TTL": 240, "data": "185.49.140.10" }
  ],
  "Comment": "Response from 185.49.140.60."
}

getdns

The default is not valid JSON, but valid JSON can be emitted using -j or -J.

{
  "answer_type": GETDNS_NAMETYPE_DNS,
  "canonical_name": <bindata for nlnetlabs.nl.>,
  "just_address_answers":
  [
    {
      "address_data": <bindata for 185.49.140.10>,
      "address_type": <bindata of "IPv4">
    }
  ],
  "replies_full":
  [
     <bindata of 0x9c0081800001000100000001096e6c6e...>
  ],
  "replies_tree":
  [
    {
      "additional":
      [
        {
          "do": 0,
          "extended_rcode": 0,
          "rdata":
          {
            "rdata_raw": <bindata of 0x>
          },
          "type": GETDNS_RRTYPE_OPT,
          "udp_payload_size": 1232,
          "version": 0,
          "z": 0
        }
      ],
      "answer":
      [
        {
          "class": GETDNS_RRCLASS_IN,
          "name": <bindata for nlnetlabs.nl.>,
          "rdata":
          {
            "ipv4_address": <bindata for 185.49.140.10>,
            "rdata_raw": <bindata of 0xb9318c0a>
          },
          "ttl": 240,
          "type": GETDNS_RRTYPE_A
        }
      ],
      "answer_type": GETDNS_NAMETYPE_DNS,
      "authority": [],
      "canonical_name": <bindata for nlnetlabs.nl.>,
      "header":
      {
        "aa": 0,
        "ad": 0,
        "ancount": 1,
        "arcount": 1,
        "cd": 0,
        "id": 39936,
        "nscount": 0,
        "opcode": GETDNS_OPCODE_QUERY,
        "qdcount": 1,
        "qr": 1,
        "ra": 1,
        "rcode": GETDNS_RCODE_NOERROR,
        "rd": 1,
        "tc": 0,
        "z": 0
      },
      "question":
      {
        "qclass": GETDNS_RRCLASS_IN,
        "qname": <bindata for nlnetlabs.nl.>,
        "qtype": GETDNS_RRTYPE_A
      }
    }
  ],
  "status": GETDNS_RESPSTATUS_GOOD
}

Other sources

RFC 8427 is used by plenty of local tools. I believe your issue is that there are multiple non-overlapping subsets of the vocabulary, and different tools have chosen different ones. RFC 8427 was developed after some of those tools had already chosen their way to do things, which is fine.

And, no, 8427 was not meant to be "ergonomic". It is meant to give tools the greatest level of choice for what data to keep, and then let the tools be ergonomic.

Having two formats is fine! And your list of tools is impressive. Pick what suits you best, as they all did.

@paulehoffman Do you have any thoughts about this draft, which adds EDNS options?

I didn't like it, and apparently the WG didn't either. It has expired.

Personally, I think the idea of having a "presentation format" that is easily confused with the zone file format is a bad idea that will lead to implementation errors. Using all uppercase seem unfriendly and unneeded.

I agree that it would be good to have a way to display things in JSON, but I don't see a reason to have a display format like they do here.