.send = dns_resolve_send,
};
+/* local function prototypes */
+static int dns_run_resolution(struct dns_requester *requester);
+
#if DEBUG
/*
* go through the resolutions associated to a resolvers section and print the ID and hostname in
*/
void dns_print_current_resolutions(struct dns_resolvers *resolvers)
{
- list_for_each_entry(resolution, &resolvers->curr_resolution, list) {
+ list_for_each_entry(resolution, &resolvers->resolution.curr, list) {
printf(" resolution %d for %s\n", resolution->query_id, resolution->hostname_dn);
}
}
#endif
+void dump_dns_config()
+{
+ struct dns_resolvers *curr_resolvers = NULL;
+ struct dns_nameserver *curr_nameserver = NULL;
+ struct dns_resolution *curr_resolution = NULL;
+ struct dns_requester *curr_requester = NULL;
+
+ printf("===============\n");
+ list_for_each_entry(curr_resolvers, &dns_resolvers, list) {
+ printf("Resolvers: %s\n", curr_resolvers->id);
+
+ printf(" nameservers:\n");
+ list_for_each_entry(curr_nameserver, &curr_resolvers->nameserver_list, list) {
+ printf(" %s\n", curr_nameserver->id);
+ }
+
+/*
+ printf(" resolution.pool list:\n");
+ list_for_each_entry(curr_resolution, &curr_resolvers->resolution.pool, list) {
+ printf(" %p\n", curr_resolution);
+ }
+*/
+
+ printf(" resolution.wait list:\n");
+ list_for_each_entry(curr_resolution, &curr_resolvers->resolution.wait, list) {
+ printf(" %p %s\n", curr_resolution, curr_resolution->hostname_dn);
+ printf(" requester.wait list:\n");
+ list_for_each_entry(curr_requester, &curr_resolution->requester.wait, list) {
+ printf(" %p %s %d\n", curr_requester, objt_server(curr_requester->requester)->id, curr_requester->prefered_query_type);
+ }
+ printf(" requester.curr list:\n");
+ list_for_each_entry(curr_requester, &curr_resolution->requester.curr, list) {
+ printf(" %p %s %d\n", curr_requester, objt_server(curr_requester->requester)->id, curr_requester->prefered_query_type);
+ }
+ }
+ printf(" resolution.curr list:\n");
+ list_for_each_entry(curr_resolution, &curr_resolvers->resolution.curr, list) {
+ printf(" %p %s\n", curr_resolution, curr_resolution->hostname_dn);
+ printf(" requester.wait list:\n");
+ list_for_each_entry(curr_requester, &curr_resolution->requester.wait, list) {
+ printf(" %p %s %d\n", curr_requester, objt_server(curr_requester->requester)->id, curr_requester->prefered_query_type);
+ }
+ printf(" requester.curr list:\n");
+ list_for_each_entry(curr_requester, &curr_resolution->requester.curr, list) {
+ printf(" %p %s %d\n", curr_requester, objt_server(curr_requester->requester)->id, curr_requester->prefered_query_type);
+ }
+ }
+ }
+
+ printf("===============\n");
+}
+
+/*
+ * Initiates a new name resolution:
+ * - generates a query id
+ * - configure the resolution structure
+ * - startup the resolvers task if required
+ *
+ * returns:
+ * - 0 if everything started properly
+ * - -1 in case of error or if resolution already running
+ */
+int dns_trigger_resolution(struct dns_resolution *resolution)
+{
+ struct dns_requester *requester = NULL, *tmprequester;
+ struct dns_resolvers *resolvers = NULL;
+ int inter;
+
+ /* process the element of the wait queue */
+ list_for_each_entry_safe(requester, tmprequester, &resolution->requester.wait, list) {
+ inter = 0;
+
+ switch (obj_type(requester->requester)) {
+ case OBJ_TYPE_SERVER:
+ inter = objt_server(requester->requester)->check.inter;
+ resolvers = objt_server(requester->requester)->resolvers;
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ return -1;
+ }
+
+ /* if data is fresh enough, let's use it */
+ if (!tick_is_expired(tick_add(resolution->last_resolution, inter), now_ms)) {
+ /* we only use cache if the response there is valid.
+ * If not valid, we run the resolution and move the requester to
+ * the run queue. */
+ if (resolution->status != RSLV_STATUS_VALID) {
+ LIST_DEL(&requester->list);
+ LIST_ADDQ(&resolution->requester.curr, &requester->list);
+ dns_run_resolution(requester);
+ continue;
+ }
+
+ requester->requester_cb(requester, NULL);
+ }
+ else {
+ LIST_DEL(&requester->list);
+ LIST_ADDQ(&resolution->requester.curr, &requester->list);
+ dns_run_resolution(requester);
+ }
+ }
+
+ if (resolvers)
+ dns_update_resolvers_timeout(resolvers);
+
+ return 0;
+}
+
+/*
+ * Prepare and send a DNS resolution.
+ *
+ * Return code:
+ * - 0 if no error occured
+ * - -1 in case of error
+ */
+static int
+dns_run_resolution(struct dns_requester *requester)
+{
+ struct dns_resolution *resolution;
+ struct dns_resolvers *resolvers;
+ int query_id, query_type, i;
+ struct proxy *proxy;
+
+ resolution = NULL;
+ resolvers = NULL;
+ proxy = NULL;
+ query_type = -1;
+ switch (obj_type(requester->requester)) {
+ case OBJ_TYPE_SERVER:
+ resolution = objt_server(requester->requester)->resolution;
+ resolvers = objt_server(requester->requester)->resolvers;
+ proxy = objt_server(requester->requester)->proxy;
+ query_type = requester->prefered_query_type;
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ return -1;
+ }
+
+ /*
+ * check if a resolution has already been started for this server
+ * return directly to avoid resolution pill up.
+ */
+ if (resolution->step != RSLV_STEP_NONE)
+ return 0;
+
+ /* generates a query id */
+ i = 0;
+ do {
+ query_id = dns_rnd16();
+ /* we do try only 100 times to find a free query id */
+ if (i++ > 100) {
+ chunk_printf(&trash, "could not generate a query id for %s, in resolvers %s",
+ resolution->hostname_dn, resolvers->id);
+
+ if (proxy)
+ send_log(proxy, LOG_NOTICE, "%s.\n", trash.str);
+ return -1;
+ }
+ } while (eb32_lookup(&resolvers->query_ids, query_id));
+
+ /* move the resolution into the run queue */
+ LIST_DEL(&resolution->list);
+ LIST_ADDQ(&resolvers->resolution.curr, &resolution->list);
+
+ /* now update resolution parameters */
+ resolution->query_id = query_id;
+ resolution->qid.key = query_id;
+ resolution->step = RSLV_STEP_RUNNING;
+ resolution->query_type = query_type;
+ resolution->try = resolvers->resolve_retries;
+ resolution->try_cname = 0;
+ resolution->nb_responses = 0;
+ eb32_insert(&resolvers->query_ids, &resolution->qid);
+
+ dns_send_query(resolution);
+ resolution->try -= 1;
+
+ /* update wakeup date if this resolution is the only one in the FIFO list */
+ if (dns_check_resolution_queue(resolvers) == 1) {
+ /* update task timeout */
+ dns_update_resolvers_timeout(resolvers);
+ task_queue(resolvers->t);
+ }
+
+ return 0;
+}
+
/*
* check if there is more than 1 resolution in the resolver's resolution list
* return value:
int dns_check_resolution_queue(struct dns_resolvers *resolvers)
{
- if (LIST_ISEMPTY(&resolvers->curr_resolution))
+ if (LIST_ISEMPTY(&resolvers->resolution.curr))
return 0;
- if ((resolvers->curr_resolution.n) && (resolvers->curr_resolution.n == resolvers->curr_resolution.p))
+ if ((resolvers->resolution.curr.n) && (resolvers->resolution.curr.n == resolvers->resolution.curr.p))
return 1;
- if (! ((resolvers->curr_resolution.n == resolvers->curr_resolution.p)
- && (&resolvers->curr_resolution != resolvers->curr_resolution.n)))
+ if (! ((resolvers->resolution.curr.n == resolvers->resolution.curr.p)
+ && (&resolvers->resolution.curr != resolvers->resolution.curr.n)))
return 2;
return 0;
}
/*
- * reset all parameters of a DNS resolution to 0 (or equivalent)
- * and clean it up from all associated lists (resolution->qid and resolution->list)
+ * reset some resolution parameters to initial values and also delete the
+ * query ID from the resolver's tree.
*/
void dns_reset_resolution(struct dns_resolution *resolution)
{
eb32_delete(&resolution->qid);
resolution->query_id = 0;
resolution->qid.key = 0;
-
- /* the second resolution in the queue becomes the first one */
- LIST_DEL(&resolution->list);
}
/*
*/
void dns_resolve_recv(struct dgram_conn *dgram)
{
- struct dns_nameserver *nameserver;
+ struct dns_nameserver *nameserver, *tmpnameserver;
struct dns_resolvers *resolvers;
- struct dns_resolution *resolution;
+ struct dns_resolution *resolution = NULL;
struct dns_query_item *query;
unsigned char buf[DNS_MAX_UDP_MESSAGE + 1];
unsigned char *bufend;
- int fd, buflen, ret;
+ int fd, buflen, dns_resp, need_resend;
unsigned short query_id;
struct eb32_node *eb;
struct lru64 *lru = NULL;
+ struct dns_requester *requester = NULL, *tmprequester = NULL;
fd = dgram->t.sock.fd;
/* number of responses received */
resolution->nb_responses += 1;
- ret = dns_validate_dns_response(buf, bufend, resolution);
+ dns_resp = dns_validate_dns_response(buf, bufend, resolution);
- /* treat only errors */
- switch (ret) {
- case DNS_RESP_QUERY_COUNT_ERROR:
- case DNS_RESP_INVALID:
- nameserver->counters.invalid += 1;
- resolution->requester_error_cb(resolution, DNS_RESP_INVALID);
- continue;
+ /* treat errors first
+ * need_resend flag could be set to 0 by default before the 'switch' and then
+ * set to 1 only where needed, but I think it's better this way to make people
+ * aware they have to think twice how to set this flag when updating this portion
+ * of the code
+ */
+ switch (dns_resp) {
+ case DNS_RESP_VALID:
+ need_resend = 0;
+ break;
- case DNS_RESP_INTERNAL:
- case DNS_RESP_ERROR:
- nameserver->counters.other += 1;
- resolution->requester_error_cb(resolution, DNS_RESP_ERROR);
- continue;
+ case DNS_RESP_INVALID:
+ case DNS_RESP_QUERY_COUNT_ERROR:
+ case DNS_RESP_WRONG_NAME:
+ if (resolution->status != RSLV_STATUS_INVALID) {
+ resolution->status = RSLV_STATUS_INVALID;
+ resolution->last_status_change = now_ms;
+ }
+ nameserver->counters.invalid += 1;
+ need_resend = 0;
+ break;
- case DNS_RESP_ANCOUNT_ZERO:
- nameserver->counters.any_err += 1;
- resolution->requester_error_cb(resolution, DNS_RESP_ANCOUNT_ZERO);
- continue;
+ case DNS_RESP_ANCOUNT_ZERO:
+ if (resolution->status != RSLV_STATUS_OTHER) {
+ resolution->status = RSLV_STATUS_OTHER;
+ resolution->last_status_change = now_ms;
+ }
+ nameserver->counters.any_err += 1;
+ need_resend = 1;
+ break;
- case DNS_RESP_NX_DOMAIN:
- nameserver->counters.nx += 1;
- resolution->requester_error_cb(resolution, DNS_RESP_NX_DOMAIN);
- continue;
+ case DNS_RESP_NX_DOMAIN:
+ if (resolution->status != RSLV_STATUS_NX) {
+ resolution->status = RSLV_STATUS_NX;
+ resolution->last_status_change = now_ms;
+ }
+ nameserver->counters.nx += 1;
+ need_resend = 0;
+ break;
- case DNS_RESP_REFUSED:
- nameserver->counters.refused += 1;
- resolution->requester_error_cb(resolution, DNS_RESP_REFUSED);
- continue;
+ case DNS_RESP_REFUSED:
+ if (resolution->status != RSLV_STATUS_REFUSED) {
+ resolution->status = RSLV_STATUS_REFUSED;
+ resolution->last_status_change = now_ms;
+ }
+ nameserver->counters.refused += 1;
+ need_resend = 0;
+ break;
- case DNS_RESP_CNAME_ERROR:
- nameserver->counters.cname_error += 1;
- resolution->requester_error_cb(resolution, DNS_RESP_CNAME_ERROR);
- continue;
+ case DNS_RESP_CNAME_ERROR:
+ if (resolution->status != RSLV_STATUS_OTHER) {
+ resolution->status = RSLV_STATUS_OTHER;
+ resolution->last_status_change = now_ms;
+ }
+ nameserver->counters.cname_error += 1;
+ need_resend = 1;
+ break;
- case DNS_RESP_TRUNCATED:
- nameserver->counters.truncated += 1;
- resolution->requester_error_cb(resolution, DNS_RESP_TRUNCATED);
- continue;
+ case DNS_RESP_TRUNCATED:
+ if (resolution->status != RSLV_STATUS_OTHER) {
+ resolution->status = RSLV_STATUS_OTHER;
+ resolution->last_status_change = now_ms;
+ }
+ nameserver->counters.truncated += 1;
+ need_resend = 1;
+ break;
- case DNS_RESP_NO_EXPECTED_RECORD:
- nameserver->counters.other += 1;
- resolution->requester_error_cb(resolution, DNS_RESP_NO_EXPECTED_RECORD);
- continue;
+ case DNS_RESP_NO_EXPECTED_RECORD:
+ if (resolution->status != RSLV_STATUS_OTHER) {
+ resolution->status = RSLV_STATUS_OTHER;
+ resolution->last_status_change = now_ms;
+ }
+ nameserver->counters.other += 1;
+ need_resend = 1;
+ break;
+
+ case DNS_RESP_ERROR:
+ case DNS_RESP_INTERNAL:
+ if (resolution->status != RSLV_STATUS_OTHER) {
+ resolution->status = RSLV_STATUS_OTHER;
+ resolution->last_status_change = now_ms;
+ }
+ nameserver->counters.other += 1;
+ need_resend = 1;
+ break;
+ }
+
+ /* some error codes trigger a re-send of the query, but switching the
+ * query type.
+ * This is the case for the following error codes:
+ * DNS_RESP_ANCOUNT_ZERO
+ * DNS_RESP_TRUNCATED
+ * DNS_RESP_ERROR
+ * DNS_RESP_INTERNAL
+ * DNS_RESP_NO_EXPECTED_RECORD
+ * DNS_RESP_CNAME_ERROR
+ */
+ if (need_resend) {
+ int family_prio;
+ int res_preferred_afinet, res_preferred_afinet6;
+
+ requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
+ switch (obj_type(requester->requester)) {
+ case OBJ_TYPE_SERVER:
+ family_prio = objt_server(requester->requester)->dns_opts.family_prio;
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ family_prio = AF_INET6;
+ }
+ res_preferred_afinet = family_prio == AF_INET && resolution->query_type == DNS_RTYPE_A;
+ res_preferred_afinet6 = family_prio == AF_INET6 && resolution->query_type == DNS_RTYPE_AAAA;
+ if ((res_preferred_afinet || res_preferred_afinet6)
+ || (resolution->try > 0)) {
+ /* let's change the query type */
+ if (res_preferred_afinet6) {
+ /* fallback from AAAA to A */
+ resolution->query_type = DNS_RTYPE_A;
+ }
+ else if (res_preferred_afinet) {
+ /* fallback from A to AAAA */
+ resolution->query_type = DNS_RTYPE_AAAA;
+ }
+ else {
+ resolution->try -= 1;
+ if (family_prio == AF_INET) {
+ resolution->query_type = DNS_RTYPE_A;
+ } else {
+ resolution->query_type = DNS_RTYPE_AAAA;
+ }
+ }
+
+ dns_send_query(resolution);
+ /*
+ * move the resolution to the last element of the FIFO queue
+ * and update timeout wakeup based on the new first entry
+ */
+ if (dns_check_resolution_queue(resolvers) > 1) {
+ /* second resolution becomes first one */
+ LIST_DEL(&resolution->list);
+ /* ex first resolution goes to the end of the queue */
+ LIST_ADDQ(&resolvers->resolution.curr, &resolution->list);
+ }
+
+ dns_update_resolvers_timeout(resolvers);
+ goto next_packet;
+ }
+
+ /* if we're there, this means that we already ran out of chances to re-send
+ * the query */
+ list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
+ requester->requester_error_cb(requester, dns_resp);
+ }
+ goto next_packet;
+ }
+
+ /* now processing those error codes only:
+ * DNS_RESP_NX_DOMAIN
+ * DNS_RESP_REFUSED
+ */
+ if (dns_resp != DNS_RESP_VALID) {
+ /* now parse list of requesters currently waiting for this resolution */
+ list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
+ requester->requester_error_cb(requester, dns_resp);
+
+ /* we can move the requester the wait queue */
+ LIST_DEL(&requester->list);
+ LIST_ADDQ(&resolution->requester.wait, &requester->list);
+ }
+ goto next_packet;
}
/* Now let's check the query's dname corresponds to the one we sent.
query = LIST_NEXT(&resolution->response.query_list, struct dns_query_item *, list);
if (query && memcmp(query->name, resolution->hostname_dn, resolution->hostname_dn_len) != 0) {
nameserver->counters.other += 1;
- resolution->requester_error_cb(resolution, DNS_RESP_WRONG_NAME);
- continue;
+ /* now parse list of requesters currently waiting for this resolution */
+ list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
+ requester->requester_error_cb(requester, DNS_RESP_WRONG_NAME);
+ /* we can move the requester the wait queue */
+ LIST_DEL(&requester->list);
+ LIST_ADDQ(&resolution->requester.wait, &requester->list);
+ }
+ goto next_packet;
}
/* no errors, we can save the response in the cache */
chunk_reset(buf);
tmp = dns_cache_key(resolution->query_type, resolution->hostname_dn,
- resolution->hostname_dn_len, buf);
+ resolution->hostname_dn_len, buf);
if (!tmp) {
nameserver->counters.other += 1;
- resolution->requester_error_cb(resolution, DNS_RESP_ERROR);
- continue;
+ /* now parse list of requesters currently waiting for this resolution */
+ list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
+ requester->requester_error_cb(requester, DNS_RESP_ERROR);
+ /* we can move the requester the wait queue */
+ LIST_DEL(&requester->list);
+ LIST_ADDQ(&resolution->requester.wait, &requester->list);
+ }
+ goto next_packet;
}
lru = lru64_get(XXH64(buf->str, buf->len, seed),
lru64_commit(lru, resolution, nameserver->resolvers, 1, NULL);
}
+ if (resolution->status != RSLV_STATUS_VALID) {
+ resolution->status = RSLV_STATUS_VALID;
+ resolution->last_status_change = now_ms;
+ }
+
nameserver->counters.valid += 1;
- resolution->requester_cb(resolution, nameserver);
- }
+ /* now parse list of requesters currently waiting for this resolution */
+ tmpnameserver = nameserver;
+ list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
+ requester->requester_cb(requester, tmpnameserver);
+ /* we can move the requester the wait queue */
+ LIST_DEL(&requester->list);
+ LIST_ADDQ(&resolution->requester.wait, &requester->list);
+ /* first response is managed by the server, others are from the cache */
+ tmpnameserver = NULL;
+ }
+
+ next_packet:
+ /* resolution may be NULL when we receive an ICMP unreachable packet */
+ if (resolution && LIST_ISEMPTY(&resolution->requester.curr)) {
+ /* move the resolution into the wait queue */
+ LIST_DEL(&resolution->list);
+ LIST_ADDQ(&resolvers->resolution.wait, &resolution->list);
+ /* update last resolution date and time */
+ resolution->last_resolution = now_ms;
+ /* reset current status flag */
+ resolution->step = RSLV_STEP_NONE;
+ /* reset values */
+ dns_reset_resolution(resolution);
+ }
+
+ } // end of while "packets" loop
+
+ dns_update_resolvers_timeout(nameserver->resolvers);
}
/*
return;
resolvers = nameserver->resolvers;
- resolution = LIST_NEXT(&resolvers->curr_resolution, struct dns_resolution *, list);
+ resolution = LIST_NEXT(&resolvers->resolution.curr, struct dns_resolution *, list);
dns_send_query(resolution);
dns_update_resolvers_timeout(resolvers);
{
struct dns_resolvers *resolvers = NULL;
struct dns_nameserver *nameserver;
+ struct dns_requester *requester = NULL;
int ret, bufsize, fd;
- resolvers = ((struct server *)resolution->requester)->resolvers;
+ /* nothing to do */
+ if (LIST_ISEMPTY(&resolution->requester.curr))
+ return 0;
+
+ requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
+
+ switch (obj_type(requester->requester)) {
+ case OBJ_TYPE_SERVER:
+ resolvers = objt_server(requester->requester)->resolvers;
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ return 0;
+ }
if (!resolvers)
return 0;
void dns_update_resolvers_timeout(struct dns_resolvers *resolvers)
{
struct dns_resolution *resolution;
+ struct dns_requester *requester;
- if (LIST_ISEMPTY(&resolvers->curr_resolution)) {
- /* no more resolution pending, so no wakeup anymore */
+ if ((LIST_ISEMPTY(&resolvers->resolution.curr)) && (LIST_ISEMPTY(&resolvers->resolution.wait))) {
resolvers->t->expire = TICK_ETERNITY;
}
- else {
- resolution = LIST_NEXT(&resolvers->curr_resolution, struct dns_resolution *, list);
- resolvers->t->expire = tick_add(resolution->last_sent_packet, resolvers->timeout.retry);
+ else if (!LIST_ISEMPTY(&resolvers->resolution.curr)) {
+ resolution = LIST_NEXT(&resolvers->resolution.curr, struct dns_resolution *, list);
+ if (!resolvers->t->expire || tick_is_le(resolvers->t->expire, tick_add(resolution->last_sent_packet, resolvers->timeout.retry))) {
+ resolvers->t->expire = tick_add(resolution->last_sent_packet, resolvers->timeout.retry);
+ }
}
+ else if (!LIST_ISEMPTY(&resolvers->resolution.wait)) {
+ int valid_period, inter, need_wakeup;
+ struct dns_resolution *res_back;
+ need_wakeup = 0;
+ list_for_each_entry_safe(resolution, res_back, &resolvers->resolution.wait, list) {
+ valid_period = 0;
+ inter = 0;
+
+ requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
+
+ switch (obj_type(requester->requester)) {
+ case OBJ_TYPE_SERVER:
+ valid_period = objt_server(requester->requester)->check.inter;
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ continue;
+ }
+
+ if (resolvers->hold.valid < valid_period)
+ inter = resolvers->hold.valid;
+ else
+ inter = valid_period;
+
+ if (tick_is_expired(tick_add(resolution->last_resolution, inter), now_ms)) {
+ switch (obj_type(requester->requester)) {
+ case OBJ_TYPE_SERVER:
+ dns_trigger_resolution(objt_server(requester->requester)->resolution);
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ ;;
+ }
+ }
+ else {
+ need_wakeup = 1;
+ }
+ }
+ /* in such case, we wake up in 1s */
+ if (need_wakeup) {
+ int r = 1000;
+
+ resolution = LIST_NEXT(&resolvers->resolution.wait, struct dns_resolution *, list);
+ if (tick_is_le(resolvers->t->expire, tick_add(now_ms, r)))
+ resolvers->t->expire = tick_add(now_ms, r);
+ resolvers->t->expire = tick_add(now_ms, 1000);
+ }
+ }
+
+ task_queue(resolvers->t);
}
/*
{
struct dns_resolvers *curr_resolvers;
struct dns_nameserver *curnameserver;
+ struct dns_resolution *resolution, *res_back;
struct dgram_conn *dgram;
struct task *t;
int fd;
/* update task's parameters */
t->process = dns_process_resolve;
t->context = curr_resolvers;
- t->expire = TICK_ETERNITY;
+ t->expire = 0;
- curr_resolvers->t = t;
+ /* no need to keep the new task if one is already affected to our resolvers
+ * section */
+ if (!curr_resolvers->t)
+ curr_resolvers->t = t;
+ else
+ task_free(t);
list_for_each_entry(curnameserver, &curr_resolvers->nameserver_list, list) {
- dgram = NULL;
+ dgram = NULL;
if (close_socket == 1) {
if (curnameserver->dgram) {
continue;
}
+ if (close_socket == 0)
+ continue;
+
+ /* now, we can trigger DNS resolution */
+ list_for_each_entry_safe(resolution, res_back, &curr_resolvers->resolution.wait, list) {
+ /* if there is no requester in the wait queue, no need to trigger the resolution */
+ if (LIST_ISEMPTY(&resolution->requester.wait))
+ continue;
+
+ dns_trigger_resolution(resolution);
+ }
+
/* task can be queued */
task_queue(t);
}
return 1;
}
+/*
+ * Allocate a pool of resolution to a resolvers section.
+ * Each resolution is associated with a UUID.
+ *
+ * Return code:
+ * - 0 if everything went smoothly
+ * - -1 if an error occured
+ */
+int dns_alloc_resolution_pool(struct dns_resolvers *resolvers)
+{
+ int i;
+ struct dns_resolution *resolution;
+
+ /* return if a pool has already been set for this resolvers */
+ if (!LIST_ISEMPTY(&resolvers->resolution.pool)) {
+ return 0;
+ }
+
+ for (i = 0; i < resolvers->resolution_pool_size; i++) {
+ resolution = dns_alloc_resolution();
+ if (!resolution) {
+ Alert("Starting [%s] resolvers: can't allocate memory for DNS resolution pool.\n", resolvers->id);
+ return -1;
+ }
+ resolution->uuid = i;
+ LIST_ADDQ(&resolvers->resolution.pool, &resolution->list);
+ }
+
+ return 0;
+}
+
/*
* Forge a DNS query. It needs the following information from the caller:
* - <query_id>: the DNS query id corresponding to this query
int res_preferred_afinet, res_preferred_afinet6;
struct dns_options *dns_opts = NULL;
- /* timeout occurs inevitably for the first element of the FIFO queue */
- if (LIST_ISEMPTY(&resolvers->curr_resolution)) {
+ /* if both there is no resolution in the run queue, we can re-schedule a wake up */
+ if (LIST_ISEMPTY(&resolvers->resolution.curr)) {
/* no first entry, so wake up was useless */
- t->expire = TICK_ETERNITY;
+ dns_update_resolvers_timeout(resolvers);
return t;
}
/* look for the first resolution which is not expired */
- list_for_each_entry_safe(resolution, res_back, &resolvers->curr_resolution, list) {
+ list_for_each_entry_safe(resolution, res_back, &resolvers->resolution.curr, list) {
+ struct dns_requester *requester = NULL;
+
/* when we find the first resolution in the future, then we can stop here */
if (tick_is_le(now_ms, resolution->last_sent_packet))
goto out;
+ if (LIST_ISEMPTY(&resolution->requester.curr))
+ goto out;
+
/*
* if current resolution has been tried too many times and finishes in timeout
* we update its status and remove it from the list
*/
if (resolution->try <= 0) {
+ struct dns_requester *tmprequester;
/* clean up resolution information and remove from the list */
dns_reset_resolution(resolution);
- /* notify the result to the requester */
- resolution->requester_error_cb(resolution, DNS_RESP_TIMEOUT);
+ LIST_DEL(&resolution->list);
+ LIST_ADDQ(&resolvers->resolution.wait, &resolution->list);
+
+ if (resolution->status != RSLV_STATUS_TIMEOUT) {
+ resolution->status = RSLV_STATUS_TIMEOUT;
+ resolution->last_status_change = now_ms;
+ }
+
+ /* notify the result to the requesters */
+ list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
+ requester->requester_error_cb(requester, DNS_RESP_TIMEOUT);
+ LIST_DEL(&requester->list);
+ LIST_ADDQ(&resolution->requester.wait, &requester->list);
+ }
goto out;
}
resolution->try -= 1;
- dns_opts = &((struct server *)resolution->requester)->dns_opts;
+ /* running queue is empty, nothing to do but wait */
+ if (LIST_ISEMPTY(&resolution->requester.curr))
+ goto out;
+
+ requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
+
+ switch (obj_type(requester->requester)) {
+ case OBJ_TYPE_SERVER:
+ dns_opts = &(objt_server(requester->requester)->dns_opts);
+ break;
+
+ case OBJ_TYPE_NONE:
+ default:
+ /* clean up resolution information and remove from the list */
+ dns_reset_resolution(resolution);
+
+ LIST_DEL(&resolution->list);
+ LIST_ADDQ(&resolvers->resolution.wait, &resolution->list);
+
+ /* notify the result to the requester */
+ requester->requester_error_cb(requester, DNS_RESP_INTERNAL);
+ goto out;
+ }
res_preferred_afinet = dns_opts->family_prio == AF_INET && resolution->query_type == DNS_RTYPE_A;
res_preferred_afinet6 = dns_opts->family_prio == AF_INET6 && resolution->query_type == DNS_RTYPE_AAAA;
if (dns_check_resolution_queue(resolvers) > 1) {
/* move the rsolution to the end of the list */
LIST_DEL(&resolution->list);
- LIST_ADDQ(&resolvers->curr_resolution, &resolution->list);
+ LIST_ADDQ(&resolvers->resolution.curr, &resolution->list);
}
}
struct lru64 *elem = NULL;
struct dns_resolution *resolution = NULL;
struct dns_resolvers *resolvers = NULL;
+ struct dns_requester *requester = NULL;
int inter = 0;
struct chunk *buf = get_trash_chunk();
struct chunk *tmp = NULL;
return NULL;
}
- resolvers = ((struct server *)resolution->requester)->resolvers;
+ requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
+
+ switch (obj_type(requester->requester)) {
+ case OBJ_TYPE_SERVER:
+ resolvers = objt_server(requester->requester)->resolvers;
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ return NULL;
+ }
if (!resolvers)
return NULL;
return 0;
}
+/*
+ * if <resolution> is provided, then the function skips the memory allocation part.
+ * It does the linking only.
+ *
+ * if <resolution> is NULL, the function links a dns resolution to a requester:
+ * - it allocates memory for the struct requester used to link
+ * the resolution to the requester
+ * - it configures the resolution if this is the first requester to be linked to it
+ * - it updates the requester with a pointer to the resolution
+ *
+ * Return code:
+ * - 0 if everything happened smoothly
+ * - -1 if an error occured. Of course, no resolution is linked to the requester
+ */
+int dns_link_resolution(void *requester, int requester_type, struct dns_resolution *resolution)
+{
+ struct dns_resolution *tmpresolution = NULL;
+ struct dns_requester *tmprequester = NULL;
+ struct dns_resolvers *resolvers = NULL;
+ char *hostname_dn = NULL;
+ int new_resolution;
+
+ if (!resolution) {
+ tmprequester = calloc(1, sizeof(*tmprequester));
+ if (!tmprequester)
+ return -1;
+
+ switch (requester_type) {
+ case OBJ_TYPE_SERVER:
+ tmprequester->requester = &((struct server *)requester)->obj_type;
+ hostname_dn = objt_server(tmprequester->requester)->hostname_dn;
+ resolvers = objt_server(tmprequester->requester)->resolvers;
+ switch (objt_server(tmprequester->requester)->dns_opts.family_prio) {
+ case AF_INET:
+ tmprequester->prefered_query_type = DNS_RTYPE_A;
+ break;
+ default:
+ tmprequester->prefered_query_type = DNS_RTYPE_AAAA;
+ }
+
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ free(tmprequester);
+ return -1;
+ }
+
+ /* get a resolution from the resolvers' wait queue or pool */
+ tmpresolution = dns_resolution_list_get(resolvers, hostname_dn, tmprequester->prefered_query_type);
+ if (!tmpresolution) {
+ free(tmprequester);
+ return -1;
+ }
+ }
+ else {
+ tmpresolution = resolution;
+
+ switch (requester_type) {
+ case OBJ_TYPE_SERVER:
+ tmprequester = ((struct server *)requester)->dns_requester;
+ resolvers = ((struct server *)requester)->resolvers;
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ return -1;
+ }
+ }
+
+ /* flag this resolution as NEW if applicable (not already linked to any requester).
+ * this is required to decide which parameters we have to update on the resolution.
+ * If new, it means we pulled up the resolution from the resolvers' pool.
+ */
+ if (LIST_ISEMPTY(&tmpresolution->requester.wait)) {
+ new_resolution = 1;
+ }
+ else
+ new_resolution = 0;
+
+ /* those parameters are related to the requester type */
+ switch (obj_type(tmprequester->requester)) {
+ case OBJ_TYPE_SERVER:
+ /* some parameters should be set only if the resolution is brand new */
+ if (new_resolution) {
+ tmpresolution->query_type = tmprequester->prefered_query_type;
+ tmpresolution->hostname_dn = objt_server(tmprequester->requester)->hostname_dn;
+ tmpresolution->hostname_dn_len = objt_server(tmprequester->requester)->hostname_dn_len;
+ }
+
+ /* update requester as well, only if we just allocated it */
+ objt_server(tmprequester->requester)->resolution = tmpresolution;
+ if (!resolution) {
+ tmprequester->requester_cb = snr_resolution_cb;
+ tmprequester->requester_error_cb = snr_resolution_error_cb;
+ objt_server(tmprequester->requester)->dns_requester = tmprequester;
+ }
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ free(tmprequester);
+ return -1;
+ }
+
+ /* update some parameters only if this is a brand new resolution */
+ if (new_resolution) {
+ /* move the resolution to the requesters' wait queue */
+ LIST_DEL(&tmpresolution->list);
+ LIST_ADDQ(&resolvers->resolution.wait, &tmpresolution->list);
+
+ tmpresolution->status = RSLV_STATUS_NONE;
+ tmpresolution->step = RSLV_STEP_NONE;
+ tmpresolution->revision = 1;
+ }
+
+ /* add the requester to the resolution's wait queue */
+ if (resolution)
+ LIST_DEL(&tmprequester->list);
+ LIST_ADDQ(&tmpresolution->requester.wait, &tmprequester->list);
+
+ return 0;
+}
+
+/*
+ * pick up an available resolution from the different resolution list associated to a resolvers section,
+ * in this order:
+ * 1. check in resolution.curr for the same hostname and query_type
+ * 2. check in resolution.wait for the same hostname and query_type
+ * 3. take an available resolution from resolution.pool
+ *
+ * return an available resolution, NULL if none found.
+ */
+struct dns_resolution *dns_resolution_list_get(struct dns_resolvers *resolvers, char *hostname_dn, int query_type)
+{
+ struct dns_resolution *resolution, *tmpresolution;
+ struct dns_requester *requester;
+
+ /* search for same hostname and query type in resolution.curr */
+ list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.curr, list) {
+ requester = NULL;
+
+ if (!LIST_ISEMPTY(&resolution->requester.wait))
+ requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
+ else if (!LIST_ISEMPTY(&resolution->requester.curr))
+ requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
+
+ if (!requester)
+ continue;
+
+ if ((query_type == requester->prefered_query_type) &&
+ (strcmp(hostname_dn, resolution->hostname_dn) == 0)) {
+ return resolution;
+ }
+ }
+
+ /* search for same hostname and query type in resolution.wait */
+ list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.wait, list) {
+ requester = NULL;
+
+ if (!LIST_ISEMPTY(&resolution->requester.wait))
+ requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
+ else if (!LIST_ISEMPTY(&resolution->requester.curr))
+ requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
+
+ if (!requester)
+ continue;
+
+ if ((query_type == requester->prefered_query_type) &&
+ (strcmp(hostname_dn, resolution->hostname_dn) == 0)) {
+ return resolution;
+ }
+ }
+
+ /* take the first one (hopefully) from the pool */
+ list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.pool, list) {
+ if (LIST_ISEMPTY(&resolution->requester.wait)) {
+ return resolution;
+ }
+ }
+
+ return NULL;
+}
+
/* This function allocates memory for a DNS resolution structure.
* It's up to the caller to set the parameters
* Returns a pointer to the structure resolution or NULL if memory could
}
chunk_init(&resolution->response_buffer, buffer, global.tune.bufsize);
+ LIST_INIT(&resolution->requester.wait);
+ LIST_INIT(&resolution->requester.curr);
return resolution;
}
return;
}
+/* this function free a resolution from its requester(s) and move it back to the pool */
+void dns_resolution_free(struct dns_resolvers *resolvers, struct dns_resolution *resolution)
+{
+ struct dns_requester *requester, *tmprequester;
+
+ /* clean up configuration */
+ dns_reset_resolution(resolution);
+ resolution->hostname_dn = NULL;
+ resolution->hostname_dn_len = 0;
+
+ list_for_each_entry_safe(requester, tmprequester, &resolution->requester.wait, list) {
+ LIST_DEL(&requester->list);
+ }
+ list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
+ LIST_DEL(&requester->list);
+ }
+
+ LIST_DEL(&resolution->list);
+ LIST_ADDQ(&resolvers->resolution.pool, &resolution->list);
+
+ return;
+}
+
+/*
+ * this function remove a requester from a resolution
+ * and takes care of all the consequences.
+ * It also cleans up some parameters from the requester
+ */
+void dns_rm_requester_from_resolution(struct dns_requester *requester, struct dns_resolution *resolution)
+{
+ char *hostname_dn;
+ struct dns_requester *tmprequester;
+
+ /* resolution is still used by other requesters, we need to move
+ * some pointers to an other requester if needed
+ */
+ switch (obj_type(requester->requester)) {
+ case OBJ_TYPE_SERVER:
+ hostname_dn = objt_server(requester->requester)->hostname_dn;
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ hostname_dn = NULL;
+ break;
+ }
+
+ if (resolution->hostname_dn != hostname_dn)
+ return;
+
+ /* First, we need to find this other requester */
+ tmprequester = NULL;
+ list_for_each_entry(tmprequester, &resolution->requester.wait, list) {
+ if (tmprequester != requester)
+ break;
+ }
+ if (!tmprequester) {
+ /* if we can't find it in wait queue, let's get one in run queue */
+ list_for_each_entry(tmprequester, &resolution->requester.curr, list) {
+ if (tmprequester != requester)
+ break;
+ }
+ }
+
+ /* move hostname_dn related pointers to the next requester */
+ switch (obj_type(tmprequester->requester)) {
+ case OBJ_TYPE_SERVER:
+ resolution->hostname_dn = objt_server(tmprequester->requester)->hostname_dn;
+ resolution->hostname_dn_len = objt_server(tmprequester->requester)->hostname_dn_len;
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ ;;
+ }
+
+
+ /* clean up the requester */
+ LIST_DEL(&requester->list);
+ switch (obj_type(requester->requester)) {
+ case OBJ_TYPE_SERVER:
+ objt_server(requester->requester)->resolution = NULL;
+ break;
+ case OBJ_TYPE_NONE:
+ default:
+ ;;
+ }
+}
+
/* This function dumps counters from all resolvers section and associated name
* servers. It returns 0 if the output buffer is full and it needs to be called
* again, otherwise non-zero. It may limit itself to the resolver pointed to by
static void srv_update_state(struct server *srv, int version, char **params);
static int srv_apply_lastaddr(struct server *srv, int *err_code);
static int srv_set_fqdn(struct server *srv, const char *fqdn);
-static void srv_free_dns_resolution(struct server *srv);
/* List head of all known server keywords */
static struct srv_kw_list srv_keywords = {
#endif
/*
- * Allocate <srv> server dns resolution.
+ * Prepare <srv> for hostname resolution.
* May be safely called with a default server as <src> argument (without hostname).
* Returns -1 in case of any allocation failure, 0 if not.
*/
-static int srv_alloc_dns_resolution(struct server *srv, const char *hostname)
+static int srv_prepare_for_resolution(struct server *srv, const char *hostname)
{
- struct dns_resolution *dst_dns_rslt;
-
if (!hostname)
return 0;
free(srv->hostname);
srv->hostname = strdup(hostname);
- dst_dns_rslt = dns_alloc_resolution();
srv->hostname_dn_len = dns_str_to_dn_label_len(hostname);
srv->hostname_dn = calloc(srv->hostname_dn_len + 1, sizeof(char));
- if (!srv->hostname || !dst_dns_rslt || !srv->hostname_dn)
+ if (!srv->hostname || !srv->hostname_dn)
goto err;
- srv->resolution = dst_dns_rslt;
-
if (!dns_str_to_dn_label(srv->hostname,
srv->hostname_dn,
srv->hostname_dn_len + 1))
goto err;
- srv->resolution->hostname_dn = srv->hostname_dn;
- srv->resolution->hostname_dn_len = srv->hostname_dn_len;
- srv->resolution->revision = 1;
- srv->resolution->requester = srv;
- srv->resolution->requester_cb = snr_resolution_cb;
- srv->resolution->requester_error_cb = snr_resolution_error_cb;
- srv->resolution->status = RSLV_STATUS_NONE;
- srv->resolution->step = RSLV_STEP_NONE;
- /* a first resolution has been done by the configuration parser */
- srv->resolution->last_resolution = 0;
-
- if (srv->resolvers_id) {
- struct dns_resolvers *curr_resolvers;
- int found = 0;
-
- list_for_each_entry(curr_resolvers, &dns_resolvers, list) {
- if (!strcmp(curr_resolvers->id, srv->resolvers_id)) {
- found = 1;
- break;
- }
- }
- if (!found)
- goto err;
- srv->resolvers = curr_resolvers;
- }
-
return 0;
err:
srv->hostname = NULL;
free(srv->hostname_dn);
srv->hostname_dn = NULL;
- dns_free_resolution(dst_dns_rslt);
return -1;
}
-static void srv_free_dns_resolution(struct server *srv)
+/*
+ * Free the link between a server and its resolution.
+ * It also performs the following tasks:
+ * - check if resolution can be moved back in the resolvers' pool
+ * (and do it)
+ * - move resolution's hostname_dn and hostname_dn_len to the next requester
+ * available (when applied)
+ */
+static void srv_free_from_resolution(struct server *srv)
{
- if (!srv->resolution)
+ struct dns_requester *requester;
+ int count;
+
+ /* check if we can move the resolution back to the pool.
+ * if <count> is greater than 1, then we can't */
+ count = 0;
+ list_for_each_entry(requester, &srv->resolution->requester.wait, list) {
+ ++count;
+ if (count > 1)
+ break;
+ }
+ list_for_each_entry(requester, &srv->resolution->requester.curr, list) {
+ ++count;
+ if (count > 1)
+ break;
+ }
+ if (count <= 1) {
+ /* move the resolution back to the pool */
+ dns_resolution_free(srv->resolvers, srv->resolution);
return;
+ }
- dns_free_resolution(srv->resolution);
- srv->resolution = NULL;
+ dns_rm_requester_from_resolution(srv->dns_requester, srv->resolution);
+
+ return;
}
/*
goto err;
srv_settings_cpy(newsrv, srv, 1);
- srv_alloc_dns_resolution(newsrv, srv->hostname);
+ srv_prepare_for_resolution(newsrv, srv->hostname);
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
if (newsrv->sni_expr) {
newsrv->ssl_ctx.sni = srv_sni_sample_parse_expr(newsrv, px, NULL, 0, NULL);
/* save hostname and create associated name resolution */
if (fqdn) {
- if (srv_alloc_dns_resolution(newsrv, fqdn) == -1) {
+ if (srv_prepare_for_resolution(newsrv, fqdn) == -1) {
Alert("parsing [%s:%d] : Can't create DNS resolution for server '%s'\n",
file, linenum, newsrv->id);
err_code |= ERR_ALERT | ERR_FATAL;
switch (resolution->status) {
case RSLV_STATUS_NONE:
- /* status when HAProxy has just (re)started */
- trigger_resolution(s);
+ /* status when HAProxy has just (re)started.
+ * Nothing to do, since the task is already automatically started */
break;
case RSLV_STATUS_VALID:
* 0 on error
* 1 when no error or safe ignore
*/
-int snr_resolution_cb(struct dns_resolution *resolution, struct dns_nameserver *nameserver)
+int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver *nameserver)
{
- struct server *s;
+ struct server *s = NULL;
+ struct dns_resolution *resolution = NULL;
void *serverip, *firstip;
short server_sin_family, firstip_sin_family;
int ret;
struct chunk *chk = get_trash_chunk();
+ s = objt_server(requester->requester);
+ if (!s)
+ return 1;
+
+ resolution = s->resolution;
+
/* initializing variables */
firstip = NULL; /* pointer to the first valid response found */
/* it will be used as the new IP if a change is required */
firstip_sin_family = AF_UNSPEC;
serverip = NULL; /* current server IP address */
- /* shortcut to the server whose name is being resolved */
- s = resolution->requester;
-
/* initializing server IP pointer */
server_sin_family = s->addr.ss_family;
switch (server_sin_family) {
switch (ret) {
case DNS_UPD_NO:
- if (resolution->status != RSLV_STATUS_VALID) {
- resolution->status = RSLV_STATUS_VALID;
- resolution->last_status_change = now_ms;
- }
- goto stop_resolution;
+ goto update_status;
case DNS_UPD_SRVIP_NOT_FOUND:
goto save_ip;
case DNS_UPD_CNAME:
- if (resolution->status != RSLV_STATUS_VALID) {
- resolution->status = RSLV_STATUS_VALID;
- resolution->last_status_change = now_ms;
- }
goto invalid;
case DNS_UPD_NO_IP_FOUND:
resolution->status = RSLV_STATUS_OTHER;
resolution->last_status_change = now_ms;
}
- goto stop_resolution;
+ goto update_status;
case DNS_UPD_NAME_ERROR:
/* if this is not the last expected response, we ignore it */
- if (resolution->nb_responses < nameserver->resolvers->count_nameservers)
+ if (nameserver && (resolution->nb_responses < nameserver->resolvers->count_nameservers))
return 0;
/* update resolution status to OTHER error type */
if (resolution->status != RSLV_STATUS_OTHER) {
resolution->status = RSLV_STATUS_OTHER;
resolution->last_status_change = now_ms;
}
- goto stop_resolution;
+ goto update_status;
default:
goto invalid;
}
save_ip:
- nameserver->counters.update += 1;
- if (resolution->status != RSLV_STATUS_VALID) {
- resolution->status = RSLV_STATUS_VALID;
- resolution->last_status_change = now_ms;
- }
+ if (nameserver)
+ nameserver->counters.update += 1;
/* save the first ip we found */
- chunk_printf(chk, "%s/%s", nameserver->resolvers->id, nameserver->id);
+ if (nameserver)
+ chunk_printf(chk, "%s/%s", nameserver->resolvers->id, nameserver->id);
+ else
+ chunk_printf(chk, "DNS cache");
update_server_addr(s, firstip, firstip_sin_family, (char *)chk->str);
- stop_resolution:
- /* update last resolution date and time */
- resolution->last_resolution = now_ms;
- /* reset current status flag */
- resolution->step = RSLV_STEP_NONE;
- /* reset values */
- dns_reset_resolution(resolution);
-
- dns_update_resolvers_timeout(nameserver->resolvers);
-
+ update_status:
snr_update_srv_status(s);
- return 0;
+ return 1;
invalid:
- nameserver->counters.invalid += 1;
+ if (nameserver)
+ nameserver->counters.invalid += 1;
if (resolution->nb_responses >= nameserver->resolvers->count_nameservers)
- goto stop_resolution;
+ goto update_status;
snr_update_srv_status(s);
return 0;
* 0 on error
* 1 when no error or safe ignore
*/
-int snr_resolution_error_cb(struct dns_resolution *resolution, int error_code)
+int snr_resolution_error_cb(struct dns_requester *requester, int error_code)
{
- struct server *s;
- struct dns_resolvers *resolvers;
- int res_preferred_afinet, res_preferred_afinet6;
+ struct server *s = NULL;
+ struct dns_resolution *resolution = NULL;
+ struct dns_resolvers *resolvers = NULL;
+
+ s = objt_server(requester->requester);
+ if (!s)
+ return 1;
- /* shortcut to the server whose name is being resolved */
- s = resolution->requester;
+ resolution = s->resolution;
resolvers = s->resolvers;
/* can be ignored if this is not the last response */
return 1;
}
- switch (error_code) {
- case DNS_RESP_INVALID:
- case DNS_RESP_WRONG_NAME:
- if (resolution->status != RSLV_STATUS_INVALID) {
- resolution->status = RSLV_STATUS_INVALID;
- resolution->last_status_change = now_ms;
- }
- break;
-
- case DNS_RESP_ANCOUNT_ZERO:
- case DNS_RESP_TRUNCATED:
- case DNS_RESP_ERROR:
- case DNS_RESP_NO_EXPECTED_RECORD:
- case DNS_RESP_CNAME_ERROR:
- res_preferred_afinet = s->dns_opts.family_prio == AF_INET && resolution->query_type == DNS_RTYPE_A;
- res_preferred_afinet6 = s->dns_opts.family_prio == AF_INET6 && resolution->query_type == DNS_RTYPE_AAAA;
-
- if ((res_preferred_afinet || res_preferred_afinet6)
- || (resolution->try > 0)) {
- /* let's change the query type */
- if (res_preferred_afinet6) {
- /* fallback from AAAA to A */
- resolution->query_type = DNS_RTYPE_A;
- }
- else if (res_preferred_afinet) {
- /* fallback from A to AAAA */
- resolution->query_type = DNS_RTYPE_AAAA;
- }
- else {
- resolution->try -= 1;
- if (s->dns_opts.family_prio == AF_INET) {
- resolution->query_type = DNS_RTYPE_A;
- } else {
- resolution->query_type = DNS_RTYPE_AAAA;
- }
- }
-
- dns_send_query(resolution);
-
- /*
- * move the resolution to the last element of the FIFO queue
- * and update timeout wakeup based on the new first entry
- */
- if (dns_check_resolution_queue(resolvers) > 1) {
- /* second resolution becomes first one */
- LIST_DEL(&resolution->list);
- /* ex first resolution goes to the end of the queue */
- LIST_ADDQ(&resolvers->curr_resolution, &resolution->list);
- }
- dns_update_resolvers_timeout(resolvers);
- goto leave;
- }
- else {
- if (resolution->status != RSLV_STATUS_OTHER) {
- resolution->status = RSLV_STATUS_OTHER;
- resolution->last_status_change = now_ms;
- }
- }
- break;
-
- case DNS_RESP_NX_DOMAIN:
- if (resolution->status != RSLV_STATUS_NX) {
- resolution->status = RSLV_STATUS_NX;
- resolution->last_status_change = now_ms;
- }
- break;
-
- case DNS_RESP_REFUSED:
- if (resolution->status != RSLV_STATUS_REFUSED) {
- resolution->status = RSLV_STATUS_REFUSED;
- resolution->last_status_change = now_ms;
- }
- break;
-
- case DNS_RESP_TIMEOUT:
- if (resolution->status != RSLV_STATUS_TIMEOUT) {
- resolution->status = RSLV_STATUS_TIMEOUT;
- resolution->last_status_change = now_ms;
- }
- break;
- }
-
- /* update last resolution date and time */
- resolution->last_resolution = now_ms;
- /* reset current status flag */
- resolution->step = RSLV_STEP_NONE;
- /* reset values */
- dns_reset_resolution(resolution);
-
- LIST_DEL(&resolution->list);
- dns_update_resolvers_timeout(resolvers);
-
- leave:
snr_update_srv_status(s);
return 1;
}
* one used for the server found in the backend
* * the server found in the backend is not our current server
*/
- if ((tmpsrv->resolution == NULL) ||
- (srv->resolution->hostname_dn_len != tmpsrv->resolution->hostname_dn_len) ||
- (strcmp(srv->resolution->hostname_dn, tmpsrv->resolution->hostname_dn) != 0) ||
+ if ((tmpsrv->hostname_dn == NULL) ||
+ (srv->hostname_dn_len != tmpsrv->hostname_dn_len) ||
+ (strcmp(srv->hostname_dn, tmpsrv->hostname_dn) != 0) ||
(srv->puid == tmpsrv->puid))
continue;
*/
int srv_set_fqdn(struct server *srv, const char *hostname)
{
- srv_free_dns_resolution(srv);
+ struct dns_resolution *resolution;
+ int hostname_dn_len;
- return srv_alloc_dns_resolution(srv, hostname);
+ /* run time DNS resolution was not active for this server
+ * and we can't enable it at run time for now.
+ */
+ if (!srv->dns_requester)
+ return -1;
+
+ chunk_reset(&trash);
+
+ /* check if hostname is really a hostname and if we have enough
+ * room to save it in its domain name format
+ */
+ hostname_dn_len = dns_str_to_dn_label_len(hostname);
+ if (hostname_dn_len == -1 || hostname_dn_len + 1 > trash.size)
+ return -1;
+
+ if (!dns_str_to_dn_label(hostname,
+ trash.str,
+ hostname_dn_len + 1))
+ return -1;
+
+
+ /* get a resolution from the curr or wait queues, or a brand new one from the pool */
+ resolution = dns_resolution_list_get(srv->resolvers, trash.str, srv->dns_requester->prefered_query_type);
+ if (!resolution)
+ return -1;
+
+ /* in this case, the new hostanme is the same than the old one */
+ if (srv->resolution == resolution)
+ return 0;
+
+ /* first, we need to unlink our server from its current resolution */
+ srv_free_from_resolution(srv);
+
+ /* now we update server's parameters */
+ free(srv->hostname);
+ free(srv->hostname_dn);
+ srv->hostname = strdup(hostname);
+ srv->hostname_dn = strdup(trash.str);
+ srv->hostname_dn_len = hostname_dn_len;
+ if (!srv->hostname || !srv->hostname_dn)
+ return -1;
+
+ /* then we can link srv to its new resolution */
+ dns_link_resolution(srv, OBJ_TYPE_SERVER, resolution);
+
+ return 0;
}
/* Sets the server's address (srv->addr) from srv->lastaddr which was filled