2 * util/data/dname.h - domain name handling
4 * Copyright (c) 2007, NLnet Labs. All rights reserved.
6 * This software is open source.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
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.
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.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
39 * This file contains domain name handling functions.
44 #include "util/data/dname.h"
45 #include "util/data/msgparse.h"
47 #include "util/storage/lookup3.h"
49 /* determine length of a dname in buffer, no compression pointers allowed */
51 query_dname_len(ldns_buffer* query)
56 if(ldns_buffer_remaining(query) < 1)
57 return 0; /* parse error, need label len */
58 labellen = ldns_buffer_read_u8(query);
60 return 0; /* no compression allowed in queries */
62 if(len > LDNS_MAX_DOMAINLEN)
63 return 0; /* too long */
66 if(ldns_buffer_remaining(query) < labellen)
67 return 0; /* parse error, need content */
68 ldns_buffer_skip(query, (ssize_t)labellen);
73 dname_valid(uint8_t* dname, size_t maxlen)
80 return 0; /* no compression ptrs allowed */
82 if(len >= LDNS_MAX_DOMAINLEN)
83 return 0; /* too long */
85 return 0; /* does not fit in memory allocation */
91 return 0; /* does not fit in memory allocation */
95 /** compare uncompressed, noncanonical, registers are hints for speed */
97 query_dname_compare(register uint8_t* d1, register uint8_t* d2)
99 register uint8_t lab1, lab2;
100 log_assert(d1 && d2);
103 while( lab1 != 0 || lab2 != 0 ) {
104 /* compare label length */
105 /* if one dname ends, it has labellength 0 */
111 log_assert(lab1 == lab2 && lab1 != 0);
112 /* compare lowercased labels. */
114 /* compare bytes first for speed */
116 tolower((int)*d1) != tolower((int)*d2)) {
117 if(tolower((int)*d1) < tolower((int)*d2))
124 /* next pair of labels. */
132 query_dname_tolower(uint8_t* dname)
134 /* the dname is stored uncompressed */
140 *dname = (uint8_t)tolower((int)*dname);
148 pkt_dname_tolower(ldns_buffer* pkt, uint8_t* dname)
152 if(dname >= ldns_buffer_end(pkt))
156 if(LABEL_IS_PTR(lablen)) {
157 if((size_t)PTR_OFFSET(lablen, *dname)
158 >= ldns_buffer_limit(pkt))
160 dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
162 if(count++ > MAX_COMPRESS_PTRS)
166 if(dname+lablen >= ldns_buffer_end(pkt))
169 *dname = (uint8_t)tolower((int)*dname);
172 if(dname >= ldns_buffer_end(pkt))
180 pkt_dname_len(ldns_buffer* pkt)
187 /* read dname and determine length */
188 /* check compression pointers, loops, out of bounds */
190 /* read next label */
191 if(ldns_buffer_remaining(pkt) < 1)
193 labellen = ldns_buffer_read_u8(pkt);
194 if(LABEL_IS_PTR(labellen)) {
195 /* compression ptr */
197 if(ldns_buffer_remaining(pkt) < 1)
199 ptr = PTR_OFFSET(labellen, ldns_buffer_read_u8(pkt));
200 if(ptrcount++ > MAX_COMPRESS_PTRS)
201 return 0; /* loop! */
202 if(ldns_buffer_limit(pkt) <= ptr)
203 return 0; /* out of bounds! */
205 endpos = ldns_buffer_position(pkt);
206 ldns_buffer_set_position(pkt, ptr);
210 return 0; /* label too long */
212 if(len > LDNS_MAX_DOMAINLEN)
218 if(ldns_buffer_remaining(pkt) < labellen)
220 ldns_buffer_skip(pkt, (ssize_t)labellen);
224 ldns_buffer_set_position(pkt, endpos);
230 dname_pkt_compare(ldns_buffer* pkt, uint8_t* d1, uint8_t* d2)
233 log_assert(pkt && d1 && d2);
236 while( len1 != 0 || len2 != 0 ) {
238 if(LABEL_IS_PTR(len1)) {
239 d1 = ldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
243 if(LABEL_IS_PTR(len2)) {
244 d2 = ldns_buffer_at(pkt, PTR_OFFSET(len2, *d2));
248 /* check label length */
249 log_assert(len1 <= LDNS_MAX_LABELLEN);
250 log_assert(len2 <= LDNS_MAX_LABELLEN);
252 if(len1 < len2) return -1;
255 log_assert(len1 == len2 && len1 != 0);
258 if(tolower((int)*d1++) != tolower((int)*d2++)) {
259 if(tolower((int)d1[-1]) < tolower((int)d2[-1]))
271 dname_query_hash(uint8_t* dname, hashvalue_t h)
273 uint8_t labuf[LDNS_MAX_LABELLEN+1];
277 /* preserve case of query, make hash label by label */
280 log_assert(lablen <= LDNS_MAX_LABELLEN);
284 labuf[++i] = (uint8_t)tolower((int)*dname++);
285 h = hashlittle(labuf, labuf[0] + 1, h);
293 dname_pkt_hash(ldns_buffer* pkt, uint8_t* dname, hashvalue_t h)
295 uint8_t labuf[LDNS_MAX_LABELLEN+1];
299 /* preserve case of query, make hash label by label */
302 if(LABEL_IS_PTR(lablen)) {
304 dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
308 log_assert(lablen <= LDNS_MAX_LABELLEN);
312 labuf[++i] = (uint8_t)tolower((int)*dname++);
313 h = hashlittle(labuf, labuf[0] + 1, h);
320 void dname_pkt_copy(ldns_buffer* pkt, uint8_t* to, uint8_t* dname)
322 /* copy over the dname and decompress it at the same time */
327 if(LABEL_IS_PTR(lablen)) {
329 dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
333 log_assert(lablen <= LDNS_MAX_LABELLEN);
334 len += (size_t)lablen+1;
335 if(len >= LDNS_MAX_DOMAINLEN) {
336 *to = 0; /* end the result prematurely */
337 log_err("bad dname in dname_pkt_copy");
341 memmove(to, dname, lablen);
350 void dname_print(FILE* out, ldns_buffer* pkt, uint8_t* dname)
353 if(!out) out = stdout;
360 if(LABEL_IS_PTR(lablen)) {
363 fputs("??compressionptr??", out);
366 dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
370 if(lablen > LDNS_MAX_LABELLEN) {
371 fputs("??extendedlabel??", out);
375 fputc((int)*dname++, out);
382 dname_count_labels(uint8_t* dname)
397 dname_count_size_labels(uint8_t* dname, size_t* size)
415 * Compare labels in memory, lowercase while comparing.
418 * @param len: number of bytes to compare.
419 * @return: 0, -1, +1 comparison result.
422 memlowercmp(uint8_t* p1, uint8_t* p2, uint8_t len)
425 if(*p1 != *p2 && tolower((int)*p1) != tolower((int)*p2)) {
426 if(tolower((int)*p1) < tolower((int)*p2))
437 dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
443 /* first skip so that we compare same label. */
445 while(atlabel > labs2) {
450 log_assert(atlabel == labs2);
451 } else if(labs1 < labs2) {
453 while(atlabel > labs1) {
458 log_assert(atlabel == labs1);
460 lastmlabs = atlabel+1;
461 /* now at same label in d1 and d2, atlabel */
462 /* www.example.com. */
463 /* 4 3 2 1 atlabel number */
464 /* repeat until at root label (which is always the same) */
469 log_assert(len1 != 0 && len2 != 0);
477 /* memlowercmp is inlined here; or just like
478 * if((c=memlowercmp(d1, d2, len1)) != 0) {
480 * lastmlabs = atlabel; } apart from d1++,d2++ */
482 if(*d1 != *d2 && tolower((int)*d1)
483 != tolower((int)*d2)) {
484 if(tolower((int)*d1) <
496 break; /* out of memlowercmp */
505 /* last difference atlabel number, so number of labels matching,
506 * at the right side, is one less. */
507 *mlabs = lastmlabs-1;
509 /* all labels compared were equal, check if one has more
510 * labels, so that example.com. > com. */
513 else if(labs1 < labs2)
520 dname_buffer_write(ldns_buffer* pkt, uint8_t* dname)
524 if(ldns_buffer_remaining(pkt) < 1)
527 ldns_buffer_write_u8(pkt, lablen);
529 if(ldns_buffer_remaining(pkt) < (size_t)lablen+1)
531 ldns_buffer_write(pkt, dname, lablen);
534 ldns_buffer_write_u8(pkt, lablen);
539 void dname_str(uint8_t* dname, char* str)
544 if(!dname || !*dname) {
551 if(lablen > LDNS_MAX_LABELLEN) {
557 if(len >= LDNS_MAX_DOMAINLEN-1) {
563 if(isalnum((int)*dname)
564 || *dname == '-' || *dname == '_'
566 *s++ = *(char*)dname++;
579 dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2)
582 /* check subdomain: d1: www.example.com. and d2: example.com. */
585 if(dname_lab_cmp(d1, labs1, d2, labs2, &m) > 0) {
586 /* subdomain if all labels match */
593 dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2)
595 return dname_strict_subdomain(d1, dname_count_labels(d1), d2,
596 dname_count_labels(d2));
600 dname_subdomain_c(uint8_t* d1, uint8_t* d2)
603 /* check subdomain: d1: www.example.com. and d2: example.com. */
604 /* or d1: example.com. and d2: example.com. */
605 int labs1 = dname_count_labels(d1);
606 int labs2 = dname_count_labels(d2);
609 if(dname_lab_cmp(d1, labs1, d2, labs2, &m) < 0) {
610 /* must have been example.com , www.example.com - wrong */
611 /* or otherwise different dnames */
618 dname_is_root(uint8_t* dname)
623 log_assert(!LABEL_IS_PTR(len));
628 dname_remove_label(uint8_t** dname, size_t* len)
631 log_assert(dname && *dname && len);
632 lablen = (*dname)[0];
633 log_assert(!LABEL_IS_PTR(lablen));
634 log_assert(*len > lablen);
636 return; /* do not modify root label */
642 dname_remove_labels(uint8_t** dname, size_t* len, int n)
646 dname_remove_label(dname, len);
650 dname_signame_label_count(uint8_t* dname)
656 if(dname[0] == 1 && dname[1] == '*')
669 dname_is_wild(uint8_t* dname)
671 return (dname[0] == 1 && dname[1] == '*');
675 * Compare labels in memory, lowercase while comparing.
676 * Returns canonical order for labels. If all is equal, the
680 * @param len1: length of label 1.
682 * @param len2: length of label 2.
683 * @return: 0, -1, +1 comparison result.
686 memcanoncmp(uint8_t* p1, uint8_t len1, uint8_t* p2, uint8_t len2)
688 uint8_t min = (len1<len2)?len1:len2;
689 int c = memlowercmp(p1, p2, min);
692 /* equal, see who is shortest */
702 dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
704 /* like dname_lab_cmp, but with different label comparison,
705 * empty character sorts before \000.
706 * So ylyly is before z. */
712 /* first skip so that we compare same label. */
714 while(atlabel > labs2) {
719 log_assert(atlabel == labs2);
720 } else if(labs1 < labs2) {
722 while(atlabel > labs1) {
727 log_assert(atlabel == labs1);
729 lastmlabs = atlabel+1;
730 /* now at same label in d1 and d2, atlabel */
731 /* www.example.com. */
732 /* 4 3 2 1 atlabel number */
733 /* repeat until at root label (which is always the same) */
738 if((c=memcanoncmp(d1, len1, d2, len2)) != 0) {
749 /* last difference atlabel number, so number of labels matching,
750 * at the right side, is one less. */
751 *mlabs = lastmlabs-1;
753 /* all labels compared were equal, check if one has more
754 * labels, so that example.com. > com. */
757 else if(labs1 < labs2)
764 dname_canonical_compare(uint8_t* d1, uint8_t* d2)
767 labs1 = dname_count_labels(d1);
768 labs2 = dname_count_labels(d2);
769 return dname_canon_lab_cmp(d1, labs1, d2, labs2, &m);
772 uint8_t* dname_get_shared_topdomain(uint8_t* d1, uint8_t* d2)
775 size_t len = LDNS_MAX_DOMAINLEN;
776 labs1 = dname_count_labels(d1);
777 labs2 = dname_count_labels(d2);
778 (void)dname_lab_cmp(d1, labs1, d2, labs2, &m);
779 dname_remove_labels(&d1, &len, labs1-m);