Commit aaec330d authored by Jan Včelák's avatar Jan Včelák 🚀

Merge multi-master support

parents 4a6f9f9d 3e1abb97
......@@ -394,8 +394,8 @@ Definition of remote servers for zone transfers or notifications.
.ft C
remote:
\- id: STR
address: ADDR[@INT]
via: ADDR[@INT]
address: ADDR[@INT] ...
via: ADDR[@INT] ...
key: key_id
.ft P
.fi
......@@ -406,16 +406,17 @@ remote:
A remote identifier.
.SS address
.sp
A destination IP address of the remote server. Optional destination port
specification (default is 53) can be appended to the address using \fB@\fP
separator.
An ordered list of destination IP addresses which are used for communication
with the remote server. The addresses are tried in sequence unless the
operation is successful. Optional destination port (default is 53)
can be appended to the address using \fB@\fP separator.
.sp
Default: empty
.SS via
.sp
A source IP address which is used to communicate with the remote server.
Optional source port specification can be appended to the address using
\fB@\fP separator.
An ordered list of source IP addresses. The first address with the same family
as the destination address is used. Optional source port (default is random)
can be appended to the address using \fB@\fP separator.
.sp
Default: empty
.SS key
......@@ -459,6 +460,7 @@ zone:
file: STR
storage: STR
master: remote_id ...
ddns\-master: remote_id
notify: remote_id ...
acl: acl_id ...
semantic\-checks: BOOL
......@@ -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.
.sp
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
.sp
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.
remote:
- id: STR
address: ADDR[@INT]
via: ADDR[@INT]
address: ADDR[@INT] ...
via: ADDR[@INT] ...
key: key_id
.. _remote_id:
......@@ -482,9 +482,10 @@ A remote identifier.
address
-------
A destination IP address of the remote server. Optional destination port
specification (default is 53) can be appended to the address using ``@``
separator.
An ordered list of destination IP addresses which are used for communication
with the remote server. The addresses are tried in sequence unless the
operation is successful. Optional destination port (default is 53)
can be appended to the address using ``@`` separator.
Default: empty
......@@ -493,9 +494,9 @@ Default: empty
via
---
A source IP address which is used to communicate with the remote server.
Optional source port specification can be appended to the address using
``@`` separator.
An ordered list of source IP addresses. The first address with the same family
as the destination address is used. Optional source port (default is random)
can be appended to the address using ``@`` separator.
Default: empty
......@@ -547,6 +548,7 @@ Definition of zones served by the server.
file: STR
storage: STR
master: remote_id ...
ddns-master: remote_id
notify: remote_id ...
acl: acl_id ...
semantic-checks: BOOL
......@@ -609,6 +611,16 @@ An ordered list of :ref:`references<remote_id>` to zone master servers.
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:
notify
......
......@@ -511,7 +511,7 @@ struct sockaddr_storage conf_addr(
struct sockaddr_storage conf_net(
conf_val_t *val,
unsigned *prefix_length)
int *prefix_length)
{
assert(val != NULL && val->item != NULL && prefix_length != NULL);
assert(val->item->type == YP_TNET ||
......@@ -521,18 +521,8 @@ struct sockaddr_storage conf_net(
struct sockaddr_storage out = { AF_UNSPEC };
if (val->code == KNOT_EOK) {
int prefix;
conf_db_val(val);
out = yp_addr(val->data, val->len, &prefix);
if (prefix != -1) {
*prefix_length = prefix;
} else {
if (out.ss_family == AF_INET) {
*prefix_length = IPV4_PREFIXLEN;
} else if (out.ss_family == AF_INET6) {
*prefix_length = IPV6_PREFIXLEN;
}
}
out = yp_addr(val->data, val->len, prefix_length);
} else {
*prefix_length = 0;
}
......@@ -824,7 +814,8 @@ int conf_user_txn(
conf_remote_t conf_remote_txn(
conf_t *conf,
namedb_txn_t *txn,
conf_val_t *id)
conf_val_t *id,
size_t index)
{
assert(id != NULL && id->item != NULL);
assert(id->item->type == YP_TSTR ||
......@@ -836,18 +827,24 @@ conf_remote_t conf_remote_txn(
conf_val_t rundir_val = conf_get_txn(conf, txn, C_SRV, C_RUNDIR);
char *rundir = conf_abs_path(&rundir_val, NULL);
// Get remote address.
// Get indexed remote address.
conf_val_t val = conf_id_get_txn(conf, txn, C_RMT, C_ADDR, id);
if (val.code != KNOT_EOK) {
CONF_LOG(LOG_ERR, "invalid remote");
free(rundir);
return out;
for (size_t i = 0; val.code == KNOT_EOK && i < index; i++) {
conf_val_next(&val);
}
// Index overflow causes empty socket.
out.addr = conf_addr(&val, rundir);
// Get outgoing address (optional).
// Get outgoing address if family matches (optional).
val = conf_id_get_txn(conf, txn, C_RMT, C_VIA, id);
out.via = conf_addr(&val, rundir);
while (val.code == KNOT_EOK) {
struct sockaddr_storage via = conf_addr(&val, rundir);
if (via.ss_family == out.addr.ss_family) {
out.via = conf_addr(&val, rundir);
break;
}
conf_val_next(&val);
}
// Get TSIG key (optional).
conf_val_t key_id = conf_id_get_txn(conf, txn, C_RMT, C_KEY, id);
......
......@@ -247,7 +247,7 @@ struct sockaddr_storage conf_addr(
struct sockaddr_storage conf_net(
conf_val_t *val,
unsigned *prefix_length
int *prefix_length
);
char* conf_abs_path(
......@@ -334,13 +334,15 @@ static inline int conf_user(
conf_remote_t conf_remote_txn(
conf_t *conf,
namedb_txn_t *txn,
conf_val_t *id
conf_val_t *id,
size_t index
);
static inline conf_remote_t conf_remote(
conf_t *conf,
conf_val_t *id)
conf_val_t *id,
size_t index)
{
return conf_remote_txn(conf, &conf->read_txn, id);
return conf_remote_txn(conf, &conf->read_txn, id, index);
}
......
......@@ -125,8 +125,8 @@ static const yp_item_t desc_control[] = {
static const yp_item_t desc_remote[] = {
{ C_ID, YP_TSTR, YP_VNONE },
{ C_ADDR, YP_TADDR, YP_VADDR = { 53 } },
{ C_VIA, YP_TADDR, YP_VNONE },
{ C_ADDR, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI },
{ C_VIA, YP_TADDR, YP_VNONE, YP_FMULTI },
{ C_KEY, YP_TREF, YP_VREF = { C_KEY }, YP_FNONE, { check_ref } },
{ C_COMMENT, YP_TSTR, YP_VNONE },
{ NULL }
......@@ -136,6 +136,7 @@ static const yp_item_t desc_remote[] = {
{ C_FILE, YP_TSTR, YP_VNONE }, \
{ C_STORAGE, YP_TSTR, YP_VSTR = { STORAGE_DIR } }, \
{ 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_ACL, YP_TREF, YP_VREF = { C_ACL }, YP_FMULTI, { check_ref } }, \
{ C_SEM_CHECKS, YP_TBOOL, YP_VNONE }, \
......
......@@ -36,6 +36,7 @@
#define C_BG_WORKERS "\x12""background-workers"
#define C_COMMENT "\x07""comment"
#define C_CTL "\x07""control"
#define C_DDNS_MASTER "\x0B""ddns-master"
#define C_DENY "\x04""deny"
#define C_DISABLE_ANY "\x0B""disable-any"
#define C_DNSSEC_SIGNING "\x0E""dnssec-signing"
......
......@@ -85,7 +85,7 @@ typedef struct synth_template {
char *zone;
uint32_t ttl;
struct sockaddr_storage addr;
unsigned mask;
int mask;
} synth_template_t;
/*! \brief Substitute all occurences of given character. */
......
......@@ -385,7 +385,7 @@ int axfr_answer_process(knot_pkt_t *pkt, struct answer_data *adata)
if (rcode != KNOT_RCODE_NOERROR) {
lookup_table_t *lut = lookup_by_id(knot_rcode_names, rcode);
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;
}
......@@ -397,7 +397,7 @@ int axfr_answer_process(knot_pkt_t *pkt, struct answer_data *adata)
int ret = axfr_answer_init(adata);
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;
}
} else {
......
......@@ -888,12 +888,14 @@ static int process_soa_answer(knot_pkt_t *pkt, struct answer_data *data)
if (serial_compare(our_serial, their_serial) >= 0) {
ANSWER_LOG(LOG_INFO, data, "refresh, outgoing", "zone is up-to-date");
zone_events_cancel(zone, ZONE_EVENT_EXPIRE);
zone_clear_preferred_master(zone);
return KNOT_STATE_DONE; /* Our zone is up to date. */
}
/* Our zone is outdated, schedule zone transfer. */
ANSWER_LOG(LOG_INFO, data, "refresh, outgoing", "master has newer serial %u -> %u",
our_serial, their_serial);
zone_set_preferred_master(zone, data->param->remote);
zone_events_schedule(zone, ZONE_EVENT_XFER, ZONE_EVENT_NOW);
return KNOT_STATE_DONE;
}
......
......@@ -98,6 +98,7 @@ int notify_process_query(knot_pkt_t *pkt, struct query_data *qdata)
/* Incoming NOTIFY expires REFRESH timer and renews EXPIRE timer. */
zone_t *zone = (zone_t *)qdata->zone;
zone_set_preferred_master(zone, qdata->param->remote);
zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW);
int ret = zone_events_write_persistent(zone);
if (ret != KNOT_EOK) {
......
......@@ -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)
{
/* 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. */
knot_pkt_t *query = knot_pkt_new(NULL, request->query->max_size, NULL);
int ret = knot_pkt_copy(query, request->query);
......@@ -359,30 +352,55 @@ static int forward_request(zone_t *zone, struct knot_request *request)
knot_wire_set_id(query->wire, dnssec_random_uint16_t());
knot_tsig_append(query->wire, &query->size, query->max_size, query->tsig_rr);
/* 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;
/* Read the ddns master or the first master. */
conf_val_t remote = conf_zone_get(conf(), C_DDNS_MASTER, zone->name);
if (remote.code != KNOT_EOK) {
remote = conf_zone_get(conf(), C_MASTER, zone->name);
}
/* Prepare packet capture layer. */
struct capture_param param;
param.sink = request->resp;
knot_requestor_overlay(&re, LAYER_CAPTURE, &param);
/* Get the number of remote addresses. */
conf_val_t addr = conf_id_get(conf(), C_RMT, C_ADDR, &remote);
size_t addr_count = conf_val_count(&addr);
/* 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);
/* Enqueue and execute request. */
ret = knot_requestor_enqueue(&re, req);
if (ret == KNOT_EOK) {
/* 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;
}
/* Execute the request. */
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT);
struct timeval tv = { conf_int(&val), 0 };
ret = knot_requestor_exec(&re, &tv);
knot_requestor_clear(&re);
if (ret == KNOT_EOK) {
break;
}
}
knot_requestor_clear(&re);
/* Restore message ID and TSIG. */
knot_wire_set_id(request->resp->wire, knot_wire_get_id(request->query->wire));
knot_tsig_append(request->resp->wire, &request->resp->size,
......
......@@ -40,7 +40,7 @@ static const uint8_t* ipv6_addr(const struct sockaddr_storage *ss) {
bool netblock_match(const struct sockaddr_storage *ss1,
const struct sockaddr_storage *ss2,
unsigned prefix)
int prefix)
{
if (ss1 == NULL || ss2 == NULL) {
return false;
......@@ -55,12 +55,20 @@ bool netblock_match(const struct sockaddr_storage *ss1,
case AF_INET:
addr1 = ipv4_addr(ss1);
addr2 = ipv4_addr(ss2);
prefix = prefix > IPV4_PREFIXLEN ? IPV4_PREFIXLEN : prefix;
if (prefix < 0) {
prefix = IPV4_PREFIXLEN;
} else if (prefix > IPV4_PREFIXLEN) {
prefix = IPV4_PREFIXLEN;
}
break;
case AF_INET6:
addr1 = ipv6_addr(ss1);
addr2 = ipv6_addr(ss2);
prefix = prefix > IPV6_PREFIXLEN ? IPV6_PREFIXLEN : prefix;
if (prefix < 0) {
prefix = IPV6_PREFIXLEN;
} else if (prefix > IPV6_PREFIXLEN) {
prefix = IPV6_PREFIXLEN;
}
break;
default:
return false;
......@@ -101,7 +109,7 @@ bool acl_allowed(conf_val_t *acl, acl_action_t action,
/* Check if the address matches the current acl address list. */
val = conf_id_get(conf(), C_ACL, C_ADDR, acl);
while (val.code == KNOT_EOK) {
unsigned prefix;
int prefix;
struct sockaddr_storage ss;
ss = conf_net(&val, &prefix);
if (!netblock_match(addr, &ss, prefix)) {
......
......@@ -44,11 +44,11 @@ typedef enum {
*
* \param ss1 First address storage.
* \param ss2 Second address storage.
* \param prefix Netblock length.
* \param prefix Netblock length (negative value for maximum prefix length).
*/
bool netblock_match(const struct sockaddr_storage *ss1,
const struct sockaddr_storage *ss2,
unsigned prefix);
int prefix);
/*!
* \brief Checks if the address and/or tsig key matches given ACL list.
......
......@@ -174,7 +174,7 @@ static int zone_query_transfer(zone_t *zone, const conf_remote_t *master, uint16
}
/* 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;
......@@ -315,6 +315,20 @@ fail:
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)
{
assert(zone);
......@@ -330,15 +344,11 @@ int event_refresh(zone_t *zone)
return KNOT_EOK;
}
const conf_remote_t master = zone_master(zone);
int ret = zone_query_execute(zone, KNOT_QUERY_NORMAL, &master);
int ret = zone_master_try(zone, try_refresh, NULL, "refresh");
const knot_rdataset_t *soa = zone_soa(zone);
if (ret != KNOT_EOK) {
/* Log connection errors. */
ZONE_QUERY_LOG(LOG_WARNING, zone, &master, "SOA query, outgoing",
"failed (%s)", knot_strerror(ret));
/* Rotate masters if current failed. */
zone_master_rotate(zone);
log_zone_error(zone->name, "refresh, failed (%s)",
knot_strerror(ret));
/* Schedule next retry. */
zone_events_schedule(zone, ZONE_EVENT_REFRESH, knot_soa_retry(soa));
start_expire_timer(zone, soa);
......@@ -350,6 +360,21 @@ int event_refresh(zone_t *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)
{
assert(zone);
......@@ -359,20 +384,26 @@ int event_xfer(zone_t *zone)
return KNOT_EOK;
}
struct transfer_data data = { 0 };
const char *err_str = "";
/* Determine transfer type. */
bool is_boostrap = zone_contents_is_empty(zone->contents);
uint16_t pkt_type = KNOT_QUERY_IXFR;
if (is_boostrap || zone->flags & ZONE_FORCE_AXFR) {
pkt_type = KNOT_QUERY_AXFR;
bool is_bootstrap = zone_contents_is_empty(zone->contents);
if (is_bootstrap || zone->flags & ZONE_FORCE_AXFR) {
data.pkt_type = KNOT_QUERY_AXFR;
err_str = "AXFR, incoming";
} else {
data.pkt_type = KNOT_QUERY_IXFR;
err_str = "IXFR, incoming";
}
/* Execute zone transfer and reschedule timers. */
const conf_remote_t master = zone_master(zone);
int ret = zone_query_transfer(zone, &master, pkt_type);
/* Handle failure during transfer. */
/* Execute zone transfer. */
int ret = zone_master_try(zone, try_xfer, &data, err_str);
zone_clear_preferred_master(zone);
if (ret != KNOT_EOK) {
if (is_boostrap) {
log_zone_error(zone->name, "%s, failed (%s)", err_str,
knot_strerror(ret));
if (is_bootstrap) {
zone->bootstrap_retry = bootstrap_next(zone->bootstrap_retry);
zone_events_schedule(zone, ZONE_EVENT_XFER, zone->bootstrap_retry);
} else {
......@@ -404,7 +435,7 @@ int event_xfer(zone_t *zone)
zone->flags &= ~ZONE_FORCE_AXFR;
/* Trim extra heap. */
if (!is_boostrap) {
if (!is_bootstrap) {
mem_trim();
}
......@@ -485,22 +516,27 @@ int event_notify(zone_t *zone)
}
/* Walk through configured remotes and send messages. */
conf_val_t val = conf_zone_get(conf(), C_NOTIFY, zone->name);
while (val.code == KNOT_EOK) {
conf_remote_t remote = conf_remote(conf(), &val);
int ret = zone_query_execute(zone, KNOT_QUERY_NOTIFY, &remote);
if (ret == KNOT_EOK) {
ZONE_QUERY_LOG(LOG_INFO, zone, &remote,
"NOTIFY, outgoing", "serial %u",
zone_contents_serial(zone->contents));
} else {
ZONE_QUERY_LOG(LOG_WARNING, zone, &remote,
"NOTIFY, outgoing", "failed (%s)",
knot_strerror(ret));
conf_val_t notify = conf_zone_get(conf(), C_NOTIFY, zone->name);
while (notify.code == KNOT_EOK) {
conf_val_t addr = conf_id_get(conf(), C_RMT, C_ADDR, &notify);
size_t addr_count = conf_val_count(&addr);
for (int i = 0; i < addr_count; i++) {
conf_remote_t slave = conf_remote(conf(), &notify, i);
int ret = zone_query_execute(zone, KNOT_QUERY_NOTIFY, &slave);
if (ret == KNOT_EOK) {
ZONE_QUERY_LOG(LOG_INFO, zone, &slave,
"NOTIFY, outgoing", "serial %u",
zone_contents_serial(zone->contents));
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;
......
......@@ -31,6 +31,7 @@
#include "knot/zone/zone.h"
#include "knot/zone/zonefile.h"
#include "knot/zone/contents.h"
#include "knot/updates/acl.h"
#include "knot/updates/apply.h"
#include "libknot/processing/requestor.h"
#include "knot/nameserver/process_query.h"
......@@ -76,6 +77,9 @@ zone_t* zone_new(const knot_dname_t *name)
// Journal lock
pthread_mutex_init(&zone->journal_lock, NULL);
// Preferred master lock
pthread_mutex_init(&zone->preferred_lock, NULL);
// Initialize events
zone_events_init(zone);
......@@ -98,6 +102,10 @@ void zone_free(zone_t **zone_ptr)
pthread_mutex_destroy(&zone->ddns_lock);
pthread_mutex_destroy(&zone->journal_lock);
/* Free preferred master. */
pthread_mutex_destroy(&zone->preferred_lock);
free(zone->preferred_master);
/* Free zone contents. */
zone_contents_deep_free(&zone->contents);
......@@ -188,29 +196,106 @@ bool zone_is_slave(const zone_t *zone)
return conf_val_count(&val) > 0 ? true : false;
}
conf_remote_t zone_master(const zone_t *zone)
void zone_set_preferred_master(zone_t *zone, const struct sockaddr_storage *addr)
{
conf_val_t val = conf_zone_get(conf(), C_MASTER, zone->name);
pthread_mutex_lock(&zone->preferred_lock);
free(zone->preferred_master);
zone->preferred_master = malloc(sizeof(struct sockaddr_storage));
*zone->preferred_master = *addr;
pthread_mutex_unlock(&zone->preferred_lock);
}
/* Seek the current master if possible. */
if (zone->master_index < conf_val_count(&val)) {
for (size_t index = 0; index < zone->master_index; index++) {
conf_val_next(&val);
void zone_clear_preferred_master(zone_t *zone)
{
pthread_mutex_lock(&zone->preferred_lock);
free(zone->preferred_master);
zone->preferred_master = NULL;
pthread_mutex_unlock(&zone->preferred_lock);
}
/*!
* \brief Get preferred zone master while checking its existence.
*/
int static preferred_master(zone_t *zone, conf_remote_t *master)
{
pthread_mutex_lock(&zone->preferred_lock);
if (zone->preferred_master == NULL) {
pthread_mutex_unlock(&zone->preferred_lock);
return KNOT_ENOENT;
}
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 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,
const char *err_str)
{
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)) {
zone->master_index += 1;
} else {
zone->master_index = 0;
/* Try the preferred server. */
conf_remote_t preferred = { { AF_UNSPEC } };
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, "%s, remote '%s' not available",
err_str, conf_str(&masters));
}
conf_val_next(&masters);
}