• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

c-ares / c-ares / #1282

19 Jul 2024 07:40PM UTC coverage: 80.577% (-0.09%) from 80.665%
#1282

push

travis-ci

bradh352
UDP write may fail indicating host isn't reachable (#821)

UDP is connectionless, but systems use ICMP unreachable messages to
indicate there is no ability to reach the host or port, which can result
in a `send()` returning an error like `ECONNREFUSED`. We need to handle
non-retryable codes like that to treat it as a connection failure so we
requeue any queries on that connection to another connection/server
immediately. Otherwise what happens is we just wait on the timeout to
expire which can greatly increase the time required to get a definitive
message.

This also adds a test case to verify the behavior.

Fixes #819
Fix By: Brad Houes (@bradh352)

0 of 9 new or added lines in 1 file covered. (0.0%)

2 existing lines in 2 files now uncovered.

7737 of 9602 relevant lines covered (80.58%)

1933.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.49
/src/lib/ares_process.c
1
/* MIT License
2
 *
3
 * Copyright (c) 1998 Massachusetts Institute of Technology
4
 * Copyright (c) 2010 Daniel Stenberg
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice (including the next
14
 * paragraph) shall be included in all copies or substantial portions of the
15
 * Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
 * SOFTWARE.
24
 *
25
 * SPDX-License-Identifier: MIT
26
 */
27

28
#include "ares_setup.h"
29

30

31
#ifdef HAVE_STRINGS_H
32
#  include <strings.h>
33
#endif
34
#ifdef HAVE_SYS_IOCTL_H
35
#  include <sys/ioctl.h>
36
#endif
37
#ifdef NETWARE
38
#  include <sys/filio.h>
39
#endif
40
#ifdef HAVE_STDINT_H
41
#  include <stdint.h>
42
#endif
43

44
#include <assert.h>
45
#include <fcntl.h>
46
#include <limits.h>
47

48
#include "ares.h"
49
#include "ares_private.h"
50
#include "ares_nameser.h"
51
#include "ares_dns.h"
52

53
static ares_bool_t try_again(int errnum);
54
static void        write_tcp_data(ares_channel_t *channel, fd_set *write_fds,
55
                                  ares_socket_t write_fd);
56
static void        read_packets(ares_channel_t *channel, fd_set *read_fds,
57
                                ares_socket_t read_fd, struct timeval *now);
58
static void process_timeouts(ares_channel_t *channel, struct timeval *now);
59
static ares_status_t process_answer(ares_channel_t      *channel,
60
                                    const unsigned char *abuf, size_t alen,
61
                                    struct server_connection *conn,
62
                                    ares_bool_t tcp, struct timeval *now);
63
static void          handle_conn_error(struct server_connection *conn,
64
                                       ares_bool_t               critical_failure,
65
                                       ares_status_t             failure_status);
66

67
static ares_bool_t   same_questions(const ares_dns_record_t *qrec,
68
                                    const ares_dns_record_t *arec);
69
static ares_bool_t   same_address(const struct sockaddr  *sa,
70
                                  const struct ares_addr *aa);
71
static void end_query(const ares_channel_t *channel, struct query *query,
72
                      ares_status_t status, const unsigned char *abuf,
73
                      size_t alen);
74

75
static void ares__query_disassociate_from_conn(struct query *query)
473✔
76
{
77
  /* If its not part of a connection, it can't be tracked for timeouts either */
78
  ares__slist_node_destroy(query->node_queries_by_timeout);
473✔
79
  ares__llist_node_destroy(query->node_queries_to_conn);
473✔
80
  query->node_queries_by_timeout = NULL;
473✔
81
  query->node_queries_to_conn    = NULL;
473✔
82
  query->conn                    = NULL;
473✔
83
}
473✔
84

85
static void server_increment_failures(struct server_state *server)
78✔
86
{
87
  ares__slist_node_t   *node;
88
  const ares_channel_t *channel = server->channel;
78✔
89

90
  node = ares__slist_node_find(channel->servers, server);
78✔
91
  if (node == NULL) {
78✔
92
    return;
×
93
  }
94
  server->consec_failures++;
78✔
95
  ares__slist_node_reinsert(node);
78✔
96
}
97

98
static void server_set_good(struct server_state *server)
359✔
99
{
100
  ares__slist_node_t   *node;
101
  const ares_channel_t *channel = server->channel;
359✔
102

103
  if (!server->consec_failures) {
359✔
104
    return;
345✔
105
  }
106

107
  node = ares__slist_node_find(channel->servers, server);
14✔
108
  if (node == NULL) {
14✔
109
    return;
×
110
  }
111

112
  server->consec_failures = 0;
14✔
113
  ares__slist_node_reinsert(node);
14✔
114
}
115

116
/* return true if now is exactly check time or later */
117
ares_bool_t ares__timedout(const struct timeval *now,
642✔
118
                           const struct timeval *check)
119
{
120
  ares_int64_t secs = ((ares_int64_t)now->tv_sec - (ares_int64_t)check->tv_sec);
642✔
121

122
  if (secs > 0) {
642✔
123
    return ARES_TRUE; /* yes, timed out */
×
124
  }
125
  if (secs < 0) {
642✔
126
    return ARES_FALSE; /* nope, not timed out */
292✔
127
  }
128

129
  /* if the full seconds were identical, check the sub second parts */
130
  return ((ares_int64_t)now->tv_usec - (ares_int64_t)check->tv_usec) >= 0
350✔
131
           ? ARES_TRUE
132
           : ARES_FALSE;
350✔
133
}
134

135
/* add the specific number of milliseconds to the time in the first argument */
136
static void timeadd(struct timeval *now, size_t millisecs)
453✔
137
{
138
  now->tv_sec  += (time_t)millisecs / 1000;
453✔
139
  now->tv_usec += (time_t)((millisecs % 1000) * 1000);
453✔
140

141
  if (now->tv_usec >= 1000000) {
453✔
142
    ++(now->tv_sec);
200✔
143
    now->tv_usec -= 1000000;
200✔
144
  }
145
}
453✔
146

147
/*
148
 * generic process function
149
 */
150
static void processfds(ares_channel_t *channel, fd_set *read_fds,
825✔
151
                       ares_socket_t read_fd, fd_set *write_fds,
152
                       ares_socket_t write_fd)
153
{
154
  struct timeval now;
155

156
  if (channel == NULL) {
825✔
157
    return;
×
158
  }
159

160
  ares__channel_lock(channel);
825✔
161

162
  now = ares__tvnow();
825✔
163
  read_packets(channel, read_fds, read_fd, &now);
825✔
164
  process_timeouts(channel, &now);
825✔
165
  /* Write last as the other 2 operations might have triggered writes */
166
  write_tcp_data(channel, write_fds, write_fd);
825✔
167

168
  /* See if any connections should be cleaned up */
169
  ares__check_cleanup_conns(channel);
825✔
170
  ares__channel_unlock(channel);
825✔
171
}
172

173
/* Something interesting happened on the wire, or there was a timeout.
174
 * See what's up and respond accordingly.
175
 */
176
void ares_process(ares_channel_t *channel, fd_set *read_fds, fd_set *write_fds)
825✔
177
{
178
  processfds(channel, read_fds, ARES_SOCKET_BAD, write_fds, ARES_SOCKET_BAD);
825✔
179
}
825✔
180

181
/* Something interesting happened on the wire, or there was a timeout.
182
 * See what's up and respond accordingly.
183
 */
184
void ares_process_fd(ares_channel_t *channel,
×
185
                     ares_socket_t   read_fd, /* use ARES_SOCKET_BAD or valid
186
                                                 file descriptors */
187
                     ares_socket_t   write_fd)
188
{
189
  processfds(channel, NULL, read_fd, NULL, write_fd);
×
190
}
191

192
/* Return 1 if the specified error number describes a readiness error, or 0
193
 * otherwise. This is mostly for HP-UX, which could return EAGAIN or
194
 * EWOULDBLOCK. See this man page
195
 *
196
 * http://devrsrc1.external.hp.com/STKS/cgi-bin/man2html?
197
 *     manpage=/usr/share/man/man2.Z/send.2
198
 */
199
static ares_bool_t try_again(int errnum)
241✔
200
{
201
#if !defined EWOULDBLOCK && !defined EAGAIN
202
#  error "Neither EWOULDBLOCK nor EAGAIN defined"
203
#endif
204

205
#ifdef EWOULDBLOCK
206
  if (errnum == EWOULDBLOCK) {
241✔
207
    return ARES_TRUE;
241✔
208
  }
209
#endif
210

211
#if defined EAGAIN && EAGAIN != EWOULDBLOCK
212
  if (errnum == EAGAIN) {
213
    return ARES_TRUE;
214
  }
215
#endif
216

217
  return ARES_FALSE;
×
218
}
219

220
/* If any TCP sockets select true for writing, write out queued data
221
 * we have for them.
222
 */
223
static void write_tcp_data(ares_channel_t *channel, fd_set *write_fds,
825✔
224
                           ares_socket_t write_fd)
225
{
226
  ares__slist_node_t *node;
227

228
  if (!write_fds && (write_fd == ARES_SOCKET_BAD)) {
825✔
229
    /* no possible action */
230
    return;
×
231
  }
232

233
  for (node = ares__slist_node_first(channel->servers); node != NULL;
1,920✔
234
       node = ares__slist_node_next(node)) {
1,095✔
235
    struct server_state *server = ares__slist_node_val(node);
1,095✔
236
    const unsigned char *data;
237
    size_t               data_len;
238
    ares_ssize_t         count;
239

240
    /* Make sure server has data to send and is selected in write_fds or
241
       write_fd. */
242
    if (ares__buf_len(server->tcp_send) == 0 || server->tcp_conn == NULL) {
1,095✔
243
      continue;
972✔
244
    }
245

246
    if (write_fds) {
178✔
247
      if (!FD_ISSET(server->tcp_conn->fd, write_fds)) {
178✔
248
        continue;
55✔
249
      }
250
    } else {
251
      if (server->tcp_conn->fd != write_fd) {
×
252
        continue;
×
253
      }
254
    }
255

256
    if (write_fds) {
123✔
257
      /* If there's an error and we close this socket, then open
258
       * another with the same fd to talk to another server, then we
259
       * don't want to think that it was the new socket that was
260
       * ready. This is not disastrous, but is likely to result in
261
       * extra system calls and confusion. */
262
      FD_CLR(server->tcp_conn->fd, write_fds);
123✔
263
    }
264

265
    data  = ares__buf_peek(server->tcp_send, &data_len);
123✔
266
    count = ares__socket_write(channel, server->tcp_conn->fd, data, data_len);
123✔
267
    if (count <= 0) {
123✔
268
      if (!try_again(SOCKERRNO)) {
×
269
        handle_conn_error(server->tcp_conn, ARES_TRUE, ARES_ECONNREFUSED);
×
270
      }
271
      continue;
×
272
    }
273

274
    /* Strip data written from the buffer */
275
    ares__buf_consume(server->tcp_send, (size_t)count);
123✔
276

277
    /* Notify state callback all data is written */
278
    if (ares__buf_len(server->tcp_send) == 0) {
123✔
279
      SOCK_STATE_CALLBACK(channel, server->tcp_conn->fd, 1, 0);
123✔
280
    }
281
  }
282
}
283

284
/* If any TCP socket selects true for reading, read some data,
285
 * allocate a buffer if we finish reading the length word, and process
286
 * a packet if we finish reading one.
287
 */
288
static void read_tcp_data(ares_channel_t           *channel,
121✔
289
                          struct server_connection *conn, struct timeval *now)
290
{
291
  ares_ssize_t         count;
292
  struct server_state *server = conn->server;
121✔
293

294
  /* Fetch buffer to store data we are reading */
295
  size_t               ptr_len = 65535;
121✔
296
  unsigned char       *ptr;
297

298
  ptr = ares__buf_append_start(server->tcp_parser, &ptr_len);
121✔
299

300
  if (ptr == NULL) {
121✔
301
    handle_conn_error(conn, ARES_FALSE /* not critical to connection */, ARES_SUCCESS);
×
302
    return; /* bail out on malloc failure. TODO: make this
7✔
303
               function return error codes */
304
  }
305

306
  /* Read from socket */
307
  count = ares__socket_recv(channel, conn->fd, ptr, ptr_len);
121✔
308
  if (count <= 0) {
121✔
309
    ares__buf_append_finish(server->tcp_parser, 0);
1✔
310
    if (!(count == -1 && try_again(SOCKERRNO))) {
1✔
311
      handle_conn_error(conn, ARES_TRUE, ARES_ECONNREFUSED);
1✔
312
    }
313
    return;
1✔
314
  }
315

316
  /* Record amount of data read */
317
  ares__buf_append_finish(server->tcp_parser, (size_t)count);
120✔
318

319
  /* Process all queued answers */
320
  while (1) {
151✔
321
    unsigned short       dns_len  = 0;
271✔
322
    const unsigned char *data     = NULL;
271✔
323
    size_t               data_len = 0;
271✔
324
    ares_status_t        status;
325

326
    /* Tag so we can roll back */
327
    ares__buf_tag(server->tcp_parser);
271✔
328

329
    /* Read length indicator */
330
    if (ares__buf_fetch_be16(server->tcp_parser, &dns_len) != ARES_SUCCESS) {
271✔
331
      ares__buf_tag_rollback(server->tcp_parser);
114✔
332
      break;
114✔
333
    }
334

335
    /* Not enough data for a full response yet */
336
    if (ares__buf_consume(server->tcp_parser, dns_len) != ARES_SUCCESS) {
157✔
337
      ares__buf_tag_rollback(server->tcp_parser);
×
338
      break;
×
339
    }
340

341
    /* Can't fail except for misuse */
342
    data = ares__buf_tag_fetch(server->tcp_parser, &data_len);
157✔
343
    if (data == NULL) {
157✔
344
      ares__buf_tag_clear(server->tcp_parser);
×
345
      break;
×
346
    }
347

348
    /* Strip off 2 bytes length */
349
    data     += 2;
157✔
350
    data_len -= 2;
157✔
351

352
    /* We finished reading this answer; process it */
353
    status = process_answer(channel, data, data_len, conn, ARES_TRUE, now);
157✔
354
    if (status != ARES_SUCCESS) {
157✔
355
      handle_conn_error(conn, ARES_TRUE, status);
6✔
356
      return;
6✔
357
    }
358

359
    /* Since we processed the answer, clear the tag so space can be reclaimed */
360
    ares__buf_tag_clear(server->tcp_parser);
151✔
361
  }
362
}
363

364
static int socket_list_append(ares_socket_t **socketlist, ares_socket_t fd,
876✔
365
                              size_t *alloc_cnt, size_t *num)
366
{
367
  if (*num >= *alloc_cnt) {
876✔
368
    /* Grow by powers of 2 */
369
    size_t         new_alloc = (*alloc_cnt) << 1;
×
370
    ares_socket_t *new_list =
371
      ares_realloc(socketlist, new_alloc * sizeof(*new_list));
×
372
    if (new_list == NULL) {
×
373
      return 0;
×
374
    }
375
    *alloc_cnt  = new_alloc;
×
376
    *socketlist = new_list;
×
377
  }
378

379
  (*socketlist)[(*num)++] = fd;
876✔
380
  return 1;
876✔
381
}
382

383
static ares_socket_t *channel_socket_list(const ares_channel_t *channel,
825✔
384
                                          size_t               *num)
385
{
386
  size_t              alloc_cnt = 1 << 4;
825✔
387
  ares_socket_t      *out       = ares_malloc(alloc_cnt * sizeof(*out));
825✔
388
  ares__slist_node_t *snode;
389

390
  *num = 0;
825✔
391

392
  if (out == NULL) {
825✔
393
    return NULL;
×
394
  }
395

396
  for (snode = ares__slist_node_first(channel->servers); snode != NULL;
1,920✔
397
       snode = ares__slist_node_next(snode)) {
1,095✔
398
    struct server_state *server = ares__slist_node_val(snode);
1,095✔
399
    ares__llist_node_t  *node;
400

401
    for (node = ares__llist_node_first(server->connections); node != NULL;
1,971✔
402
         node = ares__llist_node_next(node)) {
876✔
403
      const struct server_connection *conn = ares__llist_node_val(node);
876✔
404

405
      if (conn->fd == ARES_SOCKET_BAD) {
876✔
406
        continue;
×
407
      }
408

409
      if (!socket_list_append(&out, conn->fd, &alloc_cnt, num)) {
876✔
410
        goto fail;
×
411
      }
412
    }
413
  }
414

415
  return out;
825✔
416

417
fail:
×
418
  ares_free(out);
×
419
  *num = 0;
×
420
  return NULL;
×
421
}
422

423
/* If any UDP sockets select true for reading, process them. */
424
static void read_udp_packets_fd(ares_channel_t           *channel,
261✔
425
                                struct server_connection *conn,
426
                                struct timeval           *now)
427
{
428
  ares_ssize_t  read_len;
429
  unsigned char buf[MAXENDSSZ + 1];
430

431
#ifdef HAVE_RECVFROM
432
  ares_socklen_t fromlen;
433

434
  union {
435
    struct sockaddr     sa;
436
    struct sockaddr_in  sa4;
437
    struct sockaddr_in6 sa6;
438
  } from;
439

440
  memset(&from, 0, sizeof(from));
261✔
441
#endif
442

443
  /* To reduce event loop overhead, read and process as many
444
   * packets as we can. */
445
  do {
446
    if (conn->fd == ARES_SOCKET_BAD) {
502✔
447
      read_len = -1;
×
448
    } else {
449
      if (conn->server->addr.family == AF_INET) {
502✔
450
        fromlen = sizeof(from.sa4);
502✔
451
      } else {
452
        fromlen = sizeof(from.sa6);
×
453
      }
454
      read_len = ares__socket_recvfrom(channel, conn->fd, (void *)buf,
502✔
455
                                       sizeof(buf), 0, &from.sa, &fromlen);
456
    }
457

458
    if (read_len == 0) {
502✔
459
      /* UDP is connectionless, so result code of 0 is a 0-length UDP
460
       * packet, and not an indication the connection is closed like on
461
       * tcp */
462
      continue;
×
463
    } else if (read_len < 0) {
502✔
464
      if (try_again(SOCKERRNO)) {
241✔
465
        break;
241✔
466
      }
467

468
      handle_conn_error(conn, ARES_TRUE, ARES_ECONNREFUSED);
×
469
      return;
×
470
#ifdef HAVE_RECVFROM
471
    } else if (!same_address(&from.sa, &conn->server->addr)) {
261✔
472
      /* The address the response comes from does not match the address we
473
       * sent the request to. Someone may be attempting to perform a cache
474
       * poisoning attack. */
475
      continue;
×
476
#endif
477

478
    } else {
479
      process_answer(channel, buf, (size_t)read_len, conn, ARES_FALSE, now);
261✔
480
    }
481

482
    /* Try to read again only if *we* set up the socket, otherwise it may be
483
     * a blocking socket and would cause recvfrom to hang. */
484
  } while (read_len >= 0 && channel->sock_funcs == NULL);
261✔
485
}
486

487
static void read_packets(ares_channel_t *channel, fd_set *read_fds,
825✔
488
                         ares_socket_t read_fd, struct timeval *now)
489
{
490
  size_t                    i;
491
  ares_socket_t            *socketlist  = NULL;
825✔
492
  size_t                    num_sockets = 0;
825✔
493
  struct server_connection *conn        = NULL;
825✔
494
  ares__llist_node_t       *node        = NULL;
825✔
495

496
  if (!read_fds && (read_fd == ARES_SOCKET_BAD)) {
825✔
497
    /* no possible action */
498
    return;
×
499
  }
500

501
  /* Single socket specified */
502
  if (!read_fds) {
825✔
503
    node = ares__htable_asvp_get_direct(channel->connnode_by_socket, read_fd);
×
504
    if (node == NULL) {
×
505
      return;
×
506
    }
507

508
    conn = ares__llist_node_val(node);
×
509

510
    if (conn->is_tcp) {
×
511
      read_tcp_data(channel, conn, now);
×
512
    } else {
513
      read_udp_packets_fd(channel, conn, now);
×
514
    }
515

516
    return;
×
517
  }
518

519
  /* There is no good way to iterate across an fd_set, instead we must pull a
520
   * list of all known fds, and iterate across that checking against the fd_set.
521
   */
522
  socketlist = channel_socket_list(channel, &num_sockets);
825✔
523

524
  for (i = 0; i < num_sockets; i++) {
1,701✔
525
    if (!FD_ISSET(socketlist[i], read_fds)) {
876✔
526
      continue;
494✔
527
    }
528

529
    /* If there's an error and we close this socket, then open
530
     * another with the same fd to talk to another server, then we
531
     * don't want to think that it was the new socket that was
532
     * ready. This is not disastrous, but is likely to result in
533
     * extra system calls and confusion. */
534
    FD_CLR(socketlist[i], read_fds);
382✔
535

536
    node =
537
      ares__htable_asvp_get_direct(channel->connnode_by_socket, socketlist[i]);
382✔
538
    if (node == NULL) {
382✔
539
      return;
×
540
    }
541

542
    conn = ares__llist_node_val(node);
382✔
543

544
    if (conn->is_tcp) {
382✔
545
      read_tcp_data(channel, conn, now);
121✔
546
    } else {
547
      read_udp_packets_fd(channel, conn, now);
261✔
548
    }
549
  }
550

551
  ares_free(socketlist);
825✔
552
}
553

554
/* If any queries have timed out, note the timeout and move them on. */
555
static void process_timeouts(ares_channel_t *channel, struct timeval *now)
825✔
556
{
557
  ares__slist_node_t *node;
558

559
  /* Just keep popping off the first as this list will re-sort as things come
560
   * and go.  We don't want to try to rely on 'next' as some operation might
561
   * cause a cleanup of that pointer and would become invalid */
562
  while ((node = ares__slist_node_first(channel->queries_by_timeout)) != NULL) {
838✔
563
    struct query             *query = ares__slist_node_val(node);
642✔
564
    struct server_connection *conn;
565

566
    /* Since this is sorted, as soon as we hit a query that isn't timed out,
567
     * break */
568
    if (!ares__timedout(now, &query->timeout)) {
642✔
569
      break;
629✔
570
    }
571

572
    query->timeouts++;
13✔
573

574
    conn = query->conn;
13✔
575
    server_increment_failures(conn->server);
13✔
576
    ares__requeue_query(query, now, ARES_ETIMEOUT);
13✔
577
  }
578
}
825✔
579

580
static ares_status_t rewrite_without_edns(ares_dns_record_t *qdnsrec,
4✔
581
                                          struct query      *query)
582
{
583
  ares_status_t  status;
584
  size_t         i;
585
  ares_bool_t    found_opt_rr = ARES_FALSE;
4✔
586
  unsigned char *msg          = NULL;
4✔
587
  size_t         msglen       = 0;
4✔
588

589
  /* Find and remove the OPT RR record */
590
  for (i = 0; i < ares_dns_record_rr_cnt(qdnsrec, ARES_SECTION_ADDITIONAL);
4✔
591
       i++) {
×
592
    const ares_dns_rr_t *rr;
593
    rr = ares_dns_record_rr_get(qdnsrec, ARES_SECTION_ADDITIONAL, i);
4✔
594
    if (ares_dns_rr_get_type(rr) == ARES_REC_TYPE_OPT) {
4✔
595
      ares_dns_record_rr_del(qdnsrec, ARES_SECTION_ADDITIONAL, i);
4✔
596
      found_opt_rr = ARES_TRUE;
4✔
597
      break;
4✔
598
    }
599
  }
600

601
  if (!found_opt_rr) {
4✔
602
    status = ARES_EFORMERR;
×
603
    goto done;
×
604
  }
605

606
  /* Rewrite the DNS message */
607
  status = ares_dns_write(qdnsrec, &msg, &msglen);
4✔
608
  if (status != ARES_SUCCESS) {
4✔
609
    goto done;
×
610
  }
611

612
  ares_free(query->qbuf);
4✔
613
  query->qbuf = msg;
4✔
614
  query->qlen = msglen;
4✔
615

616
done:
4✔
617
  return status;
4✔
618
}
619

620
/* Handle an answer from a server. This must NEVER cleanup the
621
 * server connection! Return something other than ARES_SUCCESS to cause
622
 * the connection to be terminated after this call. */
623
static ares_status_t process_answer(ares_channel_t      *channel,
418✔
624
                                    const unsigned char *abuf, size_t alen,
625
                                    struct server_connection *conn,
626
                                    ares_bool_t tcp, struct timeval *now)
627
{
628
  struct query        *query;
629
  /* Cache these as once ares__send_query() gets called, it may end up
630
   * invalidating the connection all-together */
631
  struct server_state *server  = conn->server;
418✔
632
  ares_dns_record_t   *rdnsrec = NULL;
418✔
633
  ares_dns_record_t   *qdnsrec = NULL;
418✔
634
  ares_status_t        status;
635

636
  /* Parse the response */
637
  status = ares_dns_parse(abuf, alen, 0, &rdnsrec);
418✔
638
  if (status != ARES_SUCCESS) {
418✔
639
    /* Malformations are never accepted */
640
    status = ARES_EBADRESP;
6✔
641
    goto cleanup;
6✔
642
  }
643

644
  /* Find the query corresponding to this packet. The queries are
645
   * hashed/bucketed by query id, so this lookup should be quick.
646
   */
647
  query = ares__htable_szvp_get_direct(channel->queries_by_qid,
412✔
648
                                       ares_dns_record_get_id(rdnsrec));
412✔
649
  if (!query) {
412✔
650
    /* We may have stopped listening for this query, that's ok */
651
    status = ARES_SUCCESS;
1✔
652
    goto cleanup;
1✔
653
  }
654

655
  /* Parse the question we sent as we use it to compare */
656
  status = ares_dns_parse(query->qbuf, query->qlen, 0, &qdnsrec);
411✔
657
  if (status != ARES_SUCCESS) {
411✔
658
    end_query(channel, query, status, NULL, 0);
×
659
    goto cleanup;
×
660
  }
661

662
  /* Both the query id and the questions must be the same. We will drop any
663
   * replies that aren't for the same query as this is considered invalid. */
664
  if (!same_questions(qdnsrec, rdnsrec)) {
411✔
665
    /* Possible qid conflict due to delayed response, that's ok */
666
    status = ARES_SUCCESS;
×
667
    goto cleanup;
×
668
  }
669

670
  /* At this point we know we've received an answer for this query, so we should
671
   * remove it from the connection's queue so we can possibly invalidate the
672
   * connection. Delay cleaning up the connection though as we may enqueue
673
   * something new.  */
674
  ares__llist_node_destroy(query->node_queries_to_conn);
411✔
675
  query->node_queries_to_conn = NULL;
411✔
676

677
  /* If we use EDNS and server answers with FORMERR without an OPT RR, the
678
   * protocol extension is not understood by the responder. We must retry the
679
   * query without EDNS enabled. */
680
  if (ares_dns_record_get_rcode(rdnsrec) == ARES_RCODE_FORMERR &&
417✔
681
      ares_dns_has_opt_rr(qdnsrec) && !ares_dns_has_opt_rr(rdnsrec)) {
10✔
682
    status = rewrite_without_edns(qdnsrec, query);
4✔
683
    if (status != ARES_SUCCESS) {
4✔
684
      end_query(channel, query, status, NULL, 0);
×
685
      goto cleanup;
×
686
    }
687

688
    ares__send_query(query, now);
4✔
689
    status = ARES_SUCCESS;
4✔
690
    goto cleanup;
4✔
691
  }
692

693
  /* If we got a truncated UDP packet and are not ignoring truncation,
694
   * don't accept the packet, and switch the query to TCP if we hadn't
695
   * done so already.
696
   */
697
  if (ares_dns_record_get_flags(rdnsrec) & ARES_FLAG_TC && !tcp &&
407✔
698
      !(channel->flags & ARES_FLAG_IGNTC)) {
2✔
699
    query->using_tcp = ARES_TRUE;
2✔
700
    ares__send_query(query, now);
2✔
701
    status = ARES_SUCCESS; /* Switched to TCP is ok */
2✔
702
    goto cleanup;
2✔
703
  }
704

705
  /* If we aren't passing through all error packets, discard packets
706
   * with SERVFAIL, NOTIMP, or REFUSED response codes.
707
   */
708
  if (!(channel->flags & ARES_FLAG_NOCHECKRESP)) {
405✔
709
    ares_dns_rcode_t rcode = ares_dns_record_get_rcode(rdnsrec);
393✔
710
    if (rcode == ARES_RCODE_SERVFAIL || rcode == ARES_RCODE_NOTIMP ||
393✔
711
        rcode == ARES_RCODE_REFUSED) {
712
      switch (rcode) {
46✔
713
        case ARES_RCODE_SERVFAIL:
18✔
714
          status = ARES_ESERVFAIL;
18✔
715
          break;
18✔
716
        case ARES_RCODE_NOTIMP:
22✔
717
          status = ARES_ENOTIMP;
22✔
718
          break;
22✔
719
        case ARES_RCODE_REFUSED:
6✔
720
          status = ARES_EREFUSED;
6✔
721
          break;
6✔
722
        default:
×
723
          break;
×
724
      }
725
      server_increment_failures(server);
46✔
726
      ares__requeue_query(query, now, status);
46✔
727

728
      /* Should any of these cause a connection termination?
729
       * Maybe SERVER_FAILURE? */
730
      status = ARES_SUCCESS;
46✔
731
      goto cleanup;
46✔
732
    }
733
  }
734

735
  /* If cache insertion was successful, it took ownership.  We ignore
736
   * other cache insertion failures. */
737
  if (ares_qcache_insert(channel, now, query, rdnsrec) == ARES_SUCCESS) {
359✔
738
    rdnsrec = NULL;
40✔
739
  }
740

741
  server_set_good(server);
359✔
742
  end_query(channel, query, ARES_SUCCESS, abuf, alen);
359✔
743

744
  status = ARES_SUCCESS;
359✔
745

746
cleanup:
418✔
747
  ares_dns_record_destroy(rdnsrec);
418✔
748
  ares_dns_record_destroy(qdnsrec);
418✔
749
  return status;
418✔
750
}
751

752
static void handle_conn_error(struct server_connection *conn,
7✔
753
                              ares_bool_t               critical_failure,
754
                              ares_status_t             failure_status)
755
{
756
  struct server_state *server = conn->server;
7✔
757

758
  /* Increment failures first before requeue so it is unlikely to requeue
759
   * to the same server */
760
  if (critical_failure) {
7✔
761
    server_increment_failures(server);
7✔
762
  }
763

764
  /* This will requeue any connections automatically */
765
  ares__close_connection(conn, failure_status);
7✔
766
}
7✔
767

768
ares_status_t ares__requeue_query(struct query *query, struct timeval *now,
82✔
769
                                  ares_status_t status)
770
{
771
  const ares_channel_t *channel = query->channel;
82✔
772
  size_t max_tries = ares__slist_len(channel->servers) * channel->tries;
82✔
773

774
  ares__query_disassociate_from_conn(query);
82✔
775

776
  if (status != ARES_SUCCESS) {
82✔
777
    query->error_status = status;
78✔
778
  }
779

780
  query->try_count++;
82✔
781
  if (query->try_count < max_tries && !query->no_retries) {
82✔
782
    return ares__send_query(query, now);
68✔
783
  }
784

785
  /* If we are here, all attempts to perform query failed. */
786
  if (query->error_status == ARES_SUCCESS) {
14✔
787
    query->error_status = ARES_ETIMEOUT;
×
788
  }
789

790
  end_query(channel, query, query->error_status, NULL, 0);
14✔
791
  return ARES_ETIMEOUT;
14✔
792
}
793

794
/* Pick a random server from the list, we first get a random number in the
795
 * range of the number of servers, then scan until we find that server in
796
 * the list */
797
static struct server_state *ares__random_server(ares_channel_t *channel)
798
{
799
  unsigned char       c;
800
  size_t              cnt;
801
  size_t              idx;
802
  ares__slist_node_t *node;
803
  size_t              num_servers = ares__slist_len(channel->servers);
×
804

805
  /* Silence coverity, not possible */
806
  if (num_servers == 0) {
×
807
    return NULL;
×
808
  }
809

810
  ares__rand_bytes(channel->rand_state, &c, 1);
×
811

812
  cnt = c;
×
813
  idx = cnt % num_servers;
×
814

815
  cnt = 0;
×
816
  for (node = ares__slist_node_first(channel->servers); node != NULL;
×
817
       node = ares__slist_node_next(node)) {
×
818
    if (cnt == idx) {
×
819
      return ares__slist_node_val(node);
×
820
    }
821

822
    cnt++;
×
823
  }
824

825
  return NULL;
×
826
}
827

828
static ares_status_t ares__append_tcpbuf(struct server_state *server,
169✔
829
                                         const struct query  *query)
830
{
831
  ares_status_t status;
832

833
  status = ares__buf_append_be16(server->tcp_send, (unsigned short)query->qlen);
169✔
834
  if (status != ARES_SUCCESS) {
169✔
835
    return status;
×
836
  }
837
  return ares__buf_append(server->tcp_send, query->qbuf, query->qlen);
169✔
838
}
839

840
static size_t ares__calc_query_timeout(const struct query *query)
453✔
841
{
842
  const ares_channel_t *channel  = query->channel;
453✔
843
  size_t                timeplus = channel->timeout;
453✔
844
  size_t                rounds;
845
  size_t                num_servers = ares__slist_len(channel->servers);
453✔
846

847
  if (num_servers == 0) {
453✔
848
    return 0;
×
849
  }
850

851
  /* For each trip through the entire server list, we want to double the
852
   * retry from the last retry */
853
  rounds = (query->try_count / num_servers);
453✔
854

855
  if (rounds > 0) {
453✔
856
    timeplus <<= rounds;
26✔
857
  }
858

859
  if (channel->maxtimeout && timeplus > channel->maxtimeout) {
453✔
860
    timeplus = channel->maxtimeout;
×
861
  }
862

863
  /* Add some jitter to the retry timeout.
864
   *
865
   * Jitter is needed in situation when resolve requests are performed
866
   * simultaneously from multiple hosts and DNS server throttle these requests.
867
   * Adding randomness allows to avoid synchronisation of retries.
868
   *
869
   * Value of timeplus adjusted randomly to the range [0.5 * timeplus,
870
   * timeplus].
871
   */
872
  if (rounds > 0) {
453✔
873
    unsigned short r;
874
    float          delta_multiplier;
875

876
    ares__rand_bytes(channel->rand_state, (unsigned char *)&r, sizeof(r));
26✔
877
    delta_multiplier  = ((float)r / USHRT_MAX) * 0.5f;
26✔
878
    timeplus         -= (size_t)((float)timeplus * delta_multiplier);
26✔
879
  }
880

881
  /* We want explicitly guarantee that timeplus is greater or equal to timeout
882
   * specified in channel options. */
883
  if (timeplus < channel->timeout) {
453✔
884
    timeplus = channel->timeout;
×
885
  }
886

887
  return timeplus;
453✔
888
}
889

890
ares_status_t ares__send_query(struct query *query, struct timeval *now)
465✔
891
{
892
  ares_channel_t           *channel = query->channel;
465✔
893
  struct server_state      *server;
894
  struct server_connection *conn;
895
  size_t                    timeplus;
896
  ares_status_t             status;
897
  ares_bool_t               new_connection = ARES_FALSE;
465✔
898

899
  /* Choose the server to send the query to */
900
  if (channel->rotate) {
465✔
901
    server = ares__random_server(channel);
×
902
  } else {
903
    /* Pull first */
904
    server = ares__slist_first_val(channel->servers);
465✔
905
  }
906

907
  if (server == NULL) {
465✔
908
    end_query(channel, query, ARES_ESERVFAIL /* ? */, NULL, 0);
×
909
    return ARES_ECONNREFUSED;
×
910
  }
911

912
  if (query->using_tcp) {
465✔
913
    size_t prior_len = 0;
175✔
914
    /* Make sure the TCP socket for this server is set up and queue
915
     * a send request.
916
     */
917
    if (server->tcp_conn == NULL) {
175✔
918
      new_connection = ARES_TRUE;
101✔
919
      status         = ares__open_connection(channel, server, ARES_TRUE);
101✔
920
      switch (status) {
101✔
921
        /* Good result, continue on */
922
        case ARES_SUCCESS:
95✔
923
          break;
95✔
924

925
        /* These conditions are retryable as they are server-specific
926
         * error codes */
927
        case ARES_ECONNREFUSED:
6✔
928
        case ARES_EBADFAMILY:
929
          server_increment_failures(server);
6✔
930
          return ares__requeue_query(query, now, status);
6✔
931

932
        /* Anything else is not retryable, likely ENOMEM */
933
        default:
×
934
          end_query(channel, query, status, NULL, 0);
×
935
          return status;
×
936
      }
937
    }
938

939
    conn = server->tcp_conn;
169✔
940

941
    prior_len = ares__buf_len(server->tcp_send);
169✔
942

943
    status = ares__append_tcpbuf(server, query);
169✔
944
    if (status != ARES_SUCCESS) {
169✔
945
      end_query(channel, query, status, NULL, 0);
×
946

947
      /* Only safe to kill connection if it was new, otherwise it should be
948
       * cleaned up by another process later */
949
      if (new_connection) {
×
950
        ares__close_connection(conn, status);
×
951
      }
952
      return status;
×
953
    }
954

955
    if (prior_len == 0) {
169✔
956
      SOCK_STATE_CALLBACK(channel, conn->fd, 1, 1);
128✔
957
    }
958

959
  } else {
960
    ares__llist_node_t *node = ares__llist_node_first(server->connections);
290✔
961

962
    /* Don't use the found connection if we've gone over the maximum number
963
     * of queries. Also, skip over the TCP connection if it is the first in
964
     * the list */
965
    if (node != NULL) {
290✔
966
      conn = ares__llist_node_val(node);
124✔
967
      if (conn->is_tcp) {
124✔
968
        node = NULL;
×
969
      } else if (channel->udp_max_queries > 0 &&
124✔
970
                 conn->total_queries >= channel->udp_max_queries) {
31✔
971
        node = NULL;
3✔
972
      }
973
    }
974

975
    if (node == NULL) {
290✔
976
      new_connection = ARES_TRUE;
169✔
977
      status         = ares__open_connection(channel, server, ARES_FALSE);
169✔
978
      switch (status) {
169✔
979
        /* Good result, continue on */
980
        case ARES_SUCCESS:
163✔
981
          break;
163✔
982

983
        /* These conditions are retryable as they are server-specific
984
         * error codes */
985
        case ARES_ECONNREFUSED:
6✔
986
        case ARES_EBADFAMILY:
987
          server_increment_failures(server);
6✔
988
          return ares__requeue_query(query, now, status);
6✔
989

990
        /* Anything else is not retryable, likely ENOMEM */
991
        default:
×
992
          end_query(channel, query, status, NULL, 0);
×
993
          return status;
×
994
      }
995
      node = ares__llist_node_first(server->connections);
163✔
996
    }
997

998
    conn = ares__llist_node_val(node);
284✔
999

1000
    if (ares__socket_write(channel, conn->fd, query->qbuf, query->qlen) == -1) {
284✔
NEW
1001
      if (try_again(SOCKERRNO)) {      
×
NEW
1002
        status = ARES_ESERVFAIL;
×
1003
      } else {
NEW
1004
        status = ARES_ECONNREFUSED;
×
1005
      }
1006

NEW
1007
      if (status == ARES_ECONNREFUSED) {
×
NEW
1008
        handle_conn_error(conn, ARES_TRUE, status);
×
1009

1010
        /* This query wasn't yet bound to the connection, need to manually
1011
         * requeue it and return an appropriate error */
NEW
1012
        status = ares__requeue_query(query, now, status);
×
NEW
1013
        if (status == ARES_ETIMEOUT) {
×
NEW
1014
          status = ARES_ECONNREFUSED;
×
1015
        }
NEW
1016
        return status;
×
1017
      }
1018

1019
      /* FIXME: Handle EAGAIN here since it likely can happen. Right now we
1020
       * just requeue to a different server/connection. */
1021
      server_increment_failures(server);
×
1022
      status = ares__requeue_query(query, now, status);
×
1023

1024
      /* Only safe to kill connection if it was new, otherwise it should be
1025
       * cleaned up by another process later */
1026
      if (new_connection) {
×
1027
        ares__close_connection(conn, status);
×
1028
      }
1029

1030
      return status;
×
1031
    }
1032
  }
1033

1034
  timeplus = ares__calc_query_timeout(query);
453✔
1035

1036
  /* Keep track of queries bucketed by timeout, so we can process
1037
   * timeout events quickly.
1038
   */
1039
  ares__slist_node_destroy(query->node_queries_by_timeout);
453✔
1040
  query->timeout = *now;
453✔
1041
  timeadd(&query->timeout, timeplus);
453✔
1042
  query->node_queries_by_timeout =
453✔
1043
    ares__slist_insert(channel->queries_by_timeout, query);
453✔
1044
  if (!query->node_queries_by_timeout) {
453✔
1045
    end_query(channel, query, ARES_ENOMEM, NULL, 0);
×
1046
    /* Only safe to kill connection if it was new, otherwise it should be
1047
     * cleaned up by another process later */
1048
    if (new_connection) {
×
1049
      ares__close_connection(conn, ARES_SUCCESS);
×
1050
    }
1051
    return ARES_ENOMEM;
×
1052
  }
1053

1054
  /* Keep track of queries bucketed by connection, so we can process errors
1055
   * quickly. */
1056
  ares__llist_node_destroy(query->node_queries_to_conn);
453✔
1057
  query->node_queries_to_conn =
453✔
1058
    ares__llist_insert_last(conn->queries_to_conn, query);
453✔
1059

1060
  if (query->node_queries_to_conn == NULL) {
453✔
1061
    end_query(channel, query, ARES_ENOMEM, NULL, 0);
×
1062
    /* Only safe to kill connection if it was new, otherwise it should be
1063
     * cleaned up by another process later */
1064
    if (new_connection) {
×
1065
      ares__close_connection(conn, ARES_SUCCESS);
×
1066
    }
1067
    return ARES_ENOMEM;
×
1068
  }
1069

1070
  query->conn = conn;
453✔
1071
  conn->total_queries++;
453✔
1072
  return ARES_SUCCESS;
453✔
1073
}
1074

1075
static ares_bool_t same_questions(const ares_dns_record_t *qrec,
411✔
1076
                                  const ares_dns_record_t *arec)
1077
{
1078
  size_t      i;
1079
  ares_bool_t rv = ARES_FALSE;
411✔
1080

1081

1082
  if (ares_dns_record_query_cnt(qrec) != ares_dns_record_query_cnt(arec)) {
411✔
1083
    goto done;
×
1084
  }
1085

1086
  for (i = 0; i < ares_dns_record_query_cnt(qrec); i++) {
822✔
1087
    const char         *qname = NULL;
411✔
1088
    const char         *aname = NULL;
411✔
1089
    ares_dns_rec_type_t qtype;
1090
    ares_dns_rec_type_t atype;
1091
    ares_dns_class_t    qclass;
1092
    ares_dns_class_t    aclass;
1093

1094
    if (ares_dns_record_query_get(qrec, i, &qname, &qtype, &qclass) !=
411✔
1095
          ARES_SUCCESS ||
411✔
1096
        qname == NULL) {
411✔
1097
      goto done;
×
1098
    }
1099

1100
    if (ares_dns_record_query_get(arec, i, &aname, &atype, &aclass) !=
411✔
1101
          ARES_SUCCESS ||
411✔
1102
        aname == NULL) {
411✔
1103
      goto done;
×
1104
    }
1105
    if (strcasecmp(qname, aname) != 0 || qtype != atype || qclass != aclass) {
411✔
1106
      goto done;
×
1107
    }
1108
  }
1109

1110
  rv = ARES_TRUE;
411✔
1111

1112
done:
411✔
1113
  return rv;
411✔
1114
}
1115

1116
static ares_bool_t same_address(const struct sockaddr  *sa,
261✔
1117
                                const struct ares_addr *aa)
1118
{
1119
  const void *addr1;
1120
  const void *addr2;
1121

1122
  if (sa->sa_family == aa->family) {
261✔
1123
    switch (aa->family) {
261✔
1124
      case AF_INET:
261✔
1125
        addr1 = &aa->addr.addr4;
261✔
1126
        addr2 = &(CARES_INADDR_CAST(struct sockaddr_in *, sa))->sin_addr;
261✔
1127
        if (memcmp(addr1, addr2, sizeof(aa->addr.addr4)) == 0) {
261✔
1128
          return ARES_TRUE; /* match */
261✔
1129
        }
1130
        break;
×
1131
      case AF_INET6:
×
1132
        addr1 = &aa->addr.addr6;
×
1133
        addr2 = &(CARES_INADDR_CAST(struct sockaddr_in6 *, sa))->sin6_addr;
×
1134
        if (memcmp(addr1, addr2, sizeof(aa->addr.addr6)) == 0) {
×
1135
          return ARES_TRUE; /* match */
×
1136
        }
1137
        break;
×
1138
      default:
×
1139
        break; /* LCOV_EXCL_LINE */
1140
    }
1141
  }
1142
  return ARES_FALSE; /* different */
×
1143
}
1144

1145
static void ares_detach_query(struct query *query)
391✔
1146
{
1147
  /* Remove the query from all the lists in which it is linked */
1148
  ares__query_disassociate_from_conn(query);
391✔
1149
  ares__htable_szvp_remove(query->channel->queries_by_qid, query->qid);
391✔
1150
  ares__llist_node_destroy(query->node_all_queries);
391✔
1151
  query->node_all_queries        = NULL;
391✔
1152
}
391✔
1153

1154
static void end_query(const ares_channel_t *channel, struct query *query,
373✔
1155
                      ares_status_t status, const unsigned char *abuf,
1156
                      size_t alen)
1157
{
1158
  (void)channel;
1159

1160
  /* Invoke the callback. */
1161
  query->callback(query->arg, (int)status, (int)query->timeouts,
373✔
1162
                  /* due to prior design flaws, abuf isn't meant to be modified,
1163
                   * but bad prototypes, ugh.  Lets cast off constfor compat. */
1164
                  (unsigned char *)((void *)((size_t)abuf)), (int)alen);
1165
  ares__free_query(query);
373✔
1166
}
373✔
1167

1168
void ares__free_query(struct query *query)
391✔
1169
{
1170
  ares_detach_query(query);
391✔
1171
  /* Zero out some important stuff, to help catch bugs */
1172
  query->callback = NULL;
391✔
1173
  query->arg      = NULL;
391✔
1174
  /* Deallocate the memory associated with the query */
1175
  ares_free(query->qbuf);
391✔
1176

1177
  ares_free(query);
391✔
1178
}
391✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc