Coverage Report

Created: 2025-11-11 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/src/socket.c
Line
Count
Source
1
/*
2
 * socket.c - socket functions for the library
3
 *
4
 * This file is part of the SSH Library
5
 *
6
 * Copyright (c) 2008-2010      by Aris Adamantiadis
7
 *
8
 * The SSH Library is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or (at your
11
 * option) any later version.
12
 *
13
 * The SSH Library is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16
 * License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with the SSH Library; see the file COPYING.  If not, write to
20
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21
 * MA 02111-1307, USA.
22
 */
23
24
#include "config.h"
25
26
#include <errno.h>
27
#include <stdio.h>
28
#ifdef _WIN32
29
#include <winsock2.h>
30
#include <ws2tcpip.h>
31
#ifndef UNIX_PATH_MAX
32
 /* Inlining the key portions of afunix.h in Windows 10 SDK;
33
  * that header isn't available in the mingw environment. */
34
#define UNIX_PATH_MAX 108
35
struct sockaddr_un {
36
  ADDRESS_FAMILY sun_family;
37
  char sun_path[UNIX_PATH_MAX];
38
};
39
#endif
40
#else /* _WIN32 */
41
#include <fcntl.h>
42
#include <sys/types.h>
43
#include <sys/wait.h>
44
#include <sys/socket.h>
45
#include <sys/un.h>
46
#include <signal.h>
47
#ifdef HAVE_PTHREAD
48
#include <pthread.h>
49
#endif
50
#endif /* _WIN32 */
51
52
#include "libssh/priv.h"
53
#include "libssh/callbacks.h"
54
#include "libssh/socket.h"
55
#include "libssh/buffer.h"
56
#include "libssh/poll.h"
57
#include "libssh/session.h"
58
59
/**
60
 * @defgroup libssh_socket The SSH socket functions.
61
 * @ingroup libssh
62
 *
63
 * Functions for handling sockets.
64
 *
65
 * @{
66
 */
67
68
enum ssh_socket_states_e {
69
  SSH_SOCKET_NONE,
70
  SSH_SOCKET_CONNECTING,
71
  SSH_SOCKET_CONNECTED,
72
  SSH_SOCKET_EOF,
73
  SSH_SOCKET_ERROR,
74
  SSH_SOCKET_CLOSED
75
};
76
77
struct ssh_socket_struct {
78
  socket_t fd;
79
  int fd_is_socket;
80
  int last_errno;
81
  int read_wontblock; /* reading now on socket will
82
                       not block */
83
  int write_wontblock;
84
  int data_except;
85
  enum ssh_socket_states_e state;
86
  ssh_buffer out_buffer;
87
  ssh_buffer in_buffer;
88
  ssh_session session;
89
  ssh_socket_callbacks callbacks;
90
  ssh_poll_handle poll_handle;
91
#ifndef _WIN32
92
  pid_t proxy_pid;
93
#endif
94
};
95
96
#ifdef HAVE_PTHREAD
97
struct jump_thread_data_struct {
98
    ssh_session session;
99
    socket_t fd;
100
};
101
102
int proxy_disconnect = 0;
103
#endif /* HAVE_PTHREAD */
104
105
static int sockets_initialized = 0;
106
107
static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
108
                                          void *buffer,
109
                                          uint32_t len);
110
static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
111
                                           const void *buffer,
112
                                           uint32_t len);
113
114
/**
115
 * \internal
116
 * \brief inits the socket system (windows specific)
117
 */
118
int ssh_socket_init(void)
119
2
{
120
2
    if (sockets_initialized == 0) {
121
#ifdef _WIN32
122
        struct WSAData wsaData;
123
124
        /* Initiates use of the Winsock DLL by a process. */
125
        if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
126
            return -1;
127
        }
128
#endif
129
2
        ssh_poll_init();
130
131
2
        sockets_initialized = 1;
132
2
    }
133
134
2
    return 0;
135
2
}
136
137
/**
138
 * @brief Cleanup the socket system.
139
 */
140
void ssh_socket_cleanup(void)
141
0
{
142
0
    if (sockets_initialized == 1) {
143
0
        ssh_poll_cleanup();
144
#ifdef _WIN32
145
        WSACleanup();
146
#endif
147
0
        sockets_initialized = 0;
148
0
    }
149
0
}
150
151
152
/**
153
 * \internal
154
 * \brief creates a new Socket object
155
 */
156
ssh_socket ssh_socket_new(ssh_session session)
157
465
{
158
465
    ssh_socket s;
159
160
465
    s = calloc(1, sizeof(struct ssh_socket_struct));
161
465
    if (s == NULL) {
162
18
        ssh_set_error_oom(session);
163
18
        return NULL;
164
18
    }
165
447
    s->fd = SSH_INVALID_SOCKET;
166
447
    s->last_errno = -1;
167
447
    s->fd_is_socket = 1;
168
447
    s->session = session;
169
447
    s->in_buffer = ssh_buffer_new();
170
447
    if (s->in_buffer == NULL) {
171
24
        ssh_set_error_oom(session);
172
24
        SAFE_FREE(s);
173
24
        return NULL;
174
24
    }
175
423
    s->out_buffer=ssh_buffer_new();
176
423
    if (s->out_buffer == NULL) {
177
7
        ssh_set_error_oom(session);
178
7
        SSH_BUFFER_FREE(s->in_buffer);
179
7
        SAFE_FREE(s);
180
7
        return NULL;
181
7
    }
182
416
    s->read_wontblock = 0;
183
416
    s->write_wontblock = 0;
184
416
    s->data_except = 0;
185
416
    s->poll_handle = NULL;
186
416
    s->state=SSH_SOCKET_NONE;
187
416
    return s;
188
423
}
189
190
/**
191
 * @internal
192
 * @brief Reset the state of a socket so it looks brand-new
193
 * @param[in] s socket to rest
194
 */
195
void ssh_socket_reset(ssh_socket s)
196
207
{
197
207
    s->fd = SSH_INVALID_SOCKET;
198
207
    s->last_errno = -1;
199
207
    s->fd_is_socket = 1;
200
207
    ssh_buffer_reinit(s->in_buffer);
201
207
    ssh_buffer_reinit(s->out_buffer);
202
207
    s->read_wontblock = 0;
203
207
    s->write_wontblock = 0;
204
207
    s->data_except = 0;
205
207
    if (s->poll_handle != NULL) {
206
207
        ssh_poll_free(s->poll_handle);
207
207
        s->poll_handle = NULL;
208
207
    }
209
207
    s->state=SSH_SOCKET_NONE;
210
207
#ifndef _WIN32
211
207
    s->proxy_pid = 0;
212
207
#endif
213
207
}
214
215
/**
216
 * @internal
217
 * @brief the socket callbacks, i.e. callbacks to be called
218
 * upon a socket event.
219
 * @param s socket to set callbacks on.
220
 * @param callbacks a ssh_socket_callback object reference.
221
 */
222
223
void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks)
224
383
{
225
383
    s->callbacks = callbacks;
226
383
}
227
228
void ssh_socket_set_connected(ssh_socket s, struct ssh_poll_handle_struct *p)
229
207
{
230
207
    s->state = SSH_SOCKET_CONNECTED;
231
    /* POLLOUT is the event to wait for in a nonblocking connect */
232
207
    if (p != NULL) {
233
207
        ssh_poll_set_events(p, POLLIN | POLLOUT);
234
207
    }
235
207
}
236
237
/**
238
 * @brief               SSH poll callback. This callback will be used when an event
239
 *                      caught on the socket.
240
 *
241
 * @param p             Poll object this callback belongs to.
242
 * @param fd            The raw socket.
243
 * @param revents       The current poll events on the socket.
244
 * @param v_s           Userdata to be passed to the callback function,
245
 *                      in this case the socket object.
246
 *
247
 * @return              0 on success, < 0 when the poll object has been removed
248
 *                      from its poll context.
249
 */
250
int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
251
                            socket_t fd,
252
                            int revents,
253
                            void *v_s)
254
1.02k
{
255
1.02k
    ssh_socket s = (ssh_socket)v_s;
256
1.02k
    void *buffer = NULL;
257
1.02k
    ssize_t nread = 0;
258
1.02k
    int rc;
259
1.02k
    int err = 0;
260
1.02k
    socklen_t errlen = sizeof(err);
261
262
    /* Do not do anything if this socket was already closed */
263
1.02k
    if (!ssh_socket_is_open(s)) {
264
0
        return -1;
265
0
    }
266
1.02k
    SSH_LOG(SSH_LOG_TRACE,
267
1.02k
            "Poll callback on socket %d (%s%s%s), out buffer %" PRIu32, fd,
268
1.02k
            (revents & POLLIN) ? "POLLIN ":"",
269
1.02k
            (revents & POLLOUT) ? "POLLOUT ":"",
270
1.02k
            (revents & POLLERR) ? "POLLERR":"",
271
1.02k
            ssh_buffer_get_len(s->out_buffer));
272
1.02k
    if ((revents & POLLERR) || (revents & POLLHUP)) {
273
        /* Check if we are in a connecting state */
274
0
        if (s->state == SSH_SOCKET_CONNECTING) {
275
0
            s->state = SSH_SOCKET_ERROR;
276
0
            rc = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen);
277
0
            if (rc < 0) {
278
0
                err = errno;
279
0
            }
280
0
            ssh_socket_close(s);
281
            /* Overwrite ssh_socket_close() error with the real socket error */
282
0
            s->last_errno = err;
283
0
            errno = err;
284
285
0
            if (s->callbacks != NULL && s->callbacks->connected != NULL) {
286
0
                s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR,
287
0
                                        err,
288
0
                                        s->callbacks->userdata);
289
0
            }
290
291
0
            return -1;
292
0
        }
293
        /* Then we are in a more standard kind of error */
294
        /* force a read to get an explanation */
295
0
        revents |= POLLIN;
296
0
    }
297
1.02k
    if ((revents & POLLIN) && s->state == SSH_SOCKET_CONNECTED) {
298
816
        s->read_wontblock = 1;
299
816
        buffer = ssh_buffer_allocate(s->in_buffer, MAX_BUF_SIZE);
300
816
        if (buffer) {
301
816
            nread = ssh_socket_unbuffered_read(s, buffer, MAX_BUF_SIZE);
302
816
        }
303
816
        if (nread < 0) {
304
0
            ssh_buffer_pass_bytes_end(s->in_buffer, MAX_BUF_SIZE);
305
0
            if (p != NULL) {
306
0
                ssh_poll_remove_events(p, POLLIN);
307
0
            }
308
309
0
            if (s->callbacks != NULL && s->callbacks->exception != NULL) {
310
0
                s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,
311
0
                                        s->last_errno,
312
0
                                        s->callbacks->userdata);
313
0
            }
314
0
            return -2;
315
0
        }
316
317
        /* Rollback the unused space */
318
816
        ssh_buffer_pass_bytes_end(s->in_buffer,
319
816
                                  (uint32_t)(MAX_BUF_SIZE - nread));
320
321
816
        if (nread == 0) {
322
62
            if (p != NULL) {
323
62
                ssh_poll_remove_events(p, POLLIN);
324
62
            }
325
62
            if (s->callbacks != NULL && s->callbacks->exception != NULL) {
326
62
                s->callbacks->exception(SSH_SOCKET_EXCEPTION_EOF,
327
62
                                        0,
328
62
                                        s->callbacks->userdata);
329
62
            }
330
62
            return -2;
331
62
        }
332
333
754
        if (s->session->socket_counter != NULL) {
334
0
            s->session->socket_counter->in_bytes += nread;
335
0
        }
336
337
        /* Call the callback */
338
754
        if (s->callbacks != NULL && s->callbacks->data != NULL) {
339
754
            size_t processed;
340
351k
            do {
341
351k
                processed = s->callbacks->data(ssh_buffer_get(s->in_buffer),
342
351k
                                               ssh_buffer_get_len(s->in_buffer),
343
351k
                                               s->callbacks->userdata);
344
351k
                ssh_buffer_pass_bytes(s->in_buffer, (uint32_t)processed);
345
351k
            } while ((processed > 0) && (s->state == SSH_SOCKET_CONNECTED));
346
347
            /* p may have been freed, so don't use it
348
             * anymore in this function */
349
754
            p = NULL;
350
754
        }
