blob: 23e9d9de538b00b816e06349ab46d2472f2ca1dd [file] [log] [blame]
Baptiste Assmann325137d2015-04-13 23:40:55 +02001/*
2 * Name server resolution
3 *
Willy Tarreau714f3452021-05-09 06:47:26 +02004 * Copyright 2020 HAProxy Technologies
Baptiste Assmann325137d2015-04-13 23:40:55 +02005 *
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>
Baptiste Assmann325137d2015-04-13 23:40:55 +020014#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <unistd.h>
18
19#include <sys/types.h>
20
Willy Tarreau122eba92020-06-04 10:15:32 +020021#include <haproxy/action.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020022#include <haproxy/api.h>
Christopher Faulet6b0a0fb2022-04-04 11:29:28 +020023#include <haproxy/applet.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>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020032#include <haproxy/log.h>
Emeric Brund26a6232021-01-04 13:32:20 +010033#include <haproxy/ring.h>
Willy Tarreau5edca2f2022-05-27 09:25:10 +020034#include <haproxy/sc_strm.h>
Willy Tarreaucb086c62022-05-27 09:47:12 +020035#include <haproxy/stconn.h>
Emeric Brunfd647d52021-02-12 20:03:38 +010036#include <haproxy/stream.h>
Willy Tarreau9f9e9fc2021-05-08 13:09:46 +020037#include <haproxy/tools.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020038
Emeric Brund26a6232021-01-04 13:32:20 +010039static THREAD_LOCAL char *dns_msg_trash;
Baptiste Assmann325137d2015-04-13 23:40:55 +020040
Emeric Brunfd647d52021-02-12 20:03:38 +010041DECLARE_STATIC_POOL(dns_session_pool, "dns_session", sizeof(struct dns_session));
42DECLARE_STATIC_POOL(dns_query_pool, "dns_query", sizeof(struct dns_query));
43DECLARE_STATIC_POOL(dns_msg_buf, "dns_msg_buf", DNS_TCP_MSG_RING_MAX_SIZE);
44
Christopher Faulet67957bd2017-09-27 11:00:59 +020045/* Opens an UDP socket on the namesaver's IP/Port, if required. Returns 0 on
Christopher Faulet1e711be2021-03-04 16:58:35 +010046 * success, -1 otherwise. ns->dgram must be defined.
Baptiste Assmann325137d2015-04-13 23:40:55 +020047 */
Emeric Brund26a6232021-01-04 13:32:20 +010048static int dns_connect_nameserver(struct dns_nameserver *ns)
Baptiste Assmann325137d2015-04-13 23:40:55 +020049{
Christopher Faulet1e711be2021-03-04 16:58:35 +010050 struct dgram_conn *dgram = &ns->dgram->conn;
51 int fd;
Baptiste Assmann325137d2015-04-13 23:40:55 +020052
Christopher Faulet1e711be2021-03-04 16:58:35 +010053 /* Already connected */
54 if (dgram->t.sock.fd != -1)
Emeric Brun526b7922021-02-15 14:28:27 +010055 return 0;
Christopher Faulet1e711be2021-03-04 16:58:35 +010056
57 /* Create an UDP socket and connect it on the nameserver's IP/Port */
58 if ((fd = socket(dgram->addr.to.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
59 send_log(NULL, LOG_WARNING,
60 "DNS : section '%s': can't create socket for nameserver '%s'.\n",
61 ns->counters->pid, ns->id);
62 return -1;
63 }
64 if (connect(fd, (struct sockaddr*)&dgram->addr.to, get_addr_len(&dgram->addr.to)) == -1) {
65 send_log(NULL, LOG_WARNING,
66 "DNS : section '%s': can't connect socket for nameserver '%s'.\n",
67 ns->counters->id, ns->id);
68 close(fd);
69 return -1;
Emeric Brunc9437992021-02-12 19:42:55 +010070 }
Emeric Brun526b7922021-02-15 14:28:27 +010071
Christopher Faulet1e711be2021-03-04 16:58:35 +010072 /* Make the socket non blocking */
Willy Tarreau38247432022-04-26 10:24:14 +020073 fd_set_nonblock(fd);
Christopher Faulet1e711be2021-03-04 16:58:35 +010074
75 /* Add the fd in the fd list and update its parameters */
76 dgram->t.sock.fd = fd;
Willy Tarreau27a32452022-07-07 08:29:00 +020077 fd_insert(fd, dgram, dgram_fd_handler, tgid, tg->threads_enabled);
Christopher Faulet1e711be2021-03-04 16:58:35 +010078 fd_want_recv(fd);
79 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +020080}
81
Emeric Brund26a6232021-01-04 13:32:20 +010082/* Sends a message to a name server
83 * It returns message length on success
84 * or -1 in error case
85 * 0 is returned in case of output ring buffer is full
86 */
87int dns_send_nameserver(struct dns_nameserver *ns, void *buf, size_t len)
88{
89 int ret = -1;
90
91 if (ns->dgram) {
92 struct dgram_conn *dgram = &ns->dgram->conn;
Emeric Brun314e6ec2022-05-10 11:35:48 +020093 int fd;
Emeric Brund26a6232021-01-04 13:32:20 +010094
Emeric Brun314e6ec2022-05-10 11:35:48 +020095 HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
96 fd = dgram->t.sock.fd;
97 if (fd == -1) {
98 if (dns_connect_nameserver(ns) == -1) {
99 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100100 return -1;
Emeric Brun314e6ec2022-05-10 11:35:48 +0200101 }
Emeric Brund26a6232021-01-04 13:32:20 +0100102 fd = dgram->t.sock.fd;
103 }
104
105 ret = send(fd, buf, len, 0);
106 if (ret < 0) {
Willy Tarreauacef5e22022-04-25 20:32:15 +0200107 if (errno == EAGAIN || errno == EWOULDBLOCK) {
Emeric Brund26a6232021-01-04 13:32:20 +0100108 struct ist myist;
109
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100110 myist = ist2(buf, len);
Emeric Brund26a6232021-01-04 13:32:20 +0100111 ret = ring_write(ns->dgram->ring_req, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
112 if (!ret) {
113 ns->counters->snd_error++;
Emeric Brun314e6ec2022-05-10 11:35:48 +0200114 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100115 return -1;
116 }
117 fd_cant_send(fd);
Emeric Brun314e6ec2022-05-10 11:35:48 +0200118 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100119 return ret;
120 }
121 ns->counters->snd_error++;
122 fd_delete(fd);
Emeric Brund26a6232021-01-04 13:32:20 +0100123 dgram->t.sock.fd = -1;
Emeric Brun314e6ec2022-05-10 11:35:48 +0200124 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100125 return -1;
126 }
127 ns->counters->sent++;
Emeric Brun314e6ec2022-05-10 11:35:48 +0200128 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100129 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100130 else if (ns->stream) {
131 struct ist myist;
132
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100133 myist = ist2(buf, len);
Emeric Brunfd647d52021-02-12 20:03:38 +0100134 ret = ring_write(ns->stream->ring_req, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
135 if (!ret) {
136 ns->counters->snd_error++;
137 return -1;
138 }
139 task_wakeup(ns->stream->task_req, TASK_WOKEN_MSG);
140 return ret;
141 }
Emeric Brund26a6232021-01-04 13:32:20 +0100142
143 return ret;
144}
145
Emeric Brunfd647d52021-02-12 20:03:38 +0100146void dns_session_free(struct dns_session *);
147
Emeric Brund26a6232021-01-04 13:32:20 +0100148/* Receives a dns message
149 * Returns message length
150 * 0 is returned if no more message available
151 * -1 in error case
152 */
153ssize_t dns_recv_nameserver(struct dns_nameserver *ns, void *data, size_t size)
154{
155 ssize_t ret = -1;
156
157 if (ns->dgram) {
158 struct dgram_conn *dgram = &ns->dgram->conn;
Emeric Brun314e6ec2022-05-10 11:35:48 +0200159 int fd;
Emeric Brund26a6232021-01-04 13:32:20 +0100160
Emeric Brun314e6ec2022-05-10 11:35:48 +0200161 HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
162 fd = dgram->t.sock.fd;
163 if (fd == -1) {
164 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100165 return -1;
Emeric Brun314e6ec2022-05-10 11:35:48 +0200166 }
Emeric Brund26a6232021-01-04 13:32:20 +0100167
168 if ((ret = recv(fd, data, size, 0)) < 0) {
Willy Tarreauacef5e22022-04-25 20:32:15 +0200169 if (errno == EAGAIN || errno == EWOULDBLOCK) {
Emeric Brund26a6232021-01-04 13:32:20 +0100170 fd_cant_recv(fd);
Emeric Brun314e6ec2022-05-10 11:35:48 +0200171 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100172 return 0;
173 }
174 fd_delete(fd);
Emeric Brund26a6232021-01-04 13:32:20 +0100175 dgram->t.sock.fd = -1;
Emeric Brun314e6ec2022-05-10 11:35:48 +0200176 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100177 return -1;
178 }
Emeric Brun314e6ec2022-05-10 11:35:48 +0200179 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100180 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100181 else if (ns->stream) {
182 struct dns_stream_server *dss = ns->stream;
183 struct dns_session *ds;
184
185 HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
186
187 if (!LIST_ISEMPTY(&dss->wait_sess)) {
188 ds = LIST_NEXT(&dss->wait_sess, struct dns_session *, waiter);
Emeric Brunfd647d52021-02-12 20:03:38 +0100189 ret = ds->rx_msg.len < size ? ds->rx_msg.len : size;
190 memcpy(data, ds->rx_msg.area, ret);
191
192 ds->rx_msg.len = 0;
193
Willy Tarreaudde1b442021-10-21 14:33:38 +0200194 /* This barrier is here to ensure that all data is
195 * stored if the appctx detect the elem is out of the
196 * list.
197 */
198 __ha_barrier_store();
199
Emeric Brunfd647d52021-02-12 20:03:38 +0100200 LIST_DEL_INIT(&ds->waiter);
201
202 if (ds->appctx) {
Willy Tarreaudde1b442021-10-21 14:33:38 +0200203 /* This second barrier is here to ensure that
204 * the waked up appctx won't miss that the elem
205 * is removed from the list.
206 */
207 __ha_barrier_store();
208
Ilya Shipitsin0de36ad2021-02-20 00:23:36 +0500209 /* awake appctx because it may have other
Emeric Brunfd647d52021-02-12 20:03:38 +0100210 * message to receive
211 */
212 appctx_wakeup(ds->appctx);
213
214 /* dns_session could already be into free_sess list
215 * so we firstly remove it */
216 LIST_DEL_INIT(&ds->list);
217
218 /* decrease nb_queries to free a slot for a new query on that sess */
219 ds->nb_queries--;
220 if (ds->nb_queries) {
221 /* it remains pipelined unanswered request
222 * into this session but we just decrease
223 * the counter so the session
224 * can not be full of pipelined requests
225 * so we can add if to free_sess list
226 * to receive a new request
227 */
Willy Tarreau2b718102021-04-21 07:32:39 +0200228 LIST_INSERT(&ds->dss->free_sess, &ds->list);
Emeric Brunfd647d52021-02-12 20:03:38 +0100229 }
230 else {
231 /* there is no more pipelined requests
232 * into this session, so we move it
233 * to idle_sess list */
Willy Tarreau2b718102021-04-21 07:32:39 +0200234 LIST_INSERT(&ds->dss->idle_sess, &ds->list);
Emeric Brunfd647d52021-02-12 20:03:38 +0100235
236 /* update the counter of idle sessions */
237 ds->dss->idle_conns++;
238
239 /* Note: this is useless there to update
240 * the max_active_conns since we increase
241 * the idle count */
242 }
243 }
244 else {
245 /* there is no more appctx for this session
246 * it means it is ready to die
247 */
248 dns_session_free(ds);
249 }
250
251
252 }
253
254 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
255 }
Emeric Brund26a6232021-01-04 13:32:20 +0100256
257 return ret;
258}
259
260static void dns_resolve_recv(struct dgram_conn *dgram)
261{
262 struct dns_nameserver *ns;
263 int fd;
264
Emeric Brun314e6ec2022-05-10 11:35:48 +0200265 HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
266
Emeric Brund26a6232021-01-04 13:32:20 +0100267 fd = dgram->t.sock.fd;
268
269 /* check if ready for reading */
Emeric Brun314e6ec2022-05-10 11:35:48 +0200270 if ((fd == -1) || !fd_recv_ready(fd)) {
271 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100272 return;
Emeric Brun314e6ec2022-05-10 11:35:48 +0200273 }
Emeric Brund26a6232021-01-04 13:32:20 +0100274
275 /* no need to go further if we can't retrieve the nameserver */
276 if ((ns = dgram->owner) == NULL) {
Willy Tarreauf5090652021-04-06 17:23:40 +0200277 _HA_ATOMIC_AND(&fdtab[fd].state, ~(FD_POLL_HUP|FD_POLL_ERR));
Emeric Brund26a6232021-01-04 13:32:20 +0100278 fd_stop_recv(fd);
Emeric Brun314e6ec2022-05-10 11:35:48 +0200279 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100280 return;
281 }
282
Emeric Brun314e6ec2022-05-10 11:35:48 +0200283 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
284
Emeric Brund26a6232021-01-04 13:32:20 +0100285 ns->process_responses(ns);
286}
287
288/* Called when a dns network socket is ready to send data */
289static void dns_resolve_send(struct dgram_conn *dgram)
290{
291 int fd;
292 struct dns_nameserver *ns;
293 struct ring *ring;
294 struct buffer *buf;
295 uint64_t msg_len;
296 size_t len, cnt, ofs;
297
Emeric Brun314e6ec2022-05-10 11:35:48 +0200298 HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
299
Emeric Brund26a6232021-01-04 13:32:20 +0100300 fd = dgram->t.sock.fd;
301
302 /* check if ready for sending */
Emeric Brun314e6ec2022-05-10 11:35:48 +0200303 if ((fd == -1) || !fd_send_ready(fd)) {
304 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100305 return;
Emeric Brun314e6ec2022-05-10 11:35:48 +0200306 }
Emeric Brund26a6232021-01-04 13:32:20 +0100307
308 /* no need to go further if we can't retrieve the nameserver */
309 if ((ns = dgram->owner) == NULL) {
Willy Tarreauf5090652021-04-06 17:23:40 +0200310 _HA_ATOMIC_AND(&fdtab[fd].state, ~(FD_POLL_HUP|FD_POLL_ERR));
Emeric Brund26a6232021-01-04 13:32:20 +0100311 fd_stop_send(fd);
Emeric Brun314e6ec2022-05-10 11:35:48 +0200312 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100313 return;
314 }
315
316 ring = ns->dgram->ring_req;
317 buf = &ring->buf;
318
319 HA_RWLOCK_RDLOCK(DNS_LOCK, &ring->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100320
321 /* explanation for the initialization below: it would be better to do
322 * this in the parsing function but this would occasionally result in
323 * dropped events because we'd take a reference on the oldest message
324 * and keep it while being scheduled. Thus instead let's take it the
325 * first time we enter here so that we have a chance to pass many
326 * existing messages before grabbing a reference to a location. This
327 * value cannot be produced after initialization.
328 */
Aurelien DARRAGONbce0c0c2023-03-07 18:01:34 +0100329 if (unlikely(ns->dgram->ofs_req == ~0)) {
330 ns->dgram->ofs_req = b_peek_ofs(buf, 0);
331 HA_ATOMIC_INC(b_orig(buf) + ns->dgram->ofs_req);
Emeric Brund26a6232021-01-04 13:32:20 +0100332 }
333
334 /* we were already there, adjust the offset to be relative to
335 * the buffer's head and remove us from the counter.
336 */
Aurelien DARRAGONbce0c0c2023-03-07 18:01:34 +0100337 ofs = ns->dgram->ofs_req - b_head_ofs(buf);
338 if (ns->dgram->ofs_req < b_head_ofs(buf))
339 ofs += b_size(buf);
Emeric Brund26a6232021-01-04 13:32:20 +0100340 BUG_ON(ofs >= buf->size);
Willy Tarreau4781b152021-04-06 13:53:36 +0200341 HA_ATOMIC_DEC(b_peek(buf, ofs));
Emeric Brund26a6232021-01-04 13:32:20 +0100342
343 while (ofs + 1 < b_data(buf)) {
344 int ret;
345
346 cnt = 1;
347 len = b_peek_varint(buf, ofs + cnt, &msg_len);
348 if (!len)
349 break;
350 cnt += len;
351 BUG_ON(msg_len + ofs + cnt + 1 > b_data(buf));
352 if (unlikely(msg_len > DNS_TCP_MSG_MAX_SIZE)) {
353 /* too large a message to ever fit, let's skip it */
354 ofs += cnt + msg_len;
355 continue;
356 }
357
358 len = b_getblk(buf, dns_msg_trash, msg_len, ofs + cnt);
359
360 ret = send(fd, dns_msg_trash, len, 0);
361 if (ret < 0) {
Willy Tarreauacef5e22022-04-25 20:32:15 +0200362 if (errno == EAGAIN || errno == EWOULDBLOCK) {
Emeric Brund26a6232021-01-04 13:32:20 +0100363 fd_cant_send(fd);
364 goto out;
365 }
366 ns->counters->snd_error++;
367 fd_delete(fd);
Emeric Brund26a6232021-01-04 13:32:20 +0100368 fd = dgram->t.sock.fd = -1;
369 goto out;
370 }
371 ns->counters->sent++;
372
373 ofs += cnt + len;
374 }
375
376 /* we don't want/need to be waked up any more for sending
377 * because all ring content is sent */
378 fd_stop_send(fd);
379
380out:
Willy Tarreau4781b152021-04-06 13:53:36 +0200381 HA_ATOMIC_INC(b_peek(buf, ofs));
Aurelien DARRAGONbce0c0c2023-03-07 18:01:34 +0100382 ns->dgram->ofs_req = b_peek_ofs(buf, ofs);
Emeric Brund26a6232021-01-04 13:32:20 +0100383 HA_RWLOCK_RDUNLOCK(DNS_LOCK, &ring->lock);
Emeric Brun314e6ec2022-05-10 11:35:48 +0200384 HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
Emeric Brund26a6232021-01-04 13:32:20 +0100385}
386
Emeric Brunc9437992021-02-12 19:42:55 +0100387/* proto_udp callback functions for a DNS resolution */
388struct dgram_data_cb dns_dgram_cb = {
389 .recv = dns_resolve_recv,
390 .send = dns_resolve_send,
391};
Baptiste Assmann325137d2015-04-13 23:40:55 +0200392
Emeric Brunc9437992021-02-12 19:42:55 +0100393int dns_dgram_init(struct dns_nameserver *ns, struct sockaddr_storage *sk)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200394{
Emeric Brunc9437992021-02-12 19:42:55 +0100395 struct dns_dgram_server *dgram;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200396
Emeric Brunc9437992021-02-12 19:42:55 +0100397 if ((dgram = calloc(1, sizeof(*dgram))) == NULL)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200398 return -1;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200399
Emeric Brunc9437992021-02-12 19:42:55 +0100400 /* Leave dgram partially initialized, no FD attached for
401 * now. */
402 dgram->conn.owner = ns;
403 dgram->conn.data = &dns_dgram_cb;
404 dgram->conn.t.sock.fd = -1;
405 dgram->conn.addr.to = *sk;
Emeric Brun314e6ec2022-05-10 11:35:48 +0200406 HA_SPIN_INIT(&dgram->conn.lock);
Emeric Brunc9437992021-02-12 19:42:55 +0100407 ns->dgram = dgram;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200408
Emeric Brunc9437992021-02-12 19:42:55 +0100409 dgram->ofs_req = ~0; /* init ring offset */
410 dgram->ring_req = ring_new(2*DNS_TCP_MSG_RING_MAX_SIZE);
411 if (!dgram->ring_req) {
412 ha_alert("memory allocation error initializing the ring for nameserver.\n");
413 goto out;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200414 }
415
Emeric Brunc9437992021-02-12 19:42:55 +0100416 /* attach the task as reader */
417 if (!ring_attach(dgram->ring_req)) {
418 /* mark server attached to the ring */
419 ha_alert("nameserver sets too many watchers > 255 on ring. This is a bug and should not happen.\n");
420 goto out;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200421 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200422 return 0;
Emeric Brunc9437992021-02-12 19:42:55 +0100423out:
Tim Duesterhus1307cd42023-04-22 17:47:35 +0200424 ring_free(dgram->ring_req);
Christopher Fauletd6c6b5f2020-09-08 10:27:24 +0200425
Emeric Brunc9437992021-02-12 19:42:55 +0100426 free(dgram);
Olivier Houchard2ec2db92018-01-08 16:28:57 +0100427
Emeric Brunfd647d52021-02-12 20:03:38 +0100428 return -1;
429}
430
431/*
432 * IO Handler to handle message push to dns tcp server
Willy Tarreau0d626a52022-05-04 20:41:19 +0200433 * It takes its context from appctx->svcctx.
Emeric Brunfd647d52021-02-12 20:03:38 +0100434 */
435static void dns_session_io_handler(struct appctx *appctx)
436{
Willy Tarreauc12b3212022-05-27 11:08:15 +0200437 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau0d626a52022-05-04 20:41:19 +0200438 struct dns_session *ds = appctx->svcctx;
Emeric Brunfd647d52021-02-12 20:03:38 +0100439 struct ring *ring = &ds->ring;
440 struct buffer *buf = &ring->buf;
441 uint64_t msg_len;
442 int available_room;
443 size_t len, cnt, ofs;
444 int ret = 0;
445
Christopher Faulet26769b02023-03-31 10:48:03 +0200446 if (unlikely(se_fl_test(appctx->sedesc, (SE_FL_EOS|SE_FL_ERROR|SE_FL_SHR|SE_FL_SHW)))) {
447 co_skip(sc_oc(sc), co_data(sc_oc(sc)));
448 goto out;
449 }
450
Emeric Brunfd647d52021-02-12 20:03:38 +0100451 /* if stopping was requested, close immediately */
452 if (unlikely(stopping))
453 goto close;
454
455 /* we want to be sure to not miss that we have been awaked for a shutdown */
456 __ha_barrier_load();
457
458 /* that means the connection was requested to shutdown
459 * for instance idle expire */
460 if (ds->shutdown)
461 goto close;
462
Emeric Brunfd647d52021-02-12 20:03:38 +0100463 /* if the connection is not established, inform the stream that we want
464 * to be notified whenever the connection completes.
465 */
Willy Tarreaud7950ad2022-05-27 10:33:42 +0200466 if (sc_opposite(sc)->state < SC_ST_EST) {
Willy Tarreau90e8b452022-05-25 18:21:43 +0200467 applet_need_more_data(appctx);
Willy Tarreaub23edc82022-05-24 16:49:03 +0200468 se_need_remote_conn(appctx->sedesc);
Willy Tarreau4164eb92022-05-25 15:42:03 +0200469 applet_have_more_data(appctx);
Christopher Faulet26769b02023-03-31 10:48:03 +0200470 goto out;
Emeric Brunfd647d52021-02-12 20:03:38 +0100471 }
472
Emeric Brunfd647d52021-02-12 20:03:38 +0100473 HA_RWLOCK_WRLOCK(DNS_LOCK, &ring->lock);
474 LIST_DEL_INIT(&appctx->wait_entry);
475 HA_RWLOCK_WRUNLOCK(DNS_LOCK, &ring->lock);
476
477 HA_RWLOCK_RDLOCK(DNS_LOCK, &ring->lock);
478
479 /* explanation for the initialization below: it would be better to do
480 * this in the parsing function but this would occasionally result in
481 * dropped events because we'd take a reference on the oldest message
482 * and keep it while being scheduled. Thus instead let's take it the
483 * first time we enter here so that we have a chance to pass many
484 * existing messages before grabbing a reference to a location. This
485 * value cannot be produced after initialization.
486 */
Amaury Denoyelle737d10f2023-03-07 11:18:27 +0100487 if (unlikely(ds->ofs == ~0)) {
Aurelien DARRAGON5a43db22023-03-07 17:45:02 +0100488 ds->ofs = b_peek_ofs(buf, 0);
489 HA_ATOMIC_INC(b_orig(buf) + ds->ofs);
Emeric Brunfd647d52021-02-12 20:03:38 +0100490 }
491
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200492 /* we were already there, adjust the offset to be relative to
493 * the buffer's head and remove us from the counter.
Emeric Brunfd647d52021-02-12 20:03:38 +0100494 */
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200495 ofs = ds->ofs - b_head_ofs(buf);
496 if (ds->ofs < b_head_ofs(buf))
497 ofs += b_size(buf);
Amaury Denoyelle737d10f2023-03-07 11:18:27 +0100498
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200499 BUG_ON(ofs >= buf->size);
500 HA_ATOMIC_DEC(b_peek(buf, ofs));
Emeric Brunfd647d52021-02-12 20:03:38 +0100501
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200502 /* in following loop, ofs always points to the counter byte that
503 * precedes the message so that we can take our reference there if we
504 * have to stop before the end (ret=0).
505 */
506 ret = 1;
507 while (ofs + 1 < b_data(buf)) {
508 struct dns_query *query;
509 uint16_t original_qid;
510 uint16_t new_qid;
Emeric Brunfd647d52021-02-12 20:03:38 +0100511
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200512 cnt = 1;
513 len = b_peek_varint(buf, ofs + cnt, &msg_len);
514 if (!len)
515 break;
516 cnt += len;
517 BUG_ON(msg_len + ofs + cnt + 1 > b_data(buf));
Emeric Brunfd647d52021-02-12 20:03:38 +0100518
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200519 /* retrieve available room on output channel */
520 available_room = channel_recv_max(sc_ic(sc));
Emeric Brunfd647d52021-02-12 20:03:38 +0100521
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200522 /* tx_msg_offset null means we are at the start of a new message */
523 if (!ds->tx_msg_offset) {
524 uint16_t slen;
Emeric Brunfd647d52021-02-12 20:03:38 +0100525
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200526 /* check if there is enough room to put message len and query id */
527 if (available_room < sizeof(slen) + sizeof(new_qid)) {
Christopher Faulet7b3d38a2023-05-05 11:28:45 +0200528 sc_need_room(sc, sizeof(slen) + sizeof(new_qid));
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200529 ret = 0;
530 break;
531 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100532
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200533 /* put msg len into then channel */
534 slen = (uint16_t)msg_len;
535 slen = htons(slen);
536 applet_putblk(appctx, (char *)&slen, sizeof(slen));
537 available_room -= sizeof(slen);
Emeric Brunfd647d52021-02-12 20:03:38 +0100538
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200539 /* backup original query id */
540 len = b_getblk(buf, (char *)&original_qid, sizeof(original_qid), ofs + cnt);
541 if (!len) {
542 /* should never happen since messages are atomically
543 * written into ring
544 */
545 ret = 0;
546 break;
547 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100548
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200549 /* generates new query id */
550 new_qid = ++ds->query_counter;
551 new_qid = htons(new_qid);
Emeric Brunfd647d52021-02-12 20:03:38 +0100552
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200553 /* put new query id into the channel */
554 applet_putblk(appctx, (char *)&new_qid, sizeof(new_qid));
555 available_room -= sizeof(new_qid);
Emeric Brunfd647d52021-02-12 20:03:38 +0100556
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200557 /* keep query id mapping */
Emeric Brunfd647d52021-02-12 20:03:38 +0100558
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200559 query = pool_alloc(dns_query_pool);
560 if (query) {
561 query->qid.key = new_qid;
562 query->original_qid = original_qid;
563 query->expire = tick_add(now_ms, 5000);
564 LIST_INIT(&query->list);
565 if (LIST_ISEMPTY(&ds->queries)) {
566 /* enable task to handle expire */
567 ds->task_exp->expire = query->expire;
568 /* ensure this will be executed by the same
569 * thread than ds_session_release
570 * to ensure session_release is free
571 * to destroy the task */
572 task_queue(ds->task_exp);
Emeric Brunfd647d52021-02-12 20:03:38 +0100573 }
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200574 LIST_APPEND(&ds->queries, &query->list);
575 eb32_insert(&ds->query_ids, &query->qid);
576 ds->onfly_queries++;
Emeric Brunfd647d52021-02-12 20:03:38 +0100577 }
578
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200579 /* update the tx_offset to handle output in 16k streams */
580 ds->tx_msg_offset = sizeof(original_qid);
Emeric Brunfd647d52021-02-12 20:03:38 +0100581
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200582 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100583
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200584 /* check if it remains available room on output chan */
585 if (unlikely(!available_room)) {
Christopher Faulet7b3d38a2023-05-05 11:28:45 +0200586 sc_need_room(sc, 1);
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200587 ret = 0;
588 break;
589 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100590
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200591 chunk_reset(&trash);
592 if ((msg_len - ds->tx_msg_offset) > available_room) {
593 /* remaining msg data is too large to be written in output channel at one time */
Emeric Brunfd647d52021-02-12 20:03:38 +0100594
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200595 len = b_getblk(buf, trash.area, available_room, ofs + cnt + ds->tx_msg_offset);
Emeric Brunfd647d52021-02-12 20:03:38 +0100596
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200597 /* update offset to complete mesg forwarding later */
598 ds->tx_msg_offset += len;
599 }
600 else {
601 /* remaining msg data can be written in output channel at one time */
602 len = b_getblk(buf, trash.area, msg_len - ds->tx_msg_offset, ofs + cnt + ds->tx_msg_offset);
Emeric Brunfd647d52021-02-12 20:03:38 +0100603
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200604 /* reset tx_msg_offset to mark forward fully processed */
605 ds->tx_msg_offset = 0;
606 }
607 trash.data += len;
Emeric Brunfd647d52021-02-12 20:03:38 +0100608
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200609 if (applet_putchk(appctx, &trash) == -1) {
610 /* should never happen since we
611 * check available_room is large
612 * enough here.
613 */
614 ret = 0;
615 break;
Emeric Brunfd647d52021-02-12 20:03:38 +0100616 }
617
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200618 if (ds->tx_msg_offset) {
619 /* msg was not fully processed, we must be awake to drain pending data */
Christopher Faulet7b3d38a2023-05-05 11:28:45 +0200620 sc_need_room(sc, 0);
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200621 ret = 0;
622 break;
623 }
624 /* switch to next message */
625 ofs += cnt + msg_len;
Emeric Brunfd647d52021-02-12 20:03:38 +0100626 }
Christopher Faulet4d3283f2023-03-31 10:42:22 +0200627
628 HA_ATOMIC_INC(b_peek(buf, ofs));
629 ds->ofs = b_peek_ofs(buf, ofs);
630
Emeric Brunfd647d52021-02-12 20:03:38 +0100631 HA_RWLOCK_RDUNLOCK(DNS_LOCK, &ring->lock);
632
633 if (ret) {
634 /* let's be woken up once new request to write arrived */
635 HA_RWLOCK_WRLOCK(DNS_LOCK, &ring->lock);
Willy Tarreau62e467c2021-10-20 11:02:13 +0200636 BUG_ON(LIST_INLIST(&appctx->wait_entry));
Willy Tarreau2b718102021-04-21 07:32:39 +0200637 LIST_APPEND(&ring->waiters, &appctx->wait_entry);
Emeric Brunfd647d52021-02-12 20:03:38 +0100638 HA_RWLOCK_WRUNLOCK(DNS_LOCK, &ring->lock);
Willy Tarreau4164eb92022-05-25 15:42:03 +0200639 applet_have_no_more_data(appctx);
Emeric Brunfd647d52021-02-12 20:03:38 +0100640 }
641
Ilya Shipitsin0de36ad2021-02-20 00:23:36 +0500642 /* if session is not a waiter it means there is no committed
Emeric Brunfd647d52021-02-12 20:03:38 +0100643 * message into rx_buf and we are free to use it
644 * Note: we need a load barrier here to not miss the
645 * delete from the list
646 */
Willy Tarreaudde1b442021-10-21 14:33:38 +0200647 __ha_barrier_load();
648 if (!LIST_INLIST_ATOMIC(&ds->waiter)) {
Emeric Brunfd647d52021-02-12 20:03:38 +0100649 while (1) {
650 uint16_t query_id;
651 struct eb32_node *eb;
652 struct dns_query *query;
653
654 if (!ds->rx_msg.len) {
Emeric Brunfd647d52021-02-12 20:03:38 +0100655 /* retrieve message len */
Christopher Faulet28975e12023-03-30 15:49:30 +0200656 ret = co_getblk(sc_oc(sc), (char *)&msg_len, 2, 0);
657 if (ret <= 0) {
658 if (ret == -1)
Christopher Faulet26769b02023-03-31 10:48:03 +0200659 goto error;
Christopher Faulet28975e12023-03-30 15:49:30 +0200660 applet_need_more_data(appctx);
661 break;
662 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100663
664 /* mark as consumed */
Willy Tarreaud7950ad2022-05-27 10:33:42 +0200665 co_skip(sc_oc(sc), 2);
Emeric Brunfd647d52021-02-12 20:03:38 +0100666
667 /* store message len */
668 ds->rx_msg.len = ntohs(msg_len);
Christopher Faulet28975e12023-03-30 15:49:30 +0200669 if (!ds->rx_msg.len)
670 continue;
Emeric Brunfd647d52021-02-12 20:03:38 +0100671 }
672
Willy Tarreaud7950ad2022-05-27 10:33:42 +0200673 if (co_data(sc_oc(sc)) + ds->rx_msg.offset < ds->rx_msg.len) {
Emeric Brunfd647d52021-02-12 20:03:38 +0100674 /* message only partially available */
675
676 /* read available data */
Christopher Faulet28975e12023-03-30 15:49:30 +0200677 ret = co_getblk(sc_oc(sc), ds->rx_msg.area + ds->rx_msg.offset, co_data(sc_oc(sc)), 0);
678 if (ret <= 0) {
679 if (ret == -1)
Christopher Faulet26769b02023-03-31 10:48:03 +0200680 goto error;
Christopher Faulet28975e12023-03-30 15:49:30 +0200681 applet_need_more_data(appctx);
682 break;
683 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100684
685 /* update message offset */
Willy Tarreaud7950ad2022-05-27 10:33:42 +0200686 ds->rx_msg.offset += co_data(sc_oc(sc));
Emeric Brunfd647d52021-02-12 20:03:38 +0100687
688 /* consume all pending data from the channel */
Willy Tarreaud7950ad2022-05-27 10:33:42 +0200689 co_skip(sc_oc(sc), co_data(sc_oc(sc)));
Emeric Brunfd647d52021-02-12 20:03:38 +0100690
691 /* we need to wait for more data */
Christopher Faulet28975e12023-03-30 15:49:30 +0200692 applet_need_more_data(appctx);
Emeric Brunfd647d52021-02-12 20:03:38 +0100693 break;
694 }
695
Ilya Shipitsin0de36ad2021-02-20 00:23:36 +0500696 /* enough data is available into the channel to read the message until the end */
Emeric Brunfd647d52021-02-12 20:03:38 +0100697
698 /* read from the channel until the end of the message */
Christopher Faulet28975e12023-03-30 15:49:30 +0200699 ret = co_getblk(sc_oc(sc), ds->rx_msg.area + ds->rx_msg.offset, ds->rx_msg.len - ds->rx_msg.offset, 0);
700 if (ret <= 0) {
701 if (ret == -1)
Christopher Faulet26769b02023-03-31 10:48:03 +0200702 goto error;
Christopher Faulet28975e12023-03-30 15:49:30 +0200703 applet_need_more_data(appctx);
704 break;
705 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100706
707 /* consume all data until the end of the message from the channel */
Willy Tarreaud7950ad2022-05-27 10:33:42 +0200708 co_skip(sc_oc(sc), ds->rx_msg.len - ds->rx_msg.offset);
Emeric Brunfd647d52021-02-12 20:03:38 +0100709
710 /* reset reader offset to 0 for next message reand */
711 ds->rx_msg.offset = 0;
712
713 /* try remap query id to original */
714 memcpy(&query_id, ds->rx_msg.area, sizeof(query_id));
715 eb = eb32_lookup(&ds->query_ids, query_id);
716 if (!eb) {
717 /* query id not found means we have an unknown corresponding
718 * request, perhaps server's bug or or the query reached
719 * timeout
720 */
721 ds->rx_msg.len = 0;
722 continue;
723 }
724
725 /* re-map the original query id set by the requester */
726 query = eb32_entry(eb, struct dns_query, qid);
727 memcpy(ds->rx_msg.area, &query->original_qid, sizeof(query->original_qid));
728
729 /* remove query ids mapping from pending queries list/tree */
730 eb32_delete(&query->qid);
Willy Tarreau2b718102021-04-21 07:32:39 +0200731 LIST_DELETE(&query->list);
Emeric Brunfd647d52021-02-12 20:03:38 +0100732 pool_free(dns_query_pool, query);
733 ds->onfly_queries--;
734
Emeric Brunfd647d52021-02-12 20:03:38 +0100735 /* the dns_session is also added in queue of the
736 * wait_sess list where the task processing
737 * response will pop available responses
738 */
Willy Tarreaudde1b442021-10-21 14:33:38 +0200739 HA_SPIN_LOCK(DNS_LOCK, &ds->dss->lock);
740
Willy Tarreau62e467c2021-10-20 11:02:13 +0200741 BUG_ON(LIST_INLIST(&ds->waiter));
Willy Tarreau2b718102021-04-21 07:32:39 +0200742 LIST_APPEND(&ds->dss->wait_sess, &ds->waiter);
Emeric Brunfd647d52021-02-12 20:03:38 +0100743
Willy Tarreaudde1b442021-10-21 14:33:38 +0200744 HA_SPIN_UNLOCK(DNS_LOCK, &ds->dss->lock);
745
Emeric Brunfd647d52021-02-12 20:03:38 +0100746 /* awake the task processing the responses */
747 task_wakeup(ds->dss->task_rsp, TASK_WOKEN_INIT);
748
749 break;
750 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100751 }
752
Christopher Faulet26769b02023-03-31 10:48:03 +0200753out:
Emeric Brunfd647d52021-02-12 20:03:38 +0100754 return;
Christopher Faulet26769b02023-03-31 10:48:03 +0200755
Emeric Brunfd647d52021-02-12 20:03:38 +0100756close:
Christopher Faulet26769b02023-03-31 10:48:03 +0200757 se_fl_set(appctx->sedesc, SE_FL_EOS|SE_FL_EOI);
758 goto out;
759
760error:
761 se_fl_set(appctx->sedesc, SE_FL_ERROR);
762 goto out;
Emeric Brunfd647d52021-02-12 20:03:38 +0100763}
764
765void dns_queries_flush(struct dns_session *ds)
766{
767 struct dns_query *query, *queryb;
768
769 list_for_each_entry_safe(query, queryb, &ds->queries, list) {
770 eb32_delete(&query->qid);
Willy Tarreau2b718102021-04-21 07:32:39 +0200771 LIST_DELETE(&query->list);
Emeric Brunfd647d52021-02-12 20:03:38 +0100772 pool_free(dns_query_pool, query);
773 }
774}
775
776void dns_session_free(struct dns_session *ds)
777{
Tim Duesterhusc18e2442023-04-22 17:47:33 +0200778 pool_free(dns_msg_buf, ds->rx_msg.area);
779 pool_free(dns_msg_buf, ds->tx_ring_area);
Tim Duesterhusfe83f582023-04-22 17:47:34 +0200780 task_destroy(ds->task_exp);
Emeric Brunfd647d52021-02-12 20:03:38 +0100781
782 dns_queries_flush(ds);
783
Emeric Brund20dc212021-10-19 15:40:10 +0200784 /* Ensure to remove this session from external lists
785 * Note: we are under the lock of dns_stream_server
786 * which own the heads of those lists.
787 */
788 LIST_DEL_INIT(&ds->waiter);
789 LIST_DEL_INIT(&ds->list);
790
Emeric Brunfd647d52021-02-12 20:03:38 +0100791 ds->dss->cur_conns--;
792 /* Note: this is useless to update
793 * max_active_conns here because
794 * we decrease the value
795 */
Willy Tarreau62e467c2021-10-20 11:02:13 +0200796
797 BUG_ON(!LIST_ISEMPTY(&ds->list));
798 BUG_ON(!LIST_ISEMPTY(&ds->waiter));
799 BUG_ON(!LIST_ISEMPTY(&ds->queries));
800 BUG_ON(!LIST_ISEMPTY(&ds->ring.waiters));
801 BUG_ON(!eb_is_empty(&ds->query_ids));
Emeric Brunfd647d52021-02-12 20:03:38 +0100802 pool_free(dns_session_pool, ds);
803}
804
805static struct appctx *dns_session_create(struct dns_session *ds);
806
Christopher Faulet92238512022-05-12 15:24:46 +0200807static int dns_session_init(struct appctx *appctx)
808{
809 struct dns_session *ds = appctx->svcctx;
810 struct stream *s;
811 struct sockaddr_storage *addr = NULL;
812
813 if (!sockaddr_alloc(&addr, &ds->dss->srv->addr, sizeof(ds->dss->srv->addr)))
814 goto error;
815
816 if (appctx_finalize_startup(appctx, ds->dss->srv->proxy, &BUF_NULL) == -1)
817 goto error;
818
819 s = appctx_strm(appctx);
Willy Tarreau7cb9e6c2022-05-17 19:40:40 +0200820 s->scb->dst = addr;
Christopher Faulet9a790f62023-03-16 14:40:03 +0100821 s->scb->flags |= (SC_FL_RCV_ONCE|SC_FL_NOLINGER);
Christopher Faulet92238512022-05-12 15:24:46 +0200822 s->target = &ds->dss->srv->obj_type;
823 s->flags = SF_ASSIGNED;
824
825 s->do_log = NULL;
826 s->uniq_id = 0;
827
Christopher Faulet2ca4cc12023-02-22 14:22:56 +0100828 applet_expect_no_data(appctx);
Christopher Faulet92238512022-05-12 15:24:46 +0200829 ds->appctx = appctx;
830 return 0;
831
832 error:
833 return -1;
834}
835
Emeric Brunfd647d52021-02-12 20:03:38 +0100836/*
837 * Function to release a DNS tcp session
838 */
839static void dns_session_release(struct appctx *appctx)
840{
Willy Tarreau0d626a52022-05-04 20:41:19 +0200841 struct dns_session *ds = appctx->svcctx;
Willy Tarreaue3e648c2021-02-24 17:38:46 +0100842 struct dns_stream_server *dss __maybe_unused;
Emeric Brunfd647d52021-02-12 20:03:38 +0100843
844 if (!ds)
845 return;
846
Willy Tarreaub56a8782021-10-20 14:38:43 +0200847 /* We do not call ring_appctx_detach here
848 * because we want to keep readers counters
849 * to retry a conn with a different appctx.
850 */
851 HA_RWLOCK_WRLOCK(DNS_LOCK, &ds->ring.lock);
852 LIST_DEL_INIT(&appctx->wait_entry);
853 HA_RWLOCK_WRUNLOCK(DNS_LOCK, &ds->ring.lock);
854
Emeric Brunfd647d52021-02-12 20:03:38 +0100855 dss = ds->dss;
856
857 HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
858 LIST_DEL_INIT(&ds->list);
859
860 if (stopping) {
861 dns_session_free(ds);
862 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
863 return;
864 }
865
866 if (!ds->nb_queries) {
867 /* this is an idle session */
868 /* Note: this is useless to update max_active_sess
869 * here because we decrease idle_conns but
870 * dns_session_free decrease curconns
871 */
872
873 ds->dss->idle_conns--;
874 dns_session_free(ds);
875 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
876 return;
877 }
878
879 if (ds->onfly_queries == ds->nb_queries) {
880 /* the session can be released because
881 * it means that all queries AND
882 * responses are in fly */
883 dns_session_free(ds);
884 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
885 return;
886 }
887
Emeric Brunfd647d52021-02-12 20:03:38 +0100888 /* if there is no pending complete response
889 * message, ensure to reset
890 * message offsets if the session
891 * was closed with an incomplete pending response
892 */
Willy Tarreau2b718102021-04-21 07:32:39 +0200893 if (!LIST_INLIST(&ds->waiter))
Emeric Brunfd647d52021-02-12 20:03:38 +0100894 ds->rx_msg.len = ds->rx_msg.offset = 0;
895
896 /* we flush pending sent queries because we never
897 * have responses
898 */
899 ds->nb_queries -= ds->onfly_queries;
900 dns_queries_flush(ds);
901
902 /* reset offset to be sure to start from message start */
903 ds->tx_msg_offset = 0;
904
905 /* here the ofs and the attached counter
906 * are kept unchanged
907 */
908
909 /* Create a new appctx, We hope we can
910 * create from the release callback! */
911 ds->appctx = dns_session_create(ds);
912 if (!ds->appctx) {
913 dns_session_free(ds);
914 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
915 return;
916 }
917
918 if (ds->nb_queries < DNS_STREAM_MAX_PIPELINED_REQ)
Willy Tarreau2b718102021-04-21 07:32:39 +0200919 LIST_INSERT(&ds->dss->free_sess, &ds->list);
Emeric Brunfd647d52021-02-12 20:03:38 +0100920
921 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
922}
923
924/* DNS tcp session applet */
925static struct applet dns_session_applet = {
926 .obj_type = OBJ_TYPE_APPLET,
927 .name = "<STRMDNS>", /* used for logging */
928 .fct = dns_session_io_handler,
Christopher Faulet92238512022-05-12 15:24:46 +0200929 .init = dns_session_init,
Emeric Brunfd647d52021-02-12 20:03:38 +0100930 .release = dns_session_release,
931};
932
933/*
934 * Function used to create an appctx for a DNS session
Willy Tarreau0d626a52022-05-04 20:41:19 +0200935 * It sets its context into appctx->svcctx.
Emeric Brunfd647d52021-02-12 20:03:38 +0100936 */
937static struct appctx *dns_session_create(struct dns_session *ds)
938{
939 struct appctx *appctx;
Emeric Brunfd647d52021-02-12 20:03:38 +0100940
Christopher Faulet6095d572022-05-16 17:09:48 +0200941 appctx = appctx_new_here(&dns_session_applet, NULL);
Christopher Faulet2479e5f2022-01-19 14:50:11 +0100942 if (!appctx)
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100943 goto out_close;
Willy Tarreau0d626a52022-05-04 20:41:19 +0200944 appctx->svcctx = (void *)ds;
Emeric Brunfd647d52021-02-12 20:03:38 +0100945
Christopher Faulet92238512022-05-12 15:24:46 +0200946 if (appctx_init(appctx) == -1) {
Christopher Faulet13a35e52021-12-20 15:34:16 +0100947 ha_alert("out of memory in dns_session_create().\n");
Emeric Brunfd647d52021-02-12 20:03:38 +0100948 goto out_free_appctx;
Christopher Faulet13a35e52021-12-20 15:34:16 +0100949 }
950
Emeric Brunfd647d52021-02-12 20:03:38 +0100951 return appctx;
952
953 /* Error unrolling */
Emeric Brunfd647d52021-02-12 20:03:38 +0100954 out_free_appctx:
Christopher Faulet92238512022-05-12 15:24:46 +0200955 appctx_free_on_early_error(appctx);
Emeric Brunfd647d52021-02-12 20:03:38 +0100956 out_close:
957 return NULL;
958}
959
960/* Task processing expiration of unresponded queries, this one is supposed
961 * to be stuck on the same thread than the appctx handler
962 */
Willy Tarreau144f84a2021-03-02 16:09:26 +0100963static struct task *dns_process_query_exp(struct task *t, void *context, unsigned int state)
Emeric Brunfd647d52021-02-12 20:03:38 +0100964{
965 struct dns_session *ds = (struct dns_session *)context;
966 struct dns_query *query, *queryb;
967
968 t->expire = TICK_ETERNITY;
969
970 list_for_each_entry_safe(query, queryb, &ds->queries, list) {
971 if (tick_is_expired(query->expire, now_ms)) {
972 eb32_delete(&query->qid);
Willy Tarreau2b718102021-04-21 07:32:39 +0200973 LIST_DELETE(&query->list);
Emeric Brunfd647d52021-02-12 20:03:38 +0100974 pool_free(dns_query_pool, query);
975 ds->onfly_queries--;
976 }
977 else {
978 t->expire = query->expire;
979 break;
980 }
981 }
982
983 return t;
984}
985
986/* Task processing expiration of idle sessions */
Willy Tarreau144f84a2021-03-02 16:09:26 +0100987static struct task *dns_process_idle_exp(struct task *t, void *context, unsigned int state)
Emeric Brunfd647d52021-02-12 20:03:38 +0100988{
989 struct dns_stream_server *dss = (struct dns_stream_server *)context;
990 struct dns_session *ds, *dsb;
991 int target = 0;
992 int cur_active_conns;
993
994 HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
995
996
997 cur_active_conns = dss->cur_conns - dss->idle_conns;
998 if (cur_active_conns > dss->max_active_conns)
999 dss->max_active_conns = cur_active_conns;
1000
1001 target = (dss->max_active_conns - cur_active_conns) / 2;
1002 list_for_each_entry_safe(ds, dsb, &dss->idle_sess, list) {
Christopher Faulete0f47172023-04-11 07:44:34 +02001003 if (!stopping && !target)
Emeric Brunfd647d52021-02-12 20:03:38 +01001004 break;
1005
1006 /* remove conn to pending list to ensure it won't be reused */
1007 LIST_DEL_INIT(&ds->list);
1008
1009 /* force session shutdown */
1010 ds->shutdown = 1;
1011
Ilya Shipitsin0de36ad2021-02-20 00:23:36 +05001012 /* to be sure that the appctx won't miss shutdown */
Emeric Brunfd647d52021-02-12 20:03:38 +01001013 __ha_barrier_store();
1014
1015 /* wake appctx to perform the shutdown */
1016 appctx_wakeup(ds->appctx);
1017 }
1018
1019 /* reset max to current active conns */
1020 dss->max_active_conns = cur_active_conns;
1021
1022 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
1023
1024 t->expire = tick_add(now_ms, 5000);
1025
1026 return t;
1027}
1028
1029struct dns_session *dns_session_new(struct dns_stream_server *dss)
1030{
1031 struct dns_session *ds;
1032
1033 if (dss->maxconn && (dss->maxconn <= dss->cur_conns))
1034 return NULL;
1035
Christopher Faulet14a60d42022-08-03 10:30:06 +02001036 ds = pool_zalloc(dns_session_pool);
Emeric Brunfd647d52021-02-12 20:03:38 +01001037 if (!ds)
1038 return NULL;
1039
1040 ds->ofs = ~0;
1041 ds->dss = dss;
1042 LIST_INIT(&ds->list);
1043 LIST_INIT(&ds->queries);
1044 LIST_INIT(&ds->waiter);
1045 ds->rx_msg.offset = ds->rx_msg.len = 0;
1046 ds->rx_msg.area = NULL;
1047 ds->tx_ring_area = NULL;
1048 ds->task_exp = NULL;
1049 ds->appctx = NULL;
1050 ds->shutdown = 0;
1051 ds->nb_queries = 0;
1052 ds->query_ids = EB_ROOT_UNIQUE;
1053 ds->rx_msg.area = pool_alloc(dns_msg_buf);
1054 if (!ds->rx_msg.area)
1055 goto error;
1056
1057 ds->tx_ring_area = pool_alloc(dns_msg_buf);
1058 if (!ds->tx_ring_area)
1059 goto error;
1060
1061 ring_init(&ds->ring, ds->tx_ring_area, DNS_TCP_MSG_RING_MAX_SIZE);
Christopher Faulet1a1b6742021-03-04 16:53:27 +01001062 /* never fail because it is the first watcher attached to the ring */
1063 DISGUISE(ring_attach(&ds->ring));
Emeric Brunfd647d52021-02-12 20:03:38 +01001064
Willy Tarreaubeeabf52021-10-01 18:23:30 +02001065 if ((ds->task_exp = task_new_here()) == NULL)
Emeric Brunfd647d52021-02-12 20:03:38 +01001066 goto error;
1067
1068 ds->task_exp->process = dns_process_query_exp;
1069 ds->task_exp->context = ds;
1070
1071 ds->appctx = dns_session_create(ds);
1072 if (!ds->appctx)
1073 goto error;
1074
1075 dss->cur_conns++;
1076
1077 return ds;
1078
1079error:
Tim Duesterhusfe83f582023-04-22 17:47:34 +02001080 task_destroy(ds->task_exp);
Tim Duesterhusc18e2442023-04-22 17:47:33 +02001081 pool_free(dns_msg_buf, ds->rx_msg.area);
1082 pool_free(dns_msg_buf, ds->tx_ring_area);
Emeric Brunfd647d52021-02-12 20:03:38 +01001083
1084 pool_free(dns_session_pool, ds);
1085
1086 return NULL;
1087}
1088
1089/*
1090 * Task used to consume pending messages from nameserver ring
1091 * and forward them to dns_session ring.
1092 * Note: If no slot found a new dns_session is allocated
1093 */
Willy Tarreau144f84a2021-03-02 16:09:26 +01001094static struct task *dns_process_req(struct task *t, void *context, unsigned int state)
Emeric Brunfd647d52021-02-12 20:03:38 +01001095{
1096 struct dns_nameserver *ns = (struct dns_nameserver *)context;
1097 struct dns_stream_server *dss = ns->stream;
1098 struct ring *ring = dss->ring_req;
1099 struct buffer *buf = &ring->buf;
1100 uint64_t msg_len;
1101 size_t len, cnt, ofs;
1102 struct dns_session *ds, *ads;
1103 HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
1104
Emeric Brunfd647d52021-02-12 20:03:38 +01001105 HA_RWLOCK_RDLOCK(DNS_LOCK, &ring->lock);
1106
1107 /* explanation for the initialization below: it would be better to do
1108 * this in the parsing function but this would occasionally result in
1109 * dropped events because we'd take a reference on the oldest message
1110 * and keep it while being scheduled. Thus instead let's take it the
1111 * first time we enter here so that we have a chance to pass many
1112 * existing messages before grabbing a reference to a location. This
1113 * value cannot be produced after initialization.
1114 */
Amaury Denoyelle737d10f2023-03-07 11:18:27 +01001115 if (unlikely(dss->ofs_req == ~0)) {
Aurelien DARRAGON5a43db22023-03-07 17:45:02 +01001116 dss->ofs_req = b_peek_ofs(buf, 0);
1117 HA_ATOMIC_INC(b_orig(buf) + dss->ofs_req);
Emeric Brunfd647d52021-02-12 20:03:38 +01001118 }
1119
1120 /* we were already there, adjust the offset to be relative to
1121 * the buffer's head and remove us from the counter.
1122 */
Amaury Denoyelle737d10f2023-03-07 11:18:27 +01001123 ofs = dss->ofs_req - b_head_ofs(buf);
1124 if (dss->ofs_req < b_head_ofs(buf))
1125 ofs += b_size(buf);
1126
Emeric Brunfd647d52021-02-12 20:03:38 +01001127 BUG_ON(ofs >= buf->size);
Willy Tarreau4781b152021-04-06 13:53:36 +02001128 HA_ATOMIC_DEC(b_peek(buf, ofs));
Emeric Brunfd647d52021-02-12 20:03:38 +01001129
1130 while (ofs + 1 < b_data(buf)) {
1131 struct ist myist;
1132
1133 cnt = 1;
1134 len = b_peek_varint(buf, ofs + cnt, &msg_len);
1135 if (!len)
1136 break;
1137 cnt += len;
1138 BUG_ON(msg_len + ofs + cnt + 1 > b_data(buf));
1139 if (unlikely(msg_len > DNS_TCP_MSG_MAX_SIZE)) {
1140 /* too large a message to ever fit, let's skip it */
1141 ofs += cnt + msg_len;
1142 continue;
1143 }
1144
1145 len = b_getblk(buf, dns_msg_trash, msg_len, ofs + cnt);
1146
Tim Duesterhus92c696e2021-02-28 16:11:36 +01001147 myist = ist2(dns_msg_trash, len);
Emeric Brunfd647d52021-02-12 20:03:38 +01001148
1149 ads = NULL;
Ilya Shipitsin0de36ad2021-02-20 00:23:36 +05001150 /* try to push request into active sess with free slot */
Emeric Brunfd647d52021-02-12 20:03:38 +01001151 if (!LIST_ISEMPTY(&dss->free_sess)) {
1152 ds = LIST_NEXT(&dss->free_sess, struct dns_session *, list);
1153
1154 if (ring_write(&ds->ring, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1) > 0) {
1155 ds->nb_queries++;
1156 if (ds->nb_queries >= DNS_STREAM_MAX_PIPELINED_REQ)
1157 LIST_DEL_INIT(&ds->list);
1158 ads = ds;
1159 }
1160 else {
1161 /* it means we were unable to put a request in this slot,
1162 * it may be close to be full so we put it at the end
1163 * of free conn list */
1164 LIST_DEL_INIT(&ds->list);
Willy Tarreau2b718102021-04-21 07:32:39 +02001165 LIST_APPEND(&dss->free_sess, &ds->list);
Emeric Brunfd647d52021-02-12 20:03:38 +01001166 }
1167 }
1168
1169 if (!ads) {
Ilya Shipitsin0de36ad2021-02-20 00:23:36 +05001170 /* try to push request into idle, this one should have enough free space */
Emeric Brunfd647d52021-02-12 20:03:38 +01001171 if (!LIST_ISEMPTY(&dss->idle_sess)) {
1172 ds = LIST_NEXT(&dss->idle_sess, struct dns_session *, list);
1173
1174 /* ring is empty so this ring_write should never fail */
1175 ring_write(&ds->ring, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
1176 ds->nb_queries++;
1177 LIST_DEL_INIT(&ds->list);
1178
1179 ds->dss->idle_conns--;
1180
1181 /* we may have to update the max_active_conns */
1182 if (ds->dss->max_active_conns < ds->dss->cur_conns - ds->dss->idle_conns)
1183 ds->dss->max_active_conns = ds->dss->cur_conns - ds->dss->idle_conns;
1184
1185 /* since we may unable to find a free list to handle
1186 * this request, this request may be large and fill
1187 * the ring buffer so we prefer to put at the end of free
1188 * list. */
Willy Tarreau2b718102021-04-21 07:32:39 +02001189 LIST_APPEND(&dss->free_sess, &ds->list);
Emeric Brunfd647d52021-02-12 20:03:38 +01001190 ads = ds;
1191 }
1192 }
1193
Ilya Shipitsin0de36ad2021-02-20 00:23:36 +05001194 /* we didn't find a session available with large enough room */
Emeric Brunfd647d52021-02-12 20:03:38 +01001195 if (!ads) {
1196 /* allocate a new session */
1197 ads = dns_session_new(dss);
1198 if (ads) {
1199 /* ring is empty so this ring_write should never fail */
1200 ring_write(&ads->ring, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
1201 ads->nb_queries++;
Willy Tarreau2b718102021-04-21 07:32:39 +02001202 LIST_INSERT(&dss->free_sess, &ads->list);
Emeric Brunfd647d52021-02-12 20:03:38 +01001203 }
1204 else
1205 ns->counters->snd_error++;
1206 }
1207
1208 if (ads)
1209 ns->counters->sent++;
1210
1211 ofs += cnt + len;
1212 }
1213
Willy Tarreau4781b152021-04-06 13:53:36 +02001214 HA_ATOMIC_INC(b_peek(buf, ofs));
Amaury Denoyelle737d10f2023-03-07 11:18:27 +01001215 dss->ofs_req = b_peek_ofs(buf, ofs);
Emeric Brunfd647d52021-02-12 20:03:38 +01001216 HA_RWLOCK_RDUNLOCK(DNS_LOCK, &ring->lock);
1217
1218
1219 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
1220 return t;
1221}
1222
1223/*
1224 * Task used to consume response
1225 * Note: upper layer callback is called
1226 */
Willy Tarreau144f84a2021-03-02 16:09:26 +01001227static struct task *dns_process_rsp(struct task *t, void *context, unsigned int state)
Emeric Brunfd647d52021-02-12 20:03:38 +01001228{
1229 struct dns_nameserver *ns = (struct dns_nameserver *)context;
1230
1231 ns->process_responses(ns);
1232
1233 return t;
1234}
1235
1236/* Function used to initialize an TCP nameserver */
1237int dns_stream_init(struct dns_nameserver *ns, struct server *srv)
1238{
1239 struct dns_stream_server *dss = NULL;
1240
1241 dss = calloc(1, sizeof(*dss));
1242 if (!dss) {
1243 ha_alert("memory allocation error initializing dns tcp server '%s'.\n", srv->id);
1244 goto out;
1245 }
1246
1247 dss->srv = srv;
1248 dss->maxconn = srv->maxconn;
1249
1250 dss->ofs_req = ~0; /* init ring offset */
1251 dss->ring_req = ring_new(2*DNS_TCP_MSG_RING_MAX_SIZE);
1252 if (!dss->ring_req) {
1253 ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
1254 goto out;
1255 }
1256 /* Create the task associated to the resolver target handling conns */
Willy Tarreaubeeabf52021-10-01 18:23:30 +02001257 if ((dss->task_req = task_new_anywhere()) == NULL) {
Emeric Brunfd647d52021-02-12 20:03:38 +01001258 ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
1259 goto out;
1260 }
1261
1262 /* Update task's parameters */
1263 dss->task_req->process = dns_process_req;
1264 dss->task_req->context = ns;
1265
1266 /* attach the task as reader */
1267 if (!ring_attach(dss->ring_req)) {
1268 /* mark server attached to the ring */
1269 ha_alert("server '%s': too many watchers for ring. this should never happen.\n", srv->id);
1270 goto out;
1271 }
1272
1273 /* Create the task associated to the resolver target handling conns */
Willy Tarreaubeeabf52021-10-01 18:23:30 +02001274 if ((dss->task_rsp = task_new_anywhere()) == NULL) {
Emeric Brunfd647d52021-02-12 20:03:38 +01001275 ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
1276 goto out;
1277 }
1278
1279 /* Update task's parameters */
1280 dss->task_rsp->process = dns_process_rsp;
1281 dss->task_rsp->context = ns;
1282
1283 /* Create the task associated to the resolver target handling conns */
Willy Tarreaubeeabf52021-10-01 18:23:30 +02001284 if ((dss->task_idle = task_new_anywhere()) == NULL) {
Emeric Brunfd647d52021-02-12 20:03:38 +01001285 ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
1286 goto out;
1287 }
1288
1289 /* Update task's parameters */
1290 dss->task_idle->process = dns_process_idle_exp;
1291 dss->task_idle->context = dss;
1292 dss->task_idle->expire = tick_add(now_ms, 5000);
1293
Ilya Shipitsin0de36ad2021-02-20 00:23:36 +05001294 /* let start the task to free idle conns immediately */
Emeric Brunfd647d52021-02-12 20:03:38 +01001295 task_queue(dss->task_idle);
1296
1297 LIST_INIT(&dss->free_sess);
1298 LIST_INIT(&dss->idle_sess);
1299 LIST_INIT(&dss->wait_sess);
1300 HA_SPIN_INIT(&dss->lock);
1301 ns->stream = dss;
1302 return 0;
1303out:
1304 if (dss && dss->task_rsp)
1305 task_destroy(dss->task_rsp);
1306 if (dss && dss->task_req)
1307 task_destroy(dss->task_req);
1308 if (dss && dss->ring_req)
1309 ring_free(dss->ring_req);
1310
1311 free(dss);
Emeric Brunc9437992021-02-12 19:42:55 +01001312 return -1;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001313}
1314
Emeric Brunc9437992021-02-12 19:42:55 +01001315int init_dns_buffers()
Baptiste Assmann325137d2015-04-13 23:40:55 +02001316{
Emeric Brunc9437992021-02-12 19:42:55 +01001317 dns_msg_trash = malloc(DNS_TCP_MSG_MAX_SIZE);
1318 if (!dns_msg_trash)
1319 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001320
Emeric Brunc9437992021-02-12 19:42:55 +01001321 return 1;
1322}
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +02001323
Emeric Brunc9437992021-02-12 19:42:55 +01001324void deinit_dns_buffers()
1325{
Willy Tarreau61cfdf42021-02-20 10:46:51 +01001326 ha_free(&dns_msg_trash);
Emeric Brunc9437992021-02-12 19:42:55 +01001327}
Emeric Brund26a6232021-01-04 13:32:20 +01001328
1329REGISTER_PER_THREAD_ALLOC(init_dns_buffers);
1330REGISTER_PER_THREAD_FREE(deinit_dns_buffers);