Coverage Report

Created: 2026-10-16 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/dns.c
Line
Count
Source
1
/*
2
 * Name server resolution
3
 *
4
 * Copyright 2020 HAProxy Technologies
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version
9
 * 2 of the License, or (at your option) any later version.
10
 *
11
 */
12
13
#include <errno.h>
14
#include <stdio.h>
15
#include <stdlib.h>
16
#include <string.h>
17
#include <unistd.h>
18
19
#include <sys/types.h>
20
21
#include <haproxy/action.h>
22
#include <haproxy/api.h>
23
#include <haproxy/applet.h>
24
#include <haproxy/cfgparse.h>
25
#include <haproxy/channel.h>
26
#include <haproxy/check.h>
27
#include <haproxy/cli.h>
28
#include <haproxy/dgram.h>
29
#include <haproxy/dns.h>
30
#include <haproxy/dns_ring.h>
31
#include <haproxy/errors.h>
32
#include <haproxy/fd.h>
33
#include <haproxy/log.h>
34
#include <haproxy/protocol.h>
35
#include <haproxy/sc_strm.h>
36
#include <haproxy/stconn.h>
37
#include <haproxy/stream.h>
38
#include <haproxy/tools.h>
39
40
static THREAD_LOCAL char *dns_msg_trash;
41
42
DECLARE_STATIC_TYPED_POOL(dns_session_pool, "dns_session", struct dns_session);
43
DECLARE_STATIC_TYPED_POOL(dns_query_pool, "dns_query", struct dns_query);
44
DECLARE_STATIC_POOL(dns_msg_buf, "dns_msg_buf", DNS_MSG_RING_MAX_SIZE);
45
46
/* Opens an UDP socket on the namesaver's IP/Port, if required. Returns 0 on
47
 * success, -1 otherwise. ns->dgram must be defined.
48
 */
49
static int dns_connect_nameserver(struct dns_nameserver *ns)
50
0
{
51
0
  struct dgram_conn *dgram = &ns->dgram->conn;
52
0
  const struct protocol *proto;
53
0
  int fd;
54
55
  /* Already connected */
56
0
  if (dgram->t.sock.fd != -1)
57
0
    return 0;
58
59
  /* Create an UDP socket and connect it on the nameserver's IP/Port */
60
0
  proto = protocol_lookup(dgram->addr.to.ss_family, PROTO_TYPE_DGRAM, 1);
61
0
  BUG_ON(!proto);
62
0
  if ((fd = socket(proto->fam->sock_domain, proto->sock_type, proto->sock_prot)) == -1) {
63
0
    send_log(NULL, LOG_WARNING,
64
0
       "DNS : section '%s': can't create socket for nameserver '%s'.\n",
65
0
       ns->counters->pid, ns->id);
66
0
    return -1;
67
0
  }
68
69
0
  switch (proto->fam->sock_domain) {
70
0
  case AF_INET: {
71
0
    struct sockaddr_in address = {
72
0
      .sin_family = AF_INET,
73
0
      .sin_port = 0,
74
0
      .sin_addr = { .s_addr = INADDR_ANY }
75
0
    };
76
0
    if (bind(fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
77
0
      send_log(NULL, LOG_WARNING,
78
0
         "DNS : section '%s': can't bind socket for nameserver '%s' on 0.0.0.0:0.\n",
79
0
         ns->counters->pid, ns->id);
80
0
      close(fd);
81
0
      return -1;
82
0
    }
83
0
    break;
84
0
  }
85
0
  case AF_INET6: {
86
0
    struct sockaddr_in6 address6 = {
87
0
      .sin6_family = AF_INET6,
88
0
      .sin6_port = 0,
89
0
      .sin6_addr = in6addr_any,
90
0
      .sin6_flowinfo = 0,
91
0
      .sin6_scope_id = 0
92
0
    };
93
0
    if (bind(fd, (struct sockaddr *)&address6, sizeof(address6)) < 0) {
94
0
      send_log(NULL, LOG_WARNING,
95
0
         "DNS : section '%s': can't bind socket for nameserver '%s' on :::0.\n",
96
0
         ns->counters->pid, ns->id);
97
0
      close(fd);
98
0
      return -1;
99
0
    }
100
0
    break;
101
0
  }
102
0
  case AF_UNIX:
103
    /* if IPC is used via local domain sockets, we don't expect that
104
     * the path to DNS server socket can change dynamically.
105
     */
106
0
    if (connect(fd, (struct sockaddr*)&dgram->addr.to, get_addr_len(&dgram->addr.to)) == -1) {
107
0
      send_log(NULL, LOG_WARNING,
108
0
         "DNS : section '%s': can't connect socket for nameserver '%s'.\n",
109
0
         ns->counters->id, ns->id);
110
0
      close(fd);
111
0
      return -1;
112
0
    }
113
0
    break;
114
0
  default:
115
0
    close(fd);
116
0
    BUG_ON(1, "DNS: Unsupported address family.");
117
0
  }
118
119
  /* Make the socket non blocking */
120
0
  fd_set_nonblock(fd);
121
122
  /* Add the fd in the fd list and update its parameters */
123
0
  dgram->t.sock.fd = fd;
124
125
  /* let's stick the FD to the initiator thread, this will ensure that
126
   * most of the time, a resolver will not try to access its structure
127
   * at the same time as a response is processed, and will eliminate
128
   * locking contention.
129
   */
130
0
  fd_insert(fd, dgram, dgram_fd_handler, tgid, ti->ltid_bit);
131
0
  fd_want_recv(fd);
132
0
  return 0;
133
0
}
134
135
/* Sends a message using server type <type> on nameserver <ns>.
136
 * It returns message length on success
137
 * or -1 in error case
138
 * 0 is returned in case of output ring buffer is full
139
 */
140
int dns_send_nameserver(struct dns_nameserver *ns, enum dns_server_type type, void *buf, size_t len)
141
0
{
142
0
  int ret = -1;
143
144
0
  if (dns_server_type_is_dgram(type) && ns->dgram) {
145
0
    struct dgram_conn *dgram = &ns->dgram->conn;
146
0
    int fd;
147
148
0
    HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
149
0
    fd = dgram->t.sock.fd;
150
0
    if (fd == -1) {
151
0
      if (dns_connect_nameserver(ns) == -1) {
152
0
        HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
153
0
        return -1;
154
0
      }
155
0
      fd = dgram->t.sock.fd;
156
0
    }
157
158
0
    if (dgram->addr.to.ss_family == AF_UNIX) {
159
      /* we do connect for AF_UNIX sockets and from the man
160
       * sendto: "If sendto() is  used on a connection-mode
161
       * (SOCK_STREAM, SOCK_SEQPACKET) socket, the arguments
162
       * dest_addr and addrlen are ignored (and the error
163
       * EISCONN may be returned when they are not NULL and 0)..."
164
       */
165
0
      ret = send(fd, buf, len, 0);
166
0
    } else
167
0
      ret = sendto(fd, buf, len, 0, (struct sockaddr*)&dgram->addr.to, get_addr_len(&dgram->addr.to));
168
169
0
    if (ret < 0) {
170
0
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
171
0
        struct ist myist;
172
173
0
        myist = ist2(buf, len);
174
0
        ret = dns_ring_write(ns->dgram->ring_req, DNS_MAX_MSG_SIZE, NULL, 0, &myist, 1);
175
0
        if (!ret) {
176
0
          ns->counters->snd_error++;
177
0
          HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
178
0
          return -1;
179
0
        }
180
0
        fd_cant_send(fd);
181
0
        HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
182
0
        return ret;
183
0
      }
184
0
      ns->counters->snd_error++;
185
0
      fd_delete(fd);
186
0
      dgram->t.sock.fd = -1;
187
0
      HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
188
0
      return -1;
189
0
    }
190
0
    ns->counters->sent++;
191
0
    HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
192
0
  }
193
0
  else if (dns_server_type_is_stream(type) && ns->stream) {
194
0
    struct ist myist;
195
196
0
    myist = ist2(buf, len);
197
0
    ret = dns_ring_write(ns->stream->ring_req, DNS_MAX_MSG_SIZE, NULL, 0, &myist, 1);
198
0
    if (!ret) {
199
0
      ns->counters->snd_error++;
200
0
      return -1;
201
0
    }
202
0
    task_wakeup(ns->stream->task_req, TASK_WOKEN_MSG);
203
0
    return ret;
204
0
  }
205
206
0
  return ret;
207
0
}
208
209
void dns_session_free(struct dns_session *);
210
211
/* Receives a DNS message using server type <type> on nameserver <ns>.
212
 * Returns message length
213
 * 0 is returned if no more message available
214
 * -1 in error case
215
 */
216
ssize_t dns_recv_nameserver(struct dns_nameserver *ns, enum dns_server_type type, void *data, size_t size)
217
0
{
218
0
  ssize_t ret = -1;
219
220
0
  if (dns_server_type_is_dgram(type) && ns->dgram) {
221
0
    struct dgram_conn *dgram = &ns->dgram->conn;
222
0
    struct sockaddr_storage from = {0};
223
0
    socklen_t fromlen = sizeof(from);
224
0
    int fd;
225
226
0
    HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
227
0
    fd = dgram->t.sock.fd;
228
0
    if (fd == -1) {
229
0
      HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
230
0
      return -1;
231
0
    }
232
233
0
    if ((ret = recvfrom(fd, data, size, 0, (struct sockaddr *)&from, &fromlen)) < 0) {
234
0
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
235
0
        fd_cant_recv(fd);
236
0
        HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
237
0
        return 0;
238
0
      }
239
0
      fd_delete(fd);
240
0
      dgram->t.sock.fd = -1;
241
0
      HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
242
0
      return -1;
243
0
    }
244
0
    if ((dgram->addr.to.ss_family == AF_INET || dgram->addr.to.ss_family == AF_INET6) &&
245
0
        ipcmp(&from, &dgram->addr.to, 1) != 0) {
246
      /* reply from unexpected source -- silently discard */
247
0
      fd_want_recv(fd);
248
0
      ret = 0;
249
0
    }
250
0
    HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
251
0
  }
252
0
  else if (dns_server_type_is_stream(type) && ns->stream) {
253
0
    struct dns_stream_server *dss = ns->stream;
254
0
    struct dns_session *ds;
255
256
0
    HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
257
258
0
    if (!LIST_ISEMPTY(&dss->wait_sess)) {
259
0
      ds = LIST_NEXT(&dss->wait_sess, struct dns_session *, waiter);
260
0
      ret = ds->rx_msg.len < size ? ds->rx_msg.len : size;
261
0
      memcpy(data, ds->rx_msg.area, ret);
262
263
0
      ds->rx_msg.len = 0;
264
265
      /* This barrier is here to ensure that all data is
266
       * stored if the appctx detect the elem is out of the
267
       * list.
268
       */
269
0
      __ha_barrier_store();
270
271
0
      LIST_DEL_INIT(&ds->waiter);
272
273
0
      if (ds->appctx) {
274
        /* This second barrier is here to ensure that
275
         * the waked up appctx won't miss that the elem
276
         * is removed from the list.
277
         */
278
0
        __ha_barrier_store();
279
280
        /* awake appctx because it may have other
281
         * message to receive
282
         */
283
0
        appctx_wakeup(ds->appctx);
284
285
        /* dns_session could already be into free_sess list
286
         * so we firstly remove it */
287
0
        LIST_DEL_INIT(&ds->list);
288
289
        /* decrease nb_queries to free a slot for a new query on that sess */
290
0
        ds->nb_queries--;
291
0
        if (ds->nb_queries) {
292
          /* it remains pipelined unanswered request
293
           * into this session but we just decrease
294
           * the counter so the session
295
           * can not be full of pipelined requests
296
           * so we can add if to free_sess list
297
           * to receive a new request
298
          */
299
0
          LIST_INSERT(&ds->dss->free_sess, &ds->list);
300
0
        }
301
0
        else {
302
          /* there is no more pipelined requests
303
           * into this session, so we move it
304
           * to idle_sess list */
305
0
          LIST_INSERT(&ds->dss->idle_sess, &ds->list);
306
307
          /* update the counter of idle sessions */
308
0
          ds->dss->idle_conns++;
309
310
          /* Note: this is useless there to update
311
           * the max_active_conns since we increase
312
           * the idle count */
313
0
        }
314
0
      }
315
0
      else {
316
        /* there is no more appctx for this session
317
         * it means it is ready to die
318
         */
319
0
        dns_session_free(ds);
320
0
      }
321
322
323
0
    }
324
325
0
    HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
326
0
  }
327
328
0
  return ret;
