Coverage Report

Created: 2025-08-29 06:53

/src/c-ares/src/lib/ares_set_socket_functions.c
Line
Count
Source (jump to first uncovered line)
1
/* MIT License
2
 *
3
 * Copyright (c) 2024 Brad House
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining a copy
6
 * of this software and associated documentation files (the "Software"), to deal
7
 * in the Software without restriction, including without limitation the rights
8
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
 * copies of the Software, and to permit persons to whom the Software is
10
 * furnished to do so, subject to the following conditions:
11
 *
12
 * The above copyright notice and this permission notice (including the next
13
 * paragraph) shall be included in all copies or substantial portions of the
14
 * Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 *
24
 * SPDX-License-Identifier: MIT
25
 */
26
#include "ares_private.h"
27
#ifdef HAVE_SYS_UIO_H
28
#  include <sys/uio.h>
29
#endif
30
#ifdef HAVE_NETINET_IN_H
31
#  include <netinet/in.h>
32
#endif
33
#ifdef HAVE_NETINET_TCP_H
34
#  include <netinet/tcp.h>
35
#endif
36
#ifdef HAVE_NETDB_H
37
#  include <netdb.h>
38
#endif
39
#ifdef HAVE_ARPA_INET_H
40
#  include <arpa/inet.h>
41
#endif
42
43
#ifdef HAVE_STRINGS_H
44
#  include <strings.h>
45
#endif
46
#ifdef HAVE_SYS_IOCTL_H
47
#  include <sys/ioctl.h>
48
#endif
49
#ifdef NETWARE
50
#  include <sys/filio.h>
51
#endif
52
53
#include <assert.h>
54
#include <fcntl.h>
55
#include <limits.h>
56
57
58
#if defined(__linux__) && defined(TCP_FASTOPEN_CONNECT)
59
#  define TFO_SUPPORTED      1
60
#  define TFO_SKIP_CONNECT   0
61
#  define TFO_USE_SENDTO     0
62
#  define TFO_USE_CONNECTX   0
63
0
#  define TFO_CLIENT_SOCKOPT TCP_FASTOPEN_CONNECT
64
#elif (defined(__MidnightBSD__) || defined(__FreeBSD__)) && defined(TCP_FASTOPEN)
65
#  define TFO_SUPPORTED      1
66
#  define TFO_SKIP_CONNECT   1
67
#  define TFO_USE_SENDTO     1
68
#  define TFO_USE_CONNECTX   0
69
#  define TFO_CLIENT_SOCKOPT TCP_FASTOPEN
70
#elif defined(__APPLE__) && defined(HAVE_CONNECTX)
71
#  define TFO_SUPPORTED    1
72
#  define TFO_SKIP_CONNECT 0
73
#  define TFO_USE_SENDTO   0
74
#  define TFO_USE_CONNECTX 1
75
#  undef TFO_CLIENT_SOCKOPT
76
#else
77
#  define TFO_SUPPORTED 0
78
#endif
79
80
#ifndef HAVE_WRITEV
81
/* Structure for scatter/gather I/O. */
82
struct iovec {
83
  void  *iov_base; /* Pointer to data. */
84
  size_t iov_len;  /* Length of data.  */
85
};
86
#endif
87
88
ares_status_t
89
  ares_set_socket_functions_ex(ares_channel_t                        *channel,
90
                               const struct ares_socket_functions_ex *funcs,
91
                               void                                  *user_data)
92
4.29k
{
93
4.29k
  unsigned int known_versions[] = { 1 };
94
4.29k
  size_t       i;
95
96
4.29k
  if (channel == NULL || funcs == NULL) {
97
0
    return ARES_EFORMERR;
98
0
  }
99
100
  /* Check to see if we know the version referenced */
101
4.29k
  for (i = 0; i < sizeof(known_versions) / sizeof(*known_versions); i++) {
102
4.29k
    if (funcs->version == known_versions[i]) {
103
4.29k
      break;
104
4.29k
    }
105
4.29k
  }
106
4.29k
  if (i == sizeof(known_versions) / sizeof(*known_versions)) {
107
0
    return ARES_EFORMERR;
108
0
  }
109
110
4.29k
  memset(&channel->sock_funcs, 0, sizeof(channel->sock_funcs));
111
112
  /* Copy individually for ABI compliance.  memcpy() with a sizeof would do
113
   * invalid reads */
114
4.29k
  if (funcs->version >= 1) {
115
4.29k
    if (funcs->asocket == NULL || funcs->aclose == NULL ||
116
4.29k
        funcs->asetsockopt == NULL || funcs->aconnect == NULL ||
117
4.29k
        funcs->arecvfrom == NULL || funcs->asendto == NULL) {
118
0
      return ARES_EFORMERR;
119
0
    }
120
4.29k
    channel->sock_funcs.version      = funcs->version;
121
4.29k
    channel->sock_funcs.flags        = funcs->flags;
122
4.29k
    channel->sock_funcs.asocket      = funcs->asocket;
123
4.29k
    channel->sock_funcs.aclose       = funcs->aclose;
124
4.29k
    channel->sock_funcs.asetsockopt  = funcs->asetsockopt;
125
4.29k
    channel->sock_funcs.aconnect     = funcs->aconnect;
126
4.29k
    channel->sock_funcs.arecvfrom    = funcs->arecvfrom;
127
4.29k
    channel->sock_funcs.asendto      = funcs->asendto;
128
4.29k
    channel->sock_funcs.agetsockname = funcs->agetsockname;
129
4.29k
    channel->sock_funcs.abind        = funcs->abind;
130
4.29k
    channel->sock_funcs.aif_nametoindex = funcs->aif_nametoindex;
131
4.29k
    channel->sock_funcs.aif_indextoname = funcs->aif_indextoname;
132
4.29k
  }
133
134
  /* Implement newer versions here ...*/
135
136
137
4.29k
  channel->sock_func_cb_data = user_data;
138
139
4.29k
  return ARES_SUCCESS;
140
4.29k
}
141
142
static int setsocknonblock(ares_socket_t sockfd, /* operate on this */
143
                           int           nonblock /* TRUE or FALSE */)
144
0
{
145
0
#if defined(HAVE_FCNTL_O_NONBLOCK)
146
147
  /* most recent unix versions */
148
0
  int flags;
149
0
  flags = fcntl(sockfd, F_GETFL, 0);
150
0
  if (nonblock) {
151
0
    return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
152
0
  } else {
153
0
    return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); /* LCOV_EXCL_LINE */
154
0
  }
155
156
#elif defined(HAVE_IOCTL_FIONBIO)
157
158
  /* older unix versions */
159
  int flags = nonblock ? 1 : 0;
160
  return ioctl(sockfd, FIONBIO, &flags);
161
162
#elif defined(HAVE_IOCTLSOCKET_FIONBIO)
163
164
#  ifdef WATT32
165
  char flags = nonblock ? 1 : 0;
166
#  else
167
  /* Windows */
168
  unsigned long flags = nonblock ? 1UL : 0UL;
169
#  endif
170
  return ioctlsocket(sockfd, (long)FIONBIO, &flags);
171
172
#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO)
173
174
  /* Amiga */
175
  long flags = nonblock ? 1L : 0L;
176
  return IoctlSocket(sockfd, FIONBIO, flags);
177
178
#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK)
179
180
  /* BeOS */
181
  long b = nonblock ? 1L : 0L;
182
  return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
183
184
#else
185
#  error "no non-blocking method was found/used/set"
186
#endif
187
0
}
188
189
static int default_aclose(ares_socket_t sock, void *user_data)
190
0
{
191
0
  (void)user_data;
192
193
#if defined(HAVE_CLOSESOCKET)
194
  return closesocket(sock);
195
#elif defined(HAVE_CLOSESOCKET_CAMEL)
196
  return CloseSocket(sock);
197
#elif defined(HAVE_CLOSE_S)
198
  return close_s(sock);
199
#else
200
0
  return close(sock);
201
0
#endif
202
0
}
203
204
static ares_socket_t default_asocket(int domain, int type, int protocol,
205
                                     void *user_data)
206
0
{
207
0
  ares_socket_t s;
208
0
  (void)user_data;
209
210
0
  s = socket(domain, type, protocol);
211
0
  if (s == ARES_SOCKET_BAD) {
212
0
    return s;
213
0
  }
214
215
0
  if (setsocknonblock(s, 1) != 0) {
216
0
    goto fail; /* LCOV_EXCL_LINE */
217
0
  }
218
219
0
#if defined(FD_CLOEXEC) && !defined(MSDOS)
220
  /* Configure the socket fd as close-on-exec. */
221
0
  if (fcntl(s, F_SETFD, FD_CLOEXEC) != 0) {
222
0
    goto fail; /* LCOV_EXCL_LINE */
223
0
  }
224
0
#endif
225
226
  /* No need to emit SIGPIPE on socket errors */
227
#if defined(SO_NOSIGPIPE)
228
  {
229
    int opt = 1;
230
    (void)setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(opt));
231
  }
232
#endif
233
234
235
0
  if (type == SOCK_STREAM) {
236
0
    int opt = 1;
237
238
0
#ifdef TCP_NODELAY
239
    /*
240
     * Disable the Nagle algorithm (only relevant for TCP sockets, and thus not
241
     * in configure_socket). In general, in DNS lookups we're pretty much
242
     * interested in firing off a single request and then waiting for a reply,
243
     * so batching isn't very interesting.
244
     */
245
0
    if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *)&opt, sizeof(opt)) !=
246
0
        0) {
247
0
      goto fail;
248
0
    }
249
0
#endif
250
0
  }
251
252
#if defined(IPV6_V6ONLY) && defined(USE_WINSOCK)
253
  /* Support for IPv4-mapped IPv6 addresses.
254
   * Linux kernel, NetBSD, FreeBSD and Darwin: default is off;
255
   * Windows Vista and later: default is on;
256
   * DragonFly BSD: acts like off, and dummy setting;
257
   * OpenBSD and earlier Windows: unsupported.
258
   * Linux: controlled by /proc/sys/net/ipv6/bindv6only.
259
   */
260
  if (domain == PF_INET6) {
261
    int on = 0;
262
    (void)setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
263
  }
264
#endif
265
266
0
  return s;
267
268
0
fail:
269
0
  default_aclose(s, user_data);
270
0
  return ARES_SOCKET_BAD;
271
0
}
272
273
static int default_asetsockopt(ares_socket_t sock, ares_socket_opt_t opt,
274
                               const void *val, ares_socklen_t val_size,
275
                               void *user_data)
276
0
{
277
0
  switch (opt) {
278
0
    case ARES_SOCKET_OPT_SENDBUF_SIZE:
279
0
      if (val_size != sizeof(int)) {
280
0
        SET_SOCKERRNO(EINVAL);
281
0
        return -1;
282
0
      }
283
0
      return setsockopt(sock, SOL_SOCKET, SO_SNDBUF, val, val_size);
284
285
0
    case ARES_SOCKET_OPT_RECVBUF_SIZE:
286
0
      if (val_size != sizeof(int)) {
287
0
        SET_SOCKERRNO(EINVAL);
288
0
        return -1;
289
0
      }
290
0
      return setsockopt(sock, SOL_SOCKET, SO_RCVBUF, val, val_size);
291
292
0
    case ARES_SOCKET_OPT_BIND_DEVICE:
293
      /* Count the number of characters before NULL terminator then
294
       * validate those are all printable */
295
0
      if (!ares_str_isprint(val, ares_strnlen(val, (size_t)val_size))) {
296
0
        SET_SOCKERRNO(EINVAL);
297
0
        return -1;
298
0
      }
299
0
#ifdef SO_BINDTODEVICE
300
0
      return setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, val, val_size);
301
#else
302
      SET_SOCKERRNO(ENOSYS);
303
      return -1;
304
#endif
305
306
0
    case ARES_SOCKET_OPT_TCP_FASTOPEN:
307
0
      if (val_size != sizeof(ares_bool_t)) {
308
0
        SET_SOCKERRNO(EINVAL);
309
0
        return -1;
310
0
      }
311
0
#if defined(TFO_CLIENT_SOCKOPT)
312
0
      {
313
0
        int                oval;
314
0
        const ares_bool_t *pval = val;
315
0
        oval                    = (int)*pval;
316
0
        return setsockopt(sock, IPPROTO_TCP, TFO_CLIENT_SOCKOPT, (void *)&oval,
317
0
                          sizeof(oval));
318
0
      }
319
#elif TFO_SUPPORTED
320
      return 0;
321
#else
322
      SET_SOCKERRNO(ENOSYS);
323
      return -1;
324
#endif
325
0
  }
326
327
0
  (void)user_data;
328
0
  SET_SOCKERRNO(ENOSYS);
329
0
  return -1;
330
0
}
331
332
static int default_aconnect(ares_socket_t sock, const struct sockaddr *address,
333
                            ares_socklen_t address_len, unsigned int flags,
334
                            void *user_data)
335
0
{
336
0
  (void)user_data;
337
338
#if defined(TFO_SKIP_CONNECT) && TFO_SKIP_CONNECT
339
  if (flags & ARES_SOCKET_CONN_TCP_FASTOPEN) {
340
    return 0;
341
  }
342
  return connect(sock, address, address_len);
343
#elif defined(TFO_USE_CONNECTX) && TFO_USE_CONNECTX
344
  if (flags & ARES_SOCKET_CONN_TCP_FASTOPEN) {
345
    sa_endpoints_t endpoints;
346
347
    memset(&endpoints, 0, sizeof(endpoints));
348
    endpoints.sae_dstaddr    = address;
349
    endpoints.sae_dstaddrlen = address_len;
350
351
    return connectx(sock, &endpoints, SAE_ASSOCID_ANY,
352
                    CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE,
353
                    NULL, 0, NULL, NULL);
354
  } else {
355
    return connect(sock, address, address_len);
356
  }
357
#else
358
0
  (void)flags;
359
0
  return connect(sock, address, address_len);
360
0
#endif
361
0
}
362
363
static ares_ssize_t default_arecvfrom(ares_socket_t sock, void *buffer,
364
                                      size_t length, int flags,
365
                                      struct sockaddr *address,
366
                                      ares_socklen_t  *address_len,
367
                                      void            *user_data)
368
0
{
369
0
  (void)user_data;
370
371
0
#ifdef HAVE_RECVFROM
372
0
  return (ares_ssize_t)recvfrom(sock, buffer, (RECVFROM_TYPE_ARG3)length, flags,
373
0
                                address, address_len);
374
#else
375
  if (address != NULL && address_len != NULL) {
376
    memset(address, 0, (size_t)*address_len);
377
    address->sa_family = AF_UNSPEC;
378
  }
379
  return (ares_ssize_t)recv(sock, buffer, (RECVFROM_TYPE_ARG3)length, flags);
380
#endif
381
0
}
382
383
static ares_ssize_t default_asendto(ares_socket_t sock, const void *buffer,
384
                                    size_t length, int flags,
385
                                    const struct sockaddr *address,
386
                                    ares_socklen_t address_len, void *user_data)
387
0
{
388
0
  (void)user_data;
389
390
0
  if (address != NULL) {
391
0
#ifdef HAVE_SENDTO
392
0
    return (ares_ssize_t)sendto((SEND_TYPE_ARG1)sock, (SEND_TYPE_ARG2)buffer,
393
0
                                (SEND_TYPE_ARG3)length, (SEND_TYPE_ARG4)flags,
394
0
                                address, address_len);
395
#else
396
    (void)address_len;
397
#endif
398
0
  }
399
400
0
  return (ares_ssize_t)send((SEND_TYPE_ARG1)sock, (SEND_TYPE_ARG2)buffer,
401
0
                            (SEND_TYPE_ARG3)length, (SEND_TYPE_ARG4)flags);
402
0
}
403
404
static int default_agetsockname(ares_socket_t sock, struct sockaddr *address,
405
                                ares_socklen_t *address_len, void *user_data)
406
0
{
407
0
  (void)user_data;
408
0
  return getsockname(sock, address, address_len);
409
0
}
410
411
static int default_abind(ares_socket_t sock, unsigned int flags,
412
                         const struct sockaddr *address, socklen_t address_len,
413
                         void *user_data)
414
0
{
415
0
  (void)user_data;
416
417
0
#ifdef IP_BIND_ADDRESS_NO_PORT
418
0
  if (flags & ARES_SOCKET_BIND_TCP && flags & ARES_SOCKET_BIND_CLIENT) {
419
0
    int opt = 1;
420
0
    (void)setsockopt(sock, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &opt, sizeof(opt));
421
0
  }
422
#else
423
  (void)flags;
424
#endif
425
426
0
  return bind(sock, address, address_len);
427
0
}
428
429
static unsigned int default_aif_nametoindex(const char *ifname, void *user_data)
430
565
{
431
565
  (void)user_data;
432
565
  return ares_os_if_nametoindex(ifname);
433
565
}
434
435
static const char *default_aif_indextoname(unsigned int ifindex,
436
                                           char        *ifname_buf,
437
                                           size_t       ifname_buf_len,
438
                                           void        *user_data)
439
541
{
440
541
  (void)user_data;
441
541
  return ares_os_if_indextoname(ifindex, ifname_buf, ifname_buf_len);
442
541
}
443
444
static const struct ares_socket_functions_ex default_socket_functions = {
445
  1,
446
  ARES_SOCKFUNC_FLAG_NONBLOCKING,
447
  default_asocket,
448
  default_aclose,
449
  default_asetsockopt,
450
  default_aconnect,
451
  default_arecvfrom,
452
  default_asendto,
453
  default_agetsockname,
454
  default_abind,
455
  default_aif_nametoindex,
456
  default_aif_indextoname
457
};
458
459
void ares_set_socket_functions_def(ares_channel_t *channel)
460
4.29k
{
461
4.29k
  ares_set_socket_functions_ex(channel, &default_socket_functions, NULL);
462
4.29k
}
463
464
static int legacycb_aclose(ares_socket_t sock, void *user_data)
465
0
{
466
0
  ares_channel_t *channel = user_data;
467
468
0
  if (channel->legacy_sock_funcs != NULL &&
469
0
      channel->legacy_sock_funcs->aclose != NULL) {
470
0
    return channel->legacy_sock_funcs->aclose(
471
0
      sock, channel->legacy_sock_funcs_cb_data);
472
0
  }
473
474
0
  return default_aclose(sock, NULL);
475
0
}
476
477
static ares_socket_t legacycb_asocket(int domain, int type, int protocol,
478
                                      void *user_data)
479
0
{
480
0
  ares_channel_t *channel = user_data;
481
482
0
  if (channel->legacy_sock_funcs != NULL &&
483
0
      channel->legacy_sock_funcs->asocket != NULL) {
484
0
    return channel->legacy_sock_funcs->asocket(
485
0
      domain, type, protocol, channel->legacy_sock_funcs_cb_data);
486
0
  }
487
488
0
  return default_asocket(domain, type, protocol, NULL);
489
0
}
490
491
static int legacycb_asetsockopt(ares_socket_t sock, ares_socket_opt_t opt,
492
                                const void *val, ares_socklen_t val_size,
493
                                void *user_data)
494
0
{
495
0
  (void)sock;
496
0
  (void)opt;
497
0
  (void)val;
498
0
  (void)val_size;
499
0
  (void)user_data;
500
0
  SET_SOCKERRNO(ENOSYS);
501
0
  return -1;
502
0
}
503
504
static int legacycb_aconnect(ares_socket_t sock, const struct sockaddr *address,
505
                             ares_socklen_t address_len, unsigned int flags,
506
                             void *user_data)
507
0
{
508
0
  ares_channel_t *channel = user_data;
509
510
0
  if (channel->legacy_sock_funcs != NULL &&
511
0
      channel->legacy_sock_funcs->aconnect != NULL) {
512
0
    return channel->legacy_sock_funcs->aconnect(
513
0
      sock, address, address_len, channel->legacy_sock_funcs_cb_data);
514
0
  }
515
516
0
  return default_aconnect(sock, address, address_len, flags, NULL);
517
0
}
518
519
static ares_ssize_t legacycb_arecvfrom(ares_socket_t sock, void *buffer,
520
                                       size_t length, int flags,
521
                                       struct sockaddr *address,
522
                                       ares_socklen_t  *address_len,
523
                                       void            *user_data)
524
0
{
525
0
  ares_channel_t *channel = user_data;
526
527
0
  if (channel->legacy_sock_funcs != NULL &&
528
0
      channel->legacy_sock_funcs->arecvfrom != NULL) {
529
0
    if (address != NULL && address_len != NULL) {
530
0
      memset(address, 0, (size_t)*address_len);
531
0
      address->sa_family = AF_UNSPEC;
532
0
    }
533
0
    return channel->legacy_sock_funcs->arecvfrom(
534
0
      sock, buffer, length, flags, address, address_len,
535
0
      channel->legacy_sock_funcs_cb_data);
536
0
  }
537
538
0
  return default_arecvfrom(sock, buffer, length, flags, address, address_len,
539
0
                           NULL);
540
0
}
541
542
static ares_ssize_t legacycb_asendto(ares_socket_t sock, const void *buffer,
543
                                     size_t length, int flags,
544
                                     const struct sockaddr *address,
545
                                     ares_socklen_t         address_len,
546
                                     void                  *user_data)
547
0
{
548
0
  ares_channel_t *channel = user_data;
549
550
0
  if (channel->legacy_sock_funcs != NULL &&
551
0
      channel->legacy_sock_funcs->asendv != NULL) {
552
0
    struct iovec vec;
553
0
    vec.iov_base = (void *)((size_t)buffer); /* Cast off const */
554
0
    vec.iov_len  = length;
555
0
    return channel->legacy_sock_funcs->asendv(
556
0
      sock, &vec, 1, channel->legacy_sock_funcs_cb_data);
557
0
  }
558
559
0
  return default_asendto(sock, buffer, length, flags, address, address_len,
560
0
                         NULL);
561
0
}
562
563
564
static const struct ares_socket_functions_ex legacy_socket_functions = {
565
  1,
566
  0,
567
  legacycb_asocket,
568
  legacycb_aclose,
569
  legacycb_asetsockopt,
570
  legacycb_aconnect,
571
  legacycb_arecvfrom,
572
  legacycb_asendto,
573
  NULL, /* agetsockname */
574
  NULL, /* abind */
575
  NULL, /* aif_nametoindex */
576
  NULL  /* aif_indextoname */
577
};
578
579
void ares_set_socket_functions(ares_channel_t                     *channel,
580
                               const struct ares_socket_functions *funcs,
581
                               void                               *data)
582
0
{
583
0
  if (channel == NULL || channel->optmask & ARES_OPT_EVENT_THREAD) {
584
0
    return;
585
0
  }
586
587
0
  channel->legacy_sock_funcs         = funcs;
588
0
  channel->legacy_sock_funcs_cb_data = data;
589
0
  ares_set_socket_functions_ex(channel, &legacy_socket_functions, channel);
590
0
}