diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c index 01ce1bc2056d9a7392a3d3e13c5faac1f769e210..39ffeebac14f2600d957c2d9df5331155b917ec5 100644 --- a/src/knot/server/xfr-handler.c +++ b/src/knot/server/xfr-handler.c @@ -143,11 +143,15 @@ static knot_ns_xfr_t *xfr_handler_task(xfrworker_t *w, int fd) static int xfr_udp_timeout(knot_ns_xfr_t *data) { /* Close socket. */ + rcu_read_lock(); knot_zone_t *z = data->zone; if (z && knot_zone_get_contents(z) && knot_zone_data(z)) { - log_zone_info("%s Failed, timeout exceeded.\n", - data->msgpref); + if (!(knot_zone_flags(z) & KNOT_ZONE_DISCARDED)) { + log_zone_info("%s Failed, timeout exceeded.\n", + data->msgpref); + } } + rcu_read_unlock(); /* Invalidate pending query. */ xfr_free_task(data); @@ -165,6 +169,13 @@ static int xfr_udp_timeout(knot_ns_xfr_t *data) */ static int xfr_process_udp_resp(xfrworker_t *w, int fd, knot_ns_xfr_t *data) { + /* Check if zone is valid. */ + rcu_read_lock(); + if (knot_zone_flags(data->zone) & KNOT_ZONE_DISCARDED) { + return KNOTD_ECONNREFUSED; + } + rcu_read_unlock(); + /* Receive msg. */ ssize_t n = recvfrom(data->session, data->wire, data->wire_size, 0, data->addr.ptr, &data->addr.len); size_t resp_len = data->wire_size; @@ -611,8 +622,14 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, /* Read DNS/TCP packet. */ int ret = 0; int rcvd = tcp_recv(fd, buf, buflen, 0); + + /* Raise read-lock and check if zone is still valid. */ + rcu_read_lock(); + int zone_discarded = (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED); + + /* Handle incoming packet. */ data->wire_size = rcvd; - if (rcvd <= 0) { + if (rcvd <= 0 || zone_discarded) { data->wire_size = 0; ret = KNOT_ECONN; } else { @@ -673,12 +690,14 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, } /* Handle errors. */ - if (ret == KNOT_ENOXFR) { - log_server_warning("%s Finished, %s\n", - data->msgpref, knot_strerror(ret)); - } else if (ret < 0) { - log_server_error("%s %s\n", - data->msgpref, knot_strerror(ret)); + if (!zone_discarded) { + if (ret == KNOT_ENOXFR) { + log_server_warning("%s Finished, %s\n", + data->msgpref, knot_strerror(ret)); + } else if (ret < 0) { + log_server_error("%s %s\n", + data->msgpref, knot_strerror(ret)); + } } /* Check finished zone. */ @@ -698,13 +717,11 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, knot_zone_t *zone = (knot_zone_t *)data->zone; zonedata_t *zd = (zonedata_t *)knot_zone_data(zone); - /* Only for successful xfers. */ - if (ret > 0) { + /* Only for successful xfers on non-discarded zones. */ + if (ret > 0 && !zone_discarded) { ret = xfr_xfrin_finalize(w, data); /* AXFR bootstrap timeout. */ - rcu_read_lock(); - /*! \todo #1976 Do not reschedule if zone is being discarded. */ if (ret != KNOTD_EOK && !knot_zone_contents(zone)) { /* Schedule request (60 - 90s random delay). */ int tmr_s = AXFR_BOOTSTRAP_RETRY; @@ -718,8 +735,6 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, /* Update timers. */ server_t *server = (server_t *)knot_ns_get_data(w->ns); zones_timers_update(zone, zd->conf, server->sched); - rcu_read_unlock(); - } else { /* Cleanup */ xfr_xfrin_cleanup(w, data); @@ -740,6 +755,8 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, /* Disconnect. */ result = KNOTD_ECONNREFUSED; /* Make it disconnect. */ } + + rcu_read_unlock(); return result; } @@ -1418,6 +1435,14 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen) } conf_read_lock(); + + /* Check if the zone is not discarded. */ + if (knot_zone_flags(xfr.zone) & KNOT_ZONE_DISCARDED) { + xfr_request_deinit(&xfr); + knot_zone_release(xfr.zone); + conf_read_unlock(); + return KNOTD_EOK; + } /* Handle request. */ knot_ns_xfr_t *task = NULL; @@ -1430,7 +1455,6 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen) /* Report. */ if (ret != KNOTD_EOK && ret != KNOTD_EACCES) { - /*! \todo #1976 Do not reschedule if zone is being discarded. */ if (zd != NULL && !knot_zone_contents(xfr.zone)) { /* Reschedule request (120 - 240s random delay). */ int tmr_s = AXFR_BOOTSTRAP_RETRY * 2; /* Malus x2 */ diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c index edec93affd8bc8ba224f5659f73e15388e06d960..8a1896a5b78e823cd2ca9d39550dc93e4c9d9f85 100644 --- a/src/knot/server/zones.c +++ b/src/knot/server/zones.c @@ -305,6 +305,12 @@ static int zones_expire_ev(event_t *e) zonedata_t *zd = (zonedata_t *)zone->data; rcu_read_lock(); + /* Check if zone is not discarded. */ + if (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED) { + rcu_read_unlock(); + return KNOTD_EOK; + } + /* Do not issue SOA query if transfer is pending. */ int locked = pthread_mutex_trylock(&zd->xfr_in.lock); if (locked != 0) { @@ -340,6 +346,8 @@ static int zones_expire_ev(event_t *e) } /* Publish expired zone. */ + /* Need to keep a reference in case zone get's deleted in meantime. */ + knot_zone_retain(zone); rcu_read_unlock(); synchronize_rcu(); rcu_read_lock(); @@ -368,6 +376,9 @@ static int zones_expire_ev(event_t *e) pthread_mutex_unlock(&zd->xfr_in.lock); rcu_read_unlock(); + /* Release holding reference. */ + knot_zone_release(zone); + return 0; } @@ -386,6 +397,12 @@ static int zones_refresh_ev(event_t *e) /* Cancel pending timers. */ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone); + + /* Check if zone is not discarded. */ + if (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED) { + rcu_read_unlock(); + return KNOTD_EOK; + } /* Check for contents. */ if (!knot_zone_contents(zone)) { @@ -454,7 +471,6 @@ static int zones_refresh_ev(event_t *e) zd->conf->name); /* Reschedule as RETRY timer. */ - /*! \todo #1976 Do not reschedule if zone is being discarded. */ uint32_t retry_tmr = zones_jitter(zones_soa_retry(zone)); evsched_schedule(e->parent, e, retry_tmr); dbg_zones("zones: RETRY of '%s' after %u seconds\n", @@ -479,7 +495,6 @@ static int zones_refresh_ev(event_t *e) } /* Reschedule as RETRY timer. */ - /*! \todo #1976 Do not reschedule if zone is being discarded. */ uint32_t retry_tmr = zones_jitter(zones_soa_retry(zone)); evsched_schedule(e->parent, e, retry_tmr); dbg_zones("zones: RETRY of '%s' after %u seconds\n", @@ -610,6 +625,12 @@ static int zones_notify_send(event_t *e) free(ev); return KNOTD_EINVAL; } + + /* Check if zone is not discarded. */ + if (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED) { + rcu_read_unlock(); /* Event will be freed on zonedata_destroy.*/ + return KNOTD_EOK; + } /* Check for answered/cancelled query. */ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone); @@ -636,7 +657,6 @@ static int zones_notify_send(event_t *e) int retry_tmr = ev->timeout * 1000; /* Reschedule. */ - /*! \todo #1976 Do not reschedule if zone is being discarded. */ evsched_schedule(e->parent, e, retry_tmr); dbg_notify("notify: Query RETRY after %u secs (zone '%s')\n", retry_tmr / 1000, zd->conf->name); diff --git a/src/libknot/zone/zonedb.c b/src/libknot/zone/zonedb.c index 517a006b4e92611acec99d70c35b6500aaf52ddb..43b4489c2febb268f9d0a0648aa5d844e0e7273d 100644 --- a/src/libknot/zone/zonedb.c +++ b/src/libknot/zone/zonedb.c @@ -280,6 +280,7 @@ static void delete_zone_from_db(void *node, void *data) knot_zone_t *zone = (knot_zone_t *)node; assert(zone); synchronize_rcu(); + knot_zone_set_flag(zone, KNOT_ZONE_DISCARDED, 1); knot_zone_release(zone); zone = NULL; }