329
0
}
330
331
static void dns_resolve_recv(struct dgram_conn *dgram)
332
0
{
333
0
  struct dns_nameserver *ns;
334
0
  int fd;
335
336
0
  HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
337
338
0
  fd = dgram->t.sock.fd;
339
340
  /* check if ready for reading */
341
0
  if ((fd == -1) || !fd_recv_ready(fd)) {
342
0
    HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
343
0
    return;
344
0
  }
345
346
  /* no need to go further if we can't retrieve the nameserver */
347
0
  if ((ns = dgram->owner) == NULL) {
348
0
    _HA_ATOMIC_AND(&fdtab[fd].state, ~(FD_POLL_HUP|FD_POLL_ERR));
349
0
    fd_stop_recv(fd);
350
0
    HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
351
0
    return;
352
0
  }
353
354
0
  HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
355
356
0
  ns->process_responses(ns, DNS_SERVER_DGRAM);
357
0
}
358
359
/* Called when a dns network socket is ready to send data */
360
static void dns_resolve_send(struct dgram_conn *dgram)
361
0
{
362
0
  int fd;
363
0
  struct dns_nameserver *ns;
364
0
  struct dns_ring *ring;
365
0
  struct buffer *buf;
366
0
  uint64_t msg_len;
367
0
  size_t len, cnt, ofs;
368
369
0
  HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
370
371
0
  fd = dgram->t.sock.fd;
372
373
  /* check if ready for sending */
374
0
  if ((fd == -1) || !fd_send_ready(fd)) {
375
0
    HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
376
0
    return;
377
0
  }
378
379
  /* no need to go further if we can't retrieve the nameserver */
380
0
  if ((ns = dgram->owner) == NULL) {
381
0
    _HA_ATOMIC_AND(&fdtab[fd].state, ~(FD_POLL_HUP|FD_POLL_ERR));
382
0
    fd_stop_send(fd);
383
0
    HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
384
0
    return;
385
0
  }
386
387
0
  ring = ns->dgram->ring_req;
388
0
  buf = &ring->buf;
389
390
0
  HA_RWLOCK_RDLOCK(DNS_LOCK, &ring->lock);
391
392
  /* explanation for the initialization below: it would be better to do
393
   * this in the parsing function but this would occasionally result in
394
   * dropped events because we'd take a reference on the oldest message
395
   * and keep it while being scheduled. Thus instead let's take it the
396
   * first time we enter here so that we have a chance to pass many
397
   * existing messages before grabbing a reference to a location. This
398
   * value cannot be produced after initialization.
399
   */
400
0
  if (unlikely(ns->dgram->ofs_req == ~0)) {
401
0
    ns->dgram->ofs_req = b_peek_ofs(buf, 0);
402
0
    HA_ATOMIC_INC(b_orig(buf) + ns->dgram->ofs_req);
403
0
  }
404
405
  /* we were already there, adjust the offset to be relative to
406
   * the buffer's head and remove us from the counter.
407
   */
408
0
  ofs = ns->dgram->ofs_req - b_head_ofs(buf);
409
0
  if (ns->dgram->ofs_req < b_head_ofs(buf))
410
0
    ofs += b_size(buf);
411
0
  BUG_ON(ofs >= buf->size);
412
0
  HA_ATOMIC_DEC(b_peek(buf, ofs));
413
414
0
  while (ofs + 1 < b_data(buf)) {
415
0
    int ret;
416
417
0
    cnt = 1;
418
0
    len = b_peek_varint(buf, ofs + cnt, &msg_len);
419
0
    if (!len)
420
0
      break;
421
0
    cnt += len;
422
0
    BUG_ON(msg_len + ofs + cnt + 1 > b_data(buf));
423
0
    if (unlikely(msg_len > DNS_MAX_MSG_SIZE)) {
424
      /* too large a message to ever fit, let's skip it */
425
0
      ofs += cnt + msg_len;
426
0
      continue;
427
0
    }
428
429
0
    len = b_getblk(buf, dns_msg_trash, msg_len, ofs + cnt);
430
431
0
    ret = send(fd, dns_msg_trash, len, 0);
432
0
    if (ret < 0) {
433
0
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
434
0
        fd_cant_send(fd);
435
0
        goto out;
436
0
      }
437
0
      ns->counters->snd_error++;
438
0
      fd_delete(fd);
439
0
      fd = dgram->t.sock.fd = -1;
440
0
      goto out;
441
0
    }
442
0
    ns->counters->sent++;
443
444
0
    ofs += cnt + len;
445
0
  }
446
447
  /* we don't want/need to be waked up any more for sending
448
   * because all ring content is sent */
449
0
  fd_stop_send(fd);
450
451
0
out:
452
0
  HA_ATOMIC_INC(b_peek(buf, ofs));
453
0
  ns->dgram->ofs_req = b_peek_ofs(buf, ofs);
454
0
  HA_RWLOCK_RDUNLOCK(DNS_LOCK, &ring->lock);
455
0
  HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
456
0
}
457
458
/* proto_udp callback functions for a DNS resolution */
459
struct dgram_data_cb dns_dgram_cb = {
460
  .recv = dns_resolve_recv,
461
  .send = dns_resolve_send,
462
};
463
464
int dns_dgram_init(struct dns_nameserver *ns, struct sockaddr_storage *sk)
465
0
{
466
0
  struct dns_dgram_server *dgram;
467
468
0
   if ((dgram = calloc(1, sizeof(*dgram))) == NULL)
469
0
    return -1;
470
471
  /* Leave dgram partially initialized, no FD attached for
472
   * now. */
473
0
  dgram->conn.owner     = ns;
474
0
  dgram->conn.data      = &dns_dgram_cb;
475
0
  dgram->conn.t.sock.fd = -1;
476
0
  dgram->conn.addr.to = *sk;
477
0
  HA_SPIN_INIT(&dgram->conn.lock);
478
479
0
  dgram->ofs_req = ~0; /* init ring offset */
480
0
  dgram->ring_req = dns_ring_new(2*DNS_MSG_RING_MAX_SIZE);
481
0
  if (!dgram->ring_req) {
482
0
    ha_alert("memory allocation error initializing the ring for nameserver.\n");
483
0
    goto out;
484
0
  }
485
486
  /* attach the task as reader */
487
0
  if (!dns_ring_attach(dgram->ring_req)) {
488
    /* mark server attached to the ring */
489
0
    ha_alert("nameserver sets too many watchers > 255 on ring. This is a bug and should not happen.\n");
490
0
    goto out;
491
0
  }
492
0
  ns->dgram = dgram;
493
0
  return 0;
494
0
out:
495
0
  dns_ring_free(dgram->ring_req);
496
497
0
  free(dgram);
498
499
0
  return -1;
500
0
}
501
502
/*
503
 * IO Handler to handle message push to dns tcp server
504
 * It takes its context from appctx->svcctx.
505
 */
