summaryrefslogtreecommitdiff
path: root/src/libknot
diff options
context:
space:
mode:
Diffstat (limited to 'src/libknot')
-rw-r--r--src/libknot/nameserver/name-server.c376
-rw-r--r--src/libknot/nameserver/name-server.h30
-rw-r--r--src/libknot/packet/packet.c16
-rw-r--r--src/libknot/packet/packet.h7
-rw-r--r--src/libknot/rdata.c16
-rw-r--r--src/libknot/rdata.h1
-rw-r--r--src/libknot/rrset.c39
-rw-r--r--src/libknot/rrset.h2
-rw-r--r--src/libknot/tsig-op.c644
-rw-r--r--src/libknot/tsig-op.h15
-rw-r--r--src/libknot/tsig.c45
-rw-r--r--src/libknot/tsig.h4
-rw-r--r--src/libknot/updates/xfr-in.c82
-rw-r--r--src/libknot/updates/xfr-in.h2
-rw-r--r--src/libknot/util/conv.c325
-rw-r--r--src/libknot/util/conv.h44
-rw-r--r--src/libknot/util/debug.h43
-rw-r--r--src/libknot/util/error.h3
-rw-r--r--src/libknot/util/libknot_error.c1
-rw-r--r--src/libknot/zone/zone-contents.c4
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]