xref: /openbsd-src/usr.sbin/nsd/dname.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*
2  * dname.c -- Domain name handling.
3  *
4  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
5  *
6  * See LICENSE for the license.
7  *
8  */
9 
10 
11 #include "config.h"
12 
13 #include <sys/types.h>
14 
15 #include <assert.h>
16 #include <ctype.h>
17 #include <limits.h>
18 #include <stdio.h>
19 #include <string.h>
20 
21 #include "dns.h"
22 #include "dname.h"
23 #include "query.h"
24 
25 const dname_type *
26 dname_make(region_type *region, const uint8_t *name, int normalize)
27 {
28 	size_t name_size = 0;
29 	uint8_t label_offsets[MAXDOMAINLEN];
30 	uint8_t label_count = 0;
31 	const uint8_t *label = name;
32 	dname_type *result;
33 	ssize_t i;
34 
35 	assert(name);
36 
37 	while (1) {
38 		if (label_is_pointer(label))
39 			return NULL;
40 
41 		label_offsets[label_count] = (uint8_t) (label - name);
42 		++label_count;
43 		name_size += label_length(label) + 1;
44 
45 		if (label_is_root(label))
46 			break;
47 
48 		label = label_next(label);
49 	}
50 
51 	if (name_size > MAXDOMAINLEN)
52 		return NULL;
53 
54 	assert(label_count <= MAXDOMAINLEN / 2 + 1);
55 
56 	/* Reverse label offsets.  */
57 	for (i = 0; i < label_count / 2; ++i) {
58 		uint8_t tmp = label_offsets[i];
59 		label_offsets[i] = label_offsets[label_count - i - 1];
60 		label_offsets[label_count - i - 1] = tmp;
61 	}
62 
63 	result = (dname_type *) region_alloc(
64 		region,
65 		(sizeof(dname_type)
66 		 + (((size_t)label_count) + ((size_t)name_size)) * sizeof(uint8_t)));
67 	result->name_size = name_size;
68 	result->label_count = label_count;
69 	memcpy((uint8_t *) dname_label_offsets(result),
70 	       label_offsets,
71 	       label_count * sizeof(uint8_t));
72 	if (normalize) {
73 		uint8_t *dst = (uint8_t *) dname_name(result);
74 		const uint8_t *src = name;
75 		while (!label_is_root(src)) {
76 			ssize_t len = label_length(src);
77 			*dst++ = *src++;
78 			for (i = 0; i < len; ++i) {
79 				*dst++ = DNAME_NORMALIZE((unsigned char)*src++);
80 			}
81 		}
82 		*dst = *src;
83 	} else {
84 		memcpy((uint8_t *) dname_name(result),
85 		       name,
86 		       name_size * sizeof(uint8_t));
87 	}
88 	return result;
89 }
90 
91 
92 const dname_type *
93 dname_make_from_packet(region_type *region, buffer_type *packet,
94 		       int allow_pointers, int normalize)
95 {
96 	uint8_t buf[MAXDOMAINLEN + 1];
97 	if(!dname_make_wire_from_packet(buf, packet, allow_pointers))
98 		return 0;
99 	return dname_make(region, buf, normalize);
100 }
101 
102 int
103 dname_make_wire_from_packet(uint8_t *buf, buffer_type *packet,
104                        int allow_pointers)
105 {
106 	int done = 0;
107 	uint8_t visited[(MAX_PACKET_SIZE+7)/8];
108 	size_t dname_length = 0;
109 	const uint8_t *label;
110 	ssize_t mark = -1;
111 
112 	memset(visited, 0, (buffer_limit(packet)+7)/8);
113 
114 	while (!done) {
115 		if (!buffer_available(packet, 1)) {
116 /* 			error("dname out of bounds"); */
117 			return 0;
118 		}
119 
120 		if (get_bit(visited, buffer_position(packet))) {
121 /* 			error("dname loops"); */
122 			return 0;
123 		}
124 		set_bit(visited, buffer_position(packet));
125 
126 		label = buffer_current(packet);
127 		if (label_is_pointer(label)) {
128 			size_t pointer;
129 			if (!allow_pointers) {
130 				return 0;
131 			}
132 			if (!buffer_available(packet, 2)) {
133 /* 				error("dname pointer out of bounds"); */
134 				return 0;
135 			}
136 			pointer = label_pointer_location(label);
137 			if (pointer >= buffer_limit(packet)) {
138 /* 				error("dname pointer points outside packet"); */
139 				return 0;
140 			}
141 			buffer_skip(packet, 2);
142 			if (mark == -1) {
143 				mark = buffer_position(packet);
144 			}
145 			buffer_set_position(packet, pointer);
146 		} else if (label_is_normal(label)) {
147 			size_t length = label_length(label) + 1;
148 			done = label_is_root(label);
149 			if (!buffer_available(packet, length)) {
150 /* 				error("dname label out of bounds"); */
151 				return 0;
152 			}
153 			if (dname_length + length >= MAXDOMAINLEN+1) {
154 /* 				error("dname too large"); */
155 				return 0;
156 			}
157 			buffer_read(packet, buf + dname_length, length);
158 			dname_length += length;
159 		} else {
160 /* 			error("bad label type"); */
161 			return 0;
162 		}
163 	}
164 
165 	if (mark != -1) {
166 		buffer_set_position(packet, mark);
167 	}
168 
169 	return dname_length;
170 }
171 
172 const dname_type *
173 dname_parse(region_type *region, const char *name)
174 {
175 	uint8_t dname[MAXDOMAINLEN];
176 	if(!dname_parse_wire(dname, name))
177 		return 0;
178 	return dname_make(region, dname, 1);
179 }
180 
181 int dname_parse_wire(uint8_t* dname, const char* name)
182 {
183 	const uint8_t *s = (const uint8_t *) name;
184 	uint8_t *h;
185 	uint8_t *p;
186 	uint8_t *d = dname;
187 	size_t label_length;
188 
189 	if (strcmp(name, ".") == 0) {
190 		/* Root domain.  */
191 		dname[0] = 0;
192 		return 1;
193 	}
194 
195 	for (h = d, p = h + 1; *s; ++s, ++p) {
196 		if (p - dname >= MAXDOMAINLEN) {
197 			return 0;
198 		}
199 
200 		switch (*s) {
201 		case '.':
202 			if (p == h + 1) {
203 				/* Empty label.  */
204 				return 0;
205 			} else {
206 				label_length = p - h - 1;
207 				if (label_length > MAXLABELLEN) {
208 					return 0;
209 				}
210 				*h = label_length;
211 				h = p;
212 			}
213 			break;
214 		case '\\':
215 			/* Handle escaped characters (RFC1035 5.1) */
216 			if (isdigit((unsigned char)s[1]) && isdigit((unsigned char)s[2]) && isdigit((unsigned char)s[3])) {
217 				int val = (hexdigit_to_int(s[1]) * 100 +
218 					   hexdigit_to_int(s[2]) * 10 +
219 					   hexdigit_to_int(s[3]));
220 				if (0 <= val && val <= 255) {
221 					s += 3;
222 					*p = val;
223 				} else {
224 					*p = *++s;
225 				}
226 			} else if (s[1] != '\0') {
227 				*p = *++s;
228 			}
229 			break;
230 		default:
231 			*p = *s;
232 			break;
233 		}
234 	}
235 
236 	if (p != h + 1) {
237 		/* Terminate last label.  */
238 		label_length = p - h - 1;
239 		if (label_length > MAXLABELLEN) {
240 			return 0;
241 		}
242 		*h = label_length;
243 		h = p;
244 	}
245 
246 	/* Add root label.  */
247 	if (h - dname >= MAXDOMAINLEN) {
248 		return 0;
249 	}
250 	*h = 0;
251 
252 	return p-dname;
253 }
254 
255 
256 const dname_type *
257 dname_copy(region_type *region, const dname_type *dname)
258 {
259 	return (dname_type *) region_alloc_init(
260 		region, dname, dname_total_size(dname));
261 }
262 
263 
264 const dname_type *
265 dname_partial_copy(region_type *region, const dname_type *dname, uint8_t label_count)
266 {
267 	if (!dname)
268 		return NULL;
269 
270 	if (label_count == 0) {
271 		/* Always copy the root label.  */
272 		label_count = 1;
273 	}
274 
275 	assert(label_count <= dname->label_count);
276 
277 	return dname_make(region, dname_label(dname, label_count - 1), 0);
278 }
279 
280 
281 const dname_type *
282 dname_origin(region_type *region, const dname_type *dname)
283 {
284 	return dname_partial_copy(region, dname, dname->label_count - 1);
285 }
286 
287 
288 int
289 dname_is_subdomain(const dname_type *left, const dname_type *right)
290 {
291 	uint8_t i;
292 
293 	if (left->label_count < right->label_count)
294 		return 0;
295 
296 	for (i = 1; i < right->label_count; ++i) {
297 		if (label_compare(dname_label(left, i),
298 				  dname_label(right, i)) != 0)
299 			return 0;
300 	}
301 
302 	return 1;
303 }
304 
305 
306 int
307 dname_compare(const dname_type *left, const dname_type *right)
308 {
309 	int result;
310 	uint8_t label_count;
311 	uint8_t i;
312 
313 	assert(left);
314 	assert(right);
315 
316 	if (left == right) {
317 		return 0;
318 	}
319 
320 	label_count = (left->label_count <= right->label_count
321 		       ? left->label_count
322 		       : right->label_count);
323 
324 	/* Skip the root label by starting at label 1.  */
325 	for (i = 1; i < label_count; ++i) {
326 		result = label_compare(dname_label(left, i),
327 				       dname_label(right, i));
328 		if (result) {
329 			return result;
330 		}
331 	}
332 
333 	/* Dname with the fewest labels is "first".  */
334 	/* the subtraction works because the size of int is much larger than
335 	 * the label count and the values won't wrap around */
336 	return (int) left->label_count - (int) right->label_count;
337 }
338 
339 
340 int
341 label_compare(const uint8_t *left, const uint8_t *right)
342 {
343 	int left_length;
344 	int right_length;
345 	size_t size;
346 	int result;
347 
348 	assert(left);
349 	assert(right);
350 
351 	assert(label_is_normal(left));
352 	assert(label_is_normal(right));
353 
354 	left_length = label_length(left);
355 	right_length = label_length(right);
356 	size = left_length < right_length ? left_length : right_length;
357 
358 	result = memcmp(label_data(left), label_data(right), size);
359 	if (result) {
360 		return result;
361 	} else {
362 		/* the subtraction works because the size of int is much
363 		 * larger than the lengths and the values won't wrap around */
364 		return (int) left_length - (int) right_length;
365 	}
366 }
367 
368 
369 uint8_t
370 dname_label_match_count(const dname_type *left, const dname_type *right)
371 {
372 	uint8_t i;
373 
374 	assert(left);
375 	assert(right);
376 
377 	for (i = 1; i < left->label_count && i < right->label_count; ++i) {
378 		if (label_compare(dname_label(left, i),
379 				  dname_label(right, i)) != 0)
380 		{
381 			return i;
382 		}
383 	}
384 
385 	return i;
386 }
387 
388 const char *
389 dname_to_string(const dname_type *dname, const dname_type *origin)
390 {
391 	static char buf[MAXDOMAINLEN * 5];
392 	size_t i;
393 	size_t labels_to_convert = dname->label_count - 1;
394 	int absolute = 1;
395 	char *dst;
396 	const uint8_t *src;
397 
398 	if (dname->label_count == 1) {
399 		strlcpy(buf, ".", sizeof(buf));
400 		return buf;
401 	}
402 
403 	if (origin && dname_is_subdomain(dname, origin)) {
404 		int common_labels = dname_label_match_count(dname, origin);
405 		labels_to_convert = dname->label_count - common_labels;
406 		absolute = 0;
407 	}
408 
409 	dst = buf;
410 	src = dname_name(dname);
411 	for (i = 0; i < labels_to_convert; ++i) {
412 		size_t len = label_length(src);
413 		size_t j;
414 		++src;
415 		for (j = 0; j < len; ++j) {
416 			uint8_t ch = *src++;
417 			if (isalnum((unsigned char)ch) || ch == '-' || ch == '_') {
418 				*dst++ = ch;
419 			} else if (ch == '.' || ch == '\\') {
420 				*dst++ = '\\';
421 				*dst++ = ch;
422 			} else {
423 				snprintf(dst, 5, "\\%03u", (unsigned int)ch);
424 				dst += 4;
425 			}
426 		}
427 		*dst++ = '.';
428 	}
429 	if (absolute) {
430 		*dst = '\0';
431 	} else {
432 		*--dst = '\0';
433 	}
434 	return buf;
435 }
436 
437 
438 const dname_type *
439 dname_make_from_label(region_type *region,
440 		      const uint8_t *label, const size_t length)
441 {
442 	uint8_t temp[MAXLABELLEN + 2];
443 
444 	assert(length > 0 && length <= MAXLABELLEN);
445 
446 	temp[0] = length;
447 	memcpy(temp + 1, label, length * sizeof(uint8_t));
448 	temp[length + 1] = '\000';
449 
450 	return dname_make(region, temp, 1);
451 }
452 
453 
454 const dname_type *
455 dname_concatenate(region_type *region,
456 		  const dname_type *left,
457 		  const dname_type *right)
458 {
459 	uint8_t temp[MAXDOMAINLEN];
460 
461 	assert(left->name_size + right->name_size - 1 <= MAXDOMAINLEN);
462 
463 	memcpy(temp, dname_name(left), left->name_size - 1);
464 	memcpy(temp + left->name_size - 1, dname_name(right), right->name_size);
465 
466 	return dname_make(region, temp, 0);
467 }
468 
469 
470 const dname_type *
471 dname_replace(region_type* region,
472 		const dname_type* name,
473 		const dname_type* src,
474 		const dname_type* dest)
475 {
476 	/* nomenclature: name is said to be <x>.<src>. x can be null. */
477 	dname_type* res;
478 	int x_labels = name->label_count - src->label_count;
479 	int x_len = name->name_size - src->name_size;
480 	int i;
481 	assert(dname_is_subdomain(name, src));
482 
483 	/* check if final size is acceptable */
484 	if(x_len+dest->name_size > MAXDOMAINLEN)
485 		return NULL;
486 
487 	res = (dname_type*)region_alloc(region, sizeof(dname_type) +
488 		(x_labels+((int)dest->label_count) + x_len+((int)dest->name_size))
489 		*sizeof(uint8_t));
490 	res->name_size = x_len+dest->name_size;
491 	res->label_count = x_labels+dest->label_count;
492 	for(i=0; i<dest->label_count; i++)
493 		((uint8_t*)dname_label_offsets(res))[i] =
494 			dname_label_offsets(dest)[i] + x_len;
495 	for(i=dest->label_count; i<res->label_count; i++)
496 		((uint8_t*)dname_label_offsets(res))[i] =
497 			dname_label_offsets(name)[i - dest->label_count +
498 				src->label_count];
499 	memcpy((uint8_t*)dname_name(res), dname_name(name), x_len);
500 	memcpy((uint8_t*)dname_name(res)+x_len, dname_name(dest), dest->name_size);
501 	assert(dname_is_subdomain(res, dest));
502 	return res;
503 }
504 
505 char* wirelabel2str(const uint8_t* label)
506 {
507 	static char buf[MAXDOMAINLEN*5+3];
508 	char* p = buf;
509 	uint8_t lablen;
510 	lablen = *label++;
511 	while(lablen--) {
512 		uint8_t ch = *label++;
513 		if (isalnum((unsigned char)ch) || ch == '-' || ch == '_') {
514 			*p++ = ch;
515 		} else if (ch == '.' || ch == '\\') {
516 			*p++ = '\\';
517 			*p++ = ch;
518 		} else {
519 			snprintf(p, 5, "\\%03u", (unsigned int)ch);
520 			p += 4;
521 		}
522 	}
523 	*p++ = 0;
524 	return buf;
525 }
526 
527 char* wiredname2str(const uint8_t* dname)
528 {
529 	static char buf[MAXDOMAINLEN*5+3];
530 	char* p = buf;
531 	uint8_t lablen;
532 	if(*dname == 0) {
533 		strlcpy(buf, ".", sizeof(buf));
534 		return buf;
535 	}
536 	lablen = *dname++;
537 	while(lablen) {
538 		while(lablen--) {
539 			uint8_t ch = *dname++;
540 			if (isalnum((unsigned char)ch) || ch == '-' || ch == '_' || ch == '*') {
541 				*p++ = ch;
542 			} else if (ch == '.' || ch == '\\') {
543 				*p++ = '\\';
544 				*p++ = ch;
545 			} else {
546 				snprintf(p, 5, "\\%03u", (unsigned int)ch);
547 				p += 4;
548 			}
549 		}
550 		lablen = *dname++;
551 		*p++ = '.';
552 	}
553 	*p++ = 0;
554 	return buf;
555 }
556 
557 int dname_equal_nocase(uint8_t* a, uint8_t* b, uint16_t len)
558 {
559 	uint8_t i, lablen;
560 	while(len > 0) {
561 		/* check labellen */
562 		if(*a != *b)
563 			return 0;
564 		lablen = *a++;
565 		b++;
566 		len--;
567 		/* malformed or compression ptr; we stop scanning */
568 		if((lablen & 0xc0) || len < lablen)
569 			return (memcmp(a, b, len) == 0);
570 		/* check the label, lowercased */
571 		for(i=0; i<lablen; i++) {
572 			if(DNAME_NORMALIZE((unsigned char)*a++) != DNAME_NORMALIZE((unsigned char)*b++))
573 				return 0;
574 		}
575 		len -= lablen;
576 	}
577 	return 1;
578 }
579