Coverage Report

Created: 2025-07-11 06:45

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