Commit c66926b8 authored by Lubos Slovak's avatar Lubos Slovak

Handling of TSIG in AXFR-in

refs #1362 @1.5h
parent d0ebd54b
......@@ -64,8 +64,6 @@ static const uint8_t NSID_DATA[6] = {0x46, 0x6f, 0x6f, 0x42, 0x61, 0x72};
/*! \brief Internal error code to propagate need for SERVFAIL response. */
static const int NS_ERR_SERVFAIL = -999;
static const int KNOT_NS_TSIG_FREQ = 100;
/*----------------------------------------------------------------------------*/
/* Private functions */
/*----------------------------------------------------------------------------*/
......@@ -973,7 +971,6 @@ static int ns_put_nsec_nxdomain(const knot_dname_t *qname,
const knot_node_t *closest_encloser,
knot_packet_t *resp)
{
/*! \todo Change to zone contents. */
const knot_rrset_t *rrset = NULL;
// check if we have previous; if not, find one using the tree
......@@ -1068,7 +1065,6 @@ static int ns_put_nsec3_nxdomain(const knot_zone_contents_t *zone,
const knot_dname_t *qname,
knot_packet_t *resp)
{
/*! \todo Change to zone contents. */
// 1) Closest encloser proof
dbg_ns("Putting closest encloser proof.\n");
int ret = ns_put_nsec3_closest_encloser_proof(zone, &closest_encloser,
......@@ -1326,7 +1322,6 @@ static inline int ns_referral(const knot_node_t *node,
const knot_dname_t *qname,
knot_packet_t *resp)
{
/*! \todo Change to zone contents. */
dbg_ns("Referral response.\n");
while (!knot_node_is_deleg_point(node)) {
......@@ -1431,7 +1426,6 @@ static int ns_answer_from_node(const knot_node_t *node,
const knot_dname_t *qname, uint16_t qtype,
knot_packet_t *resp)
{
/*! \todo Change to zone contents. */
dbg_ns("Putting answers from found node to the response...\n");
int answers = ns_put_answer(node, qname, qtype, resp);
......@@ -1967,7 +1961,7 @@ typedef struct ns_axfr_params {
/*----------------------------------------------------------------------------*/
static int ns_tsig_required(int packet_nr)
int knot_ns_tsig_required(int packet_nr)
{
return (packet_nr % KNOT_NS_TSIG_FREQ == 0);
}
......@@ -2107,7 +2101,7 @@ rrset:
// TODO: send the packet and clean the structure
dbg_ns("Packet full, sending..\n");
ret = ns_xfr_send_and_clear(params->xfr,
ns_tsig_required(params->xfr->packet_nr));
knot_ns_tsig_required(params->xfr->packet_nr));
if (ret != KNOT_EOK) {
// some wierd problem, we should end
params->ret = KNOT_ERROR;
......@@ -2136,7 +2130,7 @@ rrsigs:
// TODO: send the packet and clean the structure
dbg_ns("Packet full, sending..\n");
ret = ns_xfr_send_and_clear(params->xfr,
ns_tsig_required(params->xfr->packet_nr));
knot_ns_tsig_required(params->xfr->packet_nr));
if (ret != KNOT_EOK) {
// some wierd problem, we should end
params->ret = KNOT_ERROR;
......@@ -2236,7 +2230,7 @@ static int ns_axfr_from_zone(knot_zone_contents_t *zone, knot_ns_xfr_t *xfr)
// add the SOA record to a new packet
dbg_ns("Packet full, sending..\n");
ret = ns_xfr_send_and_clear(xfr,
ns_tsig_required(xfr->packet_nr));
knot_ns_tsig_required(xfr->packet_nr));
if (ret != KNOT_EOK) {
return ret;
}
......@@ -2265,7 +2259,7 @@ static int ns_ixfr_put_rrset(knot_ns_xfr_t *xfr, const knot_rrset_t *rrset)
if (res == KNOT_ESPACE) {
knot_response_set_rcode(xfr->response, KNOT_RCODE_NOERROR);
/*! \todo Probably rename the function. */
ns_xfr_send_and_clear(xfr, ns_tsig_required(xfr->packet_nr));
ns_xfr_send_and_clear(xfr, knot_ns_tsig_required(xfr->packet_nr));
res = knot_response_add_rrset_answer(xfr->response,
rrset, 0, 0, 0);
......@@ -3093,17 +3087,18 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
int knot_ns_process_axfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
{
/*! \todo Implement me.
* - xfr contains partially-built zone or NULL (xfr->data)
* - incoming packet is in xfr->wire
* - incoming packet size is in xfr->wire_size
* - signalize caller, that transfer is finished/error (ret. code?)
/*!
* \todo Here we assume that 'xfr' contains TSIG information
* and the digest of the query sent to the master or the previous
* digest.
*/
dbg_ns("ns_process_axfrin: incoming packet, wire size: %zu\n",
xfr->wire_size);
int ret = xfrin_process_axfr_packet(xfr->wire, xfr->wire_size,
(xfrin_constructed_zone_t **)(&xfr->data));
int ret = xfrin_process_axfr_packet(/*xfr->wire, xfr->wire_size,*/
/*(xfrin_constructed_zone_t **)(&xfr->data)*/
xfr);
if (ret > 0) { // transfer finished
dbg_ns("ns_process_axfrin: AXFR finished, zone created.\n");
......@@ -3136,6 +3131,10 @@ int knot_ns_process_axfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
//knot_zone_contents_dump(zone, 0);
}
/*!
* \todo In case of error, shouldn't the zone be destroyed here?
*/
return ret;
}
......@@ -3281,6 +3280,10 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
}
}
/*!
* \todo In case of error, shouldn't the zone be destroyed here?
*/
return ret;
}
......
......@@ -92,15 +92,32 @@ typedef struct knot_ns_xfr {
void *owner;
/* TSIG fields */
uint8_t *tsig_data; /*!< Message(s) to sign in wireformat. */
/*! \brief Message(s) to sign in wireformat.
*
* This field should be allocated at the start of transfer and
* freed at the end. During the transfer it is only rewritten.
*/
uint8_t *tsig_data;
size_t tsig_data_size; /*!< Size of the message(s) in bytes */
knot_rrset_t *tsig; /*!< Response TSIG. */
size_t tsig_size; /*!< Size of the TSIG RR wireformat in bytes.*/
uint8_t *prev_digest; /*!< Prev. digest (request digest if 1st msg).*/
uint8_t *prev_digest; /*!< Previous digest or request digest. */
size_t prev_digest_size; /*!< Size of previous digest in bytes. */
int packet_nr; /*!< Number of the packet currently assembled.*/
/*!
* \brief Number of the packet currently assembled.
*
* In case of XFR-in, this is not the overall number of packet, just
* number counted from last TSIG check.
*/
int packet_nr;
} knot_ns_xfr_t;
static const int KNOT_NS_TSIG_FREQ = 100;
static const size_t KNOT_NS_TSIG_DATA_MAX_SIZE = 100 * 64 * 1024;
/*!
* \brief XFR request flags.
*/
......@@ -291,6 +308,8 @@ void *knot_ns_get_data(knot_nameserver_t *nameserver);
void knot_ns_set_data(knot_nameserver_t *nameserver, void *data);
int knot_ns_tsig_required(int packet_nr);
/*!
* \brief Properly destroys the name server structure.
*
......
......@@ -879,6 +879,15 @@ int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
/*----------------------------------------------------------------------------*/
int knot_packet_parse_next_rr_additional(knot_packet_t *packet,
knot_rrset_t **rr)
{
/*! \todo Implement. */
return KNOT_ENOTSUP;
}
/*----------------------------------------------------------------------------*/
size_t knot_packet_size(const knot_packet_t *packet)
{
return packet->size;
......
......@@ -275,6 +275,9 @@ int knot_packet_parse_rest(knot_packet_t *packet);
int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
knot_rrset_t **rr);
int knot_packet_parse_next_rr_additional(knot_packet_t *packet,
knot_rrset_t **rr);
size_t knot_packet_size(const knot_packet_t *packet);
/*! \brief Returns size of the wireformat of Header and Question sections. */
......
......@@ -31,6 +31,7 @@
#include "packet/response.h"
#include "util/error.h"
#include "updates/changesets.h"
#include "tsig-op.h"
/*----------------------------------------------------------------------------*/
/* Non-API functions */
......@@ -304,9 +305,82 @@ static void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs)
/*----------------------------------------------------------------------------*/
int xfrin_process_axfr_packet(const uint8_t *pkt, size_t size,
xfrin_constructed_zone_t **constr)
static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
int tsig_req)
{
assert(packet != NULL);
assert(xfr != NULL);
/*
* If we are expecting it (i.e. xfr->prev_digest_size > 0)
* a) it should be there (first, last or each 100th packet) and it
* is not
* Then we should discard the changes and close the connection.
* b) it should be there and it is or it may not be there (other
* packets) and it is
* We validate the TSIG and reset packet number counting and
* data aggregation.
*
* If we are not expecting it (i.e. xfr->prev_digest_size <= 0) and
* it is there => it should probably be considered an error
*/
knot_rrset_t *tsig = NULL;
int ret = knot_packet_parse_next_rr_additional(packet, &tsig);
if (ret != KNOT_EOK) {
return ret;
}
if (xfr->prev_digest_size > 0) {
if (tsig_req && tsig == NULL) {
// TSIG missing!!
return KNOT_EMALF;
} else if (tsig != NULL) {
// TSIG there, either required or not, process
if (xfr->packet_nr == 0) {
ret = knot_tsig_client_check(xfr->tsig,
xfr->wire, xfr->wire_size,
xfr->prev_digest, xfr->prev_digest_size);
} else {
ret = knot_tsig_client_check_next(xfr->tsig,
xfr->wire, xfr->wire_size,
xfr->prev_digest, xfr->prev_digest_size);
}
if (ret != KNOT_EOK) {
/*! \todo Check return value for TSIG errors! */
return ret;
}
// and reset the counter and data storage
xfr->packet_nr = 1;
xfr->tsig_data_size = 0;
} 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 if (tsig != NULL) {
// TSIG where it should not be
return KNOT_EMALF;
}
return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
xfrin_constructed_zone_t **constr*/
knot_ns_xfr_t *xfr)
{
const uint8_t *pkt = xfr->wire;
size_t size = xfr->wire_size;
xfrin_constructed_zone_t **constr =
(xfrin_constructed_zone_t **)(&xfr->data);
if (pkt == NULL || constr == NULL) {
dbg_xfrin("Wrong parameters supported.\n");
return KNOT_EBADARG;
......@@ -364,6 +438,10 @@ int xfrin_process_axfr_packet(const uint8_t *pkt, size_t size,
knot_zone_contents_t *zone = NULL;
if (*constr == NULL) {
// this should be the first packet
xfr->packet_nr = 0;
xfr->tsig_data_size = 0;
// create new zone
/*! \todo Ensure that the packet is the first one. */
if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
......@@ -459,8 +537,17 @@ dbg_xfrin_exec(
ret = knot_packet_parse_next_rr_answer(packet, &rr);
} else {
zone = (*constr)->contents;
++xfr->packet_nr;
// add the packet wire size to the data to be verified by TSIG
}
/*! \todo this may be optional. */
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);
while (ret == KNOT_EOK && rr != NULL) {
......@@ -572,6 +659,15 @@ dbg_xfrin_exec(
continue;
}
if (knot_rrset_type(rr) == KNOT_RRTYPE_TSIG) {
// not allowed here
dbg_xfrin("TSIG in Answer section.\n");
knot_packet_free(&packet);
knot_node_free(&node, 1, 0); // ???
knot_rrset_deep_free(&rr, 1, 1, 1);
return KNOT_EMALF;
}
knot_node_t *(*get_node)(const knot_zone_contents_t *,
const knot_dname_t *) = NULL;
......@@ -684,7 +780,15 @@ dbg_xfrin_exec(
return KNOT_ERROR; /*! \todo Other error */
}
}
/* Now check if there is not a TSIG record at the end of the
* packet.
*/
ret = xfrin_check_tsig(packet, xfr,
knot_ns_tsig_required(xfr->packet_nr));
/*! \todo Check return value for TSIG errors! */
knot_packet_free(&packet);
dbg_xfrin("Processed one AXFR packet successfully.\n");
......
......@@ -140,8 +140,9 @@ int xfrin_zone_transferred(knot_nameserver_t *nameserver,
*
* \todo Refactor!!!
*/
int xfrin_process_axfr_packet(const uint8_t *pkt, size_t size,
xfrin_constructed_zone_t **zone);
int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
xfrin_constructed_zone_t **zone*/
knot_ns_xfr_t *xfr);
/*!
* \brief Destroys the whole changesets structure.
......
......@@ -292,6 +292,11 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data)
}
data->wire_size = ret;
/*!
* \todo Somewhere before this fetch the query digest and the TSIG
* associated with this transfer and save them to 'data'.
*/
/* Process incoming packet. */
switch(data->type) {
case XFR_TYPE_AIN:
......@@ -776,6 +781,8 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
/*!
* \todo Getting TSIG info from zone configuration and
* validating query TSIG should probably come here.
* This will require parsing the rest of the query
* here (knot_packet_parse_rest()).
*/
ret = knot_ns_answer_axfr(w->ns, &xfr);
......@@ -834,6 +841,9 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
/*!
* \todo Getting TSIG info from zone configuration and
* validating query TSIG should probably come here.
* This will require parsing the rest of the query
* here (knot_packet_parse_rest()) instead of in
* knot_ns_answer_ixfr() (remove from there afterwards).
*/
ret = knot_ns_answer_ixfr(w->ns, &xfr);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment