Coverage Report

Created: 2026-01-10 06:41

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
 *
117
 * @brief Initialize socket support for libssh.
118
 *
119
 * Initializes the socket subsystem, calling WSAStartup() on Windows and
120
 * ssh_poll_init() on all platforms. Can be called multiple times.
121
 *
122
 * @return 0 on success; -1 on Windows socket initialization failure.
123
 */
124
int ssh_socket_init(void)
125
2
{
126
2
    if (sockets_initialized == 0) {
127
#ifdef _WIN32
128
        struct WSAData wsaData;
129
130
        /* Initiates use of the Winsock DLL by a process. */
131
        if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
132
            return -1;
133
        }
134
#endif
135
2
        ssh_poll_init();
136
137
2
        sockets_initialized = 1;
138
2
    }
139
140
2
    return 0;
141
2
}
142
143
/**
144
 * @internal
145
 *
146
 * @brief Cleanup socket support for libssh.
147
 *
148
 * Cleans up the socket subsystem, calling ssh_poll_cleanup() on all platforms
149
 * and WSACleanup() on Windows. Can be called multiple times.
150
 */
151
void ssh_socket_cleanup(void)
152
0
{
153
0
    if (sockets_initialized == 1) {
154
0
        ssh_poll_cleanup();
155
#ifdef _WIN32
156
        WSACleanup();
157
#endif
158
0
        sockets_initialized = 0;
159
0
    }
160
0
}
161
162
/**
163
 * @internal
164
 *
165
 * @brief Allocate and initialize a new SSH socket structure.
166
 *
167
 * Creates a new ssh_socket structure associated with the given session,
168
 * initializes input/output buffers and sets default socket state.
169
 *
170
 * @param[in] session The SSH session to associate with the socket.
171
 *
172
 * @return A new ssh_socket on success; NULL on memory allocation failure.
173
 */
174
ssh_socket ssh_socket_new(ssh_session session)
175
587
{
176
587
    ssh_socket s;
177
178
587
    s = calloc(1, sizeof(struct ssh_socket_struct));
179
587
    if (s == NULL) {
180
28
        ssh_set_error_oom(session);
181
28
        return NULL;
182
28
    }
183
559
    s->fd = SSH_INVALID_SOCKET;
184
559
    s->last_errno = -1;
185
559
    s->fd_is_socket = 1;
186
559
    s->session = session;
187
559
    s->in_buffer = ssh_buffer_new();
188
559
    if (s->in_buffer == NULL) {
189
16
        ssh_set_error_oom(session);
190
16
        SAFE_FREE(s);
191
16
        return NULL;
192
16
    }
193
543
    s->out_buffer=ssh_buffer_new();
194
543
    if (s->out_buffer == NULL) {
195
3
        ssh_set_error_oom(session);
196
3
        SSH_BUFFER_FREE(s->in_buffer);
197
3
        SAFE_FREE(s);
198
3
        return NULL;
199
3
    }
200
540
    s->read_wontblock = 0;
201
540
    s->write_wontblock = 0;
202
540
    s->data_except = 0;
203
540
    s->poll_handle = NULL;
204
540
    s->state=SSH_SOCKET_NONE;
205
540
    return s;
206
543
}
207
208
/**
209
 * @internal
210
 *
211
 * @brief Reset the state of a socket, so it looks brand new.
212
 *
213
 * Clears the file descriptor, reinitializes input/output buffers, frees
214
 * the poll handle if present, and resets all socket state flags.
215
 *
216
 * @param[in] s The SSH socket to reset.
217
 */
218
void ssh_socket_reset(ssh_socket s)
219
269
{
220
269
    s->fd = SSH_INVALID_SOCKET;
221
269
    s->last_errno = -1;
222
269
    s->fd_is_socket = 1;
223
269
    ssh_buffer_reinit(s->in_buffer);
224
269
    ssh_buffer_reinit(s->out_buffer);
225
269
    s->read_wontblock = 0;
226
269
    s->write_wontblock = 0;
227
269
    s->data_except = 0;
228
269
    if (s->poll_handle != NULL) {
229
269
        ssh_poll_free(s->poll_handle);
230
269
        s->poll_handle = NULL;
231
269
    }
232
269
    s->state=SSH_SOCKET_NONE;
233
269
#ifndef _WIN32
234
269
    s->proxy_pid = 0;
235
269
#endif
236
269
}
237
238
/**
239
 * @internal
240
 * @brief the socket callbacks, i.e. callbacks to be called
241
 * upon a socket event.
242
 * @param s socket to set callbacks on.
243
 * @param callbacks a ssh_socket_callback object reference.
244
 */
245
void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks)
246
503
{
247
503
    s->callbacks = callbacks;
248
503
}
249
250
/**
251
 * @internal
252
 *
253
 * @brief Mark an SSH socket as connected.
254
 *
255
 * Sets the socket state to connected and configures the poll handle
256
 * to wait for `POLLIN` and `POLLOUT` events (needed for non-blocking connect).
257
 *
258
 * @param[in] s The SSH socket.
259
 * @param[in] p The poll handle to configure, or NULL.
260
 */
261
void ssh_socket_set_connected(ssh_socket s, struct ssh_poll_handle_struct *p)
262
269
{
263
269
    s->state = SSH_SOCKET_CONNECTED;
264
    /* `POLLOUT` is the event to wait for in a non-blocking connect */
265
269
    if (p != NULL) {
266
269
        ssh_poll_set_events(p, POLLIN | POLLOUT);
267
269
    }
268
269
}
269
270
/**
271
 * @internal
272
 *
273
 * @brief               SSH poll callback. This callback will be used when an
274
 *                      event caught on the socket.
275
 *
276
 * @param p             Poll object this callback belongs to.
277
 * @param fd            The raw socket.
278
 * @param revents       The current poll events on the socket.
279
 * @param v_s           Userdata to be passed to the callback function,
280
 *                      in this case the socket object.
281
 *
282
 * @return              0 on success, < 0 when the poll object has been removed
283
 *                      from its poll context.
284
 */
285
int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
286
                            socket_t fd,
287
                            int revents,
288
                            void *v_s)
289
1.14k
{
290
1.14k
    ssh_socket s = (ssh_socket)v_s;
291
1.14k
    void *buffer = NULL;
292
1.14k
    ssize_t nread = 0;
293
1.14k
    int rc;
294
1.14k
    int err = 0;
295
1.14k
    socklen_t errlen = sizeof(err);
296
297
    /* Do not do anything if this socket was already closed */
298
1.14k
    if (!ssh_socket_is_open(s)) {
299
0
        return -1;
300
0
    }
301
1.14k
    SSH_LOG(SSH_LOG_TRACE,
302
1.14k
            "Poll callback on socket %d (%s%s%s), out buffer %" PRIu32, fd,
303
1.14k
            (revents & POLLIN) ? "POLLIN ":"",
304
1.14k
            (revents & POLLOUT) ? "POLLOUT ":"",
305
1.14k
            (revents & POLLERR) ? "POLLERR":"",
306
1.14k
            ssh_buffer_get_len(s->out_buffer));
307
1.14k
    if ((revents & POLLERR) || (revents & POLLHUP)) {
308
        /* Check if we are in a connecting state */
309
0
        if (s->state == SSH_SOCKET_CONNECTING) {
310
0
            s->state = SSH_SOCKET_ERROR;
311
0
            rc = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen);
312
0
            if (rc < 0) {
313
0
                err = errno;
314
0
            }
315
0
            ssh_socket_close(s);
316
            /* Overwrite ssh_socket_close() error with the real socket error */
317
0
            s->last_errno = err;
318
0
            errno = err;
319
320
0
            if (s->callbacks != NULL && s->callbacks->connected != NULL) {
321
0
                s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR,
322
0
                                        err,
323
0
                                        s->callbacks->userdata);
324
0
            }
325
326
0
            return -1;
327
0
        }
328
        /* Then we are in a more standard kind of error */
329
        /* force a read to get an explanation */
330
0
        revents |= POLLIN;
331
0
    }
332
1.14k
    if ((revents & POLLIN) && s->state == SSH_SOCKET_CONNECTED) {
333
871
        s->read_wontblock = 1;
334
871
        buffer = ssh_buffer_allocate(s->in_buffer, MAX_BUF_SIZE);
335
871
        if (buffer) {
336
871
            nread = ssh_socket_unbuffered_read(s, buffer, MAX_BUF_SIZE);
337
871
        }
338
871
        if (nread < 0) {
339
0
            ssh_buffer_pass_bytes_end(s->in_buffer, MAX_BUF_SIZE);
340
0
            if (p != NULL) {
341
0
                ssh_poll_remove_events(p, POLLIN);
342
0
            }
343
344
0
            if (s->callbacks != NULL && s->callbacks->exception != NULL) {
345
0
                s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,
346
0
                                        s->last_errno,
347
0
                                        s->callbacks->userdata);
348
0
            }
349
0
            return -2;
350
0
        }
351
352
        /* Rollback the unused space */
353
871
        ssh_buffer_pass_bytes_end(s->in_buffer,
354
871
                                  (uint32_t)(MAX_BUF_SIZE - nread));
355
356
871
        if (nread == 0) {
357
94
            if (p != NULL) {
358
94
                ssh_poll_remove_events(p, POLLIN);
359
94
            }
360
94
            if (s->callbacks != NULL && s->callbacks->exception != NULL) {
361
94
                s->callbacks->exception(SSH_SOCKET_EXCEPTION_EOF,
362
94
                                        0,
363
94
                                        s->callbacks->userdata);
364
94
            }
365
94
            return -2;
366
94
        }
367
368
777
        if (s->session->socket_counter != NULL) {
369
0
            s->session->socket_counter->in_bytes += nread;
370
0
        }
371
372
        /* Call the callback */
373
777
        if (s->callbacks != NULL && s->callbacks->data != NULL) {
374
777
            size_t processed;
375
392k
            do {
376
392k
                processed = s->callbacks->data(ssh_buffer_get(s->in_buffer),
377
392k
                                               ssh_buffer_get_len(s->in_buffer),
378
392k
                                               s->callbacks->userdata);
379
392k
                ssh_buffer_pass_bytes(s->in_buffer, (uint32_t)processed);
380
392k
            } while ((processed > 0) && (s->state == SSH_SOCKET_CONNECTED));
381
382
            /* p may have been freed, so don't use it
383
             * anymore in this function */
384
777
            p = NULL;
385
777
        }
386
777
    }
387
#ifdef _WIN32
388
    if (revents & POLLOUT || revents & POLLWRNORM) {
389
#else
390
1.04k
    if (revents & POLLOUT) {
391
1.04k
#endif
392
1.04k
        uint32_t len;
393
394
        /* First, POLLOUT is a sign we may be connected */
395
1.04k
        if (s->state == SSH_SOCKET_CONNECTING) {
396
269
            SSH_LOG(SSH_LOG_PACKET, "Received POLLOUT in connecting state");
397
269
            ssh_socket_set_connected(s, p);
398
399
269
            rc = ssh_socket_set_blocking(ssh_socket_get_fd(s));
400
269
            if (rc < 0) {
401
0
                return -1;
402
0
            }
403
404
269
            if (s->callbacks != NULL && s->callbacks->connected != NULL) {
405
269
                s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,
406
269
                                        0,
407
269
                                        s->callbacks->userdata);
408
269
            }
409
410
269
            return 0;
411
269
        }
412
413
        /* So, we can write data */
414
777
        s->write_wontblock = 1;
415
777
        if (p != NULL) {
416
0
            ssh_poll_remove_events(p, POLLOUT);
417
0
        }
418
419
        /* If buffered data is pending, write it */
420
777
        len = ssh_buffer_get_len(s->out_buffer);
421
777
        if (len > 0) {
422
256
            ssh_socket_nonblocking_flush(s);
423
521
        } else if (s->callbacks != NULL && s->callbacks->controlflow != NULL) {
424
            /* Otherwise advertise the upper level that write can be done */
425
170
            SSH_LOG(SSH_LOG_TRACE, "sending control flow event");
426
170
            s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,
427
170
                                      s->callbacks->userdata);
428
170
        }
429
        /* TODO: Find a way to put back POLLOUT when buffering occurs */
430
777
    }
431
432
    /* Return -1 if the poll handler disappeared */
433
777
    if (s->poll_handle == NULL) {
434
0
        return -1;
435
0
    }
436
437
777
    return 0;
438
777
}
439
440
/** @internal
441
 * @brief returns the poll handle corresponding to the socket,
442
 * creates it if it does not exist.
443
 * @returns allocated and initialized ssh_poll_handle object
444
 */
445
ssh_poll_handle ssh_socket_get_poll_handle(ssh_socket s)
446
1.40k
{
447
1.40k
    if (s->poll_handle) {
448
1.14k
        return s->poll_handle;
449
1.14k
    }
450
269
    s->poll_handle = ssh_poll_new(s->fd, 0, ssh_socket_pollcallback, s);
451
269
    return s->poll_handle;
452
1.40k
}
453
454
/**
455
 * @internal
456
 *
457
 * @brief Deletes a socket object.
458
 *
459
 * Closes the socket connection, frees input/output buffers and
460
 * releases the socket structure memory.
461
 *
462
 * @param[in] s The SSH socket to free, or NULL.
463
 */
