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