506
static void dns_session_io_handler(struct appctx *appctx)
507
0
{
508
0
  struct dns_session *ds = appctx->svcctx;
509
0
  struct dns_ring *ring = &ds->ring;
510
0
  struct buffer *buf = &ring->buf;
511
0
  uint64_t msg_len;
512
0
  int available_room;
513
0
  size_t len, cnt, ofs;
514
0
  int ret = 0;
515
516
0
  if (unlikely(applet_fl_test(appctx, APPCTX_FL_EOS|APPCTX_FL_ERROR))) {
517
0
    applet_reset_input(appctx);
518
0
    goto out;
519
0
  }
520
521
  /* if stopping was requested, close immediately */
522
0
  if (unlikely(stopping))
523
0
    goto close;
524
525
  /* we want to be sure to not miss that we have been awaked for a shutdown */
526
0
  __ha_barrier_load();
527
528
  /* that means the connection was requested to shutdown
529
   * for instance idle expire */
530
0
  if (ds->shutdown)
531
0
    goto close;
532
533
  /* if the connection is not established, inform the stream that we want
534
   * to be notified whenever the connection completes.
535
   */
536
0
  if (se_fl_test(appctx->sedesc, SE_FL_APPLET_NEED_CONN)) {
537
0
    applet_need_more_data(appctx);
538
0
    applet_have_more_data(appctx);
539
0
    goto out;
540
0
  }
541
542
0
  if (applet_get_outbuf(appctx) == NULL) {
543
0
    applet_have_more_data(appctx);
544
0
    goto out;
545
0
  }
546
547
0
  HA_RWLOCK_WRLOCK(DNS_LOCK, &ring->lock);
548
0
  MT_LIST_DELETE(&appctx->wait_entry);
549
0
  HA_RWLOCK_WRUNLOCK(DNS_LOCK, &ring->lock);
550
551
0
  HA_RWLOCK_RDLOCK(DNS_LOCK, &ring->lock);
552
553
  /* explanation for the initialization below: it would be better to do
554
   * this in the parsing function but this would occasionally result in
555
   * dropped events because we'd take a reference on the oldest message
556
   * and keep it while being scheduled. Thus instead let's take it the
557
   * first time we enter here so that we have a chance to pass many
558
   * existing messages before grabbing a reference to a location. This
559
   * value cannot be produced after initialization.
560
   */
561
0
  if (unlikely(ds->ofs == ~0)) {
562
0
    ds->ofs = b_peek_ofs(buf, 0);
563
0
    HA_ATOMIC_INC(b_orig(buf) + ds->ofs);
564
0
  }
565
566
  /* we were already there, adjust the offset to be relative to
567
   * the buffer's head and remove us from the counter.
568
   */
569
0
  ofs = ds->ofs - b_head_ofs(buf);
570
0
  if (ds->ofs < b_head_ofs(buf))
571
0
    ofs += b_size(buf);
572
573
0
  BUG_ON(ofs >= buf->size);
574
0
  HA_ATOMIC_DEC(b_peek(buf, ofs));
575
576
  /* in following loop, ofs always points to the counter byte that
577
   * precedes the message so that we can take our reference there if we
578
   * have to stop before the end (ret=0).
579
   */
580
0
  ret = 1;
581
0
  while (ofs + 1 < b_data(buf)) {
582
0
    struct dns_query *query;
583
0
    uint16_t original_qid;
584
0
    uint16_t new_qid;
585
586
0
    cnt = 1;
587
0
    len = b_peek_varint(buf, ofs + cnt, &msg_len);
588
0
    if (!len)
589
0
      break;
590
0
    cnt += len;
591
0
    BUG_ON(msg_len + ofs + cnt + 1 > b_data(buf));
592
593
    /* retrieve available room on output channel */
594
0
    available_room = applet_output_room(appctx);
595
596
    /* tx_msg_offset null means we are at the start of a new message */
597
0
    if (!ds->tx_msg_offset) {
598
0
      uint16_t slen;
599
600
      /* check if there is enough room to put message len and query id */
601
0
      if (available_room < sizeof(slen) + sizeof(new_qid)) {
602
0
        applet_have_more_data(appctx);
603
0
        ret = 0;
604
0
        break;
605
0
      }
606
607
      /* put msg len into then channel */
608
0
      slen = (uint16_t)msg_len;
609
0
      slen = htons(slen);
610
0
      applet_putblk(appctx, (char *)&slen, sizeof(slen));
611
0
      available_room -= sizeof(slen);
612
613
      /* backup original query id */
614
0
      len = b_getblk(buf, (char *)&original_qid, sizeof(original_qid), ofs + cnt);
615
0
      if (!len) {
616
        /* should never happen since messages are atomically
617
         * written into ring
618
         */
619
0
        ret = 0;
620
0
        break;
621
0
      }
622
623
      /* generates new query id */
624
0
      new_qid = ++ds->query_counter;
625
0
      new_qid = htons(new_qid);
626
627
      /* put new query id into the channel */
628
0
      applet_putblk(appctx, (char *)&new_qid, sizeof(new_qid));
629
0
      available_room -= sizeof(new_qid);
630
631
      /* keep query id mapping */
632
633
0
      query = pool_alloc(dns_query_pool);
634
0
      if (query) {
635
0
        query->qid.key = new_qid;
636
0
        query->original_qid = original_qid;
637
0
        query->expire = tick_add(now_ms, 5000);
638
0
        LIST_INIT(&query->list);
639
0
        if (LIST_ISEMPTY(&ds->queries)) {
640
          /* enable task to handle expire */
641
0
          ds->task_exp->expire = query->expire;
642
          /* ensure this will be executed by the same
643
           * thread than ds_session_release
644
           * to ensure session_release is free
645
           * to destroy the task */
646
0
          task_queue(ds->task_exp);
647
0
        }
648
0
        LIST_APPEND(&ds->queries, &query->list);
649
0
        eb32_insert(&ds->query_ids, &query->qid);
650
0
        ds->onfly_queries++;
651
0
        HA_ATOMIC_STORE(&ds->dss->consecutive_errors, 0);
652
0
      }
653
654
      /* update the tx_offset to handle output in 16k streams */
655
0
      ds->tx_msg_offset = sizeof(original_qid);
656
657
0
    }
658
659
    /* check if it remains available room on output chan */
660
0
    if (unlikely(!available_room)) {
661
0
      applet_have_more_data(appctx);
662
0
      ret = 0;
663
0
      break;
664
0
    }
665
666
0
    chunk_reset(&trash);
667
0
    if ((msg_len - ds->tx_msg_offset) > available_room) {
668
      /* remaining msg data is too large to be written in output channel at one time */
669
670
0
      len = b_getblk(buf, trash.area, available_room, ofs + cnt + ds->tx_msg_offset);
671
672
      /* update offset to complete mesg forwarding later */
673
0
      ds->tx_msg_offset += len;
674
0
    }
675
0
    else {
676
      /* remaining msg data can be written in output channel at one time */
677
0
      len = b_getblk(buf, trash.area, msg_len - ds->tx_msg_offset, ofs + cnt + ds->tx_msg_offset);
678
679
      /* reset tx_msg_offset to mark forward fully processed */
680
0
      ds->tx_msg_offset = 0;
681
0
    }
682
0
    trash.data += len;
683
684
0
    if (applet_putchk(appctx, &trash) == -1) {
685
      /* should never happen since we
686
       * check available_room is large
687
       * enough here.
688
       */
689
0
      ret = 0;
690
0
      break;
691
0
    }
692
693
0
    if (ds->tx_msg_offset) {
694
      /* msg was not fully processed, we must  be awake to drain pending data */
695
0
      applet_have_more_data(appctx);
696
0
      ret = 0;
697
0
      break;
698
0
    }
699
    /* switch to next message */
700
0
    ofs += cnt + msg_len;
701
0
  }
702
703
0
  HA_ATOMIC_INC(b_peek(buf, ofs));
704
0
  ds->ofs = b_peek_ofs(buf, ofs);
705
706
0
  HA_RWLOCK_RDUNLOCK(DNS_LOCK, &ring->lock);
707
708
0
  if (ret) {
709
    /* let's be woken up once new request to write arrived */
710
0
    HA_RWLOCK_WRLOCK(DNS_LOCK, &ring->lock);
711
0
    BUG_ON(MT_LIST_INLIST(&appctx->wait_entry));
712
0
    MT_LIST_APPEND(&ring->waiters, &appctx->wait_entry);
713
0
    HA_RWLOCK_WRUNLOCK(DNS_LOCK, &ring->lock);
714
0
    applet_have_no_more_data(appctx);
715
0
  }
716
717
  /* if session is not a waiter it means there is no committed
718
   * message into rx_buf and we are free to use it
719
   * Note: we need a load barrier here to not miss the
720
   * delete from the list
721
   */
722
0
  __ha_barrier_load();
723
0
  if (!LIST_INLIST_ATOMIC(&ds->waiter)) {
724
0
    if (applet_get_inbuf(appctx) == NULL) {
725
0
      applet_need_more_data(appctx);
726
0
      goto out;
727
0
    }
728
729
0
    while (1) {
730
0
      uint16_t query_id;
731
0
      struct eb32_node *eb;
732
0
      struct dns_query *query;
733
734
0
      if (!ds->rx_msg.len) {
735
        /* retrieve message len */
736
0
        ret = applet_getblk(appctx, (char *)&msg_len, 2, 0);
737
0
        if (ret <= 0) {
738
0
          if (ret == -1)
739
0
            goto error;
740
0
          applet_need_more_data(appctx);
741
0
          break;
742
0
        }
743
744
        /* mark as consumed */
745
0
        applet_skip_input(appctx, 2);
746
747
        /* store message len */
748
0
        ds->rx_msg.len = ntohs(msg_len);
749
0
        if (!ds->rx_msg.len)
750
0
          continue;
751
0
      }
752
753
0
      if (applet_input_data(appctx) + ds->rx_msg.offset < ds->rx_msg.len) {
754
        /* message only partially available */
755
756
        /* read available data */
757
0
        ret = applet_getblk(appctx, ds->rx_msg.area + ds->rx_msg.offset, applet_input_data(appctx), 0);
758
0
        if (ret <= 0) {
759
0
          if (ret == -1)
760
0
            goto error;
761
0
          applet_need_more_data(appctx);
762
0
          break;
763
0
        }
764
765
        /* update message offset */
766
0
        ds->rx_msg.offset += applet_input_data(appctx);
767
768
        /* consume all pending data from the channel */
769
0
        applet_skip_input(appctx, applet_input_data(appctx));
770
771
        /* we need to wait for more data */
772
0
        applet_need_more_data(appctx);
773
0
        break;
774
0
      }
775
776
      /* enough data is available into the channel to read the message until the end */
777
778
      /* read from the channel until the end of the message */
779
0
      ret = applet_getblk(appctx, ds->rx_msg.area + ds->rx_msg.offset, ds->rx_msg.len - ds->rx_msg.offset, 0);
780
0
      if (ret <= 0) {
781
0
        if (ret == -1)
782
0
          goto error;
783
0
        applet_need_more_data(appctx);
784
0
        break;
785
0
      }
786
787
      /* consume all data until the end of the message from the channel */
788
0
      applet_skip_input(appctx, ds->rx_msg.len - ds->rx_msg.offset);
789
790
      /* reset reader offset to 0 for next message reand */
791
0
      ds->rx_msg.offset = 0;
792
793
      /* try remap query id to original */
794
0
      memcpy(&query_id, ds->rx_msg.area, sizeof(query_id));
795
0
      eb = eb32_lookup(&ds->query_ids, query_id);
796
0
      if (!eb) {
797
        /* query id not found means we have an unknown corresponding
798
         * request, perhaps server's bug or or the query reached
799
         * timeout
800
        */
801
0
        ds->rx_msg.len = 0;
802
0
        continue;
803
0
      }
804
805
      /* re-map the original query id set by the requester */
806
0
      query = eb32_entry(eb, struct dns_query, qid);
807
0
      memcpy(ds->rx_msg.area, &query->original_qid, sizeof(query->original_qid));
808
809
      /* remove query ids mapping from pending queries list/tree */
810
0
      eb32_delete(&query->qid);
811
0
      LIST_DELETE(&query->list);
812
0
      pool_free(dns_query_pool, query);
813
0
      ds->onfly_queries--;
814
815
      /* the dns_session is also added in queue of the
816
       * wait_sess list where the task processing
817
       * response will pop available responses
818
       */
819
0
      HA_SPIN_LOCK(DNS_LOCK, &ds->dss->lock);
820
821
0
      BUG_ON(LIST_INLIST(&ds->waiter));
822
0
      LIST_APPEND(&ds->dss->wait_sess, &ds->waiter);
823
824
0
      HA_SPIN_UNLOCK(DNS_LOCK, &ds->dss->lock);
825
826
      /* awake the task processing the responses */
827
0
      task_wakeup(ds->dss->task_rsp, TASK_WOKEN_INIT);
828
829
0
      break;
830
0
    }
831
0
  }
832
833
0
out:
834
0
  return;
835
836
0
close:
837
0
  applet_set_eos(appctx);
838
0
  goto out;
839
840
0
error:
841
0
  applet_set_eos(appctx);
842
0
  applet_set_error(appctx);
843
0
  goto out;
844
0
}
845
846
void dns_queries_flush(struct dns_session *ds)
847
0
{
848
0
  struct dns_query *query, *queryb;
849
850
0
  list_for_each_entry_safe(query, queryb, &ds->queries, list) {
851
0
    eb32_delete(&query->qid);
852
0
    LIST_DELETE(&query->list);
853
0
    pool_free(dns_query_pool, query);
854
0
  }
855
0
}
856
857
void dns_session_free(struct dns_session *ds)
858
0
{
859
0
  pool_free(dns_msg_buf, ds->rx_msg.area);
860
0
  pool_free(dns_msg_buf, ds->tx_ring_area);
861
0
  task_destroy(ds->task_exp);
862
863
0
  dns_queries_flush(ds);
864
865
  /* Ensure to remove this session from external lists
866
   * Note: we are under the lock of dns_stream_server
867
   * which own the heads of those lists.
868
   */
869
0
  LIST_DEL_INIT(&ds->waiter);
870
0
  LIST_DEL_INIT(&ds->list);
871
872
0
  ds->dss->cur_conns--;
873
  /* Note: this is useless to update
874
   * max_active_conns here because
875
   * we decrease the value
876
   */
877
878
0
  BUG_ON(!LIST_ISEMPTY(&ds->list));
879
0
  BUG_ON(!LIST_ISEMPTY(&ds->waiter));
880
0
  BUG_ON(!LIST_ISEMPTY(&ds->queries));
881
0
  BUG_ON(!MT_LIST_ISEMPTY(&ds->ring.waiters));
882
0
  BUG_ON(!eb_is_empty(&ds->query_ids));
883
0
  pool_free(dns_session_pool, ds);
884
0
}
885
886
static struct appctx *dns_session_create(struct dns_session *ds, int tempo);
887
888
static int dns_session_init(struct appctx *appctx)
889
0
{
890
0
  struct dns_session *ds = appctx->svcctx;
891
0
  struct stream *s;
892
0
  struct sockaddr_storage *addr = NULL;
893
894
0
  if (!sockaddr_alloc(&addr, &ds->dss->srv->addr, sizeof(ds->dss->srv->addr)))
895
0
    goto error;
896
897
0
  if (appctx_finalize_startup(appctx, ds->dss->srv->proxy, &BUF_NULL) == -1)
898
0
    goto error;
899
900
0
  s = appctx_strm(appctx);
901
0
  s->scb->dst = addr;
902
0
  s->scb->flags |= (SC_FL_RCV_ONCE|SC_FL_NOLINGER);
903
0
  stream_set_srv_target(s, ds->dss->srv);
904
0
  s->flags = SF_ASSIGNED;
905
906
0
  s->do_log = NULL;
907
0
  s->uniq_id = 0;
908
909
0
  se_need_remote_conn(appctx->sedesc);
910
0
  applet_expect_no_data(appctx);
911
0
  ds->appctx = appctx;
912
0
  appctx->t->expire = TICK_ETERNITY;
913
0
  return 0;
914
915
0
  error:
916
0
  sockaddr_free(&addr);
917
0
  return -1;
918
0
}
919
920
/*
921
 * Function to release a DNS tcp session
922
 */
923
static void dns_session_release(struct appctx *appctx)
924
0
{
925
0
  struct dns_session *ds = appctx->svcctx;
926
0
  struct dns_stream_server *dss __maybe_unused;
927
0
  int consecutive_errors;
928
929
0
  if (!ds)
930
0
    return;
931
932
  /* We do not call dns_ring_appctx_detach here
933
   * because we want to keep readers counters
934
   * to retry a conn with a different appctx.
935
   */
936
0
  HA_RWLOCK_WRLOCK(DNS_LOCK, &ds->ring.lock);
937
0
  MT_LIST_DELETE(&appctx->wait_entry);
938
0
  HA_RWLOCK_WRUNLOCK(DNS_LOCK, &ds->ring.lock);
939
940
0
  dss = ds->dss;
941
942
0
  HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
943
0
  LIST_DEL_INIT(&ds->list);
944
945
0
  if (stopping) {
946
0
    dns_session_free(ds);
947
0
    HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
948
0
    return;
949
0
  }
950
951
0
  if (!ds->nb_queries) {
952
    /* this is an idle session */
953
    /* Note: this is useless to update max_active_sess
954
     * here because we decrease idle_conns but
955
     * dns_session_free decrease curconns
956
     */
957
958
0
    ds->dss->idle_conns--;
959
0
    dns_session_free(ds);
960
0
    HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
961
0
    return;
962
0
  }
963
964
0
  if (ds->onfly_queries == ds->nb_queries) {
965
    /* the session can be released because
966
     * it means that all queries AND
967
     * responses are in fly */
968
0
    dns_session_free(ds);
969
0
    HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
970
0
    return;
971
0
  }
972
973
  /* if there is no pending complete response
974
   * message, ensure to reset
975
   * message offsets if the session
976
   * was closed with an incomplete pending response
977
   */
978
0
  if (!LIST_INLIST(&ds->waiter))
979
0
    ds->rx_msg.len = ds->rx_msg.offset = 0;
980
981
  /* we flush pending sent queries because we never
982
   * have responses
983
   */
984
0
  ds->nb_queries -= ds->onfly_queries;
985
0
  dns_queries_flush(ds);
986
987
  /* reset offset to be sure to start from message start */
988
0
  ds->tx_msg_offset = 0;
989
990
0
  consecutive_errors = HA_ATOMIC_LOAD(&ds->dss->consecutive_errors);
991
  /* we know ds encountered an error because it failed to send all
992
   * its queries: increase consecutive_errors (we take some precautions
993
   * to prevent the counter from overflowing since it is atomically
994
   * updated)
995
   */
996
0
  while (consecutive_errors < DNS_MAX_DSS_CONSECUTIVE_ERRORS &&
997
0
         !HA_ATOMIC_CAS(&ds->dss->consecutive_errors,
998
0
                        &consecutive_errors, consecutive_errors + 1) &&
999
0
         __ha_cpu_relax());
1000
1001
  /* here the ofs and the attached counter
1002
   * are kept unchanged
1003
   */
1004
1005
  /* Create a new appctx, We hope we can
1006
   * create from the release callback! */
1007
0
  ds->appctx = dns_session_create(ds, 1);
1008
0
  if (!ds->appctx) {
1009
0
    dns_session_free(ds);
1010
0
    HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
1011
0
    return;
1012
0
  }
1013
1014
0
  if (ds->nb_queries < DNS_STREAM_MAX_PIPELINED_REQ)
1015
0
    LIST_INSERT(&ds->dss->free_sess, &ds->list);
1016
1017
0
  HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
1018
0
}
1019
1020
/* DNS tcp session applet */
1021
static struct applet dns_session_applet = {
1022
  .obj_type = OBJ_TYPE_APPLET,
1023
  .flags = APPLET_FL_NEW_API,
1024
  .name = "<STRMDNS>", /* used for logging */
1025
  .fct = dns_session_io_handler,
1026
  .rcv_buf = appctx_raw_rcv_buf,
1027
  .snd_buf = appctx_raw_snd_buf,
1028
  .init = dns_session_init,
1029
  .release = dns_session_release,
1030
};
1031
1032
/*
1033
 * Function used to create an appctx for a DNS session
1034
 * It sets its context into appctx->svcctx.
1035
 * if <tempo> is set, then the session startup will be delayed by 1
1036
 * second
1037
 */