351
754
    }
352
#ifdef _WIN32
353
    if (revents & POLLOUT || revents & POLLWRNORM) {
354
#else
355
961
    if (revents & POLLOUT) {
356
918
#endif
357
918
        uint32_t len;
358
359
        /* First, POLLOUT is a sign we may be connected */
360
918
        if (s->state == SSH_SOCKET_CONNECTING) {
361
207
            SSH_LOG(SSH_LOG_PACKET, "Received POLLOUT in connecting state");
362
207
            ssh_socket_set_connected(s, p);
363
364
207
            rc = ssh_socket_set_blocking(ssh_socket_get_fd(s));
365
207
            if (rc < 0) {
366
0
                return -1;
367
0
            }
368
369
207
            if (s->callbacks != NULL && s->callbacks->connected != NULL) {
370
207
                s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,
371
207
                                        0,
372
207
                                        s->callbacks->userdata);
373
207
            }
374
375
207
            return 0;
376
207
        }
377
378
        /* So, we can write data */
379
711
        s->write_wontblock = 1;
380
711
        if (p != NULL) {
381
0
            ssh_poll_remove_events(p, POLLOUT);
382
0
        }
383
384
        /* If buffered data is pending, write it */
385
711
        len = ssh_buffer_get_len(s->out_buffer);
386
711
        if (len > 0) {
387
220
            ssh_socket_nonblocking_flush(s);
388
491
        } else if (s->callbacks != NULL && s->callbacks->controlflow != NULL) {
389
            /* Otherwise advertise the upper level that write can be done */
390
197
            SSH_LOG(SSH_LOG_TRACE, "sending control flow event");
391
197
            s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,
392
197
                                      s->callbacks->userdata);
393
197
        }
394
        /* TODO: Find a way to put back POLLOUT when buffering occurs */
395
711
    }
396
397
    /* Return -1 if the poll handler disappeared */
398
754
    if (s->poll_handle == NULL) {
399
0
        return -1;
400
0
    }
401
402
754
    return 0;
403
754
}
404
405
/** @internal
406
 * @brief returns the poll handle corresponding to the socket,
407
 * creates it if it does not exist.
408
 * @returns allocated and initialized ssh_poll_handle object
409
 */
410
ssh_poll_handle ssh_socket_get_poll_handle(ssh_socket s)
411
1.23k
{
412
1.23k
    if (s->poll_handle) {
413
1.02k
        return s->poll_handle;
414
1.02k
    }
415
207
    s->poll_handle = ssh_poll_new(s->fd, 0, ssh_socket_pollcallback, s);
416
207
    return s->poll_handle;
417
1.23k
}
418
419
/** \internal
420
 * \brief Deletes a socket object
421
 */
422
void ssh_socket_free(ssh_socket s)
423
521
{
424
521
    if (s == NULL) {
425
105
        return;
426
105
    }
427
416
    ssh_socket_close(s);
428
416
    SSH_BUFFER_FREE(s->in_buffer);
429
416
    SSH_BUFFER_FREE(s->out_buffer);
430
416
    SAFE_FREE(s);
431
416
}
432
433
int ssh_socket_unix(ssh_socket s, const char *path)
434
0
{
435
0
    struct sockaddr_un sunaddr;
436
0
    char err_msg[SSH_ERRNO_MSG_MAX] = {0};
437
0
    socket_t fd;
438
0
    sunaddr.sun_family = AF_UNIX;
439
0
    snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path);
440
441
0
    fd = socket(AF_UNIX, SOCK_STREAM, 0);
