/src/libssh2/src/session.c
Line | Count | Source |
1 | | /* Copyright (C) Sara Golemon <sarag@libssh2.org> |
2 | | * Copyright (C) Daniel Stenberg |
3 | | * Copyright (C) Simon Josefsson <simon@josefsson.org> |
4 | | * All rights reserved. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, |
7 | | * with or without modification, are permitted provided |
8 | | * that the following conditions are met: |
9 | | * |
10 | | * Redistributions of source code must retain the above |
11 | | * copyright notice, this list of conditions and the |
12 | | * following disclaimer. |
13 | | * |
14 | | * Redistributions in binary form must reproduce the above |
15 | | * copyright notice, this list of conditions and the following |
16 | | * disclaimer in the documentation and/or other materials |
17 | | * provided with the distribution. |
18 | | * |
19 | | * Neither the name of the copyright holder nor the names |
20 | | * of any other contributors may be used to endorse or |
21 | | * promote products derived from this software without |
22 | | * specific prior written permission. |
23 | | * |
24 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
25 | | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
26 | | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
27 | | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
28 | | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
29 | | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
30 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
31 | | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
32 | | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
33 | | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
34 | | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
35 | | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
36 | | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
37 | | * OF SUCH DAMAGE. |
38 | | * |
39 | | * SPDX-License-Identifier: BSD-3-Clause |
40 | | */ |
41 | | |
42 | | #include "libssh2_priv.h" |
43 | | |
44 | | #ifdef _WIN32 |
45 | | #include <ws2tcpip.h> /* for socklen_t */ |
46 | | #endif |
47 | | #ifdef HAVE_UNISTD_H |
48 | | #include <unistd.h> |
49 | | #endif |
50 | | #ifdef HAVE_ALLOCA_H |
51 | | #include <alloca.h> |
52 | | #endif |
53 | | |
54 | | #include <errno.h> |
55 | | #include <stdlib.h> |
56 | | #include <fcntl.h> |
57 | | |
58 | | #include "transport.h" |
59 | | #include "session.h" |
60 | | #include "channel.h" |
61 | | #include "mac.h" |
62 | | |
63 | | #ifdef _WIN32 |
64 | | #define libssh2_usec_t long |
65 | | #elif defined(__APPLE__) |
66 | | #define libssh2_usec_t suseconds_t |
67 | | #else |
68 | | #undef libssh2_usec_t |
69 | | #endif |
70 | | |
71 | | /* libssh2_default_alloc |
72 | | */ |
73 | | static |
74 | | LIBSSH2_ALLOC_FUNC(libssh2_default_alloc) |
75 | 289k | { |
76 | 289k | (void)abstract; |
77 | 289k | return malloc(count); |
78 | 289k | } |
79 | | |
80 | | /* libssh2_default_free |
81 | | */ |
82 | | static |
83 | | LIBSSH2_FREE_FUNC(libssh2_default_free) |
84 | 288k | { |
85 | 288k | (void)abstract; |
86 | 288k | free(ptr); |
87 | 288k | } |
88 | | |
89 | | /* libssh2_default_realloc |
90 | | */ |
91 | | static |
92 | | LIBSSH2_REALLOC_FUNC(libssh2_default_realloc) |
93 | 0 | { |
94 | 0 | (void)abstract; |
95 | 0 | return realloc(ptr, count); |
96 | 0 | } |
97 | | |
98 | | /* |
99 | | * banner_receive |
100 | | * |
101 | | * Wait for a hello from the remote host |
102 | | * Allocate a buffer and store the banner in session->remote.banner |
103 | | * Returns: 0 on success, LIBSSH2_ERROR_EAGAIN if read would block, negative |
104 | | * on failure |
105 | | */ |
106 | | static int |
107 | | banner_receive(LIBSSH2_SESSION * session) |
108 | 4.29k | { |
109 | 4.29k | ssize_t ret; |
110 | 4.29k | size_t banner_len; |
111 | | |
112 | 4.29k | if(session->banner_TxRx_state == libssh2_NB_state_idle) { |
113 | 4.29k | banner_len = 0; |
114 | | |
115 | 4.29k | session->banner_TxRx_state = libssh2_NB_state_created; |
116 | 4.29k | } |
117 | 0 | else { |
118 | 0 | banner_len = session->banner_TxRx_total_send; |
119 | 0 | } |
120 | | |
121 | 408k | while((banner_len < sizeof(session->banner_TxRx_banner)) && |
122 | 408k | ((banner_len == 0) |
123 | 404k | || (session->banner_TxRx_banner[banner_len - 1] != '\n'))) { |
124 | 404k | char c = '\0'; |
125 | | |
126 | | /* no incoming block yet! */ |
127 | 404k | session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND; |
128 | | |
129 | 404k | ret = LIBSSH2_RECV(session, &c, 1, |
130 | 404k | LIBSSH2_SOCKET_RECV_FLAGS(session)); |
131 | 404k | if(ret < 0) { |
132 | 0 | if(session->api_block_mode || (ret != -EAGAIN)) |
133 | | /* ignore EAGAIN when non-blocking */ |
134 | 0 | _libssh2_debug((session, LIBSSH2_TRACE_SOCKET, |
135 | 0 | "Error recving %d bytes: %ld", 1, (long)-ret)); |
136 | 0 | } |
137 | 404k | else |
138 | 404k | _libssh2_debug((session, LIBSSH2_TRACE_SOCKET, |
139 | 404k | "Recved %ld bytes banner", (long)ret)); |
140 | | |
141 | 404k | if(ret < 0) { |
142 | 0 | if(ret == -EAGAIN) { |
143 | 0 | session->socket_block_directions = |
144 | 0 | LIBSSH2_SESSION_BLOCK_INBOUND; |
145 | 0 | session->banner_TxRx_total_send = banner_len; |
146 | 0 | return LIBSSH2_ERROR_EAGAIN; |
147 | 0 | } |
148 | | |
149 | | /* Some kinda error */ |
150 | 0 | session->banner_TxRx_state = libssh2_NB_state_idle; |
151 | 0 | session->banner_TxRx_total_send = 0; |
152 | 0 | return LIBSSH2_ERROR_SOCKET_RECV; |
153 | 0 | } |
154 | | |
155 | 404k | if(ret == 0) { |
156 | 161 | session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; |
157 | 161 | return LIBSSH2_ERROR_SOCKET_DISCONNECT; |
158 | 161 | } |
159 | | |
160 | 404k | if((c == '\r' || c == '\n') && banner_len == 0) { |
161 | 577 | continue; |
162 | 577 | } |
163 | | |
164 | 403k | if(c == '\0') { |
165 | | /* NULLs are not allowed in SSH banners */ |
166 | 9 | session->banner_TxRx_state = libssh2_NB_state_idle; |
167 | 9 | session->banner_TxRx_total_send = 0; |
168 | 9 | return LIBSSH2_ERROR_BANNER_RECV; |
169 | 9 | } |
170 | | |
171 | 403k | session->banner_TxRx_banner[banner_len++] = c; |
172 | 403k | } |
173 | | |
174 | 8.74k | while(banner_len && |
175 | 8.74k | ((session->banner_TxRx_banner[banner_len - 1] == '\n') || |
176 | 4.65k | (session->banner_TxRx_banner[banner_len - 1] == '\r'))) { |
177 | 4.62k | banner_len--; |
178 | 4.62k | } |
179 | | |
180 | | /* From this point on, we are done here */ |
181 | 4.12k | session->banner_TxRx_state = libssh2_NB_state_idle; |
182 | 4.12k | session->banner_TxRx_total_send = 0; |
183 | | |
184 | 4.12k | if(!banner_len) |
185 | 0 | return LIBSSH2_ERROR_BANNER_RECV; |
186 | | |
187 | 4.12k | if(session->remote.banner) |
188 | 758 | LIBSSH2_FREE(session, session->remote.banner); |
189 | | |
190 | 4.12k | session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1); |
191 | 4.12k | if(!session->remote.banner) { |
192 | 0 | return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, |
193 | 0 | "Error allocating space for remote banner"); |
194 | 0 | } |
195 | 4.12k | memcpy(session->remote.banner, session->banner_TxRx_banner, banner_len); |
196 | 4.12k | session->remote.banner[banner_len] = '\0'; |
197 | 4.12k | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, "Received Banner: %s", |
198 | 4.12k | session->remote.banner)); |
199 | 4.12k | return LIBSSH2_ERROR_NONE; |
200 | 4.12k | } |
201 | | |
202 | | /* |
203 | | * banner_send |
204 | | * |
205 | | * Send the default banner, or the one set via libssh2_setopt_string |
206 | | * |
207 | | * Returns LIBSSH2_ERROR_EAGAIN if it would block - and if it does so, you |
208 | | * should call this function again as soon as it is likely that more data can |
209 | | * be sent, and this function should then be called with the same argument set |
210 | | * (same data pointer and same data_len) until zero or failure is returned. |
211 | | */ |
212 | | static int |
213 | | banner_send(LIBSSH2_SESSION * session) |
214 | 3.42k | { |
215 | 3.42k | const char *banner = LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF; |
216 | 3.42k | size_t banner_len = sizeof(LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF) - 1; |
217 | 3.42k | ssize_t ret; |
218 | | |
219 | 3.42k | if(session->banner_TxRx_state == libssh2_NB_state_idle) { |
220 | 3.42k | if(session->local.banner) { |
221 | | /* setopt_string will have given us our \r\n characters */ |
222 | 0 | banner_len = strlen((char *) session->local.banner); |
223 | 0 | banner = (char *) session->local.banner; |
224 | 0 | } |
225 | 3.42k | #ifdef LIBSSH2DEBUG |
226 | 3.42k | { |
227 | 3.42k | char banner_dup[256]; |
228 | | |
229 | | /* Hack and slash to avoid sending CRLF in debug output */ |
230 | 3.42k | if(banner_len < 256) { |
231 | 3.42k | memcpy(banner_dup, banner, banner_len - 2); |
232 | 3.42k | banner_dup[banner_len - 2] = '\0'; |
233 | 3.42k | } |
234 | 0 | else { |
235 | 0 | memcpy(banner_dup, banner, 255); |
236 | 0 | banner_dup[255] = '\0'; |
237 | 0 | } |
238 | | |
239 | 3.42k | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, |
240 | 3.42k | "Sending Banner: %s", banner_dup)); |
241 | 3.42k | } |
242 | 3.42k | #endif |
243 | | |
244 | 3.42k | session->banner_TxRx_state = libssh2_NB_state_created; |
245 | 3.42k | } |
246 | | |
247 | | /* no outgoing block yet! */ |
248 | 3.42k | session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_OUTBOUND; |
249 | | |
250 | 3.42k | ret = LIBSSH2_SEND(session, |
251 | 3.42k | banner + session->banner_TxRx_total_send, |
252 | 3.42k | banner_len - session->banner_TxRx_total_send, |
253 | 3.42k | LIBSSH2_SOCKET_SEND_FLAGS(session)); |
254 | 3.42k | if(ret < 0) |
255 | 0 | _libssh2_debug((session, LIBSSH2_TRACE_SOCKET, |
256 | 3.42k | "Error sending %ld bytes: %ld", |
257 | 3.42k | (long)(banner_len - session->banner_TxRx_total_send), |
258 | 3.42k | (long)-ret)); |
259 | 3.42k | else |
260 | 3.42k | _libssh2_debug((session, LIBSSH2_TRACE_SOCKET, |
261 | 3.42k | "Sent %ld/%ld bytes at %p+%ld", (long)ret, |
262 | 3.42k | (long)(banner_len - session->banner_TxRx_total_send), |
263 | 3.42k | (const void *)banner, |
264 | 3.42k | (long)session->banner_TxRx_total_send)); |
265 | | |
266 | 3.42k | if(ret != (ssize_t)(banner_len - session->banner_TxRx_total_send)) { |
267 | 0 | if(ret >= 0 || ret == -EAGAIN) { |
268 | | /* the whole packet could not be sent, save the what was */ |
269 | 0 | session->socket_block_directions = |
270 | 0 | LIBSSH2_SESSION_BLOCK_OUTBOUND; |
271 | 0 | if(ret > 0) |
272 | 0 | session->banner_TxRx_total_send += ret; |
273 | 0 | return LIBSSH2_ERROR_EAGAIN; |
274 | 0 | } |
275 | 0 | session->banner_TxRx_state = libssh2_NB_state_idle; |
276 | 0 | session->banner_TxRx_total_send = 0; |
277 | 0 | return LIBSSH2_ERROR_SOCKET_RECV; |
278 | 0 | } |
279 | | |
280 | | /* Set the state back to idle */ |
281 | 3.42k | session->banner_TxRx_state = libssh2_NB_state_idle; |
282 | 3.42k | session->banner_TxRx_total_send = 0; |
283 | | |
284 | 3.42k | return 0; |
285 | 3.42k | } |
286 | | |
287 | | /* |
288 | | * session_nonblock() sets the given socket to either blocking or |
289 | | * non-blocking mode based on the 'nonblock' boolean argument. This function |
290 | | * is copied from the libcurl sources with permission. |
291 | | */ |
292 | | static int |
293 | | session_nonblock(libssh2_socket_t sockfd, /* operate on this */ |
294 | | int nonblock /* TRUE or FALSE */ ) |
295 | 6.84k | { |
296 | 6.84k | #ifdef HAVE_O_NONBLOCK |
297 | | /* most recent unix versions */ |
298 | 6.84k | int flags; |
299 | | |
300 | 6.84k | flags = fcntl(sockfd, F_GETFL, 0); |
301 | 6.84k | if(nonblock) |
302 | 3.42k | return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); |
303 | 3.42k | else |
304 | 3.42k | return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); |
305 | | #elif defined(HAVE_FIONBIO) |
306 | | /* older unix versions and VMS */ |
307 | | int flags; |
308 | | |
309 | | flags = nonblock; |
310 | | return ioctl(sockfd, FIONBIO, &flags); |
311 | | #elif defined(HAVE_IOCTLSOCKET_CASE) |
312 | | /* presumably for Amiga */ |
313 | | return IoctlSocket(sockfd, FIONBIO, (long) nonblock); |
314 | | #elif defined(HAVE_SO_NONBLOCK) |
315 | | /* BeOS */ |
316 | | long b = nonblock ? 1 : 0; |
317 | | return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); |
318 | | #elif defined(_WIN32) |
319 | | unsigned long flags; |
320 | | |
321 | | flags = nonblock; |
322 | | return ioctlsocket(sockfd, FIONBIO, &flags); |
323 | | #else |
324 | | (void)sockfd; |
325 | | (void)nonblock; |
326 | | return 0; /* returns success */ |
327 | | #endif |
328 | 6.84k | } |
329 | | |
330 | | /* |
331 | | * get_socket_nonblocking |
332 | | * |
333 | | * gets the given blocking or non-blocking state of the socket. |
334 | | */ |
335 | | static int |
336 | | get_socket_nonblocking(libssh2_socket_t sockfd) |
337 | 3.42k | { /* operate on this */ |
338 | 3.42k | #ifdef HAVE_O_NONBLOCK |
339 | | /* most recent unix versions */ |
340 | 3.42k | int flags = fcntl(sockfd, F_GETFL, 0); |
341 | | |
342 | 3.42k | if(flags == -1) { |
343 | | /* Assume blocking on error */ |
344 | 0 | return 1; |
345 | 0 | } |
346 | 3.42k | return (flags & O_NONBLOCK); |
347 | | #elif defined(HAVE_SO_NONBLOCK) |
348 | | /* BeOS */ |
349 | | long b; |
350 | | if(getsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b))) { |
351 | | /* Assume blocking on error */ |
352 | | return 1; |
353 | | } |
354 | | return (int) b; |
355 | | #elif defined(SO_STATE) && defined(__VMS) |
356 | | /* VMS TCP/IP Services */ |
357 | | |
358 | | size_t sockstat = 0; |
359 | | int callstat = 0; |
360 | | size_t size = sizeof(int); |
361 | | |
362 | | callstat = getsockopt(sockfd, SOL_SOCKET, SO_STATE, |
363 | | (char *)&sockstat, &size); |
364 | | if(callstat == -1) { |
365 | | return 0; |
366 | | } |
367 | | if((sockstat&SS_NBIO) != 0) { |
368 | | return 1; |
369 | | } |
370 | | return 0; |
371 | | #elif defined(_WIN32) |
372 | | unsigned int option_value; |
373 | | socklen_t option_len = sizeof(option_value); |
374 | | |
375 | | if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, |
376 | | (void *) &option_value, &option_len)) { |
377 | | /* Assume blocking on error */ |
378 | | return 1; |
379 | | } |
380 | | return (int) option_value; |
381 | | #else |
382 | | (void)sockfd; |
383 | | return 1; /* returns blocking */ |
384 | | #endif |
385 | 3.42k | } |
386 | | |
387 | | /* libssh2_session_banner_set |
388 | | * Set the local banner to use in the server handshake. |
389 | | */ |
390 | | LIBSSH2_API int |
391 | | libssh2_session_banner_set(LIBSSH2_SESSION * session, const char *banner) |
392 | 0 | { |
393 | 0 | size_t banner_len = banner ? strlen(banner) : 0; |
394 | |
|
395 | 0 | if(session->local.banner) { |
396 | 0 | LIBSSH2_FREE(session, session->local.banner); |
397 | 0 | session->local.banner = NULL; |
398 | 0 | } |
399 | |
|
400 | 0 | if(!banner_len) |
401 | 0 | return 0; |
402 | | |
403 | 0 | session->local.banner = LIBSSH2_ALLOC(session, banner_len + 3); |
404 | 0 | if(!session->local.banner) { |
405 | 0 | return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, |
406 | 0 | "Unable to allocate memory for local banner"); |
407 | 0 | } |
408 | | |
409 | 0 | memcpy(session->local.banner, banner, banner_len); |
410 | | |
411 | | /* first zero terminate like this so that the debug output is nice */ |
412 | 0 | session->local.banner[banner_len] = '\0'; |
413 | 0 | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, "Setting local Banner: %s", |
414 | 0 | session->local.banner)); |
415 | 0 | session->local.banner[banner_len++] = '\r'; |
416 | 0 | session->local.banner[banner_len++] = '\n'; |
417 | 0 | session->local.banner[banner_len] = '\0'; |
418 | |
|
419 | 0 | return 0; |
420 | 0 | } |
421 | | |
422 | | #ifndef LIBSSH2_NO_DEPRECATED |
423 | | /* libssh2_banner_set |
424 | | * Set the local banner. DEPRECATED VERSION |
425 | | */ |
426 | | LIBSSH2_API int |
427 | | libssh2_banner_set(LIBSSH2_SESSION * session, const char *banner) |
428 | 0 | { |
429 | 0 | return libssh2_session_banner_set(session, banner); |
430 | 0 | } |
431 | | #endif |
432 | | |
433 | | /* |
434 | | * libssh2_session_init_ex |
435 | | * |
436 | | * Allocate and initialize a libssh2 session structure. Allows for malloc |
437 | | * callbacks in case the calling program has its own memory manager It's |
438 | | * allowable (but unadvisable) to define some but not all of the malloc |
439 | | * callbacks An additional pointer value may be optionally passed to be sent |
440 | | * to the callbacks (so they know who's asking) |
441 | | */ |
442 | | LIBSSH2_API LIBSSH2_SESSION * |
443 | | libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)), |
444 | | LIBSSH2_FREE_FUNC((*my_free)), |
445 | | LIBSSH2_REALLOC_FUNC((*my_realloc)), void *abstract) |
446 | 3.42k | { |
447 | 3.42k | LIBSSH2_ALLOC_FUNC((*local_alloc)) = libssh2_default_alloc; |
448 | 3.42k | LIBSSH2_FREE_FUNC((*local_free)) = libssh2_default_free; |
449 | 3.42k | LIBSSH2_REALLOC_FUNC((*local_realloc)) = libssh2_default_realloc; |
450 | 3.42k | LIBSSH2_SESSION *session; |
451 | | |
452 | 3.42k | if(my_alloc) { |
453 | 0 | local_alloc = my_alloc; |
454 | 0 | } |
455 | 3.42k | if(my_free) { |
456 | 0 | local_free = my_free; |
457 | 0 | } |
458 | 3.42k | if(my_realloc) { |
459 | 0 | local_realloc = my_realloc; |
460 | 0 | } |
461 | | |
462 | 3.42k | session = local_alloc(sizeof(LIBSSH2_SESSION), &abstract); |
463 | 3.42k | if(session) { |
464 | 3.42k | memset(session, 0, sizeof(LIBSSH2_SESSION)); |
465 | 3.42k | session->alloc = local_alloc; |
466 | 3.42k | session->free = local_free; |
467 | 3.42k | session->realloc = local_realloc; |
468 | 3.42k | session->send = _libssh2_send; |
469 | 3.42k | session->recv = _libssh2_recv; |
470 | 3.42k | session->abstract = abstract; |
471 | 3.42k | session->api_timeout = 0; /* timeout-free API by default */ |
472 | 3.42k | session->api_block_mode = 1; /* blocking API by default */ |
473 | 3.42k | session->state = LIBSSH2_STATE_INITIAL_KEX; |
474 | 3.42k | session->fullpacket_required_type = 0; |
475 | 3.42k | session->packet_read_timeout = LIBSSH2_DEFAULT_READ_TIMEOUT; |
476 | 3.42k | session->flag.quote_paths = 1; /* default behavior is to quote paths |
477 | | for the scp subsystem */ |
478 | 3.42k | session->kex = NULL; |
479 | 3.42k | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, |
480 | 3.42k | "New session resource allocated")); |
481 | 3.42k | _libssh2_init_if_needed(); |
482 | 3.42k | } |
483 | 3.42k | return session; |
484 | 3.42k | } |
485 | | |
486 | | /* |
487 | | * libssh2_session_callback_set2 |
488 | | * |
489 | | * Set (or reset) a callback function |
490 | | * Returns the prior address |
491 | | */ |
492 | | #ifdef __clang__ |
493 | | #pragma clang diagnostic push |
494 | | #pragma clang diagnostic ignored "-Wcast-function-type" |
495 | | #endif |
496 | | LIBSSH2_API libssh2_cb_generic * |
497 | | libssh2_session_callback_set2(LIBSSH2_SESSION *session, int cbtype, |
498 | | libssh2_cb_generic *callback) |
499 | 0 | { |
500 | 0 | libssh2_cb_generic *oldcb; |
501 | |
|
502 | 0 | switch(cbtype) { |
503 | 0 | case LIBSSH2_CALLBACK_IGNORE: |
504 | 0 | oldcb = (libssh2_cb_generic *)session->ssh_msg_ignore; |
505 | 0 | session->ssh_msg_ignore = (LIBSSH2_IGNORE_FUNC((*)))callback; |
506 | 0 | return oldcb; |
507 | | |
508 | 0 | case LIBSSH2_CALLBACK_DEBUG: |
509 | 0 | oldcb = (libssh2_cb_generic *)session->ssh_msg_debug; |
510 | 0 | session->ssh_msg_debug = (LIBSSH2_DEBUG_FUNC((*)))callback; |
511 | 0 | return oldcb; |
512 | | |
513 | 0 | case LIBSSH2_CALLBACK_DISCONNECT: |
514 | 0 | oldcb = (libssh2_cb_generic *)session->ssh_msg_disconnect; |
515 | 0 | session->ssh_msg_disconnect = (LIBSSH2_DISCONNECT_FUNC((*)))callback; |
516 | 0 | return oldcb; |
517 | | |
518 | 0 | case LIBSSH2_CALLBACK_MACERROR: |
519 | 0 | oldcb = (libssh2_cb_generic *)session->macerror; |
520 | 0 | session->macerror = (LIBSSH2_MACERROR_FUNC((*)))callback; |
521 | 0 | return oldcb; |
522 | | |
523 | 0 | case LIBSSH2_CALLBACK_X11: |
524 | 0 | oldcb = (libssh2_cb_generic *)session->x11; |
525 | 0 | session->x11 = (LIBSSH2_X11_OPEN_FUNC((*)))callback; |
526 | 0 | return oldcb; |
527 | | |
528 | 0 | case LIBSSH2_CALLBACK_SEND: |
529 | 0 | oldcb = (libssh2_cb_generic *)session->send; |
530 | 0 | session->send = (LIBSSH2_SEND_FUNC((*)))callback; |
531 | 0 | return oldcb; |
532 | | |
533 | 0 | case LIBSSH2_CALLBACK_RECV: |
534 | 0 | oldcb = (libssh2_cb_generic *)session->recv; |
535 | 0 | session->recv = (LIBSSH2_RECV_FUNC((*)))callback; |
536 | 0 | return oldcb; |
537 | | |
538 | 0 | case LIBSSH2_CALLBACK_AUTHAGENT: |
539 | 0 | oldcb = (libssh2_cb_generic *)session->authagent; |
540 | 0 | session->authagent = (LIBSSH2_AUTHAGENT_FUNC((*)))callback; |
541 | 0 | return oldcb; |
542 | | |
543 | 0 | case LIBSSH2_CALLBACK_AUTHAGENT_IDENTITIES: |
544 | 0 | oldcb = (libssh2_cb_generic *)session->addLocalIdentities; |
545 | 0 | session->addLocalIdentities = |
546 | 0 | (LIBSSH2_ADD_IDENTITIES_FUNC((*)))callback; |
547 | 0 | return oldcb; |
548 | | |
549 | 0 | case LIBSSH2_CALLBACK_AUTHAGENT_SIGN: |
550 | 0 | oldcb = (libssh2_cb_generic *)session->agentSignCallback; |
551 | 0 | session->agentSignCallback = |
552 | 0 | (LIBSSH2_AUTHAGENT_SIGN_FUNC((*)))callback; |
553 | 0 | return oldcb; |
554 | 0 | } |
555 | 0 | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, "Setting Callback %d", |
556 | 0 | cbtype)); |
557 | |
|
558 | 0 | return NULL; |
559 | 0 | } |
560 | | #ifdef __clang__ |
561 | | #pragma clang diagnostic pop |
562 | | #endif |
563 | | |
564 | | /* |
565 | | * libssh2_session_callback_set (DEPRECATED, DO NOT USE!) |
566 | | * |
567 | | * Set (or reset) a callback function |
568 | | * Returns the prior address |
569 | | * |
570 | | * ALERT: this function relies on that we can typecast function pointers |
571 | | * to void pointers, which isn't allowed in ISO C! |
572 | | */ |
573 | | #ifdef _MSC_VER |
574 | | #pragma warning(push) |
575 | | /* 'type cast': from data pointer to function pointer */ |
576 | | #pragma warning(disable:4054) |
577 | | /* 'type cast': from function pointer to data pointer */ |
578 | | #pragma warning(disable:4055) |
579 | | #else |
580 | | #pragma GCC diagnostic push |
581 | | #pragma GCC diagnostic ignored "-Wpedantic" |
582 | | #endif |
583 | | LIBSSH2_API void * |
584 | | libssh2_session_callback_set(LIBSSH2_SESSION * session, |
585 | | int cbtype, void *callback) |
586 | 0 | { |
587 | 0 | return (void *)libssh2_session_callback_set2(session, cbtype, |
588 | 0 | (libssh2_cb_generic *)callback); |
589 | 0 | } |
590 | | #ifdef _MSC_VER |
591 | | #pragma warning(pop) |
592 | | #else |
593 | | #pragma GCC diagnostic pop |
594 | | #endif |
595 | | |
596 | | /* |
597 | | * _libssh2_wait_socket |
598 | | * |
599 | | * Utility function that waits for action on the socket. Returns 0 when ready |
600 | | * to run again or error on timeout. |
601 | | */ |
602 | | int _libssh2_wait_socket(LIBSSH2_SESSION *session, time_t start_time) |
603 | 28 | { |
604 | 28 | int rc; |
605 | 28 | int seconds_to_next; |
606 | 28 | int dir; |
607 | 28 | int has_timeout; |
608 | 28 | long ms_to_next = 0; |
609 | 28 | long elapsed_ms; |
610 | | |
611 | | /* since libssh2 often sets EAGAIN internally before this function is |
612 | | called, we can decrease some amount of confusion in user programs by |
613 | | resetting the error code in this function to reduce the risk of EAGAIN |
614 | | being stored as error when a blocking function has returned */ |
615 | 28 | session->err_code = LIBSSH2_ERROR_NONE; |
616 | | |
617 | 28 | rc = libssh2_keepalive_send(session, &seconds_to_next); |
618 | 28 | if(rc) |
619 | 0 | return rc; |
620 | | |
621 | 28 | ms_to_next = seconds_to_next * 1000; |
622 | | |
623 | | /* figure out what to wait for */ |
624 | 28 | dir = libssh2_session_block_directions(session); |
625 | | |
626 | 28 | if(!dir) { |
627 | 0 | _libssh2_debug((session, LIBSSH2_TRACE_SOCKET, |
628 | 0 | "Nothing to wait for in wait_socket")); |
629 | | /* To avoid that we hang below just because there's nothing set to |
630 | | wait for, we timeout on 1 second to also avoid busy-looping |
631 | | during this condition */ |
632 | 0 | ms_to_next = 1000; |
633 | 0 | } |
634 | | |
635 | 28 | if(session->api_timeout > 0 && |
636 | 0 | (seconds_to_next == 0 || |
637 | 0 | ms_to_next > session->api_timeout)) { |
638 | 0 | time_t now = time(NULL); |
639 | 0 | elapsed_ms = (long)(1000*difftime(now, start_time)); |
640 | 0 | if(elapsed_ms > session->api_timeout) { |
641 | 0 | return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, |
642 | 0 | "API timeout expired"); |
643 | 0 | } |
644 | 0 | ms_to_next = (session->api_timeout - elapsed_ms); |
645 | 0 | has_timeout = 1; |
646 | 0 | } |
647 | 28 | else if(ms_to_next > 0) { |
648 | 0 | has_timeout = 1; |
649 | 0 | } |
650 | 28 | else |
651 | 28 | has_timeout = 0; |
652 | | |
653 | 28 | #ifdef HAVE_POLL |
654 | 28 | { |
655 | 28 | struct pollfd sockets[1]; |
656 | | |
657 | 28 | sockets[0].fd = session->socket_fd; |
658 | 28 | sockets[0].events = 0; |
659 | 28 | sockets[0].revents = 0; |
660 | | |
661 | 28 | if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) |
662 | 28 | sockets[0].events |= POLLIN; |
663 | | |
664 | 28 | if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) |
665 | 0 | sockets[0].events |= POLLOUT; |
666 | | |
667 | 28 | rc = poll(sockets, 1, has_timeout ? (int)ms_to_next : -1); |
668 | 28 | } |
669 | | #else |
670 | | { |
671 | | fd_set rfd; |
672 | | fd_set wfd; |
673 | | fd_set *writefd = NULL; |
674 | | fd_set *readfd = NULL; |
675 | | struct timeval tv; |
676 | | |
677 | | tv.tv_sec = ms_to_next / 1000; |
678 | | #ifdef libssh2_usec_t |
679 | | tv.tv_usec = (libssh2_usec_t)((ms_to_next - tv.tv_sec*1000) * 1000); |
680 | | #else |
681 | | tv.tv_usec = (ms_to_next - tv.tv_sec*1000) * 1000; |
682 | | #endif |
683 | | |
684 | | if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) { |
685 | | FD_ZERO(&rfd); |
686 | | #if defined(__GNUC__) || defined(__clang__) |
687 | | #pragma GCC diagnostic push |
688 | | #pragma GCC diagnostic ignored "-Wsign-conversion" |
689 | | #endif |
690 | | FD_SET(session->socket_fd, &rfd); |
691 | | #if defined(__GNUC__) || defined(__clang__) |
692 | | #pragma GCC diagnostic pop |
693 | | #endif |
694 | | readfd = &rfd; |
695 | | } |
696 | | |
697 | | if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) { |
698 | | FD_ZERO(&wfd); |
699 | | #if defined(__GNUC__) || defined(__clang__) |
700 | | #pragma GCC diagnostic push |
701 | | #pragma GCC diagnostic ignored "-Wsign-conversion" |
702 | | #endif |
703 | | FD_SET(session->socket_fd, &wfd); |
704 | | #if defined(__GNUC__) || defined(__clang__) |
705 | | #pragma GCC diagnostic pop |
706 | | #endif |
707 | | writefd = &wfd; |
708 | | } |
709 | | |
710 | | rc = select((int)(session->socket_fd + 1), readfd, writefd, NULL, |
711 | | has_timeout ? &tv : NULL); |
712 | | } |
713 | | #endif |
714 | 28 | if(rc == 0) { |
715 | 0 | return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, |
716 | 0 | "Timed out waiting on socket"); |
717 | 0 | } |
718 | 28 | if(rc < 0) { |
719 | 0 | int err; |
720 | | #ifdef _WIN32 |
721 | | err = _libssh2_wsa2errno(); |
722 | | #else |
723 | 0 | err = errno; |
724 | 0 | #endif |
725 | | /* Profiling tools that use SIGPROF can cause EINTR responses. |
726 | | poll() / select() do not set any descriptor states on EINTR, |
727 | | but some fds may be ready, so the caller should try again */ |
728 | 0 | if(err == EINTR) |
729 | 0 | return 0; |
730 | | |
731 | 0 | return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, |
732 | 0 | "Error waiting on socket"); |
733 | 0 | } |
734 | | |
735 | 28 | return 0; /* ready to try again */ |
736 | 28 | } |
737 | | |
738 | | static int |
739 | | session_startup(LIBSSH2_SESSION *session, libssh2_socket_t sock) |
740 | 3.45k | { |
741 | 3.45k | int rc; |
742 | | |
743 | 3.45k | if(!session) { |
744 | 0 | fprintf(stderr, "Session is NULL, error: %i\n", |
745 | 0 | LIBSSH2_ERROR_PROTO); |
746 | 0 | return LIBSSH2_ERROR_PROTO; |
747 | 0 | } |
748 | | |
749 | 3.45k | if(session->startup_state == libssh2_NB_state_idle) { |
750 | 3.42k | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, |
751 | 3.42k | "session_startup for socket %ld", (long)sock)); |
752 | 3.42k | if(LIBSSH2_INVALID_SOCKET == sock) { |
753 | | /* Did we forget something? */ |
754 | 0 | return _libssh2_error(session, LIBSSH2_ERROR_BAD_SOCKET, |
755 | 0 | "Bad socket provided"); |
756 | 0 | } |
757 | 3.42k | session->socket_fd = sock; |
758 | | |
759 | 3.42k | session->socket_prev_blockstate = |
760 | 3.42k | !get_socket_nonblocking(session->socket_fd); |
761 | | |
762 | 3.42k | if(session->socket_prev_blockstate) { |
763 | | /* If in blocking state change to non-blocking */ |
764 | 3.42k | rc = session_nonblock(session->socket_fd, 1); |
765 | 3.42k | if(rc) { |
766 | 0 | return _libssh2_error(session, rc, |
767 | 0 | "Failed changing socket's " |
768 | 0 | "blocking state to non-blocking"); |
769 | 0 | } |
770 | 3.42k | } |
771 | | |
772 | 3.42k | session->startup_state = libssh2_NB_state_created; |
773 | 3.42k | } |
774 | | |
775 | 3.45k | if(session->startup_state == libssh2_NB_state_created) { |
776 | 3.42k | rc = banner_send(session); |
777 | 3.42k | if(rc == LIBSSH2_ERROR_EAGAIN) |
778 | 0 | return rc; |
779 | 3.42k | else if(rc) { |
780 | 0 | return _libssh2_error(session, rc, |
781 | 0 | "Failed sending banner"); |
782 | 0 | } |
783 | 3.42k | session->startup_state = libssh2_NB_state_sent; |
784 | 3.42k | session->banner_TxRx_state = libssh2_NB_state_idle; |
785 | 3.42k | } |
786 | | |
787 | 3.45k | if(session->startup_state == libssh2_NB_state_sent) { |
788 | 4.29k | do { |
789 | 4.29k | rc = banner_receive(session); |
790 | 4.29k | if(rc == LIBSSH2_ERROR_EAGAIN) |
791 | 0 | return rc; |
792 | 4.29k | else if(rc) |
793 | 170 | return _libssh2_error(session, rc, |
794 | 170 | "Failed getting banner"); |
795 | 4.29k | } while(strncmp("SSH-", (const char *)session->remote.banner, 4)); |
796 | | |
797 | 3.25k | session->startup_state = libssh2_NB_state_sent1; |
798 | 3.25k | } |
799 | | |
800 | 3.28k | if(session->startup_state == libssh2_NB_state_sent1) { |
801 | 3.28k | rc = _libssh2_kex_exchange(session, 0, &session->startup_key_state); |
802 | 3.28k | if(rc == LIBSSH2_ERROR_EAGAIN) |
803 | 28 | return rc; |
804 | 3.25k | else if(rc) |
805 | 3.24k | return _libssh2_error(session, rc, |
806 | 3.24k | "Unable to exchange encryption keys"); |
807 | | |
808 | 5 | session->startup_state = libssh2_NB_state_sent2; |
809 | 5 | } |
810 | | |
811 | 5 | if(session->startup_state == libssh2_NB_state_sent2) { |
812 | 5 | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, |
813 | 5 | "Requesting userauth service")); |
814 | | |
815 | | /* Request the userauth service */ |
816 | 5 | session->startup_service[0] = SSH_MSG_SERVICE_REQUEST; |
817 | 5 | _libssh2_htonu32(session->startup_service + 1, |
818 | 5 | sizeof("ssh-userauth") - 1); |
819 | 5 | memcpy(session->startup_service + 5, "ssh-userauth", |
820 | 5 | sizeof("ssh-userauth") - 1); |
821 | | |
822 | 5 | session->startup_state = libssh2_NB_state_sent3; |
823 | 5 | } |
824 | | |
825 | 5 | if(session->startup_state == libssh2_NB_state_sent3) { |
826 | 5 | rc = _libssh2_transport_send(session, session->startup_service, |
827 | 5 | sizeof("ssh-userauth") + 5 - 1, |
828 | 5 | NULL, 0); |
829 | 5 | if(rc == LIBSSH2_ERROR_EAGAIN) |
830 | 0 | return rc; |
831 | 5 | else if(rc) { |
832 | 0 | return _libssh2_error(session, rc, |
833 | 0 | "Unable to ask for ssh-userauth service"); |
834 | 0 | } |
835 | | |
836 | 5 | session->startup_state = libssh2_NB_state_sent4; |
837 | 5 | } |
838 | | |
839 | 5 | if(session->startup_state == libssh2_NB_state_sent4) { |
840 | 5 | rc = _libssh2_packet_require(session, SSH_MSG_SERVICE_ACCEPT, |
841 | 5 | &session->startup_data, |
842 | 5 | &session->startup_data_len, 0, NULL, 0, |
843 | 5 | &session->startup_req_state); |
844 | 5 | if(rc) |
845 | 2 | return _libssh2_error(session, rc, |
846 | 2 | "Failed to get response to " |
847 | 2 | "ssh-userauth request"); |
848 | | |
849 | 3 | if(session->startup_data_len < 5) { |
850 | 1 | return _libssh2_error(session, LIBSSH2_ERROR_PROTO, |
851 | 1 | "Unexpected packet length"); |
852 | 1 | } |
853 | | |
854 | 2 | session->startup_service_length = |
855 | 2 | _libssh2_ntohu32(session->startup_data + 1); |
856 | | |
857 | | |
858 | 2 | if((session->startup_service_length != (sizeof("ssh-userauth") - 1)) |
859 | 0 | || strncmp("ssh-userauth", |
860 | 0 | (const char *) session->startup_data + 5, |
861 | 2 | session->startup_service_length)) { |
862 | 2 | LIBSSH2_FREE(session, session->startup_data); |
863 | 2 | session->startup_data = NULL; |
864 | 2 | return _libssh2_error(session, LIBSSH2_ERROR_PROTO, |
865 | 2 | "Invalid response received from server"); |
866 | 2 | } |
867 | 0 | LIBSSH2_FREE(session, session->startup_data); |
868 | 0 | session->startup_data = NULL; |
869 | |
|
870 | 0 | session->startup_state = libssh2_NB_state_idle; |
871 | |
|
872 | 0 | return 0; |
873 | 2 | } |
874 | | |
875 | | /* just for safety return some error */ |
876 | 0 | return LIBSSH2_ERROR_INVAL; |
877 | 5 | } |
878 | | |
879 | | /* |
880 | | * libssh2_session_handshake |
881 | | * |
882 | | * session: LIBSSH2_SESSION struct allocated and owned by the calling program |
883 | | * sock: *must* be populated with an opened and connected socket. |
884 | | * |
885 | | * Returns: 0 on success, or non-zero on failure |
886 | | */ |
887 | | LIBSSH2_API int |
888 | | libssh2_session_handshake(LIBSSH2_SESSION *session, libssh2_socket_t sock) |
889 | 3.42k | { |
890 | 3.42k | int rc; |
891 | | |
892 | 3.42k | BLOCK_ADJUST(rc, session, session_startup(session, sock)); |
893 | | |
894 | 3.42k | return rc; |
895 | 3.42k | } |
896 | | |
897 | | #ifndef LIBSSH2_NO_DEPRECATED |
898 | | /* |
899 | | * libssh2_session_startup |
900 | | * |
901 | | * DEPRECATED. Use libssh2_session_handshake() instead! This function is not |
902 | | * portable enough. |
903 | | * |
904 | | * session: LIBSSH2_SESSION struct allocated and owned by the calling program |
905 | | * sock: *must* be populated with an opened and connected socket. |
906 | | * |
907 | | * Returns: 0 on success, or non-zero on failure |
908 | | */ |
909 | | LIBSSH2_API int |
910 | | libssh2_session_startup(LIBSSH2_SESSION *session, int sock) |
911 | 0 | { |
912 | 0 | return libssh2_session_handshake(session, (libssh2_socket_t) sock); |
913 | 0 | } |
914 | | #endif |
915 | | |
916 | | /* |
917 | | * session_free |
918 | | * |
919 | | * Frees the memory allocated to the session |
920 | | * Also closes and frees any channels attached to this session |
921 | | */ |
922 | | static int |
923 | | session_free(LIBSSH2_SESSION *session) |
924 | 3.42k | { |
925 | 3.42k | int rc; |
926 | 3.42k | LIBSSH2_PACKET *pkg; |
927 | 3.42k | LIBSSH2_CHANNEL *ch; |
928 | 3.42k | LIBSSH2_LISTENER *l; |
929 | 3.42k | int packets_left = 0; |
930 | | |
931 | 3.42k | if(session->free_state == libssh2_NB_state_idle) { |
932 | 3.42k | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, |
933 | 3.42k | "Freeing session resource %p", |
934 | 3.42k | (void *)session->remote.banner)); |
935 | | |
936 | 3.42k | session->free_state = libssh2_NB_state_created; |
937 | 3.42k | } |
938 | | |
939 | 3.42k | if(session->free_state == libssh2_NB_state_created) { |
940 | | /* !checksrc! disable EQUALSNULL 1 */ |
941 | 3.42k | while((ch = _libssh2_list_first(&session->channels)) != NULL) { |
942 | 0 | rc = _libssh2_channel_free(ch); |
943 | 0 | if(rc == LIBSSH2_ERROR_EAGAIN) |
944 | 0 | return rc; |
945 | 0 | } |
946 | | |
947 | 3.42k | session->free_state = libssh2_NB_state_sent; |
948 | 3.42k | } |
949 | | |
950 | 3.42k | if(session->free_state == libssh2_NB_state_sent) { |
951 | | /* !checksrc! disable EQUALSNULL 1 */ |
952 | 3.42k | while((l = _libssh2_list_first(&session->listeners)) != NULL) { |
953 | 0 | rc = _libssh2_channel_forward_cancel(l); |
954 | 0 | if(rc == LIBSSH2_ERROR_EAGAIN) |
955 | 0 | return rc; |
956 | 0 | } |
957 | | |
958 | 3.42k | session->free_state = libssh2_NB_state_sent1; |
959 | 3.42k | } |
960 | | |
961 | 3.42k | if(session->kex && session->kex->cleanup) { |
962 | 1.27k | session->kex->cleanup(session, |
963 | 1.27k | &session->startup_key_state.key_state_low); |
964 | 1.27k | } |
965 | | |
966 | 3.42k | if(session->state & LIBSSH2_STATE_NEWKEYS) { |
967 | | /* hostkey */ |
968 | 0 | if(session->hostkey && session->hostkey->dtor) { |
969 | 0 | session->hostkey->dtor(session, &session->server_hostkey_abstract); |
970 | 0 | } |
971 | | |
972 | | /* Client to Server */ |
973 | | /* crypt */ |
974 | 0 | if(session->local.crypt && session->local.crypt->dtor) { |
975 | 0 | session->local.crypt->dtor(session, |
976 | 0 | &session->local.crypt_abstract); |
977 | 0 | } |
978 | | /* comp */ |
979 | 0 | if(session->local.comp && session->local.comp->dtor) { |
980 | 0 | session->local.comp->dtor(session, 1, |
981 | 0 | &session->local.comp_abstract); |
982 | 0 | } |
983 | | /* mac */ |
984 | 0 | if(session->local.mac && session->local.mac->dtor) { |
985 | 0 | session->local.mac->dtor(session, &session->local.mac_abstract); |
986 | 0 | } |
987 | | |
988 | | /* Server to Client */ |
989 | | /* crypt */ |
990 | 0 | if(session->remote.crypt && session->remote.crypt->dtor) { |
991 | 0 | session->remote.crypt->dtor(session, |
992 | 0 | &session->remote.crypt_abstract); |
993 | 0 | } |
994 | | /* comp */ |
995 | 0 | if(session->remote.comp && session->remote.comp->dtor) { |
996 | 0 | session->remote.comp->dtor(session, 0, |
997 | 0 | &session->remote.comp_abstract); |
998 | 0 | } |
999 | | /* mac */ |
1000 | 0 | if(session->remote.mac && session->remote.mac->dtor) { |
1001 | 0 | session->remote.mac->dtor(session, &session->remote.mac_abstract); |
1002 | 0 | } |
1003 | | |
1004 | | /* session_id */ |
1005 | 0 | if(session->session_id) { |
1006 | 0 | LIBSSH2_FREE(session, session->session_id); |
1007 | 0 | } |
1008 | 0 | } |
1009 | | |
1010 | | /* Free banner(s) */ |
1011 | 3.42k | if(session->remote.banner) { |
1012 | 3.36k | LIBSSH2_FREE(session, session->remote.banner); |
1013 | 3.36k | } |
1014 | 3.42k | if(session->local.banner) { |
1015 | 0 | LIBSSH2_FREE(session, session->local.banner); |
1016 | 0 | } |
1017 | | |
1018 | | /* Free preference(s) */ |
1019 | 3.42k | if(session->kex_prefs) { |
1020 | 0 | LIBSSH2_FREE(session, session->kex_prefs); |
1021 | 0 | } |
1022 | 3.42k | if(session->hostkey_prefs) { |
1023 | 0 | LIBSSH2_FREE(session, session->hostkey_prefs); |
1024 | 0 | } |
1025 | | |
1026 | 3.42k | if(session->local.kexinit) { |
1027 | 0 | LIBSSH2_FREE(session, session->local.kexinit); |
1028 | 0 | } |
1029 | 3.42k | if(session->local.crypt_prefs) { |
1030 | 0 | LIBSSH2_FREE(session, session->local.crypt_prefs); |
1031 | 0 | } |
1032 | 3.42k | if(session->local.mac_prefs) { |
1033 | 0 | LIBSSH2_FREE(session, session->local.mac_prefs); |
1034 | 0 | } |
1035 | 3.42k | if(session->local.comp_prefs) { |
1036 | 0 | LIBSSH2_FREE(session, session->local.comp_prefs); |
1037 | 0 | } |
1038 | 3.42k | if(session->local.lang_prefs) { |
1039 | 0 | LIBSSH2_FREE(session, session->local.lang_prefs); |
1040 | 0 | } |
1041 | | |
1042 | 3.42k | if(session->remote.kexinit) { |
1043 | 0 | LIBSSH2_FREE(session, session->remote.kexinit); |
1044 | 0 | } |
1045 | 3.42k | if(session->remote.crypt_prefs) { |
1046 | 0 | LIBSSH2_FREE(session, session->remote.crypt_prefs); |
1047 | 0 | } |
1048 | 3.42k | if(session->remote.mac_prefs) { |
1049 | 0 | LIBSSH2_FREE(session, session->remote.mac_prefs); |
1050 | 0 | } |
1051 | 3.42k | if(session->remote.comp_prefs) { |
1052 | 0 | LIBSSH2_FREE(session, session->remote.comp_prefs); |
1053 | 0 | } |
1054 | 3.42k | if(session->remote.lang_prefs) { |
1055 | 0 | LIBSSH2_FREE(session, session->remote.lang_prefs); |
1056 | 0 | } |
1057 | 3.42k | if(session->server_sign_algorithms) { |
1058 | 31 | LIBSSH2_FREE(session, session->server_sign_algorithms); |
1059 | 31 | } |
1060 | 3.42k | if(session->sign_algo_prefs) { |
1061 | 0 | LIBSSH2_FREE(session, session->sign_algo_prefs); |
1062 | 0 | } |
1063 | | |
1064 | | /* |
1065 | | * Make sure all memory used in the state variables are free |
1066 | | */ |
1067 | 3.42k | if(session->kexinit_data) { |
1068 | 0 | LIBSSH2_FREE(session, session->kexinit_data); |
1069 | 0 | } |
1070 | 3.42k | if(session->startup_data) { |
1071 | 1 | LIBSSH2_FREE(session, session->startup_data); |
1072 | 1 | } |
1073 | 3.42k | if(session->userauth_list_data) { |
1074 | 0 | LIBSSH2_FREE(session, session->userauth_list_data); |
1075 | 0 | } |
1076 | 3.42k | if(session->userauth_banner) { |
1077 | 0 | LIBSSH2_FREE(session, session->userauth_banner); |
1078 | 0 | } |
1079 | 3.42k | if(session->userauth_pswd_data) { |
1080 | 0 | LIBSSH2_FREE(session, session->userauth_pswd_data); |
1081 | 0 | } |
1082 | 3.42k | if(session->userauth_pswd_newpw) { |
1083 | 0 | LIBSSH2_FREE(session, session->userauth_pswd_newpw); |
1084 | 0 | } |
1085 | 3.42k | if(session->userauth_host_packet) { |
1086 | 0 | LIBSSH2_FREE(session, session->userauth_host_packet); |
1087 | 0 | } |
1088 | 3.42k | if(session->userauth_host_method) { |
1089 | 0 | LIBSSH2_FREE(session, session->userauth_host_method); |
1090 | 0 | } |
1091 | 3.42k | if(session->userauth_host_data) { |
1092 | 0 | LIBSSH2_FREE(session, session->userauth_host_data); |
1093 | 0 | } |
1094 | 3.42k | if(session->userauth_pblc_data) { |
1095 | 0 | LIBSSH2_FREE(session, session->userauth_pblc_data); |
1096 | 0 | } |
1097 | 3.42k | if(session->userauth_pblc_packet) { |
1098 | 0 | LIBSSH2_FREE(session, session->userauth_pblc_packet); |
1099 | 0 | } |
1100 | 3.42k | if(session->userauth_pblc_method) { |
1101 | 0 | LIBSSH2_FREE(session, session->userauth_pblc_method); |
1102 | 0 | } |
1103 | 3.42k | if(session->userauth_kybd_data) { |
1104 | 0 | LIBSSH2_FREE(session, session->userauth_kybd_data); |
1105 | 0 | } |
1106 | 3.42k | if(session->userauth_kybd_packet) { |
1107 | 0 | LIBSSH2_FREE(session, session->userauth_kybd_packet); |
1108 | 0 | } |
1109 | 3.42k | if(session->userauth_kybd_auth_instruction) { |
1110 | 0 | LIBSSH2_FREE(session, session->userauth_kybd_auth_instruction); |
1111 | 0 | } |
1112 | 3.42k | if(session->open_packet) { |
1113 | 0 | LIBSSH2_FREE(session, session->open_packet); |
1114 | 0 | } |
1115 | 3.42k | if(session->open_data) { |
1116 | 0 | LIBSSH2_FREE(session, session->open_data); |
1117 | 0 | } |
1118 | 3.42k | if(session->direct_message) { |
1119 | 0 | LIBSSH2_FREE(session, session->direct_message); |
1120 | 0 | } |
1121 | 3.42k | if(session->fwdLstn_packet) { |
1122 | 0 | LIBSSH2_FREE(session, session->fwdLstn_packet); |
1123 | 0 | } |
1124 | 3.42k | if(session->pkeyInit_data) { |
1125 | 0 | LIBSSH2_FREE(session, session->pkeyInit_data); |
1126 | 0 | } |
1127 | 3.42k | if(session->scpRecv_command) { |
1128 | 0 | LIBSSH2_FREE(session, session->scpRecv_command); |
1129 | 0 | } |
1130 | 3.42k | if(session->scpSend_command) { |
1131 | 0 | LIBSSH2_FREE(session, session->scpSend_command); |
1132 | 0 | } |
1133 | 3.42k | if(session->sftpInit_sftp) { |
1134 | 0 | LIBSSH2_FREE(session, session->sftpInit_sftp); |
1135 | 0 | } |
1136 | | |
1137 | | /* Free payload buffer */ |
1138 | 3.42k | if(session->packet.total_num) { |
1139 | 344 | LIBSSH2_FREE(session, session->packet.payload); |
1140 | 344 | } |
1141 | | |
1142 | | /* Cleanup all remaining packets */ |
1143 | | /* !checksrc! disable EQUALSNULL 1 */ |
1144 | 8.71k | while((pkg = _libssh2_list_first(&session->packets)) != NULL) { |
1145 | 5.28k | packets_left++; |
1146 | 5.28k | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, |
1147 | 5.28k | "packet left with id %d", pkg->data[0])); |
1148 | | /* unlink the node */ |
1149 | 5.28k | _libssh2_list_remove(&pkg->node); |
1150 | | |
1151 | | /* free */ |
1152 | 5.28k | LIBSSH2_FREE(session, pkg->data); |
1153 | 5.28k | LIBSSH2_FREE(session, pkg); |
1154 | 5.28k | } |
1155 | 3.42k | (void)packets_left; |
1156 | 3.42k | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, |
1157 | 3.42k | "Extra packets left %d", packets_left)); |
1158 | | |
1159 | 3.42k | if(session->socket_prev_blockstate) { |
1160 | | /* if the socket was previously blocking, put it back so */ |
1161 | 3.42k | rc = session_nonblock(session->socket_fd, 0); |
1162 | 3.42k | if(rc) { |
1163 | 0 | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, |
1164 | 0 | "unable to reset socket's blocking state")); |
1165 | 0 | } |
1166 | 3.42k | } |
1167 | | |
1168 | 3.42k | if(session->server_hostkey) { |
1169 | 860 | LIBSSH2_FREE(session, session->server_hostkey); |
1170 | 860 | } |
1171 | | |
1172 | | /* error string */ |
1173 | 3.42k | if(session->err_msg && |
1174 | 3.42k | ((session->err_flags & LIBSSH2_ERR_FLAG_DUP) != 0)) { |
1175 | 0 | LIBSSH2_FREE(session, (char *)LIBSSH2_UNCONST(session->err_msg)); |
1176 | 0 | } |
1177 | | |
1178 | 3.42k | LIBSSH2_FREE(session, session); |
1179 | | |
1180 | 3.42k | return 0; |
1181 | 3.42k | } |
1182 | | |
1183 | | /* |
1184 | | * libssh2_session_free |
1185 | | * |
1186 | | * Frees the memory allocated to the session |
1187 | | * Also closes and frees any channels attached to this session |
1188 | | */ |
1189 | | LIBSSH2_API int |
1190 | | libssh2_session_free(LIBSSH2_SESSION * session) |
1191 | 3.42k | { |
1192 | 3.42k | int rc; |
1193 | | |
1194 | 3.42k | BLOCK_ADJUST(rc, session, session_free(session)); |
1195 | | |
1196 | 3.42k | return rc; |
1197 | 3.42k | } |
1198 | | |
1199 | | /* |
1200 | | * session_disconnect |
1201 | | */ |
1202 | | static int |
1203 | | session_disconnect(LIBSSH2_SESSION *session, int reason, |
1204 | | const char *description, |
1205 | | const char *lang) |
1206 | 628 | { |
1207 | 628 | unsigned char *s; |
1208 | 628 | size_t descr_len = 0, lang_len = 0; |
1209 | 628 | int rc; |
1210 | | |
1211 | 628 | if(session->disconnect_state == libssh2_NB_state_idle) { |
1212 | 628 | _libssh2_debug((session, LIBSSH2_TRACE_TRANS, |
1213 | 628 | "Disconnecting: reason=%d, desc=%s, lang=%s", reason, |
1214 | 628 | description, lang)); |
1215 | 628 | if(description) |
1216 | 628 | descr_len = strlen(description); |
1217 | | |
1218 | 628 | if(lang) |
1219 | 628 | lang_len = strlen(lang); |
1220 | | |
1221 | 628 | if(descr_len > 256) |
1222 | 0 | return _libssh2_error(session, LIBSSH2_ERROR_INVAL, |
1223 | 0 | "too long description"); |
1224 | | |
1225 | 628 | if(lang_len > 256) |
1226 | 0 | return _libssh2_error(session, LIBSSH2_ERROR_INVAL, |
1227 | 0 | "too long language string"); |
1228 | | |
1229 | | /* 13 = packet_type(1) + reason code(4) + descr_len(4) + lang_len(4) */ |
1230 | 628 | session->disconnect_data_len = descr_len + lang_len + 13; |
1231 | | |
1232 | 628 | s = session->disconnect_data; |
1233 | | |
1234 | 628 | *(s++) = SSH_MSG_DISCONNECT; |
1235 | 628 | _libssh2_store_u32(&s, reason); |
1236 | 628 | _libssh2_store_str(&s, description, descr_len); |
1237 | | /* store length only, lang is sent separately */ |
1238 | 628 | _libssh2_store_u32(&s, (uint32_t)lang_len); |
1239 | | |
1240 | 628 | session->disconnect_state = libssh2_NB_state_created; |
1241 | 628 | } |
1242 | | |
1243 | 628 | rc = _libssh2_transport_send(session, session->disconnect_data, |
1244 | 628 | session->disconnect_data_len, |
1245 | 628 | (const unsigned char *)lang, lang_len); |
1246 | 628 | if(rc == LIBSSH2_ERROR_EAGAIN) |
1247 | 0 | return rc; |
1248 | | |
1249 | 628 | session->disconnect_state = libssh2_NB_state_idle; |
1250 | | |
1251 | 628 | return 0; |
1252 | 628 | } |
1253 | | |
1254 | | /* |
1255 | | * libssh2_session_disconnect_ex |
1256 | | */ |
1257 | | LIBSSH2_API int |
1258 | | libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, |
1259 | | const char *desc, const char *lang) |
1260 | 628 | { |
1261 | 628 | int rc; |
1262 | 628 | session->state &= ~LIBSSH2_STATE_INITIAL_KEX; |
1263 | 628 | session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; |
1264 | 628 | BLOCK_ADJUST(rc, session, |
1265 | 628 | session_disconnect(session, reason, desc, lang)); |
1266 | | |
1267 | 628 | return rc; |
1268 | 628 | } |
1269 | | |
1270 | | /* libssh2_session_methods |
1271 | | * |
1272 | | * Return the currently active methods for method_type |
1273 | | * |
1274 | | * NOTE: Currently lang_cs and lang_sc are ALWAYS set to empty string |
1275 | | * regardless of actual negotiation Strings should NOT be freed |
1276 | | */ |
1277 | | LIBSSH2_API const char * |
1278 | | libssh2_session_methods(LIBSSH2_SESSION * session, int method_type) |
1279 | 0 | { |
1280 | | /* All methods have char *name as their first element */ |
1281 | 0 | const LIBSSH2_KEX_METHOD *method = NULL; |
1282 | |
|
1283 | 0 | switch(method_type) { |
1284 | 0 | case LIBSSH2_METHOD_KEX: |
1285 | 0 | method = session->kex; |
1286 | 0 | break; |
1287 | | |
1288 | 0 | case LIBSSH2_METHOD_HOSTKEY: |
1289 | 0 | method = (const LIBSSH2_KEX_METHOD *) session->hostkey; |
1290 | 0 | break; |
1291 | | |
1292 | 0 | case LIBSSH2_METHOD_CRYPT_CS: |
1293 | 0 | method = (const LIBSSH2_KEX_METHOD *) session->local.crypt; |
1294 | 0 | break; |
1295 | | |
1296 | 0 | case LIBSSH2_METHOD_CRYPT_SC: |
1297 | 0 | method = (const LIBSSH2_KEX_METHOD *) session->remote.crypt; |
1298 | 0 | break; |
1299 | | |
1300 | 0 | case LIBSSH2_METHOD_MAC_CS: |
1301 | 0 | method = (const LIBSSH2_KEX_METHOD *) session->local.mac; |
1302 | 0 | break; |
1303 | | |
1304 | 0 | case LIBSSH2_METHOD_MAC_SC: |
1305 | 0 | method = (const LIBSSH2_KEX_METHOD *) session->remote.mac; |
1306 | 0 | break; |
1307 | | |
1308 | 0 | case LIBSSH2_METHOD_COMP_CS: |
1309 | 0 | method = (const LIBSSH2_KEX_METHOD *) session->local.comp; |
1310 | 0 | break; |
1311 | | |
1312 | 0 | case LIBSSH2_METHOD_COMP_SC: |
1313 | 0 | method = (const LIBSSH2_KEX_METHOD *) session->remote.comp; |
1314 | 0 | break; |
1315 | | |
1316 | 0 | case LIBSSH2_METHOD_LANG_CS: |
1317 | 0 | return ""; |
1318 | | |
1319 | 0 | case LIBSSH2_METHOD_LANG_SC: |
1320 | 0 | return ""; |
1321 | | |
1322 | 0 | default: |
1323 | 0 | _libssh2_error(session, LIBSSH2_ERROR_INVAL, |
1324 | 0 | "Invalid parameter specified for method_type"); |
1325 | 0 | return NULL; |
1326 | 0 | } |
1327 | | |
1328 | 0 | if(!method) { |
1329 | 0 | _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE, |
1330 | 0 | "No method negotiated"); |
1331 | 0 | return NULL; |
1332 | 0 | } |
1333 | | |
1334 | 0 | return method->name; |
1335 | 0 | } |
1336 | | |
1337 | | /* libssh2_session_abstract |
1338 | | * Retrieve a pointer to the abstract property |
1339 | | */ |
1340 | | LIBSSH2_API void ** |
1341 | | libssh2_session_abstract(LIBSSH2_SESSION * session) |
1342 | 0 | { |
1343 | 0 | return &session->abstract; |
1344 | 0 | } |
1345 | | |
1346 | | /* libssh2_session_last_error |
1347 | | * |
1348 | | * Returns error code and populates an error string into errmsg If want_buf is |
1349 | | * non-zero then the string placed into errmsg must be freed by the calling |
1350 | | * program. Otherwise it is assumed to be owned by libssh2 |
1351 | | */ |
1352 | | LIBSSH2_API int |
1353 | | libssh2_session_last_error(LIBSSH2_SESSION * session, char **errmsg, |
1354 | | int *errmsg_len, int want_buf) |
1355 | 0 | { |
1356 | 0 | size_t msglen = 0; |
1357 | | |
1358 | | /* No error to report */ |
1359 | 0 | if(!session->err_code) { |
1360 | 0 | if(errmsg) { |
1361 | 0 | if(want_buf) { |
1362 | 0 | *errmsg = LIBSSH2_ALLOC(session, 1); |
1363 | 0 | if(*errmsg) { |
1364 | 0 | **errmsg = 0; |
1365 | 0 | } |
1366 | 0 | } |
1367 | 0 | else { |
1368 | 0 | *errmsg = (char *)LIBSSH2_UNCONST(""); |
1369 | 0 | } |
1370 | 0 | } |
1371 | 0 | if(errmsg_len) { |
1372 | 0 | *errmsg_len = 0; |
1373 | 0 | } |
1374 | 0 | return 0; |
1375 | 0 | } |
1376 | | |
1377 | 0 | if(errmsg) { |
1378 | 0 | const char *error = session->err_msg ? session->err_msg : ""; |
1379 | |
|
1380 | 0 | msglen = strlen(error); |
1381 | |
|
1382 | 0 | if(want_buf) { |
1383 | | /* Make a copy so the calling program can own it */ |
1384 | 0 | *errmsg = LIBSSH2_ALLOC(session, msglen + 1); |
1385 | 0 | if(*errmsg) { |
1386 | 0 | memcpy(*errmsg, error, msglen); |
1387 | 0 | (*errmsg)[msglen] = 0; |
1388 | 0 | } |
1389 | 0 | } |
1390 | 0 | else |
1391 | 0 | *errmsg = (char *)LIBSSH2_UNCONST(error); |
1392 | 0 | } |
1393 | |
|
1394 | 0 | if(errmsg_len) { |
1395 | 0 | *errmsg_len = (int)msglen; |
1396 | 0 | } |
1397 | |
|
1398 | 0 | return session->err_code; |
1399 | 0 | } |
1400 | | |
1401 | | /* libssh2_session_last_errno |
1402 | | * |
1403 | | * Returns error code |
1404 | | */ |
1405 | | LIBSSH2_API int |
1406 | | libssh2_session_last_errno(LIBSSH2_SESSION * session) |
1407 | 0 | { |
1408 | 0 | return session->err_code; |
1409 | 0 | } |
1410 | | |
1411 | | /* libssh2_session_set_last_error |
1412 | | * |
1413 | | * Sets the internal error code for the session. |
1414 | | * |
1415 | | * This function is available specifically to be used by high level |
1416 | | * language wrappers (i.e. Python or Perl) that may extend the library |
1417 | | * features while still relying on its error reporting mechanism. |
1418 | | */ |
1419 | | LIBSSH2_API int |
1420 | | libssh2_session_set_last_error(LIBSSH2_SESSION* session, |
1421 | | int errcode, |
1422 | | const char *errmsg) |
1423 | 0 | { |
1424 | 0 | return _libssh2_error_flags(session, errcode, errmsg, |
1425 | 0 | LIBSSH2_ERR_FLAG_DUP); |
1426 | 0 | } |
1427 | | |
1428 | | /* libssh2_session_flag |
1429 | | * |
1430 | | * Set/Get session flags |
1431 | | * |
1432 | | * Return error code. |
1433 | | */ |
1434 | | LIBSSH2_API int |
1435 | | libssh2_session_flag(LIBSSH2_SESSION * session, int flag, int value) |
1436 | 0 | { |
1437 | 0 | switch(flag) { |
1438 | 0 | case LIBSSH2_FLAG_SIGPIPE: |
1439 | 0 | session->flag.sigpipe = value; |
1440 | 0 | break; |
1441 | 0 | case LIBSSH2_FLAG_COMPRESS: |
1442 | 0 | session->flag.compress = value; |
1443 | 0 | break; |
1444 | 0 | case LIBSSH2_FLAG_QUOTE_PATHS: |
1445 | 0 | session->flag.quote_paths = value; |
1446 | 0 | break; |
1447 | 0 | default: |
1448 | | /* unknown flag */ |
1449 | 0 | return LIBSSH2_ERROR_INVAL; |
1450 | 0 | } |
1451 | | |
1452 | 0 | return LIBSSH2_ERROR_NONE; |
1453 | 0 | } |
1454 | | |
1455 | | /* _libssh2_session_set_blocking |
1456 | | * |
1457 | | * Set a session's blocking mode on or off, return the previous status when |
1458 | | * this function is called. Note this function does not alter the state of the |
1459 | | * actual socket involved. |
1460 | | */ |
1461 | | int |
1462 | | _libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking) |
1463 | 3.42k | { |
1464 | 3.42k | int bl = session->api_block_mode; |
1465 | 3.42k | _libssh2_debug((session, LIBSSH2_TRACE_CONN, |
1466 | 3.42k | "Setting blocking mode %s", blocking ? "ON" : "OFF")); |
1467 | 3.42k | session->api_block_mode = blocking; |
1468 | | |
1469 | 3.42k | return bl; |
1470 | 3.42k | } |
1471 | | |
1472 | | /* libssh2_session_set_blocking |
1473 | | * |
1474 | | * Set a channel's blocking mode on or off, similar to a socket's |
1475 | | * fcntl(fd, F_SETFL, O_NONBLOCK); type command |
1476 | | */ |
1477 | | LIBSSH2_API void |
1478 | | libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking) |
1479 | 3.42k | { |
1480 | 3.42k | (void)_libssh2_session_set_blocking(session, blocking); |
1481 | 3.42k | } |
1482 | | |
1483 | | /* libssh2_session_get_blocking |
1484 | | * |
1485 | | * Returns a session's blocking mode on or off |
1486 | | */ |
1487 | | LIBSSH2_API int |
1488 | | libssh2_session_get_blocking(LIBSSH2_SESSION * session) |
1489 | 0 | { |
1490 | 0 | return session->api_block_mode; |
1491 | 0 | } |
1492 | | |
1493 | | |
1494 | | /* libssh2_session_set_timeout |
1495 | | * |
1496 | | * Set a session's timeout (in msec) for blocking mode, |
1497 | | * or 0 to disable timeouts. |
1498 | | */ |
1499 | | LIBSSH2_API void |
1500 | | libssh2_session_set_timeout(LIBSSH2_SESSION * session, long timeout) |
1501 | 0 | { |
1502 | 0 | session->api_timeout = timeout; |
1503 | 0 | } |
1504 | | |
1505 | | /* libssh2_session_get_timeout |
1506 | | * |
1507 | | * Returns a session's timeout, or 0 if disabled |
1508 | | */ |
1509 | | LIBSSH2_API long |
1510 | | libssh2_session_get_timeout(LIBSSH2_SESSION * session) |
1511 | 0 | { |
1512 | 0 | return session->api_timeout; |
1513 | 0 | } |
1514 | | |
1515 | | /* libssh2_session_set_read_timeout |
1516 | | * |
1517 | | * Set a session's timeout (in sec) when reading packets, |
1518 | | * or 0 to use default of 60 seconds. |
1519 | | */ |
1520 | | LIBSSH2_API void |
1521 | | libssh2_session_set_read_timeout(LIBSSH2_SESSION * session, long timeout) |
1522 | 0 | { |
1523 | 0 | if(timeout <= 0) { |
1524 | 0 | timeout = LIBSSH2_DEFAULT_READ_TIMEOUT; |
1525 | 0 | } |
1526 | 0 | session->packet_read_timeout = timeout; |
1527 | 0 | } |
1528 | | |
1529 | | /* libssh2_session_get_read_timeout |
1530 | | * |
1531 | | * Returns a session's timeout. Default is 60 seconds. |
1532 | | */ |
1533 | | LIBSSH2_API long |
1534 | | libssh2_session_get_read_timeout(LIBSSH2_SESSION * session) |
1535 | 0 | { |
1536 | 0 | return session->packet_read_timeout; |
1537 | 0 | } |
1538 | | |
1539 | | /* |
1540 | | * libssh2_poll_channel_read |
1541 | | * |
1542 | | * Returns 0 if no data is waiting on channel, |
1543 | | * non-0 if data is available |
1544 | | */ |
1545 | | LIBSSH2_API int |
1546 | | libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended) |
1547 | 0 | { |
1548 | 0 | LIBSSH2_SESSION *session; |
1549 | 0 | LIBSSH2_PACKET *packet; |
1550 | |
|
1551 | 0 | if(!channel) |
1552 | 0 | return LIBSSH2_ERROR_BAD_USE; |
1553 | | |
1554 | 0 | session = channel->session; |
1555 | 0 | packet = _libssh2_list_first(&session->packets); |
1556 | |
|
1557 | 0 | while(packet) { |
1558 | 0 | if(packet->data_len < 5) { |
1559 | 0 | return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL, |
1560 | 0 | "Packet too small"); |
1561 | 0 | } |
1562 | | |
1563 | 0 | if(channel->local.id == _libssh2_ntohu32(packet->data + 1)) { |
1564 | 0 | if(extended == 1 && |
1565 | 0 | (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA |
1566 | 0 | || packet->data[0] == SSH_MSG_CHANNEL_DATA)) { |
1567 | 0 | return 1; |
1568 | 0 | } |
1569 | 0 | else if(extended == 0 && |
1570 | 0 | packet->data[0] == SSH_MSG_CHANNEL_DATA) { |
1571 | 0 | return 1; |
1572 | 0 | } |
1573 | | /* else - no data of any type is ready to be read */ |
1574 | 0 | } |
1575 | 0 | packet = _libssh2_list_next(&packet->node); |
1576 | 0 | } |
1577 | | |
1578 | 0 | return 0; |
1579 | 0 | } |
1580 | | |
1581 | | /* |
1582 | | * poll_channel_write |
1583 | | * |
1584 | | * Returns 0 if writing to channel would block, |
1585 | | * non-0 if data can be written without blocking |
1586 | | */ |
1587 | | static inline int |
1588 | | poll_channel_write(LIBSSH2_CHANNEL * channel) |
1589 | 0 | { |
1590 | 0 | return channel->local.window_size ? 1 : 0; |
1591 | 0 | } |
1592 | | |
1593 | | /* poll_listener_queued |
1594 | | * |
1595 | | * Returns 0 if no connections are waiting to be accepted |
1596 | | * non-0 if one or more connections are available |
1597 | | */ |
1598 | | static inline int |
1599 | | poll_listener_queued(LIBSSH2_LISTENER * listener) |
1600 | 0 | { |
1601 | 0 | return _libssh2_list_first(&listener->queue) ? 1 : 0; |
1602 | 0 | } |
1603 | | |
1604 | | /* |
1605 | | * libssh2_poll |
1606 | | * |
1607 | | * Poll sockets, channels, and listeners for activity |
1608 | | */ |
1609 | | LIBSSH2_API int |
1610 | | libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout) |
1611 | 0 | { |
1612 | 0 | long timeout_remaining; |
1613 | 0 | unsigned int i, active_fds; |
1614 | 0 | #ifdef HAVE_POLL |
1615 | 0 | LIBSSH2_SESSION *session = NULL; |
1616 | 0 | #ifdef HAVE_ALLOCA |
1617 | 0 | struct pollfd *sockets = alloca(sizeof(struct pollfd) * nfds); |
1618 | | #else |
1619 | | struct pollfd sockets[256]; |
1620 | | |
1621 | | if(nfds > 256) |
1622 | | /* systems without alloca use a fixed-size array, this can be fixed if |
1623 | | we really want to, at least if the compiler is a C99 capable one */ |
1624 | | return -1; |
1625 | | #endif |
1626 | | /* Setup sockets for polling */ |
1627 | 0 | for(i = 0; i < nfds; i++) { |
1628 | 0 | fds[i].revents = 0; |
1629 | 0 | switch(fds[i].type) { |
1630 | 0 | case LIBSSH2_POLLFD_SOCKET: |
1631 | 0 | sockets[i].fd = fds[i].fd.socket; |
1632 | 0 | sockets[i].events = (short)fds[i].events; |
1633 | 0 | sockets[i].revents = 0; |
1634 | 0 | break; |
1635 | | |
1636 | 0 | case LIBSSH2_POLLFD_CHANNEL: |
1637 | 0 | sockets[i].fd = fds[i].fd.channel->session->socket_fd; |
1638 | 0 | sockets[i].events = POLLIN; |
1639 | 0 | sockets[i].revents = 0; |
1640 | 0 | if(!session) |
1641 | 0 | session = fds[i].fd.channel->session; |
1642 | 0 | break; |
1643 | | |
1644 | 0 | case LIBSSH2_POLLFD_LISTENER: |
1645 | 0 | sockets[i].fd = fds[i].fd.listener->session->socket_fd; |
1646 | 0 | sockets[i].events = POLLIN; |
1647 | 0 | sockets[i].revents = 0; |
1648 | 0 | if(!session) |
1649 | 0 | session = fds[i].fd.listener->session; |
1650 | 0 | break; |
1651 | | |
1652 | 0 | default: |
1653 | 0 | if(session) |
1654 | 0 | _libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, |
1655 | 0 | "Invalid descriptor passed to libssh2_poll()"); |
1656 | 0 | return -1; |
1657 | 0 | } |
1658 | 0 | } |
1659 | | #elif defined(HAVE_SELECT) |
1660 | | LIBSSH2_SESSION *session = NULL; |
1661 | | libssh2_socket_t maxfd = 0; |
1662 | | fd_set rfds, wfds; |
1663 | | struct timeval tv; |
1664 | | |
1665 | | FD_ZERO(&rfds); |
1666 | | FD_ZERO(&wfds); |
1667 | | for(i = 0; i < nfds; i++) { |
1668 | | fds[i].revents = 0; |
1669 | | switch(fds[i].type) { |
1670 | | case LIBSSH2_POLLFD_SOCKET: |
1671 | | if(fds[i].events & LIBSSH2_POLLFD_POLLIN) { |
1672 | | #if defined(__GNUC__) || defined(__clang__) |
1673 | | #pragma GCC diagnostic push |
1674 | | #pragma GCC diagnostic ignored "-Wsign-conversion" |
1675 | | #endif |
1676 | | FD_SET(fds[i].fd.socket, &rfds); |
1677 | | #if defined(__GNUC__) || defined(__clang__) |
1678 | | #pragma GCC diagnostic pop |
1679 | | #endif |
1680 | | if(fds[i].fd.socket > maxfd) |
1681 | | maxfd = fds[i].fd.socket; |
1682 | | } |
1683 | | if(fds[i].events & LIBSSH2_POLLFD_POLLOUT) { |
1684 | | #if defined(__GNUC__) || defined(__clang__) |
1685 | | #pragma GCC diagnostic push |
1686 | | #pragma GCC diagnostic ignored "-Wsign-conversion" |
1687 | | #endif |
1688 | | FD_SET(fds[i].fd.socket, &wfds); |
1689 | | #if defined(__GNUC__) || defined(__clang__) |
1690 | | #pragma GCC diagnostic pop |
1691 | | #endif |
1692 | | if(fds[i].fd.socket > maxfd) |
1693 | | maxfd = fds[i].fd.socket; |
1694 | | } |
1695 | | break; |
1696 | | |
1697 | | case LIBSSH2_POLLFD_CHANNEL: |
1698 | | #if defined(__GNUC__) || defined(__clang__) |
1699 | | #pragma GCC diagnostic push |
1700 | | #pragma GCC diagnostic ignored "-Wsign-conversion" |
1701 | | #endif |
1702 | | FD_SET(fds[i].fd.channel->session->socket_fd, &rfds); |
1703 | | #if defined(__GNUC__) || defined(__clang__) |
1704 | | #pragma GCC diagnostic pop |
1705 | | #endif |
1706 | | if(fds[i].fd.channel->session->socket_fd > maxfd) |
1707 | | maxfd = fds[i].fd.channel->session->socket_fd; |
1708 | | if(!session) |
1709 | | session = fds[i].fd.channel->session; |
1710 | | break; |
1711 | | |
1712 | | case LIBSSH2_POLLFD_LISTENER: |
1713 | | #if defined(__GNUC__) || defined(__clang__) |
1714 | | #pragma GCC diagnostic push |
1715 | | #pragma GCC diagnostic ignored "-Wsign-conversion" |
1716 | | #endif |
1717 | | FD_SET(fds[i].fd.listener->session->socket_fd, &rfds); |
1718 | | #if defined(__GNUC__) || defined(__clang__) |
1719 | | #pragma GCC diagnostic pop |
1720 | | #endif |
1721 | | if(fds[i].fd.listener->session->socket_fd > maxfd) |
1722 | | maxfd = fds[i].fd.listener->session->socket_fd; |
1723 | | if(!session) |
1724 | | session = fds[i].fd.listener->session; |
1725 | | break; |
1726 | | |
1727 | | default: |
1728 | | if(session) |
1729 | | _libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, |
1730 | | "Invalid descriptor passed to libssh2_poll()"); |
1731 | | return -1; |
1732 | | } |
1733 | | } |
1734 | | #else |
1735 | | /* No select() or poll() |
1736 | | * no sockets structure to setup |
1737 | | */ |
1738 | | |
1739 | | timeout = 0; |
1740 | | #endif /* HAVE_POLL or HAVE_SELECT */ |
1741 | | |
1742 | 0 | timeout_remaining = timeout; |
1743 | 0 | do { |
1744 | 0 | #if defined(HAVE_POLL) || defined(HAVE_SELECT) |
1745 | 0 | int sysret; |
1746 | 0 | #endif |
1747 | |
|
1748 | 0 | active_fds = 0; |
1749 | |
|
1750 | 0 | for(i = 0; i < nfds; i++) { |
1751 | 0 | if(fds[i].events != fds[i].revents) { |
1752 | 0 | switch(fds[i].type) { |
1753 | 0 | case LIBSSH2_POLLFD_CHANNEL: |
1754 | 0 | if((fds[i].events & LIBSSH2_POLLFD_POLLIN) && |
1755 | | /* Want to be ready for read */ |
1756 | 0 | ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { |
1757 | | /* Not yet known to be ready for read */ |
1758 | 0 | fds[i].revents |= |
1759 | 0 | libssh2_poll_channel_read(fds[i].fd.channel, |
1760 | 0 | 0) ? |
1761 | 0 | LIBSSH2_POLLFD_POLLIN : 0; |
1762 | 0 | } |
1763 | 0 | if((fds[i].events & LIBSSH2_POLLFD_POLLEXT) && |
1764 | | /* Want to be ready for extended read */ |
1765 | 0 | ((fds[i].revents & LIBSSH2_POLLFD_POLLEXT) == 0)) { |
1766 | | /* Not yet known to be ready for extended read */ |
1767 | 0 | fds[i].revents |= |
1768 | 0 | libssh2_poll_channel_read(fds[i].fd.channel, |
1769 | 0 | 1) ? |
1770 | 0 | LIBSSH2_POLLFD_POLLEXT : 0; |
1771 | 0 | } |
1772 | 0 | if((fds[i].events & LIBSSH2_POLLFD_POLLOUT) && |
1773 | | /* Want to be ready for write */ |
1774 | 0 | ((fds[i].revents & LIBSSH2_POLLFD_POLLOUT) == 0)) { |
1775 | | /* Not yet known to be ready for write */ |
1776 | 0 | fds[i].revents |= |
1777 | 0 | poll_channel_write(fds[i].fd. channel) ? |
1778 | 0 | LIBSSH2_POLLFD_POLLOUT : 0; |
1779 | 0 | } |
1780 | 0 | if(fds[i].fd.channel->remote.close |
1781 | 0 | || fds[i].fd.channel->local.close) { |
1782 | 0 | fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED; |
1783 | 0 | } |
1784 | 0 | if(fds[i].fd.channel->session->socket_state == |
1785 | 0 | LIBSSH2_SOCKET_DISCONNECTED) { |
1786 | 0 | fds[i].revents |= |
1787 | 0 | LIBSSH2_POLLFD_CHANNEL_CLOSED | |
1788 | 0 | LIBSSH2_POLLFD_SESSION_CLOSED; |
1789 | 0 | } |
1790 | 0 | break; |
1791 | | |
1792 | 0 | case LIBSSH2_POLLFD_LISTENER: |
1793 | 0 | if((fds[i].events & LIBSSH2_POLLFD_POLLIN) && |
1794 | | /* Want a connection */ |
1795 | 0 | ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { |
1796 | | /* No connections known of yet */ |
1797 | 0 | fds[i].revents |= |
1798 | 0 | poll_listener_queued(fds[i].fd. listener) ? |
1799 | 0 | LIBSSH2_POLLFD_POLLIN : 0; |
1800 | 0 | } |
1801 | 0 | if(fds[i].fd.listener->session->socket_state == |
1802 | 0 | LIBSSH2_SOCKET_DISCONNECTED) { |
1803 | 0 | fds[i].revents |= |
1804 | 0 | LIBSSH2_POLLFD_LISTENER_CLOSED | |
1805 | 0 | LIBSSH2_POLLFD_SESSION_CLOSED; |
1806 | 0 | } |
1807 | 0 | break; |
1808 | 0 | } |
1809 | 0 | } |
1810 | 0 | if(fds[i].revents) { |
1811 | 0 | active_fds++; |
1812 | 0 | } |
1813 | 0 | } |
1814 | | |
1815 | 0 | if(active_fds) { |
1816 | | /* Don't block on the sockets if we have channels/listeners which |
1817 | | are ready */ |
1818 | 0 | timeout_remaining = 0; |
1819 | 0 | } |
1820 | 0 | #ifdef HAVE_POLL |
1821 | |
|
1822 | 0 | { |
1823 | 0 | struct timeval tv_begin, tv_end; |
1824 | |
|
1825 | 0 | gettimeofday(&tv_begin, NULL); |
1826 | 0 | sysret = poll(sockets, nfds, (int)timeout_remaining); |
1827 | 0 | gettimeofday(&tv_end, NULL); |
1828 | 0 | timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000; |
1829 | 0 | timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000; |
1830 | 0 | } |
1831 | |
|
1832 | 0 | if(sysret > 0) { |
1833 | 0 | for(i = 0; i < nfds; i++) { |
1834 | 0 | switch(fds[i].type) { |
1835 | 0 | case LIBSSH2_POLLFD_SOCKET: |
1836 | 0 | fds[i].revents = sockets[i].revents; |
1837 | 0 | sockets[i].revents = 0; /* In case we loop again, be |
1838 | | nice */ |
1839 | 0 | if(fds[i].revents) { |
1840 | 0 | active_fds++; |
1841 | 0 | } |
1842 | 0 | break; |
1843 | 0 | case LIBSSH2_POLLFD_CHANNEL: |
1844 | 0 | if(sockets[i].events & POLLIN) { |
1845 | | /* Spin session until no data available */ |
1846 | 0 | while(_libssh2_transport_read(fds[i].fd. |
1847 | 0 | channel->session) |
1848 | 0 | > 0); |
1849 | 0 | } |
1850 | 0 | if(sockets[i].revents & POLLHUP) { |
1851 | 0 | fds[i].revents |= |
1852 | 0 | LIBSSH2_POLLFD_CHANNEL_CLOSED | |
1853 | 0 | LIBSSH2_POLLFD_SESSION_CLOSED; |
1854 | 0 | } |
1855 | 0 | sockets[i].revents = 0; |
1856 | 0 | break; |
1857 | 0 | case LIBSSH2_POLLFD_LISTENER: |
1858 | 0 | if(sockets[i].events & POLLIN) { |
1859 | | /* Spin session until no data available */ |
1860 | 0 | while(_libssh2_transport_read(fds[i].fd. |
1861 | 0 | listener->session) |
1862 | 0 | > 0); |
1863 | 0 | } |
1864 | 0 | if(sockets[i].revents & POLLHUP) { |
1865 | 0 | fds[i].revents |= |
1866 | 0 | LIBSSH2_POLLFD_LISTENER_CLOSED | |
1867 | 0 | LIBSSH2_POLLFD_SESSION_CLOSED; |
1868 | 0 | } |
1869 | 0 | sockets[i].revents = 0; |
1870 | 0 | break; |
1871 | 0 | } |
1872 | 0 | } |
1873 | 0 | } |
1874 | | #elif defined(HAVE_SELECT) |
1875 | | tv.tv_sec = timeout_remaining / 1000; |
1876 | | #ifdef libssh2_usec_t |
1877 | | tv.tv_usec = (libssh2_usec_t)((timeout_remaining % 1000) * 1000); |
1878 | | #else |
1879 | | tv.tv_usec = (timeout_remaining % 1000) * 1000; |
1880 | | #endif |
1881 | | |
1882 | | { |
1883 | | struct timeval tv_begin, tv_end; |
1884 | | |
1885 | | gettimeofday(&tv_begin, NULL); |
1886 | | sysret = select((int)(maxfd + 1), &rfds, &wfds, NULL, &tv); |
1887 | | gettimeofday(&tv_end, NULL); |
1888 | | |
1889 | | timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000; |
1890 | | timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000; |
1891 | | } |
1892 | | |
1893 | | if(sysret > 0) { |
1894 | | for(i = 0; i < nfds; i++) { |
1895 | | switch(fds[i].type) { |
1896 | | case LIBSSH2_POLLFD_SOCKET: |
1897 | | #if defined(__GNUC__) || defined(__clang__) |
1898 | | #pragma GCC diagnostic push |
1899 | | #pragma GCC diagnostic ignored "-Wsign-conversion" |
1900 | | #endif |
1901 | | if(FD_ISSET(fds[i].fd.socket, &rfds)) { |
1902 | | fds[i].revents |= LIBSSH2_POLLFD_POLLIN; |
1903 | | } |
1904 | | if(FD_ISSET(fds[i].fd.socket, &wfds)) { |
1905 | | fds[i].revents |= LIBSSH2_POLLFD_POLLOUT; |
1906 | | } |
1907 | | if(fds[i].revents) { |
1908 | | active_fds++; |
1909 | | } |
1910 | | #if defined(__GNUC__) || defined(__clang__) |
1911 | | #pragma GCC diagnostic pop |
1912 | | #endif |
1913 | | break; |
1914 | | |
1915 | | case LIBSSH2_POLLFD_CHANNEL: |
1916 | | if(FD_ISSET(fds[i].fd.channel->session->socket_fd, |
1917 | | &rfds)) { |
1918 | | /* Spin session until no data available */ |
1919 | | while(_libssh2_transport_read(fds[i].fd. |
1920 | | channel->session) |
1921 | | > 0); |
1922 | | } |
1923 | | break; |
1924 | | |
1925 | | case LIBSSH2_POLLFD_LISTENER: |
1926 | | if(FD_ISSET |
1927 | | (fds[i].fd.listener->session->socket_fd, &rfds)) { |
1928 | | /* Spin session until no data available */ |
1929 | | while(_libssh2_transport_read(fds[i].fd. |
1930 | | listener->session) |
1931 | | > 0); |
1932 | | } |
1933 | | break; |
1934 | | } |
1935 | | } |
1936 | | } |
1937 | | #endif /* else no select() or poll() -- timeout (and by extension |
1938 | | * timeout_remaining) will be equal to 0 */ |
1939 | 0 | } while((timeout_remaining > 0) && !active_fds); |
1940 | | |
1941 | 0 | return active_fds; |
1942 | 0 | } |
1943 | | |
1944 | | /* |
1945 | | * libssh2_session_block_directions |
1946 | | * |
1947 | | * Get blocked direction when a function returns LIBSSH2_ERROR_EAGAIN |
1948 | | * Returns LIBSSH2_SOCKET_BLOCK_INBOUND if recv() blocked |
1949 | | * or LIBSSH2_SOCKET_BLOCK_OUTBOUND if send() blocked |
1950 | | */ |
1951 | | LIBSSH2_API int |
1952 | | libssh2_session_block_directions(LIBSSH2_SESSION *session) |
1953 | 28 | { |
1954 | 28 | return session->socket_block_directions; |
1955 | 28 | } |
1956 | | |
1957 | | /* libssh2_session_banner_get |
1958 | | * Get the remote banner (server ID string) |
1959 | | */ |
1960 | | |
1961 | | LIBSSH2_API const char * |
1962 | | libssh2_session_banner_get(LIBSSH2_SESSION *session) |
1963 | 0 | { |
1964 | | /* to avoid a coredump when session is NULL */ |
1965 | 0 | if(!session) |
1966 | 0 | return NULL; |
1967 | | |
1968 | 0 | if(!session->remote.banner) |
1969 | 0 | return NULL; |
1970 | | |
1971 | 0 | return (const char *) session->remote.banner; |
1972 | 0 | } |