/*! * \brief Compare suffixes and calculate score (number of matching labels). * * Update current best score. */ static bool knot_response_compr_score(uint8_t *n, uint8_t *p, uint8_t labels, uint8_t *wire, knot_compr_ptr_t *match) { uint16_t score = 0; uint16_t off = 0; while (*n != '\0') { /* Can't exceed current best coverage. */ if (score + labels <= match->lbcount) return false; /* Early cut. */ /* Keep track of contiguous matches. */ if (*n == *p && memcmp(n + 1, p + 1, *n) == 0) { if (score == 0) off = (p - wire); ++score; } else { score = 0; /* Non-contiguous match. */ } n = knot_wire_next_label(n, wire); p = knot_wire_next_label(p, wire); --labels; } /* New best score. */ if (score > match->lbcount && off <= KNOT_WIRE_PTR_MAX) { match->lbcount = score; match->off = off; return true; } return false; }
/*----------------------------------------------------------------------------*/ _public_ int knot_dname_matched_labels(const knot_dname_t *d1, const knot_dname_t *d2) { if (d1 == NULL || d2 == NULL) return KNOT_EINVAL; /* Count labels. */ int l1 = knot_dname_labels(d1, NULL); int l2 = knot_dname_labels(d2, NULL); if (l1 < 0 || l2 < 0) return KNOT_EINVAL; assert(l1 >= 0 && l1 <= KNOT_DNAME_MAXLABELS); assert(l2 >= 0 && l2 <= KNOT_DNAME_MAXLABELS); /* Align end-to-end to common suffix. */ int common = knot_dname_align(&d1, l1, &d2, l2, NULL); /* Count longest chain leading to root label. */ int matched = 0; while (common > 0) { if (label_is_equal(d1, d2)) ++matched; else matched = 0; /* Broken chain. */ /* Next label. */ d1 = knot_wire_next_label(d1, NULL); d2 = knot_wire_next_label(d2, NULL); --common; } return matched; }
/*! * \brief Align name and reference to a common number of suffix labels. */ static uint8_t knot_response_compr_align(uint8_t **name, uint8_t nlabels, uint8_t **ref, uint8_t reflabels, uint8_t *wire) { for (unsigned j = nlabels; j < reflabels; ++j) *ref = knot_wire_next_label(*ref, wire); for (unsigned j = reflabels; j < nlabels; ++j) *name = knot_wire_next_label(*name, wire); return (nlabels < reflabels) ? nlabels : reflabels; }
/*----------------------------------------------------------------------------*/ _public_ bool knot_dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2) { while(*d1 != '\0' || *d2 != '\0') { if (label_is_equal(d1, d2)) { d1 = knot_wire_next_label(d1, NULL); d2 = knot_wire_next_label(d2, NULL); } else { return false; } } return true; }
static int rosedb_query_txn(MDB_txn *txn, MDB_dbi dbi, knot_pkt_t *pkt, struct query_data *qdata) { struct iter it; int ret = KNOT_EOK; /* Find suffix for QNAME. */ const knot_dname_t *qname = knot_pkt_qname(qdata->query); const knot_dname_t *key = qname; while (key) { ret = cache_query_fetch(txn, dbi, &it, key); if (ret == 0) { /* Found */ break; } if (*key == '\0') { /* Last label, not found. */ return KNOT_ENOENT; } key = knot_wire_next_label(key, qdata->query->wire); } /* Synthetize record to response. */ ret = rosedb_synth(pkt, key, &it, qdata); cache_iter_free(&it); return ret; }
/*----------------------------------------------------------------------------*/ _public_ int knot_dname_unpack(uint8_t* dst, const knot_dname_t *src, size_t maxlen, const uint8_t *pkt) { if (dst == NULL || src == NULL) return KNOT_EINVAL; /* Seek first real label occurence. */ src = knot_wire_seek_label(src, pkt); /* Unpack rest of the labels. */ int len = 0; while (*src != '\0') { uint8_t lblen = *src + 1; if (len + lblen > maxlen) return KNOT_ESPACE; memcpy(dst + len, src, lblen); len += lblen; src = knot_wire_next_label(src, pkt); } /* Terminal label */ if (len + 1 > maxlen) return KNOT_EINVAL; *(dst + len) = '\0'; return len + 1; }
/** * Name error response check (RFC4035 3.1.3.2; RFC4035 5.4, bullet 2). * @note Returned flags must be checked in order to prove denial. * @param flags Flags to be set according to check outcome. * @param nsec NSEC RR. * @param name Name to be checked. * @param pool * @return 0 or error code. */ static int name_error_response_check_rr(int *flags, const knot_rrset_t *nsec, const knot_dname_t *name) { assert(flags && nsec && name); if (nsec_nonamematch(nsec, name) == 0) { *flags |= FLG_NOEXIST_RRSET; } /* Try to find parent wildcard that is proved by this NSEC. */ uint8_t namebuf[KNOT_DNAME_MAXLEN]; int ret = knot_dname_to_wire(namebuf, name, sizeof(namebuf)); if (ret < 0) return ret; knot_dname_t *ptr = namebuf; while (ptr[0]) { /* Remove leftmost label and replace it with '\1*'. */ ptr = (uint8_t *) knot_wire_next_label(ptr, NULL); *(--ptr) = '*'; *(--ptr) = 1; /* True if this wildcard provably doesn't exist. */ if (nsec_nonamematch(nsec, ptr) == 0) { *flags |= FLG_NOEXIST_WILDCARD; break; } /* Remove added leftmost asterisk. */ ptr += 2; } return kr_ok(); }
/*----------------------------------------------------------------------------*/ _public_ int knot_dname_align(const uint8_t **d1, uint8_t d1_labels, const uint8_t **d2, uint8_t d2_labels, uint8_t *wire) { if (d1 == NULL || d2 == NULL) return KNOT_EINVAL; for (unsigned j = d1_labels; j < d2_labels; ++j) *d2 = knot_wire_next_label(*d2, wire); for (unsigned j = d2_labels; j < d1_labels; ++j) *d1 = knot_wire_next_label(*d1, wire); return (d1_labels < d2_labels) ? d1_labels : d2_labels; }
/*----------------------------------------------------------------------------*/ _public_ int knot_dname_lf(uint8_t *dst, const knot_dname_t *src, const uint8_t *pkt) { if (dst == NULL || src == NULL) return KNOT_EINVAL; uint8_t *len = dst++; *len = '\0'; *dst = '\0'; const uint8_t* l = src; /*! \todo This could be made as offsets to pkt? */ const uint8_t* lstack[KNOT_DNAME_MAXLABELS]; const uint8_t **sp = lstack; while(*l != 0) { /* build label stack */ *sp++ = l; l = knot_wire_next_label(l, pkt); } while(sp != lstack) { /* consume stack */ l = *--sp; /* fetch rightmost label */ memcpy(dst, l+1, *l); /* write label */ for (int i = 0; i < *l; ++i) { /* convert to lowercase */ dst[i] = knot_tolower(dst[i]); } dst += *l; *dst++ = '\0'; /* label separator */ *len += *l + 1; } /* root label special case */ if (*len == 0) *len = 1; /* \x00 */ return KNOT_EOK; }
/*----------------------------------------------------------------------------*/ _public_ knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *name, unsigned labels, const knot_dname_t *suffix) { if (name == NULL) return NULL; /* Calculate prefix and suffix lengths. */ int dname_lbs = knot_dname_labels(name, NULL); assert(dname_lbs >= labels); unsigned prefix_lbs = dname_lbs - labels; /* Trim 1 octet from prefix, as it is measured as FQDN. */ int prefix_len = knot_dname_prefixlen(name, prefix_lbs, NULL) - 1; int suffix_len = knot_dname_size(suffix); if (prefix_len < 0 || suffix_len < 0) return NULL; /* Create target name. */ int new_len = prefix_len + suffix_len; knot_dname_t *out = malloc(new_len); if (out == NULL) return NULL; /* Copy prefix. */ uint8_t *dst = out; while (prefix_lbs > 0) { memcpy(dst, name, *name + 1); dst += *name + 1; name = knot_wire_next_label(name, NULL); --prefix_lbs; } /* Copy suffix. */ while (*suffix != '\0') { memcpy(dst, suffix, *suffix + 1); dst += *suffix + 1; suffix = knot_wire_next_label(suffix, NULL); } *dst = '\0'; return out; }
/*----------------------------------------------------------------------------*/ _public_ int knot_dname_labels(const uint8_t *name, const uint8_t *pkt) { if (name == NULL) return KNOT_EINVAL; uint8_t count = 0; while (*name != '\0') { ++count; name = knot_wire_next_label((uint8_t *)name, (uint8_t *)pkt); if (!name) return KNOT_EMALF; } return count; }
/*----------------------------------------------------------------------------*/ _public_ bool knot_dname_is_sub(const knot_dname_t *sub, const knot_dname_t *domain) { if (sub == domain) return false; /* Count labels. */ assert(sub != NULL && domain != NULL); int sub_l = knot_dname_labels(sub, NULL); int domain_l = knot_dname_labels(domain, NULL); if (sub_l < 0 || domain_l < 0) return false; assert(sub_l >= 0 && sub_l <= KNOT_DNAME_MAXLABELS); assert(domain_l >= 0 && domain_l <= KNOT_DNAME_MAXLABELS); /* Subdomain must have more labels as parent. */ if (sub_l <= domain_l) return false; /* Align end-to-end to common suffix. */ int common = knot_dname_align(&sub, sub_l, &domain, domain_l, NULL); /* Compare common suffix. */ while(common > 0) { /* Compare label. */ if (!label_is_equal(sub, domain)) return false; /* Next label. */ sub = knot_wire_next_label(sub, NULL); domain = knot_wire_next_label(domain, NULL); --common; } return true; }
/*----------------------------------------------------------------------------*/ _public_ int knot_dname_to_lower(knot_dname_t *name) { if (name == NULL) return KNOT_EINVAL; /*! \todo Faster with \xdfdf mask. */ while (*name != '\0') { for (uint8_t i = 0; i < *name; ++i) name[1 + i] = knot_tolower(name[1 + i]); name = (uint8_t *)knot_wire_next_label(name, NULL); if (name == NULL) { /* Must not be used on compressed names. */ return KNOT_EMALF; } } return KNOT_EOK; }
/*! \brief Parse address from reverse query QNAME and return address family. */ static int reverse_addr_parse(struct query_data *qdata, synth_template_t *tpl, char *addr_str) { /* QNAME required format is [address].[subnet/zone] * f.e. [1.0...0].[h.g.f.e.0.0.0.0.d.c.b.a.ip6.arpa] represents * [abcd:0:efgh::1] */ const knot_dname_t* label = qdata->name; const uint8_t *query_wire = qdata->query->wire; /* Push labels on stack for reverse walkthrough. */ const uint8_t* label_stack[KNOT_DNAME_MAXLABELS]; const uint8_t** sp = label_stack; int label_count = knot_dname_labels(label, query_wire); while(label_count > ARPA_ZONE_LABELS) { *sp++ = label; label = knot_wire_next_label(label, query_wire); --label_count; } /* Write formatted address string. */ char sep = str_separator(tpl->subnet.ss.ss_family); int sep_frequency = 1; if (sep == ':') { sep_frequency = 4; /* Separator per 4 hexdigits. */ } char *dst = addr_str; label_count = 0; while(sp != label_stack) { label = *--sp; /* Write separator for each Nth label. */ if (label_count == sep_frequency) { *dst = sep; dst += 1; label_count = 0; } /* Write label */ memcpy(dst, label + 1, label[0]); dst += label[0]; label_count += 1; } return KNOT_EOK; }
/*----------------------------------------------------------------------------*/ _public_ int knot_dname_prefixlen(const uint8_t *name, unsigned nlabels, const uint8_t *pkt) { if (name == NULL) return KNOT_EINVAL; /* Zero labels means 1 octet \x00 */ if (nlabels == 0) return 1; /* Seek first real label occurence. */ name = knot_wire_seek_label(name, pkt); int len = 1; /* Terminal label */ while (*name != '\0') { len += *name + 1; name = knot_wire_next_label(name, pkt); if (--nlabels == 0) /* Count N first labels only. */ break; } return len; }
/*! * \brief Add covered RRs to signing context. * * Requires all DNAMEs in canonical form and all RRs ordered canonically. * * \param ctx Signing context. * \param covered Covered RRs. * * \return Error code, KNOT_EOK if successful. */ static int sign_ctx_add_records(dnssec_sign_ctx_t *ctx, const knot_rrset_t *covered, uint32_t orig_ttl, int trim_labels) { // huge block of rrsets can be optionally created static uint8_t wire_buffer[KNOT_WIRE_MAX_PKTSIZE]; int written = knot_rrset_to_wire(covered, wire_buffer, sizeof(wire_buffer), NULL); if (written < 0) { return written; } /* Set original ttl. */ int ret = adjust_wire_ttl(wire_buffer, written, orig_ttl); if (ret != 0) { return ret; } /* RFC4035 5.3.2 * Remove leftmost labels and replace them with '*.'. */ uint8_t *beginp = wire_buffer; if (trim_labels > 0) { for (int i = 0; i < trim_labels; ++i) { assert(beginp[0]); beginp = (uint8_t *) knot_wire_next_label(beginp, NULL); } *(--beginp) = '*'; *(--beginp) = 1; } dnssec_binary_t wire_binary = { .size = written - (beginp - wire_buffer), .data = beginp }; return dnssec_sign_add(ctx, &wire_binary); } /*! * \brief Add all data covered by signature into signing context. * * RFC 4034: The signature covers RRSIG RDATA field (excluding the signature) * and all matching RR records, which are ordered canonically. * * Requires all DNAMEs in canonical form and all RRs ordered canonically. * * \param ctx Signing context. * \param rrsig_rdata RRSIG RDATA with populated fields except signature. * \param covered Covered RRs. * * \return Error code, KNOT_EOK if successful. */ /* TODO -- Taken from knot/src/knot/dnssec/rrset-sign.c. Re-write for better fit needed. */ static int sign_ctx_add_data(dnssec_sign_ctx_t *ctx, const uint8_t *rrsig_rdata, const knot_rrset_t *covered, uint32_t orig_ttl, int trim_labels) { int result = sign_ctx_add_self(ctx, rrsig_rdata); if (result != KNOT_EOK) { return result; } return sign_ctx_add_records(ctx, covered, orig_ttl, trim_labels); }
int knot_response_compress_dname(const knot_dname_t *dname, knot_compr_t *compr, uint8_t *dst, size_t max) { if (!dname || !compr || !dst) { return KNOT_EINVAL; } /* Do not compress small dnames. */ uint8_t *name = dname->name; if (dname->size <= 2) { if (dname->size > max) return KNOT_ESPACE; memcpy(dst, name, dname->size); return dname->size; } /* Align and compare name and pointer in the compression table. */ unsigned i = 0; unsigned lbcount = 0; unsigned match_id = 0; knot_compr_ptr_t match = { 0, 0 }; for (; i < COMPR_MAXLEN && compr->table[i].off > 0; ++i) { uint8_t *name = dname->name; uint8_t *ref = compr->wire + compr->table[i].off; lbcount = knot_response_compr_align(&name, dname->label_count, &ref, compr->table[i].lbcount, compr->wire); if (knot_response_compr_score(name, ref, lbcount, compr->wire, &match)) { match_id = i; if (match.lbcount == dname->label_count) break; /* Best match, break. */ } } /* Write non-matching prefix. */ unsigned written = 0; for (unsigned j = match.lbcount; j < dname->label_count; ++j) { if (written + *name + 1 > max) return KNOT_ESPACE; memcpy(dst + written, name, *name + 1); written += *name + 1; name = knot_wire_next_label(name, compr->wire); } /* Write out pointer covering suffix. */ if (*name != '\0') { if (written + sizeof(uint16_t) > max) return KNOT_ESPACE; knot_wire_put_pointer(dst + written, match.off); written += sizeof(uint16_t); } else { /* Not covered by compression table, write terminal. */ if (written + 1 > max) return KNOT_ESPACE; *(dst + written) = '\0'; written += 1; } /* Promote good matches. */ if (match_id > 1) { match = compr->table[match_id]; compr->table[match_id] = compr->table[match_id - 1]; compr->table[match_id - 1] = match; } /* Do not insert if exceeds bounds or full match. */ if (match.lbcount == dname->label_count || compr->wire_pos > KNOT_WIRE_PTR_MAX) return written; /* If table is full, elect name from the lower 1/4 of the table * and replace it. */ if (i == COMPR_MAXLEN - 1) { i = COMPR_FIXEDLEN + rand() % COMPR_VOLATILE; compr->table[i].off = 0; } /* Store in dname table. */ if (compr->table[i].off == 0) { compr->table[i].off = (uint16_t)compr->wire_pos; compr->table[i].lbcount = dname->label_count; } return written; }
static int zone_contents_add_node(zone_contents_t *zone, zone_node_t *node, bool create_parents) { if (zone == NULL || node == NULL) { return KNOT_EINVAL; } int ret = 0; if ((ret = zone_contents_check_node(zone, node)) != 0) { dbg_zone("Node check failed.\n"); return ret; } ret = zone_tree_insert(zone->nodes, node); if (ret != KNOT_EOK) { dbg_zone("Failed to insert node into zone tree.\n"); return ret; } if (!create_parents) { return KNOT_EOK; } dbg_zone_detail("Creating parents of the node.\n"); /* No parents for root domain. */ if (*node->owner == '\0') return KNOT_EOK; zone_node_t *next_node = NULL; const uint8_t *parent = knot_wire_next_label(node->owner, NULL); if (knot_dname_cmp(zone->apex->owner, parent) == 0) { dbg_zone_detail("Zone apex is the parent.\n"); node_set_parent(node, zone->apex); // check if the node is not wildcard child of the parent if (knot_dname_is_wildcard(node->owner)) { zone->apex->flags |= NODE_FLAGS_WILDCARD_CHILD; } } else { while (parent != NULL && !(next_node = zone_contents_get_node(zone, parent))) { /* Create a new node. */ dbg_zone_detail("Creating new node.\n"); next_node = node_new(parent, NULL); if (next_node == NULL) { return KNOT_ENOMEM; } /* Insert node to a tree. */ dbg_zone_detail("Inserting new node to zone tree.\n"); ret = zone_tree_insert(zone->nodes, next_node); if (ret != KNOT_EOK) { node_free(&next_node, NULL); return ret; } /* Update node pointers. */ node_set_parent(node, next_node); if (knot_dname_is_wildcard(node->owner)) { next_node->flags |= NODE_FLAGS_WILDCARD_CHILD; } dbg_zone_detail("Next parent.\n"); node = next_node; parent = knot_wire_next_label(parent, NULL); } // set the found parent (in the zone) as the parent of the last // inserted node assert(node->parent == NULL); node_set_parent(node, next_node); dbg_zone_detail("Created all parents.\n"); } return KNOT_EOK; }