442
0
    if (fd == SSH_INVALID_SOCKET) {
443
0
        ssh_set_error(s->session, SSH_FATAL,
444
0
                      "Error from socket(AF_UNIX, SOCK_STREAM, 0): %s",
445
0
                      ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
446
0
        return SSH_ERROR;
447
0
    }
448
449
0
#ifndef _WIN32
450
0
    if (fcntl(fd, F_SETFD, 1) == -1) {
451
0
        ssh_set_error(s->session, SSH_FATAL,
452
0
                      "Error from fcntl(fd, F_SETFD, 1): %s",
453
0
                      ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
454
0
        CLOSE_SOCKET(fd);
455
0
        return SSH_ERROR;
456
0
    }
457
0
#endif
458
459
0
    if (connect(fd, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) {
460
0
        ssh_set_error(s->session, SSH_FATAL, "Error from connect(%s): %s",
461
0
                      path,
462
0
                      ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
463
0
        CLOSE_SOCKET(fd);
464
0
        return SSH_ERROR;
465
0
    }
466
0
    return ssh_socket_set_fd(s, fd);
467
0
}
468
469
/** \internal
470
 * \brief closes a socket
471
 */
472
void ssh_socket_close(ssh_socket s)
473
623
{
474
623
    if (ssh_socket_is_open(s)) {
475
#ifdef _WIN32
476
        CLOSE_SOCKET(s->fd);
477
        s->last_errno = WSAGetLastError();
478
#else
479
0
        CLOSE_SOCKET(s->fd);
480
0
        s->last_errno = errno;
481
0
#endif
482
0
    }
483
484
623
    if (s->poll_handle != NULL && !ssh_poll_is_locked(s->poll_handle)) {
485
0
        ssh_poll_free(s->poll_handle);
486
0
        s->poll_handle = NULL;
487
0
    }
488
489
623
    s->state = SSH_SOCKET_CLOSED;
490
491
623
#ifndef _WIN32
492
    /* If the proxy command still runs try to kill it */
493
623
    if (s->proxy_pid != 0) {
494
0
        int status;
495
0
        pid_t pid = s->proxy_pid;
496
497
0
        s->proxy_pid = 0;
498
0
        kill(pid, SIGTERM);
499
0
        while (waitpid(pid, &status, 0) == -1) {
500
0
            if (errno != EINTR) {
501
0
                char err_msg[SSH_ERRNO_MSG_MAX] = {0};
502
0
                SSH_LOG(SSH_LOG_TRACE, "waitpid failed: %s",
503
0
                        ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
504
0
                return;
505
0
            }
506
0
        }
507
0
        if (!WIFEXITED(status)) {
508
0
            SSH_LOG(SSH_LOG_TRACE, "Proxy command exited abnormally");
509
0
            return;
510
0
        }
511
0
        SSH_LOG(SSH_LOG_TRACE, "Proxy command returned %d", WEXITSTATUS(status));
512
0
    }
513
623
#endif
514
623
}
515
516
/**
517
 * @internal
518
 * @brief sets the file descriptor of the socket.
519
 * @param[out] s ssh_socket to update
520
 * @param[in] fd file descriptor to set
521
 * @warning this function updates both the input and output
522
 * file descriptors
523
 */
524
int ssh_socket_set_fd(ssh_socket s, socket_t fd)
525
207
{
526
207
    ssh_poll_handle h = NULL;
527
528
207
    s->fd = fd;
529
530
207
    if (s->poll_handle) {
531
0
        ssh_poll_set_fd(s->poll_handle,fd);
532
207
    } else {
533
207
        s->state = SSH_SOCKET_CONNECTING;
534
207
        h = ssh_socket_get_poll_handle(s);
535
207
        if (h == NULL) {
536
0
            return SSH_ERROR;
537
0
        }
538
539
        /* POLLOUT is the event to wait for in a nonblocking connect */
540
207
        ssh_poll_set_events(h, POLLOUT);
541
#ifdef _WIN32
542
        ssh_poll_add_events(h, POLLWRNORM);
543
#endif
544
207
    }
545
207
    return SSH_OK;
546
207
}
547
548
/** \internal
549
 * \brief returns the input file descriptor of the socket
550
 */
551
socket_t ssh_socket_get_fd(ssh_socket s)
552
207
{
553
207
    return s->fd;
554
207
}
555
556
/** \internal
557
 * \brief returns nonzero if the socket is open
558
 */
559
int ssh_socket_is_open(ssh_socket s)
560
24.3k
{
561
24.3k
    return s->fd != SSH_INVALID_SOCKET;
562
24.3k
}
563
564
/** \internal
565
 * \brief read len bytes from socket into buffer
566
 */
567
static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
568
                                          void *buffer,
569
                                          uint32_t len)
570
816
{
571
816
    ssize_t rc = -1;
572
573
816
    if (s->data_except) {
574
0
        return -1;
575
0
    }
576
816
    if (s->fd_is_socket) {
577
816
        rc = recv(s->fd, buffer, len, 0);
578
816
    } else {
579
0
        rc = read(s->fd, buffer, len);
580
0
    }
581
#ifdef _WIN32
582
    s->last_errno = WSAGetLastError();
583
#else
584
816
    s->last_errno = errno;
585
816
#endif
586
816
    s->read_wontblock = 0;
587
588
816
    if (rc < 0) {
589
0
        s->data_except = 1;
590
816
    } else {
591
816
        SSH_LOG(SSH_LOG_TRACE, "read %zd", rc);
592
816
    }
593
594
816
    return rc;
595
816
}
596
597
/** \internal
598
 * \brief writes len bytes from buffer to socket
599
 */
600
static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
601
                                           const void *buffer,
602
                                           uint32_t len)
603
523
{
604
523
    ssize_t w = -1;
605
523
    int flags = 0;
606
607
523
#ifdef MSG_NOSIGNAL
608
523
    flags |= MSG_NOSIGNAL;
609
523
#endif
610
611
523
    if (s->data_except) {
612
0
        return -1;
613
0
    }
614
615
523
    if (s->fd_is_socket) {
616
523
        w = send(s->fd, buffer, len, flags);
617
523
    } else {
618
0
        w = write(s->fd, buffer, len);
619
0
    }
620
#ifdef _WIN32
621
    s->last_errno = WSAGetLastError();
622
#else
623
523
    s->last_errno = errno;
624
523
#endif
625
523
    s->write_wontblock = 0;
626
    /* Reactive the POLLOUT detector in the poll multiplexer system */
627
523
    if (s->poll_handle) {
628
523
        SSH_LOG(SSH_LOG_PACKET, "Enabling POLLOUT for socket");
629
523
        ssh_poll_add_events(s->poll_handle, POLLOUT);
630
523
    }
631
523
    if (w < 0) {
632
0
        s->data_except = 1;
633
0
    }
634
635
523
    SSH_LOG(SSH_LOG_TRACE, "wrote %zd", w);
636
523
    return w;
637
523
}
638
639
/** \internal
640
 * \brief returns nonzero if the current socket is in the fd_set
641
 */
642
int ssh_socket_fd_isset(ssh_socket s, fd_set *set)
643
0
{
644
0
    if(s->fd == SSH_INVALID_SOCKET) {
645
0
        return 0;
646
0
    }
647
0
    return FD_ISSET(s->fd,set);
648
0
}
649
650
/** \internal
651
 * \brief sets the current fd in a fd_set and updates the max_fd
652
 */
653
654
void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd)
655
0
{
656
0
    if (s->fd == SSH_INVALID_SOCKET) {
657
0
        return;
658
0
    }
659
660
0
    FD_SET(s->fd,set);
661
662
0
    if (s->fd >= 0 &&
663
0
        s->fd >= *max_fd &&
664
0
        s->fd != SSH_INVALID_SOCKET) {
665
0
        *max_fd = s->fd + 1;
666
0
    }
667
0
}
668
669
/** \internal
670
 * \brief buffered write of data
671
 * \returns SSH_OK, or SSH_ERROR
672
 * \warning has no effect on socket before a flush
673
 */
674
int ssh_socket_write(ssh_socket s, const void *buffer, uint32_t len)
675
22.2k
{
676
22.2k
    if (len > 0) {
677
22.2k
        if (ssh_buffer_add_data(s->out_buffer, buffer, len) < 0) {
678
0
            ssh_set_error_oom(s->session);
679
0
            return SSH_ERROR;
680
0
        }
681
22.2k
        ssh_socket_nonblocking_flush(s);
682
22.2k
    }
683
684
22.2k
    return SSH_OK;
685
22.2k
}
686
687
688
/** \internal
689
 * \brief starts a nonblocking flush of the output buffer
690
 *
691
 */
692
int ssh_socket_nonblocking_flush(ssh_socket s)
693
22.4k
{
694
22.4k
    ssh_session session = s->session;
695
22.4k
    uint32_t len;
696
697
22.4k
    if (!ssh_socket_is_open(s)) {
698
0
        session->alive = 0;
699
0
        if (s->callbacks && s->callbacks->exception) {
700
0
            s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,
701
0
                                    s->last_errno,
702
0
                                    s->callbacks->userdata);
703
0
        } else {
704
0
            char err_msg[SSH_ERRNO_MSG_MAX] = {0};
705
0
            ssh_set_error(session,
706
0
                          SSH_FATAL,
707
0
                          "Writing packet: error on socket (or connection "
708
0
                          "closed): %s",
709
0
                          ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
710
0
        }
711
712
0
        return SSH_ERROR;
713
0
    }
714
715
22.4k
    len = ssh_buffer_get_len(s->out_buffer);
716
22.4k
    if (!s->write_wontblock && s->poll_handle && len > 0) {
717
        /* force the poll system to catch pollout events */
718
21.9k
        ssh_poll_add_events(s->poll_handle, POLLOUT);
719
720
21.9k
        return SSH_AGAIN;
721
21.9k
    }
722
723
523
    if (s->write_wontblock && len > 0) {
724
523
        ssize_t bwritten;
725
726
523
        bwritten = ssh_socket_unbuffered_write(s,
727
523
                                               ssh_buffer_get(s->out_buffer),
728
523
                                               len);
729
523
        if (bwritten < 0) {
730
0
            session->alive = 0;
731
0
            ssh_socket_close(s);
732
733
0
            if (s->callbacks && s->callbacks->exception) {
734
0
                s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,
735
0
                                        s->last_errno,
736
0
                                        s->callbacks->userdata);
737
0
            } else {
738
0
                char err_msg[SSH_ERRNO_MSG_MAX] = {0};
739
0
                ssh_set_error(session,
740
0
                              SSH_FATAL,
741
0
                              "Writing packet: error on socket (or connection "
742
0
                              "closed): %s",
743
0
                              ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
744
0
            }
745
746
0
            return SSH_ERROR;
747
0
        }
748
749
523
        ssh_buffer_pass_bytes(s->out_buffer, (uint32_t)bwritten);
750
523
        if (s->session->socket_counter != NULL) {
751
0
            s->session->socket_counter->out_bytes += bwritten;
752
0
        }
753
523
    }
754
755
    /* Is there some data pending? */
756
523
    len = ssh_buffer_get_len(s->out_buffer);
757
523
    if (s->poll_handle && len > 0) {
758
0
        SSH_LOG(SSH_LOG_TRACE,
759
0
                "did not send all the data, queuing pollout event");
760
        /* force the poll system to catch pollout events */
761
0
        ssh_poll_add_events(s->poll_handle, POLLOUT);
762
763
0
        return SSH_AGAIN;
764
0
    }
765
766
    /* all data written */
767
523
    return SSH_OK;
768
523
}
769
770
void ssh_socket_set_write_wontblock(ssh_socket s)
771
207
{
772
207
    s->write_wontblock = 1;
773
207
}
774
775
void ssh_socket_set_read_wontblock(ssh_socket s)
776
0
{
777
0
    s->read_wontblock = 1;
778
0
}
779
780
void ssh_socket_set_except(ssh_socket s)
781
0
{
782
0
    s->data_except = 1;
783
0
}
784
785
int ssh_socket_data_available(ssh_socket s)
786
0
{
787
0
    return s->read_wontblock;
788
0
}
789
790
int ssh_socket_data_writable(ssh_socket s)
791
0
{
792
0
    return s->write_wontblock;
793
0
}
794
795
/** @internal
796
 * @brief returns the number of outgoing bytes currently buffered
797
 * @param s the socket
798
 * @returns numbers of bytes buffered, or 0 if the socket isn't connected
799
 */
800
int ssh_socket_buffered_write_bytes(ssh_socket s)
801
0
{
802
0
    if (s==NULL || s->out_buffer == NULL) {
803
0
        return 0;
804
0
    }
805
806
0
    return ssh_buffer_get_len(s->out_buffer);
807
0
}
808
809
810
int ssh_socket_get_status(ssh_socket s)
811
0
{
812
0
    int r = 0;
813
814
0
    if (ssh_buffer_get_len(s->in_buffer) > 0) {
815
0
        r |= SSH_READ_PENDING;
816
0
    }
817
818
0
    if (ssh_buffer_get_len(s->out_buffer) > 0) {
819
0
        r |= SSH_WRITE_PENDING;
820
0
    }
821
822
0
    if (s->data_except) {
823
0
        r |= SSH_CLOSED_ERROR;
824
0
    }
825
826
0
    return r;
827
0
}
828
829
int ssh_socket_get_poll_flags(ssh_socket s)
830
0
{
831
0
    int r = 0;
832
0
    if (s->poll_handle != NULL && (ssh_poll_get_events (s->poll_handle) & POLLIN) > 0) {
833
0
        r |= SSH_READ_PENDING;
834
0
    }
835
0
    if (s->poll_handle != NULL && (ssh_poll_get_events (s->poll_handle) & POLLOUT) > 0) {
836
0
        r |= SSH_WRITE_PENDING;
837
0
    }
838
0
    return r;
839
0
}
840
841
#ifdef _WIN32
842
int ssh_socket_set_nonblocking(socket_t fd)
843
{
844
    u_long nonblocking = 1;
845
    return ioctlsocket(fd, FIONBIO, &nonblocking);
846
}
847
848
int ssh_socket_set_blocking(socket_t fd)
849
{
850
    u_long nonblocking = 0;
851
    return ioctlsocket(fd, FIONBIO, &nonblocking);
852
}
853
854
#else /* _WIN32 */
855
int ssh_socket_set_nonblocking(socket_t fd)
856
0
{
857
0
    return fcntl(fd, F_SETFL, O_NONBLOCK);
858
0
}
859
860
int ssh_socket_set_blocking(socket_t fd)
861
207
{
862
207
    return fcntl(fd, F_SETFL, 0);
863
207
}
864
#endif /* _WIN32 */
865
866
/**
867
 * @internal
868
 * @brief Launches a socket connection
869
 * If the socket connected callback has been defined and
870
 * a poll object exists, this call will be non blocking.
871
 * @param s    socket to connect.
872
 * @param host hostname or ip address to connect to.
873
 * @param port port number to connect to.
874
 * @param bind_addr address to bind to, or NULL for default.
875
 * @returns SSH_OK socket is being connected.
876
 * @returns SSH_ERROR error while connecting to remote host.
877
 */
878
int ssh_socket_connect(ssh_socket s,
879
                       const char *host,
880
                       uint16_t port,
881
                       const char *bind_addr)
882
0
{
883
0
    socket_t fd;
884
885
0
    if (s->state != SSH_SOCKET_NONE) {
886
0
        ssh_set_error(s->session, SSH_FATAL,
887
0
                      "ssh_socket_connect called on socket not unconnected");
888
0
        return SSH_ERROR;
889
0
    }
890
0
    fd = ssh_connect_host_nonblocking(s->session, host, bind_addr, port);
891
0
    SSH_LOG(SSH_LOG_DEBUG, "Nonblocking connection socket: %d", fd);
892
0
    if (fd == SSH_INVALID_SOCKET) {
893
0
        return SSH_ERROR;
894
0
    }
895
0
    return ssh_socket_set_fd(s, fd);
896
0
}
897
898
#ifdef WITH_EXEC
899
/**
900
 * @internal
901
 * @brief executes a command and redirect input and outputs
902
 * @param command command to execute
903
 * @param in input file descriptor
904
 * @param out output file descriptor
905
 */
906
void
907
ssh_execute_command(const char *command, socket_t in, socket_t out)
908
{
909
    const char *shell = NULL;
910
    const char *args[] = {NULL/*shell*/, "-c", command, NULL};
911
    int devnull;
912
    int rc;
913
914
    /* Prepare /dev/null socket for the stderr redirection */
915
    devnull = open("/dev/null", O_WRONLY);
916
    if (devnull == -1) {
917
        SSH_LOG(SSH_LOG_TRACE, "Failed to open /dev/null");
918
        exit(1);
919
    }
920
921
    /*
922
     * By default, use the current users shell. This could fail with some
923
     * shells like zsh or dash ...
924
     */
925
    shell = getenv("SHELL");
926
    if (shell == NULL || shell[0] == '\0') {
927
        /* Fall back to the /bin/sh only if the bash is not available. But there are
928
         * issues with dash or whatever people tend to link to /bin/sh */
929
        rc = access("/bin/bash", 0);
930
        if (rc != 0) {
931
            shell = "/bin/sh";
932
        } else {
933
            shell = "/bin/bash";
934
        }
935
    }
936
    args[0] = shell;
937
938
    /* redirect in and out to stdin, stdout */
939
    dup2(in, 0);
940
    dup2(out, 1);
941
    /* Ignore anything on the stderr */
942
    dup2(devnull, STDERR_FILENO);
943
    close(in);
944
    close(out);
945
    rc = execv(args[0], (char * const *)args);
946
    if (rc < 0) {
947
        char err_msg[SSH_ERRNO_MSG_MAX] = {0};
948
949
        SSH_LOG(SSH_LOG_WARN, "Failed to execute command %s: %s",
950
                command, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
951
    }
952
    exit(1);
953
}
954
955
/**
956
 * @internal
957
 * @brief Open a socket on a ProxyCommand
958
 * This call will always be nonblocking.
959
 * @param s    socket to connect.
960
 * @param command Command to execute.
961
 * @returns SSH_OK socket is being connected.
962
 * @returns SSH_ERROR error while executing the command.
963
 */
964
int
965
ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
966
{
967
    socket_t pair[2];
968
    ssh_poll_handle h = NULL;
969
    int pid;
970
    int rc;
971
972
    if (s->state != SSH_SOCKET_NONE) {
973
        return SSH_ERROR;
974
    }
975
976
    rc = socketpair(PF_UNIX, SOCK_STREAM, 0, pair);
977
    if (rc < 0) {
978
        return SSH_ERROR;
979
    }
980
981
    SSH_LOG(SSH_LOG_DEBUG, "Executing proxycommand '%s'", command);
982
    pid = fork();
983
    if (pid == 0) {
984
        ssh_execute_command(command, pair[0], pair[0]);
985
        /* Does not return */
986
    }
987
    s->proxy_pid = pid;
988
    close(pair[0]);
989
    SSH_LOG(SSH_LOG_DEBUG,
990
            "ProxyCommand connection pipe: [%d,%d]",
991
            pair[0],
992
            pair[1]);
993
994
    rc = ssh_socket_set_fd(s, pair[1]);
995
    if (rc != SSH_OK) {
996
        return rc;
997
    }
998
999
    s->fd_is_socket = 0;
1000
    h = ssh_socket_get_poll_handle(s);
1001
    if (h == NULL) {
1002
        return SSH_ERROR;
1003
    }
1004
    ssh_socket_set_connected(s, h);
1005
    if (s->callbacks && s->callbacks->connected) {
1006
        s->callbacks->connected(SSH_SOCKET_CONNECTED_OK, 0, s->callbacks->userdata);
1007
    }
1008
1009
    return SSH_OK;
1010
}
1011
#endif /* WITH_EXEC */
1012
1013
#ifndef _WIN32
1014
#ifdef HAVE_PTHREAD
1015
static int
1016
verify_knownhost(ssh_session session)
1017
0
{
1018
0
    enum ssh_known_hosts_e state;
1019
1020
0
    state = ssh_session_is_known_server(session);
1021
1022
0
    switch (state) {
1023
0
    case SSH_KNOWN_HOSTS_OK:
1024
0
        break; /* ok */
1025
0
    default:
1026
0
        SSH_LOG(SSH_LOG_WARN, "Couldn't verify knownhost during proxyjump.");
1027
0
        return SSH_ERROR;
1028
0
    }
1029
1030
0
    return SSH_OK;
1031
0
}
1032
1033
static void *
1034
jump_thread_func(void *arg)
1035
0
{
1036
0
    struct jump_thread_data_struct *jump_thread_data = NULL;
1037
0
    struct ssh_jump_info_struct *jis = NULL;
1038
0
    struct ssh_jump_callbacks_struct *cb = NULL;
1039
0
    ssh_session jump_session = NULL;
1040
0
    ssh_channel caa = NULL;
1041
0
    int rc;
1042
0
    ssh_event event = NULL;
1043
0
    ssh_connector connector_in = NULL, connector_out = NULL;
1044
0
    ssh_session session = NULL;
1045
0
    int next_port;
1046
0
    char *next_hostname = NULL;
1047
1048
0
    jump_thread_data = (struct jump_thread_data_struct *)arg;
1049
0
    session = jump_thread_data->session;
1050
1051
0
    next_port = session->opts.port;
1052
0
    next_hostname = strdup(session->opts.host);
1053
1054
0
    jump_session = ssh_new();
1055
0
    if (jump_session == NULL) {
1056
0
        goto exit;
1057
0
    }
1058
1059
0
    jump_session->proxy_root = false;
1060
    /* Reset the global variable if it was previously 1 */
1061
0
    if (session->proxy_root) {
1062
0
        proxy_disconnect = 0;
1063
0
    }
1064
1065
0
    for (jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
1066
0
                                 session->opts.proxy_jumps);
1067
0
         jis != NULL;
1068
0
         jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
1069
0
                                 session->opts.proxy_jumps)) {
1070
0
        rc = ssh_list_append(jump_session->opts.proxy_jumps, jis);
1071
0
        if (rc != SSH_OK) {
1072
0
            ssh_set_error_oom(session);
1073
0
            goto exit;
1074
0
        }
1075
0
    }
1076
0
    for (jis =
1077
0
            ssh_list_pop_head(struct ssh_jump_info_struct *,
1078
0
                              session->opts.proxy_jumps_user_cb);
1079
0
         jis != NULL;
1080
0
         jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
1081
0
                                 session->opts.proxy_jumps_user_cb)) {
1082
0
        rc = ssh_list_append(jump_session->opts.proxy_jumps_user_cb, jis);
1083
0
        if (rc != SSH_OK) {
1084
0
            ssh_set_error_oom(session);
1085
0
            goto exit;
1086
0
        }
1087
0
    }
