Coverage Report

Created: 2025-11-30 06:53

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