464
void ssh_socket_free(ssh_socket s)
465
652
{
466
652
    if (s == NULL) {
467
112
        return;
468
112
    }
469
540
    ssh_socket_close(s);
470
540
    SSH_BUFFER_FREE(s->in_buffer);
471
540
    SSH_BUFFER_FREE(s->out_buffer);
472
540
    SAFE_FREE(s);
473
540
}
474
475
/**
476
 * @internal
477
 *
478
 * @brief Connect an SSH socket to a Unix domain socket.
479
 *
480
 * Creates a Unix domain socket connection to the given @p path and associates
481
 * it with the SSH socket.
482
 *
483
 * @param[in] s    The SSH socket to connect.
484
 * @param[in] path Path to the Unix domain socket.
485
 *
486
 * @return `SSH_OK` on success; `SSH_ERROR` on socket creation, connect, or fd
487
 * setup failure.
488
 */
489
int ssh_socket_unix(ssh_socket s, const char *path)
490
0
{
491
0
    struct sockaddr_un sunaddr;
492
0
    char err_msg[SSH_ERRNO_MSG_MAX] = {0};
493
0
    socket_t fd;
494
0
    sunaddr.sun_family = AF_UNIX;
495
0
    snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path);
496
497
0
    fd = socket(AF_UNIX, SOCK_STREAM, 0);
