Commit a7f538f4 authored by Marek Vavruša's avatar Marek Vavruša

xfr_handler: packet parsing code unified, axfr fallback bugfix

The problem with previous code was that the packet stripped the TSIG
section when it parsed the first IXFR packet from wire. If the code
failed though, the wire was parsed again but this time the TSIG was
already stripped on the wire so the result was garbled data.
I've thrown in some minor cleanup as well.
parent 5c5354bf
......@@ -224,7 +224,7 @@ int axfr_answer(knot_pkt_t *pkt, struct query_data *qdata)
}
}
int axfr_process_answer(knot_ns_xfr_t *xfr)
int axfr_process_answer(knot_pkt_t *pkt, knot_ns_xfr_t *xfr)
{
/*
* Here we assume that 'xfr' contains TSIG information
......@@ -234,7 +234,7 @@ int axfr_process_answer(knot_ns_xfr_t *xfr)
dbg_ns("ns_process_axfrin: incoming packet, wire size: %zu\n",
xfr->wire_size);
int ret = xfrin_process_axfr_packet(xfr,
int ret = xfrin_process_axfr_packet(pkt, xfr,
(knot_zone_contents_t **)&xfr->data);
if (ret > 0) { // transfer finished
dbg_ns("ns_process_axfrin: AXFR finished, zone created.\n");
......
......@@ -65,7 +65,7 @@ int axfr_answer(knot_pkt_t *pkt, struct query_data *qdata);
* \param xfr Persistent transfer-specific data.
*
*/
int axfr_process_answer(knot_ns_xfr_t *xfr);
int axfr_process_answer(knot_pkt_t *pkt, knot_ns_xfr_t *xfr);
#endif /* _KNOT_AXFR_H_ */
......
......@@ -338,7 +338,7 @@ int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata)
return ret;
}
int ixfr_process_answer(knot_ns_xfr_t *xfr)
int ixfr_process_answer(knot_pkt_t *pkt, knot_ns_xfr_t *xfr)
{
dbg_ns("ns_process_ixfrin: incoming packet\n");
......@@ -347,7 +347,7 @@ int ixfr_process_answer(knot_ns_xfr_t *xfr)
* and the digest of the query sent to the master or the previous
* digest.
*/
int ret = xfrin_process_ixfr_packet(xfr);
int ret = xfrin_process_ixfr_packet(pkt, xfr);
if (ret == XFRIN_RES_FALLBACK) {
dbg_ns("ns_process_ixfrin: Fallback to AXFR.\n");
......
......@@ -64,7 +64,7 @@ int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata);
* \retval Other If any other error occured. The connection should be closed.
*
*/
int ixfr_process_answer(knot_ns_xfr_t *xfr);
int ixfr_process_answer(knot_pkt_t *pkt, knot_ns_xfr_t *xfr);
#endif /* _KNOT_IXFR_H_ */
......
......@@ -109,31 +109,6 @@ static int xfr_recv_tcp(int fd, struct sockaddr *addr, uint8_t *buf, size_t bufl
static int xfr_recv_udp(int fd, struct sockaddr *addr, uint8_t *buf, size_t buflen)
{ return recv(fd, buf, buflen, 0); }
/*! \todo This should be obsoleted by the generic response parsing layer. */
static int parse_packet(knot_pkt_t *packet, knot_pkt_type_t *type)
{
dbg_xfr("%s(%p, %p)\n", __func__, packet, type);
if (packet == NULL || type == NULL) {
return KNOT_EINVAL;
}
// 1) create empty response
int ret = KNOT_ERROR;
*type = KNOT_QUERY_INVALID;
if ((ret = knot_pkt_parse_question(packet)) != KNOT_EOK) {
dbg_xfr("%s: couldn't parse question = %d\n", __func__, ret);
return KNOT_RCODE_FORMERR;
}
// 2) determine the query type
*type = knot_pkt_type(packet);
if (*type == KNOT_QUERY_INVALID) {
return KNOT_RCODE_NOTIMPL;
}
return KNOT_RCODE_NOERROR;
}
/*! \brief Create forwarded query. */
static int forward_packet(knot_ns_xfr_t *data, knot_pkt_t *pkt)
{
......@@ -203,11 +178,7 @@ static int xfr_task_setmsg(knot_ns_xfr_t *rq, const char *keytag)
}
/* Prepare log message. */
const char *zname = rq->zname;
if (zname == NULL) {
zname = rq->zone->conf->name;
}
const char *zname = rq->zone->conf->name;
rq->msg = sprintf_alloc(xd->name, zname, kstr ? kstr : "'unknown'");
free(kstr);
return KNOT_EOK;
......@@ -289,7 +260,6 @@ static void xfr_task_cleanup(knot_ns_xfr_t *rq)
/* Cleanup other data - so that the structure may be reused. */
rq->packet_nr = 0;
rq->tsig_data_size = 0;
hattrie_clear(rq->lookup_tree);
}
/*! \brief End task properly and free it. */
......@@ -545,7 +515,6 @@ static int xfr_async_finish(fdset_t *set, unsigned id)
switch(rq->type) {
case XFR_TYPE_AIN:
case XFR_TYPE_IIN:
rq->lookup_tree = hattrie_create();
if (ret != KNOT_EOK) {
pthread_mutex_lock(&zone->lock);
zone->xfr_in.state = XFR_IDLE;
......@@ -654,62 +623,52 @@ static int xfr_task_finalize(knot_ns_xfr_t *rq)
}
/*! \brief Query response event handler function. */
static int xfr_task_resp(xfrhandler_t *xfr, knot_ns_xfr_t *rq)
static int xfr_task_resp(xfrhandler_t *xfr, knot_ns_xfr_t *rq, knot_pkt_t *pkt)
{
knot_pkt_t *re = knot_pkt_new(rq->wire, rq->wire_size, NULL);
if (re == NULL) {
return KNOT_ENOMEM;
}
knot_pkt_type_t rt = KNOT_RESPONSE_NORMAL;
int ret = parse_packet(re, &rt);
if (ret != KNOT_EOK) {
knot_pkt_free(&re);
return KNOT_EOK; /* Ignore */
}
knot_pkt_type_t pkt_type = knot_pkt_type(pkt);
/* Ignore other packets. */
switch(rt) {
switch(pkt_type) {
case KNOT_RESPONSE_NORMAL:
case KNOT_RESPONSE_NOTIFY:
case KNOT_RESPONSE_UPDATE:
break;
default:
knot_pkt_free(&re);
knot_pkt_free(&pkt);
return KNOT_EOK; /* Ignore */
}
ret = knot_pkt_parse_payload(re, KNOT_PF_NO_MERGE);
int ret = knot_pkt_parse_payload(pkt, KNOT_PF_NO_MERGE);
if (ret != KNOT_EOK) {
knot_pkt_free(&re);
knot_pkt_free(&pkt);
return KNOT_EOK; /* Ignore */
}
/* Check TSIG. */
const knot_rrset_t * tsig_rr = re->tsig_rr;
const knot_rrset_t * tsig_rr = pkt->tsig_rr;
if (rq->tsig_key != NULL) {
ret = knot_tsig_client_check(tsig_rr, re->wire, re->size,
ret = knot_tsig_client_check(tsig_rr, pkt->wire, pkt->size,
rq->digest, rq->digest_size,
rq->tsig_key, 0);
if (ret != KNOT_EOK) {
log_zone_error("%s %s\n", rq->msg, knot_strerror(ret));
knot_pkt_free(&re);
knot_pkt_free(&pkt);
return KNOT_ECONNREFUSED;
}
}
/* Process response. */
switch(rt) {
switch(pkt_type) {
case KNOT_RESPONSE_NORMAL:
ret = zones_process_response(xfr->server, rq->packet_nr, &rq->addr,
re);
pkt);
break;
case KNOT_RESPONSE_NOTIFY:
ret = notify_process_response(re, rq->packet_nr);
ret = notify_process_response(pkt, rq->packet_nr);
break;
case KNOT_RESPONSE_UPDATE:
ret = forward_packet_response(rq, re);
ret = forward_packet_response(rq, pkt);
if (ret == KNOT_EOK) {
log_zone_info("%s Forwarded response.\n", rq->msg);
}
......@@ -719,7 +678,6 @@ static int xfr_task_resp(xfrhandler_t *xfr, knot_ns_xfr_t *rq)
break;
}
knot_pkt_free(&re);
if (ret == KNOT_EUPTODATE) { /* Check up-to-date zone. */
log_zone_info("%s %s (serial %u)\n", rq->msg,
knot_strerror(ret),
......@@ -769,16 +727,44 @@ static int xfr_fallback_axfr(knot_ns_xfr_t *rq)
return xfr_task_start(rq);
}
static int xfr_task_xfer(xfrhandler_t *xfr, knot_ns_xfr_t *rq)
static int xfr_parse_packet(knot_pkt_t *pkt)
{
/* This is important, don't merge RRs together. The SOAs are ordered
* in a special way for a reason. */
int ret = knot_pkt_parse(pkt, KNOT_PF_NO_MERGE);
if (ret != KNOT_EOK) {
return ret;
}
// check if the response is OK
if (knot_wire_get_rcode(pkt->wire) != KNOT_RCODE_NOERROR) {
return KNOT_EXFRREFUSED;
}
// check if the TC bit is set (it must not be)
if (knot_wire_get_tc(pkt->wire)) {
return KNOT_EMALF;
}
return ret;
}
static int xfr_task_xfer(xfrhandler_t *xfr, knot_ns_xfr_t *rq, knot_pkt_t *pkt)
{
/* Parse transfer packet. */
int ret = xfr_parse_packet(pkt);
if (ret != KNOT_EOK) {
log_zone_error("%s %s\n", rq->msg, knot_strerror(ret));
return ret;
}
/* Process incoming packet. */
int ret = KNOT_EOK;
switch(rq->type) {
case XFR_TYPE_AIN:
ret = axfr_process_answer(rq);
ret = axfr_process_answer(pkt, rq);
break;
case XFR_TYPE_IIN:
ret = ixfr_process_answer(rq);
ret = ixfr_process_answer(pkt, rq);
break;
default:
ret = KNOT_EINVAL;
......@@ -792,7 +778,7 @@ static int xfr_task_xfer(xfrhandler_t *xfr, knot_ns_xfr_t *rq)
xfr_task_cleanup(rq);
rq->type = XFR_TYPE_AIN;
rq->msg[XFR_MSG_DLTTR] = 'A';
ret = axfr_process_answer(rq);
ret = axfr_process_answer(pkt, rq);
}
/* IXFR refused, try again with AXFR. */
......@@ -876,15 +862,32 @@ static int xfr_process_event(xfrhandler_t *xfr, knot_ns_xfr_t *rq)
rq->wire_size = n;
}
/* Handle SOA/NOTIFY responses. */
/* Parse question. */
knot_pkt_t *pkt = knot_pkt_new(rq->wire, rq->wire_size, NULL);
if (pkt == NULL) {
return KNOT_ENOMEM;
}
int ret = knot_pkt_parse_question(pkt);
if (ret != KNOT_EOK) {
knot_pkt_free(&pkt);
return ret;
}
/* Process packet by request type. */
switch(rq->type) {
case XFR_TYPE_NOTIFY:
case XFR_TYPE_SOA:
case XFR_TYPE_FORWARD:
return xfr_task_resp(xfr, rq);
ret = xfr_task_resp(xfr, rq, pkt);
break;
default:
return xfr_task_xfer(xfr, rq);
ret = xfr_task_xfer(xfr, rq, pkt);
break;
}
knot_pkt_free(&pkt);
return ret;
}
/*! \brief Sweep inactive connection. */
......@@ -1183,10 +1186,6 @@ int xfr_task_free(knot_ns_xfr_t *rq)
return KNOT_EINVAL;
}
/* Free DNAME trie. */
hattrie_free(rq->lookup_tree);
rq->lookup_tree = NULL;
/* Free TSIG buffers. */
free(rq->digest);
rq->digest = NULL;
......
......@@ -119,7 +119,6 @@ typedef struct knot_ns_xfr {
size_t wire_maxlen;
void *data;
zone_t *zone;
char* zname;
knot_zone_contents_t *new_contents;
char *msg;
......@@ -152,8 +151,6 @@ typedef struct knot_ns_xfr {
* number counted from last TSIG check.
*/
int packet_nr;
hattrie_t *lookup_tree;
} knot_ns_xfr_t;
/*!
......
......@@ -223,50 +223,11 @@ static int xfrin_check_tsig(knot_pkt_t *packet, knot_ns_xfr_t *xfr,
/*----------------------------------------------------------------------------*/
static int xfrin_parse(knot_pkt_t **dst, uint8_t *wire, size_t wire_size)
{
assert(dst != NULL);
int ret = KNOT_EOK;
knot_pkt_t *pkt = knot_pkt_new(wire, wire_size, NULL);
if (pkt == NULL) {
knot_pkt_free(&pkt);
return KNOT_ENOMEM;
}
/* This is important, don't merge RRs together. The SOAs are ordered
* in a special way for a reason. */
ret = knot_pkt_parse(pkt, KNOT_PF_NO_MERGE);
if (ret != KNOT_EOK) {
knot_pkt_free(&pkt);
return ret;
}
// check if the response is OK
if (knot_wire_get_rcode(pkt->wire) != KNOT_RCODE_NOERROR) {
knot_pkt_free(&pkt);
return KNOT_EXFRREFUSED;
}
// check if the TC bit is set (it must not be)
if (knot_wire_get_tc(pkt->wire)) {
knot_pkt_free(&pkt);
return KNOT_EMALF;
}
*dst = pkt;
return KNOT_EOK;
}
static int xfrin_take_rr(const knot_pktsection_t *answer, knot_rrset_t **rr, uint16_t *cur)
{
int ret = KNOT_EOK;
if (*cur < answer->count) {
/*! \note For now, the RRSets are allocated using malloc(),
* clearing the KNOT_PF_FREE flags takes their ownership.
*/
*rr = (knot_rrset_t *)answer->rr[*cur];
answer->rrinfo[*cur].flags &= ~KNOT_PF_FREE;
ret = knot_rrset_copy(answer->rr[*cur], rr, NULL);
*cur += 1;
} else {
*rr = NULL;
......@@ -278,23 +239,17 @@ static int xfrin_take_rr(const knot_pktsection_t *answer, knot_rrset_t **rr, uin
/*----------------------------------------------------------------------------*/
int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr, knot_zone_contents_t **zone)
int xfrin_process_axfr_packet(knot_pkt_t *pkt, knot_ns_xfr_t *xfr, knot_zone_contents_t **zone)
{
if (xfr->wire == NULL) {
if (pkt == NULL) {
return KNOT_EINVAL;
}
knot_pkt_t *packet = NULL;
int ret = xfrin_parse(&packet, xfr->wire, xfr->wire_size);
if (ret != KNOT_EOK ) {
return ret;
}
uint16_t rr_id = 0;
knot_rrset_t *rr = NULL;
const knot_pktsection_t *answer = knot_pkt_section(packet, KNOT_ANSWER);
const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
ret = xfrin_take_rr(answer, &rr, &rr_id);
int ret = xfrin_take_rr(answer, &rr, &rr_id);
if (*zone == NULL) {
// Transfer start, init zone
if (rr->type != KNOT_RRTYPE_SOA) {
......@@ -302,7 +257,6 @@ int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr, knot_zone_contents_t **zone)
}
*zone = knot_zone_contents_new(rr->owner);
if (*zone == NULL) {
knot_pkt_free(&packet);
return KNOT_ENOMEM;
}
xfr->packet_nr = 0;
......@@ -318,8 +272,7 @@ int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr, knot_zone_contents_t **zone)
if (rr->type == KNOT_RRTYPE_SOA &&
knot_node_rrset(zc.z->apex, KNOT_RRTYPE_SOA)) {
// Last SOA, last message, check TSIG.
ret = xfrin_check_tsig(packet, xfr, 1);
knot_pkt_free(&packet);
ret = xfrin_check_tsig(pkt, xfr, 1);
knot_rrset_free(&rr, NULL);
if (ret != KNOT_EOK) {
return ret;
......@@ -328,7 +281,6 @@ int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr, knot_zone_contents_t **zone)
} else {
ret = zcreator_step(&zc, rr);
if (ret != KNOT_EOK) {
knot_pkt_free(&packet);
knot_rrset_free(&rr, NULL);
return ret;
}
......@@ -338,34 +290,26 @@ int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr, knot_zone_contents_t **zone)
assert(rr == NULL);
// Check possible TSIG at the end of DNS message.
ret = xfrin_check_tsig(packet, xfr,
ret = xfrin_check_tsig(pkt, xfr,
knot_ns_tsig_required(xfr->packet_nr));
knot_pkt_free(&packet);
return ret; // ret == KNOT_EOK means processing continues.
}
/*----------------------------------------------------------------------------*/
int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
int xfrin_process_ixfr_packet(knot_pkt_t *pkt, knot_ns_xfr_t *xfr)
{
knot_changesets_t **chs = (knot_changesets_t **)(&xfr->data);
if (xfr->wire == NULL || chs == NULL) {
if (pkt == NULL || chs == NULL) {
dbg_xfrin("Wrong parameters supported.\n");
return KNOT_EINVAL;
}
knot_pkt_t *packet = NULL;
int ret = xfrin_parse(&packet, xfr->wire, xfr->wire_size);
if (ret != KNOT_EOK ) {
return ret;
}
uint16_t rr_id = 0;
knot_rrset_t *rr = NULL;
const knot_pktsection_t *answer = knot_pkt_section(packet, KNOT_ANSWER);
ret = xfrin_take_rr(answer, &rr, &rr_id);
const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
int ret = xfrin_take_rr(answer, &rr, &rr_id);
if (ret != KNOT_EOK) {
knot_pkt_free(&packet);
return KNOT_EXFRREFUSED; /* Empty, try again with AXFR */
}
......@@ -382,7 +326,6 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
ret = knot_changesets_init(chs);
if (ret != KNOT_EOK) {
knot_rrset_free(&rr, NULL);
knot_pkt_free(&packet);
return ret;
}
......@@ -421,11 +364,9 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
*/
if (rr == NULL) {
dbg_xfrin("Response containing only SOA,\n");
knot_pkt_free(&packet);
return XFRIN_RES_SOA_ONLY;
} else if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
knot_rrset_free(&rr, NULL);
knot_pkt_free(&packet);
dbg_xfrin("Fallback to AXFR.\n");
ret = XFRIN_RES_FALLBACK;
return ret;
......@@ -538,11 +479,10 @@ dbg_xfrin_exec_verb(
/*! \note [TSIG] Check TSIG, we're at the end of
* transfer.
*/
ret = xfrin_check_tsig(packet, xfr, 1);
ret = xfrin_check_tsig(pkt, xfr, 1);
// last SOA, discard and end
knot_rrset_free(&rr, NULL);
knot_pkt_free(&packet);
/*! \note [TSIG] If TSIG validates, consider
* transfer complete. */
......@@ -624,7 +564,7 @@ dbg_xfrin_exec_verb(
/*! \note Check TSIG, we're at the end of packet. It may not be
* required.
*/
ret = xfrin_check_tsig(packet, xfr,
ret = xfrin_check_tsig(pkt, xfr,
knot_ns_tsig_required(xfr->packet_nr));
dbg_xfrin_verb("xfrin_check_tsig() returned %d\n", ret);
++xfr->packet_nr;
......@@ -636,7 +576,6 @@ dbg_xfrin_exec_verb(
// here no RRs remain in the packet but the transfer is not finished
// yet, return EOK
knot_pkt_free(&packet);
return KNOT_EOK;
cleanup:
......@@ -645,7 +584,6 @@ cleanup:
dbg_xfrin_detail("Cleanup after processing IXFR/IN packet.\n");
knot_changesets_free(chs);
knot_pkt_free(&packet);
xfr->data = 0;
return ret;
}
......
......@@ -114,7 +114,7 @@ int xfrin_create_ixfr_query(const zone_t *zone, knot_pkt_t *pkt);
*
* \todo Refactor!!!
*/
int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr, knot_zone_contents_t **zone);
int xfrin_process_axfr_packet(knot_pkt_t *pkt, knot_ns_xfr_t *xfr, knot_zone_contents_t **zone);
/*!
* \brief Destroys the whole changesets structure.
......@@ -138,7 +138,7 @@ void xfrin_free_changesets(knot_changesets_t **changesets);
* \retval KNOT_EMALF
* \retval KNOT_ENOMEM
*/
int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr);
int xfrin_process_ixfr_packet(knot_pkt_t *pkt, knot_ns_xfr_t *xfr);
/*!
* \brief Applies changesets *with* zone shallow copy.
......
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