xref: /freebsd/contrib/unbound/util/data/dname.c (revision 2f513db7)
1 /*
2  * util/data/dname.h - domain name handling
3  *
4  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5  *
6  * This software is open source.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * Neither the name of the NLNET LABS nor the names of its contributors may
20  * be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 /**
37  * \file
38  *
39  * This file contains domain name handling functions.
40  */
41 
42 #include "config.h"
43 #include <ctype.h>
44 #include "util/data/dname.h"
45 #include "util/data/msgparse.h"
46 #include "util/log.h"
47 #include "util/storage/lookup3.h"
48 #include "sldns/sbuffer.h"
49 
50 /* determine length of a dname in buffer, no compression pointers allowed */
51 size_t
52 query_dname_len(sldns_buffer* query)
53 {
54 	size_t len = 0;
55 	size_t labellen;
56 	while(1) {
57 		if(sldns_buffer_remaining(query) < 1)
58 			return 0; /* parse error, need label len */
59 		labellen = sldns_buffer_read_u8(query);
60 		if(labellen&0xc0)
61 			return 0; /* no compression allowed in queries */
62 		len += labellen + 1;
63 		if(len > LDNS_MAX_DOMAINLEN)
64 			return 0; /* too long */
65 		if(labellen == 0)
66 			return len;
67 		if(sldns_buffer_remaining(query) < labellen)
68 			return 0; /* parse error, need content */
69 		sldns_buffer_skip(query, (ssize_t)labellen);
70 	}
71 }
72 
73 size_t
74 dname_valid(uint8_t* dname, size_t maxlen)
75 {
76 	size_t len = 0;
77 	size_t labellen;
78 	if(maxlen == 0)
79 		return 0; /* too short, shortest is '0' root label */
80 	labellen = *dname++;
81 	while(labellen) {
82 		if(labellen&0xc0)
83 			return 0; /* no compression ptrs allowed */
84 		len += labellen + 1;
85 		if(len >= LDNS_MAX_DOMAINLEN)
86 			return 0; /* too long */
87 		if(len > maxlen)
88 			return 0; /* does not fit in memory allocation */
89 		dname += labellen;
90 		labellen = *dname++;
91 	}
92 	len += 1;
93 	if(len > maxlen)
94 		return 0; /* does not fit in memory allocation */
95 	return len;
96 }
97 
98 /** compare uncompressed, noncanonical, registers are hints for speed */
99 int
100 query_dname_compare(register uint8_t* d1, register uint8_t* d2)
101 {
102 	register uint8_t lab1, lab2;
103 	log_assert(d1 && d2);
104 	lab1 = *d1++;
105 	lab2 = *d2++;
106 	while( lab1 != 0 || lab2 != 0 ) {
107 		/* compare label length */
108 		/* if one dname ends, it has labellength 0 */
109 		if(lab1 != lab2) {
110 			if(lab1 < lab2)
111 				return -1;
112 			return 1;
113 		}
114 		log_assert(lab1 == lab2 && lab1 != 0);
115 		/* compare lowercased labels. */
116 		while(lab1--) {
117 			/* compare bytes first for speed */
118 			if(*d1 != *d2 &&
119 				tolower((unsigned char)*d1) != tolower((unsigned char)*d2)) {
120 				if(tolower((unsigned char)*d1) < tolower((unsigned char)*d2))
121 					return -1;
122 				return 1;
123 			}
124 			d1++;
125 			d2++;
126 		}
127 		/* next pair of labels. */
128 		lab1 = *d1++;
129 		lab2 = *d2++;
130 	}
131 	return 0;
132 }
133 
134 void
135 query_dname_tolower(uint8_t* dname)
136 {
137 	/* the dname is stored uncompressed */
138 	uint8_t labellen;
139 	labellen = *dname;
140 	while(labellen) {
141 		dname++;
142 		while(labellen--) {
143 			*dname = (uint8_t)tolower((unsigned char)*dname);
144 			dname++;
145 		}
146 		labellen = *dname;
147 	}
148 }
149 
150 void
151 pkt_dname_tolower(sldns_buffer* pkt, uint8_t* dname)
152 {
153 	uint8_t lablen;
154 	int count = 0;
155 	if(dname >= sldns_buffer_end(pkt))
156 		return;
157 	lablen = *dname++;
158 	while(lablen) {
159 		if(LABEL_IS_PTR(lablen)) {
160 			if((size_t)PTR_OFFSET(lablen, *dname)
161 				>= sldns_buffer_limit(pkt))
162 				return;
163 			dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
164 			lablen = *dname++;
165 			if(count++ > MAX_COMPRESS_PTRS)
166 				return;
167 			continue;
168 		}
169 		if(dname+lablen >= sldns_buffer_end(pkt))
170 			return;
171 		while(lablen--) {
172 			*dname = (uint8_t)tolower((unsigned char)*dname);
173 			dname++;
174 		}
175 		if(dname >= sldns_buffer_end(pkt))
176 			return;
177 		lablen = *dname++;
178 	}
179 }
180 
181 
182 size_t
183 pkt_dname_len(sldns_buffer* pkt)
184 {
185 	size_t len = 0;
186 	int ptrcount = 0;
187 	uint8_t labellen;
188 	size_t endpos = 0;
189 
190 	/* read dname and determine length */
191 	/* check compression pointers, loops, out of bounds */
192 	while(1) {
193 		/* read next label */
194 		if(sldns_buffer_remaining(pkt) < 1)
195 			return 0;
196 		labellen = sldns_buffer_read_u8(pkt);
197 		if(LABEL_IS_PTR(labellen)) {
198 			/* compression ptr */
199 			uint16_t ptr;
200 			if(sldns_buffer_remaining(pkt) < 1)
201 				return 0;
202 			ptr = PTR_OFFSET(labellen, sldns_buffer_read_u8(pkt));
203 			if(ptrcount++ > MAX_COMPRESS_PTRS)
204 				return 0; /* loop! */
205 			if(sldns_buffer_limit(pkt) <= ptr)
206 				return 0; /* out of bounds! */
207 			if(!endpos)
208 				endpos = sldns_buffer_position(pkt);
209 			sldns_buffer_set_position(pkt, ptr);
210 		} else {
211 			/* label contents */
212 			if(labellen > 0x3f)
213 				return 0; /* label too long */
214 			len += 1 + labellen;
215 			if(len > LDNS_MAX_DOMAINLEN)
216 				return 0;
217 			if(labellen == 0) {
218 				/* end of dname */
219 				break;
220 			}
221 			if(sldns_buffer_remaining(pkt) < labellen)
222 				return 0;
223 			sldns_buffer_skip(pkt, (ssize_t)labellen);
224 		}
225 	}
226 	if(endpos)
227 		sldns_buffer_set_position(pkt, endpos);
228 
229 	return len;
230 }
231 
232 int
233 dname_pkt_compare(sldns_buffer* pkt, uint8_t* d1, uint8_t* d2)
234 {
235 	uint8_t len1, len2;
236 	log_assert(pkt && d1 && d2);
237 	len1 = *d1++;
238 	len2 = *d2++;
239 	while( len1 != 0 || len2 != 0 ) {
240 		/* resolve ptrs */
241 		if(LABEL_IS_PTR(len1)) {
242 			d1 = sldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
243 			len1 = *d1++;
244 			continue;
245 		}
246 		if(LABEL_IS_PTR(len2)) {
247 			d2 = sldns_buffer_at(pkt, PTR_OFFSET(len2, *d2));
248 			len2 = *d2++;
249 			continue;
250 		}
251 		/* check label length */
252 		log_assert(len1 <= LDNS_MAX_LABELLEN);
253 		log_assert(len2 <= LDNS_MAX_LABELLEN);
254 		if(len1 != len2) {
255 			if(len1 < len2) return -1;
256 			return 1;
257 		}
258 		log_assert(len1 == len2 && len1 != 0);
259 		/* compare labels */
260 		while(len1--) {
261 			if(tolower((unsigned char)*d1) != tolower((unsigned char)*d2)) {
262 				if(tolower((unsigned char)*d1) < tolower((unsigned char)*d2))
263 					return -1;
264 				return 1;
265 			}
266 			d1++;
267 			d2++;
268 		}
269 		len1 = *d1++;
270 		len2 = *d2++;
271 	}
272 	return 0;
273 }
274 
275 hashvalue_type
276 dname_query_hash(uint8_t* dname, hashvalue_type h)
277 {
278 	uint8_t labuf[LDNS_MAX_LABELLEN+1];
279 	uint8_t lablen;
280 	int i;
281 
282 	/* preserve case of query, make hash label by label */
283 	lablen = *dname++;
284 	while(lablen) {
285 		log_assert(lablen <= LDNS_MAX_LABELLEN);
286 		labuf[0] = lablen;
287 		i=0;
288 		while(lablen--) {
289 			labuf[++i] = (uint8_t)tolower((unsigned char)*dname);
290 			dname++;
291 		}
292 		h = hashlittle(labuf, labuf[0] + 1, h);
293 		lablen = *dname++;
294 	}
295 
296 	return h;
297 }
298 
299 hashvalue_type
300 dname_pkt_hash(sldns_buffer* pkt, uint8_t* dname, hashvalue_type h)
301 {
302 	uint8_t labuf[LDNS_MAX_LABELLEN+1];
303 	uint8_t lablen;
304 	int i;
305 
306 	/* preserve case of query, make hash label by label */
307 	lablen = *dname++;
308 	while(lablen) {
309 		if(LABEL_IS_PTR(lablen)) {
310 			/* follow pointer */
311 			dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
312 			lablen = *dname++;
313 			continue;
314 		}
315 		log_assert(lablen <= LDNS_MAX_LABELLEN);
316 		labuf[0] = lablen;
317 		i=0;
318 		while(lablen--) {
319 			labuf[++i] = (uint8_t)tolower((unsigned char)*dname);
320 			dname++;
321 		}
322 		h = hashlittle(labuf, labuf[0] + 1, h);
323 		lablen = *dname++;
324 	}
325 
326 	return h;
327 }
328 
329 void dname_pkt_copy(sldns_buffer* pkt, uint8_t* to, uint8_t* dname)
330 {
331 	/* copy over the dname and decompress it at the same time */
332 	size_t comprcount = 0;
333 	size_t len = 0;
334 	uint8_t lablen;
335 	lablen = *dname++;
336 	while(lablen) {
337 		if(LABEL_IS_PTR(lablen)) {
338 			if(comprcount++ > MAX_COMPRESS_PTRS) {
339 				/* too many compression pointers */
340 				*to = 0; /* end the result prematurely */
341 				return;
342 			}
343 			/* follow pointer */
344 			dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
345 			lablen = *dname++;
346 			continue;
347 		}
348 		if(lablen > LDNS_MAX_LABELLEN) {
349 			*to = 0; /* end the result prematurely */
350 			return;
351 		}
352 		log_assert(lablen <= LDNS_MAX_LABELLEN);
353 		len += (size_t)lablen+1;
354 		if(len >= LDNS_MAX_DOMAINLEN) {
355 			*to = 0; /* end the result prematurely */
356 			log_err("bad dname in dname_pkt_copy");
357 			return;
358 		}
359 		*to++ = lablen;
360 		memmove(to, dname, lablen);
361 		dname += lablen;
362 		to += lablen;
363 		lablen = *dname++;
364 	}
365 	/* copy last \0 */
366 	*to = 0;
367 }
368 
369 void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname)
370 {
371 	uint8_t lablen;
372 	if(!out) out = stdout;
373 	if(!dname) return;
374 
375 	lablen = *dname++;
376 	if(!lablen)
377 		fputc('.', out);
378 	while(lablen) {
379 		if(LABEL_IS_PTR(lablen)) {
380 			/* follow pointer */
381 			if(!pkt) {
382 				fputs("??compressionptr??", out);
383 				return;
384 			}
385 			dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
386 			lablen = *dname++;
387 			continue;
388 		}
389 		if(lablen > LDNS_MAX_LABELLEN) {
390 			fputs("??extendedlabel??", out);
391 			return;
392 		}
393 		while(lablen--)
394 			fputc((int)*dname++, out);
395 		fputc('.', out);
396 		lablen = *dname++;
397 	}
398 }
399 
400 int
401 dname_count_labels(uint8_t* dname)
402 {
403 	uint8_t lablen;
404 	int labs = 1;
405 
406 	lablen = *dname++;
407 	while(lablen) {
408 		labs++;
409 		dname += lablen;
410 		lablen = *dname++;
411 	}
412 	return labs;
413 }
414 
415 int
416 dname_count_size_labels(uint8_t* dname, size_t* size)
417 {
418 	uint8_t lablen;
419 	int labs = 1;
420 	size_t sz = 1;
421 
422 	lablen = *dname++;
423 	while(lablen) {
424 		labs++;
425 		sz += lablen+1;
426 		dname += lablen;
427 		lablen = *dname++;
428 	}
429 	*size = sz;
430 	return labs;
431 }
432 
433 /**
434  * Compare labels in memory, lowercase while comparing.
435  * @param p1: label 1
436  * @param p2: label 2
437  * @param len: number of bytes to compare.
438  * @return: 0, -1, +1 comparison result.
439  */
440 static int
441 memlowercmp(uint8_t* p1, uint8_t* p2, uint8_t len)
442 {
443 	while(len--) {
444 		if(*p1 != *p2 && tolower((unsigned char)*p1) != tolower((unsigned char)*p2)) {
445 			if(tolower((unsigned char)*p1) < tolower((unsigned char)*p2))
446 				return -1;
447 			return 1;
448 		}
449 		p1++;
450 		p2++;
451 	}
452 	return 0;
453 }
454 
455 int
456 dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
457 {
458 	uint8_t len1, len2;
459 	int atlabel = labs1;
460 	int lastmlabs;
461 	int lastdiff = 0;
462 	/* first skip so that we compare same label. */
463 	if(labs1 > labs2) {
464 		while(atlabel > labs2) {
465 			len1 = *d1++;
466 			d1 += len1;
467 			atlabel--;
468 		}
469 		log_assert(atlabel == labs2);
470 	} else if(labs1 < labs2) {
471 		atlabel = labs2;
472 		while(atlabel > labs1) {
473 			len2 = *d2++;
474 			d2 += len2;
475 			atlabel--;
476 		}
477 		log_assert(atlabel == labs1);
478 	}
479 	lastmlabs = atlabel+1;
480 	/* now at same label in d1 and d2, atlabel */
481 	/* www.example.com.                  */
482 	/* 4   3       2  1   atlabel number */
483 	/* repeat until at root label (which is always the same) */
484 	while(atlabel > 1) {
485 		len1 = *d1++;
486 		len2 = *d2++;
487 		if(len1 != len2) {
488 			log_assert(len1 != 0 && len2 != 0);
489 			if(len1<len2)
490 				lastdiff = -1;
491 			else	lastdiff = 1;
492 			lastmlabs = atlabel;
493 			d1 += len1;
494 			d2 += len2;
495 		} else {
496 			/* memlowercmp is inlined here; or just like
497 			 * if((c=memlowercmp(d1, d2, len1)) != 0) {
498 			 *	lastdiff = c;
499 			 *	lastmlabs = atlabel; } apart from d1++,d2++ */
500 			while(len1) {
501 				if(*d1 != *d2 && tolower((unsigned char)*d1)
502 					!= tolower((unsigned char)*d2)) {
503 					if(tolower((unsigned char)*d1) <
504 						tolower((unsigned char)*d2)) {
505 						lastdiff = -1;
506 						lastmlabs = atlabel;
507 						d1 += len1;
508 						d2 += len1;
509 						break;
510 					}
511 					lastdiff = 1;
512 					lastmlabs = atlabel;
513 					d1 += len1;
514 					d2 += len1;
515 					break; /* out of memlowercmp */
516 				}
517 				d1++;
518 				d2++;
519 				len1--;
520 			}
521 		}
522 		atlabel--;
523 	}
524 	/* last difference atlabel number, so number of labels matching,
525 	 * at the right side, is one less. */
526 	*mlabs = lastmlabs-1;
527 	if(lastdiff == 0) {
528 		/* all labels compared were equal, check if one has more
529 		 * labels, so that example.com. > com. */
530 		if(labs1 > labs2)
531 			return 1;
532 		else if(labs1 < labs2)
533 			return -1;
534 	}
535 	return lastdiff;
536 }
537 
538 int
539 dname_lab_startswith(uint8_t* label, char* prefix, char** endptr)
540 {
541 	size_t plen = strlen(prefix);
542 	size_t orig_plen = plen;
543 	size_t lablen = (size_t)*label;
544 	if(plen > lablen)
545 		return 0;
546 	label++;
547 	while(plen--) {
548 		if(*prefix != tolower((unsigned char)*label)) {
549 			return 0;
550 		}
551 		prefix++; label++;
552 	}
553 	if(orig_plen < lablen)
554 		*endptr = (char *)label;
555 	else
556 		/* prefix length == label length */
557 		*endptr = NULL;
558 	return 1;
559 }
560 
561 int
562 dname_buffer_write(sldns_buffer* pkt, uint8_t* dname)
563 {
564 	uint8_t lablen;
565 
566 	if(sldns_buffer_remaining(pkt) < 1)
567 		return 0;
568 	lablen = *dname++;
569 	sldns_buffer_write_u8(pkt, lablen);
570 	while(lablen) {
571 		if(sldns_buffer_remaining(pkt) < (size_t)lablen+1)
572 			return 0;
573 		sldns_buffer_write(pkt, dname, lablen);
574 		dname += lablen;
575 		lablen = *dname++;
576 		sldns_buffer_write_u8(pkt, lablen);
577 	}
578 	return 1;
579 }
580 
581 void dname_str(uint8_t* dname, char* str)
582 {
583 	size_t len = 0;
584 	uint8_t lablen = 0;
585 	char* s = str;
586 	if(!dname || !*dname) {
587 		*s++ = '.';
588 		*s = 0;
589 		return;
590 	}
591 	lablen = *dname++;
592 	while(lablen) {
593 		if(lablen > LDNS_MAX_LABELLEN) {
594 			*s++ = '#';
595 			*s = 0;
596 			return;
597 		}
598 		len += lablen+1;
599 		if(len >= LDNS_MAX_DOMAINLEN-1) {
600 			*s++ = '&';
601 			*s = 0;
602 			return;
603 		}
604 		while(lablen--) {
605 			if(isalnum((unsigned char)*dname)
606 				|| *dname == '-' || *dname == '_'
607 				|| *dname == '*')
608 				*s++ = *(char*)dname++;
609 			else	{
610 				*s++ = '?';
611 				dname++;
612 			}
613 		}
614 		*s++ = '.';
615 		lablen = *dname++;
616 	}
617 	*s = 0;
618 }
619 
620 int
621 dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2)
622 {
623 	int m;
624 	/* check subdomain: d1: www.example.com. and d2: example.com. */
625 	if(labs2 >= labs1)
626 		return 0;
627 	if(dname_lab_cmp(d1, labs1, d2, labs2, &m) > 0) {
628 		/* subdomain if all labels match */
629 		return (m == labs2);
630 	}
631 	return 0;
632 }
633 
634 int
635 dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2)
636 {
637 	return dname_strict_subdomain(d1, dname_count_labels(d1), d2,
638 		dname_count_labels(d2));
639 }
640 
641 int
642 dname_subdomain_c(uint8_t* d1, uint8_t* d2)
643 {
644 	int m;
645 	/* check subdomain: d1: www.example.com. and d2: example.com. */
646 	/*  	or 	    d1: example.com. and d2: example.com. */
647 	int labs1 = dname_count_labels(d1);
648 	int labs2 = dname_count_labels(d2);
649 	if(labs2 > labs1)
650 		return 0;
651 	if(dname_lab_cmp(d1, labs1, d2, labs2, &m) < 0) {
652 		/* must have been example.com , www.example.com - wrong */
653 		/* or otherwise different dnames */
654 		return 0;
655 	}
656 	return (m == labs2);
657 }
658 
659 int
660 dname_is_root(uint8_t* dname)
661 {
662 	uint8_t len;
663 	log_assert(dname);
664 	len = dname[0];
665 	log_assert(!LABEL_IS_PTR(len));
666 	return (len == 0);
667 }
668 
669 void
670 dname_remove_label(uint8_t** dname, size_t* len)
671 {
672 	size_t lablen;
673 	log_assert(dname && *dname && len);
674 	lablen = (*dname)[0];
675 	log_assert(!LABEL_IS_PTR(lablen));
676 	log_assert(*len > lablen);
677 	if(lablen == 0)
678 		return; /* do not modify root label */
679 	*len -= lablen+1;
680 	*dname += lablen+1;
681 }
682 
683 void
684 dname_remove_labels(uint8_t** dname, size_t* len, int n)
685 {
686 	int i;
687 	for(i=0; i<n; i++)
688 		dname_remove_label(dname, len);
689 }
690 
691 int
692 dname_signame_label_count(uint8_t* dname)
693 {
694 	uint8_t lablen;
695 	int count = 0;
696 	if(!*dname)
697 		return 0;
698 	if(dname[0] == 1 && dname[1] == '*')
699 		dname += 2;
700 	lablen = dname[0];
701 	while(lablen) {
702 		count++;
703 		dname += lablen;
704 		dname += 1;
705 		lablen = dname[0];
706 	}
707 	return count;
708 }
709 
710 int
711 dname_is_wild(uint8_t* dname)
712 {
713 	return (dname[0] == 1 && dname[1] == '*');
714 }
715 
716 /**
717  * Compare labels in memory, lowercase while comparing.
718  * Returns canonical order for labels. If all is equal, the
719  * shortest is first.
720  *
721  * @param p1: label 1
722  * @param len1: length of label 1.
723  * @param p2: label 2
724  * @param len2: length of label 2.
725  * @return: 0, -1, +1 comparison result.
726  */
727 static int
728 memcanoncmp(uint8_t* p1, uint8_t len1, uint8_t* p2, uint8_t len2)
729 {
730 	uint8_t min = (len1<len2)?len1:len2;
731 	int c = memlowercmp(p1, p2, min);
732 	if(c != 0)
733 		return c;
734 	/* equal, see who is shortest */
735 	if(len1 < len2)
736 		return -1;
737 	if(len1 > len2)
738 		return 1;
739 	return 0;
740 }
741 
742 
743 int
744 dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
745 {
746 	/* like dname_lab_cmp, but with different label comparison,
747 	 * empty character sorts before \000.
748 	 * So   ylyly is before z. */
749 	uint8_t len1, len2;
750 	int atlabel = labs1;
751 	int lastmlabs;
752 	int lastdiff = 0;
753 	int c;
754 	/* first skip so that we compare same label. */
755 	if(labs1 > labs2) {
756 		while(atlabel > labs2) {
757 			len1 = *d1++;
758 			d1 += len1;
759 			atlabel--;
760 		}
761 		log_assert(atlabel == labs2);
762 	} else if(labs1 < labs2) {
763 		atlabel = labs2;
764 		while(atlabel > labs1) {
765 			len2 = *d2++;
766 			d2 += len2;
767 			atlabel--;
768 		}
769 		log_assert(atlabel == labs1);
770 	}
771 	lastmlabs = atlabel+1;
772 	/* now at same label in d1 and d2, atlabel */
773 	/* www.example.com.                  */
774 	/* 4   3       2  1   atlabel number */
775 	/* repeat until at root label (which is always the same) */
776 	while(atlabel > 1) {
777 		len1 = *d1++;
778 		len2 = *d2++;
779 
780 		if((c=memcanoncmp(d1, len1, d2, len2)) != 0) {
781 			if(c<0)
782 				lastdiff = -1;
783 			else	lastdiff = 1;
784 			lastmlabs = atlabel;
785 		}
786 
787 		d1 += len1;
788 		d2 += len2;
789 		atlabel--;
790 	}
791 	/* last difference atlabel number, so number of labels matching,
792 	 * at the right side, is one less. */
793 	*mlabs = lastmlabs-1;
794 	if(lastdiff == 0) {
795 		/* all labels compared were equal, check if one has more
796 		 * labels, so that example.com. > com. */
797 		if(labs1 > labs2)
798 			return 1;
799 		else if(labs1 < labs2)
800 			return -1;
801 	}
802 	return lastdiff;
803 }
804 
805 int
806 dname_canonical_compare(uint8_t* d1, uint8_t* d2)
807 {
808 	int labs1, labs2, m;
809 	labs1 = dname_count_labels(d1);
810 	labs2 = dname_count_labels(d2);
811 	return dname_canon_lab_cmp(d1, labs1, d2, labs2, &m);
812 }
813 
814 uint8_t* dname_get_shared_topdomain(uint8_t* d1, uint8_t* d2)
815 {
816 	int labs1, labs2, m;
817 	size_t len = LDNS_MAX_DOMAINLEN;
818 	labs1 = dname_count_labels(d1);
819 	labs2 = dname_count_labels(d2);
820 	(void)dname_lab_cmp(d1, labs1, d2, labs2, &m);
821 	dname_remove_labels(&d1, &len, labs1-m);
822 	return d1;
823 }
824