498
0
    if (fd == SSH_INVALID_SOCKET) {
499
0
        ssh_set_error(s->session, SSH_FATAL,
500
0
                      "Error from socket(AF_UNIX, SOCK_STREAM, 0): %s",
501
0
                      ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
502
0
        return SSH_ERROR;
503
0
    }
504
505
0
#ifndef _WIN32
506
0
    if (fcntl(fd, F_SETFD, 1) == -1) {
507
0
        ssh_set_error(s->session, SSH_FATAL,
508
0
                      "Error from fcntl(fd, F_SETFD, 1): %s",
509
0
                      ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
510
0
        CLOSE_SOCKET(fd);
511
0
        return SSH_ERROR;
512
0
    }
513
0
#endif
514
515
0
    if (connect(fd, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) {
516
0
        ssh_set_error(s->session, SSH_FATAL, "Error from connect(%s): %s",
517
0
                      path,
518
0
                      ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
519
0
        CLOSE_SOCKET(fd);
520
0
        return SSH_ERROR;
521
0
    }
522
0
    return ssh_socket_set_fd(s, fd);
523
0
}
524
525
/**
526
 * @internal
527
 *
528
 * @brief Close an SSH socket.
529
 *
530
 * Closes the socket file descriptor if open, saves the last error code,
531
 * frees the poll handle if unlocked, and marks the socket state as closed.
532
 * On Unix, attempts to terminate and wait for any running proxy command
533
 * process.
534
 *
535
 * @param[in] s The SSH socket to close.
536
 */
537
void ssh_socket_close(ssh_socket s)
538
809
{
539
809
    if (ssh_socket_is_open(s)) {
540
#ifdef _WIN32
541
        CLOSE_SOCKET(s->fd);
542
        s->last_errno = WSAGetLastError();
543
#else
544
0
        CLOSE_SOCKET(s->fd);
545
0
        s->last_errno = errno;
546
0
#endif
547
0
    }
548
549
809
    if (s->poll_handle != NULL && !ssh_poll_is_locked(s->poll_handle)) {
550
0
        ssh_poll_free(s->poll_handle);
551
0
        s->poll_handle = NULL;
552
0
    }
553
554
809
    s->state = SSH_SOCKET_CLOSED;
555
556
809
#ifndef _WIN32
557
    /* If the proxy command still runs try to kill it */
558
809
    if (s->proxy_pid != 0) {
559
0
        int status;
560
0
        pid_t pid = s->proxy_pid;
561
562
0
        s->proxy_pid = 0;
563
0
        kill(pid, SIGTERM);
564
0
        while (waitpid(pid, &status, 0) == -1) {
565
0
            if (errno != EINTR) {
566
0
                char err_msg[SSH_ERRNO_MSG_MAX] = {0};
567
0
                SSH_LOG(SSH_LOG_TRACE, "waitpid failed: %s",
568
0
                        ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
569
0
                return;
570
0
            }
571
0
        }
572
0
        if (!WIFEXITED(status)) {
573
0
            SSH_LOG(SSH_LOG_TRACE, "Proxy command exited abnormally");
574
0
            return;
575
0
        }
576
0
        SSH_LOG(SSH_LOG_TRACE, "Proxy command returned %d", WEXITSTATUS(status));
577
0
    }
578
809
#endif
579
809
}
580
581
/**
582
 * @internal
583
 * @brief sets the file descriptor of the socket.
584
 * @param[out] s ssh_socket to update
585
 * @param[in] fd file descriptor to set
586
 * @warning this function updates both the input and output
587
 * file descriptors
588
 */
589
int ssh_socket_set_fd(ssh_socket s, socket_t fd)
590
269
{
591
269
    ssh_poll_handle h = NULL;
592
593
269
    s->fd = fd;
594
595
269
    if (s->poll_handle) {
596
0
        ssh_poll_set_fd(s->poll_handle,fd);
597
269
    } else {
598
269
        s->state = SSH_SOCKET_CONNECTING;
599
269
        h = ssh_socket_get_poll_handle(s);
600
269
        if (h == NULL) {
601
0
            return SSH_ERROR;
602
0
        }
603
604
        /* POLLOUT is the event to wait for in a nonblocking connect */
605
269
        ssh_poll_set_events(h, POLLOUT);
606
#ifdef _WIN32
607
        ssh_poll_add_events(h, POLLWRNORM);
608
#endif
609
269
    }
610
269
    return SSH_OK;
611
269
}
612
613
/**
614
 * @internal
615
 *
616
 * @brief Returns the input file descriptor of a socket.
617
 *
618
 * @param[in] s The SSH socket.
619
 *
620
 * @return The socket file descriptor (socket_t).
621
 */
622
socket_t ssh_socket_get_fd(ssh_socket s)
623
269
{
624
269
    return s->fd;
625
269
}
626
627
/**
628
 * @internal
629
 *
630
 * @brief Check if an SSH socket is open.
631
 *
632
 * @param[in] s The SSH socket.
633
 *
634
 * @return Non-zero if socket is open, 0 if closed or invalid.
635
 */
636
int ssh_socket_is_open(ssh_socket s)
637
7.25k
{
638
7.25k
    return s->fd != SSH_INVALID_SOCKET;
639
7.25k
}
640
641
/**
642
 * @internal
643
 *
644
 * @brief Perform an unbuffered read from an SSH socket.
645
 *
646
 * Reads @p len bytes from the socket file descriptor directly into @p buffer,
647
 * using `recv()` if the descriptor is a socket, or `read()` otherwise.
648
 * Updates internal error and state flags based on the result.
649
 *
650
 * @param[in]  s      The SSH socket.
651
 * @param[out] buffer Buffer to read data into.
652
 * @param[in]  len    Maximum number of bytes to read.
653
 *
654
 * @return Number of bytes read on success, or -1 on error.
655
 */
656
static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
657
                                          void *buffer,
658
                                          uint32_t len)
659
871
{
660
871
    ssize_t rc = -1;
661
662
871
    if (s->data_except) {
663
0
        return -1;
664
0
    }
665
871
    if (s->fd_is_socket) {
666
871
        rc = recv(s->fd, buffer, len, 0);
667
871
    } else {
668
0
        rc = read(s->fd, buffer, len);
669
0
    }
670
#ifdef _WIN32
671
    s->last_errno = WSAGetLastError();
672
#else
673
871
    s->last_errno = errno;
674
871
#endif
675
871
    s->read_wontblock = 0;
676
677
871
    if (rc < 0) {
678
0
        s->data_except = 1;
679
871
    } else {
680
871
        SSH_LOG(SSH_LOG_TRACE, "read %zd", rc);
681
871
    }
682
683
871
    return rc;
684
871
}
685
686
/**
687
 * @internal
688
 *
689
 * @brief Perform an unbuffered write to an SSH socket.
690
 *
691
 * Writes @p len bytes from @p buffer to the socket file descriptor,
692
 * using `send()` if the descriptor is a socket or `write()` otherwise.
693
 * Updates internal error and state flags, and re-enables POLLOUT
694
 * polling if a poll handle exists.
695
 *
696
 * @param[in] s      The SSH socket.
697
 * @param[in] buffer Buffer containing data to write.
698
 * @param[in] len    Number of bytes to write.
699
 *
700
 * @return Number of bytes written on success, or -1 on error.
701
 */
702
static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
703
                                           const void *buffer,
704
                                           uint32_t len)
705
595
{
706
595
    ssize_t w = -1;
707
595
    int flags = 0;
708
709
595
#ifdef MSG_NOSIGNAL
710
595
    flags |= MSG_NOSIGNAL;
711
595
#endif
712
713
595
    if (s->data_except) {
714
0
        return -1;
715
0
    }
716
717
595
    if (s->fd_is_socket) {
718
595
        w = send(s->fd, buffer, len, flags);
719
595
    } else {
720
0
        w = write(s->fd, buffer, len);
721
0
    }
722
#ifdef _WIN32
723
    s->last_errno = WSAGetLastError();
724
#else
725
595
    s->last_errno = errno;
726
595
#endif
727
595
    s->write_wontblock = 0;
728
    /* Reactive the POLLOUT detector in the poll multiplexer system */
729
595
    if (s->poll_handle) {
730
595
        SSH_LOG(SSH_LOG_PACKET, "Enabling POLLOUT for socket");
731
595
        ssh_poll_add_events(s->poll_handle, POLLOUT);
732
595
    }
733
595
    if (w < 0) {
734
0
        s->data_except = 1;
735
0
    }
736
737
595
    SSH_LOG(SSH_LOG_TRACE, "wrote %zd", w);
738
595
    return w;
739
595
}
740
741
/**
742
 * @internal
743
 *
744
 * @brief Check if SSH socket file descriptor is set in an fd_set.
745
 *
746
 * Tests if the socket's file descriptor is present in the
747
 * given @p set (fd_set) . Returns 0 if the socket has no valid file descriptor.
748
 *
749
 * @param[in] s   The SSH socket.
750
 * @param[in] set The fd_set to test against.
751
 *
752
 * @return Non-zero if the socket fd is set in the fd_set, 0 otherwise.
753
 */
754
int ssh_socket_fd_isset(ssh_socket s, fd_set *set)
755
0
{
756
0
    if(s->fd == SSH_INVALID_SOCKET) {
757
0
        return 0;
758
0
    }
759
0
    return FD_ISSET(s->fd,set);
760
0
}
761
762
/**
763
 * @internal
764
 *
765
 * @brief Add SSH socket file descriptor to an fd_set.
766
 *
767
 * Adds the socket's file descriptor to the given @p set (fd_set)
768
 * and updates @p max_fd if this socket has the highest file descriptor number.
769
 * @param[in]     s       The SSH socket.
770
 * @param[in,out] set     The fd_set to add the socket to.
771
 * @param[in,out] max_fd  the maximum fd value.
772
 */
773
void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd)
774
0
{
775
0
    if (s->fd == SSH_INVALID_SOCKET) {
776
0
        return;
777
0
    }
778
779
0
    FD_SET(s->fd,set);
780
781
0
    if (s->fd >= 0 &&
782
0
        s->fd >= *max_fd &&
783
0
        s->fd != SSH_INVALID_SOCKET) {
784
0
        *max_fd = s->fd + 1;
785
0
    }
786
0
}
787
788
/**
789
 * @internal
790
 *
791
 * @brief Write data to an SSH socket output buffer.
792
 *
793
 * Adds the data to the socket's output @p buffer and calls a nonblocking
794
 * flush attempt to send buffered data.
795
 *
796
 * @param[in] s      The SSH socket.
797
 * @param[in] buffer Data to write.
798
 * @param[in] len    Number of bytes to write.
799
 *
800
 * @return `SSH_OK` on success; `SSH_ERROR` on buffer allocation failure.
801
 *
802
 * @warning It has no effect on socket before a flush.
803
 */
804
int ssh_socket_write(ssh_socket s, const void *buffer, uint32_t len)
805
4.78k
{
806
4.78k
    if (len > 0) {
807
4.78k
        if (ssh_buffer_add_data(s->out_buffer, buffer, len) < 0) {
808
0
            ssh_set_error_oom(s->session);
809
0
            return SSH_ERROR;
810
0
        }
811
4.78k
        ssh_socket_nonblocking_flush(s);
812
4.78k
    }
813
814
4.78k
    return SSH_OK;
815
4.78k
}
816
817
/**
818
 * @internal
819
 *
820
 * @brief Starts a nonblocking flush of the output buffer.
821
 *
822
 * Sends all buffered data from the socket's output buffer.
823
 * If the socket is not open, marks the session as dead and calls an
824
 * exception callback or sets a fatal error. If the socket cannot currently
825
 * accept data, polls for writable events and returns `SSH_AGAIN`.
826
 * On write errors, closes the socket and signals the error. Updates
827
 * byte counters on successful writes.
828
 *
829
 * @param[in] s The SSH socket.
830
 *
831
 * @return `SSH_OK` if all data was sent; `SSH_AGAIN` if the operation should
832
 *         be retried later; `SSH_ERROR` on fatal socket error.
833
 */
834
int ssh_socket_nonblocking_flush(ssh_socket s)
835
5.03k
{
836
5.03k
    ssh_session session = s->session;
837
5.03k
    uint32_t len;
838
839
5.03k
    if (!ssh_socket_is_open(s)) {
840
0
        session->alive = 0;
841
0
        if (s->callbacks && s->callbacks->exception) {
842
0
            s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,
843
0
                                    s->last_errno,
844
0
                                    s->callbacks->userdata);
845
0
        } else {
846
0
            char err_msg[SSH_ERRNO_MSG_MAX] = {0};
847
0
            ssh_set_error(session,
848
0
                          SSH_FATAL,
849
0
                          "Writing packet: error on socket (or connection "
850
0
                          "closed): %s",
851
0
                          ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
852
0
        }
853
854
0
        return SSH_ERROR;
855
0
    }
856
857
5.03k
    len = ssh_buffer_get_len(s->out_buffer);
858
5.03k
    if (!s->write_wontblock && s->poll_handle && len > 0) {
859
        /* force the poll system to catch pollout events */
860
4.44k
        ssh_poll_add_events(s->poll_handle, POLLOUT);
861
862
4.44k
        return SSH_AGAIN;
863
4.44k
    }
864
865
595
    if (s->write_wontblock && len > 0) {
866
595
        ssize_t bwritten;
867
868
595
        bwritten = ssh_socket_unbuffered_write(s,
869
595
                                               ssh_buffer_get(s->out_buffer),
870
595
                                               len);
871
595
        if (bwritten < 0) {
872
0
            session->alive = 0;
873
0
            ssh_socket_close(s);
874
875
0
            if (s->callbacks && s->callbacks->exception) {
876
0
                s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,
877
0
                                        s->last_errno,
878
0
                                        s->callbacks->userdata);
879
0
            } else {
880
0
                char err_msg[SSH_ERRNO_MSG_MAX] = {0};
881
0
                ssh_set_error(session,
882
0
                              SSH_FATAL,
883
0
                              "Writing packet: error on socket (or connection "
884
0
                              "closed): %s",
885
0
                              ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
886
0
            }
887
888
0
            return SSH_ERROR;
889
0
        }
890
891
595
        ssh_buffer_pass_bytes(s->out_buffer, (uint32_t)bwritten);
892
595
        if (s->session->socket_counter != NULL) {
893
0
            s->session->socket_counter->out_bytes += bwritten;
894
0
        }
895
595
    }
896
897
    /* Is there some data pending? */
898
595
    len = ssh_buffer_get_len(s->out_buffer);
899
595
    if (s->poll_handle && len > 0) {
900
0
        SSH_LOG(SSH_LOG_TRACE,
901
0
                "did not send all the data, queuing pollout event");
902
        /* force the poll system to catch pollout events */
903
0
        ssh_poll_add_events(s->poll_handle, POLLOUT);
904
905
0
        return SSH_AGAIN;
906
0
    }
907
908
    /* all data written */
909
595
    return SSH_OK;
910
595
}
911
912
/**
913
 * @internal
914
 *
915
 * @brief Set the SSH socket write_wontblock flag.
916
 *
917
 * Marks the socket as ready for nonblocking writes (`write_wontblock = 1`).
918
 * Used by the poll system when POLLOUT becomes available.
919
 *
920
 * @param[in] s The SSH socket.
921
 */
922
void ssh_socket_set_write_wontblock(ssh_socket s)
923
269
{
924
269
    s->write_wontblock = 1;
925
269
}
926
927
/**
928
 * @internal
929
 *
930
 * @brief Set the SSH socket read_wontblock flag.
931
 *
932
 * Marks the socket as ready for nonblocking reads (`read_wontblock = 1`).
933
 * Used by the poll system when POLLIN becomes available.
934
 *
935
 * @param[in] s The SSH socket.
936
 */
937
void ssh_socket_set_read_wontblock(ssh_socket s)
938
0
{
939
0
    s->read_wontblock = 1;
940
0
}
941
942
/**
943
 * @internal
944
 *
945
 * @brief Set the SSH socket exception flag.
946
 *
947
 * Marks the socket as having an exception condition (`data_except = 1`).
948
 *
949
 * @param[in] s The SSH socket.
950
 */
951
void ssh_socket_set_except(ssh_socket s)
952
0
{
953
0
    s->data_except = 1;
954
0
}
955
956
/**
957
 * @internal
958
 *
959
 * @brief Check if SSH socket data is available for reading.
960
 *
961
 * Returns true if the socket is ready for nonblocking reads
962
 * (`read_wontblock` flag is set).
963
 *
964
 * @param[in] s The SSH socket.
965
 *
966
 * @return 1 if data is available, 0 otherwise.
967
 */
968
int ssh_socket_data_available(ssh_socket s)
969
0
{
970
0
    return s->read_wontblock;
971
0
}
972
973
/**
974
 * @internal
975
 *
976
 * @brief Check if SSH socket is writable.
977
 *
978
 * Returns true if the socket is ready for nonblocking writes
979
 * (`write_wontblock` flag is set).
980
 *
981
 * @param[in] s The SSH socket.
982
 *
983
 * @return 1 if socket is writable, 0 otherwise.
984
 */
985
int ssh_socket_data_writable(ssh_socket s)
986
0
{
987
0
    return s->write_wontblock;
988
0
}
989
990
/** @internal
991
 * @brief returns the number of outgoing bytes currently buffered
992
 * @param s the socket
993
 * @returns numbers of bytes buffered, or 0 if the socket isn't connected
994
 */
995
int ssh_socket_buffered_write_bytes(ssh_socket s)
996
0
{
997
0
    if (s==NULL || s->out_buffer == NULL) {
998
0
        return 0;
999
0
    }
1000
1001
0
    return ssh_buffer_get_len(s->out_buffer);
1002
0
}
1003
1004
/**
1005
 * @internal
1006
 *
1007
 * @brief Get the current status of an SSH socket.
1008
 *
1009
 * Checks the input/output buffers and exception flag to determine socket
1010
 * status: `SSH_READ_PENDING` if input data available, `SSH_WRITE_PENDING`
1011
 * if output data pending, `SSH_CLOSED_ERROR` if exception occurred.
1012
 *
1013
 * @param[in] s The SSH socket.
1014
 *
1015
 * @return Socket status flags.
1016
 */
1017
int ssh_socket_get_status(ssh_socket s)
1018
0
{
1019
0
    int r = 0;
1020
1021
0
    if (ssh_buffer_get_len(s->in_buffer) > 0) {
1022
0
        r |= SSH_READ_PENDING;
1023
0
    }
1024
1025
0
    if (ssh_buffer_get_len(s->out_buffer) > 0) {
1026
0
        r |= SSH_WRITE_PENDING;
1027
0
    }
1028
1029
0
    if (s->data_except) {
1030
0
        r |= SSH_CLOSED_ERROR;
1031
0
    }
1032
1033
0
    return r;
1034
0
}
1035
1036
/**
1037
 * @internal
1038
 *
1039
 * @brief Get SSH socket poll flags from the poll handle.
1040
 *
1041
 * Checks the poll handle events and returns `SSH_READ_PENDING` if POLLIN
1042
 * is set, `SSH_WRITE_PENDING` if POLLOUT is set.
1043
 *
1044
 * @param[in] s The SSH socket.
1045
 *
1046
 * @return Socket status flags based on poll events.
1047
 */
1048
int ssh_socket_get_poll_flags(ssh_socket s)
1049
0
{
1050
0
    int r = 0;
1051
0
    if (s->poll_handle != NULL && (ssh_poll_get_events (s->poll_handle) & POLLIN) > 0) {
1052
0
        r |= SSH_READ_PENDING;
1053
0
    }
1054
0
    if (s->poll_handle != NULL && (ssh_poll_get_events (s->poll_handle) & POLLOUT) > 0) {
1055
0
        r |= SSH_WRITE_PENDING;
1056
0
    }
1057
0
    return r;
1058
0
}
1059
1060
#ifdef _WIN32
1061
int ssh_socket_set_nonblocking(socket_t fd)
1062
{
1063
    u_long nonblocking = 1;
1064
    return ioctlsocket(fd, FIONBIO, &nonblocking);
1065
}
1066
1067
int ssh_socket_set_blocking(socket_t fd)
1068
{
1069
    u_long nonblocking = 0;
1070
    return ioctlsocket(fd, FIONBIO, &nonblocking);
1071
}
1072
1073
#else /* _WIN32 */
1074
int ssh_socket_set_nonblocking(socket_t fd)
1075
0
{
1076
0
    return fcntl(fd, F_SETFL, O_NONBLOCK);
1077
0
}
1078
1079
int ssh_socket_set_blocking(socket_t fd)
1080
269
{
1081
269
    return fcntl(fd, F_SETFL, 0);
1082
269
}
1083
#endif /* _WIN32 */
1084
1085
/**
1086
 * @internal
1087
 * @brief Launches a socket connection
1088
 * If the socket connected callback has been defined and
1089
 * a poll object exists, this call will be non blocking.
1090
 * @param s    socket to connect.
1091
 * @param host hostname or ip address to connect to.
1092
 * @param port port number to connect to.
1093
 * @param bind_addr address to bind to, or NULL for default.
1094
 * @returns `SSH_OK` socket is being connected.
1095
 * @returns `SSH_ERROR` error while connecting to remote host.
1096
 */
1097
int ssh_socket_connect(ssh_socket s,
1098
                       const char *host,
1099
                       uint16_t port,
1100
                       const char *bind_addr)
1101
0
{
1102
0
    socket_t fd;
1103
1104
0
    if (s->state != SSH_SOCKET_NONE) {
1105
0
        ssh_set_error(s->session, SSH_FATAL,
1106
0
                      "ssh_socket_connect called on socket not unconnected");
1107
0
        return SSH_ERROR;
1108
0
    }
1109
0
    fd = ssh_connect_host_nonblocking(s->session, host, bind_addr, port);
1110
0
    SSH_LOG(SSH_LOG_DEBUG, "Nonblocking connection socket: %d", fd);
1111
0
    if (fd == SSH_INVALID_SOCKET) {
1112
0
        return SSH_ERROR;
1113
0
    }
1114
0
    return ssh_socket_set_fd(s, fd);
1115
0
}
1116
1117
#ifdef WITH_EXEC
1118
/**
1119
 * @internal
1120
 * @brief executes a command and redirect input and outputs
1121
 * @param command command to execute
1122
 * @param in input file descriptor
1123
 * @param out output file descriptor
1124
 */
1125
void
1126
ssh_execute_command(const char *command, socket_t in, socket_t out)
1127
{
1128
    const char *shell = NULL;
1129
    const char *args[] = {NULL/*shell*/, "-c", command, NULL};
1130
    int devnull;
1131
    int rc;
1132
1133
    /* Prepare /dev/null socket for the stderr redirection */
1134
    devnull = open("/dev/null", O_WRONLY);
1135
    if (devnull == -1) {
1136
        SSH_LOG(SSH_LOG_TRACE, "Failed to open /dev/null");
1137
        exit(1);
1138
    }
1139
1140
    /*
1141
     * By default, use the current users shell. This could fail with some
1142
     * shells like zsh or dash ...
1143
     */
1144
    shell = getenv("SHELL");
1145
    if (shell == NULL || shell[0] == '\0') {
1146
        /* Fall back to the /bin/sh only if the bash is not available. But there are
1147
         * issues with dash or whatever people tend to link to /bin/sh */
1148
        rc = access("/bin/bash", 0);
1149
        if (rc != 0) {
1150
            shell = "/bin/sh";
1151
        } else {
1152
            shell = "/bin/bash";
1153
        }
1154
    }
1155
    args[0] = shell;
1156
1157
    /* redirect in and out to stdin, stdout */
1158
    dup2(in, 0);
1159
    dup2(out, 1);
1160
    /* Ignore anything on the stderr */
1161
    dup2(devnull, STDERR_FILENO);
1162
    close(in);
1163
    close(out);
1164
    rc = execv(args[0], (char * const *)args);
1165
    if (rc < 0) {
1166
        char err_msg[SSH_ERRNO_MSG_MAX] = {0};
1167
1168
        SSH_LOG(SSH_LOG_WARN, "Failed to execute command %s: %s",
1169
                command, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
1170
    }
1171
    exit(1);
1172
}
1173
1174
/**
1175
 * @internal
1176
 * @brief Open a socket on a ProxyCommand
1177
 * This call will always be nonblocking.
1178
 * @param s    socket to connect.
1179
 * @param command Command to execute.
1180
 * @returns `SSH_OK` socket is being connected.
1181
 * @returns `SSH_ERROR` error while executing the command.
1182
 */
1183
int
1184
ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
1185
{
1186
    socket_t pair[2];
1187
    ssh_poll_handle h = NULL;
1188
    int pid;
1189
    int rc;
1190
1191
    if (s->state != SSH_SOCKET_NONE) {
1192
        return SSH_ERROR;
1193
    }
1194
1195
    rc = socketpair(PF_UNIX, SOCK_STREAM, 0, pair);
1196
    if (rc < 0) {
1197
        return SSH_ERROR;
1198
    }
1199
1200
    SSH_LOG(SSH_LOG_DEBUG, "Executing proxycommand '%s'", command);
1201
    pid = fork();
1202
    if (pid == 0) {
1203
        ssh_execute_command(command, pair[0], pair[0]);
1204
        /* Does not return */
1205
    }
1206
    s->proxy_pid = pid;
1207
    close(pair[0]);
1208
    SSH_LOG(SSH_LOG_DEBUG,
1209
            "ProxyCommand connection pipe: [%d,%d]",
1210
            pair[0],
1211
            pair[1]);
1212
1213
    rc = ssh_socket_set_fd(s, pair[1]);
1214
    if (rc != SSH_OK) {
1215
        return rc;
1216
    }
1217
1218
    s->fd_is_socket = 0;
1219
    h = ssh_socket_get_poll_handle(s);
1220
    if (h == NULL) {
1221
        return SSH_ERROR;
1222
    }
1223
    ssh_socket_set_connected(s, h);
1224
    if (s->callbacks && s->callbacks->connected) {
1225
        s->callbacks->connected(SSH_SOCKET_CONNECTED_OK, 0, s->callbacks->userdata);
1226
    }
1227
1228
    return SSH_OK;
1229
}
1230
#endif /* WITH_EXEC */
1231
1232
#ifndef _WIN32
1233
#ifdef HAVE_PTHREAD
1234
static int
1235
verify_knownhost(ssh_session session)
1236
0
{
1237
0
    enum ssh_known_hosts_e state;
1238
1239
0
    state = ssh_session_is_known_server(session);
1240
1241
0
    switch (state) {
1242
0
    case SSH_KNOWN_HOSTS_OK:
1243
0
        break; /* ok */
1244
0
    default:
1245
0
        SSH_LOG(SSH_LOG_WARN, "Couldn't verify knownhost during proxyjump.");
1246
0
        return SSH_ERROR;
1247
0
    }
1248
1249
0
    return SSH_OK;
1250
0
}
1251
1252
static void *
1253
jump_thread_func(void *arg)
1254
0
{
1255
0
    struct jump_thread_data_struct *jump_thread_data = NULL;
1256
0
    struct ssh_jump_info_struct *jis = NULL;
1257
0
    struct ssh_jump_callbacks_struct *cb = NULL;
1258
0
    ssh_session jump_session = NULL;
1259
0
    ssh_channel caa = NULL;
1260
0
    int rc;
1261
0
    ssh_event event = NULL;
1262
0
    ssh_connector connector_in = NULL, connector_out = NULL;
1263
0
    ssh_session session = NULL;
1264
0
    int next_port;
1265
0
    char *next_hostname = NULL;
1266
1267
0
    jump_thread_data = (struct jump_thread_data_struct *)arg;
1268
0
    session = jump_thread_data->session;
1269
1270
0
    next_port = session->opts.port;
1271
0
    next_hostname = strdup(session->opts.host);
1272
1273
0
    jump_session = ssh_new();
1274
0
    if (jump_session == NULL) {
1275
0
        goto exit;
1276
0
    }
1277
1278
0
    jump_session->proxy_root = false;
1279
    /* Reset the global variable if it was previously 1 */
1280
0
    if (session->proxy_root) {
1281
0
        proxy_disconnect = 0;
1282
0
    }
1283
1284
0
    for (jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
1285
0
                                 session->opts.proxy_jumps);
1286
0
         jis != NULL;
1287
0
         jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
1288
0
                                 session->opts.proxy_jumps)) {
1289
0
        rc = ssh_list_append(jump_session->opts.proxy_jumps, jis);
1290
0
        if (rc != SSH_OK) {
1291
0
            ssh_set_error_oom(session);
1292
0
            goto exit;
1293
0
        }
1294
0
    }
1295
0
    for (jis =
1296
0
            ssh_list_pop_head(struct ssh_jump_info_struct *,
1297
0
                              session->opts.proxy_jumps_user_cb);
1298
0
         jis != NULL;
1299
0
         jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
1300
0
                                 session->opts.proxy_jumps_user_cb)) {
1301
0
        rc = ssh_list_append(jump_session->opts.proxy_jumps_user_cb, jis);
1302
0
        if (rc != SSH_OK) {
1303
0
            ssh_set_error_oom(session);
1304
0
            goto exit;
1305
0
        }
1306
0
    }
