Commit b5bfac19 authored by Daniel Salzman's avatar Daniel Salzman

knotd: introduce multimaster with failover

parent 6a2c8ff0
...@@ -394,8 +394,8 @@ Definition of remote servers for zone transfers or notifications. ...@@ -394,8 +394,8 @@ Definition of remote servers for zone transfers or notifications.
.ft C .ft C
remote: remote:
\- id: STR \- id: STR
address: ADDR[@INT] address: ADDR[@INT] ...
via: ADDR[@INT] via: ADDR[@INT] ...
key: key_id key: key_id
.ft P .ft P
.fi .fi
...@@ -406,16 +406,17 @@ remote: ...@@ -406,16 +406,17 @@ remote:
A remote identifier. A remote identifier.
.SS address .SS address
.sp .sp
A destination IP address of the remote server. Optional destination port An ordered list of destination IP addresses which are used for communication
specification (default is 53) can be appended to the address using \fB@\fP with the remote server. The addresses are tried in sequence unless the
separator. operation is successful. Optional destination port (default is 53)
can be appended to the address using \fB@\fP separator.
.sp .sp
Default: empty Default: empty
.SS via .SS via
.sp .sp
A source IP address which is used to communicate with the remote server. An ordered list of source IP addresses. The first address with the same family
Optional source port specification can be appended to the address using as the destination address is used. Optional source port (default is random)
\fB@\fP separator. can be appended to the address using \fB@\fP separator.
.sp .sp
Default: empty Default: empty
.SS key .SS key
...@@ -459,6 +460,7 @@ zone: ...@@ -459,6 +460,7 @@ zone:
file: STR file: STR
storage: STR storage: STR
master: remote_id ... master: remote_id ...
ddns\-master: remote_id
notify: remote_id ... notify: remote_id ...
acl: acl_id ... acl: acl_id ...
semantic\-checks: BOOL semantic\-checks: BOOL
...@@ -507,6 +509,12 @@ Default: \fB${localstatedir}/lib/knot\fP (configured with \fB\-\-with\-storage=p ...@@ -507,6 +509,12 @@ Default: \fB${localstatedir}/lib/knot\fP (configured with \fB\-\-with\-storage=p
An ordered list of \fI\%references\fP to zone master servers. An ordered list of \fI\%references\fP to zone master servers.
.sp .sp
Default: empty Default: empty
.SS ddns\-master
.sp
A \fI\%references\fP to zone primary master server.
If not specified, the first \fI\%master\fP server is used.
.sp
Default: empty
.SS notify .SS notify
.sp .sp
An ordered list of \fI\%references\fP to remotes to which notify An ordered list of \fI\%references\fP to remotes to which notify
......
...@@ -466,8 +466,8 @@ Definition of remote servers for zone transfers or notifications. ...@@ -466,8 +466,8 @@ Definition of remote servers for zone transfers or notifications.
remote: remote:
- id: STR - id: STR
address: ADDR[@INT] address: ADDR[@INT] ...
via: ADDR[@INT] via: ADDR[@INT] ...
key: key_id key: key_id
.. _remote_id: .. _remote_id:
...@@ -482,9 +482,10 @@ A remote identifier. ...@@ -482,9 +482,10 @@ A remote identifier.
address address
------- -------
A destination IP address of the remote server. Optional destination port An ordered list of destination IP addresses which are used for communication
specification (default is 53) can be appended to the address using ``@`` with the remote server. The addresses are tried in sequence unless the
separator. operation is successful. Optional destination port (default is 53)
can be appended to the address using ``@`` separator.
Default: empty Default: empty
...@@ -493,9 +494,9 @@ Default: empty ...@@ -493,9 +494,9 @@ Default: empty
via via
--- ---
A source IP address which is used to communicate with the remote server. An ordered list of source IP addresses. The first address with the same family
Optional source port specification can be appended to the address using as the destination address is used. Optional source port (default is random)
``@`` separator. can be appended to the address using ``@`` separator.
Default: empty Default: empty
...@@ -547,6 +548,7 @@ Definition of zones served by the server. ...@@ -547,6 +548,7 @@ Definition of zones served by the server.
file: STR file: STR
storage: STR storage: STR
master: remote_id ... master: remote_id ...
ddns-master: remote_id
notify: remote_id ... notify: remote_id ...
acl: acl_id ... acl: acl_id ...
semantic-checks: BOOL semantic-checks: BOOL
...@@ -609,6 +611,16 @@ An ordered list of :ref:`references<remote_id>` to zone master servers. ...@@ -609,6 +611,16 @@ An ordered list of :ref:`references<remote_id>` to zone master servers.
Default: empty Default: empty
.. _zone_ddns-master:
ddns-master
-----------
A :ref:`references<remote_id>` to zone primary master server.
If not specified, the first :ref:`master<zone_master>` server is used.
Default: empty
.. _zone_notify: .. _zone_notify:
notify notify
......
...@@ -136,6 +136,7 @@ static const yp_item_t desc_remote[] = { ...@@ -136,6 +136,7 @@ static const yp_item_t desc_remote[] = {
{ C_FILE, YP_TSTR, YP_VNONE }, \ { C_FILE, YP_TSTR, YP_VNONE }, \
{ C_STORAGE, YP_TSTR, YP_VSTR = { STORAGE_DIR } }, \ { C_STORAGE, YP_TSTR, YP_VSTR = { STORAGE_DIR } }, \
{ C_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \ { C_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \
{ C_DDNS_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FNONE, { check_ref } }, \
{ C_NOTIFY, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \ { C_NOTIFY, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \
{ C_ACL, YP_TREF, YP_VREF = { C_ACL }, YP_FMULTI, { check_ref } }, \ { C_ACL, YP_TREF, YP_VREF = { C_ACL }, YP_FMULTI, { check_ref } }, \
{ C_SEM_CHECKS, YP_TBOOL, YP_VNONE }, \ { C_SEM_CHECKS, YP_TBOOL, YP_VNONE }, \
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#define C_BG_WORKERS "\x12""background-workers" #define C_BG_WORKERS "\x12""background-workers"
#define C_COMMENT "\x07""comment" #define C_COMMENT "\x07""comment"
#define C_CTL "\x07""control" #define C_CTL "\x07""control"
#define C_DDNS_MASTER "\x0B""ddns-master"
#define C_DENY "\x04""deny" #define C_DENY "\x04""deny"
#define C_DISABLE_ANY "\x0B""disable-any" #define C_DISABLE_ANY "\x0B""disable-any"
#define C_DNSSEC_SIGNING "\x0E""dnssec-signing" #define C_DNSSEC_SIGNING "\x0E""dnssec-signing"
......
...@@ -385,7 +385,7 @@ int axfr_answer_process(knot_pkt_t *pkt, struct answer_data *adata) ...@@ -385,7 +385,7 @@ int axfr_answer_process(knot_pkt_t *pkt, struct answer_data *adata)
if (rcode != KNOT_RCODE_NOERROR) { if (rcode != KNOT_RCODE_NOERROR) {
lookup_table_t *lut = lookup_by_id(knot_rcode_names, rcode); lookup_table_t *lut = lookup_by_id(knot_rcode_names, rcode);
if (lut != NULL) { if (lut != NULL) {
AXFRIN_LOG(LOG_ERR, "server responded with %s", lut->name); AXFRIN_LOG(LOG_WARNING, "server responded with %s", lut->name);
} }
return KNOT_STATE_FAIL; return KNOT_STATE_FAIL;
} }
...@@ -397,7 +397,7 @@ int axfr_answer_process(knot_pkt_t *pkt, struct answer_data *adata) ...@@ -397,7 +397,7 @@ int axfr_answer_process(knot_pkt_t *pkt, struct answer_data *adata)
int ret = axfr_answer_init(adata); int ret = axfr_answer_init(adata);
if (ret != KNOT_EOK) { if (ret != KNOT_EOK) {
AXFRIN_LOG(LOG_ERR, "failed (%s)", knot_strerror(ret)); AXFRIN_LOG(LOG_WARNING, "failed (%s)", knot_strerror(ret));
return KNOT_STATE_FAIL; return KNOT_STATE_FAIL;
} }
} else { } else {
......
...@@ -888,12 +888,27 @@ static int process_soa_answer(knot_pkt_t *pkt, struct answer_data *data) ...@@ -888,12 +888,27 @@ static int process_soa_answer(knot_pkt_t *pkt, struct answer_data *data)
if (serial_compare(our_serial, their_serial) >= 0) { if (serial_compare(our_serial, their_serial) >= 0) {
ANSWER_LOG(LOG_INFO, data, "refresh, outgoing", "zone is up-to-date"); ANSWER_LOG(LOG_INFO, data, "refresh, outgoing", "zone is up-to-date");
zone_events_cancel(zone, ZONE_EVENT_EXPIRE); zone_events_cancel(zone, ZONE_EVENT_EXPIRE);
/* Clear preferred master. */
pthread_mutex_lock(&zone->preferred_lock);
free(zone->preferred_master);
zone->preferred_master = NULL;
pthread_mutex_unlock(&zone->preferred_lock);
return KNOT_STATE_DONE; /* Our zone is up to date. */ return KNOT_STATE_DONE; /* Our zone is up to date. */
} }
/* Our zone is outdated, schedule zone transfer. */ /* Our zone is outdated, schedule zone transfer. */
ANSWER_LOG(LOG_INFO, data, "refresh, outgoing", "master has newer serial %u -> %u", ANSWER_LOG(LOG_INFO, data, "refresh, outgoing", "master has newer serial %u -> %u",
our_serial, their_serial); our_serial, their_serial);
/* Update preferred master. */
pthread_mutex_lock(&zone->preferred_lock);
free(zone->preferred_master);
zone->preferred_master = malloc(sizeof(struct sockaddr_storage));
*zone->preferred_master = *data->param->remote;
pthread_mutex_unlock(&zone->preferred_lock);
zone_events_schedule(zone, ZONE_EVENT_XFER, ZONE_EVENT_NOW); zone_events_schedule(zone, ZONE_EVENT_XFER, ZONE_EVENT_NOW);
return KNOT_STATE_DONE; return KNOT_STATE_DONE;
} }
......
...@@ -98,6 +98,14 @@ int notify_process_query(knot_pkt_t *pkt, struct query_data *qdata) ...@@ -98,6 +98,14 @@ int notify_process_query(knot_pkt_t *pkt, struct query_data *qdata)
/* Incoming NOTIFY expires REFRESH timer and renews EXPIRE timer. */ /* Incoming NOTIFY expires REFRESH timer and renews EXPIRE timer. */
zone_t *zone = (zone_t *)qdata->zone; zone_t *zone = (zone_t *)qdata->zone;
/* Update preferred master. */
pthread_mutex_lock(&zone->preferred_lock);
free(zone->preferred_master);
zone->preferred_master = malloc(sizeof(struct sockaddr_storage));
*zone->preferred_master = *qdata->param->remote;
pthread_mutex_unlock(&zone->preferred_lock);
zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW); zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW);
int ret = zone_events_write_persistent(zone); int ret = zone_events_write_persistent(zone);
if (ret != KNOT_EOK) { if (ret != KNOT_EOK) {
......
...@@ -341,13 +341,6 @@ static int process_requests(zone_t *zone, list_t *requests) ...@@ -341,13 +341,6 @@ static int process_requests(zone_t *zone, list_t *requests)
static int forward_request(zone_t *zone, struct knot_request *request) static int forward_request(zone_t *zone, struct knot_request *request)
{ {
/* Create requestor instance. */
struct knot_requestor re;
knot_requestor_init(&re, NULL);
/* Fetch primary master. */
const conf_remote_t master = zone_master(zone);
/* Copy request and assign new ID. */ /* Copy request and assign new ID. */
knot_pkt_t *query = knot_pkt_new(NULL, request->query->max_size, NULL); knot_pkt_t *query = knot_pkt_new(NULL, request->query->max_size, NULL);
int ret = knot_pkt_copy(query, request->query); int ret = knot_pkt_copy(query, request->query);
...@@ -359,29 +352,56 @@ static int forward_request(zone_t *zone, struct knot_request *request) ...@@ -359,29 +352,56 @@ static int forward_request(zone_t *zone, struct knot_request *request)
knot_wire_set_id(query->wire, dnssec_random_uint16_t()); knot_wire_set_id(query->wire, dnssec_random_uint16_t());
knot_tsig_append(query->wire, &query->size, query->max_size, query->tsig_rr); knot_tsig_append(query->wire, &query->size, query->max_size, query->tsig_rr);
/* Create a request. */ /* Read the ddns master or the first master. */
const struct sockaddr *dst = (const struct sockaddr *)&master.addr; conf_val_t remote = conf_zone_get(conf(), C_DDNS_MASTER, zone->name);
const struct sockaddr *src = (const struct sockaddr *)&master.via; if (remote.code != KNOT_EOK) {
struct knot_request *req = knot_request_make(re.mm, dst, src, query, 0); remote = conf_zone_get(conf(), C_MASTER, zone->name);
if (req == NULL) {
knot_pkt_free(&query);
return KNOT_ENOMEM;
} }
/* Prepare packet capture layer. */ /* Get the number of remote addresses. */
struct capture_param param; conf_val_t addr = conf_id_get(conf(), C_RMT, C_ADDR, &remote);
param.sink = request->resp; size_t addr_count = conf_val_count(&addr);
knot_requestor_overlay(&re, LAYER_CAPTURE, &param);
/* Try all remote addresses to forward the request to. */
for (size_t i = 0; i < addr_count; i++) {
conf_remote_t master = conf_remote(conf(), &remote, i);
/* Create requestor instance. */
struct knot_requestor re;
knot_requestor_init(&re, NULL);
/* Prepare packet capture layer. */
struct capture_param param;
param.sink = request->resp;
knot_requestor_overlay(&re, LAYER_CAPTURE, &param);
/* Create a request. */
const struct sockaddr *dst = (const struct sockaddr *)&master.addr;
const struct sockaddr *src = (const struct sockaddr *)&master.via;
struct knot_request *req = knot_request_make(re.mm, dst, src, query, 0);
if (req == NULL) {
knot_pkt_free(&query);
return KNOT_ENOMEM;
}
/* Enqueue the request. */
ret = knot_requestor_enqueue(&re, req);
if (ret != KNOT_EOK) {
knot_requestor_clear(&re);
continue;
}
/* Enqueue and execute request. */ /* Execute the request. */
ret = knot_requestor_enqueue(&re, req);
if (ret == KNOT_EOK) {
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT); conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT);
struct timeval tv = { conf_int(&val), 0 }; struct timeval tv = { conf_int(&val), 0 };
ret = knot_requestor_exec(&re, &tv); ret = knot_requestor_exec(&re, &tv);
} if (ret == KNOT_EOK) {
knot_requestor_clear(&re);
break;
}
knot_requestor_clear(&re); knot_requestor_clear(&re);
}
/* Restore message ID and TSIG. */ /* Restore message ID and TSIG. */
knot_wire_set_id(request->resp->wire, knot_wire_get_id(request->query->wire)); knot_wire_set_id(request->resp->wire, knot_wire_get_id(request->query->wire));
......
...@@ -174,7 +174,7 @@ static int zone_query_transfer(zone_t *zone, const conf_remote_t *master, uint16 ...@@ -174,7 +174,7 @@ static int zone_query_transfer(zone_t *zone, const conf_remote_t *master, uint16
} }
/* Log connection errors. */ /* Log connection errors. */
ZONE_XFER_LOG(LOG_ERR, pkt_type, "failed (%s)", knot_strerror(ret)); ZONE_XFER_LOG(LOG_WARNING, pkt_type, "failed (%s)", knot_strerror(ret));
} }
return ret; return ret;
...@@ -315,6 +315,20 @@ fail: ...@@ -315,6 +315,20 @@ fail:
return result; return result;
} }
static int try_refresh(zone_t *zone, const conf_remote_t *master, void *ctx)
{
assert(zone);
assert(master);
int ret = zone_query_execute(zone, KNOT_QUERY_NORMAL, master);
if (ret != KNOT_EOK) {
ZONE_QUERY_LOG(LOG_WARNING, zone, master, "refresh, outgoing",
"failed (%s)", knot_strerror(ret));
}
return ret;
}
int event_refresh(zone_t *zone) int event_refresh(zone_t *zone)
{ {
assert(zone); assert(zone);
...@@ -330,15 +344,11 @@ int event_refresh(zone_t *zone) ...@@ -330,15 +344,11 @@ int event_refresh(zone_t *zone)
return KNOT_EOK; return KNOT_EOK;
} }
const conf_remote_t master = zone_master(zone); int ret = zone_master_try(zone, try_refresh, NULL);
int ret = zone_query_execute(zone, KNOT_QUERY_NORMAL, &master);
const knot_rdataset_t *soa = zone_soa(zone); const knot_rdataset_t *soa = zone_soa(zone);
if (ret != KNOT_EOK) { if (ret != KNOT_EOK) {
/* Log connection errors. */ log_zone_error(zone->name, "refresh, failed (%s)",
ZONE_QUERY_LOG(LOG_WARNING, zone, &master, "SOA query, outgoing", knot_strerror(ret));
"failed (%s)", knot_strerror(ret));
/* Rotate masters if current failed. */
zone_master_rotate(zone);
/* Schedule next retry. */ /* Schedule next retry. */
zone_events_schedule(zone, ZONE_EVENT_REFRESH, knot_soa_retry(soa)); zone_events_schedule(zone, ZONE_EVENT_REFRESH, knot_soa_retry(soa));
start_expire_timer(zone, soa); start_expire_timer(zone, soa);
...@@ -350,6 +360,21 @@ int event_refresh(zone_t *zone) ...@@ -350,6 +360,21 @@ int event_refresh(zone_t *zone)
return zone_events_write_persistent(zone); return zone_events_write_persistent(zone);
} }
struct transfer_data {
uint16_t pkt_type;
};
static int try_xfer(zone_t *zone, const conf_remote_t *master, void *_data)
{
assert(zone);
assert(master);
assert(_data);
struct transfer_data *data = _data;
return zone_query_transfer(zone, master, data->pkt_type);
}
int event_xfer(zone_t *zone) int event_xfer(zone_t *zone)
{ {
assert(zone); assert(zone);
...@@ -359,20 +384,29 @@ int event_xfer(zone_t *zone) ...@@ -359,20 +384,29 @@ int event_xfer(zone_t *zone)
return KNOT_EOK; return KNOT_EOK;
} }
struct transfer_data data = { 0 };
/* Determine transfer type. */ /* Determine transfer type. */
bool is_boostrap = zone_contents_is_empty(zone->contents); bool is_bootstrap = zone_contents_is_empty(zone->contents);
uint16_t pkt_type = KNOT_QUERY_IXFR; if (is_bootstrap || zone->flags & ZONE_FORCE_AXFR) {
if (is_boostrap || zone->flags & ZONE_FORCE_AXFR) { data.pkt_type = KNOT_QUERY_AXFR;
pkt_type = KNOT_QUERY_AXFR; } else {
data.pkt_type = KNOT_QUERY_IXFR;
} }
/* Execute zone transfer and reschedule timers. */ /* Execute zone transfer. */
const conf_remote_t master = zone_master(zone); int ret = zone_master_try(zone, try_xfer, &data);
int ret = zone_query_transfer(zone, &master, pkt_type);
/* Clear preferred master. */
pthread_mutex_lock(&zone->preferred_lock);
free(zone->preferred_master);
zone->preferred_master = NULL;
pthread_mutex_unlock(&zone->preferred_lock);
/* Handle failure during transfer. */
if (ret != KNOT_EOK) { if (ret != KNOT_EOK) {
if (is_boostrap) { log_zone_error(zone->name, "transfer, failed (%s)",
knot_strerror(ret));
if (is_bootstrap) {
zone->bootstrap_retry = bootstrap_next(zone->bootstrap_retry); zone->bootstrap_retry = bootstrap_next(zone->bootstrap_retry);
zone_events_schedule(zone, ZONE_EVENT_XFER, zone->bootstrap_retry); zone_events_schedule(zone, ZONE_EVENT_XFER, zone->bootstrap_retry);
} else { } else {
...@@ -404,7 +438,7 @@ int event_xfer(zone_t *zone) ...@@ -404,7 +438,7 @@ int event_xfer(zone_t *zone)
zone->flags &= ~ZONE_FORCE_AXFR; zone->flags &= ~ZONE_FORCE_AXFR;
/* Trim extra heap. */ /* Trim extra heap. */
if (!is_boostrap) { if (!is_bootstrap) {
mem_trim(); mem_trim();
} }
...@@ -485,22 +519,27 @@ int event_notify(zone_t *zone) ...@@ -485,22 +519,27 @@ int event_notify(zone_t *zone)
} }
/* Walk through configured remotes and send messages. */ /* Walk through configured remotes and send messages. */
conf_val_t val = conf_zone_get(conf(), C_NOTIFY, zone->name); conf_val_t notify = conf_zone_get(conf(), C_NOTIFY, zone->name);
while (val.code == KNOT_EOK) { while (notify.code == KNOT_EOK) {
conf_remote_t remote = conf_remote(conf(), &val); conf_val_t addr = conf_id_get(conf(), C_RMT, C_ADDR, &notify);
size_t addr_count = conf_val_count(&addr);
int ret = zone_query_execute(zone, KNOT_QUERY_NOTIFY, &remote);
if (ret == KNOT_EOK) { for (int i = 0; i < addr_count; i++) {
ZONE_QUERY_LOG(LOG_INFO, zone, &remote, conf_remote_t slave = conf_remote(conf(), &notify, i);
"NOTIFY, outgoing", "serial %u", int ret = zone_query_execute(zone, KNOT_QUERY_NOTIFY, &slave);
zone_contents_serial(zone->contents)); if (ret == KNOT_EOK) {
} else { ZONE_QUERY_LOG(LOG_INFO, zone, &slave,
ZONE_QUERY_LOG(LOG_WARNING, zone, &remote, "NOTIFY, outgoing", "serial %u",
"NOTIFY, outgoing", "failed (%s)", zone_contents_serial(zone->contents));
knot_strerror(ret)); break;
} else {
ZONE_QUERY_LOG(LOG_WARNING, zone, &slave,
"NOTIFY, outgoing", "failed (%s)",
knot_strerror(ret));
}
} }
conf_val_next(&val); conf_val_next(&notify);
} }
return KNOT_EOK; return KNOT_EOK;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "knot/zone/zone.h" #include "knot/zone/zone.h"
#include "knot/zone/zonefile.h" #include "knot/zone/zonefile.h"
#include "knot/zone/contents.h" #include "knot/zone/contents.h"
#include "knot/updates/acl.h"
#include "knot/updates/apply.h" #include "knot/updates/apply.h"
#include "libknot/processing/requestor.h" #include "libknot/processing/requestor.h"
#include "knot/nameserver/process_query.h" #include "knot/nameserver/process_query.h"
...@@ -76,6 +77,9 @@ zone_t* zone_new(const knot_dname_t *name) ...@@ -76,6 +77,9 @@ zone_t* zone_new(const knot_dname_t *name)
// Journal lock // Journal lock
pthread_mutex_init(&zone->journal_lock, NULL); pthread_mutex_init(&zone->journal_lock, NULL);
// Preferred master lock
pthread_mutex_init(&zone->preferred_lock, NULL);
// Initialize events // Initialize events
zone_events_init(zone); zone_events_init(zone);
...@@ -98,6 +102,10 @@ void zone_free(zone_t **zone_ptr) ...@@ -98,6 +102,10 @@ void zone_free(zone_t **zone_ptr)
pthread_mutex_destroy(&zone->ddns_lock); pthread_mutex_destroy(&zone->ddns_lock);
pthread_mutex_destroy(&zone->journal_lock); pthread_mutex_destroy(&zone->journal_lock);
/* Free preferred master. */
pthread_mutex_destroy(&zone->preferred_lock);
free(zone->preferred_master);
/* Free zone contents. */ /* Free zone contents. */
zone_contents_deep_free(&zone->contents); zone_contents_deep_free(&zone->contents);
...@@ -188,29 +196,88 @@ bool zone_is_slave(const zone_t *zone) ...@@ -188,29 +196,88 @@ bool zone_is_slave(const zone_t *zone)
return conf_val_count(&val) > 0 ? true : false; return conf_val_count(&val) > 0 ? true : false;
} }
conf_remote_t zone_master(const zone_t *zone) /*!
* \brief Get preferred zone master while checking its existence.
*/
int static preferred_master(zone_t *zone, conf_remote_t *master)
{ {
conf_val_t val = conf_zone_get(conf(), C_MASTER, zone->name); pthread_mutex_lock(&zone->preferred_lock);
if (zone->preferred_master == NULL) {
pthread_mutex_unlock(&zone->preferred_lock);
return KNOT_EINVAL;
}
/* Seek the current master if possible. */ conf_val_t masters = conf_zone_get(conf(), C_MASTER, zone->name);
if (zone->master_index < conf_val_count(&val)) { while (masters.code == KNOT_EOK) {
for (size_t index = 0; index < zone->master_index; index++) { conf_val_t addr = conf_id_get(conf(), C_RMT, C_ADDR, &masters);
conf_val_next(&val); size_t addr_count = conf_val_count(&addr);
for (size_t i = 0; i < addr_count; i++) {
conf_remote_t remote = conf_remote(conf(), &masters, i);
if (netblock_match(&remote.addr, zone->preferred_master, -1)) {
*master = remote;
pthread_mutex_unlock(&zone->preferred_lock);
return KNOT_EOK;
}
} }
conf_val_next(&masters);
} }
return conf_remote(conf(), &val); pthread_mutex_unlock(&zone->preferred_lock);
return KNOT_ENOENT;
} }
void zone_master_rotate(zone_t *zone) int zone_master_try(zone_t *zone, zone_master_cb callback, void *callback_data)
{ {
conf_val_t val = conf_zone_get(conf(), C_MASTER, zone->name); if (zone == NULL) {
return KNOT_EINVAL;
}
if (zone->master_index + 2 <= conf_val_count(&val)) { /* Try the preferred server. */
zone->master_index += 1;
} else { conf_remote_t preferred = { { AF_UNSPEC } };
zone->master_index = 0; if (preferred_master(zone, &preferred) == KNOT_EOK) {
int ret = callback(zone, &preferred, callback_data);
if (ret == KNOT_EOK) {
return ret;
}
}
/* Try all the other servers. */
bool success = false;
conf_val_t masters = conf_zone_get(conf(), C_MASTER, zone->name);
while (masters.code == KNOT_EOK) {
conf_val_t addr = conf_id_get(conf(), C_RMT, C_ADDR, &masters);
size_t addr_count = conf_val_count(&addr);
for (size_t i = 0; i < addr_count; i++) {
conf_remote_t master = conf_remote(conf(), &masters, i);
if (preferred.addr.ss_family != AF_UNSPEC &&
netblock_match(&master.addr, &preferred.addr, -1)) {
preferred.addr.ss_family = AF_UNSPEC;
continue;
}
int ret = callback(zone, &master, callback_data);
if (ret == KNOT_EOK) {
success = true;
break;
}
}
if (!success) {
log_zone_warning(zone->name, "refresh, remote '%s' "
"not available", conf_str(&masters));
}
conf_val_next(&masters);
} }
return success ? KNOT_EOK : KNOT_ENOMASTER;
} }
int zone_flush_journal(zone_t *zone) int zone_flush_journal(zone_t *zone)
......
...@@ -67,12 +67,14 @@ typedef struct zone ...@@ -67,12 +67,14 @@ typedef struct zone
/*! \brief Zone events. */ /*! \brief Zone events. */
zone_events_t events; /*!< Zone events timers. */ zone_events_t events; /*!< Zone events timers. */
uint32_t bootstrap_retry; /*!< AXFR/IN bootstrap retry. */
time_t zonefile_mtime; time_t zonefile_mtime;
uint32_t bootstrap_retry; /*!< AXFR/IN bootstrap retry. */
uint32_t zonefile_serial; uint32_t zonefile_serial;
/*! \brief Config master list index of the current master server. */ /*! \brief Preferred master lock. */
size_t master_index; pthread_mutex_t preferred_lock;
/*! \brief Preferred master for remote operation. */
struct sockaddr_storage *preferred_master;
/*! \brief Query modules. */ /*! \brief Query modules. */
list_t query_modules; list_t query_modules;
...@@ -109,17 +111,23 @@ int zone_change_store(zone_t *zone, changeset_t *change); ...@@ -109,17 +111,23 @@ int zone_change_store(zone_t *zone, changeset_t *change);
/*!