"use strict";

var _tape = _interopRequireWildcard(require("tape"), true);
var packet = _interopRequireWildcard(require("./index.js"), true);
var rcodes = _interopRequireWildcard(require("./rcodes.js"), true);
var opcodes = _interopRequireWildcard(require("./opcodes.js"), true);
var optioncodes = _interopRequireWildcard(require("./optioncodes.js"), true);
var _utf8Codec = require("utf8-codec");
var _buffer_utils = require("./buffer_utils.js");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
(0, _tape.default)('unknown', function (t) {
  testEncoder(t, packet.unknown, Buffer.from('hello world'));
  t.end();
});
(0, _tape.default)('txt', function (t) {
  testEncoder(t, packet.txt, []);
  testEncoder(t, packet.txt, ['hello world']);
  testEncoder(t, packet.txt, ['hello', 'world']);
  testEncoder(t, packet.txt, [Buffer.from([0, 1, 2, 3, 4, 5])]);
  testEncoder(t, packet.txt, ['a', 'b', Buffer.from([0, 1, 2, 3, 4, 5])]);
  testEncoder(t, packet.txt, ['', Buffer.allocUnsafe(0)]);
  t.end();
});
(0, _tape.default)('txt-scalar-string', function (t) {
  const buf = packet.txt.encode('hi');
  const val = packet.txt.decode(buf);
  t.equal(val.length, 1, 'array length');
  t.ok(compare(t, val[0], Buffer.from('hi')), 'streamDecoded type match');
  t.end();
});
(0, _tape.default)('txt-scalar-buffer', function (t) {
  const data = Buffer.from([0, 1, 2, 3, 4, 5]);
  const buf = packet.txt.encode(data);
  const val = packet.txt.decode(buf);
  t.equal(val.length, 1, 'array length');
  t.ok(compare(t, val[0], data), 'data');
  t.end();
});
(0, _tape.default)('txt-invalid-data', function (t) {
  t.throws(function () {
    packet.txt.encode(null);
  }, 'null');
  t.throws(function () {
    packet.txt.encode(undefined);
  }, 'undefined');
  t.throws(function () {
    packet.txt.encode(10);
  }, 'number');
  t.end();
});
(0, _tape.default)('null', function (t) {
  testEncoder(t, packet.null, Buffer.from([0, 1, 2, 3, 4, 5]));
  t.end();
});
(0, _tape.default)('hinfo', function (t) {
  testEncoder(t, packet.hinfo, {
    cpu: 'intel',
    os: 'best one'
  });
  t.end();
});
(0, _tape.default)('ptr', function (t) {
  testEncoder(t, packet.ptr, 'hello.world.com');
  t.end();
});
(0, _tape.default)('cname', function (t) {
  testEncoder(t, packet.cname, 'hello.cname.world.com');
  t.end();
});
(0, _tape.default)('dname', function (t) {
  testEncoder(t, packet.dname, 'hello.dname.world.com');
  t.end();
});
(0, _tape.default)('srv', function (t) {
  testEncoder(t, packet.srv, {
    port: 9999,
    target: 'hello.world.com'
  });
  testEncoder(t, packet.srv, {
    port: 9999,
    target: 'hello.world.com',
    priority: 42,
    weight: 10
  });
  t.end();
});
(0, _tape.default)('caa', function (t) {
  testEncoder(t, packet.caa, {
    flags: 128,
    tag: 'issue',
    value: 'letsencrypt.org',
    issuerCritical: true
  });
  testEncoder(t, packet.caa, {
    tag: 'issue',
    value: 'letsencrypt.org',
    issuerCritical: true
  });
  testEncoder(t, packet.caa, {
    tag: 'issue',
    value: 'letsencrypt.org'
  });
  t.end();
});
(0, _tape.default)('mx', function (t) {
  testEncoder(t, packet.mx, {
    preference: 10,
    exchange: 'mx.hello.world.com'
  });
  testEncoder(t, packet.mx, {
    exchange: 'mx.hello.world.com'
  });
  t.end();
});
(0, _tape.default)('ns', function (t) {
  testEncoder(t, packet.ns, 'ns.world.com');
  t.end();
});
(0, _tape.default)('soa', function (t) {
  testEncoder(t, packet.soa, {
    mname: 'hello.world.com',
    rname: 'root.hello.world.com',
    serial: 2018010400,
    refresh: 14400,
    retry: 3600,
    expire: 604800,
    minimum: 3600
  });
  t.end();
});
(0, _tape.default)('sshfp', function (t) {
  testEncoder(t, packet.sshfp, {
    algorithm: 1,
    hash: 1,
    fingerprint: 'a108c9f834354d5b37af988141c9294822f5bc00'
  });
  testEncoder(t, packet.sshfp, {
    algorithm: 1,
    hash: 2,
    fingerprint: 'a108c9f834354d5b37af988141c9294822f5bc00afa0dfafa2dfa1dfafa5dfa1'
  });
  t.end();
});
(0, _tape.default)('a', function (t) {
  testEncoder(t, packet.a, '127.0.0.1');
  t.end();
});
(0, _tape.default)('aaaa', function (t) {
  testEncoder(t, packet.aaaa, 'fe80::1');
  t.end();
});
(0, _tape.default)('query', function (t) {
  testEncoder(t, packet, {
    type: 'query',
    questions: [{
      type: 'A',
      name: 'hello.a.com'
    }, {
      type: 'SRV',
      name: 'hello.srv.com'
    }]
  });
  testEncoder(t, packet, {
    type: 'query',
    id: 42,
    questions: [{
      type: 'A',
      class: 'IN',
      name: 'hello.a.com'
    }, {
      type: 'SRV',
      name: 'hello.srv.com'
    }]
  });
  testEncoder(t, packet, {
    type: 'query',
    id: 42,
    questions: [{
      type: 'A',
      class: 'CH',
      name: 'hello.a.com'
    }, {
      type: 'SRV',
      name: 'hello.srv.com'
    }]
  });
  t.end();
});
(0, _tape.default)('response', function (t) {
  testEncoder(t, packet, {
    type: 'response',
    answers: [{
      type: 'A',
      class: 'IN',
      flush: true,
      name: 'hello.a.com',
      data: '127.0.0.1'
    }]
  });
  testEncoder(t, packet, {
    type: 'response',
    flags: packet.TRUNCATED_RESPONSE,
    answers: [{
      type: 'A',
      class: 'IN',
      name: 'hello.a.com',
      data: '127.0.0.1'
    }, {
      type: 'SRV',
      class: 'IN',
      name: 'hello.srv.com',
      data: {
        port: 9090,
        target: 'hello.target.com'
      }
    }, {
      type: 'CNAME',
      class: 'IN',
      name: 'hello.cname.com',
      data: 'hello.other.domain.com'
    }]
  });
  testEncoder(t, packet, {
    type: 'response',
    id: 100,
    flags: 0,
    additionals: [{
      type: 'AAAA',
      name: 'hello.a.com',
      data: 'fe80::1'
    }, {
      type: 'PTR',
      name: 'hello.ptr.com',
      data: 'hello.other.ptr.com'
    }, {
      type: 'SRV',
      name: 'hello.srv.com',
      ttl: 42,
      data: {
        port: 9090,
        target: 'hello.target.com'
      }
    }],
    answers: [{
      type: 'NULL',
      name: 'hello.null.com',
      data: Buffer.from([1, 2, 3, 4, 5])
    }]
  });
  testEncoder(t, packet, {
    type: 'response',
    answers: [{
      type: 'TXT',
      name: 'emptytxt.com',
      data: ''
    }]
  });
  t.end();
});
(0, _tape.default)('rcode', function (t) {
  const errors = ['NOERROR', 'FORMERR', 'SERVFAIL', 'NXDOMAIN', 'NOTIMP', 'REFUSED', 'YXDOMAIN', 'YXRRSET', 'NXRRSET', 'NOTAUTH', 'NOTZONE', 'RCODE_11', 'RCODE_12', 'RCODE_13', 'RCODE_14', 'RCODE_15'];
  for (const i in errors) {
    const code = rcodes.toRcode(errors[i]);
    t.ok(errors[i] === rcodes.toString(code), 'rcode conversion from/to string matches: ' + rcodes.toString(code));
  }
  const ops = ['QUERY', 'IQUERY', 'STATUS', 'OPCODE_3', 'NOTIFY', 'UPDATE', 'OPCODE_6', 'OPCODE_7', 'OPCODE_8', 'OPCODE_9', 'OPCODE_10', 'OPCODE_11', 'OPCODE_12', 'OPCODE_13', 'OPCODE_14', 'OPCODE_15'];
  for (const j in ops) {
    const ocode = opcodes.toOpcode(ops[j]);
    t.ok(ops[j] === opcodes.toString(ocode), 'opcode conversion from/to string matches: ' + opcodes.toString(ocode));
  }
  const buf = packet.encode({
    type: 'response',
    id: 45632,
    flags: 0x8480,
    answers: [{
      type: 'A',
      name: 'hello.example.net',
      data: '127.0.0.1'
    }]
  });
  const val = packet.decode(buf);
  t.ok(val.type === 'response', 'decode type');
  t.ok(val.opcode === 'QUERY', 'decode opcode');
  t.ok(val.flag_qr === true, 'decode flag_qr');
  t.ok(val.flag_aa === true, 'decode flag_aa');
  t.ok(val.flag_tc === false, 'decode flag_tc');
  t.ok(val.flag_rd === false, 'decode flag_rd');
  t.ok(val.flag_ra === true, 'decode flag_ra');
  t.ok(val.flag_z === false, 'decode flag_z');
  t.ok(val.flag_ad === false, 'decode flag_ad');
  t.ok(val.flag_cd === false, 'decode flag_cd');
  t.ok(val.rcode === 'NOERROR', 'decode rcode');
  t.end();
});
(0, _tape.default)('name_encoding', function (t) {
  let data = 'foo.example.com';
  const buf = Buffer.allocUnsafe(255);
  let offset = 0;
  packet.name.encode(data, buf, offset);
  t.ok(packet.name.encode.bytes === 17, 'name encoding length matches');
  let dd = packet.name.decode(buf, offset);
  t.ok(data === dd, 'encode/decode matches');
  offset += packet.name.encode.bytes;
  data = 'com';
  packet.name.encode(data, buf, offset);
  t.ok(packet.name.encode.bytes === 5, 'name encoding length matches');
  dd = packet.name.decode(buf, offset);
  t.ok(data === dd, 'encode/decode matches');
  offset += packet.name.encode.bytes;
  data = 'example.com.';
  packet.name.encode(data, buf, offset);
  t.ok(packet.name.encode.bytes === 13, 'name encoding length matches');
  dd = packet.name.decode(buf, offset);
  t.ok(data.slice(0, -1) === dd, 'encode/decode matches');
  offset += packet.name.encode.bytes;
  data = '.';
  packet.name.encode(data, buf, offset);
  t.ok(packet.name.encode.bytes === 1, 'name encoding length matches');
  dd = packet.name.decode(buf, offset);
  t.ok(data === dd, 'encode/decode matches');
  t.end();
});
(0, _tape.default)('name_decoding', function (t) {
  // The two most significant bits of a valid label header must be either both zero or both one
  t.throws(function () {
    packet.name.decode(Buffer.from([0x80]));
  }, /Cannot decode name \(bad label\)$/);
  t.throws(function () {
    packet.name.decode(Buffer.from([0xb0]));
  }, /Cannot decode name \(bad label\)$/);

  // Ensure there's enough buffer to read
  t.throws(function () {
    packet.name.decode(Buffer.from([]));
  }, /Cannot decode name \(buffer overflow\)$/);
  t.throws(function () {
    packet.name.decode(Buffer.from([0x01, 0x00]));
  }, /Cannot decode name \(buffer overflow\)$/);
  t.throws(function () {
    packet.name.decode(Buffer.from([0x01]));
  }, /Cannot decode name \(buffer overflow\)$/);
  t.throws(function () {
    packet.name.decode(Buffer.from([0xc0]));
  }, /Cannot decode name \(buffer overflow\)$/);

  // Allow only pointers backwards
  t.throws(function () {
    packet.name.decode(Buffer.from([0xc0, 0x00]));
  }, /Cannot decode name \(bad pointer\)$/);
  t.throws(function () {
    packet.name.decode(Buffer.from([0xc0, 0x01]));
  }, /Cannot decode name \(bad pointer\)$/);

  // A name can be only 253 characters (when connected with dots)
  const maxLength = Buffer.alloc(255);
  maxLength.fill(Buffer.from([0x01, 0x61]), 0, 254);
  t.ok(packet.name.decode(maxLength) === new Array(127).fill('a').join('.'));
  const tooLong = Buffer.alloc(256);
  tooLong.fill(Buffer.from([0x01, 0x61]));
  t.throws(function () {
    packet.name.decode(tooLong);
  }, /Cannot decode name \(name too long\)$/);

  // Ensure jumps don't reset the total length counter
  const tooLongWithJump = Buffer.alloc(403);
  tooLongWithJump.fill(Buffer.from([0x01, 0x61]), 0, 200);
  tooLongWithJump.fill(Buffer.from([0x01, 0x61]), 201, 401);
  tooLongWithJump.set([0xc0, 0x00], 401);
  t.throws(function () {
    packet.name.decode(tooLongWithJump, 201);
  }, /Cannot decode name \(name too long\)$/);

  // Ensure a jump to a null byte doesn't add extra dots
  t.ok(packet.name.decode(Buffer.from([0x00, 0x01, 0x61, 0xc0, 0x00]), 1) === 'a');

  // Ensure deeply nested pointers don't cause "Maximum call stack size exceeded" errors
  const buf = Buffer.alloc(16386);
  for (let i = 0; i < 16384; i += 2) {
    buf.writeUInt16BE(0xc000 | i, i + 2);
  }
  t.ok(packet.name.decode(buf, 16384) === '.');
  t.end();
});
(0, _tape.default)('stream', function (t) {
  const val = {
    type: 'query',
    id: 45632,
    flags: 0x8480,
    answers: [{
      type: 'A',
      name: 'test2.example.net',
      data: '198.51.100.1'
    }]
  };
  const buf = packet.streamEncode(val);
  const val2 = packet.streamDecode(buf);
  t.same(buf.length, packet.streamEncode.bytes, 'streamEncode.bytes was set correctly');
  t.ok(compare(t, val2.type, val.type), 'streamDecoded type match');
  t.ok(compare(t, val2.id, val.id), 'streamDecoded id match');
  t.ok(parseInt(val2.flags) === parseInt(val.flags & 0x7FFF), 'streamDecoded flags match');
  const answer = val.answers[0];
  const answer2 = val2.answers[0];
  t.ok(compare(t, answer.type, answer2.type), 'streamDecoded RR type match');
  t.ok(compare(t, answer.name, answer2.name), 'streamDecoded RR name match');
  t.ok(compare(t, answer.data, answer2.data), 'streamDecoded RR rdata match');
  t.end();
});
(0, _tape.default)('opt', function (t) {
  const val = {
    type: 'query',
    questions: [{
      type: 'A',
      name: 'hello.a.com'
    }],
    additionals: [{
      type: 'OPT',
      name: '.',
      udpPayloadSize: 1024
    }]
  };
  testEncoder(t, packet, val);
  let buf = packet.encode(val);
  let val2 = packet.decode(buf);
  const additional1 = val.additionals[0];
  let additional2 = val2.additionals[0];
  t.ok(compare(t, additional1.name, additional2.name), 'name matches');
  t.ok(compare(t, additional1.udpPayloadSize, additional2.udpPayloadSize), 'udp payload size matches');
  t.ok(compare(t, 0, additional2.flags), 'flags match');
  additional1.flags = packet.DNSSEC_OK;
  additional1.extendedRcode = 0x80;
  additional1.options = [{
    code: 'CLIENT_SUBNET',
    // edns-client-subnet, see RFC 7871
    ip: 'fe80::',
    sourcePrefixLength: 64
  }, {
    code: 8,
    // still ECS
    ip: '5.6.0.0',
    sourcePrefixLength: 16,
    scopePrefixLength: 16
  }, {
    code: 'padding',
    length: 31
  }, {
    code: 'TCP_KEEPALIVE'
  }, {
    code: 'tcp_keepalive',
    timeout: 150
  }, {
    code: 'KEY_TAG',
    tags: [1, 82, 987]
  }];
  buf = packet.encode(val);
  val2 = packet.decode(buf);
  additional2 = val2.additionals[0];
  t.ok(compare(t, 1 << 15, additional2.flags), 'DO bit set in flags');
  t.ok(compare(t, true, additional2.flag_do), 'DO bit set');
  t.ok(compare(t, additional1.extendedRcode, additional2.extendedRcode), 'extended rcode matches');
  t.ok(compare(t, 8, additional2.options[0].code));
  t.ok(compare(t, 'fe80::', additional2.options[0].ip));
  t.ok(compare(t, 64, additional2.options[0].sourcePrefixLength));
  t.ok(compare(t, '5.6.0.0', additional2.options[1].ip));
  t.ok(compare(t, 16, additional2.options[1].sourcePrefixLength));
  t.ok(compare(t, 16, additional2.options[1].scopePrefixLength));
  t.ok(compare(t, additional1.options[2].length, additional2.options[2].data.length));
  t.ok(compare(t, additional1.options[3].timeout, undefined));
  t.ok(compare(t, additional1.options[4].timeout, additional2.options[4].timeout));
  t.ok(compare(t, additional1.options[5].tags, additional2.options[5].tags));
  t.end();
});
(0, _tape.default)('dnskey', function (t) {
  testEncoder(t, packet.dnskey, {
    flags: packet.dnskey.SECURE_ENTRYPOINT | packet.dnskey.ZONE_KEY,
    algorithm: 1,
    key: Buffer.from([0, 1, 2, 3, 4, 5])
  });
  t.end();
});
(0, _tape.default)('rrsig', function (t) {
  const testRRSIG = {
    typeCovered: 'A',
    algorithm: 1,
    labels: 2,
    originalTTL: 3600,
    expiration: 1234,
    inception: 1233,
    keyTag: 2345,
    signersName: 'foo.com',
    signature: Buffer.from([0, 1, 2, 3, 4, 5])
  };
  testEncoder(t, packet.rrsig, testRRSIG);

  // Check the signature length is correct with extra junk at the end
  const buf = Buffer.allocUnsafe(packet.rrsig.encodingLength(testRRSIG) + 4);
  packet.rrsig.encode(testRRSIG, buf);
  const val2 = packet.rrsig.decode(buf);
  t.ok(compare(t, testRRSIG, val2));
  t.end();
});
(0, _tape.default)('rrp', function (t) {
  testEncoder(t, packet.rp, {
    mbox: 'foo.bar.com',
    txt: 'baz.bar.com'
  });
  testEncoder(t, packet.rp, {
    mbox: 'foo.bar.com'
  });
  testEncoder(t, packet.rp, {
    txt: 'baz.bar.com'
  });
  testEncoder(t, packet.rp, {});
  t.end();
});
(0, _tape.default)('nsec', function (t) {
  testEncoder(t, packet.nsec, {
    nextDomain: 'foo.com',
    rrtypes: ['A', 'DNSKEY', 'CAA', 'DLV']
  });
  testEncoder(t, packet.nsec, {
    nextDomain: 'foo.com',
    rrtypes: ['TXT'] // 16
  });

  testEncoder(t, packet.nsec, {
    nextDomain: 'foo.com',
    rrtypes: ['TKEY'] // 249
  });

  testEncoder(t, packet.nsec, {
    nextDomain: 'foo.com',
    rrtypes: ['RRSIG', 'NSEC']
  });
  testEncoder(t, packet.nsec, {
    nextDomain: 'foo.com',
    rrtypes: ['TXT', 'RRSIG']
  });
  testEncoder(t, packet.nsec, {
    nextDomain: 'foo.com',
    rrtypes: ['TXT', 'NSEC']
  });

  // Test with the sample NSEC from https://tools.ietf.org/html/rfc4034#section-4.3
  const sampleNSEC = new Uint8Array(Buffer.from('003704686f7374076578616d706c6503636f6d00' + '0006400100000003041b000000000000000000000000000000000000000000000' + '000000020', 'hex'));
  const decoded = packet.nsec.decode(sampleNSEC);
  t.ok(compare(t, decoded, {
    nextDomain: 'host.example.com',
    rrtypes: ['A', 'MX', 'RRSIG', 'NSEC', 'UNKNOWN_1234']
  }));
  const reencoded = packet.nsec.encode(decoded);
  t.same(sampleNSEC.length, reencoded.length);
  t.same(sampleNSEC, reencoded);
  t.end();
});
(0, _tape.default)('nsec3', function (t) {
  testEncoder(t, packet.nsec3, {
    algorithm: 1,
    flags: 0,
    iterations: 257,
    salt: Buffer.from([42, 42, 42]),
    nextDomain: Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),
    rrtypes: ['A', 'DNSKEY', 'CAA', 'DLV']
  });
  t.end();
});
(0, _tape.default)('ds', function (t) {
  testEncoder(t, packet.ds, {
    keyTag: 1234,
    algorithm: 1,
    digestType: 1,
    digest: Buffer.from([0, 1, 2, 3, 4, 5])
  });
  t.end();
});
(0, _tape.default)('unpack', function (t) {
  const buf = Buffer.from([0x00, 0x79, 0xde, 0xad, 0x85, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x02, 0x6f, 0x6a, 0x05, 0x62, 0x61, 0x6e, 0x67, 0x6a, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x04, 0x81, 0xfa, 0x0b, 0xaa, 0xc0, 0x0f, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x05, 0x02, 0x63, 0x6a, 0xc0, 0x0f, 0xc0, 0x0f, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x02, 0xc0, 0x0c, 0xc0, 0x3a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x04, 0x45, 0x4d, 0x9b, 0x9c, 0xc0, 0x0c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x10, 0x20, 0x01, 0x04, 0x18, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf9]);
  const val = packet.streamDecode(buf);
  const answer = val.answers[0];
  const authority = val.authorities[1];
  t.ok(val.rcode === 'NOERROR', 'decode rcode');
  t.ok(compare(t, answer.type, 'A'), 'streamDecoded RR type match');
  t.ok(compare(t, answer.name, 'oj.bangj.com'), 'streamDecoded RR name match');
  t.ok(compare(t, answer.data, '129.250.11.170'), 'streamDecoded RR rdata match');
  t.ok(compare(t, authority.type, 'NS'), 'streamDecoded RR type match');
  t.ok(compare(t, authority.name, 'bangj.com'), 'streamDecoded RR name match');
  t.ok(compare(t, authority.data, 'oj.bangj.com'), 'streamDecoded RR rdata match');
  t.end();
});
(0, _tape.default)('optioncodes', function (t) {
  const opts = [[0, 'OPTION_0'], [1, 'LLQ'], [2, 'UL'], [3, 'NSID'], [4, 'OPTION_4'], [5, 'DAU'], [6, 'DHU'], [7, 'N3U'], [8, 'CLIENT_SUBNET'], [9, 'EXPIRE'], [10, 'COOKIE'], [11, 'TCP_KEEPALIVE'], [12, 'PADDING'], [13, 'CHAIN'], [14, 'KEY_TAG'], [26946, 'DEVICEID'], [65535, 'OPTION_65535'], [64000, 'OPTION_64000'], [65002, 'OPTION_65002'], [-1, null]];
  for (const [code, str] of opts) {
    const s = optioncodes.toString(code);
    t.ok(compare(t, s, str), `${code} => ${str}`);
    t.ok(compare(t, optioncodes.toCode(s), code), `${str} => ${code}`);
  }
  t.ok(compare(t, optioncodes.toCode('INVALIDINVALID'), -1));
  t.end();
});
(0, _tape.default)('packet exported codec', function (t) {
  const input = {
    type: 'query',
    questions: [{
      type: 'A',
      name: 'hello.a.com',
      class: 'IN'
    }]
  };
  packet.encode.bytes = 0;
  t.equals(packet.packet.encode.bytes, 0);
  t.equals(packet.packet.encode.bytes, packet.encode.bytes);
  const buf = packet.packet.encode(input);
  t.equals(packet.packet.encode.bytes, 29);
  t.equals(packet.packet.encode.bytes, packet.encode.bytes);
  t.deepEqual(buf, packet.encode(input));
  packet.decode.bytes = 0;
  t.equals(packet.packet.decode.bytes, 0);
  t.equals(packet.packet.decode.bytes, packet.decode.bytes);
  const obj = packet.packet.decode(buf);
  t.equals(packet.packet.decode.bytes, 29);
  t.equals(packet.packet.decode.bytes, packet.decode.bytes);
  t.deepEqual(obj, packet.decode(buf));
  t.end();
});
(0, _tape.default)('single query error with multiple questions', function (t) {
  t.throws(() => {
    packet.query.encode({
      questions: []
    });
  }, /Only one .question object expected instead of a .questions array!/);
  t.end();
});
(0, _tape.default)('single query -> response encoding', function (t) {
  const question = {
    type: 'A',
    name: 'hello.a.com',
    class: 'IN'
  };
  const length = packet.query.encodingLength({
    question
  });
  t.equals(length, 29);
  t.equals(packet.query.encode.bytes, 0);
  const queryBytes = packet.query.encode({
    question
  });
  const decodedQuestion = packet.decode(queryBytes);
  t.equals(packet.query.encode.bytes, length);
  t.equal(decodedQuestion.type, 'query');
  t.deepEqual(decodedQuestion.questions, [question]);
  t.equals(packet.query.decode.bytes, 0);
  decodedQuestion.question = decodedQuestion.questions[0];
  delete decodedQuestion.questions;
  t.deepEqual(packet.query.decode(queryBytes), decodedQuestion);
  const responseBytes = packet.encode({
    type: 'response',
    questions: [question]
  });
  const decodedResponse = packet.response.decode(responseBytes);
  t.equal(packet.response.encodingLength(decodedResponse), length);
  t.equals(packet.response.decode.bytes, length);
  t.deepEqual(decodedResponse.question, question);
  t.deepEqual(packet.response.encode(decodedResponse), responseBytes);
  t.end();
});
(0, _tape.test)('buffer utf8', function (sub) {
  for (const [index, fixture] of [
  // '', // empty
  'basic: hi', 'japanese: 日本語', 'mixed: 日本語 hi', 'min 1 byte: \x00', 'odd 1 byte: \x4c', 'max 1 byte: \x7f', 'min 2 byte: \x80', 'odd 2 byte: \xd941', 'max 2 byte: \x7fff', 'min 3 byte: \x8000', 'odd 3 byte: \xa158', 'max 3 byte: \xffff', `4 byte: ${String.fromCodePoint(100000)}`].entries()) {
    sub.test(`fixture #${index}`, t => {
      const check = Buffer.from(fixture);
      const checkHex = check.toString('hex');
      const len = check.length;
      t.equals((0, _buffer_utils.bytelength)(fixture), len, `fixture ${fixture} length`);
      const buf = new Uint8Array(len);
      t.equals((0, _buffer_utils.write)(buf, fixture, 0), len, 'write.num');
      t.equals((0, _buffer_utils.toHex)(buf, 0, len), checkHex, `write: ${fixture}`);
      t.equals(check.compare(Buffer.from((0, _buffer_utils.writeHex)(buf, checkHex, 0, (0, _buffer_utils.hexLength)(checkHex)))), 0);
      t.equals((0, _utf8Codec.decode)(check, 0, check.length), check.toString(), `toUtf8: ${fixture}`);
      t.end();
    });
  }
  sub.test('surrogate pairs', function (t) {
    [[0xd821, 0xdea0], [0xd800, 0xdc00], [0xd801, 0xdc01], [0xddff, 0xdfff], [0xd821, 0x0000], [0xd821, 0xd821, 0xdea0]].forEach(function (bytes, index) {
      const str = String.fromCharCode(...bytes);
      const buf = new Uint8Array((0, _buffer_utils.bytelength)(str));
      const check = Buffer.from(str);
      t.equal(buf.length, check.length);
      (0, _buffer_utils.write)(buf, str, 0);
      t.equal((0, _buffer_utils.toHex)(buf, 0, buf.length), check.toString('hex'), `#${index} [${bytes}].toHex() ... ${check.toString('hex')}`);
      t.equal((0, _utf8Codec.decode)(buf, 0, buf.length), check.toString(), `#${index} [${bytes}].toUtf8`);
    });
    t.end();
  });
  sub.test('all code points', function (t) {
    const blockSize = 2048;
    const blocks = 65536 / blockSize;
    let code = 0;
    for (let block = 0; block < blocks; block += 1) {
      const expected = {};
      const actual = {};
      for (let i = 0; i < blockSize; i += 1, code += 1) {
        const str = String.fromCharCode(code);
        const buf = new Uint8Array((0, _buffer_utils.bytelength)(str));
        (0, _buffer_utils.write)(buf, str, 0);
        const exp = Buffer.from(str);
        expected[code] = exp.toString('hex');
        actual[code] = (0, _buffer_utils.toHex)(buf, 0, buf.length);
      }
      t.same(actual, expected);
    }
    t.end();
  });
});
function testEncoder(t, rpacket, val) {
  const buf = rpacket.encode(val);
  const val2 = rpacket.decode(buf);
  t.same(buf.length, rpacket.encode.bytes, 'encode.bytes was set correctly');
  t.same(buf.length, rpacket.encodingLength(val), 'encoding length matches');
  t.ok(compare(t, val, val2), 'decoded object match');
  const buf2 = rpacket.encode(val2);
  const val3 = rpacket.decode(buf2);
  t.same(buf2.length, rpacket.encode.bytes, 'encode.bytes was set correctly on re-encode');
  t.same(buf2.length, rpacket.encodingLength(val), 'encoding length matches on re-encode');
  t.ok(compare(t, val, val3), 'decoded object match on re-encode');
  t.ok(compare(t, val2, val3), 're-encoded decoded object match on re-encode');
  const bigger = Buffer.allocUnsafe(buf2.length + 10);
  const buf3 = rpacket.encode(val, bigger, 10);
  const val4 = rpacket.decode(buf3, 10);
  t.ok(buf3 === bigger, 'echoes buffer on external buffer');
  t.same(rpacket.encode.bytes, buf.length, 'encode.bytes is the same on external buffer');
  t.ok(compare(t, val, val4), 'decoded object match on external buffer');
}
function compare(t, a, b) {
  if (a instanceof Uint8Array) return (0, _buffer_utils.toHex)(a, 0, a.length) === (0, _buffer_utils.toHex)(b, 0, b.length);
  if (typeof a === 'object' && a && b) {
    const keys = Object.keys(a);
    for (let i = 0; i < keys.length; i++) {
      if (!compare(t, a[keys[i]], b[keys[i]])) {
        return false;
      }
    }
  } else if (Array.isArray(b) && !Array.isArray(a)) {
    // TXT always decode as array
    return a.toString() === b[0].toString();
  } else {
    return a === b;
  }
  return true;
}