Coverage Report

Created: 2026-04-12 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/c-ares/src/lib/ares_conn.c
Line
Count
Source
1
/* MIT License
2
 *
3
 * Copyright (c) Massachusetts Institute of Technology
4
 * Copyright (c) The c-ares project and its contributors
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
#include "ares_private.h"
28
29
void ares_conn_sock_state_cb_update(ares_conn_t            *conn,
30
                                    ares_conn_state_flags_t flags)
31
0
{
32
0
  ares_channel_t *channel = conn->server->channel;
33
34
0
  if ((conn->state_flags & ARES_CONN_STATE_CBFLAGS) != flags &&
35
0
      channel->sock_state_cb) {
36
0
    channel->sock_state_cb(channel->sock_state_cb_data, conn->fd,
37
0
                           flags & ARES_CONN_STATE_READ ? 1 : 0,
38
0
                           flags & ARES_CONN_STATE_WRITE ? 1 : 0);
39
0
  }
40
41
0
  conn->state_flags &= ~((unsigned int)ARES_CONN_STATE_CBFLAGS);
42
0
  conn->state_flags |= flags;
43
0
}
44
45
ares_conn_err_t ares_conn_read(ares_conn_t *conn, void *data, size_t len,
46
                               size_t *read_bytes)
47
0
{
48
0
  ares_channel_t *channel = conn->server->channel;
49
0
  ares_conn_err_t err;
50
51
0
  if (!(conn->flags & ARES_CONN_FLAG_TCP)) {
52
0
    struct sockaddr_storage sa_storage;
53
0
    ares_socklen_t          salen = sizeof(sa_storage);
54
55
0
    memset(&sa_storage, 0, sizeof(sa_storage));
56
57
0
    err =
58
0
      ares_socket_recvfrom(channel, conn->fd, ARES_FALSE, data, len, 0,
59
0
                           (struct sockaddr *)&sa_storage, &salen, read_bytes);
60
61
0
#ifdef HAVE_RECVFROM
62
0
    if (err == ARES_CONN_ERR_SUCCESS &&
63
0
        !ares_sockaddr_addr_eq((struct sockaddr *)&sa_storage,
64
0
                               &conn->server->addr)) {
65
0
      err = ARES_CONN_ERR_WOULDBLOCK;
66
0
    }
67
0
#endif
68
0
  } else {
69
0
    err = ares_socket_recv(channel, conn->fd, ARES_TRUE, data, len, read_bytes);
70
0
  }
71
72
  /* Toggle connected state if needed */
73
0
  if (err == ARES_CONN_ERR_SUCCESS) {
74
0
    conn->state_flags |= ARES_CONN_STATE_CONNECTED;
75
0
  }
76
77
0
  return err;
78
0
}
79
80
/* Use like:
81
 *   struct sockaddr_storage sa_storage;
82
 *   ares_socklen_t          salen     = sizeof(sa_storage);
83
 *   struct sockaddr        *sa        = (struct sockaddr *)&sa_storage;
84
 *   ares_conn_set_sockaddr(conn, sa, &salen);
85
 */
86
static ares_status_t ares_conn_set_sockaddr(const ares_conn_t *conn,
87
                                            struct sockaddr   *sa,
88
                                            ares_socklen_t    *salen)
89
0
{
90
0
  const ares_server_t *server = conn->server;
91
0
  unsigned short       port =
92
0
    conn->flags & ARES_CONN_FLAG_TCP ? server->tcp_port : server->udp_port;
93
0
  struct sockaddr_in  *sin;
94
0
  struct sockaddr_in6 *sin6;
95
96
0
  switch (server->addr.family) {
97
0
    case AF_INET:
98
0
      sin = (struct sockaddr_in *)(void *)sa;
99
0
      if (*salen < (ares_socklen_t)sizeof(*sin)) {
100
0
        return ARES_EFORMERR;
101
0
      }
102
0
      *salen = sizeof(*sin);
103
0
      memset(sin, 0, sizeof(*sin));
104
0
      sin->sin_family = AF_INET;
105
0
      sin->sin_port   = htons(port);
106
0
      memcpy(&sin->sin_addr, &server->addr.addr.addr4, sizeof(sin->sin_addr));
107
0
      return ARES_SUCCESS;
108
0
    case AF_INET6:
109
0
      sin6 = (struct sockaddr_in6 *)(void *)sa;
110
0
      if (*salen < (ares_socklen_t)sizeof(*sin6)) {
111
0
        return ARES_EFORMERR;
112
0
      }
113
0
      *salen = sizeof(*sin6);
114
0
      memset(sin6, 0, sizeof(*sin6));
115
0
      sin6->sin6_family = AF_INET6;
116
0
      sin6->sin6_port   = htons(port);
117
0
      memcpy(&sin6->sin6_addr, &server->addr.addr.addr6,
118
0
             sizeof(sin6->sin6_addr));
119
0
#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
120
0
      sin6->sin6_scope_id = server->ll_scope;
121
0
#endif
122
0
      return ARES_SUCCESS;
123
0
    default:
124
0
      break;
125
0
  }
126
127
0
  return ARES_EBADFAMILY;
128
0
}
129
130
static ares_status_t ares_conn_set_self_ip(ares_conn_t *conn, ares_bool_t early)
131
0
{
132
0
  ares_channel_t         *channel = conn->server->channel;
133
0
  struct sockaddr_storage sa_storage;
134
0
  int                     rv;
135
0
  ares_socklen_t          len = sizeof(sa_storage);
136
137
  /* We call this twice on TFO, if we already have the IP we can go ahead and
138
   * skip processing */
139
0
  if (!early && conn->self_ip.family != AF_UNSPEC) {
140
0
    return ARES_SUCCESS;
141
0
  }
142
143
0
  memset(&sa_storage, 0, sizeof(sa_storage));
144
145
0
  if (channel->sock_funcs.agetsockname == NULL) {
146
    /* Not specified, we can still use cookies cooked with an empty self_ip */
147
0
    memset(&conn->self_ip, 0, sizeof(conn->self_ip));
148
0
    return ARES_SUCCESS;
149
0
  }
150
0
  rv = channel->sock_funcs.agetsockname(conn->fd,
151
0
                                        (struct sockaddr *)(void *)&sa_storage,
152
0
                                        &len, channel->sock_func_cb_data);
153
0
  if (rv != 0) {
154
    /* During TCP FastOpen, we can't get the IP this early since connect()
155
     * may not be called.  That's ok, we'll try again later */
156
0
    if (early && conn->flags & ARES_CONN_FLAG_TCP &&
157
0
        conn->flags & ARES_CONN_FLAG_TFO) {
158
0
      memset(&conn->self_ip, 0, sizeof(conn->self_ip));
159
0
      return ARES_SUCCESS;
160
0
    }
161
0
    return ARES_ECONNREFUSED;
162
0
  }
163
164
0
  if (!ares_sockaddr_to_ares_addr(&conn->self_ip, NULL,
165
0
                                  (struct sockaddr *)(void *)&sa_storage)) {
166
0
    return ARES_ECONNREFUSED;
167
0
  }
168
169
0
  return ARES_SUCCESS;
170
0
}
171
172
ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len,
173
                                size_t *written)
174
0
{
175
0
  ares_channel_t         *channel = conn->server->channel;
176
0
  ares_bool_t             is_tfo  = ARES_FALSE;
177
0
  ares_conn_err_t         err     = ARES_CONN_ERR_SUCCESS;
178
0
  struct sockaddr_storage sa_storage;
179
0
  ares_socklen_t          salen = 0;
180
0
  struct sockaddr        *sa    = NULL;
181
182
0
  *written = 0;
183
184
  /* Don't try to write if not doing initial TFO and not connected */
185
0
  if (conn->flags & ARES_CONN_FLAG_TCP &&
186
0
      !(conn->state_flags & ARES_CONN_STATE_CONNECTED) &&
187
0
      !(conn->flags & ARES_CONN_FLAG_TFO_INITIAL)) {
188
0
    return ARES_CONN_ERR_WOULDBLOCK;
189
0
  }
190
191
  /* On initial write during TFO we need to send an address */
192
0
  if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) {
193
0
    salen = sizeof(sa_storage);
194
0
    sa    = (struct sockaddr *)&sa_storage;
195
196
0
    conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO_INITIAL);
197
0
    is_tfo       = ARES_TRUE;
198
199
0
    if (ares_conn_set_sockaddr(conn, sa, &salen) != ARES_SUCCESS) {
200
0
      return ARES_CONN_ERR_FAILURE;
201
0
    }
202
0
  }
203
204
0
  err = ares_socket_write(channel, conn->fd, data, len, written, sa, salen);
205
0
  if (err != ARES_CONN_ERR_SUCCESS) {
206
0
    goto done;
207
0
  }
208
209
0
  if (is_tfo) {
210
    /* If using TFO, we might not have been able to get an IP earlier, since
211
     * we hadn't informed the OS of the destination.  When using sendto()
212
     * now we have so we should be able to fetch it */
213
0
    ares_conn_set_self_ip(conn, ARES_FALSE);
214
0
    goto done;
215
0
  }
216
217
0
done:
218
0
  if (err == ARES_CONN_ERR_SUCCESS && len == *written) {
219
    /* Wrote all data, make sure we're not listening for write events unless
220
     * using TFO, in which case we'll need a write event to know when
221
     * we're connected. */
222
0
    ares_conn_sock_state_cb_update(
223
0
      conn, ARES_CONN_STATE_READ |
224
0
              (is_tfo ? ARES_CONN_STATE_WRITE : ARES_CONN_STATE_NONE));
225
0
  } else if (err == ARES_CONN_ERR_WOULDBLOCK) {
226
    /* Need to wait on more buffer space to write */
227
0
    ares_conn_sock_state_cb_update(conn, ARES_CONN_STATE_READ |
228
0
                                           ARES_CONN_STATE_WRITE);
229
0
  }
230
231
0
  return err;
232
0
}
233
234
ares_status_t ares_conn_flush(ares_conn_t *conn)
235
0
{
236
0
  const unsigned char *data;
237
0
  size_t               data_len;
238
0
  size_t               count;
239
0
  ares_conn_err_t      err;
240
0
  ares_status_t        status;
241
0
  ares_bool_t          tfo = ARES_FALSE;
242
243
0
  if (conn == NULL) {
244
0
    return ARES_EFORMERR;
245
0
  }
246
247
0
  if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) {
248
0
    tfo = ARES_TRUE;
249
0
  }
250
251
0
  do {
252
0
    if (ares_buf_len(conn->out_buf) == 0) {
253
0
      status = ARES_SUCCESS;
254
0
      goto done;
255
0
    }
256
257
0
    if (conn->flags & ARES_CONN_FLAG_TCP) {
258
0
      data = ares_buf_peek(conn->out_buf, &data_len);
259
0
    } else {
260
0
      unsigned short msg_len;
261
262
      /* Read length, then provide buffer without length */
263
0
      ares_buf_tag(conn->out_buf);
264
0
      status = ares_buf_fetch_be16(conn->out_buf, &msg_len);
265
0
      if (status != ARES_SUCCESS) {
266
0
        return status;
267
0
      }
268
0
      ares_buf_tag_rollback(conn->out_buf);
269
270
0
      data = ares_buf_peek(conn->out_buf, &data_len);
271
0
      if (data_len < (size_t)(msg_len + 2)) {
272
0
        status = ARES_EFORMERR;
273
0
        goto done;
274
0
      }
275
0
      data     += 2;
276
0
      data_len  = msg_len;
277
0
    }
278
279
0
    err = ares_conn_write(conn, data, data_len, &count);
280
0
    if (err != ARES_CONN_ERR_SUCCESS) {
281
0
      if (err != ARES_CONN_ERR_WOULDBLOCK) {
282
0
        status = ARES_ECONNREFUSED;
283
0
        goto done;
284
0
      }
285
0
      status = ARES_SUCCESS;
286
0
      goto done;
287
0
    }
288
289
    /* UDP didn't send the length prefix so augment that here */
290
0
    if (!(conn->flags & ARES_CONN_FLAG_TCP)) {
291
0
      count += 2;
292
0
    }
293
294
    /* Strip data written from the buffer */
295
0
    ares_buf_consume(conn->out_buf, count);
296
0
    status = ARES_SUCCESS;
297
298
    /* Loop only for UDP since we have to send per-packet.  We already
299
     * sent everything we could if using tcp */
300
0
  } while (!(conn->flags & ARES_CONN_FLAG_TCP));
301
302
0
done:
303
0
  if (status == ARES_SUCCESS) {
304
0
    ares_conn_state_flags_t flags = ARES_CONN_STATE_READ;
305
306
    /* When using TFO, the we need to enabling waiting on a write event to
307
     * be notified of when a connection is actually established */
308
0
    if (tfo) {
309
0
      flags |= ARES_CONN_STATE_WRITE;
310
0
    }
311
312
    /* If using TCP and not all data was written (partial write), that means
313
     * we need to also wait on a write event */
314
0
    if (conn->flags & ARES_CONN_FLAG_TCP && ares_buf_len(conn->out_buf)) {
315
0
      flags |= ARES_CONN_STATE_WRITE;
316
0
    }
317
318
0
    ares_conn_sock_state_cb_update(conn, flags);
319
0
  }
320
321
0
  return status;
322
0
}
323
324
static ares_status_t ares_conn_connect(ares_conn_t           *conn,
325
                                       const struct sockaddr *sa,
326
                                       ares_socklen_t         salen)
327
0
{
328
0
  ares_conn_err_t err;
329
330
0
  err = ares_socket_connect(
331
0
    conn->server->channel, conn->fd,
332
0
    (conn->flags & ARES_CONN_FLAG_TFO) ? ARES_TRUE : ARES_FALSE, sa, salen);
333
334
0
  if (err != ARES_CONN_ERR_WOULDBLOCK && err != ARES_CONN_ERR_SUCCESS) {
335
0
    return ARES_ECONNREFUSED;
336
0
  }
337
0
  return ARES_SUCCESS;
338
0
}
339
340
ares_status_t ares_open_connection(ares_conn_t   **conn_out,
341
                                   ares_channel_t *channel,
342
                                   ares_server_t *server, ares_bool_t is_tcp)
343
0
{
344
0
  ares_status_t           status;
345
0
  struct sockaddr_storage sa_storage;
346
0
  ares_socklen_t          salen = sizeof(sa_storage);
347
0
  struct sockaddr        *sa    = (struct sockaddr *)&sa_storage;
348
0
  ares_conn_t            *conn;
349
0
  ares_llist_node_t      *node  = NULL;
350
0
  int                     stype = is_tcp ? SOCK_STREAM : SOCK_DGRAM;
351
0
  ares_conn_state_flags_t state_flags;
352
353
0
  *conn_out = NULL;
354
355
0
  conn = ares_malloc(sizeof(*conn));
356
0
  if (conn == NULL) {
357
0
    return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
358
0
  }
359
360
0
  memset(conn, 0, sizeof(*conn));
361
0
  conn->fd              = ARES_SOCKET_BAD;
362
0
  conn->server          = server;
363
0
  conn->queries_to_conn = ares_llist_create(NULL);
364
0
  conn->flags           = is_tcp ? ARES_CONN_FLAG_TCP : ARES_CONN_FLAG_NONE;
365
0
  conn->out_buf         = ares_buf_create();
366
0
  conn->in_buf          = ares_buf_create();
367
368
0
  if (conn->queries_to_conn == NULL || conn->out_buf == NULL ||
369
0
      conn->in_buf == NULL) {
370
    /* LCOV_EXCL_START: OutOfMemory */
371
0
    status = ARES_ENOMEM;
372
0
    goto done;
373
    /* LCOV_EXCL_STOP */
374
0
  }
375
376
  /* Try to enable TFO always if using TCP. it will fail later on if its
377
   * really not supported when we try to enable it on the socket. */
378
0
  if (conn->flags & ARES_CONN_FLAG_TCP) {
379
0
    conn->flags |= ARES_CONN_FLAG_TFO;
380
0
  }
381
382
  /* Convert into the struct sockaddr structure needed by the OS */
383
0
  status = ares_conn_set_sockaddr(conn, sa, &salen);
384
0
  if (status != ARES_SUCCESS) {
385
0
    goto done;
386
0
  }
387
388
  /* Acquire a socket. */
389
0
  if (ares_socket_open(&conn->fd, channel, server->addr.family, stype, 0) !=
390
0
      ARES_CONN_ERR_SUCCESS) {
391
0
    status = ARES_ECONNREFUSED;
392
0
    goto done;
393
0
  }
394
395
  /* Configure channel configured options */
396
0
  status = ares_socket_configure(
397
0
    channel, server->addr.family,
398
0
    (conn->flags & ARES_CONN_FLAG_TCP) ? ARES_TRUE : ARES_FALSE, conn->fd);
399
0
  if (status != ARES_SUCCESS) {
400
0
    goto done;
401
0
  }
402
403
  /* Enable TFO if possible */
404
0
  if (conn->flags & ARES_CONN_FLAG_TFO &&
405
0
      ares_socket_enable_tfo(channel, conn->fd) != ARES_CONN_ERR_SUCCESS) {
406
0
    conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO);
407
0
  }
408
409
0
  if (channel->sock_config_cb) {
410
0
    int err =
411
0
      channel->sock_config_cb(conn->fd, stype, channel->sock_config_cb_data);
412
0
    if (err < 0) {
413
0
      status = ARES_ECONNREFUSED;
414
0
      goto done;
415
0
    }
416
0
  }
417
418
  /* Connect */
419
0
  status = ares_conn_connect(conn, sa, salen);
420
0
  if (status != ARES_SUCCESS) {
421
0
    goto done;
422
0
  }
423
424
0
  if (channel->sock_create_cb) {
425
0
    int err =
426
0
      channel->sock_create_cb(conn->fd, stype, channel->sock_create_cb_data);
427
0
    if (err < 0) {
428
0
      status = ARES_ECONNREFUSED;
429
0
      goto done;
430
0
    }
431
0
  }
432
433
  /* Let the connection know we haven't written our first packet yet for TFO */
434
0
  if (conn->flags & ARES_CONN_FLAG_TFO) {
435
0
    conn->flags |= ARES_CONN_FLAG_TFO_INITIAL;
436
0
  }
437
438
  /* Need to store our own ip for DNS cookie support */
439
0
  status = ares_conn_set_self_ip(conn, ARES_TRUE);
440
0
  if (status != ARES_SUCCESS) {
441
0
    goto done; /* LCOV_EXCL_LINE: UntestablePath */
442
0
  }
443
444
  /* TCP connections are thrown to the end as we don't spawn multiple TCP
445
   * connections. UDP connections are put on front where the newest connection
446
   * can be quickly pulled */
447
0
  if (is_tcp) {
448
0
    node = ares_llist_insert_last(server->connections, conn);
449
0
  } else {
450
0
    node = ares_llist_insert_first(server->connections, conn);
451
0
  }
452
0
  if (node == NULL) {
453
    /* LCOV_EXCL_START: OutOfMemory */
454
0
    status = ARES_ENOMEM;
455
0
    goto done;
456
    /* LCOV_EXCL_STOP */
457
0
  }
458
459
  /* Register globally to quickly map event on file descriptor to connection
460
   * node object */
461
0
  if (!ares_htable_asvp_insert(channel->connnode_by_socket, conn->fd, node)) {
462
    /* LCOV_EXCL_START: OutOfMemory */
463
0
    status = ARES_ENOMEM;
464
0
    goto done;
465
    /* LCOV_EXCL_STOP */
466
0
  }
467
468
0
  state_flags = ARES_CONN_STATE_READ;
469
470
  /* Get notified on connect if using TCP */
471
0
  if (conn->flags & ARES_CONN_FLAG_TCP) {
472
0
    state_flags |= ARES_CONN_STATE_WRITE;
473
0
  }
474
475
  /* Dot no attempt to update sock state callbacks on TFO until *after* the
476
   * initial write is performed.  Due to the notification event, its possible
477
   * an erroneous read can come in before the attempt to write the data which
478
   * might be used to set the ip address */
479
0
  if (!(conn->flags & ARES_CONN_FLAG_TFO_INITIAL)) {
480
0
    ares_conn_sock_state_cb_update(conn, state_flags);
481
0
  }
482
483
0
  if (is_tcp) {
484
0
    server->tcp_conn = conn;
485
0
  }
486
487
0
done:
488
0
  if (status != ARES_SUCCESS) {
489
0
    ares_llist_node_claim(node);
490
0
    ares_llist_destroy(conn->queries_to_conn);
491
0
    ares_socket_close(channel, conn->fd);
492
0
    ares_buf_destroy(conn->out_buf);
493
0
    ares_buf_destroy(conn->in_buf);
494
0
    ares_free(conn);
495
0
  } else {
496
0
    *conn_out = conn;
497
0
  }
498
0
  return status;
499
0
}
500
501
ares_conn_t *ares_conn_from_fd(const ares_channel_t *channel, ares_socket_t fd)
502
0
{
503
0
  ares_llist_node_t *node;
504
505
0
  node = ares_htable_asvp_get_direct(channel->connnode_by_socket, fd);
506
0
  if (node == NULL) {
507
0
    return NULL;
508
0
  }
509
510
0
  return ares_llist_node_val(node);
511
0
}