blob: 72436a6dd01708fb2762f862e41858d1ab43c9f8 [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
Willy Tarreau122eba92020-06-04 10:15:32 +020022#include <haproxy/action.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020023#include <haproxy/api.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020024#include <haproxy/cfgparse.h>
Willy Tarreauf1d32c42020-06-04 21:07:02 +020025#include <haproxy/channel.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020026#include <haproxy/check.h>
Willy Tarreau83487a82020-06-04 20:19:54 +020027#include <haproxy/cli.h>
Willy Tarreau7c18b542020-06-11 09:23:02 +020028#include <haproxy/dgram.h>
Willy Tarreaueb92deb2020-06-04 10:53:16 +020029#include <haproxy/dns.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020030#include <haproxy/errors.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020031#include <haproxy/fd.h>
32#include <haproxy/global.h>
Willy Tarreauc761f842020-06-04 11:40:28 +020033#include <haproxy/http_rules.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020034#include <haproxy/log.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020035#include <haproxy/net_helper.h>
Emeric Brun8a551932020-11-24 17:24:34 +010036#include <haproxy/protocol.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020037#include <haproxy/proxy.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020038#include <haproxy/sample.h>
Willy Tarreau1e56f922020-06-04 23:20:13 +020039#include <haproxy/server.h>
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +020040#include <haproxy/stats.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020041#include <haproxy/stream_interface.h>
Willy Tarreaucea0e1b2020-06-04 17:25:40 +020042#include <haproxy/task.h>
Willy Tarreau8b550af2020-06-04 17:42:48 +020043#include <haproxy/tcp_rules.h>
Willy Tarreauc2f7c582020-06-02 18:15:32 +020044#include <haproxy/ticks.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020045#include <haproxy/time.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020046#include <haproxy/vars.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020047
Baptiste Assmann325137d2015-04-13 23:40:55 +020048
Emeric Brun750fe792020-12-23 16:51:12 +010049struct list sec_resolvers = LIST_HEAD_INIT(sec_resolvers);
Emeric Brun08622d32020-12-23 17:41:43 +010050struct list resolv_srvrq_list = LIST_HEAD_INIT(resolv_srvrq_list);
Baptiste Assmann325137d2015-04-13 23:40:55 +020051
Emeric Brund30e9a12020-12-23 18:49:16 +010052static THREAD_LOCAL uint64_t resolv_query_id_seed = 0; /* random seed */
Emeric Brun8a551932020-11-24 17:24:34 +010053struct resolvers *curr_resolvers = NULL;
Willy Tarreau8ceae722018-11-26 11:58:30 +010054
Emeric Brun85914e92020-12-23 16:38:06 +010055DECLARE_STATIC_POOL(resolv_answer_item_pool, "resolv_answer_item", sizeof(struct resolv_answer_item));
Emeric Brun08622d32020-12-23 17:41:43 +010056DECLARE_STATIC_POOL(resolv_resolution_pool, "resolv_resolution", sizeof(struct resolv_resolution));
57DECLARE_POOL(resolv_requester_pool, "resolv_requester", sizeof(struct resolv_requester));
Willy Tarreau8ceae722018-11-26 11:58:30 +010058
Christopher Faulet67957bd2017-09-27 11:00:59 +020059static unsigned int resolution_uuid = 1;
Emeric Brund30e9a12020-12-23 18:49:16 +010060unsigned int resolv_failed_resolutions = 0;
Baptiste Assmann201c07f2017-05-22 15:17:15 +020061
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +020062enum {
63 DNS_STAT_ID,
Emeric Brun50c870e2021-01-04 10:40:46 +010064 DNS_STAT_PID,
65 DNS_STAT_SENT,
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +020066 DNS_STAT_SND_ERROR,
67 DNS_STAT_VALID,
68 DNS_STAT_UPDATE,
69 DNS_STAT_CNAME,
70 DNS_STAT_CNAME_ERROR,
71 DNS_STAT_ANY_ERR,
72 DNS_STAT_NX,
73 DNS_STAT_TIMEOUT,
74 DNS_STAT_REFUSED,
75 DNS_STAT_OTHER,
76 DNS_STAT_INVALID,
77 DNS_STAT_TOO_BIG,
78 DNS_STAT_TRUNCATED,
79 DNS_STAT_OUTDATED,
80 DNS_STAT_END,
81};
82
83static struct name_desc dns_stats[] = {
84 [DNS_STAT_ID] = { .name = "id", .desc = "ID" },
Emeric Brun50c870e2021-01-04 10:40:46 +010085 [DNS_STAT_PID] = { .name = "pid", .desc = "Parent ID" },
86 [DNS_STAT_SENT] = { .name = "sent", .desc = "Sent" },
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +020087 [DNS_STAT_SND_ERROR] = { .name = "send_error", .desc = "Send error" },
88 [DNS_STAT_VALID] = { .name = "valid", .desc = "Valid" },
89 [DNS_STAT_UPDATE] = { .name = "update", .desc = "Update" },
90 [DNS_STAT_CNAME] = { .name = "cname", .desc = "CNAME" },
91 [DNS_STAT_CNAME_ERROR] = { .name = "cname_error", .desc = "CNAME error" },
92 [DNS_STAT_ANY_ERR] = { .name = "any_err", .desc = "Any errors" },
93 [DNS_STAT_NX] = { .name = "nx", .desc = "NX" },
94 [DNS_STAT_TIMEOUT] = { .name = "timeout", .desc = "Timeout" },
95 [DNS_STAT_REFUSED] = { .name = "refused", .desc = "Refused" },
96 [DNS_STAT_OTHER] = { .name = "other", .desc = "Other" },
97 [DNS_STAT_INVALID] = { .name = "invalid", .desc = "Invalid" },
98 [DNS_STAT_TOO_BIG] = { .name = "too_big", .desc = "Too big" },
99 [DNS_STAT_TRUNCATED] = { .name = "truncated", .desc = "Truncated" },
100 [DNS_STAT_OUTDATED] = { .name = "outdated", .desc = "Outdated" },
101};
102
103static struct dns_counters dns_counters;
104
105static void dns_fill_stats(void *d, struct field *stats)
106{
107 struct dns_counters *counters = d;
108 stats[DNS_STAT_ID] = mkf_str(FO_CONFIG, counters->id);
Emeric Brun50c870e2021-01-04 10:40:46 +0100109 stats[DNS_STAT_PID] = mkf_str(FO_CONFIG, counters->pid);
110 stats[DNS_STAT_SENT] = mkf_u64(FN_GAUGE, counters->sent);
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +0200111 stats[DNS_STAT_SND_ERROR] = mkf_u64(FN_GAUGE, counters->snd_error);
112 stats[DNS_STAT_VALID] = mkf_u64(FN_GAUGE, counters->valid);
113 stats[DNS_STAT_UPDATE] = mkf_u64(FN_GAUGE, counters->update);
114 stats[DNS_STAT_CNAME] = mkf_u64(FN_GAUGE, counters->cname);
115 stats[DNS_STAT_CNAME_ERROR] = mkf_u64(FN_GAUGE, counters->cname_error);
116 stats[DNS_STAT_ANY_ERR] = mkf_u64(FN_GAUGE, counters->any_err);
117 stats[DNS_STAT_NX] = mkf_u64(FN_GAUGE, counters->nx);
118 stats[DNS_STAT_TIMEOUT] = mkf_u64(FN_GAUGE, counters->timeout);
119 stats[DNS_STAT_REFUSED] = mkf_u64(FN_GAUGE, counters->refused);
120 stats[DNS_STAT_OTHER] = mkf_u64(FN_GAUGE, counters->other);
121 stats[DNS_STAT_INVALID] = mkf_u64(FN_GAUGE, counters->invalid);
122 stats[DNS_STAT_TOO_BIG] = mkf_u64(FN_GAUGE, counters->too_big);
123 stats[DNS_STAT_TRUNCATED] = mkf_u64(FN_GAUGE, counters->truncated);
124 stats[DNS_STAT_OUTDATED] = mkf_u64(FN_GAUGE, counters->outdated);
125}
126
127static struct stats_module dns_stats_module = {
128 .name = "dns",
129 .domain_flags = STATS_DOMAIN_DNS << STATS_DOMAIN,
130 .fill_stats = dns_fill_stats,
131 .stats = dns_stats,
132 .stats_count = DNS_STAT_END,
133 .counters = &dns_counters,
134 .counters_size = sizeof(dns_counters),
135 .clearable = 0,
136};
137
138INITCALL1(STG_REGISTER, stats_register_module, &dns_stats_module);
139
Christopher Faulet67957bd2017-09-27 11:00:59 +0200140/* Returns a pointer to the resolvers matching the id <id>. NULL is returned if
141 * no match is found.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200142 */
Emeric Brun750fe792020-12-23 16:51:12 +0100143struct resolvers *find_resolvers_by_id(const char *id)
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200144{
Emeric Brun750fe792020-12-23 16:51:12 +0100145 struct resolvers *res;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200146
Emeric Brun750fe792020-12-23 16:51:12 +0100147 list_for_each_entry(res, &sec_resolvers, list) {
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100148 if (strcmp(res->id, id) == 0)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200149 return res;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200150 }
Christopher Faulet67957bd2017-09-27 11:00:59 +0200151 return NULL;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200152}
153
Olivier Houchardb17b8842020-04-01 18:30:27 +0200154/* Compare hostnames in a case-insensitive way .
155 * Returns 0 if they are the same, non-zero otherwise
156 */
Emeric Brund30e9a12020-12-23 18:49:16 +0100157static __inline int resolv_hostname_cmp(const char *name1, const char *name2, int len)
Olivier Houchardb17b8842020-04-01 18:30:27 +0200158{
159 int i;
160
161 for (i = 0; i < len; i++)
Willy Tarreauf278eec2020-07-05 21:46:32 +0200162 if (tolower((unsigned char)name1[i]) != tolower((unsigned char)name2[i]))
Olivier Houchardb17b8842020-04-01 18:30:27 +0200163 return -1;
164 return 0;
165}
166
Christopher Faulet67957bd2017-09-27 11:00:59 +0200167/* Returns a pointer on the SRV request matching the name <name> for the proxy
168 * <px>. NULL is returned if no match is found.
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200169 */
Emeric Brun08622d32020-12-23 17:41:43 +0100170struct resolv_srvrq *find_srvrq_by_name(const char *name, struct proxy *px)
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200171{
Emeric Brun08622d32020-12-23 17:41:43 +0100172 struct resolv_srvrq *srvrq;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200173
Emeric Brun08622d32020-12-23 17:41:43 +0100174 list_for_each_entry(srvrq, &resolv_srvrq_list, list) {
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100175 if (srvrq->proxy == px && strcmp(srvrq->name, name) == 0)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200176 return srvrq;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200177 }
Christopher Faulet67957bd2017-09-27 11:00:59 +0200178 return NULL;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200179}
180
Christopher Faulet67957bd2017-09-27 11:00:59 +0200181/* Allocates a new SRVRQ for the given server with the name <fqdn>. It returns
182 * NULL if an error occurred. */
Emeric Brun08622d32020-12-23 17:41:43 +0100183struct resolv_srvrq *new_resolv_srvrq(struct server *srv, char *fqdn)
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200184{
Christopher Faulet67957bd2017-09-27 11:00:59 +0200185 struct proxy *px = srv->proxy;
Emeric Brun08622d32020-12-23 17:41:43 +0100186 struct resolv_srvrq *srvrq = NULL;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200187 int fqdn_len, hostname_dn_len;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200188
Christopher Faulet67957bd2017-09-27 11:00:59 +0200189 fqdn_len = strlen(fqdn);
Emeric Brund30e9a12020-12-23 18:49:16 +0100190 hostname_dn_len = resolv_str_to_dn_label(fqdn, fqdn_len + 1, trash.area,
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200191 trash.size);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200192 if (hostname_dn_len == -1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100193 ha_alert("config : %s '%s', server '%s': failed to parse FQDN '%s'\n",
194 proxy_type_str(px), px->id, srv->id, fqdn);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200195 goto err;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200196 }
197
Christopher Faulet67957bd2017-09-27 11:00:59 +0200198 if ((srvrq = calloc(1, sizeof(*srvrq))) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100199 ha_alert("config : %s '%s', server '%s': out of memory\n",
200 proxy_type_str(px), px->id, srv->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200201 goto err;
202 }
203 srvrq->obj_type = OBJ_TYPE_SRVRQ;
204 srvrq->proxy = px;
205 srvrq->name = strdup(fqdn);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200206 srvrq->hostname_dn = strdup(trash.area);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200207 srvrq->hostname_dn_len = hostname_dn_len;
208 if (!srvrq->name || !srvrq->hostname_dn) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100209 ha_alert("config : %s '%s', server '%s': out of memory\n",
210 proxy_type_str(px), px->id, srv->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200211 goto err;
212 }
Emeric Brun08622d32020-12-23 17:41:43 +0100213 LIST_ADDQ(&resolv_srvrq_list, &srvrq->list);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200214 return srvrq;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200215
Christopher Faulet67957bd2017-09-27 11:00:59 +0200216 err:
217 if (srvrq) {
218 free(srvrq->name);
219 free(srvrq->hostname_dn);
220 free(srvrq);
221 }
222 return NULL;
223}
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200224
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200225
Christopher Faulet67957bd2017-09-27 11:00:59 +0200226/* 2 bytes random generator to generate DNS query ID */
Emeric Brund30e9a12020-12-23 18:49:16 +0100227static inline uint16_t resolv_rnd16(void)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200228{
Emeric Brund30e9a12020-12-23 18:49:16 +0100229 if (!resolv_query_id_seed)
230 resolv_query_id_seed = now_ms;
231 resolv_query_id_seed ^= resolv_query_id_seed << 13;
232 resolv_query_id_seed ^= resolv_query_id_seed >> 7;
233 resolv_query_id_seed ^= resolv_query_id_seed << 17;
234 return resolv_query_id_seed;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200235}
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200236
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200237
Emeric Brund30e9a12020-12-23 18:49:16 +0100238static inline int resolv_resolution_timeout(struct resolv_resolution *res)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200239{
Baptiste Assmannf50e1ac2019-11-07 11:02:18 +0100240 return res->resolvers->timeout.resolve;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200241}
242
Christopher Faulet67957bd2017-09-27 11:00:59 +0200243/* Updates a resolvers' task timeout for next wake up and queue it */
Emeric Brund30e9a12020-12-23 18:49:16 +0100244static void resolv_update_resolvers_timeout(struct resolvers *resolvers)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200245{
Emeric Brun08622d32020-12-23 17:41:43 +0100246 struct resolv_resolution *res;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200247 int next;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200248
Christopher Faulet67957bd2017-09-27 11:00:59 +0200249 next = tick_add(now_ms, resolvers->timeout.resolve);
250 if (!LIST_ISEMPTY(&resolvers->resolutions.curr)) {
Emeric Brun08622d32020-12-23 17:41:43 +0100251 res = LIST_NEXT(&resolvers->resolutions.curr, struct resolv_resolution *, list);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200252 next = MIN(next, tick_add(res->last_query, resolvers->timeout.retry));
253 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200254
Christopher Faulet67957bd2017-09-27 11:00:59 +0200255 list_for_each_entry(res, &resolvers->resolutions.wait, list)
Emeric Brund30e9a12020-12-23 18:49:16 +0100256 next = MIN(next, tick_add(res->last_resolution, resolv_resolution_timeout(res)));
Baptiste Assmann325137d2015-04-13 23:40:55 +0200257
Christopher Faulet67957bd2017-09-27 11:00:59 +0200258 resolvers->t->expire = next;
259 task_queue(resolvers->t);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200260}
261
Christopher Faulet67957bd2017-09-27 11:00:59 +0200262/* Opens an UDP socket on the namesaver's IP/Port, if required. Returns 0 on
263 * success, -1 otherwise.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200264 */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200265static int dns_connect_namesaver(struct dns_nameserver *ns)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200266{
Christopher Faulet67957bd2017-09-27 11:00:59 +0200267 struct dgram_conn *dgram = ns->dgram;
268 int fd;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200269
Christopher Faulet67957bd2017-09-27 11:00:59 +0200270 /* Already connected */
271 if (dgram->t.sock.fd != -1)
272 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200273
Christopher Faulet67957bd2017-09-27 11:00:59 +0200274 /* Create an UDP socket and connect it on the nameserver's IP/Port */
275 if ((fd = socket(ns->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
276 send_log(NULL, LOG_WARNING,
277 "DNS : resolvers '%s': can't create socket for nameserver '%s'.\n",
Emeric Brun6a2006a2021-01-04 13:18:55 +0100278 ns->counters->pid, ns->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200279 return -1;
280 }
281 if (connect(fd, (struct sockaddr*)&ns->addr, get_addr_len(&ns->addr)) == -1) {
282 send_log(NULL, LOG_WARNING,
283 "DNS : resolvers '%s': can't connect socket for nameserver '%s'.\n",
Emeric Brun6a2006a2021-01-04 13:18:55 +0100284 ns->counters->id, ns->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200285 close(fd);
286 return -1;
287 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200288
Christopher Faulet67957bd2017-09-27 11:00:59 +0200289 /* Make the socket non blocking */
290 fcntl(fd, F_SETFL, O_NONBLOCK);
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200291
Christopher Faulet67957bd2017-09-27 11:00:59 +0200292 /* Add the fd in the fd list and update its parameters */
293 dgram->t.sock.fd = fd;
Willy Tarreaua9786b62018-01-25 07:22:13 +0100294 fd_insert(fd, dgram, dgram_fd_handler, MAX_THREADS_MASK);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200295 fd_want_recv(fd);
296 return 0;
297}
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200298
Christopher Faulet67957bd2017-09-27 11:00:59 +0200299/* Forges a DNS query. It needs the following information from the caller:
300 * - <query_id> : the DNS query id corresponding to this query
301 * - <query_type> : DNS_RTYPE_* request DNS record type (A, AAAA, ANY...)
302 * - <hostname_dn> : hostname in domain name format
303 * - <hostname_dn_len> : length of <hostname_dn>
304 *
305 * To store the query, the caller must pass a buffer <buf> and its size
306 * <bufsize>. It returns the number of written bytes in success, -1 if <buf> is
307 * too short.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200308 */
Emeric Brund30e9a12020-12-23 18:49:16 +0100309static int resolv_build_query(int query_id, int query_type, unsigned int accepted_payload_size,
310 char *hostname_dn, int hostname_dn_len, char *buf, int bufsize)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200311{
Christopher Faulet67957bd2017-09-27 11:00:59 +0200312 struct dns_header dns_hdr;
313 struct dns_question qinfo;
314 struct dns_additional_record edns;
315 char *p = buf;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +0200316
Christopher Faulet67957bd2017-09-27 11:00:59 +0200317 if (sizeof(dns_hdr) + sizeof(qinfo) + sizeof(edns) + hostname_dn_len >= bufsize)
318 return -1;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +0200319
Christopher Faulet67957bd2017-09-27 11:00:59 +0200320 memset(buf, 0, bufsize);
Baptiste Assmannfa4a6632017-05-04 09:05:00 +0200321
Christopher Faulet67957bd2017-09-27 11:00:59 +0200322 /* Set dns query headers */
323 dns_hdr.id = (unsigned short) htons(query_id);
324 dns_hdr.flags = htons(0x0100); /* qr=0, opcode=0, aa=0, tc=0, rd=1, ra=0, z=0, rcode=0 */
325 dns_hdr.qdcount = htons(1); /* 1 question */
326 dns_hdr.ancount = 0;
327 dns_hdr.nscount = 0;
328 dns_hdr.arcount = htons(1);
329 memcpy(p, &dns_hdr, sizeof(dns_hdr));
330 p += sizeof(dns_hdr);
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200331
Christopher Faulet67957bd2017-09-27 11:00:59 +0200332 /* Set up query hostname */
333 memcpy(p, hostname_dn, hostname_dn_len);
334 p += hostname_dn_len;
335 *p++ = 0;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200336
Christopher Faulet67957bd2017-09-27 11:00:59 +0200337 /* Set up query info (type and class) */
338 qinfo.qtype = htons(query_type);
339 qinfo.qclass = htons(DNS_RCLASS_IN);
340 memcpy(p, &qinfo, sizeof(qinfo));
341 p += sizeof(qinfo);
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200342
Christopher Faulet67957bd2017-09-27 11:00:59 +0200343 /* Set the DNS extension */
344 edns.name = 0;
345 edns.type = htons(DNS_RTYPE_OPT);
346 edns.udp_payload_size = htons(accepted_payload_size);
347 edns.extension = 0;
348 edns.data_length = 0;
349 memcpy(p, &edns, sizeof(edns));
350 p += sizeof(edns);
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200351
Christopher Faulet67957bd2017-09-27 11:00:59 +0200352 return (p - buf);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200353}
354
Christopher Faulet67957bd2017-09-27 11:00:59 +0200355/* Sends a DNS query to resolvers associated to a resolution. It returns 0 on
356 * success, -1 otherwise.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200357 */
Emeric Brund30e9a12020-12-23 18:49:16 +0100358static int resolv_send_query(struct resolv_resolution *resolution)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200359{
Emeric Brun750fe792020-12-23 16:51:12 +0100360 struct resolvers *resolvers = resolution->resolvers;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200361 struct dns_nameserver *ns;
Willy Tarreau0eae6322019-12-20 11:18:54 +0100362 int len;
363
364 /* Update resolution */
365 resolution->nb_queries = 0;
366 resolution->nb_responses = 0;
367 resolution->last_query = now_ms;
368
Emeric Brund30e9a12020-12-23 18:49:16 +0100369 len = resolv_build_query(resolution->query_id, resolution->query_type,
Willy Tarreau0eae6322019-12-20 11:18:54 +0100370 resolvers->accepted_payload_size,
371 resolution->hostname_dn, resolution->hostname_dn_len,
372 trash.area, trash.size);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200373
Christopher Faulet67957bd2017-09-27 11:00:59 +0200374 list_for_each_entry(ns, &resolvers->nameservers, list) {
375 int fd = ns->dgram->t.sock.fd;
Willy Tarreau0eae6322019-12-20 11:18:54 +0100376 int ret;
377
Christopher Faulet67957bd2017-09-27 11:00:59 +0200378 if (fd == -1) {
379 if (dns_connect_namesaver(ns) == -1)
380 continue;
381 fd = ns->dgram->t.sock.fd;
382 resolvers->nb_nameservers++;
383 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200384
Willy Tarreau0eae6322019-12-20 11:18:54 +0100385 if (len < 0)
386 goto snd_error;
387
388 ret = send(fd, trash.area, len, 0);
389 if (ret == len) {
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +0200390 ns->counters->sent++;
Willy Tarreau0eae6322019-12-20 11:18:54 +0100391 resolution->nb_queries++;
392 continue;
393 }
394
395 if (ret == -1 && errno == EAGAIN) {
396 /* retry once the socket is ready */
397 fd_cant_send(fd);
398 continue;
399 }
400
401 snd_error:
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +0200402 ns->counters->snd_error++;
Willy Tarreau0eae6322019-12-20 11:18:54 +0100403 resolution->nb_queries++;
404 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200405
Christopher Faulet67957bd2017-09-27 11:00:59 +0200406 /* Push the resolution at the end of the active list */
407 LIST_DEL(&resolution->list);
408 LIST_ADDQ(&resolvers->resolutions.curr, &resolution->list);
409 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200410}
411
Christopher Faulet67957bd2017-09-27 11:00:59 +0200412/* Prepares and sends a DNS resolution. It returns 1 if the query was sent, 0 if
413 * skipped and -1 if an error occurred.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200414 */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200415static int
Emeric Brund30e9a12020-12-23 18:49:16 +0100416resolv_run_resolution(struct resolv_resolution *resolution)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200417{
Emeric Brun750fe792020-12-23 16:51:12 +0100418 struct resolvers *resolvers = resolution->resolvers;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200419 int query_id, i;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200420
Christopher Faulet67957bd2017-09-27 11:00:59 +0200421 /* Avoid sending requests for resolutions that don't yet have an
422 * hostname, ie resolutions linked to servers that do not yet have an
423 * fqdn */
424 if (!resolution->hostname_dn)
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200425 return 0;
426
Christopher Faulet67957bd2017-09-27 11:00:59 +0200427 /* Check if a resolution has already been started for this server return
428 * directly to avoid resolution pill up. */
429 if (resolution->step != RSLV_STEP_NONE)
430 return 0;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200431
Christopher Faulet67957bd2017-09-27 11:00:59 +0200432 /* Generates a new query id. We try at most 100 times to find a free
433 * query id */
434 for (i = 0; i < 100; ++i) {
Emeric Brund30e9a12020-12-23 18:49:16 +0100435 query_id = resolv_rnd16();
Christopher Faulet67957bd2017-09-27 11:00:59 +0200436 if (!eb32_lookup(&resolvers->query_ids, query_id))
Olivier Houchard8da5f982017-08-04 18:35:36 +0200437 break;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200438 query_id = -1;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200439 }
Christopher Faulet67957bd2017-09-27 11:00:59 +0200440 if (query_id == -1) {
441 send_log(NULL, LOG_NOTICE,
442 "could not generate a query id for %s, in resolvers %s.\n",
443 resolution->hostname_dn, resolvers->id);
444 return -1;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200445 }
446
Christopher Faulet67957bd2017-09-27 11:00:59 +0200447 /* Update resolution parameters */
448 resolution->query_id = query_id;
449 resolution->qid.key = query_id;
450 resolution->step = RSLV_STEP_RUNNING;
451 resolution->query_type = resolution->prefered_query_type;
452 resolution->try = resolvers->resolve_retries;
453 eb32_insert(&resolvers->query_ids, &resolution->qid);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200454
Christopher Faulet67957bd2017-09-27 11:00:59 +0200455 /* Send the DNS query */
456 resolution->try -= 1;
Emeric Brund30e9a12020-12-23 18:49:16 +0100457 resolv_send_query(resolution);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200458 return 1;
459}
460
Christopher Faulet67957bd2017-09-27 11:00:59 +0200461/* Performs a name resolution for the requester <req> */
Emeric Brund30e9a12020-12-23 18:49:16 +0100462void resolv_trigger_resolution(struct resolv_requester *req)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200463{
Emeric Brun750fe792020-12-23 16:51:12 +0100464 struct resolvers *resolvers;
Emeric Brun08622d32020-12-23 17:41:43 +0100465 struct resolv_resolution *res;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200466 int exp;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200467
Christopher Faulet67957bd2017-09-27 11:00:59 +0200468 if (!req || !req->resolution)
469 return;
470 res = req->resolution;
471 resolvers = res->resolvers;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200472
Christopher Faulet67957bd2017-09-27 11:00:59 +0200473 /* The resolution must not be triggered yet. Use the cached response, if
474 * valid */
475 exp = tick_add(res->last_resolution, resolvers->hold.valid);
Olivier Houchardf3d9e602018-05-22 18:40:07 +0200476 if (resolvers->t && (res->status != RSLV_STATUS_VALID ||
477 !tick_isset(res->last_resolution) || tick_is_expired(exp, now_ms)))
478 task_wakeup(resolvers->t, TASK_WOKEN_OTHER);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200479}
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200480
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200481
Christopher Faulet67957bd2017-09-27 11:00:59 +0200482/* Resets some resolution parameters to initial values and also delete the query
483 * ID from the resolver's tree.
484 */
Emeric Brund30e9a12020-12-23 18:49:16 +0100485static void resolv_reset_resolution(struct resolv_resolution *resolution)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200486{
487 /* update resolution status */
488 resolution->step = RSLV_STEP_NONE;
489 resolution->try = 0;
490 resolution->last_resolution = now_ms;
491 resolution->nb_queries = 0;
492 resolution->nb_responses = 0;
493 resolution->query_type = resolution->prefered_query_type;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200494
Christopher Faulet67957bd2017-09-27 11:00:59 +0200495 /* clean up query id */
496 eb32_delete(&resolution->qid);
497 resolution->query_id = 0;
498 resolution->qid.key = 0;
499}
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200500
Christopher Faulet67957bd2017-09-27 11:00:59 +0200501/* Returns the query id contained in a DNS response */
Emeric Brund30e9a12020-12-23 18:49:16 +0100502static inline unsigned short resolv_response_get_query_id(unsigned char *resp)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200503{
504 return resp[0] * 256 + resp[1];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200505}
506
Christopher Faulet67957bd2017-09-27 11:00:59 +0200507
508/* Analyses, re-builds and copies the name <name> from the DNS response packet
509 * <buffer>. <name> must point to the 'data_len' information or pointer 'c0'
510 * for compressed data. The result is copied into <dest>, ensuring we don't
511 * overflow using <dest_len> Returns the number of bytes the caller can move
512 * forward. If 0 it means an error occurred while parsing the name. <offset> is
513 * the number of bytes the caller could move forward.
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200514 */
Emeric Brund30e9a12020-12-23 18:49:16 +0100515int resolv_read_name(unsigned char *buffer, unsigned char *bufend,
516 unsigned char *name, char *destination, int dest_len,
517 int *offset, unsigned int depth)
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200518{
519 int nb_bytes = 0, n = 0;
520 int label_len;
521 unsigned char *reader = name;
522 char *dest = destination;
523
524 while (1) {
Remi Gacogne2d19fbc2018-12-05 17:55:10 +0100525 if (reader >= bufend)
526 goto err;
527
Christopher Faulet67957bd2017-09-27 11:00:59 +0200528 /* Name compression is in use */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200529 if ((*reader & 0xc0) == 0xc0) {
Remi Gacogne2d19fbc2018-12-05 17:55:10 +0100530 if (reader + 1 >= bufend)
531 goto err;
532
Christopher Faulet67957bd2017-09-27 11:00:59 +0200533 /* Must point BEFORE current position */
534 if ((buffer + reader[1]) > reader)
535 goto err;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200536
Remi Gacogne58df5ae2018-12-05 17:52:54 +0100537 if (depth++ > 100)
538 goto err;
539
Emeric Brund30e9a12020-12-23 18:49:16 +0100540 n = resolv_read_name(buffer, bufend, buffer + (*reader & 0x3f)*256 + reader[1],
541 dest, dest_len - nb_bytes, offset, depth);
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200542 if (n == 0)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200543 goto err;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200544
Christopher Faulet67957bd2017-09-27 11:00:59 +0200545 dest += n;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200546 nb_bytes += n;
547 goto out;
548 }
549
550 label_len = *reader;
551 if (label_len == 0)
552 goto out;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200553
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200554 /* Check if:
555 * - we won't read outside the buffer
556 * - there is enough place in the destination
557 */
558 if ((reader + label_len >= bufend) || (nb_bytes + label_len >= dest_len))
Christopher Faulet67957bd2017-09-27 11:00:59 +0200559 goto err;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200560
561 /* +1 to take label len + label string */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200562 label_len++;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200563
564 memcpy(dest, reader, label_len);
565
Christopher Faulet67957bd2017-09-27 11:00:59 +0200566 dest += label_len;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200567 nb_bytes += label_len;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200568 reader += label_len;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200569 }
570
Christopher Faulet67957bd2017-09-27 11:00:59 +0200571 out:
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200572 /* offset computation:
573 * parse from <name> until finding either NULL or a pointer "c0xx"
574 */
Christopher Faulet67957bd2017-09-27 11:00:59 +0200575 reader = name;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200576 *offset = 0;
577 while (reader < bufend) {
578 if ((reader[0] & 0xc0) == 0xc0) {
579 *offset += 2;
580 break;
581 }
582 else if (*reader == 0) {
583 *offset += 1;
584 break;
585 }
586 *offset += 1;
587 ++reader;
588 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200589 return nb_bytes;
590
Christopher Faulet67957bd2017-09-27 11:00:59 +0200591 err:
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200592 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200593}
594
Christopher Faulet67957bd2017-09-27 11:00:59 +0200595/* Checks for any obsolete record, also identify any SRV request, and try to
596 * find a corresponding server.
597*/
Emeric Brund30e9a12020-12-23 18:49:16 +0100598static void resolv_check_response(struct resolv_resolution *res)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200599{
Emeric Brun750fe792020-12-23 16:51:12 +0100600 struct resolvers *resolvers = res->resolvers;
Emeric Brun08622d32020-12-23 17:41:43 +0100601 struct resolv_requester *req, *reqback;
Emeric Brun85914e92020-12-23 16:38:06 +0100602 struct resolv_answer_item *item, *itemback;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200603 struct server *srv;
Emeric Brun08622d32020-12-23 17:41:43 +0100604 struct resolv_srvrq *srvrq;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200605
606 list_for_each_entry_safe(item, itemback, &res->response.answer_list, list) {
Emeric Brun85914e92020-12-23 16:38:06 +0100607 struct resolv_answer_item *ar_item = item->ar_item;
Christopher Faulet5a891752020-09-08 10:06:01 +0200608
609 /* clean up obsolete Additional record */
610 if (ar_item && (ar_item->last_seen + resolvers->hold.obsolete / 1000) < now.tv_sec) {
Emeric Brun85914e92020-12-23 16:38:06 +0100611 pool_free(resolv_answer_item_pool, ar_item);
Christopher Faulet5a891752020-09-08 10:06:01 +0200612 item->ar_item = NULL;
613 }
Christopher Faulet67957bd2017-09-27 11:00:59 +0200614
615 /* Remove obsolete items */
616 if ((item->last_seen + resolvers->hold.obsolete / 1000) < now.tv_sec) {
617 if (item->type != DNS_RTYPE_SRV)
618 goto rm_obselete_item;
619
620 list_for_each_entry_safe(req, reqback, &res->requesters, list) {
Emeric Brun08622d32020-12-23 17:41:43 +0100621 if ((srvrq = objt_resolv_srvrq(req->owner)) == NULL)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200622 continue;
623
624 /* Remove any associated server */
625 for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100626 HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200627 if (srv->srvrq == srvrq && srv->svc_port == item->port &&
628 item->data_len == srv->hostname_dn_len &&
Emeric Brund30e9a12020-12-23 18:49:16 +0100629 !resolv_hostname_cmp(srv->hostname_dn, item->target, item->data_len)) {
Christopher Faulet67957bd2017-09-27 11:00:59 +0200630 snr_update_srv_status(srv, 1);
631 free(srv->hostname);
632 free(srv->hostname_dn);
633 srv->hostname = NULL;
634 srv->hostname_dn = NULL;
635 srv->hostname_dn_len = 0;
Emeric Brund30e9a12020-12-23 18:49:16 +0100636 resolv_unlink_resolution(srv->resolv_requester);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200637 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100638 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200639 }
640 }
641
642 rm_obselete_item:
643 LIST_DEL(&item->list);
Christopher Faulet5a891752020-09-08 10:06:01 +0200644 if (item->ar_item) {
Emeric Brun85914e92020-12-23 16:38:06 +0100645 pool_free(resolv_answer_item_pool, item->ar_item);
Christopher Faulet5a891752020-09-08 10:06:01 +0200646 item->ar_item = NULL;
647 }
Emeric Brun85914e92020-12-23 16:38:06 +0100648 pool_free(resolv_answer_item_pool, item);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200649 continue;
650 }
651
652 if (item->type != DNS_RTYPE_SRV)
653 continue;
654
655 /* Now process SRV records */
656 list_for_each_entry_safe(req, reqback, &res->requesters, list) {
Emeric Brun08622d32020-12-23 17:41:43 +0100657 if ((srvrq = objt_resolv_srvrq(req->owner)) == NULL)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200658 continue;
659
660 /* Check if a server already uses that hostname */
661 for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100662 HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200663 if (srv->srvrq == srvrq && srv->svc_port == item->port &&
664 item->data_len == srv->hostname_dn_len &&
Emeric Brund30e9a12020-12-23 18:49:16 +0100665 !resolv_hostname_cmp(srv->hostname_dn, item->target, item->data_len)) {
Christopher Faulet67957bd2017-09-27 11:00:59 +0200666 break;
667 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100668 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200669 }
Christopher Faulet67957bd2017-09-27 11:00:59 +0200670
671 /* If not, try to find a server with undefined hostname */
Christopher Fauletd6c6b5f2020-09-08 10:27:24 +0200672 if (!srv) {
673 for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
674 HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
675 if (srv->srvrq == srvrq && !srv->hostname_dn)
676 break;
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100677 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Olivier Houchard55dcdf42017-11-06 15:15:04 +0100678 }
Christopher Fauletd6c6b5f2020-09-08 10:27:24 +0200679 }
Baptiste Assmann13a92322019-06-07 09:40:55 +0200680
Christopher Fauletd6c6b5f2020-09-08 10:27:24 +0200681 /* And update this server, if found (srv is locked here) */
682 if (srv) {
Baptiste Assmann13a92322019-06-07 09:40:55 +0200683 /* Check if an Additional Record is associated to this SRV record.
684 * Perform some sanity checks too to ensure the record can be used.
685 * If all fine, we simply pick up the IP address found and associate
686 * it to the server.
687 */
688 if ((item->ar_item != NULL) &&
689 (item->ar_item->type == DNS_RTYPE_A || item->ar_item->type == DNS_RTYPE_AAAA))
690 {
691
692 switch (item->ar_item->type) {
693 case DNS_RTYPE_A:
Baptiste Assmanncde83032020-08-04 10:54:14 +0200694 update_server_addr(srv, &(((struct sockaddr_in*)&item->ar_item->address)->sin_addr), AF_INET, "DNS additional record");
Baptiste Assmann13a92322019-06-07 09:40:55 +0200695 break;
696 case DNS_RTYPE_AAAA:
Baptiste Assmanncde83032020-08-04 10:54:14 +0200697 update_server_addr(srv, &(((struct sockaddr_in6*)&item->ar_item->address)->sin6_addr), AF_INET6, "DNS additional record");
Baptiste Assmann13a92322019-06-07 09:40:55 +0200698 break;
699 }
700
701 srv->flags |= SRV_F_NO_RESOLUTION;
702 }
703
Christopher Fauletd6c6b5f2020-09-08 10:27:24 +0200704 if (!srv->hostname_dn) {
705 const char *msg = NULL;
706 char hostname[DNS_MAX_NAME_SIZE];
707
Emeric Brund30e9a12020-12-23 18:49:16 +0100708 if (resolv_dn_label_to_str(item->target, item->data_len+1,
Christopher Fauletd6c6b5f2020-09-08 10:27:24 +0200709 hostname, DNS_MAX_NAME_SIZE) == -1) {
710 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
711 continue;
712 }
713 msg = update_server_fqdn(srv, hostname, "SRV record", 1);
714 if (msg)
715 send_log(srv->proxy, LOG_NOTICE, "%s", msg);
716 }
Christopher Faulet67957bd2017-09-27 11:00:59 +0200717
Baptiste Assmann87138c32020-08-04 10:57:21 +0200718 /* now we have an IP address associated to this server, we can update its status */
719 snr_update_srv_status(srv, 0);
720
Christopher Faulet67957bd2017-09-27 11:00:59 +0200721 srv->svc_port = item->port;
722 srv->flags &= ~SRV_F_MAPPORTS;
Olivier Houchard2ec2db92018-01-08 16:28:57 +0100723
Emeric Brun21fbeed2020-12-23 18:01:04 +0100724 if (!srv->resolv_opts.ignore_weight) {
Christopher Fauletd6c6b5f2020-09-08 10:27:24 +0200725 char weight[9];
726 int ha_weight;
727
Daniel Corbettf8716912019-11-17 09:48:56 -0500728 /* DNS weight range if from 0 to 65535
729 * HAProxy weight is from 0 to 256
730 * The rule below ensures that weight 0 is well respected
731 * while allowing a "mapping" from DNS weight into HAProxy's one.
732 */
733 ha_weight = (item->weight + 255) / 256;
Olivier Houchard2ec2db92018-01-08 16:28:57 +0100734
Daniel Corbettf8716912019-11-17 09:48:56 -0500735 snprintf(weight, sizeof(weight), "%d", ha_weight);
736 server_parse_weight_change_request(srv, weight);
737 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100738 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +0200739 }
740 }
741 }
742}
743
744/* Validates that the buffer DNS response provided in <resp> and finishing
745 * before <bufend> is valid from a DNS protocol point of view.
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200746 *
Christopher Faulet67957bd2017-09-27 11:00:59 +0200747 * The result is stored in <resolution>' response, buf_response,
748 * response_query_records and response_answer_records members.
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200749 *
Emeric Brun30c766e2020-12-23 18:12:31 +0100750 * This function returns one of the RSLV_RESP_* code to indicate the type of
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200751 * error found.
Baptiste Assmann325137d2015-04-13 23:40:55 +0200752 */
Emeric Brund30e9a12020-12-23 18:49:16 +0100753static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufend,
754 struct resolv_resolution *resolution, int max_answer_records)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200755{
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200756 unsigned char *reader;
757 char *previous_dname, tmpname[DNS_MAX_NAME_SIZE];
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200758 int len, flags, offset;
Emeric Brun85914e92020-12-23 16:38:06 +0100759 int query_record_id;
Baptiste Assmann69fce672017-05-04 08:37:45 +0200760 int nb_saved_records;
Emeric Brun85914e92020-12-23 16:38:06 +0100761 struct resolv_query_item *query;
762 struct resolv_answer_item *answer_record, *tmp_record;
763 struct resolv_response *r_res;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200764 int i, found = 0;
Emeric Brun30c766e2020-12-23 18:12:31 +0100765 int cause = RSLV_RESP_ERROR;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200766
Christopher Faulet67957bd2017-09-27 11:00:59 +0200767 reader = resp;
768 len = 0;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200769 previous_dname = NULL;
Emeric Brun85914e92020-12-23 16:38:06 +0100770 query = NULL;
771 answer_record = NULL;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200772
Christopher Faulet67957bd2017-09-27 11:00:59 +0200773 /* Initialization of response buffer and structure */
Emeric Brun85914e92020-12-23 16:38:06 +0100774 r_res = &resolution->response;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200775
776 /* query id */
777 if (reader + 2 >= bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +0200778 goto invalid_resp;
779
Emeric Brun85914e92020-12-23 16:38:06 +0100780 r_res->header.id = reader[0] * 256 + reader[1];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200781 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200782
Christopher Faulet67957bd2017-09-27 11:00:59 +0200783 /* Flags and rcode are stored over 2 bytes
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200784 * First byte contains:
785 * - response flag (1 bit)
786 * - opcode (4 bits)
787 * - authoritative (1 bit)
788 * - truncated (1 bit)
789 * - recursion desired (1 bit)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200790 */
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200791 if (reader + 2 >= bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +0200792 goto invalid_resp;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200793
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200794 flags = reader[0] * 256 + reader[1];
795
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200796 if ((flags & DNS_FLAG_REPLYCODE) != DNS_RCODE_NO_ERROR) {
Willy Tarreau963f7012020-07-22 17:00:45 +0200797 if ((flags & DNS_FLAG_REPLYCODE) == DNS_RCODE_NX_DOMAIN) {
Emeric Brun30c766e2020-12-23 18:12:31 +0100798 cause = RSLV_RESP_NX_DOMAIN;
Willy Tarreau963f7012020-07-22 17:00:45 +0200799 goto return_error;
800 }
801 else if ((flags & DNS_FLAG_REPLYCODE) == DNS_RCODE_REFUSED) {
Emeric Brun30c766e2020-12-23 18:12:31 +0100802 cause = RSLV_RESP_REFUSED;
Willy Tarreau963f7012020-07-22 17:00:45 +0200803 goto return_error;
804 }
805 else {
Emeric Brun30c766e2020-12-23 18:12:31 +0100806 cause = RSLV_RESP_ERROR;
Willy Tarreau963f7012020-07-22 17:00:45 +0200807 goto return_error;
808 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200809 }
810
Christopher Faulet67957bd2017-09-27 11:00:59 +0200811 /* Move forward 2 bytes for flags */
Baptiste Assmann3440f0d2015-09-02 22:08:38 +0200812 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200813
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200814 /* 2 bytes for question count */
815 if (reader + 2 >= bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +0200816 goto invalid_resp;
Emeric Brun85914e92020-12-23 16:38:06 +0100817 r_res->header.qdcount = reader[0] * 256 + reader[1];
Christopher Faulet67957bd2017-09-27 11:00:59 +0200818 /* (for now) we send one query only, so we expect only one in the
819 * response too */
Emeric Brun85914e92020-12-23 16:38:06 +0100820 if (r_res->header.qdcount != 1) {
Emeric Brun30c766e2020-12-23 18:12:31 +0100821 cause = RSLV_RESP_QUERY_COUNT_ERROR;
Willy Tarreau963f7012020-07-22 17:00:45 +0200822 goto return_error;
823 }
824
Emeric Brun85914e92020-12-23 16:38:06 +0100825 if (r_res->header.qdcount > DNS_MAX_QUERY_RECORDS)
Willy Tarreau963f7012020-07-22 17:00:45 +0200826 goto invalid_resp;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200827 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200828
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200829 /* 2 bytes for answer count */
830 if (reader + 2 >= bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +0200831 goto invalid_resp;
Emeric Brun85914e92020-12-23 16:38:06 +0100832 r_res->header.ancount = reader[0] * 256 + reader[1];
833 if (r_res->header.ancount == 0) {
Emeric Brun30c766e2020-12-23 18:12:31 +0100834 cause = RSLV_RESP_ANCOUNT_ZERO;
Willy Tarreau963f7012020-07-22 17:00:45 +0200835 goto return_error;
836 }
837
Christopher Faulet67957bd2017-09-27 11:00:59 +0200838 /* Check if too many records are announced */
Emeric Brun85914e92020-12-23 16:38:06 +0100839 if (r_res->header.ancount > max_answer_records)
Willy Tarreau963f7012020-07-22 17:00:45 +0200840 goto invalid_resp;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200841 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200842
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200843 /* 2 bytes authority count */
844 if (reader + 2 >= bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +0200845 goto invalid_resp;
Emeric Brun85914e92020-12-23 16:38:06 +0100846 r_res->header.nscount = reader[0] * 256 + reader[1];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200847 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200848
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200849 /* 2 bytes additional count */
850 if (reader + 2 >= bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +0200851 goto invalid_resp;
Emeric Brun85914e92020-12-23 16:38:06 +0100852 r_res->header.arcount = reader[0] * 256 + reader[1];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200853 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200854
Christopher Faulet67957bd2017-09-27 11:00:59 +0200855 /* Parsing dns queries */
Emeric Brun85914e92020-12-23 16:38:06 +0100856 LIST_INIT(&r_res->query_list);
857 for (query_record_id = 0; query_record_id < r_res->header.qdcount; query_record_id++) {
858 /* Use next pre-allocated resolv_query_item after ensuring there is
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200859 * still one available.
Christopher Faulet67957bd2017-09-27 11:00:59 +0200860 * It's then added to our packet query list. */
Emeric Brun85914e92020-12-23 16:38:06 +0100861 if (query_record_id > DNS_MAX_QUERY_RECORDS)
Willy Tarreau963f7012020-07-22 17:00:45 +0200862 goto invalid_resp;
Emeric Brun85914e92020-12-23 16:38:06 +0100863 query = &resolution->response_query_records[query_record_id];
864 LIST_ADDQ(&r_res->query_list, &query->list);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200865
Christopher Faulet67957bd2017-09-27 11:00:59 +0200866 /* Name is a NULL terminated string in our case, since we have
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200867 * one query per response and the first one can't be compressed
Christopher Faulet67957bd2017-09-27 11:00:59 +0200868 * (using the 0x0c format) */
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200869 offset = 0;
Emeric Brund30e9a12020-12-23 18:49:16 +0100870 len = resolv_read_name(resp, bufend, reader, query->name, DNS_MAX_NAME_SIZE, &offset, 0);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200871
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200872 if (len == 0)
Willy Tarreau963f7012020-07-22 17:00:45 +0200873 goto invalid_resp;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200874
875 reader += offset;
Emeric Brun85914e92020-12-23 16:38:06 +0100876 previous_dname = query->name;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200877
878 /* move forward 2 bytes for question type */
879 if (reader + 2 >= bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +0200880 goto invalid_resp;
Emeric Brun85914e92020-12-23 16:38:06 +0100881 query->type = reader[0] * 256 + reader[1];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200882 reader += 2;
883
884 /* move forward 2 bytes for question class */
885 if (reader + 2 >= bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +0200886 goto invalid_resp;
Emeric Brun85914e92020-12-23 16:38:06 +0100887 query->class = reader[0] * 256 + reader[1];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200888 reader += 2;
889 }
Baptiste Assmann325137d2015-04-13 23:40:55 +0200890
Baptiste Assmann251abb92017-08-11 09:58:27 +0200891 /* TRUNCATED flag must be checked after we could read the query type
Christopher Faulet67957bd2017-09-27 11:00:59 +0200892 * because a TRUNCATED SRV query type response can still be exploited */
Emeric Brun85914e92020-12-23 16:38:06 +0100893 if (query->type != DNS_RTYPE_SRV && flags & DNS_FLAG_TRUNCATED) {
Emeric Brun30c766e2020-12-23 18:12:31 +0100894 cause = RSLV_RESP_TRUNCATED;
Willy Tarreau963f7012020-07-22 17:00:45 +0200895 goto return_error;
896 }
Baptiste Assmann251abb92017-08-11 09:58:27 +0200897
Baptiste Assmann325137d2015-04-13 23:40:55 +0200898 /* now parsing response records */
Baptiste Assmann69fce672017-05-04 08:37:45 +0200899 nb_saved_records = 0;
Emeric Brun85914e92020-12-23 16:38:06 +0100900 for (i = 0; i < r_res->header.ancount; i++) {
Baptiste Assmann325137d2015-04-13 23:40:55 +0200901 if (reader >= bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +0200902 goto invalid_resp;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200903
Emeric Brun85914e92020-12-23 16:38:06 +0100904 answer_record = pool_alloc(resolv_answer_item_pool);
905 if (answer_record == NULL)
Willy Tarreau963f7012020-07-22 17:00:45 +0200906 goto invalid_resp;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200907
Baptiste Assmann65547422021-01-15 17:01:24 +0100908 /* initialization */
Emeric Brun85914e92020-12-23 16:38:06 +0100909 answer_record->ar_item = NULL;
Baptiste Assmann65547422021-01-15 17:01:24 +0100910
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200911 offset = 0;
Emeric Brund30e9a12020-12-23 18:49:16 +0100912 len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200913
Willy Tarreau963f7012020-07-22 17:00:45 +0200914 if (len == 0)
915 goto invalid_resp;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200916
Christopher Faulet67957bd2017-09-27 11:00:59 +0200917 /* Check if the current record dname is valid. previous_dname
918 * points either to queried dname or last CNAME target */
Emeric Brund30e9a12020-12-23 18:49:16 +0100919 if (query->type != DNS_RTYPE_SRV && resolv_hostname_cmp(previous_dname, tmpname, len) != 0) {
Olivier Houcharda8c6db82017-07-06 18:46:47 +0200920 if (i == 0) {
Christopher Faulet67957bd2017-09-27 11:00:59 +0200921 /* First record, means a mismatch issue between
922 * queried dname and dname found in the first
923 * record */
Willy Tarreau963f7012020-07-22 17:00:45 +0200924 goto invalid_resp;
Christopher Faulet67957bd2017-09-27 11:00:59 +0200925 }
926 else {
927 /* If not the first record, this means we have a
Willy Tarreau963f7012020-07-22 17:00:45 +0200928 * CNAME resolution error.
929 */
Emeric Brun30c766e2020-12-23 18:12:31 +0100930 cause = RSLV_RESP_CNAME_ERROR;
Willy Tarreau963f7012020-07-22 17:00:45 +0200931 goto return_error;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200932 }
933
Baptiste Assmann325137d2015-04-13 23:40:55 +0200934 }
935
Emeric Brun85914e92020-12-23 16:38:06 +0100936 memcpy(answer_record->name, tmpname, len);
937 answer_record->name[len] = 0;
Baptiste Assmann2359ff12015-08-07 11:24:05 +0200938
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200939 reader += offset;
Willy Tarreau963f7012020-07-22 17:00:45 +0200940 if (reader >= bufend)
941 goto invalid_resp;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200942
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200943 /* 2 bytes for record type (A, AAAA, CNAME, etc...) */
Willy Tarreau963f7012020-07-22 17:00:45 +0200944 if (reader + 2 > bufend)
945 goto invalid_resp;
946
Emeric Brun85914e92020-12-23 16:38:06 +0100947 answer_record->type = reader[0] * 256 + reader[1];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200948 reader += 2;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200949
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200950 /* 2 bytes for class (2) */
Willy Tarreau963f7012020-07-22 17:00:45 +0200951 if (reader + 2 > bufend)
952 goto invalid_resp;
953
Emeric Brun85914e92020-12-23 16:38:06 +0100954 answer_record->class = reader[0] * 256 + reader[1];
Baptiste Assmann325137d2015-04-13 23:40:55 +0200955 reader += 2;
956
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200957 /* 4 bytes for ttl (4) */
Willy Tarreau963f7012020-07-22 17:00:45 +0200958 if (reader + 4 > bufend)
959 goto invalid_resp;
960
Emeric Brun85914e92020-12-23 16:38:06 +0100961 answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536
962 + reader[2] * 256 + reader[3];
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200963 reader += 4;
Baptiste Assmann325137d2015-04-13 23:40:55 +0200964
Christopher Faulet67957bd2017-09-27 11:00:59 +0200965 /* Now reading data len */
Willy Tarreau963f7012020-07-22 17:00:45 +0200966 if (reader + 2 > bufend)
967 goto invalid_resp;
968
Emeric Brun85914e92020-12-23 16:38:06 +0100969 answer_record->data_len = reader[0] * 256 + reader[1];
Baptiste Assmann325137d2015-04-13 23:40:55 +0200970
Christopher Faulet67957bd2017-09-27 11:00:59 +0200971 /* Move forward 2 bytes for data len */
Baptiste Assmann325137d2015-04-13 23:40:55 +0200972 reader += 2;
973
Emeric Brun85914e92020-12-23 16:38:06 +0100974 if (reader + answer_record->data_len > bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +0200975 goto invalid_resp;
Remi Gacogneefbbdf72018-12-05 17:56:29 +0100976
Christopher Faulet67957bd2017-09-27 11:00:59 +0200977 /* Analyzing record content */
Emeric Brun85914e92020-12-23 16:38:06 +0100978 switch (answer_record->type) {
Baptiste Assmann325137d2015-04-13 23:40:55 +0200979 case DNS_RTYPE_A:
980 /* ipv4 is stored on 4 bytes */
Emeric Brun85914e92020-12-23 16:38:06 +0100981 if (answer_record->data_len != 4)
Willy Tarreau963f7012020-07-22 17:00:45 +0200982 goto invalid_resp;
983
Emeric Brun85914e92020-12-23 16:38:06 +0100984 answer_record->address.sa_family = AF_INET;
985 memcpy(&(((struct sockaddr_in *)&answer_record->address)->sin_addr),
986 reader, answer_record->data_len);
Baptiste Assmann325137d2015-04-13 23:40:55 +0200987 break;
988
989 case DNS_RTYPE_CNAME:
Christopher Faulet67957bd2017-09-27 11:00:59 +0200990 /* Check if this is the last record and update the caller about the status:
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200991 * no IP could be found and last record was a CNAME. Could be triggered
992 * by a wrong query type
993 *
Emeric Brun85914e92020-12-23 16:38:06 +0100994 * + 1 because answer_record_id starts at 0
Christopher Faulet67957bd2017-09-27 11:00:59 +0200995 * while number of answers is an integer and
996 * starts at 1.
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200997 */
Emeric Brun85914e92020-12-23 16:38:06 +0100998 if (i + 1 == r_res->header.ancount) {
Emeric Brun30c766e2020-12-23 18:12:31 +0100999 cause = RSLV_RESP_CNAME_ERROR;
Willy Tarreau963f7012020-07-22 17:00:45 +02001000 goto return_error;
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001001 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +02001002
1003 offset = 0;
Emeric Brund30e9a12020-12-23 18:49:16 +01001004 len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
Willy Tarreau963f7012020-07-22 17:00:45 +02001005 if (len == 0)
1006 goto invalid_resp;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +02001007
Emeric Brun85914e92020-12-23 16:38:06 +01001008 memcpy(answer_record->target, tmpname, len);
1009 answer_record->target[len] = 0;
1010 previous_dname = answer_record->target;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001011 break;
1012
Olivier Houchard8da5f982017-08-04 18:35:36 +02001013
1014 case DNS_RTYPE_SRV:
Christopher Faulet67957bd2017-09-27 11:00:59 +02001015 /* Answer must contain :
Olivier Houchard8da5f982017-08-04 18:35:36 +02001016 * - 2 bytes for the priority
1017 * - 2 bytes for the weight
1018 * - 2 bytes for the port
1019 * - the target hostname
1020 */
Emeric Brun85914e92020-12-23 16:38:06 +01001021 if (answer_record->data_len <= 6)
Willy Tarreau963f7012020-07-22 17:00:45 +02001022 goto invalid_resp;
1023
Emeric Brun85914e92020-12-23 16:38:06 +01001024 answer_record->priority = read_n16(reader);
Olivier Houchard8da5f982017-08-04 18:35:36 +02001025 reader += sizeof(uint16_t);
Emeric Brun85914e92020-12-23 16:38:06 +01001026 answer_record->weight = read_n16(reader);
Olivier Houchard8da5f982017-08-04 18:35:36 +02001027 reader += sizeof(uint16_t);
Emeric Brun85914e92020-12-23 16:38:06 +01001028 answer_record->port = read_n16(reader);
Olivier Houchard8da5f982017-08-04 18:35:36 +02001029 reader += sizeof(uint16_t);
1030 offset = 0;
Emeric Brund30e9a12020-12-23 18:49:16 +01001031 len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
Willy Tarreau963f7012020-07-22 17:00:45 +02001032 if (len == 0)
1033 goto invalid_resp;
1034
Emeric Brun85914e92020-12-23 16:38:06 +01001035 answer_record->data_len = len;
1036 memcpy(answer_record->target, tmpname, len);
1037 answer_record->target[len] = 0;
1038 if (answer_record->ar_item != NULL) {
1039 pool_free(resolv_answer_item_pool, answer_record->ar_item);
1040 answer_record->ar_item = NULL;
Baptiste Assmann65547422021-01-15 17:01:24 +01001041 }
Olivier Houchard8da5f982017-08-04 18:35:36 +02001042 break;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001043
Baptiste Assmann325137d2015-04-13 23:40:55 +02001044 case DNS_RTYPE_AAAA:
1045 /* ipv6 is stored on 16 bytes */
Emeric Brun85914e92020-12-23 16:38:06 +01001046 if (answer_record->data_len != 16)
Willy Tarreau963f7012020-07-22 17:00:45 +02001047 goto invalid_resp;
1048
Emeric Brun85914e92020-12-23 16:38:06 +01001049 answer_record->address.sa_family = AF_INET6;
1050 memcpy(&(((struct sockaddr_in6 *)&answer_record->address)->sin6_addr),
1051 reader, answer_record->data_len);
Baptiste Assmann325137d2015-04-13 23:40:55 +02001052 break;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +02001053
Baptiste Assmann325137d2015-04-13 23:40:55 +02001054 } /* switch (record type) */
1055
Christopher Faulet67957bd2017-09-27 11:00:59 +02001056 /* Increment the counter for number of records saved into our
1057 * local response */
1058 nb_saved_records++;
Baptiste Assmann69fce672017-05-04 08:37:45 +02001059
Emeric Brun85914e92020-12-23 16:38:06 +01001060 /* Move forward answer_record->data_len for analyzing next
Christopher Faulet67957bd2017-09-27 11:00:59 +02001061 * record in the response */
Emeric Brun85914e92020-12-23 16:38:06 +01001062 reader += ((answer_record->type == DNS_RTYPE_SRV)
Christopher Faulet67957bd2017-09-27 11:00:59 +02001063 ? offset
Emeric Brun85914e92020-12-23 16:38:06 +01001064 : answer_record->data_len);
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001065
1066 /* Lookup to see if we already had this entry */
Olivier Houchard8da5f982017-08-04 18:35:36 +02001067 found = 0;
Emeric Brun85914e92020-12-23 16:38:06 +01001068 list_for_each_entry(tmp_record, &r_res->answer_list, list) {
1069 if (tmp_record->type != answer_record->type)
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001070 continue;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001071
1072 switch(tmp_record->type) {
1073 case DNS_RTYPE_A:
Emeric Brun85914e92020-12-23 16:38:06 +01001074 if (!memcmp(&((struct sockaddr_in *)&answer_record->address)->sin_addr,
Christopher Faulet67957bd2017-09-27 11:00:59 +02001075 &((struct sockaddr_in *)&tmp_record->address)->sin_addr,
1076 sizeof(in_addr_t)))
1077 found = 1;
1078 break;
1079
1080 case DNS_RTYPE_AAAA:
Emeric Brun85914e92020-12-23 16:38:06 +01001081 if (!memcmp(&((struct sockaddr_in6 *)&answer_record->address)->sin6_addr,
Christopher Faulet67957bd2017-09-27 11:00:59 +02001082 &((struct sockaddr_in6 *)&tmp_record->address)->sin6_addr,
1083 sizeof(struct in6_addr)))
1084 found = 1;
1085 break;
1086
Olivier Houchard8da5f982017-08-04 18:35:36 +02001087 case DNS_RTYPE_SRV:
Emeric Brun85914e92020-12-23 16:38:06 +01001088 if (answer_record->data_len == tmp_record->data_len &&
Emeric Brund30e9a12020-12-23 18:49:16 +01001089 !resolv_hostname_cmp(answer_record->target, tmp_record->target, answer_record->data_len) &&
Emeric Brun85914e92020-12-23 16:38:06 +01001090 answer_record->port == tmp_record->port) {
1091 tmp_record->weight = answer_record->weight;
Olivier Houchard8da5f982017-08-04 18:35:36 +02001092 found = 1;
1093 }
1094 break;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001095
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001096 default:
1097 break;
1098 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001099
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001100 if (found == 1)
1101 break;
1102 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001103
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001104 if (found == 1) {
1105 tmp_record->last_seen = now.tv_sec;
Emeric Brun85914e92020-12-23 16:38:06 +01001106 pool_free(resolv_answer_item_pool, answer_record);
1107 answer_record = NULL;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001108 }
1109 else {
Emeric Brun85914e92020-12-23 16:38:06 +01001110 answer_record->last_seen = now.tv_sec;
1111 answer_record->ar_item = NULL;
1112 LIST_ADDQ(&r_res->answer_list, &answer_record->list);
1113 answer_record = NULL;
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001114 }
Baptiste Assmann325137d2015-04-13 23:40:55 +02001115 } /* for i 0 to ancount */
1116
Christopher Faulet67957bd2017-09-27 11:00:59 +02001117 /* Save the number of records we really own */
Emeric Brun85914e92020-12-23 16:38:06 +01001118 r_res->header.ancount = nb_saved_records;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001119
Baptiste Assmann37950c82020-02-19 01:08:51 +01001120 /* now parsing additional records for SRV queries only */
Emeric Brun85914e92020-12-23 16:38:06 +01001121 if (query->type != DNS_RTYPE_SRV)
Baptiste Assmann37950c82020-02-19 01:08:51 +01001122 goto skip_parsing_additional_records;
Willy Tarreau963f7012020-07-22 17:00:45 +02001123
Jerome Magnin4002f8d2020-07-26 12:13:12 +02001124 /* if we find Authority records, just skip them */
Emeric Brun85914e92020-12-23 16:38:06 +01001125 for (i = 0; i < r_res->header.nscount; i++) {
Jerome Magnin4002f8d2020-07-26 12:13:12 +02001126 offset = 0;
Emeric Brund30e9a12020-12-23 18:49:16 +01001127 len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE,
Jerome Magnin4002f8d2020-07-26 12:13:12 +02001128 &offset, 0);
1129 if (len == 0)
1130 continue;
1131
1132 if (reader + offset + 10 >= bufend)
1133 goto invalid_resp;
1134
1135 reader += offset;
1136 /* skip 2 bytes for class */
1137 reader += 2;
1138 /* skip 2 bytes for type */
1139 reader += 2;
1140 /* skip 4 bytes for ttl */
1141 reader += 4;
1142 /* read data len */
1143 len = reader[0] * 256 + reader[1];
1144 reader += 2;
1145
1146 if (reader + len >= bufend)
1147 goto invalid_resp;
1148
1149 reader += len;
1150 }
1151
Baptiste Assmann13a92322019-06-07 09:40:55 +02001152 nb_saved_records = 0;
Emeric Brun85914e92020-12-23 16:38:06 +01001153 for (i = 0; i < r_res->header.arcount; i++) {
Baptiste Assmann13a92322019-06-07 09:40:55 +02001154 if (reader >= bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +02001155 goto invalid_resp;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001156
Emeric Brun85914e92020-12-23 16:38:06 +01001157 answer_record = pool_alloc(resolv_answer_item_pool);
1158 if (answer_record == NULL)
Willy Tarreau963f7012020-07-22 17:00:45 +02001159 goto invalid_resp;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001160
1161 offset = 0;
Emeric Brund30e9a12020-12-23 18:49:16 +01001162 len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
Baptiste Assmann13a92322019-06-07 09:40:55 +02001163
1164 if (len == 0) {
Emeric Brun85914e92020-12-23 16:38:06 +01001165 pool_free(resolv_answer_item_pool, answer_record);
1166 answer_record = NULL;
Baptiste Assmann37950c82020-02-19 01:08:51 +01001167 continue;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001168 }
1169
Emeric Brun85914e92020-12-23 16:38:06 +01001170 memcpy(answer_record->name, tmpname, len);
1171 answer_record->name[len] = 0;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001172
1173 reader += offset;
Willy Tarreau963f7012020-07-22 17:00:45 +02001174 if (reader >= bufend)
1175 goto invalid_resp;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001176
1177 /* 2 bytes for record type (A, AAAA, CNAME, etc...) */
Willy Tarreau963f7012020-07-22 17:00:45 +02001178 if (reader + 2 > bufend)
1179 goto invalid_resp;
1180
Emeric Brun85914e92020-12-23 16:38:06 +01001181 answer_record->type = reader[0] * 256 + reader[1];
Baptiste Assmann13a92322019-06-07 09:40:55 +02001182 reader += 2;
1183
1184 /* 2 bytes for class (2) */
Willy Tarreau963f7012020-07-22 17:00:45 +02001185 if (reader + 2 > bufend)
1186 goto invalid_resp;
1187
Emeric Brun85914e92020-12-23 16:38:06 +01001188 answer_record->class = reader[0] * 256 + reader[1];
Baptiste Assmann13a92322019-06-07 09:40:55 +02001189 reader += 2;
1190
1191 /* 4 bytes for ttl (4) */
Willy Tarreau963f7012020-07-22 17:00:45 +02001192 if (reader + 4 > bufend)
1193 goto invalid_resp;
1194
Emeric Brun85914e92020-12-23 16:38:06 +01001195 answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536
1196 + reader[2] * 256 + reader[3];
Baptiste Assmann13a92322019-06-07 09:40:55 +02001197 reader += 4;
1198
1199 /* Now reading data len */
Willy Tarreau963f7012020-07-22 17:00:45 +02001200 if (reader + 2 > bufend)
1201 goto invalid_resp;
1202
Emeric Brun85914e92020-12-23 16:38:06 +01001203 answer_record->data_len = reader[0] * 256 + reader[1];
Baptiste Assmann13a92322019-06-07 09:40:55 +02001204
1205 /* Move forward 2 bytes for data len */
1206 reader += 2;
1207
Emeric Brun85914e92020-12-23 16:38:06 +01001208 if (reader + answer_record->data_len > bufend)
Willy Tarreau963f7012020-07-22 17:00:45 +02001209 goto invalid_resp;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001210
1211 /* Analyzing record content */
Emeric Brun85914e92020-12-23 16:38:06 +01001212 switch (answer_record->type) {
Baptiste Assmann13a92322019-06-07 09:40:55 +02001213 case DNS_RTYPE_A:
1214 /* ipv4 is stored on 4 bytes */
Emeric Brun85914e92020-12-23 16:38:06 +01001215 if (answer_record->data_len != 4)
Willy Tarreau963f7012020-07-22 17:00:45 +02001216 goto invalid_resp;
1217
Emeric Brun85914e92020-12-23 16:38:06 +01001218 answer_record->address.sa_family = AF_INET;
1219 memcpy(&(((struct sockaddr_in *)&answer_record->address)->sin_addr),
1220 reader, answer_record->data_len);
Baptiste Assmann13a92322019-06-07 09:40:55 +02001221 break;
1222
1223 case DNS_RTYPE_AAAA:
1224 /* ipv6 is stored on 16 bytes */
Emeric Brun85914e92020-12-23 16:38:06 +01001225 if (answer_record->data_len != 16)
Willy Tarreau963f7012020-07-22 17:00:45 +02001226 goto invalid_resp;
1227
Emeric Brun85914e92020-12-23 16:38:06 +01001228 answer_record->address.sa_family = AF_INET6;
1229 memcpy(&(((struct sockaddr_in6 *)&answer_record->address)->sin6_addr),
1230 reader, answer_record->data_len);
Baptiste Assmann13a92322019-06-07 09:40:55 +02001231 break;
1232
1233 default:
Emeric Brun85914e92020-12-23 16:38:06 +01001234 pool_free(resolv_answer_item_pool, answer_record);
1235 answer_record = NULL;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001236 continue;
1237
1238 } /* switch (record type) */
1239
1240 /* Increment the counter for number of records saved into our
1241 * local response */
1242 nb_saved_records++;
1243
Emeric Brun85914e92020-12-23 16:38:06 +01001244 /* Move forward answer_record->data_len for analyzing next
Baptiste Assmann13a92322019-06-07 09:40:55 +02001245 * record in the response */
Emeric Brun85914e92020-12-23 16:38:06 +01001246 reader += ((answer_record->type == DNS_RTYPE_SRV)
Baptiste Assmann13a92322019-06-07 09:40:55 +02001247 ? offset
Emeric Brun85914e92020-12-23 16:38:06 +01001248 : answer_record->data_len);
Baptiste Assmann13a92322019-06-07 09:40:55 +02001249
1250 /* Lookup to see if we already had this entry */
1251 found = 0;
Emeric Brun85914e92020-12-23 16:38:06 +01001252 list_for_each_entry(tmp_record, &r_res->answer_list, list) {
1253 if (tmp_record->type != answer_record->type)
Baptiste Assmann13a92322019-06-07 09:40:55 +02001254 continue;
1255
1256 switch(tmp_record->type) {
1257 case DNS_RTYPE_A:
Emeric Brun85914e92020-12-23 16:38:06 +01001258 if (!memcmp(&((struct sockaddr_in *)&answer_record->address)->sin_addr,
Baptiste Assmann13a92322019-06-07 09:40:55 +02001259 &((struct sockaddr_in *)&tmp_record->address)->sin_addr,
1260 sizeof(in_addr_t)))
1261 found = 1;
1262 break;
1263
1264 case DNS_RTYPE_AAAA:
Emeric Brun85914e92020-12-23 16:38:06 +01001265 if (!memcmp(&((struct sockaddr_in6 *)&answer_record->address)->sin6_addr,
Baptiste Assmann13a92322019-06-07 09:40:55 +02001266 &((struct sockaddr_in6 *)&tmp_record->address)->sin6_addr,
1267 sizeof(struct in6_addr)))
1268 found = 1;
1269 break;
1270
1271 default:
1272 break;
1273 }
1274
1275 if (found == 1)
1276 break;
1277 }
1278
1279 if (found == 1) {
1280 tmp_record->last_seen = now.tv_sec;
Emeric Brun85914e92020-12-23 16:38:06 +01001281 pool_free(resolv_answer_item_pool, answer_record);
1282 answer_record = NULL;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001283 }
1284 else {
Emeric Brun85914e92020-12-23 16:38:06 +01001285 answer_record->last_seen = now.tv_sec;
1286 answer_record->ar_item = NULL;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001287
1288 // looking for the SRV record in the response list linked to this additional record
Emeric Brun85914e92020-12-23 16:38:06 +01001289 list_for_each_entry(tmp_record, &r_res->answer_list, list) {
Christopher Faulet5a891752020-09-08 10:06:01 +02001290 if (tmp_record->type == DNS_RTYPE_SRV &&
Baptiste Assmann65547422021-01-15 17:01:24 +01001291 tmp_record->ar_item == NULL &&
Emeric Brund30e9a12020-12-23 18:49:16 +01001292 !resolv_hostname_cmp(tmp_record->target, answer_record->name, tmp_record->data_len)) {
Christopher Faulet5a891752020-09-08 10:06:01 +02001293 /* Always use the received additional record to refresh info */
Emeric Brun85914e92020-12-23 16:38:06 +01001294 if (tmp_record->ar_item)
1295 pool_free(resolv_answer_item_pool, tmp_record->ar_item);
1296 tmp_record->ar_item = answer_record;
Christopher Faulet5a891752020-09-08 10:06:01 +02001297 break;
1298 }
Baptiste Assmann13a92322019-06-07 09:40:55 +02001299 }
Emeric Brun85914e92020-12-23 16:38:06 +01001300 if (tmp_record->ar_item != answer_record)
1301 pool_free(resolv_answer_item_pool, answer_record);
1302 answer_record = NULL;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001303 }
1304 } /* for i 0 to arcount */
1305
Baptiste Assmann37950c82020-02-19 01:08:51 +01001306 skip_parsing_additional_records:
1307
Baptiste Assmann13a92322019-06-07 09:40:55 +02001308 /* Save the number of records we really own */
Emeric Brun85914e92020-12-23 16:38:06 +01001309 r_res->header.arcount = nb_saved_records;
Baptiste Assmann13a92322019-06-07 09:40:55 +02001310
Emeric Brund30e9a12020-12-23 18:49:16 +01001311 resolv_check_response(resolution);
Emeric Brun30c766e2020-12-23 18:12:31 +01001312 return RSLV_RESP_VALID;
Willy Tarreau963f7012020-07-22 17:00:45 +02001313
1314 invalid_resp:
Emeric Brun30c766e2020-12-23 18:12:31 +01001315 cause = RSLV_RESP_INVALID;
Willy Tarreau963f7012020-07-22 17:00:45 +02001316
1317 return_error:
Emeric Brun85914e92020-12-23 16:38:06 +01001318 pool_free(resolv_answer_item_pool, answer_record);
Willy Tarreau963f7012020-07-22 17:00:45 +02001319 return cause;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001320}
1321
Christopher Faulet67957bd2017-09-27 11:00:59 +02001322/* Searches dn_name resolution in resp.
Baptiste Assmann325137d2015-04-13 23:40:55 +02001323 * If existing IP not found, return the first IP matching family_priority,
1324 * otherwise, first ip found
1325 * The following tasks are the responsibility of the caller:
Emeric Brund30e9a12020-12-23 18:49:16 +01001326 * - <r_res> contains an error free DNS response
1327 * For both cases above, resolv_validate_dns_response is required
Emeric Brun456de772020-12-23 18:17:31 +01001328 * returns one of the RSLV_UPD_* code
Baptiste Assmann325137d2015-04-13 23:40:55 +02001329 */
Emeric Brund30e9a12020-12-23 18:49:16 +01001330int resolv_get_ip_from_response(struct resolv_response *r_res,
Emeric Brun21fbeed2020-12-23 18:01:04 +01001331 struct resolv_options *resolv_opts, void *currentip,
Thierry Fournierada34842016-02-17 21:25:09 +01001332 short currentip_sin_family,
Baptiste Assmannfb7091e2017-05-03 15:43:12 +02001333 void **newip, short *newip_sin_family,
1334 void *owner)
Baptiste Assmann325137d2015-04-13 23:40:55 +02001335{
Emeric Brun85914e92020-12-23 16:38:06 +01001336 struct resolv_answer_item *record;
Thierry Fournierada34842016-02-17 21:25:09 +01001337 int family_priority;
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001338 int currentip_found;
Baptiste Assmann3cf7f982016-04-17 22:43:26 +02001339 unsigned char *newip4, *newip6;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001340 int currentip_sel;
1341 int j;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001342 int score, max_score;
Baptiste Assmann8e2d9432018-06-22 15:04:43 +02001343 int allowed_duplicated_ip;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001344
Emeric Brun21fbeed2020-12-23 18:01:04 +01001345 family_priority = resolv_opts->family_prio;
1346 allowed_duplicated_ip = resolv_opts->accept_duplicate_ip;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001347 *newip = newip4 = newip6 = NULL;
1348 currentip_found = 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001349 *newip_sin_family = AF_UNSPEC;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001350 max_score = -1;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001351
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001352 /* Select an IP regarding configuration preference.
Joseph Herlant42cf6392018-11-15 10:33:28 -08001353 * Top priority is the preferred network ip version,
1354 * second priority is the preferred network.
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001355 * the last priority is the currently used IP,
1356 *
1357 * For these three priorities, a score is calculated. The
1358 * weight are:
Joseph Herlant42cf6392018-11-15 10:33:28 -08001359 * 8 - preferred ip version.
1360 * 4 - preferred network.
Baptistefc725902016-12-26 23:21:08 +01001361 * 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 +01001362 * 1 - current ip.
1363 * The result with the biggest score is returned.
1364 */
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001365
Emeric Brun85914e92020-12-23 16:38:06 +01001366 list_for_each_entry(record, &r_res->answer_list, list) {
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001367 void *ip;
1368 unsigned char ip_type;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001369
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001370 if (record->type == DNS_RTYPE_A) {
1371 ip = &(((struct sockaddr_in *)&record->address)->sin_addr);
1372 ip_type = AF_INET;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001373 }
1374 else if (record->type == DNS_RTYPE_AAAA) {
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001375 ip_type = AF_INET6;
1376 ip = &(((struct sockaddr_in6 *)&record->address)->sin6_addr);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001377 }
1378 else
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001379 continue;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001380 score = 0;
1381
Joseph Herlant42cf6392018-11-15 10:33:28 -08001382 /* Check for preferred ip protocol. */
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001383 if (ip_type == family_priority)
Baptistefc725902016-12-26 23:21:08 +01001384 score += 8;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001385
Joseph Herlant42cf6392018-11-15 10:33:28 -08001386 /* Check for preferred network. */
Emeric Brun21fbeed2020-12-23 18:01:04 +01001387 for (j = 0; j < resolv_opts->pref_net_nb; j++) {
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001388
Ilya Shipitsin46a030c2020-07-05 16:36:08 +05001389 /* Compare only the same addresses class. */
Emeric Brun21fbeed2020-12-23 18:01:04 +01001390 if (resolv_opts->pref_net[j].family != ip_type)
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001391 continue;
1392
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001393 if ((ip_type == AF_INET &&
1394 in_net_ipv4(ip,
Emeric Brun21fbeed2020-12-23 18:01:04 +01001395 &resolv_opts->pref_net[j].mask.in4,
1396 &resolv_opts->pref_net[j].addr.in4)) ||
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001397 (ip_type == AF_INET6 &&
1398 in_net_ipv6(ip,
Emeric Brun21fbeed2020-12-23 18:01:04 +01001399 &resolv_opts->pref_net[j].mask.in6,
1400 &resolv_opts->pref_net[j].addr.in6))) {
Baptistefc725902016-12-26 23:21:08 +01001401 score += 4;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001402 break;
1403 }
1404 }
1405
Christopher Faulet67957bd2017-09-27 11:00:59 +02001406 /* Check if the IP found in the record is already affected to a
Baptiste Assmann84221b42018-06-22 13:03:50 +02001407 * member of a group. If not, the score should be incremented
Christopher Faulet67957bd2017-09-27 11:00:59 +02001408 * by 2. */
Baptiste Assmann84221b42018-06-22 13:03:50 +02001409 if (owner && snr_check_ip_callback(owner, ip, &ip_type)) {
Baptiste Assmann8e2d9432018-06-22 15:04:43 +02001410 if (!allowed_duplicated_ip) {
1411 continue;
1412 }
Baptiste Assmann84221b42018-06-22 13:03:50 +02001413 } else {
1414 score += 2;
1415 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001416
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001417 /* Check for current ip matching. */
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001418 if (ip_type == currentip_sin_family &&
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001419 ((currentip_sin_family == AF_INET &&
Christopher Faulet67957bd2017-09-27 11:00:59 +02001420 !memcmp(ip, currentip, 4)) ||
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001421 (currentip_sin_family == AF_INET6 &&
Christopher Faulet67957bd2017-09-27 11:00:59 +02001422 !memcmp(ip, currentip, 16)))) {
1423 score++;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001424 currentip_sel = 1;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001425 }
1426 else
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001427 currentip_sel = 0;
1428
1429 /* Keep the address if the score is better than the previous
Christopher Faulet67957bd2017-09-27 11:00:59 +02001430 * score. The maximum score is 15, if this value is reached, we
1431 * break the parsing. Implicitly, this score is reached the ip
1432 * selected is the current ip. */
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001433 if (score > max_score) {
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001434 if (ip_type == AF_INET)
1435 newip4 = ip;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001436 else
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001437 newip6 = ip;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001438 currentip_found = currentip_sel;
Baptistefc725902016-12-26 23:21:08 +01001439 if (score == 15)
Emeric Brun456de772020-12-23 18:17:31 +01001440 return RSLV_UPD_NO;
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001441 max_score = score;
1442 }
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001443 } /* list for each record entries */
Thierry Fournierac88cfe2016-02-17 22:05:30 +01001444
Christopher Faulet67957bd2017-09-27 11:00:59 +02001445 /* No IP found in the response */
Olivier Houcharda8c6db82017-07-06 18:46:47 +02001446 if (!newip4 && !newip6)
Emeric Brun456de772020-12-23 18:17:31 +01001447 return RSLV_UPD_NO_IP_FOUND;
Baptiste Assmann0453a1d2015-09-09 00:51:08 +02001448
Christopher Faulet67957bd2017-09-27 11:00:59 +02001449 /* Case when the caller looks first for an IPv4 address */
Baptiste Assmann325137d2015-04-13 23:40:55 +02001450 if (family_priority == AF_INET) {
1451 if (newip4) {
1452 *newip = newip4;
1453 *newip_sin_family = AF_INET;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001454 }
1455 else if (newip6) {
1456 *newip = newip6;
1457 *newip_sin_family = AF_INET6;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001458 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001459 if (!currentip_found)
1460 goto not_found;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001461 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001462 /* Case when the caller looks first for an IPv6 address */
Baptiste Assmann325137d2015-04-13 23:40:55 +02001463 else if (family_priority == AF_INET6) {
1464 if (newip6) {
1465 *newip = newip6;
1466 *newip_sin_family = AF_INET6;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001467 }
1468 else if (newip4) {
1469 *newip = newip4;
1470 *newip_sin_family = AF_INET;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001471 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001472 if (!currentip_found)
1473 goto not_found;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001474 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001475 /* Case when the caller have no preference (we prefer IPv6) */
Baptiste Assmann325137d2015-04-13 23:40:55 +02001476 else if (family_priority == AF_UNSPEC) {
1477 if (newip6) {
1478 *newip = newip6;
1479 *newip_sin_family = AF_INET6;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001480 }
1481 else if (newip4) {
1482 *newip = newip4;
1483 *newip_sin_family = AF_INET;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001484 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001485 if (!currentip_found)
1486 goto not_found;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001487 }
1488
Christopher Faulet67957bd2017-09-27 11:00:59 +02001489 /* No reason why we should change the server's IP address */
Emeric Brun456de772020-12-23 18:17:31 +01001490 return RSLV_UPD_NO;
Baptiste Assmann8ea0bcc2017-05-04 08:24:11 +02001491
Christopher Faulet67957bd2017-09-27 11:00:59 +02001492 not_found:
Emeric Brun85914e92020-12-23 16:38:06 +01001493 list_for_each_entry(record, &r_res->answer_list, list) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02001494 /* Move the first record to the end of the list, for internal
1495 * round robin */
1496 LIST_DEL(&record->list);
Emeric Brun85914e92020-12-23 16:38:06 +01001497 LIST_ADDQ(&r_res->answer_list, &record->list);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001498 break;
Baptiste Assmann8ea0bcc2017-05-04 08:24:11 +02001499 }
Emeric Brun456de772020-12-23 18:17:31 +01001500 return RSLV_UPD_SRVIP_NOT_FOUND;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001501}
1502
Christopher Faulet67957bd2017-09-27 11:00:59 +02001503/* Turns a domain name label into a string.
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001504 *
Christopher Faulet67957bd2017-09-27 11:00:59 +02001505 * <dn> must be a null-terminated string. <dn_len> must include the terminating
1506 * null byte. <str> must be allocated and its size must be passed in <str_len>.
Baptiste Assmann325137d2015-04-13 23:40:55 +02001507 *
Christopher Faulet67957bd2017-09-27 11:00:59 +02001508 * In case of error, -1 is returned, otherwise, the number of bytes copied in
1509 * <str> (including the terminating null byte).
Baptiste Assmann325137d2015-04-13 23:40:55 +02001510 */
Emeric Brund30e9a12020-12-23 18:49:16 +01001511int resolv_dn_label_to_str(const char *dn, int dn_len, char *str, int str_len)
Baptiste Assmann325137d2015-04-13 23:40:55 +02001512{
Christopher Faulet67957bd2017-09-27 11:00:59 +02001513 char *ptr;
1514 int i, sz;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001515
Christopher Faulet67957bd2017-09-27 11:00:59 +02001516 if (str_len < dn_len - 1)
Baptiste Assmann2af08fe2017-08-14 00:13:01 +02001517 return -1;
1518
Christopher Faulet67957bd2017-09-27 11:00:59 +02001519 ptr = str;
1520 for (i = 0; i < dn_len-1; ++i) {
1521 sz = dn[i];
1522 if (i)
1523 *ptr++ = '.';
1524 memcpy(ptr, dn+i+1, sz);
1525 ptr += sz;
1526 i += sz;
Olivier Houchard8da5f982017-08-04 18:35:36 +02001527 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001528 *ptr++ = '\0';
1529 return (ptr - str);
Olivier Houchard8da5f982017-08-04 18:35:36 +02001530}
1531
Christopher Faulet67957bd2017-09-27 11:00:59 +02001532/* Turns a string into domain name label: www.haproxy.org into 3www7haproxy3org
1533 *
1534 * <str> must be a null-terminated string. <str_len> must include the
1535 * terminating null byte. <dn> buffer must be allocated and its size must be
1536 * passed in <dn_len>.
1537 *
1538 * In case of error, -1 is returned, otherwise, the number of bytes copied in
1539 * <dn> (excluding the terminating null byte).
Baptiste Assmann325137d2015-04-13 23:40:55 +02001540 */
Emeric Brund30e9a12020-12-23 18:49:16 +01001541int resolv_str_to_dn_label(const char *str, int str_len, char *dn, int dn_len)
Baptiste Assmann325137d2015-04-13 23:40:55 +02001542{
Baptiste Assmann325137d2015-04-13 23:40:55 +02001543 int i, offset;
1544
Christopher Faulet67957bd2017-09-27 11:00:59 +02001545 if (dn_len < str_len + 1)
1546 return -1;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001547
Christopher Faulet67957bd2017-09-27 11:00:59 +02001548 /* First byte of dn will be used to store the length of the first
1549 * label */
1550 offset = 0;
1551 for (i = 0; i < str_len; ++i) {
1552 if (str[i] == '.') {
1553 /* 2 or more consecutive dots is invalid */
1554 if (i == offset)
1555 return -1;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001556
Lukas Tribus81725b82020-02-27 15:47:24 +01001557 /* ignore trailing dot */
1558 if (i + 2 == str_len) {
1559 i++;
1560 break;
1561 }
1562
Christopher Faulet67957bd2017-09-27 11:00:59 +02001563 dn[offset] = (i - offset);
1564 offset = i+1;
1565 continue;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001566 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001567 dn[i+1] = str[i];
Baptiste Assmann325137d2015-04-13 23:40:55 +02001568 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001569 dn[offset] = (i - offset - 1);
1570 dn[i] = '\0';
1571 return i;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001572}
1573
Christopher Faulet67957bd2017-09-27 11:00:59 +02001574/* Validates host name:
Baptiste Assmann325137d2015-04-13 23:40:55 +02001575 * - total size
1576 * - each label size individually
1577 * returns:
1578 * 0 in case of error. If <err> is not NULL, an error message is stored there.
1579 * 1 when no error. <err> is left unaffected.
1580 */
Emeric Brund30e9a12020-12-23 18:49:16 +01001581int resolv_hostname_validation(const char *string, char **err)
Baptiste Assmann325137d2015-04-13 23:40:55 +02001582{
Baptiste Assmann325137d2015-04-13 23:40:55 +02001583 int i;
1584
1585 if (strlen(string) > DNS_MAX_NAME_SIZE) {
1586 if (err)
1587 *err = DNS_TOO_LONG_FQDN;
1588 return 0;
1589 }
1590
William Dauchyaecd5dc2020-01-26 19:52:34 +01001591 while (*string) {
Baptiste Assmann325137d2015-04-13 23:40:55 +02001592 i = 0;
William Dauchyaecd5dc2020-01-26 19:52:34 +01001593 while (*string && *string != '.' && i < DNS_MAX_LABEL_SIZE) {
1594 if (!(*string == '-' || *string == '_' ||
1595 (*string >= 'a' && *string <= 'z') ||
1596 (*string >= 'A' && *string <= 'Z') ||
1597 (*string >= '0' && *string <= '9'))) {
Baptiste Assmann325137d2015-04-13 23:40:55 +02001598 if (err)
1599 *err = DNS_INVALID_CHARACTER;
1600 return 0;
1601 }
William Dauchyaecd5dc2020-01-26 19:52:34 +01001602 i++;
1603 string++;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001604 }
1605
William Dauchyaecd5dc2020-01-26 19:52:34 +01001606 if (!(*string))
1607 break;
1608
1609 if (*string != '.' && i >= DNS_MAX_LABEL_SIZE) {
Baptiste Assmann325137d2015-04-13 23:40:55 +02001610 if (err)
1611 *err = DNS_LABEL_TOO_LONG;
1612 return 0;
1613 }
1614
William Dauchyaecd5dc2020-01-26 19:52:34 +01001615 string++;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001616 }
Baptiste Assmann325137d2015-04-13 23:40:55 +02001617 return 1;
1618}
1619
Christopher Faulet67957bd2017-09-27 11:00:59 +02001620/* Picks up an available resolution from the different resolution list
1621 * associated to a resolvers section, in this order:
1622 * 1. check in resolutions.curr for the same hostname and query_type
1623 * 2. check in resolutions.wait for the same hostname and query_type
1624 * 3. Get a new resolution from resolution pool
1625 *
1626 * Returns an available resolution, NULL if none found.
Baptiste Assmann325137d2015-04-13 23:40:55 +02001627 */
Emeric Brund30e9a12020-12-23 18:49:16 +01001628static struct resolv_resolution *resolv_pick_resolution(struct resolvers *resolvers,
1629 char **hostname_dn, int hostname_dn_len,
1630 int query_type)
Baptiste Assmann325137d2015-04-13 23:40:55 +02001631{
Emeric Brun08622d32020-12-23 17:41:43 +01001632 struct resolv_resolution *res;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001633
1634 if (!*hostname_dn)
1635 goto from_pool;
1636
1637 /* Search for same hostname and query type in resolutions.curr */
1638 list_for_each_entry(res, &resolvers->resolutions.curr, list) {
1639 if (!res->hostname_dn)
1640 continue;
1641 if ((query_type == res->prefered_query_type) &&
1642 hostname_dn_len == res->hostname_dn_len &&
Emeric Brund30e9a12020-12-23 18:49:16 +01001643 !resolv_hostname_cmp(*hostname_dn, res->hostname_dn, hostname_dn_len))
Christopher Faulet67957bd2017-09-27 11:00:59 +02001644 return res;
1645 }
1646
1647 /* Search for same hostname and query type in resolutions.wait */
1648 list_for_each_entry(res, &resolvers->resolutions.wait, list) {
1649 if (!res->hostname_dn)
1650 continue;
1651 if ((query_type == res->prefered_query_type) &&
1652 hostname_dn_len == res->hostname_dn_len &&
Emeric Brund30e9a12020-12-23 18:49:16 +01001653 !resolv_hostname_cmp(*hostname_dn, res->hostname_dn, hostname_dn_len))
Christopher Faulet67957bd2017-09-27 11:00:59 +02001654 return res;
1655 }
1656
1657 from_pool:
1658 /* No resolution could be found, so let's allocate a new one */
Emeric Brun08622d32020-12-23 17:41:43 +01001659 res = pool_alloc(resolv_resolution_pool);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001660 if (res) {
1661 memset(res, 0, sizeof(*res));
1662 res->resolvers = resolvers;
1663 res->uuid = resolution_uuid;
1664 res->status = RSLV_STATUS_NONE;
1665 res->step = RSLV_STEP_NONE;
1666 res->last_valid = now_ms;
1667
1668 LIST_INIT(&res->requesters);
1669 LIST_INIT(&res->response.answer_list);
1670
1671 res->prefered_query_type = query_type;
1672 res->query_type = query_type;
1673 res->hostname_dn = *hostname_dn;
1674 res->hostname_dn_len = hostname_dn_len;
1675
1676 ++resolution_uuid;
1677
1678 /* Move the resolution to the resolvers wait queue */
1679 LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
1680 }
1681 return res;
1682}
1683
1684/* Releases a resolution from its requester(s) and move it back to the pool */
Emeric Brund30e9a12020-12-23 18:49:16 +01001685static void resolv_free_resolution(struct resolv_resolution *resolution)
Christopher Faulet67957bd2017-09-27 11:00:59 +02001686{
Emeric Brun08622d32020-12-23 17:41:43 +01001687 struct resolv_requester *req, *reqback;
Emeric Brun85914e92020-12-23 16:38:06 +01001688 struct resolv_answer_item *item, *itemback;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001689
1690 /* clean up configuration */
Emeric Brund30e9a12020-12-23 18:49:16 +01001691 resolv_reset_resolution(resolution);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001692 resolution->hostname_dn = NULL;
1693 resolution->hostname_dn_len = 0;
1694
1695 list_for_each_entry_safe(req, reqback, &resolution->requesters, list) {
1696 LIST_DEL(&req->list);
1697 req->resolution = NULL;
1698 }
1699
Christopher Faulet010ab352020-07-22 15:55:49 +02001700 list_for_each_entry_safe(item, itemback, &resolution->response.answer_list, list) {
1701 LIST_DEL(&item->list);
Christopher Faulet5a891752020-09-08 10:06:01 +02001702 if (item->ar_item) {
Emeric Brun85914e92020-12-23 16:38:06 +01001703 pool_free(resolv_answer_item_pool, item->ar_item);
Christopher Faulet5a891752020-09-08 10:06:01 +02001704 item->ar_item = NULL;
1705 }
Emeric Brun85914e92020-12-23 16:38:06 +01001706 pool_free(resolv_answer_item_pool, item);
Christopher Faulet010ab352020-07-22 15:55:49 +02001707 }
1708
Christopher Faulet67957bd2017-09-27 11:00:59 +02001709 LIST_DEL(&resolution->list);
Emeric Brun08622d32020-12-23 17:41:43 +01001710 pool_free(resolv_resolution_pool, resolution);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001711}
1712
Emeric Brun08622d32020-12-23 17:41:43 +01001713/* Links a requester (a server or a resolv_srvrq) with a resolution. It returns 0
Christopher Faulet67957bd2017-09-27 11:00:59 +02001714 * on success, -1 otherwise.
1715 */
Emeric Brund30e9a12020-12-23 18:49:16 +01001716int resolv_link_resolution(void *requester, int requester_type, int requester_locked)
Christopher Faulet67957bd2017-09-27 11:00:59 +02001717{
Emeric Brun08622d32020-12-23 17:41:43 +01001718 struct resolv_resolution *res = NULL;
1719 struct resolv_requester *req;
Emeric Brun750fe792020-12-23 16:51:12 +01001720 struct resolvers *resolvers;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001721 struct server *srv = NULL;
Emeric Brun08622d32020-12-23 17:41:43 +01001722 struct resolv_srvrq *srvrq = NULL;
Baptiste Assmann333939c2019-01-21 08:34:50 +01001723 struct stream *stream = NULL;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001724 char **hostname_dn;
1725 int hostname_dn_len, query_type;
1726
1727 switch (requester_type) {
1728 case OBJ_TYPE_SERVER:
1729 srv = (struct server *)requester;
1730 hostname_dn = &srv->hostname_dn;
1731 hostname_dn_len = srv->hostname_dn_len;
1732 resolvers = srv->resolvers;
Emeric Brun21fbeed2020-12-23 18:01:04 +01001733 query_type = ((srv->resolv_opts.family_prio == AF_INET)
Christopher Faulet67957bd2017-09-27 11:00:59 +02001734 ? DNS_RTYPE_A
1735 : DNS_RTYPE_AAAA);
1736 break;
1737
1738 case OBJ_TYPE_SRVRQ:
Emeric Brun08622d32020-12-23 17:41:43 +01001739 srvrq = (struct resolv_srvrq *)requester;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001740 hostname_dn = &srvrq->hostname_dn;
1741 hostname_dn_len = srvrq->hostname_dn_len;
1742 resolvers = srvrq->resolvers;
1743 query_type = DNS_RTYPE_SRV;
1744 break;
1745
Baptiste Assmann333939c2019-01-21 08:34:50 +01001746 case OBJ_TYPE_STREAM:
1747 stream = (struct stream *)requester;
Emeric Brun08622d32020-12-23 17:41:43 +01001748 hostname_dn = &stream->resolv_ctx.hostname_dn;
1749 hostname_dn_len = stream->resolv_ctx.hostname_dn_len;
Emeric Brun21fbeed2020-12-23 18:01:04 +01001750 resolvers = stream->resolv_ctx.parent->arg.resolv.resolvers;
1751 query_type = ((stream->resolv_ctx.parent->arg.resolv.opts->family_prio == AF_INET)
Baptiste Assmann333939c2019-01-21 08:34:50 +01001752 ? DNS_RTYPE_A
1753 : DNS_RTYPE_AAAA);
1754 break;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001755 default:
1756 goto err;
1757 }
1758
1759 /* Get a resolution from the resolvers' wait queue or pool */
Emeric Brund30e9a12020-12-23 18:49:16 +01001760 if ((res = resolv_pick_resolution(resolvers, hostname_dn, hostname_dn_len, query_type)) == NULL)
Christopher Faulet67957bd2017-09-27 11:00:59 +02001761 goto err;
1762
1763 if (srv) {
Olivier Houchard55dcdf42017-11-06 15:15:04 +01001764 if (!requester_locked)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001765 HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
Emeric Brun08622d32020-12-23 17:41:43 +01001766 if (srv->resolv_requester == NULL) {
1767 if ((req = pool_alloc(resolv_requester_pool)) == NULL) {
Olivier Houchard55dcdf42017-11-06 15:15:04 +01001768 if (!requester_locked)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001769 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001770 goto err;
Willy Tarreau5ec84572017-11-05 10:35:57 +01001771 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001772 req->owner = &srv->obj_type;
Emeric Brun08622d32020-12-23 17:41:43 +01001773 srv->resolv_requester = req;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001774 }
1775 else
Emeric Brun08622d32020-12-23 17:41:43 +01001776 req = srv->resolv_requester;
Olivier Houchard55dcdf42017-11-06 15:15:04 +01001777 if (!requester_locked)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001778 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
Baptiste Assmanndb4c8522018-01-30 08:08:04 +01001779
1780 req->requester_cb = snr_resolution_cb;
1781 req->requester_error_cb = snr_resolution_error_cb;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001782 }
1783 else if (srvrq) {
Emeric Brun08622d32020-12-23 17:41:43 +01001784 if (srvrq->requester == NULL) {
1785 if ((req = pool_alloc(resolv_requester_pool)) == NULL)
Christopher Faulet67957bd2017-09-27 11:00:59 +02001786 goto err;
1787 req->owner = &srvrq->obj_type;
Emeric Brun08622d32020-12-23 17:41:43 +01001788 srvrq->requester = req;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001789 }
1790 else
Emeric Brun08622d32020-12-23 17:41:43 +01001791 req = srvrq->requester;
Baptiste Assmanndb4c8522018-01-30 08:08:04 +01001792
1793 req->requester_cb = snr_resolution_cb;
1794 req->requester_error_cb = snr_resolution_error_cb;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001795 }
Baptiste Assmann333939c2019-01-21 08:34:50 +01001796 else if (stream) {
Emeric Brun08622d32020-12-23 17:41:43 +01001797 if (stream->resolv_ctx.requester == NULL) {
1798 if ((req = pool_alloc(resolv_requester_pool)) == NULL)
Baptiste Assmann333939c2019-01-21 08:34:50 +01001799 goto err;
1800 req->owner = &stream->obj_type;
Emeric Brun08622d32020-12-23 17:41:43 +01001801 stream->resolv_ctx.requester = req;
Baptiste Assmann333939c2019-01-21 08:34:50 +01001802 }
1803 else
Emeric Brun08622d32020-12-23 17:41:43 +01001804 req = stream->resolv_ctx.requester;
Baptiste Assmann333939c2019-01-21 08:34:50 +01001805
1806 req->requester_cb = act_resolution_cb;
1807 req->requester_error_cb = act_resolution_error_cb;
1808 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001809 else
1810 goto err;
1811
1812 req->resolution = res;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001813
1814 LIST_ADDQ(&res->requesters, &req->list);
1815 return 0;
1816
1817 err:
1818 if (res && LIST_ISEMPTY(&res->requesters))
Emeric Brund30e9a12020-12-23 18:49:16 +01001819 resolv_free_resolution(res);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001820 return -1;
1821}
1822
Ilya Shipitsin46a030c2020-07-05 16:36:08 +05001823/* Removes a requester from a DNS resolution. It takes takes care of all the
Christopher Faulet67957bd2017-09-27 11:00:59 +02001824 * consequences. It also cleans up some parameters from the requester.
1825 */
Emeric Brund30e9a12020-12-23 18:49:16 +01001826void resolv_unlink_resolution(struct resolv_requester *requester)
Christopher Faulet67957bd2017-09-27 11:00:59 +02001827{
Emeric Brun08622d32020-12-23 17:41:43 +01001828 struct resolv_resolution *res;
1829 struct resolv_requester *req;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001830
1831 /* Nothing to do */
1832 if (!requester || !requester->resolution)
1833 return;
1834 res = requester->resolution;
1835
1836 /* Clean up the requester */
1837 LIST_DEL(&requester->list);
1838 requester->resolution = NULL;
1839
1840 /* We need to find another requester linked on this resolution */
1841 if (!LIST_ISEMPTY(&res->requesters))
Emeric Brun08622d32020-12-23 17:41:43 +01001842 req = LIST_NEXT(&res->requesters, struct resolv_requester *, list);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001843 else {
Emeric Brund30e9a12020-12-23 18:49:16 +01001844 resolv_free_resolution(res);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001845 return;
1846 }
1847
1848 /* Move hostname_dn related pointers to the next requester */
1849 switch (obj_type(req->owner)) {
1850 case OBJ_TYPE_SERVER:
Willy Tarreau433c16f2018-09-20 11:15:27 +02001851 res->hostname_dn = __objt_server(req->owner)->hostname_dn;
1852 res->hostname_dn_len = __objt_server(req->owner)->hostname_dn_len;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001853 break;
1854 case OBJ_TYPE_SRVRQ:
Emeric Brun08622d32020-12-23 17:41:43 +01001855 res->hostname_dn = __objt_resolv_srvrq(req->owner)->hostname_dn;
1856 res->hostname_dn_len = __objt_resolv_srvrq(req->owner)->hostname_dn_len;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001857 break;
Baptiste Assmann333939c2019-01-21 08:34:50 +01001858 case OBJ_TYPE_STREAM:
Emeric Brun08622d32020-12-23 17:41:43 +01001859 res->hostname_dn = __objt_stream(req->owner)->resolv_ctx.hostname_dn;
1860 res->hostname_dn_len = __objt_stream(req->owner)->resolv_ctx.hostname_dn_len;
Baptiste Assmann333939c2019-01-21 08:34:50 +01001861 break;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001862 default:
1863 res->hostname_dn = NULL;
1864 res->hostname_dn_len = 0;
1865 break;
1866 }
Baptiste Assmann325137d2015-04-13 23:40:55 +02001867}
1868
Christopher Faulet67957bd2017-09-27 11:00:59 +02001869/* Called when a network IO is generated on a name server socket for an incoming
1870 * packet. It performs the following actions:
1871 * - check if the packet requires processing (not outdated resolution)
1872 * - ensure the DNS packet received is valid and call requester's callback
1873 * - call requester's error callback if invalid response
1874 * - check the dn_name in the packet against the one sent
1875 */
1876static void dns_resolve_recv(struct dgram_conn *dgram)
1877{
Emeric Brun50c870e2021-01-04 10:40:46 +01001878 struct dns_nameserver *ns;
1879 struct dns_counters *tmpcounters;
Emeric Brun750fe792020-12-23 16:51:12 +01001880 struct resolvers *resolvers;
Emeric Brun08622d32020-12-23 17:41:43 +01001881 struct resolv_resolution *res;
Emeric Brun85914e92020-12-23 16:38:06 +01001882 struct resolv_query_item *query;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001883 unsigned char buf[DNS_MAX_UDP_MESSAGE + 1];
1884 unsigned char *bufend;
1885 int fd, buflen, dns_resp;
1886 int max_answer_records;
1887 unsigned short query_id;
1888 struct eb32_node *eb;
Emeric Brun08622d32020-12-23 17:41:43 +01001889 struct resolv_requester *req;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001890
1891 fd = dgram->t.sock.fd;
1892
1893 /* check if ready for reading */
1894 if (!fd_recv_ready(fd))
1895 return;
1896
1897 /* no need to go further if we can't retrieve the nameserver */
Willy Tarreau1c759952019-12-10 18:38:09 +01001898 if ((ns = dgram->owner) == NULL) {
1899 _HA_ATOMIC_AND(&fdtab[fd].ev, ~(FD_POLL_HUP|FD_POLL_ERR));
1900 fd_stop_recv(fd);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001901 return;
Willy Tarreau1c759952019-12-10 18:38:09 +01001902 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02001903
Emeric Brun6a2006a2021-01-04 13:18:55 +01001904 resolvers = ns->parent;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001905 HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001906
1907 /* process all pending input messages */
Willy Tarreau1c759952019-12-10 18:38:09 +01001908 while (fd_recv_ready(fd)) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02001909 /* read message received */
1910 memset(buf, '\0', resolvers->accepted_payload_size + 1);
1911 if ((buflen = recv(fd, (char*)buf , resolvers->accepted_payload_size + 1, 0)) < 0) {
Willy Tarreau1c759952019-12-10 18:38:09 +01001912 /* FIXME : for now we consider EAGAIN only, but at
1913 * least we purge sticky errors that would cause us to
1914 * be called in loops.
1915 */
1916 _HA_ATOMIC_AND(&fdtab[fd].ev, ~(FD_POLL_HUP|FD_POLL_ERR));
Christopher Faulet67957bd2017-09-27 11:00:59 +02001917 fd_cant_recv(fd);
1918 break;
1919 }
1920
1921 /* message too big */
1922 if (buflen > resolvers->accepted_payload_size) {
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02001923 ns->counters->too_big++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001924 continue;
1925 }
1926
1927 /* initializing variables */
1928 bufend = buf + buflen; /* pointer to mark the end of the buffer */
1929
1930 /* read the query id from the packet (16 bits) */
1931 if (buf + 2 > bufend) {
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02001932 ns->counters->invalid++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001933 continue;
1934 }
Emeric Brund30e9a12020-12-23 18:49:16 +01001935 query_id = resolv_response_get_query_id(buf);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001936
1937 /* search the query_id in the pending resolution tree */
1938 eb = eb32_lookup(&resolvers->query_ids, query_id);
1939 if (eb == NULL) {
1940 /* unknown query id means an outdated response and can be safely ignored */
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02001941 ns->counters->outdated++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001942 continue;
1943 }
1944
William Dauchybe8a3872019-11-27 23:32:41 +01001945 /* known query id means a resolution in progress */
Emeric Brun08622d32020-12-23 17:41:43 +01001946 res = eb32_entry(eb, struct resolv_resolution, qid);
Christopher Faulet67957bd2017-09-27 11:00:59 +02001947 /* number of responses received */
1948 res->nb_responses++;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001949
Christopher Faulet67957bd2017-09-27 11:00:59 +02001950 max_answer_records = (resolvers->accepted_payload_size - DNS_HEADER_SIZE) / DNS_MIN_RECORD_SIZE;
Emeric Brund30e9a12020-12-23 18:49:16 +01001951 dns_resp = resolv_validate_dns_response(buf, bufend, res, max_answer_records);
Baptiste Assmann325137d2015-04-13 23:40:55 +02001952
Christopher Faulet67957bd2017-09-27 11:00:59 +02001953 switch (dns_resp) {
Emeric Brun30c766e2020-12-23 18:12:31 +01001954 case RSLV_RESP_VALID:
Christopher Faulet67957bd2017-09-27 11:00:59 +02001955 break;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001956
Emeric Brun30c766e2020-12-23 18:12:31 +01001957 case RSLV_RESP_INVALID:
1958 case RSLV_RESP_QUERY_COUNT_ERROR:
1959 case RSLV_RESP_WRONG_NAME:
Christopher Faulet67957bd2017-09-27 11:00:59 +02001960 res->status = RSLV_STATUS_INVALID;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02001961 ns->counters->invalid++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001962 break;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001963
Emeric Brun30c766e2020-12-23 18:12:31 +01001964 case RSLV_RESP_NX_DOMAIN:
Christopher Faulet67957bd2017-09-27 11:00:59 +02001965 res->status = RSLV_STATUS_NX;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02001966 ns->counters->nx++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001967 break;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001968
Emeric Brun30c766e2020-12-23 18:12:31 +01001969 case RSLV_RESP_REFUSED:
Christopher Faulet67957bd2017-09-27 11:00:59 +02001970 res->status = RSLV_STATUS_REFUSED;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02001971 ns->counters->refused++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001972 break;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001973
Emeric Brun30c766e2020-12-23 18:12:31 +01001974 case RSLV_RESP_ANCOUNT_ZERO:
Christopher Faulet67957bd2017-09-27 11:00:59 +02001975 res->status = RSLV_STATUS_OTHER;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02001976 ns->counters->any_err++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001977 break;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001978
Emeric Brun30c766e2020-12-23 18:12:31 +01001979 case RSLV_RESP_CNAME_ERROR:
Christopher Faulet67957bd2017-09-27 11:00:59 +02001980 res->status = RSLV_STATUS_OTHER;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02001981 ns->counters->cname_error++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001982 break;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02001983
Emeric Brun30c766e2020-12-23 18:12:31 +01001984 case RSLV_RESP_TRUNCATED:
Christopher Faulet67957bd2017-09-27 11:00:59 +02001985 res->status = RSLV_STATUS_OTHER;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02001986 ns->counters->truncated++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001987 break;
Baptiste Assmanne70bc052017-08-21 16:51:09 +02001988
Emeric Brun30c766e2020-12-23 18:12:31 +01001989 case RSLV_RESP_NO_EXPECTED_RECORD:
1990 case RSLV_RESP_ERROR:
1991 case RSLV_RESP_INTERNAL:
Christopher Faulet67957bd2017-09-27 11:00:59 +02001992 res->status = RSLV_STATUS_OTHER;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02001993 ns->counters->other++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001994 break;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001995 }
1996
Christopher Faulet67957bd2017-09-27 11:00:59 +02001997 /* Wait all nameservers response to handle errors */
Emeric Brun30c766e2020-12-23 18:12:31 +01001998 if (dns_resp != RSLV_RESP_VALID && res->nb_responses < resolvers->nb_nameservers)
Christopher Faulet67957bd2017-09-27 11:00:59 +02001999 continue;
Olivier Houchard8da5f982017-08-04 18:35:36 +02002000
Christopher Faulet67957bd2017-09-27 11:00:59 +02002001 /* Process error codes */
Emeric Brun30c766e2020-12-23 18:12:31 +01002002 if (dns_resp != RSLV_RESP_VALID) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02002003 if (res->prefered_query_type != res->query_type) {
2004 /* The fallback on the query type was already performed,
2005 * so check the try counter. If it falls to 0, we can
2006 * report an error. Else, wait the next attempt. */
2007 if (!res->try)
2008 goto report_res_error;
2009 }
2010 else {
2011 /* Fallback from A to AAAA or the opposite and re-send
2012 * the resolution immediately. try counter is not
2013 * decremented. */
2014 if (res->prefered_query_type == DNS_RTYPE_A) {
2015 res->query_type = DNS_RTYPE_AAAA;
Emeric Brund30e9a12020-12-23 18:49:16 +01002016 resolv_send_query(res);
Olivier Houchard8da5f982017-08-04 18:35:36 +02002017 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02002018 else if (res->prefered_query_type == DNS_RTYPE_AAAA) {
2019 res->query_type = DNS_RTYPE_A;
Emeric Brund30e9a12020-12-23 18:49:16 +01002020 resolv_send_query(res);
Olivier Houchard8da5f982017-08-04 18:35:36 +02002021 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02002022 }
2023 continue;
2024 }
Olivier Houchard8da5f982017-08-04 18:35:36 +02002025
Christopher Faulet67957bd2017-09-27 11:00:59 +02002026 /* Now let's check the query's dname corresponds to the one we
2027 * sent. We can check only the first query of the list. We send
2028 * one query at a time so we get one query in the response */
Emeric Brun85914e92020-12-23 16:38:06 +01002029 query = LIST_NEXT(&res->response.query_list, struct resolv_query_item *, list);
Emeric Brund30e9a12020-12-23 18:49:16 +01002030 if (query && resolv_hostname_cmp(query->name, res->hostname_dn, res->hostname_dn_len) != 0) {
Emeric Brun30c766e2020-12-23 18:12:31 +01002031 dns_resp = RSLV_RESP_WRONG_NAME;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002032 ns->counters->other++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002033 goto report_res_error;
2034 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002035
Christopher Faulet67957bd2017-09-27 11:00:59 +02002036 /* So the resolution succeeded */
2037 res->status = RSLV_STATUS_VALID;
2038 res->last_valid = now_ms;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002039 ns->counters->valid++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002040 goto report_res_success;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002041
Christopher Faulet67957bd2017-09-27 11:00:59 +02002042 report_res_error:
2043 list_for_each_entry(req, &res->requesters, list)
2044 req->requester_error_cb(req, dns_resp);
Emeric Brund30e9a12020-12-23 18:49:16 +01002045 resolv_reset_resolution(res);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002046 LIST_DEL(&res->list);
2047 LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
2048 continue;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002049
Christopher Faulet67957bd2017-09-27 11:00:59 +02002050 report_res_success:
2051 /* Only the 1rst requester s managed by the server, others are
2052 * from the cache */
Emeric Brun50c870e2021-01-04 10:40:46 +01002053 tmpcounters = ns->counters;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002054 list_for_each_entry(req, &res->requesters, list) {
Olivier Houchard28381072017-11-06 17:30:28 +01002055 struct server *s = objt_server(req->owner);
2056
2057 if (s)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002058 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Emeric Brun50c870e2021-01-04 10:40:46 +01002059 req->requester_cb(req, tmpcounters);
Olivier Houchard28381072017-11-06 17:30:28 +01002060 if (s)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002061 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Emeric Brun50c870e2021-01-04 10:40:46 +01002062 tmpcounters = NULL;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002063 }
Baptiste Assmann42746372017-05-03 12:12:02 +02002064
Emeric Brund30e9a12020-12-23 18:49:16 +01002065 resolv_reset_resolution(res);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002066 LIST_DEL(&res->list);
2067 LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
2068 continue;
Baptiste Assmann325137d2015-04-13 23:40:55 +02002069 }
Emeric Brund30e9a12020-12-23 18:49:16 +01002070 resolv_update_resolvers_timeout(resolvers);
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002071 HA_SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
Baptiste Assmann325137d2015-04-13 23:40:55 +02002072}
William Lallemand69e96442016-11-19 00:58:54 +01002073
Christopher Faulet67957bd2017-09-27 11:00:59 +02002074/* Called when a resolvers network socket is ready to send data */
2075static void dns_resolve_send(struct dgram_conn *dgram)
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002076{
Emeric Brun750fe792020-12-23 16:51:12 +01002077 struct resolvers *resolvers;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002078 struct dns_nameserver *ns;
Emeric Brun08622d32020-12-23 17:41:43 +01002079 struct resolv_resolution *res;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002080 int fd;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002081
Christopher Faulet67957bd2017-09-27 11:00:59 +02002082 fd = dgram->t.sock.fd;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002083
Christopher Faulet67957bd2017-09-27 11:00:59 +02002084 /* check if ready for sending */
2085 if (!fd_send_ready(fd))
2086 return;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002087
Christopher Faulet67957bd2017-09-27 11:00:59 +02002088 /* we don't want/need to be waked up any more for sending */
2089 fd_stop_send(fd);
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002090
Christopher Faulet67957bd2017-09-27 11:00:59 +02002091 /* no need to go further if we can't retrieve the nameserver */
2092 if ((ns = dgram->owner) == NULL)
2093 return;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002094
Emeric Brun6a2006a2021-01-04 13:18:55 +01002095 resolvers = ns->parent;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002096 HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
Christopher Fauletb2812a62017-10-04 16:17:58 +02002097
Christopher Faulet67957bd2017-09-27 11:00:59 +02002098 list_for_each_entry(res, &resolvers->resolutions.curr, list) {
Willy Tarreauf6ee9dc2018-08-22 04:52:02 +02002099 int ret, len;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002100
Christopher Faulet67957bd2017-09-27 11:00:59 +02002101 if (res->nb_queries == resolvers->nb_nameservers)
2102 continue;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002103
Emeric Brund30e9a12020-12-23 18:49:16 +01002104 len = resolv_build_query(res->query_id, res->query_type,
Willy Tarreauf6ee9dc2018-08-22 04:52:02 +02002105 resolvers->accepted_payload_size,
2106 res->hostname_dn, res->hostname_dn_len,
2107 trash.area, trash.size);
2108 if (len == -1)
Christopher Faulet67957bd2017-09-27 11:00:59 +02002109 goto snd_error;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002110
Willy Tarreauf6ee9dc2018-08-22 04:52:02 +02002111 ret = send(fd, trash.area, len, 0);
Willy Tarreau0eae6322019-12-20 11:18:54 +01002112 if (ret != len) {
2113 if (ret == -1 && errno == EAGAIN) {
2114 /* retry once the socket is ready */
2115 fd_cant_send(fd);
2116 continue;
2117 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02002118 goto snd_error;
Willy Tarreau0eae6322019-12-20 11:18:54 +01002119 }
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002120
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002121 ns->counters->sent++;
2122
Christopher Faulet67957bd2017-09-27 11:00:59 +02002123 res->nb_queries++;
2124 continue;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002125
Christopher Faulet67957bd2017-09-27 11:00:59 +02002126 snd_error:
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002127 ns->counters->snd_error++;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002128 res->nb_queries++;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002129 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002130 HA_SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002131}
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002132
Christopher Faulet67957bd2017-09-27 11:00:59 +02002133/* Processes DNS resolution. First, it checks the active list to detect expired
2134 * resolutions and retry them if possible. Else a timeout is reported. Then, it
2135 * checks the wait list to trigger new resolutions.
2136 */
Emeric Brund30e9a12020-12-23 18:49:16 +01002137static struct task *process_resolvers(struct task *t, void *context, unsigned short state)
Christopher Faulet67957bd2017-09-27 11:00:59 +02002138{
Emeric Brun750fe792020-12-23 16:51:12 +01002139 struct resolvers *resolvers = context;
Emeric Brun08622d32020-12-23 17:41:43 +01002140 struct resolv_resolution *res, *resback;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002141 int exp;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002142
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002143 HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
Christopher Fauletb2812a62017-10-04 16:17:58 +02002144
Christopher Faulet67957bd2017-09-27 11:00:59 +02002145 /* Handle all expired resolutions from the active list */
2146 list_for_each_entry_safe(res, resback, &resolvers->resolutions.curr, list) {
2147 /* When we find the first resolution in the future, then we can
2148 * stop here */
2149 exp = tick_add(res->last_query, resolvers->timeout.retry);
2150 if (!tick_is_expired(exp, now_ms))
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002151 break;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002152
Christopher Faulet67957bd2017-09-27 11:00:59 +02002153 /* If current resolution has been tried too many times and
2154 * finishes in timeout we update its status and remove it from
2155 * the list */
2156 if (!res->try) {
Emeric Brun08622d32020-12-23 17:41:43 +01002157 struct resolv_requester *req;
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002158
Christopher Faulet67957bd2017-09-27 11:00:59 +02002159 /* Notify the result to the requesters */
2160 if (!res->nb_responses)
2161 res->status = RSLV_STATUS_TIMEOUT;
2162 list_for_each_entry(req, &res->requesters, list)
2163 req->requester_error_cb(req, res->status);
Baptiste Assmannfa4a6632017-05-04 09:05:00 +02002164
Christopher Faulet67957bd2017-09-27 11:00:59 +02002165 /* Clean up resolution info and remove it from the
2166 * current list */
Emeric Brund30e9a12020-12-23 18:49:16 +01002167 resolv_reset_resolution(res);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002168 LIST_DEL(&res->list);
2169 LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
William Lallemand69e96442016-11-19 00:58:54 +01002170 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02002171 else {
2172 /* Otherwise resend the DNS query and requeue the resolution */
2173 if (!res->nb_responses || res->prefered_query_type != res->query_type) {
2174 /* No response received (a real timeout) or fallback already done */
2175 res->query_type = res->prefered_query_type;
2176 res->try--;
2177 }
2178 else {
2179 /* Fallback from A to AAAA or the opposite and re-send
2180 * the resolution immediately. try counter is not
2181 * decremented. */
2182 if (res->prefered_query_type == DNS_RTYPE_A)
2183 res->query_type = DNS_RTYPE_AAAA;
2184 else if (res->prefered_query_type == DNS_RTYPE_AAAA)
2185 res->query_type = DNS_RTYPE_A;
2186 else
2187 res->try--;
2188 }
Emeric Brund30e9a12020-12-23 18:49:16 +01002189 resolv_send_query(res);
William Lallemand69e96442016-11-19 00:58:54 +01002190 }
2191 }
William Lallemand69e96442016-11-19 00:58:54 +01002192
Christopher Faulet67957bd2017-09-27 11:00:59 +02002193 /* Handle all resolutions in the wait list */
2194 list_for_each_entry_safe(res, resback, &resolvers->resolutions.wait, list) {
Emeric Brund30e9a12020-12-23 18:49:16 +01002195 exp = tick_add(res->last_resolution, resolv_resolution_timeout(res));
Christopher Faulet67957bd2017-09-27 11:00:59 +02002196 if (tick_isset(res->last_resolution) && !tick_is_expired(exp, now_ms))
2197 continue;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002198
Emeric Brund30e9a12020-12-23 18:49:16 +01002199 if (resolv_run_resolution(res) != 1) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02002200 res->last_resolution = now_ms;
2201 LIST_DEL(&res->list);
2202 LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002203 }
2204 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002205
Emeric Brund30e9a12020-12-23 18:49:16 +01002206 resolv_update_resolvers_timeout(resolvers);
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002207 HA_SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002208 return t;
2209}
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002210
Christopher Faulet67957bd2017-09-27 11:00:59 +02002211/* proto_udp callback functions for a DNS resolution */
2212struct dgram_data_cb resolve_dgram_cb = {
2213 .recv = dns_resolve_recv,
2214 .send = dns_resolve_send,
2215};
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002216
Christopher Faulet67957bd2017-09-27 11:00:59 +02002217/* Release memory allocated by DNS */
Emeric Brund30e9a12020-12-23 18:49:16 +01002218static void resolvers_deinit(void)
Christopher Faulet67957bd2017-09-27 11:00:59 +02002219{
Emeric Brun750fe792020-12-23 16:51:12 +01002220 struct resolvers *resolvers, *resolversback;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002221 struct dns_nameserver *ns, *nsback;
Emeric Brun08622d32020-12-23 17:41:43 +01002222 struct resolv_resolution *res, *resback;
2223 struct resolv_requester *req, *reqback;
2224 struct resolv_srvrq *srvrq, *srvrqback;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002225
Emeric Brun750fe792020-12-23 16:51:12 +01002226 list_for_each_entry_safe(resolvers, resolversback, &sec_resolvers, list) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02002227 list_for_each_entry_safe(ns, nsback, &resolvers->nameservers, list) {
2228 free(ns->id);
2229 free((char *)ns->conf.file);
2230 if (ns->dgram && ns->dgram->t.sock.fd != -1)
2231 fd_delete(ns->dgram->t.sock.fd);
2232 free(ns->dgram);
2233 LIST_DEL(&ns->list);
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002234 EXTRA_COUNTERS_FREE(ns->extra_counters);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002235 free(ns);
2236 }
2237
2238 list_for_each_entry_safe(res, resback, &resolvers->resolutions.curr, list) {
2239 list_for_each_entry_safe(req, reqback, &res->requesters, list) {
2240 LIST_DEL(&req->list);
Emeric Brun08622d32020-12-23 17:41:43 +01002241 pool_free(resolv_requester_pool, req);
Olivier Houchard8da5f982017-08-04 18:35:36 +02002242 }
Emeric Brund30e9a12020-12-23 18:49:16 +01002243 resolv_free_resolution(res);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002244 }
Olivier Houchard8da5f982017-08-04 18:35:36 +02002245
Christopher Faulet67957bd2017-09-27 11:00:59 +02002246 list_for_each_entry_safe(res, resback, &resolvers->resolutions.wait, list) {
2247 list_for_each_entry_safe(req, reqback, &res->requesters, list) {
2248 LIST_DEL(&req->list);
Emeric Brun08622d32020-12-23 17:41:43 +01002249 pool_free(resolv_requester_pool, req);
Olivier Houchard8da5f982017-08-04 18:35:36 +02002250 }
Emeric Brund30e9a12020-12-23 18:49:16 +01002251 resolv_free_resolution(res);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002252 }
Olivier Houchard8da5f982017-08-04 18:35:36 +02002253
Christopher Faulet67957bd2017-09-27 11:00:59 +02002254 free(resolvers->id);
2255 free((char *)resolvers->conf.file);
Olivier Houchard3f795f72019-04-17 22:51:06 +02002256 task_destroy(resolvers->t);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002257 LIST_DEL(&resolvers->list);
2258 free(resolvers);
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002259 }
2260
Emeric Brun08622d32020-12-23 17:41:43 +01002261 list_for_each_entry_safe(srvrq, srvrqback, &resolv_srvrq_list, list) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02002262 free(srvrq->name);
2263 free(srvrq->hostname_dn);
2264 LIST_DEL(&srvrq->list);
2265 free(srvrq);
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002266 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002267}
2268
Christopher Faulet67957bd2017-09-27 11:00:59 +02002269/* Finalizes the DNS configuration by allocating required resources and checking
2270 * live parameters.
2271 * Returns 0 on success, ERR_* flags otherwise.
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002272 */
Emeric Brund30e9a12020-12-23 18:49:16 +01002273static int resolvers_finalize_config(void)
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002274{
Emeric Brun750fe792020-12-23 16:51:12 +01002275 struct resolvers *resolvers;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002276 struct proxy *px;
2277 int err_code = 0;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002278
Christopher Faulet67957bd2017-09-27 11:00:59 +02002279 /* allocate pool of resolution per resolvers */
Emeric Brun750fe792020-12-23 16:51:12 +01002280 list_for_each_entry(resolvers, &sec_resolvers, list) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02002281 struct dns_nameserver *ns;
2282 struct task *t;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002283
Christopher Faulet67957bd2017-09-27 11:00:59 +02002284 /* Check if we can create the socket with nameservers info */
2285 list_for_each_entry(ns, &resolvers->nameservers, list) {
2286 struct dgram_conn *dgram = NULL;
2287 int fd;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002288
Christopher Faulet67957bd2017-09-27 11:00:59 +02002289 /* Check nameserver info */
2290 if ((fd = socket(ns->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002291 ha_alert("config : resolvers '%s': can't create socket for nameserver '%s'.\n",
2292 resolvers->id, ns->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002293 err_code |= (ERR_ALERT|ERR_ABORT);
Olivier Houchard8da5f982017-08-04 18:35:36 +02002294 continue;
Olivier Houchard8da5f982017-08-04 18:35:36 +02002295 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02002296 if (connect(fd, (struct sockaddr*)&ns->addr, get_addr_len(&ns->addr)) == -1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002297 ha_alert("config : resolvers '%s': can't connect socket for nameserver '%s'.\n",
2298 resolvers->id, ns->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002299 close(fd);
2300 err_code |= (ERR_ALERT|ERR_ABORT);
Olivier Houchard8da5f982017-08-04 18:35:36 +02002301 continue;
Olivier Houchard8da5f982017-08-04 18:35:36 +02002302 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02002303 close(fd);
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002304
Christopher Faulet67957bd2017-09-27 11:00:59 +02002305 /* Create dgram structure that will hold the UPD socket
2306 * and attach it on the current nameserver */
2307 if ((dgram = calloc(1, sizeof(*dgram))) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002308 ha_alert("config: resolvers '%s' : out of memory.\n",
2309 resolvers->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002310 err_code |= (ERR_ALERT|ERR_ABORT);
2311 goto err;
2312 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002313
Christopher Faulet67957bd2017-09-27 11:00:59 +02002314 /* Leave dgram partially initialized, no FD attached for
2315 * now. */
2316 dgram->owner = ns;
2317 dgram->data = &resolve_dgram_cb;
2318 dgram->t.sock.fd = -1;
2319 ns->dgram = dgram;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002320
Christopher Faulet67957bd2017-09-27 11:00:59 +02002321 }
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02002322
Christopher Faulet67957bd2017-09-27 11:00:59 +02002323 /* Create the task associated to the resolvers section */
Emeric Brunc60def82017-09-27 14:59:38 +02002324 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002325 ha_alert("config : resolvers '%s' : out of memory.\n", resolvers->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002326 err_code |= (ERR_ALERT|ERR_ABORT);
2327 goto err;
2328 }
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02002329
Christopher Faulet67957bd2017-09-27 11:00:59 +02002330 /* Update task's parameters */
Emeric Brund30e9a12020-12-23 18:49:16 +01002331 t->process = process_resolvers;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002332 t->context = resolvers;
2333 resolvers->t = t;
2334 task_wakeup(t, TASK_WOKEN_INIT);
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02002335 }
2336
Olivier Houchardfbc74e82017-11-24 16:54:05 +01002337 for (px = proxies_list; px; px = px->next) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02002338 struct server *srv;
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02002339
Christopher Faulet67957bd2017-09-27 11:00:59 +02002340 for (srv = px->srv; srv; srv = srv->next) {
Emeric Brun750fe792020-12-23 16:51:12 +01002341 struct resolvers *resolvers;
Baptiste Assmann81ed1a02017-05-03 10:11:44 +02002342
Christopher Faulet67957bd2017-09-27 11:00:59 +02002343 if (!srv->resolvers_id)
2344 continue;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002345
Christopher Faulet67957bd2017-09-27 11:00:59 +02002346 if ((resolvers = find_resolvers_by_id(srv->resolvers_id)) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002347 ha_alert("config : %s '%s', server '%s': unable to find required resolvers '%s'\n",
2348 proxy_type_str(px), px->id, srv->id, srv->resolvers_id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002349 err_code |= (ERR_ALERT|ERR_ABORT);
2350 continue;
2351 }
2352 srv->resolvers = resolvers;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002353
Christopher Faulet67957bd2017-09-27 11:00:59 +02002354 if (srv->srvrq && !srv->srvrq->resolvers) {
2355 srv->srvrq->resolvers = srv->resolvers;
Emeric Brund30e9a12020-12-23 18:49:16 +01002356 if (resolv_link_resolution(srv->srvrq, OBJ_TYPE_SRVRQ, 0) == -1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002357 ha_alert("config : %s '%s' : unable to set DNS resolution for server '%s'.\n",
2358 proxy_type_str(px), px->id, srv->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002359 err_code |= (ERR_ALERT|ERR_ABORT);
2360 continue;
2361 }
2362 }
Emeric Brund30e9a12020-12-23 18:49:16 +01002363 if (resolv_link_resolution(srv, OBJ_TYPE_SERVER, 0) == -1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002364 ha_alert("config : %s '%s', unable to set DNS resolution for server '%s'.\n",
2365 proxy_type_str(px), px->id, srv->id);
Christopher Faulet67957bd2017-09-27 11:00:59 +02002366 err_code |= (ERR_ALERT|ERR_ABORT);
2367 continue;
2368 }
2369 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002370 }
2371
Christopher Faulet67957bd2017-09-27 11:00:59 +02002372 if (err_code & (ERR_ALERT|ERR_ABORT))
2373 goto err;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002374
Christopher Faulet67957bd2017-09-27 11:00:59 +02002375 return err_code;
2376 err:
Emeric Brund30e9a12020-12-23 18:49:16 +01002377 resolvers_deinit();
Christopher Faulet67957bd2017-09-27 11:00:59 +02002378 return err_code;
2379
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002380}
2381
2382static int stats_dump_dns_to_buffer(struct stream_interface *si,
2383 struct dns_nameserver *ns,
2384 struct field *stats, size_t stats_count,
2385 struct list *stat_modules)
2386{
2387 struct appctx *appctx = __objt_appctx(si->end);
2388 struct channel *rep = si_ic(si);
2389 struct stats_module *mod;
2390 size_t idx = 0;
2391
2392 memset(stats, 0, sizeof(struct field) * stats_count);
2393
2394 list_for_each_entry(mod, stat_modules, list) {
2395 struct counters_node *counters = EXTRA_COUNTERS_GET(ns->extra_counters, mod);
2396
2397 mod->fill_stats(counters, stats + idx);
2398 idx += mod->stats_count;
2399 }
2400
2401 if (!stats_dump_one_line(stats, idx, appctx))
2402 return 0;
2403
2404 if (!stats_putchk(rep, NULL, &trash))
2405 goto full;
2406
2407 return 1;
2408
2409 full:
2410 si_rx_room_rdy(si);
2411 return 0;
2412}
2413
2414/* Uses <appctx.ctx.stats.obj1> as a pointer to the current resolver and <obj2>
2415 * as a pointer to the current nameserver.
2416 */
2417int stats_dump_dns(struct stream_interface *si,
2418 struct field *stats, size_t stats_count,
2419 struct list *stat_modules)
2420{
2421 struct appctx *appctx = __objt_appctx(si->end);
2422 struct channel *rep = si_ic(si);
Emeric Brun750fe792020-12-23 16:51:12 +01002423 struct resolvers *resolver = appctx->ctx.stats.obj1;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002424 struct dns_nameserver *ns = appctx->ctx.stats.obj2;
2425
2426 if (!resolver)
Emeric Brun750fe792020-12-23 16:51:12 +01002427 resolver = LIST_NEXT(&sec_resolvers, struct resolvers *, list);
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002428
2429 /* dump resolvers */
Emeric Brun750fe792020-12-23 16:51:12 +01002430 list_for_each_entry_from(resolver, &sec_resolvers, list) {
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002431 appctx->ctx.stats.obj1 = resolver;
2432
2433 ns = appctx->ctx.stats.obj2 ?
2434 appctx->ctx.stats.obj2 :
2435 LIST_NEXT(&resolver->nameservers, struct dns_nameserver *, list);
2436
2437 list_for_each_entry_from(ns, &resolver->nameservers, list) {
2438 appctx->ctx.stats.obj2 = ns;
2439
2440 if (buffer_almost_full(&rep->buf))
2441 goto full;
2442
2443 if (!stats_dump_dns_to_buffer(si, ns,
2444 stats, stats_count,
2445 stat_modules)) {
2446 return 0;
2447 }
2448 }
2449
2450 appctx->ctx.stats.obj2 = NULL;
2451 }
2452
2453 return 1;
2454
2455 full:
2456 si_rx_room_blk(si);
2457 return 0;
2458}
2459
2460void dns_stats_clear_counters(int clrall, struct list *stat_modules)
2461{
Emeric Brun750fe792020-12-23 16:51:12 +01002462 struct resolvers *resolvers;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002463 struct dns_nameserver *ns;
2464 struct stats_module *mod;
2465 void *counters;
2466
2467 list_for_each_entry(mod, stat_modules, list) {
2468 if (!mod->clearable && !clrall)
2469 continue;
2470
Emeric Brun750fe792020-12-23 16:51:12 +01002471 list_for_each_entry(resolvers, &sec_resolvers, list) {
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002472 list_for_each_entry(ns, &resolvers->nameservers, list) {
2473 counters = EXTRA_COUNTERS_GET(ns->extra_counters, mod);
2474 memcpy(counters, mod->counters, mod->counters_size);
2475 }
2476 }
2477 }
2478
2479}
2480
2481int dns_allocate_counters(struct list *stat_modules)
2482{
2483 struct stats_module *mod;
Emeric Brun750fe792020-12-23 16:51:12 +01002484 struct resolvers *resolvers;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002485 struct dns_nameserver *ns;
2486
Emeric Brun750fe792020-12-23 16:51:12 +01002487 list_for_each_entry(resolvers, &sec_resolvers, list) {
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002488 list_for_each_entry(ns, &resolvers->nameservers, list) {
2489 EXTRA_COUNTERS_REGISTER(&ns->extra_counters, COUNTERS_DNS,
2490 alloc_failed);
2491
2492 list_for_each_entry(mod, stat_modules, list) {
2493 EXTRA_COUNTERS_ADD(mod,
2494 ns->extra_counters,
2495 mod->counters,
2496 mod->counters_size);
2497 }
2498
2499 EXTRA_COUNTERS_ALLOC(ns->extra_counters, alloc_failed);
2500
2501 list_for_each_entry(mod, stat_modules, list) {
2502 memcpy(ns->extra_counters->data + mod->counters_off[ns->extra_counters->type],
2503 mod->counters, mod->counters_size);
2504
2505 /* Store the ns counters pointer */
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01002506 if (strcmp(mod->name, "dns") == 0) {
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002507 ns->counters = (struct dns_counters *)ns->extra_counters->data + mod->counters_off[COUNTERS_DNS];
2508 ns->counters->id = ns->id;
Emeric Brun6a2006a2021-01-04 13:18:55 +01002509 ns->counters->pid = resolvers->id;
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002510 }
2511 }
2512 }
2513 }
2514
2515 return 1;
2516
2517alloc_failed:
2518 return 0;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002519}
2520
Christopher Faulet67957bd2017-09-27 11:00:59 +02002521/* if an arg is found, it sets the resolvers section pointer into cli.p0 */
Aurélien Nephtaliabbf6072018-04-18 13:26:46 +02002522static int cli_parse_stat_resolvers(char **args, char *payload, struct appctx *appctx, void *private)
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002523{
Emeric Brun750fe792020-12-23 16:51:12 +01002524 struct resolvers *presolvers;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002525
Christopher Faulet67957bd2017-09-27 11:00:59 +02002526 if (*args[2]) {
Emeric Brun750fe792020-12-23 16:51:12 +01002527 list_for_each_entry(presolvers, &sec_resolvers, list) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02002528 if (strcmp(presolvers->id, args[2]) == 0) {
2529 appctx->ctx.cli.p0 = presolvers;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002530 break;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002531 }
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002532 }
Willy Tarreau9d008692019-08-09 11:21:01 +02002533 if (appctx->ctx.cli.p0 == NULL)
2534 return cli_err(appctx, "Can't find that resolvers section\n");
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002535 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02002536 return 0;
Baptiste Assmann201c07f2017-05-22 15:17:15 +02002537}
2538
Christopher Faulet67957bd2017-09-27 11:00:59 +02002539/* Dumps counters from all resolvers section and associated name servers. It
2540 * returns 0 if the output buffer is full and it needs to be called again,
2541 * otherwise non-zero. It may limit itself to the resolver pointed to by
Willy Tarreau777b5602016-12-16 18:06:26 +01002542 * <cli.p0> if it's not null.
William Lallemand69e96442016-11-19 00:58:54 +01002543 */
2544static int cli_io_handler_dump_resolvers_to_buffer(struct appctx *appctx)
2545{
2546 struct stream_interface *si = appctx->owner;
Emeric Brun750fe792020-12-23 16:51:12 +01002547 struct resolvers *resolvers;
Christopher Faulet67957bd2017-09-27 11:00:59 +02002548 struct dns_nameserver *ns;
William Lallemand69e96442016-11-19 00:58:54 +01002549
2550 chunk_reset(&trash);
2551
2552 switch (appctx->st2) {
2553 case STAT_ST_INIT:
2554 appctx->st2 = STAT_ST_LIST; /* let's start producing data */
2555 /* fall through */
2556
2557 case STAT_ST_LIST:
Emeric Brun750fe792020-12-23 16:51:12 +01002558 if (LIST_ISEMPTY(&sec_resolvers)) {
William Lallemand69e96442016-11-19 00:58:54 +01002559 chunk_appendf(&trash, "No resolvers found\n");
2560 }
2561 else {
Emeric Brun750fe792020-12-23 16:51:12 +01002562 list_for_each_entry(resolvers, &sec_resolvers, list) {
Christopher Faulet67957bd2017-09-27 11:00:59 +02002563 if (appctx->ctx.cli.p0 != NULL && appctx->ctx.cli.p0 != resolvers)
William Lallemand69e96442016-11-19 00:58:54 +01002564 continue;
2565
Christopher Faulet67957bd2017-09-27 11:00:59 +02002566 chunk_appendf(&trash, "Resolvers section %s\n", resolvers->id);
2567 list_for_each_entry(ns, &resolvers->nameservers, list) {
2568 chunk_appendf(&trash, " nameserver %s:\n", ns->id);
Amaury Denoyellefbd0bc92020-10-05 11:49:46 +02002569 chunk_appendf(&trash, " sent: %lld\n", ns->counters->sent);
2570 chunk_appendf(&trash, " snd_error: %lld\n", ns->counters->snd_error);
2571 chunk_appendf(&trash, " valid: %lld\n", ns->counters->valid);
2572 chunk_appendf(&trash, " update: %lld\n", ns->counters->update);
2573 chunk_appendf(&trash, " cname: %lld\n", ns->counters->cname);
2574 chunk_appendf(&trash, " cname_error: %lld\n", ns->counters->cname_error);
2575 chunk_appendf(&trash, " any_err: %lld\n", ns->counters->any_err);
2576 chunk_appendf(&trash, " nx: %lld\n", ns->counters->nx);
2577 chunk_appendf(&trash, " timeout: %lld\n", ns->counters->timeout);
2578 chunk_appendf(&trash, " refused: %lld\n", ns->counters->refused);
2579 chunk_appendf(&trash, " other: %lld\n", ns->counters->other);
2580 chunk_appendf(&trash, " invalid: %lld\n", ns->counters->invalid);
2581 chunk_appendf(&trash, " too_big: %lld\n", ns->counters->too_big);
2582 chunk_appendf(&trash, " truncated: %lld\n", ns->counters->truncated);
2583 chunk_appendf(&trash, " outdated: %lld\n", ns->counters->outdated);
William Lallemand69e96442016-11-19 00:58:54 +01002584 }
Christopher Faulet67957bd2017-09-27 11:00:59 +02002585 chunk_appendf(&trash, "\n");
William Lallemand69e96442016-11-19 00:58:54 +01002586 }
2587 }
2588
2589 /* display response */
Willy Tarreau06d80a92017-10-19 14:32:15 +02002590 if (ci_putchk(si_ic(si), &trash) == -1) {
William Lallemand69e96442016-11-19 00:58:54 +01002591 /* let's try again later from this session. We add ourselves into
2592 * this session's users so that it can remove us upon termination.
2593 */
Willy Tarreaudb398432018-11-15 11:08:52 +01002594 si_rx_room_blk(si);
William Lallemand69e96442016-11-19 00:58:54 +01002595 return 0;
2596 }
William Lallemand69e96442016-11-19 00:58:54 +01002597 /* fall through */
2598
2599 default:
2600 appctx->st2 = STAT_ST_FIN;
2601 return 1;
2602 }
2603}
2604
2605/* register cli keywords */
Christopher Fauletff88efb2017-10-03 16:00:57 +02002606static struct cli_kw_list cli_kws = {{ }, {
2607 { { "show", "resolvers", NULL }, "show resolvers [id]: dumps counters from all resolvers section and\n"
2608 " associated name servers",
2609 cli_parse_stat_resolvers, cli_io_handler_dump_resolvers_to_buffer },
2610 {{},}
2611 }
2612};
William Lallemand69e96442016-11-19 00:58:54 +01002613
Willy Tarreau0108d902018-11-25 19:14:37 +01002614INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
William Lallemand69e96442016-11-19 00:58:54 +01002615
Baptiste Assmann333939c2019-01-21 08:34:50 +01002616/*
2617 * Prepare <rule> for hostname resolution.
2618 * Returns -1 in case of any allocation failure, 0 if not.
2619 * On error, a global failure counter is also incremented.
2620 */
2621static int action_prepare_for_resolution(struct stream *stream, const char *hostname)
2622{
2623 char *hostname_dn;
2624 int hostname_len, hostname_dn_len;
2625 struct buffer *tmp = get_trash_chunk();
2626
2627 if (!hostname)
2628 return 0;
2629
2630 hostname_len = strlen(hostname);
2631 hostname_dn = tmp->area;
Emeric Brund30e9a12020-12-23 18:49:16 +01002632 hostname_dn_len = resolv_str_to_dn_label(hostname, hostname_len + 1,
2633 hostname_dn, tmp->size);
Baptiste Assmann333939c2019-01-21 08:34:50 +01002634 if (hostname_dn_len == -1)
2635 goto err;
2636
2637
Emeric Brun08622d32020-12-23 17:41:43 +01002638 stream->resolv_ctx.hostname_dn = strdup(hostname_dn);
2639 stream->resolv_ctx.hostname_dn_len = hostname_dn_len;
2640 if (!stream->resolv_ctx.hostname_dn)
Baptiste Assmann333939c2019-01-21 08:34:50 +01002641 goto err;
2642
2643 return 0;
2644
2645 err:
Emeric Brun08622d32020-12-23 17:41:43 +01002646 free(stream->resolv_ctx.hostname_dn); stream->resolv_ctx.hostname_dn = NULL;
Emeric Brund30e9a12020-12-23 18:49:16 +01002647 resolv_failed_resolutions += 1;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002648 return -1;
2649}
2650
2651
2652/*
2653 * Execute the "do-resolution" action. May be called from {tcp,http}request.
2654 */
Emeric Brund30e9a12020-12-23 18:49:16 +01002655enum act_return resolv_action_do_resolve(struct act_rule *rule, struct proxy *px,
Baptiste Assmann333939c2019-01-21 08:34:50 +01002656 struct session *sess, struct stream *s, int flags)
2657{
Emeric Brun08622d32020-12-23 17:41:43 +01002658 struct resolv_resolution *resolution;
Willy Tarreau45726fd2019-07-17 10:38:45 +02002659 struct sample *smp;
2660 char *fqdn;
Emeric Brun08622d32020-12-23 17:41:43 +01002661 struct resolv_requester *req;
Emeric Brun750fe792020-12-23 16:51:12 +01002662 struct resolvers *resolvers;
Emeric Brun08622d32020-12-23 17:41:43 +01002663 struct resolv_resolution *res;
Christopher Faulet5098a082020-07-22 11:46:32 +02002664 int exp, locked = 0;
2665 enum act_return ret = ACT_RET_CONT;
2666
Emeric Brun21fbeed2020-12-23 18:01:04 +01002667 resolvers = rule->arg.resolv.resolvers;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002668
2669 /* we have a response to our DNS resolution */
Baptiste Assmann7264dfe2019-10-30 16:06:53 +01002670 use_cache:
Emeric Brun08622d32020-12-23 17:41:43 +01002671 if (s->resolv_ctx.requester && s->resolv_ctx.requester->resolution != NULL) {
2672 resolution = s->resolv_ctx.requester->resolution;
Christopher Faulet5098a082020-07-22 11:46:32 +02002673 if (!locked) {
2674 HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
2675 locked = 1;
2676 }
2677
Christopher Faulet385101e2020-07-28 10:21:54 +02002678 if (resolution->step == RSLV_STEP_RUNNING)
2679 goto yield;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002680 if (resolution->step == RSLV_STEP_NONE) {
2681 /* We update the variable only if we have a valid response. */
2682 if (resolution->status == RSLV_STATUS_VALID) {
2683 struct sample smp;
2684 short ip_sin_family = 0;
2685 void *ip = NULL;
2686
Emeric Brund30e9a12020-12-23 18:49:16 +01002687 resolv_get_ip_from_response(&resolution->response, rule->arg.resolv.opts, NULL,
Baptiste Assmann333939c2019-01-21 08:34:50 +01002688 0, &ip, &ip_sin_family, NULL);
2689
2690 switch (ip_sin_family) {
2691 case AF_INET:
2692 smp.data.type = SMP_T_IPV4;
2693 memcpy(&smp.data.u.ipv4, ip, 4);
2694 break;
2695 case AF_INET6:
2696 smp.data.type = SMP_T_IPV6;
2697 memcpy(&smp.data.u.ipv6, ip, 16);
2698 break;
2699 default:
2700 ip = NULL;
2701 }
2702
2703 if (ip) {
2704 smp.px = px;
2705 smp.sess = sess;
2706 smp.strm = s;
2707
Emeric Brun21fbeed2020-12-23 18:01:04 +01002708 vars_set_by_name(rule->arg.resolv.varname, strlen(rule->arg.resolv.varname), &smp);
Baptiste Assmann333939c2019-01-21 08:34:50 +01002709 }
2710 }
2711 }
2712
Christopher Faulet385101e2020-07-28 10:21:54 +02002713 goto release_requester;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002714 }
2715
2716 /* need to configure and start a new DNS resolution */
Emeric Brun21fbeed2020-12-23 18:01:04 +01002717 smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.resolv.expr, SMP_T_STR);
Willy Tarreau45726fd2019-07-17 10:38:45 +02002718 if (smp == NULL)
Christopher Faulet5098a082020-07-22 11:46:32 +02002719 goto end;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002720
Willy Tarreau45726fd2019-07-17 10:38:45 +02002721 fqdn = smp->data.u.str.area;
2722 if (action_prepare_for_resolution(s, fqdn) == -1)
Christopher Faulet5098a082020-07-22 11:46:32 +02002723 goto end; /* on error, ignore the action */
Baptiste Assmann333939c2019-01-21 08:34:50 +01002724
Emeric Brun08622d32020-12-23 17:41:43 +01002725 s->resolv_ctx.parent = rule;
Christopher Faulet5098a082020-07-22 11:46:32 +02002726
2727 HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
2728 locked = 1;
2729
Emeric Brund30e9a12020-12-23 18:49:16 +01002730 resolv_link_resolution(s, OBJ_TYPE_STREAM, 0);
Baptiste Assmann7264dfe2019-10-30 16:06:53 +01002731
2732 /* Check if there is a fresh enough response in the cache of our associated resolution */
Emeric Brun08622d32020-12-23 17:41:43 +01002733 req = s->resolv_ctx.requester;
Christopher Faulet385101e2020-07-28 10:21:54 +02002734 if (!req || !req->resolution)
2735 goto release_requester; /* on error, ignore the action */
Christopher Faulet5098a082020-07-22 11:46:32 +02002736 res = req->resolution;
Baptiste Assmann7264dfe2019-10-30 16:06:53 +01002737
2738 exp = tick_add(res->last_resolution, resolvers->hold.valid);
2739 if (resolvers->t && res->status == RSLV_STATUS_VALID && tick_isset(res->last_resolution)
Christopher Faulet385101e2020-07-28 10:21:54 +02002740 && !tick_is_expired(exp, now_ms)) {
Baptiste Assmann7264dfe2019-10-30 16:06:53 +01002741 goto use_cache;
2742 }
2743
Emeric Brund30e9a12020-12-23 18:49:16 +01002744 resolv_trigger_resolution(s->resolv_ctx.requester);
Christopher Faulet385101e2020-07-28 10:21:54 +02002745
2746 yield:
2747 if (flags & ACT_OPT_FINAL)
2748 goto release_requester;
Christopher Faulet5098a082020-07-22 11:46:32 +02002749 ret = ACT_RET_YIELD;
2750
2751 end:
2752 if (locked)
2753 HA_SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
2754 return ret;
Christopher Faulet385101e2020-07-28 10:21:54 +02002755
2756 release_requester:
Emeric Brun08622d32020-12-23 17:41:43 +01002757 free(s->resolv_ctx.hostname_dn);
2758 s->resolv_ctx.hostname_dn = NULL;
2759 s->resolv_ctx.hostname_dn_len = 0;
2760 if (s->resolv_ctx.requester) {
Emeric Brund30e9a12020-12-23 18:49:16 +01002761 resolv_unlink_resolution(s->resolv_ctx.requester);
Emeric Brun08622d32020-12-23 17:41:43 +01002762 pool_free(resolv_requester_pool, s->resolv_ctx.requester);
2763 s->resolv_ctx.requester = NULL;
Christopher Faulet385101e2020-07-28 10:21:54 +02002764 }
2765 goto end;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002766}
2767
Emeric Brund30e9a12020-12-23 18:49:16 +01002768static void release_resolv_action(struct act_rule *rule)
Christopher Faulet3b2bb632020-01-24 18:12:58 +01002769{
Emeric Brun21fbeed2020-12-23 18:01:04 +01002770 release_sample_expr(rule->arg.resolv.expr);
2771 free(rule->arg.resolv.varname);
2772 free(rule->arg.resolv.resolvers_id);
2773 free(rule->arg.resolv.opts);
Christopher Faulet3b2bb632020-01-24 18:12:58 +01002774}
2775
Baptiste Assmann333939c2019-01-21 08:34:50 +01002776
2777/* parse "do-resolve" action
2778 * This action takes the following arguments:
2779 * do-resolve(<varName>,<resolversSectionName>,<resolvePrefer>) <expr>
2780 *
2781 * - <varName> is the variable name where the result of the DNS resolution will be stored
2782 * (mandatory)
2783 * - <resolversSectionName> is the name of the resolvers section to use to perform the resolution
2784 * (mandatory)
2785 * - <resolvePrefer> can be either 'ipv4' or 'ipv6' and is the IP family we would like to resolve first
2786 * (optional), defaults to ipv6
2787 * - <expr> is an HAProxy expression used to fetch the name to be resolved
2788 */
Emeric Brund30e9a12020-12-23 18:49:16 +01002789enum act_parse_ret resolv_parse_do_resolve(const char **args, int *orig_arg, struct proxy *px, struct act_rule *rule, char **err)
Baptiste Assmann333939c2019-01-21 08:34:50 +01002790{
2791 int cur_arg;
2792 struct sample_expr *expr;
2793 unsigned int where;
2794 const char *beg, *end;
2795
2796 /* orig_arg points to the first argument, but we need to analyse the command itself first */
2797 cur_arg = *orig_arg - 1;
2798
2799 /* locate varName, which is mandatory */
2800 beg = strchr(args[cur_arg], '(');
2801 if (beg == NULL)
2802 goto do_resolve_parse_error;
2803 beg = beg + 1; /* beg should points to the first character after opening parenthesis '(' */
2804 end = strchr(beg, ',');
2805 if (end == NULL)
2806 goto do_resolve_parse_error;
Emeric Brun21fbeed2020-12-23 18:01:04 +01002807 rule->arg.resolv.varname = my_strndup(beg, end - beg);
2808 if (rule->arg.resolv.varname == NULL)
Baptiste Assmann333939c2019-01-21 08:34:50 +01002809 goto do_resolve_parse_error;
2810
2811
2812 /* locate resolversSectionName, which is mandatory.
2813 * Since next parameters are optional, the delimiter may be comma ','
2814 * or closing parenthesis ')'
2815 */
2816 beg = end + 1;
2817 end = strchr(beg, ',');
2818 if (end == NULL)
2819 end = strchr(beg, ')');
2820 if (end == NULL)
2821 goto do_resolve_parse_error;
Emeric Brun21fbeed2020-12-23 18:01:04 +01002822 rule->arg.resolv.resolvers_id = my_strndup(beg, end - beg);
2823 if (rule->arg.resolv.resolvers_id == NULL)
Baptiste Assmann333939c2019-01-21 08:34:50 +01002824 goto do_resolve_parse_error;
2825
2826
Emeric Brun21fbeed2020-12-23 18:01:04 +01002827 rule->arg.resolv.opts = calloc(1, sizeof(*rule->arg.resolv.opts));
2828 if (rule->arg.resolv.opts == NULL)
Christopher Fauleta4168432020-01-24 18:08:42 +01002829 goto do_resolve_parse_error;
2830
Baptiste Assmann333939c2019-01-21 08:34:50 +01002831 /* Default priority is ipv6 */
Emeric Brun21fbeed2020-12-23 18:01:04 +01002832 rule->arg.resolv.opts->family_prio = AF_INET6;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002833
2834 /* optional arguments accepted for now:
2835 * ipv4 or ipv6
2836 */
2837 while (*end != ')') {
2838 beg = end + 1;
2839 end = strchr(beg, ',');
2840 if (end == NULL)
2841 end = strchr(beg, ')');
2842 if (end == NULL)
2843 goto do_resolve_parse_error;
2844
2845 if (strncmp(beg, "ipv4", end - beg) == 0) {
Emeric Brun21fbeed2020-12-23 18:01:04 +01002846 rule->arg.resolv.opts->family_prio = AF_INET;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002847 }
2848 else if (strncmp(beg, "ipv6", end - beg) == 0) {
Emeric Brun21fbeed2020-12-23 18:01:04 +01002849 rule->arg.resolv.opts->family_prio = AF_INET6;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002850 }
2851 else {
2852 goto do_resolve_parse_error;
2853 }
2854 }
2855
2856 cur_arg = cur_arg + 1;
2857
Willy Tarreaue3b57bf2020-02-14 16:50:14 +01002858 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args, NULL);
Baptiste Assmann333939c2019-01-21 08:34:50 +01002859 if (!expr)
2860 goto do_resolve_parse_error;
2861
2862
2863 where = 0;
2864 if (px->cap & PR_CAP_FE)
2865 where |= SMP_VAL_FE_HRQ_HDR;
2866 if (px->cap & PR_CAP_BE)
2867 where |= SMP_VAL_BE_HRQ_HDR;
2868
2869 if (!(expr->fetch->val & where)) {
2870 memprintf(err,
2871 "fetch method '%s' extracts information from '%s', none of which is available here",
2872 args[cur_arg-1], sample_src_names(expr->fetch->use));
2873 free(expr);
2874 return ACT_RET_PRS_ERR;
2875 }
Emeric Brun21fbeed2020-12-23 18:01:04 +01002876 rule->arg.resolv.expr = expr;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002877 rule->action = ACT_CUSTOM;
Emeric Brund30e9a12020-12-23 18:49:16 +01002878 rule->action_ptr = resolv_action_do_resolve;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002879 *orig_arg = cur_arg;
2880
2881 rule->check_ptr = check_action_do_resolve;
Emeric Brund30e9a12020-12-23 18:49:16 +01002882 rule->release_ptr = release_resolv_action;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002883
2884 return ACT_RET_PRS_OK;
2885
2886 do_resolve_parse_error:
Emeric Brun21fbeed2020-12-23 18:01:04 +01002887 free(rule->arg.resolv.varname); rule->arg.resolv.varname = NULL;
2888 free(rule->arg.resolv.resolvers_id); rule->arg.resolv.resolvers_id = NULL;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002889 memprintf(err, "Can't parse '%s'. Expects 'do-resolve(<varname>,<resolvers>[,<options>]) <expr>'. Available options are 'ipv4' and 'ipv6'",
2890 args[cur_arg]);
2891 return ACT_RET_PRS_ERR;
2892}
2893
2894static struct action_kw_list http_req_kws = { { }, {
Emeric Brund30e9a12020-12-23 18:49:16 +01002895 { "do-resolve", resolv_parse_do_resolve, 1 },
Baptiste Assmann333939c2019-01-21 08:34:50 +01002896 { /* END */ }
2897}};
2898
2899INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_kws);
2900
2901static struct action_kw_list tcp_req_cont_actions = {ILH, {
Emeric Brund30e9a12020-12-23 18:49:16 +01002902 { "do-resolve", resolv_parse_do_resolve, 1 },
Baptiste Assmann333939c2019-01-21 08:34:50 +01002903 { /* END */ }
2904}};
2905
2906INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_actions);
2907
2908/* Check an "http-request do-resolve" action.
2909 *
2910 * The function returns 1 in success case, otherwise, it returns 0 and err is
2911 * filled.
2912 */
2913int check_action_do_resolve(struct act_rule *rule, struct proxy *px, char **err)
2914{
Emeric Brun750fe792020-12-23 16:51:12 +01002915 struct resolvers *resolvers = NULL;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002916
Emeric Brun21fbeed2020-12-23 18:01:04 +01002917 if (rule->arg.resolv.resolvers_id == NULL) {
Baptiste Assmann333939c2019-01-21 08:34:50 +01002918 memprintf(err,"Proxy '%s': %s", px->id, "do-resolve action without resolvers");
2919 return 0;
2920 }
2921
Emeric Brun21fbeed2020-12-23 18:01:04 +01002922 resolvers = find_resolvers_by_id(rule->arg.resolv.resolvers_id);
Baptiste Assmann333939c2019-01-21 08:34:50 +01002923 if (resolvers == NULL) {
Emeric Brun21fbeed2020-12-23 18:01:04 +01002924 memprintf(err,"Can't find resolvers section '%s' for do-resolve action", rule->arg.resolv.resolvers_id);
Baptiste Assmann333939c2019-01-21 08:34:50 +01002925 return 0;
2926 }
Emeric Brun21fbeed2020-12-23 18:01:04 +01002927 rule->arg.resolv.resolvers = resolvers;
Baptiste Assmann333939c2019-01-21 08:34:50 +01002928
2929 return 1;
2930}
Emeric Brun8a551932020-11-24 17:24:34 +01002931/*
2932 * Parse a <resolvers> section.
2933 * Returns the error code, 0 if OK, or any combination of :
2934 * - ERR_ABORT: must abort ASAP
2935 * - ERR_FATAL: we can continue parsing but not start the service
2936 * - ERR_WARN: a warning has been emitted
2937 * - ERR_ALERT: an alert has been emitted
2938 * Only the two first ones can stop processing, the two others are just
2939 * indicators.
2940 */
2941int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
2942{
2943 const char *err;
2944 int err_code = 0;
2945 char *errmsg = NULL;
2946
2947 if (strcmp(args[0], "resolvers") == 0) { /* new resolvers section */
2948 if (!*args[1]) {
2949 ha_alert("parsing [%s:%d] : missing name for resolvers section.\n", file, linenum);
2950 err_code |= ERR_ALERT | ERR_ABORT;
2951 goto out;
2952 }
2953
2954 err = invalid_char(args[1]);
2955 if (err) {
2956 ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
2957 file, linenum, *err, args[0], args[1]);
2958 err_code |= ERR_ALERT | ERR_ABORT;
2959 goto out;
2960 }
2961
2962 list_for_each_entry(curr_resolvers, &sec_resolvers, list) {
2963 /* Error if two resolvers owns the same name */
2964 if (strcmp(curr_resolvers->id, args[1]) == 0) {
2965 ha_alert("Parsing [%s:%d]: resolvers '%s' has same name as another resolvers (declared at %s:%d).\n",
2966 file, linenum, args[1], curr_resolvers->conf.file, curr_resolvers->conf.line);
2967 err_code |= ERR_ALERT | ERR_ABORT;
2968 }
2969 }
2970
2971 if ((curr_resolvers = calloc(1, sizeof(*curr_resolvers))) == NULL) {
2972 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
2973 err_code |= ERR_ALERT | ERR_ABORT;
2974 goto out;
2975 }
2976
2977 /* default values */
2978 LIST_ADDQ(&sec_resolvers, &curr_resolvers->list);
2979 curr_resolvers->conf.file = strdup(file);
2980 curr_resolvers->conf.line = linenum;
2981 curr_resolvers->id = strdup(args[1]);
2982 curr_resolvers->query_ids = EB_ROOT;
2983 /* default maximum response size */
2984 curr_resolvers->accepted_payload_size = 512;
2985 /* default hold period for nx, other, refuse and timeout is 30s */
2986 curr_resolvers->hold.nx = 30000;
2987 curr_resolvers->hold.other = 30000;
2988 curr_resolvers->hold.refused = 30000;
2989 curr_resolvers->hold.timeout = 30000;
2990 curr_resolvers->hold.obsolete = 0;
2991 /* default hold period for valid is 10s */
2992 curr_resolvers->hold.valid = 10000;
2993 curr_resolvers->timeout.resolve = 1000;
2994 curr_resolvers->timeout.retry = 1000;
2995 curr_resolvers->resolve_retries = 3;
2996 curr_resolvers->nb_nameservers = 0;
2997 LIST_INIT(&curr_resolvers->nameservers);
2998 LIST_INIT(&curr_resolvers->resolutions.curr);
2999 LIST_INIT(&curr_resolvers->resolutions.wait);
3000 HA_SPIN_INIT(&curr_resolvers->lock);
3001 }
3002 else if (strcmp(args[0], "nameserver") == 0) { /* nameserver definition */
3003 struct dns_nameserver *newnameserver = NULL;
3004 struct sockaddr_storage *sk;
3005 int port1, port2;
3006
3007 if (!*args[2]) {
3008 ha_alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
3009 file, linenum, args[0]);
3010 err_code |= ERR_ALERT | ERR_FATAL;
3011 goto out;
3012 }
3013
3014 err = invalid_char(args[1]);
3015 if (err) {
3016 ha_alert("parsing [%s:%d] : character '%c' is not permitted in server name '%s'.\n",
3017 file, linenum, *err, args[1]);
3018 err_code |= ERR_ALERT | ERR_FATAL;
3019 goto out;
3020 }
3021
3022 list_for_each_entry(newnameserver, &curr_resolvers->nameservers, list) {
3023 /* Error if two resolvers owns the same name */
3024 if (strcmp(newnameserver->id, args[1]) == 0) {
3025 ha_alert("Parsing [%s:%d]: nameserver '%s' has same name as another nameserver (declared at %s:%d).\n",
3026 file, linenum, args[1], newnameserver->conf.file, newnameserver->conf.line);
3027 err_code |= ERR_ALERT | ERR_FATAL;
3028 }
3029 }
3030
3031 if ((newnameserver = calloc(1, sizeof(*newnameserver))) == NULL) {
3032 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3033 err_code |= ERR_ALERT | ERR_ABORT;
3034 goto out;
3035 }
3036
3037 /* the nameservers are linked backward first */
3038 LIST_ADDQ(&curr_resolvers->nameservers, &newnameserver->list);
Emeric Brun6a2006a2021-01-04 13:18:55 +01003039 newnameserver->parent = curr_resolvers;
Emeric Brun8a551932020-11-24 17:24:34 +01003040 newnameserver->conf.file = strdup(file);
3041 newnameserver->conf.line = linenum;
3042 newnameserver->id = strdup(args[1]);
3043
3044 sk = str2sa_range(args[2], NULL, &port1, &port2, NULL, NULL,
3045 &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_DGRAM);
3046 if (!sk) {
3047 ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
3048 err_code |= ERR_ALERT | ERR_FATAL;
3049 goto out;
3050 }
3051
3052 newnameserver->addr = *sk;
3053 }
3054 else if (strcmp(args[0], "parse-resolv-conf") == 0) {
3055 struct dns_nameserver *newnameserver = NULL;
3056 const char *whitespace = "\r\n\t ";
3057 char *resolv_line = NULL;
3058 int resolv_linenum = 0;
3059 FILE *f = NULL;
3060 char *address = NULL;
3061 struct sockaddr_storage *sk = NULL;
3062 struct protocol *proto;
3063 int duplicate_name = 0;
3064
3065 if ((resolv_line = malloc(sizeof(*resolv_line) * LINESIZE)) == NULL) {
3066 ha_alert("parsing [%s:%d] : out of memory.\n",
3067 file, linenum);
3068 err_code |= ERR_ALERT | ERR_FATAL;
3069 goto resolv_out;
3070 }
3071
3072 if ((f = fopen("/etc/resolv.conf", "r")) == NULL) {
3073 ha_alert("parsing [%s:%d] : failed to open /etc/resolv.conf.\n",
3074 file, linenum);
3075 err_code |= ERR_ALERT | ERR_FATAL;
3076 goto resolv_out;
3077 }
3078
3079 sk = calloc(1, sizeof(*sk));
3080 if (sk == NULL) {
3081 ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n",
3082 resolv_linenum);
3083 err_code |= ERR_ALERT | ERR_FATAL;
3084 goto resolv_out;
3085 }
3086
3087 while (fgets(resolv_line, LINESIZE, f) != NULL) {
3088 resolv_linenum++;
3089 if (strncmp(resolv_line, "nameserver", 10) != 0)
3090 continue;
3091
3092 address = strtok(resolv_line + 10, whitespace);
3093 if (address == resolv_line + 10)
3094 continue;
3095
3096 if (address == NULL) {
3097 ha_warning("parsing [/etc/resolv.conf:%d] : nameserver line is missing address.\n",
3098 resolv_linenum);
3099 err_code |= ERR_WARN;
3100 continue;
3101 }
3102
3103 duplicate_name = 0;
3104 list_for_each_entry(newnameserver, &curr_resolvers->nameservers, list) {
3105 if (strcmp(newnameserver->id, address) == 0) {
3106 ha_warning("Parsing [/etc/resolv.conf:%d] : generated name for /etc/resolv.conf nameserver '%s' conflicts with another nameserver (declared at %s:%d), it appears to be a duplicate and will be excluded.\n",
3107 resolv_linenum, address, newnameserver->conf.file, newnameserver->conf.line);
3108 err_code |= ERR_WARN;
3109 duplicate_name = 1;
3110 }
3111 }
3112
3113 if (duplicate_name)
3114 continue;
3115
3116 memset(sk, 0, sizeof(*sk));
3117 if (!str2ip2(address, sk, 1)) {
3118 ha_warning("parsing [/etc/resolv.conf:%d] : address '%s' could not be recognized, nameserver will be excluded.\n",
3119 resolv_linenum, address);
3120 err_code |= ERR_WARN;
3121 continue;
3122 }
3123
3124 set_host_port(sk, 53);
3125
3126 proto = protocol_by_family(sk->ss_family);
3127 if (!proto || !proto->connect) {
3128 ha_warning("parsing [/etc/resolv.conf:%d] : '%s' : connect() not supported for this address family.\n",
3129 resolv_linenum, address);
3130 err_code |= ERR_WARN;
3131 continue;
3132 }
3133
3134 if ((newnameserver = calloc(1, sizeof(*newnameserver))) == NULL) {
3135 ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum);
3136 err_code |= ERR_ALERT | ERR_FATAL;
3137 goto resolv_out;
3138 }
3139
3140 newnameserver->conf.file = strdup("/etc/resolv.conf");
3141 if (newnameserver->conf.file == NULL) {
3142 ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum);
3143 err_code |= ERR_ALERT | ERR_FATAL;
3144 free(newnameserver);
3145 goto resolv_out;
3146 }
3147
3148 newnameserver->id = strdup(address);
3149 if (newnameserver->id == NULL) {
3150 ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum);
3151 err_code |= ERR_ALERT | ERR_FATAL;
3152 free((char *)newnameserver->conf.file);
3153 free(newnameserver);
3154 goto resolv_out;
3155 }
3156
Emeric Brun6a2006a2021-01-04 13:18:55 +01003157 newnameserver->parent = curr_resolvers;
Emeric Brun8a551932020-11-24 17:24:34 +01003158 newnameserver->conf.line = resolv_linenum;
3159 newnameserver->addr = *sk;
3160
3161 LIST_ADDQ(&curr_resolvers->nameservers, &newnameserver->list);
3162 }
3163
3164resolv_out:
3165 free(sk);
3166 free(resolv_line);
3167 if (f != NULL)
3168 fclose(f);
3169 }
3170 else if (strcmp(args[0], "hold") == 0) { /* hold periods */
3171 const char *res;
3172 unsigned int time;
3173
3174 if (!*args[2]) {
3175 ha_alert("parsing [%s:%d] : '%s' expects an <event> and a <time> as arguments.\n",
3176 file, linenum, args[0]);
3177 ha_alert("<event> can be either 'valid', 'nx', 'refused', 'timeout', or 'other'\n");
3178 err_code |= ERR_ALERT | ERR_FATAL;
3179 goto out;
3180 }
3181 res = parse_time_err(args[2], &time, TIME_UNIT_MS);
3182 if (res == PARSE_TIME_OVER) {
3183 ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to <%s>, maximum value is 2147483647 ms (~24.8 days).\n",
3184 file, linenum, args[1], args[0]);
3185 err_code |= ERR_ALERT | ERR_FATAL;
3186 goto out;
3187 }
3188 else if (res == PARSE_TIME_UNDER) {
3189 ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to <%s>, minimum non-null value is 1 ms.\n",
3190 file, linenum, args[1], args[0]);
3191 err_code |= ERR_ALERT | ERR_FATAL;
3192 goto out;
3193 }
3194 else if (res) {
3195 ha_alert("parsing [%s:%d]: unexpected character '%c' in argument to <%s>.\n",
3196 file, linenum, *res, args[0]);
3197 err_code |= ERR_ALERT | ERR_FATAL;
3198 goto out;
3199 }
3200 if (strcmp(args[1], "nx") == 0)
3201 curr_resolvers->hold.nx = time;
3202 else if (strcmp(args[1], "other") == 0)
3203 curr_resolvers->hold.other = time;
3204 else if (strcmp(args[1], "refused") == 0)
3205 curr_resolvers->hold.refused = time;
3206 else if (strcmp(args[1], "timeout") == 0)
3207 curr_resolvers->hold.timeout = time;
3208 else if (strcmp(args[1], "valid") == 0)
3209 curr_resolvers->hold.valid = time;
3210 else if (strcmp(args[1], "obsolete") == 0)
3211 curr_resolvers->hold.obsolete = time;
3212 else {
3213 ha_alert("parsing [%s:%d] : '%s' unknown <event>: '%s', expects either 'nx', 'timeout', 'valid', 'obsolete' or 'other'.\n",
3214 file, linenum, args[0], args[1]);
3215 err_code |= ERR_ALERT | ERR_FATAL;
3216 goto out;
3217 }
3218
3219 }
3220 else if (strcmp(args[0], "accepted_payload_size") == 0) {
3221 int i = 0;
3222
3223 if (!*args[1]) {
3224 ha_alert("parsing [%s:%d] : '%s' expects <nb> as argument.\n",
3225 file, linenum, args[0]);
3226 err_code |= ERR_ALERT | ERR_FATAL;
3227 goto out;
3228 }
3229
3230 i = atoi(args[1]);
3231 if (i < DNS_HEADER_SIZE || i > DNS_MAX_UDP_MESSAGE) {
3232 ha_alert("parsing [%s:%d] : '%s' must be between %d and %d inclusive (was %s).\n",
3233 file, linenum, args[0], DNS_HEADER_SIZE, DNS_MAX_UDP_MESSAGE, args[1]);
3234 err_code |= ERR_ALERT | ERR_FATAL;
3235 goto out;
3236 }
3237
3238 curr_resolvers->accepted_payload_size = i;
3239 }
3240 else if (strcmp(args[0], "resolution_pool_size") == 0) {
3241 ha_alert("parsing [%s:%d] : '%s' directive is not supported anymore (it never appeared in a stable release).\n",
3242 file, linenum, args[0]);
3243 err_code |= ERR_ALERT | ERR_FATAL;
3244 goto out;
3245 }
3246 else if (strcmp(args[0], "resolve_retries") == 0) {
3247 if (!*args[1]) {
3248 ha_alert("parsing [%s:%d] : '%s' expects <nb> as argument.\n",
3249 file, linenum, args[0]);
3250 err_code |= ERR_ALERT | ERR_FATAL;
3251 goto out;
3252 }
3253 curr_resolvers->resolve_retries = atoi(args[1]);
3254 }
3255 else if (strcmp(args[0], "timeout") == 0) {
3256 if (!*args[1]) {
3257 ha_alert("parsing [%s:%d] : '%s' expects 'retry' or 'resolve' and <time> as arguments.\n",
3258 file, linenum, args[0]);
3259 err_code |= ERR_ALERT | ERR_FATAL;
3260 goto out;
3261 }
3262 else if (strcmp(args[1], "retry") == 0 ||
3263 strcmp(args[1], "resolve") == 0) {
3264 const char *res;
3265 unsigned int tout;
3266
3267 if (!*args[2]) {
3268 ha_alert("parsing [%s:%d] : '%s %s' expects <time> as argument.\n",
3269 file, linenum, args[0], args[1]);
3270 err_code |= ERR_ALERT | ERR_FATAL;
3271 goto out;
3272 }
3273 res = parse_time_err(args[2], &tout, TIME_UNIT_MS);
3274 if (res == PARSE_TIME_OVER) {
3275 ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to <%s %s>, maximum value is 2147483647 ms (~24.8 days).\n",
3276 file, linenum, args[2], args[0], args[1]);
3277 err_code |= ERR_ALERT | ERR_FATAL;
3278 goto out;
3279 }
3280 else if (res == PARSE_TIME_UNDER) {
3281 ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to <%s %s>, minimum non-null value is 1 ms.\n",
3282 file, linenum, args[2], args[0], args[1]);
3283 err_code |= ERR_ALERT | ERR_FATAL;
3284 goto out;
3285 }
3286 else if (res) {
3287 ha_alert("parsing [%s:%d]: unexpected character '%c' in argument to <%s %s>.\n",
3288 file, linenum, *res, args[0], args[1]);
3289 err_code |= ERR_ALERT | ERR_FATAL;
3290 goto out;
3291 }
3292 if (args[1][2] == 't')
3293 curr_resolvers->timeout.retry = tout;
3294 else
3295 curr_resolvers->timeout.resolve = tout;
3296 }
3297 else {
3298 ha_alert("parsing [%s:%d] : '%s' expects 'retry' or 'resolve' and <time> as arguments got '%s'.\n",
3299 file, linenum, args[0], args[1]);
3300 err_code |= ERR_ALERT | ERR_FATAL;
3301 goto out;
3302 }
3303 }
3304 else if (*args[0] != 0) {
3305 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], cursection);
3306 err_code |= ERR_ALERT | ERR_FATAL;
3307 goto out;
3308 }
3309
3310 out:
3311 free(errmsg);
3312 return err_code;
3313}
Baptiste Assmann333939c2019-01-21 08:34:50 +01003314
Emeric Brun8a551932020-11-24 17:24:34 +01003315REGISTER_CONFIG_SECTION("resolvers", cfg_parse_resolvers, NULL);
Emeric Brund30e9a12020-12-23 18:49:16 +01003316REGISTER_POST_DEINIT(resolvers_deinit);
3317REGISTER_CONFIG_POSTPARSER("dns runtime resolver", resolvers_finalize_config);