xref: /openbsd/usr.sbin/nsd/dname.c (revision 91f110e0)
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 		 + (label_count + 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(*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(s[1]) && isdigit(s[2]) && isdigit(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 	*h = 0;
248 
249 	return p-dname;
250 }
251 
252 
253 const dname_type *
254 dname_copy(region_type *region, const dname_type *dname)
255 {
256 	return (dname_type *) region_alloc_init(
257 		region, dname, dname_total_size(dname));
258 }
259 
260 
261 const dname_type *
262 dname_partial_copy(region_type *region, const dname_type *dname, uint8_t label_count)
263 {
264 	if (!dname)
265 		return NULL;
266 
267 	if (label_count == 0) {
268 		/* Always copy the root label.  */
269 		label_count = 1;
270 	}
271 
272 	assert(label_count <= dname->label_count);
273 
274 	return dname_make(region, dname_label(dname, label_count - 1), 0);
275 }
276 
277 
278 const dname_type *
279 dname_origin(region_type *region, const dname_type *dname)
280 {
281 	return dname_partial_copy(region, dname, dname->label_count - 1);
282 }
283 
284 
285 int
286 dname_is_subdomain(const dname_type *left, const dname_type *right)
287 {
288 	uint8_t i;
289 
290 	if (left->label_count < right->label_count)
291 		return 0;
292 
293 	for (i = 1; i < right->label_count; ++i) {
294 		if (label_compare(dname_label(left, i),
295 				  dname_label(right, i)) != 0)
296 			return 0;
297 	}
298 
299 	return 1;
300 }
301 
302 
303 int
304 dname_compare(const dname_type *left, const dname_type *right)
305 {
306 	int result;
307 	uint8_t label_count;
308 	uint8_t i;
309 
310 	assert(left);
311 	assert(right);
312 
313 	if (left == right) {
314 		return 0;
315 	}
316 
317 	label_count = (left->label_count <= right->label_count
318 		       ? left->label_count
319 		       : right->label_count);
320 
321 	/* Skip the root label by starting at label 1.  */
322 	for (i = 1; i < label_count; ++i) {
323 		result = label_compare(dname_label(left, i),
324 				       dname_label(right, i));
325 		if (result) {
326 			return result;
327 		}
328 	}
329 
330 	/* Dname with the fewest labels is "first".  */
331 	return (int) left->label_count - (int) right->label_count;
332 }
333 
334 
335 int
336 label_compare(const uint8_t *left, const uint8_t *right)
337 {
338 	int left_length;
339 	int right_length;
340 	size_t size;
341 	int result;
342 
343 	assert(left);
344 	assert(right);
345 
346 	assert(label_is_normal(left));
347 	assert(label_is_normal(right));
348 
349 	left_length = label_length(left);
350 	right_length = label_length(right);
351 	size = left_length < right_length ? left_length : right_length;
352 
353 	result = memcmp(label_data(left), label_data(right), size);
354 	if (result) {
355 		return result;
356 	} else {
357 		return (int) left_length - (int) right_length;
358 	}
359 }
360 
361 
362 uint8_t
363 dname_label_match_count(const dname_type *left, const dname_type *right)
364 {
365 	uint8_t i;
366 
367 	assert(left);
368 	assert(right);
369 
370 	for (i = 1; i < left->label_count && i < right->label_count; ++i) {
371 		if (label_compare(dname_label(left, i),
372 				  dname_label(right, i)) != 0)
373 		{
374 			return i;
375 		}
376 	}
377 
378 	return i;
379 }
380 
381 const char *
382 dname_to_string(const dname_type *dname, const dname_type *origin)
383 {
384 	static char buf[MAXDOMAINLEN * 5];
385 	size_t i;
386 	size_t labels_to_convert = dname->label_count - 1;
387 	int absolute = 1;
388 	char *dst;
389 	const uint8_t *src;
390 
391 	if (dname->label_count == 1) {
392 		strlcpy(buf, ".", sizeof(buf));
393 		return buf;
394 	}
395 
396 	if (origin && dname_is_subdomain(dname, origin)) {
397 		int common_labels = dname_label_match_count(dname, origin);
398 		labels_to_convert = dname->label_count - common_labels;
399 		absolute = 0;
400 	}
401 
402 	dst = buf;
403 	src = dname_name(dname);
404 	for (i = 0; i < labels_to_convert; ++i) {
405 		size_t len = label_length(src);
406 		size_t j;
407 		++src;
408 		for (j = 0; j < len; ++j) {
409 			uint8_t ch = *src++;
410 			if (isalnum(ch) || ch == '-' || ch == '_') {
411 				*dst++ = ch;
412 			} else if (ch == '.' || ch == '\\') {
413 				*dst++ = '\\';
414 				*dst++ = ch;
415 			} else {
416 				snprintf(dst, 5, "\\%03u", (unsigned int)ch);
417 				dst += 4;
418 			}
419 		}
420 		*dst++ = '.';
421 	}
422 	if (absolute) {
423 		*dst = '\0';
424 	} else {
425 		*--dst = '\0';
426 	}
427 	return buf;
428 }
429 
430 
431 const dname_type *
432 dname_make_from_label(region_type *region,
433 		      const uint8_t *label, const size_t length)
434 {
435 	uint8_t temp[MAXLABELLEN + 2];
436 
437 	assert(length > 0 && length <= MAXLABELLEN);
438 
439 	temp[0] = length;
440 	memcpy(temp + 1, label, length * sizeof(uint8_t));
441 	temp[length + 1] = '\000';
442 
443 	return dname_make(region, temp, 1);
444 }
445 
446 
447 const dname_type *
448 dname_concatenate(region_type *region,
449 		  const dname_type *left,
450 		  const dname_type *right)
451 {
452 	uint8_t temp[MAXDOMAINLEN];
453 
454 	assert(left->name_size + right->name_size - 1 <= MAXDOMAINLEN);
455 
456 	memcpy(temp, dname_name(left), left->name_size - 1);
457 	memcpy(temp + left->name_size - 1, dname_name(right), right->name_size);
458 
459 	return dname_make(region, temp, 0);
460 }
461 
462 
463 const dname_type *
464 dname_replace(region_type* region,
465 		const dname_type* name,
466 		const dname_type* src,
467 		const dname_type* dest)
468 {
469 	/* nomenclature: name is said to be <x>.<src>. x can be null. */
470 	dname_type* res;
471 	int x_labels = name->label_count - src->label_count;
472 	int x_len = name->name_size - src->name_size;
473 	int i;
474 	assert(dname_is_subdomain(name, src));
475 
476 	/* check if final size is acceptable */
477 	if(x_len+dest->name_size > MAXDOMAINLEN)
478 		return NULL;
479 
480 	res = (dname_type*)region_alloc(region, sizeof(dname_type) +
481 		(x_labels+dest->label_count + x_len+dest->name_size)
482 		*sizeof(uint8_t));
483 	res->name_size = x_len+dest->name_size;
484 	res->label_count = x_labels+dest->label_count;
485 	for(i=0; i<dest->label_count; i++)
486 		((uint8_t*)dname_label_offsets(res))[i] =
487 			dname_label_offsets(dest)[i] + x_len;
488 	for(i=dest->label_count; i<res->label_count; i++)
489 		((uint8_t*)dname_label_offsets(res))[i] =
490 			dname_label_offsets(name)[i - dest->label_count +
491 				src->label_count];
492 	memcpy((uint8_t*)dname_name(res), dname_name(name), x_len);
493 	memcpy((uint8_t*)dname_name(res)+x_len, dname_name(dest), dest->name_size);
494 	assert(dname_is_subdomain(res, dest));
495 	return res;
496 }
497 
498 char* wirelabel2str(const uint8_t* label)
499 {
500 	static char buf[MAXDOMAINLEN*5+3];
501 	char* p = buf;
502 	uint8_t lablen;
503 	lablen = *label++;
504 	while(lablen--) {
505 		uint8_t ch = *label++;
506 		if (isalnum(ch) || ch == '-' || ch == '_') {
507 			*p++ = ch;
508 		} else if (ch == '.' || ch == '\\') {
509 			*p++ = '\\';
510 			*p++ = ch;
511 		} else {
512 			snprintf(p, 5, "\\%03u", (unsigned int)ch);
513 			p += 4;
514 		}
515 	}
516 	*p++ = 0;
517 	return buf;
518 }
519 
520 char* wiredname2str(const uint8_t* dname)
521 {
522 	static char buf[MAXDOMAINLEN*5+3];
523 	char* p = buf;
524 	uint8_t lablen;
525 	if(*dname == 0) {
526 		strlcpy(buf, ".", sizeof(buf));
527 		return buf;
528 	}
529 	lablen = *dname++;
530 	while(lablen) {
531 		while(lablen--) {
532 			uint8_t ch = *dname++;
533 			if (isalnum(ch) || ch == '-' || ch == '_') {
534 				*p++ = ch;
535 			} else if (ch == '.' || ch == '\\') {
536 				*p++ = '\\';
537 				*p++ = ch;
538 			} else {
539 				snprintf(p, 5, "\\%03u", (unsigned int)ch);
540 				p += 4;
541 			}
542 		}
543 		lablen = *dname++;
544 		*p++ = '.';
545 	}
546 	*p++ = 0;
547 	return buf;
548 }
549 
550 int dname_equal_nocase(uint8_t* a, uint8_t* b, uint16_t len)
551 {
552 	uint8_t i, lablen;
553 	while(len > 0) {
554 		/* check labellen */
555 		if(*a != *b)
556 			return 0;
557 		lablen = *a++;
558 		b++;
559 		len--;
560 		/* malformed or compression ptr; we stop scanning */
561 		if((lablen & 0xc0) || len < lablen)
562 			return (memcmp(a, b, len) == 0);
563 		/* check the label, lowercased */
564 		for(i=0; i<lablen; i++) {
565 			if(DNAME_NORMALIZE(*a++) != DNAME_NORMALIZE(*b++))
566 				return 0;
567 		}
568 		len -= lablen;
569 	}
570 	return 1;
571 }
572