Coverage Report

Created: 2025-07-01 07:11

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