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