1 /* $NetBSD: compress.h,v 1.9 2026/06/19 20:10:01 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 #pragma once 17 18 #include <inttypes.h> 19 #include <stdbool.h> 20 21 #include <isc/lang.h> 22 #include <isc/region.h> 23 24 #include <dns/name.h> 25 #include <dns/types.h> 26 27 ISC_LANG_BEGINDECLS 28 29 /*! \file dns/compress.h 30 * Direct manipulation of the structures is strongly discouraged. 31 * 32 * A name compression context handles compression of multiple DNS names in 33 * relation to a single DNS message. The context can be used to selectively 34 * turn on/off compression for specific names (depending on the RR type, 35 * according to RFC 3597) by using \c dns_compress_setpermitted(). 36 * 37 * The nameserver can be configured not to use compression at all using 38 * \c dns_compress_disable(). 39 * 40 * DNS name compression only needs exact matches on (suffixes of) names. We 41 * could use a data structure that supports longest-match lookups, but that 42 * would introduce a lot of heavyweight machinery, and all we need is 43 * something that exists very briefly to store a few names before it is 44 * thrown away. 45 * 46 * In the abstract we need a map from DNS names to compression offsets. But 47 * a compression offset refers to a point in the message where the name has 48 * been written. So in fact all we need is a hash set of compression offsets. 49 * 50 * Typical messages do not contain more than a few dozen names, so by 51 * default our hash set is small (64 entries, 256 bytes). It can be 52 * enlarged when a message is likely to contain a lot of names, such as for 53 * outgoing zone transfers (which are handled in lib/ns/xfrout.c) and 54 * update requests (for which nsupdate uses DNS_REQUESTOPT_LARGE - see 55 * request.h). 56 */ 57 58 /* 59 * Logarithms of hash set sizes. In the usual (small) case, allow for a 60 * few dozen names in the hash set. (We can't actually use every slot because 61 * space is reserved for performance reasons.) For large messages, the number 62 * of names is limited by the minimum size of an RR (owner, type, class, ttl, 63 * length) which is 12 bytes - call it 16 bytes to make space for a new label. 64 * Divide the maximum compression offset 0x4000 by 16 and you get 0x400 == 1024. 65 * In practice, the root zone (for example) uses less than 200 distinct names 66 * per message. 67 */ 68 enum { 69 DNS_COMPRESS_SMALLBITS = 6, 70 DNS_COMPRESS_LARGEBITS = 10, 71 }; 72 73 /* 74 * Compression context flags 75 */ 76 enum dns_compress_flags { 77 /* affecting the whole message */ 78 DNS_COMPRESS_DISABLED = 0x00000001U, 79 DNS_COMPRESS_CASE = 0x00000002U, 80 DNS_COMPRESS_LARGE = 0x00000004U, 81 /* can toggle while rendering a message */ 82 DNS_COMPRESS_PERMITTED = 0x00000008U, 83 }; 84 85 /* 86 * The hash may be any 16 bit value. Unused slots have coff == 0. (Valid 87 * compression offsets cannot be zero because of the DNS message header.) 88 */ 89 struct dns_compress_slot { 90 uint16_t hash; 91 uint16_t coff; 92 }; 93 94 struct dns_compress { 95 unsigned int magic; 96 dns_compress_flags_t flags; 97 uint16_t mask; 98 uint16_t count; 99 isc_mem_t *mctx; 100 dns_compress_slot_t *set; 101 dns_compress_slot_t smallset[1 << DNS_COMPRESS_SMALLBITS]; 102 }; 103 104 /* 105 * Deompression context 106 */ 107 enum dns_decompress { 108 DNS_DECOMPRESS_DEFAULT, 109 DNS_DECOMPRESS_PERMITTED, 110 DNS_DECOMPRESS_NEVER, 111 DNS_DECOMPRESS_ALWAYS, 112 }; 113 114 void 115 dns_compress_init(dns_compress_t *cctx, isc_mem_t *mctx, 116 dns_compress_flags_t flags); 117 /*%< 118 * Initialise the compression context structure pointed to by 119 * 'cctx'. 120 * 121 * The `flags` argument is usually zero; or some combination of: 122 *\li DNS_COMPRESS_DISABLED, so the whole message is uncompressed 123 *\li DNS_COMPRESS_CASE, for case-sensitive compression 124 *\li DNS_COMPRESS_LARGE, for messages with many names 125 * 126 * (See also dns_request_create()'s options argument) 127 * 128 * Requires: 129 *\li 'cctx' is a dns_compress_t structure on the stack. 130 *\li 'mctx' is an initialized memory context. 131 * Ensures: 132 *\li 'cctx' is initialized. 133 *\li 'dns_compress_getpermitted(cctx)' is true 134 */ 135 136 void 137 dns_compress_invalidate(dns_compress_t *cctx); 138 139 /*%< 140 * Invalidate the compression structure pointed to by 141 * 'cctx', freeing any memory that has been allocated. 142 * 143 * Requires: 144 *\li 'cctx' is an initialized dns_compress_t 145 */ 146 147 void 148 dns_compress_setpermitted(dns_compress_t *cctx, bool permitted); 149 150 /*%< 151 * Sets whether compression is allowed, according to RFC 3597. 152 * This can vary depending on the rdata type. 153 * 154 * Requires: 155 *\li 'cctx' to be initialized. 156 */ 157 158 bool 159 dns_compress_getpermitted(dns_compress_t *cctx); 160 161 /*%< 162 * Find out whether compression is allowed, according to RFC 3597. 163 * 164 * Requires: 165 *\li 'cctx' to be initialized. 166 * 167 * Returns: 168 *\li allowed compression bitmap. 169 */ 170 171 void 172 dns_compress_name(dns_compress_t *cctx, isc_buffer_t *buffer, 173 const dns_name_t *name, unsigned int *return_prefix, 174 unsigned int *return_coff); 175 /*%< 176 * Finds longest suffix matching 'name' in the compression table, 177 * and adds any remaining prefix of 'name' to the table. 178 * 179 * This is used by dns_name_towire() for both compressed and uncompressed 180 * names; for uncompressed names, dns_name_towire() does not need to know 181 * about the matching suffix, but it still needs to add the name for use 182 * by later compression pointers. For example, an owner name of a record 183 * in the additional section will often need to refer back to an RFC 3597 184 * uncompressed name in the rdata of a record in the answer section. 185 * 186 * Requires: 187 *\li 'cctx' to be initialized. 188 *\li 'buffer' contains the rendered message. 189 *\li 'name' to be a absolute name. 190 *\li 'return_prefix' points to an unsigned int. 191 *\li 'return_coff' points to an unsigned int, which must be zero. 192 * 193 * Ensures: 194 *\li When no suffix is found, the return variables 195 * 'return_prefix' and 'return_coff' are unchanged 196 * 197 *\li Otherwise, '*return_prefix' is set to the length of the 198 * prefix of the name that did not match, and '*suffix_coff' 199 * is set to a nonzero compression offset of the match. 200 */ 201 202 void 203 dns_compress_rollback(dns_compress_t *cctx, unsigned int offset); 204 /*%< 205 * Remove any compression pointers from the table that are >= offset. 206 * 207 * Requires: 208 *\li 'cctx' is initialized. 209 */ 210 211 /*% 212 * Set whether decompression is allowed, according to RFC 3597 213 */ 214 static inline dns_decompress_t /* inline to suppress code generation */ 215 dns_decompress_setpermitted(dns_decompress_t dctx, bool permitted) { 216 if (dctx == DNS_DECOMPRESS_NEVER || dctx == DNS_DECOMPRESS_ALWAYS) { 217 return dctx; 218 } else if (permitted) { 219 return DNS_DECOMPRESS_PERMITTED; 220 } else { 221 return DNS_DECOMPRESS_DEFAULT; 222 } 223 } 224 225 /*% 226 * Returns whether decompression is allowed here 227 */ 228 static inline bool /* inline to suppress code generation */ 229 dns_decompress_getpermitted(dns_decompress_t dctx) { 230 return dctx == DNS_DECOMPRESS_ALWAYS || 231 dctx == DNS_DECOMPRESS_PERMITTED; 232 } 233 234 ISC_LANG_ENDDECLS 235