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