summaryrefslogtreecommitdiffstats
path: root/src/core/object/dns
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/object/dns.c471
-rw-r--r--src/core/object/dns.h184
-rw-r--r--src/core/object/dns.hh119
-rw-r--r--src/core/object/dns.lua797
-rw-r--r--src/core/object/dns/label.lua118
-rw-r--r--src/core/object/dns/q.lua52
-rw-r--r--src/core/object/dns/rr.lua85
7 files changed, 1826 insertions, 0 deletions
diff --git a/src/core/object/dns.c b/src/core/object/dns.c
new file mode 100644
index 0000000..49aec6d
--- /dev/null
+++ b/src/core/object/dns.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "core/object/dns.h"
+#include "core/object/payload.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#else
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#else
+#ifdef HAVE_MACHINE_ENDIAN_H
+#include <machine/endian.h>
+#endif
+#endif
+#endif
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+#ifndef bswap_16
+#ifndef bswap16
+#define bswap_16(x) swap16(x)
+#define bswap_32(x) swap32(x)
+#define bswap_64(x) swap64(x)
+#else
+#define bswap_16(x) bswap16(x)
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+#endif
+#endif
+
+#define _ERR_MALFORMED -2
+#define _ERR_NEEDLABELS -3
+
+static core_log_t _log = LOG_T_INIT("core.object.dns");
+static core_object_dns_t _defaults = CORE_OBJECT_DNS_INIT(0);
+
+static core_object_dns_label_t _defaults_label = { 0 };
+static core_object_dns_rr_t _defaults_rr = { 0 };
+static core_object_dns_q_t _defaults_q = { 0 };
+
+core_log_t* core_object_dns_log()
+{
+ return &_log;
+}
+
+core_object_dns_t* core_object_dns_new()
+{
+ core_object_dns_t* self;
+
+ mlfatal_oom(self = malloc(sizeof(core_object_dns_t)));
+ *self = _defaults;
+
+ return self;
+}
+
+core_object_dns_t* core_object_dns_copy(const core_object_dns_t* self)
+{
+ core_object_dns_t* copy;
+ mlassert_self();
+
+ mlfatal_oom(copy = malloc(sizeof(core_object_dns_t)));
+ memcpy(copy, self, sizeof(core_object_dns_t));
+ copy->obj_prev = 0;
+
+ return (core_object_dns_t*)copy;
+}
+
+void core_object_dns_free(core_object_dns_t* self)
+{
+ mlassert_self();
+ free(self);
+}
+
+#define need8(v, p, l) \
+ if (l < 1) { \
+ break; \
+ } \
+ v = *p; \
+ p += 1; \
+ l -= 1
+
+static inline uint16_t _need16(const void* ptr)
+{
+ uint16_t v;
+ memcpy(&v, ptr, sizeof(v));
+ return be16toh(v);
+}
+
+#define need16(v, p, l) \
+ if (l < 2) { \
+ break; \
+ } \
+ v = _need16(p); \
+ p += 2; \
+ l -= 2
+
+static inline uint32_t _need32(const void* ptr)
+{
+ uint32_t v;
+ memcpy(&v, ptr, sizeof(v));
+ return be32toh(v);
+}
+
+#define need32(v, p, l) \
+ if (l < 4) { \
+ break; \
+ } \
+ v = _need32(p); \
+ p += 4; \
+ l -= 4
+
+#define needxb(b, x, p, l) \
+ if (l < x) { \
+ break; \
+ } \
+ memcpy(b, p, x); \
+ p += x; \
+ l -= x
+
+#define advancexb(x, p, l) \
+ if (l < x) { \
+ break; \
+ } \
+ p += x; \
+ l -= x
+
+int core_object_dns_parse_header(core_object_dns_t* self)
+{
+ const core_object_payload_t* payload;
+ uint8_t byte;
+ mlassert_self();
+
+ if (!(payload = (core_object_payload_t*)self->obj_prev) || payload->obj_type != CORE_OBJECT_PAYLOAD) {
+ mlfatal("no obj_prev or invalid type");
+ }
+ if (!payload->payload || !payload->len) {
+ mlfatal("no payload set or zero length");
+ }
+
+ self->payload = self->at = payload->payload;
+ self->len = self->left = payload->len;
+
+ for (;;) {
+ if (self->includes_dnslen) {
+ need16(self->dnslen, self->at, self->left);
+ self->have_dnslen = 1;
+ }
+ need16(self->id, self->at, self->left);
+ self->have_id = 1;
+
+ need8(byte, self->at, self->left);
+ self->qr = byte & (1 << 7) ? 1 : 0;
+ self->opcode = (byte >> 3) & 0xf;
+ self->aa = byte & (1 << 2) ? 1 : 0;
+ self->tc = byte & (1 << 1) ? 1 : 0;
+ self->rd = byte & (1 << 0) ? 1 : 0;
+ self->have_qr = self->have_opcode = self->have_aa = self->have_tc = self->have_rd = 1;
+
+ need8(byte, self->at, self->left);
+ self->ra = byte & (1 << 7) ? 1 : 0;
+ self->z = byte & (1 << 6) ? 1 : 0;
+ self->ad = byte & (1 << 5) ? 1 : 0;
+ self->cd = byte & (1 << 4) ? 1 : 0;
+ self->rcode = byte & 0xf;
+ self->have_ra = self->have_z = self->have_ad = self->have_cd = self->have_rcode = 1;
+
+ need16(self->qdcount, self->at, self->left);
+ self->have_qdcount = 1;
+
+ need16(self->ancount, self->at, self->left);
+ self->have_ancount = 1;
+
+ need16(self->nscount, self->at, self->left);
+ self->have_nscount = 1;
+
+ need16(self->arcount, self->at, self->left);
+ self->have_arcount = 1;
+
+ return 0;
+ }
+
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+}
+
+static inline size_t _rdata_labels(uint16_t type)
+{
+ switch (type) {
+ case CORE_OBJECT_DNS_TYPE_NS:
+ case CORE_OBJECT_DNS_TYPE_MD:
+ case CORE_OBJECT_DNS_TYPE_MF:
+ case CORE_OBJECT_DNS_TYPE_CNAME:
+ case CORE_OBJECT_DNS_TYPE_MB:
+ case CORE_OBJECT_DNS_TYPE_MG:
+ case CORE_OBJECT_DNS_TYPE_MR:
+ case CORE_OBJECT_DNS_TYPE_PTR:
+ case CORE_OBJECT_DNS_TYPE_NXT:
+ case CORE_OBJECT_DNS_TYPE_DNAME:
+ case CORE_OBJECT_DNS_TYPE_NSEC:
+ case CORE_OBJECT_DNS_TYPE_TKEY:
+ case CORE_OBJECT_DNS_TYPE_TSIG:
+ return 1;
+
+ case CORE_OBJECT_DNS_TYPE_SOA:
+ case CORE_OBJECT_DNS_TYPE_MINFO:
+ case CORE_OBJECT_DNS_TYPE_RP:
+ case CORE_OBJECT_DNS_TYPE_TALINK:
+ return 2;
+
+ case CORE_OBJECT_DNS_TYPE_MX:
+ case CORE_OBJECT_DNS_TYPE_AFSDB:
+ case CORE_OBJECT_DNS_TYPE_RT:
+ case CORE_OBJECT_DNS_TYPE_KX:
+ case CORE_OBJECT_DNS_TYPE_LP:
+ return 1;
+
+ case CORE_OBJECT_DNS_TYPE_PX:
+ return 2;
+
+ case CORE_OBJECT_DNS_TYPE_SIG:
+ case CORE_OBJECT_DNS_TYPE_RRSIG:
+ return 1;
+
+ case CORE_OBJECT_DNS_TYPE_SRV:
+ return 1;
+
+ case CORE_OBJECT_DNS_TYPE_NAPTR:
+ return 1;
+
+ case CORE_OBJECT_DNS_TYPE_HIP:
+ return 1;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static inline size_t _label(core_object_dns_t* self, core_object_dns_label_t* label, size_t labels)
+{
+ size_t n;
+
+ for (n = 0; self->left && n < labels; n++) {
+ core_object_dns_label_t* l = &label[n];
+ *l = _defaults_label;
+
+ need8(l->length, self->at, self->left);
+
+ if ((l->length & 0xc0) == 0xc0) {
+ need8(l->offset, self->at, self->left);
+ l->offset |= (l->length & 0x3f) << 8;
+ l->have_offset = 1;
+ return n;
+ } else if (l->length & 0xc0) {
+ l->extension_bits = l->length >> 6;
+ l->have_extension_bits = 1;
+ return n;
+ } else if (l->length) {
+ l->have_length = 1;
+
+ l->offset = self->at - self->payload - 1;
+ advancexb(l->length, self->at, self->left);
+ l->have_dn = 1;
+ } else {
+ l->is_end = 1;
+ return n;
+ }
+ }
+
+ return n;
+}
+
+int core_object_dns_parse_q(core_object_dns_t* self, core_object_dns_q_t* q, core_object_dns_label_t* label, size_t labels)
+{
+ mlassert_self();
+ mlassert(q, "q is nil");
+ mlassert(label, "label is nil");
+ mlassert(labels, "labels is zero");
+ mlassert(self->at, "at is nil");
+
+ for (;;) {
+ *q = _defaults_q;
+ q->labels = _label(self, label, labels);
+ if (q->labels < labels) {
+ core_object_dns_label_t* l = &label[q->labels];
+ if (!(l->have_offset | l->have_extension_bits | l->is_end)) {
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+ }
+ } else {
+ mlwarning("need more labels, aborting DNS parsing");
+ return _ERR_NEEDLABELS;
+ }
+ q->labels++;
+
+ need16(q->type, self->at, self->left);
+ q->have_type = 1;
+
+ need16(q->class, self->at, self->left);
+ q->have_class = 1;
+
+ return 0;
+ }
+
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+}
+
+int core_object_dns_parse_rr(core_object_dns_t* self, core_object_dns_rr_t* rr, core_object_dns_label_t* label, size_t labels)
+{
+ size_t rdata_label_sets;
+ mlassert_self();
+ mlassert(rr, "rr is nil");
+ mlassert(label, "label is nil");
+ mlassert(labels, "labels is zero");
+ mlassert(self->at, "at is nil");
+
+ for (;;) {
+ *rr = _defaults_rr;
+ rr->labels = _label(self, label, labels);
+ if (rr->labels < labels) {
+ core_object_dns_label_t* l = &label[rr->labels];
+ if (!(l->have_offset | l->have_extension_bits | l->is_end)) {
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+ }
+ } else {
+ mlwarning("need more labels, aborting DNS parsing");
+ return _ERR_NEEDLABELS;
+ }
+ rr->labels++;
+
+ need16(rr->type, self->at, self->left);
+ rr->have_type = 1;
+
+ need16(rr->class, self->at, self->left);
+ rr->have_class = 1;
+
+ need32(rr->ttl, self->at, self->left);
+ rr->have_ttl = 1;
+
+ need16(rr->rdlength, self->at, self->left);
+ rr->have_rdlength = 1;
+
+ rr->rdata_offset = self->at - self->payload;
+ if (!(rdata_label_sets = _rdata_labels(rr->type))) {
+ advancexb(rr->rdlength, self->at, self->left);
+ rr->have_rdata = 1;
+ return 0;
+ }
+
+ switch (rr->type) {
+ case CORE_OBJECT_DNS_TYPE_MX:
+ case CORE_OBJECT_DNS_TYPE_AFSDB:
+ case CORE_OBJECT_DNS_TYPE_RT:
+ case CORE_OBJECT_DNS_TYPE_KX:
+ case CORE_OBJECT_DNS_TYPE_LP:
+ case CORE_OBJECT_DNS_TYPE_PX:
+ advancexb(2, self->at, self->left);
+ break;
+
+ case CORE_OBJECT_DNS_TYPE_SIG:
+ case CORE_OBJECT_DNS_TYPE_RRSIG:
+ advancexb(18, self->at, self->left);
+ break;
+
+ case CORE_OBJECT_DNS_TYPE_SRV:
+ advancexb(6, self->at, self->left);
+ break;
+
+ case CORE_OBJECT_DNS_TYPE_NAPTR: {
+ uint8_t naptr_length;
+
+ advancexb(4, self->at, self->left);
+ need8(naptr_length, self->at, self->left);
+ advancexb(naptr_length, self->at, self->left);
+ need8(naptr_length, self->at, self->left);
+ advancexb(naptr_length, self->at, self->left);
+ need8(naptr_length, self->at, self->left);
+ advancexb(naptr_length, self->at, self->left);
+ } break;
+
+ case CORE_OBJECT_DNS_TYPE_HIP: {
+ uint8_t hit_length;
+ uint16_t pk_length;
+
+ need8(hit_length, self->at, self->left);
+ advancexb(1, self->at, self->left);
+ need16(pk_length, self->at, self->left);
+ advancexb(hit_length, self->at, self->left);
+ advancexb(pk_length, self->at, self->left);
+
+ if (self->at - self->payload >= rr->rdata_offset + rr->rdlength) {
+ rdata_label_sets = 0;
+ }
+ } break;
+ }
+
+ while (rdata_label_sets) {
+ rr->rdata_labels += _label(self, &label[rr->labels + rr->rdata_labels], labels - rr->labels - rr->rdata_labels);
+ if (rr->labels + rr->rdata_labels < labels) {
+ core_object_dns_label_t* l = &label[rr->labels + rr->rdata_labels];
+ if (!(l->have_offset | l->have_extension_bits | l->is_end)) {
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+ }
+ } else {
+ mlwarning("need more labels, aborting DNS parsing");
+ return _ERR_NEEDLABELS;
+ }
+ rr->rdata_labels++;
+
+ if (rr->type == CORE_OBJECT_DNS_TYPE_HIP && self->at - self->payload < rr->rdata_offset + rr->rdlength) {
+ continue;
+ }
+
+ rdata_label_sets--;
+ }
+
+ if (self->at - self->payload < rr->rdata_offset + rr->rdlength) {
+ rr->padding_offset = self->at - self->payload;
+ rr->padding_length = rr->rdlength - (rr->padding_offset - rr->rdata_offset);
+
+ advancexb(rr->padding_length, self->at, self->left);
+
+ /*
+ * TODO:
+ *
+ * This can indicate padding but we do not set that we have padding
+ * yet because we need to fully understand all record types before
+ * that and process valid data after the labels
+ *
+ rr->have_padding = 1;
+ */
+ } else if (self->at - self->payload > rr->rdata_offset + rr->rdlength) {
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+ }
+ rr->have_rdata = 1;
+
+ return 0;
+ }
+
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+}
diff --git a/src/core/object/dns.h b/src/core/object/dns.h
new file mode 100644
index 0000000..5fbaf53
--- /dev/null
+++ b/src/core/object/dns.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "core/log.h"
+#include "core/object.h"
+
+#ifndef __dnsjit_core_object_dns_h
+#define __dnsjit_core_object_dns_h
+
+#include <netinet/in.h>
+#include <sys/types.h>
+
+#include "core/object/dns.hh"
+
+#define CORE_OBJECT_DNS_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_DNS, prev) \
+ , \
+ 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ }
+
+/*
+ * 2016-12-09 https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
+ */
+
+#define CORE_OBJECT_DNS_CLASS_IN 1
+#define CORE_OBJECT_DNS_CLASS_CH 3
+#define CORE_OBJECT_DNS_CLASS_HS 4
+#define CORE_OBJECT_DNS_CLASS_NONE 254
+#define CORE_OBJECT_DNS_CLASS_ANY 255
+
+#define CORE_OBJECT_DNS_TYPE_A 1
+#define CORE_OBJECT_DNS_TYPE_NS 2
+#define CORE_OBJECT_DNS_TYPE_MD 3
+#define CORE_OBJECT_DNS_TYPE_MF 4
+#define CORE_OBJECT_DNS_TYPE_CNAME 5
+#define CORE_OBJECT_DNS_TYPE_SOA 6
+#define CORE_OBJECT_DNS_TYPE_MB 7
+#define CORE_OBJECT_DNS_TYPE_MG 8
+#define CORE_OBJECT_DNS_TYPE_MR 9
+#define CORE_OBJECT_DNS_TYPE_NULL 10
+#define CORE_OBJECT_DNS_TYPE_WKS 11
+#define CORE_OBJECT_DNS_TYPE_PTR 12
+#define CORE_OBJECT_DNS_TYPE_HINFO 13
+#define CORE_OBJECT_DNS_TYPE_MINFO 14
+#define CORE_OBJECT_DNS_TYPE_MX 15
+#define CORE_OBJECT_DNS_TYPE_TXT 16
+#define CORE_OBJECT_DNS_TYPE_RP 17
+#define CORE_OBJECT_DNS_TYPE_AFSDB 18
+#define CORE_OBJECT_DNS_TYPE_X25 19
+#define CORE_OBJECT_DNS_TYPE_ISDN 20
+#define CORE_OBJECT_DNS_TYPE_RT 21
+#define CORE_OBJECT_DNS_TYPE_NSAP 22
+#define CORE_OBJECT_DNS_TYPE_NSAP_PTR 23
+#define CORE_OBJECT_DNS_TYPE_SIG 24
+#define CORE_OBJECT_DNS_TYPE_KEY 25
+#define CORE_OBJECT_DNS_TYPE_PX 26
+#define CORE_OBJECT_DNS_TYPE_GPOS 27
+#define CORE_OBJECT_DNS_TYPE_AAAA 28
+#define CORE_OBJECT_DNS_TYPE_LOC 29
+#define CORE_OBJECT_DNS_TYPE_NXT 30
+#define CORE_OBJECT_DNS_TYPE_EID 31
+#define CORE_OBJECT_DNS_TYPE_NIMLOC 32
+#define CORE_OBJECT_DNS_TYPE_SRV 33
+#define CORE_OBJECT_DNS_TYPE_ATMA 34
+#define CORE_OBJECT_DNS_TYPE_NAPTR 35
+#define CORE_OBJECT_DNS_TYPE_KX 36
+#define CORE_OBJECT_DNS_TYPE_CERT 37
+#define CORE_OBJECT_DNS_TYPE_A6 38
+#define CORE_OBJECT_DNS_TYPE_DNAME 39
+#define CORE_OBJECT_DNS_TYPE_SINK 40
+#define CORE_OBJECT_DNS_TYPE_OPT 41
+#define CORE_OBJECT_DNS_TYPE_APL 42
+#define CORE_OBJECT_DNS_TYPE_DS 43
+#define CORE_OBJECT_DNS_TYPE_SSHFP 44
+#define CORE_OBJECT_DNS_TYPE_IPSECKEY 45
+#define CORE_OBJECT_DNS_TYPE_RRSIG 46
+#define CORE_OBJECT_DNS_TYPE_NSEC 47
+#define CORE_OBJECT_DNS_TYPE_DNSKEY 48
+#define CORE_OBJECT_DNS_TYPE_DHCID 49
+#define CORE_OBJECT_DNS_TYPE_NSEC3 50
+#define CORE_OBJECT_DNS_TYPE_NSEC3PARAM 51
+#define CORE_OBJECT_DNS_TYPE_TLSA 52
+#define CORE_OBJECT_DNS_TYPE_SMIMEA 53
+#define CORE_OBJECT_DNS_TYPE_HIP 55
+#define CORE_OBJECT_DNS_TYPE_NINFO 56
+#define CORE_OBJECT_DNS_TYPE_RKEY 57
+#define CORE_OBJECT_DNS_TYPE_TALINK 58
+#define CORE_OBJECT_DNS_TYPE_CDS 59
+#define CORE_OBJECT_DNS_TYPE_CDNSKEY 60
+#define CORE_OBJECT_DNS_TYPE_OPENPGPKEY 61
+#define CORE_OBJECT_DNS_TYPE_CSYNC 62
+#define CORE_OBJECT_DNS_TYPE_SPF 99
+#define CORE_OBJECT_DNS_TYPE_UINFO 100
+#define CORE_OBJECT_DNS_TYPE_UID 101
+#define CORE_OBJECT_DNS_TYPE_GID 102
+#define CORE_OBJECT_DNS_TYPE_UNSPEC 103
+#define CORE_OBJECT_DNS_TYPE_NID 104
+#define CORE_OBJECT_DNS_TYPE_L32 105
+#define CORE_OBJECT_DNS_TYPE_L64 106
+#define CORE_OBJECT_DNS_TYPE_LP 107
+#define CORE_OBJECT_DNS_TYPE_EUI48 108
+#define CORE_OBJECT_DNS_TYPE_EUI64 109
+#define CORE_OBJECT_DNS_TYPE_TKEY 249
+#define CORE_OBJECT_DNS_TYPE_TSIG 250
+#define CORE_OBJECT_DNS_TYPE_IXFR 251
+#define CORE_OBJECT_DNS_TYPE_AXFR 252
+#define CORE_OBJECT_DNS_TYPE_MAILB 253
+#define CORE_OBJECT_DNS_TYPE_MAILA 254
+#define CORE_OBJECT_DNS_TYPE_ANY 255
+#define CORE_OBJECT_DNS_TYPE_URI 256
+#define CORE_OBJECT_DNS_TYPE_CAA 257
+#define CORE_OBJECT_DNS_TYPE_AVC 258
+#define CORE_OBJECT_DNS_TYPE_TA 32768
+#define CORE_OBJECT_DNS_TYPE_DLV 32769
+
+#define CORE_OBJECT_DNS_OPCODE_QUERY 0
+#define CORE_OBJECT_DNS_OPCODE_IQUERY 1
+#define CORE_OBJECT_DNS_OPCODE_STATUS 2
+#define CORE_OBJECT_DNS_OPCODE_NOTIFY 4
+#define CORE_OBJECT_DNS_OPCODE_UPDATE 5
+
+#define CORE_OBJECT_DNS_RCODE_NOERROR 0
+#define CORE_OBJECT_DNS_RCODE_FORMERR 1
+#define CORE_OBJECT_DNS_RCODE_SERVFAIL 2
+#define CORE_OBJECT_DNS_RCODE_NXDOMAIN 3
+#define CORE_OBJECT_DNS_RCODE_NOTIMP 4
+#define CORE_OBJECT_DNS_RCODE_REFUSED 5
+#define CORE_OBJECT_DNS_RCODE_YXDOMAIN 6
+#define CORE_OBJECT_DNS_RCODE_YXRRSET 7
+#define CORE_OBJECT_DNS_RCODE_NXRRSET 8
+#define CORE_OBJECT_DNS_RCODE_NOTAUTH 9
+#define CORE_OBJECT_DNS_RCODE_NOTZONE 10
+#define CORE_OBJECT_DNS_RCODE_BADVERS 16
+#define CORE_OBJECT_DNS_RCODE_BADSIG 16
+#define CORE_OBJECT_DNS_RCODE_BADKEY 17
+#define CORE_OBJECT_DNS_RCODE_BADTIME 18
+#define CORE_OBJECT_DNS_RCODE_BADMODE 19
+#define CORE_OBJECT_DNS_RCODE_BADNAME 20
+#define CORE_OBJECT_DNS_RCODE_BADALG 21
+#define CORE_OBJECT_DNS_RCODE_BADTRUNC 22
+#define CORE_OBJECT_DNS_RCODE_BADCOOKIE 23
+
+#define CORE_OBJECT_DNS_AFSDB_SUBTYPE_AFS3LOCSRV 1
+#define CORE_OBJECT_DNS_AFSDB_SUBTYPE_DCENCA_ROOT 2
+
+#define CORE_OBJECT_DNS_DHCID_TYPE_1OCTET 0
+#define CORE_OBJECT_DNS_DHCID_TYPE_DATAOCTET 1
+#define CORE_OBJECT_DNS_DHCID_TYPE_CLIENT_DUID 2
+
+#define CORE_OBJECT_DNS_EDNS0_OPT_LLQ 1
+#define CORE_OBJECT_DNS_EDNS0_OPT_UL 2
+#define CORE_OBJECT_DNS_EDNS0_OPT_NSID 3
+#define CORE_OBJECT_DNS_EDNS0_OPT_DAU 5
+#define CORE_OBJECT_DNS_EDNS0_OPT_DHU 6
+#define CORE_OBJECT_DNS_EDNS0_OPT_N3U 7
+#define CORE_OBJECT_DNS_EDNS0_OPT_CLIENT_SUBNET 8
+#define CORE_OBJECT_DNS_EDNS0_OPT_EXPIRE 9
+#define CORE_OBJECT_DNS_EDNS0_OPT_COOKIE 10
+#define CORE_OBJECT_DNS_EDNS0_OPT_TCP_KEEPALIVE 11
+#define CORE_OBJECT_DNS_EDNS0_OPT_PADDING 12
+#define CORE_OBJECT_DNS_EDNS0_OPT_CHAIN 13
+#define CORE_OBJECT_DNS_EDNS0_OPT_DEVICEID 26946
+
+#endif
diff --git a/src/core/object/dns.hh b/src/core/object/dns.hh
new file mode 100644
index 0000000..dfe9719
--- /dev/null
+++ b/src/core/object/dns.hh
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//lua:require("dnsjit.core.log")
+//lua:require("dnsjit.core.object_h")
+
+typedef struct core_object_dns_label {
+ uint8_t is_end;
+ uint8_t have_length;
+ uint8_t have_offset;
+ uint8_t have_extension_bits;
+ uint8_t have_dn;
+ uint8_t extension_bits;
+
+ uint8_t length;
+ uint16_t offset;
+} core_object_dns_label_t;
+
+typedef struct core_object_dns_rr {
+ uint8_t have_type;
+ uint8_t have_class;
+ uint8_t have_ttl;
+ uint8_t have_rdlength;
+ uint8_t have_rdata;
+ uint8_t have_rdata_labels;
+ uint8_t have_padding;
+
+ uint16_t type;
+ uint16_t class;
+ uint32_t ttl;
+ uint16_t rdlength;
+
+ size_t labels;
+ size_t rdata_offset;
+ size_t rdata_labels;
+ size_t padding_offset;
+ size_t padding_length;
+} core_object_dns_rr_t;
+
+typedef struct core_object_dns_q {
+ uint8_t have_type;
+ uint8_t have_class;
+
+ uint16_t type;
+ uint16_t class;
+
+ size_t labels;
+} core_object_dns_q_t;
+
+typedef struct core_object_dns {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ int includes_dnslen;
+ const uint8_t *payload, *at;
+ size_t len, left;
+
+ uint8_t have_dnslen;
+ uint8_t have_id;
+ uint8_t have_qr;
+ uint8_t have_opcode;
+ uint8_t have_aa;
+ uint8_t have_tc;
+ uint8_t have_rd;
+ uint8_t have_ra;
+ uint8_t have_z;
+ uint8_t have_ad;
+ uint8_t have_cd;
+ uint8_t have_rcode;
+ uint8_t have_qdcount;
+ uint8_t have_ancount;
+ uint8_t have_nscount;
+ uint8_t have_arcount;
+
+ uint16_t dnslen;
+ uint16_t id;
+ int8_t qr;
+ uint8_t opcode;
+ uint8_t aa;
+ uint8_t tc;
+ uint8_t rd;
+ uint8_t ra;
+ uint8_t z;
+ uint8_t ad;
+ uint8_t cd;
+ uint8_t rcode;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+} core_object_dns_t;
+
+core_log_t* core_object_dns_log();
+
+core_object_dns_t* core_object_dns_new();
+core_object_dns_t* core_object_dns_copy(const core_object_dns_t* self);
+void core_object_dns_free(core_object_dns_t* self);
+void core_object_dns_reset(core_object_dns_t* self, const core_object_t* obj);
+
+int core_object_dns_parse_header(core_object_dns_t* self);
+int core_object_dns_parse_q(core_object_dns_t* self, core_object_dns_q_t* q, core_object_dns_label_t* label, size_t labels);
+int core_object_dns_parse_rr(core_object_dns_t* self, core_object_dns_rr_t* rr, core_object_dns_label_t* label, size_t labels);
diff --git a/src/core/object/dns.lua b/src/core/object/dns.lua
new file mode 100644
index 0000000..e8fb12b
--- /dev/null
+++ b/src/core/object/dns.lua
@@ -0,0 +1,797 @@
+-- Copyright (c) 2018-2021, OARC, Inc.
+-- All rights reserved.
+--
+-- This file is part of dnsjit.
+--
+-- dnsjit is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- dnsjit is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+
+-- dnsjit.core.object.dns
+-- Container of a DNS message
+-- .SS Parse DNS header and check if query or response
+-- local dns = require("dnsjit.core.object.dns").new(payload)
+-- if dns:parse_header() == 0 then
+-- if dns.qr == 0 then
+-- print(dns.id, dns.opcode_tostring(dns.opcode))
+-- else
+-- print(dns.id, dns.rcode_tostring(dns.rcode))
+-- end
+-- end
+-- .SS Print a DNS payload
+-- local dns = require("dnsjit.core.object.dns").new(payload)
+-- dns:print()
+-- .SS Parse a DNS payload
+-- local dns = require("dnsjit.core.object.dns").new(payload)
+-- local qs, q_labels, rrs, rr_labels = dns:parse()
+-- if qs and q_labels then
+-- ...
+-- if rrs and rr_labels then
+-- ...
+-- end
+-- end
+--
+-- The object that describes a DNS message.
+-- .SS Attributes
+-- .TP
+-- includes_dnslen
+-- If non-zero then this indicates that the DNS length is included in the
+-- payload (for example if the transport is TCP) and will affect parsing of it.
+-- .TP
+-- have_dnslen
+-- Set if the dnslen was included in the payload and could be read.
+-- .TP
+-- have_id
+-- Set if there is a DNS ID.
+-- .TP
+-- have_qr
+-- Set if there is a QR flag.
+-- .TP
+-- have_opcode
+-- Set if there is an OPCODE.
+-- .TP
+-- have_aa
+-- Set if there is a AA flag.
+-- .TP
+-- have_tc
+-- Set if there is a TC flag.
+-- .TP
+-- have_rd
+-- Set if there is a RD flag.
+-- .TP
+-- have_ra
+-- Set if there is a RA flag.
+-- .TP
+-- have_z
+-- Set if there is a Z flag.
+-- .TP
+-- have_ad
+-- Set if there is a AD flag.
+-- .TP
+-- have_cd
+-- Set if there is a CD flag.
+-- .TP
+-- have_rcode
+-- Set if there is a RCODE.
+-- .TP
+-- have_qdcount
+-- Set if there is an QDCOUNT.
+-- .TP
+-- have_ancount
+-- Set if there is an ANCOUNT.
+-- .TP
+-- have_nscount
+-- Set if there is a NSCOUNT.
+-- .TP
+-- have_arcount
+-- Set if there is an ARCOUNT.
+-- .TP
+-- dnslen
+-- The DNS length found in the payload.
+-- .TP
+-- id
+-- The DNS ID.
+-- .TP
+-- qr
+-- The QR flag.
+-- .TP
+-- opcode
+-- The OPCODE.
+-- .TP
+-- aa
+-- The AA flag.
+-- .TP
+-- tc
+-- The TC flag.
+-- .TP
+-- rd
+-- The RD flag.
+-- .TP
+-- ra
+-- The RA flag.
+-- .TP
+-- z
+-- The Z flag.
+-- .TP
+-- ad
+-- The AD flag.
+-- .TP
+-- cd
+-- The CD flag.
+-- .TP
+-- rcode
+-- The RCODE.
+-- .TP
+-- qdcount
+-- The QDCOUNT.
+-- .TP
+-- ancount
+-- The ANCOUNT.
+-- .TP
+-- nscount
+-- The NSCOUNT.
+-- .TP
+-- arcount
+-- The ARCOUNT.
+-- .SS Constants
+-- The following tables exists for DNS parameters, taken from
+-- .I https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
+-- on the 2016-12-09.
+-- .LP
+-- .IR CLASS ,
+-- .IR CLASS_STR ,
+-- .IR TYPE ,
+-- .IR TYPE_STR ,
+-- .IR OPCODE ,
+-- .IR OPCODE_STR ,
+-- .IR RCODE ,
+-- .IR RCODE_STR ,
+-- .IR AFSDB ,
+-- .IR AFSDB_STR ,
+-- .IR DHCID ,
+-- .IR DHCID_STR ,
+-- .IR ENDS0 ,
+-- .IR ENDS0_STR
+-- .LP
+-- The
+-- .I *_STR
+-- tables can be used to get a textual representation of the numbers, see also
+-- .IR class_tostring() ,
+-- .IR type_tostring() ,
+-- .IR opcode_tostring() ,
+-- .IR rcode_tostring() ,
+-- .IR afsdb_tostring() ,
+-- .I dhcid_tostring()
+-- and
+-- .IR edns0_tostring() .
+module(...,package.seeall)
+
+require("dnsjit.core.object.dns_h")
+local label = require("dnsjit.core.object.dns.label")
+local Q = require("dnsjit.core.object.dns.q")
+local RR = require("dnsjit.core.object.dns.rr")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_dns_t"
+local core_object_dns_t
+local Dns = {
+ CLASS = {
+ IN = 1,
+ CH = 3,
+ HS = 4,
+ NONE = 254,
+ ANY = 255,
+ },
+ TYPE = {
+ A = 1,
+ NS = 2,
+ MD = 3,
+ MF = 4,
+ CNAME = 5,
+ SOA = 6,
+ MB = 7,
+ MG = 8,
+ MR = 9,
+ NULL = 10,
+ WKS = 11,
+ PTR = 12,
+ HINFO = 13,
+ MINFO = 14,
+ MX = 15,
+ TXT = 16,
+ RP = 17,
+ AFSDB = 18,
+ X25 = 19,
+ ISDN = 20,
+ RT = 21,
+ NSAP = 22,
+ NSAP_PTR = 23,
+ SIG = 24,
+ KEY = 25,
+ PX = 26,
+ GPOS = 27,
+ AAAA = 28,
+ LOC = 29,
+ NXT = 30,
+ EID = 31,
+ NIMLOC = 32,
+ SRV = 33,
+ ATMA = 34,
+ NAPTR = 35,
+ KX = 36,
+ CERT = 37,
+ A6 = 38,
+ DNAME = 39,
+ SINK = 40,
+ OPT = 41,
+ APL = 42,
+ DS = 43,
+ SSHFP = 44,
+ IPSECKEY = 45,
+ RRSIG = 46,
+ NSEC = 47,
+ DNSKEY = 48,
+ DHCID = 49,
+ NSEC3 = 50,
+ NSEC3PARAM = 51,
+ TLSA = 52,
+ SMIMEA = 53,
+ HIP = 55,
+ NINFO = 56,
+ RKEY = 57,
+ TALINK = 58,
+ CDS = 59,
+ CDNSKEY = 60,
+ OPENPGPKEY = 61,
+ CSYNC = 62,
+ SPF = 99,
+ UINFO = 100,
+ UID = 101,
+ GID = 102,
+ UNSPEC = 103,
+ NID = 104,
+ L32 = 105,
+ L64 = 106,
+ LP = 107,
+ EUI48 = 108,
+ EUI64 = 109,
+ TKEY = 249,
+ TSIG = 250,
+ IXFR = 251,
+ AXFR = 252,
+ MAILB = 253,
+ MAILA = 254,
+ ANY = 255,
+ URI = 256,
+ CAA = 257,
+ AVC = 258,
+ TA = 32768,
+ DLV = 32769,
+ },
+ OPCODE = {
+ QUERY = 0,
+ IQUERY = 1,
+ STATUS = 2,
+ NOTIFY = 4,
+ UPDATE = 5,
+ },
+ RCODE = {
+ NOERROR = 0,
+ FORMERR = 1,
+ SERVFAIL = 2,
+ NXDOMAIN = 3,
+ NOTIMP = 4,
+ REFUSED = 5,
+ YXDOMAIN = 6,
+ YXRRSET = 7,
+ NXRRSET = 8,
+ NOTAUTH = 9,
+ NOTZONE = 10,
+ BADVERS = 16,
+ BADSIG = 16,
+ BADKEY = 17,
+ BADTIME = 18,
+ BADMODE = 19,
+ BADNAME = 20,
+ BADALG = 21,
+ BADTRUNC = 22,
+ BADCOOKIE = 23,
+ },
+ AFSDB = {
+ SUBTYPE_AFS3LOCSRV = 1,
+ SUBTYPE_DCENCA_ROOT = 2,
+ },
+ DHCID = {
+ TYPE_1OCTET = 0,
+ TYPE_DATAOCTET = 1,
+ TYPE_CLIENT_DUID = 2,
+ },
+ EDNS0 = {
+ OPT_LLQ = 1,
+ OPT_UL = 2,
+ OPT_NSID = 3,
+ OPT_DAU = 5,
+ OPT_DHU = 6,
+ OPT_N3U = 7,
+ OPT_CLIENT_SUBNET = 8,
+ OPT_EXPIRE = 9,
+ OPT_COOKIE = 10,
+ OPT_TCP_KEEPALIVE = 11,
+ OPT_PADDING = 12,
+ OPT_CHAIN = 13,
+ OPT_DEVICEID = 26946,
+ },
+}
+local _CLASS = {}
+_CLASS[Dns.CLASS.IN] = "IN"
+_CLASS[Dns.CLASS.CH] = "CH"
+_CLASS[Dns.CLASS.HS] = "HS"
+_CLASS[Dns.CLASS.NONE] = "NONE"
+_CLASS[Dns.CLASS.ANY] = "ANY"
+local _TYPE = {}
+_TYPE[Dns.TYPE.A] = "A"
+_TYPE[Dns.TYPE.NS] = "NS"
+_TYPE[Dns.TYPE.MD] = "MD"
+_TYPE[Dns.TYPE.MF] = "MF"
+_TYPE[Dns.TYPE.CNAME] = "CNAME"
+_TYPE[Dns.TYPE.SOA] = "SOA"
+_TYPE[Dns.TYPE.MB] = "MB"
+_TYPE[Dns.TYPE.MG] = "MG"
+_TYPE[Dns.TYPE.MR] = "MR"
+_TYPE[Dns.TYPE.NULL] = "NULL"
+_TYPE[Dns.TYPE.WKS] = "WKS"
+_TYPE[Dns.TYPE.PTR] = "PTR"
+_TYPE[Dns.TYPE.HINFO] = "HINFO"
+_TYPE[Dns.TYPE.MINFO] = "MINFO"
+_TYPE[Dns.TYPE.MX] = "MX"
+_TYPE[Dns.TYPE.TXT] = "TXT"
+_TYPE[Dns.TYPE.RP] = "RP"
+_TYPE[Dns.TYPE.AFSDB] = "AFSDB"
+_TYPE[Dns.TYPE.X25] = "X25"
+_TYPE[Dns.TYPE.ISDN] = "ISDN"
+_TYPE[Dns.TYPE.RT] = "RT"
+_TYPE[Dns.TYPE.NSAP] = "NSAP"
+_TYPE[Dns.TYPE.NSAP_PTR] = "NSAP_PTR"
+_TYPE[Dns.TYPE.SIG] = "SIG"
+_TYPE[Dns.TYPE.KEY] = "KEY"
+_TYPE[Dns.TYPE.PX] = "PX"
+_TYPE[Dns.TYPE.GPOS] = "GPOS"
+_TYPE[Dns.TYPE.AAAA] = "AAAA"
+_TYPE[Dns.TYPE.LOC] = "LOC"
+_TYPE[Dns.TYPE.NXT] = "NXT"
+_TYPE[Dns.TYPE.EID] = "EID"
+_TYPE[Dns.TYPE.NIMLOC] = "NIMLOC"
+_TYPE[Dns.TYPE.SRV] = "SRV"
+_TYPE[Dns.TYPE.ATMA] = "ATMA"
+_TYPE[Dns.TYPE.NAPTR] = "NAPTR"
+_TYPE[Dns.TYPE.KX] = "KX"
+_TYPE[Dns.TYPE.CERT] = "CERT"
+_TYPE[Dns.TYPE.A6] = "A6"
+_TYPE[Dns.TYPE.DNAME] = "DNAME"
+_TYPE[Dns.TYPE.SINK] = "SINK"
+_TYPE[Dns.TYPE.OPT] = "OPT"
+_TYPE[Dns.TYPE.APL] = "APL"
+_TYPE[Dns.TYPE.DS] = "DS"
+_TYPE[Dns.TYPE.SSHFP] = "SSHFP"
+_TYPE[Dns.TYPE.IPSECKEY] = "IPSECKEY"
+_TYPE[Dns.TYPE.RRSIG] = "RRSIG"
+_TYPE[Dns.TYPE.NSEC] = "NSEC"
+_TYPE[Dns.TYPE.DNSKEY] = "DNSKEY"
+_TYPE[Dns.TYPE.DHCID] = "DHCID"
+_TYPE[Dns.TYPE.NSEC3] = "NSEC3"
+_TYPE[Dns.TYPE.NSEC3PARAM] = "NSEC3PARAM"
+_TYPE[Dns.TYPE.TLSA] = "TLSA"
+_TYPE[Dns.TYPE.SMIMEA] = "SMIMEA"
+_TYPE[Dns.TYPE.HIP] = "HIP"
+_TYPE[Dns.TYPE.NINFO] = "NINFO"
+_TYPE[Dns.TYPE.RKEY] = "RKEY"
+_TYPE[Dns.TYPE.TALINK] = "TALINK"
+_TYPE[Dns.TYPE.CDS] = "CDS"
+_TYPE[Dns.TYPE.CDNSKEY] = "CDNSKEY"
+_TYPE[Dns.TYPE.OPENPGPKEY] = "OPENPGPKEY"
+_TYPE[Dns.TYPE.CSYNC] = "CSYNC"
+_TYPE[Dns.TYPE.SPF] = "SPF"
+_TYPE[Dns.TYPE.UINFO] = "UINFO"
+_TYPE[Dns.TYPE.UID] = "UID"
+_TYPE[Dns.TYPE.GID] = "GID"
+_TYPE[Dns.TYPE.UNSPEC] = "UNSPEC"
+_TYPE[Dns.TYPE.NID] = "NID"
+_TYPE[Dns.TYPE.L32] = "L32"
+_TYPE[Dns.TYPE.L64] = "L64"
+_TYPE[Dns.TYPE.LP] = "LP"
+_TYPE[Dns.TYPE.EUI48] = "EUI48"
+_TYPE[Dns.TYPE.EUI64] = "EUI64"
+_TYPE[Dns.TYPE.TKEY] = "TKEY"
+_TYPE[Dns.TYPE.TSIG] = "TSIG"
+_TYPE[Dns.TYPE.IXFR] = "IXFR"
+_TYPE[Dns.TYPE.AXFR] = "AXFR"
+_TYPE[Dns.TYPE.MAILB] = "MAILB"
+_TYPE[Dns.TYPE.MAILA] = "MAILA"
+_TYPE[Dns.TYPE.ANY] = "ANY"
+_TYPE[Dns.TYPE.URI] = "URI"
+_TYPE[Dns.TYPE.CAA] = "CAA"
+_TYPE[Dns.TYPE.AVC] = "AVC"
+_TYPE[Dns.TYPE.TA] = "TA"
+_TYPE[Dns.TYPE.DLV] = "DLV"
+local _OPCODE = {}
+_OPCODE[Dns.OPCODE.QUERY] = "QUERY"
+_OPCODE[Dns.OPCODE.IQUERY] = "IQUERY"
+_OPCODE[Dns.OPCODE.STATUS] = "STATUS"
+_OPCODE[Dns.OPCODE.NOTIFY] = "NOTIFY"
+_OPCODE[Dns.OPCODE.UPDATE] = "UPDATE"
+local _RCODE = {}
+_RCODE[Dns.RCODE.NOERROR] = "NOERROR"
+_RCODE[Dns.RCODE.FORMERR] = "FORMERR"
+_RCODE[Dns.RCODE.SERVFAIL] = "SERVFAIL"
+_RCODE[Dns.RCODE.NXDOMAIN] = "NXDOMAIN"
+_RCODE[Dns.RCODE.NOTIMP] = "NOTIMP"
+_RCODE[Dns.RCODE.REFUSED] = "REFUSED"
+_RCODE[Dns.RCODE.YXDOMAIN] = "YXDOMAIN"
+_RCODE[Dns.RCODE.YXRRSET] = "YXRRSET"
+_RCODE[Dns.RCODE.NXRRSET] = "NXRRSET"
+_RCODE[Dns.RCODE.NOTAUTH] = "NOTAUTH"
+_RCODE[Dns.RCODE.NOTZONE] = "NOTZONE"
+_RCODE[Dns.RCODE.BADVERS] = "BADVERS"
+_RCODE[Dns.RCODE.BADSIG] = "BADSIG"
+_RCODE[Dns.RCODE.BADKEY] = "BADKEY"
+_RCODE[Dns.RCODE.BADTIME] = "BADTIME"
+_RCODE[Dns.RCODE.BADMODE] = "BADMODE"
+_RCODE[Dns.RCODE.BADNAME] = "BADNAME"
+_RCODE[Dns.RCODE.BADALG] = "BADALG"
+_RCODE[Dns.RCODE.BADTRUNC] = "BADTRUNC"
+_RCODE[Dns.RCODE.BADCOOKIE] = "BADCOOKIE"
+local _AFSDB = {}
+_AFSDB[Dns.AFSDB.SUBTYPE_AFS3LOCSRV] = "SUBTYPE_AFS3LOCSRV"
+_AFSDB[Dns.AFSDB.SUBTYPE_DCENCA_ROOT] = "SUBTYPE_DCENCA_ROOT"
+local _DHCID = {}
+_DHCID[Dns.DHCID.TYPE_1OCTET] = "TYPE_1OCTET"
+_DHCID[Dns.DHCID.TYPE_DATAOCTET] = "TYPE_DATAOCTET"
+_DHCID[Dns.DHCID.TYPE_CLIENT_DUID] = "TYPE_CLIENT_DUID"
+local _EDNS0 = {}
+_EDNS0[Dns.EDNS0.OPT_LLQ] = "OPT_LLQ"
+_EDNS0[Dns.EDNS0.OPT_UL] = "OPT_UL"
+_EDNS0[Dns.EDNS0.OPT_NSID] = "OPT_NSID"
+_EDNS0[Dns.EDNS0.OPT_DAU] = "OPT_DAU"
+_EDNS0[Dns.EDNS0.OPT_DHU] = "OPT_DHU"
+_EDNS0[Dns.EDNS0.OPT_N3U] = "OPT_N3U"
+_EDNS0[Dns.EDNS0.OPT_CLIENT_SUBNET] = "OPT_CLIENT_SUBNET"
+_EDNS0[Dns.EDNS0.OPT_EXPIRE] = "OPT_EXPIRE"
+_EDNS0[Dns.EDNS0.OPT_COOKIE] = "OPT_COOKIE"
+_EDNS0[Dns.EDNS0.OPT_TCP_KEEPALIVE] = "OPT_TCP_KEEPALIVE"
+_EDNS0[Dns.EDNS0.OPT_PADDING] = "OPT_PADDING"
+_EDNS0[Dns.EDNS0.OPT_CHAIN] = "OPT_CHAIN"
+_EDNS0[Dns.EDNS0.OPT_DEVICEID] = "OPT_DEVICEID"
+Dns.CLASS_STR = _CLASS
+Dns.TYPE_STR = _TYPE
+Dns.OPCODE_STR = _OPCODE
+Dns.RCODE_STR = _RCODE
+Dns.AFSDB_STR = _AFSDB
+Dns.DHCID_STR = _DHCID
+Dns.EDNS0_STR = _EDNS0
+
+-- Create a new DNS object, optionally on-top of another object.
+function Dns.new(obj)
+ local self = C.core_object_dns_new()
+ self.obj_prev = obj
+ ffi.gc(self, C.core_object_dns_free)
+ return self
+end
+
+-- Return the textual type of the object.
+function Dns:type()
+ return "dns"
+end
+
+-- Return the previous object.
+function Dns:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Dns:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Dns:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Dns:copy()
+ return C.core_object_dns_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Dns:free()
+ C.core_object_dns_free(self)
+end
+
+-- Return the Log object to control logging of this module.
+function Dns:log()
+ return C.core_object_dns_log()
+end
+
+-- Begin parsing the underlaying object, first the header is parsed then
+-- optionally continue calling
+-- .IR parse_q ()
+-- for the number of questions (see
+-- .IR qdcount ).
+-- After that continue calling
+-- .IR parse_rr ()
+-- for the number of answers, authorities and additionals resource records
+-- (see
+-- .IR ancount ", "
+-- .I nscount
+-- and
+-- .IR arcount ).
+-- Returns 0 on success or negative integer on error which can be for
+-- malformed or truncated DNS (-2) or if more space for labels is needed (-3).
+function Dns:parse_header()
+ return C.core_object_dns_parse_header(self)
+end
+
+-- Parse the next resource record as a question.
+-- Returns 0 on success or negative integer on error which can be for
+-- malformed or truncated DNS (-2) or if more space for labels is needed (-3).
+function Dns:parse_q(q, labels, num_labels)
+ return C.core_object_dns_parse_q(self, q, labels, num_labels)
+end
+
+-- Parse the next resource record.
+-- Returns 0 on success or negative integer on error which can be for
+-- malformed or truncated DNS (-2) or if more space for labels is needed (-3).
+function Dns:parse_rr(rr, labels, num_labels)
+ return C.core_object_dns_parse_rr(self, rr, labels, num_labels)
+end
+
+-- Begin parsing the underlaying object using
+-- .IR parse_header "(), "
+-- .IR parse_q ()
+-- and
+-- .IR parse_rr ().
+-- The optional
+-- .I num_labels
+-- can be used to set a specific number of labels used for each question
+-- and resource record (default 16).
+-- Returns result code, an array of questions, an array of question labels,
+-- an array of resource records and an array of resource records labels.
+-- Result code is 0 on success or negative integer on error which can be for
+-- malformed or truncated DNS (-2) or if more space for labels is needed (-3).
+function Dns:parse(num_labels)
+ local qs, qls, rrs, rrls = {}, {}, {}, {}
+ if num_labels == nil then
+ num_labels = 16
+ end
+
+ ret = self:parse_header()
+ if ret ~= 0 then
+ return ret, qs, qls, rrs, rrls
+ end
+ for n = 1, self.qdcount do
+ local labels = label.new(num_labels)
+ local q = Q.new()
+ local ret = C.core_object_dns_parse_q(self, q, labels, num_labels)
+
+ if ret ~= 0 then
+ return ret, qs, qls, rrs, rrls
+ end
+ table.insert(qs, q)
+ table.insert(qls, labels)
+ end
+ for n = 1, self.ancount do
+ local labels = label.new(num_labels)
+ local rr = RR.new()
+ local ret = C.core_object_dns_parse_rr(self, rr, labels, num_labels)
+
+ if ret ~= 0 then
+ return ret, qs, qls, rrs, rrls
+ end
+ table.insert(rrs, rr)
+ table.insert(rrls, labels)
+ end
+ for n = 1, self.nscount do
+ local labels = label.new(num_labels)
+ local rr = RR.new()
+ local ret = C.core_object_dns_parse_rr(self, rr, labels, num_labels)
+
+ if ret ~= 0 then
+ return ret, qs, qls, rrs, rrls
+ end
+ table.insert(rrs, rr)
+ table.insert(rrls, labels)
+ end
+ for n = 1, self.arcount do
+ local labels = label.new(num_labels)
+ local rr = RR.new()
+ local ret = C.core_object_dns_parse_rr(self, rr, labels, num_labels)
+
+ if ret ~= 0 then
+ return ret, qs, qls, rrs, rrls
+ end
+ table.insert(rrs, rr)
+ table.insert(rrls, labels)
+ end
+
+ return 0, qs, qls, rrs, rrls
+end
+
+-- Begin parsing the underlaying object using
+-- .IR parse_header "(), "
+-- .IR parse_q ()
+-- and
+-- .IR parse_rr (),
+-- and print it's content.
+-- The optional
+-- .I num_labels
+-- can be used to set a specific number of labels used for each question
+-- and resource record (default 16).
+function Dns:print(num_labels)
+ if num_labels == nil then
+ num_labels = 16
+ end
+ local labels = label.new(num_labels)
+ local q = Q.new()
+ local rr = RR.new()
+
+ if self:parse_header() ~= 0 then
+ return
+ end
+
+ local flags = {}
+ if self.have_aa and self.aa == 1 then
+ table.insert(flags, "AA")
+ end
+ if self.have_tc and self.tc == 1 then
+ table.insert(flags, "TC")
+ end
+ if self.have_rd and self.rd == 1 then
+ table.insert(flags, "RD")
+ end
+ if self.have_ra and self.ra == 1 then
+ table.insert(flags, "RA")
+ end
+ if self.have_z and self.z == 1 then
+ table.insert(flags, "Z")
+ end
+ if self.have_ad and self.ad == 1 then
+ table.insert(flags, "AD")
+ end
+ if self.have_cd and self.cd == 1 then
+ table.insert(flags, "CD")
+ end
+
+ print("id:", self.id)
+ print("", "qr:", self.qr)
+ print("", "opcode:", Dns.opcode_tostring(self.opcode))
+ print("", "flags:", table.concat(flags, " "))
+ print("", "rcode:", Dns.rcode_tostring(self.rcode))
+ print("", "qdcount:", self.qdcount)
+ print("", "ancount:", self.ancount)
+ print("", "nscount:", self.nscount)
+ print("", "arcount:", self.arcount)
+
+ if self.qdcount > 0 then
+ print("questions:", "class", "type", "labels")
+ for n = 1, self.qdcount do
+ if C.core_object_dns_parse_q(self, q, labels, num_labels) ~= 0 then
+ return
+ end
+ print("", Dns.class_tostring(q.class), Dns.type_tostring(q.type), label.tooffstr(self, labels, num_labels))
+ end
+ end
+ if self.ancount > 0 then
+ print("answers:", "class", "type", "ttl", "labels", "RR labels")
+ for n = 1, self.ancount do
+ if C.core_object_dns_parse_rr(self, rr, labels, num_labels) ~= 0 then
+ return
+ end
+ if rr.rdata_labels == 0 then
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels))
+ else
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels), label.tooffstr(self, labels, rr.rdata_labels, rr.labels))
+ end
+ end
+ end
+ if self.nscount > 0 then
+ print("authorities:", "class", "type", "ttl", "labels", "RR labels")
+ for n = 1, self.nscount do
+ if C.core_object_dns_parse_rr(self, rr, labels, num_labels) ~= 0 then
+ return
+ end
+ if rr.rdata_labels == 0 then
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels))
+ else
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels), label.tooffstr(self, labels, rr.rdata_labels, rr.labels))
+ end
+ end
+ end
+ if self.arcount > 0 then
+ print("additionals:", "class", "type", "ttl", "labels", "RR labels")
+ for n = 1, self.arcount do
+ if C.core_object_dns_parse_rr(self, rr, labels, num_labels) ~= 0 then
+ return
+ end
+ if rr.rdata_labels == 0 then
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels))
+ else
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels), label.tooffstr(self, labels, rr.rdata_labels, rr.labels))
+ end
+ end
+ end
+end
+
+-- Return the textual name for a class.
+function Dns.class_tostring(class)
+ if Dns.CLASS_STR[class] == nil then
+ return "UNKNOWN("..class..")"
+ end
+ return Dns.CLASS_STR[class]
+end
+
+-- Return the textual name for a type.
+function Dns.type_tostring(type)
+ if Dns.TYPE_STR[type] == nil then
+ return "UNKNOWN("..type..")"
+ end
+ return Dns.TYPE_STR[type]
+end
+
+-- Return the textual name for an opcode.
+function Dns.opcode_tostring(opcode)
+ if Dns.OPCODE_STR[opcode] == nil then
+ return "UNKNOWN("..opcode..")"
+ end
+ return Dns.OPCODE_STR[opcode]
+end
+
+-- Return the textual name for a rcode.
+function Dns.rcode_tostring(rcode)
+ if Dns.RCODE_STR[rcode] == nil then
+ return "UNKNOWN("..rcode..")"
+ end
+ return Dns.RCODE_STR[rcode]
+end
+
+-- Return the textual name for an afsdb subtype.
+function Dns.afsdb_tostring(afsdb)
+ if Dns.AFSDB_STR[afsdb] == nil then
+ return "UNKNOWN("..afsdb..")"
+ end
+ return Dns.AFSDB_STR[afsdb]
+end
+
+-- Return the textual name for a dhcid type.
+function Dns.dhcid_tostring(dhcid)
+ if Dns.DHCID_STR[dhcid] == nil then
+ return "UNKNOWN("..dhcid..")"
+ end
+ return Dns.DHCID_STR[dhcid]
+end
+
+-- Return the textual name for an EDNS0 OPT record.
+function Dns.edns0_tostring(edns0)
+ if Dns.EDNS0_STR[edns0] == nil then
+ return "UNKNOWN("..edns0..")"
+ end
+ return Dns.EDNS0_STR[edns0]
+end
+
+core_object_dns_t = ffi.metatype(t_name, { __index = Dns })
+
+-- dnsjit.core.object (3),
+-- dnsjit.core.object.payload (3),
+-- dnsjit.core.object.dns.label (3),
+-- dnsjit.core.object.dns.q (3),
+-- dnsjit.core.object.dns.rr (3)
+return Dns
diff --git a/src/core/object/dns/label.lua b/src/core/object/dns/label.lua
new file mode 100644
index 0000000..bc78758
--- /dev/null
+++ b/src/core/object/dns/label.lua
@@ -0,0 +1,118 @@
+-- Copyright (c) 2018-2021, OARC, Inc.
+-- All rights reserved.
+--
+-- This file is part of dnsjit.
+--
+-- dnsjit is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- dnsjit is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+
+-- dnsjit.core.object.dns.label
+-- Container of a DNS label
+--
+-- The object that describes a DNS label.
+-- To extract a domain name label first check that
+-- .I have_dn
+-- is set, then use
+-- .I "offset + 1"
+-- to indicate where in the payload the label start and
+-- .I length
+-- for how many bytes long it is.
+-- .SS Attributes
+-- .TP
+-- is_end
+-- .TP
+-- have_length
+-- Set if there is a length.
+-- .TP
+-- have_offset
+-- Set if there is an offset.
+-- .TP
+-- have_extension_bits
+-- Set if there is extension bits.
+-- .TP
+-- have_dn
+-- Set if the label contained a domain name.
+-- .TP
+-- extension_bits
+-- The extension bits.
+-- .TP
+-- length
+-- The length of the domain name.
+-- .TP
+-- offset
+-- If
+-- .I have_dn
+-- is set then this contains the offset within the payload to where this label
+-- start otherwise it contains the offset to another label.
+module(...,package.seeall)
+
+require("dnsjit.core.object.dns_h")
+local ffi = require("ffi")
+
+local Label = {}
+
+-- Create a new array of labels.
+function Label.new(size)
+ return ffi.new("core_object_dns_label_t[?]", size)
+end
+
+-- Returns labels as a string and an offset to the next label.
+-- The string may be nil if the first label was an offset.
+-- The offset may be nil if the last label was an extension bits or end marker.
+function Label.tostring(dns, labels, num_labels, offset_labels)
+ if offset_labels == nil then
+ offset_labels = 0
+ end
+ local dn
+ for n = 1, tonumber(num_labels) do
+ local label = labels[n - 1 + offset_labels]
+
+ if label.have_dn == 1 then
+ if dn == nil then
+ dn = ""
+ end
+ dn = dn .. ffi.string(dns.payload + label.offset + 1, label.length) .. "."
+ elseif label.have_offset == 1 then
+ return dn, label.offset
+ else
+ return dn, nil
+ end
+ end
+ return dn, nil
+end
+
+-- Returns labels as a string which also includes a textual notation of the
+-- offset in the form of
+-- .IR "<offset>label" .
+function Label.tooffstr(dns, labels, num_labels, offset_labels)
+ if offset_labels == nil then
+ offset_labels = 0
+ end
+ local dn = ""
+ for n = 1, tonumber(num_labels) do
+ local label = labels[n - 1 + offset_labels]
+
+ if label.have_dn == 1 then
+ dn = dn .. "<" .. tonumber(label.offset) .. ">" .. ffi.string(dns.payload + label.offset + 1, label.length) .. "."
+ elseif label.have_offset == 1 then
+ dn = dn .. "<" .. tonumber(label.offset) .. ">"
+ break
+ else
+ break
+ end
+ end
+ return dn
+end
+
+-- dnsjit.core.object.dns (3)
+return Label
diff --git a/src/core/object/dns/q.lua b/src/core/object/dns/q.lua
new file mode 100644
index 0000000..f65d9e8
--- /dev/null
+++ b/src/core/object/dns/q.lua
@@ -0,0 +1,52 @@
+-- Copyright (c) 2018-2021, OARC, Inc.
+-- All rights reserved.
+--
+-- This file is part of dnsjit.
+--
+-- dnsjit is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- dnsjit is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+
+-- dnsjit.core.object.dns.q
+-- Container of a DNS question
+--
+-- The object that describes a DNS question.
+-- .SS Attributes
+-- .TP
+-- have_type
+-- Set if there is a type.
+-- .TP
+-- have_class
+-- Set if there is a class.
+-- .TP
+-- type
+-- The type.
+-- .TP
+-- class
+-- The class.
+-- .TP
+-- labels
+-- The number of labels found in the question.
+module(...,package.seeall)
+
+require("dnsjit.core.object.dns_h")
+local ffi = require("ffi")
+
+local Q = {}
+
+-- Create a new question.
+function Q.new(size)
+ return ffi.new("core_object_dns_q_t")
+end
+
+-- dnsjit.core.object.dns (3)
+return Q
diff --git a/src/core/object/dns/rr.lua b/src/core/object/dns/rr.lua
new file mode 100644
index 0000000..d360d96
--- /dev/null
+++ b/src/core/object/dns/rr.lua
@@ -0,0 +1,85 @@
+-- Copyright (c) 2018-2021, OARC, Inc.
+-- All rights reserved.
+--
+-- This file is part of dnsjit.
+--
+-- dnsjit is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- dnsjit is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+
+-- dnsjit.core.object.dns.rr
+-- Container of a DNS resource record
+--
+-- The object that describes a DNS resource record.
+-- .SS Attributes
+-- .TP
+-- have_type
+-- Set if there is a type.
+-- .TP
+-- have_class
+-- Set if there is a class.
+-- .TP
+-- have_ttl
+-- Set if there is a ttl.
+-- .TP
+-- have_rdlength
+-- Set if there is a rdlength.
+-- .TP
+-- have_rdata
+-- Set if there is resource record data.
+-- .TP
+-- have_rdata_labels
+-- Set if there are any labels within the rdata.
+-- .TP
+-- have_padding
+-- Set if there is padding.
+-- .TP
+-- type
+-- The type.
+-- .TP
+-- class
+-- The class.
+-- .TP
+-- ttl
+-- The TTL.
+-- .TP
+-- rdlength
+-- The resource record data length.
+-- .TP
+-- labels
+-- The number of labels found in the record.
+-- .TP
+-- rdata_offset
+-- The offset within the payload for the resource record data.
+-- .TP
+-- rdata_labels
+-- The number of labels found inside the resource record data.
+-- .TP
+-- padding_offset
+-- The offset within the payload where the padding starts.
+-- .TP
+-- padding_length
+-- The length of the padding.
+module(...,package.seeall)
+
+require("dnsjit.core.object.dns_h")
+local ffi = require("ffi")
+
+local Rr = {}
+
+-- Create a new resource record.
+function Rr.new()
+ return ffi.new("core_object_dns_rr_t")
+end
+
+-- dnsjit.core.object.dns (3)
+return Rr