dns-suite.js/node_modules/native-dns-packet/packet.js

780 lines
24 KiB
JavaScript

// Copyright 2011 Timothy J Fontaine <tjfontaine@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
// TODO: change the default UDP packet size that node-dns sends
// from 4096 to conform to these:
// - [requestor's payload size](https://tools.ietf.org/html/rfc6891#section-6.2.3)
// - [responders's payload size](https://tools.ietf.org/html/rfc6891#section-6.2.4)
'use strict';
var consts = require('./consts'),
BufferCursor = require('buffercursor'),
BufferCursorOverflow = BufferCursor.BufferCursorOverflow,
ipaddr = require('ipaddr.js'),
assert = require('assert'),
util = require('util');
function assertUndefined(val, msg) {
assert(typeof val != 'undefined', msg);
}
var Packet = module.exports = function() {
this.header = {
id: 0,
qr: 0,
opcode: 0,
aa: 0,
tc: 0,
rd: 1,
ra: 0,
res1: 0,
res2: 0,
res3: 0,
rcode: 0
};
this.question = [];
this.answer = [];
this.authority = [];
this.additional = [];
this.edns_options = []; // TODO: DEPRECATED! Use `.edns.options` instead!
this.payload = undefined; // TODO: DEPRECATED! Use `.edns.payload` instead!
};
var LABEL_POINTER = 0xC0;
var isPointer = function(len) {
return (len & LABEL_POINTER) === LABEL_POINTER;
};
function nameUnpack(buff) {
var len, comp, end, pos, part, combine = '';
len = buff.readUInt8();
comp = false;
end = buff.tell();
while (len !== 0) {
if (isPointer(len)) {
len -= LABEL_POINTER;
len = len << 8;
pos = len + buff.readUInt8();
if (!comp)
end = buff.tell();
buff.seek(pos);
len = buff.readUInt8();
comp = true;
continue;
}
part = buff.toString('ascii', len);
if (combine.length)
combine = combine + '.' + part;
else
combine = part;
len = buff.readUInt8();
if (!comp)
end = buff.tell();
}
buff.seek(end);
return combine;
}
function namePack(str, buff, index) {
var offset, dot, part;
while (str) {
if (index[str]) {
offset = (LABEL_POINTER << 8) + index[str];
buff.writeUInt16BE(offset);
break;
} else {
index[str] = buff.tell();
dot = str.indexOf('.');
if (dot > -1) {
part = str.slice(0, dot);
str = str.slice(dot + 1);
} else {
part = str;
str = undefined;
}
buff.writeUInt8(part.length);
buff.write(part, part.length, 'ascii');
}
}
if (!str) {
buff.writeUInt8(0);
}
}
var
WRITE_HEADER = 100001,
WRITE_TRUNCATE = 100002,
WRITE_QUESTION = 100003,
WRITE_RESOURCE_RECORD = 100004,
WRITE_RESOURCE_WRITE = 100005,
WRITE_RESOURCE_DONE = 100006,
WRITE_RESOURCE_END = 100007,
WRITE_EDNS = 100008,
WRITE_END = 100009,
WRITE_A = consts.NAME_TO_QTYPE.A,
WRITE_AAAA = consts.NAME_TO_QTYPE.AAAA,
WRITE_NS = consts.NAME_TO_QTYPE.NS,
WRITE_CNAME = consts.NAME_TO_QTYPE.CNAME,
WRITE_PTR = consts.NAME_TO_QTYPE.PTR,
WRITE_SPF = consts.NAME_TO_QTYPE.SPF,
WRITE_MX = consts.NAME_TO_QTYPE.MX,
WRITE_SRV = consts.NAME_TO_QTYPE.SRV,
WRITE_TXT = consts.NAME_TO_QTYPE.TXT,
WRITE_SOA = consts.NAME_TO_QTYPE.SOA,
WRITE_OPT = consts.NAME_TO_QTYPE.OPT,
WRITE_NAPTR = consts.NAME_TO_QTYPE.NAPTR,
WRITE_TLSA = consts.NAME_TO_QTYPE.TLSA;
function writeHeader(buff, packet) {
assert(packet.header, 'Packet requires "header"');
buff.writeUInt16BE(packet.header.id & 0xFFFF);
var val = 0;
val += (packet.header.qr << 15) & 0x8000;
val += (packet.header.opcode << 11) & 0x7800;
val += (packet.header.aa << 10) & 0x400;
val += (packet.header.tc << 9) & 0x200;
val += (packet.header.rd << 8) & 0x100;
val += (packet.header.ra << 7) & 0x80;
val += (packet.header.res1 << 6) & 0x40;
val += (packet.header.res2 << 5) & 0x20;
val += (packet.header.res3 << 4) & 0x10;
val += packet.header.rcode & 0xF;
buff.writeUInt16BE(val & 0xFFFF);
assert(packet.question.length == 1, 'DNS requires one question');
// aren't used
buff.writeUInt16BE(1);
// answer offset 6
buff.writeUInt16BE(packet.answer.length & 0xFFFF);
// authority offset 8
buff.writeUInt16BE(packet.authority.length & 0xFFFF);
// additional offset 10
buff.writeUInt16BE(packet.additional.length & 0xFFFF);
return WRITE_QUESTION;
}
function writeTruncate(buff, packet, section, val) {
// XXX FIXME TODO truncation is currently done wrong.
// Quote rfc2181 section 9
// The TC bit should not be set merely because some extra information
// could have been included, but there was insufficient room. This
// includes the results of additional section processing. In such cases
// the entire RRSet that will not fit in the response should be omitted,
// and the reply sent as is, with the TC bit clear. If the recipient of
// the reply needs the omitted data, it can construct a query for that
// data and send that separately.
//
// TODO IOW only set TC if we hit it in ANSWERS otherwise make sure an
// entire RRSet is removed during a truncation.
var pos;
buff.seek(2);
val = buff.readUInt16BE();
val |= (1 << 9) & 0x200;
buff.seek(2);
buff.writeUInt16BE(val);
switch (section) {
case 'answer':
pos = 6;
// seek to authority and clear it and additional out
buff.seek(8);
buff.writeUInt16BE(0);
buff.writeUInt16BE(0);
break;
case 'authority':
pos = 8;
// seek to additional and clear it out
buff.seek(10);
buff.writeUInt16BE(0);
break;
case 'additional':
pos = 10;
break;
}
buff.seek(pos);
buff.writeUInt16BE(count - 1); // TODO: count not defined!
buff.seek(last_resource); // TODO: last_resource not defined!
return WRITE_END;
}
function writeQuestion(buff, val, label_index) {
assert(val, 'Packet requires a question');
assertUndefined(val.name, 'Question requires a "name"');
assertUndefined(val.type, 'Question requires a "type"');
assertUndefined(val.class, 'Questionn requires a "class"');
namePack(val.name, buff, label_index);
buff.writeUInt16BE(val.type & 0xFFFF);
buff.writeUInt16BE(val.class & 0xFFFF);
return WRITE_RESOURCE_RECORD;
}
function writeResource(buff, val, label_index, rdata) {
assert(val, 'Resource must be defined');
assertUndefined(val.name, 'Resource record requires "name"');
assertUndefined(val.type, 'Resource record requires "type"');
assertUndefined(val.class, 'Resource record requires "class"');
assertUndefined(val.ttl, 'Resource record requires "ttl"');
namePack(val.name, buff, label_index);
buff.writeUInt16BE(val.type & 0xFFFF);
buff.writeUInt16BE(val.class & 0xFFFF);
buff.writeUInt32BE(val.ttl & 0xFFFFFFFF);
rdata.pos = buff.tell();
buff.writeUInt16BE(0); // if there is rdata, then this value will be updated
// to the correct value by 'writeResourceDone'
return val.type;
}
function writeResourceDone(buff, rdata) {
var pos = buff.tell();
buff.seek(rdata.pos);
buff.writeUInt16BE(pos - rdata.pos - 2);
buff.seek(pos);
return WRITE_RESOURCE_RECORD;
}
function writeIp(buff, val) {
//TODO XXX FIXME -- assert that address is of proper type
assertUndefined(val.address, 'A/AAAA record requires "address"');
val = ipaddr.parse(val.address).toByteArray();
val.forEach(function(b) {
buff.writeUInt8(b);
});
return WRITE_RESOURCE_DONE;
}
function writeCname(buff, val, label_index) {
assertUndefined(val.data, 'NS/CNAME/PTR record requires "data"');
namePack(val.data, buff, label_index);
return WRITE_RESOURCE_DONE;
}
// For <character-string> see: http://tools.ietf.org/html/rfc1035#section-3.3
// For TXT: http://tools.ietf.org/html/rfc1035#section-3.3.14
function writeTxt(buff, val) {
//TODO XXX FIXME -- split on max char string and loop
assertUndefined(val.data, 'TXT record requires "data"');
for (var i=0,len=val.data.length; i<len; i++) {
var dataLen = Buffer.byteLength(val.data[i], 'utf8');
buff.writeUInt8(dataLen);
buff.write(val.data[i], dataLen, 'utf8');
}
return WRITE_RESOURCE_DONE;
}
function writeMx(buff, val, label_index) {
assertUndefined(val.priority, 'MX record requires "priority"');
assertUndefined(val.exchange, 'MX record requires "exchange"');
buff.writeUInt16BE(val.priority & 0xFFFF);
namePack(val.exchange, buff, label_index);
return WRITE_RESOURCE_DONE;
}
// SRV: https://tools.ietf.org/html/rfc2782
// TODO: SRV fixture failing for '_xmpp-server._tcp.gmail.com.srv.js'
function writeSrv(buff, val, label_index) {
assertUndefined(val.priority, 'SRV record requires "priority"');
assertUndefined(val.weight, 'SRV record requires "weight"');
assertUndefined(val.port, 'SRV record requires "port"');
assertUndefined(val.target, 'SRV record requires "target"');
buff.writeUInt16BE(val.priority & 0xFFFF);
buff.writeUInt16BE(val.weight & 0xFFFF);
buff.writeUInt16BE(val.port & 0xFFFF);
namePack(val.target, buff, label_index);
return WRITE_RESOURCE_DONE;
}
function writeSoa(buff, val, label_index) {
assertUndefined(val.primary, 'SOA record requires "primary"');
assertUndefined(val.admin, 'SOA record requires "admin"');
assertUndefined(val.serial, 'SOA record requires "serial"');
assertUndefined(val.refresh, 'SOA record requires "refresh"');
assertUndefined(val.retry, 'SOA record requires "retry"');
assertUndefined(val.expiration, 'SOA record requires "expiration"');
assertUndefined(val.minimum, 'SOA record requires "minimum"');
namePack(val.primary, buff, label_index);
namePack(val.admin, buff, label_index);
buff.writeUInt32BE(val.serial & 0xFFFFFFFF);
buff.writeInt32BE(val.refresh & 0xFFFFFFFF);
buff.writeInt32BE(val.retry & 0xFFFFFFFF);
buff.writeInt32BE(val.expiration & 0xFFFFFFFF);
buff.writeInt32BE(val.minimum & 0xFFFFFFFF);
return WRITE_RESOURCE_DONE;
}
// http://tools.ietf.org/html/rfc3403#section-4.1
function writeNaptr(buff, val, label_index) {
assertUndefined(val.order, 'NAPTR record requires "order"');
assertUndefined(val.preference, 'NAPTR record requires "preference"');
assertUndefined(val.flags, 'NAPTR record requires "flags"');
assertUndefined(val.service, 'NAPTR record requires "service"');
assertUndefined(val.regexp, 'NAPTR record requires "regexp"');
assertUndefined(val.replacement, 'NAPTR record requires "replacement"');
buff.writeUInt16BE(val.order & 0xFFFF);
buff.writeUInt16BE(val.preference & 0xFFFF);
buff.writeUInt8(val.flags.length);
buff.write(val.flags, val.flags.length, 'ascii');
buff.writeUInt8(val.service.length);
buff.write(val.service, val.service.length, 'ascii');
buff.writeUInt8(val.regexp.length);
buff.write(val.regexp, val.regexp.length, 'ascii');
namePack(val.replacement, buff, label_index);
return WRITE_RESOURCE_DONE;
}
// https://tools.ietf.org/html/rfc6698
function writeTlsa(buff, val) {
assertUndefined(val.usage, 'TLSA record requires "usage"');
assertUndefined(val.selector, 'TLSA record requires "selector"');
assertUndefined(val.matchingtype, 'TLSA record requires "matchingtype"');
assertUndefined(val.buff, 'TLSA record requires "buff"');
buff.writeUInt8(val.usage);
buff.writeUInt8(val.selector);
buff.writeUInt8(val.matchingtype);
buff.copy(val.buff);
return WRITE_RESOURCE_DONE;
}
function makeEdns(packet) {
packet.edns = {
name: '',
type: consts.NAME_TO_QTYPE.OPT,
class: packet.payload,
options: [],
ttl: 0
};
packet.edns_options = packet.edns.options; // TODO: 'edns_options' is DEPRECATED!
packet.additional.push(packet.edns);
return WRITE_HEADER;
}
function writeOpt(buff, val) {
var opt;
for (var i=0, len=val.options.length; i<len; i++) {
opt = val.options[i];
buff.writeUInt16BE(opt.code);
buff.writeUInt16BE(opt.data.length);
buff.copy(opt.data);
}
return WRITE_RESOURCE_DONE;
}
Packet.write = function(buff, packet) {
var state = WRITE_HEADER,
val,
section,
count,
rdata,
last_resource,
label_index = {};
buff = new BufferCursor(buff);
// the existence of 'edns' in a packet indicates that a proper OPT record exists
// in 'additional' and that all of the other fields in packet (that are parsed by
// 'parseOpt') are properly set. If it does not exist, we assume that the user
// is requesting that we create one for them.
if (typeof packet.edns_version !== 'undefined' && typeof packet.edns === "undefined")
state = makeEdns(packet);
// TODO: this is unnecessarily inefficient. rewrite this using a
// function table instead. (same for Packet.parse too).
while (true) {
try {
switch (state) {
case WRITE_HEADER:
state = writeHeader(buff, packet);
break;
case WRITE_TRUNCATE:
state = writeTruncate(buff, packet, section, last_resource);
break;
case WRITE_QUESTION:
state = writeQuestion(buff, packet.question[0], label_index);
section = 'answer';
count = 0;
break;
case WRITE_RESOURCE_RECORD:
last_resource = buff.tell();
if (packet[section].length == count) {
switch (section) {
case 'answer':
section = 'authority';
state = WRITE_RESOURCE_RECORD;
break;
case 'authority':
section = 'additional';
state = WRITE_RESOURCE_RECORD;
break;
case 'additional':
state = WRITE_END;
break;
}
count = 0;
} else {
state = WRITE_RESOURCE_WRITE;
}
break;
case WRITE_RESOURCE_WRITE:
rdata = {};
val = packet[section][count];
state = writeResource(buff, val, label_index, rdata);
break;
case WRITE_RESOURCE_DONE:
count += 1;
state = writeResourceDone(buff, rdata);
break;
case WRITE_A:
case WRITE_AAAA:
state = writeIp(buff, val);
break;
case WRITE_NS:
case WRITE_CNAME:
case WRITE_PTR:
state = writeCname(buff, val, label_index);
break;
case WRITE_SPF:
case WRITE_TXT:
state = writeTxt(buff, val);
break;
case WRITE_MX:
state = writeMx(buff, val, label_index);
break;
case WRITE_SRV:
state = writeSrv(buff, val, label_index);
break;
case WRITE_SOA:
state = writeSoa(buff, val, label_index);
break;
case WRITE_OPT:
state = writeOpt(buff, val);
break;
case WRITE_NAPTR:
state = writeNaptr(buff, val, label_index);
break;
case WRITE_TLSA:
state = writeTlsa(buff, val);
break;
case WRITE_END:
return buff.tell();
default:
if (typeof val.data !== 'object')
throw new Error('Packet.write Unknown State: ' + state);
// write unhandled RR type
buff.copy(val.data);
state = WRITE_RESOURCE_DONE;
}
} catch (e) {
if (e instanceof BufferCursorOverflow) {
state = WRITE_TRUNCATE;
} else {
throw e;
}
}
}
};
function parseHeader(msg, packet) {
packet.header.id = msg.readUInt16BE();
var val = msg.readUInt16BE();
packet.header.qr = (val & 0x8000) >> 15;
packet.header.opcode = (val & 0x7800) >> 11;
packet.header.aa = (val & 0x400) >> 10;
packet.header.tc = (val & 0x200) >> 9;
packet.header.rd = (val & 0x100) >> 8;
packet.header.ra = (val & 0x80) >> 7;
packet.header.res1 = (val & 0x40) >> 6;
packet.header.res2 = (val & 0x20) >> 5;
packet.header.res3 = (val & 0x10) >> 4;
packet.header.rcode = (val & 0xF);
packet.question = new Array(msg.readUInt16BE());
packet.answer = new Array(msg.readUInt16BE());
packet.authority = new Array(msg.readUInt16BE());
packet.additional = new Array(msg.readUInt16BE());
return PARSE_QUESTION;
}
function parseQuestion(msg, packet) {
var val = {};
val.name = nameUnpack(msg);
val.type = msg.readUInt16BE();
val.class = msg.readUInt16BE();
packet.question[0] = val;
assert(packet.question.length === 1);
// TODO handle qdcount > 1 in practice no one sends this
return PARSE_RESOURCE_RECORD;
}
function parseRR(msg, val, rdata) {
val.name = nameUnpack(msg);
val.type = msg.readUInt16BE();
val.class = msg.readUInt16BE();
val.ttl = msg.readUInt32BE();
rdata.len = msg.readUInt16BE();
return val.type;
}
function parseA(val, msg) {
var address = '' +
msg.readUInt8() +
'.' + msg.readUInt8() +
'.' + msg.readUInt8() +
'.' + msg.readUInt8();
val.address = address;
return PARSE_RESOURCE_DONE;
}
function parseAAAA(val, msg) {
var address = '';
var compressed = false;
for (var i = 0; i < 8; i++) {
if (i > 0) address += ':';
// TODO zero compression
address += msg.readUInt16BE().toString(16);
}
val.address = address;
return PARSE_RESOURCE_DONE;
}
function parseCname(val, msg) {
val.data = nameUnpack(msg);
return PARSE_RESOURCE_DONE;
}
function parseTxt(val, msg, rdata) {
val.data = [];
var end = msg.tell() + rdata.len;
while (msg.tell() != end) {
var len = msg.readUInt8();
val.data.push(msg.toString('utf8', len));
}
return PARSE_RESOURCE_DONE;
}
function parseMx(val, msg, rdata) {
val.priority = msg.readUInt16BE();
val.exchange = nameUnpack(msg);
return PARSE_RESOURCE_DONE;
}
// TODO: SRV fixture failing for '_xmpp-server._tcp.gmail.com.srv.js'
// https://tools.ietf.org/html/rfc2782
function parseSrv(val, msg) {
val.priority = msg.readUInt16BE();
val.weight = msg.readUInt16BE();
val.port = msg.readUInt16BE();
val.target = nameUnpack(msg);
return PARSE_RESOURCE_DONE;
}
function parseSoa(val, msg) {
val.primary = nameUnpack(msg);
val.admin = nameUnpack(msg);
val.serial = msg.readUInt32BE();
val.refresh = msg.readInt32BE();
val.retry = msg.readInt32BE();
val.expiration = msg.readInt32BE();
val.minimum = msg.readInt32BE();
return PARSE_RESOURCE_DONE;
}
// http://tools.ietf.org/html/rfc3403#section-4.1
function parseNaptr(val, msg) {
val.order = msg.readUInt16BE();
val.preference = msg.readUInt16BE();
var len = msg.readUInt8();
val.flags = msg.toString('ascii', len);
len = msg.readUInt8();
val.service = msg.toString('ascii', len);
len = msg.readUInt8();
val.regexp = msg.toString('ascii', len);
val.replacement = nameUnpack(msg);
return PARSE_RESOURCE_DONE;
}
function parseTlsa(val, msg, rdata) {
val.usage = msg.readUInt8();
val.selector = msg.readUInt8();
val.matchingtype = msg.readUInt8();
val.buff = msg.slice(rdata.len - 3).buffer; // 3 because of the 3 UInt8s above.
return PARSE_RESOURCE_DONE;
}
// https://tools.ietf.org/html/rfc6891#section-6.1.2
// https://tools.ietf.org/html/rfc2671#section-4.4
// - [payload size selection](https://tools.ietf.org/html/rfc6891#section-6.2.5)
function parseOpt(val, msg, rdata, packet) {
// assert first entry in additional
rdata.buf = msg.slice(rdata.len);
val.rcode = ((val.ttl & 0xFF000000) >> 20) + packet.header.rcode;
val.version = (val.ttl >> 16) & 0xFF;
val.do = (val.ttl >> 15) & 1;
val.z = val.ttl & 0x7F;
val.options = [];
packet.edns = val;
packet.edns_version = val.version; // TODO: return BADVERS for unsupported version! (Section 6.1.3)
// !! BEGIN DEPRECATION NOTICE !!
// THESE FIELDS MAY BE REMOVED IN THE FUTURE!
packet.edns_options = val.options;
packet.payload = val.class;
// !! END DEPRECATION NOTICE !!
while (!rdata.buf.eof()) {
val.options.push({
code: rdata.buf.readUInt16BE(),
data: rdata.buf.slice(rdata.buf.readUInt16BE()).buffer
});
}
return PARSE_RESOURCE_DONE;
}
var
PARSE_HEADER = 100000,
PARSE_QUESTION = 100001,
PARSE_RESOURCE_RECORD = 100002,
PARSE_RR_UNPACK = 100003,
PARSE_RESOURCE_DONE = 100004,
PARSE_END = 100005,
PARSE_A = consts.NAME_TO_QTYPE.A,
PARSE_NS = consts.NAME_TO_QTYPE.NS,
PARSE_CNAME = consts.NAME_TO_QTYPE.CNAME,
PARSE_SOA = consts.NAME_TO_QTYPE.SOA,
PARSE_PTR = consts.NAME_TO_QTYPE.PTR,
PARSE_MX = consts.NAME_TO_QTYPE.MX,
PARSE_TXT = consts.NAME_TO_QTYPE.TXT,
PARSE_AAAA = consts.NAME_TO_QTYPE.AAAA,
PARSE_SRV = consts.NAME_TO_QTYPE.SRV,
PARSE_NAPTR = consts.NAME_TO_QTYPE.NAPTR,
PARSE_OPT = consts.NAME_TO_QTYPE.OPT,
PARSE_SPF = consts.NAME_TO_QTYPE.SPF,
PARSE_TLSA = consts.NAME_TO_QTYPE.TLSA;
Packet.parse = function(msg) {
var state,
pos,
val,
rdata,
section,
count;
var packet = new Packet();
pos = 0;
state = PARSE_HEADER;
msg = new BufferCursor(msg);
while (true) {
switch (state) {
case PARSE_HEADER:
state = parseHeader(msg, packet);
break;
case PARSE_QUESTION:
state = parseQuestion(msg, packet);
section = 'answer';
count = 0;
break;
case PARSE_RESOURCE_RECORD:
// console.log('PARSE_RESOURCE_RECORD: count = %d, %s.len = %d', count, section, packet[section].length);
if (count === packet[section].length) {
switch (section) {
case 'answer':
section = 'authority';
count = 0;
break;
case 'authority':
section = 'additional';
count = 0;
break;
case 'additional':
state = PARSE_END;
break;
}
} else {
state = PARSE_RR_UNPACK;
}
break;
case PARSE_RR_UNPACK:
val = {};
rdata = {};
state = parseRR(msg, val, rdata);
break;
case PARSE_RESOURCE_DONE:
packet[section][count++] = val;
state = PARSE_RESOURCE_RECORD;
break;
case PARSE_A:
state = parseA(val, msg);
break;
case PARSE_AAAA:
state = parseAAAA(val, msg);
break;
case PARSE_NS:
case PARSE_CNAME:
case PARSE_PTR:
state = parseCname(val, msg);
break;
case PARSE_SPF:
case PARSE_TXT:
state = parseTxt(val, msg, rdata);
break;
case PARSE_MX:
state = parseMx(val, msg);
break;
case PARSE_SRV:
state = parseSrv(val, msg);
break;
case PARSE_SOA:
state = parseSoa(val, msg);
break;
case PARSE_OPT:
state = parseOpt(val, msg, rdata, packet);
break;
case PARSE_NAPTR:
state = parseNaptr(val, msg);
break;
case PARSE_TLSA:
state = parseTlsa(val, msg, rdata);
break;
case PARSE_END:
return packet;
default:
//console.log(state, val);
val.data = msg.slice(rdata.len);
state = PARSE_RESOURCE_DONE;
break;
}
}
};