1088
1089
0
    ssh_options_set(jump_session,
1090
0
                    SSH_OPTIONS_LOG_VERBOSITY,
1091
0
                    &session->common.log_verbosity);
1092
1093
    /* Pop the information about the current jump */
1094
0
    jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
1095
0
                            jump_session->opts.proxy_jumps);
1096
0
    if (jis == NULL) {
1097
0
        SSH_LOG(SSH_LOG_WARN, "Inconsistent list of proxy jumps received");
1098
0
        goto exit;
1099
0
    }
1100
1101
0
    ssh_options_set(jump_session, SSH_OPTIONS_HOST, jis->hostname);
1102
0
    ssh_options_set(jump_session, SSH_OPTIONS_USER, jis->username);
1103
0
    ssh_options_set(jump_session, SSH_OPTIONS_PORT, &jis->port);
1104
1105
    /* Pop the callbacks for the current jump */
1106
0
    cb = ssh_list_pop_head(struct ssh_jump_callbacks_struct *,
1107
0
                           jump_session->opts.proxy_jumps_user_cb);
1108
1109
0
    if (cb != NULL) {
1110
0
        rc = cb->before_connection(jump_session, cb->userdata);
1111
0
        if (rc != SSH_OK) {
1112
0
            SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session));
1113
0
            goto exit;
