blob: 0193f7396f77ff8e657fc5d5326294d78dbdeb79 [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
22#include <common/time.h>
23#include <common/ticks.h>
24
Baptiste Assmannfa4a6632017-05-04 09:05:00 +020025#include <import/lru.h>
26#include <import/xxhash.h>
27
William Lallemand69e96442016-11-19 00:58:54 +010028#include <types/applet.h>
29#include <types/cli.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020030#include <types/global.h>
31#include <types/dns.h>
32#include <types/proto_udp.h>
William Lallemand69e96442016-11-19 00:58:54 +010033#include <types/stats.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020034
William Lallemand69e96442016-11-19 00:58:54 +010035#include <proto/channel.h>
36#include <proto/cli.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020037#include <proto/checks.h>
38#include <proto/dns.h>
39#include <proto/fd.h>
40#include <proto/log.h>
41#include <proto/server.h>
42#include <proto/task.h>
43#include <proto/proto_udp.h>
William Lallemand69e96442016-11-19 00:58:54 +010044#include <proto/stream_interface.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020045
46struct list dns_resolvers = LIST_HEAD_INIT(dns_resolvers);
47struct dns_resolution *resolution = NULL;
48
49static int64_t dns_query_id_seed; /* random seed */
50
Baptiste Assmannfa4a6632017-05-04 09:05:00 +020051static struct lru64_head *dns_lru_tree;
52static int dns_cache_size = 1024; /* arbitrary DNS cache size */
53
Baptiste Assmann325137d2015-04-13 23:40:55 +020054/* proto_udp callback functions for a DNS resolution */
55struct dgram_data_cb resolve_dgram_cb = {
56 .recv = dns_resolve_recv,
57 .send = dns_resolve_send,
58};
59
60#if DEBUG
61/*
62 * go through the resolutions associated to a resolvers section and print the ID and hostname in
63 * domain name format
64 * should be used for debug purpose only
65 */
66void dns_print_current_resolutions(struct dns_resolvers *resolvers)
67{
68 list_for_each_entry(resolution, &resolvers->curr_resolution, list) {
69 printf(" resolution %d for %s\n", resolution->query_id, resolution->hostname_dn);
70 }
71}
72#endif
73
74/*
75 * check if there is more than 1 resolution in the resolver's resolution list
76 * return value:
77 * 0: empty list
78 * 1: exactly one entry in the list
79 * 2: more than one entry in the list
80 */
81int dns_check_resolution_queue(struct dns_resolvers *resolvers)
82{
83
84 if (LIST_ISEMPTY(&resolvers->curr_resolution))
85 return 0;
86
87 if ((resolvers->curr_resolution.n) && (resolvers->curr_resolution.n == resolvers->curr_resolution.p))
88 return 1;
89
90 if (! ((resolvers->curr_resolution.n == resolvers->curr_resolution.p)
91 && (&resolvers->curr_resolution != resolvers->curr_resolution.n)))
92 return 2;
93
94 return 0;
95}
96
97/*
98 * reset all parameters of a DNS resolution to 0 (or equivalent)
99 * and clean it up from all associated lists (resolution->qid and resolution->list)
100 */
101void dns_reset_resolution(struct dns_resolution *resolution)
102{
103 /* update resolution status */
104 resolution->step = RSLV_STEP_NONE;
105
106 resolution->try = 0;
107 resolution->try_cname = 0;
108 resolution->last_resolution = now_ms;
109 resolution->nb_responses = 0;
110
111 /* clean up query id */
112 eb32_delete(&resolution->qid);
113 resolution->query_id = 0;
114 resolution->qid.key = 0;
115
Baptiste Assmann325137d2015-04-13 23:40:55 +0200116 /* the second resolution in the queue becomes the first one */
117 LIST_DEL(&resolution->list);
118}
119
120/*
121 * function called when a network IO is generated on a name server socket for an incoming packet
122 * It performs the following actions:
123 * - check if the packet requires processing (not outdated resolution)
124 * - ensure the DNS packet received is valid and call requester's callback
125 * - call requester's error callback if invalid response
Baptiste Assmann3cf7f982016-04-17 22:43:26 +0200126 * - check the dn_name in the packet against the one sent
Baptiste Assmann325137d2015-04-13 23:40:55 +0200127 */
128void dns_resolve_recv(struct dgram_conn *dgram)
129{
130 struct dns_nameserver *nameserver;
131 struct dns_resolvers *resolvers;
132 struct dns_resolution *resolution;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200133 struct dns_query_item *query;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200134 unsigned char buf[DNS_MAX_UDP_MESSAGE + 1];
135 unsigned char *bufend;
136 int fd, buflen, ret;
137 unsigned short query_id;
138 struct eb32_node *eb;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +0200139 struct lru64 *lru = NULL;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200140
141 fd = dgram->t.sock.fd;
142
143 /* check if ready for reading */
144 if (!fd_recv_ready(fd))
145 return;
146
147 /* no need to go further if we can't retrieve the nameserver */
Vincent Bernat3c2f2f22016-04-03 13:48:42 +0200148 if ((nameserver = dgram->owner) == NULL)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200149 return;
150
151 resolvers = nameserver->resolvers;
152
153 /* process all pending input messages */
154 while (1) {
155 /* read message received */
156 memset(buf, '\0', DNS_MAX_UDP_MESSAGE + 1);
157 if ((buflen = recv(fd, (char*)buf , DNS_MAX_UDP_MESSAGE, 0)) < 0) {
158 /* FIXME : for now we consider EAGAIN only */
159 fd_cant_recv(fd);
160 break;
161 }
162
163 /* message too big */
164 if (buflen > DNS_MAX_UDP_MESSAGE) {
165 nameserver->counters.too_big += 1;
166 continue;
167 }
168
169 /* initializing variables */
170 bufend = buf + buflen; /* pointer to mark the end of the buffer */
171
172 /* read the query id from the packet (16 bits) */
173 if (buf + 2 > bufend) {
174 nameserver->counters.invalid += 1;
175 continue;
176 }
177 query_id = dns_response_get_query_id(buf);
178
179 /* search the query_id in the pending resolution tree */
Baptiste Assmann01daef32015-09-02 22:05:24 +0200180 eb = eb32_lookup(&resolvers->query_ids, query_id);
181 if (eb == NULL) {
Baptiste Assmann325137d2015-04-13 23:40:55 +0200182 /* unknown query id means an outdated response and can be safely ignored */
183 nameserver->counters.outdated += 1;
184 continue;
185 }
186
187 /* known query id means a resolution in prgress */
188 resolution = eb32_entry(eb, struct dns_resolution, qid);
189
190 if (!resolution) {
191 nameserver->counters.outdated += 1;
192 continue;
193 }
194
195 /* number of responses received */
196 resolution->nb_responses += 1;
197
Baptiste Assmann729c9012017-05-22 15:13:10 +0200198 ret = dns_validate_dns_response(buf, bufend, resolution);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200199
200 /* treat only errors */
201 switch (ret) {
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200202 case DNS_RESP_QUERY_COUNT_ERROR:
Baptiste Assmann325137d2015-04-13 23:40:55 +0200203 case DNS_RESP_INVALID:
Baptiste Assmann325137d2015-04-13 23:40:55 +0200204 nameserver->counters.invalid += 1;
205 resolution->requester_error_cb(resolution, DNS_RESP_INVALID);
206 continue;
207
Baptiste Assmann42746372017-05-03 12:12:02 +0200208 case DNS_RESP_INTERNAL:
Baptiste Assmann325137d2015-04-13 23:40:55 +0200209 case DNS_RESP_ERROR:
210 nameserver->counters.other += 1;
211 resolution->requester_error_cb(resolution, DNS_RESP_ERROR);
212 continue;
213
214 case DNS_RESP_ANCOUNT_ZERO:
215 nameserver->counters.any_err += 1;
216 resolution->requester_error_cb(resolution, DNS_RESP_ANCOUNT_ZERO);
217 continue;
218
219 case DNS_RESP_NX_DOMAIN:
220 nameserver->counters.nx += 1;
221 resolution->requester_error_cb(resolution, DNS_RESP_NX_DOMAIN);
222 continue;
223
224 case DNS_RESP_REFUSED:
225 nameserver->counters.refused += 1;
226 resolution->requester_error_cb(resolution, DNS_RESP_REFUSED);
227 continue;
228
229 case DNS_RESP_CNAME_ERROR:
230 nameserver->counters.cname_error += 1;
231 resolution->requester_error_cb(resolution, DNS_RESP_CNAME_ERROR);
232 continue;
233
Baptiste Assmann0df5d962015-09-02 21:58:32 +0200234 case DNS_RESP_TRUNCATED:
235 nameserver->counters.truncated += 1;
236 resolution->requester_error_cb(resolution, DNS_RESP_TRUNCATED);
237 continue;
Baptiste Assmann96972bc2015-09-09 00:46:58 +0200238
239 case DNS_RESP_NO_EXPECTED_RECORD:
240 nameserver->counters.other += 1;
241 resolution->requester_error_cb(resolution, DNS_RESP_NO_EXPECTED_RECORD);
242 continue;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200243 }
244
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200245 /* Now let's check the query's dname corresponds to the one we sent.
246 * We can check only the first query of the list. We send one query at a time
247 * so we get one query in the response */
Baptiste Assmann729c9012017-05-22 15:13:10 +0200248 query = LIST_NEXT(&resolution->response.query_list, struct dns_query_item *, list);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200249 if (query && memcmp(query->name, resolution->hostname_dn, resolution->hostname_dn_len) != 0) {
250 nameserver->counters.other += 1;
251 resolution->requester_error_cb(resolution, DNS_RESP_WRONG_NAME);
252 continue;
253 }
254
Baptiste Assmannfa4a6632017-05-04 09:05:00 +0200255 /* no errors, we can save the response in the cache */
256 if (dns_lru_tree) {
257 unsigned long long seed = 1;
258 struct chunk *buf = get_trash_chunk();
259 struct chunk *tmp = NULL;
260
261 chunk_reset(buf);
262 tmp = dns_cache_key(resolution->query_type, resolution->hostname_dn,
263 resolution->hostname_dn_len, buf);
264 if (!tmp) {
265 nameserver->counters.other += 1;
266 resolution->requester_error_cb(resolution, DNS_RESP_ERROR);
267 continue;
268 }
269
270 lru = lru64_get(XXH64(buf->str, buf->len, seed),
271 dns_lru_tree, nameserver->resolvers, 1);
272
273 lru64_commit(lru, resolution, nameserver->resolvers, 1, NULL);
274 }
275
Baptiste Assmann37bb3722015-08-07 10:18:32 +0200276 nameserver->counters.valid += 1;
Baptiste Assmann729c9012017-05-22 15:13:10 +0200277 resolution->requester_cb(resolution, nameserver);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200278 }
279}
280
281/*
282 * function called when a resolvers network socket is ready to send data
283 * It performs the following actions:
284 */
285void dns_resolve_send(struct dgram_conn *dgram)
286{
287 int fd;
288 struct dns_nameserver *nameserver;
289 struct dns_resolvers *resolvers;
290 struct dns_resolution *resolution;
291
292 fd = dgram->t.sock.fd;
293
294 /* check if ready for sending */
295 if (!fd_send_ready(fd))
296 return;
297
298 /* we don't want/need to be waked up any more for sending */
299 fd_stop_send(fd);
300
301 /* no need to go further if we can't retrieve the nameserver */
Vincent Bernat3c2f2f22016-04-03 13:48:42 +0200302 if ((nameserver = dgram->owner) == NULL)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200303 return;
304
305 resolvers = nameserver->resolvers;
306 resolution = LIST_NEXT(&resolvers->curr_resolution, struct dns_resolution *, list);
307
308 dns_send_query(resolution);
309 dns_update_resolvers_timeout(resolvers);
310}
311
312/*
313 * forge and send a DNS query to resolvers associated to a resolution
314 * It performs the following actions:
315 * returns:
316 * 0 in case of error or safe ignorance
317 * 1 if no error
318 */
319int dns_send_query(struct dns_resolution *resolution)
320{
Baptiste Assmann42746372017-05-03 12:12:02 +0200321 struct dns_resolvers *resolvers = NULL;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200322 struct dns_nameserver *nameserver;
Erwan Velu5457eb42015-10-15 15:07:26 +0200323 int ret, bufsize, fd;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200324
Baptiste Assmann42746372017-05-03 12:12:02 +0200325 resolvers = ((struct server *)resolution->requester)->resolvers;
326
327 if (!resolvers)
328 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200329
Baptiste Assmann325137d2015-04-13 23:40:55 +0200330 bufsize = dns_build_query(resolution->query_id, resolution->query_type, resolution->hostname_dn,
331 resolution->hostname_dn_len, trash.str, trash.size);
332
333 if (bufsize == -1)
334 return 0;
335
336 list_for_each_entry(nameserver, &resolvers->nameserver_list, list) {
337 fd = nameserver->dgram->t.sock.fd;
338 errno = 0;
339
340 ret = send(fd, trash.str, bufsize, 0);
341
342 if (ret > 0)
343 nameserver->counters.sent += 1;
344
345 if (ret == 0 || errno == EAGAIN) {
346 /* nothing written, let's update the poller that we wanted to send
347 * but we were not able to */
348 fd_want_send(fd);
349 fd_cant_send(fd);
350 }
351 }
352
353 /* update resolution */
Baptiste Assmann325137d2015-04-13 23:40:55 +0200354 resolution->nb_responses = 0;
355 resolution->last_sent_packet = now_ms;
356
357 return 1;
358}
359
360/*
361 * update a resolvers' task timeout for next wake up
362 */
363void dns_update_resolvers_timeout(struct dns_resolvers *resolvers)
364{
365 struct dns_resolution *resolution;
366
367 if (LIST_ISEMPTY(&resolvers->curr_resolution)) {
368 /* no more resolution pending, so no wakeup anymore */
369 resolvers->t->expire = TICK_ETERNITY;
370 }
371 else {
372 resolution = LIST_NEXT(&resolvers->curr_resolution, struct dns_resolution *, list);
373 resolvers->t->expire = tick_add(resolution->last_sent_packet, resolvers->timeout.retry);
374 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200375}
376
377/*
378 * Analyse, re-build and copy the name <name> from the DNS response packet <buffer>.
379 * <name> must point to the 'data_len' information or pointer 'c0' for compressed data.
380 * The result is copied into <dest>, ensuring we don't overflow using <dest_len>
381 * Returns the number of bytes the caller can move forward. If 0 it means an error occured
382 * while parsing the name.
383 * <offset> is the number of bytes the caller could move forward.
384 */
385int dns_read_name(unsigned char *buffer, unsigned char *bufend, unsigned char *name, char *destination, int dest_len, int *offset)
386{
387 int nb_bytes = 0, n = 0;
388 int label_len;
389 unsigned char *reader = name;
390 char *dest = destination;
391
392 while (1) {
393 /* name compression is in use */
394 if ((*reader & 0xc0) == 0xc0) {
395 /* a pointer must point BEFORE current position */
396 if ((buffer + reader[1]) > reader) {
397 goto out_error;
398 }
399
400 n = dns_read_name(buffer, bufend, buffer + reader[1], dest, dest_len - nb_bytes, offset);
401 if (n == 0)
402 goto out_error;
403
404 dest += n;
405 nb_bytes += n;
406 goto out;
407 }
408
409 label_len = *reader;
410 if (label_len == 0)
411 goto out;
412 /* Check if:
413 * - we won't read outside the buffer
414 * - there is enough place in the destination
415 */
416 if ((reader + label_len >= bufend) || (nb_bytes + label_len >= dest_len))
417 goto out_error;
418
419 /* +1 to take label len + label string */
420 label_len += 1;
421
422 memcpy(dest, reader, label_len);
423
424 dest += label_len;
425 nb_bytes += label_len;
426 reader += label_len;
427 }
428
429 out:
430 /* offset computation:
431 * parse from <name> until finding either NULL or a pointer "c0xx"
432 */
433 reader = name;
434 *offset = 0;
435 while (reader < bufend) {
436 if ((reader[0] & 0xc0) == 0xc0) {
437 *offset += 2;
438 break;
439 }
440 else if (*reader == 0) {
441 *offset += 1;
442 break;
443 }
444 *offset += 1;
445 ++reader;
446 }
447
448 return nb_bytes;
449
450 out_error:
451 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200452}
453
454/*
455 * Function to validate that the buffer DNS response provided in <resp> and
456 * finishing before <bufend> is valid from a DNS protocol point of view.
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200457 *
Baptiste Assmann729c9012017-05-22 15:13:10 +0200458 * The result is stored in <resolution>' response, buf_response, response_query_records
459 * and response_answer_records members.
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200460 *
461 * This function returns one of the DNS_RESP_* code to indicate the type of
462 * error found.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200463 */
Baptiste Assmann729c9012017-05-22 15:13:10 +0200464int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct dns_resolution *resolution)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200465{
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200466 unsigned char *reader;
467 char *previous_dname, tmpname[DNS_MAX_NAME_SIZE];
468 int len, flags, offset, ret;
469 int dns_query_record_id, dns_answer_record_id;
Baptiste Assmann69fce672017-05-04 08:37:45 +0200470 int nb_saved_records;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200471 struct dns_query_item *dns_query;
472 struct dns_answer_item *dns_answer_record;
Baptiste Assmann729c9012017-05-22 15:13:10 +0200473 struct dns_response_packet *dns_p;
474 struct chunk *dns_response_buffer;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200475
476 reader = resp;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200477 len = 0;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200478 previous_dname = NULL;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200479
Baptiste Assmann729c9012017-05-22 15:13:10 +0200480 /* initialization of response buffer and structure */
481 dns_p = &resolution->response;
482 dns_response_buffer = &resolution->response_buffer;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200483 memset(dns_p, '\0', sizeof(struct dns_response_packet));
Baptiste Assmann729c9012017-05-22 15:13:10 +0200484 chunk_reset(dns_response_buffer);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200485
486 /* query id */
487 if (reader + 2 >= bufend)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200488 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200489 dns_p->header.id = reader[0] * 256 + reader[1];
490 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200491
492 /*
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200493 * flags and rcode are stored over 2 bytes
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200494 * First byte contains:
495 * - response flag (1 bit)
496 * - opcode (4 bits)
497 * - authoritative (1 bit)
498 * - truncated (1 bit)
499 * - recursion desired (1 bit)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200500 */
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200501 if (reader + 2 >= bufend)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200502 return DNS_RESP_INVALID;
503
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200504 flags = reader[0] * 256 + reader[1];
505
506 if (flags & DNS_FLAG_TRUNCATED)
507 return DNS_RESP_TRUNCATED;
508
509 if ((flags & DNS_FLAG_REPLYCODE) != DNS_RCODE_NO_ERROR) {
510 if ((flags & DNS_FLAG_REPLYCODE) == DNS_RCODE_NX_DOMAIN)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200511 return DNS_RESP_NX_DOMAIN;
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200512 else if ((flags & DNS_FLAG_REPLYCODE) == DNS_RCODE_REFUSED)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200513 return DNS_RESP_REFUSED;
514
515 return DNS_RESP_ERROR;
516 }
517
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200518 /* move forward 2 bytes for flags */
519 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200520
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200521 /* 2 bytes for question count */
522 if (reader + 2 >= bufend)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200523 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200524 dns_p->header.qdcount = reader[0] * 256 + reader[1];
525 /* (for now) we send one query only, so we expect only one in the response too */
526 if (dns_p->header.qdcount != 1)
527 return DNS_RESP_QUERY_COUNT_ERROR;
528 if (dns_p->header.qdcount > DNS_MAX_QUERY_RECORDS)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200529 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200530 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200531
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200532 /* 2 bytes for answer count */
533 if (reader + 2 >= bufend)
534 return DNS_RESP_INVALID;
535 dns_p->header.ancount = reader[0] * 256 + reader[1];
536 if (dns_p->header.ancount == 0)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200537 return DNS_RESP_ANCOUNT_ZERO;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200538 /* check if too many records are announced */
539 if (dns_p->header.ancount > DNS_MAX_ANSWER_RECORDS)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200540 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200541 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200542
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200543 /* 2 bytes authority count */
544 if (reader + 2 >= bufend)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200545 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200546 dns_p->header.nscount = reader[0] * 256 + reader[1];
547 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200548
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200549 /* 2 bytes additional count */
550 if (reader + 2 >= bufend)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200551 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200552 dns_p->header.arcount = reader[0] * 256 + reader[1];
553 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200554
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200555 /* parsing dns queries */
556 LIST_INIT(&dns_p->query_list);
557 for (dns_query_record_id = 0; dns_query_record_id < dns_p->header.qdcount; dns_query_record_id++) {
558 /* use next pre-allocated dns_query_item after ensuring there is
559 * still one available.
560 * It's then added to our packet query list.
561 */
562 if (dns_query_record_id > DNS_MAX_QUERY_RECORDS)
563 return DNS_RESP_INVALID;
Baptiste Assmann729c9012017-05-22 15:13:10 +0200564 dns_query = &resolution->response_query_records[dns_query_record_id];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200565 LIST_ADDQ(&dns_p->query_list, &dns_query->list);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200566
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200567 /* name is a NULL terminated string in our case, since we have
568 * one query per response and the first one can't be compressed
569 * (using the 0x0c format)
570 */
571 offset = 0;
572 len = dns_read_name(resp, bufend, reader, dns_query->name, DNS_MAX_NAME_SIZE, &offset);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200573
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200574 if (len == 0)
575 return DNS_RESP_INVALID;
576
577 reader += offset;
578 previous_dname = dns_query->name;
579
580 /* move forward 2 bytes for question type */
581 if (reader + 2 >= bufend)
582 return DNS_RESP_INVALID;
583 dns_query->type = reader[0] * 256 + reader[1];
584 reader += 2;
585
586 /* move forward 2 bytes for question class */
587 if (reader + 2 >= bufend)
588 return DNS_RESP_INVALID;
589 dns_query->class = reader[0] * 256 + reader[1];
590 reader += 2;
591 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200592
593 /* now parsing response records */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200594 LIST_INIT(&dns_p->answer_list);
Baptiste Assmann69fce672017-05-04 08:37:45 +0200595 nb_saved_records = 0;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200596 for (dns_answer_record_id = 0; dns_answer_record_id < dns_p->header.ancount; dns_answer_record_id++) {
Baptiste Assmann325137d2015-04-13 23:40:55 +0200597 if (reader >= bufend)
598 return DNS_RESP_INVALID;
599
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200600 /* pull next response record from the list, if still one available, then add it
601 * to the record list */
602 if (dns_answer_record_id > DNS_MAX_ANSWER_RECORDS)
603 return DNS_RESP_INVALID;
Baptiste Assmann729c9012017-05-22 15:13:10 +0200604 dns_answer_record = &resolution->response_answer_records[dns_answer_record_id];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200605 LIST_ADDQ(&dns_p->answer_list, &dns_answer_record->list);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200606
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200607 offset = 0;
608 len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200609
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200610 if (len == 0)
611 return DNS_RESP_INVALID;
612
613 /* check if the current record dname is valid.
614 * previous_dname points either to queried dname or last CNAME target
615 */
616 if (memcmp(previous_dname, tmpname, len) != 0) {
617 if (dns_answer_record_id == 0) {
618 /* first record, means a mismatch issue between queried dname
619 * and dname found in the first record */
Baptiste Assmann325137d2015-04-13 23:40:55 +0200620 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200621 } else {
622 /* if not the first record, this means we have a CNAME resolution
623 * error */
624 return DNS_RESP_CNAME_ERROR;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200625 }
626
Baptiste Assmann325137d2015-04-13 23:40:55 +0200627 }
628
Baptiste Assmann729c9012017-05-22 15:13:10 +0200629 dns_answer_record->name = chunk_newstr(dns_response_buffer);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200630 if (dns_answer_record->name == NULL)
631 return DNS_RESP_INVALID;
Baptiste Assmann5d681ba2015-10-15 15:23:28 +0200632
Baptiste Assmann729c9012017-05-22 15:13:10 +0200633 ret = chunk_strncat(dns_response_buffer, tmpname, len);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200634 if (ret == 0)
635 return DNS_RESP_INVALID;
Baptiste Assmann2359ff12015-08-07 11:24:05 +0200636
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200637 reader += offset;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200638 if (reader >= bufend)
639 return DNS_RESP_INVALID;
640
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200641 if (reader >= bufend)
642 return DNS_RESP_INVALID;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200643
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200644 /* 2 bytes for record type (A, AAAA, CNAME, etc...) */
Baptiste Assmann325137d2015-04-13 23:40:55 +0200645 if (reader + 2 > bufend)
646 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200647 dns_answer_record->type = reader[0] * 256 + reader[1];
648 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200649
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200650 /* 2 bytes for class (2) */
651 if (reader + 2 > bufend)
652 return DNS_RESP_INVALID;
653 dns_answer_record->class = reader[0] * 256 + reader[1];
Baptiste Assmann325137d2015-04-13 23:40:55 +0200654 reader += 2;
655
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200656 /* 4 bytes for ttl (4) */
657 if (reader + 4 > bufend)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200658 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200659 dns_answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536
660 + reader[2] * 256 + reader[3];
661 reader += 4;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200662
663 /* now reading data len */
664 if (reader + 2 > bufend)
665 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200666 dns_answer_record->data_len = reader[0] * 256 + reader[1];
Baptiste Assmann325137d2015-04-13 23:40:55 +0200667
668 /* move forward 2 bytes for data len */
669 reader += 2;
670
671 /* analyzing record content */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200672 switch (dns_answer_record->type) {
Baptiste Assmann325137d2015-04-13 23:40:55 +0200673 case DNS_RTYPE_A:
674 /* ipv4 is stored on 4 bytes */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200675 if (dns_answer_record->data_len != 4)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200676 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200677 dns_answer_record->address.sa_family = AF_INET;
678 memcpy(&(((struct sockaddr_in *)&dns_answer_record->address)->sin_addr),
679 reader, dns_answer_record->data_len);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200680 break;
681
682 case DNS_RTYPE_CNAME:
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200683 /* check if this is the last record and update the caller about the status:
684 * no IP could be found and last record was a CNAME. Could be triggered
685 * by a wrong query type
686 *
687 * + 1 because dns_answer_record_id starts at 0 while number of answers
688 * is an integer and starts at 1.
689 */
690 if (dns_answer_record_id + 1 == dns_p->header.ancount)
691 return DNS_RESP_CNAME_ERROR;
692
693 offset = 0;
694 len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
695
696 if (len == 0)
697 return DNS_RESP_INVALID;
698
Baptiste Assmann729c9012017-05-22 15:13:10 +0200699 dns_answer_record->target = chunk_newstr(dns_response_buffer);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200700 if (dns_answer_record->target == NULL)
701 return DNS_RESP_INVALID;
702
Baptiste Assmann729c9012017-05-22 15:13:10 +0200703 ret = chunk_strncat(dns_response_buffer, tmpname, len);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200704 if (ret == 0)
705 return DNS_RESP_INVALID;
706
707 previous_dname = dns_answer_record->target;
708
Baptiste Assmann325137d2015-04-13 23:40:55 +0200709 break;
710
711 case DNS_RTYPE_AAAA:
712 /* ipv6 is stored on 16 bytes */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200713 if (dns_answer_record->data_len != 16)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200714 return DNS_RESP_INVALID;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200715 dns_answer_record->address.sa_family = AF_INET6;
716 memcpy(&(((struct sockaddr_in6 *)&dns_answer_record->address)->sin6_addr),
717 reader, dns_answer_record->data_len);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200718 break;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200719
Baptiste Assmann325137d2015-04-13 23:40:55 +0200720 } /* switch (record type) */
721
Baptiste Assmann69fce672017-05-04 08:37:45 +0200722 /* increment the counter for number of records saved into our local response */
723 nb_saved_records += 1;
724
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200725 /* move forward dns_answer_record->data_len for analyzing next record in the response */
726 reader += dns_answer_record->data_len;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200727 } /* for i 0 to ancount */
728
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200729 /* let's add a last \0 to close our last string */
Baptiste Assmann729c9012017-05-22 15:13:10 +0200730 ret = chunk_strncat(dns_response_buffer, "\0", 1);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200731 if (ret == 0)
732 return DNS_RESP_INVALID;
Baptiste Assmann96972bc2015-09-09 00:46:58 +0200733
Baptiste Assmann69fce672017-05-04 08:37:45 +0200734 /* save the number of records we really own */
735 dns_p->header.ancount = nb_saved_records;
736
Baptiste Assmann325137d2015-04-13 23:40:55 +0200737 return DNS_RESP_VALID;
738}
739
740/*
741 * search dn_name resolution in resp.
742 * If existing IP not found, return the first IP matching family_priority,
743 * otherwise, first ip found
744 * The following tasks are the responsibility of the caller:
Baptiste Assmann3cf7f982016-04-17 22:43:26 +0200745 * - <dns_p> contains an error free DNS response
Baptiste Assmann325137d2015-04-13 23:40:55 +0200746 * For both cases above, dns_validate_dns_response is required
747 * returns one of the DNS_UPD_* code
748 */
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100749#define DNS_MAX_IP_REC 20
Baptiste Assmannfb7091e2017-05-03 15:43:12 +0200750int dns_get_ip_from_response(struct dns_response_packet *dns_p,
Baptiste Assmann42746372017-05-03 12:12:02 +0200751 struct dns_options *dns_opts, void *currentip,
Thierry Fournierada34842016-02-17 21:25:09 +0100752 short currentip_sin_family,
Baptiste Assmannfb7091e2017-05-03 15:43:12 +0200753 void **newip, short *newip_sin_family,
754 void *owner)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200755{
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200756 struct dns_answer_item *record;
Thierry Fournierada34842016-02-17 21:25:09 +0100757 int family_priority;
Baptiste Assmann3cf7f982016-04-17 22:43:26 +0200758 int i, currentip_found;
759 unsigned char *newip4, *newip6;
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100760 struct {
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200761 void *ip;
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100762 unsigned char type;
763 } rec[DNS_MAX_IP_REC];
764 int currentip_sel;
765 int j;
766 int rec_nb = 0;
767 int score, max_score;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200768
Baptiste Assmann42746372017-05-03 12:12:02 +0200769 family_priority = dns_opts->family_prio;
Baptiste Assmann3cf7f982016-04-17 22:43:26 +0200770 *newip = newip4 = newip6 = NULL;
771 currentip_found = 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200772 *newip_sin_family = AF_UNSPEC;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200773
774 /* now parsing response records */
Baptiste Assmann729c9012017-05-22 15:13:10 +0200775 list_for_each_entry(record, &dns_p->answer_list, list) {
Baptiste Assmann325137d2015-04-13 23:40:55 +0200776 /* analyzing record content */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200777 switch (record->type) {
Baptiste Assmann325137d2015-04-13 23:40:55 +0200778 case DNS_RTYPE_A:
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100779 /* Store IPv4, only if some room is avalaible. */
780 if (rec_nb < DNS_MAX_IP_REC) {
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200781 rec[rec_nb].ip = &(((struct sockaddr_in *)&record->address)->sin_addr);
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100782 rec[rec_nb].type = AF_INET;
783 rec_nb++;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200784 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200785 break;
786
Baptiste Assmann3cf7f982016-04-17 22:43:26 +0200787 /* we're looking for IPs only. CNAME validation is done when
788 * parsing the response buffer for the first time */
Baptiste Assmann325137d2015-04-13 23:40:55 +0200789 case DNS_RTYPE_CNAME:
Baptiste Assmann325137d2015-04-13 23:40:55 +0200790 break;
791
792 case DNS_RTYPE_AAAA:
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100793 /* Store IPv6, only if some room is avalaible. */
794 if (rec_nb < DNS_MAX_IP_REC) {
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200795 rec[rec_nb].ip = &(((struct sockaddr_in6 *)&record->address)->sin6_addr);
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100796 rec[rec_nb].type = AF_INET6;
797 rec_nb++;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200798 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200799 break;
800
Baptiste Assmann325137d2015-04-13 23:40:55 +0200801 } /* switch (record type) */
Baptiste Assmannbcbd4912016-04-18 19:42:57 +0200802 } /* list for each record entries */
Baptiste Assmann325137d2015-04-13 23:40:55 +0200803
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100804 /* Select an IP regarding configuration preference.
805 * Top priority is the prefered network ip version,
806 * second priority is the prefered network.
807 * the last priority is the currently used IP,
808 *
809 * For these three priorities, a score is calculated. The
810 * weight are:
Baptistefc725902016-12-26 23:21:08 +0100811 * 8 - prefered netwok ip version.
812 * 4 - prefered network.
813 * 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 +0100814 * 1 - current ip.
815 * The result with the biggest score is returned.
816 */
817 max_score = -1;
818 for (i = 0; i < rec_nb; i++) {
Baptistefc725902016-12-26 23:21:08 +0100819 int record_ip_already_affected = 0;
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100820
821 score = 0;
822
823 /* Check for prefered ip protocol. */
824 if (rec[i].type == family_priority)
Baptistefc725902016-12-26 23:21:08 +0100825 score += 8;
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100826
827 /* Check for prefered network. */
Baptiste Assmann42746372017-05-03 12:12:02 +0200828 for (j = 0; j < dns_opts->pref_net_nb; j++) {
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100829
830 /* Compare only the same adresses class. */
Baptiste Assmann42746372017-05-03 12:12:02 +0200831 if (dns_opts->pref_net[j].family != rec[i].type)
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100832 continue;
833
834 if ((rec[i].type == AF_INET &&
Willy Tarreaueec1d382016-07-13 11:59:39 +0200835 in_net_ipv4(rec[i].ip,
Baptiste Assmann42746372017-05-03 12:12:02 +0200836 &dns_opts->pref_net[j].mask.in4,
837 &dns_opts->pref_net[j].addr.in4)) ||
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100838 (rec[i].type == AF_INET6 &&
Willy Tarreaueec1d382016-07-13 11:59:39 +0200839 in_net_ipv6(rec[i].ip,
Baptiste Assmann42746372017-05-03 12:12:02 +0200840 &dns_opts->pref_net[j].mask.in6,
841 &dns_opts->pref_net[j].addr.in6))) {
Baptistefc725902016-12-26 23:21:08 +0100842 score += 4;
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100843 break;
844 }
845 }
846
Baptiste Assmannfb7091e2017-05-03 15:43:12 +0200847 /* Check if the IP found in the record is already affected to a member of a group.
848 * If yes, the score should be incremented by 2.
849 */
850 if (owner) {
851 if (snr_check_ip_callback(owner, rec[i].ip, &rec[i].type))
Baptistefc725902016-12-26 23:21:08 +0100852 record_ip_already_affected = 1;
Baptistefc725902016-12-26 23:21:08 +0100853 }
Baptiste Assmannfb7091e2017-05-03 15:43:12 +0200854 if (record_ip_already_affected == 0)
Baptistefc725902016-12-26 23:21:08 +0100855 score += 2;
856
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100857 /* Check for current ip matching. */
858 if (rec[i].type == currentip_sin_family &&
859 ((currentip_sin_family == AF_INET &&
Willy Tarreaueec1d382016-07-13 11:59:39 +0200860 memcmp(rec[i].ip, currentip, 4) == 0) ||
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100861 (currentip_sin_family == AF_INET6 &&
862 memcmp(rec[i].ip, currentip, 16) == 0))) {
863 score += 1;
864 currentip_sel = 1;
865 } else
866 currentip_sel = 0;
867
Baptistefc725902016-12-26 23:21:08 +0100868
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100869 /* Keep the address if the score is better than the previous
Baptistefc725902016-12-26 23:21:08 +0100870 * score. The maximum score is 15, if this value is reached,
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100871 * we break the parsing. Implicitly, this score is reached
872 * the ip selected is the current ip.
873 */
874 if (score > max_score) {
875 if (rec[i].type == AF_INET)
876 newip4 = rec[i].ip;
877 else
878 newip6 = rec[i].ip;
879 currentip_found = currentip_sel;
Baptistefc725902016-12-26 23:21:08 +0100880 if (score == 15)
Thierry Fournierac88cfe2016-02-17 22:05:30 +0100881 return DNS_UPD_NO;
882 max_score = score;
883 }
884 }
885
Baptiste Assmann0453a1d2015-09-09 00:51:08 +0200886 /* no IP found in the response */
887 if (!newip4 && !newip6) {
888 return DNS_UPD_NO_IP_FOUND;
889 }
890
Baptiste Assmann325137d2015-04-13 23:40:55 +0200891 /* case when the caller looks first for an IPv4 address */
892 if (family_priority == AF_INET) {
893 if (newip4) {
894 *newip = newip4;
895 *newip_sin_family = AF_INET;
896 if (currentip_found == 1)
897 return DNS_UPD_NO;
898 return DNS_UPD_SRVIP_NOT_FOUND;
899 }
900 else if (newip6) {
901 *newip = newip6;
902 *newip_sin_family = AF_INET6;
903 if (currentip_found == 1)
904 return DNS_UPD_NO;
905 return DNS_UPD_SRVIP_NOT_FOUND;
906 }
907 }
908 /* case when the caller looks first for an IPv6 address */
909 else if (family_priority == AF_INET6) {
910 if (newip6) {
911 *newip = newip6;
912 *newip_sin_family = AF_INET6;
913 if (currentip_found == 1)
914 return DNS_UPD_NO;
915 return DNS_UPD_SRVIP_NOT_FOUND;
916 }
917 else if (newip4) {
918 *newip = newip4;
919 *newip_sin_family = AF_INET;
920 if (currentip_found == 1)
921 return DNS_UPD_NO;
922 return DNS_UPD_SRVIP_NOT_FOUND;
923 }
924 }
925 /* case when the caller have no preference (we prefer IPv6) */
926 else if (family_priority == AF_UNSPEC) {
927 if (newip6) {
928 *newip = newip6;
929 *newip_sin_family = AF_INET6;
930 if (currentip_found == 1)
931 return DNS_UPD_NO;
932 return DNS_UPD_SRVIP_NOT_FOUND;
933 }
934 else if (newip4) {
935 *newip = newip4;
936 *newip_sin_family = AF_INET;
937 if (currentip_found == 1)
938 return DNS_UPD_NO;
939 return DNS_UPD_SRVIP_NOT_FOUND;
940 }
941 }
942
943 /* no reason why we should change the server's IP address */
944 return DNS_UPD_NO;
945}
946
947/*
948 * returns the query id contained in a DNS response
949 */
Thiago Farinab1af23e2016-01-20 23:46:34 +0100950unsigned short dns_response_get_query_id(unsigned char *resp)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200951{
952 /* read the query id from the response */
953 return resp[0] * 256 + resp[1];
954}
955
956/*
957 * used during haproxy's init phase
958 * parses resolvers sections and initializes:
959 * - task (time events) for each resolvers section
960 * - the datagram layer (network IO events) for each nameserver
Baptiste Assmann5cd1b922017-02-02 22:44:15 +0100961 * It takes one argument:
962 * - close_first takes 2 values: 0 or 1. If 1, the connection is closed first.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200963 * returns:
964 * 0 in case of error
965 * 1 when no error
966 */
Baptiste Assmann5cd1b922017-02-02 22:44:15 +0100967int dns_init_resolvers(int close_socket)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200968{
969 struct dns_resolvers *curr_resolvers;
970 struct dns_nameserver *curnameserver;
971 struct dgram_conn *dgram;
972 struct task *t;
973 int fd;
974
Baptiste Assmannfa4a6632017-05-04 09:05:00 +0200975 /* initialize our DNS resolution cache */
976 dns_lru_tree = lru64_new(dns_cache_size);
977
Baptiste Assmann325137d2015-04-13 23:40:55 +0200978 /* give a first random value to our dns query_id seed */
979 dns_query_id_seed = random();
980
981 /* run through the resolvers section list */
982 list_for_each_entry(curr_resolvers, &dns_resolvers, list) {
983 /* create the task associated to the resolvers section */
984 if ((t = task_new()) == NULL) {
985 Alert("Starting [%s] resolvers: out of memory.\n", curr_resolvers->id);
986 return 0;
987 }
988
989 /* update task's parameters */
990 t->process = dns_process_resolve;
991 t->context = curr_resolvers;
992 t->expire = TICK_ETERNITY;
993
994 curr_resolvers->t = t;
995
996 list_for_each_entry(curnameserver, &curr_resolvers->nameserver_list, list) {
Baptiste Assmann5cd1b922017-02-02 22:44:15 +0100997 dgram = NULL;
998
999 if (close_socket == 1) {
1000 if (curnameserver->dgram) {
Frédéric Lécaille64920532017-05-12 09:57:15 +02001001 fd_delete(curnameserver->dgram->t.sock.fd);
Baptiste Assmann5cd1b922017-02-02 22:44:15 +01001002 memset(curnameserver->dgram, '\0', sizeof(*dgram));
1003 dgram = curnameserver->dgram;
1004 }
1005 }
1006
1007 /* allocate memory only if it has not already been allocated
1008 * by a previous call to this function */
1009 if (!dgram && (dgram = calloc(1, sizeof(*dgram))) == NULL) {
Baptiste Assmann325137d2015-04-13 23:40:55 +02001010 Alert("Starting [%s/%s] nameserver: out of memory.\n", curr_resolvers->id,
1011 curnameserver->id);
1012 return 0;
1013 }
1014 /* update datagram's parameters */
1015 dgram->owner = (void *)curnameserver;
1016 dgram->data = &resolve_dgram_cb;
1017
1018 /* create network UDP socket for this nameserver */
Frédéric Lécaille5e5bc9f2017-04-11 08:46:37 +02001019 if ((fd = socket(curnameserver->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
Baptiste Assmann325137d2015-04-13 23:40:55 +02001020 Alert("Starting [%s/%s] nameserver: can't create socket.\n", curr_resolvers->id,
1021 curnameserver->id);
1022 free(dgram);
1023 dgram = NULL;
1024 return 0;
1025 }
1026
1027 /* "connect" the UDP socket to the name server IP */
Baptiste Assmann8c62c472015-09-21 20:55:08 +02001028 if (connect(fd, (struct sockaddr*)&curnameserver->addr, get_addr_len(&curnameserver->addr)) == -1) {
Baptiste Assmann325137d2015-04-13 23:40:55 +02001029 Alert("Starting [%s/%s] nameserver: can't connect socket.\n", curr_resolvers->id,
1030 curnameserver->id);
1031 close(fd);
1032 free(dgram);
1033 dgram = NULL;
1034 return 0;
1035 }
1036
1037 /* make the socket non blocking */
1038 fcntl(fd, F_SETFL, O_NONBLOCK);
1039
1040 /* add the fd in the fd list and update its parameters */
1041 fd_insert(fd);
1042 fdtab[fd].owner = dgram;
1043 fdtab[fd].iocb = dgram_fd_handler;
1044 fd_want_recv(fd);
1045 dgram->t.sock.fd = fd;
1046
1047 /* update nameserver's datagram property */
1048 curnameserver->dgram = dgram;
1049
1050 continue;
1051 }
1052
1053 /* task can be queued */
1054 task_queue(t);
1055 }
1056
1057 return 1;
1058}
1059
1060/*
1061 * Forge a DNS query. It needs the following information from the caller:
1062 * - <query_id>: the DNS query id corresponding to this query
1063 * - <query_type>: DNS_RTYPE_* request DNS record type (A, AAAA, ANY, etc...)
1064 * - <hostname_dn>: hostname in domain name format
1065 * - <hostname_dn_len>: length of <hostname_dn>
1066 * To store the query, the caller must pass a buffer <buf> and its size <bufsize>
1067 *
1068 * the DNS query is stored in <buf>
1069 * returns:
1070 * -1 if <buf> is too short
1071 */
1072int dns_build_query(int query_id, int query_type, char *hostname_dn, int hostname_dn_len, char *buf, int bufsize)
1073{
1074 struct dns_header *dns;
Vincent Bernat9b7125c2016-04-08 22:17:45 +02001075 struct dns_question qinfo;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001076 char *ptr, *bufend;
1077
1078 memset(buf, '\0', bufsize);
1079 ptr = buf;
1080 bufend = buf + bufsize;
1081
1082 /* check if there is enough room for DNS headers */
1083 if (ptr + sizeof(struct dns_header) >= bufend)
1084 return -1;
1085
1086 /* set dns query headers */
1087 dns = (struct dns_header *)ptr;
1088 dns->id = (unsigned short) htons(query_id);
Nenad Merdanovic8ab79422016-07-13 14:03:43 +02001089 dns->flags = htons(0x0100); /* qr=0, opcode=0, aa=0, tc=0, rd=1, ra=0, z=0, rcode=0 */
Baptiste Assmann325137d2015-04-13 23:40:55 +02001090 dns->qdcount = htons(1); /* 1 question */
1091 dns->ancount = 0;
1092 dns->nscount = 0;
1093 dns->arcount = 0;
1094
1095 /* move forward ptr */
1096 ptr += sizeof(struct dns_header);
1097
1098 /* check if there is enough room for query hostname */
1099 if ((ptr + hostname_dn_len) >= bufend)
1100 return -1;
1101
1102 /* set up query hostname */
1103 memcpy(ptr, hostname_dn, hostname_dn_len);
1104 ptr[hostname_dn_len + 1] = '\0';
1105
1106 /* move forward ptr */
1107 ptr += (hostname_dn_len + 1);
1108
1109 /* check if there is enough room for query hostname*/
1110 if (ptr + sizeof(struct dns_question) >= bufend)
1111 return -1;
1112
1113 /* set up query info (type and class) */
Vincent Bernat9b7125c2016-04-08 22:17:45 +02001114 qinfo.qtype = htons(query_type);
1115 qinfo.qclass = htons(DNS_RCLASS_IN);
1116 memcpy(ptr, &qinfo, sizeof(qinfo));
Baptiste Assmann325137d2015-04-13 23:40:55 +02001117
1118 ptr += sizeof(struct dns_question);
1119
1120 return ptr - buf;
1121}
1122
1123/*
1124 * turn a string into domain name label:
1125 * www.haproxy.org into 3www7haproxy3org
1126 * if dn memory is pre-allocated, you must provide its size in dn_len
1127 * if dn memory isn't allocated, dn_len must be set to 0.
1128 * In the second case, memory will be allocated.
1129 * in case of error, -1 is returned, otherwise, number of bytes copied in dn
1130 */
Willy Tarreau2100b492015-07-22 16:42:43 +02001131char *dns_str_to_dn_label(const char *string, char *dn, int dn_len)
Baptiste Assmann325137d2015-04-13 23:40:55 +02001132{
1133 char *c, *d;
1134 int i, offset;
1135
1136 /* offset between string size and theorical dn size */
1137 offset = 1;
1138
1139 /*
1140 * first, get the size of the string turned into its domain name version
1141 * This function also validates the string respect the RFC
1142 */
1143 if ((i = dns_str_to_dn_label_len(string)) == -1)
1144 return NULL;
1145
1146 /* yes, so let's check there is enough memory */
1147 if (dn_len < i + offset)
1148 return NULL;
1149
Willy Tarreaud69d6f32015-07-22 16:45:36 +02001150 i = strlen(string);
Baptiste Assmann325137d2015-04-13 23:40:55 +02001151 memcpy(dn + offset, string, i);
1152 dn[i + offset] = '\0';
1153 /* avoid a '\0' at the beginning of dn string which may prevent the for loop
1154 * below from working.
1155 * Actually, this is the reason of the offset. */
1156 dn[0] = '0';
1157
1158 for (c = dn; *c ; ++c) {
1159 /* c points to the first '0' char or a dot, which we don't want to read */
1160 d = c + offset;
1161 i = 0;
1162 while (*d != '.' && *d) {
1163 i++;
1164 d++;
1165 }
1166 *c = i;
1167
1168 c = d - 1; /* because of c++ of the for loop */
1169 }
1170
1171 return dn;
1172}
1173
1174/*
1175 * compute and return the length of <string> it it were translated into domain name
1176 * label:
1177 * www.haproxy.org into 3www7haproxy3org would return 16
1178 * NOTE: add +1 for '\0' when allocating memory ;)
1179 */
1180int dns_str_to_dn_label_len(const char *string)
1181{
1182 return strlen(string) + 1;
1183}
1184
1185/*
1186 * validates host name:
1187 * - total size
1188 * - each label size individually
1189 * returns:
1190 * 0 in case of error. If <err> is not NULL, an error message is stored there.
1191 * 1 when no error. <err> is left unaffected.
1192 */
1193int dns_hostname_validation(const char *string, char **err)
1194{
1195 const char *c, *d;
1196 int i;
1197
1198 if (strlen(string) > DNS_MAX_NAME_SIZE) {
1199 if (err)
1200 *err = DNS_TOO_LONG_FQDN;
1201 return 0;
1202 }
1203
1204 c = string;
1205 while (*c) {
1206 d = c;
1207
1208 i = 0;
1209 while (*d != '.' && *d && i <= DNS_MAX_LABEL_SIZE) {
1210 i++;
1211 if (!((*d == '-') || (*d == '_') ||
1212 ((*d >= 'a') && (*d <= 'z')) ||
1213 ((*d >= 'A') && (*d <= 'Z')) ||
1214 ((*d >= '0') && (*d <= '9')))) {
1215 if (err)
1216 *err = DNS_INVALID_CHARACTER;
1217 return 0;
1218 }
1219 d++;
1220 }
1221
1222 if ((i >= DNS_MAX_LABEL_SIZE) && (d[i] != '.')) {
1223 if (err)
1224 *err = DNS_LABEL_TOO_LONG;
1225 return 0;
1226 }
1227
1228 if (*d == '\0')
1229 goto out;
1230
1231 c = ++d;
1232 }
1233 out:
1234 return 1;
1235}
1236
1237/*
1238 * 2 bytes random generator to generate DNS query ID
1239 */
1240uint16_t dns_rnd16(void)
1241{
1242 dns_query_id_seed ^= dns_query_id_seed << 13;
1243 dns_query_id_seed ^= dns_query_id_seed >> 7;
1244 dns_query_id_seed ^= dns_query_id_seed << 17;
1245 return dns_query_id_seed;
1246}
1247
1248
1249/*
1250 * function called when a timeout occurs during name resolution process
1251 * if max number of tries is reached, then stop, otherwise, retry.
1252 */
1253struct task *dns_process_resolve(struct task *t)
1254{
1255 struct dns_resolvers *resolvers = t->context;
1256 struct dns_resolution *resolution, *res_back;
Baptiste Assmann060e5732016-01-06 02:01:59 +01001257 int res_preferred_afinet, res_preferred_afinet6;
Baptiste Assmann42746372017-05-03 12:12:02 +02001258 struct dns_options *dns_opts = NULL;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001259
1260 /* timeout occurs inevitably for the first element of the FIFO queue */
1261 if (LIST_ISEMPTY(&resolvers->curr_resolution)) {
1262 /* no first entry, so wake up was useless */
1263 t->expire = TICK_ETERNITY;
1264 return t;
1265 }
1266
1267 /* look for the first resolution which is not expired */
1268 list_for_each_entry_safe(resolution, res_back, &resolvers->curr_resolution, list) {
1269 /* when we find the first resolution in the future, then we can stop here */
1270 if (tick_is_le(now_ms, resolution->last_sent_packet))
1271 goto out;
1272
1273 /*
1274 * if current resolution has been tried too many times and finishes in timeout
1275 * we update its status and remove it from the list
1276 */
Baptiste Assmannf778bb42015-09-09 00:54:38 +02001277 if (resolution->try <= 0) {
Baptiste Assmann325137d2015-04-13 23:40:55 +02001278 /* clean up resolution information and remove from the list */
1279 dns_reset_resolution(resolution);
1280
1281 /* notify the result to the requester */
1282 resolution->requester_error_cb(resolution, DNS_RESP_TIMEOUT);
Baptiste Assmann382824c2016-01-06 01:53:46 +01001283 goto out;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001284 }
1285
Baptiste Assmannf778bb42015-09-09 00:54:38 +02001286 resolution->try -= 1;
1287
Baptiste Assmann42746372017-05-03 12:12:02 +02001288 dns_opts = &((struct server *)resolution->requester)->dns_opts;
1289
1290 res_preferred_afinet = dns_opts->family_prio == AF_INET && resolution->query_type == DNS_RTYPE_A;
1291 res_preferred_afinet6 = dns_opts->family_prio == AF_INET6 && resolution->query_type == DNS_RTYPE_AAAA;
Baptiste Assmann060e5732016-01-06 02:01:59 +01001292
1293 /* let's change the query type if needed */
1294 if (res_preferred_afinet6) {
1295 /* fallback from AAAA to A */
1296 resolution->query_type = DNS_RTYPE_A;
1297 }
1298 else if (res_preferred_afinet) {
1299 /* fallback from A to AAAA */
1300 resolution->query_type = DNS_RTYPE_AAAA;
1301 }
1302
Baptiste Assmann382824c2016-01-06 01:53:46 +01001303 /* resend the DNS query */
1304 dns_send_query(resolution);
Baptiste Assmann325137d2015-04-13 23:40:55 +02001305
Baptiste Assmann382824c2016-01-06 01:53:46 +01001306 /* check if we have more than one resolution in the list */
1307 if (dns_check_resolution_queue(resolvers) > 1) {
1308 /* move the rsolution to the end of the list */
1309 LIST_DEL(&resolution->list);
1310 LIST_ADDQ(&resolvers->curr_resolution, &resolution->list);
Baptiste Assmann325137d2015-04-13 23:40:55 +02001311 }
1312 }
1313
1314 out:
1315 dns_update_resolvers_timeout(resolvers);
1316 return t;
1317}
William Lallemand69e96442016-11-19 00:58:54 +01001318
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02001319/*
1320 * build a dns cache key composed as follow:
1321 * <query type>#<hostname in domain name format>
1322 * and store it into <str>.
1323 * It's up to the caller to allocate <buf> and to reset it.
1324 * The function returns NULL in case of error (IE <buf> too small) or a pointer
1325 * to buf if successful
1326 */
1327struct chunk *
1328dns_cache_key(int query_type, char *hostname_dn, int hostname_dn_len, struct chunk *buf)
1329{
1330 int len, size;
1331 char *str;
1332
1333 str = buf->str;
1334 len = buf->len;
1335 size = buf->size;
1336
1337 switch (query_type) {
1338 case DNS_RTYPE_A:
1339 if (len + 1 > size)
1340 return NULL;
1341 memcpy(&str[len], "A", 1);
1342 len += 1;
1343 break;
1344 case DNS_RTYPE_AAAA:
1345 if (len + 4 > size)
1346 return NULL;
1347 memcpy(&str[len], "AAAA", 4);
1348 len += 4;
1349 break;
1350 default:
1351 return NULL;
1352 }
1353
1354 if (len + 1 > size)
1355 return NULL;
1356 memcpy(&str[len], "#", 1);
1357 len += 1;
1358
1359 if (len + hostname_dn_len + 1 > size) // +1 for trailing zero
1360 return NULL;
1361 memcpy(&str[len], hostname_dn, hostname_dn_len);
1362 len += hostname_dn_len;
1363 str[len] = '\0';
1364
1365 return buf;
1366}
1367
1368/*
1369 * returns a pointer to a cache entry which may still be considered as up to date
1370 * by the caller.
1371 * returns NULL if no entry can be found or if the data found is outdated.
1372 */
1373struct lru64 *
1374dns_cache_lookup(int query_type, char *hostname_dn, int hostname_dn_len, int valid_period, void *cache_domain) {
1375 struct lru64 *elem = NULL;
1376 struct dns_resolution *resolution = NULL;
1377 struct dns_resolvers *resolvers = NULL;
1378 int inter = 0;
1379 struct chunk *buf = get_trash_chunk();
1380 struct chunk *tmp = NULL;
1381
1382 if (!dns_lru_tree)
1383 return NULL;
1384
1385 chunk_reset(buf);
1386 tmp = dns_cache_key(query_type, hostname_dn, hostname_dn_len, buf);
1387 if (tmp == NULL)
1388 return NULL;
1389
1390 elem = lru64_lookup(XXH64(buf->str, buf->len, 1), dns_lru_tree, cache_domain, 1);
1391
1392 if (!elem || !elem->data)
1393 return NULL;
1394
1395 resolution = elem->data;
1396
1397 /* since we can change the fqdn of a server at run time, it may happen that
1398 * we got an innacurate elem.
1399 * This is because resolution->hostname_dn points to (owner)->hostname_dn (which
1400 * may be changed at run time)
1401 */
1402 if ((hostname_dn_len == resolution->hostname_dn_len) &&
1403 (memcmp(hostname_dn, resolution->hostname_dn, hostname_dn_len) != 0)) {
1404 return NULL;
1405 }
1406
1407 resolvers = ((struct server *)resolution->requester)->resolvers;
1408
1409 if (!resolvers)
1410 return NULL;
1411
1412 if (resolvers->hold.valid < valid_period)
1413 inter = resolvers->hold.valid;
1414 else
1415 inter = valid_period;
1416
1417 if (!tick_is_expired(tick_add(resolution->last_resolution, inter), now_ms))
1418 return elem;
1419
1420 return NULL;
1421}
1422
Willy Tarreau777b5602016-12-16 18:06:26 +01001423/* if an arg is found, it sets the resolvers section pointer into cli.p0 */
William Lallemand69e96442016-11-19 00:58:54 +01001424static int cli_parse_stat_resolvers(char **args, struct appctx *appctx, void *private)
1425{
1426 struct dns_resolvers *presolvers;
1427
1428 if (*args[3]) {
William Lallemand69e96442016-11-19 00:58:54 +01001429 list_for_each_entry(presolvers, &dns_resolvers, list) {
1430 if (strcmp(presolvers->id, args[3]) == 0) {
Willy Tarreau777b5602016-12-16 18:06:26 +01001431 appctx->ctx.cli.p0 = presolvers;
William Lallemand69e96442016-11-19 00:58:54 +01001432 break;
1433 }
1434 }
Willy Tarreau777b5602016-12-16 18:06:26 +01001435 if (appctx->ctx.cli.p0 == NULL) {
William Lallemand69e96442016-11-19 00:58:54 +01001436 appctx->ctx.cli.msg = "Can't find that resolvers section\n";
Willy Tarreau3b6e5472016-11-24 15:53:53 +01001437 appctx->st0 = CLI_ST_PRINT;
William Lallemand69e96442016-11-19 00:58:54 +01001438 return 1;
1439 }
1440 }
Willy Tarreau3067bfa2016-12-05 14:50:15 +01001441 return 0;
William Lallemand69e96442016-11-19 00:58:54 +01001442}
1443
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02001444/* This function allocates memory for a DNS resolution structure.
1445 * It's up to the caller to set the parameters
1446 * Returns a pointer to the structure resolution or NULL if memory could
1447 * not be allocated.
1448 */
1449struct dns_resolution *dns_alloc_resolution(void)
1450{
1451 struct dns_resolution *resolution = NULL;
Baptiste Assmann729c9012017-05-22 15:13:10 +02001452 char *buffer = NULL;
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02001453
1454 resolution = calloc(1, sizeof(*resolution));
Baptiste Assmann729c9012017-05-22 15:13:10 +02001455 buffer = calloc(1, global.tune.bufsize);
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02001456
Baptiste Assmann729c9012017-05-22 15:13:10 +02001457 if (!resolution || !buffer) {
1458 free(buffer);
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02001459 free(resolution);
1460 return NULL;
1461 }
1462
Baptiste Assmann729c9012017-05-22 15:13:10 +02001463 chunk_init(&resolution->response_buffer, buffer, global.tune.bufsize);
1464
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02001465 return resolution;
1466}
1467
1468/* This function free the memory allocated to a DNS resolution */
1469void dns_free_resolution(struct dns_resolution *resolution)
1470{
Baptiste Assmann729c9012017-05-22 15:13:10 +02001471 chunk_destroy(&resolution->response_buffer);
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02001472 free(resolution);
1473
1474 return;
1475}
1476
Willy Tarreau777b5602016-12-16 18:06:26 +01001477/* This function dumps counters from all resolvers section and associated name
1478 * servers. It returns 0 if the output buffer is full and it needs to be called
1479 * again, otherwise non-zero. It may limit itself to the resolver pointed to by
1480 * <cli.p0> if it's not null.
William Lallemand69e96442016-11-19 00:58:54 +01001481 */
1482static int cli_io_handler_dump_resolvers_to_buffer(struct appctx *appctx)
1483{
1484 struct stream_interface *si = appctx->owner;
1485 struct dns_resolvers *presolvers;
1486 struct dns_nameserver *pnameserver;
1487
1488 chunk_reset(&trash);
1489
1490 switch (appctx->st2) {
1491 case STAT_ST_INIT:
1492 appctx->st2 = STAT_ST_LIST; /* let's start producing data */
1493 /* fall through */
1494
1495 case STAT_ST_LIST:
1496 if (LIST_ISEMPTY(&dns_resolvers)) {
1497 chunk_appendf(&trash, "No resolvers found\n");
1498 }
1499 else {
1500 list_for_each_entry(presolvers, &dns_resolvers, list) {
Willy Tarreau777b5602016-12-16 18:06:26 +01001501 if (appctx->ctx.cli.p0 != NULL && appctx->ctx.cli.p0 != presolvers)
William Lallemand69e96442016-11-19 00:58:54 +01001502 continue;
1503
1504 chunk_appendf(&trash, "Resolvers section %s\n", presolvers->id);
1505 list_for_each_entry(pnameserver, &presolvers->nameserver_list, list) {
1506 chunk_appendf(&trash, " nameserver %s:\n", pnameserver->id);
1507 chunk_appendf(&trash, " sent: %ld\n", pnameserver->counters.sent);
1508 chunk_appendf(&trash, " valid: %ld\n", pnameserver->counters.valid);
1509 chunk_appendf(&trash, " update: %ld\n", pnameserver->counters.update);
1510 chunk_appendf(&trash, " cname: %ld\n", pnameserver->counters.cname);
1511 chunk_appendf(&trash, " cname_error: %ld\n", pnameserver->counters.cname_error);
1512 chunk_appendf(&trash, " any_err: %ld\n", pnameserver->counters.any_err);
1513 chunk_appendf(&trash, " nx: %ld\n", pnameserver->counters.nx);
1514 chunk_appendf(&trash, " timeout: %ld\n", pnameserver->counters.timeout);
1515 chunk_appendf(&trash, " refused: %ld\n", pnameserver->counters.refused);
1516 chunk_appendf(&trash, " other: %ld\n", pnameserver->counters.other);
1517 chunk_appendf(&trash, " invalid: %ld\n", pnameserver->counters.invalid);
1518 chunk_appendf(&trash, " too_big: %ld\n", pnameserver->counters.too_big);
1519 chunk_appendf(&trash, " truncated: %ld\n", pnameserver->counters.truncated);
1520 chunk_appendf(&trash, " outdated: %ld\n", pnameserver->counters.outdated);
1521 }
1522 }
1523 }
1524
1525 /* display response */
1526 if (bi_putchk(si_ic(si), &trash) == -1) {
1527 /* let's try again later from this session. We add ourselves into
1528 * this session's users so that it can remove us upon termination.
1529 */
1530 si->flags |= SI_FL_WAIT_ROOM;
1531 return 0;
1532 }
1533
1534 appctx->st2 = STAT_ST_FIN;
1535 /* fall through */
1536
1537 default:
1538 appctx->st2 = STAT_ST_FIN;
1539 return 1;
1540 }
1541}
1542
1543/* register cli keywords */
1544static struct cli_kw_list cli_kws = {{ },{
1545 { { "show", "stat", "resolvers", NULL }, "show stat resolvers [id]: dumps counters from all resolvers section and\n"
1546 " associated name servers",
1547 cli_parse_stat_resolvers, cli_io_handler_dump_resolvers_to_buffer },
1548 {{},}
1549}};
1550
1551
1552__attribute__((constructor))
1553static void __dns_init(void)
1554{
1555 cli_register_kw(&cli_kws);
1556}
1557