Coverage Report

Created: 2026-07-15 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/src/poll.c
Line
Count
Source
1
/*
2
 * poll.c - poll wrapper
3
 *
4
 * This file is part of the SSH Library
5
 *
6
 * Copyright (c) 2009-2013 by Andreas Schneider <asn@cryptomilk.org>
7
 * Copyright (c) 2003-2013 by Aris Adamantiadis
8
 * Copyright (c) 2009 Aleksandar Kanchev
9
 *
10
 * The SSH Library is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU Lesser General Public License as published by
12
 * the Free Software Foundation; either version 2.1 of the License, or (at your
13
 * option) any later version.
14
 *
15
 * The SSH Library is distributed in the hope that it will be useful, but
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
18
 * License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public License
21
 * along with the SSH Library; see the file COPYING.  If not, write to
22
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
23
 * MA 02111-1307, USA.
24
 */
25
26
#include "config.h"
27
28
#include <errno.h>
29
#include <stdlib.h>
30
31
#include "libssh/priv.h"
32
#include "libssh/libssh.h"
33
#include "libssh/poll.h"
34
#include "libssh/socket.h"
35
#include "libssh/session.h"
36
#include "libssh/misc.h"
37
#ifdef WITH_SERVER
38
#include "libssh/server.h"
39
#endif
40
41
42
#ifndef SSH_POLL_CTX_CHUNK
43
0
#define SSH_POLL_CTX_CHUNK      5
44
#endif
45
46
/**
47
 * @defgroup libssh_poll The SSH poll functions
48
 * @ingroup libssh
49
 *
50
 * Add a generic way to handle sockets asynchronously.
51
 *
52
 * It's based on poll objects, each of which store a socket, its events and a
53
 * callback, which gets called whenever an event is set. The poll objects are
54
 * attached to a poll context, which should be allocated on a per thread basis.
55
 *
56
 * Polling the poll context will poll all the attached poll objects and call
57
 * their callbacks (handlers) if any of the socket events are set. This should
58
 * be done within the main loop of an application.
59
 *
60
 * @{
61
 */
62
63
struct ssh_poll_handle_struct {
64
    ssh_poll_ctx ctx;
65
    ssh_session session;
66
    union {
67
        socket_t fd;
68
        size_t idx;
69
    } x;
70
    short events;
71
    uint32_t lock_cnt;
72
    ssh_poll_callback cb;
73
    void *cb_data;
74
};
75
76
struct ssh_poll_ctx_struct {
77
    ssh_poll_handle *pollptrs;
78
    ssh_pollfd_t *pollfds;
79
    size_t polls_allocated;
80
    size_t polls_used;
81
    size_t chunk_size;
82
};
83
84
#ifdef HAVE_POLL
85
#include <poll.h>
86
87
/** @internal
88
 * @brief Initialize the poll subsystem. No-op when native poll is available.
89
 */
90
void ssh_poll_init(void)
91
2
{
92
2
    return;
93
2
}
94
95
/** @internal
96
 * @brief Clean up the poll subsystem. No-op when native poll is available.
97
 */
98
void ssh_poll_cleanup(void)
99
0
{
100
0
    return;
101
0
}
102
103
/** @internal
104
 * @brief Wait for events on a set of file descriptors.
105
 *
106
 * @param fds      Array of pollfd structures specifying the file descriptors.
107
 * @param nfds     Number of file descriptors in the array.
108
 * @param timeout  Timeout in milliseconds, `SSH_TIMEOUT_INFINITE`
109
 *                 to block indefinitely.
110
 *
111
 * @return         Number of file descriptors with events, 0 on timeout,
112
 *                 -1 on error.
113
 */
114
int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
115
0
{
116
0
    return poll((struct pollfd *)fds, nfds, timeout);
117
0
}
118
119
#else /* HAVE_POLL */
120
121
typedef int (*poll_fn)(ssh_pollfd_t *, nfds_t, int);
122
static poll_fn ssh_poll_emu;
123
124
#include <sys/types.h>
125
#include <stdbool.h>
126
127
#ifdef _WIN32
128
#ifndef STRICT
129
#define STRICT
130
#endif /* STRICT */
131
132
#include <time.h>
133
#include <windows.h>
134
#include <winsock2.h>
135
#else /* _WIN32 */
136
#include <sys/select.h>
137
#include <sys/socket.h>
138
139
# ifdef HAVE_SYS_TIME_H
140
#  include <sys/time.h>
141
# endif
142
143
#endif /* _WIN32 */
144
145
#ifdef HAVE_UNISTD_H
146
#include <unistd.h>
147
#endif
148
149
static bool bsd_socket_not_connected(int sock_err)
150
{
151
    switch (sock_err) {
152
#ifdef _WIN32
153
    case WSAENOTCONN:
154
#else
155
    case ENOTCONN:
156
#endif
157
        return true;
158
    default:
159
        return false;
160
    }
161
162
    return false;
163
}
164
165
static bool bsd_socket_reset(int sock_err)
166
{
167
    switch (sock_err) {
168
#ifdef _WIN32
169
    case WSAECONNABORTED:
170
    case WSAECONNRESET:
171
    case WSAENETRESET:
172
    case WSAESHUTDOWN:
173
    case WSAECONNREFUSED:
174
    case WSAETIMEDOUT:
175
#else
176
    case ECONNABORTED:
177
    case ECONNRESET:
178
    case ENETRESET:
179
    case ESHUTDOWN:
180
#endif
181
        return true;
182
    default:
183
        return false;
184
    }
185
186
    return false;
187
}
188
189
static short bsd_socket_compute_revents(int fd, short events)
190
{
191
    int save_errno = errno;
192
    int sock_errno = errno;
193
    char data[64] = {0};
194
    short revents = 0;
195
    int flags = MSG_PEEK;
196
    int ret;
197
198
#ifdef MSG_NOSIGNAL
199
    flags |= MSG_NOSIGNAL;
200
#endif
201
202
    /* support for POLLHUP */
203
#ifdef _WIN32
204
    WSASetLastError(0);
205
#endif
206
207
    ret = recv(fd, data, 64, flags);
208
209
    errno = save_errno;
210
211
#ifdef _WIN32
212
    sock_errno = WSAGetLastError();
213
    WSASetLastError(0);
214
#endif
215
216
    if (ret > 0 || bsd_socket_not_connected(sock_errno)) {
217
        revents = (POLLIN | POLLRDNORM) & events;
218
    } else if (ret == 0 || bsd_socket_reset(sock_errno)) {
219
        errno = sock_errno;
220
        revents = POLLHUP;
221
    } else {
222
        revents = POLLERR;
223
    }
224
225
    return revents;
226
}
227
228
/*
229
 * This is a poll(2)-emulation using select for systems not providing a native
230
 * poll implementation.
231
 *
232
 * Keep in mind that select is terribly inefficient. The interface is simply not
233
 * meant to be used with maximum descriptor value greater than, say, 32 or so.
234
 * With a value as high as 1024 on Linux you'll pay dearly in every single call.
235
 * poll() will be orders of magnitude faster.
236
 */
237
static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
238
{
239
    fd_set readfds, writefds, exceptfds;
240
    struct timeval tv, *ptv = NULL;
241
    socket_t max_fd;
242
    int rc;
243
    nfds_t i;
244
245
    if (fds == NULL) {
246
        errno = EFAULT;
247
        return -1;
248
    }
249
250
    ZERO_STRUCT(readfds);
251
    FD_ZERO(&readfds);
252
    ZERO_STRUCT(writefds);
253
    FD_ZERO(&writefds);
254
    ZERO_STRUCT(exceptfds);
255
    FD_ZERO(&exceptfds);
256
257
    /* compute fd_sets and find largest descriptor */
258
    for (rc = -1, max_fd = 0, i = 0; i < nfds; i++) {
259
        if (fds[i].fd == SSH_INVALID_SOCKET) {
260
            continue;
261
        }
262
#ifndef _WIN32
263
        if (fds[i].fd >= FD_SETSIZE) {
264
            rc = -1;
265
            break;
266
        }
267
#endif
268
269
        // we use the readfds to get POLLHUP and POLLERR, which are provided
270
        // even when not requested
271
        FD_SET(fds[i].fd, &readfds);
272
273
        if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
274
            FD_SET(fds[i].fd, &writefds);
275
        }
276
        if (fds[i].events & (POLLPRI | POLLRDBAND)) {
277
            FD_SET(fds[i].fd, &exceptfds);
278
        }
279
280
        if (fds[i].fd > max_fd) {
281
            max_fd = fds[i].fd;
282
            rc = 0;
283
        }
284
    }
285
286
    if (max_fd == SSH_INVALID_SOCKET || rc == -1) {
287
        errno = EINVAL;
288
        return -1;
289
    }
290
291
    if (timeout < 0) {
292
        ptv = NULL;
293
    } else {
294
        ptv = &tv;
295
        if (timeout == 0) {
296
            tv.tv_sec = 0;
297
            tv.tv_usec = 0;
298
        } else {
299
            tv.tv_sec = timeout / 1000;
300
            tv.tv_usec = (timeout % 1000) * 1000;
301
        }
302
    }
303
304
    rc = select(max_fd + 1, &readfds, &writefds, &exceptfds, ptv);
305
    if (rc < 0) {
306
        return -1;
307
    }
308
    /* A timeout occurred */
309
    if (rc == 0) {
310
        return 0;
311
    }
312
313
    for (rc = 0, i = 0; i < nfds; i++) {
314
        if (fds[i].fd >= 0) {
315
            fds[i].revents = 0;
316
317
            if (FD_ISSET(fds[i].fd, &readfds)) {
318
                fds[i].revents =
319
                    bsd_socket_compute_revents(fds[i].fd, fds[i].events);
320
            }
321
            if (FD_ISSET(fds[i].fd, &writefds)) {
322
                fds[i].revents |=
323
                    fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND);
324
            }
325
326
            if (FD_ISSET(fds[i].fd, &exceptfds)) {
327
                fds[i].revents |= fds[i].events & (POLLPRI | POLLRDBAND);
328
            }
329
330
            if (fds[i].revents != 0) {
331
                rc++;
332
            }
333
        } else {
334
            fds[i].revents = POLLNVAL;
335
        }
336
    }
337
338
    return rc;
339
}
340
341
/** @internal
342
 * @brief Initialize the poll subsystem using the BSD poll emulation.
343
 */
344
void ssh_poll_init(void)
345
{
346
    ssh_poll_emu = bsd_poll;
347
}
348
349
/** @internal
350
 * @brief Clean up the poll subsystem, resetting to the BSD poll emulation.
351
 */
