/src/bind9/lib/dns/name.c
Line | Count | Source (jump to first uncovered line) |
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 | | /*! \file */ |
15 | | |
16 | | #include <ctype.h> |
17 | | #include <inttypes.h> |
18 | | #include <stdbool.h> |
19 | | #include <stdlib.h> |
20 | | #include <unistd.h> |
21 | | |
22 | | #include <isc/ascii.h> |
23 | | #include <isc/attributes.h> |
24 | | #include <isc/buffer.h> |
25 | | #include <isc/hash.h> |
26 | | #include <isc/hex.h> |
27 | | #include <isc/mem.h> |
28 | | #include <isc/once.h> |
29 | | #include <isc/random.h> |
30 | | #include <isc/result.h> |
31 | | #include <isc/string.h> |
32 | | #include <isc/thread.h> |
33 | | #include <isc/util.h> |
34 | | |
35 | | #include <dns/compress.h> |
36 | | #include <dns/fixedname.h> |
37 | | #include <dns/name.h> |
38 | | |
39 | | typedef enum { |
40 | | ft_init = 0, |
41 | | ft_start, |
42 | | ft_ordinary, |
43 | | ft_initialescape, |
44 | | ft_escape, |
45 | | ft_escdecimal, |
46 | | ft_at |
47 | | } ft_state; |
48 | | |
49 | | /*% |
50 | | * Note that the name data must be a char array, not a string |
51 | | * literal, to avoid compiler warnings about discarding |
52 | | * the const attribute of a string. |
53 | | */ |
54 | | static unsigned char root_ndata[] = { "" }; |
55 | | static dns_name_t root = DNS_NAME_INITABSOLUTE(root_ndata); |
56 | | const dns_name_t *dns_rootname = &root; |
57 | | |
58 | | static unsigned char wild_ndata[] = { "\001*" }; |
59 | | |
60 | | static dns_name_t const wild = DNS_NAME_INITNONABSOLUTE(wild_ndata); |
61 | | const dns_name_t *dns_wildcardname = &wild; |
62 | | |
63 | | /* |
64 | | * dns_name_t to text post-conversion procedure. |
65 | | */ |
66 | | static thread_local dns_name_totextfilter_t *totext_filter_proc = NULL; |
67 | | |
68 | | bool |
69 | 0 | dns_name_isvalid(const dns_name_t *name) { |
70 | 0 | unsigned char *ndata; |
71 | 0 | unsigned int offset, count, length, nlabels; |
72 | |
|
73 | 0 | if (!DNS_NAME_VALID(name)) { |
74 | 0 | return false; |
75 | 0 | } |
76 | | |
77 | 0 | ndata = name->ndata; |
78 | 0 | length = name->length; |
79 | 0 | offset = 0; |
80 | 0 | nlabels = 0; |
81 | |
|
82 | 0 | while (offset != length) { |
83 | 0 | count = *ndata; |
84 | 0 | if (count > DNS_NAME_LABELLEN) { |
85 | 0 | return false; |
86 | 0 | } |
87 | | |
88 | 0 | nlabels++; |
89 | 0 | offset += count + 1; |
90 | 0 | ndata += count + 1; |
91 | 0 | if (offset > length) { |
92 | 0 | return false; |
93 | 0 | } |
94 | | |
95 | 0 | if (count == 0) { |
96 | 0 | break; |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | 0 | if (nlabels > DNS_NAME_MAXLABELS || offset != name->length) { |
101 | 0 | return false; |
102 | 0 | } |
103 | | |
104 | 0 | return true; |
105 | 0 | } |
106 | | |
107 | | bool |
108 | 0 | dns_name_hasbuffer(const dns_name_t *name) { |
109 | | /* |
110 | | * Does 'name' have a dedicated buffer? |
111 | | */ |
112 | |
|
113 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
114 | |
|
115 | 0 | if (name->buffer != NULL) { |
116 | 0 | return true; |
117 | 0 | } |
118 | | |
119 | 0 | return false; |
120 | 0 | } |
121 | | |
122 | | bool |
123 | 0 | dns_name_isabsolute(const dns_name_t *name) { |
124 | | /* |
125 | | * Does 'name' end in the root label? |
126 | | */ |
127 | |
|
128 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
129 | |
|
130 | 0 | return name->attributes.absolute; |
131 | 0 | } |
132 | | |
133 | 0 | #define hyphenchar(c) ((c) == 0x2d) |
134 | | #define asterchar(c) ((c) == 0x2a) |
135 | | #define alphachar(c) \ |
136 | 0 | (((c) >= 0x41 && (c) <= 0x5a) || ((c) >= 0x61 && (c) <= 0x7a)) |
137 | 0 | #define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) |
138 | 0 | #define borderchar(c) (alphachar(c) || digitchar(c)) |
139 | 0 | #define middlechar(c) (borderchar(c) || hyphenchar(c)) |
140 | 0 | #define domainchar(c) ((c) > 0x20 && (c) < 0x7f) |
141 | | |
142 | | bool |
143 | 0 | dns_name_ismailbox(const dns_name_t *name) { |
144 | 0 | unsigned char *ndata, ch; |
145 | 0 | unsigned int n; |
146 | 0 | bool first; |
147 | |
|
148 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
149 | 0 | REQUIRE(name->length > 0); |
150 | 0 | REQUIRE(name->attributes.absolute); |
151 | | |
152 | | /* |
153 | | * Root label. |
154 | | */ |
155 | 0 | if (name->length == 1) { |
156 | 0 | return true; |
157 | 0 | } |
158 | | |
159 | 0 | ndata = name->ndata; |
160 | 0 | n = *ndata++; |
161 | 0 | INSIST(n <= DNS_NAME_LABELLEN); |
162 | 0 | while (n--) { |
163 | 0 | ch = *ndata++; |
164 | 0 | if (!domainchar(ch)) { |
165 | 0 | return false; |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | 0 | if (ndata == name->ndata + name->length) { |
170 | 0 | return false; |
171 | 0 | } |
172 | | |
173 | | /* |
174 | | * RFC952/RFC1123 hostname. |
175 | | */ |
176 | 0 | while (ndata < (name->ndata + name->length)) { |
177 | 0 | n = *ndata++; |
178 | 0 | INSIST(n <= DNS_NAME_LABELLEN); |
179 | 0 | first = true; |
180 | 0 | while (n--) { |
181 | 0 | ch = *ndata++; |
182 | 0 | if (first || n == 0) { |
183 | 0 | if (!borderchar(ch)) { |
184 | 0 | return false; |
185 | 0 | } |
186 | 0 | } else { |
187 | 0 | if (!middlechar(ch)) { |
188 | 0 | return false; |
189 | 0 | } |
190 | 0 | } |
191 | 0 | first = false; |
192 | 0 | } |
193 | 0 | } |
194 | 0 | return true; |
195 | 0 | } |
196 | | |
197 | | bool |
198 | 0 | dns_name_ishostname(const dns_name_t *name, bool wildcard) { |
199 | 0 | unsigned char *ndata, ch; |
200 | 0 | unsigned int n; |
201 | 0 | bool first; |
202 | |
|
203 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
204 | 0 | REQUIRE(name->length > 0); |
205 | 0 | REQUIRE(name->attributes.absolute); |
206 | | |
207 | | /* |
208 | | * Root label. |
209 | | */ |
210 | 0 | if (name->length == 1) { |
211 | 0 | return true; |
212 | 0 | } |
213 | | |
214 | | /* |
215 | | * Skip wildcard if this is a ownername. |
216 | | */ |
217 | 0 | ndata = name->ndata; |
218 | 0 | if (wildcard && ndata[0] == 1 && ndata[1] == '*') { |
219 | 0 | ndata += 2; |
220 | 0 | } |
221 | | |
222 | | /* |
223 | | * RFC952/RFC1123 hostname. |
224 | | */ |
225 | 0 | while (ndata < (name->ndata + name->length)) { |
226 | 0 | n = *ndata++; |
227 | 0 | INSIST(n <= DNS_NAME_LABELLEN); |
228 | 0 | first = true; |
229 | 0 | while (n--) { |
230 | 0 | ch = *ndata++; |
231 | 0 | if (first || n == 0) { |
232 | 0 | if (!borderchar(ch)) { |
233 | 0 | return false; |
234 | 0 | } |
235 | 0 | } else { |
236 | 0 | if (!middlechar(ch)) { |
237 | 0 | return false; |
238 | 0 | } |
239 | 0 | } |
240 | 0 | first = false; |
241 | 0 | } |
242 | 0 | } |
243 | 0 | return true; |
244 | 0 | } |
245 | | |
246 | | bool |
247 | 0 | dns_name_iswildcard(const dns_name_t *name) { |
248 | 0 | unsigned char *ndata; |
249 | | |
250 | | /* |
251 | | * Is 'name' a wildcard name? |
252 | | */ |
253 | |
|
254 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
255 | 0 | REQUIRE(name->length > 0); |
256 | |
|
257 | 0 | if (name->length >= 2) { |
258 | 0 | ndata = name->ndata; |
259 | 0 | if (ndata[0] == 1 && ndata[1] == '*') { |
260 | 0 | return true; |
261 | 0 | } |
262 | 0 | } |
263 | | |
264 | 0 | return false; |
265 | 0 | } |
266 | | |
267 | | bool |
268 | 0 | dns_name_internalwildcard(const dns_name_t *name) { |
269 | 0 | unsigned char *ndata; |
270 | 0 | unsigned int count; |
271 | 0 | unsigned int label; |
272 | | |
273 | | /* |
274 | | * Does 'name' contain a internal wildcard? |
275 | | */ |
276 | |
|
277 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
278 | | |
279 | | /* |
280 | | * Skip first label. |
281 | | */ |
282 | 0 | ndata = name->ndata; |
283 | 0 | count = *ndata++; |
284 | 0 | INSIST(count <= DNS_NAME_LABELLEN); |
285 | 0 | ndata += count; |
286 | 0 | label = 1; |
287 | |
|
288 | 0 | uint8_t labels = dns_name_countlabels(name); |
289 | 0 | while (label + 1 < labels) { |
290 | 0 | count = *ndata++; |
291 | 0 | INSIST(count <= DNS_NAME_LABELLEN); |
292 | |
|
293 | 0 | if (count == 1 && *ndata == '*') { |
294 | 0 | return true; |
295 | 0 | } |
296 | 0 | ndata += count; |
297 | 0 | label++; |
298 | 0 | } |
299 | 0 | return false; |
300 | 0 | } |
301 | | |
302 | | uint32_t |
303 | 0 | dns_name_hash(const dns_name_t *name) { |
304 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
305 | |
|
306 | 0 | return isc_hash32(name->ndata, name->length, false); |
307 | 0 | } |
308 | | |
309 | | dns_namereln_t |
310 | | dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2, |
311 | 0 | int *orderp, unsigned int *nlabelsp) { |
312 | 0 | unsigned int l1, l2, l, count1, count2, count, nlabels; |
313 | 0 | int cdiff, ldiff, diff; |
314 | 0 | unsigned char *label1, *label2; |
315 | 0 | dns_offsets_t offsets1, offsets2; |
316 | 0 | dns_namereln_t namereln = dns_namereln_none; |
317 | | |
318 | | /* |
319 | | * Determine the relative ordering under the DNSSEC order relation of |
320 | | * 'name1' and 'name2', and also determine the hierarchical |
321 | | * relationship of the names. |
322 | | * |
323 | | * Note: It makes no sense for one of the names to be relative and the |
324 | | * other absolute. If both names are relative, then to be meaningfully |
325 | | * compared the caller must ensure that they are both relative to the |
326 | | * same domain. |
327 | | */ |
328 | |
|
329 | 0 | REQUIRE(DNS_NAME_VALID(name1)); |
330 | 0 | REQUIRE(DNS_NAME_VALID(name2)); |
331 | 0 | REQUIRE(orderp != NULL); |
332 | 0 | REQUIRE(nlabelsp != NULL); |
333 | | /* |
334 | | * Either name1 is absolute and name2 is absolute, or neither is. |
335 | | */ |
336 | 0 | REQUIRE((name1->attributes.absolute) == (name2->attributes.absolute)); |
337 | |
|
338 | 0 | if (name1 == name2) { |
339 | 0 | *orderp = 0; |
340 | 0 | *nlabelsp = dns_name_countlabels(name1); |
341 | |
|
342 | 0 | return dns_namereln_equal; |
343 | 0 | } |
344 | | |
345 | 0 | l1 = dns_name_offsets(name1, offsets1); |
346 | 0 | l2 = dns_name_offsets(name2, offsets2); |
347 | |
|
348 | 0 | nlabels = 0; |
349 | 0 | if (l2 > l1) { |
350 | 0 | l = l1; |
351 | 0 | ldiff = 0 - (l2 - l1); |
352 | 0 | } else { |
353 | 0 | l = l2; |
354 | 0 | ldiff = l1 - l2; |
355 | 0 | } |
356 | |
|
357 | 0 | while (l-- > 0) { |
358 | 0 | l1--; |
359 | 0 | l2--; |
360 | 0 | label1 = &name1->ndata[offsets1[l1]]; |
361 | 0 | label2 = &name2->ndata[offsets2[l2]]; |
362 | 0 | count1 = *label1++; |
363 | 0 | count2 = *label2++; |
364 | |
|
365 | 0 | cdiff = (int)count1 - (int)count2; |
366 | 0 | if (cdiff < 0) { |
367 | 0 | count = count1; |
368 | 0 | } else { |
369 | 0 | count = count2; |
370 | 0 | } |
371 | |
|
372 | 0 | diff = isc_ascii_lowercmp(label1, label2, count); |
373 | 0 | if (diff != 0) { |
374 | 0 | *orderp = diff; |
375 | 0 | goto done; |
376 | 0 | } |
377 | | |
378 | 0 | if (cdiff != 0) { |
379 | 0 | *orderp = cdiff; |
380 | 0 | goto done; |
381 | 0 | } |
382 | 0 | nlabels++; |
383 | 0 | } |
384 | | |
385 | 0 | *orderp = ldiff; |
386 | 0 | if (ldiff < 0) { |
387 | 0 | namereln = dns_namereln_contains; |
388 | 0 | } else if (ldiff > 0) { |
389 | 0 | namereln = dns_namereln_subdomain; |
390 | 0 | } else { |
391 | 0 | namereln = dns_namereln_equal; |
392 | 0 | } |
393 | 0 | *nlabelsp = nlabels; |
394 | 0 | return namereln; |
395 | | |
396 | 0 | done: |
397 | 0 | *nlabelsp = nlabels; |
398 | 0 | if (nlabels > 0) { |
399 | 0 | namereln = dns_namereln_commonancestor; |
400 | 0 | } |
401 | |
|
402 | 0 | return namereln; |
403 | 0 | } |
404 | | |
405 | | int |
406 | 0 | dns_name_compare(const dns_name_t *name1, const dns_name_t *name2) { |
407 | 0 | int order; |
408 | 0 | unsigned int nlabels; |
409 | | |
410 | | /* |
411 | | * Determine the relative ordering under the DNSSEC order relation of |
412 | | * 'name1' and 'name2'. |
413 | | * |
414 | | * Note: It makes no sense for one of the names to be relative and the |
415 | | * other absolute. If both names are relative, then to be meaningfully |
416 | | * compared the caller must ensure that they are both relative to the |
417 | | * same domain. |
418 | | */ |
419 | |
|
420 | 0 | (void)dns_name_fullcompare(name1, name2, &order, &nlabels); |
421 | |
|
422 | 0 | return order; |
423 | 0 | } |
424 | | |
425 | | bool |
426 | 0 | dns_name_equal(const dns_name_t *name1, const dns_name_t *name2) { |
427 | 0 | unsigned int length; |
428 | | |
429 | | /* |
430 | | * Are 'name1' and 'name2' equal? |
431 | | * |
432 | | * Note: It makes no sense for one of the names to be relative and the |
433 | | * other absolute. If both names are relative, then to be meaningfully |
434 | | * compared the caller must ensure that they are both relative to the |
435 | | * same domain. |
436 | | */ |
437 | |
|
438 | 0 | REQUIRE(DNS_NAME_VALID(name1)); |
439 | 0 | REQUIRE(DNS_NAME_VALID(name2)); |
440 | | /* |
441 | | * Either name1 is absolute and name2 is absolute, or neither is. |
442 | | */ |
443 | 0 | REQUIRE((name1->attributes.absolute) == (name2->attributes.absolute)); |
444 | |
|
445 | 0 | if (name1 == name2) { |
446 | 0 | return true; |
447 | 0 | } |
448 | | |
449 | 0 | length = name1->length; |
450 | 0 | if (length != name2->length) { |
451 | 0 | return false; |
452 | 0 | } |
453 | | |
454 | | /* label lengths are < 64 so tolower() does not affect them */ |
455 | 0 | return isc_ascii_lowerequal(name1->ndata, name2->ndata, length); |
456 | 0 | } |
457 | | |
458 | | bool |
459 | 0 | dns_name_caseequal(const dns_name_t *name1, const dns_name_t *name2) { |
460 | | /* |
461 | | * Are 'name1' and 'name2' equal? |
462 | | * |
463 | | * Note: It makes no sense for one of the names to be relative and the |
464 | | * other absolute. If both names are relative, then to be meaningfully |
465 | | * compared the caller must ensure that they are both relative to the |
466 | | * same domain. |
467 | | */ |
468 | |
|
469 | 0 | REQUIRE(DNS_NAME_VALID(name1)); |
470 | 0 | REQUIRE(DNS_NAME_VALID(name2)); |
471 | | /* |
472 | | * Either name1 is absolute and name2 is absolute, or neither is. |
473 | | */ |
474 | 0 | REQUIRE((name1->attributes.absolute) == (name2->attributes.absolute)); |
475 | |
|
476 | 0 | if (name1->length != name2->length) { |
477 | 0 | return false; |
478 | 0 | } |
479 | | |
480 | 0 | if (memcmp(name1->ndata, name2->ndata, name1->length) != 0) { |
481 | 0 | return false; |
482 | 0 | } |
483 | | |
484 | 0 | return true; |
485 | 0 | } |
486 | | |
487 | | int |
488 | 0 | dns_name_rdatacompare(const dns_name_t *name1, const dns_name_t *name2) { |
489 | | /* |
490 | | * Compare two absolute names as rdata. |
491 | | */ |
492 | |
|
493 | 0 | REQUIRE(DNS_NAME_VALID(name1)); |
494 | 0 | REQUIRE(name1->length > 0); |
495 | 0 | REQUIRE(name1->attributes.absolute); |
496 | 0 | REQUIRE(DNS_NAME_VALID(name2)); |
497 | 0 | REQUIRE(name2->length > 0); |
498 | 0 | REQUIRE(name2->attributes.absolute); |
499 | | |
500 | | /* label lengths are < 64 so tolower() does not affect them */ |
501 | 0 | return isc_ascii_lowercmp(name1->ndata, name2->ndata, |
502 | 0 | ISC_MIN(name1->length, name2->length)); |
503 | 0 | } |
504 | | |
505 | | bool |
506 | 0 | dns_name_issubdomain(const dns_name_t *name1, const dns_name_t *name2) { |
507 | 0 | int order; |
508 | 0 | unsigned int nlabels; |
509 | 0 | dns_namereln_t namereln; |
510 | | |
511 | | /* |
512 | | * Is 'name1' a subdomain of 'name2'? |
513 | | * |
514 | | * Note: It makes no sense for one of the names to be relative and the |
515 | | * other absolute. If both names are relative, then to be meaningfully |
516 | | * compared the caller must ensure that they are both relative to the |
517 | | * same domain. |
518 | | */ |
519 | |
|
520 | 0 | namereln = dns_name_fullcompare(name1, name2, &order, &nlabels); |
521 | 0 | if (namereln == dns_namereln_subdomain || |
522 | 0 | namereln == dns_namereln_equal) |
523 | 0 | { |
524 | 0 | return true; |
525 | 0 | } |
526 | | |
527 | 0 | return false; |
528 | 0 | } |
529 | | |
530 | | bool |
531 | 0 | dns_name_matcheswildcard(const dns_name_t *name, const dns_name_t *wname) { |
532 | 0 | int order; |
533 | 0 | unsigned int nlabels, labels; |
534 | 0 | dns_name_t tname; |
535 | |
|
536 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
537 | 0 | REQUIRE(name->length > 0); |
538 | 0 | REQUIRE(DNS_NAME_VALID(wname)); |
539 | 0 | labels = dns_name_countlabels(wname); |
540 | 0 | REQUIRE(labels > 0); |
541 | 0 | REQUIRE(dns_name_iswildcard(wname)); |
542 | |
|
543 | 0 | dns_name_init(&tname); |
544 | 0 | dns_name_getlabelsequence(wname, 1, labels - 1, &tname); |
545 | 0 | if (dns_name_fullcompare(name, &tname, &order, &nlabels) == |
546 | 0 | dns_namereln_subdomain) |
547 | 0 | { |
548 | 0 | return true; |
549 | 0 | } |
550 | 0 | return false; |
551 | 0 | } |
552 | | |
553 | | void |
554 | 0 | dns_name_getlabel(const dns_name_t *name, unsigned int n, dns_label_t *label) { |
555 | 0 | dns_offsets_t offsets; |
556 | | |
557 | | /* |
558 | | * Make 'label' refer to the 'n'th least significant label of 'name'. |
559 | | */ |
560 | |
|
561 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
562 | 0 | REQUIRE(label != NULL); |
563 | |
|
564 | 0 | uint8_t labels = dns_name_offsets(name, offsets); |
565 | |
|
566 | 0 | REQUIRE(labels > 0); |
567 | 0 | REQUIRE(n < labels); |
568 | |
|
569 | 0 | label->base = &name->ndata[offsets[n]]; |
570 | 0 | if (n == (unsigned int)labels - 1) { |
571 | 0 | label->length = name->length - offsets[n]; |
572 | 0 | } else { |
573 | 0 | label->length = offsets[n + 1] - offsets[n]; |
574 | 0 | } |
575 | 0 | } |
576 | | |
577 | | void |
578 | | dns_name_getlabelsequence(const dns_name_t *source, unsigned int first, |
579 | 0 | unsigned int n, dns_name_t *target) { |
580 | 0 | unsigned char *p, l; |
581 | 0 | unsigned int firstoffset, endoffset; |
582 | 0 | unsigned int i; |
583 | | |
584 | | /* |
585 | | * Make 'target' refer to the 'n' labels including and following |
586 | | * 'first' in 'source'. |
587 | | */ |
588 | |
|
589 | 0 | REQUIRE(DNS_NAME_VALID(source)); |
590 | 0 | REQUIRE(DNS_NAME_VALID(target)); |
591 | 0 | REQUIRE(DNS_NAME_BINDABLE(target)); |
592 | |
|
593 | 0 | uint8_t labels = dns_name_countlabels(source); |
594 | 0 | REQUIRE(first <= labels && n <= labels - first); |
595 | |
|
596 | 0 | p = source->ndata; |
597 | 0 | if (first == labels) { |
598 | 0 | firstoffset = source->length; |
599 | 0 | } else { |
600 | 0 | for (i = 0; i < first; i++) { |
601 | 0 | l = *p; |
602 | 0 | p += l + 1; |
603 | 0 | } |
604 | 0 | firstoffset = (unsigned int)(p - source->ndata); |
605 | 0 | } |
606 | |
|
607 | 0 | if (first + n == labels) { |
608 | 0 | endoffset = source->length; |
609 | 0 | } else { |
610 | 0 | for (i = 0; i < n; i++) { |
611 | 0 | l = *p; |
612 | 0 | p += l + 1; |
613 | 0 | } |
614 | 0 | endoffset = (unsigned int)(p - source->ndata); |
615 | 0 | } |
616 | |
|
617 | 0 | target->ndata = &source->ndata[firstoffset]; |
618 | 0 | target->length = endoffset - firstoffset; |
619 | |
|
620 | 0 | if (first + n == labels && n > 0 && source->attributes.absolute) { |
621 | 0 | target->attributes.absolute = true; |
622 | 0 | } else { |
623 | 0 | target->attributes.absolute = false; |
624 | 0 | } |
625 | 0 | } |
626 | | |
627 | | void |
628 | 0 | dns_name_clone(const dns_name_t *source, dns_name_t *target) { |
629 | | /* |
630 | | * Make 'target' refer to the same name as 'source'. |
631 | | */ |
632 | |
|
633 | 0 | REQUIRE(DNS_NAME_VALID(source)); |
634 | 0 | REQUIRE(DNS_NAME_VALID(target)); |
635 | 0 | REQUIRE(DNS_NAME_BINDABLE(target)); |
636 | |
|
637 | 0 | target->ndata = source->ndata; |
638 | 0 | target->length = source->length; |
639 | 0 | target->attributes = source->attributes; |
640 | 0 | target->attributes.readonly = false; |
641 | 0 | target->attributes.dynamic = false; |
642 | 0 | } |
643 | | |
644 | | void |
645 | 0 | dns_name_fromregion(dns_name_t *name, const isc_region_t *r) { |
646 | 0 | size_t length; |
647 | 0 | isc_region_t r2 = { .base = NULL, .length = 0 }; |
648 | | |
649 | | /* |
650 | | * Make 'name' refer to region 'r'. |
651 | | */ |
652 | |
|
653 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
654 | 0 | REQUIRE(r != NULL); |
655 | 0 | REQUIRE(DNS_NAME_BINDABLE(name)); |
656 | |
|
657 | 0 | name->ndata = r->base; |
658 | 0 | if (name->buffer != NULL) { |
659 | 0 | isc_buffer_clear(name->buffer); |
660 | 0 | isc_buffer_availableregion(name->buffer, &r2); |
661 | 0 | length = (r->length < r2.length) ? r->length : r2.length; |
662 | 0 | if (length > DNS_NAME_MAXWIRE) { |
663 | 0 | length = DNS_NAME_MAXWIRE; |
664 | 0 | } |
665 | 0 | } else { |
666 | 0 | length = (r->length <= DNS_NAME_MAXWIRE) ? r->length |
667 | 0 | : DNS_NAME_MAXWIRE; |
668 | 0 | } |
669 | |
|
670 | 0 | name->attributes.absolute = false; |
671 | |
|
672 | 0 | if (length > 0) { |
673 | 0 | size_t offset = 0; |
674 | 0 | uint8_t nlabels = 0; |
675 | 0 | while (offset != length) { |
676 | 0 | uint8_t count; |
677 | |
|
678 | 0 | INSIST(nlabels < DNS_NAME_MAXLABELS); |
679 | 0 | nlabels++; |
680 | |
|
681 | 0 | count = name->ndata[offset]; |
682 | 0 | INSIST(count <= DNS_NAME_LABELLEN); |
683 | |
|
684 | 0 | offset += count + 1; |
685 | 0 | INSIST(offset <= length); |
686 | |
|
687 | 0 | if (count == 0) { |
688 | 0 | name->attributes.absolute = true; |
689 | 0 | break; |
690 | 0 | } |
691 | 0 | } |
692 | 0 | name->length = offset; |
693 | 0 | } |
694 | |
|
695 | 0 | if (name->buffer != NULL) { |
696 | | /* |
697 | | * name->length has been updated by set_offsets to the actual |
698 | | * length of the name data so we can now copy the actual name |
699 | | * data and not anything after it. |
700 | | */ |
701 | 0 | if (name->length > 0) { |
702 | 0 | memmove(r2.base, r->base, name->length); |
703 | 0 | } |
704 | 0 | name->ndata = r2.base; |
705 | 0 | isc_buffer_add(name->buffer, name->length); |
706 | 0 | } |
707 | 0 | } |
708 | | |
709 | | static isc_result_t |
710 | | convert_text(isc_buffer_t *source, const dns_name_t *origin, |
711 | 0 | unsigned int options, dns_name_t *name, isc_buffer_t *target) { |
712 | 0 | unsigned char *ndata = NULL, *label = NULL; |
713 | 0 | char *tdata = NULL; |
714 | 0 | char c; |
715 | 0 | ft_state state; |
716 | 0 | unsigned int value = 0, count = 0; |
717 | 0 | unsigned int n1 = 0, n2 = 0; |
718 | 0 | unsigned int tlen, nrem, nused, digits = 0, labels, tused; |
719 | 0 | bool done; |
720 | 0 | bool downcase; |
721 | |
|
722 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
723 | 0 | REQUIRE(ISC_BUFFER_VALID(source)); |
724 | 0 | REQUIRE(ISC_BUFFER_VALID(target)); |
725 | |
|
726 | 0 | downcase = ((options & DNS_NAME_DOWNCASE) != 0); |
727 | |
|
728 | 0 | if (target == NULL && name->buffer != NULL) { |
729 | 0 | target = name->buffer; |
730 | 0 | isc_buffer_clear(target); |
731 | 0 | } |
732 | |
|
733 | 0 | REQUIRE(DNS_NAME_BINDABLE(name)); |
734 | | |
735 | | /* |
736 | | * Set up the state machine. |
737 | | */ |
738 | 0 | tdata = (char *)source->base + source->current; |
739 | 0 | tlen = isc_buffer_remaininglength(source); |
740 | 0 | tused = 0; |
741 | 0 | ndata = isc_buffer_used(target); |
742 | 0 | nrem = isc_buffer_availablelength(target); |
743 | 0 | if (nrem > DNS_NAME_MAXWIRE) { |
744 | 0 | nrem = DNS_NAME_MAXWIRE; |
745 | 0 | } |
746 | 0 | nused = 0; |
747 | 0 | labels = 0; |
748 | 0 | done = false; |
749 | 0 | state = ft_init; |
750 | |
|
751 | 0 | while (nrem > 0 && tlen > 0 && !done) { |
752 | 0 | c = *tdata++; |
753 | 0 | tlen--; |
754 | 0 | tused++; |
755 | |
|
756 | 0 | switch (state) { |
757 | 0 | case ft_init: |
758 | | /* |
759 | | * Is this the root name? |
760 | | */ |
761 | 0 | if (c == '.') { |
762 | 0 | if (tlen != 0) { |
763 | 0 | return DNS_R_EMPTYLABEL; |
764 | 0 | } |
765 | 0 | labels++; |
766 | 0 | *ndata++ = 0; |
767 | 0 | nrem--; |
768 | 0 | nused++; |
769 | 0 | done = true; |
770 | 0 | break; |
771 | 0 | } |
772 | 0 | if (c == '@' && tlen == 0) { |
773 | 0 | state = ft_at; |
774 | 0 | break; |
775 | 0 | } |
776 | | |
777 | 0 | FALLTHROUGH; |
778 | 0 | case ft_start: |
779 | 0 | label = ndata; |
780 | 0 | ndata++; |
781 | 0 | nrem--; |
782 | 0 | nused++; |
783 | 0 | count = 0; |
784 | 0 | if (c == '\\') { |
785 | 0 | state = ft_initialescape; |
786 | 0 | break; |
787 | 0 | } |
788 | 0 | state = ft_ordinary; |
789 | 0 | if (nrem == 0) { |
790 | 0 | return ISC_R_NOSPACE; |
791 | 0 | } |
792 | 0 | FALLTHROUGH; |
793 | 0 | case ft_ordinary: |
794 | 0 | if (c == '.') { |
795 | 0 | if (count == 0) { |
796 | 0 | return DNS_R_EMPTYLABEL; |
797 | 0 | } |
798 | 0 | *label = count; |
799 | 0 | labels++; |
800 | 0 | INSIST(labels < DNS_NAME_MAXLABELS); |
801 | 0 | if (tlen == 0) { |
802 | 0 | labels++; |
803 | 0 | *ndata++ = 0; |
804 | 0 | nrem--; |
805 | 0 | nused++; |
806 | 0 | done = true; |
807 | 0 | } |
808 | 0 | state = ft_start; |
809 | 0 | } else if (c == '\\') { |
810 | 0 | state = ft_escape; |
811 | 0 | } else { |
812 | 0 | if (count >= DNS_NAME_LABELLEN) { |
813 | 0 | return DNS_R_LABELTOOLONG; |
814 | 0 | } |
815 | 0 | count++; |
816 | 0 | if (downcase) { |
817 | 0 | c = isc_ascii_tolower(c); |
818 | 0 | } |
819 | 0 | *ndata++ = c; |
820 | 0 | nrem--; |
821 | 0 | nused++; |
822 | 0 | } |
823 | 0 | break; |
824 | 0 | case ft_initialescape: |
825 | 0 | if (c == '[') { |
826 | | /* |
827 | | * This looks like a bitstring label, which |
828 | | * was deprecated. Intentionally drop it. |
829 | | */ |
830 | 0 | return DNS_R_BADLABELTYPE; |
831 | 0 | } |
832 | 0 | state = ft_escape; |
833 | 0 | POST(state); |
834 | 0 | FALLTHROUGH; |
835 | 0 | case ft_escape: |
836 | 0 | if (!isdigit((unsigned char)c)) { |
837 | 0 | if (count >= DNS_NAME_LABELLEN) { |
838 | 0 | return DNS_R_LABELTOOLONG; |
839 | 0 | } |
840 | 0 | count++; |
841 | 0 | if (downcase) { |
842 | 0 | c = isc_ascii_tolower(c); |
843 | 0 | } |
844 | 0 | *ndata++ = c; |
845 | 0 | nrem--; |
846 | 0 | nused++; |
847 | 0 | state = ft_ordinary; |
848 | 0 | break; |
849 | 0 | } |
850 | 0 | digits = 0; |
851 | 0 | value = 0; |
852 | 0 | state = ft_escdecimal; |
853 | 0 | FALLTHROUGH; |
854 | 0 | case ft_escdecimal: |
855 | 0 | if (!isdigit((unsigned char)c)) { |
856 | 0 | return DNS_R_BADESCAPE; |
857 | 0 | } |
858 | 0 | value = 10 * value + c - '0'; |
859 | 0 | digits++; |
860 | 0 | if (digits == 3) { |
861 | 0 | if (value > 255) { |
862 | 0 | return DNS_R_BADESCAPE; |
863 | 0 | } |
864 | 0 | if (count >= DNS_NAME_LABELLEN) { |
865 | 0 | return DNS_R_LABELTOOLONG; |
866 | 0 | } |
867 | 0 | count++; |
868 | 0 | if (downcase) { |
869 | 0 | value = isc_ascii_tolower(value); |
870 | 0 | } |
871 | 0 | *ndata++ = value; |
872 | 0 | nrem--; |
873 | 0 | nused++; |
874 | 0 | state = ft_ordinary; |
875 | 0 | } |
876 | 0 | break; |
877 | 0 | default: |
878 | 0 | FATAL_ERROR("Unexpected state %d", state); |
879 | | /* Does not return. */ |
880 | 0 | } |
881 | 0 | } |
882 | | |
883 | 0 | if (!done) { |
884 | 0 | if (nrem == 0) { |
885 | 0 | return ISC_R_NOSPACE; |
886 | 0 | } |
887 | 0 | INSIST(tlen == 0); |
888 | 0 | if (state != ft_ordinary && state != ft_at) { |
889 | 0 | return ISC_R_UNEXPECTEDEND; |
890 | 0 | } |
891 | 0 | if (state == ft_ordinary) { |
892 | 0 | INSIST(count != 0); |
893 | 0 | INSIST(label != NULL); |
894 | 0 | *label = count; |
895 | 0 | labels++; |
896 | 0 | INSIST(labels < DNS_NAME_MAXLABELS); |
897 | 0 | } |
898 | 0 | if (origin != NULL) { |
899 | 0 | if (nrem < origin->length) { |
900 | 0 | return ISC_R_NOSPACE; |
901 | 0 | } |
902 | 0 | label = origin->ndata; |
903 | 0 | n1 = origin->length; |
904 | 0 | nrem -= n1; |
905 | 0 | POST(nrem); |
906 | 0 | while (n1 > 0) { |
907 | 0 | n2 = *label++; |
908 | 0 | INSIST(n2 <= DNS_NAME_LABELLEN); |
909 | 0 | *ndata++ = n2; |
910 | 0 | n1 -= n2 + 1; |
911 | 0 | nused += n2 + 1; |
912 | 0 | while (n2 > 0) { |
913 | 0 | c = *label++; |
914 | 0 | if (downcase) { |
915 | 0 | c = isc_ascii_tolower(c); |
916 | 0 | } |
917 | 0 | *ndata++ = c; |
918 | 0 | n2--; |
919 | 0 | } |
920 | 0 | labels++; |
921 | 0 | if (n1 > 0) { |
922 | 0 | INSIST(labels < DNS_NAME_MAXLABELS); |
923 | 0 | } |
924 | 0 | } |
925 | 0 | if (origin->attributes.absolute) { |
926 | 0 | name->attributes.absolute = true; |
927 | 0 | } |
928 | 0 | } |
929 | 0 | } else { |
930 | 0 | name->attributes.absolute = true; |
931 | 0 | } |
932 | | |
933 | 0 | name->ndata = (unsigned char *)target->base + target->used; |
934 | 0 | name->length = nused; |
935 | |
|
936 | 0 | isc_buffer_forward(source, tused); |
937 | 0 | isc_buffer_add(target, name->length); |
938 | |
|
939 | 0 | return ISC_R_SUCCESS; |
940 | 0 | } |
941 | | |
942 | | isc_result_t |
943 | | dns_name_wirefromtext(isc_buffer_t *source, const dns_name_t *origin, |
944 | 0 | unsigned int options, isc_buffer_t *target) { |
945 | 0 | dns_name_t name; |
946 | |
|
947 | 0 | REQUIRE(ISC_BUFFER_VALID(target)); |
948 | |
|
949 | 0 | dns_name_init(&name); |
950 | 0 | return convert_text(source, origin, options, &name, target); |
951 | 0 | } |
952 | | |
953 | | isc_result_t |
954 | | dns_name_fromtext(dns_name_t *name, isc_buffer_t *source, |
955 | 0 | const dns_name_t *origin, unsigned int options) { |
956 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
957 | 0 | REQUIRE(ISC_BUFFER_VALID(name->buffer)); |
958 | |
|
959 | 0 | isc_buffer_clear(name->buffer); |
960 | 0 | return convert_text(source, origin, options, name, name->buffer); |
961 | 0 | } |
962 | | |
963 | | isc_result_t |
964 | | dns_name_totext(const dns_name_t *name, unsigned int options, |
965 | 0 | isc_buffer_t *target) { |
966 | 0 | unsigned char *ndata; |
967 | 0 | char *tdata; |
968 | 0 | unsigned int nlen, tlen; |
969 | 0 | unsigned char c; |
970 | 0 | unsigned int trem, count; |
971 | 0 | unsigned int labels; |
972 | 0 | bool saw_root = false; |
973 | 0 | unsigned int oused; |
974 | 0 | bool omit_final_dot = ((options & DNS_NAME_OMITFINALDOT) != 0); |
975 | | |
976 | | /* |
977 | | * This function assumes the name is in proper uncompressed |
978 | | * wire format. |
979 | | */ |
980 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
981 | 0 | REQUIRE(ISC_BUFFER_VALID(target)); |
982 | |
|
983 | 0 | oused = target->used; |
984 | |
|
985 | 0 | ndata = name->ndata; |
986 | 0 | nlen = name->length; |
987 | 0 | labels = dns_name_countlabels(name); |
988 | 0 | tdata = isc_buffer_used(target); |
989 | 0 | tlen = isc_buffer_availablelength(target); |
990 | |
|
991 | 0 | trem = tlen; |
992 | |
|
993 | 0 | if (labels == 0 && nlen == 0) { |
994 | | /* |
995 | | * Special handling for an empty name. |
996 | | */ |
997 | 0 | if (trem == 0) { |
998 | 0 | return ISC_R_NOSPACE; |
999 | 0 | } |
1000 | | |
1001 | | /* |
1002 | | * The names of these booleans are misleading in this case. |
1003 | | * This empty name is not necessarily from the root node of |
1004 | | * the DNS root zone, nor is a final dot going to be included. |
1005 | | * They need to be set this way, though, to keep the "@" |
1006 | | * from being trounced. |
1007 | | */ |
1008 | 0 | saw_root = true; |
1009 | 0 | omit_final_dot = false; |
1010 | 0 | *tdata++ = '@'; |
1011 | 0 | trem--; |
1012 | | |
1013 | | /* |
1014 | | * Skip the while() loop. |
1015 | | */ |
1016 | 0 | nlen = 0; |
1017 | 0 | } else if (nlen == 1 && labels == 1 && *ndata == '\0') { |
1018 | | /* |
1019 | | * Special handling for the root label. |
1020 | | */ |
1021 | 0 | if (trem == 0) { |
1022 | 0 | return ISC_R_NOSPACE; |
1023 | 0 | } |
1024 | | |
1025 | 0 | saw_root = true; |
1026 | 0 | omit_final_dot = false; |
1027 | 0 | *tdata++ = '.'; |
1028 | 0 | trem--; |
1029 | | |
1030 | | /* |
1031 | | * Skip the while() loop. |
1032 | | */ |
1033 | 0 | nlen = 0; |
1034 | 0 | } |
1035 | | |
1036 | 0 | while (labels > 0 && nlen > 0 && trem > 0) { |
1037 | 0 | labels--; |
1038 | 0 | count = *ndata++; |
1039 | 0 | nlen--; |
1040 | 0 | if (count == 0) { |
1041 | 0 | saw_root = true; |
1042 | 0 | break; |
1043 | 0 | } |
1044 | 0 | if (count <= DNS_NAME_LABELLEN) { |
1045 | 0 | INSIST(nlen >= count); |
1046 | 0 | while (count > 0) { |
1047 | 0 | c = *ndata; |
1048 | 0 | switch (c) { |
1049 | | /* Special modifiers in zone files. */ |
1050 | 0 | case 0x40: /* '@' */ |
1051 | 0 | case 0x24: /* '$' */ |
1052 | 0 | if ((options & DNS_NAME_PRINCIPAL) != 0) |
1053 | 0 | { |
1054 | 0 | goto no_escape; |
1055 | 0 | } |
1056 | 0 | FALLTHROUGH; |
1057 | 0 | case 0x22: /* '"' */ |
1058 | 0 | case 0x28: /* '(' */ |
1059 | 0 | case 0x29: /* ')' */ |
1060 | 0 | case 0x2E: /* '.' */ |
1061 | 0 | case 0x3B: /* ';' */ |
1062 | 0 | case 0x5C: /* '\\' */ |
1063 | 0 | if (trem < 2) { |
1064 | 0 | return ISC_R_NOSPACE; |
1065 | 0 | } |
1066 | 0 | *tdata++ = '\\'; |
1067 | 0 | *tdata++ = c; |
1068 | 0 | ndata++; |
1069 | 0 | trem -= 2; |
1070 | 0 | nlen--; |
1071 | 0 | break; |
1072 | 0 | no_escape: |
1073 | 0 | default: |
1074 | 0 | if (c > 0x20 && c < 0x7f) { |
1075 | 0 | if (trem == 0) { |
1076 | 0 | return ISC_R_NOSPACE; |
1077 | 0 | } |
1078 | 0 | *tdata++ = c; |
1079 | 0 | ndata++; |
1080 | 0 | trem--; |
1081 | 0 | nlen--; |
1082 | 0 | } else { |
1083 | 0 | if (trem < 4) { |
1084 | 0 | return ISC_R_NOSPACE; |
1085 | 0 | } |
1086 | 0 | *tdata++ = 0x5c; |
1087 | 0 | *tdata++ = 0x30 + |
1088 | 0 | ((c / 100) % 10); |
1089 | 0 | *tdata++ = 0x30 + |
1090 | 0 | ((c / 10) % 10); |
1091 | 0 | *tdata++ = 0x30 + (c % 10); |
1092 | 0 | trem -= 4; |
1093 | 0 | ndata++; |
1094 | 0 | nlen--; |
1095 | 0 | } |
1096 | 0 | } |
1097 | 0 | count--; |
1098 | 0 | } |
1099 | 0 | } else { |
1100 | 0 | FATAL_ERROR("Unexpected label type %02x", count); |
1101 | 0 | UNREACHABLE(); |
1102 | 0 | } |
1103 | | |
1104 | | /* |
1105 | | * The following assumes names are absolute. If not, we |
1106 | | * fix things up later. Note that this means that in some |
1107 | | * cases one more byte of text buffer is required than is |
1108 | | * needed in the final output. |
1109 | | */ |
1110 | 0 | if (trem == 0) { |
1111 | 0 | return ISC_R_NOSPACE; |
1112 | 0 | } |
1113 | 0 | *tdata++ = '.'; |
1114 | 0 | trem--; |
1115 | 0 | } |
1116 | | |
1117 | 0 | if (nlen != 0 && trem == 0) { |
1118 | 0 | return ISC_R_NOSPACE; |
1119 | 0 | } |
1120 | | |
1121 | 0 | if (!saw_root || omit_final_dot) { |
1122 | 0 | trem++; |
1123 | 0 | tdata--; |
1124 | 0 | } |
1125 | 0 | if (trem > 0) { |
1126 | 0 | *tdata = 0; |
1127 | 0 | } |
1128 | 0 | isc_buffer_add(target, tlen - trem); |
1129 | |
|
1130 | 0 | if (totext_filter_proc != NULL) { |
1131 | 0 | return (totext_filter_proc)(target, oused); |
1132 | 0 | } |
1133 | | |
1134 | 0 | return ISC_R_SUCCESS; |
1135 | 0 | } |
1136 | | |
1137 | | isc_result_t |
1138 | | dns_name_tofilenametext(const dns_name_t *name, bool omit_final_dot, |
1139 | 0 | isc_buffer_t *target) { |
1140 | 0 | unsigned char *ndata; |
1141 | 0 | char *tdata; |
1142 | 0 | unsigned int nlen, tlen; |
1143 | 0 | unsigned char c; |
1144 | 0 | unsigned int trem, count; |
1145 | 0 | unsigned int labels; |
1146 | | |
1147 | | /* |
1148 | | * This function assumes the name is in proper uncompressed |
1149 | | * wire format. |
1150 | | */ |
1151 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1152 | 0 | REQUIRE(name->attributes.absolute); |
1153 | 0 | REQUIRE(ISC_BUFFER_VALID(target)); |
1154 | |
|
1155 | 0 | ndata = name->ndata; |
1156 | 0 | nlen = name->length; |
1157 | 0 | labels = dns_name_countlabels(name); |
1158 | 0 | tdata = isc_buffer_used(target); |
1159 | 0 | tlen = isc_buffer_availablelength(target); |
1160 | |
|
1161 | 0 | trem = tlen; |
1162 | |
|
1163 | 0 | if (nlen == 1 && labels == 1 && *ndata == '\0') { |
1164 | | /* |
1165 | | * Special handling for the root label. |
1166 | | */ |
1167 | 0 | if (trem == 0) { |
1168 | 0 | return ISC_R_NOSPACE; |
1169 | 0 | } |
1170 | | |
1171 | 0 | omit_final_dot = false; |
1172 | 0 | *tdata++ = '.'; |
1173 | 0 | trem--; |
1174 | | |
1175 | | /* |
1176 | | * Skip the while() loop. |
1177 | | */ |
1178 | 0 | nlen = 0; |
1179 | 0 | } |
1180 | | |
1181 | 0 | while (labels > 0 && nlen > 0 && trem > 0) { |
1182 | 0 | labels--; |
1183 | 0 | count = *ndata++; |
1184 | 0 | nlen--; |
1185 | 0 | if (count == 0) { |
1186 | 0 | break; |
1187 | 0 | } |
1188 | 0 | if (count <= DNS_NAME_LABELLEN) { |
1189 | 0 | INSIST(nlen >= count); |
1190 | 0 | while (count > 0) { |
1191 | 0 | c = *ndata; |
1192 | 0 | if ((c >= 0x30 && c <= 0x39) || /* digit */ |
1193 | 0 | (c >= 0x41 && c <= 0x5A) || /* uppercase */ |
1194 | 0 | (c >= 0x61 && c <= 0x7A) || /* lowercase */ |
1195 | 0 | c == 0x2D || /* hyphen */ |
1196 | 0 | c == 0x5F) /* underscore */ |
1197 | 0 | { |
1198 | 0 | if (trem == 0) { |
1199 | 0 | return ISC_R_NOSPACE; |
1200 | 0 | } |
1201 | | /* downcase */ |
1202 | 0 | if (c >= 0x41 && c <= 0x5A) { |
1203 | 0 | c += 0x20; |
1204 | 0 | } |
1205 | 0 | *tdata++ = c; |
1206 | 0 | ndata++; |
1207 | 0 | trem--; |
1208 | 0 | nlen--; |
1209 | 0 | } else { |
1210 | 0 | if (trem < 4) { |
1211 | 0 | return ISC_R_NOSPACE; |
1212 | 0 | } |
1213 | 0 | snprintf(tdata, trem, "%%%02X", c); |
1214 | 0 | tdata += 3; |
1215 | 0 | trem -= 3; |
1216 | 0 | ndata++; |
1217 | 0 | nlen--; |
1218 | 0 | } |
1219 | 0 | count--; |
1220 | 0 | } |
1221 | 0 | } else { |
1222 | 0 | FATAL_ERROR("Unexpected label type %02x", count); |
1223 | 0 | UNREACHABLE(); |
1224 | 0 | } |
1225 | | |
1226 | | /* |
1227 | | * The following assumes names are absolute. If not, we |
1228 | | * fix things up later. Note that this means that in some |
1229 | | * cases one more byte of text buffer is required than is |
1230 | | * needed in the final output. |
1231 | | */ |
1232 | 0 | if (trem == 0) { |
1233 | 0 | return ISC_R_NOSPACE; |
1234 | 0 | } |
1235 | 0 | *tdata++ = '.'; |
1236 | 0 | trem--; |
1237 | 0 | } |
1238 | | |
1239 | 0 | if (nlen != 0 && trem == 0) { |
1240 | 0 | return ISC_R_NOSPACE; |
1241 | 0 | } |
1242 | | |
1243 | 0 | if (omit_final_dot) { |
1244 | 0 | trem++; |
1245 | 0 | } |
1246 | |
|
1247 | 0 | isc_buffer_add(target, tlen - trem); |
1248 | |
|
1249 | 0 | return ISC_R_SUCCESS; |
1250 | 0 | } |
1251 | | |
1252 | | isc_result_t |
1253 | 0 | dns_name_downcase(const dns_name_t *source, dns_name_t *name) { |
1254 | | /* |
1255 | | * Downcase 'source'. |
1256 | | */ |
1257 | |
|
1258 | 0 | REQUIRE(DNS_NAME_VALID(source)); |
1259 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1260 | |
|
1261 | 0 | if (source == name) { |
1262 | 0 | REQUIRE(!name->attributes.readonly); |
1263 | 0 | isc_ascii_lowercopy(name->ndata, source->ndata, source->length); |
1264 | 0 | return ISC_R_SUCCESS; |
1265 | 0 | } |
1266 | | |
1267 | 0 | REQUIRE(DNS_NAME_BINDABLE(name)); |
1268 | 0 | REQUIRE(ISC_BUFFER_VALID(name->buffer)); |
1269 | |
|
1270 | 0 | isc_buffer_clear(name->buffer); |
1271 | 0 | name->ndata = (uint8_t *)name->buffer->base + name->buffer->used; |
1272 | | |
1273 | | /* label lengths are < 64 so tolower() does not affect them */ |
1274 | 0 | isc_ascii_lowercopy(name->ndata, source->ndata, source->length); |
1275 | |
|
1276 | 0 | name->length = source->length; |
1277 | 0 | name->attributes = (struct dns_name_attrs){ |
1278 | 0 | .absolute = source->attributes.absolute |
1279 | 0 | }; |
1280 | 0 | isc_buffer_add(name->buffer, name->length); |
1281 | |
|
1282 | 0 | return ISC_R_SUCCESS; |
1283 | 0 | } |
1284 | | |
1285 | | isc_result_t |
1286 | | dns_name_fromwire(dns_name_t *const name, isc_buffer_t *const source, |
1287 | 0 | const dns_decompress_t dctx, isc_buffer_t *target) { |
1288 | | /* |
1289 | | * Copy the name at source into target, decompressing it. |
1290 | | * |
1291 | | * *** WARNING *** |
1292 | | * |
1293 | | * dns_name_fromwire() deals with raw network data. An error in this |
1294 | | * routine could result in the failure or hijacking of the server. |
1295 | | * |
1296 | | * The description of name compression in RFC 1035 section 4.1.4 is |
1297 | | * subtle wrt certain edge cases. The first important sentence is: |
1298 | | * |
1299 | | * > In this scheme, an entire domain name or a list of labels at the |
1300 | | * > end of a domain name is replaced with a pointer to a prior |
1301 | | * > occurance of the same name. |
1302 | | * |
1303 | | * The key word is "prior". This says that compression pointers must |
1304 | | * point strictly earlier in the message (before our "marker" variable), |
1305 | | * which is enough to prevent DoS attacks due to compression loops. |
1306 | | * |
1307 | | * The next important sentence is: |
1308 | | * |
1309 | | * > If a domain name is contained in a part of the message subject to a |
1310 | | * > length field (such as the RDATA section of an RR), and compression |
1311 | | * > is used, the length of the compressed name is used in the length |
1312 | | * > calculation, rather than the length of the expanded name. |
1313 | | * |
1314 | | * When decompressing, this means that the amount of the source buffer |
1315 | | * that we consumed (which is checked wrt the container's length field) |
1316 | | * is the length of the compressed name. A compressed name is defined as |
1317 | | * a sequence of labels ending with the root label or a compression |
1318 | | * pointer, that is, the segment of the name that dns_name_fromwire() |
1319 | | * examines first. |
1320 | | * |
1321 | | * This matters when handling names that play dirty tricks, like: |
1322 | | * |
1323 | | * +---+---+---+---+---+---+ |
1324 | | * | 4 | 1 |'a'|192| 0 | 0 | |
1325 | | * +---+---+---+---+---+---+ |
1326 | | * |
1327 | | * We start at octet 1. There is an ordinary single character label "a", |
1328 | | * followed by a compression pointer that refers back to octet zero. |
1329 | | * Here there is a label of length 4, which weirdly re-uses the octets |
1330 | | * we already examined as the data for the label. It is followed by the |
1331 | | * root label, |
1332 | | * |
1333 | | * The specification says that the compressed name ends after the first |
1334 | | * zero octet (after the compression pointer) not the second zero octet, |
1335 | | * even though the second octet is later in the message. This shows the |
1336 | | * correct way to set our "consumed" variable. |
1337 | | */ |
1338 | |
|
1339 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1340 | 0 | REQUIRE(DNS_NAME_BINDABLE(name)); |
1341 | 0 | REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) || |
1342 | 0 | (target == NULL && ISC_BUFFER_VALID(name->buffer))); |
1343 | |
|
1344 | 0 | if (target == NULL && name->buffer != NULL) { |
1345 | 0 | target = name->buffer; |
1346 | 0 | isc_buffer_clear(target); |
1347 | 0 | } |
1348 | |
|
1349 | 0 | uint8_t *const name_buf = isc_buffer_used(target); |
1350 | 0 | const uint32_t name_max = ISC_MIN(DNS_NAME_MAXWIRE, |
1351 | 0 | isc_buffer_availablelength(target)); |
1352 | 0 | uint32_t name_len = 0; |
1353 | | |
1354 | | /* |
1355 | | * After chasing a compression pointer, these variables refer to the |
1356 | | * source buffer as follows: |
1357 | | * |
1358 | | * sb --- mr --- cr --- st --- cd --- sm |
1359 | | * |
1360 | | * sb = source_buf (const) |
1361 | | * mr = marker |
1362 | | * cr = cursor |
1363 | | * st = start (const) |
1364 | | * cd = consumed |
1365 | | * sm = source_max (const) |
1366 | | * |
1367 | | * The marker hops backwards for each pointer. |
1368 | | * The cursor steps forwards for each label. |
1369 | | * The amount of the source we consumed is set once. |
1370 | | */ |
1371 | 0 | const uint8_t *const source_buf = isc_buffer_base(source); |
1372 | 0 | const uint8_t *const source_max = isc_buffer_used(source); |
1373 | 0 | const uint8_t *const start = isc_buffer_current(source); |
1374 | 0 | const uint8_t *marker = start; |
1375 | 0 | const uint8_t *cursor = start; |
1376 | 0 | const uint8_t *consumed = NULL; |
1377 | | |
1378 | | /* |
1379 | | * One iteration per label. |
1380 | | */ |
1381 | 0 | while (cursor < source_max) { |
1382 | 0 | const uint8_t label_len = *cursor++; |
1383 | 0 | if (label_len <= DNS_NAME_LABELLEN) { |
1384 | | /* |
1385 | | * Normal label: record its offset, and check bounds on |
1386 | | * the name length, which also ensures we don't overrun |
1387 | | * the offsets array. Don't touch any source bytes yet! |
1388 | | * The source bounds check will happen when we loop. |
1389 | | */ |
1390 | | /* and then a step to the ri-i-i-i-i-ight */ |
1391 | 0 | cursor += label_len; |
1392 | 0 | name_len += label_len + 1; |
1393 | 0 | if (name_len > name_max) { |
1394 | 0 | return name_max == DNS_NAME_MAXWIRE |
1395 | 0 | ? DNS_R_NAMETOOLONG |
1396 | 0 | : ISC_R_NOSPACE; |
1397 | 0 | } else if (label_len == 0) { |
1398 | 0 | goto root_label; |
1399 | 0 | } |
1400 | 0 | } else if (label_len < 192) { |
1401 | 0 | return DNS_R_BADLABELTYPE; |
1402 | 0 | } else if (!dns_decompress_getpermitted(dctx)) { |
1403 | 0 | return DNS_R_DISALLOWED; |
1404 | 0 | } else if (cursor < source_max) { |
1405 | | /* |
1406 | | * Compression pointer. Ensure it does not loop. |
1407 | | * |
1408 | | * Copy multiple labels in one go, to make the most of |
1409 | | * memmove() performance. Start at the marker and finish |
1410 | | * just before the pointer's hi+lo bytes, before the |
1411 | | * cursor. Bounds were already checked. |
1412 | | */ |
1413 | 0 | const uint32_t hi = label_len & 0x3F; |
1414 | 0 | const uint32_t lo = *cursor++; |
1415 | 0 | const uint8_t *pointer = source_buf + (256 * hi + lo); |
1416 | 0 | if (pointer >= marker) { |
1417 | 0 | return DNS_R_BADPOINTER; |
1418 | 0 | } |
1419 | 0 | const uint32_t copy_len = (cursor - 2) - marker; |
1420 | 0 | uint8_t *const dest = name_buf + name_len - copy_len; |
1421 | 0 | memmove(dest, marker, copy_len); |
1422 | 0 | consumed = consumed != NULL ? consumed : cursor; |
1423 | | /* it's just a jump to the left */ |
1424 | 0 | cursor = marker = pointer; |
1425 | 0 | } |
1426 | 0 | } |
1427 | 0 | return ISC_R_UNEXPECTEDEND; |
1428 | 0 | root_label:; |
1429 | | /* |
1430 | | * Copy labels almost like we do for compression pointers, |
1431 | | * from the marker up to and including the root label. |
1432 | | */ |
1433 | 0 | const uint32_t copy_len = cursor - marker; |
1434 | 0 | memmove(name_buf + name_len - copy_len, marker, copy_len); |
1435 | 0 | consumed = consumed != NULL ? consumed : cursor; |
1436 | 0 | isc_buffer_forward(source, consumed - start); |
1437 | |
|
1438 | 0 | name->attributes.absolute = true; |
1439 | 0 | name->ndata = name_buf; |
1440 | 0 | name->length = name_len; |
1441 | 0 | isc_buffer_add(target, name_len); |
1442 | |
|
1443 | 0 | return ISC_R_SUCCESS; |
1444 | 0 | } |
1445 | | |
1446 | | isc_result_t |
1447 | | dns_name_towire(const dns_name_t *name, dns_compress_t *cctx, |
1448 | 0 | isc_buffer_t *target) { |
1449 | 0 | bool compress, multi; |
1450 | 0 | unsigned int here; |
1451 | 0 | unsigned int prefix_length; |
1452 | 0 | unsigned int suffix_coff; |
1453 | | |
1454 | | /* |
1455 | | * Convert 'name' into wire format, compressing it as specified by the |
1456 | | * compression context 'cctx' (or without compressing if 'cctx' |
1457 | | * is NULL), and storing the result in 'target'. |
1458 | | */ |
1459 | |
|
1460 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1461 | 0 | REQUIRE(ISC_BUFFER_VALID(target)); |
1462 | |
|
1463 | 0 | if (cctx == NULL) { |
1464 | 0 | if (isc_buffer_availablelength(target) < name->length) { |
1465 | 0 | return ISC_R_NOSPACE; |
1466 | 0 | } |
1467 | 0 | memmove(isc_buffer_used(target), name->ndata, name->length); |
1468 | 0 | isc_buffer_add(target, name->length); |
1469 | 0 | return ISC_R_SUCCESS; |
1470 | 0 | } |
1471 | | |
1472 | 0 | compress = !name->attributes.nocompress && |
1473 | 0 | dns_compress_getpermitted(cctx); |
1474 | 0 | multi = compress && dns_compress_getmultiuse(cctx); |
1475 | | |
1476 | | /* |
1477 | | * Write a compression pointer directly if the caller passed us |
1478 | | * a pointer to this name's offset that we saved previously. |
1479 | | */ |
1480 | 0 | if (multi && cctx->coff < 0x4000) { |
1481 | 0 | if (isc_buffer_availablelength(target) < 2) { |
1482 | 0 | return ISC_R_NOSPACE; |
1483 | 0 | } |
1484 | 0 | isc_buffer_putuint16(target, cctx->coff | 0xc000); |
1485 | 0 | return ISC_R_SUCCESS; |
1486 | 0 | } |
1487 | | |
1488 | | /* |
1489 | | * Always add the name to the compression context; if compression |
1490 | | * is off, reset the return values before writing the name. |
1491 | | */ |
1492 | 0 | prefix_length = name->length; |
1493 | 0 | suffix_coff = 0; |
1494 | 0 | dns_compress_name(cctx, target, name, &prefix_length, &suffix_coff); |
1495 | 0 | if (!compress) { |
1496 | 0 | prefix_length = name->length; |
1497 | 0 | suffix_coff = 0; |
1498 | 0 | } |
1499 | | |
1500 | | /* |
1501 | | * Return this name's compression offset for use next time, provided |
1502 | | * it isn't too short for compression to help (i.e. it's the root) |
1503 | | */ |
1504 | 0 | here = isc_buffer_usedlength(target); |
1505 | 0 | if (multi && here < 0x4000 && prefix_length > 1) { |
1506 | 0 | cctx->coff = (uint16_t)here; |
1507 | 0 | } |
1508 | |
|
1509 | 0 | if (prefix_length > 0) { |
1510 | 0 | if (isc_buffer_availablelength(target) < prefix_length) { |
1511 | 0 | return ISC_R_NOSPACE; |
1512 | 0 | } |
1513 | 0 | memmove(isc_buffer_used(target), name->ndata, prefix_length); |
1514 | 0 | isc_buffer_add(target, prefix_length); |
1515 | 0 | } |
1516 | | |
1517 | 0 | if (suffix_coff > 0) { |
1518 | 0 | if (multi && prefix_length == 0) { |
1519 | 0 | cctx->coff = suffix_coff; |
1520 | 0 | } |
1521 | 0 | if (isc_buffer_availablelength(target) < 2) { |
1522 | 0 | return ISC_R_NOSPACE; |
1523 | 0 | } |
1524 | 0 | isc_buffer_putuint16(target, suffix_coff | 0xc000); |
1525 | 0 | } |
1526 | | |
1527 | 0 | return ISC_R_SUCCESS; |
1528 | 0 | } |
1529 | | |
1530 | | isc_result_t |
1531 | | dns_name_concatenate(const dns_name_t *prefix, const dns_name_t *suffix, |
1532 | 0 | dns_name_t *name) { |
1533 | 0 | unsigned char *ndata = NULL; |
1534 | 0 | unsigned int nrem, prefix_length, length; |
1535 | 0 | bool copy_prefix = true; |
1536 | 0 | bool copy_suffix = true; |
1537 | 0 | bool absolute = false; |
1538 | 0 | dns_name_t tmp_name; |
1539 | 0 | isc_buffer_t *target = NULL; |
1540 | | |
1541 | | /* |
1542 | | * Concatenate 'prefix' and 'suffix'. |
1543 | | */ |
1544 | |
|
1545 | 0 | REQUIRE(prefix == NULL || DNS_NAME_VALID(prefix)); |
1546 | 0 | REQUIRE(suffix == NULL || DNS_NAME_VALID(suffix)); |
1547 | 0 | REQUIRE(DNS_NAME_VALID(name) && ISC_BUFFER_VALID(name->buffer)); |
1548 | 0 | REQUIRE(DNS_NAME_BINDABLE(name)); |
1549 | |
|
1550 | 0 | if (prefix == NULL || prefix->length == 0) { |
1551 | 0 | copy_prefix = false; |
1552 | 0 | } |
1553 | 0 | if (suffix == NULL || suffix->length == 0) { |
1554 | 0 | copy_suffix = false; |
1555 | 0 | } |
1556 | 0 | if (copy_prefix && prefix->attributes.absolute) { |
1557 | 0 | absolute = true; |
1558 | 0 | REQUIRE(!copy_suffix); |
1559 | 0 | } |
1560 | 0 | if (name == NULL) { |
1561 | 0 | dns_name_init(&tmp_name); |
1562 | 0 | name = &tmp_name; |
1563 | 0 | } |
1564 | |
|
1565 | 0 | target = name->buffer; |
1566 | 0 | isc_buffer_clear(target); |
1567 | | |
1568 | | /* |
1569 | | * Set up. |
1570 | | */ |
1571 | 0 | nrem = target->length - target->used; |
1572 | 0 | ndata = (unsigned char *)target->base + target->used; |
1573 | 0 | if (nrem > DNS_NAME_MAXWIRE) { |
1574 | 0 | nrem = DNS_NAME_MAXWIRE; |
1575 | 0 | } |
1576 | 0 | length = 0; |
1577 | 0 | prefix_length = 0; |
1578 | 0 | if (copy_prefix) { |
1579 | 0 | prefix_length = prefix->length; |
1580 | 0 | length += prefix_length; |
1581 | 0 | } |
1582 | 0 | if (copy_suffix) { |
1583 | 0 | length += suffix->length; |
1584 | 0 | } |
1585 | 0 | if (length > DNS_NAME_MAXWIRE) { |
1586 | 0 | return DNS_R_NAMETOOLONG; |
1587 | 0 | } |
1588 | 0 | if (length > nrem) { |
1589 | 0 | return ISC_R_NOSPACE; |
1590 | 0 | } |
1591 | | |
1592 | 0 | if (copy_suffix) { |
1593 | 0 | if (suffix->attributes.absolute) { |
1594 | 0 | absolute = true; |
1595 | 0 | } |
1596 | 0 | memmove(ndata + prefix_length, suffix->ndata, suffix->length); |
1597 | 0 | } |
1598 | | |
1599 | | /* |
1600 | | * If 'prefix' and 'name' are the same object, we don't have to |
1601 | | * copy anything. |
1602 | | */ |
1603 | 0 | if (copy_prefix && (prefix != name || prefix->buffer != target)) { |
1604 | 0 | memmove(ndata, prefix->ndata, prefix_length); |
1605 | 0 | } |
1606 | |
|
1607 | 0 | name->ndata = ndata; |
1608 | 0 | name->length = length; |
1609 | 0 | name->attributes.absolute = absolute; |
1610 | |
|
1611 | 0 | isc_buffer_add(target, name->length); |
1612 | |
|
1613 | 0 | return ISC_R_SUCCESS; |
1614 | 0 | } |
1615 | | |
1616 | | void |
1617 | 0 | dns_name_dup(const dns_name_t *source, isc_mem_t *mctx, dns_name_t *target) { |
1618 | | /* |
1619 | | * Make 'target' a dynamically allocated copy of 'source'. |
1620 | | */ |
1621 | |
|
1622 | 0 | REQUIRE(DNS_NAME_VALID(source)); |
1623 | 0 | REQUIRE(source->length > 0); |
1624 | 0 | REQUIRE(DNS_NAME_VALID(target)); |
1625 | 0 | REQUIRE(DNS_NAME_BINDABLE(target)); |
1626 | |
|
1627 | 0 | target->ndata = isc_mem_get(mctx, source->length); |
1628 | |
|
1629 | 0 | memmove(target->ndata, source->ndata, source->length); |
1630 | |
|
1631 | 0 | target->length = source->length; |
1632 | 0 | target->attributes = (struct dns_name_attrs){ .dynamic = true }; |
1633 | 0 | target->attributes.absolute = source->attributes.absolute; |
1634 | 0 | } |
1635 | | |
1636 | | void |
1637 | 0 | dns_name_free(dns_name_t *name, isc_mem_t *mctx) { |
1638 | 0 | size_t size; |
1639 | | |
1640 | | /* |
1641 | | * Free 'name'. |
1642 | | */ |
1643 | |
|
1644 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1645 | 0 | REQUIRE(name->attributes.dynamic); |
1646 | |
|
1647 | 0 | size = name->length; |
1648 | 0 | isc_mem_put(mctx, name->ndata, size); |
1649 | 0 | dns_name_invalidate(name); |
1650 | 0 | } |
1651 | | |
1652 | | size_t |
1653 | 0 | dns_name_size(const dns_name_t *name) { |
1654 | 0 | size_t size; |
1655 | |
|
1656 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1657 | |
|
1658 | 0 | if (!name->attributes.dynamic) { |
1659 | 0 | return 0; |
1660 | 0 | } |
1661 | | |
1662 | 0 | size = name->length; |
1663 | |
|
1664 | 0 | return size; |
1665 | 0 | } |
1666 | | |
1667 | | isc_result_t |
1668 | 0 | dns_name_digest(const dns_name_t *name, dns_digestfunc_t digest, void *arg) { |
1669 | | /* |
1670 | | * Send 'name' in DNSSEC canonical form to 'digest'. |
1671 | | */ |
1672 | |
|
1673 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1674 | 0 | REQUIRE(digest != NULL); |
1675 | |
|
1676 | 0 | unsigned char ndata[DNS_NAME_MAXWIRE]; |
1677 | 0 | isc_ascii_lowercopy(ndata, name->ndata, name->length); |
1678 | |
|
1679 | 0 | isc_region_t r = { |
1680 | 0 | .base = ndata, |
1681 | 0 | .length = name->length, |
1682 | 0 | }; |
1683 | 0 | return (digest)(arg, &r); |
1684 | 0 | } |
1685 | | |
1686 | | bool |
1687 | 0 | dns_name_dynamic(const dns_name_t *name) { |
1688 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1689 | | |
1690 | | /* |
1691 | | * Returns whether there is dynamic memory associated with this name. |
1692 | | */ |
1693 | |
|
1694 | 0 | return name->attributes.dynamic; |
1695 | 0 | } |
1696 | | |
1697 | | isc_result_t |
1698 | 0 | dns_name_print(const dns_name_t *name, FILE *stream) { |
1699 | 0 | isc_result_t result; |
1700 | 0 | isc_buffer_t b; |
1701 | 0 | isc_region_t r; |
1702 | 0 | char t[1024]; |
1703 | | |
1704 | | /* |
1705 | | * Print 'name' on 'stream'. |
1706 | | */ |
1707 | |
|
1708 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1709 | |
|
1710 | 0 | isc_buffer_init(&b, t, sizeof(t)); |
1711 | 0 | result = dns_name_totext(name, 0, &b); |
1712 | 0 | if (result != ISC_R_SUCCESS) { |
1713 | 0 | return result; |
1714 | 0 | } |
1715 | 0 | isc_buffer_usedregion(&b, &r); |
1716 | 0 | fprintf(stream, "%.*s", (int)r.length, (char *)r.base); |
1717 | |
|
1718 | 0 | return ISC_R_SUCCESS; |
1719 | 0 | } |
1720 | | |
1721 | | isc_result_t |
1722 | 0 | dns_name_settotextfilter(dns_name_totextfilter_t *proc) { |
1723 | | /* |
1724 | | * If we already have been here set / clear as appropriate. |
1725 | | */ |
1726 | 0 | if (totext_filter_proc != NULL && proc != NULL) { |
1727 | 0 | if (totext_filter_proc == proc) { |
1728 | 0 | return ISC_R_SUCCESS; |
1729 | 0 | } |
1730 | 0 | } |
1731 | 0 | if (proc == NULL && totext_filter_proc != NULL) { |
1732 | 0 | totext_filter_proc = NULL; |
1733 | 0 | return ISC_R_SUCCESS; |
1734 | 0 | } |
1735 | | |
1736 | 0 | totext_filter_proc = proc; |
1737 | |
|
1738 | 0 | return ISC_R_SUCCESS; |
1739 | 0 | } |
1740 | | |
1741 | | void |
1742 | 0 | dns_name_format(const dns_name_t *name, char *cp, unsigned int size) { |
1743 | 0 | isc_result_t result; |
1744 | 0 | isc_buffer_t buf; |
1745 | |
|
1746 | 0 | REQUIRE(size > 0); |
1747 | | |
1748 | | /* |
1749 | | * Leave room for null termination after buffer. |
1750 | | */ |
1751 | 0 | isc_buffer_init(&buf, cp, size - 1); |
1752 | 0 | result = dns_name_totext(name, DNS_NAME_OMITFINALDOT, &buf); |
1753 | 0 | if (result == ISC_R_SUCCESS) { |
1754 | 0 | isc_buffer_putuint8(&buf, (uint8_t)'\0'); |
1755 | 0 | } else { |
1756 | 0 | snprintf(cp, size, "<unknown>"); |
1757 | 0 | } |
1758 | 0 | } |
1759 | | |
1760 | | /* |
1761 | | * dns_name_tostring() -- similar to dns_name_format() but allocates its own |
1762 | | * memory. |
1763 | | */ |
1764 | | isc_result_t |
1765 | 0 | dns_name_tostring(const dns_name_t *name, char **target, isc_mem_t *mctx) { |
1766 | 0 | isc_result_t result; |
1767 | 0 | isc_buffer_t buf; |
1768 | 0 | isc_region_t reg; |
1769 | 0 | char *p, txt[DNS_NAME_FORMATSIZE]; |
1770 | |
|
1771 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1772 | 0 | REQUIRE(target != NULL && *target == NULL); |
1773 | |
|
1774 | 0 | isc_buffer_init(&buf, txt, sizeof(txt)); |
1775 | 0 | result = dns_name_totext(name, 0, &buf); |
1776 | 0 | if (result != ISC_R_SUCCESS) { |
1777 | 0 | return result; |
1778 | 0 | } |
1779 | | |
1780 | 0 | isc_buffer_usedregion(&buf, ®); |
1781 | 0 | p = isc_mem_allocate(mctx, reg.length + 1); |
1782 | 0 | memmove(p, (char *)reg.base, (int)reg.length); |
1783 | 0 | p[reg.length] = '\0'; |
1784 | |
|
1785 | 0 | *target = p; |
1786 | 0 | return ISC_R_SUCCESS; |
1787 | 0 | } |
1788 | | |
1789 | | isc_result_t |
1790 | | dns_name_fromstring(dns_name_t *target, const char *src, |
1791 | | const dns_name_t *origin, unsigned int options, |
1792 | 0 | isc_mem_t *mctx) { |
1793 | 0 | isc_result_t result; |
1794 | 0 | isc_buffer_t buf; |
1795 | 0 | dns_fixedname_t fn; |
1796 | 0 | dns_name_t *name; |
1797 | |
|
1798 | 0 | REQUIRE(src != NULL); |
1799 | |
|
1800 | 0 | isc_buffer_constinit(&buf, src, strlen(src)); |
1801 | 0 | isc_buffer_add(&buf, strlen(src)); |
1802 | 0 | if (DNS_NAME_BINDABLE(target) && target->buffer != NULL) { |
1803 | 0 | name = target; |
1804 | 0 | } else { |
1805 | 0 | name = dns_fixedname_initname(&fn); |
1806 | 0 | } |
1807 | |
|
1808 | 0 | result = dns_name_fromtext(name, &buf, origin, options); |
1809 | 0 | if (result != ISC_R_SUCCESS) { |
1810 | 0 | return result; |
1811 | 0 | } |
1812 | | |
1813 | 0 | if (name != target) { |
1814 | 0 | dns_name_dup(name, mctx, target); |
1815 | 0 | } |
1816 | 0 | return result; |
1817 | 0 | } |
1818 | | |
1819 | | void |
1820 | 0 | dns_name_copy(const dns_name_t *source, dns_name_t *dest) { |
1821 | 0 | isc_buffer_t *target = NULL; |
1822 | 0 | unsigned char *ndata = NULL; |
1823 | |
|
1824 | 0 | REQUIRE(DNS_NAME_VALID(source)); |
1825 | 0 | REQUIRE(DNS_NAME_VALID(dest)); |
1826 | 0 | REQUIRE(DNS_NAME_BINDABLE(dest)); |
1827 | |
|
1828 | 0 | target = dest->buffer; |
1829 | |
|
1830 | 0 | REQUIRE(target != NULL); |
1831 | 0 | REQUIRE(target->length >= source->length); |
1832 | |
|
1833 | 0 | isc_buffer_clear(target); |
1834 | |
|
1835 | 0 | ndata = (unsigned char *)target->base; |
1836 | 0 | dest->ndata = target->base; |
1837 | |
|
1838 | 0 | if (source->length != 0) { |
1839 | 0 | memmove(ndata, source->ndata, source->length); |
1840 | 0 | } |
1841 | |
|
1842 | 0 | dest->ndata = ndata; |
1843 | 0 | dest->length = source->length; |
1844 | 0 | dest->attributes.absolute = source->attributes.absolute; |
1845 | |
|
1846 | 0 | isc_buffer_add(target, dest->length); |
1847 | 0 | } |
1848 | | |
1849 | | /* |
1850 | | * Service Discovery Prefixes RFC 6763. |
1851 | | */ |
1852 | | static unsigned char b_dns_sd_udp_data[] = "\001b\007_dns-sd\004_udp"; |
1853 | | static unsigned char db_dns_sd_udp_data[] = "\002db\007_dns-sd\004_udp"; |
1854 | | static unsigned char r_dns_sd_udp_data[] = "\001r\007_dns-sd\004_udp"; |
1855 | | static unsigned char dr_dns_sd_udp_data[] = "\002dr\007_dns-sd\004_udp"; |
1856 | | static unsigned char lb_dns_sd_udp_data[] = "\002lb\007_dns-sd\004_udp"; |
1857 | | |
1858 | | static dns_name_t const dns_sd[] = { |
1859 | | DNS_NAME_INITNONABSOLUTE(b_dns_sd_udp_data), |
1860 | | DNS_NAME_INITNONABSOLUTE(db_dns_sd_udp_data), |
1861 | | DNS_NAME_INITNONABSOLUTE(r_dns_sd_udp_data), |
1862 | | DNS_NAME_INITNONABSOLUTE(dr_dns_sd_udp_data), |
1863 | | DNS_NAME_INITNONABSOLUTE(lb_dns_sd_udp_data) |
1864 | | }; |
1865 | | |
1866 | | bool |
1867 | 0 | dns_name_isdnssd(const dns_name_t *name) { |
1868 | 0 | size_t i; |
1869 | 0 | dns_name_t prefix; |
1870 | |
|
1871 | 0 | if (dns_name_countlabels(name) > 3U) { |
1872 | 0 | dns_name_init(&prefix); |
1873 | 0 | dns_name_getlabelsequence(name, 0, 3, &prefix); |
1874 | 0 | for (i = 0; i < (sizeof(dns_sd) / sizeof(dns_sd[0])); i++) { |
1875 | 0 | if (dns_name_equal(&prefix, &dns_sd[i])) { |
1876 | 0 | return true; |
1877 | 0 | } |
1878 | 0 | } |
1879 | 0 | } |
1880 | | |
1881 | 0 | return false; |
1882 | 0 | } |
1883 | | |
1884 | | static unsigned char inaddr10[] = "\00210\007IN-ADDR\004ARPA"; |
1885 | | |
1886 | | static unsigned char inaddr16172[] = "\00216\003172\007IN-ADDR\004ARPA"; |
1887 | | static unsigned char inaddr17172[] = "\00217\003172\007IN-ADDR\004ARPA"; |
1888 | | static unsigned char inaddr18172[] = "\00218\003172\007IN-ADDR\004ARPA"; |
1889 | | static unsigned char inaddr19172[] = "\00219\003172\007IN-ADDR\004ARPA"; |
1890 | | static unsigned char inaddr20172[] = "\00220\003172\007IN-ADDR\004ARPA"; |
1891 | | static unsigned char inaddr21172[] = "\00221\003172\007IN-ADDR\004ARPA"; |
1892 | | static unsigned char inaddr22172[] = "\00222\003172\007IN-ADDR\004ARPA"; |
1893 | | static unsigned char inaddr23172[] = "\00223\003172\007IN-ADDR\004ARPA"; |
1894 | | static unsigned char inaddr24172[] = "\00224\003172\007IN-ADDR\004ARPA"; |
1895 | | static unsigned char inaddr25172[] = "\00225\003172\007IN-ADDR\004ARPA"; |
1896 | | static unsigned char inaddr26172[] = "\00226\003172\007IN-ADDR\004ARPA"; |
1897 | | static unsigned char inaddr27172[] = "\00227\003172\007IN-ADDR\004ARPA"; |
1898 | | static unsigned char inaddr28172[] = "\00228\003172\007IN-ADDR\004ARPA"; |
1899 | | static unsigned char inaddr29172[] = "\00229\003172\007IN-ADDR\004ARPA"; |
1900 | | static unsigned char inaddr30172[] = "\00230\003172\007IN-ADDR\004ARPA"; |
1901 | | static unsigned char inaddr31172[] = "\00231\003172\007IN-ADDR\004ARPA"; |
1902 | | |
1903 | | static unsigned char inaddr168192[] = "\003168\003192\007IN-ADDR\004ARPA"; |
1904 | | |
1905 | | static dns_name_t const rfc1918names[] = { |
1906 | | DNS_NAME_INITABSOLUTE(inaddr10), DNS_NAME_INITABSOLUTE(inaddr16172), |
1907 | | DNS_NAME_INITABSOLUTE(inaddr17172), DNS_NAME_INITABSOLUTE(inaddr18172), |
1908 | | DNS_NAME_INITABSOLUTE(inaddr19172), DNS_NAME_INITABSOLUTE(inaddr20172), |
1909 | | DNS_NAME_INITABSOLUTE(inaddr21172), DNS_NAME_INITABSOLUTE(inaddr22172), |
1910 | | DNS_NAME_INITABSOLUTE(inaddr23172), DNS_NAME_INITABSOLUTE(inaddr24172), |
1911 | | DNS_NAME_INITABSOLUTE(inaddr25172), DNS_NAME_INITABSOLUTE(inaddr26172), |
1912 | | DNS_NAME_INITABSOLUTE(inaddr27172), DNS_NAME_INITABSOLUTE(inaddr28172), |
1913 | | DNS_NAME_INITABSOLUTE(inaddr29172), DNS_NAME_INITABSOLUTE(inaddr30172), |
1914 | | DNS_NAME_INITABSOLUTE(inaddr31172), DNS_NAME_INITABSOLUTE(inaddr168192) |
1915 | | }; |
1916 | | |
1917 | | bool |
1918 | 0 | dns_name_isrfc1918(const dns_name_t *name) { |
1919 | 0 | size_t i; |
1920 | |
|
1921 | 0 | for (i = 0; i < (sizeof(rfc1918names) / sizeof(*rfc1918names)); i++) { |
1922 | 0 | if (dns_name_issubdomain(name, &rfc1918names[i])) { |
1923 | 0 | return true; |
1924 | 0 | } |
1925 | 0 | } |
1926 | 0 | return false; |
1927 | 0 | } |
1928 | | |
1929 | | static unsigned char ip6fc[] = "\001c\001f\003ip6\004ARPA"; |
1930 | | static unsigned char ip6fd[] = "\001d\001f\003ip6\004ARPA"; |
1931 | | |
1932 | | static dns_name_t const ulanames[] = { DNS_NAME_INITABSOLUTE(ip6fc), |
1933 | | DNS_NAME_INITABSOLUTE(ip6fd) }; |
1934 | | |
1935 | | bool |
1936 | 0 | dns_name_isula(const dns_name_t *name) { |
1937 | 0 | size_t i; |
1938 | |
|
1939 | 0 | for (i = 0; i < (sizeof(ulanames) / sizeof(*ulanames)); i++) { |
1940 | 0 | if (dns_name_issubdomain(name, &ulanames[i])) { |
1941 | 0 | return true; |
1942 | 0 | } |
1943 | 0 | } |
1944 | 0 | return false; |
1945 | 0 | } |
1946 | | |
1947 | | bool |
1948 | 0 | dns_name_istat(const dns_name_t *name) { |
1949 | 0 | unsigned char len; |
1950 | 0 | const unsigned char *ndata; |
1951 | |
|
1952 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1953 | |
|
1954 | 0 | if (name->length == 0) { |
1955 | 0 | return false; |
1956 | 0 | } |
1957 | | |
1958 | 0 | ndata = name->ndata; |
1959 | 0 | len = ndata[0]; |
1960 | 0 | INSIST(len <= name->length); |
1961 | 0 | ndata++; |
1962 | | |
1963 | | /* |
1964 | | * Is there at least one trust anchor reported and is the |
1965 | | * label length consistent with a trust-anchor-telemetry label. |
1966 | | */ |
1967 | 0 | if ((len < 8) || (len - 3) % 5 != 0) { |
1968 | 0 | return false; |
1969 | 0 | } |
1970 | | |
1971 | 0 | if (ndata[0] != '_' || isc_ascii_tolower(ndata[1]) != 't' || |
1972 | 0 | isc_ascii_tolower(ndata[2]) != 'a') |
1973 | 0 | { |
1974 | 0 | return false; |
1975 | 0 | } |
1976 | 0 | ndata += 3; |
1977 | 0 | len -= 3; |
1978 | |
|
1979 | 0 | while (len > 0) { |
1980 | 0 | INSIST(len >= 5); |
1981 | 0 | if (ndata[0] != '-' || !isc_hex_char(ndata[1]) || |
1982 | 0 | !isc_hex_char(ndata[2]) || !isc_hex_char(ndata[3]) || |
1983 | 0 | !isc_hex_char(ndata[4])) |
1984 | 0 | { |
1985 | 0 | return false; |
1986 | 0 | } |
1987 | 0 | ndata += 5; |
1988 | 0 | len -= 5; |
1989 | 0 | } |
1990 | 0 | return true; |
1991 | 0 | } |
1992 | | |
1993 | | bool |
1994 | 0 | dns_name_isdnssvcb(const dns_name_t *name) { |
1995 | 0 | unsigned char len, len1; |
1996 | 0 | const unsigned char *ndata; |
1997 | |
|
1998 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
1999 | |
|
2000 | 0 | if (name->length < 5) { |
2001 | 0 | return false; |
2002 | 0 | } |
2003 | | |
2004 | 0 | ndata = name->ndata; |
2005 | 0 | len = len1 = ndata[0]; |
2006 | 0 | INSIST(len <= name->length); |
2007 | 0 | ndata++; |
2008 | |
|
2009 | 0 | if (len < 2 || ndata[0] != '_') { |
2010 | 0 | return false; |
2011 | 0 | } |
2012 | 0 | if (isdigit(ndata[1]) && name->length > len + 1) { |
2013 | 0 | char buf[sizeof("65000")]; |
2014 | 0 | long port; |
2015 | 0 | char *endp; |
2016 | | |
2017 | | /* |
2018 | | * Do we have a valid _port label? |
2019 | | */ |
2020 | 0 | if (len > 6U || (ndata[1] == '0' && len != 2)) { |
2021 | 0 | return false; |
2022 | 0 | } |
2023 | 0 | memcpy(buf, ndata + 1, len - 1); |
2024 | 0 | buf[len - 1] = 0; |
2025 | 0 | port = strtol(buf, &endp, 10); |
2026 | 0 | if (*endp != 0 || port < 0 || port > 0xffff) { |
2027 | 0 | return false; |
2028 | 0 | } |
2029 | | |
2030 | | /* |
2031 | | * Move to next label. |
2032 | | */ |
2033 | 0 | ndata += len; |
2034 | 0 | INSIST(len1 + 1U < name->length); |
2035 | 0 | len = *ndata; |
2036 | 0 | INSIST(len + len1 + 1U <= name->length); |
2037 | 0 | ndata++; |
2038 | 0 | } |
2039 | | |
2040 | 0 | if (len == 4U && strncasecmp((const char *)ndata, "_dns", 4) == 0) { |
2041 | 0 | return true; |
2042 | 0 | } |
2043 | | |
2044 | 0 | return false; |
2045 | 0 | } |
2046 | | |
2047 | | bool |
2048 | 0 | dns_name_israd(const dns_name_t *name, const dns_name_t *rad) { |
2049 | 0 | dns_name_t suffix; |
2050 | 0 | char labelbuf[64]; |
2051 | 0 | unsigned long v, last = ULONG_MAX; |
2052 | 0 | char *end, *l; |
2053 | |
|
2054 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
2055 | 0 | REQUIRE(DNS_NAME_VALID(rad)); |
2056 | |
|
2057 | 0 | uint8_t name_labels = dns_name_countlabels(name); |
2058 | 0 | uint8_t rad_labels = dns_name_countlabels(rad); |
2059 | |
|
2060 | 0 | if (name_labels < rad_labels + 4U || name->length < 4U) { |
2061 | 0 | return false; |
2062 | 0 | } |
2063 | | |
2064 | 0 | if (name->ndata[0] != 3 || name->ndata[1] != '_' || |
2065 | 0 | tolower(name->ndata[2]) != 'e' || tolower(name->ndata[3]) != 'r') |
2066 | 0 | { |
2067 | 0 | return false; |
2068 | 0 | } |
2069 | | |
2070 | 0 | dns_name_init(&suffix); |
2071 | 0 | dns_name_split(name, rad_labels + 1, NULL, &suffix); |
2072 | |
|
2073 | 0 | if (suffix.ndata[0] != 3 || suffix.ndata[1] != '_' || |
2074 | 0 | tolower(suffix.ndata[2]) != 'e' || tolower(suffix.ndata[3]) != 'r') |
2075 | 0 | { |
2076 | 0 | return false; |
2077 | 0 | } |
2078 | | |
2079 | | /* type list */ |
2080 | 0 | dns_name_split(name, name_labels - 1, NULL, &suffix); |
2081 | 0 | INSIST(*suffix.ndata < sizeof(labelbuf)); |
2082 | 0 | memmove(labelbuf, suffix.ndata + 1, *suffix.ndata); |
2083 | 0 | labelbuf[*suffix.ndata] = 0; |
2084 | 0 | if (strlen(labelbuf) != *suffix.ndata) { |
2085 | 0 | return false; |
2086 | 0 | } |
2087 | 0 | l = labelbuf; |
2088 | 0 | do { |
2089 | 0 | v = strtoul(l, &end, 10); |
2090 | 0 | if (v > 0xffff || (*end != 0 && *end != '-') || end == l) { |
2091 | 0 | return false; |
2092 | 0 | } |
2093 | 0 | if (last != ULONG_MAX && v <= last) { |
2094 | 0 | return false; |
2095 | 0 | } |
2096 | 0 | last = v; |
2097 | 0 | if (*end == '-') { |
2098 | 0 | l = end + 1; |
2099 | 0 | } |
2100 | 0 | } while (*end != 0); |
2101 | | |
2102 | | /* extended error code */ |
2103 | 0 | dns_name_split(name, rad_labels + 2, NULL, &suffix); |
2104 | 0 | INSIST(*suffix.ndata < sizeof(labelbuf)); |
2105 | 0 | memmove(labelbuf, suffix.ndata + 1, *suffix.ndata); |
2106 | 0 | labelbuf[*suffix.ndata] = 0; |
2107 | 0 | if (strlen(labelbuf) != *suffix.ndata) { |
2108 | 0 | return false; |
2109 | 0 | } |
2110 | 0 | v = strtoul(labelbuf, &end, 10); |
2111 | 0 | if (v > 0xfff || *end != 0) { |
2112 | 0 | return false; |
2113 | 0 | } |
2114 | | |
2115 | 0 | return dns_name_issubdomain(name, rad); |
2116 | 0 | } |
2117 | | |
2118 | | uint8_t |
2119 | 0 | dns_name_offsets(const dns_name_t *name, dns_offsets_t offsets) { |
2120 | 0 | REQUIRE(DNS_NAME_VALID(name)); |
2121 | 0 | unsigned int offset, count, length, nlabels; |
2122 | 0 | unsigned char *ndata; |
2123 | |
|
2124 | 0 | ndata = name->ndata; |
2125 | 0 | length = name->length; |
2126 | 0 | offset = 0; |
2127 | 0 | nlabels = 0; |
2128 | 0 | while (offset != length) { |
2129 | 0 | INSIST(nlabels < DNS_NAME_MAXLABELS); |
2130 | 0 | if (offsets != NULL) { |
2131 | 0 | offsets[nlabels] = offset; |
2132 | 0 | } |
2133 | 0 | nlabels++; |
2134 | 0 | count = *ndata; |
2135 | 0 | INSIST(count <= DNS_NAME_LABELLEN); |
2136 | 0 | offset += count + 1; |
2137 | 0 | ndata += count + 1; |
2138 | 0 | INSIST(offset <= length); |
2139 | 0 | if (count == 0) { |
2140 | | /* Final root label */ |
2141 | 0 | break; |
2142 | 0 | } |
2143 | 0 | } |
2144 | 0 | INSIST(offset == name->length); |
2145 | |
|
2146 | 0 | return nlabels; |
2147 | 0 | } |