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