1307
1308
0
    ssh_options_set(jump_session,
1309
0
                    SSH_OPTIONS_LOG_VERBOSITY,
1310
0
                    &session->common.log_verbosity);
1311
1312
    /* Pop the information about the current jump */
1313
0
    jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
1314
0
                            jump_session->opts.proxy_jumps);
1315
0
    if (jis == NULL) {
1316
0
        SSH_LOG(SSH_LOG_WARN, "Inconsistent list of proxy jumps received");
1317
0
        goto exit;
1318
0
    }
1319
1320
0
    ssh_options_set(jump_session, SSH_OPTIONS_HOST, jis->hostname);
1321
0
    ssh_options_set(jump_session, SSH_OPTIONS_USER, jis->username);
1322
0
    ssh_options_set(jump_session, SSH_OPTIONS_PORT, &jis->port);
1323
1324
    /* Pop the callbacks for the current jump */
1325
0
    cb = ssh_list_pop_head(struct ssh_jump_callbacks_struct *,
1326
0
                           jump_session->opts.proxy_jumps_user_cb);
1327
1328
0
    if (cb != NULL) {
1329
0
        rc = cb->before_connection(jump_session, cb->userdata);
1330
0
        if (rc != SSH_OK) {
1331
0
            SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session));
1332
0
            goto exit;
1333
0
        }
1334
0
    }
1335
1336
    /* If there are more jumps then this will make a new thread and call the
1337
     * current function again, until there are no jumps. When there are no jumps
1338
     * it connects normally. */
1339
0
    rc = ssh_connect(jump_session);
1340
0
    if (rc != SSH_OK) {
1341
0
        SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session));
1342
0
        goto exit;
1343
0
    }
1344
1345
    /* Use the callback or default implementation for verifying knownhost */
1346
0
    if (cb != NULL && cb->verify_knownhost != NULL) {
1347
0
        rc = cb->verify_knownhost(jump_session, cb->userdata);
1348
0
    } else {
1349
0
        rc = verify_knownhost(jump_session);
1350
0
    }
1351
0
    if (rc != SSH_OK) {
1352
0
        goto exit;
1353
0
    }
1354
1355
    /* Use the callback or publickey method to authenticate */
1356
0
    if (cb != NULL && cb->authenticate != NULL) {
1357
0
        rc = cb->authenticate(jump_session, cb->userdata);
1358
0
    } else {
1359
0
        rc = ssh_userauth_publickey_auto(jump_session, NULL, NULL);
1360
0
    }
1361
0
    if (rc != SSH_OK) {
1362
0
        SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session));
1363
0
        goto exit;
1364
0
    }
1365
1366
0
    caa = ssh_channel_new(jump_session);
1367
0
    if (caa == NULL) {
1368
0
        goto exit;
1369
0
    }
1370
    /* The origin hostname and port are set to match OpenSSH implementation
1371
     * they are only used for logging on the server */
1372
0
    rc = ssh_channel_open_forward(caa,
1373
0
                                  next_hostname,
1374
0
                                  next_port,
1375
0
                                  "127.0.0.1",
1376
0
                                  65535);
1377
0
    if (rc != SSH_OK) {
1378
0
        SSH_LOG(SSH_LOG_WARN,
1379
0
                "Error opening port forwarding channel: %s",
1380
0
                ssh_get_error(jump_session));
1381
0
        goto exit;
1382
0
    }
1383
1384
0
    event = ssh_event_new();
1385
0
    if (event == NULL) {
1386
0
        goto exit;
1387
0
    }
1388
1389
0
    connector_in = ssh_connector_new(jump_session);
1390
0
    if (connector_in == NULL) {
1391
0
        goto exit;
1392
0
    }
1393
0
    ssh_connector_set_out_channel(connector_in, caa, SSH_CONNECTOR_STDINOUT);
1394
0
    ssh_connector_set_in_fd(connector_in, jump_thread_data->fd);
1395
0
    ssh_event_add_connector(event, connector_in);
1396
1397
0
    connector_out = ssh_connector_new(jump_session);
1398
0
    if (connector_out == NULL) {
1399
0
        goto exit;
1400
0
    }
1401
0
    ssh_connector_set_out_fd(connector_out, jump_thread_data->fd);
1402
0
    ssh_connector_set_in_channel(connector_out, caa, SSH_CONNECTOR_STDINOUT);
1403
0
    ssh_event_add_connector(event, connector_out);
1404
1405
0
    while (ssh_channel_is_open(caa)) {
1406
0
        if (proxy_disconnect == 1) {
1407
0
            break;
1408
0
        }
1409
0
        rc = ssh_event_dopoll(event, 60000);
1410
0
        if (rc == SSH_ERROR) {
1411
0
            SSH_LOG(SSH_LOG_WARN,
1412
0
                    "Error in ssh_event_dopoll() during proxy jump");
1413
0
            break;
1414
0
        }
1415
0
    }
1416
1417
0
exit:
1418
0
    if (connector_in != NULL) {
1419
0
        ssh_event_remove_connector(event, connector_in);
1420
0
        ssh_connector_free(connector_in);
1421
0
    }
1422
0
    if (connector_out != NULL) {
1423
0
        ssh_event_remove_connector(event, connector_out);
1424
0
        ssh_connector_free(connector_out);
1425
0
    }
1426
0
    SAFE_FREE(next_hostname);
1427
0
    if (jis != NULL) {
1428
0
        SAFE_FREE(jis->hostname);
1429
0
        SAFE_FREE(jis->username);
1430
0
    }
1431
0
    SAFE_FREE(jis);
1432
1433
0
    ssh_disconnect(jump_session);
1434
0
    ssh_event_free(event);
1435
0
    ssh_free(jump_session);
1436
1437
0
    shutdown(jump_thread_data->fd, SHUT_RDWR);
1438
0
    close(jump_thread_data->fd);
1439
0
    SAFE_FREE(jump_thread_data);
1440
1441
0
    pthread_exit(NULL);
1442
0
}
1443
1444
int
1445
ssh_socket_connect_proxyjump(ssh_socket s)
1446
0
{
1447
0
    ssh_poll_handle h = NULL;
1448
0
    int rc;
1449
0
    pthread_t jump_thread;
1450
0
    struct jump_thread_data_struct *jump_thread_data = NULL;
1451
0
    socket_t pair[2];
1452
1453
0
    if (s->state != SSH_SOCKET_NONE) {
1454
0
        ssh_set_error(
1455
0
            s->session,
1456
0
            SSH_FATAL,
1457
0
            "ssh_socket_connect_proxyjump called on socket not unconnected");
1458
0
        return SSH_ERROR;
1459
0
    }
1460
1461
0
    jump_thread_data = calloc(1, sizeof(struct jump_thread_data_struct));
1462
0
    if (jump_thread_data == NULL) {
1463
0
        ssh_set_error_oom(s->session);
1464
0
        return SSH_ERROR;
1465
0
    }
1466
1467
0
    rc = socketpair(PF_UNIX, SOCK_STREAM, 0, pair);
1468
0
    if (rc == -1) {
1469
0
        char err_msg[SSH_ERRNO_MSG_MAX] = {0};
1470
1471
0
        ssh_set_error(s->session,
1472
0
                      SSH_FATAL,
1473
0
                      "Creating socket pair failed: %s",
1474
0
                      ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
1475
0
        SAFE_FREE(jump_thread_data);
1476
0
        return SSH_ERROR;
1477
0
    }
1478
1479
0
    jump_thread_data->session = s->session;
1480
0
    jump_thread_data->fd = pair[0];
1481
1482
0
    rc = pthread_create(&jump_thread, NULL, jump_thread_func, jump_thread_data);
1483
0
    if (rc != 0) {
1484
0
        char err_msg[SSH_ERRNO_MSG_MAX] = {0};
1485
1486
0
        ssh_set_error(s->session,
1487
0
                      SSH_FATAL,
1488
0
                      "Creating new thread failed: %s",
1489
0
                      ssh_strerror(rc, err_msg, SSH_ERRNO_MSG_MAX));
1490
0
        SAFE_FREE(jump_thread_data);
1491
0
        return SSH_ERROR;
1492
0
    }
1493
0
    rc = pthread_detach(jump_thread);
1494
0
    if (rc != 0) {
1495
0
        char err_msg[SSH_ERRNO_MSG_MAX] = {0};
1496
1497
0
        ssh_set_error(s->session,
1498
0
                      SSH_FATAL,
1499
0
                      "Failed to detach thread: %s",
1500
0
                      ssh_strerror(rc, err_msg, SSH_ERRNO_MSG_MAX));
1501
0
        SAFE_FREE(jump_thread_data);
1502
0
        return SSH_ERROR;
1503
0
    }
1504
1505
0
    SSH_LOG(SSH_LOG_DEBUG,
1506
0
            "ProxyJump connection pipe: [%d,%d]",
1507
0
            pair[0],
1508
0
            pair[1]);
1509
1510
0
    rc = ssh_socket_set_fd(s, pair[1]);
1511
0
    if (rc != SSH_OK) {
1512
0
        return rc;
1513
0
    }
1514
1515
0
    s->fd_is_socket = 1;
1516
0
    h = ssh_socket_get_poll_handle(s);
1517
0
    if (h == NULL) {
1518
0
        return SSH_ERROR;
1519
0
    }
1520
0
    ssh_socket_set_connected(s, h);
1521
0
    if (s->callbacks && s->callbacks->connected) {
1522
0
        s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,
1523
0
                                0,
1524
0
                                s->callbacks->userdata);
1525
0
    }
1526
1527
0
    return SSH_OK;
1528
0
}
1529
1530
#endif /* HAVE_PTHREAD */
1531
1532
#endif /* _WIN32 */
1533
/** @} */