1114
0
        }
1115
0
    }
1116
1117
    /* If there are more jumps then this will make a new thread and call the
1118
     * current function again, until there are no jumps. When there are no jumps
1119
     * it connects normally. */
1120
0
    rc = ssh_connect(jump_session);
1121
0
    if (rc != SSH_OK) {
1122
0
        SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session));
1123
0
        goto exit;
1124
0
    }
1125
1126
    /* Use the callback or default implementation for verifying knownhost */
1127
0
    if (cb != NULL && cb->verify_knownhost != NULL) {
1128
0
        rc = cb->verify_knownhost(jump_session, cb->userdata);
1129
0
    } else {
1130
0
        rc = verify_knownhost(jump_session);
1131
0
    }
1132
0
    if (rc != SSH_OK) {
1133
0
        goto exit;
1134
0
    }
1135
1136
    /* Use the callback or publickey method to authenticate */
1137
0
    if (cb != NULL && cb->authenticate != NULL) {
1138
0
        rc = cb->authenticate(jump_session, cb->userdata);
1139
0
    } else {
1140
0
        rc = ssh_userauth_publickey_auto(jump_session, NULL, NULL);
1141
0
    }
1142
0
    if (rc != SSH_OK) {
1143
0
        SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session));
1144
0
        goto exit;
