diff options
Diffstat (limited to 'contrib/ldns/dname.c')
-rw-r--r-- | contrib/ldns/dname.c | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/contrib/ldns/dname.c b/contrib/ldns/dname.c new file mode 100644 index 0000000..f3770fe --- /dev/null +++ b/contrib/ldns/dname.c @@ -0,0 +1,565 @@ +/* + * dname.c + * + * dname specific rdata implementations + * A dname is a rdf structure with type LDNS_RDF_TYPE_DNAME + * It is not a /real/ type! All function must therefor check + * for LDNS_RDF_TYPE_DNAME. + * + * a Net::DNS like library for C + * + * (c) NLnet Labs, 2004-2006 + * + * See the file LICENSE for the license + */ + +#include <ldns/config.h> + +#include <ldns/ldns.h> + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +ldns_rdf * +ldns_dname_cat_clone(const ldns_rdf *rd1, const ldns_rdf *rd2) +{ + ldns_rdf *new; + uint16_t new_size; + uint8_t *buf; + uint16_t left_size; + + if (ldns_rdf_get_type(rd1) != LDNS_RDF_TYPE_DNAME || + ldns_rdf_get_type(rd2) != LDNS_RDF_TYPE_DNAME) { + return NULL; + } + + /* remove root label if it is present at the end of the left + * rd, by reducing the size with 1 + */ + left_size = ldns_rdf_size(rd1); + if (left_size > 0 &&ldns_rdf_data(rd1)[left_size - 1] == 0) { + left_size--; + } + + /* we overwrite the nullbyte of rd1 */ + new_size = left_size + ldns_rdf_size(rd2); + buf = LDNS_XMALLOC(uint8_t, new_size); + if (!buf) { + return NULL; + } + + /* put the two dname's after each other */ + memcpy(buf, ldns_rdf_data(rd1), left_size); + memcpy(buf + left_size, ldns_rdf_data(rd2), ldns_rdf_size(rd2)); + + new = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, new_size, buf); + + LDNS_FREE(buf); + return new; +} + +ldns_status +ldns_dname_cat(ldns_rdf *rd1, ldns_rdf *rd2) +{ + uint16_t left_size; + uint16_t size; + uint8_t* newd; + + if (ldns_rdf_get_type(rd1) != LDNS_RDF_TYPE_DNAME || + ldns_rdf_get_type(rd2) != LDNS_RDF_TYPE_DNAME) { + return LDNS_STATUS_ERR; + } + + /* remove root label if it is present at the end of the left + * rd, by reducing the size with 1 + */ + left_size = ldns_rdf_size(rd1); + if (left_size > 0 &&ldns_rdf_data(rd1)[left_size - 1] == 0) { + left_size--; + } + + size = left_size + ldns_rdf_size(rd2); + newd = LDNS_XREALLOC(ldns_rdf_data(rd1), uint8_t, size); + if(!newd) { + return LDNS_STATUS_MEM_ERR; + } + + ldns_rdf_set_data(rd1, newd); + memcpy(ldns_rdf_data(rd1) + left_size, ldns_rdf_data(rd2), + ldns_rdf_size(rd2)); + ldns_rdf_set_size(rd1, size); + + return LDNS_STATUS_OK; +} + +ldns_rdf * +ldns_dname_reverse(const ldns_rdf *d) +{ + ldns_rdf *new; + ldns_rdf *tmp; + ldns_rdf *d_tmp; + ldns_status status; + + d_tmp = ldns_rdf_clone(d); + + new = ldns_dname_new_frm_str("."); + if(!new) + return NULL; + + while(ldns_dname_label_count(d_tmp) > 0) { + tmp = ldns_dname_label(d_tmp, 0); + status = ldns_dname_cat(tmp, new); + if(status != LDNS_STATUS_OK) { + ldns_rdf_deep_free(new); + ldns_rdf_deep_free(d_tmp); + return NULL; + } + ldns_rdf_deep_free(new); + new = tmp; + tmp = ldns_dname_left_chop(d_tmp); + ldns_rdf_deep_free(d_tmp); + d_tmp = tmp; + } + ldns_rdf_deep_free(d_tmp); + + return new; +} + +ldns_rdf * +ldns_dname_clone_from(const ldns_rdf *d, uint16_t n) +{ + uint8_t *data; + uint8_t label_size; + size_t data_size; + + if (!d || + ldns_rdf_get_type(d) != LDNS_RDF_TYPE_DNAME || + ldns_dname_label_count(d) < n) { + return NULL; + } + + data = ldns_rdf_data(d); + data_size = ldns_rdf_size(d); + while (n > 0) { + label_size = data[0] + 1; + data += label_size; + if (data_size < label_size) { + /* this label is very broken */ + return NULL; + } + data_size -= label_size; + n--; + } + + return ldns_dname_new_frm_data(data_size, data); +} + +ldns_rdf * +ldns_dname_left_chop(const ldns_rdf *d) +{ + uint8_t label_pos; + ldns_rdf *chop; + + if (!d) { + return NULL; + } + + if (ldns_rdf_get_type(d) != LDNS_RDF_TYPE_DNAME) { + return NULL; + } + if (ldns_dname_label_count(d) == 0) { + /* root label */ + return NULL; + } + /* 05blaat02nl00 */ + label_pos = ldns_rdf_data(d)[0]; + + chop = ldns_dname_new_frm_data(ldns_rdf_size(d) - label_pos - 1, + ldns_rdf_data(d) + label_pos + 1); + return chop; +} + +uint8_t +ldns_dname_label_count(const ldns_rdf *r) +{ + uint16_t src_pos; + uint16_t len; + uint8_t i; + size_t r_size; + + if (!r) { + return 0; + } + + i = 0; + src_pos = 0; + r_size = ldns_rdf_size(r); + + if (ldns_rdf_get_type(r) != LDNS_RDF_TYPE_DNAME) { + return 0; + } else { + len = ldns_rdf_data(r)[src_pos]; /* start of the label */ + + /* single root label */ + if (1 == r_size) { + return 0; + } else { + while ((len > 0) && src_pos < r_size) { + src_pos++; + src_pos += len; + len = ldns_rdf_data(r)[src_pos]; + i++; + } + } + } + return i; +} + +ldns_rdf * +ldns_dname_new(uint16_t s, void *d) +{ + ldns_rdf *rd; + + rd = LDNS_MALLOC(ldns_rdf); + if (!rd) { + return NULL; + } + ldns_rdf_set_size(rd, s); + ldns_rdf_set_type(rd, LDNS_RDF_TYPE_DNAME); + ldns_rdf_set_data(rd, d); + return rd; +} + +ldns_rdf * +ldns_dname_new_frm_str(const char *str) +{ + return ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, str); +} + +ldns_rdf * +ldns_dname_new_frm_data(uint16_t size, const void *data) +{ + return ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, size, data); +} + +void +ldns_dname2canonical(const ldns_rdf *rd) +{ + uint8_t *rdd; + uint16_t i; + + if (ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_DNAME) { + return; + } + + rdd = (uint8_t*)ldns_rdf_data(rd); + for (i = 0; i < ldns_rdf_size(rd); i++, rdd++) { + *rdd = (uint8_t)LDNS_DNAME_NORMALIZE((int)*rdd); + } +} + +bool +ldns_dname_is_subdomain(const ldns_rdf *sub, const ldns_rdf *parent) +{ + uint8_t sub_lab; + uint8_t par_lab; + int8_t i, j; + ldns_rdf *tmp_sub = NULL; + ldns_rdf *tmp_par = NULL; + ldns_rdf *sub_clone; + ldns_rdf *parent_clone; + bool result = true; + + if (ldns_rdf_get_type(sub) != LDNS_RDF_TYPE_DNAME || + ldns_rdf_get_type(parent) != LDNS_RDF_TYPE_DNAME || + ldns_rdf_compare(sub, parent) == 0) { + return false; + } + + /* would be nicer if we do not have to clone... */ + sub_clone = ldns_dname_clone_from(sub, 0); + parent_clone = ldns_dname_clone_from(parent, 0); + ldns_dname2canonical(sub_clone); + ldns_dname2canonical(parent_clone); + + sub_lab = ldns_dname_label_count(sub_clone); + par_lab = ldns_dname_label_count(parent_clone); + + /* if sub sits above parent, it cannot be a child/sub domain */ + if (sub_lab < par_lab) { + result = false; + } else { + /* check all labels the from the parent labels, from right to left. + * When they /all/ match we have found a subdomain + */ + j = sub_lab - 1; /* we count from zero, thank you */ + for (i = par_lab -1; i >= 0; i--) { + tmp_sub = ldns_dname_label(sub_clone, j); + tmp_par = ldns_dname_label(parent_clone, i); + if (!tmp_sub || !tmp_par) { + /* deep free does null check */ + ldns_rdf_deep_free(tmp_sub); + ldns_rdf_deep_free(tmp_par); + result = false; + break; + } + + if (ldns_rdf_compare(tmp_sub, tmp_par) != 0) { + /* they are not equal */ + ldns_rdf_deep_free(tmp_sub); + ldns_rdf_deep_free(tmp_par); + result = false; + break; + } + ldns_rdf_deep_free(tmp_sub); + ldns_rdf_deep_free(tmp_par); + j--; + } + } + ldns_rdf_deep_free(sub_clone); + ldns_rdf_deep_free(parent_clone); + return result; +} + +int +ldns_dname_compare(const ldns_rdf *dname1, const ldns_rdf *dname2) +{ + size_t lc1, lc2, lc1f, lc2f; + size_t i; + int result = 0; + uint8_t *lp1, *lp2; + + /* see RFC4034 for this algorithm */ + /* this algorithm assumes the names are normalized to case */ + + /* only when both are not NULL we can say anything about them */ + if (!dname1 && !dname2) { + return 0; + } + if (!dname1 || !dname2) { + return -1; + } + /* asserts must happen later as we are looking in the + * dname, which could be NULL. But this case is handled + * above + */ + assert(ldns_rdf_get_type(dname1) == LDNS_RDF_TYPE_DNAME); + assert(ldns_rdf_get_type(dname2) == LDNS_RDF_TYPE_DNAME); + + lc1 = ldns_dname_label_count(dname1); + lc2 = ldns_dname_label_count(dname2); + + if (lc1 == 0 && lc2 == 0) { + return 0; + } + if (lc1 == 0) { + return -1; + } + if (lc2 == 0) { + return 1; + } + lc1--; + lc2--; + /* we start at the last label */ + while (true) { + /* find the label first */ + lc1f = lc1; + lp1 = ldns_rdf_data(dname1); + while (lc1f > 0) { + lp1 += *lp1 + 1; + lc1f--; + } + + /* and find the other one */ + lc2f = lc2; + lp2 = ldns_rdf_data(dname2); + while (lc2f > 0) { + lp2 += *lp2 + 1; + lc2f--; + } + + /* now check the label character for character. */ + for (i = 1; i < (size_t)(*lp1 + 1); i++) { + if (i > *lp2) { + /* apparently label 1 is larger */ + result = 1; + goto done; + } + if (LDNS_DNAME_NORMALIZE((int) *(lp1 + i)) < + LDNS_DNAME_NORMALIZE((int) *(lp2 + i))) { + result = -1; + goto done; + } else if (LDNS_DNAME_NORMALIZE((int) *(lp1 + i)) > + LDNS_DNAME_NORMALIZE((int) *(lp2 + i))) { + result = 1; + goto done; + } + } + if (*lp1 < *lp2) { + /* apparently label 2 is larger */ + result = -1; + goto done; + } + if (lc1 == 0 && lc2 > 0) { + result = -1; + goto done; + } else if (lc1 > 0 && lc2 == 0) { + result = 1; + goto done; + } else if (lc1 == 0 && lc2 == 0) { + result = 0; + goto done; + } + lc1--; + lc2--; + } + + done: + return result; +} + +int +ldns_dname_is_wildcard(const ldns_rdf* dname) +{ + return ( ldns_dname_label_count(dname) > 0 && + ldns_rdf_data(dname)[0] == 1 && + ldns_rdf_data(dname)[1] == '*'); +} + +int +ldns_dname_match_wildcard(const ldns_rdf *dname, const ldns_rdf *wildcard) +{ + ldns_rdf *wc_chopped; + int result; + /* check whether it really is a wildcard */ + if (ldns_dname_is_wildcard(wildcard)) { + /* ok, so the dname needs to be a subdomain of the wildcard + * without the * + */ + wc_chopped = ldns_dname_left_chop(wildcard); + result = (int) ldns_dname_is_subdomain(dname, wc_chopped); + ldns_rdf_deep_free(wc_chopped); + } else { + result = (ldns_dname_compare(dname, wildcard) == 0); + } + return result; +} + +/* nsec test: does prev <= middle < next + * -1 = yes + * 0 = error/can't tell + * 1 = no + */ +int +ldns_dname_interval(const ldns_rdf *prev, const ldns_rdf *middle, + const ldns_rdf *next) +{ + int prev_check, next_check; + + assert(ldns_rdf_get_type(prev) == LDNS_RDF_TYPE_DNAME); + assert(ldns_rdf_get_type(middle) == LDNS_RDF_TYPE_DNAME); + assert(ldns_rdf_get_type(next) == LDNS_RDF_TYPE_DNAME); + + prev_check = ldns_dname_compare(prev, middle); + next_check = ldns_dname_compare(middle, next); + /* <= next. This cannot be the case for nsec, because then we would + * have gotten the nsec of next... + */ + if (next_check == 0) { + return 0; + } + + /* <= */ + if ((prev_check == -1 || prev_check == 0) && + /* < */ + next_check == -1) { + return -1; + } else { + return 1; + } +} + + +bool +ldns_dname_str_absolute(const char *dname_str) +{ + const char* s; + if(dname_str && strcmp(dname_str, ".") == 0) + return 1; + if(!dname_str || strlen(dname_str) < 2) + return 0; + if(dname_str[strlen(dname_str) - 1] != '.') + return 0; + if(dname_str[strlen(dname_str) - 2] != '\\') + return 1; /* ends in . and no \ before it */ + /* so we have the case of ends in . and there is \ before it */ + for(s=dname_str; *s; s++) { + if(*s == '\\') { + if(s[1] && s[2] && s[3] /* check length */ + && isdigit(s[1]) && isdigit(s[2]) && + isdigit(s[3])) + s += 3; + else if(!s[1] || isdigit(s[1])) /* escape of nul,0-9 */ + return 0; /* parse error */ + else s++; /* another character escaped */ + } + else if(!*(s+1) && *s == '.') + return 1; /* trailing dot, unescaped */ + } + return 0; +} + +ldns_rdf * +ldns_dname_label(const ldns_rdf *rdf, uint8_t labelpos) +{ + uint8_t labelcnt; + uint16_t src_pos; + uint16_t len; + ldns_rdf *tmpnew; + size_t s; + uint8_t *data; + + if (ldns_rdf_get_type(rdf) != LDNS_RDF_TYPE_DNAME) { + return NULL; + } + + labelcnt = 0; + src_pos = 0; + s = ldns_rdf_size(rdf); + + len = ldns_rdf_data(rdf)[src_pos]; /* label start */ + while ((len > 0) && src_pos < s) { + if (labelcnt == labelpos) { + /* found our label */ + data = LDNS_XMALLOC(uint8_t, len + 2); + if (!data) { + return NULL; + } + memcpy(data, ldns_rdf_data(rdf) + src_pos, len + 1); + data[len + 2 - 1] = 0; + + tmpnew = ldns_rdf_new( LDNS_RDF_TYPE_DNAME + , len + 2, data); + if (!tmpnew) { + LDNS_FREE(data); + return NULL; + } + return tmpnew; + } + src_pos++; + src_pos += len; + len = ldns_rdf_data(rdf)[src_pos]; + labelcnt++; + } + return NULL; +} |