1038
static struct appctx *dns_session_create(struct dns_session *ds, int tempo)
1039
0
{
1040
0
  struct appctx *appctx;
1041
1042
0
  appctx = appctx_new_here(&dns_session_applet, NULL);
1043
0
  if (!appctx)
1044
0
    goto out_close;
1045
0
  appctx->svcctx = (void *)ds;
1046
1047
0
  if (!tempo) {
1048
0
    if (appctx_init(appctx) == -1) {
1049
0
      ha_alert("out of memory in dns_session_create().\n");
1050
0
      goto out_free_appctx;
1051
0
    }
1052
0
  }
1053
0
  else
1054
0
    appctx_schedule(appctx, tick_add(now_ms, MS_TO_TICKS(1000)));
1055
1056
0
  return appctx;
1057
1058
  /* Error unrolling */
1059
0
 out_free_appctx:
1060
0
  appctx_free_on_early_error(appctx);
1061
0
 out_close:
1062
0
  return NULL;
1063
0
}
1064
1065
/* Task processing expiration of unresponded queries, this one is supposed
1066
 * to be stuck on the same thread than the appctx handler
1067
 */
1068
static struct task *dns_process_query_exp(struct task *t, void *context, unsigned int state)
1069
0
{
1070
0
  struct dns_session *ds = (struct dns_session *)context;
1071
0
  struct dns_query *query, *queryb;
1072
1073
0
  t->expire = TICK_ETERNITY;
1074
1075
0
  list_for_each_entry_safe(query, queryb, &ds->queries, list) {
1076
0
    if (tick_is_expired(query->expire, now_ms)) {
1077
0
      eb32_delete(&query->qid);
1078
0
      LIST_DELETE(&query->list);
1079
0
      pool_free(dns_query_pool, query);
1080
0
      ds->onfly_queries--;
1081
0
    }
1082
0
    else {
1083
0
      t->expire = query->expire;
1084
0
      break;
1085
0
    }
1086
0
  }
1087
1088
0
  return t;
1089
0
}
1090
1091
/* Task processing expiration of idle sessions */
1092
static struct task *dns_process_idle_exp(struct task *t, void *context, unsigned int state)
1093
0
{
1094
0
  struct dns_stream_server *dss = (struct dns_stream_server *)context;
1095
0
  struct dns_session *ds, *dsb;
1096
0
  int target = 0;
1097
0
  int cur_active_conns;
1098
1099
0
  HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
1100
1101
1102
0
  cur_active_conns = dss->cur_conns - dss->idle_conns;
1103
0
  if (cur_active_conns > dss->max_active_conns)
1104
0
    dss->max_active_conns = cur_active_conns;
1105
1106
0
  target = (dss->max_active_conns - cur_active_conns) / 2;
1107
0
  list_for_each_entry_safe(ds, dsb, &dss->idle_sess, list) {
1108
0
    if (!stopping && !target)
1109
0
      break;
1110
1111
    /* remove conn to pending list to ensure it won't be reused */
1112
0
    LIST_DEL_INIT(&ds->list);
1113
1114
    /* force session shutdown */
1115
0
    ds->shutdown = 1;
1116
1117
    /* to be sure that the appctx won't miss shutdown */
1118
0
    __ha_barrier_store();
1119
1120
    /* wake appctx to perform the shutdown */
1121
0
    appctx_wakeup(ds->appctx);
1122
0
  }
1123
1124
  /* reset max to current active conns */
1125
0
  dss->max_active_conns = cur_active_conns;
1126
1127
0
  HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
1128
1129
0
  t->expire = tick_add(now_ms, 5000);
1130
1131
0
  return t;
1132
0
}
1133
1134
struct dns_session *dns_session_new(struct dns_stream_server *dss)
1135
0
{
1136
0
  struct dns_session *ds;
1137
1138
0
  if (dss->maxconn && (dss->maxconn <= dss->cur_conns))
1139
0
    return NULL;
1140
1141
0
  if (HA_ATOMIC_LOAD(&dss->consecutive_errors) >= DNS_MAX_DSS_CONSECUTIVE_ERRORS)
1142
0
    return NULL;
1143
1144
0
  ds = pool_zalloc(dns_session_pool);
1145
0
  if (!ds)
1146
0
    return NULL;
1147
1148
0
  ds->ofs = ~0;
1149
0
  ds->dss = dss;
1150
0
  LIST_INIT(&ds->list);
1151
0
  LIST_INIT(&ds->queries);
1152
0
  LIST_INIT(&ds->waiter);
1153
0
  ds->rx_msg.offset = ds->rx_msg.len = 0;
1154
0
  ds->rx_msg.area = NULL;
1155
0
  ds->tx_ring_area = NULL;
1156
0
  ds->task_exp = NULL;
1157
0
  ds->appctx = NULL;
1158
0
  ds->shutdown = 0;
1159
0
  ds->nb_queries = 0;
1160
0
  ds->query_ids = EB_ROOT_UNIQUE;
1161
0
  ds->rx_msg.area = pool_alloc(dns_msg_buf);
1162
0
  if (!ds->rx_msg.area)
1163
0
    goto error;
1164
1165
0
  ds->tx_ring_area = pool_alloc(dns_msg_buf);
1166
0
  if (!ds->tx_ring_area)
1167
0
    goto error;
1168
1169
0
  dns_ring_init(&ds->ring, ds->tx_ring_area, DNS_MSG_RING_MAX_SIZE);
1170
  /* never fail because it is the first watcher attached to the ring */
1171
0
  DISGUISE(dns_ring_attach(&ds->ring));
1172
1173
0
  if ((ds->task_exp = task_new_here()) == NULL)
1174
0
    goto error;
1175
1176
0
  ds->task_exp->process = dns_process_query_exp;
1177
0
  ds->task_exp->context = ds;
1178
1179
0
  ds->appctx = dns_session_create(ds, 0);
1180
0
  if (!ds->appctx)
1181
0
    goto error;
1182
1183
0
  dss->cur_conns++;
1184
1185
0
  return ds;
1186
1187
0
error:
1188
0
  task_destroy(ds->task_exp);
1189
0
  pool_free(dns_msg_buf, ds->rx_msg.area);
1190
0
  pool_free(dns_msg_buf, ds->tx_ring_area);
1191
1192
0
  pool_free(dns_session_pool, ds);
1193
1194
0
  return NULL;
1195
0
}
1196
1197
/*
1198
 * Task used to consume pending messages from nameserver ring
1199
 * and forward them to dns_session ring.
1200
 * Note: If no slot found a new dns_session is allocated
1201
 */