1145
0
    }
1146
1147
0
    caa = ssh_channel_new(jump_session);
1148
0
    if (caa == NULL) {
1149
0
        goto exit;
1150
0
    }
1151
    /* The origin hostname and port are set to match OpenSSH implementation
1152
     * they are only used for logging on the server */
1153
0
    rc = ssh_channel_open_forward(caa,
1154
0
                                  next_hostname,
1155
0
                                  next_port,
1156
0
                                  "127.0.0.1",
1157
0
                                  65535);
1158
0
    if (rc != SSH_OK) {
1159
0
        SSH_LOG(SSH_LOG_WARN,
1160
0
                "Error opening port forwarding channel: %s",
1161
0
                ssh_get_error(jump_session));
1162
0
        goto exit;
1163
0
    }
1164
1165
0
    event = ssh_event_new();
1166
0
    if (event == NULL) {
1167
0
        goto exit;
1168
0
    }
1169
1170
0
    connector_in = ssh_connector_new(jump_session);
1171
0
    if (connector_in == NULL) {
1172
0
        goto exit;
1173
0
    }
1174
0
    ssh_connector_set_out_channel(connector_in, caa, SSH_CONNECTOR_STDINOUT);
1175
0
    ssh_connector_set_in_fd(connector_in, jump_thread_data->fd);
