diff options
Diffstat (limited to 'src/libknot')
-rw-r--r-- | src/libknot/nameserver/name-server.c | 376 | ||||
-rw-r--r-- | src/libknot/nameserver/name-server.h | 30 | ||||
-rw-r--r-- | src/libknot/packet/packet.c | 16 | ||||
-rw-r--r-- | src/libknot/packet/packet.h | 7 | ||||
-rw-r--r-- | src/libknot/rdata.c | 16 | ||||
-rw-r--r-- | src/libknot/rdata.h | 1 | ||||
-rw-r--r-- | src/libknot/rrset.c | 39 | ||||
-rw-r--r-- | src/libknot/rrset.h | 2 | ||||
-rw-r--r-- | src/libknot/tsig-op.c | 644 | ||||
-rw-r--r-- | src/libknot/tsig-op.h | 15 | ||||
-rw-r--r-- | src/libknot/tsig.c | 45 | ||||
-rw-r--r-- | src/libknot/tsig.h | 4 | ||||
-rw-r--r-- | src/libknot/updates/xfr-in.c | 82 | ||||
-rw-r--r-- | src/libknot/updates/xfr-in.h | 2 | ||||
-rw-r--r-- | src/libknot/util/conv.c | 325 | ||||
-rw-r--r-- | src/libknot/util/conv.h | 44 | ||||
-rw-r--r-- | src/libknot/util/debug.h | 43 | ||||
-rw-r--r-- | src/libknot/util/error.h | 3 | ||||
-rw-r--r-- | src/libknot/util/libknot_error.c | 1 | ||||
-rw-r--r-- | src/libknot/zone/zone-contents.c | 4 |
20 files changed, 1085 insertions, 614 deletions
diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c index f88f802..f353ee7 100644 --- a/src/libknot/nameserver/name-server.c +++ b/src/libknot/nameserver/name-server.c @@ -619,17 +619,35 @@ static void ns_put_authority_ns(const knot_zone_contents_t *zone, * \param zone Zone to take the SOA RRSet from. * \param resp Response where to add the RRSet. */ -static void ns_put_authority_soa(const knot_zone_contents_t *zone, +static int ns_put_authority_soa(const knot_zone_contents_t *zone, knot_packet_t *resp) { const knot_rrset_t *soa_rrset = knot_node_rrset( knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA); assert(soa_rrset != NULL); - knot_response_add_rrset_authority(resp, soa_rrset, 0, 0, 0); - ns_add_rrsigs(soa_rrset, resp, - knot_node_owner(knot_zone_contents_apex(zone)), - knot_response_add_rrset_authority, 1); + // if SOA's TTL is larger than MINIMUM, copy the RRSet and set + // MINIMUM as TTL + uint32_t min = knot_rdata_soa_minimum(knot_rrset_rdata(soa_rrset)); + if (min < knot_rrset_ttl(soa_rrset)) { + knot_rrset_t *soa_copy = NULL; + knot_rrset_deep_copy(soa_rrset, &soa_copy); + CHECK_ALLOC_LOG(soa_copy, KNOT_ENOMEM); + + knot_rrset_set_ttl(soa_copy, min); + soa_rrset = soa_copy; + } + + assert(soa_rrset != NULL); + + int ret = knot_response_add_rrset_authority(resp, soa_rrset, 0, 0, 0); + if (ret == KNOT_EOK) { + ret = ns_add_rrsigs(soa_rrset, resp, + knot_node_owner(knot_zone_contents_apex(zone)), + knot_response_add_rrset_authority, 1); + } + + return ret; } /*----------------------------------------------------------------------------*/ @@ -1637,13 +1655,15 @@ static void ns_add_dnskey(const knot_node_t *apex, knot_packet_t *resp) * \todo Describe the answering logic in detail. */ static int ns_answer_from_zone(const knot_zone_contents_t *zone, - const knot_dname_t *qname, uint16_t qtype, knot_packet_t *resp) { const knot_node_t *node = NULL, *closest_encloser = NULL, *previous = NULL; int cname = 0, auth_soa = 0, ret = 0, find_ret = 0; + const knot_dname_t *qname = knot_packet_qname(resp); + uint16_t qtype = knot_packet_qtype(resp); + search: #ifdef USE_HASH_TABLE /*! \todo Check version. */ @@ -1839,20 +1859,20 @@ finalize: * \retval KNOT_EOK * \retval NS_ERR_SERVFAIL */ -static int ns_answer(knot_zonedb_t *db, knot_packet_t *resp) -{ - const knot_dname_t *qname = knot_packet_qname(resp); - assert(qname != NULL); - - uint16_t qtype = knot_packet_qtype(resp); -dbg_ns_exec( - char *name_str = knot_dname_to_str(qname); - dbg_ns("Trying to find zone for QNAME %s\n", name_str); - free(name_str); -); - // find zone in which to search for the name - const knot_zone_t *zone = - ns_get_zone_for_qname(db, qname, qtype); +static int ns_answer(const knot_zone_t *zone, knot_packet_t *resp) +{ +// const knot_dname_t *qname = knot_packet_qname(resp); +// assert(qname != NULL); + +// uint16_t qtype = knot_packet_qtype(resp); +//dbg_ns_exec( +// char *name_str = knot_dname_to_str(qname); +// dbg_ns("Trying to find zone for QNAME %s\n", name_str); +// free(name_str); +//); +// // find zone in which to search for the name +// const knot_zone_t *zone = +// ns_get_zone_for_qname(db, qname, qtype); const knot_zone_contents_t *contents = knot_zone_contents(zone); // if no zone found, return REFUSED @@ -1875,25 +1895,15 @@ dbg_ns_exec( // take the zone contents and use only them for answering - return ns_answer_from_zone(contents, qname, qtype, resp); + return ns_answer_from_zone(contents, resp); //knot_dname_free(&qname); } /*----------------------------------------------------------------------------*/ -/*! - * \brief Converts the response to wire format. - * - * \param resp Response to convert. - * \param wire Place for the wire format of the response. - * \param wire_size In: space available for the wire format in bytes. - * Out: actual size of the wire format in bytes. - * - * \retval KNOT_EOK - * \retval NS_ERR_SERVFAIL - */ -static int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire, - size_t *wire_size) + +int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire, + size_t *wire_size) { uint8_t *rwire = NULL; size_t rsize = 0; @@ -2001,11 +2011,6 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) size_t real_size = xfr->wire_size; if (ns_response_to_wire(xfr->response, xfr->wire, &real_size) != 0) { return NS_ERR_SERVFAIL; -// // send back SERVFAIL (as this is our problem) -// ns_error_response(nameserver, -// knot_wire_get_id(query_wire), -// KNOT_RCODE_SERVFAIL, response_wire, -// rsize); } int res = 0; @@ -2013,6 +2018,17 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) size_t digest_real_size = xfr->digest_max_size; dbg_ns_detail("xfr->tsig_key=%p\n", xfr->tsig_key); + dbg_ns_detail("xfr->tsig_rcode=%d\n", xfr->tsig_rcode); + + if (xfr->tsig_key) { + // add the data to TSIG data + assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size + >= xfr->wire_size); + memcpy(xfr->tsig_data + xfr->tsig_data_size, + xfr->wire, real_size); + xfr->tsig_data_size += real_size; + } + /*! \note [TSIG] Generate TSIG if required (during XFR/IN). */ if (xfr->tsig_key && add_tsig) { if (xfr->packet_nr == 0) { @@ -2026,7 +2042,8 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) xfr->wire_size, xfr->digest, xfr->digest_size, xfr->digest, &digest_real_size, - xfr->tsig_key); + xfr->tsig_key, xfr->tsig_rcode, + xfr->tsig_prev_time_signed); } else { /* Add key, digest and digest length. */ dbg_ns_detail("Calling tsig_sign_next()\n"); @@ -2036,7 +2053,8 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) xfr->digest_size, xfr->digest, &digest_real_size, - xfr->tsig_key); + xfr->tsig_key, xfr->tsig_data, + xfr->tsig_data_size); } dbg_ns_detail("Sign function returned: %s\n", @@ -2050,6 +2068,27 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) assert(digest_real_size > 0); // save the new previous digest size xfr->digest_size = digest_real_size; + + // clear the TSIG data + xfr->tsig_data_size = 0; + + } else if (xfr->tsig_rcode != 0) { + dbg_ns_detail("Adding TSIG without signing, TSIG RCODE: %d.\n", + xfr->tsig_rcode); + assert(xfr->tsig_rcode != KNOT_TSIG_RCODE_BADTIME); + // add TSIG without signing + assert(xfr->query != NULL); + assert(knot_packet_additional_rrset_count(xfr->query) > 0); + + const knot_rrset_t *tsig = knot_packet_additional_rrset( + xfr->query, + knot_packet_additional_rrset_count(xfr->query) - 1); + + res = knot_tsig_add(xfr->wire, &real_size, xfr->wire_size, + xfr->tsig_rcode, tsig); + if (res != KNOT_EOK) { + return res; + } } // Send the response @@ -2071,7 +2110,9 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) // increment the packet number ++xfr->packet_nr; - if (xfr->tsig_key && add_tsig) { + if ((xfr->tsig_key && knot_ns_tsig_required(xfr->packet_nr)) + || xfr->tsig_rcode != 0) { + /*! \todo Where is xfr->tsig_size set?? */ knot_packet_set_tsig_size(xfr->response, xfr->tsig_size); } else { knot_packet_set_tsig_size(xfr->response, 0); @@ -2312,7 +2353,7 @@ static int ns_ixfr_put_rrset(knot_ns_xfr_t *xfr, const knot_rrset_t *rrset) /*! \todo Probably rename the function. */ ns_xfr_send_and_clear(xfr, 1); //socket_close(xfr->session); /*! \todo Remove for UDP.*/ - return KNOT_ERROR; + return res; } return KNOT_EOK; @@ -2363,38 +2404,9 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr) assert(xfr->response != NULL); assert(knot_packet_authority_rrset_count(xfr->query) > 0); assert(xfr->data != NULL); - - /*! \todo REMOVE start */ -// const knot_rrset_t *zone_soa = -// knot_node_rrset(knot_zone_contents_apex( -// knot_zone_contents(xfr->zone)), -// KNOT_RRTYPE_SOA); -// // retrieve origin (xfr) serial and target (zone) serial -// uint32_t zone_serial = knot_rdata_soa_serial( -// knot_rrset_rdata(zone_soa)); -// uint32_t xfr_serial = knot_rdata_soa_serial(knot_rrset_rdata( -// knot_packet_authority_rrset(xfr->query, 0))); - -// // 3) load changesets from journal -// knot_changesets_t *chgsets = (knot_changesets_t *) -// calloc(1, sizeof(knot_changesets_t)); -// int res = xfr_load_changesets(xfr->zone, chgsets, xfr_serial, -// zone_serial); -// if (res != KNOT_EOK) { -// dbg_ns("IXFR query cannot be answered: %s.\n", -// knot_strerror(res)); -// /*! \todo Probably send back AXFR instead. */ -// knot_response_set_rcode(xfr->response, KNOT_RCODE_SERVFAIL); -// /*! \todo Probably rename the function. */ -// ns_axfr_send_and_clear(xfr); -// //socket_close(xfr->session); /*! \todo Remove for UDP. */ -// return 1; -// } - - /*! \todo REMOVE end */ knot_changesets_t *chgsets = (knot_changesets_t *)xfr->data; - knot_zone_contents_t* contents = knot_zone_get_contents(xfr->zone); + knot_zone_contents_t *contents = knot_zone_get_contents(xfr->zone); assert(contents); const knot_rrset_t *zone_soa = knot_node_rrset(knot_zone_contents_apex(contents), @@ -2411,15 +2423,15 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr) /*! \todo Probably rename the function. */ ns_xfr_send_and_clear(xfr, 1); // socket_close(xfr->session); /*! \todo Remove for UDP.*/ - return 1; + return res; } // 5) put the changesets into the response while they fit in for (int i = 0; i < chgsets->count; ++i) { res = ns_ixfr_put_changeset(xfr, &chgsets->sets[i]); if (res != KNOT_EOK) { - // answer is sent, socket is closed - return KNOT_EOK; + // answer is sent + return res; } } @@ -2431,7 +2443,7 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr) /*! \todo Probably rename the function. */ ns_xfr_send_and_clear(xfr, 1); //socket_close(xfr->session); /*! \todo Remove for UDP.*/ - return 1; +// return 1; } return KNOT_EOK; @@ -2454,7 +2466,7 @@ static int ns_ixfr(knot_ns_xfr_t *xfr) /*! \todo Probably rename the function. */ ns_xfr_send_and_clear(xfr, 1); //socket_close(xfr->session); - return 1; + return KNOT_EMALF; } const knot_rrset_t *soa = knot_packet_authority_rrset(xfr->query, 0); @@ -2470,7 +2482,7 @@ static int ns_ixfr(knot_ns_xfr_t *xfr) /*! \todo Probably rename the function. */ ns_xfr_send_and_clear(xfr, 1); //socket_close(xfr->session); /*! \todo Remove for UDP. */ - return 1; + return KNOT_EMALF; } return ns_ixfr_from_zone(xfr); @@ -2728,10 +2740,16 @@ void knot_ns_error_response_full(knot_nameserver_t *nameserver, /*----------------------------------------------------------------------------*/ -int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query, - uint8_t *response_wire, size_t *rsize) +int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, + knot_packet_t *query, knot_packet_t **resp, + const knot_zone_t **zone) { - dbg_ns("ns_answer_normal()\n"); + dbg_ns("knot_ns_prep_normal_response()\n"); + + if (nameserver == NULL || query == NULL || resp == NULL + || zone == NULL) { + return KNOT_EBADARG; + } // first, parse the rest of the packet assert(knot_packet_is_query(query)); @@ -2743,12 +2761,7 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query, if (ret != KNOT_EOK) { dbg_ns("Failed to parse rest of the query: " "%s.\n", knot_strerror(ret)); - knot_ns_error_response(nameserver, knot_packet_id(query), - (ret == KNOT_EMALF) - ? KNOT_RCODE_FORMERR - : KNOT_RCODE_SERVFAIL, response_wire, - rsize); - return KNOT_EOK; + return ret; } /* @@ -2764,18 +2777,15 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query, || knot_packet_nscount(query) > 0 || knot_packet_qdcount(query) != 1) { dbg_ns("ANCOUNT or NSCOUNT not 0 in query, reply FORMERR.\n"); - knot_ns_error_response(nameserver, knot_packet_id(query), - KNOT_RCODE_FORMERR, response_wire, - rsize); - return KNOT_EOK; + return KNOT_EMALF; } size_t resp_max_size = 0; - - assert(*rsize >= MAX_UDP_PAYLOAD); + + //assert(*rsize >= MAX_UDP_PAYLOAD); knot_packet_dump(query); - + if (knot_query_edns_supported(query)) { if (knot_edns_get_payload(&query->opt_rr) < knot_edns_get_payload(nameserver->opt_rr)) { @@ -2785,28 +2795,23 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query, nameserver->opt_rr); } } - + if (resp_max_size < MAX_UDP_PAYLOAD) { resp_max_size = MAX_UDP_PAYLOAD; } - - knot_packet_t *response; - ret = knot_ns_prepare_response(nameserver, query, &response, + + ret = knot_ns_prepare_response(nameserver, query, resp, resp_max_size); if (ret != KNOT_EOK) { - knot_ns_error_response(nameserver, knot_packet_id(query), - KNOT_RCODE_SERVFAIL, response_wire, - rsize); - return KNOT_EOK; + return KNOT_ERROR; } - dbg_ns("Query - parsed: %zu, total wire size: %zu\n", + dbg_ns("Query - parsed: %zu, total wire size: %zu\n", query->parsed, query->size); - dbg_ns("Opt RR: version: %d, payload: %d\n", + dbg_ns("Opt RR: version: %d, payload: %d\n", query->opt_rr.version, query->opt_rr.payload); // get the answer for the query - rcu_read_lock(); knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db); dbg_ns("EDNS supported in query: %d\n", @@ -2814,74 +2819,66 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query, // set the OPT RR to the response if (knot_query_edns_supported(query)) { - /*! \todo API. */ -// if (knot_edns_get_payload(&query->opt_rr) > MAX_UDP_PAYLOAD) { -// ret = knot_packet_set_max_size(response, -// knot_edns_get_payload(&query->opt_rr)); -// } else { -// ret = knot_packet_set_max_size(response, -// MAX_UDP_PAYLOAD); -// } - -// if (ret != KNOT_EOK) { -// dbg_ns("Failed to set max size.\n"); -// knot_ns_error_response_full(nameserver, response, -// KNOT_RCODE_SERVFAIL, -// response_wire, rsize); -// return KNOT_EOK; -// } - - ret = knot_response_add_opt(response, nameserver->opt_rr, 1); + ret = knot_response_add_opt(*resp, nameserver->opt_rr, 1); if (ret != KNOT_EOK) { dbg_ns("Failed to set OPT RR to the response" - ": %s\n",knot_strerror(ret)); + ": %s\n", knot_strerror(ret)); } else { // copy the DO bit from the query if (knot_query_dnssec_requested(query)) { /*! \todo API for this. */ - knot_edns_set_do(&response->opt_rr); + knot_edns_set_do(&(*resp)->opt_rr); } } - }/* else { - dbg_ns("Setting max size to %u.\n", MAX_UDP_PAYLOAD); - ret = knot_packet_set_max_size(response, MAX_UDP_PAYLOAD); - if (ret != KNOT_EOK) { - dbg_ns("Failed to set max size to %u\n", - MAX_UDP_PAYLOAD); - knot_ns_error_response_full(nameserver, response, - KNOT_RCODE_SERVFAIL, - response_wire, rsize); - return KNOT_EOK; - } - }*/ - - dbg_ns("Response max size: %zu\n", response->max_size); + } + + dbg_ns("Response max size: %zu\n", (*resp)->max_size); + + const knot_dname_t *qname = knot_packet_qname(*resp); + assert(qname != NULL); + + uint16_t qtype = knot_packet_qtype(*resp); +dbg_ns_exec( + char *name_str = knot_dname_to_str(qname); + dbg_ns("Trying to find zone for QNAME %s\n", name_str); + free(name_str); +); + // find zone in which to search for the name + *zone = ns_get_zone_for_qname(zonedb, qname, qtype); + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +int knot_ns_answer_normal(knot_nameserver_t *nameserver, + const knot_zone_t *zone, knot_packet_t *resp, + uint8_t *response_wire, size_t *rsize) +{ + dbg_ns("ns_answer_normal()\n"); + + int ret = ns_answer(zone, resp); - ret = ns_answer(zonedb, response); if (ret != 0) { // now only one type of error (SERVFAIL), later maybe more - knot_ns_error_response_full(nameserver, response, + knot_ns_error_response_full(nameserver, resp, KNOT_RCODE_SERVFAIL, response_wire, rsize); } else { dbg_ns("Created response packet.\n"); //knot_response_dump(resp); - knot_packet_dump(response); + knot_packet_dump(resp); // 4) Transform the packet into wire format - if (ns_response_to_wire(response, response_wire, rsize) != 0) { + if (ns_response_to_wire(resp, response_wire, rsize) != 0) { // send back SERVFAIL (as this is our problem) - knot_ns_error_response_full(nameserver, response, + knot_ns_error_response_full(nameserver, resp, KNOT_RCODE_SERVFAIL, response_wire, rsize); } } - rcu_read_unlock(); - knot_packet_free(&response); - dbg_ns("Returning response with wire size %zu\n", *rsize); - //dbg_ns_hex((char *)response_wire, *rsize); return KNOT_EOK; } @@ -3158,12 +3155,7 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) int ret = knot_packet_parse_rest(xfr->query); if (ret != KNOT_EOK) { dbg_ns("Failed to parse rest of the packet. Reply FORMERR.\n"); -// knot_ns_error_response_full(nameserver, xfr->response, -// KNOT_RCODE_FORMERR, xfr->wire, -// &size); knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_FORMERR); - - //ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size); knot_packet_free(&xfr->response); return ret; } @@ -3172,11 +3164,6 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) if (knot_zone_contents(xfr->zone) == NULL) { dbg_ns("Zone expired or not bootstrapped. Reply SERVFAIL.\n"); ret = knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL); -// knot_ns_error_response_full(nameserver, xfr->response, -// KNOT_RCODE_SERVFAIL, xfr->wire, -// &size); - -// ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size); knot_packet_free(&xfr->response); return ret; } @@ -3195,35 +3182,15 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) ret = ns_ixfr(xfr); - /*! \todo Somehow distinguish when it makes sense to send the SERVFAIL - * and when it does not. E.g. if there was problem in sending - * packet, it will probably fail when sending the SERVFAIL also. - */ - if (ret < 0) { - dbg_ns("IXFR failed, sending SERVFAIL.\n"); - // now only one type of error (SERVFAIL), later maybe more - - /*! \todo Extract this to some function. */ -// knot_response_set_rcode(xfr->response, KNOT_RCODE_SERVFAIL); -// uint8_t *wire = NULL; -// ret = knot_packet_to_wire(xfr->response, &wire, &size); -// if (ret != KNOT_EOK) { -//// knot_ns_error_response(nameserver, -//// xfr->query->header.id, -//// KNOT_RCODE_SERVFAIL, xfr->wire, -//// &size); -//// ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, -//// size); -// knot_ns_xfr_send_error(xfr, KNOT_RCODE_SERVFAIL); -// knot_packet_free(&xfr->response); -// return ret; -// } else { -// ret = xfr->send(xfr->session, &xfr->addr, wire, size); -// } - knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL); - } /*else if (ret > 0) { - ret = KNOT_ERROR; - }*/ +// /*! \todo Somehow distinguish when it makes sense to send the SERVFAIL +// * and when it does not. E.g. if there was problem in sending +// * packet, it will probably fail when sending the SERVFAIL also. +// */ +// if (ret < 0) { +// dbg_ns("IXFR failed, sending SERVFAIL.\n"); +// // now only one type of error (SERVFAIL), later maybe more +// knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL); +// } knot_packet_free(&xfr->response); @@ -3363,16 +3330,11 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver, * digest. */ - int ret = xfrin_process_ixfr_packet(xfr/*xfr->wire, xfr->wire_size, - (knot_changesets_t **)(&xfr->data)*/); + int ret = xfrin_process_ixfr_packet(xfr); if (ret == XFRIN_RES_FALLBACK) { dbg_ns("ns_process_ixfrin: Fallback to AXFR.\n"); - assert(xfr->data == NULL); -// dbg_ns("xfr->zone = %p\n", xfr->zone); -// dbg_ns("Zone name: %.*s\n", -// xfr->zone->name->size, xfr->zone->name->name); -// assert(xfr->zone == NULL); + knot_free_changesets((knot_changesets_t **)&xfr->data); knot_packet_free(&xfr->query); return KNOT_ENOIXFR; } @@ -3417,12 +3379,24 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver, return KNOT_ERROR; } - if (knot_rdata_soa_serial(knot_rrset_rdata( - chgsets->first_soa)) - != knot_rdata_soa_serial(knot_rrset_rdata( - zone_soa))) { - dbg_ns("Update did not fit.\n"); - return KNOT_EAGAIN; + if (ns_serial_compare(knot_rdata_soa_serial( + knot_rrset_rdata(chgsets->first_soa)), + knot_rdata_soa_serial(knot_rrset_rdata(zone_soa))) + > 0) { + if ((xfr->flags & XFR_FLAG_UDP) > 0) { + // IXFR over UDP + dbg_ns("Update did not fit.\n"); + return KNOT_EIXFRSPACE; + } else { + // fallback to AXFR + dbg_ns("ns_process_ixfrin: " + "Fallback to AXFR.\n"); + knot_free_changesets( + (knot_changesets_t **)&xfr->data); + knot_packet_free(&xfr->query); + return KNOT_ENOIXFR; + } + } else { // free changesets dbg_ns("No update needed.\n"); diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h index 0d93df6..928d79c 100644 --- a/src/libknot/nameserver/name-server.h +++ b/src/libknot/nameserver/name-server.h @@ -118,6 +118,9 @@ typedef struct knot_ns_xfr { uint8_t *digest; /*!< Buffer for counting digest. */ size_t digest_size; /*!< Size of the digest. */ size_t digest_max_size; /*!< Size of the buffer. */ + + uint16_t tsig_rcode; + uint64_t tsig_prev_time_signed; /*! \brief Previous digest or request digest. * @@ -219,6 +222,14 @@ int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize, void knot_ns_error_response(const knot_nameserver_t *nameserver, uint16_t query_id, uint8_t rcode, uint8_t *response_wire, size_t *rsize); +void knot_ns_error_response_full(knot_nameserver_t *nameserver, + knot_packet_t *response, uint8_t rcode, + uint8_t *response_wire, size_t *rsize); + +int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, + knot_packet_t *query, knot_packet_t **resp, + const knot_zone_t **zone); + /*! * \brief Creates a response for the given normal query using the data of the * nameserver. @@ -232,8 +243,9 @@ void knot_ns_error_response(const knot_nameserver_t *nameserver, uint16_t query_ * \retval KNOT_EOK if a valid response was created. * \retval KNOT_EMALF if an error occured and the response is not valid. */ -int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query, - uint8_t *response_wire, size_t *rsize); +int knot_ns_answer_normal(knot_nameserver_t *nameserver, + const knot_zone_t *zone, knot_packet_t *resp, + uint8_t *response_wire, size_t *rsize); int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr); @@ -346,6 +358,20 @@ void knot_ns_set_data(knot_nameserver_t *nameserver, void *data); int knot_ns_tsig_required(int packet_nr); /*! + * \brief Converts the response to wire format. + * + * \param resp Response to convert. + * \param wire Place for the wire format of the response. + * \param wire_size In: space available for the wire format in bytes. + * Out: actual size of the wire format in bytes. + * + * \retval KNOT_EOK + * \retval NS_ERR_SERVFAIL + */ +int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire, + size_t *wire_size); + +/*! * \brief Properly destroys the name server structure. * * \param nameserver Nameserver to destroy. diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c index 82e818f..59c1a0d 100644 --- a/src/libknot/packet/packet.c +++ b/src/libknot/packet/packet.c @@ -1159,6 +1159,20 @@ void knot_packet_set_tsig_size(knot_packet_t *packet, size_t tsig_size) /*----------------------------------------------------------------------------*/ +const knot_rrset_t *knot_packet_tsig(const knot_packet_t *packet) +{ + return packet->tsig_rr; +} + +/*----------------------------------------------------------------------------*/ + +void knot_packet_set_tsig(knot_packet_t *packet, const knot_rrset_t *tsig_rr) +{ + packet->tsig_rr = (knot_rrset_t *)tsig_rr; +} + +/*----------------------------------------------------------------------------*/ + short knot_packet_answer_rrset_count(const knot_packet_t *packet) { if (packet == NULL) { @@ -1217,7 +1231,7 @@ const knot_rrset_t *knot_packet_authority_rrset( /*----------------------------------------------------------------------------*/ const knot_rrset_t *knot_packet_additional_rrset( - knot_packet_t *packet, short pos) + knot_packet_t *packet, short pos) { if (packet == NULL || pos > packet->ar_rrsets) { return NULL; diff --git a/src/libknot/packet/packet.h b/src/libknot/packet/packet.h index 1bf74a9..81b1812 100644 --- a/src/libknot/packet/packet.h +++ b/src/libknot/packet/packet.h @@ -143,6 +143,7 @@ struct knot_packet { knot_packet_prealloc_type_t prealloc_type; size_t tsig_size; /*!< Space to reserve for the TSIG RR. */ + knot_rrset_t *tsig_rr; /*!< TSIG RR stored in the packet. */ }; typedef struct knot_packet knot_packet_t; @@ -378,6 +379,10 @@ int knot_packet_arcount(const knot_packet_t *packet); void knot_packet_set_tsig_size(knot_packet_t *packet, size_t tsig_size); +const knot_rrset_t *knot_packet_tsig(const knot_packet_t *packet); + +void knot_packet_set_tsig(knot_packet_t *packet, const knot_rrset_t *tsig_rr); + /*! * \brief Returns number of RRSets in Answer section of the packet. * @@ -439,7 +444,7 @@ const knot_rrset_t *knot_packet_authority_rrset( * or NULL if there is no such RRSet. */ const knot_rrset_t *knot_packet_additional_rrset( - knot_packet_t *packet, short pos); + knot_packet_t *packet, short pos); /*! * \brief Checks if the packet already contains the given RRSet. diff --git a/src/libknot/rdata.c b/src/libknot/rdata.c index 0c51f5b..27fa6ec 100644 --- a/src/libknot/rdata.c +++ b/src/libknot/rdata.c @@ -828,6 +828,22 @@ uint32_t knot_rdata_soa_expire(const knot_rdata_t *rdata) /*---------------------------------------------------------------------------*/ +uint32_t knot_rdata_soa_minimum(const knot_rdata_t *rdata) +{ + if (!rdata) { + return -1; + } + + if (rdata->count < 7) { + return 0; /*! \todo Some other error value. */ + } + + // the number is in network byte order, transform it + return knot_wire_read_u32((uint8_t *)(rdata->items[6].raw_data + 1)); +} + +/*---------------------------------------------------------------------------*/ + uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata) { if (rdata->count < 1) { diff --git a/src/libknot/rdata.h b/src/libknot/rdata.h index 5d328c9..6d9907d 100644 --- a/src/libknot/rdata.h +++ b/src/libknot/rdata.h @@ -331,6 +331,7 @@ int64_t knot_rdata_soa_serial(const knot_rdata_t *rdata); uint32_t knot_rdata_soa_refresh(const knot_rdata_t *rdata); uint32_t knot_rdata_soa_retry(const knot_rdata_t *rdata); uint32_t knot_rdata_soa_expire(const knot_rdata_t *rdata); +uint32_t knot_rdata_soa_minimum(const knot_rdata_t *rdata); uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata); diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c index 6083f77..f037716 100644 --- a/src/libknot/rrset.c +++ b/src/libknot/rrset.c @@ -24,6 +24,7 @@ #include "rrset.h" #include "util/descriptor.h" #include "util/error.h" +#include "util/debug.h" #include "util/utils.h" /*----------------------------------------------------------------------------*/ @@ -211,6 +212,15 @@ void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t* owner) /*----------------------------------------------------------------------------*/ +void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl) +{ + if (rrset) { + rrset->ttl = ttl; + } +} + +/*----------------------------------------------------------------------------*/ + uint16_t knot_rrset_type(const knot_rrset_t *rrset) { return rrset->type; @@ -385,7 +395,7 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset, assert(pos != NULL); assert(*pos != NULL); - fprintf(stderr, "Max size: %zu, owner: %p, owner size: %d\n", + dbg_rrset_detail("Max size: %zu, owner: %p, owner size: %d\n", max_size, rrset->owner, rrset->owner->size); // check if owner fits @@ -398,21 +408,21 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset, *pos += knot_dname_size(rrset->owner); size += knot_dname_size(rrset->owner); - fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size); + dbg_rrset_detail("Max size: %zu, size: %d\n", max_size, size); - fprintf(stderr, "Wire format:\n"); + dbg_rrset_detail("Wire format:\n"); // put rest of RR 'header' knot_wire_write_u16(*pos, rrset->type); - fprintf(stderr, " Type: %u\n", rrset->type); + dbg_rrset_detail(" Type: %u\n", rrset->type); *pos += 2; knot_wire_write_u16(*pos, rrset->rclass); - fprintf(stderr, " Class: %u\n", rrset->rclass); + dbg_rrset_detail(" Class: %u\n", rrset->rclass); *pos += 2; knot_wire_write_u32(*pos, rrset->ttl); - fprintf(stderr, " TTL: %u\n", rrset->ttl); + dbg_rrset_detail(" TTL: %u\n", rrset->ttl); *pos += 4; // save space for RDLENGTH @@ -422,7 +432,7 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset, size += 10; // compr->wire_pos += size; - fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size); + dbg_rrset_detail("Max size: %zu, size: %d\n", max_size, size); knot_rrtype_descriptor_t *desc = knot_rrtype_descriptor_by_type(rrset->type); @@ -447,8 +457,9 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset, // save whole domain name memcpy(*pos, knot_dname_name(dname), knot_dname_size(dname)); - fprintf(stderr, "Uncompressed dname size: %d\n", - knot_dname_size(dname)); + dbg_rrset_detail(stderr, + "Uncompressed dname size: %d\n", + knot_dname_size(dname)); *pos += knot_dname_size(dname); rdlength += knot_dname_size(dname); // compr->wire_pos += dname->size; @@ -464,7 +475,7 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset, // copy just the rdata item data (without size) memcpy(*pos, raw_data + 1, raw_data[0]); - fprintf(stderr, "Raw data size: %d\n", raw_data[0]); + dbg_rrset_detail("Raw data size: %d\n", raw_data[0]); *pos += raw_data[0]; rdlength += raw_data[0]; // compr->wire_pos += raw_data[0]; @@ -473,7 +484,7 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset, } } - fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size); + dbg_rrset_detail("Max size: %zu, size: %d\n", max_size, size); assert(size + rdlength <= max_size); size += rdlength; @@ -509,11 +520,11 @@ int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size, if (ret < 0) { // some RR didn't fit in, so no RRs should be used // TODO: remove last entries from compression table - fprintf(stderr, "Some RR didn't fit in.\n"); + dbg_rrset_detail("Some RR didn't fit in.\n"); return KNOT_ESPACE; } - fprintf(stderr, "RR of size %d added.\n", ret); + dbg_rrset_detail("RR of size %d added.\n", ret); rrset_size += ret; ++rrs; } while ((rdata = knot_rrset_rdata_next(rrset, rdata)) != NULL); @@ -523,7 +534,7 @@ int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size, assert(pos - wire == rrset_size); *size = rrset_size; - fprintf(stderr, " Size after: %zu\n", *size); + dbg_rrset_detail("Size after: %zu\n", *size); *rr_count = rrs; diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h index 7754c7f..e13cbba 100644 --- a/src/libknot/rrset.h +++ b/src/libknot/rrset.h @@ -146,6 +146,8 @@ knot_dname_t *knot_rrset_get_owner(const knot_rrset_t *rrset); */ void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t* owner); +void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl); + /*! * \brief Returns the TYPE of the RRSet. * diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c index 3178a23..edc68d8 100644 --- a/src/libknot/tsig-op.c +++ b/src/libknot/tsig-op.c @@ -25,305 +25,10 @@ #include "tsig.h" #include "tsig-op.h" #include "util/wire.h" +#include "libknot/util/conv.h" #include "util/error.h" #include "util/debug.h" - - -static const char Base64[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char Pad64 = '='; - - -static int b64rmap_initialized = 0; -static uint8_t b64rmap[256]; - -static const uint8_t b64rmap_special = 0xf0; -static const uint8_t b64rmap_end = 0xfd; -static const uint8_t b64rmap_space = 0xfe; -static const uint8_t b64rmap_invalid = 0xff; - -/** - * Initializing the reverse map is not thread safe. - * Which is fine for NSD. For now... - **/ -void b64_initialize_rmap() -{ - int i; - char ch; - - /* Null: end of string, stop parsing */ - b64rmap[0] = b64rmap_end; - - for (i = 1; i < 256; ++i) { - ch = (char)i; - /* Whitespaces */ - if (isspace(ch)) { - b64rmap[i] = b64rmap_space; - } - /* Padding: stop parsing */ - else if (ch == Pad64) { - b64rmap[i] = b64rmap_end; - } - /* Non-base64 char */ - else { - b64rmap[i] = b64rmap_invalid; - } - } - - /* Fill reverse mapping for base64 chars */ - for (i = 0; Base64[i] != '\0'; ++i) { - b64rmap[(uint8_t)Base64[i]] = i; - } - - b64rmap_initialized = 1; -} - -int b64_pton_do(char const *src, uint8_t *target, size_t targsize) -{ - int tarindex, state, ch; - uint8_t ofs; - - state = 0; - tarindex = 0; - - while (1) { - ch = *src++; - ofs = b64rmap[ch]; - - if (ofs >= b64rmap_special) { - /* Ignore whitespaces */ - if (ofs == b64rmap_space) { - continue; - } - /* End of base64 characters */ - if (ofs == b64rmap_end) { - break; - } - /* A non-base64 character. */ - return (-1); - } - - switch (state) { - case 0: - if ((size_t)tarindex >= targsize) { - return (-1); - } - target[tarindex] = ofs << 2; - state = 1; - break; - case 1: - if ((size_t)tarindex + 1 >= targsize) { - return (-1); - } - target[tarindex] |= ofs >> 4; - target[tarindex+1] = (ofs & 0x0f) - << 4 ; - tarindex++; - state = 2; - break; - case 2: - if ((size_t)tarindex + 1 >= targsize) { - return (-1); - } - target[tarindex] |= ofs >> 2; - target[tarindex+1] = (ofs & 0x03) - << 6; - tarindex++; - state = 3; - break; - case 3: - if ((size_t)tarindex >= targsize) { - return (-1); - } - target[tarindex] |= ofs; - tarindex++; - state = 0; - break; - default: - abort(); - } - } - - /* - * We are done decoding Base-64 chars. Let's see if we ended - * on a byte boundary, and/or with erroneous trailing characters. - */ - - if (ch == Pad64) { /* We got a pad char. */ - ch = *src++; /* Skip it, get next. */ - switch (state) { - case 0: /* Invalid = in first position */ - case 1: /* Invalid = in second position */ - return (-1); - - case 2: /* Valid, means one byte of info */ - /* Skip any number of spaces. */ - for ((void)NULL; ch != '\0'; ch = *src++) - if (b64rmap[ch] != b64rmap_space) { - break; - } - /* Make sure there is another trailing = sign. */ - if (ch != Pad64) { - return (-1); - } - ch = *src++; /* Skip the = */ - /* Fall through to "single trailing =" case. */ - /* FALLTHROUGH */ - - case 3: /* Valid, means two bytes of info */ - /* - * We know this char is an =. Is there anything but - * whitespace after it? - */ - for ((void)NULL; ch != '\0'; ch = *src++) - if (b64rmap[ch] != b64rmap_space) { - return (-1); - } - - /* - * Now make sure for cases 2 and 3 that the "extra" - * bits that slopped past the last full byte were - * zeros. If we don't check them, they become a - * subliminal channel. - */ - if (target[tarindex] != 0) { - return (-1); - } - } - } else { - /* - * We ended by seeing the end of the string. Make sure we - * have no partial bytes lying around. - */ - if (state != 0) { - return (-1); - } - } - - return (tarindex); -} - - -int b64_pton_len(char const *src) -{ - int tarindex, state, ch; - uint8_t ofs; - - state = 0; - tarindex = 0; - - while (1) { - ch = *src++; - ofs = b64rmap[ch]; - - if (ofs >= b64rmap_special) { - /* Ignore whitespaces */ - if (ofs == b64rmap_space) { - continue; - } - /* End of base64 characters */ - if (ofs == b64rmap_end) { - break; - } - /* A non-base64 character. */ - return (-1); - } - - switch (state) { - case 0: - state = 1; - break; - case 1: - tarindex++; - state = 2; - break; - case 2: - tarindex++; - state = 3; - break; - case 3: - tarindex++; - state = 0; - break; - default: - abort(); - } - } - - /* - * We are done decoding Base-64 chars. Let's see if we ended - * on a byte boundary, and/or with erroneous trailing characters. - */ - - if (ch == Pad64) { /* We got a pad char. */ - ch = *src++; /* Skip it, get next. */ - switch (state) { - case 0: /* Invalid = in first position */ - case 1: /* Invalid = in second position */ - return (-1); - - case 2: /* Valid, means one byte of info */ - /* Skip any number of spaces. */ - for ((void)NULL; ch != '\0'; ch = *src++) - if (b64rmap[ch] != b64rmap_space) { - break; - } - /* Make sure there is another trailing = sign. */ - if (ch != Pad64) { - return (-1); - } - ch = *src++; /* Skip the = */ - /* Fall through to "single trailing =" case. */ - /* FALLTHROUGH */ - - case 3: /* Valid, means two bytes of info */ - /* - * We know this char is an =. Is there anything but - * whitespace after it? - */ - for ((void)NULL; ch != '\0'; ch = *src++) - if (b64rmap[ch] != b64rmap_space) { - return (-1); - } - - } - } else { - /* - * We ended by seeing the end of the string. Make sure we - * have no partial bytes lying around. - */ - if (state != 0) { - return (-1); - } - } - - return (tarindex); -} - -int b64_pton(char const *src, uint8_t *target, size_t targsize) -{ - if (!b64rmap_initialized) { - b64_initialize_rmap(); - } - - if (target) { - return b64_pton_do(src, target, targsize); - } else { - return b64_pton_len(src); - } -} - -#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */ - - - - - - - - - - +#include "consts.h" const int KNOT_TSIG_MAX_DIGEST_SIZE = 64; // size of HMAC-SHA512 digest @@ -354,7 +59,7 @@ static int knot_tsig_check_key(const knot_rrset_t *tsig_rr, return KNOT_EMALF; } - const char *name = knot_dname_to_str(tsig_name); + char *name = knot_dname_to_str(tsig_name); if (!name) { return KNOT_EMALF; } @@ -362,9 +67,11 @@ static int knot_tsig_check_key(const knot_rrset_t *tsig_rr, if (knot_dname_compare(tsig_name, tsig_key->name) != 0) { /*!< \todo which error. */ dbg_tsig("TSIG: unknown key: %s\n", name); + free(name); return KNOT_TSIG_EBADKEY; } + free(name); return KNOT_EOK; } @@ -402,15 +109,15 @@ static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len, B64BUFSIZE); if (decoded_key_size < 0) { dbg_tsig("TSIG: Could not decode Base64\n"); - return KNOT_EMALF; + return KNOT_ERROR; } - dbg_tsig("TSIG: decoded key size: %d\n", decoded_key_size); - dbg_tsig("TSIG: decoded key: '%*s'\n", decoded_key_size, decoded_key); - - dbg_tsig("TSIG: using this wire for digest calculation\n"); + dbg_tsig_detail("TSIG: decoded key size: %d\n", decoded_key_size); + dbg_tsig_detail("TSIG: decoded key: '%*s'\n", decoded_key_size, decoded_key); - //dbg_tsig_hex(wire, wire_len); +// dbg_tsig_detail("TSIG: using this wire for digest calculation\n"); +// dbg_tsig_hex_detail(wire, wire_len); + dbg_tsig_detail("Wire for signing is %zu bytes long.\n", wire_len); /* Compute digest. */ HMAC_CTX ctx; @@ -420,6 +127,14 @@ static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len, HMAC_Init(&ctx, decoded_key, decoded_key_size, EVP_md5()); break; + case KNOT_TSIG_ALG_HMAC_SHA1: + HMAC_Init(&ctx, decoded_key, + decoded_key_size, EVP_sha1()); + break; + case KNOT_TSIG_ALG_HMAC_SHA256: + HMAC_Init(&ctx, decoded_key, + decoded_key_size, EVP_sha256()); + break; default: return KNOT_ENOTSUP; } /* switch */ @@ -428,11 +143,14 @@ static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len, HMAC_Update(&ctx, (const unsigned char *)wire, wire_len); HMAC_Final(&ctx, digest, &tmp_dig_len); *digest_len = tmp_dig_len; + + HMAC_CTX_cleanup(&ctx); return KNOT_EOK; } -static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr) +static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr, + uint64_t prev_time_signed) { if (!tsig_rr) { return KNOT_EBADARG; @@ -452,7 +170,15 @@ static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr) time_t curr_time = time(NULL); /*!< \todo bleeding eyes. */ - if (difftime(curr_time, (time_t)time_signed) > fudge) { + double diff = difftime(curr_time, (time_t)time_signed); + + if (diff > fudge || diff < -fudge) { + return KNOT_TSIG_EBADTIME; + } + + diff = difftime((time_t)time_signed, prev_time_signed); + + if (diff < 0) { return KNOT_TSIG_EBADTIME; } @@ -569,7 +295,7 @@ static int knot_tsig_wire_write_timers(uint8_t *wire, return KNOT_EOK; } -int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len, +static int knot_tsig_create_sign_wire(const 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, @@ -635,6 +361,7 @@ int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len, if (ret != KNOT_EOK) { dbg_tsig("TSIG: create wire: failed to write TSIG " "variables: %s\n", knot_strerror(ret)); + free(wire); return ret; } @@ -645,6 +372,7 @@ int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len, dbg_tsig("TSIG: create wire: failed to compute digest: %s\n", knot_strerror(ret)); *digest_len = 0; + free(wire); return ret; } @@ -666,11 +394,11 @@ int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len, } static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len, - const uint8_t *prev_mac, - size_t prev_mac_len, - uint8_t *digest, size_t *digest_len, - const knot_rrset_t *tmp_tsig, - const knot_key_t *key) + const uint8_t *prev_mac, + size_t prev_mac_len, + uint8_t *digest, size_t *digest_len, + const knot_rrset_t *tmp_tsig, + const knot_key_t *key) { if (!msg || !key || digest_len == NULL) { dbg_tsig("TSIG: create wire: bad args.\n"); @@ -689,7 +417,7 @@ static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len, tsig_rdata_tsig_timers_length()); size_t wire_len = sizeof(uint8_t) * (msg_len + prev_mac_len + - tsig_rdata_tsig_timers_length()); + tsig_rdata_tsig_timers_length() + 2); uint8_t *wire = malloc(wire_len); if (!wire) { ERR_ALLOC_FAILED; @@ -699,19 +427,21 @@ static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len, memset(wire, 0, wire_len); /* Copy the request MAC - should work even if NULL. */ + dbg_tsig("Copying request mac size.\n"); + knot_wire_write_u16(wire, prev_mac_len); dbg_tsig("Copying request mac.\n"); - memcpy(wire, prev_mac, sizeof(uint8_t) * prev_mac_len); + memcpy(wire + 2, prev_mac, sizeof(uint8_t) * prev_mac_len); dbg_tsig_detail("TSIG: create wire: request mac: "); - dbg_tsig_hex_detail(wire, prev_mac_len); + dbg_tsig_hex_detail(wire + 2, prev_mac_len); /* Copy the original message. */ dbg_tsig("Copying original message.\n"); - memcpy(wire + prev_mac_len, msg, msg_len); + memcpy(wire + prev_mac_len + 2, msg, msg_len); dbg_tsig_detail("TSIG: create wire: original message: \n"); //dbg_tsig_hex_detail(wire + prev_mac_len, msg_len); /* Copy TSIG variables. */ dbg_tsig("Writing TSIG timers.\n"); - ret = knot_tsig_write_tsig_timers(wire + prev_mac_len + msg_len, + ret = knot_tsig_write_tsig_timers(wire + prev_mac_len + msg_len + 2, tmp_tsig); // ret = knot_tsig_write_tsig_variables(wire + prev_mac_len + msg_len, // tmp_tsig); @@ -740,7 +470,8 @@ 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_key_t *key) + const knot_key_t *key, uint16_t tsig_rcode, + uint64_t request_time_signed) { if (!msg || !msg_len || !key || digest == NULL || digest_len == NULL) { return KNOT_EBADARG; @@ -755,6 +486,8 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, knot_rrset_t *tmp_tsig = knot_rrset_new(key_name_copy, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0); + /* Should be retained by rrsig or freed, release. */ + knot_dname_release(key_name_copy); if (!tmp_tsig) { dbg_tsig_detail("TSIG: tmp_tsig = NULL\n"); return KNOT_ENOMEM; @@ -764,6 +497,7 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, knot_rdata_t *rdata = knot_rdata_new(); if (!rdata) { dbg_tsig_detail("TSIG: rdata = NULL\n"); + knot_rrset_free(&tmp_tsig); return KNOT_ENOMEM; } @@ -779,6 +513,8 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, if (!items) { dbg_tsig_detail("TSIG: items = NULL\n"); ERR_ALLOC_FAILED; + knot_rrset_free(&tmp_tsig); + knot_rdata_free(&rdata); return KNOT_ENOMEM; } @@ -787,23 +523,42 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, int ret = knot_rdata_set_items(rdata, items, desc->length); if (ret != KNOT_EOK) { dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", knot_strerror(ret)); + free(items); + knot_rrset_free(&tmp_tsig); + knot_rdata_free(&rdata); return ret; } free(items); tsig_rdata_set_alg(tmp_tsig, key->algorithm); - tsig_rdata_store_current_time(tmp_tsig); - tsig_rdata_set_fudge(tmp_tsig, 300); - /* Set original ID */ - tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); + /* Distinguish BADTIME response. */ + if (tsig_rcode == KNOT_TSIG_RCODE_BADTIME) { + /* Set error */ + tsig_rdata_set_tsig_error(tmp_tsig, tsig_rcode); + /* Set client's time signed into the time signed field. */ + tsig_rdata_set_time_signed(tmp_tsig, request_time_signed); - /* Set error */ - /*! \todo [TSIG] Set error and other data if appropriate. */ - tsig_rdata_set_tsig_error(tmp_tsig, 0); + /* Store current time into Other data. */ + uint8_t time_signed[3]; + time_t curr_time = time(NULL); - /* Set other len. */ - tsig_rdata_set_other_data(tmp_tsig, 0, 0); + /*! \todo bleeding eyes. */ + knot_wire_write_u48(time_signed, (uint64_t)curr_time); + + tsig_rdata_set_other_data(tmp_tsig, 6, time_signed); + } else { + tsig_rdata_store_current_time(tmp_tsig); + tsig_rdata_set_tsig_error(tmp_tsig, 0); + + /* Set other len. */ + tsig_rdata_set_other_data(tmp_tsig, 0, 0); + } + + tsig_rdata_set_fudge(tmp_tsig, 300); /*! \todo Bleeding eyes :-) */ + + /* 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; @@ -818,6 +573,10 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, if (ret != KNOT_EOK) { dbg_tsig("TSIG: could not create wire or sign wire: %s\n", knot_strerror(ret)); + free(items); + knot_rrset_free(&tmp_tsig); + knot_rdata_free(&rdata); + return ret; } @@ -834,6 +593,9 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, if (ret != KNOT_EOK) { dbg_tsig_detail("TSIG: rrset_to_wire = %s\n", knot_strerror(ret)); *digest_len = 0; + free(items); + knot_rrset_free(&tmp_tsig); + knot_rdata_free(&rdata); return ret; } @@ -854,7 +616,8 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, 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_key_t *key) + const knot_key_t *key, uint8_t *to_sign, + size_t to_sign_len) { if (!msg || !msg_len || !key || !key || !digest || !digest_len) { return KNOT_EBADARG; @@ -869,52 +632,135 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, if (!tmp_tsig) { return KNOT_ENOMEM; } + + /* Create rdata for TSIG RR. */ + knot_rdata_t *rdata = knot_rdata_new(); + if (!rdata) { + dbg_tsig_detail("TSIG: rdata = NULL\n"); + knot_rrset_free(&tmp_tsig); + return KNOT_ENOMEM; + } + + int ret = 0; - tsig_rdata_store_current_time(tmp_tsig); + ret = knot_rrset_add_rdata(tmp_tsig, rdata); + if (ret != KNOT_EOK) { + dbg_tsig_detail("TSIG: could not add rdata\n"); + knot_rrset_free(&tmp_tsig); + knot_rdata_free(&rdata); + return ret; + } + + /* Create items for TSIG RR. */ + knot_rrtype_descriptor_t *desc = + knot_rrtype_descriptor_by_type(KNOT_RRTYPE_TSIG); + assert(desc); + + knot_rdata_item_t *items = + malloc(sizeof(knot_rdata_item_t) * desc->length); + if (!items) { + dbg_tsig_detail("TSIG: items = NULL\n"); + ERR_ALLOC_FAILED; + knot_rrset_free(&tmp_tsig); + knot_rdata_free(&rdata); + return KNOT_ENOMEM; + } + + memset(items, 0, sizeof(knot_rdata_item_t) * desc->length); + ret = knot_rdata_set_items(rdata, items, desc->length); + if (ret != KNOT_EOK) { + dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", knot_strerror(ret)); + knot_rrset_free(&tmp_tsig); + knot_rdata_free(&rdata); + free(items); + return ret; + } + free(items); + + tsig_rdata_store_current_time(tmp_tsig); + tsig_rdata_set_fudge(tmp_tsig, 300); + /* Create wire to be signed. */ - size_t wire_len = prev_digest_len + *msg_len + KNOT_TSIG_TIMERS_LENGTH; + 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); + knot_rdata_deep_free(&rdata, KNOT_RRTYPE_TSIG, 0); 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, prev_digest, sizeof(uint8_t) * prev_digest_len); + memcpy(wire + 2, prev_digest, sizeof(uint8_t) * prev_digest_len); /* Write original message. */ - memcpy(msg + prev_digest_len, msg, *msg_len); + memcpy(wire + prev_digest_len + 2, to_sign, to_sign_len); /* Write timers. */ - knot_tsig_wire_write_timers(msg + prev_digest_len + *msg_len, tmp_tsig); + 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(prev_digest, prev_digest_len); + + dbg_tsig_detail("Timers: \n"); + dbg_tsig_hex_detail(wire + prev_digest_len + *msg_len, + KNOT_TSIG_TIMERS_LENGTH); - int ret = 0; 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_deep_free(&tmp_tsig, 1, 1, 1); *digest_len = 0; return ret; } if (digest_tmp_len > *digest_len) { + knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); *digest_len = 0; return KNOT_ESPACE; } - free(wire); - /* Set the MAC. */ - tsig_rdata_set_mac(tmp_tsig, *digest_len, digest); + tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp); + + /* Set algorithm. */ + tsig_rdata_set_alg(tmp_tsig, key->algorithm); + + /* Set original id. */ + tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); + + /* Set TSIG error. */ + tsig_rdata_set_tsig_error(tmp_tsig, 0); + + /* Set other data. */ + tsig_rdata_set_other_data(tmp_tsig, 0, NULL); + + dbg_tsig_detail("Message max length: %zu, message length: %zu\n", + msg_max_len, *msg_len); size_t tsig_wire_size = msg_max_len - *msg_len; int rr_count = 0; ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, &tsig_wire_size, &rr_count); if (ret != KNOT_EOK) { + knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); *digest_len = 0; return ret; } + /* This should not happen, at least one rr has to be converted. */ + if (rr_count == 0) { + knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); + return KNOT_EBADARG; + } + knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); *msg_len += tsig_wire_size; @@ -932,6 +778,7 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr, const uint8_t *request_mac, size_t request_mac_len, const knot_key_t *tsig_key, + uint64_t prev_time_signed, int use_times) { if (!tsig_rr || !wire || !tsig_key) { @@ -939,7 +786,7 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr, } /* Check time signed. */ - int ret = knot_tsig_check_time_signed(tsig_rr); + int ret = knot_tsig_check_time_signed(tsig_rr, prev_time_signed); if (ret != KNOT_EOK) { return ret; } @@ -1061,29 +908,140 @@ int knot_tsig_server_check(const knot_rrset_t *tsig_rr, const knot_key_t *tsig_key) { dbg_tsig_verb("tsig_server_check()\n"); - return knot_tsig_check_digest(tsig_rr, wire, size, NULL, 0, tsig_key, 0); + return knot_tsig_check_digest(tsig_rr, wire, size, NULL, 0, tsig_key, + 0, 0); } int knot_tsig_client_check(const knot_rrset_t *tsig_rr, const uint8_t *wire, size_t size, const uint8_t *request_mac, size_t request_mac_len, - const knot_key_t *tsig_key) + const knot_key_t *tsig_key, + uint64_t prev_time_signed) { dbg_tsig_verb("tsig_client_check()\n"); return knot_tsig_check_digest(tsig_rr, wire, size, request_mac, - request_mac_len, tsig_key, 0); + request_mac_len, tsig_key, + prev_time_signed, 0); } int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr, const uint8_t *wire, size_t size, const uint8_t *prev_digest, size_t prev_digest_len, - const knot_key_t *tsig_key) + const knot_key_t *tsig_key, + uint64_t prev_time_signed) { // return knot_tsig_client_check(tsig_rr, wire, size, prev_digest, // prev_digest_len, tsig_key); dbg_tsig_verb("tsig_client_check_next()\n"); return knot_tsig_check_digest(tsig_rr, wire, size, prev_digest, - prev_digest_len, tsig_key, 1); + prev_digest_len, tsig_key, + prev_time_signed, 1); return KNOT_ENOTSUP; } + +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_EBADARG; + } + + /*! \todo What key to use, when we do not sign? Does this even work? */ + knot_dname_t *key_name = + knot_dname_deep_copy(knot_rrset_owner(tsig_rr)); + if (key_name == NULL) { + dbg_tsig_detail("TSIG: failed to copy owner\n"); + return KNOT_ERROR; + } + + knot_rrset_t *tmp_tsig = + knot_rrset_new(key_name, + KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0); + if (!tmp_tsig) { + dbg_tsig_detail("TSIG: tmp_tsig = NULL\n"); + knot_dname_free(&key_name); + return KNOT_ENOMEM; + } + + /* Create rdata for TSIG RR. */ + knot_rdata_t *rdata = knot_rdata_new(); + if (!rdata) { + dbg_tsig_detail("TSIG: rdata = NULL\n"); + knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); + return KNOT_ENOMEM; + } + + knot_rrset_add_rdata(tmp_tsig, rdata); + + /* Create items for TSIG RR. */ + knot_rrtype_descriptor_t *desc = + knot_rrtype_descriptor_by_type(KNOT_RRTYPE_TSIG); + assert(desc); + + knot_rdata_item_t *items = + malloc(sizeof(knot_rdata_item_t) * desc->length); + if (!items) { + dbg_tsig_detail("TSIG: items = NULL\n"); + ERR_ALLOC_FAILED; + knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); + return KNOT_ENOMEM; + } + + memset(items, 0, sizeof(knot_rdata_item_t) * desc->length); + + int ret = knot_rdata_set_items(rdata, items, desc->length); + if (ret != KNOT_EOK) { + dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", + knot_strerror(ret)); + knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); + return ret; + } + free(items); + + knot_dname_t *alg_name = + knot_dname_deep_copy(tsig_rdata_alg_name(tsig_rr)); + if (alg_name == NULL) { + dbg_tsig_detail("TSIG: failed to copy alg name\n"); + return KNOT_ERROR; + } + + tsig_rdata_set_alg_name(tmp_tsig, alg_name); + tsig_rdata_set_time_signed(tmp_tsig, tsig_rdata_time_signed(tsig_rr)); + tsig_rdata_set_fudge(tmp_tsig, tsig_rdata_fudge(tsig_rr)); + tsig_rdata_set_mac(tmp_tsig, 0, NULL); + + /* Set original ID */ + tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); + + /* Set error */ + tsig_rdata_set_tsig_error(tmp_tsig, tsig_rcode); + + assert(tsig_rcode != KNOT_TSIG_RCODE_BADTIME); + /* Set other len. */ + tsig_rdata_set_other_data(tmp_tsig, 0, 0); + + size_t tsig_wire_len = msg_max_len - *msg_len; + int rr_count = 0; + + /* Write RRSet to wire */ + ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, + &tsig_wire_len, &rr_count); + if (ret != KNOT_EOK) { + dbg_tsig_detail("TSIG: rrset_to_wire = %s\n", knot_strerror(ret)); + knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); + return ret; + } + + knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); + + *msg_len += tsig_wire_len; + + uint16_t arcount = knot_wire_get_arcount(msg); + knot_wire_set_arcount(msg, ++arcount); + + return KNOT_EOK; +} + diff --git a/src/libknot/tsig-op.h b/src/libknot/tsig-op.h index b206dc7..07a84a8 100644 --- a/src/libknot/tsig-op.h +++ b/src/libknot/tsig-op.h @@ -63,7 +63,8 @@ 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_key_t *key); + const knot_key_t *key, uint16_t tsig_rcode, + uint64_t request_time_signed); /*! * \brief Generate TSIG signature of a 2nd or later message in a TCP session. @@ -96,7 +97,8 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len, 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_key_t *key); + const knot_key_t *key, uint8_t *to_sign, + size_t to_sign_len); /*! * \brief Checks incoming request. @@ -133,7 +135,8 @@ int knot_tsig_server_check(const knot_rrset_t *tsig_rr, int knot_tsig_client_check(const knot_rrset_t *tsig_rr, const uint8_t *wire, size_t size, const uint8_t *request_mac, size_t request_mac_len, - const knot_key_t *key); + const knot_key_t *key, + uint64_t prev_time_signed); /*! * \brief Checks signature of 2nd or next packet in a TCP session. @@ -154,7 +157,11 @@ int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr, const uint8_t *wire, size_t size, const uint8_t *prev_digest, size_t prev_digest_len, - const knot_key_t *key); + const knot_key_t *key, + uint64_t prev_time_signed); + +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); #endif /* _KNOT_TSIG_H_ */ diff --git a/src/libknot/tsig.c b/src/libknot/tsig.c index 432539f..5790b68 100644 --- a/src/libknot/tsig.c +++ b/src/libknot/tsig.c @@ -32,7 +32,7 @@ /*! \brief TSIG algorithms table. */ #define TSIG_ALG_TABLE_SIZE 8 static knot_lookup_table_t tsig_alg_table[TSIG_ALG_TABLE_SIZE] = { - { KNOT_TSIG_ALG_GSS_TSIG, "gss-tsig." }, + { KNOT_TSIG_ALG_NULL, "gss-tsig." }, { KNOT_TSIG_ALG_HMAC_MD5, "hmac-md5.sig-alg.reg.int." }, { KNOT_TSIG_ALG_HMAC_SHA1, "hmac-sha1." }, { KNOT_TSIG_ALG_HMAC_SHA224, "hmac-sha224." }, @@ -84,6 +84,9 @@ int tsig_rdata_set_alg_name(knot_rrset_t *tsig, knot_dname_t *alg_name) } knot_rdata_item_set_dname(rdata, 0, alg_name_copy); + + /* Release the dname. We want it to have 1 reference only. */ + knot_dname_release(alg_name_copy); return KNOT_EOK; } @@ -103,12 +106,15 @@ int tsig_rdata_set_alg(knot_rrset_t *tsig, tsig_algorithm_t alg) const char *alg_str = tsig_alg_to_str(alg); knot_dname_t *alg_name_copy = knot_dname_new_from_str(alg_str, strlen(alg_str), - 0); + NULL); if (!alg_name_copy) { return KNOT_ENOMEM; } - + knot_rdata_item_set_dname(rdata, 0, alg_name_copy); + + /* Release the dname. We want it to have 1 reference only. */ + knot_dname_release(alg_name_copy); return KNOT_EOK; } @@ -306,8 +312,35 @@ const knot_dname_t *tsig_rdata_alg_name(const knot_rrset_t *tsig) tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig) { - /*! \todo [TSIG] Implement me. */ - return KNOT_TSIG_ALG_HMAC_MD5; + if (!tsig) { + return KNOT_TSIG_ALG_NULL; + } + + /* Get the algorithm name. */ + const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig); + if (!alg_name) { + dbg_tsig_detail("TSIG: rdata: cannot get algorithm name.\n"); + return KNOT_TSIG_ALG_NULL; + } + + /* Convert alg name to string. */ + char *name = knot_dname_to_str(alg_name); + if (!name) { + dbg_tsig_detail("TSIG: rdata: cannot convert alg name.\n"); + return KNOT_TSIG_ALG_NULL; + } + + knot_lookup_table_t *item = knot_lookup_by_name(tsig_alg_table, name); + if (!item) { + dbg_tsig_detail("TSIG: rdata: unknown algorithm.\n"); + return KNOT_TSIG_ALG_NULL; + } + free(name); + + int id = item->id; + + + return id; } uint64_t tsig_rdata_time_signed(const knot_rrset_t *tsig) @@ -581,7 +614,7 @@ const char* tsig_alg_to_str(tsig_algorithm_t alg) size_t tsig_wire_maxsize(const knot_key_t* key) { size_t alg_name_size = strlen(tsig_alg_to_str(key->algorithm)) + 1; - + return knot_dname_size(key->name) + sizeof(uint16_t) + /* TYPE */ sizeof(uint16_t) + /* CLASS */ diff --git a/src/libknot/tsig.h b/src/libknot/tsig.h index eafcfab..73fa832 100644 --- a/src/libknot/tsig.h +++ b/src/libknot/tsig.h @@ -59,9 +59,9 @@ typedef struct knot_key knot_key_t; enum tsig_algorithm_digest_length { KNOT_TSIG_ALG_DIG_LENGTH_GSS_TSIG = 0, KNOT_TSIG_ALG_DIG_LENGTH_HMAC_MD5 = 16, - KNOT_TSIG_ALG_DIG_LENGTH_SHA1 = 0, + KNOT_TSIG_ALG_DIG_LENGTH_SHA1 = 20, KNOT_TSIG_ALG_DIG_LENGTH_SHA224 = 0, - KNOT_TSIG_ALG_DIG_LENGTH_SHA256 = 0, + KNOT_TSIG_ALG_DIG_LENGTH_SHA256 = 32, KNOT_TSIG_ALG_DIG_LENGTH_SHA384 = 0, KNOT_TSIG_ALG_DIG_LENGTH_SHA512 = 0 }; diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c index 51be430..b1feddb 100644 --- a/src/libknot/updates/xfr-in.c +++ b/src/libknot/updates/xfr-in.c @@ -121,7 +121,8 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype, xfr->digest_size = xfr->digest_max_size; rc = knot_tsig_sign(wire, &wire_size, *size, NULL, 0, - xfr->digest, &xfr->digest_size, xfr->tsig_key); + xfr->digest, &xfr->digest_size, xfr->tsig_key, + 0, 0); if (rc != KNOT_EOK) { /*! \todo [TSIG] Handle TSIG errors. */ knot_packet_free(&pkt); @@ -315,7 +316,7 @@ static int xfrin_process_orphan_rrsigs(knot_zone_contents_t *zone, /*----------------------------------------------------------------------------*/ -static void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs) +void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs) { xfrin_orphan_rrsig_t *r = *rrsigs; while (r != NULL) { @@ -358,6 +359,15 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr, } if (xfr->tsig_key) { + // just append the wireformat to the TSIG data + assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size + >= xfr->wire_size); + memcpy(xfr->tsig_data + xfr->tsig_data_size, + xfr->wire, xfr->wire_size); + xfr->tsig_data_size += xfr->wire_size; + } + + if (xfr->tsig_key) { if (tsig_req && tsig == NULL) { // TSIG missing!! return KNOT_EMALF; @@ -367,12 +377,14 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr, ret = knot_tsig_client_check(tsig, xfr->wire, xfr->wire_size, xfr->digest, xfr->digest_size, - xfr->tsig_key); + xfr->tsig_key, + xfr->tsig_prev_time_signed); } else { ret = knot_tsig_client_check_next(tsig, - xfr->wire, xfr->wire_size, + xfr->tsig_data, xfr->tsig_data_size, xfr->digest, xfr->digest_size, - xfr->tsig_key); + xfr->tsig_key, + xfr->tsig_prev_time_signed); } if (ret != KNOT_EOK) { @@ -392,20 +404,23 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr, memcpy(xfr->digest, tsig_rdata_mac(tsig), tsig_rdata_mac_length(tsig)); xfr->digest_size = tsig_rdata_mac_length(tsig); + + // Extract the time signed from the TSIG and store it + // We may rewrite the tsig_req_time_signed field + xfr->tsig_prev_time_signed = + tsig_rdata_time_signed(tsig); + - } else { // TSIG not required and not there - // just append the wireformat to the TSIG data - assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size - >= xfr->wire_size); - memcpy(xfr->tsig_data + xfr->tsig_data_size, - xfr->wire, xfr->wire_size); - xfr->tsig_data_size += xfr->wire_size; - } + }/* else { // TSIG not required and not there + + }*/ } else if (tsig != NULL) { // TSIG where it should not be return KNOT_EMALF; } + knot_rrset_deep_free(&tsig, 1, 1, 1); + return KNOT_EOK; } @@ -482,8 +497,8 @@ int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size, // this should be the first packet /*! \note [TSIG] Packet number for checking TSIG validation. */ xfr->packet_nr = 0; - /*! \note [TSIG] Storing total size of data for TSIG digest. */ - xfr->tsig_data_size = 0; +// /*! \note [TSIG] Storing total size of data for TSIG digest. */ +// xfr->tsig_data_size = 0; // create new zone /*! \todo Ensure that the packet is the first one. */ @@ -586,16 +601,16 @@ dbg_xfrin_exec( /*! \note [TSIG] add the packet wire size to the data to be verified by * TSIG */ - if (xfr->tsig_key) { - dbg_xfrin("Adding packet wire to TSIG data (size till now: %zu," - " adding: %zu).\n", xfr->tsig_data_size, - xfr->wire_size); - assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size - >= xfr->wire_size); - memcpy(xfr->tsig_data + xfr->tsig_data_size, xfr->wire, - xfr->wire_size); - xfr->tsig_data_size += xfr->wire_size; - } +// if (xfr->tsig_key) { +// dbg_xfrin("Adding packet wire to TSIG data (size till now: %zu," +// " adding: %zu).\n", xfr->tsig_data_size, +// xfr->wire_size); +// assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size +// >= xfr->wire_size); +// memcpy(xfr->tsig_data + xfr->tsig_data_size, xfr->wire, +// xfr->wire_size); +// xfr->tsig_data_size += xfr->wire_size; +// } assert(zone != NULL); @@ -904,8 +919,7 @@ static int xfrin_parse_first_rr(knot_packet_t **packet, const uint8_t *pkt, /*----------------------------------------------------------------------------*/ -int xfrin_process_ixfr_packet(/*const uint8_t *pkt, size_t size, - knot_changesets_t **chs*/knot_ns_xfr_t *xfr) +int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr) { size_t size = xfr->wire_size; const uint8_t *pkt = xfr->wire; @@ -922,8 +936,6 @@ int xfrin_process_ixfr_packet(/*const uint8_t *pkt, size_t size, } knot_packet_t *packet = NULL; -// knot_rrset_t *soa1 = NULL; -// knot_rrset_t *soa2 = NULL; knot_rrset_t *rr = NULL; int ret; @@ -977,13 +989,16 @@ int xfrin_process_ixfr_packet(/*const uint8_t *pkt, size_t size, /* * If there is no other records in the response than the SOA, it - * means one of these two cases: + * means one of these three cases: * * 1) The server does not have newer zone than ours. * This is indicated by serial equal to the one of our zone. * 2) The server wants to send the transfer but is unable to fit * it in the packet. This is indicated by serial different - * (newer) from the one of our zone. + * (newer) from the one of our zone, but applies only for + * IXFR/UDP. + * 3) The master is weird and sends only SOA in the first packet + * of a fallback to AXFR answer (PowerDNS does this). * * The serials must be compared in other parts of the server, so * just indicate that the answer contains only one SOA. @@ -996,8 +1011,6 @@ int xfrin_process_ixfr_packet(/*const uint8_t *pkt, size_t size, knot_rrset_deep_free(&rr, 1, 1, 1); dbg_xfrin("Fallback to AXFR.\n"); ret = XFRIN_RES_FALLBACK; - knot_free_changesets(chs); - xfr->data = 0; return ret; } } else { @@ -2014,7 +2027,6 @@ static knot_node_t *xfrin_add_new_node(knot_zone_contents_t *contents, // dbg_xfrin("Adding new node to zone. From owner: %s type %s\n", // knot_dname_to_str(node->owner), // knot_rrtype_to_string(rrset->type)); -// getchar(); if (knot_rrset_type(rrset) == KNOT_RRTYPE_NSEC3) { ret = knot_zone_contents_add_nsec3_node(contents, node, 1, 0, 1); @@ -2067,7 +2079,6 @@ static int xfrin_apply_add_normal(xfrin_changes_t *changes, dbg_xfrin("applying rrset:\n"); knot_rrset_dump(add, 0); -// getchar(); if (!*rrset || knot_dname_compare(knot_rrset_owner(*rrset), @@ -2089,7 +2100,6 @@ dbg_xfrin_exec_verb( knot_rrtype_to_string(add->type)); free(name); ); -// getchar(); // add the RRSet from the changeset to the node /*! \todo What about domain names?? Shouldn't we use the * zone-contents' version of this function?? diff --git a/src/libknot/updates/xfr-in.h b/src/libknot/updates/xfr-in.h index 8a7c64b..bb1b98b 100644 --- a/src/libknot/updates/xfr-in.h +++ b/src/libknot/updates/xfr-in.h @@ -151,6 +151,8 @@ int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size, xfrin_constructed_zone_t **zone*/ knot_ns_xfr_t *xfr); +void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs); + /*! * \brief Destroys the whole changesets structure. * diff --git a/src/libknot/util/conv.c b/src/libknot/util/conv.c new file mode 100644 index 0000000..6626ddd --- /dev/null +++ b/src/libknot/util/conv.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <ctype.h> +#include "conv.h" + +#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */ + + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +static int b64rmap_initialized = 0; +static uint8_t b64rmap[256]; + +static const uint8_t b64rmap_special = 0xf0; +static const uint8_t b64rmap_end = 0xfd; +static const uint8_t b64rmap_space = 0xfe; +static const uint8_t b64rmap_invalid = 0xff; + +/** + * Initializing the reverse map is not thread safe. + * Which is fine for NSD. For now... + **/ +static void b64_initialize_rmap() +{ + int i; + char ch; + + /* Null: end of string, stop parsing */ + b64rmap[0] = b64rmap_end; + + for (i = 1; i < 256; ++i) { + ch = (char)i; + /* Whitespaces */ + if (isspace(ch)) { + b64rmap[i] = b64rmap_space; + } + /* Padding: stop parsing */ + else if (ch == Pad64) { + b64rmap[i] = b64rmap_end; + } + /* Non-base64 char */ + else { + b64rmap[i] = b64rmap_invalid; + } + } + + /* Fill reverse mapping for base64 chars */ + for (i = 0; Base64[i] != '\0'; ++i) { + b64rmap[(uint8_t)Base64[i]] = i; + } + + b64rmap_initialized = 1; +} + +static int b64_pton_do(char const *src, uint8_t *target, size_t targsize) +{ + int tarindex, state, ch; + uint8_t ofs; + + state = 0; + tarindex = 0; + + while (1) { + ch = *src++; + ofs = b64rmap[ch]; + + if (ofs >= b64rmap_special) { + /* Ignore whitespaces */ + if (ofs == b64rmap_space) { + continue; + } + /* End of base64 characters */ + if (ofs == b64rmap_end) { + break; + } + /* A non-base64 character. */ + return (-1); + } + + switch (state) { + case 0: + if ((size_t)tarindex >= targsize) { + return (-1); + } + target[tarindex] = ofs << 2; + state = 1; + break; + case 1: + if ((size_t)tarindex + 1 >= targsize) { + return (-1); + } + target[tarindex] |= ofs >> 4; + target[tarindex+1] = (ofs & 0x0f) + << 4 ; + tarindex++; + state = 2; + break; + case 2: + if ((size_t)tarindex + 1 >= targsize) { + return (-1); + } + target[tarindex] |= ofs >> 2; + target[tarindex+1] = (ofs & 0x03) + << 6; + tarindex++; + state = 3; + break; + case 3: + if ((size_t)tarindex >= targsize) { + return (-1); + } + target[tarindex] |= ofs; + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (b64rmap[ch] != b64rmap_space) { + break; + } + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) { + return (-1); + } + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (b64rmap[ch] != b64rmap_space) { + return (-1); + } + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target[tarindex] != 0) { + return (-1); + } + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) { + return (-1); + } + } + + return (tarindex); +} + + +static int b64_pton_len(char const *src) +{ + int tarindex, state, ch; + uint8_t ofs; + + state = 0; + tarindex = 0; + + while (1) { + ch = *src++; + ofs = b64rmap[ch]; + + if (ofs >= b64rmap_special) { + /* Ignore whitespaces */ + if (ofs == b64rmap_space) { + continue; + } + /* End of base64 characters */ + if (ofs == b64rmap_end) { + break; + } + /* A non-base64 character. */ + return (-1); + } + + switch (state) { + case 0: + state = 1; + break; + case 1: + tarindex++; + state = 2; + break; + case 2: + tarindex++; + state = 3; + break; + case 3: + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (b64rmap[ch] != b64rmap_space) { + break; + } + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) { + return (-1); + } + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (b64rmap[ch] != b64rmap_space) { + return (-1); + } + + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) { + return (-1); + } + } + + return (tarindex); +} + +int b64_pton(char const *src, uint8_t *target, size_t targsize) +{ + if (!b64rmap_initialized) { + b64_initialize_rmap(); + } + + if (target) { + return b64_pton_do(src, target, targsize); + } else { + return b64_pton_len(src); + } +} + +#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */ diff --git a/src/libknot/util/conv.h b/src/libknot/util/conv.h new file mode 100644 index 0000000..d1e6dae --- /dev/null +++ b/src/libknot/util/conv.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KNOT_CONV_H_ +#define _KNOT_CONV_H_ + +#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */ + +/*! + * \brief Base64 presentation to wire conversion. + */ +int b64_pton(char const *src, uint8_t *target, size_t targsize); + +#endif // _KNOT_CONV_H_ diff --git a/src/libknot/util/debug.h b/src/libknot/util/debug.h index 2f9f5fd..2c03c9c 100644 --- a/src/libknot/util/debug.h +++ b/src/libknot/util/debug.h @@ -57,10 +57,10 @@ //#define KNOT_NSEC3_DEBUG //#define CUCKOO_DEBUG //#define CUCKOO_DEBUG_HASH -//#define KNOT_NS_DEBUG +#define KNOT_NS_DEBUG //#define KNOT_XFRIN_DEBUG //#define KNOT_DDNS_DEBUG -//#define KNOT_TSIG_DEBUG +#define KNOT_TSIG_DEBUG /*! * \brief Dumps RDATA of the given type. @@ -748,6 +748,45 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone); #define dbg_tsig_hex_detail(data, len) #endif +#ifdef KNOT_RRSET_DEBUG + +/* Brief messages. */ +#ifdef DEBUG_ENABLE_BRIEF +#define dbg_rrset(msg...) fprintf(stderr, msg) +#define dbg_rrset_hex(data, len) hex_print((data), (len)) +#else +#define dbg_rrset(msg...) +#define dbg_rrset_hex(data, len) +#endif + +/* Verbose messages. */ +#ifdef DEBUG_ENABLE_VERBOSE +#define dbg_rrset_verb(msg...) fprintf(stderr, msg) +#define dbg_rrset_hex_verb(data, len) hex_print((data), (len)) +#else +#define dbg_rrset_verb(msg...) +#define dbg_rrset_hex_verb(data, len) +#endif + +/* Detail messages. */ +#ifdef DEBUG_ENABLE_DETAILS +#define dbg_rrset_detail(msg...) fprintf(stderr, msg) +#define dbg_rrset_hex_detail(data, len) hex_print((data), (len)) +#else +#define dbg_rrset_detail(msg...) +#define dbg_rrset_hex_detail(data, len) +#endif + +/* No messages. */ +#else +#define dbg_rrset(msg...) +#define dbg_rrset_hex(data, len) +#define dbg_rrset_verb(msg...) +#define dbg_rrset_hex_verb(data, len) +#define dbg_rrset_detail(msg...) +#define dbg_rrset_hex_detail(data, len) +#endif + /******************************************************************************/ #endif /* _KNOT_DEBUG_H_ */ diff --git a/src/libknot/util/error.h b/src/libknot/util/error.h index da45151..2f34a28 100644 --- a/src/libknot/util/error.h +++ b/src/libknot/util/error.h @@ -64,7 +64,8 @@ enum knot_error { KNOT_ENOIXFR, /*!< Transfer is not IXFR (is in AXFR format). */ KNOT_EXFRREFUSED, /*!< Zone transfer refused by the server. */ KNOT_ECONN, /*!< Connection reset. */ - KNOT_ERROR_COUNT = 30 + KNOT_EIXFRSPACE, /*!< IXFR reply did not fit in. */ + KNOT_ERROR_COUNT = 31 }; /*! \brief Table linking error messages to error codes. */ diff --git a/src/libknot/util/libknot_error.c b/src/libknot/util/libknot_error.c index bc2bed2..5795a23 100644 --- a/src/libknot/util/libknot_error.c +++ b/src/libknot/util/libknot_error.c @@ -49,5 +49,6 @@ const error_table_t knot_error_msgs[KNOT_ERROR_COUNT] = { {KNOT_TSIG_EBADKEY, "TSIG key not recognized or invalid." }, {KNOT_TSIG_EBADTIME, "TSIG signing time out of range." }, {KNOT_ECONN, "Connection reset."}, + {KNOT_EIXFRSPACE, "IXFR reply did not fit in."}, {KNOT_ERROR, 0} }; diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c index d550728..feec8e5 100644 --- a/src/libknot/zone/zone-contents.c +++ b/src/libknot/zone/zone-contents.c @@ -676,7 +676,9 @@ static int knot_zone_contents_dnames_from_rdata_to_table( { unsigned int count = knot_rdata_item_count(rdata); int rc = 0; - assert(count <= d->length); + if (d->fixed_items) { + assert(count <= d->length); + } // for each RDATA item for (unsigned int j = 0; j < count; ++j) { if (d->wireformat[j] |