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