xref: /freebsd/contrib/unbound/util/data/dname.c (revision 1fb9c5ffe25fcba0857c4fdf6ed19ca5bf6bc858)
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
query_dname_len(sldns_buffer * query)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
dname_valid(uint8_t * dname,size_t maxlen)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
query_dname_compare(register uint8_t * d1,register uint8_t * d2)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
query_dname_tolower(uint8_t * dname)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
pkt_dname_tolower(sldns_buffer * pkt,uint8_t * dname)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
pkt_dname_len(sldns_buffer * pkt)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 			goto fail;
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 				goto fail;
202 			ptr = PTR_OFFSET(labellen, sldns_buffer_read_u8(pkt));
203 			if(ptrcount++ > MAX_COMPRESS_PTRS)
204 				goto fail; /* loop! */
205 			if(sldns_buffer_limit(pkt) <= ptr)
206 				goto fail; /* 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 				goto fail; /* label too long */
214 			len += 1 + labellen;
215 			if(len > LDNS_MAX_DOMAINLEN)
216 				goto fail;
217 			if(labellen == 0) {
218 				/* end of dname */
219 				break;
220 			}
221 			if(sldns_buffer_remaining(pkt) < labellen)
222 				goto fail;
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 fail:
231 	/* Restore the position on failure too: callers (rdata_copy) compute
232 	 * the consumed field length from the buffer position and must not
233 	 * see a partial walk of a name that failed to parse. */
234 	if(endpos)
235 		sldns_buffer_set_position(pkt, endpos);
236 	return 0;
237 }
238 
239 int
dname_pkt_compare(sldns_buffer * pkt,uint8_t * d1,uint8_t * d2)240 dname_pkt_compare(sldns_buffer* pkt, uint8_t* d1, uint8_t* d2)
241 {
242 	uint8_t len1, len2;
243 	int count1 = 0, count2 = 0;
244 	log_assert(pkt && d1 && d2);
245 	len1 = *d1++;
246 	len2 = *d2++;
247 	while( len1 != 0 || len2 != 0 ) {
248 		/* resolve ptrs */
249 		if(LABEL_IS_PTR(len1)) {
250 			if((size_t)PTR_OFFSET(len1, *d1)
251 				>= sldns_buffer_limit(pkt))
252 				return -1;
253 			if(count1++ > MAX_COMPRESS_PTRS)
254 				return -1;
255 			d1 = sldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
256 			len1 = *d1++;
257 			continue;
258 		}
259 		if(LABEL_IS_PTR(len2)) {
260 			if((size_t)PTR_OFFSET(len2, *d2)
261 				>= sldns_buffer_limit(pkt))
262 				return 1;
263 			if(count2++ > MAX_COMPRESS_PTRS)
264 				return 1;
265 			d2 = sldns_buffer_at(pkt, PTR_OFFSET(len2, *d2));
266 			len2 = *d2++;
267 			continue;
268 		}
269 		/* check label length */
270 		log_assert(len1 <= LDNS_MAX_LABELLEN);
271 		log_assert(len2 <= LDNS_MAX_LABELLEN);
272 		if(len1 != len2) {
273 			if(len1 < len2) return -1;
274 			return 1;
275 		}
276 		log_assert(len1 == len2 && len1 != 0);
277 		/* compare labels */
278 		while(len1--) {
279 			if(tolower((unsigned char)*d1) != tolower((unsigned char)*d2)) {
280 				if(tolower((unsigned char)*d1) < tolower((unsigned char)*d2))
281 					return -1;
282 				return 1;
283 			}
284 			d1++;
285 			d2++;
286 		}
287 		len1 = *d1++;
288 		len2 = *d2++;
289 	}
290 	return 0;
291 }
292 
293 hashvalue_type
dname_query_hash(uint8_t * dname,hashvalue_type h)294 dname_query_hash(uint8_t* dname, hashvalue_type h)
295 {
296 	uint8_t labuf[LDNS_MAX_LABELLEN+1];
297 	uint8_t lablen;
298 	int i;
299 
300 	/* preserve case of query, make hash label by label */
301 	lablen = *dname++;
302 	while(lablen) {
303 		log_assert(lablen <= LDNS_MAX_LABELLEN);
304 		labuf[0] = lablen;
305 		i=0;
306 		while(lablen--) {
307 			labuf[++i] = (uint8_t)tolower((unsigned char)*dname);
308 			dname++;
309 		}
310 		h = hashlittle(labuf, labuf[0] + 1, h);
311 		lablen = *dname++;
312 	}
313 
314 	return h;
315 }
316 
317 hashvalue_type
dname_pkt_hash(sldns_buffer * pkt,uint8_t * dname,hashvalue_type h)318 dname_pkt_hash(sldns_buffer* pkt, uint8_t* dname, hashvalue_type h)
319 {
320 	uint8_t labuf[LDNS_MAX_LABELLEN+1];
321 	uint8_t lablen;
322 	int i;
323 	int count = 0;
324 
325 	/* preserve case of query, make hash label by label */
326 	lablen = *dname++;
327 	while(lablen) {
328 		if(LABEL_IS_PTR(lablen)) {
329 			/* follow pointer */
330 			if((size_t)PTR_OFFSET(lablen, *dname)
331 				>= sldns_buffer_limit(pkt))
332 				return h;
333 			if(count++ > MAX_COMPRESS_PTRS)
334 				return h;
335 			dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
336 			lablen = *dname++;
337 			continue;
338 		}
339 		log_assert(lablen <= LDNS_MAX_LABELLEN);
340 		labuf[0] = lablen;
341 		i=0;
342 		while(lablen--) {
343 			labuf[++i] = (uint8_t)tolower((unsigned char)*dname);
344 			dname++;
345 		}
346 		h = hashlittle(labuf, labuf[0] + 1, h);
347 		lablen = *dname++;
348 	}
349 
350 	return h;
351 }
352 
dname_pkt_copy(sldns_buffer * pkt,uint8_t * to,uint8_t * dname)353 void dname_pkt_copy(sldns_buffer* pkt, uint8_t* to, uint8_t* dname)
354 {
355 	/* copy over the dname and decompress it at the same time */
356 	size_t comprcount = 0;
357 	size_t len = 0;
358 	uint8_t lablen;
359 	lablen = *dname++;
360 	while(lablen) {
361 		if(LABEL_IS_PTR(lablen)) {
362 			if(comprcount++ > MAX_COMPRESS_PTRS) {
363 				/* too many compression pointers */
364 				*to = 0; /* end the result prematurely */
365 				return;
366 			}
367 			/* follow pointer */
368 			if((size_t)PTR_OFFSET(lablen, *dname)
369 				>= sldns_buffer_limit(pkt))
370 				return;
371 			dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
372 			lablen = *dname++;
373 			continue;
374 		}
375 		if(lablen > LDNS_MAX_LABELLEN) {
376 			*to = 0; /* end the result prematurely */
377 			return;
378 		}
379 		log_assert(lablen <= LDNS_MAX_LABELLEN);
380 		len += (size_t)lablen+1;
381 		if(len >= LDNS_MAX_DOMAINLEN) {
382 			*to = 0; /* end the result prematurely */
383 			log_err("bad dname in dname_pkt_copy");
384 			return;
385 		}
386 		*to++ = lablen;
387 		memmove(to, dname, lablen);
388 		dname += lablen;
389 		to += lablen;
390 		lablen = *dname++;
391 	}
392 	/* copy last \0 */
393 	*to = 0;
394 }
395 
dname_print(FILE * out,struct sldns_buffer * pkt,uint8_t * dname)396 void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname)
397 {
398 	uint8_t lablen;
399 	int count = 0;
400 	if(!out) out = stdout;
401 	if(!dname) return;
402 
403 	lablen = *dname++;
404 	if(!lablen)
405 		fputc('.', out);
406 	while(lablen) {
407 		if(LABEL_IS_PTR(lablen)) {
408 			/* follow pointer */
409 			if(!pkt) {
410 				fputs("??compressionptr??", out);
411 				return;
412 			}
413 			if((size_t)PTR_OFFSET(lablen, *dname)
414 				>= sldns_buffer_limit(pkt)) {
415 				fputs("??compressionptr??", out);
416 				return;
417 			}
418 			if(count++ > MAX_COMPRESS_PTRS) {
419 				fputs("??compressionptr??", out);
420 				return;
421 			}
422 			dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
423 			lablen = *dname++;
424 			continue;
425 		}
426 		if(lablen > LDNS_MAX_LABELLEN) {
427 			fputs("??extendedlabel??", out);
428 			return;
429 		}
430 		while(lablen--)
431 			fputc((int)*dname++, out);
432 		fputc('.', out);
433 		lablen = *dname++;
434 	}
435 }
436 
437 int
dname_count_labels(uint8_t * dname)438 dname_count_labels(uint8_t* dname)
439 {
440 	uint8_t lablen;
441 	int labs = 1;
442 
443 	lablen = *dname++;
444 	while(lablen) {
445 		labs++;
446 		dname += lablen;
447 		lablen = *dname++;
448 	}
449 	return labs;
450 }
451 
452 int
dname_count_size_labels(uint8_t * dname,size_t * size)453 dname_count_size_labels(uint8_t* dname, size_t* size)
454 {
455 	uint8_t lablen;
456 	int labs = 1;
457 	size_t sz = 1;
458 
459 	lablen = *dname++;
460 	while(lablen) {
461 		labs++;
462 		sz += lablen+1;
463 		dname += lablen;
464 		lablen = *dname++;
465 	}
466 	*size = sz;
467 	return labs;
468 }
469 
470 /**
471  * Compare labels in memory, lowercase while comparing.
472  * @param p1: label 1
473  * @param p2: label 2
474  * @param len: number of bytes to compare.
475  * @return: 0, -1, +1 comparison result.
476  */
477 static int
memlowercmp(uint8_t * p1,uint8_t * p2,uint8_t len)478 memlowercmp(uint8_t* p1, uint8_t* p2, uint8_t len)
479 {
480 	while(len--) {
481 		if(*p1 != *p2 && tolower((unsigned char)*p1) != tolower((unsigned char)*p2)) {
482 			if(tolower((unsigned char)*p1) < tolower((unsigned char)*p2))
483 				return -1;
484 			return 1;
485 		}
486 		p1++;
487 		p2++;
488 	}
489 	return 0;
490 }
491 
492 int
dname_lab_cmp(uint8_t * d1,int labs1,uint8_t * d2,int labs2,int * mlabs)493 dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
494 {
495 	uint8_t len1, len2;
496 	int atlabel = labs1;
497 	int lastmlabs;
498 	int lastdiff = 0;
499 	/* first skip so that we compare same label. */
500 	if(labs1 > labs2) {
501 		while(atlabel > labs2) {
502 			len1 = *d1++;
503 			d1 += len1;
504 			atlabel--;
505 		}
506 		log_assert(atlabel == labs2);
507 	} else if(labs1 < labs2) {
508 		atlabel = labs2;
509 		while(atlabel > labs1) {
510 			len2 = *d2++;
511 			d2 += len2;
512 			atlabel--;
513 		}
514 		log_assert(atlabel == labs1);
515 	}
516 	lastmlabs = atlabel+1;
517 	/* now at same label in d1 and d2, atlabel */
518 	/* www.example.com.                  */
519 	/* 4   3       2  1   atlabel number */
520 	/* repeat until at root label (which is always the same) */
521 	while(atlabel > 1) {
522 		len1 = *d1++;
523 		len2 = *d2++;
524 		if(len1 != len2) {
525 			log_assert(len1 != 0 && len2 != 0);
526 			if(len1<len2)
527 				lastdiff = -1;
528 			else	lastdiff = 1;
529 			lastmlabs = atlabel;
530 			d1 += len1;
531 			d2 += len2;
532 		} else {
533 			/* memlowercmp is inlined here; or just like
534 			 * if((c=memlowercmp(d1, d2, len1)) != 0) {
535 			 *	lastdiff = c;
536 			 *	lastmlabs = atlabel; } apart from d1++,d2++ */
537 			while(len1) {
538 				if(*d1 != *d2 && tolower((unsigned char)*d1)
539 					!= tolower((unsigned char)*d2)) {
540 					if(tolower((unsigned char)*d1) <
541 						tolower((unsigned char)*d2)) {
542 						lastdiff = -1;
543 						lastmlabs = atlabel;
544 						d1 += len1;
545 						d2 += len1;
546 						break;
547 					}
548 					lastdiff = 1;
549 					lastmlabs = atlabel;
550 					d1 += len1;
551 					d2 += len1;
552 					break; /* out of memlowercmp */
553 				}
554 				d1++;
555 				d2++;
556 				len1--;
557 			}
558 		}
559 		atlabel--;
560 	}
561 	/* last difference atlabel number, so number of labels matching,
562 	 * at the right side, is one less. */
563 	*mlabs = lastmlabs-1;
564 	if(lastdiff == 0) {
565 		/* all labels compared were equal, check if one has more
566 		 * labels, so that example.com. > com. */
567 		if(labs1 > labs2)
568 			return 1;
569 		else if(labs1 < labs2)
570 			return -1;
571 	}
572 	return lastdiff;
573 }
574 
575 int
dname_lab_startswith(uint8_t * label,char * prefix,char ** endptr)576 dname_lab_startswith(uint8_t* label, char* prefix, char** endptr)
577 {
578 	size_t plen = strlen(prefix);
579 	size_t orig_plen = plen;
580 	size_t lablen = (size_t)*label;
581 	if(plen > lablen)
582 		return 0;
583 	label++;
584 	while(plen--) {
585 		if(*prefix != tolower((unsigned char)*label)) {
586 			return 0;
587 		}
588 		prefix++; label++;
589 	}
590 	if(orig_plen < lablen)
591 		*endptr = (char *)label;
592 	else
593 		/* prefix length == label length */
594 		*endptr = NULL;
595 	return 1;
596 }
597 
598 int
dname_has_label(uint8_t * dname,size_t dnamelen,uint8_t * label)599 dname_has_label(uint8_t* dname, size_t dnamelen, uint8_t* label)
600 {
601 	size_t len;
602 
603 	/* 1 byte needed for the label length */
604 	if(dnamelen < 1)
605 		return 0;
606 
607 	len = *dname;
608 	while(len <= dnamelen) {
609 		if(!(*dname)) {
610 			if(*dname == *label)
611 				return 1; /* empty label match */
612 			/* termination label found, stop iterating */
613 			return 0;
614 		}
615 		if(*dname == *label && *label &&
616 			memlowercmp(dname+1, label+1, *dname) == 0)
617 			return 1;
618 		len += *dname;
619 		dname += *dname;
620 		dname++;
621 		len++;
622 	}
623 	return 0;
624 }
625 
626 int
dname_buffer_write(sldns_buffer * pkt,uint8_t * dname)627 dname_buffer_write(sldns_buffer* pkt, uint8_t* dname)
628 {
629 	uint8_t lablen;
630 
631 	if(sldns_buffer_remaining(pkt) < 1)
632 		return 0;
633 	lablen = *dname++;
634 	sldns_buffer_write_u8(pkt, lablen);
635 	while(lablen) {
636 		if(sldns_buffer_remaining(pkt) < (size_t)lablen+1)
637 			return 0;
638 		sldns_buffer_write(pkt, dname, lablen);
639 		dname += lablen;
640 		lablen = *dname++;
641 		sldns_buffer_write_u8(pkt, lablen);
642 	}
643 	return 1;
644 }
645 
dname_str(uint8_t * dname,char * str)646 void dname_str(uint8_t* dname, char* str)
647 {
648 	size_t len = 0;
649 	uint8_t lablen = 0;
650 	char* s = str;
651 	if(!dname || !*dname) {
652 		*s++ = '.';
653 		*s = 0;
654 		return;
655 	}
656 	lablen = *dname++;
657 	while(lablen) {
658 		len += lablen+1;
659 		if(len >= LDNS_MAX_DOMAINLEN) {
660 			if ((s-str) >= (LDNS_MAX_DOMAINLEN-1))
661 				s = str + LDNS_MAX_DOMAINLEN - 2;
662 			*s++ = '&';
663 			*s = 0;
664 			return;
665 		}
666 		if(lablen > LDNS_MAX_LABELLEN) {
667 			*s++ = '#';
668 			*s = 0;
669 			return;
670 		}
671 		while(lablen--) {
672 			if(isalnum((unsigned char)*dname)
673 				|| *dname == '-' || *dname == '_'
674 				|| *dname == '*')
675 				*s++ = *(char*)dname++;
676 			else	{
677 				*s++ = '?';
678 				dname++;
679 			}
680 		}
681 		*s++ = '.';
682 		lablen = *dname++;
683 	}
684 	*s = 0;
685 }
686 
687 int
dname_strict_subdomain(uint8_t * d1,int labs1,uint8_t * d2,int labs2)688 dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2)
689 {
690 	int m;
691 	/* check subdomain: d1: www.example.com. and d2: example.com. */
692 	if(labs2 >= labs1)
693 		return 0;
694 	if(dname_lab_cmp(d1, labs1, d2, labs2, &m) > 0) {
695 		/* subdomain if all labels match */
696 		return (m == labs2);
697 	}
698 	return 0;
699 }
700 
701 int
dname_strict_subdomain_c(uint8_t * d1,uint8_t * d2)702 dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2)
703 {
704 	return dname_strict_subdomain(d1, dname_count_labels(d1), d2,
705 		dname_count_labels(d2));
706 }
707 
708 int
dname_subdomain_c(uint8_t * d1,uint8_t * d2)709 dname_subdomain_c(uint8_t* d1, uint8_t* d2)
710 {
711 	int m;
712 	/* check subdomain: d1: www.example.com. and d2: example.com. */
713 	/*  	or 	    d1: example.com. and d2: example.com. */
714 	int labs1 = dname_count_labels(d1);
715 	int labs2 = dname_count_labels(d2);
716 	if(labs2 > labs1)
717 		return 0;
718 	if(dname_lab_cmp(d1, labs1, d2, labs2, &m) < 0) {
719 		/* must have been example.com , www.example.com - wrong */
720 		/* or otherwise different dnames */
721 		return 0;
722 	}
723 	return (m == labs2);
724 }
725 
726 int
dname_is_root(uint8_t * dname)727 dname_is_root(uint8_t* dname)
728 {
729 	uint8_t len;
730 	log_assert(dname);
731 	len = dname[0];
732 	log_assert(!LABEL_IS_PTR(len));
733 	return (len == 0);
734 }
735 
736 void
dname_remove_label(uint8_t ** dname,size_t * len)737 dname_remove_label(uint8_t** dname, size_t* len)
738 {
739 	size_t lablen;
740 	log_assert(dname && *dname && len);
741 	lablen = (*dname)[0];
742 	log_assert(!LABEL_IS_PTR(lablen));
743 	log_assert(*len > lablen);
744 	if(lablen == 0)
745 		return; /* do not modify root label */
746 	*len -= lablen+1;
747 	*dname += lablen+1;
748 }
749 
750 int
dname_remove_label_limit_len(uint8_t ** dname,size_t * len,size_t lenlimit)751 dname_remove_label_limit_len(uint8_t** dname, size_t* len, size_t lenlimit)
752 {
753 	size_t lablen;
754 	log_assert(dname && *dname && len);
755 	lablen = (*dname)[0];
756 	log_assert(!LABEL_IS_PTR(lablen));
757 	log_assert(*len > lablen);
758 	if(lablen == 0)
759 		return 0; /* do not modify root label */
760 	if(*len - (lablen + 1) < lenlimit) return 0;
761 	*len -= lablen+1;
762 	*dname += lablen+1;
763 	return 1;
764 }
765 
766 void
dname_remove_labels(uint8_t ** dname,size_t * len,int n)767 dname_remove_labels(uint8_t** dname, size_t* len, int n)
768 {
769 	int i;
770 	for(i=0; i<n; i++)
771 		dname_remove_label(dname, len);
772 }
773 
774 int
dname_signame_label_count(uint8_t * dname)775 dname_signame_label_count(uint8_t* dname)
776 {
777 	uint8_t lablen;
778 	int count = 0;
779 	if(!*dname)
780 		return 0;
781 	if(dname[0] == 1 && dname[1] == '*')
782 		dname += 2;
783 	lablen = dname[0];
784 	while(lablen) {
785 		count++;
786 		dname += lablen;
787 		dname += 1;
788 		lablen = dname[0];
789 	}
790 	return count;
791 }
792 
793 int
dname_is_wild(uint8_t * dname)794 dname_is_wild(uint8_t* dname)
795 {
796 	return (dname[0] == 1 && dname[1] == '*');
797 }
798 
799 /**
800  * Compare labels in memory, lowercase while comparing.
801  * Returns canonical order for labels. If all is equal, the
802  * shortest is first.
803  *
804  * @param p1: label 1
805  * @param len1: length of label 1.
806  * @param p2: label 2
807  * @param len2: length of label 2.
808  * @return: 0, -1, +1 comparison result.
809  */
810 static int
memcanoncmp(uint8_t * p1,uint8_t len1,uint8_t * p2,uint8_t len2)811 memcanoncmp(uint8_t* p1, uint8_t len1, uint8_t* p2, uint8_t len2)
812 {
813 	uint8_t min = (len1<len2)?len1:len2;
814 	int c = memlowercmp(p1, p2, min);
815 	if(c != 0)
816 		return c;
817 	/* equal, see who is shortest */
818 	if(len1 < len2)
819 		return -1;
820 	if(len1 > len2)
821 		return 1;
822 	return 0;
823 }
824 
825 
826 int
dname_canon_lab_cmp(uint8_t * d1,int labs1,uint8_t * d2,int labs2,int * mlabs)827 dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
828 {
829 	/* like dname_lab_cmp, but with different label comparison,
830 	 * empty character sorts before \000.
831 	 * So   ylyly is before z. */
832 	uint8_t len1, len2;
833 	int atlabel = labs1;
834 	int lastmlabs;
835 	int lastdiff = 0;
836 	int c;
837 	/* first skip so that we compare same label. */
838 	if(labs1 > labs2) {
839 		while(atlabel > labs2) {
840 			len1 = *d1++;
841 			d1 += len1;
842 			atlabel--;
843 		}
844 		log_assert(atlabel == labs2);
845 	} else if(labs1 < labs2) {
846 		atlabel = labs2;
847 		while(atlabel > labs1) {
848 			len2 = *d2++;
849 			d2 += len2;
850 			atlabel--;
851 		}
852 		log_assert(atlabel == labs1);
853 	}
854 	lastmlabs = atlabel+1;
855 	/* now at same label in d1 and d2, atlabel */
856 	/* www.example.com.                  */
857 	/* 4   3       2  1   atlabel number */
858 	/* repeat until at root label (which is always the same) */
859 	while(atlabel > 1) {
860 		len1 = *d1++;
861 		len2 = *d2++;
862 
863 		if((c=memcanoncmp(d1, len1, d2, len2)) != 0) {
864 			if(c<0)
865 				lastdiff = -1;
866 			else	lastdiff = 1;
867 			lastmlabs = atlabel;
868 		}
869 
870 		d1 += len1;
871 		d2 += len2;
872 		atlabel--;
873 	}
874 	/* last difference atlabel number, so number of labels matching,
875 	 * at the right side, is one less. */
876 	*mlabs = lastmlabs-1;
877 	if(lastdiff == 0) {
878 		/* all labels compared were equal, check if one has more
879 		 * labels, so that example.com. > com. */
880 		if(labs1 > labs2)
881 			return 1;
882 		else if(labs1 < labs2)
883 			return -1;
884 	}
885 	return lastdiff;
886 }
887 
888 int
dname_canonical_compare(uint8_t * d1,uint8_t * d2)889 dname_canonical_compare(uint8_t* d1, uint8_t* d2)
890 {
891 	int labs1, labs2, m;
892 	labs1 = dname_count_labels(d1);
893 	labs2 = dname_count_labels(d2);
894 	return dname_canon_lab_cmp(d1, labs1, d2, labs2, &m);
895 }
896 
dname_get_shared_topdomain(uint8_t * d1,uint8_t * d2)897 uint8_t* dname_get_shared_topdomain(uint8_t* d1, uint8_t* d2)
898 {
899 	int labs1, labs2, m;
900 	size_t len = LDNS_MAX_DOMAINLEN;
901 	labs1 = dname_count_labels(d1);
902 	labs2 = dname_count_labels(d2);
903 	(void)dname_lab_cmp(d1, labs1, d2, labs2, &m);
904 	dname_remove_labels(&d1, &len, labs1-m);
905 	return d1;
906 }
907