352
void ssh_poll_cleanup(void)
353
{
354
    ssh_poll_emu = bsd_poll;
355
}
356
357
/** @internal
358
 * @brief Wait for events on a set of file descriptors.
359
 *
360
 * @param fds      Array of pollfd structures specifying the file descriptors.
361
 * @param nfds     Number of file descriptors in the array.
362
 * @param timeout  Timeout in milliseconds, `SSH_TIMEOUT_INFINITE`
363
 *                 to block indefinitely.
364
 *
365
 * @return         Number of file descriptors with events, 0 on timeout,
366
 *                 -1 on error.
367
 */
368
int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
369
{
370
    return (ssh_poll_emu)(fds, nfds, timeout);
371
}
372
373
#endif /* HAVE_POLL */
374
375
/**
376
 * @brief  Allocate a new poll object, which could be used within a poll
377
 * context.
378
 *
379
 * @param[in]  fd           Socket that will be polled.
380
 * @param[in]  events       Poll events that will be monitored for the socket.
381
 *                          i.e. POLLIN, POLLPRI, POLLOUT
382
 * @param[in]  cb           Function to be called if any of the events are set.
383
 *                          The prototype of cb is:
384
 *                          int (*ssh_poll_callback)(ssh_poll_handle p,
385
 *                                                   socket_t fd,
386
 *                                                   int revents,
387
 *                                                   void *userdata);
388
 * @param[in]  userdata     Userdata to be passed to the callback function.
389
 *                          NULL if not needed.
390
 *
391
 * @return                  A new poll object, NULL on error
392
 */
393
394
ssh_poll_handle
395
ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb, void *userdata)
396
0
{
397
0
    ssh_poll_handle p = NULL;
398
399
0
    p = malloc(sizeof(struct ssh_poll_handle_struct));
400
0
    if (p == NULL) {
401
0
        return NULL;
402
0
    }
403
0
    ZERO_STRUCTP(p);
404
405
0
    p->x.fd = fd;
406
0
    p->events = events;
407
0
    p->cb = cb;
408
0
    p->cb_data = userdata;
409
410
0
    return p;
411
0
}
412
413
/**
414
 * @brief  Free a poll object.
415
 *
416
 * @param  p            Pointer to an already allocated poll object.
417
 */
418
419
void ssh_poll_free(ssh_poll_handle p)
420
0
{
421
0
    if (p->ctx != NULL) {
422
0
        ssh_poll_ctx_remove(p->ctx, p);
423
0
        p->ctx = NULL;
424
0
    }
425
0
    SAFE_FREE(p);
426
0
}
427
428
/**
429
 * @brief  Get the poll context of a poll object.
430
 *
431
 * @param  p            Pointer to an already allocated poll object.
432
 *
433
 * @return              Poll context or NULL if the poll object isn't attached.
434
 */
435
ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p)
436
0
{
437
0
    return p->ctx;
438
0
}
439
440
/**
441
 * @brief  Get the events of a poll object.
442
 *
443
 * @param  p            Pointer to an already allocated poll object.
444
 *
445
 * @return              Poll events.
446
 */
447
short ssh_poll_get_events(ssh_poll_handle p)
448
0
{
449
0
    return p->events;
450
0
}
451
452
/**
453
 * @brief  Set the events of a poll object. The events will also be propagated
454
 *         to an associated poll context unless the fd is locked. In that case,
455
 *         only the POLLOUT can be set.
456
 *
457
 * @param  p            Pointer to an already allocated poll object.
458
 * @param  events       Poll events.
459
 */
460
void ssh_poll_set_events(ssh_poll_handle p, short events)
461
0
{
462
0
    p->events = events;
463
0
    if (p->ctx != NULL) {
464
0
        if (!ssh_poll_is_locked(p)) {
465
0
            p->ctx->pollfds[p->x.idx].events = events;
466
0
        } else if (!(p->ctx->pollfds[p->x.idx].events & POLLOUT)) {
467
            /* if locked, allow only setting POLLOUT to prevent recursive
468
             * callbacks */
469
0
            p->ctx->pollfds[p->x.idx].events = events & POLLOUT;
470
0
        }
471
0
    }
472
0
}
473
474
/**
475
 * @brief  Set the file descriptor of a poll object. The FD will also be
476
 * propagated to an associated poll context.
477
 *
478
 * @param  p            Pointer to an already allocated poll object.
479
 * @param  fd       New file descriptor.
480
 */
481
void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd)
482
0
{
483
0
    if (p->ctx != NULL) {
484
0
        p->ctx->pollfds[p->x.idx].fd = fd;
485
0
    } else {
486
0
        p->x.fd = fd;
487
0
    }
488
0
}
489
490
/**
491
 * @brief  Add extra events to a poll object. Duplicates are ignored.
492
 *         The events will also be propagated to an associated poll context.
493
 *
494
 * @param  p            Pointer to an already allocated poll object.
495
 * @param  events       Poll events.
496
 */
497
void ssh_poll_add_events(ssh_poll_handle p, short events)
498
0
{
499
0
    ssh_poll_set_events(p, ssh_poll_get_events(p) | events);
500
0
}
501
502
/**
503
 * @brief  Remove events from a poll object. Non-existent are ignored.
504
 *         The events will also be propagated to an associated poll context.
505
 *
506
 * @param  p            Pointer to an already allocated poll object.
507
 * @param  events       Poll events.
508
 */
