blob: 69364eb0b1a9ed7853849457d37f6c384c36c8fe [file] [log] [blame]
Baptiste Assmann325137d2015-04-13 23:40:55 +02001/*
2 * Name server resolution
3 *
Emeric Brunc9437992021-02-12 19:42:55 +01004 * 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>
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>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020032#include <haproxy/log.h>
Emeric Brund26a6232021-01-04 13:32:20 +010033#include <haproxy/ring.h>
Emeric Brunfd647d52021-02-12 20:03:38 +010034#include <haproxy/stream.h>
35#include <haproxy/stream_interface.h>
Baptiste Assmann325137d2015-04-13 23:40:55 +020036
Emeric Brund26a6232021-01-04 13:32:20 +010037static THREAD_LOCAL char *dns_msg_trash;
Baptiste Assmann325137d2015-04-13 23:40:55 +020038
Emeric Brunfd647d52021-02-12 20:03:38 +010039DECLARE_STATIC_POOL(dns_session_pool, "dns_session", sizeof(struct dns_session));
40DECLARE_STATIC_POOL(dns_query_pool, "dns_query", sizeof(struct dns_query));
41DECLARE_STATIC_POOL(dns_msg_buf, "dns_msg_buf", DNS_TCP_MSG_RING_MAX_SIZE);
42
Christopher Faulet67957bd2017-09-27 11:00:59 +020043/* Opens an UDP socket on the namesaver's IP/Port, if required. Returns 0 on
44 * success, -1 otherwise.
Baptiste Assmann325137d2015-04-13 23:40:55 +020045 */
Emeric Brund26a6232021-01-04 13:32:20 +010046static int dns_connect_nameserver(struct dns_nameserver *ns)
Baptiste Assmann325137d2015-04-13 23:40:55 +020047{
Emeric Brund26a6232021-01-04 13:32:20 +010048 if (ns->dgram) {
49 struct dgram_conn *dgram = &ns->dgram->conn;
50 int fd;
Baptiste Assmann325137d2015-04-13 23:40:55 +020051
Emeric Brund26a6232021-01-04 13:32:20 +010052 /* Already connected */
53 if (dgram->t.sock.fd != -1)
54 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +020055
Emeric Brund26a6232021-01-04 13:32:20 +010056 /* Create an UDP socket and connect it on the nameserver's IP/Port */
57 if ((fd = socket(dgram->addr.to.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
58 send_log(NULL, LOG_WARNING,
Emeric Brunc9437992021-02-12 19:42:55 +010059 "DNS : section '%s': can't create socket for nameserver '%s'.\n",
Emeric Brund26a6232021-01-04 13:32:20 +010060 ns->counters->pid, ns->id);
61 return -1;
62 }
63 if (connect(fd, (struct sockaddr*)&dgram->addr.to, get_addr_len(&dgram->addr.to)) == -1) {
64 send_log(NULL, LOG_WARNING,
Emeric Brunc9437992021-02-12 19:42:55 +010065 "DNS : section '%s': can't connect socket for nameserver '%s'.\n",
Emeric Brund26a6232021-01-04 13:32:20 +010066 ns->counters->id, ns->id);
Emeric Brunc9437992021-02-12 19:42:55 +010067 close(fd);
Emeric Brund26a6232021-01-04 13:32:20 +010068 return -1;
69 }
Baptiste Assmann325137d2015-04-13 23:40:55 +020070
Emeric Brund26a6232021-01-04 13:32:20 +010071 /* Make the socket non blocking */
72 fcntl(fd, F_SETFL, O_NONBLOCK);
Olivier Houcharda8c6db82017-07-06 18:46:47 +020073
Emeric Brund26a6232021-01-04 13:32:20 +010074 /* Add the fd in the fd list and update its parameters */
75 dgram->t.sock.fd = fd;
76 fd_insert(fd, dgram, dgram_fd_handler, MAX_THREADS_MASK);
77 fd_want_recv(fd);
Emeric Brun526b7922021-02-15 14:28:27 +010078 return 0;
Emeric Brunc9437992021-02-12 19:42:55 +010079 }
Emeric Brun526b7922021-02-15 14:28:27 +010080
81 return -1;
Baptiste Assmann325137d2015-04-13 23:40:55 +020082}
83
Emeric Brund26a6232021-01-04 13:32:20 +010084/* Sends a message to a name server
85 * It returns message length on success
86 * or -1 in error case
87 * 0 is returned in case of output ring buffer is full
88 */
89int dns_send_nameserver(struct dns_nameserver *ns, void *buf, size_t len)
90{
91 int ret = -1;
92
93 if (ns->dgram) {
94 struct dgram_conn *dgram = &ns->dgram->conn;
95 int fd = dgram->t.sock.fd;
96
97 if (dgram->t.sock.fd == -1) {
98 if (dns_connect_nameserver(ns) == -1)
99 return -1;
100 fd = dgram->t.sock.fd;
101 }
102
103 ret = send(fd, buf, len, 0);
104 if (ret < 0) {
105 if (errno == EAGAIN) {
106 struct ist myist;
107
108 myist.ptr = buf;
109 myist.len = len;
110 ret = ring_write(ns->dgram->ring_req, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
111 if (!ret) {
112 ns->counters->snd_error++;
113 return -1;
114 }
115 fd_cant_send(fd);
116 return ret;
117 }
118 ns->counters->snd_error++;
119 fd_delete(fd);
Emeric Brund26a6232021-01-04 13:32:20 +0100120 dgram->t.sock.fd = -1;
121 return -1;
122 }
123 ns->counters->sent++;
124 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100125 else if (ns->stream) {
126 struct ist myist;
127
128 myist.ptr = buf;
129 myist.len = len;
130 ret = ring_write(ns->stream->ring_req, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
131 if (!ret) {
132 ns->counters->snd_error++;
133 return -1;
134 }
135 task_wakeup(ns->stream->task_req, TASK_WOKEN_MSG);
136 return ret;
137 }
Emeric Brund26a6232021-01-04 13:32:20 +0100138
139 return ret;
140}
141
Emeric Brunfd647d52021-02-12 20:03:38 +0100142void dns_session_free(struct dns_session *);
143
Emeric Brund26a6232021-01-04 13:32:20 +0100144/* Receives a dns message
145 * Returns message length
146 * 0 is returned if no more message available
147 * -1 in error case
148 */
149ssize_t dns_recv_nameserver(struct dns_nameserver *ns, void *data, size_t size)
150{
151 ssize_t ret = -1;
152
153 if (ns->dgram) {
154 struct dgram_conn *dgram = &ns->dgram->conn;
155 int fd = dgram->t.sock.fd;
156
157 if (fd == -1)
158 return -1;
159
160 if ((ret = recv(fd, data, size, 0)) < 0) {
161 if (errno == EAGAIN) {
162 fd_cant_recv(fd);
163 return 0;
164 }
165 fd_delete(fd);
Emeric Brund26a6232021-01-04 13:32:20 +0100166 dgram->t.sock.fd = -1;
167 return -1;
168 }
169 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100170 else if (ns->stream) {
171 struct dns_stream_server *dss = ns->stream;
172 struct dns_session *ds;
173
174 HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
175
176 if (!LIST_ISEMPTY(&dss->wait_sess)) {
177 ds = LIST_NEXT(&dss->wait_sess, struct dns_session *, waiter);
178 fprintf(stderr, "ds: %p\n", ds);
179 ret = ds->rx_msg.len < size ? ds->rx_msg.len : size;
180 memcpy(data, ds->rx_msg.area, ret);
181
182 ds->rx_msg.len = 0;
183
184 /* This barrier is here to ensure that all data is
185 * stored if the appctx detect the elem is out of the list */
186 __ha_barrier_store();
187
188 LIST_DEL_INIT(&ds->waiter);
189
190 if (ds->appctx) {
191 /* This second barrier is here to ensure that
192 * the waked up appctx won't miss that the
193 * elem is removed from the list */
194 __ha_barrier_store();
195
196 /* awake appctx beacause it may have other
197 * message to receive
198 */
199 appctx_wakeup(ds->appctx);
200
201 /* dns_session could already be into free_sess list
202 * so we firstly remove it */
203 LIST_DEL_INIT(&ds->list);
204
205 /* decrease nb_queries to free a slot for a new query on that sess */
206 ds->nb_queries--;
207 if (ds->nb_queries) {
208 /* it remains pipelined unanswered request
209 * into this session but we just decrease
210 * the counter so the session
211 * can not be full of pipelined requests
212 * so we can add if to free_sess list
213 * to receive a new request
214 */
215 LIST_ADD(&ds->dss->free_sess, &ds->list);
216 }
217 else {
218 /* there is no more pipelined requests
219 * into this session, so we move it
220 * to idle_sess list */
221 LIST_ADD(&ds->dss->idle_sess, &ds->list);
222
223 /* update the counter of idle sessions */
224 ds->dss->idle_conns++;
225
226 /* Note: this is useless there to update
227 * the max_active_conns since we increase
228 * the idle count */
229 }
230 }
231 else {
232 /* there is no more appctx for this session
233 * it means it is ready to die
234 */
235 dns_session_free(ds);
236 }
237
238
239 }
240
241 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
242 }
Emeric Brund26a6232021-01-04 13:32:20 +0100243
244 return ret;
245}
246
247static void dns_resolve_recv(struct dgram_conn *dgram)
248{
249 struct dns_nameserver *ns;
250 int fd;
251
252 fd = dgram->t.sock.fd;
253
254 /* check if ready for reading */
255 if (!fd_recv_ready(fd))
256 return;
257
258 /* no need to go further if we can't retrieve the nameserver */
259 if ((ns = dgram->owner) == NULL) {
260 _HA_ATOMIC_AND(&fdtab[fd].ev, ~(FD_POLL_HUP|FD_POLL_ERR));
261 fd_stop_recv(fd);
262 return;
263 }
264
265 ns->process_responses(ns);
266}
267
268/* Called when a dns network socket is ready to send data */
269static void dns_resolve_send(struct dgram_conn *dgram)
270{
271 int fd;
272 struct dns_nameserver *ns;
273 struct ring *ring;
274 struct buffer *buf;
275 uint64_t msg_len;
276 size_t len, cnt, ofs;
277
278 fd = dgram->t.sock.fd;
279
280 /* check if ready for sending */
281 if (!fd_send_ready(fd))
282 return;
283
284 /* no need to go further if we can't retrieve the nameserver */
285 if ((ns = dgram->owner) == NULL) {
286 _HA_ATOMIC_AND(&fdtab[fd].ev, ~(FD_POLL_HUP|FD_POLL_ERR));
287 fd_stop_send(fd);
288 return;
289 }
290
291 ring = ns->dgram->ring_req;
292 buf = &ring->buf;
293
294 HA_RWLOCK_RDLOCK(DNS_LOCK, &ring->lock);
295 ofs = ns->dgram->ofs_req;
296
297 /* explanation for the initialization below: it would be better to do
298 * this in the parsing function but this would occasionally result in
299 * dropped events because we'd take a reference on the oldest message
300 * and keep it while being scheduled. Thus instead let's take it the
301 * first time we enter here so that we have a chance to pass many
302 * existing messages before grabbing a reference to a location. This
303 * value cannot be produced after initialization.
304 */
305 if (unlikely(ofs == ~0)) {
306 ofs = 0;
307 HA_ATOMIC_ADD(b_peek(buf, ofs), 1);
308 ofs += ring->ofs;
309 }
310
311 /* we were already there, adjust the offset to be relative to
312 * the buffer's head and remove us from the counter.
313 */
314 ofs -= ring->ofs;
315 BUG_ON(ofs >= buf->size);
316 HA_ATOMIC_SUB(b_peek(buf, ofs), 1);
317
318 while (ofs + 1 < b_data(buf)) {
319 int ret;
320
321 cnt = 1;
322 len = b_peek_varint(buf, ofs + cnt, &msg_len);
323 if (!len)
324 break;
325 cnt += len;
326 BUG_ON(msg_len + ofs + cnt + 1 > b_data(buf));
327 if (unlikely(msg_len > DNS_TCP_MSG_MAX_SIZE)) {
328 /* too large a message to ever fit, let's skip it */
329 ofs += cnt + msg_len;
330 continue;
331 }
332
333 len = b_getblk(buf, dns_msg_trash, msg_len, ofs + cnt);
334
335 ret = send(fd, dns_msg_trash, len, 0);
336 if (ret < 0) {
337 if (errno == EAGAIN) {
338 fd_cant_send(fd);
339 goto out;
340 }
341 ns->counters->snd_error++;
342 fd_delete(fd);
Emeric Brund26a6232021-01-04 13:32:20 +0100343 fd = dgram->t.sock.fd = -1;
344 goto out;
345 }
346 ns->counters->sent++;
347
348 ofs += cnt + len;
349 }
350
351 /* we don't want/need to be waked up any more for sending
352 * because all ring content is sent */
353 fd_stop_send(fd);
354
355out:
356
357 HA_ATOMIC_ADD(b_peek(buf, ofs), 1);
358 ofs += ring->ofs;
359 ns->dgram->ofs_req = ofs;
360 HA_RWLOCK_RDUNLOCK(DNS_LOCK, &ring->lock);
361
362}
363
Emeric Brunc9437992021-02-12 19:42:55 +0100364/* proto_udp callback functions for a DNS resolution */
365struct dgram_data_cb dns_dgram_cb = {
366 .recv = dns_resolve_recv,
367 .send = dns_resolve_send,
368};
Baptiste Assmann325137d2015-04-13 23:40:55 +0200369
Emeric Brunc9437992021-02-12 19:42:55 +0100370int dns_dgram_init(struct dns_nameserver *ns, struct sockaddr_storage *sk)
Baptiste Assmann325137d2015-04-13 23:40:55 +0200371{
Emeric Brunc9437992021-02-12 19:42:55 +0100372 struct dns_dgram_server *dgram;
Baptiste Assmann201c07f2017-05-22 15:17:15 +0200373
Emeric Brunc9437992021-02-12 19:42:55 +0100374 if ((dgram = calloc(1, sizeof(*dgram))) == NULL)
Christopher Faulet67957bd2017-09-27 11:00:59 +0200375 return -1;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200376
Emeric Brunc9437992021-02-12 19:42:55 +0100377 /* Leave dgram partially initialized, no FD attached for
378 * now. */
379 dgram->conn.owner = ns;
380 dgram->conn.data = &dns_dgram_cb;
381 dgram->conn.t.sock.fd = -1;
382 dgram->conn.addr.to = *sk;
383 ns->dgram = dgram;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200384
Emeric Brunc9437992021-02-12 19:42:55 +0100385 dgram->ofs_req = ~0; /* init ring offset */
386 dgram->ring_req = ring_new(2*DNS_TCP_MSG_RING_MAX_SIZE);
387 if (!dgram->ring_req) {
388 ha_alert("memory allocation error initializing the ring for nameserver.\n");
389 goto out;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200390 }
391
Emeric Brunc9437992021-02-12 19:42:55 +0100392 /* attach the task as reader */
393 if (!ring_attach(dgram->ring_req)) {
394 /* mark server attached to the ring */
395 ha_alert("nameserver sets too many watchers > 255 on ring. This is a bug and should not happen.\n");
396 goto out;
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200397 }
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +0200398 return 0;
Emeric Brunc9437992021-02-12 19:42:55 +0100399out:
400 if (dgram->ring_req)
401 ring_free(dgram->ring_req);
Christopher Fauletd6c6b5f2020-09-08 10:27:24 +0200402
Emeric Brunc9437992021-02-12 19:42:55 +0100403 free(dgram);
Olivier Houchard2ec2db92018-01-08 16:28:57 +0100404
Emeric Brunfd647d52021-02-12 20:03:38 +0100405 return -1;
406}
407
408/*
409 * IO Handler to handle message push to dns tcp server
410 */
411static void dns_session_io_handler(struct appctx *appctx)
412{
413 struct stream_interface *si = appctx->owner;
414 struct dns_session *ds = appctx->ctx.sft.ptr;
415 struct ring *ring = &ds->ring;
416 struct buffer *buf = &ring->buf;
417 uint64_t msg_len;
418 int available_room;
419 size_t len, cnt, ofs;
420 int ret = 0;
421
422 /* if stopping was requested, close immediately */
423 if (unlikely(stopping))
424 goto close;
425
426 /* we want to be sure to not miss that we have been awaked for a shutdown */
427 __ha_barrier_load();
428
429 /* that means the connection was requested to shutdown
430 * for instance idle expire */
431 if (ds->shutdown)
432 goto close;
433
434 /* an error was detected */
435 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
436 goto close;
437
438 /* con closed by server side, we will skip data write and drain data from channel */
439 if ((si_oc(si)->flags & CF_SHUTW)) {
440 goto read;
441 }
442
443 /* if the connection is not established, inform the stream that we want
444 * to be notified whenever the connection completes.
445 */
446 if (si_opposite(si)->state < SI_ST_EST) {
447 si_cant_get(si);
448 si_rx_conn_blk(si);
449 si_rx_endp_more(si);
450 return;
451 }
452
453
454 ofs = ds->ofs;
455
456 HA_RWLOCK_WRLOCK(DNS_LOCK, &ring->lock);
457 LIST_DEL_INIT(&appctx->wait_entry);
458 HA_RWLOCK_WRUNLOCK(DNS_LOCK, &ring->lock);
459
460 HA_RWLOCK_RDLOCK(DNS_LOCK, &ring->lock);
461
462 /* explanation for the initialization below: it would be better to do
463 * this in the parsing function but this would occasionally result in
464 * dropped events because we'd take a reference on the oldest message
465 * and keep it while being scheduled. Thus instead let's take it the
466 * first time we enter here so that we have a chance to pass many
467 * existing messages before grabbing a reference to a location. This
468 * value cannot be produced after initialization.
469 */
470 if (unlikely(ofs == ~0)) {
471 ofs = 0;
472
473 HA_ATOMIC_ADD(b_peek(buf, ofs), 1);
474 ofs += ring->ofs;
475 }
476
477 /* in this loop, ofs always points to the counter byte that precedes
478 * the message so that we can take our reference there if we have to
479 * stop before the end (ret=0).
480 */
481 if (si_opposite(si)->state == SI_ST_EST) {
482 /* we were already there, adjust the offset to be relative to
483 * the buffer's head and remove us from the counter.
484 */
485 ofs -= ring->ofs;
486 BUG_ON(ofs >= buf->size);
487 HA_ATOMIC_SUB(b_peek(buf, ofs), 1);
488
489 ret = 1;
490 while (ofs + 1 < b_data(buf)) {
491 struct dns_query *query;
492 uint16_t original_qid;
493 uint16_t new_qid;
494
495 cnt = 1;
496 len = b_peek_varint(buf, ofs + cnt, &msg_len);
497 if (!len)
498 break;
499 cnt += len;
500 BUG_ON(msg_len + ofs + cnt + 1 > b_data(buf));
501
502 /* retrieve available room on output channel */
503 available_room = channel_recv_max(si_ic(si));
504
505 /* tx_msg_offset null means we are at the start of a new message */
506 if (!ds->tx_msg_offset) {
507 uint16_t slen;
508
509 /* check if there is enough room to put message len and query id */
510 if (available_room < sizeof(slen) + sizeof(new_qid)) {
511 si_rx_room_blk(si);
512 ret = 0;
513 break;
514 }
515
516 /* put msg len into then channel */
517 slen = (uint16_t)msg_len;
518 slen = htons(slen);
519 ci_putblk(si_ic(si), (char *)&slen, sizeof(slen));
520 available_room -= sizeof(slen);
521
522 /* backup original query id */
523 len = b_getblk(buf, (char *)&original_qid, sizeof(original_qid), ofs + cnt);
Emeric Brun538bb042021-02-15 13:58:06 +0100524 if (!len) {
525 /* should never happen since messages are atomically
526 * written into ring
527 */
528 ret = 0;
529 break;
530 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100531
532 /* generates new query id */
533 new_qid = ++ds->query_counter;
534 new_qid = htons(new_qid);
535
536 /* put new query id into the channel */
537 ci_putblk(si_ic(si), (char *)&new_qid, sizeof(new_qid));
538 available_room -= sizeof(new_qid);
539
540 /* keep query id mapping */
541
542 query = pool_alloc(dns_query_pool);
543 if (query) {
544 query->qid.key = new_qid;
545 query->original_qid = original_qid;
546 query->expire = tick_add(now_ms, 5000);
547 LIST_INIT(&query->list);
548 if (LIST_ISEMPTY(&ds->queries)) {
549 /* enable task to handle expire */
550 ds->task_exp->expire = query->expire;
551 /* ensure this will be executed by the same
552 * thread than ds_session_release
553 * to ensure session_release is free
554 * to destroy the task */
555 task_queue(ds->task_exp);
556 }
557 LIST_ADDQ(&ds->queries, &query->list);
558 eb32_insert(&ds->query_ids, &query->qid);
559 ds->onfly_queries++;
560 }
561
562 /* update the tx_offset to handle output in 16k streams */
563 ds->tx_msg_offset = sizeof(original_qid);
564
565 }
566
567 /* check if it remains available room on output chan */
568 if (unlikely(!available_room)) {
569 si_rx_room_blk(si);
570 ret = 0;
571 break;
572 }
573
574 chunk_reset(&trash);
575 if ((msg_len - ds->tx_msg_offset) > available_room) {
576 /* remaining msg data is too large to be written in output channel at one time */
577
578 len = b_getblk(buf, trash.area, available_room, ofs + cnt + ds->tx_msg_offset);
579
580 /* update offset to complete mesg forwarding later */
581 ds->tx_msg_offset += len;
582 }
583 else {
584 /* remaining msg data can be written in output channel at one time */
585 len = b_getblk(buf, trash.area, msg_len - ds->tx_msg_offset, ofs + cnt + ds->tx_msg_offset);
586
587 /* reset tx_msg_offset to mark forward fully processed */
588 ds->tx_msg_offset = 0;
589 }
590 trash.data += len;
591
Emeric Brun743afee2021-02-15 14:12:06 +0100592 if (ci_putchk(si_ic(si), &trash) == -1) {
593 /* should never happen since we
594 * check available_room is large
595 * enought here.
596 */
597 si_rx_room_blk(si);
598 ret = 0;
599 break;
600 }
Emeric Brunfd647d52021-02-12 20:03:38 +0100601
602 if (ds->tx_msg_offset) {
603 /* msg was not fully processed, we must be awake to drain pending data */
604
605 si_rx_room_blk(si);
606 ret = 0;
607 break;
608 }
609 /* switch to next message */
610 ofs += cnt + msg_len;
611 }
612
613 HA_ATOMIC_ADD(b_peek(buf, ofs), 1);
614 ofs += ring->ofs;
615 ds->ofs = ofs;
616 }
617 HA_RWLOCK_RDUNLOCK(DNS_LOCK, &ring->lock);
618
619 if (ret) {
620 /* let's be woken up once new request to write arrived */
621 HA_RWLOCK_WRLOCK(DNS_LOCK, &ring->lock);
622 LIST_ADDQ(&ring->waiters, &appctx->wait_entry);
623 HA_RWLOCK_WRUNLOCK(DNS_LOCK, &ring->lock);
624 si_rx_endp_done(si);
625 }
626
627read:
628
629 /* if session is not a waiter it means there is no commited
630 * message into rx_buf and we are free to use it
631 * Note: we need a load barrier here to not miss the
632 * delete from the list
633 */
634 __ha_barrier_load();
635 if (!LIST_ADDED(&ds->waiter)) {
636 while (1) {
637 uint16_t query_id;
638 struct eb32_node *eb;
639 struct dns_query *query;
640
641 if (!ds->rx_msg.len) {
642 /* next message len is not fully available into the channel */
643 if (co_data(si_oc(si)) < 2)
644 break;
645
646 /* retrieve message len */
647 co_getblk(si_oc(si), (char *)&msg_len, 2, 0);
648
649 /* mark as consumed */
650 co_skip(si_oc(si), 2);
651
652 /* store message len */
653 ds->rx_msg.len = ntohs(msg_len);
654 }
655
656 if (!co_data(si_oc(si))) {
657 /* we need more data but nothing is available */
658 break;
659 }
660
661 if (co_data(si_oc(si)) + ds->rx_msg.offset < ds->rx_msg.len) {
662 /* message only partially available */
663
664 /* read available data */
665 co_getblk(si_oc(si), ds->rx_msg.area + ds->rx_msg.offset, co_data(si_oc(si)), 0);
666
667 /* update message offset */
668 ds->rx_msg.offset += co_data(si_oc(si));
669
670 /* consume all pending data from the channel */
671 co_skip(si_oc(si), co_data(si_oc(si)));
672
673 /* we need to wait for more data */
674 break;
675 }
676
677 /* enougth data is available into the channel to read the message until the end */
678
679 /* read from the channel until the end of the message */
680 co_getblk(si_oc(si), ds->rx_msg.area + ds->rx_msg.offset, ds->rx_msg.len - ds->rx_msg.offset, 0);
681
682 /* consume all data until the end of the message from the channel */
683 co_skip(si_oc(si), ds->rx_msg.len - ds->rx_msg.offset);
684
685 /* reset reader offset to 0 for next message reand */
686 ds->rx_msg.offset = 0;
687
688 /* try remap query id to original */
689 memcpy(&query_id, ds->rx_msg.area, sizeof(query_id));
690 eb = eb32_lookup(&ds->query_ids, query_id);
691 if (!eb) {
692 /* query id not found means we have an unknown corresponding
693 * request, perhaps server's bug or or the query reached
694 * timeout
695 */
696 ds->rx_msg.len = 0;
697 continue;
698 }
699
700 /* re-map the original query id set by the requester */
701 query = eb32_entry(eb, struct dns_query, qid);
702 memcpy(ds->rx_msg.area, &query->original_qid, sizeof(query->original_qid));
703
704 /* remove query ids mapping from pending queries list/tree */
705 eb32_delete(&query->qid);
706 LIST_DEL(&query->list);
707 pool_free(dns_query_pool, query);
708 ds->onfly_queries--;
709
710 /* lock the dns_stream_server containing lists heads */
711 HA_SPIN_LOCK(DNS_LOCK, &ds->dss->lock);
712
713 /* the dns_session is also added in queue of the
714 * wait_sess list where the task processing
715 * response will pop available responses
716 */
717 LIST_ADDQ(&ds->dss->wait_sess, &ds->waiter);
718
719 /* lock the dns_stream_server containing lists heads */
720 HA_SPIN_UNLOCK(DNS_LOCK, &ds->dss->lock);
721
722 /* awake the task processing the responses */
723 task_wakeup(ds->dss->task_rsp, TASK_WOKEN_INIT);
724
725 break;
726 }
727
728 if (!LIST_ADDED(&ds->waiter)) {
729 /* there is no more pending data to read and the con was closed by the server side */
730 if (!co_data(si_oc(si)) && (si_oc(si)->flags & CF_SHUTW)) {
731 goto close;
732 }
733 }
734
735 }
736
737
738 return;
739close:
740 si_shutw(si);
741 si_shutr(si);
742 si_ic(si)->flags |= CF_READ_NULL;
743}
744
745void dns_queries_flush(struct dns_session *ds)
746{
747 struct dns_query *query, *queryb;
748
749 list_for_each_entry_safe(query, queryb, &ds->queries, list) {
750 eb32_delete(&query->qid);
751 LIST_DEL(&query->list);
752 pool_free(dns_query_pool, query);
753 }
754}
755
756void dns_session_free(struct dns_session *ds)
757{
758 if (ds->rx_msg.area)
759 pool_free(dns_msg_buf, ds->rx_msg.area);
760 if (ds->tx_ring_area)
761 pool_free(dns_msg_buf, ds->tx_ring_area);
762 if (ds->task_exp)
763 task_destroy(ds->task_exp);
764
765 dns_queries_flush(ds);
766
767 ds->dss->cur_conns--;
768 /* Note: this is useless to update
769 * max_active_conns here because
770 * we decrease the value
771 */
772 pool_free(dns_session_pool, ds);
773}
774
775static struct appctx *dns_session_create(struct dns_session *ds);
776
777/*
778 * Function to release a DNS tcp session
779 */
780static void dns_session_release(struct appctx *appctx)
781{
782 struct dns_session *ds = appctx->ctx.sft.ptr;
Willy Tarreaue3e648c2021-02-24 17:38:46 +0100783 struct dns_stream_server *dss __maybe_unused;
Emeric Brunfd647d52021-02-12 20:03:38 +0100784
785 if (!ds)
786 return;
787
788 dss = ds->dss;
789
790 HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
791 LIST_DEL_INIT(&ds->list);
792
793 if (stopping) {
794 dns_session_free(ds);
795 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
796 return;
797 }
798
799 if (!ds->nb_queries) {
800 /* this is an idle session */
801 /* Note: this is useless to update max_active_sess
802 * here because we decrease idle_conns but
803 * dns_session_free decrease curconns
804 */
805
806 ds->dss->idle_conns--;
807 dns_session_free(ds);
808 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
809 return;
810 }
811
812 if (ds->onfly_queries == ds->nb_queries) {
813 /* the session can be released because
814 * it means that all queries AND
815 * responses are in fly */
816 dns_session_free(ds);
817 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
818 return;
819 }
820
821 /* We do not call ring_appctx_detach here
822 * because we want to keep readers counters
823 * to retry a con with a different appctx*/
824 HA_RWLOCK_WRLOCK(DNS_LOCK, &ds->ring.lock);
825 LIST_DEL_INIT(&appctx->wait_entry);
826 HA_RWLOCK_WRUNLOCK(DNS_LOCK, &ds->ring.lock);
827
828 /* if there is no pending complete response
829 * message, ensure to reset
830 * message offsets if the session
831 * was closed with an incomplete pending response
832 */
833 if (!LIST_ADDED(&ds->waiter))
834 ds->rx_msg.len = ds->rx_msg.offset = 0;
835
836 /* we flush pending sent queries because we never
837 * have responses
838 */
839 ds->nb_queries -= ds->onfly_queries;
840 dns_queries_flush(ds);
841
842 /* reset offset to be sure to start from message start */
843 ds->tx_msg_offset = 0;
844
845 /* here the ofs and the attached counter
846 * are kept unchanged
847 */
848
849 /* Create a new appctx, We hope we can
850 * create from the release callback! */
851 ds->appctx = dns_session_create(ds);
852 if (!ds->appctx) {
853 dns_session_free(ds);
854 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
855 return;
856 }
857
858 if (ds->nb_queries < DNS_STREAM_MAX_PIPELINED_REQ)
859 LIST_ADD(&ds->dss->free_sess, &ds->list);
860
861 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
862}
863
864/* DNS tcp session applet */
865static struct applet dns_session_applet = {
866 .obj_type = OBJ_TYPE_APPLET,
867 .name = "<STRMDNS>", /* used for logging */
868 .fct = dns_session_io_handler,
869 .release = dns_session_release,
870};
871
872/*
873 * Function used to create an appctx for a DNS session
874 */
875static struct appctx *dns_session_create(struct dns_session *ds)
876{
877 struct appctx *appctx;
878 struct session *sess;
879 struct stream *s;
880 struct applet *applet = &dns_session_applet;
881
882 appctx = appctx_new(applet, tid_bit);
883 if (!appctx)
884 goto out_close;
885
886 appctx->ctx.sft.ptr = (void *)ds;
887
888 sess = session_new(ds->dss->srv->proxy, NULL, &appctx->obj_type);
889 if (!sess) {
890 ha_alert("out of memory in peer_session_create().\n");
891 goto out_free_appctx;
892 }
893
894 if ((s = stream_new(sess, &appctx->obj_type, &BUF_NULL)) == NULL) {
895 ha_alert("Failed to initialize stream in peer_session_create().\n");
896 goto out_free_sess;
897 }
898
899
900 s->target = &ds->dss->srv->obj_type;
901 if (!sockaddr_alloc(&s->target_addr, &ds->dss->srv->addr, sizeof(ds->dss->srv->addr)))
902 goto out_free_strm;
903 s->flags = SF_ASSIGNED|SF_ADDR_SET;
904 s->si[1].flags |= SI_FL_NOLINGER;
905
906 s->do_log = NULL;
907 s->uniq_id = 0;
908
909 s->res.flags |= CF_READ_DONTWAIT;
910 /* for rto and rex to eternity to not expire on idle recv:
911 * We are using a syslog server.
912 */
913 s->res.rto = TICK_ETERNITY;
914 s->res.rex = TICK_ETERNITY;
915 ds->appctx = appctx;
916 task_wakeup(s->task, TASK_WOKEN_INIT);
917 return appctx;
918
919 /* Error unrolling */
920 out_free_strm:
921 LIST_DEL(&s->list);
922 pool_free(pool_head_stream, s);
923 out_free_sess:
924 session_free(sess);
925 out_free_appctx:
926 appctx_free(appctx);
927 out_close:
928 return NULL;
929}
930
931/* Task processing expiration of unresponded queries, this one is supposed
932 * to be stuck on the same thread than the appctx handler
933 */
934static struct task *dns_process_query_exp(struct task *t, void *context, unsigned short state)
935{
936 struct dns_session *ds = (struct dns_session *)context;
937 struct dns_query *query, *queryb;
938
939 t->expire = TICK_ETERNITY;
940
941 list_for_each_entry_safe(query, queryb, &ds->queries, list) {
942 if (tick_is_expired(query->expire, now_ms)) {
943 eb32_delete(&query->qid);
944 LIST_DEL(&query->list);
945 pool_free(dns_query_pool, query);
946 ds->onfly_queries--;
947 }
948 else {
949 t->expire = query->expire;
950 break;
951 }
952 }
953
954 return t;
955}
956
957/* Task processing expiration of idle sessions */
958static struct task *dns_process_idle_exp(struct task *t, void *context, unsigned short state)
959{
960 struct dns_stream_server *dss = (struct dns_stream_server *)context;
961 struct dns_session *ds, *dsb;
962 int target = 0;
963 int cur_active_conns;
964
965 HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
966
967
968 cur_active_conns = dss->cur_conns - dss->idle_conns;
969 if (cur_active_conns > dss->max_active_conns)
970 dss->max_active_conns = cur_active_conns;
971
972 target = (dss->max_active_conns - cur_active_conns) / 2;
973 list_for_each_entry_safe(ds, dsb, &dss->idle_sess, list) {
974 if (!target)
975 break;
976
977 /* remove conn to pending list to ensure it won't be reused */
978 LIST_DEL_INIT(&ds->list);
979
980 /* force session shutdown */
981 ds->shutdown = 1;
982
983 /* to be sure that the appctx wont miss shutdown */
984 __ha_barrier_store();
985
986 /* wake appctx to perform the shutdown */
987 appctx_wakeup(ds->appctx);
988 }
989
990 /* reset max to current active conns */
991 dss->max_active_conns = cur_active_conns;
992
993 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
994
995 t->expire = tick_add(now_ms, 5000);
996
997 return t;
998}
999
1000struct dns_session *dns_session_new(struct dns_stream_server *dss)
1001{
1002 struct dns_session *ds;
1003
1004 if (dss->maxconn && (dss->maxconn <= dss->cur_conns))
1005 return NULL;
1006
1007 ds = pool_alloc(dns_session_pool);
1008 if (!ds)
1009 return NULL;
1010
1011 ds->ofs = ~0;
1012 ds->dss = dss;
1013 LIST_INIT(&ds->list);
1014 LIST_INIT(&ds->queries);
1015 LIST_INIT(&ds->waiter);
1016 ds->rx_msg.offset = ds->rx_msg.len = 0;
1017 ds->rx_msg.area = NULL;
1018 ds->tx_ring_area = NULL;
1019 ds->task_exp = NULL;
1020 ds->appctx = NULL;
1021 ds->shutdown = 0;
1022 ds->nb_queries = 0;
1023 ds->query_ids = EB_ROOT_UNIQUE;
1024 ds->rx_msg.area = pool_alloc(dns_msg_buf);
1025 if (!ds->rx_msg.area)
1026 goto error;
1027
1028 ds->tx_ring_area = pool_alloc(dns_msg_buf);
1029 if (!ds->tx_ring_area)
1030 goto error;
1031
1032 ring_init(&ds->ring, ds->tx_ring_area, DNS_TCP_MSG_RING_MAX_SIZE);
Emeric Brun0e40fda2021-02-15 15:13:31 +01001033 if (!ring_attach(&ds->ring)) {
1034 /* Should never happen
1035 * since we are the first attached
1036 * here
1037 */
1038 goto error;
1039 }
Emeric Brunfd647d52021-02-12 20:03:38 +01001040
1041 if ((ds->task_exp = task_new(tid_bit)) == NULL)
1042 goto error;
1043
1044 ds->task_exp->process = dns_process_query_exp;
1045 ds->task_exp->context = ds;
1046
1047 ds->appctx = dns_session_create(ds);
1048 if (!ds->appctx)
1049 goto error;
1050
1051 dss->cur_conns++;
1052
1053 return ds;
1054
1055error:
1056 if (ds->task_exp)
1057 task_destroy(ds->task_exp);
1058 if (ds->rx_msg.area)
1059 pool_free(dns_msg_buf, ds->rx_msg.area);
1060 if (ds->tx_ring_area)
1061 pool_free(dns_msg_buf, ds->tx_ring_area);
1062
1063 pool_free(dns_session_pool, ds);
1064
1065 return NULL;
1066}
1067
1068/*
1069 * Task used to consume pending messages from nameserver ring
1070 * and forward them to dns_session ring.
1071 * Note: If no slot found a new dns_session is allocated
1072 */
1073static struct task *dns_process_req(struct task *t, void *context, unsigned short state)
1074{
1075 struct dns_nameserver *ns = (struct dns_nameserver *)context;
1076 struct dns_stream_server *dss = ns->stream;
1077 struct ring *ring = dss->ring_req;
1078 struct buffer *buf = &ring->buf;
1079 uint64_t msg_len;
1080 size_t len, cnt, ofs;
1081 struct dns_session *ds, *ads;
1082 HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
1083
1084 ofs = dss->ofs_req;
1085
1086 HA_RWLOCK_RDLOCK(DNS_LOCK, &ring->lock);
1087
1088 /* explanation for the initialization below: it would be better to do
1089 * this in the parsing function but this would occasionally result in
1090 * dropped events because we'd take a reference on the oldest message
1091 * and keep it while being scheduled. Thus instead let's take it the
1092 * first time we enter here so that we have a chance to pass many
1093 * existing messages before grabbing a reference to a location. This
1094 * value cannot be produced after initialization.
1095 */
1096 if (unlikely(ofs == ~0)) {
1097 ofs = 0;
1098 HA_ATOMIC_ADD(b_peek(buf, ofs), 1);
1099 ofs += ring->ofs;
1100 }
1101
1102 /* we were already there, adjust the offset to be relative to
1103 * the buffer's head and remove us from the counter.
1104 */
1105 ofs -= ring->ofs;
1106 BUG_ON(ofs >= buf->size);
1107 HA_ATOMIC_SUB(b_peek(buf, ofs), 1);
1108
1109 while (ofs + 1 < b_data(buf)) {
1110 struct ist myist;
1111
1112 cnt = 1;
1113 len = b_peek_varint(buf, ofs + cnt, &msg_len);
1114 if (!len)
1115 break;
1116 cnt += len;
1117 BUG_ON(msg_len + ofs + cnt + 1 > b_data(buf));
1118 if (unlikely(msg_len > DNS_TCP_MSG_MAX_SIZE)) {
1119 /* too large a message to ever fit, let's skip it */
1120 ofs += cnt + msg_len;
1121 continue;
1122 }
1123
1124 len = b_getblk(buf, dns_msg_trash, msg_len, ofs + cnt);
1125
1126 myist.ptr = dns_msg_trash;
1127 myist.len = len;
1128
1129 ads = NULL;
1130 /* try to push request into activ sess with free slot */
1131 if (!LIST_ISEMPTY(&dss->free_sess)) {
1132 ds = LIST_NEXT(&dss->free_sess, struct dns_session *, list);
1133
1134 if (ring_write(&ds->ring, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1) > 0) {
1135 ds->nb_queries++;
1136 if (ds->nb_queries >= DNS_STREAM_MAX_PIPELINED_REQ)
1137 LIST_DEL_INIT(&ds->list);
1138 ads = ds;
1139 }
1140 else {
1141 /* it means we were unable to put a request in this slot,
1142 * it may be close to be full so we put it at the end
1143 * of free conn list */
1144 LIST_DEL_INIT(&ds->list);
1145 LIST_ADDQ(&dss->free_sess, &ds->list);
1146 }
1147 }
1148
1149 if (!ads) {
1150 /* try to push request into idle, this one should have enought free space */
1151 if (!LIST_ISEMPTY(&dss->idle_sess)) {
1152 ds = LIST_NEXT(&dss->idle_sess, struct dns_session *, list);
1153
1154 /* ring is empty so this ring_write should never fail */
1155 ring_write(&ds->ring, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
1156 ds->nb_queries++;
1157 LIST_DEL_INIT(&ds->list);
1158
1159 ds->dss->idle_conns--;
1160
1161 /* we may have to update the max_active_conns */
1162 if (ds->dss->max_active_conns < ds->dss->cur_conns - ds->dss->idle_conns)
1163 ds->dss->max_active_conns = ds->dss->cur_conns - ds->dss->idle_conns;
1164
1165 /* since we may unable to find a free list to handle
1166 * this request, this request may be large and fill
1167 * the ring buffer so we prefer to put at the end of free
1168 * list. */
1169 LIST_ADDQ(&dss->free_sess, &ds->list);
1170 ads = ds;
1171 }
1172 }
1173
1174 /* we didn't find a session avalaible with large enough room */
1175 if (!ads) {
1176 /* allocate a new session */
1177 ads = dns_session_new(dss);
1178 if (ads) {
1179 /* ring is empty so this ring_write should never fail */
1180 ring_write(&ads->ring, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
1181 ads->nb_queries++;
1182 LIST_ADD(&dss->free_sess, &ads->list);
1183 }
1184 else
1185 ns->counters->snd_error++;
1186 }
1187
1188 if (ads)
1189 ns->counters->sent++;
1190
1191 ofs += cnt + len;
1192 }
1193
1194 HA_ATOMIC_ADD(b_peek(buf, ofs), 1);
1195 ofs += ring->ofs;
1196 dss->ofs_req = ofs;
1197 HA_RWLOCK_RDUNLOCK(DNS_LOCK, &ring->lock);
1198
1199
1200 HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
1201 return t;
1202}
1203
1204/*
1205 * Task used to consume response
1206 * Note: upper layer callback is called
1207 */
1208static struct task *dns_process_rsp(struct task *t, void *context, unsigned short state)
1209{
1210 struct dns_nameserver *ns = (struct dns_nameserver *)context;
1211
1212 ns->process_responses(ns);
1213
1214 return t;
1215}
1216
1217/* Function used to initialize an TCP nameserver */
1218int dns_stream_init(struct dns_nameserver *ns, struct server *srv)
1219{
1220 struct dns_stream_server *dss = NULL;
1221
1222 dss = calloc(1, sizeof(*dss));
1223 if (!dss) {
1224 ha_alert("memory allocation error initializing dns tcp server '%s'.\n", srv->id);
1225 goto out;
1226 }
1227
1228 dss->srv = srv;
1229 dss->maxconn = srv->maxconn;
1230
1231 dss->ofs_req = ~0; /* init ring offset */
1232 dss->ring_req = ring_new(2*DNS_TCP_MSG_RING_MAX_SIZE);
1233 if (!dss->ring_req) {
1234 ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
1235 goto out;
1236 }
1237 /* Create the task associated to the resolver target handling conns */
1238 if ((dss->task_req = task_new(MAX_THREADS_MASK)) == NULL) {
1239 ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
1240 goto out;
1241 }
1242
1243 /* Update task's parameters */
1244 dss->task_req->process = dns_process_req;
1245 dss->task_req->context = ns;
1246
1247 /* attach the task as reader */
1248 if (!ring_attach(dss->ring_req)) {
1249 /* mark server attached to the ring */
1250 ha_alert("server '%s': too many watchers for ring. this should never happen.\n", srv->id);
1251 goto out;
1252 }
1253
1254 /* Create the task associated to the resolver target handling conns */
1255 if ((dss->task_rsp = task_new(MAX_THREADS_MASK)) == NULL) {
1256 ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
1257 goto out;
1258 }
1259
1260 /* Update task's parameters */
1261 dss->task_rsp->process = dns_process_rsp;
1262 dss->task_rsp->context = ns;
1263
1264 /* Create the task associated to the resolver target handling conns */
1265 if ((dss->task_idle = task_new(MAX_THREADS_MASK)) == NULL) {
1266 ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
1267 goto out;
1268 }
1269
1270 /* Update task's parameters */
1271 dss->task_idle->process = dns_process_idle_exp;
1272 dss->task_idle->context = dss;
1273 dss->task_idle->expire = tick_add(now_ms, 5000);
1274
1275 /* let start the task to free idle conns immediatly */
1276 task_queue(dss->task_idle);
1277
1278 LIST_INIT(&dss->free_sess);
1279 LIST_INIT(&dss->idle_sess);
1280 LIST_INIT(&dss->wait_sess);
1281 HA_SPIN_INIT(&dss->lock);
1282 ns->stream = dss;
1283 return 0;
1284out:
1285 if (dss && dss->task_rsp)
1286 task_destroy(dss->task_rsp);
1287 if (dss && dss->task_req)
1288 task_destroy(dss->task_req);
1289 if (dss && dss->ring_req)
1290 ring_free(dss->ring_req);
1291
1292 free(dss);
Emeric Brunc9437992021-02-12 19:42:55 +01001293 return -1;
Christopher Faulet67957bd2017-09-27 11:00:59 +02001294}
1295
Emeric Brunc9437992021-02-12 19:42:55 +01001296int init_dns_buffers()
Baptiste Assmann325137d2015-04-13 23:40:55 +02001297{
Emeric Brunc9437992021-02-12 19:42:55 +01001298 dns_msg_trash = malloc(DNS_TCP_MSG_MAX_SIZE);
1299 if (!dns_msg_trash)
1300 return 0;
Baptiste Assmann325137d2015-04-13 23:40:55 +02001301
Emeric Brunc9437992021-02-12 19:42:55 +01001302 return 1;
1303}
Baptiste Assmannc1ce5f32016-05-14 11:26:22 +02001304
Emeric Brunc9437992021-02-12 19:42:55 +01001305void deinit_dns_buffers()
1306{
1307 free(dns_msg_trash);
1308 dns_msg_trash = NULL;
1309}
Emeric Brund26a6232021-01-04 13:32:20 +01001310
1311REGISTER_PER_THREAD_ALLOC(init_dns_buffers);
1312REGISTER_PER_THREAD_FREE(deinit_dns_buffers);