1176
0
    ssh_event_add_connector(event, connector_in);
1177
1178
0
    connector_out = ssh_connector_new(jump_session);
1179
0
    if (connector_out == NULL) {
1180
0
        goto exit;
1181
0
    }
1182
0
    ssh_connector_set_out_fd(connector_out, jump_thread_data->fd);
1183
0
    ssh_connector_set_in_channel(connector_out, caa, SSH_CONNECTOR_STDINOUT);
1184
0
    ssh_event_add_connector(event, connector_out);
1185
1186
0
    while (ssh_channel_is_open(caa)) {
1187
0
        if (proxy_disconnect == 1) {
1188
0
            break;
1189
0
        }
1190
0
        rc = ssh_event_dopoll(event, 60000);
1191
0
        if (rc == SSH_ERROR) {
1192
0
            SSH_LOG(SSH_LOG_WARN,
1193
0
                    "Error in ssh_event_dopoll() during proxy jump");
1194
0
            break;
1195
0
        }
1196
0
    }
1197
1198
0
exit:
1199
0
    if (connector_in != NULL) {
1200
0
        ssh_event_remove_connector(event, connector_in);
1201
0
        ssh_connector_free(connector_in);
1202
0
    }
1203
0
    if (connector_out != NULL) {
1204
0
        ssh_event_remove_connector(event, connector_out);
1205
0
        ssh_connector_free(connector_out);
1206
0
    }
1207
0
    SAFE_FREE(next_hostname);
1208
0
    if (jis != NULL) {
1209
0
        SAFE_FREE(jis->hostname);
1210
0
        SAFE_FREE(jis->username);
1211
0
    }
1212
0
    SAFE_FREE(jis);
1213
1214
0
    ssh_disconnect(jump_session);
1215
0
    ssh_event_free(event);
1216
0
    ssh_free(jump_session);
1217
1218
0
    SAFE_FREE(jump_thread_data);
1219
1220
0
    pthread_exit(NULL);
1221
0
}
1222
1223
int
1224
ssh_socket_connect_proxyjump(ssh_socket s)
1225
0
{
1226
0
    ssh_poll_handle h = NULL;
1227
0
    int rc;
1228
0
    pthread_t jump_thread;
1229
0
    struct jump_thread_data_struct *jump_thread_data = NULL;
1230
0
    socket_t pair[2];
1231
1232
0
    if (s->state != SSH_SOCKET_NONE) {
1233
0
        ssh_set_error(
1234
0
            s->session,
1235
0
            SSH_FATAL,
1236
0
            "ssh_socket_connect_proxyjump called on socket not unconnected");
1237
0
        return SSH_ERROR;
1238
0
    }
1239
1240
0
    jump_thread_data = calloc(1, sizeof(struct jump_thread_data_struct));
1241
0
    if (jump_thread_data == NULL) {
1242
0
        ssh_set_error_oom(s->session);
1243
0
        return SSH_ERROR;
1244
0
    }
1245
1246
0
    rc = socketpair(PF_UNIX, SOCK_STREAM, 0, pair);
1247
0
    if (rc == -1) {
1248
0
        char err_msg[SSH_ERRNO_MSG_MAX] = {0};
1249
1250
0
        ssh_set_error(s->session,
1251
0
                      SSH_FATAL,
1252
0
                      "Creating socket pair failed: %s",
1253
0
                      ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
1254
0
        SAFE_FREE(jump_thread_data);
1255
0
        return SSH_ERROR;
1256
0
    }
1257
1258
0
    jump_thread_data->session = s->session;
1259
0
    jump_thread_data->fd = pair[0];
1260
1261
0
    rc = pthread_create(&jump_thread, NULL, jump_thread_func, jump_thread_data);
1262
0
    if (rc != 0) {
1263
0
        char err_msg[SSH_ERRNO_MSG_MAX] = {0};
1264
1265
0
        ssh_set_error(s->session,
1266
0
                      SSH_FATAL,
1267
0
                      "Creating new thread failed: %s",
1268
0
                      ssh_strerror(rc, err_msg, SSH_ERRNO_MSG_MAX));
1269
0
        SAFE_FREE(jump_thread_data);
1270
0
        return SSH_ERROR;
1271
0
    }
1272
0
    rc = pthread_detach(jump_thread);
1273
0
    if (rc != 0) {
1274
0
        char err_msg[SSH_ERRNO_MSG_MAX] = {0};
1275
1276
0
        ssh_set_error(s->session,
1277
0
                      SSH_FATAL,
1278
0
                      "Failed to detach thread: %s",
1279
0
                      ssh_strerror(rc, err_msg, SSH_ERRNO_MSG_MAX));
1280
0
        SAFE_FREE(jump_thread_data);
1281
0
        return SSH_ERROR;
1282
0
    }
1283
1284
0
    SSH_LOG(SSH_LOG_DEBUG,
1285
0
            "ProxyJump connection pipe: [%d,%d]",
1286
0
            pair[0],
1287
0
            pair[1]);
1288
1289
0
    rc = ssh_socket_set_fd(s, pair[1]);
1290
0
    if (rc != SSH_OK) {
1291
0
        return rc;
1292
0
    }
1293
1294
0
    s->fd_is_socket = 1;
1295
0
    h = ssh_socket_get_poll_handle(s);
1296
0
    if (h == NULL) {
1297
0
        return SSH_ERROR;
1298
0
    }
1299
0
    ssh_socket_set_connected(s, h);
1300
0
    if (s->callbacks && s->callbacks->connected) {
1301
0
        s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,
1302
0
                                0,
1303
0
                                s->callbacks->userdata);
1304
0
    }
1305
1306
0
    return SSH_OK;
1307
0
}
1308
1309
#endif /* HAVE_PTHREAD */
1310
1311
#endif /* _WIN32 */
1312
/** @} */