509
void ssh_poll_remove_events(ssh_poll_handle p, short events)
510
0
{
511
0
    ssh_poll_set_events(p, ssh_poll_get_events(p) & ~events);
512
0
}
513
514
/**
515
 * @brief  Get the raw socket of a poll object.
516
 *
517
 * @param  p            Pointer to an already allocated poll object.
518
 *
519
 * @return              Raw socket.
520
 */
521
522
socket_t ssh_poll_get_fd(ssh_poll_handle p)
523
0
{
524
0
    if (p->ctx != NULL) {
525
0
        return p->ctx->pollfds[p->x.idx].fd;
526
0
    }
527
528
0
    return p->x.fd;
529
0
}
530
/**
531
 * @brief  Set the callback of a poll object.
532
 *
533
 * @param  p            Pointer to an already allocated poll object.
534
 * @param  cb           Function to be called if any of the events are set.
535
 * @param  userdata     Userdata to be passed to the callback function. NULL if
536
 *                      not needed.
537
 */
538
void ssh_poll_set_callback(ssh_poll_handle p,
539
                           ssh_poll_callback cb,
540
                           void *userdata)
541
0
{
542
0
    if (cb != NULL) {
543
0
        p->cb = cb;
544
0
        p->cb_data = userdata;
545
0
    }
546
0
}
547
548
/**
549
 * @brief  Create a new poll context. It could be associated with many poll object
550
 *         which are going to be polled at the same time as the poll context. You
551
 *         would need a single poll context per thread.
552
 *
553
 * @param  chunk_size   The size of the memory chunk that will be allocated, when
554
 *                      more memory is needed. This is for efficiency reasons,
555
 *                      i.e. don't allocate memory for each new poll object, but
556
 *                      for the next 5. Set it to 0 if you want to use the
557
 *                      library's default value.
558
 */
559
ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size)
560
0
{
561
0
    ssh_poll_ctx ctx;
562
563
0
    ctx = malloc(sizeof(struct ssh_poll_ctx_struct));
564
0
    if (ctx == NULL) {
565
0
        return NULL;
566
0
    }
567
0
    ZERO_STRUCTP(ctx);
568
569
0
    if (chunk_size == 0) {
570
0
        chunk_size = SSH_POLL_CTX_CHUNK;
571
0
    }
572
573
0
    ctx->chunk_size = chunk_size;
574
575
0
    return ctx;
576
0
}
577
578
/**
579
 * @brief  Free a poll context.
580
 *
581
 * @param  ctx          Pointer to an already allocated poll context.
582
 */
583
void ssh_poll_ctx_free(ssh_poll_ctx ctx)
584
0
{
585
0
    if (ctx->polls_allocated > 0) {
586
0
        while (ctx->polls_used > 0) {
587
0
            ssh_poll_handle p = ctx->pollptrs[0];
588
            /*
589
             * The free function calls ssh_poll_ctx_remove() and decrements
590
             * ctx->polls_used
591
             */
592
0
            ssh_poll_free(p);
593
0
        }
594
595
0
        SAFE_FREE(ctx->pollptrs);
596
0
        SAFE_FREE(ctx->pollfds);
597
0
    }
598
599
0
    SAFE_FREE(ctx);
600
0
}
601
602
static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size)
603
0
{
604
0
    ssh_poll_handle *pollptrs = NULL;
605
0
    ssh_pollfd_t *pollfds = NULL;
606
607
0
    pollptrs = realloc(ctx->pollptrs, sizeof(ssh_poll_handle) * new_size);
608
0
    if (pollptrs == NULL) {
609
        /* Fail, but keep the old value to be freed later */
610
0
        return SSH_ERROR;
611
0
    }
612
0
    ctx->pollptrs = pollptrs;
613
614
0
    pollfds = realloc(ctx->pollfds, sizeof(ssh_pollfd_t) * new_size);
615
0
    if (pollfds == NULL) {
616
0
        if (ctx->polls_allocated == 0) {
617
            /* This was initial allocation -- just free what we allocated above
618
             * and fail */
619
0
            SAFE_FREE(ctx->pollptrs);
620
0
            return SSH_ERROR;
621
0
        }
622
        /* Try to realloc the pollptrs back to the original size */
623
0
        pollptrs = realloc(ctx->pollptrs,
624
0
                           sizeof(ssh_poll_handle) * ctx->polls_allocated);
625
0
        if (pollptrs == NULL) {
626
0
            return SSH_ERROR;
627
0
        }
628
0
        ctx->pollptrs = pollptrs;
629
0
        return SSH_ERROR;
630
0
    }
631
632
0
    ctx->pollfds = pollfds;
633
0
    ctx->polls_allocated = new_size;
634
635
0
    return SSH_OK;
636
0
}
637
638
/**
639
 * @brief  Add a poll object to a poll context.
640
 *
641
 * @param  ctx          Pointer to an already allocated poll context.
642
 * @param  p            Pointer to an already allocated poll object.
643
 *
644
 * @return              0 on success, < 0 on error
645
 */
646
int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p)
647
0
{
648
0
    socket_t fd;
649
650
0
    if (p->ctx != NULL) {
651
        /* already attached to a context */
652
0
        return -1;
653
0
    }
654
655
0
    if (ctx->polls_used == ctx->polls_allocated &&
656
0
        ssh_poll_ctx_resize(ctx, ctx->polls_allocated + ctx->chunk_size) < 0) {
657
0
        return -1;
658
0
    }
659
660
0
    fd = p->x.fd;
661
0
    p->x.idx = ctx->polls_used++;
662
0
    ctx->pollptrs[p->x.idx] = p;
663
0
    ctx->pollfds[p->x.idx].fd = fd;
664
0
    ctx->pollfds[p->x.idx].events = p->events;
665
0
    ctx->pollfds[p->x.idx].revents = 0;
666
0
    p->ctx = ctx;
667
668
0
    return 0;
669
0
}
670
671
/**
672
 * @brief  Add a socket object to a poll context.
673
 *
674
 * @param  ctx          Pointer to an already allocated poll context.
675
 * @param  s            A SSH socket handle
676
 *
677
 * @return              0 on success, < 0 on error
678
 */
679
int ssh_poll_ctx_add_socket(ssh_poll_ctx ctx, ssh_socket s)
680
0
{
681
0
    ssh_poll_handle p = NULL;
682
683
0
    p = ssh_socket_get_poll_handle(s);
684
0
    if (p == NULL) {
685
0
        return -1;
686
0
    }
687
0
    return ssh_poll_ctx_add(ctx, p);
688
0
}
689
690
/**
691
 * @brief  Remove a poll object from a poll context.
692
 *
693
 * @param  ctx          Pointer to an already allocated poll context.
694
 * @param  p            Pointer to an already allocated poll object.
695
 */
696
void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p)
697
0
{
698
0
    size_t i;
699
700
0
    i = p->x.idx;
701
0
    p->x.fd = ctx->pollfds[i].fd;
702
0
    p->ctx = NULL;
703
704
0
    ctx->polls_used--;
705
706
    /* fill the empty poll slot with the last one */
707
0
    if (ctx->polls_used > 0 && ctx->polls_used != i) {
708
0
        ctx->pollfds[i] = ctx->pollfds[ctx->polls_used];
709
0
        ctx->pollptrs[i] = ctx->pollptrs[ctx->polls_used];
710
0
        ctx->pollptrs[i]->x.idx = i;
711
0
    }
712
713
    /* this will always leave at least chunk_size polls allocated */
714
0
    if (ctx->polls_allocated - ctx->polls_used > ctx->chunk_size) {
715
0
        ssh_poll_ctx_resize(ctx, ctx->polls_allocated - ctx->chunk_size);
716
0
    }
717
0
}
718
719
/**
720
 * @brief  Returns if a poll object is locked.
721
 *
722
 * @param  p            Pointer to an already allocated poll object.
723
 * @returns true if the poll object is locked; false otherwise.
724
 */
725
bool ssh_poll_is_locked(ssh_poll_handle p)
726
0
{
727
0
    if (p == NULL) {
728
0
        return false;
729
0
    }
730
0
    return p->lock_cnt > 0;
731
0
}
732
733
/**
734
 * @brief  Poll all the sockets associated through a poll object with a
735
 *         poll context. If any of the events are set after the poll, the
736
 *         call back function of the socket will be called.
737
 *         This function should be called once within the program's main loop.
738
 *
739
 * @param  ctx          Pointer to an already allocated poll context.
740
 * @param  timeout      An upper limit on the time for which ssh_poll_ctx() will
741
 *                      block, in milliseconds. Specifying a negative value
742
 *                      means an infinite timeout. This parameter is passed to
743
 *                      the poll() function.
744
 * @returns SSH_OK      No error.
745
 *          SSH_ERROR   Error happened during the poll.
746
 *          SSH_AGAIN   Timeout occurred
747
 */
748
749
int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
750
0
{
751
0
    int rc;
752
0
    size_t i, used;
753
0
    ssh_poll_handle p = NULL;
754
0
    socket_t fd;
755
0
    int revents;
756
0
    struct ssh_timestamp ts;
757
758
0
    if (ctx->polls_used == 0) {
759
0
        return SSH_ERROR;
760
0
    }
761
762
    /* Allow only POLLOUT events on locked sockets as that means we are called
763
     * recursively and we only want process the POLLOUT events here to flush
764
     * output buffer */
765
0
    for (i = 0; i < ctx->polls_used; i++) {
766
        /* The lock allows only POLLOUT events: drop the rest */
767
0
        if (ssh_poll_is_locked(ctx->pollptrs[i])) {
768
0
            ctx->pollfds[i].events &= POLLOUT;
769
0
        }
770
0
    }
771
0
    ssh_timestamp_init(&ts);
772
0
    do {
773
0
        int tm = ssh_timeout_update(&ts, timeout);
774
0
        rc = ssh_poll(ctx->pollfds, ctx->polls_used, tm);
775
0
    } while (rc == -1 && errno == EINTR);
776
777
0
    if (rc < 0) {
778
0
        return SSH_ERROR;
779
0
    }
780
0
    if (rc == 0) {
781
0
        return SSH_AGAIN;
782
0
    }
783
784
0
    used = ctx->polls_used;
785
0
    for (i = 0; i < used && rc > 0; ) {
786
0
        revents = ctx->pollfds[i].revents;
787
        /* Do not pass any other events except for POLLOUT to callback when
788
         * called recursively more than 2 times. On s390x the poll will be
789
         * spammed with POLLHUP events causing infinite recursion when the user
790
         * callback issues some write/flush/poll calls. */
791
0
        if (ctx->pollptrs[i]->lock_cnt > 2) {
792
0
            revents &= POLLOUT;
793
0
        }
794
0
        if (revents == 0) {
795
0
            i++;
796
0
        } else {
797
0
            int ret;
798
799
0
            p = ctx->pollptrs[i];
800
0
            fd = ctx->pollfds[i].fd;
801
            /* avoid having any event caught during callback */
802
0
            ctx->pollfds[i].events = 0;
803
0
            p->lock_cnt++;
804
0
            if (p->cb && (ret = p->cb(p, fd, revents, p->cb_data)) < 0) {
805
0
                if (ret == -2) {
806
0
                    return -1;
807
0
                }
808
                /* the poll was removed, reload the used counter and start again
809
                 */
810
0
                used = ctx->polls_used;
811
0
                i = 0;
812
0
            } else {
813
0
                ctx->pollfds[i].revents = 0;
814
0
                ctx->pollfds[i].events = p->events;
815
0
                p->lock_cnt--;
816
0
                i++;
817
0
            }
818
819
0
            rc--;
820
0
        }
821
0
    }
822
823
0
    return rc;
824
0
}
825
826
/**
827
 * @internal
828
 * @brief gets the default poll structure for the current session,
829
 * when used in blocking mode.
830
 * @param session SSH session
831
 * @returns the default ssh_poll_ctx
832
 */
833
ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session)
834
0
{
835
0
    if (session->default_poll_ctx != NULL) {
836
0
        return session->default_poll_ctx;
837
0
    }
838
    /* 2 is enough for the default one */
839
0
    session->default_poll_ctx = ssh_poll_ctx_new(2);
840
0
    return session->default_poll_ctx;
841
0
}
842
843
/* public event API */
844
845
struct ssh_event_fd_wrapper {
846
    ssh_event_callback cb;
847
    void *userdata;
848
};
849
850
struct ssh_event_struct {
851
    ssh_poll_ctx ctx;
852
#ifdef WITH_SERVER
853
    struct ssh_list *sessions;
854
#endif
855
};
856
857
/**
858
 * @brief  Create a new event context. It could be associated with many
859
 *         ssh_session objects and socket fd which are going to be polled at the
860
 *         same time as the event context. You would need a single event context
861
 *         per thread.
862
 *
863
 * @return  The ssh_event object on success, NULL on failure.
864
 */
865
ssh_event ssh_event_new(void)
866
0
{
867
0
    ssh_event event;
868
869
0
    event = malloc(sizeof(struct ssh_event_struct));
870
0
    if (event == NULL) {
871
0
        return NULL;
872
0
    }
873
0
    ZERO_STRUCTP(event);
874
875
0
    event->ctx = ssh_poll_ctx_new(2);
876
0
    if (event->ctx == NULL) {
877
0
        free(event);
878
0
        return NULL;
879
0
    }
880
881
0
#ifdef WITH_SERVER
882
0
    event->sessions = ssh_list_new();
883
0
    if (event->sessions == NULL) {
884
0
        ssh_poll_ctx_free(event->ctx);
885
0
        free(event);
886
0
        return NULL;
887
0
    }
888
0
#endif
889
890
0
    return event;
891
0
}
892
893
static int ssh_event_fd_wrapper_callback(ssh_poll_handle p,
894
                                         socket_t fd,
895
                                         int revents,
896
                                         void *userdata)
897
0
{
898
0
    struct ssh_event_fd_wrapper *pw = (struct ssh_event_fd_wrapper *)userdata;
899
900
0
    (void)p;
901
0
    if (pw->cb != NULL) {
902
0
        return pw->cb(fd, revents, pw->userdata);
903
0
    }
904
0
    return 0;
905
0
}
906
907
/**
908
 * @brief Add a fd to the event and assign it a callback,
909
 * when used in blocking mode.
910
 * @param event         The ssh_event
911
 * @param  fd           Socket that will be polled.
912
 * @param  events       Poll events that will be monitored for the socket. i.e.
913
 *                      POLLIN, POLLPRI, POLLOUT
914
 * @param  cb           Function to be called if any of the events are set.
915
 *                      The prototype of cb is:
916
 *                      int (*ssh_event_callback)(socket_t fd, int revents,
917
 *                                                          void *userdata);
918
 * @param  userdata     Userdata to be passed to the callback function. NULL if
919
 *                      not needed.
920
 *
921
 * @returns SSH_OK      on success
922
 *          SSH_ERROR   on failure
923
 */
924
int ssh_event_add_fd(ssh_event event,
925
                     socket_t fd,
926
                     short events,
927
                     ssh_event_callback cb,
928
                     void *userdata)
929
0
{
930
0
    ssh_poll_handle p = NULL;
931
0
    struct ssh_event_fd_wrapper *pw = NULL;
932
0
    int rc;
933
934
0
    if (event == NULL || event->ctx == NULL || cb == NULL ||
935
0
        fd == SSH_INVALID_SOCKET) {
936
0
        return SSH_ERROR;
937
0
    }
938
0
    pw = malloc(sizeof(struct ssh_event_fd_wrapper));
939
0
    if (pw == NULL) {
940
0
        return SSH_ERROR;
941
0
    }
942
943
0
    pw->cb = cb;
944
0
    pw->userdata = userdata;
945
946
    /* pw is freed by ssh_event_remove_fd */
947
0
    p = ssh_poll_new(fd, events, ssh_event_fd_wrapper_callback, pw);
948
0
    if (p == NULL) {
949
0
        free(pw);
950
0
        return SSH_ERROR;
951
0
    }
952
953
0
    rc = ssh_poll_ctx_add(event->ctx, p);
954
0
    if (rc < 0) {
955
0
        free(pw);
956
0
        ssh_poll_free(p);
957
0
        return SSH_ERROR;
958
0
    }
959
0
    return SSH_OK;
960
0
}
961
962
/**
963
 * @brief Add a poll handle to the event.
964
 *
965
 * @param   event     the ssh_event
966
 *
967
 * @param   p         the poll handle
968
 *
969
 * @returns SSH_OK    on success
970
 *          SSH_ERROR on failure
971
 */
