1 /* $NetBSD: old.c,v 1.2 2025/01/26 16:25:21 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 #include <isc/ascii.h> 17 #include <isc/buffer.h> 18 #include <isc/result.h> 19 #include <isc/types.h> 20 #include <isc/util.h> 21 22 #include <dns/compress.h> 23 #include <dns/types.h> 24 25 /* 26 */ 27 28 #include "old.h" 29 30 /* 31 * code copied from lib/dns/name.c as of commit 32 * 6967973568fe80b03e1729259f8907ce8792be34 33 */ 34 35 typedef enum { fw_start = 0, fw_ordinary, fw_newcurrent } fw_state; 36 37 #define VALID_NAME(n) ISC_MAGIC_VALID(n, DNS_NAME_MAGIC) 38 39 #define INIT_OFFSETS(name, var, default_offsets) \ 40 if ((name)->offsets != NULL) \ 41 var = (name)->offsets; \ 42 else \ 43 var = (default_offsets); 44 45 #define MAKE_EMPTY(name) \ 46 do { \ 47 name->ndata = NULL; \ 48 name->length = 0; \ 49 name->labels = 0; \ 50 name->attributes.absolute = false; \ 51 } while (0) 52 53 #define BINDABLE(name) (!name->attributes.readonly && !name->attributes.dynamic) 54 55 isc_result_t 56 old_name_fromwire(dns_name_t *name, isc_buffer_t *source, dns_decompress_t dctx, 57 unsigned int options, isc_buffer_t *target) { 58 unsigned char *cdata, *ndata; 59 unsigned int cused; /* Bytes of compressed name data used */ 60 unsigned int nused, labels, n, nmax; 61 unsigned int current, new_current, biggest_pointer; 62 bool done; 63 fw_state state = fw_start; 64 unsigned int c; 65 unsigned char *offsets; 66 dns_offsets_t odata; 67 bool downcase; 68 bool seen_pointer; 69 70 /* 71 * Copy the possibly-compressed name at source into target, 72 * decompressing it. Loop prevention is performed by checking 73 * the new pointer against biggest_pointer. 74 */ 75 76 REQUIRE(VALID_NAME(name)); 77 REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) || 78 (target == NULL && ISC_BUFFER_VALID(name->buffer))); 79 80 downcase = ((options & DNS_NAME_DOWNCASE) != 0); 81 82 if (target == NULL && name->buffer != NULL) { 83 target = name->buffer; 84 isc_buffer_clear(target); 85 } 86 87 REQUIRE(BINDABLE(name)); 88 89 INIT_OFFSETS(name, offsets, odata); 90 91 /* 92 * Make 'name' empty in case of failure. 93 */ 94 MAKE_EMPTY(name); 95 96 /* 97 * Initialize things to make the compiler happy; they're not required. 98 */ 99 n = 0; 100 new_current = 0; 101 102 /* 103 * Set up. 104 */ 105 labels = 0; 106 done = false; 107 108 ndata = isc_buffer_used(target); 109 nused = 0; 110 seen_pointer = false; 111 112 /* 113 * Find the maximum number of uncompressed target name 114 * bytes we are willing to generate. This is the smaller 115 * of the available target buffer length and the 116 * maximum legal domain name length (255). 117 */ 118 nmax = isc_buffer_availablelength(target); 119 if (nmax > DNS_NAME_MAXWIRE) { 120 nmax = DNS_NAME_MAXWIRE; 121 } 122 123 cdata = isc_buffer_current(source); 124 cused = 0; 125 126 current = source->current; 127 biggest_pointer = current; 128 129 /* 130 * Note: The following code is not optimized for speed, but 131 * rather for correctness. Speed will be addressed in the future. 132 */ 133 134 while (current < source->active && !done) { 135 c = *cdata++; 136 current++; 137 if (!seen_pointer) { 138 cused++; 139 } 140 141 switch (state) { 142 case fw_start: 143 if (c < 64) { 144 offsets[labels] = nused; 145 labels++; 146 if (nused + c + 1 > nmax) { 147 goto full; 148 } 149 nused += c + 1; 150 *ndata++ = c; 151 if (c == 0) { 152 done = true; 153 } 154 n = c; 155 state = fw_ordinary; 156 } else if (c >= 192) { 157 /* 158 * 14-bit compression pointer 159 */ 160 if (!dns_decompress_getpermitted(dctx)) { 161 return DNS_R_DISALLOWED; 162 } 163 new_current = c & 0x3F; 164 state = fw_newcurrent; 165 } else { 166 return DNS_R_BADLABELTYPE; 167 } 168 break; 169 case fw_ordinary: 170 if (downcase) { 171 c = isc_ascii_tolower(c); 172 } 173 *ndata++ = c; 174 n--; 175 if (n == 0) { 176 state = fw_start; 177 } 178 break; 179 case fw_newcurrent: 180 new_current *= 256; 181 new_current += c; 182 if (new_current >= biggest_pointer) { 183 return DNS_R_BADPOINTER; 184 } 185 biggest_pointer = new_current; 186 current = new_current; 187 cdata = (unsigned char *)source->base + current; 188 seen_pointer = true; 189 state = fw_start; 190 break; 191 default: 192 FATAL_ERROR("Unknown state %d", state); 193 /* Does not return. */ 194 } 195 } 196 197 if (!done) { 198 return ISC_R_UNEXPECTEDEND; 199 } 200 201 name->ndata = (unsigned char *)target->base + target->used; 202 name->labels = labels; 203 name->length = nused; 204 name->attributes.absolute = true; 205 206 isc_buffer_forward(source, cused); 207 isc_buffer_add(target, name->length); 208 209 return ISC_R_SUCCESS; 210 211 full: 212 if (nmax == DNS_NAME_MAXWIRE) { 213 /* 214 * The name did not fit even though we had a buffer 215 * big enough to fit a maximum-length name. 216 */ 217 return DNS_R_NAMETOOLONG; 218 } else { 219 /* 220 * The name might fit if only the caller could give us a 221 * big enough buffer. 222 */ 223 return ISC_R_NOSPACE; 224 } 225 } 226