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; }
/*! * \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; }
/*! \brief Create zone query packet. */ static knot_pkt_t *zone_query(const zone_t *zone, uint16_t pkt_type, knot_mm_t *mm) { /* Determine query type and opcode. */ uint16_t query_type = KNOT_RRTYPE_SOA; uint16_t opcode = KNOT_OPCODE_QUERY; switch(pkt_type) { case KNOT_QUERY_AXFR: query_type = KNOT_RRTYPE_AXFR; break; case KNOT_QUERY_IXFR: query_type = KNOT_RRTYPE_IXFR; break; case KNOT_QUERY_NOTIFY: opcode = KNOT_OPCODE_NOTIFY; break; } knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, mm); if (pkt == NULL) { return NULL; } knot_wire_set_id(pkt->wire, dnssec_random_uint16_t()); knot_wire_set_opcode(pkt->wire, opcode); if (pkt_type == KNOT_QUERY_NOTIFY) { knot_wire_set_aa(pkt->wire); } knot_pkt_put_question(pkt, zone->name, KNOT_CLASS_IN, query_type); /* Put current SOA (optional). */ zone_contents_t *contents = zone->contents; if (pkt_type == KNOT_QUERY_IXFR) { /* RFC1995, SOA in AUTHORITY. */ knot_pkt_begin(pkt, KNOT_AUTHORITY); knot_rrset_t soa_rr = node_rrset(contents->apex, KNOT_RRTYPE_SOA); knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, &soa_rr, 0); } else if (pkt_type == KNOT_QUERY_NOTIFY) { /* RFC1996, SOA in ANSWER. */ knot_pkt_begin(pkt, KNOT_ANSWER); knot_rrset_t soa_rr = node_rrset(contents->apex, KNOT_RRTYPE_SOA); knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, &soa_rr, 0); } return pkt; }
int xfr_process_list(knot_pkt_t *pkt, xfr_put_cb process_item, struct query_data *qdata) { if (pkt == NULL || qdata == NULL || qdata->ext == NULL) { return KNOT_EINVAL; } int ret = KNOT_EOK; mm_ctx_t *mm = qdata->mm; struct xfr_proc *xfer = qdata->ext; zone_contents_t *zone = qdata->zone->contents; knot_rrset_t soa_rr = node_rrset(zone->apex, KNOT_RRTYPE_SOA); /* Prepend SOA on first packet. */ if (xfer->npkts == 0) { ret = knot_pkt_put(pkt, 0, &soa_rr, KNOT_PF_NOTRUNC); if (ret != KNOT_EOK) { return ret; } } /* Process all items in the list. */ while (!EMPTY_LIST(xfer->nodes)) { ptrnode_t *head = HEAD(xfer->nodes); ret = process_item(pkt, head->d, xfer); if (ret == KNOT_EOK) { /* Finished. */ /* Complete change set. */ rem_node((node_t *)head); mm_free(mm, head); } else { /* Packet full or other error. */ break; } } /* Append SOA on last packet. */ if (ret == KNOT_EOK) { ret = knot_pkt_put(pkt, 0, &soa_rr, KNOT_PF_NOTRUNC); } /* Update counters. */ xfer->npkts += 1; xfer->nbytes += pkt->size; return ret; }
/*!< \todo this could be generic function for adding / removing. */ static int knot_zone_diff_node(zone_node_t **node_ptr, void *data) { if (node_ptr == NULL || *node_ptr == NULL || data == NULL) { return KNOT_EINVAL; } zone_node_t *node = *node_ptr; struct zone_diff_param *param = (struct zone_diff_param *)data; if (param->changeset == NULL) { return KNOT_EINVAL; } /* * First, we have to search the second tree to see if there's according * node, if not, the whole node has been removed. */ const zone_node_t *node_in_second_tree = NULL; const knot_dname_t *node_owner = node->owner; assert(node_owner); zone_tree_find(param->nodes, node_owner, &node_in_second_tree); if (node_in_second_tree == NULL) { return knot_zone_diff_remove_node(param->changeset, node); } assert(node_in_second_tree != node); /* The nodes are in both trees, we have to diff each RRSet. */ if (node->rrset_count == 0) { /* * If there are no RRs in the first tree, all of the RRs * in the second tree will have to be inserted to ADD section. */ return knot_zone_diff_add_node(node_in_second_tree, param->changeset); } for (unsigned i = 0; i < node->rrset_count; i++) { /* Search for the RRSet in the node from the second tree. */ knot_rrset_t rrset = node_rrset_at(node, i); /* SOAs are handled explicitely. */ if (rrset.type == KNOT_RRTYPE_SOA) { continue; } knot_rrset_t rrset_from_second_node = node_rrset(node_in_second_tree, rrset.type); if (knot_rrset_empty(&rrset_from_second_node)) { /* RRSet has been removed. Make a copy and remove. */ int ret = changeset_rem_rrset( param->changeset, &rrset); if (ret != KNOT_EOK) { return ret; } } else { /* Diff RRSets. */ int ret = knot_zone_diff_rrsets(&rrset, &rrset_from_second_node, param->changeset); if (ret != KNOT_EOK) { return ret; } } } for (unsigned i = 0; i < node_in_second_tree->rrset_count; i++) { /* Search for the RRSet in the node from the second tree. */ knot_rrset_t rrset = node_rrset_at(node_in_second_tree, i); /* SOAs are handled explicitely. */ if (rrset.type == KNOT_RRTYPE_SOA) { continue; } knot_rrset_t rrset_from_first_node = node_rrset(node, rrset.type); if (knot_rrset_empty(&rrset_from_first_node)) { /* RRSet has been added. Make a copy and add. */ int ret = changeset_add_rrset( param->changeset, &rrset); if (ret != KNOT_EOK) { return ret; } } } return KNOT_EOK; }
int main(int argc, char *argv[]) { plan(8*6 + 3); /* exec_query = 6 TAP tests */ /* Create processing context. */ knot_process_t query_ctx; memset(&query_ctx, 0, sizeof(knot_process_t)); mm_ctx_mempool(&query_ctx.mm, sizeof(knot_pkt_t)); /* Create name server. */ server_t server; server_init(&server); server.opt_rr = knot_edns_new(); knot_edns_set_version(server.opt_rr, EDNS_VERSION); knot_edns_set_payload(server.opt_rr, 4096); conf()->identity = strdup("bogus.ns"); conf()->version = strdup("0.11"); /* Insert root zone. */ create_root_zone(&server, &query_ctx.mm); zone_t *zone = knot_zonedb_find(server.zone_db, ROOT_DNAME); /* Prepare. */ int state = NS_PROC_FAIL; uint8_t query_wire[KNOT_WIRE_MAX_PKTSIZE]; uint16_t query_len = KNOT_WIRE_MAX_PKTSIZE; knot_pkt_t *query = knot_pkt_new(query_wire, query_len, &query_ctx.mm); /* Create query processing parameter. */ struct sockaddr_storage ss; memset(&ss, 0, sizeof(struct sockaddr_storage)); sockaddr_set(&ss, AF_INET, "127.0.0.1", 53); struct process_query_param param = {0}; param.query_source = &ss; param.server = &server; /* Query processor (CH zone) */ state = knot_process_begin(&query_ctx, ¶m, NS_PROC_QUERY); const uint8_t chaos_dname[] = "\2""id""\6""server"; /* id.server */ knot_pkt_clear(query); knot_pkt_put_question(query, chaos_dname, KNOT_CLASS_CH, KNOT_RRTYPE_TXT); exec_query(&query_ctx, "CH TXT", query->wire, query->size, KNOT_RCODE_NOERROR); /* Query processor (valid input). */ state = knot_process_reset(&query_ctx); knot_pkt_clear(query); knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA); exec_query(&query_ctx, "IN/root", query->wire, query->size, KNOT_RCODE_NOERROR); /* Query processor (-1 bytes, not enough data). */ state = knot_process_reset(&query_ctx); exec_query(&query_ctx, "IN/few-data", query->wire, query->size - 1, KNOT_RCODE_FORMERR); /* Query processor (+1 bytes trailing). */ state = knot_process_reset(&query_ctx); query->wire[query->size] = '\1'; /* Initialize the "garbage" value. */ exec_query(&query_ctx, "IN/trail-garbage", query->wire, query->size + 1, KNOT_RCODE_FORMERR); /* Forge NOTIFY query from SOA query. */ state = knot_process_reset(&query_ctx); knot_wire_set_opcode(query->wire, KNOT_OPCODE_NOTIFY); exec_query(&query_ctx, "IN/notify", query->wire, query->size, KNOT_RCODE_NOTAUTH); /* Forge AXFR query. */ knot_process_reset(&query_ctx); knot_pkt_clear(query); knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_AXFR); exec_query(&query_ctx, "IN/axfr", query->wire, query->size, KNOT_RCODE_NOTAUTH); /* Forge IXFR query (badly formed, no SOA in AUTHORITY section). */ knot_process_reset(&query_ctx); knot_pkt_clear(query); knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_IXFR); exec_query(&query_ctx, "IN/ixfr-formerr", query->wire, query->size, KNOT_RCODE_FORMERR); /* Forge IXFR query (well formed). */ knot_process_reset(&query_ctx); /* Append SOA RR. */ knot_rrset_t soa_rr = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA); knot_pkt_begin(query, KNOT_AUTHORITY); knot_pkt_put(query, COMPR_HINT_NONE, &soa_rr, 0); exec_query(&query_ctx, "IN/ixfr", query->wire, query->size, KNOT_RCODE_NOTAUTH); /* \note Tests below are not possible without proper zone and zone data. */ /* #189 Process UPDATE query. */ /* #189 Process AXFR client. */ /* #189 Process IXFR client. */ /* Query processor (smaller than DNS header, ignore). */ state = knot_process_reset(&query_ctx); knot_pkt_clear(query); knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA); state = knot_process_in(query->wire, KNOT_WIRE_HEADER_SIZE - 1, &query_ctx); ok(state == NS_PROC_NOOP, "ns: IN/less-than-header query ignored"); /* Query processor (response, ignore). */ state = knot_process_reset(&query_ctx); knot_wire_set_qr(query->wire); state = knot_process_in(query->wire, query->size, &query_ctx); ok(state == NS_PROC_NOOP, "ns: IN/less-than-header query ignored"); /* Finish. */ state = knot_process_finish(&query_ctx); ok(state == NS_PROC_NOOP, "ns: processing end" ); /* Cleanup. */ mp_delete((struct mempool *)query_ctx.mm.ctx); server_deinit(&server); return 0; }
int main(int argc, char *argv[]) { plan(8*6 + 4); /* exec_query = 6 TAP tests */ knot_mm_t mm; mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE); /* Create processing context. */ knot_layer_t proc; memset(&proc, 0, sizeof(knot_layer_t)); proc.mm = &mm; /* Create fake server environment. */ server_t server; int ret = create_fake_server(&server, proc.mm); ok(ret == KNOT_EOK, "ns: fake server initialization"); zone_t *zone = knot_zonedb_find(server.zone_db, ROOT_DNAME); /* Prepare. */ knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, proc.mm); /* Create query processing parameter. */ struct sockaddr_storage ss; memset(&ss, 0, sizeof(struct sockaddr_storage)); sockaddr_set(&ss, AF_INET, "127.0.0.1", 53); struct process_query_param param = {0}; param.remote = &ss; param.server = &server; /* Query processor (CH zone) */ knot_layer_begin(&proc, NS_PROC_QUERY, ¶m); knot_pkt_clear(query); knot_pkt_put_question(query, IDSERVER_DNAME, KNOT_CLASS_CH, KNOT_RRTYPE_TXT); exec_query(&proc, "CH TXT", query, KNOT_RCODE_NOERROR); /* Query processor (valid input). */ knot_layer_reset(&proc); knot_pkt_clear(query); knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA); exec_query(&proc, "IN/root", query, KNOT_RCODE_NOERROR); /* Query processor (-1 bytes, not enough data). */ knot_layer_reset(&proc); query->size -= 1; exec_query(&proc, "IN/few-data", query, KNOT_RCODE_FORMERR); query->size += 1; /* Query processor (+1 bytes trailing). */ knot_layer_reset(&proc); query->wire[query->size] = '\1'; /* Initialize the "garbage" value. */ query->size += 1; exec_query(&proc, "IN/trail-garbage", query, KNOT_RCODE_FORMERR); query->size -= 1; /* Forge NOTIFY query from SOA query. */ knot_layer_reset(&proc); knot_wire_set_opcode(query->wire, KNOT_OPCODE_NOTIFY); exec_query(&proc, "IN/notify", query, KNOT_RCODE_NOTAUTH); /* Forge AXFR query. */ knot_layer_reset(&proc); knot_pkt_clear(query); knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_AXFR); exec_query(&proc, "IN/axfr", query, KNOT_RCODE_NOTAUTH); /* Forge IXFR query (badly formed, no SOA in AUTHORITY section). */ knot_layer_reset(&proc); knot_pkt_clear(query); knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_IXFR); exec_query(&proc, "IN/ixfr-formerr", query, KNOT_RCODE_FORMERR); /* Forge IXFR query (well formed). */ knot_layer_reset(&proc); knot_pkt_clear(query); knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_IXFR); /* Append SOA RR. */ knot_rrset_t soa_rr = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA); knot_pkt_begin(query, KNOT_AUTHORITY); knot_pkt_put(query, KNOT_COMPR_HINT_NONE, &soa_rr, 0); exec_query(&proc, "IN/ixfr", query, KNOT_RCODE_NOTAUTH); /* \note Tests below are not possible without proper zone and zone data. */ /* #189 Process UPDATE query. */ /* #189 Process AXFR client. */ /* #189 Process IXFR client. */ /* Query processor (smaller than DNS header, ignore). */ knot_layer_reset(&proc); knot_pkt_clear(query); knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA); size_t orig_query_size = query->size; query->size = KNOT_WIRE_HEADER_SIZE - 1; int state = knot_layer_consume(&proc, query); ok(state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored"); query->size = orig_query_size; /* Query processor (response, ignore). */ knot_layer_reset(&proc); knot_wire_set_qr(query->wire); state = knot_layer_consume(&proc, query); ok(state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored"); /* Finish. */ state = knot_layer_finish(&proc); ok(state == KNOT_STATE_NOOP, "ns: processing end" ); /* Cleanup. */ mp_delete((struct mempool *)mm.ctx); server_deinit(&server); conf_free(conf()); return 0; }