summaryrefslogtreecommitdiffstats
path: root/lib/dns/compress.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns/compress.c')
-rw-r--r--lib/dns/compress.c400
1 files changed, 400 insertions, 0 deletions
diff --git a/lib/dns/compress.c b/lib/dns/compress.c
new file mode 100644
index 0000000..ec97235
--- /dev/null
+++ b/lib/dns/compress.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#define DNS_NAME_USEINLINE 1
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/compress.h>
+#include <dns/fixedname.h>
+#include <dns/rbt.h>
+#include <dns/result.h>
+
+#define CCTX_MAGIC ISC_MAGIC('C', 'C', 'T', 'X')
+#define VALID_CCTX(x) ISC_MAGIC_VALID(x, CCTX_MAGIC)
+
+#define DCTX_MAGIC ISC_MAGIC('D', 'C', 'T', 'X')
+#define VALID_DCTX(x) ISC_MAGIC_VALID(x, DCTX_MAGIC)
+
+#define TABLE_READY \
+ do { \
+ unsigned int i; \
+ \
+ if ((cctx->allowed & DNS_COMPRESS_READY) == 0) { \
+ cctx->allowed |= DNS_COMPRESS_READY; \
+ for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++) \
+ cctx->table[i] = NULL; \
+ } \
+ } while (0)
+
+/***
+ *** Compression
+ ***/
+
+isc_result_t
+dns_compress_init(dns_compress_t *cctx, int edns, isc_mem_t *mctx) {
+ REQUIRE(cctx != NULL);
+ REQUIRE(mctx != NULL); /* See: rdataset.c:towiresorted(). */
+
+ cctx->edns = edns;
+ cctx->mctx = mctx;
+ cctx->count = 0;
+ cctx->allowed = DNS_COMPRESS_ENABLED;
+ cctx->magic = CCTX_MAGIC;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_compress_invalidate(dns_compress_t *cctx) {
+ dns_compressnode_t *node;
+ unsigned int i;
+
+ REQUIRE(VALID_CCTX(cctx));
+
+ if ((cctx->allowed & DNS_COMPRESS_READY) != 0) {
+ for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++) {
+ while (cctx->table[i] != NULL) {
+ node = cctx->table[i];
+ cctx->table[i] = cctx->table[i]->next;
+ if ((node->offset & 0x8000) != 0)
+ isc_mem_put(cctx->mctx, node->r.base,
+ node->r.length);
+ if (node->count < DNS_COMPRESS_INITIALNODES)
+ continue;
+ isc_mem_put(cctx->mctx, node, sizeof(*node));
+ }
+ }
+ }
+ cctx->magic = 0;
+ cctx->allowed = 0;
+ cctx->edns = -1;
+}
+
+void
+dns_compress_setmethods(dns_compress_t *cctx, unsigned int allowed) {
+ REQUIRE(VALID_CCTX(cctx));
+
+ cctx->allowed &= ~DNS_COMPRESS_ALL;
+ cctx->allowed |= (allowed & DNS_COMPRESS_ALL);
+}
+
+unsigned int
+dns_compress_getmethods(dns_compress_t *cctx) {
+ REQUIRE(VALID_CCTX(cctx));
+ return (cctx->allowed & DNS_COMPRESS_ALL);
+}
+
+void
+dns_compress_disable(dns_compress_t *cctx) {
+ REQUIRE(VALID_CCTX(cctx));
+ cctx->allowed &= ~DNS_COMPRESS_ENABLED;
+}
+
+void
+dns_compress_setsensitive(dns_compress_t *cctx, bool sensitive) {
+ REQUIRE(VALID_CCTX(cctx));
+
+ if (sensitive)
+ cctx->allowed |= DNS_COMPRESS_CASESENSITIVE;
+ else
+ cctx->allowed &= ~DNS_COMPRESS_CASESENSITIVE;
+}
+
+bool
+dns_compress_getsensitive(dns_compress_t *cctx) {
+ REQUIRE(VALID_CCTX(cctx));
+
+ return (cctx->allowed & DNS_COMPRESS_CASESENSITIVE);
+}
+
+int
+dns_compress_getedns(dns_compress_t *cctx) {
+ REQUIRE(VALID_CCTX(cctx));
+ return (cctx->edns);
+}
+
+#define NODENAME(node, name) \
+do { \
+ (name)->length = (node)->r.length; \
+ (name)->labels = (node)->labels; \
+ (name)->ndata = (node)->r.base; \
+ (name)->attributes = DNS_NAMEATTR_ABSOLUTE; \
+} while (0)
+
+/*
+ * Find the longest match of name in the table.
+ * If match is found return true. prefix, suffix and offset are updated.
+ * If no match is found return false.
+ */
+bool
+dns_compress_findglobal(dns_compress_t *cctx, const dns_name_t *name,
+ dns_name_t *prefix, uint16_t *offset)
+{
+ dns_name_t tname, nname;
+ dns_compressnode_t *node = NULL;
+ unsigned int labels, hash, n;
+
+ REQUIRE(VALID_CCTX(cctx));
+ REQUIRE(dns_name_isabsolute(name) == true);
+ REQUIRE(offset != NULL);
+
+ if ((cctx->allowed & DNS_COMPRESS_ENABLED) == 0)
+ return (false);
+
+ TABLE_READY;
+
+ if (cctx->count == 0)
+ return (false);
+
+ labels = dns_name_countlabels(name);
+ INSIST(labels > 0);
+
+ dns_name_init(&tname, NULL);
+ dns_name_init(&nname, NULL);
+
+ for (n = 0; n < labels - 1; n++) {
+ dns_name_getlabelsequence(name, n, labels - n, &tname);
+ hash = dns_name_hash(&tname, false) %
+ DNS_COMPRESS_TABLESIZE;
+ for (node = cctx->table[hash]; node != NULL; node = node->next)
+ {
+ NODENAME(node, &nname);
+ if ((cctx->allowed & DNS_COMPRESS_CASESENSITIVE) != 0) {
+ if (dns_name_caseequal(&nname, &tname))
+ break;
+ } else {
+ if (dns_name_equal(&nname, &tname))
+ break;
+ }
+ }
+ if (node != NULL)
+ break;
+ }
+
+ /*
+ * If node == NULL, we found no match at all.
+ */
+ if (node == NULL)
+ return (false);
+
+ if (n == 0)
+ dns_name_reset(prefix);
+ else
+ dns_name_getlabelsequence(name, 0, n, prefix);
+
+ *offset = (node->offset & 0x7fff);
+ return (true);
+}
+
+static inline unsigned int
+name_length(const dns_name_t *name) {
+ isc_region_t r;
+ dns_name_toregion(name, &r);
+ return (r.length);
+}
+
+void
+dns_compress_add(dns_compress_t *cctx, const dns_name_t *name,
+ const dns_name_t *prefix, uint16_t offset)
+{
+ dns_name_t tname, xname;
+ unsigned int start;
+ unsigned int n;
+ unsigned int count;
+ unsigned int hash;
+ dns_compressnode_t *node;
+ unsigned int length;
+ unsigned int tlength;
+ uint16_t toffset;
+ unsigned char *tmp;
+ isc_region_t r;
+
+ REQUIRE(VALID_CCTX(cctx));
+ REQUIRE(dns_name_isabsolute(name));
+
+ if ((cctx->allowed & DNS_COMPRESS_ENABLED) == 0)
+ return;
+
+ TABLE_READY;
+
+ if (offset >= 0x4000)
+ return;
+ dns_name_init(&tname, NULL);
+ dns_name_init(&xname, NULL);
+
+ n = dns_name_countlabels(name);
+ count = dns_name_countlabels(prefix);
+ if (dns_name_isabsolute(prefix))
+ count--;
+ if (count == 0)
+ return;
+ start = 0;
+ dns_name_toregion(name, &r);
+ length = r.length;
+ tmp = isc_mem_get(cctx->mctx, length);
+ if (tmp == NULL)
+ return;
+ /*
+ * Copy name data to 'tmp' and make 'r' use 'tmp'.
+ */
+ memmove(tmp, r.base, r.length);
+ r.base = tmp;
+ dns_name_fromregion(&xname, &r);
+
+ while (count > 0) {
+ dns_name_getlabelsequence(&xname, start, n, &tname);
+ hash = dns_name_hash(&tname, false) %
+ DNS_COMPRESS_TABLESIZE;
+ tlength = name_length(&tname);
+ toffset = (uint16_t)(offset + (length - tlength));
+ if (toffset >= 0x4000)
+ break;
+ /*
+ * Create a new node and add it.
+ */
+ if (cctx->count < DNS_COMPRESS_INITIALNODES)
+ node = &cctx->initialnodes[cctx->count];
+ else {
+ node = isc_mem_get(cctx->mctx,
+ sizeof(dns_compressnode_t));
+ if (node == NULL)
+ break;
+ }
+ node->count = cctx->count++;
+ /*
+ * 'node->r.base' becomes 'tmp' when start == 0.
+ * Record this by setting 0x8000 so it can be freed later.
+ */
+ if (start == 0)
+ toffset |= 0x8000;
+ node->offset = toffset;
+ dns_name_toregion(&tname, &node->r);
+ node->labels = (uint8_t)dns_name_countlabels(&tname);
+ node->next = cctx->table[hash];
+ cctx->table[hash] = node;
+ start++;
+ n--;
+ count--;
+ }
+
+ if (start == 0)
+ isc_mem_put(cctx->mctx, tmp, length);
+}
+
+void
+dns_compress_rollback(dns_compress_t *cctx, uint16_t offset) {
+ unsigned int i;
+ dns_compressnode_t *node;
+
+ REQUIRE(VALID_CCTX(cctx));
+
+ if ((cctx->allowed & DNS_COMPRESS_ENABLED) == 0)
+ return;
+
+ if ((cctx->allowed & DNS_COMPRESS_READY) == 0)
+ return;
+
+ for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++) {
+ node = cctx->table[i];
+ /*
+ * This relies on nodes with greater offsets being
+ * closer to the beginning of the list, and the
+ * items with the greatest offsets being at the end
+ * of the initialnodes[] array.
+ */
+ while (node != NULL && (node->offset & 0x7fff) >= offset) {
+ cctx->table[i] = node->next;
+ if ((node->offset & 0x8000) != 0)
+ isc_mem_put(cctx->mctx, node->r.base,
+ node->r.length);
+ if (node->count >= DNS_COMPRESS_INITIALNODES)
+ isc_mem_put(cctx->mctx, node, sizeof(*node));
+ cctx->count--;
+ node = cctx->table[i];
+ }
+ }
+}
+
+/***
+ *** Decompression
+ ***/
+
+void
+dns_decompress_init(dns_decompress_t *dctx, int edns,
+ dns_decompresstype_t type) {
+
+ REQUIRE(dctx != NULL);
+ REQUIRE(edns >= -1 && edns <= 255);
+
+ dctx->allowed = DNS_COMPRESS_NONE;
+ dctx->edns = edns;
+ dctx->type = type;
+ dctx->magic = DCTX_MAGIC;
+}
+
+void
+dns_decompress_invalidate(dns_decompress_t *dctx) {
+
+ REQUIRE(VALID_DCTX(dctx));
+
+ dctx->magic = 0;
+}
+
+void
+dns_decompress_setmethods(dns_decompress_t *dctx, unsigned int allowed) {
+
+ REQUIRE(VALID_DCTX(dctx));
+
+ switch (dctx->type) {
+ case DNS_DECOMPRESS_ANY:
+ dctx->allowed = DNS_COMPRESS_ALL;
+ break;
+ case DNS_DECOMPRESS_NONE:
+ dctx->allowed = DNS_COMPRESS_NONE;
+ break;
+ case DNS_DECOMPRESS_STRICT:
+ dctx->allowed = allowed;
+ break;
+ }
+}
+
+unsigned int
+dns_decompress_getmethods(dns_decompress_t *dctx) {
+
+ REQUIRE(VALID_DCTX(dctx));
+
+ return (dctx->allowed);
+}
+
+int
+dns_decompress_edns(dns_decompress_t *dctx) {
+
+ REQUIRE(VALID_DCTX(dctx));
+
+ return (dctx->edns);
+}
+
+dns_decompresstype_t
+dns_decompress_type(dns_decompress_t *dctx) {
+
+ REQUIRE(VALID_DCTX(dctx));
+
+ return (dctx->type);
+}