getdnsapi / getdns-node

Node.js bindings of getdns, a modern asynchronous DNS API.
https://getdnsapi.net/
Other
64 stars 8 forks source link

Segfault on specific domains with DNSSEC validation #33

Open smeinecke opened 6 years ago

smeinecke commented 6 years ago

I've tested it multiple times with multiple versions of the getdns library and the getdns-node module and node (v6, v8 and v9) and it seems as if I send a request with enabled DNSSEC validation for specific domains I get the following segmentation fault and the whole node process dies.

My local resolver is unbound 1.6.7

const getdns = require('getdns');
var ctx = getdns.createContext({
        // Upstream recursive servers.
        upstreams : [ '127.0.0.1', ],
        // Request timeout time in milliseconds.
        timeout : 5000,
        // Always return DNSSEC status.
        return_dnssec_status : true
    });

var extensions = {
    // NOTE: enforce DNSSEC security validation and return only secure replies.
    dnssec_return_only_secure: true,
};

var domains = [ 'nic.menu', 'nic.uno', 'nic.onl', 'nic.buzz', 'nic.pink', 'nic.build', 'nic.rich', 'nic.red', 'nic.luxury', 'nic.shiksha', 'nic.kim' ],
    i = 0;
ctx.general(domains[i], getdns.RRTYPE_SOA, extensions);

