/*! * \brief Connect two nodes by adding a NSEC RR into the first node. * * Callback function, signature chain_iterate_cb. * * \param a First node. * \param b Second node (immediate follower of a). * \param data Pointer to nsec_chain_iterate_data_t holding parameters * including changeset. * * \return Error code, KNOT_EOK if successful. */ static int connect_nsec_nodes(zone_node_t *a, zone_node_t *b, nsec_chain_iterate_data_t *data) { assert(a); assert(b); assert(data); if (b->rrset_count == 0 || b->flags & NODE_FLAGS_NONAUTH) { return NSEC_NODE_SKIP; } int ret = 0; /*! * If the node has no other RRSets than NSEC (and possibly RRSIGs), * just remove the NSEC and its RRSIG, they are redundant */ if (node_rrtype_exists(b, KNOT_RRTYPE_NSEC) && knot_nsec_empty_nsec_and_rrsigs_in_node(b)) { ret = knot_nsec_changeset_remove(b, data->changeset); if (ret != KNOT_EOK) { return ret; } // Skip the 'b' node return NSEC_NODE_SKIP; } // create new NSEC knot_rrset_t *new_nsec = create_nsec_rrset(a, b, data->ttl); if (!new_nsec) { dbg_dnssec_detail("Failed to create new NSEC.\n"); return KNOT_ENOMEM; } knot_rrset_t old_nsec = node_rrset(a, KNOT_RRTYPE_NSEC); if (!knot_rrset_empty(&old_nsec)) { if (knot_rrset_equal(new_nsec, &old_nsec, KNOT_RRSET_COMPARE_WHOLE)) { // current NSEC is valid, do nothing dbg_dnssec_detail("NSECs equal.\n"); knot_rrset_free(&new_nsec, NULL); return KNOT_EOK; } dbg_dnssec_detail("NSECs not equal, replacing.\n"); // Mark the node so that we do not sign this NSEC a->flags |= NODE_FLAGS_REMOVED_NSEC; ret = knot_nsec_changeset_remove(a, data->changeset); if (ret != KNOT_EOK) { knot_rrset_free(&new_nsec, NULL); return ret; } } dbg_dnssec_detail("Adding new NSEC to changeset.\n"); // Add new NSEC to the changeset (no matter if old was removed) return knot_changeset_add_rrset(data->changeset, new_nsec, KNOT_CHANGESET_ADD); }
/*! * \brief Add entry for removed NSEC to the changeset. */ int knot_nsec_changeset_remove(const zone_node_t *n, knot_changeset_t *changeset) { if (changeset == NULL) { return KNOT_EINVAL; } int result = KNOT_EOK; knot_rrset_t *nsec = node_create_rrset(n, KNOT_RRTYPE_NSEC); if (nsec == NULL) { nsec = node_create_rrset(n, KNOT_RRTYPE_NSEC3); } if (nsec) { // update changeset result = knot_changeset_add_rrset(changeset, nsec, KNOT_CHANGESET_REMOVE); if (result != KNOT_EOK) { knot_rrset_free(&nsec, NULL); return result; } } knot_rrset_t rrsigs = node_rrset(n, KNOT_RRTYPE_RRSIG); if (!knot_rrset_empty(&rrsigs)) { knot_rrset_t *synth_rrsigs = knot_rrset_new(n->owner, KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, NULL); if (synth_rrsigs == NULL) { return KNOT_ENOMEM; } result = knot_synth_rrsig(KNOT_RRTYPE_NSEC, &rrsigs.rrs, &synth_rrsigs->rrs, NULL); if (result == KNOT_ENOENT) { // Try removing NSEC3 RRSIGs result = knot_synth_rrsig(KNOT_RRTYPE_NSEC3, &rrsigs.rrs, &synth_rrsigs->rrs, NULL); } if (result != KNOT_EOK) { knot_rrset_free(&synth_rrsigs, NULL); if (result != KNOT_ENOENT) { return result; } return KNOT_EOK; } // store RRSIG result = knot_changeset_add_rrset(changeset, synth_rrsigs, KNOT_CHANGESET_REMOVE); if (result != KNOT_EOK) { knot_rrset_free(&synth_rrsigs, NULL); return result; } } return KNOT_EOK; }
static knot_rrset_t *update_ds(struct kr_zonecut *cut, const knot_pktsection_t *sec) { /* Aggregate DS records (if using multiple keys) */ knot_rrset_t *new_ds = NULL; for (unsigned i = 0; i < sec->count; ++i) { const knot_rrset_t *rr = knot_pkt_rr(sec, i); if (rr->type != KNOT_RRTYPE_DS) { continue; } int ret = 0; if (new_ds) { ret = knot_rdataset_merge(&new_ds->rrs, &rr->rrs, cut->pool); } else { new_ds = knot_rrset_copy(rr, cut->pool); if (!new_ds) { return NULL; } } if (ret != 0) { knot_rrset_free(&new_ds, cut->pool); return NULL; } } return new_ds; }
_public_ int knot_sig0_sign(uint8_t *wire, size_t *wire_size, size_t wire_max_size, knot_dnssec_key_t *key) { knot_rrset_t *sig_rrset = sig0_create_rrset(); if (!sig_rrset) { return KNOT_ENOMEM; } // SIG(0) headers without signature uint8_t *sig_rdata = sig0_create_rdata(sig_rrset, key); if (!sig_rdata) { knot_rrset_free(&sig_rrset, NULL); return KNOT_ENOMEM; } // convert to wire uint8_t *wire_end = wire + *wire_size; size_t wire_avail_size = wire_max_size - *wire_size; size_t wire_sig_size = 0; int result = knot_rrset_to_wire(sig_rrset, wire_end, wire_avail_size, NULL); knot_rrset_free(&sig_rrset, NULL); if (result < 0) { return result; } wire_sig_size = result; // create signature result = sig0_write_signature(wire, *wire_size, wire_sig_size, key); if (result != KNOT_EOK) { return result; } uint16_t wire_arcount = knot_wire_get_arcount(wire); knot_wire_set_arcount(wire, wire_arcount + 1); *wire_size += wire_sig_size; return KNOT_EOK; }
static int knot_zone_diff_load_soas(const zone_contents_t *zone1, const zone_contents_t *zone2, changeset_t *changeset) { if (zone1 == NULL || zone2 == NULL || changeset == NULL) { return KNOT_EINVAL; } const zone_node_t *apex1 = zone1->apex; const zone_node_t *apex2 = zone2->apex; if (apex1 == NULL || apex2 == NULL) { return KNOT_EINVAL; } knot_rrset_t soa_rrset1 = node_rrset(apex1, KNOT_RRTYPE_SOA); knot_rrset_t soa_rrset2 = node_rrset(apex2, KNOT_RRTYPE_SOA); if (knot_rrset_empty(&soa_rrset1) || knot_rrset_empty(&soa_rrset2)) { return KNOT_EINVAL; } if (soa_rrset1.rrs.rr_count == 0 || soa_rrset2.rrs.rr_count == 0) { return KNOT_EINVAL; } int64_t soa_serial1 = knot_soa_serial(&soa_rrset1.rrs); int64_t soa_serial2 = knot_soa_serial(&soa_rrset2.rrs); if (serial_compare(soa_serial1, soa_serial2) == 0) { return KNOT_ENODIFF; } if (serial_compare(soa_serial1, soa_serial2) > 0) { return KNOT_ERANGE; } assert(changeset); changeset->soa_from = knot_rrset_copy(&soa_rrset1, NULL); if (changeset->soa_from == NULL) { return KNOT_ENOMEM; } changeset->soa_to = knot_rrset_copy(&soa_rrset2, NULL); if (changeset->soa_to == NULL) { knot_rrset_free(&changeset->soa_from, NULL); return KNOT_ENOMEM; } dbg_zonediff_verb("zone_diff: load_soas: SOAs diffed. " "(%"PRId64" -> %"PRId64")\n", soa_serial1, soa_serial2); return KNOT_EOK; }
static int wrk_resolve(lua_State *L) { struct worker_ctx *worker = wrk_luaget(L); if (!worker) { return 0; } /* Create query packet */ knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_EDNS_MAX_UDP_PAYLOAD, NULL); if (!pkt) { lua_pushstring(L, strerror(ENOMEM)); lua_error(L); } uint8_t dname[KNOT_DNAME_MAXLEN]; knot_dname_from_str(dname, lua_tostring(L, 1), sizeof(dname)); /* Check class and type */ uint16_t rrtype = lua_tointeger(L, 2); if (!lua_isnumber(L, 2)) { lua_pushstring(L, "invalid RR type"); lua_error(L); } uint16_t rrclass = lua_tointeger(L, 3); if (!lua_isnumber(L, 3)) { /* Default class is IN */ rrclass = KNOT_CLASS_IN; } knot_pkt_put_question(pkt, dname, rrclass, rrtype); knot_wire_set_rd(pkt->wire); /* Add OPT RR */ pkt->opt_rr = knot_rrset_copy(worker->engine->resolver.opt_rr, NULL); if (!pkt->opt_rr) { return kr_error(ENOMEM); } /* Add completion callback */ int ret = 0; unsigned options = lua_tointeger(L, 4); if (lua_isfunction(L, 5)) { /* Store callback in registry */ lua_pushvalue(L, 5); int cb = luaL_ref(L, LUA_REGISTRYINDEX); ret = worker_resolve(worker, pkt, options, resolve_callback, (void *) (intptr_t)cb); } else { ret = worker_resolve(worker, pkt, options, NULL, NULL); } knot_rrset_free(&pkt->opt_rr, NULL); knot_pkt_free(&pkt); lua_pushboolean(L, ret == 0); return 1; }
int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len, uint16_t tsig_rcode, const knot_rrset_t *tsig_rr) { /*! \todo Revise!! */ if (!msg || !msg_len || !tsig_rr) { return KNOT_EINVAL; } /*! \todo What key to use, when we do not sign? Does this even work? */ knot_rrset_t *tmp_tsig = knot_rrset_new(tsig_rr->owner, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, NULL); if (!tmp_tsig) { dbg_tsig("TSIG: tmp_tsig = NULL\n"); return KNOT_ENOMEM; } assert(tsig_rcode != KNOT_RCODE_BADTIME); tsig_create_rdata(tmp_tsig, tsig_rdata_alg_name(tsig_rr), 0, tsig_rcode); tsig_rdata_set_time_signed(tmp_tsig, tsig_rdata_time_signed(tsig_rr)); /* Comparing to BIND it was found out that the Fudge should always be * set to the server's value. */ tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT); /* Set original ID */ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); /* Set other len. */ tsig_rdata_set_other_data(tmp_tsig, 0, 0); /* Append TSIG RR. */ int ret = knot_tsig_append(msg, msg_len, msg_max_len, tsig_rr); /* key_name already referenced in RRSet, no need to free separately. */ knot_rrset_free(&tmp_tsig, NULL); return ret; }
/*! * \brief Create NSEC RR set. * * \param from Node that should contain the new RRSet * \param to Node that should be pointed to from 'from' * \param ttl Record TTL (SOA's minimum TTL). * * \return NSEC RR set, NULL on error. */ static knot_rrset_t *create_nsec_rrset(const zone_node_t *from, const zone_node_t *to, uint32_t ttl) { assert(from); assert(to); knot_rrset_t *rrset = knot_rrset_new(from->owner, KNOT_RRTYPE_NSEC, KNOT_CLASS_IN, NULL); if (!rrset) { return NULL; } // Create bitmap bitmap_t rr_types = { 0 }; bitmap_add_node_rrsets(&rr_types, from); bitmap_add_type(&rr_types, KNOT_RRTYPE_NSEC); bitmap_add_type(&rr_types, KNOT_RRTYPE_RRSIG); if (node_rrtype_exists(from, KNOT_RRTYPE_SOA)) { bitmap_add_type(&rr_types, KNOT_RRTYPE_DNSKEY); } // Create RDATA assert(to->owner); size_t next_owner_size = knot_dname_size(to->owner); size_t rdata_size = next_owner_size + bitmap_size(&rr_types); uint8_t rdata[rdata_size]; // Fill RDATA memcpy(rdata, to->owner, next_owner_size); bitmap_write(&rr_types, rdata + next_owner_size); int ret = knot_rrset_add_rdata(rrset, rdata, rdata_size, ttl, NULL); if (ret != KNOT_EOK) { knot_rrset_free(&rrset, NULL); return NULL; } return rrset; }
static knot_rrset_t *synth_rr(char *addr_str, synth_template_t *tpl, knot_pkt_t *pkt, struct query_data *qdata) { knot_rrset_t *rr = knot_rrset_new(qdata->name, 0, KNOT_CLASS_IN, &pkt->mm); if (rr == NULL) { return NULL; } /* Fill in the specific data. */ int ret = KNOT_ERROR; switch (tpl->type) { case SYNTH_REVERSE: ret = reverse_rr(addr_str, tpl, pkt, rr); break; case SYNTH_FORWARD: ret = forward_rr(addr_str, tpl, pkt, rr); break; default: break; } if (ret != KNOT_EOK) { knot_rrset_free(&rr, &pkt->mm); return NULL; } return rr; }
static void print_section_question(const knot_dname_t *owner, const uint16_t qclass, const uint16_t qtype, const style_t *style) { size_t buflen = 8192; char *buf = calloc(buflen, 1); // Don't print zero TTL. knot_dump_style_t qstyle = style->style; qstyle.empty_ttl = true; knot_rrset_t *question = knot_rrset_new(owner, qtype, qclass, NULL); if (knot_rrset_txt_dump_header(question, 0, buf, buflen, &qstyle) < 0) { WARN("can't print whole question section\n"); } printf("%s\n", buf); knot_rrset_free(&question, NULL); free(buf); }
int main(int argc, char *argv[]) { plan(25); /* Create memory pool context. */ int ret = 0; knot_mm_t mm; mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE); /* Create names and data. */ knot_dname_t* dnames[NAMECOUNT] = {0}; knot_rrset_t* rrsets[NAMECOUNT] = {0}; for (unsigned i = 0; i < NAMECOUNT; ++i) { dnames[i] = knot_dname_from_str_alloc(g_names[i]); } uint8_t *edns_str = (uint8_t *)"ab"; /* Create OPT RR. */ knot_rrset_t opt_rr; ret = knot_edns_init(&opt_rr, 1024, 0, 0, &mm); if (ret != KNOT_EOK) { skip_block(25, "Failed to initialize OPT RR."); return 0; } /* Add NSID */ ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_NSID, strlen((char *)edns_str), edns_str, &mm); if (ret != KNOT_EOK) { knot_rrset_clear(&opt_rr, &mm); skip_block(25, "Failed to add NSID to OPT RR."); return 0; } /* * Packet writer tests. */ /* Create packet. */ knot_pkt_t *out = knot_pkt_new(NULL, MM_DEFAULT_BLKSIZE, &mm); ok(out != NULL, "pkt: new"); /* Mark as response (not part of the test). */ knot_wire_set_qr(out->wire); /* Secure packet. */ const char *tsig_secret = "abcd"; knot_tsig_key_t tsig_key; tsig_key.algorithm = DNSSEC_TSIG_HMAC_MD5; tsig_key.name = dnames[0]; tsig_key.secret.data = (uint8_t *)strdup(tsig_secret); tsig_key.secret.size = strlen(tsig_secret); ret = knot_pkt_reserve(out, knot_tsig_wire_maxsize(&tsig_key)); ok(ret == KNOT_EOK, "pkt: set TSIG key"); /* Write question. */ ret = knot_pkt_put_question(out, dnames[0], KNOT_CLASS_IN, KNOT_RRTYPE_A); ok(ret == KNOT_EOK, "pkt: put question"); /* Add OPT to packet (empty NSID). */ ret = knot_pkt_reserve(out, knot_edns_wire_size(&opt_rr)); ok(ret == KNOT_EOK, "pkt: reserve OPT RR"); /* Begin ANSWER section. */ ret = knot_pkt_begin(out, KNOT_ANSWER); ok(ret == KNOT_EOK, "pkt: begin ANSWER"); /* Write ANSWER section. */ rrsets[0] = knot_rrset_new(dnames[0], KNOT_RRTYPE_A, KNOT_CLASS_IN, NULL); knot_dname_free(&dnames[0], NULL); knot_rrset_add_rdata(rrsets[0], RDVAL(0), RDLEN(0), TTL, NULL); ret = knot_pkt_put(out, KNOT_COMPR_HINT_QNAME, rrsets[0], 0); ok(ret == KNOT_EOK, "pkt: write ANSWER"); /* Begin AUTHORITY. */ ret = knot_pkt_begin(out, KNOT_AUTHORITY); ok(ret == KNOT_EOK, "pkt: begin AUTHORITY"); /* Write rest to AUTHORITY. */ ret = KNOT_EOK; for (unsigned i = 1; i < NAMECOUNT; ++i) { rrsets[i] = knot_rrset_new(dnames[i], KNOT_RRTYPE_NS, KNOT_CLASS_IN, NULL); knot_dname_free(&dnames[i], NULL); knot_rrset_add_rdata(rrsets[i], RDVAL(i), RDLEN(i), TTL, NULL); ret |= knot_pkt_put(out, KNOT_COMPR_HINT_NONE, rrsets[i], 0); } ok(ret == KNOT_EOK, "pkt: write AUTHORITY(%u)", NAMECOUNT - 1); /* Begin ADDITIONALS */ ret = knot_pkt_begin(out, KNOT_ADDITIONAL); ok(ret == KNOT_EOK, "pkt: begin ADDITIONALS"); /* Encode OPT RR. */ ret = knot_pkt_put(out, KNOT_COMPR_HINT_NONE, &opt_rr, 0); ok(ret == KNOT_EOK, "pkt: write OPT RR"); /* * Packet reader tests. */ /* Create new packet from query packet. */ knot_pkt_t *in = knot_pkt_new(out->wire, out->size, &out->mm); ok(in != NULL, "pkt: create packet for parsing"); /* Read packet header. */ ret = knot_pkt_parse_question(in); ok(ret == KNOT_EOK, "pkt: read header"); /* Read packet payload. */ ret = knot_pkt_parse_payload(in, 0); ok(ret == KNOT_EOK, "pkt: read payload"); /* Compare parsed packet to written packet. */ packet_match(in, out); /* * Copied packet tests. */ knot_pkt_t *copy = knot_pkt_new(NULL, in->max_size, &in->mm); ret = knot_pkt_copy(copy, in); ok(ret == KNOT_EOK, "pkt: create packet copy"); /* Compare copied packet to original. */ packet_match(in, copy); /* Free packets. */ knot_pkt_free(©); knot_pkt_free(&out); knot_pkt_free(&in); ok(in == NULL && out == NULL && copy == NULL, "pkt: free"); /* Free extra data. */ for (unsigned i = 0; i < NAMECOUNT; ++i) { knot_rrset_free(&rrsets[i], NULL); } free(tsig_key.secret.data); mp_delete((struct mempool *)mm.ctx); return 0; }
static int validate_records(struct kr_query *qry, knot_pkt_t *answer, knot_mm_t *pool, bool has_nsec3) { if (!qry->zone_cut.key) { DEBUG_MSG(qry, "<= no DNSKEY, can't validate\n"); return kr_error(EBADMSG); } kr_rrset_validation_ctx_t vctx = { .pkt = answer, .section_id = KNOT_ANSWER, .keys = qry->zone_cut.key, .zone_name = qry->zone_cut.name, .timestamp = qry->timestamp.tv_sec, .has_nsec3 = has_nsec3, .flags = 0, .result = 0 }; int ret = validate_section(&vctx, pool); if (ret != 0) { return ret; } uint32_t an_flags = vctx.flags; vctx.section_id = KNOT_AUTHORITY; /* zone_name can be changed by validate_section(), restore it */ vctx.zone_name = qry->zone_cut.name; vctx.flags = 0; vctx.result = 0; ret = validate_section(&vctx, pool); if (ret != 0) { return ret; } /* Records were validated. * If there is wildcard expansion in answer, flag the query. */ if (an_flags & KR_DNSSEC_VFLG_WEXPAND) { qry->flags |= QUERY_DNSSEC_WEXPAND; } return ret; } static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer, bool has_nsec3) { /* Merge DNSKEY records from answer that are below/at current cut. */ bool updated_key = false; const knot_pktsection_t *an = knot_pkt_section(answer, KNOT_ANSWER); for (unsigned i = 0; i < an->count; ++i) { const knot_rrset_t *rr = knot_pkt_rr(an, i); if ((rr->type != KNOT_RRTYPE_DNSKEY) || !knot_dname_in(qry->zone_cut.name, rr->owner)) { continue; } /* Merge with zone cut (or replace ancestor key). */ if (!qry->zone_cut.key || !knot_dname_is_equal(qry->zone_cut.key->owner, rr->owner)) { qry->zone_cut.key = knot_rrset_copy(rr, qry->zone_cut.pool); if (!qry->zone_cut.key) { return kr_error(ENOMEM); } updated_key = true; } else { int ret = knot_rdataset_merge(&qry->zone_cut.key->rrs, &rr->rrs, qry->zone_cut.pool); if (ret != 0) { knot_rrset_free(&qry->zone_cut.key, qry->zone_cut.pool); return ret; } updated_key = true; } } /* Check if there's a key for current TA. */ if (updated_key && !(qry->flags & QUERY_CACHED)) { kr_rrset_validation_ctx_t vctx = { .pkt = answer, .section_id = KNOT_ANSWER, .keys = qry->zone_cut.key, .zone_name = qry->zone_cut.name, .timestamp = qry->timestamp.tv_sec, .has_nsec3 = has_nsec3, .flags = 0, .result = 0 }; int ret = kr_dnskeys_trusted(&vctx, qry->zone_cut.trust_anchor); if (ret != 0) { knot_rrset_free(&qry->zone_cut.key, qry->zone_cut.pool); return ret; } if (vctx.flags & KR_DNSSEC_VFLG_WEXPAND) { qry->flags |= QUERY_DNSSEC_WEXPAND; } } return kr_ok(); }
int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, const uint8_t *prev_digest, size_t prev_digest_len, uint8_t *digest, size_t *digest_len, const knot_tsig_key_t *key, uint8_t *to_sign, size_t to_sign_len) { if (!msg || !msg_len || !key || !digest || !digest_len) { return KNOT_EINVAL; } uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE]; size_t digest_tmp_len = 0; knot_rrset_t *tmp_tsig = knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, NULL); if (!tmp_tsig) { return KNOT_ENOMEM; } /* Create rdata for TSIG RR. */ tsig_create_rdata(tmp_tsig, tsig_alg_to_dname(key->algorithm), knot_tsig_digest_length(key->algorithm), 0); tsig_rdata_store_current_time(tmp_tsig); tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT); /* Create wire to be signed. */ size_t wire_len = prev_digest_len + to_sign_len + KNOT_TSIG_TIMERS_LENGTH + 2; uint8_t *wire = malloc(wire_len); if (!wire) { ERR_ALLOC_FAILED; knot_rrset_free(&tmp_tsig, NULL); return KNOT_ENOMEM; } memset(wire, 0, wire_len); /* Write previous digest length. */ knot_wire_write_u16(wire, prev_digest_len); /* Write previous digest. */ memcpy(wire + 2, prev_digest, sizeof(uint8_t) * prev_digest_len); /* Write original message. */ memcpy(wire + prev_digest_len + 2, to_sign, to_sign_len); /* Write timers. */ knot_tsig_wire_write_timers(wire + prev_digest_len + to_sign_len + 2, tmp_tsig); dbg_tsig_detail("Previous digest: \n"); dbg_tsig_hex_detail((char *)prev_digest, prev_digest_len); dbg_tsig_detail("Timers: \n"); dbg_tsig_hex_detail((char *)(wire + prev_digest_len + *msg_len), KNOT_TSIG_TIMERS_LENGTH); int ret = KNOT_ERROR; ret = knot_tsig_compute_digest(wire, wire_len, digest_tmp, &digest_tmp_len, key); /* No matter how the function did, this data is no longer needed. */ free(wire); if (ret != KNOT_EOK) { knot_rrset_free(&tmp_tsig, NULL); *digest_len = 0; return ret; } if (digest_tmp_len > *digest_len) { knot_rrset_free(&tmp_tsig, NULL); *digest_len = 0; return KNOT_ESPACE; } /* Set the MAC. */ tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp); /* Set original id. */ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); /* Set other data. */ tsig_rdata_set_other_data(tmp_tsig, 0, NULL); dbg_tsig_verb("Message max length: %zu, message length: %zu\n", msg_max_len, *msg_len); size_t tsig_wire_size = 0; uint16_t rr_count = 0; ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, &tsig_wire_size, msg_max_len - *msg_len, &rr_count, NULL); if (ret != KNOT_EOK) { knot_rrset_free(&tmp_tsig, NULL); *digest_len = 0; return ret; } /* This should not happen, at least one rr has to be converted. */ if (rr_count == 0) { knot_rrset_free(&tmp_tsig, NULL); return KNOT_EINVAL; } knot_rrset_free(&tmp_tsig, NULL); *msg_len += tsig_wire_size; uint16_t arcount = knot_wire_get_arcount(msg); knot_wire_set_arcount(msg, ++arcount); memcpy(digest, digest_tmp, digest_tmp_len); *digest_len = digest_tmp_len; return KNOT_EOK; }
int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len, const uint8_t *request_mac, size_t request_mac_len, uint8_t *digest, size_t *digest_len, const knot_tsig_key_t *key, uint16_t tsig_rcode, uint64_t request_time_signed) { if (!msg || !msg_len || !key || digest == NULL || digest_len == NULL) { return KNOT_EINVAL; } knot_rrset_t *tmp_tsig = knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, NULL); if (!tmp_tsig) { dbg_tsig("TSIG: tmp_tsig = NULL\n"); return KNOT_ENOMEM; } /* Create rdata for TSIG RR. */ uint16_t rdata_rcode = 0; if (tsig_rcode == KNOT_RCODE_BADTIME) rdata_rcode = tsig_rcode; tsig_create_rdata(tmp_tsig, tsig_alg_to_dname(key->algorithm), knot_tsig_digest_length(key->algorithm), rdata_rcode); /* Distinguish BADTIME response. */ if (tsig_rcode == KNOT_RCODE_BADTIME) { /* Set client's time signed into the time signed field. */ tsig_rdata_set_time_signed(tmp_tsig, request_time_signed); /* Store current time into Other data. */ uint8_t time_signed[6]; time_t curr_time = time(NULL); uint64_t time64 = curr_time; knot_wire_write_u48(time_signed, time64); tsig_rdata_set_other_data(tmp_tsig, 6, time_signed); } else { tsig_rdata_store_current_time(tmp_tsig); /* Set other len. */ tsig_rdata_set_other_data(tmp_tsig, 0, 0); } tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT); /* Set original ID */ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE]; size_t digest_tmp_len = 0; int ret = KNOT_ERROR; ret = knot_tsig_create_sign_wire(msg, *msg_len, /*msg_max_len,*/ request_mac, request_mac_len, digest_tmp, &digest_tmp_len, tmp_tsig, key); if (ret != KNOT_EOK) { dbg_tsig("TSIG: could not create wire or sign wire: %s\n", knot_strerror(ret)); knot_rrset_free(&tmp_tsig, NULL); return ret; } /* Set the digest. */ size_t tsig_wire_len = 0; dbg_tsig("TSIG: msg_len=%zu, msg_max_len=%zu, tsig_max_len=%zu\n", *msg_len, msg_max_len, tsig_wire_len); uint16_t rr_count = 0; tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp); /* Write RRSet to wire */ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, &tsig_wire_len, msg_max_len - *msg_len, &rr_count, NULL); if (ret != KNOT_EOK) { dbg_tsig("TSIG: rrset_to_wire = %s\n", knot_strerror(ret)); *digest_len = 0; knot_rrset_free(&tmp_tsig, NULL); return ret; } knot_rrset_free(&tmp_tsig, NULL); dbg_tsig("TSIG: written TSIG RR (wire len %zu)\n", tsig_wire_len); *msg_len += tsig_wire_len; uint16_t arcount = knot_wire_get_arcount(msg); knot_wire_set_arcount(msg, ++arcount); // everything went ok, save the digest to the output parameter memcpy(digest, digest_tmp, digest_tmp_len); *digest_len = digest_tmp_len; return KNOT_EOK; }
int main(int argc, char *argv[]) { plan(23); // Test with NULL changeset ok(changeset_size(NULL) == 0, "changeset: NULL size"); ok(changeset_empty(NULL), "changeset: NULL empty"); // Test creation. knot_dname_t *d = knot_dname_from_str_alloc("test."); assert(d); changeset_t *ch = changeset_new(d); knot_dname_free(&d, NULL); ok(ch != NULL, "changeset: new"); ok(changeset_empty(ch), "changeset: empty"); ch->soa_to = (knot_rrset_t *)0xdeadbeef; ok(!changeset_empty(ch), "changseset: empty SOA"); ch->soa_to = NULL; ok(changeset_size(ch) == 0, "changeset: empty size"); // Test additions. d = knot_dname_from_str_alloc("non.terminals.test."); assert(d); knot_rrset_t *apex_txt_rr = knot_rrset_new(d, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, NULL); assert(apex_txt_rr); uint8_t data[8] = "\7teststr"; knot_rrset_add_rdata(apex_txt_rr, data, sizeof(data), 3600, NULL); int ret = changeset_add_rrset(ch, apex_txt_rr); ok(ret == KNOT_EOK, "changeset: add RRSet"); ok(changeset_size(ch) == 1, "changeset: size add"); ret = changeset_rem_rrset(ch, apex_txt_rr); ok(ret == KNOT_EOK, "changeset: rem RRSet"); ok(changeset_size(ch) == 2, "changeset: size remove"); ok(!changeset_empty(ch), "changeset: not empty"); // Add another RR to node. knot_rrset_t *apex_spf_rr = knot_rrset_new(d, KNOT_RRTYPE_SPF, KNOT_CLASS_IN, NULL); assert(apex_spf_rr); knot_rrset_add_rdata(apex_spf_rr, data, sizeof(data), 3600, NULL); ret = changeset_add_rrset(ch, apex_spf_rr); ok(ret == KNOT_EOK, "changeset: add multiple"); // Add another node. knot_dname_free(&d, NULL); d = knot_dname_from_str_alloc("here.come.more.non.terminals.test"); assert(d); knot_rrset_t *other_rr = knot_rrset_new(d, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, NULL); assert(other_rr); knot_rrset_add_rdata(other_rr, data, sizeof(data), 3600, NULL); ret = changeset_add_rrset(ch, other_rr); ok(ret == KNOT_EOK, "changeset: remove multiple"); // Test add traversal. changeset_iter_t it; ret = changeset_iter_add(&it, ch, true); ok(ret == KNOT_EOK, "changeset: create iter add"); // Order: non.terminals.test. TXT, SPF, here.come.more.non.terminals.test. TXT. knot_rrset_t iter = changeset_iter_next(&it); bool trav_ok = knot_rrset_equal(&iter, apex_txt_rr, KNOT_RRSET_COMPARE_WHOLE); iter = changeset_iter_next(&it); trav_ok = trav_ok && knot_rrset_equal(&iter, apex_spf_rr, KNOT_RRSET_COMPARE_WHOLE); iter = changeset_iter_next(&it); trav_ok = trav_ok && knot_rrset_equal(&iter, other_rr, KNOT_RRSET_COMPARE_WHOLE); ok(trav_ok, "changeset: add traversal"); iter = changeset_iter_next(&it); changeset_iter_clear(&it); ok(knot_rrset_empty(&iter), "changeset: traversal: skip non-terminals"); // Test remove traversal. ret = changeset_iter_rem(&it, ch, false); ok(ret == KNOT_EOK, "changeset: create iter rem"); iter = changeset_iter_next(&it); ok(knot_rrset_equal(&iter, apex_txt_rr, KNOT_RRSET_COMPARE_WHOLE), "changeset: rem traversal"); changeset_iter_clear(&it); // Test all traversal - just count. ret = changeset_iter_all(&it, ch, false); ok(ret == KNOT_EOK, "changest: create iter all"); size_t size = 0; iter = changeset_iter_next(&it); while (!knot_rrset_empty(&iter)) { ++size; iter = changeset_iter_next(&it); } changeset_iter_clear(&it); ok(size == 4, "changeset: iter all"); // Create new changeset. knot_dname_free(&d, NULL); d = knot_dname_from_str_alloc("test."); assert(d); changeset_t *ch2 = changeset_new(d); knot_dname_free(&d, NULL); assert(ch2); // Add something to add section. knot_dname_free(&apex_txt_rr->owner, NULL); apex_txt_rr->owner = knot_dname_from_str_alloc("something.test."); assert(apex_txt_rr->owner); ret = changeset_add_rrset(ch2, apex_txt_rr); assert(ret == KNOT_EOK); // Add something to remove section. knot_dname_free(&apex_txt_rr->owner, NULL); apex_txt_rr->owner = knot_dname_from_str_alloc("and.now.for.something.completely.different.test."); assert(apex_txt_rr->owner); ret = changeset_rem_rrset(ch2, apex_txt_rr); assert(ret == KNOT_EOK); // Test merge. ret = changeset_merge(ch, ch2); ok(ret == KNOT_EOK && changeset_size(ch) == 6, "changeset: merge"); // Test cleanup. changeset_clear(ch); ok(changeset_empty(ch), "changeset: clear"); free(ch); list_t chgs; init_list(&chgs); add_head(&chgs, &ch2->n); changesets_clear(&chgs); ok(changeset_empty(ch2), "changeset: clear list"); free(ch2); knot_rrset_free(&apex_txt_rr, NULL); knot_rrset_free(&apex_spf_rr, NULL); knot_rrset_free(&other_rr, NULL); return 0; }