1202
static struct task *dns_process_req(struct task *t, void *context, unsigned int state)
1203
0
{
1204
0
  struct dns_nameserver *ns = (struct dns_nameserver *)context;
1205
0
  struct dns_stream_server *dss = ns->stream;
1206
0
  struct dns_ring *ring = dss->ring_req;
1207
0
  struct buffer *buf = &ring->buf;
1208
0
  uint64_t msg_len;
1209
0
  size_t len, cnt, ofs;
1210
0
  struct dns_session *ds, *ads;
1211
0
  HA_SPIN_LOCK(DNS_LOCK, &dss->lock);
1212
1213
0
  HA_RWLOCK_RDLOCK(DNS_LOCK, &ring->lock);
1214
1215
  /* explanation for the initialization below: it would be better to do
1216
   * this in the parsing function but this would occasionally result in
1217
   * dropped events because we'd take a reference on the oldest message
1218
   * and keep it while being scheduled. Thus instead let's take it the
1219
   * first time we enter here so that we have a chance to pass many
1220
   * existing messages before grabbing a reference to a location. This
1221
   * value cannot be produced after initialization.
1222
   */
1223
0
  if (unlikely(dss->ofs_req == ~0)) {
1224
0
    dss->ofs_req = b_peek_ofs(buf, 0);
1225
0
    HA_ATOMIC_INC(b_orig(buf) + dss->ofs_req);
1226
0
  }
1227
1228
  /* we were already there, adjust the offset to be relative to
1229
   * the buffer's head and remove us from the counter.
1230
   */
1231
0
  ofs = dss->ofs_req - b_head_ofs(buf);
1232
0
  if (dss->ofs_req < b_head_ofs(buf))
1233
0
    ofs += b_size(buf);
1234
1235
0
  BUG_ON(ofs >= buf->size);
1236
0
  HA_ATOMIC_DEC(b_peek(buf, ofs));
1237
1238
0
  while (ofs + 1 < b_data(buf)) {
1239
0
    struct ist myist;
1240
1241
0
    cnt = 1;
1242
0
    len = b_peek_varint(buf, ofs + cnt, &msg_len);
1243
0
    if (!len)
1244
0
      break;
1245
0
    cnt += len;
1246
0
    BUG_ON(msg_len + ofs + cnt + 1 > b_data(buf));
1247
0
    if (unlikely(msg_len > DNS_MAX_MSG_SIZE)) {
1248
      /* too large a message to ever fit, let's skip it */
1249
0
      ofs += cnt + msg_len;
1250
0
      continue;
1251
0
    }
1252
1253
0
    len = b_getblk(buf, dns_msg_trash, msg_len, ofs + cnt);
1254
1255
0
    myist = ist2(dns_msg_trash, len);
1256
1257
0
    ads = NULL;
1258
    /* try to push request into active sess with free slot */
1259
0
    if (!LIST_ISEMPTY(&dss->free_sess)) {
1260
0
      ds = LIST_NEXT(&dss->free_sess, struct dns_session *, list);
1261
1262
0
      if (dns_ring_write(&ds->ring, DNS_MAX_MSG_SIZE, NULL, 0, &myist, 1) > 0) {
1263
0
        ds->nb_queries++;
1264
0
        if (ds->nb_queries >= DNS_STREAM_MAX_PIPELINED_REQ)
1265
0
          LIST_DEL_INIT(&ds->list);
1266
0
        ads = ds;
1267
0
      }
1268
0
      else {
1269
        /* it means we were unable to put a request in this slot,
1270
         * it may be close to be full so we put it at the end
1271
         * of free conn list */
1272
0
        LIST_DEL_INIT(&ds->list);
1273
0
        LIST_APPEND(&dss->free_sess, &ds->list);
1274
0
      }
1275
0
    }
1276
1277
0
    if (!ads) {
1278
      /* try to push request into idle, this one should have enough free space */
1279
0
      if (!LIST_ISEMPTY(&dss->idle_sess)) {
1280
0
        ds = LIST_NEXT(&dss->idle_sess, struct dns_session *, list);
1281
1282
        /* ring is empty so this dns_ring_write should never fail */
1283
0
        dns_ring_write(&ds->ring, DNS_MAX_MSG_SIZE, NULL, 0, &myist, 1);
1284
0
        ds->nb_queries++;
1285
0
        LIST_DEL_INIT(&ds->list);
1286
1287
0
        ds->dss->idle_conns--;
1288
1289
        /* we may have to update the max_active_conns */
1290
0
        if (ds->dss->max_active_conns < ds->dss->cur_conns - ds->dss->idle_conns)
1291
0
          ds->dss->max_active_conns = ds->dss->cur_conns - ds->dss->idle_conns;
1292
1293
        /* since we may unable to find a free list to handle
1294
         * this request, this request may be large and fill
1295
         * the ring buffer so we prefer to put at the end of free
1296
         * list. */
1297
0
        LIST_APPEND(&dss->free_sess, &ds->list);
1298
0
        ads = ds;
1299
0
      }
1300
0
    }
1301
1302
    /* we didn't find a session available with large enough room */
1303
0
    if (!ads) {
1304
      /* allocate a new session */
1305
0
      ads = dns_session_new(dss);
1306
0
      if (ads) {
1307
        /* ring is empty so this dns_ring_write should never fail */
1308
0
        dns_ring_write(&ads->ring, DNS_MAX_MSG_SIZE, NULL, 0, &myist, 1);
1309
0
        ads->nb_queries++;
1310
0
        LIST_INSERT(&dss->free_sess, &ads->list);
1311
0
      }
1312
0
      else
1313
0
        ns->counters->snd_error++;
1314
0
    }
1315
1316
0
    if (ads)
1317
0
      ns->counters->sent++;
1318
1319
0
    ofs += cnt + len;
1320
0
  }
1321
1322
0
  HA_ATOMIC_INC(b_peek(buf, ofs));
1323
0
  dss->ofs_req = b_peek_ofs(buf, ofs);
1324
0
  HA_RWLOCK_RDUNLOCK(DNS_LOCK, &ring->lock);
1325
1326
1327
0
  HA_SPIN_UNLOCK(DNS_LOCK, &dss->lock);
1328
0
  return t;
1329
0
}
1330
1331
/*
1332
 * Task used to consume response
1333
 * Note: upper layer callback is called
1334
 */
