blob: fceef2e480f78c3a24cdcb2cf650a3dd819cc910 [file] [log] [blame]
Baptiste Assmann325137d2015-04-13 23:40:55 +02001/*
2 * Name server resolution
3 *
4 * Copyright 2014 Baptiste Assmann <bedis9@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <errno.h>
14#include <fcntl.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <unistd.h>
19
20#include <sys/types.h>
21
Christopher Faulet67957bd2017-09-27 11:00:59 +020022#include <common/errors.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020023#include <common/time.h>
24#include <common/ticks.h>
Olivier Houchard8da5f982017-08-04 18:35:36 +020025#include <common/net_helper.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020026
William Lallemand69e96442016-11-19 00:58:54 +010027#include <types/applet.h>
28#include <types/cli.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020029#include <types/global.h>
30#include <types/dns.h>
William Lallemand69e96442016-11-19 00:58:54 +010031#include <types/stats.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020032
William Lallemand69e96442016-11-19 00:58:54 +010033#include <proto/channel.h>
34#include <proto/cli.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020035#include <proto/checks.h>
36#include <proto/dns.h>
37#include <proto/fd.h>
38#include <proto/log.h>
39#include <proto/server.h>
40#include <proto/task.h>
41#include <proto/proto_udp.h>
Christopher Faulet67957bd2017-09-27 11:00:59 +020042#include <proto/proxy.h>
William Lallemand69e96442016-11-19 00:58:54 +010043#include <proto/stream_interface.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020044
Christopher Faulet67957bd2017-09-27 11:00:59 +020045struct list dns_resolvers = LIST_HEAD_INIT(dns_resolvers);
46struct list dns_srvrq_list = LIST_HEAD_INIT(dns_srvrq_list);
Baptiste Assmann325137d2015-04-13 23:40:55 +020047
Christopher Fauletb2812a62017-10-04 16:17:58 +020048static THREAD_LOCAL int64_t dns_query_id_seed = 0; /* random seed */
Christopher Faulet67957bd2017-09-27 11:00:59 +020049static struct pool_head *dns_answer_item_pool = NULL;
50static struct pool_head *dns_resolution_pool = NULL;
51static unsigned int resolution_uuid = 1;
Baptiste Assmann201c07f2017-05-22 15:17:15 +020052
Christopher Faulet67957bd2017-09-27 11:00:59 +020053/* Returns a pointer to the resolvers matching the id <id>. NULL is returned if
54 * no match is found.
Baptiste Assmann325137d2015-04-13 23:40:55 +020055 */
Christopher Faulet67957bd2017-09-27 11:00:59 +020056struct dns_resolvers *find_resolvers_by_id(const char *id)
Baptiste Assmann201c07f2017-05-22 15:17:15 +020057{
Christopher Faulet67957bd2017-09-27 11:00:59 +020058 struct dns_resolvers *res;
Baptiste Assmann201c07f2017-05-22 15:17:15 +020059
Christopher Faulet67957bd2017-09-27 11:00:59 +020060 list_for_each_entry(res, &dns_resolvers, list) {
61 if (!strcmp(res->id, id))
62 return res;
Baptiste Assmann201c07f2017-05-22 15:17:15 +020063 }
Christopher Faulet67957bd2017-09-27 11:00:59 +020064 return NULL;
Baptiste Assmann201c07f2017-05-22 15:17:15 +020065}
66
Christopher Faulet67957bd2017-09-27 11:00:59 +020067/* Returns a pointer on the SRV request matching the name <name> for the proxy
68 * <px>. NULL is returned if no match is found.
Baptiste Assmann201c07f2017-05-22 15:17:15 +020069 */
Christopher Faulet67957bd2017-09-27 11:00:59 +020070struct dns_srvrq *find_srvrq_by_name(const char *name, struct proxy *px)
Baptiste Assmann201c07f2017-05-22 15:17:15 +020071{
Christopher Faulet67957bd2017-09-27 11:00:59 +020072 struct dns_srvrq *srvrq;
Baptiste Assmann201c07f2017-05-22 15:17:15 +020073
Christopher Faulet67957bd2017-09-27 11:00:59 +020074 list_for_each_entry(srvrq, &dns_srvrq_list, list) {
75 if (srvrq->proxy == px && !strcmp(srvrq->name, name))
76 return srvrq;
Baptiste Assmann201c07f2017-05-22 15:17:15 +020077 }
Christopher Faulet67957bd2017-09-27 11:00:59 +020078 return NULL;
Baptiste Assmann201c07f2017-05-22 15:17:15 +020079}
80
Christopher Faulet67957bd2017-09-27 11:00:59 +020081/* Allocates a new SRVRQ for the given server with the name <fqdn>. It returns
82 * NULL if an error occurred. */
83struct dns_srvrq *new_dns_srvrq(struct server *srv, char *fqdn)
Baptiste Assmann201c07f2017-05-22 15:17:15 +020084{
Christopher Faulet67957bd2017-09-27 11:00:59 +020085 struct proxy *px = srv->proxy;
86 struct dns_srvrq *srvrq = NULL;
87 int fqdn_len, hostname_dn_len;
Baptiste Assmann201c07f2017-05-22 15:17:15 +020088
Christopher Faulet67957bd2017-09-27 11:00:59 +020089 fqdn_len = strlen(fqdn);
90 hostname_dn_len = dns_str_to_dn_label(fqdn, fqdn_len + 1, trash.str, trash.size);
91 if (hostname_dn_len == -1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +010092 ha_alert("config : %s '%s', server '%s': failed to parse FQDN '%s'\n",
93 proxy_type_str(px), px->id, srv->id, fqdn);
Christopher Faulet67957bd2017-09-27 11:00:59 +020094 goto err;
Baptiste Assmann201c07f2017-05-22 15:17:15 +020095 }
96
Christopher Faulet67957bd2017-09-27 11:00:59 +020097 if ((srvrq = calloc(1, sizeof(*srvrq))) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +010098 ha_alert("config : %s '%s', server '%s': out of memory\n",
99 proxy_type_str(px), px->id, srv->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200100 goto err;
101 }
102 srvrq->obj_type = OBJ_TYPE_SRVRQ;
103 srvrq->proxy = px;
104 srvrq->name = strdup(fqdn);
105 srvrq->hostname_dn = strdup(trash.str);
106 srvrq->hostname_dn_len = hostname_dn_len;
107 if (!srvrq->name || !srvrq->hostname_dn) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100108 ha_alert("config : %s '%s', server '%s': out of memory\n",
109 proxy_type_str(px), px->id, srv->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200110 goto err;
111 }
112 LIST_ADDQ(&dns_srvrq_list, &srvrq->list);
113 return srvrq;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200114
Christopher Faulet67957bd2017-09-27 11:00:59 +0200115 err:
116 if (srvrq) {
117 free(srvrq->name);
118 free(srvrq->hostname_dn);
119 free(srvrq);
120 }
121 return NULL;
122}
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200123
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200124
Christopher Faulet67957bd2017-09-27 11:00:59 +0200125/* 2 bytes random generator to generate DNS query ID */
126static inline uint16_t dns_rnd16(void)
127{
Christopher Fauletb2812a62017-10-04 16:17:58 +0200128 if (!dns_query_id_seed)
129 dns_query_id_seed = now_ms;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200130 dns_query_id_seed ^= dns_query_id_seed << 13;
131 dns_query_id_seed ^= dns_query_id_seed >> 7;
132 dns_query_id_seed ^= dns_query_id_seed << 17;
133 return dns_query_id_seed;
134}
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200135
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200136
Christopher Faulet67957bd2017-09-27 11:00:59 +0200137static inline int dns_resolution_timeout(struct dns_resolution *res)
138{
139 switch (res->status) {
140 case RSLV_STATUS_VALID: return res->resolvers->hold.valid;
141 default: return res->resolvers->timeout.resolve;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200142 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200143}
144
Christopher Faulet67957bd2017-09-27 11:00:59 +0200145/* Updates a resolvers' task timeout for next wake up and queue it */
146static void dns_update_resolvers_timeout(struct dns_resolvers *resolvers)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200147{
Christopher Faulet67957bd2017-09-27 11:00:59 +0200148 struct dns_resolution *res;
149 int next;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200150
Christopher Faulet67957bd2017-09-27 11:00:59 +0200151 next = tick_add(now_ms, resolvers->timeout.resolve);
152 if (!LIST_ISEMPTY(&resolvers->resolutions.curr)) {
153 res = LIST_NEXT(&resolvers->resolutions.curr, struct dns_resolution *, list);
154 next = MIN(next, tick_add(res->last_query, resolvers->timeout.retry));
155 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200156
Christopher Faulet67957bd2017-09-27 11:00:59 +0200157 list_for_each_entry(res, &resolvers->resolutions.wait, list)
158 next = MIN(next, tick_add(res->last_resolution, dns_resolution_timeout(res)));
Baptiste Assmann325137d2015-04-13 23:40:55 +0200159
Christopher Faulet67957bd2017-09-27 11:00:59 +0200160 resolvers->t->expire = next;
161 task_queue(resolvers->t);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200162}
163
Christopher Faulet67957bd2017-09-27 11:00:59 +0200164/* Opens an UDP socket on the namesaver's IP/Port, if required. Returns 0 on
165 * success, -1 otherwise.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200166 */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200167static int dns_connect_namesaver(struct dns_nameserver *ns)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200168{
Christopher Faulet67957bd2017-09-27 11:00:59 +0200169 struct dgram_conn *dgram = ns->dgram;
170 int fd;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200171
Christopher Faulet67957bd2017-09-27 11:00:59 +0200172 /* Already connected */
173 if (dgram->t.sock.fd != -1)
174 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200175
Christopher Faulet67957bd2017-09-27 11:00:59 +0200176 /* Create an UDP socket and connect it on the nameserver's IP/Port */
177 if ((fd = socket(ns->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
178 send_log(NULL, LOG_WARNING,
179 "DNS : resolvers '%s': can't create socket for nameserver '%s'.\n",
180 ns->resolvers->id, ns->id);
181 return -1;
182 }
183 if (connect(fd, (struct sockaddr*)&ns->addr, get_addr_len(&ns->addr)) == -1) {
184 send_log(NULL, LOG_WARNING,
185 "DNS : resolvers '%s': can't connect socket for nameserver '%s'.\n",
186 ns->resolvers->id, ns->id);
187 close(fd);
188 return -1;
189 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200190
Christopher Faulet67957bd2017-09-27 11:00:59 +0200191 /* Make the socket non blocking */
192 fcntl(fd, F_SETFL, O_NONBLOCK);
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200193
Christopher Faulet67957bd2017-09-27 11:00:59 +0200194 /* Add the fd in the fd list and update its parameters */
195 dgram->t.sock.fd = fd;
196 fdtab[fd].owner = dgram;
197 fdtab[fd].iocb = dgram_fd_handler;
Christopher Faulet36716a72017-05-30 11:07:16 +0200198 fd_insert(fd, (unsigned long)-1);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200199 fd_want_recv(fd);
200 return 0;
201}
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200202
Christopher Faulet67957bd2017-09-27 11:00:59 +0200203/* Forges a DNS query. It needs the following information from the caller:
204 * - <query_id> : the DNS query id corresponding to this query
205 * - <query_type> : DNS_RTYPE_* request DNS record type (A, AAAA, ANY...)
206 * - <hostname_dn> : hostname in domain name format
207 * - <hostname_dn_len> : length of <hostname_dn>
208 *
209 * To store the query, the caller must pass a buffer <buf> and its size
210 * <bufsize>. It returns the number of written bytes in success, -1 if <buf> is
211 * too short.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200212 */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200213static int dns_build_query(int query_id, int query_type, unsigned int accepted_payload_size,
214 char *hostname_dn, int hostname_dn_len, char *buf, int bufsize)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200215{
Christopher Faulet67957bd2017-09-27 11:00:59 +0200216 struct dns_header dns_hdr;
217 struct dns_question qinfo;
218 struct dns_additional_record edns;
219 char *p = buf;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +0200220
Christopher Faulet67957bd2017-09-27 11:00:59 +0200221 if (sizeof(dns_hdr) + sizeof(qinfo) + sizeof(edns) + hostname_dn_len >= bufsize)
222 return -1;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +0200223
Christopher Faulet67957bd2017-09-27 11:00:59 +0200224 memset(buf, 0, bufsize);
Baptiste Assmannfa4a6632017-05-04 09:05:00 +0200225
Christopher Faulet67957bd2017-09-27 11:00:59 +0200226 /* Set dns query headers */
227 dns_hdr.id = (unsigned short) htons(query_id);
228 dns_hdr.flags = htons(0x0100); /* qr=0, opcode=0, aa=0, tc=0, rd=1, ra=0, z=0, rcode=0 */
229 dns_hdr.qdcount = htons(1); /* 1 question */
230 dns_hdr.ancount = 0;
231 dns_hdr.nscount = 0;
232 dns_hdr.arcount = htons(1);
233 memcpy(p, &dns_hdr, sizeof(dns_hdr));
234 p += sizeof(dns_hdr);
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200235
Christopher Faulet67957bd2017-09-27 11:00:59 +0200236 /* Set up query hostname */
237 memcpy(p, hostname_dn, hostname_dn_len);
238 p += hostname_dn_len;
239 *p++ = 0;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200240
Christopher Faulet67957bd2017-09-27 11:00:59 +0200241 /* Set up query info (type and class) */
242 qinfo.qtype = htons(query_type);
243 qinfo.qclass = htons(DNS_RCLASS_IN);
244 memcpy(p, &qinfo, sizeof(qinfo));
245 p += sizeof(qinfo);
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200246
Christopher Faulet67957bd2017-09-27 11:00:59 +0200247 /* Set the DNS extension */
248 edns.name = 0;
249 edns.type = htons(DNS_RTYPE_OPT);
250 edns.udp_payload_size = htons(accepted_payload_size);
251 edns.extension = 0;
252 edns.data_length = 0;
253 memcpy(p, &edns, sizeof(edns));
254 p += sizeof(edns);
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200255
Christopher Faulet67957bd2017-09-27 11:00:59 +0200256 return (p - buf);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200257}
258
Christopher Faulet67957bd2017-09-27 11:00:59 +0200259/* Sends a DNS query to resolvers associated to a resolution. It returns 0 on
260 * success, -1 otherwise.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200261 */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200262static int dns_send_query(struct dns_resolution *resolution)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200263{
Christopher Faulet67957bd2017-09-27 11:00:59 +0200264 struct dns_resolvers *resolvers = resolution->resolvers;
265 struct dns_nameserver *ns;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200266
Christopher Faulet67957bd2017-09-27 11:00:59 +0200267 list_for_each_entry(ns, &resolvers->nameservers, list) {
268 int fd = ns->dgram->t.sock.fd;
269 if (fd == -1) {
270 if (dns_connect_namesaver(ns) == -1)
271 continue;
272 fd = ns->dgram->t.sock.fd;
273 resolvers->nb_nameservers++;
274 }
275 fd_want_send(fd);
276 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200277
Christopher Faulet67957bd2017-09-27 11:00:59 +0200278 /* Update resolution */
279 resolution->nb_queries = 0;
280 resolution->nb_responses = 0;
281 resolution->last_query = now_ms;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200282
Christopher Faulet67957bd2017-09-27 11:00:59 +0200283 /* Push the resolution at the end of the active list */
284 LIST_DEL(&resolution->list);
285 LIST_ADDQ(&resolvers->resolutions.curr, &resolution->list);
286 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200287}
288
Christopher Faulet67957bd2017-09-27 11:00:59 +0200289/* Prepares and sends a DNS resolution. It returns 1 if the query was sent, 0 if
290 * skipped and -1 if an error occurred.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200291 */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200292static int
293dns_run_resolution(struct dns_resolution *resolution)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200294{
Christopher Faulet67957bd2017-09-27 11:00:59 +0200295 struct dns_resolvers *resolvers = resolution->resolvers;
296 int query_id, i;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200297
Christopher Faulet67957bd2017-09-27 11:00:59 +0200298 /* Avoid sending requests for resolutions that don't yet have an
299 * hostname, ie resolutions linked to servers that do not yet have an
300 * fqdn */
301 if (!resolution->hostname_dn)
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200302 return 0;
303
Christopher Faulet67957bd2017-09-27 11:00:59 +0200304 /* Check if a resolution has already been started for this server return
305 * directly to avoid resolution pill up. */
306 if (resolution->step != RSLV_STEP_NONE)
307 return 0;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200308
Christopher Faulet67957bd2017-09-27 11:00:59 +0200309 /* Generates a new query id. We try at most 100 times to find a free
310 * query id */
311 for (i = 0; i < 100; ++i) {
312 query_id = dns_rnd16();
313 if (!eb32_lookup(&resolvers->query_ids, query_id))
Olivier Houchard8da5f982017-08-04 18:35:36 +0200314 break;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200315 query_id = -1;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200316 }
Christopher Faulet67957bd2017-09-27 11:00:59 +0200317 if (query_id == -1) {
318 send_log(NULL, LOG_NOTICE,
319 "could not generate a query id for %s, in resolvers %s.\n",
320 resolution->hostname_dn, resolvers->id);
321 return -1;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200322 }
323
Christopher Faulet67957bd2017-09-27 11:00:59 +0200324 /* Update resolution parameters */
325 resolution->query_id = query_id;
326 resolution->qid.key = query_id;
327 resolution->step = RSLV_STEP_RUNNING;
328 resolution->query_type = resolution->prefered_query_type;
329 resolution->try = resolvers->resolve_retries;
330 eb32_insert(&resolvers->query_ids, &resolution->qid);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200331
Christopher Faulet67957bd2017-09-27 11:00:59 +0200332 /* Send the DNS query */
333 resolution->try -= 1;
334 dns_send_query(resolution);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200335 return 1;
336}
337
Christopher Faulet67957bd2017-09-27 11:00:59 +0200338/* Performs a name resolution for the requester <req> */
339void dns_trigger_resolution(struct dns_requester *req)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200340{
Christopher Faulet67957bd2017-09-27 11:00:59 +0200341 struct dns_resolvers *resolvers;
342 struct dns_resolution *res;
343 int exp;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200344
Christopher Faulet67957bd2017-09-27 11:00:59 +0200345 if (!req || !req->resolution)
346 return;
347 res = req->resolution;
348 resolvers = res->resolvers;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200349
Christopher Faulet67957bd2017-09-27 11:00:59 +0200350 /* The resolution must not be triggered yet. Use the cached response, if
351 * valid */
352 exp = tick_add(res->last_resolution, resolvers->hold.valid);
353 if (res->status == RSLV_STATUS_VALID &&
354 tick_isset(res->last_resolution) && !tick_is_expired(exp, now_ms))
355 req->requester_cb(req, NULL);
356 else
357 dns_run_resolution(res);
358}
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200359
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200360
Christopher Faulet67957bd2017-09-27 11:00:59 +0200361/* Resets some resolution parameters to initial values and also delete the query
362 * ID from the resolver's tree.
363 */
364static void dns_reset_resolution(struct dns_resolution *resolution)
365{
366 /* update resolution status */
367 resolution->step = RSLV_STEP_NONE;
368 resolution->try = 0;
369 resolution->last_resolution = now_ms;
370 resolution->nb_queries = 0;
371 resolution->nb_responses = 0;
372 resolution->query_type = resolution->prefered_query_type;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200373
Christopher Faulet67957bd2017-09-27 11:00:59 +0200374 /* clean up query id */
375 eb32_delete(&resolution->qid);
376 resolution->query_id = 0;
377 resolution->qid.key = 0;
378}
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200379
Christopher Faulet67957bd2017-09-27 11:00:59 +0200380/* Returns the query id contained in a DNS response */
381static inline unsigned short dns_response_get_query_id(unsigned char *resp)
382{
383 return resp[0] * 256 + resp[1];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200384}
385
Christopher Faulet67957bd2017-09-27 11:00:59 +0200386
387/* Analyses, re-builds and copies the name <name> from the DNS response packet
388 * <buffer>. <name> must point to the 'data_len' information or pointer 'c0'
389 * for compressed data. The result is copied into <dest>, ensuring we don't
390 * overflow using <dest_len> Returns the number of bytes the caller can move
391 * forward. If 0 it means an error occurred while parsing the name. <offset> is
392 * the number of bytes the caller could move forward.
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200393 */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200394int dns_read_name(unsigned char *buffer, unsigned char *bufend,
395 unsigned char *name, char *destination, int dest_len,
396 int *offset)
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200397{
398 int nb_bytes = 0, n = 0;
399 int label_len;
400 unsigned char *reader = name;
401 char *dest = destination;
402
403 while (1) {
Christopher Faulet67957bd2017-09-27 11:00:59 +0200404 /* Name compression is in use */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200405 if ((*reader & 0xc0) == 0xc0) {
Christopher Faulet67957bd2017-09-27 11:00:59 +0200406 /* Must point BEFORE current position */
407 if ((buffer + reader[1]) > reader)
408 goto err;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200409
Christopher Faulet67957bd2017-09-27 11:00:59 +0200410 n = dns_read_name(buffer, bufend, buffer + reader[1],
411 dest, dest_len - nb_bytes, offset);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200412 if (n == 0)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200413 goto err;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200414
Christopher Faulet67957bd2017-09-27 11:00:59 +0200415 dest += n;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200416 nb_bytes += n;
417 goto out;
418 }
419
420 label_len = *reader;
421 if (label_len == 0)
422 goto out;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200423
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200424 /* Check if:
425 * - we won't read outside the buffer
426 * - there is enough place in the destination
427 */
428 if ((reader + label_len >= bufend) || (nb_bytes + label_len >= dest_len))
Christopher Faulet67957bd2017-09-27 11:00:59 +0200429 goto err;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200430
431 /* +1 to take label len + label string */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200432 label_len++;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200433
434 memcpy(dest, reader, label_len);
435
Christopher Faulet67957bd2017-09-27 11:00:59 +0200436 dest += label_len;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200437 nb_bytes += label_len;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200438 reader += label_len;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200439 }
440
Christopher Faulet67957bd2017-09-27 11:00:59 +0200441 out:
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200442 /* offset computation:
443 * parse from <name> until finding either NULL or a pointer "c0xx"
444 */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200445 reader = name;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200446 *offset = 0;
447 while (reader < bufend) {
448 if ((reader[0] & 0xc0) == 0xc0) {
449 *offset += 2;
450 break;
451 }
452 else if (*reader == 0) {
453 *offset += 1;
454 break;
455 }
456 *offset += 1;
457 ++reader;
458 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200459 return nb_bytes;
460
Christopher Faulet67957bd2017-09-27 11:00:59 +0200461 err:
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200462 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200463}
464
Christopher Faulet67957bd2017-09-27 11:00:59 +0200465/* Checks for any obsolete record, also identify any SRV request, and try to
466 * find a corresponding server.
467*/
468static void dns_check_dns_response(struct dns_resolution *res)
469{
470 struct dns_resolvers *resolvers = res->resolvers;
471 struct dns_requester *req, *reqback;
472 struct dns_answer_item *item, *itemback;
473 struct server *srv;
474 struct dns_srvrq *srvrq;
475
476 list_for_each_entry_safe(item, itemback, &res->response.answer_list, list) {
477
478 /* Remove obsolete items */
479 if ((item->last_seen + resolvers->hold.obsolete / 1000) < now.tv_sec) {
480 if (item->type != DNS_RTYPE_SRV)
481 goto rm_obselete_item;
482
483 list_for_each_entry_safe(req, reqback, &res->requesters, list) {
484 if ((srvrq = objt_dns_srvrq(req->owner)) == NULL)
485 continue;
486
487 /* Remove any associated server */
488 for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100489 HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200490 if (srv->srvrq == srvrq && srv->svc_port == item->port &&
491 item->data_len == srv->hostname_dn_len &&
492 !memcmp(srv->hostname_dn, item->target, item->data_len)) {
493 snr_update_srv_status(srv, 1);
494 free(srv->hostname);
495 free(srv->hostname_dn);
496 srv->hostname = NULL;
497 srv->hostname_dn = NULL;
498 srv->hostname_dn_len = 0;
499 dns_unlink_resolution(srv->dns_requester);
500 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100501 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200502 }
503 }
504
505 rm_obselete_item:
506 LIST_DEL(&item->list);
Willy Tarreaubafbe012017-11-24 17:34:44 +0100507 pool_free(dns_answer_item_pool, item);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200508 continue;
509 }
510
511 if (item->type != DNS_RTYPE_SRV)
512 continue;
513
514 /* Now process SRV records */
515 list_for_each_entry_safe(req, reqback, &res->requesters, list) {
516 if ((srvrq = objt_dns_srvrq(req->owner)) == NULL)
517 continue;
518
519 /* Check if a server already uses that hostname */
520 for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100521 HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200522 if (srv->srvrq == srvrq && srv->svc_port == item->port &&
523 item->data_len == srv->hostname_dn_len &&
524 !memcmp(srv->hostname_dn, item->target, item->data_len)) {
525 if (srv->uweight != item->weight) {
526 char weight[9];
527
528 snprintf(weight, sizeof(weight), "%d", item->weight);
529 server_parse_weight_change_request(srv, weight);
530 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100531 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200532 break;
533 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100534 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200535 }
536 if (srv)
537 continue;
538
539 /* If not, try to find a server with undefined hostname */
540 for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100541 HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200542 if (srv->srvrq == srvrq && !srv->hostname_dn)
543 break;
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100544 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200545 }
546 /* And update this server, if found */
547 if (srv) {
548 const char *msg = NULL;
549 char weight[9];
550 char hostname[DNS_MAX_NAME_SIZE];
551
552 if (dns_dn_label_to_str(item->target, item->data_len+1,
Olivier Houchard55dcdf42017-11-06 15:15:04 +0100553 hostname, DNS_MAX_NAME_SIZE) == -1) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100554 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200555 continue;
Olivier Houchard55dcdf42017-11-06 15:15:04 +0100556 }
Olivier Houchardd16bfe62017-10-31 15:21:19 +0100557 msg = update_server_fqdn(srv, hostname, "SRV record", 1);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200558 if (msg)
559 send_log(srv->proxy, LOG_NOTICE, "%s", msg);
560
561 srv->svc_port = item->port;
562 srv->flags &= ~SRV_F_MAPPORTS;
563 if ((srv->check.state & CHK_ST_CONFIGURED) &&
564 !(srv->flags & SRV_F_CHECKPORT))
565 srv->check.port = item->port;
566 snprintf(weight, sizeof(weight), "%d", item->weight);
567 server_parse_weight_change_request(srv, weight);
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100568 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200569 }
570 }
571 }
572}
573
574/* Validates that the buffer DNS response provided in <resp> and finishing
575 * before <bufend> is valid from a DNS protocol point of view.
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200576 *
Christopher Faulet67957bd2017-09-27 11:00:59 +0200577 * The result is stored in <resolution>' response, buf_response,
578 * response_query_records and response_answer_records members.
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200579 *
580 * This function returns one of the DNS_RESP_* code to indicate the type of
581 * error found.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200582 */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200583static int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend,
584 struct dns_resolution *resolution, int max_answer_records)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200585{
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200586 unsigned char *reader;
587 char *previous_dname, tmpname[DNS_MAX_NAME_SIZE];
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200588 int len, flags, offset;
589 int dns_query_record_id;
Baptiste Assmann69fce672017-05-04 08:37:45 +0200590 int nb_saved_records;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200591 struct dns_query_item *dns_query;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200592 struct dns_answer_item *dns_answer_record, *tmp_record;
Baptiste Assmann729c9012017-05-22 15:13:10 +0200593 struct dns_response_packet *dns_p;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200594 int i, found = 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200595
Christopher Faulet67957bd2017-09-27 11:00:59 +0200596 reader = resp;
597 len = 0;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200598 previous_dname = NULL;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200599 dns_query = NULL;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200600
Christopher Faulet67957bd2017-09-27 11:00:59 +0200601 /* Initialization of response buffer and structure */
Baptiste Assmann729c9012017-05-22 15:13:10 +0200602 dns_p = &resolution->response;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200603
604 /* query id */
605 if (reader + 2 >= bufend)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200606 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200607 dns_p->header.id = reader[0] * 256 + reader[1];
608 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200609
Christopher Faulet67957bd2017-09-27 11:00:59 +0200610 /* Flags and rcode are stored over 2 bytes
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200611 * First byte contains:
612 * - response flag (1 bit)
613 * - opcode (4 bits)
614 * - authoritative (1 bit)
615 * - truncated (1 bit)
616 * - recursion desired (1 bit)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200617 */
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200618 if (reader + 2 >= bufend)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200619 return DNS_RESP_INVALID;
620
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200621 flags = reader[0] * 256 + reader[1];
622
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200623 if ((flags & DNS_FLAG_REPLYCODE) != DNS_RCODE_NO_ERROR) {
624 if ((flags & DNS_FLAG_REPLYCODE) == DNS_RCODE_NX_DOMAIN)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200625 return DNS_RESP_NX_DOMAIN;
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200626 else if ((flags & DNS_FLAG_REPLYCODE) == DNS_RCODE_REFUSED)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200627 return DNS_RESP_REFUSED;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200628 return DNS_RESP_ERROR;
629 }
630
Christopher Faulet67957bd2017-09-27 11:00:59 +0200631 /* Move forward 2 bytes for flags */
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200632 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200633
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200634 /* 2 bytes for question count */
635 if (reader + 2 >= bufend)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200636 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200637 dns_p->header.qdcount = reader[0] * 256 + reader[1];
Christopher Faulet67957bd2017-09-27 11:00:59 +0200638 /* (for now) we send one query only, so we expect only one in the
639 * response too */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200640 if (dns_p->header.qdcount != 1)
641 return DNS_RESP_QUERY_COUNT_ERROR;
642 if (dns_p->header.qdcount > DNS_MAX_QUERY_RECORDS)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200643 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200644 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200645
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200646 /* 2 bytes for answer count */
647 if (reader + 2 >= bufend)
648 return DNS_RESP_INVALID;
649 dns_p->header.ancount = reader[0] * 256 + reader[1];
650 if (dns_p->header.ancount == 0)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200651 return DNS_RESP_ANCOUNT_ZERO;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200652 /* Check if too many records are announced */
Baptiste Assmann9d8dbbc2017-08-18 23:35:08 +0200653 if (dns_p->header.ancount > max_answer_records)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200654 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200655 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200656
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200657 /* 2 bytes authority count */
658 if (reader + 2 >= bufend)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200659 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200660 dns_p->header.nscount = reader[0] * 256 + reader[1];
661 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200662
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200663 /* 2 bytes additional count */
664 if (reader + 2 >= bufend)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200665 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200666 dns_p->header.arcount = reader[0] * 256 + reader[1];
667 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200668
Christopher Faulet67957bd2017-09-27 11:00:59 +0200669 /* Parsing dns queries */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200670 LIST_INIT(&dns_p->query_list);
671 for (dns_query_record_id = 0; dns_query_record_id < dns_p->header.qdcount; dns_query_record_id++) {
Christopher Faulet67957bd2017-09-27 11:00:59 +0200672 /* Use next pre-allocated dns_query_item after ensuring there is
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200673 * still one available.
Christopher Faulet67957bd2017-09-27 11:00:59 +0200674 * It's then added to our packet query list. */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200675 if (dns_query_record_id > DNS_MAX_QUERY_RECORDS)
676 return DNS_RESP_INVALID;
Baptiste Assmann729c9012017-05-22 15:13:10 +0200677 dns_query = &resolution->response_query_records[dns_query_record_id];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200678 LIST_ADDQ(&dns_p->query_list, &dns_query->list);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200679
Christopher Faulet67957bd2017-09-27 11:00:59 +0200680 /* Name is a NULL terminated string in our case, since we have
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200681 * one query per response and the first one can't be compressed
Christopher Faulet67957bd2017-09-27 11:00:59 +0200682 * (using the 0x0c format) */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200683 offset = 0;
684 len = dns_read_name(resp, bufend, reader, dns_query->name, DNS_MAX_NAME_SIZE, &offset);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200685
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200686 if (len == 0)
687 return DNS_RESP_INVALID;
688
689 reader += offset;
690 previous_dname = dns_query->name;
691
692 /* move forward 2 bytes for question type */
693 if (reader + 2 >= bufend)
694 return DNS_RESP_INVALID;
695 dns_query->type = reader[0] * 256 + reader[1];
696 reader += 2;
697
698 /* move forward 2 bytes for question class */
699 if (reader + 2 >= bufend)
700 return DNS_RESP_INVALID;
701 dns_query->class = reader[0] * 256 + reader[1];
702 reader += 2;
703 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200704
Baptiste Assmann251abb92017-08-11 09:58:27 +0200705 /* TRUNCATED flag must be checked after we could read the query type
Christopher Faulet67957bd2017-09-27 11:00:59 +0200706 * because a TRUNCATED SRV query type response can still be exploited */
Baptiste Assmann251abb92017-08-11 09:58:27 +0200707 if (dns_query->type != DNS_RTYPE_SRV && flags & DNS_FLAG_TRUNCATED)
708 return DNS_RESP_TRUNCATED;
709
Baptiste Assmann325137d2015-04-13 23:40:55 +0200710 /* now parsing response records */
Baptiste Assmann69fce672017-05-04 08:37:45 +0200711 nb_saved_records = 0;
Olivier Houchard8da5f982017-08-04 18:35:36 +0200712 for (i = 0; i < dns_p->header.ancount; i++) {
Baptiste Assmann325137d2015-04-13 23:40:55 +0200713 if (reader >= bufend)
714 return DNS_RESP_INVALID;
715
Willy Tarreaubafbe012017-11-24 17:34:44 +0100716 dns_answer_record = pool_alloc(dns_answer_item_pool);
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200717 if (dns_answer_record == NULL)
718 return (DNS_RESP_INVALID);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200719
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200720 offset = 0;
721 len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200722
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200723 if (len == 0) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100724 pool_free(dns_answer_item_pool, dns_answer_record);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200725 return DNS_RESP_INVALID;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200726 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200727
Christopher Faulet67957bd2017-09-27 11:00:59 +0200728 /* Check if the current record dname is valid. previous_dname
729 * points either to queried dname or last CNAME target */
Baptiste Assmannddc8ce62017-08-11 10:31:22 +0200730 if (dns_query->type != DNS_RTYPE_SRV && memcmp(previous_dname, tmpname, len) != 0) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100731 pool_free(dns_answer_item_pool, dns_answer_record);
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200732 if (i == 0) {
Christopher Faulet67957bd2017-09-27 11:00:59 +0200733 /* First record, means a mismatch issue between
734 * queried dname and dname found in the first
735 * record */
Baptiste Assmann325137d2015-04-13 23:40:55 +0200736 return DNS_RESP_INVALID;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200737 }
738 else {
739 /* If not the first record, this means we have a
740 * CNAME resolution error */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200741 return DNS_RESP_CNAME_ERROR;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200742 }
743
Baptiste Assmann325137d2015-04-13 23:40:55 +0200744 }
745
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200746 memcpy(dns_answer_record->name, tmpname, len);
747 dns_answer_record->name[len] = 0;
Baptiste Assmann2359ff12015-08-07 11:24:05 +0200748
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200749 reader += offset;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200750 if (reader >= bufend) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100751 pool_free(dns_answer_item_pool, dns_answer_record);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200752 return DNS_RESP_INVALID;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200753 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200754
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200755 /* 2 bytes for record type (A, AAAA, CNAME, etc...) */
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200756 if (reader + 2 > bufend) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100757 pool_free(dns_answer_item_pool, dns_answer_record);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200758 return DNS_RESP_INVALID;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200759 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200760 dns_answer_record->type = reader[0] * 256 + reader[1];
761 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200762
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200763 /* 2 bytes for class (2) */
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200764 if (reader + 2 > bufend) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100765 pool_free(dns_answer_item_pool, dns_answer_record);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200766 return DNS_RESP_INVALID;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200767 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200768 dns_answer_record->class = reader[0] * 256 + reader[1];
Baptiste Assmann325137d2015-04-13 23:40:55 +0200769 reader += 2;
770
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200771 /* 4 bytes for ttl (4) */
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200772 if (reader + 4 > bufend) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100773 pool_free(dns_answer_item_pool, dns_answer_record);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200774 return DNS_RESP_INVALID;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200775 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200776 dns_answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536
777 + reader[2] * 256 + reader[3];
778 reader += 4;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200779
Christopher Faulet67957bd2017-09-27 11:00:59 +0200780 /* Now reading data len */
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200781 if (reader + 2 > bufend) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100782 pool_free(dns_answer_item_pool, dns_answer_record);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200783 return DNS_RESP_INVALID;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200784 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200785 dns_answer_record->data_len = reader[0] * 256 + reader[1];
Baptiste Assmann325137d2015-04-13 23:40:55 +0200786
Christopher Faulet67957bd2017-09-27 11:00:59 +0200787 /* Move forward 2 bytes for data len */
Baptiste Assmann325137d2015-04-13 23:40:55 +0200788 reader += 2;
789
Christopher Faulet67957bd2017-09-27 11:00:59 +0200790 /* Analyzing record content */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200791 switch (dns_answer_record->type) {
Baptiste Assmann325137d2015-04-13 23:40:55 +0200792 case DNS_RTYPE_A:
793 /* ipv4 is stored on 4 bytes */
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200794 if (dns_answer_record->data_len != 4) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100795 pool_free(dns_answer_item_pool, dns_answer_record);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200796 return DNS_RESP_INVALID;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200797 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200798 dns_answer_record->address.sa_family = AF_INET;
799 memcpy(&(((struct sockaddr_in *)&dns_answer_record->address)->sin_addr),
800 reader, dns_answer_record->data_len);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200801 break;
802
803 case DNS_RTYPE_CNAME:
Christopher Faulet67957bd2017-09-27 11:00:59 +0200804 /* Check if this is the last record and update the caller about the status:
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200805 * no IP could be found and last record was a CNAME. Could be triggered
806 * by a wrong query type
807 *
Christopher Faulet67957bd2017-09-27 11:00:59 +0200808 * + 1 because dns_answer_record_id starts at 0
809 * while number of answers is an integer and
810 * starts at 1.
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200811 */
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200812 if (i + 1 == dns_p->header.ancount) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100813 pool_free(dns_answer_item_pool, dns_answer_record);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200814 return DNS_RESP_CNAME_ERROR;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200815 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200816
817 offset = 0;
818 len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200819 if (len == 0) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100820 pool_free(dns_answer_item_pool, dns_answer_record);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200821 return DNS_RESP_INVALID;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200822 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200823
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200824 memcpy(dns_answer_record->target, tmpname, len);
825 dns_answer_record->target[len] = 0;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200826 previous_dname = dns_answer_record->target;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200827 break;
828
Olivier Houchard8da5f982017-08-04 18:35:36 +0200829
830 case DNS_RTYPE_SRV:
Christopher Faulet67957bd2017-09-27 11:00:59 +0200831 /* Answer must contain :
Olivier Houchard8da5f982017-08-04 18:35:36 +0200832 * - 2 bytes for the priority
833 * - 2 bytes for the weight
834 * - 2 bytes for the port
835 * - the target hostname
836 */
837 if (dns_answer_record->data_len <= 6) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100838 pool_free(dns_answer_item_pool, dns_answer_record);
Olivier Houchard8da5f982017-08-04 18:35:36 +0200839 return DNS_RESP_INVALID;
840 }
Willy Tarreaud5370e12017-09-19 14:59:52 +0200841 dns_answer_record->priority = read_n16(reader);
Olivier Houchard8da5f982017-08-04 18:35:36 +0200842 reader += sizeof(uint16_t);
Willy Tarreaud5370e12017-09-19 14:59:52 +0200843 dns_answer_record->weight = read_n16(reader);
Olivier Houchard8da5f982017-08-04 18:35:36 +0200844 reader += sizeof(uint16_t);
Willy Tarreaud5370e12017-09-19 14:59:52 +0200845 dns_answer_record->port = read_n16(reader);
Olivier Houchard8da5f982017-08-04 18:35:36 +0200846 reader += sizeof(uint16_t);
847 offset = 0;
848 len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
849 if (len == 0) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100850 pool_free(dns_answer_item_pool, dns_answer_record);
Olivier Houchard8da5f982017-08-04 18:35:36 +0200851 return DNS_RESP_INVALID;
852 }
Olivier Houchard8da5f982017-08-04 18:35:36 +0200853 dns_answer_record->data_len = len;
854 memcpy(dns_answer_record->target, tmpname, len);
855 dns_answer_record->target[len] = 0;
856 break;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200857
Baptiste Assmann325137d2015-04-13 23:40:55 +0200858 case DNS_RTYPE_AAAA:
859 /* ipv6 is stored on 16 bytes */
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200860 if (dns_answer_record->data_len != 16) {
Willy Tarreaubafbe012017-11-24 17:34:44 +0100861 pool_free(dns_answer_item_pool, dns_answer_record);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200862 return DNS_RESP_INVALID;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200863 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200864 dns_answer_record->address.sa_family = AF_INET6;
865 memcpy(&(((struct sockaddr_in6 *)&dns_answer_record->address)->sin6_addr),
866 reader, dns_answer_record->data_len);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200867 break;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200868
Baptiste Assmann325137d2015-04-13 23:40:55 +0200869 } /* switch (record type) */
870
Christopher Faulet67957bd2017-09-27 11:00:59 +0200871 /* Increment the counter for number of records saved into our
872 * local response */
873 nb_saved_records++;
Baptiste Assmann69fce672017-05-04 08:37:45 +0200874
Christopher Faulet67957bd2017-09-27 11:00:59 +0200875 /* Move forward dns_answer_record->data_len for analyzing next
876 * record in the response */
877 reader += ((dns_answer_record->type == DNS_RTYPE_SRV)
878 ? offset
879 : dns_answer_record->data_len);
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200880
881 /* Lookup to see if we already had this entry */
Olivier Houchard8da5f982017-08-04 18:35:36 +0200882 found = 0;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200883 list_for_each_entry(tmp_record, &dns_p->answer_list, list) {
884 if (tmp_record->type != dns_answer_record->type)
885 continue;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200886
887 switch(tmp_record->type) {
888 case DNS_RTYPE_A:
889 if (!memcmp(&((struct sockaddr_in *)&dns_answer_record->address)->sin_addr,
890 &((struct sockaddr_in *)&tmp_record->address)->sin_addr,
891 sizeof(in_addr_t)))
892 found = 1;
893 break;
894
895 case DNS_RTYPE_AAAA:
896 if (!memcmp(&((struct sockaddr_in6 *)&dns_answer_record->address)->sin6_addr,
897 &((struct sockaddr_in6 *)&tmp_record->address)->sin6_addr,
898 sizeof(struct in6_addr)))
899 found = 1;
900 break;
901
Olivier Houchard8da5f982017-08-04 18:35:36 +0200902 case DNS_RTYPE_SRV:
903 if (dns_answer_record->data_len == tmp_record->data_len &&
Christopher Faulet67957bd2017-09-27 11:00:59 +0200904 !memcmp(dns_answer_record->target, tmp_record->target, dns_answer_record->data_len) &&
Olivier Houchard8da5f982017-08-04 18:35:36 +0200905 dns_answer_record->port == tmp_record->port) {
906 tmp_record->weight = dns_answer_record->weight;
907 found = 1;
908 }
909 break;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200910
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200911 default:
912 break;
913 }
Christopher Faulet67957bd2017-09-27 11:00:59 +0200914
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200915 if (found == 1)
916 break;
917 }
Christopher Faulet67957bd2017-09-27 11:00:59 +0200918
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200919 if (found == 1) {
920 tmp_record->last_seen = now.tv_sec;
Willy Tarreaubafbe012017-11-24 17:34:44 +0100921 pool_free(dns_answer_item_pool, dns_answer_record);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200922 }
923 else {
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200924 dns_answer_record->last_seen = now.tv_sec;
925 LIST_ADDQ(&dns_p->answer_list, &dns_answer_record->list);
926 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200927 } /* for i 0 to ancount */
928
Christopher Faulet67957bd2017-09-27 11:00:59 +0200929 /* Save the number of records we really own */
Baptiste Assmann69fce672017-05-04 08:37:45 +0200930 dns_p->header.ancount = nb_saved_records;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200931 dns_check_dns_response(resolution);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200932 return DNS_RESP_VALID;
933}
934
Christopher Faulet67957bd2017-09-27 11:00:59 +0200935/* Searches dn_name resolution in resp.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200936 * If existing IP not found, return the first IP matching family_priority,
937 * otherwise, first ip found
938 * The following tasks are the responsibility of the caller:
Baptiste Assmann3cf7f982016-04-17 22:43:26 +0200939 * - <dns_p> contains an error free DNS response
Baptiste Assmann325137d2015-04-13 23:40:55 +0200940 * For both cases above, dns_validate_dns_response is required
941 * returns one of the DNS_UPD_* code
942 */
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100943#define DNS_MAX_IP_REC 20
Baptiste Assmannfb7091e2017-05-03 15:43:12 +0200944int dns_get_ip_from_response(struct dns_response_packet *dns_p,
Baptiste Assmann42746372017-05-03 12:12:02 +0200945 struct dns_options *dns_opts, void *currentip,
Thierry Fournierada34842016-02-17 21:25:09 +0100946 short currentip_sin_family,
Baptiste Assmannfb7091e2017-05-03 15:43:12 +0200947 void **newip, short *newip_sin_family,
948 void *owner)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200949{
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200950 struct dns_answer_item *record;
Thierry Fournierada34842016-02-17 21:25:09 +0100951 int family_priority;
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200952 int currentip_found;
Baptiste Assmann3cf7f982016-04-17 22:43:26 +0200953 unsigned char *newip4, *newip6;
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100954 int currentip_sel;
955 int j;
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100956 int score, max_score;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200957
Christopher Faulet67957bd2017-09-27 11:00:59 +0200958 family_priority = dns_opts->family_prio;
959 *newip = newip4 = newip6 = NULL;
960 currentip_found = 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200961 *newip_sin_family = AF_UNSPEC;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200962 max_score = -1;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200963
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100964 /* Select an IP regarding configuration preference.
965 * Top priority is the prefered network ip version,
966 * second priority is the prefered network.
967 * the last priority is the currently used IP,
968 *
969 * For these three priorities, a score is calculated. The
970 * weight are:
Baptistefc725902016-12-26 23:21:08 +0100971 * 8 - prefered netwok ip version.
972 * 4 - prefered network.
973 * 2 - if the ip in the record is not affected to any other server in the same backend (duplication)
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100974 * 1 - current ip.
975 * The result with the biggest score is returned.
976 */
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200977
978 list_for_each_entry(record, &dns_p->answer_list, list) {
979 void *ip;
980 unsigned char ip_type;
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100981
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200982 if (record->type == DNS_RTYPE_A) {
983 ip = &(((struct sockaddr_in *)&record->address)->sin_addr);
984 ip_type = AF_INET;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200985 }
986 else if (record->type == DNS_RTYPE_AAAA) {
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200987 ip_type = AF_INET6;
988 ip = &(((struct sockaddr_in6 *)&record->address)->sin6_addr);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200989 }
990 else
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200991 continue;
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100992 score = 0;
993
994 /* Check for prefered ip protocol. */
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200995 if (ip_type == family_priority)
Baptistefc725902016-12-26 23:21:08 +0100996 score += 8;
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100997
998 /* Check for prefered network. */
Baptiste Assmann42746372017-05-03 12:12:02 +0200999 for (j = 0; j < dns_opts->pref_net_nb; j++) {
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001000
1001 /* Compare only the same adresses class. */
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001002 if (dns_opts->pref_net[j].family != ip_type)
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001003 continue;
1004
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001005 if ((ip_type == AF_INET &&
1006 in_net_ipv4(ip,
Baptiste Assmann42746372017-05-03 12:12:02 +02001007 &dns_opts->pref_net[j].mask.in4,
1008 &dns_opts->pref_net[j].addr.in4)) ||
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001009 (ip_type == AF_INET6 &&
1010 in_net_ipv6(ip,
Baptiste Assmann42746372017-05-03 12:12:02 +02001011 &dns_opts->pref_net[j].mask.in6,
1012 &dns_opts->pref_net[j].addr.in6))) {
Baptistefc725902016-12-26 23:21:08 +01001013 score += 4;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001014 break;
1015 }
1016 }
1017
Christopher Faulet67957bd2017-09-27 11:00:59 +02001018 /* Check if the IP found in the record is already affected to a
1019 * member of a group. If yes, the score should be incremented
1020 * by 2. */
1021 if (owner && snr_check_ip_callback(owner, ip, &ip_type))
1022 continue;
1023
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001024 /* Check for current ip matching. */
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001025 if (ip_type == currentip_sin_family &&
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001026 ((currentip_sin_family == AF_INET &&
Christopher Faulet67957bd2017-09-27 11:00:59 +02001027 !memcmp(ip, currentip, 4)) ||
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001028 (currentip_sin_family == AF_INET6 &&
Christopher Faulet67957bd2017-09-27 11:00:59 +02001029 !memcmp(ip, currentip, 16)))) {
1030 score++;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001031 currentip_sel = 1;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001032 }
1033 else
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001034 currentip_sel = 0;
1035
1036 /* Keep the address if the score is better than the previous
Christopher Faulet67957bd2017-09-27 11:00:59 +02001037 * score. The maximum score is 15, if this value is reached, we
1038 * break the parsing. Implicitly, this score is reached the ip
1039 * selected is the current ip. */
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001040 if (score > max_score) {
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001041 if (ip_type == AF_INET)
1042 newip4 = ip;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001043 else
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001044 newip6 = ip;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001045 currentip_found = currentip_sel;
Baptistefc725902016-12-26 23:21:08 +01001046 if (score == 15)
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001047 return DNS_UPD_NO;
1048 max_score = score;
1049 }
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001050 } /* list for each record entries */
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001051
Christopher Faulet67957bd2017-09-27 11:00:59 +02001052 /* No IP found in the response */
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001053 if (!newip4 && !newip6)
Baptiste Assmann0453a1d2015-09-09 00:51:08 +02001054 return DNS_UPD_NO_IP_FOUND;
Baptiste Assmann0453a1d2015-09-09 00:51:08 +02001055
Christopher Faulet67957bd2017-09-27 11:00:59 +02001056 /* Case when the caller looks first for an IPv4 address */
Baptiste Assmann325137d2015-04-13 23:40:55 +02001057 if (family_priority == AF_INET) {
1058 if (newip4) {
1059 *newip = newip4;
1060 *newip_sin_family = AF_INET;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001061 }
1062 else if (newip6) {
1063 *newip = newip6;
1064 *newip_sin_family = AF_INET6;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001065 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001066 if (!currentip_found)
1067 goto not_found;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001068 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001069 /* Case when the caller looks first for an IPv6 address */
Baptiste Assmann325137d2015-04-13 23:40:55 +02001070 else if (family_priority == AF_INET6) {
1071 if (newip6) {
1072 *newip = newip6;
1073 *newip_sin_family = AF_INET6;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001074 }
1075 else if (newip4) {
1076 *newip = newip4;
1077 *newip_sin_family = AF_INET;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001078 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001079 if (!currentip_found)
1080 goto not_found;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001081 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001082 /* Case when the caller have no preference (we prefer IPv6) */
Baptiste Assmann325137d2015-04-13 23:40:55 +02001083 else if (family_priority == AF_UNSPEC) {
1084 if (newip6) {
1085 *newip = newip6;
1086 *newip_sin_family = AF_INET6;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001087 }
1088 else if (newip4) {
1089 *newip = newip4;
1090 *newip_sin_family = AF_INET;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001091 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001092 if (!currentip_found)
1093 goto not_found;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001094 }
1095
Christopher Faulet67957bd2017-09-27 11:00:59 +02001096 /* No reason why we should change the server's IP address */
Baptiste Assmann325137d2015-04-13 23:40:55 +02001097 return DNS_UPD_NO;
Baptiste Assmann8ea0bcc2017-05-04 08:24:11 +02001098
Christopher Faulet67957bd2017-09-27 11:00:59 +02001099 not_found:
Baptiste Assmann8ea0bcc2017-05-04 08:24:11 +02001100 list_for_each_entry(record, &dns_p->answer_list, list) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02001101 /* Move the first record to the end of the list, for internal
1102 * round robin */
1103 LIST_DEL(&record->list);
1104 LIST_ADDQ(&dns_p->answer_list, &record->list);
1105 break;
Baptiste Assmann8ea0bcc2017-05-04 08:24:11 +02001106 }
1107 return DNS_UPD_SRVIP_NOT_FOUND;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001108}
1109
Christopher Faulet67957bd2017-09-27 11:00:59 +02001110/* Turns a domain name label into a string.
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001111 *
Christopher Faulet67957bd2017-09-27 11:00:59 +02001112 * <dn> must be a null-terminated string. <dn_len> must include the terminating
1113 * null byte. <str> must be allocated and its size must be passed in <str_len>.
Baptiste Assmann325137d2015-04-13 23:40:55 +02001114 *
Christopher Faulet67957bd2017-09-27 11:00:59 +02001115 * In case of error, -1 is returned, otherwise, the number of bytes copied in
1116 * <str> (including the terminating null byte).
Baptiste Assmann325137d2015-04-13 23:40:55 +02001117 */
Christopher Faulet67957bd2017-09-27 11:00:59 +02001118int dns_dn_label_to_str(const char *dn, int dn_len, char *str, int str_len)
Baptiste Assmann325137d2015-04-13 23:40:55 +02001119{
Christopher Faulet67957bd2017-09-27 11:00:59 +02001120 char *ptr;
1121 int i, sz;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001122
Christopher Faulet67957bd2017-09-27 11:00:59 +02001123 if (str_len < dn_len - 1)
Baptiste Assmann2af08fe2017-08-14 00:13:01 +02001124 return -1;
1125
Christopher Faulet67957bd2017-09-27 11:00:59 +02001126 ptr = str;
1127 for (i = 0; i < dn_len-1; ++i) {
1128 sz = dn[i];
1129 if (i)
1130 *ptr++ = '.';
1131 memcpy(ptr, dn+i+1, sz);
1132 ptr += sz;
1133 i += sz;
Olivier Houchard8da5f982017-08-04 18:35:36 +02001134 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001135 *ptr++ = '\0';
1136 return (ptr - str);
Olivier Houchard8da5f982017-08-04 18:35:36 +02001137}
1138
Christopher Faulet67957bd2017-09-27 11:00:59 +02001139/* Turns a string into domain name label: www.haproxy.org into 3www7haproxy3org
1140 *
1141 * <str> must be a null-terminated string. <str_len> must include the
1142 * terminating null byte. <dn> buffer must be allocated and its size must be
1143 * passed in <dn_len>.
1144 *
1145 * In case of error, -1 is returned, otherwise, the number of bytes copied in
1146 * <dn> (excluding the terminating null byte).
Baptiste Assmann325137d2015-04-13 23:40:55 +02001147 */
Christopher Faulet67957bd2017-09-27 11:00:59 +02001148int dns_str_to_dn_label(const char *str, int str_len, char *dn, int dn_len)
Baptiste Assmann325137d2015-04-13 23:40:55 +02001149{
Baptiste Assmann325137d2015-04-13 23:40:55 +02001150 int i, offset;
1151
Christopher Faulet67957bd2017-09-27 11:00:59 +02001152 if (dn_len < str_len + 1)
1153 return -1;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001154
Christopher Faulet67957bd2017-09-27 11:00:59 +02001155 /* First byte of dn will be used to store the length of the first
1156 * label */
1157 offset = 0;
1158 for (i = 0; i < str_len; ++i) {
1159 if (str[i] == '.') {
1160 /* 2 or more consecutive dots is invalid */
1161 if (i == offset)
1162 return -1;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001163
Christopher Faulet67957bd2017-09-27 11:00:59 +02001164 dn[offset] = (i - offset);
1165 offset = i+1;
1166 continue;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001167 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001168 dn[i+1] = str[i];
Baptiste Assmann325137d2015-04-13 23:40:55 +02001169 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001170 dn[offset] = (i - offset - 1);
1171 dn[i] = '\0';
1172 return i;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001173}
1174
Christopher Faulet67957bd2017-09-27 11:00:59 +02001175/* Validates host name:
Baptiste Assmann325137d2015-04-13 23:40:55 +02001176 * - total size
1177 * - each label size individually
1178 * returns:
1179 * 0 in case of error. If <err> is not NULL, an error message is stored there.
1180 * 1 when no error. <err> is left unaffected.
1181 */
1182int dns_hostname_validation(const char *string, char **err)
1183{
1184 const char *c, *d;
1185 int i;
1186
1187 if (strlen(string) > DNS_MAX_NAME_SIZE) {
1188 if (err)
1189 *err = DNS_TOO_LONG_FQDN;
1190 return 0;
1191 }
1192
1193 c = string;
1194 while (*c) {
1195 d = c;
1196
1197 i = 0;
1198 while (*d != '.' && *d && i <= DNS_MAX_LABEL_SIZE) {
1199 i++;
1200 if (!((*d == '-') || (*d == '_') ||
1201 ((*d >= 'a') && (*d <= 'z')) ||
1202 ((*d >= 'A') && (*d <= 'Z')) ||
1203 ((*d >= '0') && (*d <= '9')))) {
1204 if (err)
1205 *err = DNS_INVALID_CHARACTER;
1206 return 0;
1207 }
1208 d++;
1209 }
1210
1211 if ((i >= DNS_MAX_LABEL_SIZE) && (d[i] != '.')) {
1212 if (err)
1213 *err = DNS_LABEL_TOO_LONG;
1214 return 0;
1215 }
1216
1217 if (*d == '\0')
1218 goto out;
1219
1220 c = ++d;
1221 }
1222 out:
1223 return 1;
1224}
1225
Christopher Faulet67957bd2017-09-27 11:00:59 +02001226/* Picks up an available resolution from the different resolution list
1227 * associated to a resolvers section, in this order:
1228 * 1. check in resolutions.curr for the same hostname and query_type
1229 * 2. check in resolutions.wait for the same hostname and query_type
1230 * 3. Get a new resolution from resolution pool
1231 *
1232 * Returns an available resolution, NULL if none found.
Baptiste Assmann325137d2015-04-13 23:40:55 +02001233 */
Christopher Faulet67957bd2017-09-27 11:00:59 +02001234static struct dns_resolution *dns_pick_resolution(struct dns_resolvers *resolvers,
1235 char **hostname_dn, int hostname_dn_len,
1236 int query_type)
Baptiste Assmann325137d2015-04-13 23:40:55 +02001237{
Christopher Faulet67957bd2017-09-27 11:00:59 +02001238 struct dns_resolution *res;
1239
1240 if (!*hostname_dn)
1241 goto from_pool;
1242
1243 /* Search for same hostname and query type in resolutions.curr */
1244 list_for_each_entry(res, &resolvers->resolutions.curr, list) {
1245 if (!res->hostname_dn)
1246 continue;
1247 if ((query_type == res->prefered_query_type) &&
1248 hostname_dn_len == res->hostname_dn_len &&
1249 !memcmp(*hostname_dn, res->hostname_dn, hostname_dn_len))
1250 return res;
1251 }
1252
1253 /* Search for same hostname and query type in resolutions.wait */
1254 list_for_each_entry(res, &resolvers->resolutions.wait, list) {
1255 if (!res->hostname_dn)
1256 continue;
1257 if ((query_type == res->prefered_query_type) &&
1258 hostname_dn_len == res->hostname_dn_len &&
1259 !memcmp(*hostname_dn, res->hostname_dn, hostname_dn_len))
1260 return res;
1261 }
1262
1263 from_pool:
1264 /* No resolution could be found, so let's allocate a new one */
Willy Tarreaubafbe012017-11-24 17:34:44 +01001265 res = pool_alloc(dns_resolution_pool);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001266 if (res) {
1267 memset(res, 0, sizeof(*res));
1268 res->resolvers = resolvers;
1269 res->uuid = resolution_uuid;
1270 res->status = RSLV_STATUS_NONE;
1271 res->step = RSLV_STEP_NONE;
1272 res->last_valid = now_ms;
1273
1274 LIST_INIT(&res->requesters);
1275 LIST_INIT(&res->response.answer_list);
1276
1277 res->prefered_query_type = query_type;
1278 res->query_type = query_type;
1279 res->hostname_dn = *hostname_dn;
1280 res->hostname_dn_len = hostname_dn_len;
1281
1282 ++resolution_uuid;
1283
1284 /* Move the resolution to the resolvers wait queue */
1285 LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
1286 }
1287 return res;
1288}
1289
1290/* Releases a resolution from its requester(s) and move it back to the pool */
1291static void dns_free_resolution(struct dns_resolution *resolution)
1292{
1293 struct dns_requester *req, *reqback;
1294
1295 /* clean up configuration */
1296 dns_reset_resolution(resolution);
1297 resolution->hostname_dn = NULL;
1298 resolution->hostname_dn_len = 0;
1299
1300 list_for_each_entry_safe(req, reqback, &resolution->requesters, list) {
1301 LIST_DEL(&req->list);
1302 req->resolution = NULL;
1303 }
1304
1305 LIST_DEL(&resolution->list);
Willy Tarreaubafbe012017-11-24 17:34:44 +01001306 pool_free(dns_resolution_pool, resolution);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001307}
1308
1309/* Links a requester (a server or a dns_srvrq) with a resolution. It returns 0
1310 * on success, -1 otherwise.
1311 */
Olivier Houchard55dcdf42017-11-06 15:15:04 +01001312int dns_link_resolution(void *requester, int requester_type, int requester_locked)
Christopher Faulet67957bd2017-09-27 11:00:59 +02001313{
1314 struct dns_resolution *res = NULL;
1315 struct dns_requester *req;
1316 struct dns_resolvers *resolvers;
1317 struct server *srv = NULL;
1318 struct dns_srvrq *srvrq = NULL;
1319 char **hostname_dn;
1320 int hostname_dn_len, query_type;
1321
1322 switch (requester_type) {
1323 case OBJ_TYPE_SERVER:
1324 srv = (struct server *)requester;
1325 hostname_dn = &srv->hostname_dn;
1326 hostname_dn_len = srv->hostname_dn_len;
1327 resolvers = srv->resolvers;
1328 query_type = ((srv->dns_opts.family_prio == AF_INET)
1329 ? DNS_RTYPE_A
1330 : DNS_RTYPE_AAAA);
1331 break;
1332
1333 case OBJ_TYPE_SRVRQ:
1334 srvrq = (struct dns_srvrq *)requester;
1335 hostname_dn = &srvrq->hostname_dn;
1336 hostname_dn_len = srvrq->hostname_dn_len;
1337 resolvers = srvrq->resolvers;
1338 query_type = DNS_RTYPE_SRV;
1339 break;
1340
1341 default:
1342 goto err;
1343 }
1344
1345 /* Get a resolution from the resolvers' wait queue or pool */
1346 if ((res = dns_pick_resolution(resolvers, hostname_dn, hostname_dn_len, query_type)) == NULL)
1347 goto err;
1348
1349 if (srv) {
Olivier Houchard55dcdf42017-11-06 15:15:04 +01001350 if (!requester_locked)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001351 HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001352 if (srv->dns_requester == NULL) {
Willy Tarreau5ec84572017-11-05 10:35:57 +01001353 if ((req = calloc(1, sizeof(*req))) == NULL) {
Olivier Houchard55dcdf42017-11-06 15:15:04 +01001354 if (!requester_locked)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001355 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001356 goto err;
Willy Tarreau5ec84572017-11-05 10:35:57 +01001357 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001358 req->owner = &srv->obj_type;
1359 srv->dns_requester = req;
1360 }
1361 else
1362 req = srv->dns_requester;
Olivier Houchard55dcdf42017-11-06 15:15:04 +01001363 if (!requester_locked)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001364 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001365 }
1366 else if (srvrq) {
1367 if (srvrq->dns_requester == NULL) {
1368 if ((req = calloc(1, sizeof(*req))) == NULL)
1369 goto err;
1370 req->owner = &srvrq->obj_type;
1371 srvrq->dns_requester = req;
1372 }
1373 else
1374 req = srvrq->dns_requester;
1375 }
1376 else
1377 goto err;
1378
1379 req->resolution = res;
1380 req->requester_cb = snr_resolution_cb;
1381 req->requester_error_cb = snr_resolution_error_cb;
1382
1383 LIST_ADDQ(&res->requesters, &req->list);
1384 return 0;
1385
1386 err:
1387 if (res && LIST_ISEMPTY(&res->requesters))
1388 dns_free_resolution(res);
1389 return -1;
1390}
1391
1392/* Removes a requester from a DNS resoltion. It takes takes care of all the
1393 * consequences. It also cleans up some parameters from the requester.
1394 */
1395void dns_unlink_resolution(struct dns_requester *requester)
1396{
1397 struct dns_resolution *res;
1398 struct dns_requester *req;
1399
1400 /* Nothing to do */
1401 if (!requester || !requester->resolution)
1402 return;
1403 res = requester->resolution;
1404
1405 /* Clean up the requester */
1406 LIST_DEL(&requester->list);
1407 requester->resolution = NULL;
1408
1409 /* We need to find another requester linked on this resolution */
1410 if (!LIST_ISEMPTY(&res->requesters))
1411 req = LIST_NEXT(&res->requesters, struct dns_requester *, list);
1412 else {
1413 dns_free_resolution(res);
1414 return;
1415 }
1416
1417 /* Move hostname_dn related pointers to the next requester */
1418 switch (obj_type(req->owner)) {
1419 case OBJ_TYPE_SERVER:
1420 res->hostname_dn = objt_server(req->owner)->hostname_dn;
1421 res->hostname_dn_len = objt_server(req->owner)->hostname_dn_len;
1422 break;
1423 case OBJ_TYPE_SRVRQ:
1424 res->hostname_dn = objt_dns_srvrq(req->owner)->hostname_dn;
1425 res->hostname_dn_len = objt_dns_srvrq(req->owner)->hostname_dn_len;
1426 break;
1427 default:
1428 res->hostname_dn = NULL;
1429 res->hostname_dn_len = 0;
1430 break;
1431 }
Baptiste Assmann325137d2015-04-13 23:40:55 +02001432}
1433
Christopher Faulet67957bd2017-09-27 11:00:59 +02001434/* Called when a network IO is generated on a name server socket for an incoming
1435 * packet. It performs the following actions:
1436 * - check if the packet requires processing (not outdated resolution)
1437 * - ensure the DNS packet received is valid and call requester's callback
1438 * - call requester's error callback if invalid response
1439 * - check the dn_name in the packet against the one sent
1440 */
1441static void dns_resolve_recv(struct dgram_conn *dgram)
1442{
1443 struct dns_nameserver *ns, *tmpns;
1444 struct dns_resolvers *resolvers;
1445 struct dns_resolution *res;
1446 struct dns_query_item *query;
1447 unsigned char buf[DNS_MAX_UDP_MESSAGE + 1];
1448 unsigned char *bufend;
1449 int fd, buflen, dns_resp;
1450 int max_answer_records;
1451 unsigned short query_id;
1452 struct eb32_node *eb;
1453 struct dns_requester *req;
1454
1455 fd = dgram->t.sock.fd;
1456
1457 /* check if ready for reading */
1458 if (!fd_recv_ready(fd))
1459 return;
1460
1461 /* no need to go further if we can't retrieve the nameserver */
1462 if ((ns = dgram->owner) == NULL)
1463 return;
1464
1465 resolvers = ns->resolvers;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001466 HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001467
1468 /* process all pending input messages */
1469 while (1) {
1470 /* read message received */
1471 memset(buf, '\0', resolvers->accepted_payload_size + 1);
1472 if ((buflen = recv(fd, (char*)buf , resolvers->accepted_payload_size + 1, 0)) < 0) {
1473 /* FIXME : for now we consider EAGAIN only */
1474 fd_cant_recv(fd);
1475 break;
1476 }
1477
1478 /* message too big */
1479 if (buflen > resolvers->accepted_payload_size) {
1480 ns->counters.too_big++;
1481 continue;
1482 }
1483
1484 /* initializing variables */
1485 bufend = buf + buflen; /* pointer to mark the end of the buffer */
1486
1487 /* read the query id from the packet (16 bits) */
1488 if (buf + 2 > bufend) {
1489 ns->counters.invalid++;
1490 continue;
1491 }
1492 query_id = dns_response_get_query_id(buf);
1493
1494 /* search the query_id in the pending resolution tree */
1495 eb = eb32_lookup(&resolvers->query_ids, query_id);
1496 if (eb == NULL) {
1497 /* unknown query id means an outdated response and can be safely ignored */
1498 ns->counters.outdated++;
1499 continue;
1500 }
1501
1502 /* known query id means a resolution in prgress */
1503 res = eb32_entry(eb, struct dns_resolution, qid);
1504 if (!res) {
1505 ns->counters.outdated++;
1506 continue;
1507 }
Baptiste Assmann325137d2015-04-13 23:40:55 +02001508
Christopher Faulet67957bd2017-09-27 11:00:59 +02001509 /* number of responses received */
1510 res->nb_responses++;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001511
Christopher Faulet67957bd2017-09-27 11:00:59 +02001512 max_answer_records = (resolvers->accepted_payload_size - DNS_HEADER_SIZE) / DNS_MIN_RECORD_SIZE;
1513 dns_resp = dns_validate_dns_response(buf, bufend, res, max_answer_records);
Baptiste Assmann325137d2015-04-13 23:40:55 +02001514
Christopher Faulet67957bd2017-09-27 11:00:59 +02001515 switch (dns_resp) {
1516 case DNS_RESP_VALID:
1517 break;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001518
Christopher Faulet67957bd2017-09-27 11:00:59 +02001519 case DNS_RESP_INVALID:
1520 case DNS_RESP_QUERY_COUNT_ERROR:
1521 case DNS_RESP_WRONG_NAME:
1522 res->status = RSLV_STATUS_INVALID;
1523 ns->counters.invalid++;
1524 break;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001525
Christopher Faulet67957bd2017-09-27 11:00:59 +02001526 case DNS_RESP_NX_DOMAIN:
1527 res->status = RSLV_STATUS_NX;
1528 ns->counters.nx++;
1529 break;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001530
Christopher Faulet67957bd2017-09-27 11:00:59 +02001531 case DNS_RESP_REFUSED:
1532 res->status = RSLV_STATUS_REFUSED;
1533 ns->counters.refused++;
1534 break;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001535
Christopher Faulet67957bd2017-09-27 11:00:59 +02001536 case DNS_RESP_ANCOUNT_ZERO:
1537 res->status = RSLV_STATUS_OTHER;
1538 ns->counters.any_err++;
1539 break;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001540
Christopher Faulet67957bd2017-09-27 11:00:59 +02001541 case DNS_RESP_CNAME_ERROR:
1542 res->status = RSLV_STATUS_OTHER;
1543 ns->counters.cname_error++;
1544 break;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001545
Christopher Faulet67957bd2017-09-27 11:00:59 +02001546 case DNS_RESP_TRUNCATED:
1547 res->status = RSLV_STATUS_OTHER;
1548 ns->counters.truncated++;
1549 break;
Baptiste Assmanne70bc052017-08-21 16:51:09 +02001550
Christopher Faulet67957bd2017-09-27 11:00:59 +02001551 case DNS_RESP_NO_EXPECTED_RECORD:
1552 case DNS_RESP_ERROR:
1553 case DNS_RESP_INTERNAL:
1554 res->status = RSLV_STATUS_OTHER;
1555 ns->counters.other++;
1556 break;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001557 }
1558
Christopher Faulet67957bd2017-09-27 11:00:59 +02001559 /* Wait all nameservers response to handle errors */
1560 if (dns_resp != DNS_RESP_VALID && res->nb_responses < resolvers->nb_nameservers)
1561 continue;
Olivier Houchard8da5f982017-08-04 18:35:36 +02001562
Christopher Faulet67957bd2017-09-27 11:00:59 +02001563 /* Process error codes */
1564 if (dns_resp != DNS_RESP_VALID) {
1565 if (res->prefered_query_type != res->query_type) {
1566 /* The fallback on the query type was already performed,
1567 * so check the try counter. If it falls to 0, we can
1568 * report an error. Else, wait the next attempt. */
1569 if (!res->try)
1570 goto report_res_error;
1571 }
1572 else {
1573 /* Fallback from A to AAAA or the opposite and re-send
1574 * the resolution immediately. try counter is not
1575 * decremented. */
1576 if (res->prefered_query_type == DNS_RTYPE_A) {
1577 res->query_type = DNS_RTYPE_AAAA;
1578 dns_send_query(res);
Olivier Houchard8da5f982017-08-04 18:35:36 +02001579 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001580 else if (res->prefered_query_type == DNS_RTYPE_AAAA) {
1581 res->query_type = DNS_RTYPE_A;
1582 dns_send_query(res);
Olivier Houchard8da5f982017-08-04 18:35:36 +02001583 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001584 }
1585 continue;
1586 }
Olivier Houchard8da5f982017-08-04 18:35:36 +02001587
Christopher Faulet67957bd2017-09-27 11:00:59 +02001588 /* Now let's check the query's dname corresponds to the one we
1589 * sent. We can check only the first query of the list. We send
1590 * one query at a time so we get one query in the response */
1591 query = LIST_NEXT(&res->response.query_list, struct dns_query_item *, list);
1592 if (query && memcmp(query->name, res->hostname_dn, res->hostname_dn_len) != 0) {
1593 dns_resp = DNS_RESP_WRONG_NAME;
1594 ns->counters.other++;
1595 goto report_res_error;
1596 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001597
Christopher Faulet67957bd2017-09-27 11:00:59 +02001598 /* So the resolution succeeded */
1599 res->status = RSLV_STATUS_VALID;
1600 res->last_valid = now_ms;
1601 ns->counters.valid++;
1602 goto report_res_success;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001603
Christopher Faulet67957bd2017-09-27 11:00:59 +02001604 report_res_error:
1605 list_for_each_entry(req, &res->requesters, list)
1606 req->requester_error_cb(req, dns_resp);
1607 dns_reset_resolution(res);
1608 LIST_DEL(&res->list);
1609 LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
1610 continue;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001611
Christopher Faulet67957bd2017-09-27 11:00:59 +02001612 report_res_success:
1613 /* Only the 1rst requester s managed by the server, others are
1614 * from the cache */
1615 tmpns = ns;
1616 list_for_each_entry(req, &res->requesters, list) {
Olivier Houchard28381072017-11-06 17:30:28 +01001617 struct server *s = objt_server(req->owner);
1618
1619 if (s)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001620 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001621 req->requester_cb(req, tmpns);
Olivier Houchard28381072017-11-06 17:30:28 +01001622 if (s)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001623 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001624 tmpns = NULL;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001625 }
Baptiste Assmann42746372017-05-03 12:12:02 +02001626
Christopher Faulet67957bd2017-09-27 11:00:59 +02001627 dns_reset_resolution(res);
1628 LIST_DEL(&res->list);
1629 LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
1630 continue;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001631 }
Baptiste Assmann325137d2015-04-13 23:40:55 +02001632 dns_update_resolvers_timeout(resolvers);
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001633 HA_SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
Baptiste Assmann325137d2015-04-13 23:40:55 +02001634}
William Lallemand69e96442016-11-19 00:58:54 +01001635
Christopher Faulet67957bd2017-09-27 11:00:59 +02001636/* Called when a resolvers network socket is ready to send data */
1637static void dns_resolve_send(struct dgram_conn *dgram)
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001638{
Christopher Faulet67957bd2017-09-27 11:00:59 +02001639 struct dns_resolvers *resolvers;
1640 struct dns_nameserver *ns;
1641 struct dns_resolution *res;
1642 int fd;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001643
Christopher Faulet67957bd2017-09-27 11:00:59 +02001644 fd = dgram->t.sock.fd;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001645
Christopher Faulet67957bd2017-09-27 11:00:59 +02001646 /* check if ready for sending */
1647 if (!fd_send_ready(fd))
1648 return;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001649
Christopher Faulet67957bd2017-09-27 11:00:59 +02001650 /* we don't want/need to be waked up any more for sending */
1651 fd_stop_send(fd);
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001652
Christopher Faulet67957bd2017-09-27 11:00:59 +02001653 /* no need to go further if we can't retrieve the nameserver */
1654 if ((ns = dgram->owner) == NULL)
1655 return;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001656
Christopher Faulet67957bd2017-09-27 11:00:59 +02001657 resolvers = ns->resolvers;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001658 HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
Christopher Fauletb2812a62017-10-04 16:17:58 +02001659
Christopher Faulet67957bd2017-09-27 11:00:59 +02001660 list_for_each_entry(res, &resolvers->resolutions.curr, list) {
1661 int ret;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001662
Christopher Faulet67957bd2017-09-27 11:00:59 +02001663 if (res->nb_queries == resolvers->nb_nameservers)
1664 continue;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001665
Christopher Faulet67957bd2017-09-27 11:00:59 +02001666 trash.len = dns_build_query(res->query_id, res->query_type,
1667 resolvers->accepted_payload_size,
1668 res->hostname_dn, res->hostname_dn_len,
1669 trash.str, trash.size);
1670 if (trash.len == -1)
1671 goto snd_error;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001672
Christopher Faulet67957bd2017-09-27 11:00:59 +02001673 ret = send(fd, trash.str, trash.len, 0);
1674 if (ret != trash.len)
1675 goto snd_error;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001676
Christopher Faulet67957bd2017-09-27 11:00:59 +02001677 ns->counters.sent++;
1678 res->nb_queries++;
1679 continue;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001680
Christopher Faulet67957bd2017-09-27 11:00:59 +02001681 snd_error:
1682 ns->counters.snd_error++;
1683 res->nb_queries++;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001684 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001685 HA_SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001686}
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001687
Christopher Faulet67957bd2017-09-27 11:00:59 +02001688/* Processes DNS resolution. First, it checks the active list to detect expired
1689 * resolutions and retry them if possible. Else a timeout is reported. Then, it
1690 * checks the wait list to trigger new resolutions.
1691 */
1692static struct task *dns_process_resolvers(struct task *t)
1693{
1694 struct dns_resolvers *resolvers = t->context;
1695 struct dns_resolution *res, *resback;
1696 int exp;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001697
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001698 HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
Christopher Fauletb2812a62017-10-04 16:17:58 +02001699
Christopher Faulet67957bd2017-09-27 11:00:59 +02001700 /* Handle all expired resolutions from the active list */
1701 list_for_each_entry_safe(res, resback, &resolvers->resolutions.curr, list) {
1702 /* When we find the first resolution in the future, then we can
1703 * stop here */
1704 exp = tick_add(res->last_query, resolvers->timeout.retry);
1705 if (!tick_is_expired(exp, now_ms))
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001706 break;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001707
Christopher Faulet67957bd2017-09-27 11:00:59 +02001708 /* If current resolution has been tried too many times and
1709 * finishes in timeout we update its status and remove it from
1710 * the list */
1711 if (!res->try) {
1712 struct dns_requester *req;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001713
Christopher Faulet67957bd2017-09-27 11:00:59 +02001714 /* Notify the result to the requesters */
1715 if (!res->nb_responses)
1716 res->status = RSLV_STATUS_TIMEOUT;
1717 list_for_each_entry(req, &res->requesters, list)
1718 req->requester_error_cb(req, res->status);
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001719
Christopher Faulet67957bd2017-09-27 11:00:59 +02001720 /* Clean up resolution info and remove it from the
1721 * current list */
1722 dns_reset_resolution(res);
1723 LIST_DEL(&res->list);
1724 LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
William Lallemand69e96442016-11-19 00:58:54 +01001725
Christopher Faulet67957bd2017-09-27 11:00:59 +02001726 /* This might be triggered by too big UDP packets
1727 * dropped somewhere on the network, so lowering the
1728 * accepted_payload_size announced */
1729 if (resolvers->accepted_payload_size > 1280)
1730 resolvers->accepted_payload_size = 1280;
William Lallemand69e96442016-11-19 00:58:54 +01001731 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001732 else {
1733 /* Otherwise resend the DNS query and requeue the resolution */
1734 if (!res->nb_responses || res->prefered_query_type != res->query_type) {
1735 /* No response received (a real timeout) or fallback already done */
1736 res->query_type = res->prefered_query_type;
1737 res->try--;
1738 }
1739 else {
1740 /* Fallback from A to AAAA or the opposite and re-send
1741 * the resolution immediately. try counter is not
1742 * decremented. */
1743 if (res->prefered_query_type == DNS_RTYPE_A)
1744 res->query_type = DNS_RTYPE_AAAA;
1745 else if (res->prefered_query_type == DNS_RTYPE_AAAA)
1746 res->query_type = DNS_RTYPE_A;
1747 else
1748 res->try--;
1749 }
1750 dns_send_query(res);
William Lallemand69e96442016-11-19 00:58:54 +01001751 }
1752 }
William Lallemand69e96442016-11-19 00:58:54 +01001753
Christopher Faulet67957bd2017-09-27 11:00:59 +02001754 /* Handle all resolutions in the wait list */
1755 list_for_each_entry_safe(res, resback, &resolvers->resolutions.wait, list) {
1756 exp = tick_add(res->last_resolution, dns_resolution_timeout(res));
1757 if (tick_isset(res->last_resolution) && !tick_is_expired(exp, now_ms))
1758 continue;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001759
Christopher Faulet67957bd2017-09-27 11:00:59 +02001760 if (dns_run_resolution(res) != 1) {
1761 res->last_resolution = now_ms;
1762 LIST_DEL(&res->list);
1763 LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001764 }
1765 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001766
Christopher Faulet67957bd2017-09-27 11:00:59 +02001767 dns_update_resolvers_timeout(resolvers);
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001768 HA_SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001769 return t;
1770}
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001771
Christopher Faulet67957bd2017-09-27 11:00:59 +02001772/* proto_udp callback functions for a DNS resolution */
1773struct dgram_data_cb resolve_dgram_cb = {
1774 .recv = dns_resolve_recv,
1775 .send = dns_resolve_send,
1776};
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001777
Christopher Faulet67957bd2017-09-27 11:00:59 +02001778/* Release memory allocated by DNS */
1779static void dns_deinit(void)
1780{
1781 struct dns_resolvers *resolvers, *resolversback;
1782 struct dns_nameserver *ns, *nsback;
1783 struct dns_resolution *res, *resback;
1784 struct dns_requester *req, *reqback;
1785 struct dns_srvrq *srvrq, *srvrqback;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001786
Christopher Faulet67957bd2017-09-27 11:00:59 +02001787 list_for_each_entry_safe(resolvers, resolversback, &dns_resolvers, list) {
1788 list_for_each_entry_safe(ns, nsback, &resolvers->nameservers, list) {
1789 free(ns->id);
1790 free((char *)ns->conf.file);
1791 if (ns->dgram && ns->dgram->t.sock.fd != -1)
1792 fd_delete(ns->dgram->t.sock.fd);
1793 free(ns->dgram);
1794 LIST_DEL(&ns->list);
1795 free(ns);
1796 }
1797
1798 list_for_each_entry_safe(res, resback, &resolvers->resolutions.curr, list) {
1799 list_for_each_entry_safe(req, reqback, &res->requesters, list) {
1800 LIST_DEL(&req->list);
1801 free(req);
Olivier Houchard8da5f982017-08-04 18:35:36 +02001802 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001803 dns_free_resolution(res);
1804 }
Olivier Houchard8da5f982017-08-04 18:35:36 +02001805
Christopher Faulet67957bd2017-09-27 11:00:59 +02001806 list_for_each_entry_safe(res, resback, &resolvers->resolutions.wait, list) {
1807 list_for_each_entry_safe(req, reqback, &res->requesters, list) {
1808 LIST_DEL(&req->list);
1809 free(req);
Olivier Houchard8da5f982017-08-04 18:35:36 +02001810 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001811 dns_free_resolution(res);
1812 }
Olivier Houchard8da5f982017-08-04 18:35:36 +02001813
Christopher Faulet67957bd2017-09-27 11:00:59 +02001814 free(resolvers->id);
1815 free((char *)resolvers->conf.file);
1816 task_delete(resolvers->t);
1817 task_free(resolvers->t);
1818 LIST_DEL(&resolvers->list);
1819 free(resolvers);
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001820 }
1821
Christopher Faulet67957bd2017-09-27 11:00:59 +02001822 list_for_each_entry_safe(srvrq, srvrqback, &dns_srvrq_list, list) {
1823 free(srvrq->name);
1824 free(srvrq->hostname_dn);
1825 LIST_DEL(&srvrq->list);
1826 free(srvrq);
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001827 }
1828
Willy Tarreaubafbe012017-11-24 17:34:44 +01001829 pool_destroy(dns_answer_item_pool);
1830 pool_destroy(dns_resolution_pool);
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001831}
1832
Christopher Faulet67957bd2017-09-27 11:00:59 +02001833/* Finalizes the DNS configuration by allocating required resources and checking
1834 * live parameters.
1835 * Returns 0 on success, ERR_* flags otherwise.
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001836 */
Christopher Faulet67957bd2017-09-27 11:00:59 +02001837static int dns_finalize_config(void)
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001838{
Christopher Faulet67957bd2017-09-27 11:00:59 +02001839 struct dns_resolvers *resolvers;
1840 struct proxy *px;
1841 int err_code = 0;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001842
Christopher Faulet67957bd2017-09-27 11:00:59 +02001843 /* allocate pool of resolution per resolvers */
1844 list_for_each_entry(resolvers, &dns_resolvers, list) {
1845 struct dns_nameserver *ns;
1846 struct task *t;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001847
Christopher Faulet67957bd2017-09-27 11:00:59 +02001848 /* Check if we can create the socket with nameservers info */
1849 list_for_each_entry(ns, &resolvers->nameservers, list) {
1850 struct dgram_conn *dgram = NULL;
1851 int fd;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001852
Christopher Faulet67957bd2017-09-27 11:00:59 +02001853 /* Check nameserver info */
1854 if ((fd = socket(ns->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001855 ha_alert("config : resolvers '%s': can't create socket for nameserver '%s'.\n",
1856 resolvers->id, ns->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001857 err_code |= (ERR_ALERT|ERR_ABORT);
Olivier Houchard8da5f982017-08-04 18:35:36 +02001858 continue;
Olivier Houchard8da5f982017-08-04 18:35:36 +02001859 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001860 if (connect(fd, (struct sockaddr*)&ns->addr, get_addr_len(&ns->addr)) == -1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001861 ha_alert("config : resolvers '%s': can't connect socket for nameserver '%s'.\n",
1862 resolvers->id, ns->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001863 close(fd);
1864 err_code |= (ERR_ALERT|ERR_ABORT);
Olivier Houchard8da5f982017-08-04 18:35:36 +02001865 continue;
Olivier Houchard8da5f982017-08-04 18:35:36 +02001866 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001867 close(fd);
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001868
Christopher Faulet67957bd2017-09-27 11:00:59 +02001869 /* Create dgram structure that will hold the UPD socket
1870 * and attach it on the current nameserver */
1871 if ((dgram = calloc(1, sizeof(*dgram))) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001872 ha_alert("config: resolvers '%s' : out of memory.\n",
1873 resolvers->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001874 err_code |= (ERR_ALERT|ERR_ABORT);
1875 goto err;
1876 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001877
Christopher Faulet67957bd2017-09-27 11:00:59 +02001878 /* Leave dgram partially initialized, no FD attached for
1879 * now. */
1880 dgram->owner = ns;
1881 dgram->data = &resolve_dgram_cb;
1882 dgram->t.sock.fd = -1;
1883 ns->dgram = dgram;
1884 }
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02001885
Christopher Faulet67957bd2017-09-27 11:00:59 +02001886 /* Create the task associated to the resolvers section */
Emeric Brunc60def82017-09-27 14:59:38 +02001887 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001888 ha_alert("config : resolvers '%s' : out of memory.\n", resolvers->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001889 err_code |= (ERR_ALERT|ERR_ABORT);
1890 goto err;
1891 }
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02001892
Christopher Faulet67957bd2017-09-27 11:00:59 +02001893 /* Update task's parameters */
1894 t->process = dns_process_resolvers;
1895 t->context = resolvers;
1896 resolvers->t = t;
1897 task_wakeup(t, TASK_WOKEN_INIT);
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02001898 }
1899
Olivier Houchardfbc74e82017-11-24 16:54:05 +01001900 for (px = proxies_list; px; px = px->next) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02001901 struct server *srv;
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02001902
Christopher Faulet67957bd2017-09-27 11:00:59 +02001903 for (srv = px->srv; srv; srv = srv->next) {
1904 struct dns_resolvers *resolvers;
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02001905
Christopher Faulet67957bd2017-09-27 11:00:59 +02001906 if (!srv->resolvers_id)
1907 continue;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001908
Christopher Faulet67957bd2017-09-27 11:00:59 +02001909 if ((resolvers = find_resolvers_by_id(srv->resolvers_id)) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001910 ha_alert("config : %s '%s', server '%s': unable to find required resolvers '%s'\n",
1911 proxy_type_str(px), px->id, srv->id, srv->resolvers_id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001912 err_code |= (ERR_ALERT|ERR_ABORT);
1913 continue;
1914 }
1915 srv->resolvers = resolvers;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001916
Christopher Faulet67957bd2017-09-27 11:00:59 +02001917 if (srv->srvrq && !srv->srvrq->resolvers) {
1918 srv->srvrq->resolvers = srv->resolvers;
Olivier Houchard55dcdf42017-11-06 15:15:04 +01001919 if (dns_link_resolution(srv->srvrq, OBJ_TYPE_SRVRQ, 0) == -1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001920 ha_alert("config : %s '%s' : unable to set DNS resolution for server '%s'.\n",
1921 proxy_type_str(px), px->id, srv->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001922 err_code |= (ERR_ALERT|ERR_ABORT);
1923 continue;
1924 }
1925 }
Olivier Houchard55dcdf42017-11-06 15:15:04 +01001926 if (dns_link_resolution(srv, OBJ_TYPE_SERVER, 0) == -1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001927 ha_alert("config : %s '%s', unable to set DNS resolution for server '%s'.\n",
1928 proxy_type_str(px), px->id, srv->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001929 err_code |= (ERR_ALERT|ERR_ABORT);
1930 continue;
1931 }
1932 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001933 }
1934
Christopher Faulet67957bd2017-09-27 11:00:59 +02001935 if (err_code & (ERR_ALERT|ERR_ABORT))
1936 goto err;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001937
Christopher Faulet67957bd2017-09-27 11:00:59 +02001938 return err_code;
1939 err:
1940 dns_deinit();
1941 return err_code;
1942
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001943}
1944
Christopher Faulet67957bd2017-09-27 11:00:59 +02001945/* if an arg is found, it sets the resolvers section pointer into cli.p0 */
1946static int cli_parse_stat_resolvers(char **args, struct appctx *appctx, void *private)
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001947{
Christopher Faulet67957bd2017-09-27 11:00:59 +02001948 struct dns_resolvers *presolvers;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001949
Christopher Faulet67957bd2017-09-27 11:00:59 +02001950 if (*args[2]) {
1951 list_for_each_entry(presolvers, &dns_resolvers, list) {
1952 if (strcmp(presolvers->id, args[2]) == 0) {
1953 appctx->ctx.cli.p0 = presolvers;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001954 break;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001955 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001956 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001957 if (appctx->ctx.cli.p0 == NULL) {
1958 appctx->ctx.cli.severity = LOG_ERR;
1959 appctx->ctx.cli.msg = "Can't find that resolvers section\n";
1960 appctx->st0 = CLI_ST_PRINT;
1961 return 1;
1962 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001963 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001964 return 0;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001965}
1966
Christopher Faulet67957bd2017-09-27 11:00:59 +02001967/* Dumps counters from all resolvers section and associated name servers. It
1968 * returns 0 if the output buffer is full and it needs to be called again,
1969 * otherwise non-zero. It may limit itself to the resolver pointed to by
Willy Tarreau777b5602016-12-16 18:06:26 +01001970 * <cli.p0> if it's not null.
William Lallemand69e96442016-11-19 00:58:54 +01001971 */
1972static int cli_io_handler_dump_resolvers_to_buffer(struct appctx *appctx)
1973{
1974 struct stream_interface *si = appctx->owner;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001975 struct dns_resolvers *resolvers;
1976 struct dns_nameserver *ns;
William Lallemand69e96442016-11-19 00:58:54 +01001977
1978 chunk_reset(&trash);
1979
1980 switch (appctx->st2) {
1981 case STAT_ST_INIT:
1982 appctx->st2 = STAT_ST_LIST; /* let's start producing data */
1983 /* fall through */
1984
1985 case STAT_ST_LIST:
1986 if (LIST_ISEMPTY(&dns_resolvers)) {
1987 chunk_appendf(&trash, "No resolvers found\n");
1988 }
1989 else {
Christopher Faulet67957bd2017-09-27 11:00:59 +02001990 list_for_each_entry(resolvers, &dns_resolvers, list) {
1991 if (appctx->ctx.cli.p0 != NULL && appctx->ctx.cli.p0 != resolvers)
William Lallemand69e96442016-11-19 00:58:54 +01001992 continue;
1993
Christopher Faulet67957bd2017-09-27 11:00:59 +02001994 chunk_appendf(&trash, "Resolvers section %s\n", resolvers->id);
1995 list_for_each_entry(ns, &resolvers->nameservers, list) {
1996 chunk_appendf(&trash, " nameserver %s:\n", ns->id);
1997 chunk_appendf(&trash, " sent: %lld\n", ns->counters.sent);
1998 chunk_appendf(&trash, " snd_error: %lld\n", ns->counters.snd_error);
1999 chunk_appendf(&trash, " valid: %lld\n", ns->counters.valid);
2000 chunk_appendf(&trash, " update: %lld\n", ns->counters.update);
2001 chunk_appendf(&trash, " cname: %lld\n", ns->counters.cname);
2002 chunk_appendf(&trash, " cname_error: %lld\n", ns->counters.cname_error);
2003 chunk_appendf(&trash, " any_err: %lld\n", ns->counters.any_err);
2004 chunk_appendf(&trash, " nx: %lld\n", ns->counters.nx);
2005 chunk_appendf(&trash, " timeout: %lld\n", ns->counters.timeout);
2006 chunk_appendf(&trash, " refused: %lld\n", ns->counters.refused);
2007 chunk_appendf(&trash, " other: %lld\n", ns->counters.other);
2008 chunk_appendf(&trash, " invalid: %lld\n", ns->counters.invalid);
2009 chunk_appendf(&trash, " too_big: %lld\n", ns->counters.too_big);
2010 chunk_appendf(&trash, " truncated: %lld\n", ns->counters.truncated);
2011 chunk_appendf(&trash, " outdated: %lld\n", ns->counters.outdated);
William Lallemand69e96442016-11-19 00:58:54 +01002012 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02002013 chunk_appendf(&trash, "\n");
William Lallemand69e96442016-11-19 00:58:54 +01002014 }
2015 }
2016
2017 /* display response */
Willy Tarreau06d80a92017-10-19 14:32:15 +02002018 if (ci_putchk(si_ic(si), &trash) == -1) {
William Lallemand69e96442016-11-19 00:58:54 +01002019 /* let's try again later from this session. We add ourselves into
2020 * this session's users so that it can remove us upon termination.
2021 */
2022 si->flags |= SI_FL_WAIT_ROOM;
2023 return 0;
2024 }
2025
2026 appctx->st2 = STAT_ST_FIN;
2027 /* fall through */
2028
2029 default:
2030 appctx->st2 = STAT_ST_FIN;
2031 return 1;
2032 }
2033}
2034
2035/* register cli keywords */
Christopher Fauletff88efb2017-10-03 16:00:57 +02002036static struct cli_kw_list cli_kws = {{ }, {
2037 { { "show", "resolvers", NULL }, "show resolvers [id]: dumps counters from all resolvers section and\n"
2038 " associated name servers",
2039 cli_parse_stat_resolvers, cli_io_handler_dump_resolvers_to_buffer },
2040 {{},}
2041 }
2042};
William Lallemand69e96442016-11-19 00:58:54 +01002043
2044
2045__attribute__((constructor))
2046static void __dns_init(void)
2047{
Christopher Faulet67957bd2017-09-27 11:00:59 +02002048 dns_answer_item_pool = create_pool("dns_answer_item", sizeof(struct dns_answer_item), MEM_F_SHARED);
2049 dns_resolution_pool = create_pool("dns_resolution", sizeof(struct dns_resolution), MEM_F_SHARED);
2050
Christopher Faulet67957bd2017-09-27 11:00:59 +02002051 hap_register_post_check(dns_finalize_config);
2052 hap_register_post_deinit(dns_deinit);
2053
William Lallemand69e96442016-11-19 00:58:54 +01002054 cli_register_kw(&cli_kws);
2055}