node: ../deps/uv/src/unix/poll.c:120: uv_poll_start: Assertion `!(((handle)->flags & (UV_CLOSING | UV_CLOSED)) != 0)' failed.

with enabled segfault-handler:

PID 3770 received SIGSEGV for address: 0x7f2342e3ed88
/home/daemons/node_modules/segfault-handler/build/Release/segfault-handler.node(+0x1aaa)[0x7f213c651aaa]
/lib/x86_64-linux-gnu/libpthread.so.0(+0xf890)[0x7f2140538890]
node[0x1340739]
node(uv_poll_start+0x6f)[0x13379ff]
/home/daemons/getdns/build/Release/getdns.node(+0xadea)[0x7f213df72dea]
/usr/lib/x86_64-linux-gnu/libgetdns.so.6(+0x2e4d9)[0x7f213dd234d9]
/usr/lib/x86_64-linux-gnu/libgetdns.so.6(+0x3918c)[0x7f213dd2e18c]
/usr/lib/x86_64-linux-gnu/libgetdns.so.6(+0x212ec)[0x7f213dd162ec]
/usr/lib/x86_64-linux-gnu/libgetdns.so.6(+0x21aa1)[0x7f213dd16aa1]
/usr/lib/x86_64-linux-gnu/libgetdns.so.6(+0x20b89)[0x7f213dd15b89]
/usr/lib/x86_64-linux-gnu/libgetdns.so.6(+0x20d60)[0x7f213dd15d60]
/usr/lib/x86_64-linux-gnu/libgetdns.so.6(+0x2205a)[0x7f213dd1705a]
/usr/lib/x86_64-linux-gnu/libgetdns.so.6(+0x2ff45)[0x7f213dd24f45]
/home/daemons/getdns/build/Release/getdns.node(+0xaf50)[0x7f213df72f50]
node[0x1340c08]
node(uv_run+0x156)[0x132f6d6]
node(_ZN4node5StartEiPPc+0x580)[0x10abcf0]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7f214019fb45]
node[0x7ba78d]

Any idea?

smeinecke commented 6 years ago

Oh, btw. the getdns commandline tool works fine:

# getdns_query +dnssec_return_status -a nic.menu SOA
{
  "answer_type": GETDNS_NAMETYPE_DNS,
  "canonical_name": <bindata for nic.menu.>,
  "replies_full":
  [
     <bindata of 0x000081a00001000200000001036e6963...>
  ],
  "replies_tree":
  [
    {
      "additional":
      [
        {
          "do": 1,
          "extended_rcode": 0,
          "rdata":
          {
            "rdata_raw": <bindata of 0x>
          },
          "type": GETDNS_RRTYPE_OPT,
          "udp_payload_size": 65535,
          "version": 0,
          "z": 0
        }
      ],
      "answer":
      [
        {
          "class": GETDNS_RRCLASS_IN,
          "name": <bindata for nic.menu.>,
          "rdata":
          {
            "expire": 1814400,
            "minimum": 1800,
            "mname": <bindata for a.nic.menu.>,
            "rdata_raw": <bindata of 0x0161c00c07737570706f72740b617269...>,
            "refresh": 1800,
            "retry": 300,
            "rname": <bindata for support.ariservices.com.>,
            "serial": 1518897626
          },
          "ttl": 900,
          "type": GETDNS_RRTYPE_SOA
        },
        {
          "class": GETDNS_RRCLASS_IN,
          "name": <bindata for nic.menu.>,
          "rdata":
          {
            "algorithm": 8,
            "key_tag": 64353,
            "labels": 2,
            "original_ttl": 900,
            "rdata_raw": <bindata of 0x00060802000003845ad7b9805ab01e70...>,
            "signature": <bindata of 0x463819766cb4943f4a665411caf8601b...>,
            "signature_expiration": 1524087168,
            "signature_inception": 1521491568,
            "signers_name": <bindata for nic.menu.>,
            "type_covered": GETDNS_RRTYPE_SOA
          },
          "ttl": 900,
          "type": GETDNS_RRTYPE_RRSIG
        }
      ],
      "answer_type": GETDNS_NAMETYPE_DNS,
      "authority": [],
      "canonical_name": <bindata for nic.menu.>,
      "dnssec_status": GETDNS_DNSSEC_SECURE,
      "header":
      {
        "aa": 0,
        "ad": 1,
        "ancount": 2,
        "arcount": 1,
        "cd": 0,
        "id": 0,
        "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 nic.menu.>,
        "qtype": GETDNS_RRTYPE_SOA
      }
    }
  ],
  "status": GETDNS_RESPSTATUS_GOOD
}
smeinecke commented 6 years ago

Does anyone have the same problem and could confirm this issue?

joelpurra commented 6 years ago

@smeinecke: thanks for the report and test code! I can confirm the problem, but see below for configuration details.

I sometimes get segfaults from unbound, but haven't had enough time to dig deep enough. @wcawijngaards tried to explain what I was looking at in the weekly getdns developer's hangouts meeting once (or twice) but it's not a level of programming I'm fast/comfortable debugging.

My setup:

Recursive mode

Note that you are running in default recursive mode, as resolution_type: getdns.RESOLUTION_STUB was not set in the context; your upstreams should be ignored. (See also getdns_query below.)

// NOTE: need a callback to run test code.
const loggingCallback = (...args) => { console.log(...args); };

// NOTE: added callback to single test lookup.
ctx.general(domains[i], getdns.RRTYPE_SOA, extensions, loggingCallback);

// NOTE: looping over all example domains also works.
domains.forEach((domain) => ctx.general(domain, getdns.RRTYPE_SOA, extensions, loggingCallback));

Your test code seems to work for me in recursive mode, after adding a simple lookup result callback.

Note that the results should contain status GETDNS_RESPSTATUS_GOOD (900) and per-answer dnssec_status GETDNS_DNSSEC_SECURE (400).

Stub mode

{
// NOTE: added option for stub resolver context, deferring lookups to the upstream recursive servers.
resolution_type: getdns.RESOLUTION_STUB,
upstreams: ...
}

The test code segfaults in stub mode. Testing with other domains (joelpurra.se, getdnsapi.net) surprisingly works well.

getdns_query

Note that you are running getdns_query in recursive mode as well. Try this:

# NOTE: use local dns server in stub mode.
getdns_query @127.0.0.1 +dnssec_return_status -s -a nic.menu SOA

unbound

Even if getdns_query works, can you confirm that your local unbound resolver is correctly set up to handle DNSSEC lookups? I've forgotten to do that before.

# NOTE: check that "flags" include "ad".
dig @127.0.0.1 com. SOA +adflag
NodePing commented 5 years ago

Just getting started with getdns and I'm seeing the same thing. Only with DNSSEC enabled queries and only about 1/4 of the time do they segfault. I'm doing a query for A records.

I'm using remote resolvers (authoritative ones). I've tried several different ones and it doesn't seem to make a difference what the resolvers are.

When it segfaults the runtime is about 1/10 of a regular query (22ms vs 250ms) so I think it's dorking out before a response is received.

I hope that helps and you're able to track down the issue.