1335
static struct task *dns_process_rsp(struct task *t, void *context, unsigned int state)
1336
0
{
1337
0
  struct dns_nameserver *ns = (struct dns_nameserver *)context;
1338
1339
0
  ns->process_responses(ns, DNS_SERVER_STREAM);
1340
1341
0
  return t;
1342
0
}
1343
1344
/* Function used to initialize an TCP nameserver */
1345
int dns_stream_init(struct dns_nameserver *ns, struct server *srv)
1346
0
{
1347
0
  struct dns_stream_server *dss = NULL;
1348
1349
0
  dss = calloc(1, sizeof(*dss));
1350
0
  if (!dss) {
1351
0
    ha_alert("memory allocation error initializing dns tcp server '%s'.\n", srv->id);
1352
0
    goto out;
1353
0
  }
1354
1355
0
  dss->srv = srv;
1356
0
  dss->maxconn = srv->maxconn;
1357
1358
0
  dss->ofs_req = ~0; /* init ring offset */
1359
0
  dss->ring_req = dns_ring_new(2*DNS_MSG_RING_MAX_SIZE);
1360
0
  if (!dss->ring_req) {
1361
0
    ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
1362
0
    goto out;
1363
0
  }
1364
  /* Create the task associated to the resolver target handling conns */
1365
0
  if ((dss->task_req = task_new_anywhere()) == NULL) {
1366
0
    ha_alert("memory allocation error initializing req task for dns tcp server '%s'.\n", srv->id);
1367
0
    goto out;
1368
0
  }
1369
1370
  /* Update task's parameters */
1371
0
  dss->task_req->process = dns_process_req;
1372
0
  dss->task_req->context = ns;
1373
1374
  /* attach the task as reader */
1375
0
  if (!dns_ring_attach(dss->ring_req)) {
1376
    /* mark server attached to the ring */
1377
0
    ha_alert("server '%s': too many watchers for ring. this should never happen.\n", srv->id);
1378
0
    goto out;
1379
0
  }
1380
1381
  /* Create the task associated to the resolver target handling conns */
1382
0
  if ((dss->task_rsp = task_new_anywhere()) == NULL) {
1383
0
    ha_alert("memory allocation error initializing rsp task for dns tcp server '%s'.\n", srv->id);
1384
0
    goto out;
1385
0
  }
1386
1387
  /* Update task's parameters */
1388
0
  dss->task_rsp->process = dns_process_rsp;
1389
0
  dss->task_rsp->context = ns;
1390
1391
  /* Create the task associated to the resolver target handling conns */
1392
0
  if ((dss->task_idle = task_new_anywhere()) == NULL) {
1393
0
    ha_alert("memory allocation error initializing idle task for dns tcp server '%s'.\n", srv->id);
1394
0
    goto out;
1395
0
  }
1396
1397
  /* Update task's parameters */
1398
0
  dss->task_idle->process = dns_process_idle_exp;
1399
0
  dss->task_idle->context = dss;
1400
0
  dss->task_idle->expire = tick_add(now_ms, 5000);
1401
1402
0
  LIST_INIT(&dss->free_sess);
1403
0
  LIST_INIT(&dss->idle_sess);
1404
0
  LIST_INIT(&dss->wait_sess);
1405
0
  HA_SPIN_INIT(&dss->lock);
1406
0
  ns->stream = dss;
1407
0
  return 0;
1408
0
out:
1409
0
  if (dss && dss->task_rsp)
1410
0
    task_destroy(dss->task_rsp);
1411
0
  if (dss && dss->task_req)
1412
0
    task_destroy(dss->task_req);
1413
0
  if (dss && dss->ring_req)
1414
0
    dns_ring_free(dss->ring_req);
1415
1416
0
  free(dss);
1417
0
  return -1;
1418
0
}
1419
1420
/* Releases transport allocations owned by <ns>. */
1421
void dns_nameserver_deinit(struct dns_nameserver *ns)
1422
0
{
1423
0
  if (ns->dgram) {
1424
0
    dns_ring_free(ns->dgram->ring_req);
1425
0
    free(ns->dgram);
1426
0
    ns->dgram = NULL;
1427
0
  }
1428
1429
0
  if (ns->stream) {
1430
0
    dns_ring_free(ns->stream->ring_req);
1431
0
    task_destroy(ns->stream->task_req);
1432
0
    task_destroy(ns->stream->task_rsp);
1433
0
    task_destroy(ns->stream->task_idle);
1434
0
    free(ns->stream);
1435
0
    ns->stream = NULL;
1436
0
  }
1437
0
}
1438
1439
int init_dns_buffers()
1440
0
{
1441
0
  dns_msg_trash = malloc(DNS_MAX_MSG_SIZE);
1442
0
  if (!dns_msg_trash)
1443
0
    return 0;
1444
1445
0
  return 1;
1446
0
}
1447
1448
void deinit_dns_buffers()
1449
0
{
1450
  ha_free(&dns_msg_trash);
1451
0
}
1452
1453
REGISTER_PER_THREAD_ALLOC(init_dns_buffers);
1454
REGISTER_PER_THREAD_FREE(deinit_dns_buffers);