/*----------------------------------------------------------------------------*/ _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; }
static int addr_parse(struct query_data *qdata, synth_template_t *tpl, char *addr_str) { /* Check if we have at least 1 label below zone. */ int zone_labels = knot_dname_labels(qdata->zone->name, NULL); int query_labels = knot_dname_labels(qdata->name, qdata->query->wire); if (query_labels < zone_labels + 1) { return KNOT_EINVAL; } switch (tpl->type) { case SYNTH_REVERSE: return reverse_addr_parse(qdata, tpl, addr_str); case SYNTH_FORWARD: return forward_addr_parse(qdata, tpl, addr_str); default: return KNOT_EINVAL; } }
static int str_label( const knot_dname_t *zone, char *buff, size_t buff_len, uint8_t right_index) { int labels = knot_dname_labels(zone, NULL); // Check for root label of the root zone. if (labels == 0 && right_index == 0) { return str_zone(zone, buff, buff_len); // Check for labels error or for an exceeded index. } else if (labels < 1 || labels <= right_index) { buff[0] = '\0'; return KNOT_EOK; } // ~ Label length + label + root label. knot_dname_t label[1 + KNOT_DNAME_MAXLABELLEN + 1]; // Compute the index from the left. assert(labels > right_index); uint8_t index = labels - right_index - 1; // Create a dname from the single label. int prefix = (index > 0) ? knot_dname_prefixlen(zone, index, NULL) : 0; uint8_t label_len = *(zone + prefix); memcpy(label, zone + prefix, 1 + label_len); label[1 + label_len] = '\0'; return str_zone(label, buff, buff_len); }
/** * Returns the number of labels that have been added by wildcard expansion. * @param expanded Expanded wildcard. * @param rrsigs RRSet containing the signatures. * @param sig_pos Specifies the signature within the RRSIG RRSet. * @return Number of added labels, -1 on error. */ static int wildcard_radix_len_diff(const knot_dname_t *expanded, const knot_rrset_t *rrsigs, size_t sig_pos) { if (!expanded || !rrsigs) { return -1; } return knot_dname_labels(expanded, NULL) - knot_rrsig_labels(&rrsigs->rrs, sig_pos); }
/*----------------------------------------------------------------------------*/ _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; }
/*! \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_ 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; }
/** * Perform check for RR type wildcard existence denial according to RFC4035 5.4, bullet 1. * @param flags Flags to be set according to check outcome. * @param nsec NSEC RR. * @param sec Packet section to work with. * @return 0 or error code. */ static int no_data_wildcard_existence_check(int *flags, const knot_rrset_t *nsec, const knot_pktsection_t *sec) { assert(flags && nsec && sec); int rrsig_labels = coverign_rrsig_labels(nsec, sec); if (rrsig_labels < 0) { return rrsig_labels; } int nsec_labels = knot_dname_labels(nsec->owner, NULL); if (nsec_labels < 0) { return nsec_labels; } if (rrsig_labels == nsec_labels) { *flags |= FLG_NOEXIST_WILDCARD; } return kr_ok(); }
_public_ int knot_pkt_put(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr, uint16_t flags) { if (pkt == NULL || rr == NULL) { return KNOT_EINVAL; } /* Reserve memory for RR descriptors. */ int ret = pkt_rr_array_alloc(pkt, pkt->rrset_count + 1); if (ret != KNOT_EOK) { return ret; } knot_rrinfo_t *rrinfo = &pkt->rr_info[pkt->rrset_count]; memset(rrinfo, 0, sizeof(knot_rrinfo_t)); rrinfo->pos = pkt->size; rrinfo->flags = flags; rrinfo->compress_ptr[0] = compr_hint; memcpy(pkt->rr + pkt->rrset_count, rr, sizeof(knot_rrset_t)); /* Check for double insertion. */ if ((flags & KNOT_PF_CHECKDUP) && pkt_contains(pkt, rr)) { return KNOT_EOK; /*! \todo return rather a number of added RRs */ } uint8_t *pos = pkt->wire + pkt->size; size_t maxlen = pkt_remaining(pkt); /* Create compression context. */ knot_compr_t compr; compr.wire = pkt->wire; compr.rrinfo = rrinfo; compr.suffix.pos = KNOT_WIRE_HEADER_SIZE; compr.suffix.labels = knot_dname_labels(compr.wire + compr.suffix.pos, compr.wire); /* Write RRSet to wireformat. */ ret = knot_rrset_to_wire(rr, pos, maxlen, &compr); if (ret < 0) { /* Truncate packet if required. */ if (ret == KNOT_ESPACE && !(flags & KNOT_PF_NOTRUNC)) { knot_wire_set_tc(pkt->wire); } return ret; } size_t len = ret; uint16_t rr_added = rr->rrs.rr_count; /* Keep reference to special types. */ if (rr->type == KNOT_RRTYPE_OPT) { pkt->opt_rr = &pkt->rr[pkt->rrset_count]; } if (rr_added > 0) { pkt->rrset_count += 1; pkt->sections[pkt->current].count += 1; pkt->size += len; pkt_rr_wirecount_add(pkt, pkt->current, rr_added); } return KNOT_EOK; }
int kr_rrset_validate_with_key(const knot_pkt_t *pkt, knot_section_t section_id, const knot_rrset_t *covered, const knot_rrset_t *keys, size_t key_pos, const struct dseckey *key, const knot_dname_t *zone_name, uint32_t timestamp, bool has_nsec3) { struct dseckey *created_key = NULL; if (key == NULL) { const knot_rdata_t *krr = knot_rdataset_at(&keys->rrs, key_pos); int ret = kr_dnssec_key_from_rdata(&created_key, keys->owner, knot_rdata_data(krr), knot_rdata_rdlen(krr)); if (ret != 0) { return ret; } key = created_key; } uint16_t keytag = dnssec_key_get_keytag((dnssec_key_t *)key); int covered_labels = knot_dname_labels(covered->owner, NULL); if (knot_dname_is_wildcard(covered->owner)) { /* The asterisk does not count, RFC4034 3.1.3, paragraph 3. */ --covered_labels; } const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id); for (unsigned i = 0; i < sec->count; ++i) { /* Consider every RRSIG that matches owner and covers the class/type. */ const knot_rrset_t *rrsig = knot_pkt_rr(sec, i); if (rrsig->type != KNOT_RRTYPE_RRSIG) { continue; } if ((covered->rclass != rrsig->rclass) || !knot_dname_is_equal(covered->owner, rrsig->owner)) { continue; } for (uint16_t j = 0; j < rrsig->rrs.rr_count; ++j) { int val_flgs = 0; int trim_labels = 0; if (knot_rrsig_type_covered(&rrsig->rrs, j) != covered->type) { continue; } if (validate_rrsig_rr(&val_flgs, covered_labels, rrsig, j, keys, key_pos, keytag, zone_name, timestamp) != 0) { continue; } if (val_flgs & FLG_WILDCARD_EXPANSION) { trim_labels = wildcard_radix_len_diff(covered->owner, rrsig, j); if (trim_labels < 0) { break; } } if (kr_check_signature(rrsig, j, (dnssec_key_t *) key, covered, trim_labels) != 0) { continue; } if (val_flgs & FLG_WILDCARD_EXPANSION) { int ret = 0; if (!has_nsec3) { ret = kr_nsec_wildcard_answer_response_check(pkt, KNOT_AUTHORITY, covered->owner); } else { ret = kr_nsec3_wildcard_answer_response_check(pkt, KNOT_AUTHORITY, covered->owner, trim_labels - 1); } if (ret != 0) { continue; } } /* Validated with current key, OK */ kr_dnssec_key_free(&created_key); return kr_ok(); } } /* No applicable key found, cannot be validated. */ kr_dnssec_key_free(&created_key); return kr_error(ENOENT); }