972
int ssh_event_add_poll(ssh_event event, ssh_poll_handle p)
973
0
{
974
0
    return ssh_poll_ctx_add(event->ctx, p);
975
0
}
976
977
/**
978
 * @brief remove a poll handle to the event.
979
 *
980
 * @param   event     the ssh_event
981
 *
982
 * @param   p         the poll handle
983
 */
984
void ssh_event_remove_poll(ssh_event event, ssh_poll_handle p)
985
0
{
986
0
    ssh_poll_ctx_remove(event->ctx, p);
987
0
}
988
989
/**
990
 * @brief remove the poll handle from session and assign them to an event,
991
 * when used in blocking mode.
992
 *
993
 * @param event     The ssh_event object
994
 * @param session   The session to add to the event.
995
 *
996
 * @returns SSH_OK      on success
997
 *          SSH_ERROR   on failure
998
 */
999
int ssh_event_add_session(ssh_event event, ssh_session session)
1000
0
{
1001
0
    ssh_poll_handle p = NULL;
1002
0
#ifdef WITH_SERVER
1003
0
    struct ssh_iterator *iterator = NULL;
1004
0
#endif
1005
0
    int rc;
1006
1007
0
    if (event == NULL || event->ctx == NULL || session == NULL) {
1008
0
        return SSH_ERROR;
1009
0
    }
1010
0
    if (session->default_poll_ctx == NULL) {
1011
0
        return SSH_ERROR;
1012
0
    }
1013
0
    while (session->default_poll_ctx->polls_used > 0) {
1014
0
        p = session->default_poll_ctx->pollptrs[0];
1015
        /*
1016
         * ssh_poll_ctx_remove() decrements
1017
         * session->default_poll_ctx->polls_used
1018
         */
1019
0
        ssh_poll_ctx_remove(session->default_poll_ctx, p);
1020
0
        rc = ssh_poll_ctx_add(event->ctx, p);
1021
0
        if (rc != SSH_OK) {
1022
0
            return rc;
1023
0
        }
1024
        /* associate the pollhandler with a session so we can put it back
1025
         * at ssh_event_free()
1026
         */
1027
0
        p->session = session;
1028
0
    }
1029
0
#ifdef WITH_SERVER
1030
0
    iterator = ssh_list_get_iterator(event->sessions);
1031
0
    while (iterator != NULL) {
1032
0
        if ((ssh_session)iterator->data == session) {
1033
            /* allow only one instance of this session */
1034
0
            return SSH_OK;
1035
0
        }
1036
0
        iterator = iterator->next;
1037
0
    }
1038
0
    if (ssh_list_append(event->sessions, session) == SSH_ERROR) {
1039
0
        return SSH_ERROR;
1040
0
    }
1041
0
#endif
1042
0
    return SSH_OK;
1043
0
}
1044
1045
/**
1046
 * @brief Add a connector to the SSH event loop
1047
 *
1048
 * @param[in] event The SSH event loop
1049
 *
1050
 * @param[in] connector The connector object
1051
 *
1052
 * @return SSH_OK
1053
 *
1054
 * @return SSH_ERROR in case of error
1055
 */
1056
int ssh_event_add_connector(ssh_event event, ssh_connector connector)
1057
0
{
1058
0
    return ssh_connector_set_event(connector, event);
1059
0
}
1060
1061
/**
1062
 * @brief Poll all the sockets and sessions associated through an event object.
1063
 *
1064
 * If any of the events are set after the poll, the call back functions of the
1065
 * sessions or sockets will be called.
1066
 * This function should be called once within the programs main loop.
1067
 * In case of failure, the errno should be consulted to find more information
1068
 * about the failure set by underlying poll imlpementation.
1069
 *
1070
 * @param  event        The ssh_event object to poll.
1071
 *
1072
 * @param  timeout      An upper limit on the time for which the poll will
1073
 *                      block, in milliseconds. Specifying a negative value
1074
 *                      means an infinite timeout. This parameter is passed to
1075
 *                      the poll() function.
1076
 * @returns SSH_OK      on success.
1077
 *          SSH_ERROR   Error happened during the poll. Check errno to get more
1078
 *                      details about why it failed.
1079
 *          SSH_AGAIN   Timeout occurred
1080
 */
1081
int ssh_event_dopoll(ssh_event event, int timeout)
1082
0
{
1083
0
    int rc;
1084
1085
0
    if (event == NULL || event->ctx == NULL) {
1086
0
        return SSH_ERROR;
1087
0
    }
1088
0
    rc = ssh_poll_ctx_dopoll(event->ctx, timeout);
1089
0
    return rc;
1090
0
}
1091
1092
/**
1093
 * @brief  Remove a socket fd from an event context.
1094
 *
1095
 * @param  event        The ssh_event object.
1096
 * @param  fd           The fd to remove.
1097
 *
1098
 * @returns SSH_OK      on success
1099
 *          SSH_ERROR   on failure
1100
 */
