15185a700Sflorian /*
25185a700Sflorian * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian *
45185a700Sflorian * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian * copyright notice and this permission notice appear in all copies.
75185a700Sflorian *
85185a700Sflorian * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian */
165185a700Sflorian
17*1fb015a8Sflorian /* $Id: compress.c,v 1.6 2020/09/14 08:40:43 florian Exp $ */
185185a700Sflorian
195185a700Sflorian /*! \file */
205185a700Sflorian
215185a700Sflorian #include <stdlib.h>
225185a700Sflorian
235185a700Sflorian #include <isc/util.h>
245185a700Sflorian
255185a700Sflorian #include <dns/compress.h>
265185a700Sflorian #include <dns/fixedname.h>
275185a700Sflorian
285185a700Sflorian /***
295185a700Sflorian *** Compression
305185a700Sflorian ***/
315185a700Sflorian
325185a700Sflorian isc_result_t
dns_compress_init(dns_compress_t * cctx,int edns)335185a700Sflorian dns_compress_init(dns_compress_t *cctx, int edns) {
345185a700Sflorian unsigned int i;
355185a700Sflorian
365185a700Sflorian REQUIRE(cctx != NULL);
375185a700Sflorian
385185a700Sflorian cctx->allowed = 0;
395185a700Sflorian cctx->edns = edns;
405185a700Sflorian for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++)
415185a700Sflorian cctx->table[i] = NULL;
425185a700Sflorian cctx->count = 0;
435185a700Sflorian return (ISC_R_SUCCESS);
445185a700Sflorian }
455185a700Sflorian
465185a700Sflorian void
dns_compress_invalidate(dns_compress_t * cctx)475185a700Sflorian dns_compress_invalidate(dns_compress_t *cctx) {
485185a700Sflorian dns_compressnode_t *node;
495185a700Sflorian unsigned int i;
505185a700Sflorian
515185a700Sflorian for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++) {
525185a700Sflorian while (cctx->table[i] != NULL) {
535185a700Sflorian node = cctx->table[i];
545185a700Sflorian cctx->table[i] = cctx->table[i]->next;
555185a700Sflorian if (node->count < DNS_COMPRESS_INITIALNODES)
565185a700Sflorian continue;
575185a700Sflorian free(node);
585185a700Sflorian }
595185a700Sflorian }
605185a700Sflorian cctx->allowed = 0;
615185a700Sflorian cctx->edns = -1;
625185a700Sflorian }
635185a700Sflorian
645185a700Sflorian void
dns_compress_setmethods(dns_compress_t * cctx,unsigned int allowed)655185a700Sflorian dns_compress_setmethods(dns_compress_t *cctx, unsigned int allowed) {
665185a700Sflorian cctx->allowed &= ~DNS_COMPRESS_ALL;
675185a700Sflorian cctx->allowed |= (allowed & DNS_COMPRESS_ALL);
685185a700Sflorian }
695185a700Sflorian
705185a700Sflorian unsigned int
dns_compress_getmethods(dns_compress_t * cctx)715185a700Sflorian dns_compress_getmethods(dns_compress_t *cctx) {
725185a700Sflorian return (cctx->allowed & DNS_COMPRESS_ALL);
735185a700Sflorian }
745185a700Sflorian
755185a700Sflorian #define NODENAME(node, name) \
765185a700Sflorian do { \
775185a700Sflorian (name)->length = (node)->r.length; \
785185a700Sflorian (name)->labels = (node)->labels; \
795185a700Sflorian (name)->ndata = (node)->r.base; \
805185a700Sflorian (name)->attributes = DNS_NAMEATTR_ABSOLUTE; \
815185a700Sflorian } while (0)
825185a700Sflorian
835185a700Sflorian /*
845185a700Sflorian * Find the longest match of name in the table.
85*1fb015a8Sflorian * If match is found return 1. prefix, suffix and offset are updated.
86*1fb015a8Sflorian * If no match is found return 0.
875185a700Sflorian */
88*1fb015a8Sflorian int
dns_compress_findglobal(dns_compress_t * cctx,const dns_name_t * name,dns_name_t * prefix,uint16_t * offset)895185a700Sflorian dns_compress_findglobal(dns_compress_t *cctx, const dns_name_t *name,
905185a700Sflorian dns_name_t *prefix, uint16_t *offset)
915185a700Sflorian {
925185a700Sflorian dns_name_t tname, nname;
935185a700Sflorian dns_compressnode_t *node = NULL;
945185a700Sflorian unsigned int labels, hash, n;
955185a700Sflorian
96*1fb015a8Sflorian REQUIRE(dns_name_isabsolute(name));
975185a700Sflorian REQUIRE(offset != NULL);
985185a700Sflorian
995185a700Sflorian if (cctx->count == 0)
100*1fb015a8Sflorian return (0);
1015185a700Sflorian
1025185a700Sflorian labels = dns_name_countlabels(name);
1035185a700Sflorian INSIST(labels > 0);
1045185a700Sflorian
1055185a700Sflorian dns_name_init(&tname, NULL);
1065185a700Sflorian dns_name_init(&nname, NULL);
1075185a700Sflorian
1085185a700Sflorian for (n = 0; n < labels - 1; n++) {
1095185a700Sflorian dns_name_getlabelsequence(name, n, labels - n, &tname);
110*1fb015a8Sflorian hash = dns_name_hash(&tname, 0) %
1115185a700Sflorian DNS_COMPRESS_TABLESIZE;
1125185a700Sflorian for (node = cctx->table[hash]; node != NULL; node = node->next)
1135185a700Sflorian {
1145185a700Sflorian NODENAME(node, &nname);
1155185a700Sflorian if ((cctx->allowed & DNS_COMPRESS_CASESENSITIVE) != 0) {
1165185a700Sflorian if (dns_name_caseequal(&nname, &tname))
1175185a700Sflorian break;
1185185a700Sflorian } else {
1195185a700Sflorian if (dns_name_equal(&nname, &tname))
1205185a700Sflorian break;
1215185a700Sflorian }
1225185a700Sflorian }
1235185a700Sflorian if (node != NULL)
1245185a700Sflorian break;
1255185a700Sflorian }
1265185a700Sflorian
1275185a700Sflorian /*
1285185a700Sflorian * If node == NULL, we found no match at all.
1295185a700Sflorian */
1305185a700Sflorian if (node == NULL)
131*1fb015a8Sflorian return (0);
1325185a700Sflorian
1335185a700Sflorian if (n == 0)
1345185a700Sflorian dns_name_reset(prefix);
1355185a700Sflorian else
1365185a700Sflorian dns_name_getlabelsequence(name, 0, n, prefix);
1375185a700Sflorian
1385185a700Sflorian *offset = node->offset;
139*1fb015a8Sflorian return (1);
1405185a700Sflorian }
1415185a700Sflorian
1425185a700Sflorian void
dns_compress_add(dns_compress_t * cctx,const dns_name_t * name,const dns_name_t * prefix,uint16_t offset)1435185a700Sflorian dns_compress_add(dns_compress_t *cctx, const dns_name_t *name,
1445185a700Sflorian const dns_name_t *prefix, uint16_t offset)
1455185a700Sflorian {
1465185a700Sflorian dns_name_t tname;
1475185a700Sflorian unsigned int start;
1485185a700Sflorian unsigned int n;
1495185a700Sflorian unsigned int count;
1505185a700Sflorian unsigned int hash;
1515185a700Sflorian dns_compressnode_t *node;
1525185a700Sflorian unsigned int length;
1535185a700Sflorian unsigned int tlength;
1545185a700Sflorian uint16_t toffset;
1555185a700Sflorian
1565185a700Sflorian REQUIRE(dns_name_isabsolute(name));
1575185a700Sflorian
1585185a700Sflorian dns_name_init(&tname, NULL);
1595185a700Sflorian
1605185a700Sflorian n = dns_name_countlabels(name);
1615185a700Sflorian count = dns_name_countlabels(prefix);
1625185a700Sflorian if (dns_name_isabsolute(prefix))
1635185a700Sflorian count--;
1645185a700Sflorian start = 0;
165f1dacf25Sflorian length = name->length;
1665185a700Sflorian while (count > 0) {
1675185a700Sflorian if (offset >= 0x4000)
1685185a700Sflorian break;
1695185a700Sflorian dns_name_getlabelsequence(name, start, n, &tname);
170*1fb015a8Sflorian hash = dns_name_hash(&tname, 0) %
1715185a700Sflorian DNS_COMPRESS_TABLESIZE;
172f1dacf25Sflorian tlength = tname.length;
1735185a700Sflorian toffset = (uint16_t)(offset + (length - tlength));
1745185a700Sflorian /*
1755185a700Sflorian * Create a new node and add it.
1765185a700Sflorian */
1775185a700Sflorian if (cctx->count < DNS_COMPRESS_INITIALNODES)
1785185a700Sflorian node = &cctx->initialnodes[cctx->count];
1795185a700Sflorian else {
1805185a700Sflorian node = malloc(sizeof(dns_compressnode_t));
1815185a700Sflorian if (node == NULL)
1825185a700Sflorian return;
1835185a700Sflorian }
1845185a700Sflorian node->count = cctx->count++;
1855185a700Sflorian node->offset = toffset;
1865185a700Sflorian dns_name_toregion(&tname, &node->r);
1875185a700Sflorian node->labels = (uint8_t)dns_name_countlabels(&tname);
1885185a700Sflorian node->next = cctx->table[hash];
1895185a700Sflorian cctx->table[hash] = node;
1905185a700Sflorian start++;
1915185a700Sflorian n--;
1925185a700Sflorian count--;
1935185a700Sflorian }
1945185a700Sflorian }
1955185a700Sflorian
1965185a700Sflorian void
dns_compress_rollback(dns_compress_t * cctx,uint16_t offset)1975185a700Sflorian dns_compress_rollback(dns_compress_t *cctx, uint16_t offset) {
1985185a700Sflorian unsigned int i;
1995185a700Sflorian dns_compressnode_t *node;
2005185a700Sflorian
2015185a700Sflorian for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++) {
2025185a700Sflorian node = cctx->table[i];
2035185a700Sflorian /*
2045185a700Sflorian * This relies on nodes with greater offsets being
2055185a700Sflorian * closer to the beginning of the list, and the
2065185a700Sflorian * items with the greatest offsets being at the end
2075185a700Sflorian * of the initialnodes[] array.
2085185a700Sflorian */
2095185a700Sflorian while (node != NULL && node->offset >= offset) {
2105185a700Sflorian cctx->table[i] = node->next;
2115185a700Sflorian if (node->count >= DNS_COMPRESS_INITIALNODES)
2125185a700Sflorian free(node);
2135185a700Sflorian cctx->count--;
2145185a700Sflorian node = cctx->table[i];
2155185a700Sflorian }
2165185a700Sflorian }
2175185a700Sflorian }
2185185a700Sflorian
2195185a700Sflorian /***
2205185a700Sflorian *** Decompression
2215185a700Sflorian ***/
2225185a700Sflorian
2235185a700Sflorian void
dns_decompress_init(dns_decompress_t * dctx,int edns,dns_decompresstype_t type)2245185a700Sflorian dns_decompress_init(dns_decompress_t *dctx, int edns,
2255185a700Sflorian dns_decompresstype_t type) {
2265185a700Sflorian
2275185a700Sflorian REQUIRE(dctx != NULL);
2285185a700Sflorian REQUIRE(edns >= -1 && edns <= 255);
2295185a700Sflorian
2305185a700Sflorian dctx->allowed = DNS_COMPRESS_NONE;
2315185a700Sflorian dctx->edns = edns;
2325185a700Sflorian dctx->type = type;
2335185a700Sflorian }
2345185a700Sflorian
2355185a700Sflorian void
dns_decompress_setmethods(dns_decompress_t * dctx,unsigned int allowed)2365185a700Sflorian dns_decompress_setmethods(dns_decompress_t *dctx, unsigned int allowed) {
2375185a700Sflorian switch (dctx->type) {
2385185a700Sflorian case DNS_DECOMPRESS_ANY:
2395185a700Sflorian dctx->allowed = DNS_COMPRESS_ALL;
2405185a700Sflorian break;
2415185a700Sflorian case DNS_DECOMPRESS_NONE:
2425185a700Sflorian dctx->allowed = DNS_COMPRESS_NONE;
2435185a700Sflorian break;
2445185a700Sflorian case DNS_DECOMPRESS_STRICT:
2455185a700Sflorian dctx->allowed = allowed;
2465185a700Sflorian break;
2475185a700Sflorian }
2485185a700Sflorian }
249