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
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.
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"
48 #include "sldns/sbuffer.h"
50 /* determine length of a dname in buffer, no compression pointers allowed */
52 query_dname_len(sldns_buffer* query)
57 if(sldns_buffer_remaining(query) < 1)
58 return 0; /* parse error, need label len */
59 labellen = sldns_buffer_read_u8(query);
61 return 0; /* no compression allowed in queries */
63 if(len > LDNS_MAX_DOMAINLEN)
64 return 0; /* too long */
67 if(sldns_buffer_remaining(query) < labellen)
68 return 0; /* parse error, need content */
69 sldns_buffer_skip(query, (ssize_t)labellen);
74 dname_valid(uint8_t* dname, size_t maxlen)
79 return 0; /* too short, shortest is '0' root label */
83 return 0; /* no compression ptrs allowed */
85 if(len >= LDNS_MAX_DOMAINLEN)
86 return 0; /* too long */
88 return 0; /* does not fit in memory allocation */
94 return 0; /* does not fit in memory allocation */
98 /** compare uncompressed, noncanonical, registers are hints for speed */
100 query_dname_compare(register uint8_t* d1, register uint8_t* d2)
102 register uint8_t lab1, lab2;
103 log_assert(d1 && d2);
106 while( lab1 != 0 || lab2 != 0 ) {
107 /* compare label length */
108 /* if one dname ends, it has labellength 0 */
114 log_assert(lab1 == lab2 && lab1 != 0);
115 /* compare lowercased labels. */
117 /* compare bytes first for speed */
119 tolower((unsigned char)*d1) != tolower((unsigned char)*d2)) {
120 if(tolower((unsigned char)*d1) < tolower((unsigned char)*d2))
127 /* next pair of labels. */
135 query_dname_tolower(uint8_t* dname)
137 /* the dname is stored uncompressed */
143 *dname = (uint8_t)tolower((unsigned char)*dname);
151 pkt_dname_tolower(sldns_buffer* pkt, uint8_t* dname)
155 if(dname >= sldns_buffer_end(pkt))
159 if(LABEL_IS_PTR(lablen)) {
160 if((size_t)PTR_OFFSET(lablen, *dname)
161 >= sldns_buffer_limit(pkt))
163 dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
165 if(count++ > MAX_COMPRESS_PTRS)
169 if(dname+lablen >= sldns_buffer_end(pkt))
172 *dname = (uint8_t)tolower((unsigned char)*dname);
175 if(dname >= sldns_buffer_end(pkt))
183 pkt_dname_len(sldns_buffer* pkt)
190 /* read dname and determine length */
191 /* check compression pointers, loops, out of bounds */
193 /* read next label */
194 if(sldns_buffer_remaining(pkt) < 1)
196 labellen = sldns_buffer_read_u8(pkt);
197 if(LABEL_IS_PTR(labellen)) {
198 /* compression ptr */
200 if(sldns_buffer_remaining(pkt) < 1)
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! */
208 endpos = sldns_buffer_position(pkt);
209 sldns_buffer_set_position(pkt, ptr);
213 return 0; /* label too long */
215 if(len > LDNS_MAX_DOMAINLEN)
221 if(sldns_buffer_remaining(pkt) < labellen)
223 sldns_buffer_skip(pkt, (ssize_t)labellen);
227 sldns_buffer_set_position(pkt, endpos);
233 dname_pkt_compare(sldns_buffer* pkt, uint8_t* d1, uint8_t* d2)
236 int count1 = 0, count2 = 0;
237 log_assert(pkt && d1 && d2);
240 while( len1 != 0 || len2 != 0 ) {
242 if(LABEL_IS_PTR(len1)) {
243 if((size_t)PTR_OFFSET(len1, *d1)
244 >= sldns_buffer_limit(pkt))
246 if(count1++ > MAX_COMPRESS_PTRS)
248 d1 = sldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
252 if(LABEL_IS_PTR(len2)) {
253 if((size_t)PTR_OFFSET(len2, *d2)
254 >= sldns_buffer_limit(pkt))
256 if(count2++ > MAX_COMPRESS_PTRS)
258 d2 = sldns_buffer_at(pkt, PTR_OFFSET(len2, *d2));
262 /* check label length */
263 log_assert(len1 <= LDNS_MAX_LABELLEN);
264 log_assert(len2 <= LDNS_MAX_LABELLEN);
266 if(len1 < len2) return -1;
269 log_assert(len1 == len2 && len1 != 0);
272 if(tolower((unsigned char)*d1) != tolower((unsigned char)*d2)) {
273 if(tolower((unsigned char)*d1) < tolower((unsigned char)*d2))
287 dname_query_hash(uint8_t* dname, hashvalue_type h)
289 uint8_t labuf[LDNS_MAX_LABELLEN+1];
293 /* preserve case of query, make hash label by label */
296 log_assert(lablen <= LDNS_MAX_LABELLEN);
300 labuf[++i] = (uint8_t)tolower((unsigned char)*dname);
303 h = hashlittle(labuf, labuf[0] + 1, h);
311 dname_pkt_hash(sldns_buffer* pkt, uint8_t* dname, hashvalue_type h)
313 uint8_t labuf[LDNS_MAX_LABELLEN+1];
318 /* preserve case of query, make hash label by label */
321 if(LABEL_IS_PTR(lablen)) {
323 if((size_t)PTR_OFFSET(lablen, *dname)
324 >= sldns_buffer_limit(pkt))
326 if(count++ > MAX_COMPRESS_PTRS)
328 dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
332 log_assert(lablen <= LDNS_MAX_LABELLEN);
336 labuf[++i] = (uint8_t)tolower((unsigned char)*dname);
339 h = hashlittle(labuf, labuf[0] + 1, h);
346 void dname_pkt_copy(sldns_buffer* pkt, uint8_t* to, uint8_t* dname)
348 /* copy over the dname and decompress it at the same time */
349 size_t comprcount = 0;
354 if(LABEL_IS_PTR(lablen)) {
355 if(comprcount++ > MAX_COMPRESS_PTRS) {
356 /* too many compression pointers */
357 *to = 0; /* end the result prematurely */
361 if((size_t)PTR_OFFSET(lablen, *dname)
362 >= sldns_buffer_limit(pkt))
364 dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
368 if(lablen > LDNS_MAX_LABELLEN) {
369 *to = 0; /* end the result prematurely */
372 log_assert(lablen <= LDNS_MAX_LABELLEN);
373 len += (size_t)lablen+1;
374 if(len >= LDNS_MAX_DOMAINLEN) {
375 *to = 0; /* end the result prematurely */
376 log_err("bad dname in dname_pkt_copy");
380 memmove(to, dname, lablen);
389 void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname)
393 if(!out) out = stdout;
400 if(LABEL_IS_PTR(lablen)) {
403 fputs("??compressionptr??", out);
406 if((size_t)PTR_OFFSET(lablen, *dname)
407 >= sldns_buffer_limit(pkt)) {
408 fputs("??compressionptr??", out);
411 if(count++ > MAX_COMPRESS_PTRS) {
412 fputs("??compressionptr??", out);
415 dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
419 if(lablen > LDNS_MAX_LABELLEN) {
420 fputs("??extendedlabel??", out);
424 fputc((int)*dname++, out);
431 dname_count_labels(uint8_t* dname)
446 dname_count_size_labels(uint8_t* dname, size_t* size)
464 * Compare labels in memory, lowercase while comparing.
467 * @param len: number of bytes to compare.
468 * @return: 0, -1, +1 comparison result.
471 memlowercmp(uint8_t* p1, uint8_t* p2, uint8_t len)
474 if(*p1 != *p2 && tolower((unsigned char)*p1) != tolower((unsigned char)*p2)) {
475 if(tolower((unsigned char)*p1) < tolower((unsigned char)*p2))
486 dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
492 /* first skip so that we compare same label. */
494 while(atlabel > labs2) {
499 log_assert(atlabel == labs2);
500 } else if(labs1 < labs2) {
502 while(atlabel > labs1) {
507 log_assert(atlabel == labs1);
509 lastmlabs = atlabel+1;
510 /* now at same label in d1 and d2, atlabel */
511 /* www.example.com. */
512 /* 4 3 2 1 atlabel number */
513 /* repeat until at root label (which is always the same) */
518 log_assert(len1 != 0 && len2 != 0);
526 /* memlowercmp is inlined here; or just like
527 * if((c=memlowercmp(d1, d2, len1)) != 0) {
529 * lastmlabs = atlabel; } apart from d1++,d2++ */
531 if(*d1 != *d2 && tolower((unsigned char)*d1)
532 != tolower((unsigned char)*d2)) {
533 if(tolower((unsigned char)*d1) <
534 tolower((unsigned char)*d2)) {
545 break; /* out of memlowercmp */
554 /* last difference atlabel number, so number of labels matching,
555 * at the right side, is one less. */
556 *mlabs = lastmlabs-1;
558 /* all labels compared were equal, check if one has more
559 * labels, so that example.com. > com. */
562 else if(labs1 < labs2)
569 dname_lab_startswith(uint8_t* label, char* prefix, char** endptr)
571 size_t plen = strlen(prefix);
572 size_t orig_plen = plen;
573 size_t lablen = (size_t)*label;
578 if(*prefix != tolower((unsigned char)*label)) {
583 if(orig_plen < lablen)
584 *endptr = (char *)label;
586 /* prefix length == label length */
592 dname_has_label(uint8_t* dname, size_t dnamelen, uint8_t* label)
596 /* 1 byte needed for the label length */
601 while(len <= dnamelen) {
604 return 1; /* empty label match */
605 /* termination label found, stop iterating */
608 if(*dname == *label && *label &&
609 memlowercmp(dname+1, label+1, *dname) == 0)
620 dname_buffer_write(sldns_buffer* pkt, uint8_t* dname)
624 if(sldns_buffer_remaining(pkt) < 1)
627 sldns_buffer_write_u8(pkt, lablen);
629 if(sldns_buffer_remaining(pkt) < (size_t)lablen+1)
631 sldns_buffer_write(pkt, dname, lablen);
634 sldns_buffer_write_u8(pkt, lablen);
639 void dname_str(uint8_t* dname, char* str)
644 if(!dname || !*dname) {
651 if(lablen > LDNS_MAX_LABELLEN) {
657 if(len >= LDNS_MAX_DOMAINLEN-1) {
663 if(isalnum((unsigned char)*dname)
664 || *dname == '-' || *dname == '_'
666 *s++ = *(char*)dname++;
679 dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2)
682 /* check subdomain: d1: www.example.com. and d2: example.com. */
685 if(dname_lab_cmp(d1, labs1, d2, labs2, &m) > 0) {
686 /* subdomain if all labels match */
693 dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2)
695 return dname_strict_subdomain(d1, dname_count_labels(d1), d2,
696 dname_count_labels(d2));
700 dname_subdomain_c(uint8_t* d1, uint8_t* d2)
703 /* check subdomain: d1: www.example.com. and d2: example.com. */
704 /* or d1: example.com. and d2: example.com. */
705 int labs1 = dname_count_labels(d1);
706 int labs2 = dname_count_labels(d2);
709 if(dname_lab_cmp(d1, labs1, d2, labs2, &m) < 0) {
710 /* must have been example.com , www.example.com - wrong */
711 /* or otherwise different dnames */
718 dname_is_root(uint8_t* dname)
723 log_assert(!LABEL_IS_PTR(len));
728 dname_remove_label(uint8_t** dname, size_t* len)
731 log_assert(dname && *dname && len);
732 lablen = (*dname)[0];
733 log_assert(!LABEL_IS_PTR(lablen));
734 log_assert(*len > lablen);
736 return; /* do not modify root label */
742 dname_remove_labels(uint8_t** dname, size_t* len, int n)
746 dname_remove_label(dname, len);
750 dname_signame_label_count(uint8_t* dname)
756 if(dname[0] == 1 && dname[1] == '*')
769 dname_is_wild(uint8_t* dname)
771 return (dname[0] == 1 && dname[1] == '*');
775 * Compare labels in memory, lowercase while comparing.
776 * Returns canonical order for labels. If all is equal, the
780 * @param len1: length of label 1.
782 * @param len2: length of label 2.
783 * @return: 0, -1, +1 comparison result.
786 memcanoncmp(uint8_t* p1, uint8_t len1, uint8_t* p2, uint8_t len2)
788 uint8_t min = (len1<len2)?len1:len2;
789 int c = memlowercmp(p1, p2, min);
792 /* equal, see who is shortest */
802 dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
804 /* like dname_lab_cmp, but with different label comparison,
805 * empty character sorts before \000.
806 * So ylyly is before z. */
812 /* first skip so that we compare same label. */
814 while(atlabel > labs2) {
819 log_assert(atlabel == labs2);
820 } else if(labs1 < labs2) {
822 while(atlabel > labs1) {
827 log_assert(atlabel == labs1);
829 lastmlabs = atlabel+1;
830 /* now at same label in d1 and d2, atlabel */
831 /* www.example.com. */
832 /* 4 3 2 1 atlabel number */
833 /* repeat until at root label (which is always the same) */
838 if((c=memcanoncmp(d1, len1, d2, len2)) != 0) {
849 /* last difference atlabel number, so number of labels matching,
850 * at the right side, is one less. */
851 *mlabs = lastmlabs-1;
853 /* all labels compared were equal, check if one has more
854 * labels, so that example.com. > com. */
857 else if(labs1 < labs2)
864 dname_canonical_compare(uint8_t* d1, uint8_t* d2)
867 labs1 = dname_count_labels(d1);
868 labs2 = dname_count_labels(d2);
869 return dname_canon_lab_cmp(d1, labs1, d2, labs2, &m);
872 uint8_t* dname_get_shared_topdomain(uint8_t* d1, uint8_t* d2)
875 size_t len = LDNS_MAX_DOMAINLEN;
876 labs1 = dname_count_labels(d1);
877 labs2 = dname_count_labels(d2);
878 (void)dname_lab_cmp(d1, labs1, d2, labs2, &m);
879 dname_remove_labels(&d1, &len, labs1-m);