xref: /freebsd/contrib/ldns/dname.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
1 /*
2  * dname.c
3  *
4  * dname specific rdata implementations
5  * A dname is a rdf structure with type LDNS_RDF_TYPE_DNAME
6  * It is not a /real/ type! All function must therefore check
7  * for LDNS_RDF_TYPE_DNAME.
8  *
9  * a Net::DNS like library for C
10  *
11  * (c) NLnet Labs, 2004-2006
12  *
13  * See the file LICENSE for the license
14  */
15 
16 #include <ldns/config.h>
17 
18 #include <ldns/ldns.h>
19 
20 #ifdef HAVE_NETINET_IN_H
21 #include <netinet/in.h>
22 #endif
23 #ifdef HAVE_SYS_SOCKET_H
24 #include <sys/socket.h>
25 #endif
26 #ifdef HAVE_NETDB_H
27 #include <netdb.h>
28 #endif
29 #ifdef HAVE_ARPA_INET_H
30 #include <arpa/inet.h>
31 #endif
32 
33 /* Returns whether the last label in the name is a root label (a empty label).
34  * Note that it is not enough to just test the last character to be 0,
35  * because it may be part of the last label itself.
36  */
37 static bool
38 ldns_dname_last_label_is_root_label(const ldns_rdf* dname)
39 {
40 	size_t src_pos;
41 	size_t len = 0;
42 
43 	for (src_pos = 0; src_pos < ldns_rdf_size(dname); src_pos += len + 1) {
44 		len = ldns_rdf_data(dname)[src_pos];
45 	}
46 	assert(src_pos == ldns_rdf_size(dname));
47 
48 	return src_pos > 0 && len == 0;
49 }
50 
51 ldns_rdf *
52 ldns_dname_cat_clone(const ldns_rdf *rd1, const ldns_rdf *rd2)
53 {
54 	ldns_rdf *new;
55 	uint16_t new_size;
56 	uint8_t *buf;
57 	uint16_t left_size;
58 
59 	if (ldns_rdf_get_type(rd1) != LDNS_RDF_TYPE_DNAME ||
60 			ldns_rdf_get_type(rd2) != LDNS_RDF_TYPE_DNAME) {
61 		return NULL;
62 	}
63 
64 	/* remove root label if it is present at the end of the left
65 	 * rd, by reducing the size with 1
66 	 */
67 	left_size = ldns_rdf_size(rd1);
68 	if (ldns_dname_last_label_is_root_label(rd1)) {
69 		left_size--;
70 	}
71 
72 	/* we overwrite the nullbyte of rd1 */
73 	new_size = left_size + ldns_rdf_size(rd2);
74 	buf = LDNS_XMALLOC(uint8_t, new_size);
75 	if (!buf) {
76 		return NULL;
77 	}
78 
79 	/* put the two dname's after each other */
80 	memcpy(buf, ldns_rdf_data(rd1), left_size);
81 	memcpy(buf + left_size, ldns_rdf_data(rd2), ldns_rdf_size(rd2));
82 
83 	new = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, new_size, buf);
84 
85 	LDNS_FREE(buf);
86 	return new;
87 }
88 
89 ldns_status
90 ldns_dname_cat(ldns_rdf *rd1, const ldns_rdf *rd2)
91 {
92 	uint16_t left_size;
93 	uint16_t size;
94 	uint8_t* newd;
95 
96 	if (ldns_rdf_get_type(rd1) != LDNS_RDF_TYPE_DNAME ||
97 			ldns_rdf_get_type(rd2) != LDNS_RDF_TYPE_DNAME) {
98 		return LDNS_STATUS_ERR;
99 	}
100 
101 	/* remove root label if it is present at the end of the left
102 	 * rd, by reducing the size with 1
103 	 */
104 	left_size = ldns_rdf_size(rd1);
105 	if (ldns_dname_last_label_is_root_label(rd1)) {
106 		left_size--;
107 	}
108 
109 	size = left_size + ldns_rdf_size(rd2);
110 	newd = LDNS_XREALLOC(ldns_rdf_data(rd1), uint8_t, size);
111 	if(!newd) {
112 		return LDNS_STATUS_MEM_ERR;
113 	}
114 
115 	ldns_rdf_set_data(rd1, newd);
116 	memcpy(ldns_rdf_data(rd1) + left_size, ldns_rdf_data(rd2),
117 			ldns_rdf_size(rd2));
118 	ldns_rdf_set_size(rd1, size);
119 
120 	return LDNS_STATUS_OK;
121 }
122 
123 ldns_rdf*
124 ldns_dname_reverse(const ldns_rdf *dname)
125 {
126 	size_t rd_size;
127 	uint8_t* buf;
128 	ldns_rdf* new;
129 	size_t src_pos;
130 	size_t len ;
131 
132 	assert(ldns_rdf_get_type(dname) == LDNS_RDF_TYPE_DNAME);
133 
134 	rd_size = ldns_rdf_size(dname);
135 	buf = LDNS_XMALLOC(uint8_t, rd_size);
136 	if (! buf) {
137 		return NULL;
138 	}
139 	new = ldns_rdf_new(LDNS_RDF_TYPE_DNAME, rd_size, buf);
140 	if (! new) {
141 		LDNS_FREE(buf);
142 		return NULL;
143 	}
144 
145 	/* If dname ends in a root label, the reverse should too.
146 	 */
147 	if (ldns_dname_last_label_is_root_label(dname)) {
148 		buf[rd_size - 1] = 0;
149 		rd_size -= 1;
150 	}
151 	for (src_pos = 0; src_pos < rd_size; src_pos += len + 1) {
152 		len = ldns_rdf_data(dname)[src_pos];
153 		memcpy(&buf[rd_size - src_pos - len - 1],
154 				&ldns_rdf_data(dname)[src_pos], len + 1);
155 	}
156 	return new;
157 }
158 
159 ldns_rdf *
160 ldns_dname_clone_from(const ldns_rdf *d, uint16_t n)
161 {
162 	uint8_t *data;
163 	uint8_t label_size;
164 	size_t data_size;
165 
166 	if (!d ||
167 	    ldns_rdf_get_type(d) != LDNS_RDF_TYPE_DNAME ||
168 	    ldns_dname_label_count(d) < n) {
169 		return NULL;
170 	}
171 
172 	data = ldns_rdf_data(d);
173 	data_size = ldns_rdf_size(d);
174 	while (n > 0) {
175 		label_size = data[0] + 1;
176 		data += label_size;
177 		if (data_size < label_size) {
178 			/* this label is very broken */
179 			return NULL;
180 		}
181 		data_size -= label_size;
182 		n--;
183 	}
184 
185 	return ldns_dname_new_frm_data(data_size, data);
186 }
187 
188 ldns_rdf *
189 ldns_dname_left_chop(const ldns_rdf *d)
190 {
191 	uint8_t label_pos;
192 	ldns_rdf *chop;
193 
194 	if (!d) {
195 		return NULL;
196 	}
197 
198 	if (ldns_rdf_get_type(d) != LDNS_RDF_TYPE_DNAME) {
199 		return NULL;
200 	}
201 	if (ldns_dname_label_count(d) == 0) {
202 		/* root label */
203 		return NULL;
204 	}
205 	/* 05blaat02nl00 */
206 	label_pos = ldns_rdf_data(d)[0];
207 
208 	chop = ldns_dname_new_frm_data(ldns_rdf_size(d) - label_pos - 1,
209 			ldns_rdf_data(d) + label_pos + 1);
210 	return chop;
211 }
212 
213 uint8_t
214 ldns_dname_label_count(const ldns_rdf *r)
215 {
216         uint16_t src_pos;
217         uint16_t len;
218         uint8_t i;
219         size_t r_size;
220 
221 	if (!r) {
222 		return 0;
223 	}
224 
225 	i = 0;
226 	src_pos = 0;
227 	r_size = ldns_rdf_size(r);
228 
229 	if (ldns_rdf_get_type(r) != LDNS_RDF_TYPE_DNAME) {
230 		return 0;
231 	} else {
232 		len = ldns_rdf_data(r)[src_pos]; /* start of the label */
233 
234 		/* single root label */
235 		if (1 == r_size) {
236 			return 0;
237 		} else {
238 			while ((len > 0) && src_pos < r_size) {
239 				src_pos++;
240 				src_pos += len;
241 				len = ldns_rdf_data(r)[src_pos];
242 				i++;
243 			}
244 		}
245 	}
246 	return i;
247 }
248 
249 ldns_rdf *
250 ldns_dname_new(uint16_t s, void *d)
251 {
252         ldns_rdf *rd;
253 
254         if (!s || !d) {
255                 return NULL;
256         }
257         rd = LDNS_MALLOC(ldns_rdf);
258         if (!rd) {
259                 return NULL;
260         }
261         ldns_rdf_set_size(rd, s);
262         ldns_rdf_set_type(rd, LDNS_RDF_TYPE_DNAME);
263         ldns_rdf_set_data(rd, d);
264         return rd;
265 }
266 
267 ldns_rdf *
268 ldns_dname_new_frm_str(const char *str)
269 {
270 	return ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, str);
271 }
272 
273 ldns_rdf *
274 ldns_dname_new_frm_data(uint16_t size, const void *data)
275 {
276 	return ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, size, data);
277 }
278 
279 void
280 ldns_dname2canonical(const ldns_rdf *rd)
281 {
282 	uint8_t *rdd;
283 	uint16_t i;
284 
285 	if (ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_DNAME) {
286 		return;
287 	}
288 
289 	rdd = (uint8_t*)ldns_rdf_data(rd);
290 	for (i = 0; i < ldns_rdf_size(rd); i++, rdd++) {
291 		*rdd = (uint8_t)LDNS_DNAME_NORMALIZE((int)*rdd);
292 	}
293 }
294 
295 bool
296 ldns_dname_is_subdomain(const ldns_rdf *sub, const ldns_rdf *parent)
297 {
298 	uint8_t sub_lab;
299 	uint8_t par_lab;
300 	int8_t i, j;
301 	ldns_rdf *tmp_sub = NULL;
302 	ldns_rdf *tmp_par = NULL;
303     ldns_rdf *sub_clone;
304     ldns_rdf *parent_clone;
305     bool result = true;
306 
307 	if (ldns_rdf_get_type(sub) != LDNS_RDF_TYPE_DNAME ||
308 			ldns_rdf_get_type(parent) != LDNS_RDF_TYPE_DNAME ||
309 			ldns_rdf_compare(sub, parent) == 0) {
310 		return false;
311 	}
312 
313     /* would be nicer if we do not have to clone... */
314     sub_clone = ldns_dname_clone_from(sub, 0);
315     parent_clone = ldns_dname_clone_from(parent, 0);
316     ldns_dname2canonical(sub_clone);
317     ldns_dname2canonical(parent_clone);
318 
319 	sub_lab = ldns_dname_label_count(sub_clone);
320 	par_lab = ldns_dname_label_count(parent_clone);
321 
322 	/* if sub sits above parent, it cannot be a child/sub domain */
323 	if (sub_lab < par_lab) {
324 		result = false;
325 	} else {
326 		/* check all labels the from the parent labels, from right to left.
327 		 * When they /all/ match we have found a subdomain
328 		 */
329 		j = sub_lab - 1; /* we count from zero, thank you */
330 		for (i = par_lab -1; i >= 0; i--) {
331 			tmp_sub = ldns_dname_label(sub_clone, j);
332 			tmp_par = ldns_dname_label(parent_clone, i);
333 			if (!tmp_sub || !tmp_par) {
334 				/* deep free does null check */
335 				ldns_rdf_deep_free(tmp_sub);
336 				ldns_rdf_deep_free(tmp_par);
337 				result = false;
338 				break;
339 			}
340 
341 			if (ldns_rdf_compare(tmp_sub, tmp_par) != 0) {
342 				/* they are not equal */
343 				ldns_rdf_deep_free(tmp_sub);
344 				ldns_rdf_deep_free(tmp_par);
345 				result = false;
346 				break;
347 			}
348 			ldns_rdf_deep_free(tmp_sub);
349 			ldns_rdf_deep_free(tmp_par);
350 			j--;
351 		}
352 	}
353 	ldns_rdf_deep_free(sub_clone);
354 	ldns_rdf_deep_free(parent_clone);
355 	return result;
356 }
357 
358 int
359 ldns_dname_compare(const ldns_rdf *dname1, const ldns_rdf *dname2)
360 {
361 	size_t lc1, lc2, lc1f, lc2f;
362 	size_t i;
363 	int result = 0;
364 	uint8_t *lp1, *lp2;
365 
366 	/* see RFC4034 for this algorithm */
367 	/* this algorithm assumes the names are normalized to case */
368 
369         /* only when both are not NULL we can say anything about them */
370         if (!dname1 && !dname2) {
371                 return 0;
372         }
373         if (!dname1 || !dname2) {
374                 return -1;
375         }
376 	/* asserts must happen later as we are looking in the
377 	 * dname, which could be NULL. But this case is handled
378 	 * above
379 	 */
380 	assert(ldns_rdf_get_type(dname1) == LDNS_RDF_TYPE_DNAME);
381 	assert(ldns_rdf_get_type(dname2) == LDNS_RDF_TYPE_DNAME);
382 
383 	lc1 = ldns_dname_label_count(dname1);
384 	lc2 = ldns_dname_label_count(dname2);
385 
386 	if (lc1 == 0 && lc2 == 0) {
387 		return 0;
388 	}
389 	if (lc1 == 0) {
390 		return -1;
391 	}
392 	if (lc2 == 0) {
393 		return 1;
394 	}
395 	lc1--;
396 	lc2--;
397 	/* we start at the last label */
398 	while (true) {
399 		/* find the label first */
400 		lc1f = lc1;
401 		lp1 = ldns_rdf_data(dname1);
402 		while (lc1f > 0) {
403 			lp1 += *lp1 + 1;
404 			lc1f--;
405 		}
406 
407 		/* and find the other one */
408 		lc2f = lc2;
409 		lp2 = ldns_rdf_data(dname2);
410 		while (lc2f > 0) {
411 			lp2 += *lp2 + 1;
412 			lc2f--;
413 		}
414 
415 		/* now check the label character for character. */
416 		for (i = 1; i < (size_t)(*lp1 + 1); i++) {
417 			if (i > *lp2) {
418 				/* apparently label 1 is larger */
419 				result = 1;
420 				goto done;
421 			}
422 			if (LDNS_DNAME_NORMALIZE((int) *(lp1 + i)) <
423 			    LDNS_DNAME_NORMALIZE((int) *(lp2 + i))) {
424 			    result = -1;
425 			    goto done;
426 			} else if (LDNS_DNAME_NORMALIZE((int) *(lp1 + i)) >
427 			    LDNS_DNAME_NORMALIZE((int) *(lp2 + i))) {
428 			    result = 1;
429 			    goto done;
430 			}
431 		}
432 		if (*lp1 < *lp2) {
433 			/* apparently label 2 is larger */
434 			result = -1;
435 			goto done;
436 		}
437 		if (lc1 == 0 && lc2 > 0) {
438 			result = -1;
439 			goto done;
440 		} else if (lc1 > 0 && lc2 == 0) {
441 			result = 1;
442 			goto done;
443 		} else if (lc1 == 0 && lc2 == 0) {
444 			result = 0;
445 			goto done;
446 		}
447 		lc1--;
448 		lc2--;
449 	}
450 
451 	done:
452 	return result;
453 }
454 
455 int
456 ldns_dname_is_wildcard(const ldns_rdf* dname)
457 {
458 	return ( ldns_dname_label_count(dname) > 0 &&
459 		 ldns_rdf_data(dname)[0] == 1 &&
460 		 ldns_rdf_data(dname)[1] == '*');
461 }
462 
463 int
464 ldns_dname_match_wildcard(const ldns_rdf *dname, const ldns_rdf *wildcard)
465 {
466 	ldns_rdf *wc_chopped;
467 	int result;
468 	/* check whether it really is a wildcard */
469 	if (ldns_dname_is_wildcard(wildcard)) {
470 		/* ok, so the dname needs to be a subdomain of the wildcard
471 		 * without the *
472 		 */
473 		wc_chopped = ldns_dname_left_chop(wildcard);
474 		result = (int) ldns_dname_is_subdomain(dname, wc_chopped);
475 		ldns_rdf_deep_free(wc_chopped);
476 	} else {
477 		result = (ldns_dname_compare(dname, wildcard) == 0);
478 	}
479 	return result;
480 }
481 
482 /* nsec test: does prev <= middle < next
483  * -1 = yes
484  * 0 = error/can't tell
485  * 1 = no
486  */
487 int
488 ldns_dname_interval(const ldns_rdf *prev, const ldns_rdf *middle,
489 		const ldns_rdf *next)
490 {
491 	int prev_check, next_check;
492 
493 	assert(ldns_rdf_get_type(prev) == LDNS_RDF_TYPE_DNAME);
494 	assert(ldns_rdf_get_type(middle) == LDNS_RDF_TYPE_DNAME);
495 	assert(ldns_rdf_get_type(next) == LDNS_RDF_TYPE_DNAME);
496 
497 	prev_check = ldns_dname_compare(prev, middle);
498 	next_check = ldns_dname_compare(middle, next);
499 	/* <= next. This cannot be the case for nsec, because then we would
500 	 * have gotten the nsec of next...
501 	 */
502 	if (next_check == 0) {
503 		return 0;
504 	}
505 
506 			/* <= */
507 	if ((prev_check == -1 || prev_check == 0) &&
508 			/* < */
509 			next_check == -1) {
510 		return -1;
511 	} else {
512 		return 1;
513 	}
514 }
515 
516 
517 bool
518 ldns_dname_str_absolute(const char *dname_str)
519 {
520         const char* s;
521 	if(dname_str && strcmp(dname_str, ".") == 0)
522 		return 1;
523         if(!dname_str || strlen(dname_str) < 2)
524                 return 0;
525         if(dname_str[strlen(dname_str) - 1] != '.')
526                 return 0;
527         if(dname_str[strlen(dname_str) - 2] != '\\')
528                 return 1; /* ends in . and no \ before it */
529         /* so we have the case of ends in . and there is \ before it */
530         for(s=dname_str; *s; s++) {
531                 if(*s == '\\') {
532                         if(s[1] && s[2] && s[3] /* check length */
533                                 && isdigit((unsigned char)s[1])
534 				&& isdigit((unsigned char)s[2])
535 				&& isdigit((unsigned char)s[3]))
536                                 s += 3;
537                         else if(!s[1] || isdigit((unsigned char)s[1])) /* escape of nul,0-9 */
538                                 return 0; /* parse error */
539                         else s++; /* another character escaped */
540                 }
541                 else if(!*(s+1) && *s == '.')
542                         return 1; /* trailing dot, unescaped */
543         }
544         return 0;
545 }
546 
547 bool
548 ldns_dname_absolute(const ldns_rdf *rdf)
549 {
550 	char *str = ldns_rdf2str(rdf);
551 	if (str) {
552 		bool r = ldns_dname_str_absolute(str);
553 		LDNS_FREE(str);
554 		return r;
555 	}
556 	return false;
557 }
558 
559 ldns_rdf *
560 ldns_dname_label(const ldns_rdf *rdf, uint8_t labelpos)
561 {
562 	uint8_t labelcnt;
563 	uint16_t src_pos;
564 	uint16_t len;
565 	ldns_rdf *tmpnew;
566 	size_t s;
567 	uint8_t *data;
568 
569 	if (ldns_rdf_get_type(rdf) != LDNS_RDF_TYPE_DNAME) {
570 		return NULL;
571 	}
572 
573 	labelcnt = 0;
574 	src_pos = 0;
575 	s = ldns_rdf_size(rdf);
576 
577 	len = ldns_rdf_data(rdf)[src_pos]; /* label start */
578 	while ((len > 0) && src_pos < s) {
579 		if (labelcnt == labelpos) {
580 			/* found our label */
581 			data = LDNS_XMALLOC(uint8_t, len + 2);
582 			if (!data) {
583 				return NULL;
584 			}
585 			memcpy(data, ldns_rdf_data(rdf) + src_pos, len + 1);
586 			data[len + 2 - 1] = 0;
587 
588 			tmpnew = ldns_rdf_new( LDNS_RDF_TYPE_DNAME
589 					     , len + 2, data);
590 			if (!tmpnew) {
591 				LDNS_FREE(data);
592 				return NULL;
593 			}
594 			return tmpnew;
595 		}
596 		src_pos++;
597 		src_pos += len;
598 		len = ldns_rdf_data(rdf)[src_pos];
599 		labelcnt++;
600 	}
601 	return NULL;
602 }
603