1101
int ssh_event_remove_fd(ssh_event event, socket_t fd)
1102
0
{
1103
0
    register size_t i, used;
1104
0
    int rc = SSH_ERROR;
1105
1106
0
    if (event == NULL || event->ctx == NULL) {
1107
0
        return SSH_ERROR;
1108
0
    }
1109
1110
0
    used = event->ctx->polls_used;
1111
0
    for (i = 0; i < used; i++) {
1112
0
        if (fd == event->ctx->pollfds[i].fd) {
1113
0
            ssh_poll_handle p = event->ctx->pollptrs[i];
1114
0
            if (p->session != NULL) {
1115
                /* we cannot free that handle, it's owned by its session */
1116
0
                continue;
1117
0
            }
1118
0
            if (p->cb == ssh_event_fd_wrapper_callback) {
1119
0
                struct ssh_event_fd_wrapper *pw = p->cb_data;
1120
0
                SAFE_FREE(pw);
1121
0
            }
1122
1123
            /*
1124
             * The free function calls ssh_poll_ctx_remove() and decrements
1125
             * event->ctx->polls_used.
1126
             */
1127
0
            ssh_poll_free(p);
1128
0
            rc = SSH_OK;
1129
1130
            /* restart the loop */
1131
0
            used = event->ctx->polls_used;
1132
0
            i = 0;
1133
0
        }
1134
0
    }
1135
1136
0
    return rc;
1137
0
}
1138
1139
/**
1140
 * @brief  Remove a session object from an event context.
1141
 *
1142
 * @param  event        The ssh_event object.
1143
 * @param  session      The session to remove.
1144
 *
1145
 * @returns SSH_OK      on success
1146
 *          SSH_ERROR   on failure
1147
 */
1148
int ssh_event_remove_session(ssh_event event, ssh_session session)
1149
0
{
1150
0
    ssh_poll_handle p = NULL;
1151
0
    register size_t i, used;
1152
0
    int rc = SSH_ERROR;
1153
0
#ifdef WITH_SERVER
1154
0
    struct ssh_iterator *iterator = NULL;
1155
0
#endif
1156
1157
0
    if (event == NULL || event->ctx == NULL || session == NULL) {
1158
0
        return SSH_ERROR;
1159
0
    }
1160
1161
0
    used = event->ctx->polls_used;
1162
0
    for (i = 0; i < used; i++) {
1163
0
        p = event->ctx->pollptrs[i];
1164
0
        if (p->session == session) {
1165
            /*
1166
             * ssh_poll_ctx_remove() decrements
1167
             * event->ctx->polls_used
1168
             */
1169
0
            ssh_poll_ctx_remove(event->ctx, p);
1170
0
            p->session = NULL;
1171
0
            rc = ssh_poll_ctx_add(session->default_poll_ctx, p);
1172
0
            if (rc != SSH_OK) {
1173
0
                return rc;
1174
0
            }
1175
0
            rc = SSH_OK;
1176
            /*
1177
             * Restart the loop!
1178
             * A session can initially have two pollhandlers.
1179
             */
1180
0
            used = event->ctx->polls_used;
1181
0
            i = 0;
1182
0
        }
1183
0
    }
1184
0
#ifdef WITH_SERVER
1185
0
    iterator = ssh_list_get_iterator(event->sessions);
1186
0
    while (iterator != NULL) {
1187
0
        if ((ssh_session)iterator->data == session) {
1188
0
            ssh_list_remove(event->sessions, iterator);
1189
            /* there should be only one instance of this session */
1190
0
            break;
1191
0
        }
1192
0
        iterator = iterator->next;
1193
0
    }
1194
0
#endif
1195
1196
0
    return rc;
1197
0
}
1198
1199
/** @brief Remove a connector from an event context
1200
 * @param[in] event The ssh_event object.
1201
 * @param[in] connector connector object to remove
1202
 * @return SSH_OK on success
1203
 * @return SSH_ERROR on failure
1204
 */
1205
int ssh_event_remove_connector(ssh_event event, ssh_connector connector)
1206
0
{
1207
0
    (void)event;
1208
0
    return ssh_connector_remove_event(connector);
1209
0
}
1210
1211
/**
1212
 * @brief  Free an event context.
1213
 *
1214
 * @param  event        The ssh_event object to free.
1215
 *                      Note: you have to manually remove sessions and socket
1216
 *                      fds before freeing the event object.
1217
 *
1218
 */
1219
void ssh_event_free(ssh_event event)
1220
0
{
1221
0
    size_t used, i;
1222
0
    ssh_poll_handle p = NULL;
1223
1224
0
    if (event == NULL) {
1225
0
        return;
1226
0
    }
1227
1228
0
    if (event->ctx != NULL) {
1229
0
        used = event->ctx->polls_used;
1230
0
        for (i = 0; i < used; i++) {
1231
0
            p = event->ctx->pollptrs[i];
1232
0
            if (p->session != NULL) {
1233
0
                ssh_poll_ctx_remove(event->ctx, p);
1234
0
                ssh_poll_ctx_add(p->session->default_poll_ctx, p);
1235
0
                p->session = NULL;
1236
0
                used = 0;
1237
0
            }
1238
0
        }
1239
1240
0
        ssh_poll_ctx_free(event->ctx);
1241
0
    }
1242
0
#ifdef WITH_SERVER
1243
0
    if (event->sessions != NULL) {
1244
0
        ssh_list_free(event->sessions);
1245
0
    }
1246
0
#endif
1247
0
    free(event);
1248
0
}
1249
1250
/** @} */