diff --git a/src/knot/server/notify.c b/src/knot/server/notify.c index 78b5480f3cfb202e38dfd413bbe6f6cbcff2705c..d0292e8ff847c822a01bcf4d494b1b7e770871b9 100644 --- a/src/knot/server/notify.c +++ b/src/knot/server/notify.c @@ -270,10 +270,12 @@ int notify_process_request(knot_nameserver_t *ns, } // find the zone + rcu_read_lock(); const knot_dname_t *qname = knot_packet_qname(notify); const knot_zone_t *z = knot_zonedb_find_zone_for_name( ns->zone_db, qname); if (z == NULL) { + rcu_read_unlock(); dbg_notify("notify: failed to find zone by name\n"); knot_ns_error_response_from_query(ns, notify, KNOT_RCODE_FORMERR, buffer, @@ -282,7 +284,7 @@ int notify_process_request(knot_nameserver_t *ns, } notify_check_and_schedule(ns, z, from); - + rcu_read_unlock(); return KNOTD_EOK; } @@ -302,13 +304,16 @@ int notify_process_response(knot_nameserver_t *nameserver, *size = 0; /* Find matching zone. */ + rcu_read_lock(); const knot_dname_t *zone_name = knot_packet_qname(notify); knot_zone_t *zone = knot_zonedb_find_zone(nameserver->zone_db, zone_name); if (!zone) { + rcu_read_unlock(); return KNOTD_ENOENT; } if (!knot_zone_data(zone)) { + rcu_read_unlock(); return KNOTD_ENOENT; } @@ -326,6 +331,7 @@ int notify_process_response(knot_nameserver_t *nameserver, /* Found waiting NOTIFY query? */ if (!match) { + rcu_read_unlock(); pthread_mutex_unlock(&zd->lock); return KNOTD_ERROR; } @@ -335,6 +341,8 @@ int notify_process_response(knot_nameserver_t *nameserver, /* Zone was removed/reloaded. */ pthread_mutex_unlock(&zd->lock); + + rcu_read_unlock(); return KNOTD_EOK; } diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c index eb11c89c2037c7543add5329bbf04244c029d37d..f52ea779ef967f45ff99a37d4330cd7e14f74809 100644 --- a/src/knot/server/xfr-handler.c +++ b/src/knot/server/xfr-handler.c @@ -93,6 +93,9 @@ static void xfr_free_task(knot_ns_xfr_t *task) } } + /* No further access to zone. */ + knot_zone_release(task->zone); + /* Deinitialize */ xfr_request_deinit(task); if (!task->session_closed) { @@ -1289,6 +1292,7 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *xfr) return KNOTD_EINVAL; } + rcu_read_lock(); int ret = knot_ns_init_xfr(ns, xfr); int xfr_failed = (ret != KNOT_EOK); @@ -1372,6 +1376,7 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *xfr) free(xfr->tsig_data); xfr->tsig_data = NULL; xfr_request_deinit(xfr); + rcu_read_unlock(); /* Cleanup. */ free(xfr->digest); @@ -1423,6 +1428,7 @@ 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 */ @@ -1439,6 +1445,7 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen) log_server_error("%s %s\n", xfr.msgpref, knotd_strerror(ret)); } + knot_zone_release(xfr.zone); /* No further access to zone. */ } break; @@ -1447,14 +1454,14 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen) /* Register task. */ task = xfr_register_task(w, &xfr); if (!task) { + knot_zone_release(xfr.zone); /* No further access to zone. */ ret = KNOTD_ENOMEM; - break; + } else { + /* Add timeout. */ + fdset_set_watchdog(w->fdset, task->session, XFR_QUERY_WD); + log_server_info("%s Query issued.\n", xfr.msgpref); + ret = KNOTD_EOK; } - - /* Add timeout. */ - fdset_set_watchdog(w->fdset, task->session, XFR_QUERY_WD); - log_server_info("%s Query issued.\n", xfr.msgpref); - ret = KNOTD_EOK; break; default: log_server_error("Unknown XFR request type (%d).\n", xfr.type); diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c index 65ad797c47e866b2653de4ec99192f56b70a80a0..edec93affd8bc8ba224f5659f73e15388e06d960 100644 --- a/src/knot/server/zones.c +++ b/src/knot/server/zones.c @@ -377,8 +377,10 @@ static int zones_expire_ev(event_t *e) static int zones_refresh_ev(event_t *e) { dbg_zones("zones: REFRESH or RETRY timer event\n"); + rcu_read_lock(); knot_zone_t *zone = (knot_zone_t *)e->data; if (zone == NULL || zone->data == NULL) { + rcu_read_unlock(); return KNOTD_EINVAL; } @@ -386,7 +388,6 @@ static int zones_refresh_ev(event_t *e) zonedata_t *zd = (zonedata_t *)knot_zone_data(zone); /* Check for contents. */ - rcu_read_lock(); if (!knot_zone_contents(zone)) { /* Bootstrap from XFR master. */ @@ -429,13 +430,20 @@ static int zones_refresh_ev(event_t *e) ++zd->xfr_in.scheduled; pthread_mutex_unlock(&zd->xfr_in.lock); + /* Retain pointer to zone for processing. */ + knot_zone_retain(xfr_req.zone); + /* Unlock zone contents. */ rcu_read_unlock(); /* Mark as finished to prevent stalling. */ evsched_event_finished(e->parent); + int ret = xfr_request(zd->server->xfr_h, &xfr_req); + if (ret != KNOTD_EOK) { + knot_zone_release(xfr_req.zone); /* Discard */ + } + return ret; - return xfr_request(zd->server->xfr_h, &xfr_req); } /* Do not issue SOA query if transfer is pending. */ @@ -446,6 +454,7 @@ 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", @@ -470,6 +479,7 @@ 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", @@ -558,10 +568,14 @@ static int zones_refresh_ev(event_t *e) memcpy(&req.saddr, &zd->xfr_in.via, sizeof(sockaddr_t)); sockaddr_update(&req.addr); sockaddr_update(&req.saddr); + + /* Retain pointer to zone and issue. */ + knot_zone_retain(req.zone); if (ret == KNOTD_EOK) { ret = xfr_request(zd->server->xfr_h, &req); } if (ret != KNOTD_EOK) { + knot_zone_release(req.zone); /* Discard */ log_server_warning("Failed to issue SOA query for zone '%s' (%s).\n", zd->conf->name, errstr); } @@ -580,22 +594,23 @@ static int zones_refresh_ev(event_t *e) static int zones_notify_send(event_t *e) { dbg_notify("notify: NOTIFY timer event\n"); - + rcu_read_lock(); notify_ev_t *ev = (notify_ev_t *)e->data; if (ev == NULL) { + rcu_read_unlock(); log_zone_error("NOTIFY invalid event received\n"); return KNOTD_EINVAL; } + knot_zone_t *zone = ev->zone; if (zone == NULL || zone->data == NULL) { + rcu_read_unlock(); log_zone_error("NOTIFY invalid event data received\n"); evsched_event_free(e->parent, e); free(ev); return KNOTD_EINVAL; } - rcu_read_lock(); - /* Check for answered/cancelled query. */ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone); knot_zone_contents_t *contents = knot_zone_get_contents(zone); @@ -621,6 +636,7 @@ 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); @@ -675,7 +691,13 @@ static int zones_notify_send(event_t *e) req.zone = zone; memcpy(&req.addr, &ev->addr, sizeof(sockaddr_t)); memcpy(&req.saddr, &ev->saddr, sizeof(sockaddr_t)); - xfr_request(zd->server->xfr_h, &req); + + /* Retain pointer to zone and issue request. */ + knot_zone_retain(req.zone); + ret = xfr_request(zd->server->xfr_h, &req); + if (ret != KNOTD_EOK) { + knot_zone_release(req.zone); /* Discard */ + } } free(qbuf); @@ -2440,6 +2462,7 @@ int zones_process_response(knot_nameserver_t *nameserver, } /* Find matching zone and ID. */ + rcu_read_lock(); const knot_dname_t *zone_name = knot_packet_qname(packet); /*! \todo Change the access to the zone db. */ knot_zone_t *zone = knot_zonedb_find_zone( @@ -2447,7 +2470,6 @@ int zones_process_response(knot_nameserver_t *nameserver, zone_name); /* Get zone contents. */ - rcu_read_lock(); const knot_zone_contents_t *contents = knot_zone_contents(zone); @@ -2526,9 +2548,13 @@ int zones_process_response(knot_nameserver_t *nameserver, /* Unlock zone contents. */ rcu_read_unlock(); - /* Enqueue XFR request. */ - return xfr_request(((server_t *)knot_ns_get_data( - nameserver))->xfr_h, &xfr_req); + /* Retain pointer to zone for processing. */ + knot_zone_retain(xfr_req.zone); + ret = xfr_request(((server_t *)knot_ns_get_data( + nameserver))->xfr_h, &xfr_req); + if (ret != KNOTD_EOK) { + knot_zone_release(xfr_req.zone); /* Discard */ + } } return KNOTD_EOK; diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c index d214763739194f29608db519c4a27ae07138df35..134fa8a6596138982d94f15976a77bc587eda3af 100644 --- a/src/libknot/nameserver/name-server.c +++ b/src/libknot/nameserver/name-server.c @@ -3936,9 +3936,9 @@ int knot_ns_switch_zone(knot_nameserver_t *nameserver, return KNOT_ENOZONE; } - // find the zone in the zone db - knot_zone_t *z = knot_zonedb_find_zone(nameserver->zone_db, - knot_node_owner(knot_zone_contents_apex(zone))); + /* Zone must not be looked-up from server, as it may be a different zone if + * a reload occurs when transfer is pending. */ + knot_zone_t *z = xfr->zone; if (z == NULL) { char *name = knot_dname_to_str(knot_node_owner( knot_zone_contents_apex(zone))); @@ -3957,6 +3957,8 @@ dbg_ns_exec_verb( dbg_ns_verb("Zone db contents: (zone count: %zu)\n", nameserver->zone_db->zone_count); + /* Warning: may not show updated zone if updated zone that is already + * discarded from zone db (reload with pending transfer). */ const knot_zone_t **zones = knot_zonedb_zones(nameserver->zone_db); for (int i = 0; i < knot_zonedb_zone_count (nameserver->zone_db); i++) {