Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON output #12

Open
paulehoffman opened this issue May 11, 2024 · 5 comments
Open

JSON output #12

paulehoffman opened this issue May 11, 2024 · 5 comments

Comments

@paulehoffman
Copy link

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

@pemensik
Copy link

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

@tertsdiepraam
Copy link
Contributor

tertsdiepraam commented May 28, 2024

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

@paulehoffman
Copy link
Author

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.

@AlexanderBand
Copy link
Member

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

@paulehoffman
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants