Coverage Report

Created: 2025-07-11 06:33

/src/PROJ/curl/lib/select.c
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
#include "curl_setup.h"
26
27
#if !defined(HAVE_SELECT) && !defined(HAVE_POLL)
28
#error "We cannot compile without select() or poll() support."
29
#endif
30
31
#include <limits.h>
32
33
#ifdef HAVE_SYS_SELECT_H
34
#include <sys/select.h>
35
#elif defined(HAVE_UNISTD_H)
36
#include <unistd.h>
37
#endif
38
39
#include <curl/curl.h>
40
41
#include "urldata.h"
42
#include "connect.h"
43
#include "select.h"
44
#include "curlx/timediff.h"
45
#include "curlx/wait.h"
46
#include "curlx/warnless.h"
47
/* The last 3 #include files should be in this order */
48
#include "curl_printf.h"
49
#include "curl_memory.h"
50
#include "memdebug.h"
51
52
#ifndef HAVE_POLL
53
/*
54
 * This is a wrapper around select() to aid in Windows compatibility. A
55
 * negative timeout value makes this function wait indefinitely, unless no
56
 * valid file descriptor is given, when this happens the negative timeout is
57
 * ignored and the function times out immediately.
58
 *
59
 * Return values:
60
 *   -1 = system call error or fd >= FD_SETSIZE
61
 *    0 = timeout
62
 *    N = number of signalled file descriptors
63
 */
64
static int our_select(curl_socket_t maxfd,   /* highest socket number */
65
                      fd_set *fds_read,      /* sockets ready for reading */
66
                      fd_set *fds_write,     /* sockets ready for writing */
67
                      fd_set *fds_err,       /* sockets with errors */
68
                      timediff_t timeout_ms) /* milliseconds to wait */
69
{
70
  struct timeval pending_tv;
71
  struct timeval *ptimeout;
72
73
#ifdef USE_WINSOCK
74
  /* Winsock select() cannot handle zero events. See the comment below. */
75
  if((!fds_read || fds_read->fd_count == 0) &&
76
     (!fds_write || fds_write->fd_count == 0) &&
77
     (!fds_err || fds_err->fd_count == 0)) {
78
    /* no sockets, just wait */
79
    return curlx_wait_ms(timeout_ms);
80
  }
81
#endif
82
83
  ptimeout = curlx_mstotv(&pending_tv, timeout_ms);
84
85
#ifdef USE_WINSOCK
86
  /* Winsock select() must not be called with an fd_set that contains zero
87
    fd flags, or it will return WSAEINVAL. But, it also cannot be called
88
    with no fd_sets at all!  From the documentation:
89
90
    Any two of the parameters, readfds, writefds, or exceptfds, can be
91
    given as null. At least one must be non-null, and any non-null
92
    descriptor set must contain at least one handle to a socket.
93
94
    It is unclear why Winsock does not just handle this for us instead of
95
    calling this an error. Luckily, with Winsock, we can _also_ ask how
96
    many bits are set on an fd_set. So, let's just check it beforehand.
97
  */
98
  return select((int)maxfd + 1,
99
                fds_read && fds_read->fd_count ? fds_read : NULL,
100
                fds_write && fds_write->fd_count ? fds_write : NULL,
101
                fds_err && fds_err->fd_count ? fds_err : NULL, ptimeout);
102
#else
103
  return select((int)maxfd + 1, fds_read, fds_write, fds_err, ptimeout);
104
#endif
105
}
106
107
#endif
108
109
/*
110
 * Wait for read or write events on a set of file descriptors. It uses poll()
111
 * when poll() is available, in order to avoid limits with FD_SETSIZE,
112
 * otherwise select() is used. An error is returned if select() is being used
113
 * and a file descriptor is too large for FD_SETSIZE.
114
 *
115
 * A negative timeout value makes this function wait indefinitely, unless no
116
 * valid file descriptor is given, when this happens the negative timeout is
117
 * ignored and the function times out immediately.
118
 *
119
 * Return values:
120
 *   -1 = system call error or fd >= FD_SETSIZE
121
 *    0 = timeout
122
 *    [bitmask] = action as described below
123
 *
124
 * CURL_CSELECT_IN - first socket is readable
125
 * CURL_CSELECT_IN2 - second socket is readable
126
 * CURL_CSELECT_OUT - write socket is writable
127
 * CURL_CSELECT_ERR - an error condition occurred
128
 */
129
int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */
130
                      curl_socket_t readfd1,
131
                      curl_socket_t writefd, /* socket to write to */
132
                      timediff_t timeout_ms) /* milliseconds to wait */
133
0
{
134
0
  struct pollfd pfd[3];
135
0
  int num;
136
0
  int r;
137
138
0
  if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) &&
139
0
     (writefd == CURL_SOCKET_BAD)) {
140
    /* no sockets, just wait */
141
0
    return curlx_wait_ms(timeout_ms);
142
0
  }
143
144
  /* Avoid initial timestamp, avoid curlx_now() call, when elapsed
145
     time in this function does not need to be measured. This happens
146
     when function is called with a zero timeout or a negative timeout
147
     value indicating a blocking call should be performed. */
148
149
0
  num = 0;
150
0
  if(readfd0 != CURL_SOCKET_BAD) {
151
0
    pfd[num].fd = readfd0;
152
0
    pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
153
0
    pfd[num].revents = 0;
154
0
    num++;
155
0
  }
156
0
  if(readfd1 != CURL_SOCKET_BAD) {
157
0
    pfd[num].fd = readfd1;
158
0
    pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
159
0
    pfd[num].revents = 0;
160
0
    num++;
161
0
  }
162
0
  if(writefd != CURL_SOCKET_BAD) {
163
0
    pfd[num].fd = writefd;
164
0
    pfd[num].events = POLLWRNORM|POLLOUT|POLLPRI;
165
0
    pfd[num].revents = 0;
166
0
    num++;
167
0
  }
168
169
0
  r = Curl_poll(pfd, (unsigned int)num, timeout_ms);
170
0
  if(r <= 0)
171
0
    return r;
172
173
0
  r = 0;
174
0
  num = 0;
175
0
  if(readfd0 != CURL_SOCKET_BAD) {
176
0
    if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
177
0
      r |= CURL_CSELECT_IN;
178
0
    if(pfd[num].revents & (POLLPRI|POLLNVAL))
179
0
      r |= CURL_CSELECT_ERR;
180
0
    num++;
181
0
  }
182
0
  if(readfd1 != CURL_SOCKET_BAD) {
183
0
    if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
184
0
      r |= CURL_CSELECT_IN2;
185
0
    if(pfd[num].revents & (POLLPRI|POLLNVAL))
186
0
      r |= CURL_CSELECT_ERR;
187
0
    num++;
188
0
  }
189
0
  if(writefd != CURL_SOCKET_BAD) {
190
0
    if(pfd[num].revents & (POLLWRNORM|POLLOUT))
191
0
      r |= CURL_CSELECT_OUT;
192
0
    if(pfd[num].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL))
193
0
      r |= CURL_CSELECT_ERR;
194
0
  }
195
196
0
  return r;
197
0
}
198
199
/*
200
 * This is a wrapper around poll(). If poll() does not exist, then
201
 * select() is used instead. An error is returned if select() is
202
 * being used and a file descriptor is too large for FD_SETSIZE.
203
 * A negative timeout value makes this function wait indefinitely,
204
 * unless no valid file descriptor is given, when this happens the
205
 * negative timeout is ignored and the function times out immediately.
206
 *
207
 * Return values:
208
 *   -1 = system call error or fd >= FD_SETSIZE
209
 *    0 = timeout
210
 *    N = number of structures with non zero revent fields
211
 */
212
int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms)
213
0
{
214
0
#ifdef HAVE_POLL
215
0
  int pending_ms;
216
#else
217
  fd_set fds_read;
218
  fd_set fds_write;
219
  fd_set fds_err;
220
  curl_socket_t maxfd;
221
#endif
222
0
  bool fds_none = TRUE;
223
0
  unsigned int i;
224
0
  int r;
225
226
0
  if(ufds) {
227
0
    for(i = 0; i < nfds; i++) {
228
0
      if(ufds[i].fd != CURL_SOCKET_BAD) {
229
0
        fds_none = FALSE;
230
0
        break;
231
0
      }
232
0
    }
233
0
  }
234
0
  if(fds_none) {
235
    /* no sockets, just wait */
236
0
    return curlx_wait_ms(timeout_ms);
237
0
  }
238
239
  /* Avoid initial timestamp, avoid curlx_now() call, when elapsed
240
     time in this function does not need to be measured. This happens
241
     when function is called with a zero timeout or a negative timeout
242
     value indicating a blocking call should be performed. */
243
244
0
#ifdef HAVE_POLL
245
246
  /* prevent overflow, timeout_ms is typecast to int. */
247
0
#if TIMEDIFF_T_MAX > INT_MAX
248
0
  if(timeout_ms > INT_MAX)
249
0
    timeout_ms = INT_MAX;
250
0
#endif
251
0
  if(timeout_ms > 0)
252
0
    pending_ms = (int)timeout_ms;
253
0
  else if(timeout_ms < 0)
254
0
    pending_ms = -1;
255
0
  else
256
0
    pending_ms = 0;
257
0
  r = poll(ufds, nfds, pending_ms);
258
0
  if(r <= 0) {
259
0
    if((r == -1) && (SOCKERRNO == SOCKEINTR))
260
      /* make EINTR from select or poll not a "lethal" error */
261
0
      r = 0;
262
0
    return r;
263
0
  }
264
265
0
  for(i = 0; i < nfds; i++) {
266
0
    if(ufds[i].fd == CURL_SOCKET_BAD)
267
0
      continue;
268
0
    if(ufds[i].revents & POLLHUP)
269
0
      ufds[i].revents |= POLLIN;
270
0
    if(ufds[i].revents & POLLERR)
271
0
      ufds[i].revents |= POLLIN|POLLOUT;
272
0
  }
273
274
#else  /* HAVE_POLL */
275
276
  FD_ZERO(&fds_read);
277
  FD_ZERO(&fds_write);
278
  FD_ZERO(&fds_err);
279
  maxfd = (curl_socket_t)-1;
280
281
  for(i = 0; i < nfds; i++) {
282
    ufds[i].revents = 0;
283
    if(ufds[i].fd == CURL_SOCKET_BAD)
284
      continue;
285
    VERIFY_SOCK(ufds[i].fd);
286
    if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI|
287
                         POLLRDNORM|POLLWRNORM|POLLRDBAND)) {
288
      if(ufds[i].fd > maxfd)
289
        maxfd = ufds[i].fd;
290
      if(ufds[i].events & (POLLRDNORM|POLLIN))
291
        FD_SET(ufds[i].fd, &fds_read);
292
      if(ufds[i].events & (POLLWRNORM|POLLOUT))
293
        FD_SET(ufds[i].fd, &fds_write);
294
      if(ufds[i].events & (POLLRDBAND|POLLPRI))
295
        FD_SET(ufds[i].fd, &fds_err);
296
    }
297
  }
298
299
  /*
300
     Note also that Winsock ignores the first argument, so we do not worry
301
     about the fact that maxfd is computed incorrectly with Winsock (since
302
     curl_socket_t is unsigned in such cases and thus -1 is the largest
303
     value).
304
  */
305
  r = our_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms);
306
  if(r <= 0) {
307
    if((r == -1) && (SOCKERRNO == SOCKEINTR))
308
      /* make EINTR from select or poll not a "lethal" error */
309
      r = 0;
310
    return r;
311
  }
312
313
  r = 0;
314
  for(i = 0; i < nfds; i++) {
315
    ufds[i].revents = 0;
316
    if(ufds[i].fd == CURL_SOCKET_BAD)
317
      continue;
318
    if(FD_ISSET(ufds[i].fd, &fds_read)) {
319
      if(ufds[i].events & POLLRDNORM)
320
        ufds[i].revents |= POLLRDNORM;
321
      if(ufds[i].events & POLLIN)
322
        ufds[i].revents |= POLLIN;
323
    }
324
    if(FD_ISSET(ufds[i].fd, &fds_write)) {
325
      if(ufds[i].events & POLLWRNORM)
326
        ufds[i].revents |= POLLWRNORM;
327
      if(ufds[i].events & POLLOUT)
328
        ufds[i].revents |= POLLOUT;
329
    }
330
    if(FD_ISSET(ufds[i].fd, &fds_err)) {
331
      if(ufds[i].events & POLLRDBAND)
332
        ufds[i].revents |= POLLRDBAND;
333
      if(ufds[i].events & POLLPRI)
334
        ufds[i].revents |= POLLPRI;
335
    }
336
    if(ufds[i].revents)
337
      r++;
338
  }
339
340
#endif  /* HAVE_POLL */
341
342
0
  return r;
343
0
}
344
345
void Curl_pollfds_init(struct curl_pollfds *cpfds,
346
                       struct pollfd *static_pfds,
347
                       unsigned int static_count)
348
0
{
349
0
  DEBUGASSERT(cpfds);
350
0
  memset(cpfds, 0, sizeof(*cpfds));
351
0
  if(static_pfds && static_count) {
352
0
    cpfds->pfds = static_pfds;
353
0
    cpfds->count = static_count;
354
0
  }
355
0
}
356
357
void Curl_pollfds_reset(struct curl_pollfds *cpfds)
358
0
{
359
0
  cpfds->n = 0;
360
0
}
361
362
void Curl_pollfds_cleanup(struct curl_pollfds *cpfds)
363
0
{
364
0
  DEBUGASSERT(cpfds);
365
0
  if(cpfds->allocated_pfds) {
366
0
    free(cpfds->pfds);
367
0
  }
368
0
  memset(cpfds, 0, sizeof(*cpfds));
369
0
}
370
371
static CURLcode cpfds_increase(struct curl_pollfds *cpfds, unsigned int inc)
372
0
{
373
0
  struct pollfd *new_fds;
374
0
  unsigned int new_count = cpfds->count + inc;
375
376
0
  new_fds = calloc(new_count, sizeof(struct pollfd));
377
0
  if(!new_fds)
378
0
    return CURLE_OUT_OF_MEMORY;
379
380
0
  memcpy(new_fds, cpfds->pfds, cpfds->count * sizeof(struct pollfd));
381
0
  if(cpfds->allocated_pfds)
382
0
    free(cpfds->pfds);
383
0
  cpfds->pfds = new_fds;
384
0
  cpfds->count = new_count;
385
0
  cpfds->allocated_pfds = TRUE;
386
0
  return CURLE_OK;
387
0
}
388
389
static CURLcode cpfds_add_sock(struct curl_pollfds *cpfds,
390
                               curl_socket_t sock, short events, bool fold)
391
0
{
392
0
  int i;
393
394
0
  if(fold && cpfds->n <= INT_MAX) {
395
0
    for(i = (int)cpfds->n - 1; i >= 0; --i) {
396
0
      if(sock == cpfds->pfds[i].fd) {
397
0
        cpfds->pfds[i].events |= events;
398
0
        return CURLE_OK;
399
0
      }
400
0
    }
401
0
  }
402
  /* not folded, add new entry */
403
0
  if(cpfds->n >= cpfds->count) {
404
0
    if(cpfds_increase(cpfds, 100))
405
0
      return CURLE_OUT_OF_MEMORY;
406
0
  }
407
0
  cpfds->pfds[cpfds->n].fd = sock;
408
0
  cpfds->pfds[cpfds->n].events = events;
409
0
  ++cpfds->n;
410
0
  return CURLE_OK;
411
0
}
412
413
CURLcode Curl_pollfds_add_sock(struct curl_pollfds *cpfds,
414
                               curl_socket_t sock, short events)
415
0
{
416
0
  return cpfds_add_sock(cpfds, sock, events, FALSE);
417
0
}
418
419
CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds,
420
                             struct easy_pollset *ps)
421
0
{
422
0
  size_t i;
423
424
0
  DEBUGASSERT(cpfds);
425
0
  DEBUGASSERT(ps);
426
0
  for(i = 0; i < ps->num; i++) {
427
0
    short events = 0;
428
0
    if(ps->actions[i] & CURL_POLL_IN)
429
0
      events |= POLLIN;
430
0
    if(ps->actions[i] & CURL_POLL_OUT)
431
0
      events |= POLLOUT;
432
0
    if(events) {
433
0
      if(cpfds_add_sock(cpfds, ps->sockets[i], events, TRUE))
434
0
        return CURLE_OUT_OF_MEMORY;
435
0
    }
436
0
  }
437
0
  return CURLE_OK;
438
0
}
439
440
void Curl_waitfds_init(struct Curl_waitfds *cwfds,
441
                       struct curl_waitfd *static_wfds,
442
                       unsigned int static_count)
443
0
{
444
0
  DEBUGASSERT(cwfds);
445
0
  DEBUGASSERT(static_wfds || !static_count);
446
0
  memset(cwfds, 0, sizeof(*cwfds));
447
0
  cwfds->wfds = static_wfds;
448
0
  cwfds->count = static_count;
449
0
}
450
451
static unsigned int cwfds_add_sock(struct Curl_waitfds *cwfds,
452
                                   curl_socket_t sock, short events)
453
0
{
454
0
  int i;
455
0
  if(!cwfds->wfds) {
456
0
    DEBUGASSERT(!cwfds->count && !cwfds->n);
457
0
    return 1;
458
0
  }
459
0
  if(cwfds->n <= INT_MAX) {
460
0
    for(i = (int)cwfds->n - 1; i >= 0; --i) {
461
0
      if(sock == cwfds->wfds[i].fd) {
462
0
        cwfds->wfds[i].events |= events;
463
0
        return 0;
464
0
      }
465
0
    }
466
0
  }
467
  /* not folded, add new entry */
468
0
  if(cwfds->n < cwfds->count) {
469
0
    cwfds->wfds[cwfds->n].fd = sock;
470
0
    cwfds->wfds[cwfds->n].events = events;
471
0
    ++cwfds->n;
472
0
  }
473
0
  return 1;
474
0
}
475
476
unsigned int Curl_waitfds_add_ps(struct Curl_waitfds *cwfds,
477
                                 struct easy_pollset *ps)
478
0
{
479
0
  size_t i;
480
0
  unsigned int need = 0;
481
482
0
  DEBUGASSERT(cwfds);
483
0
  DEBUGASSERT(ps);
484
0
  for(i = 0; i < ps->num; i++) {
485
0
    short events = 0;
486
0
    if(ps->actions[i] & CURL_POLL_IN)
487
0
      events |= CURL_WAIT_POLLIN;
488
0
    if(ps->actions[i] & CURL_POLL_OUT)
489
0
      events |= CURL_WAIT_POLLOUT;
490
0
    if(events)
491
0
      need += cwfds_add_sock(cwfds, ps->sockets[i], events);
492
0
  }
493
0
  return need;
494
0
}