Compare commits

..

34 Commits

Author SHA1 Message Date
Sara Golemon
9203a62789 Late commit -- This is for release 0.10 2005-05-24 14:01:38 +00:00
Sara Golemon
6de7ed8a7b Drop these old references to initial window size, they were a mistake 2005-05-18 17:12:40 +00:00
Sara Golemon
8937980044 BFN 2005-05-18 17:10:27 +00:00
Sara Golemon
cc7703092f Save up multiple small refunds in order to cut down on unnecessary WINDOW_ADJUST packets 2005-05-18 17:08:29 +00:00
Sara Golemon
7502920c7f Remove /test/* 2005-05-17 19:30:59 +00:00
Sara Golemon
ff6f2dbe52 Typo: ALWAYS TEST FIRST 2005-05-17 17:18:48 +00:00
Sara Golemon
e5ee4a5be3 Fix sftp packet queueing mechanism 2005-05-17 05:22:41 +00:00
Sara Golemon
30bb7db0d1 Reduce busy-looping of libssh2_sftp_packet_requirev() 2005-05-16 23:12:09 +00:00
Sara Golemon
8d90bbfc28 Fix segfault when client/host can't agree on hostkey/crypt/mac/comp 2005-05-16 17:16:25 +00:00
Sara Golemon
e9b0710b4b Fix format specifiers 2005-05-11 05:26:12 +00:00
Sara Golemon
379e510c87 Whoops 2005-05-11 05:16:32 +00:00
Sara Golemon
ebfbd22e59 Add debugging hooks for development diagnostics 2005-05-11 05:11:31 +00:00
Sara Golemon
9a0ba35457 Ignore extended data in the SFTP layer. 2005-05-10 04:57:34 +00:00
Sara Golemon
023c54d95d One last (better be last) fix for FXP_INIT 2005-05-10 04:49:43 +00:00
Sara Golemon
ba2f21eb85 More fixes for channel_write() 2005-05-06 18:30:43 +00:00
Sara Golemon
3ea661a574 Bump for release 2005-05-05 18:48:51 +00:00
Sara Golemon
30755999c5 Check for and link against libm and libsocket 2005-05-05 18:45:42 +00:00
Sara Golemon
8e0e6d81dd Block means block 2005-05-04 19:01:48 +00:00
Sara Golemon
cba673de6f Make blocking_read's polling loop less arbitrary 2005-05-04 18:48:47 +00:00
Sara Golemon
11b27e52c3 Put filetype into open/diropen/mkdir attributes 2005-05-04 18:06:07 +00:00
Sara Golemon
f289bcdd54 Fix constant from last patch 2005-05-04 17:52:32 +00:00
Sara Golemon
66e7462f01 Late BFN 2005-05-03 18:35:40 +00:00
Sara Golemon
ed88c32368 FXP_INIT packet should not have had a request_id component and sftp_packet_require shouldn't expect FXP_VERSION to have one when coming back. Thanks puudeli! 2005-05-03 13:57:40 +00:00
Sara Golemon
83b95eb13e Fixed SFTP to be properly BC with version 1 and 2 servers 2005-04-07 21:53:55 +00:00
Sara Golemon
ba420fc7bf Block means block. 2005-04-05 23:08:41 +00:00
Sara Golemon
ae9ad1ef6f break; fallthroughs in win32 code 2005-04-02 05:43:17 +00:00
Sara Golemon
d3f854c21b Phase II of packet loop migration: Clean code is good code 2005-04-01 20:17:09 +00:00
Sara Golemon
5d9c0d50f3 Another typo 2005-04-01 15:34:14 +00:00
Sara Golemon
35a3e7a6c0 typo 2005-04-01 06:55:44 +00:00
Sara Golemon
15b8489046 Phase 1 of the Great Timeout Cleanup 2005-04-01 06:11:34 +00:00
Sara Golemon
dce388e9c5 Nix unused variable 2005-03-28 23:03:41 +00:00
Sara Golemon
0138e36352 Add libssh2_channel_window_(read|write)_ex() 2005-03-28 22:59:35 +00:00
Sara Golemon
d6039f39e4 Nix the memset loop
Speaking of which: Why wasn't it JUST a memset() call?

The encrypted version of packet_write fills in this data with OpenSSL
RAND_bytes() data, and as far as the unencrypted version goes?
Well, it's unencrypted, randomness doesn't help at that point.
2005-03-26 05:31:31 +00:00
Sara Golemon
0e0ed2aff4 Don't let a dead session leave poll() hanging 2005-03-26 01:26:14 +00:00
21 changed files with 1232 additions and 1705 deletions

42
README
View File

@@ -1,6 +1,48 @@
libssh2 - SSH2 library libssh2 - SSH2 library
====================== ======================
Version 0.11
------------
Added libssh2_channel_receive_window_adjust() to be able to increase the size of the receive window.
Added queueing for small window_adjust packets to avoid unnecessary packet conversation.
Version 0.10
------------
Added developer debugging hooks. See --enable-debug-* options to ./configure
Ignore extended data in the SFTP layer. With no other mechanism to deal with that data it'd just fill up and get stuck.
(Re)Fixed channel_write() to provide an opportunity for window space to become available again.
(Re)Fixed SFTP INIT to send the correct SFTP packet length.
Fixed segfault when client and host can't agree on a hostkey/crypt/mac/comp method. (Thanks puudeli)
Fixed major issue with sftp packet buffering mechanism. Using wrong blocking semantics. (No puudeli, YOU the man)
Reduced busy-looping of libssh2_sftp_packet_requirev.
Version 0.9
-----------
Changed blocking_read to only block as much as necessary and not an arbitrary length of time. (Thanks Felix)
Fixed SFTP INIT/VERSION to exclude request_id and send correct maximum version number.
Fixed SFTP to be properly BC with version 1 and 2 servers.
Fixed libssh2_poll() to recognized closed sessions/channels.
Fixed libssh2_channel_write_ex() to fully block when set to blocking mode. Return actual bytes written as well. (Thanks deadem)
Added tests for -lm and -lsocket and add them when necessary.
Added libssh2_channel_window_read_ex() and libssh2_channel_window_write_ex()
for examining the ssh transport windowing states.
Version 0.8 Version 0.8
----------- -----------

View File

@@ -1,5 +1,5 @@
# AC_PREREQ(2.57) # AC_PREREQ(2.57)
AC_INIT(libssh2,0.8,sarag@libssh2.org) AC_INIT(libssh2,0.10,sarag@libssh2.org)
AC_CONFIG_SRCDIR([src]) AC_CONFIG_SRCDIR([src])
AC_CONFIG_HEADER([include/libssh2_config.h]) AC_CONFIG_HEADER([include/libssh2_config.h])
@@ -21,8 +21,17 @@ case "$host" in
SHLIB_LDFLAGS="-shared" SHLIB_LDFLAGS="-shared"
;; ;;
esac esac
AC_CHECK_LIB(socket, socket, [
SHLIB_LDFLAGS="$SHLIB_LDFLAGS -lsocket"
LIBS="$LIBS -lsocket"
])
AC_CHECK_LIB(m, ceil, [ SHLIB_LDFLAGS="$SHLIB_LDFLAGS -lm" ])
AC_SUBST(SHLIB_SUFFIX_NAME) AC_SUBST(SHLIB_SUFFIX_NAME)
AC_SUBST(SHLIB_LDFLAGS) AC_SUBST(SHLIB_LDFLAGS)
AC_SUBST(LIBS)
AC_PROG_CC AC_PROG_CC
AC_PROG_INSTALL AC_PROG_INSTALL
@@ -160,6 +169,44 @@ if test "$GEX_NEW" != "no"; then
AC_DEFINE(LIBSSH2_DH_GEX_NEW, 1, [Enable newer diffie-hellman-group-exchange-sha1 syntax]) AC_DEFINE(LIBSSH2_DH_GEX_NEW, 1, [Enable newer diffie-hellman-group-exchange-sha1 syntax])
fi fi
#
# Optional debugging -- Meant for developer maintenance only
# When enabled, the relevant debugging information will be written on stderr
#
AC_ARG_ENABLE(debug-transport,
AC_HELP_STRING([--enable-debug-transport],[Output transport layer debugging info to stderr]),
[AC_DEFINE(LIBSSH2_DEBUG_TRANSPORT, 1, [Output transport layer debugging info to stderr])])
AC_ARG_ENABLE(debug-kex,
AC_HELP_STRING([--enable-debug-kex],[Output Key Exchange debugging info to stderr]),
[AC_DEFINE(LIBSSH2_DEBUG_KEX, 1, [Output Key Exchange debugging info to stderr])])
AC_ARG_ENABLE(debug-userauth,
AC_HELP_STRING([--enable-debug-userauth],[Output userauth debugging info to stderr]),
[AC_DEFINE(LIBSSH2_DEBUG_USERAUTH, 1, [Output userauth layer debugging info to stderr])])
AC_ARG_ENABLE(debug-channel,
AC_HELP_STRING([--enable-debug-connection],[Output connection layer debugging info to stderr]),
[AC_DEFINE(LIBSSH2_DEBUG_CONNECTION, 1, [Output connection layer debugging info to stderr])])
AC_ARG_ENABLE(debug-scp,
AC_HELP_STRING([--enable-debug-scp],[Output scp subsystem debugging info to stderr]),
[AC_DEFINE(LIBSSH2_DEBUG_SCP, 1, [Output scp subsystem debugging info to stderr])])
AC_ARG_ENABLE(debug-sftp,
AC_HELP_STRING([--enable-debug-sftp],[Output sftp subsystem debugging info to stderr]),
[AC_DEFINE(LIBSSH2_DEBUG_SFTP, 1, [Output sftp subsystem debugging info to stderr])])
AC_ARG_ENABLE(debug-errors,
AC_HELP_STRING([--enable-debug-errors],[Output failure events to stderr]),
[AC_DEFINE(LIBSSH2_DEBUG_ERRORS, 1, [Output failure events to stderr])])
AC_ARG_ENABLE(debug-all,
AC_HELP_STRING([--enable-debug-all],[Output debugging info for all layers to stderr]),
[
AC_DEFINE(LIBSSH2_DEBUG_TRANSPORT, 1, [Output transport layer debugging info to stderr])
AC_DEFINE(LIBSSH2_DEBUG_KEX, 1, [Output Key Exchange debugging info to stderr])
AC_DEFINE(LIBSSH2_DEBUG_USERAUTH, 1, [Output userauth layer debugging info to stderr])
AC_DEFINE(LIBSSH2_DEBUG_CONNECTION, 1, [Output connection layer debugging info to stderr])
AC_DEFINE(LIBSSH2_DEBUG_SCP, 1, [Output scp subsystem debugging info to stderr])
AC_DEFINE(LIBSSH2_DEBUG_SFTP, 1, [Output sftp subsystem debugging info to stderr])
AC_DEFINE(LIBSSH2_DEBUG_ERRORS, 1, [Output failure events to stderr])
])
# Checks for header files. # Checks for header files.
# AC_HEADER_STDC # AC_HEADER_STDC
AC_CHECK_HEADERS([errno.h fcntl.h stdio.h stdlib.h unistd.h sys/uio.h sys/select.h]) AC_CHECK_HEADERS([errno.h fcntl.h stdio.h stdlib.h unistd.h sys/uio.h sys/select.h])

View File

@@ -67,8 +67,8 @@ typedef unsigned long long libssh2_uint64_t;
typedef long long libssh2_int64_t; typedef long long libssh2_int64_t;
#endif #endif
#define LIBSSH2_VERSION "0.8" #define LIBSSH2_VERSION "0.10"
#define LIBSSH2_APINO 200503221619 #define LIBSSH2_APINO 200503281457
/* Part of every banner, user specified or not */ /* Part of every banner, user specified or not */
#define LIBSSH2_SSH_BANNER "SSH-2.0-libssh2_" LIBSSH2_VERSION #define LIBSSH2_SSH_BANNER "SSH-2.0-libssh2_" LIBSSH2_VERSION
@@ -163,15 +163,18 @@ typedef struct _LIBSSH2_POLLFD {
/* Note: Win32 Doesn't actually have a poll() implementation, so some of these values are faked with select() data */ /* Note: Win32 Doesn't actually have a poll() implementation, so some of these values are faked with select() data */
/* Poll FD events/revents -- Match sys/poll.h where possible */ /* Poll FD events/revents -- Match sys/poll.h where possible */
#define LIBSSH2_POLLFD_POLLIN 0x0001 /* Data available to be read or connection available -- All */ #define LIBSSH2_POLLFD_POLLIN 0x0001 /* Data available to be read or connection available -- All */
#define LIBSSH2_POLLFD_POLLPRI 0x0002 /* Priority data available to be read -- Socket only */ #define LIBSSH2_POLLFD_POLLPRI 0x0002 /* Priority data available to be read -- Socket only */
#define LIBSSH2_POLLFD_POLLEXT 0x0002 /* Extended data available to be read -- Channel only */ #define LIBSSH2_POLLFD_POLLEXT 0x0002 /* Extended data available to be read -- Channel only */
#define LIBSSH2_POLLFD_POLLOUT 0x0004 /* Can may be written -- Socket/Channel */ #define LIBSSH2_POLLFD_POLLOUT 0x0004 /* Can may be written -- Socket/Channel */
/* revents only */ /* revents only */
#define LIBSSH2_POLLFD_POLLERR 0x0008 /* Error Condition -- Socket */ #define LIBSSH2_POLLFD_POLLERR 0x0008 /* Error Condition -- Socket */
#define LIBSSH2_POLLFD_POLLHUP 0x0010 /* HangUp/EOF -- Socket/Channel */ #define LIBSSH2_POLLFD_POLLHUP 0x0010 /* HangUp/EOF -- Socket */
#define LIBSSH2_POLLFD_POLLNVAL 0x0020 /* Invalid request -- Socket Only */ #define LIBSSH2_POLLFD_SESSION_CLOSED 0x0010 /* Session Disconnect */
#define LIBSSH2_POLLFD_POLLEX 0x0040 /* Exception Condition -- Socket/Win32 */ #define LIBSSH2_POLLFD_POLLNVAL 0x0020 /* Invalid request -- Socket Only */
#define LIBSSH2_POLLFD_POLLEX 0x0040 /* Exception Condition -- Socket/Win32 */
#define LIBSSH2_POLLFD_CHANNEL_CLOSED 0x0080 /* Channel Disconnect */
#define LIBSSH2_POLLFD_LISTENER_CLOSED 0x0080 /* Listener Disconnect */
/* Hash Types */ /* Hash Types */
#define LIBSSH2_HOSTKEY_HASH_MD5 1 #define LIBSSH2_HOSTKEY_HASH_MD5 1
@@ -276,6 +279,7 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeou
/* Channel API */ /* Channel API */
#define LIBSSH2_CHANNEL_WINDOW_DEFAULT 65536 #define LIBSSH2_CHANNEL_WINDOW_DEFAULT 65536
#define LIBSSH2_CHANNEL_PACKET_DEFAULT 16384 #define LIBSSH2_CHANNEL_PACKET_DEFAULT 16384
#define LIBSSH2_CHANNEL_MINADJUST 1024
/* Extended Data Handling */ /* Extended Data Handling */
#define LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL 0 #define LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL 0
@@ -315,10 +319,18 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
#define libssh2_channel_read(channel, buf, buflen) libssh2_channel_read_ex((channel), 0, (buf), (buflen)) #define libssh2_channel_read(channel, buf, buflen) libssh2_channel_read_ex((channel), 0, (buf), (buflen))
#define libssh2_channel_read_stderr(channel, buf, buflen) libssh2_channel_read_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen)) #define libssh2_channel_read_stderr(channel, buf, buflen) libssh2_channel_read_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen))
LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, unsigned long *read_avail, unsigned long *window_size_initial);
#define libssh2_channel_window_read(channel) libssh2_channel_window_read_ex((channel), NULL, NULL)
LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force);
LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen); LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen);
#define libssh2_channel_write(channel, buf, buflen) libssh2_channel_write_ex((channel), 0, (buf), (buflen)) #define libssh2_channel_write(channel, buf, buflen) libssh2_channel_write_ex((channel), 0, (buf), (buflen))
#define libssh2_channel_write_stderr(channel, buf, buflen) libssh2_channel_write_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen)) #define libssh2_channel_write_stderr(channel, buf, buflen) libssh2_channel_write_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen))
LIBSSH2_API unsigned long libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, unsigned long *window_size_initial);
#define libssh2_channel_window_write(channel) libssh2_channel_window_write_ex((channel), NULL)
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking); LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking);
LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode); LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode);
/* libssh2_channel_ignore_extended_data() is defined below for BC with version 0.1 /* libssh2_channel_ignore_extended_data() is defined below for BC with version 0.1

View File

@@ -54,6 +54,27 @@
/* Enable "none" cipher -- NOT RECOMMENDED */ /* Enable "none" cipher -- NOT RECOMMENDED */
#undef LIBSSH2_CRYPT_NONE #undef LIBSSH2_CRYPT_NONE
/* Output connection layer debugging info to stderr */
#undef LIBSSH2_DEBUG_CONNECTION
/* Output failure events to stderr */
#undef LIBSSH2_DEBUG_ERRORS
/* Output Key Exchange debugging info to stderr */
#undef LIBSSH2_DEBUG_KEX
/* Output scp subsystem debugging info to stderr */
#undef LIBSSH2_DEBUG_SCP
/* Output sftp subsystem debugging info to stderr */
#undef LIBSSH2_DEBUG_SFTP
/* Output transport layer debugging info to stderr */
#undef LIBSSH2_DEBUG_TRANSPORT
/* Output userauth layer debugging info to stderr */
#undef LIBSSH2_DEBUG_USERAUTH
/* Enable newer diffie-hellman-group-exchange-sha1 syntax */ /* Enable newer diffie-hellman-group-exchange-sha1 syntax */
#undef LIBSSH2_DH_GEX_NEW #undef LIBSSH2_DH_GEX_NEW

View File

@@ -113,6 +113,7 @@ struct _LIBSSH2_CHANNEL {
int blocking; int blocking;
libssh2_channel_data local, remote; libssh2_channel_data local, remote;
unsigned long adjust_queue; /* Amount of bytes to be refunded to receive window (but not yet sent) */
LIBSSH2_SESSION *session; LIBSSH2_SESSION *session;
@@ -317,6 +318,37 @@ struct _LIBSSH2_MAC_METHOD {
int (*dtor)(LIBSSH2_SESSION *session, void **abstract); int (*dtor)(LIBSSH2_SESSION *session, void **abstract);
}; };
#if defined(LIBSSH2_DEBUG_TRANSPORT) || defined(LIBSSH2_DEBUG_KEX) || defined(LIBSSH2_DEBUG_USERAUTH) || defined(LIBSSH2_DEBUG_CONNECTION) || defined(LIBSSH2_DEBUG_SCP) || defined(LIBSSH2_DEBUG_SFTP) || defined(LIBSSH2_DEBUG_ERRORS)
#define LIBSSH2_DEBUG_ENABLED
/* Internal debugging contexts -- Used with --enable-debug-* */
#define LIBSSH2_DBG_TRANS 1
#define LIBSSH2_DBG_KEX 2
#define LIBSSH2_DBG_AUTH 3
#define LIBSSH2_DBG_CONN 4
#define LIBSSH2_DBG_SCP 5
#define LIBSSH2_DBG_SFTP 6
#define LIBSSH2_DBG_ERROR 7
void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, ...);
#endif /* LIBSSH2_DEBUG_ENABLED */
#ifdef LIBSSH2_DEBUG_ERRORS
#define libssh2_error(session, errcode, errmsg, should_free) \
{ \
if (session->err_msg && session->err_should_free) { \
LIBSSH2_FREE(session, session->err_msg); \
} \
session->err_msg = errmsg; \
session->err_msglen = strlen(errmsg); \
session->err_should_free = should_free; \
session->err_code = errcode; \
_libssh2_debug(session, LIBSSH2_DBG_ERROR, "%d - %s", session->err_code, session->err_msg); \
}
#else /* ! LIBSSH2_DEBUG_ERRORS */
#define libssh2_error(session, errcode, errmsg, should_free) \ #define libssh2_error(session, errcode, errmsg, should_free) \
{ \ { \
if (session->err_msg && session->err_should_free) { \ if (session->err_msg && session->err_should_free) { \
@@ -328,6 +360,9 @@ struct _LIBSSH2_MAC_METHOD {
session->err_code = errcode; \ session->err_code = errcode; \
} }
#endif /* LIBSSH2_DEBUG_ENABLED */
#define LIBSSH2_SOCKET_UNKNOWN 1 #define LIBSSH2_SOCKET_UNKNOWN 1
#define LIBSSH2_SOCKET_CONNECTED 0 #define LIBSSH2_SOCKET_CONNECTED 0
#define LIBSSH2_SOCKET_DISCONNECTED -1 #define LIBSSH2_SOCKET_DISCONNECTED -1
@@ -401,9 +436,15 @@ int libssh2_packet_read(LIBSSH2_SESSION *session, int block);
int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket); int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket);
#define libssh2_packet_ask(session, packet_type, data, data_len, poll_socket) \ #define libssh2_packet_ask(session, packet_type, data, data_len, poll_socket) \
libssh2_packet_ask_ex((session), (packet_type), (data), (data_len), 0, NULL, 0, (poll_socket)) libssh2_packet_ask_ex((session), (packet_type), (data), (data_len), 0, NULL, 0, (poll_socket))
int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket);
#define libssh2_packet_askv(session, packet_types, data, data_len, poll_socket) \
libssh2_packet_askv_ex((session), (packet_types), (data), (data_len), 0, NULL, 0, (poll_socket))
int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len); int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len);
#define libssh2_packet_require(session, packet_type, data, data_len) \ #define libssh2_packet_require(session, packet_type, data, data_len) \
libssh2_packet_require_ex((session), (packet_type), (data), (data_len), 0, NULL, 0) libssh2_packet_require_ex((session), (packet_type), (data), (data_len), 0, NULL, 0)
int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len);
#define libssh2_packet_requirev(session, packet_types, data, data_len) \
libssh2_packet_requirev_ex((session), (packet_types), (data), (data_len), 0, NULL, 0)
int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len); int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len);
int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange); int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange);
unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session); unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session);

View File

@@ -64,7 +64,9 @@ unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session)
* Gets picked up by the new one.... Pretty unlikely all told... * Gets picked up by the new one.... Pretty unlikely all told...
*/ */
session->next_channel = id + 1; session->next_channel = id + 1;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Allocated new channel ID#%lu", id);
#endif
return id; return id;
} }
/* }}} */ /* }}} */
@@ -106,6 +108,7 @@ LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, char *channel_type, int channel_type_len, int window_size, int packet_size, LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, char *channel_type, int channel_type_len, int window_size, int packet_size,
char *message, int message_len) char *message, int message_len)
{ {
unsigned char reply_codes[3] = { SSH_MSG_CHANNEL_OPEN_CONFIRMATION, SSH_MSG_CHANNEL_OPEN_FAILURE, 0 };
LIBSSH2_CHANNEL *channel = NULL; LIBSSH2_CHANNEL *channel = NULL;
unsigned long local_channel = libssh2_channel_nextid(session); unsigned long local_channel = libssh2_channel_nextid(session);
unsigned char *s, *packet = NULL; unsigned char *s, *packet = NULL;
@@ -113,8 +116,10 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, c
window_size(4) + packet_size(4) */ window_size(4) + packet_size(4) */
unsigned char *data = NULL; unsigned char *data = NULL;
unsigned long data_len; unsigned long data_len;
int polls = 0;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Opening Channel - win %d pack %d", window_size, packet_size);
#endif
channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL));
if (!channel) { if (!channel) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for channel data", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for channel data", 0);
@@ -161,35 +166,30 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, c
goto channel_error; goto channel_error;
} }
while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) { if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, packet + 5 + channel_type_len, 4)) {
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &data, &data_len, 1, packet + 5 + channel_type_len, 4, 1) == 0) { goto channel_error;
/* YAY! You like me! */
break;
}
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_OPEN_FAILURE, &data, &data_len, 1, packet + 5 + channel_type_len, 4, 0) == 0) {
/* But! Dear! I thought we had something! */
/* TODO: provide reason code and description */
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Channel open failure", 0);
goto channel_error;
}
usleep(LIBSSH2_SOCKET_POLL_UDELAY);
if (polls++ > LIBSSH2_SOCKET_POLL_MAXLOOPS) {
/* Give up waiting */
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timed out waiting for response", 0);
goto channel_error;
}
} }
channel->remote.id = libssh2_ntohu32(data + 5); if (data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
channel->local.window_size = libssh2_ntohu32(data + 9); channel->remote.id = libssh2_ntohu32(data + 5);
channel->local.window_size_initial = libssh2_ntohu32(data + 9); channel->local.window_size = libssh2_ntohu32(data + 9);
channel->local.packet_size = libssh2_ntohu32(data + 13); channel->local.window_size_initial = libssh2_ntohu32(data + 9);
channel->local.packet_size = libssh2_ntohu32(data + 13);
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Connection Established - ID: %lu/%lu win: %lu/%lu pack: %lu/%lu",
channel->local.id, channel->remote.id,
channel->local.window_size, channel->remote.window_size,
channel->local.packet_size, channel->remote.packet_size);
#endif
LIBSSH2_FREE(session, packet);
LIBSSH2_FREE(session, data);
LIBSSH2_FREE(session, packet); return channel;
LIBSSH2_FREE(session, data); }
return channel; if (data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) {
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Channel open failure", 0);
}
channel_error: channel_error:
@@ -240,6 +240,10 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *se
unsigned long host_len = strlen(host), shost_len = strlen(shost); unsigned long host_len = strlen(host), shost_len = strlen(shost);
unsigned long message_len = host_len + shost_len + 16; /* host_len(4) + port(4) + shost_len(4) + sport(4) */ unsigned long message_len = host_len + shost_len + 16; /* host_len(4) + port(4) + shost_len(4) + sport(4) */
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting direct-tcpip session to from %s:%d to %s:%d", shost, sport, host, port);
#endif
s = message = LIBSSH2_ALLOC(session, message_len); s = message = LIBSSH2_ALLOC(session, message_len);
if (!message) { if (!message) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for direct-tcpip connection", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for direct-tcpip connection", 0);
@@ -265,11 +269,16 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *se
*/ */
LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, char *host, int port, int *bound_port, int queue_maxsize) LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, char *host, int port, int *bound_port, int queue_maxsize)
{ {
unsigned char *packet, *s; unsigned char *packet, *s, *data, reply_codes[3] = { SSH_MSG_REQUEST_SUCCESS, SSH_MSG_REQUEST_FAILURE, 0 };
unsigned long data_len;
unsigned long host_len = (host ? strlen(host) : (sizeof("0.0.0.0") - 1)); unsigned long host_len = (host ? strlen(host) : (sizeof("0.0.0.0") - 1));
unsigned long packet_len = host_len + (sizeof("tcpip-forward") - 1) + 14; unsigned long packet_len = host_len + (sizeof("tcpip-forward") - 1) + 14;
/* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */ /* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting tcpip-forward session for %s:%d", host, port);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0);
@@ -292,59 +301,61 @@ LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION
} }
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
unsigned char *data; return NULL;
unsigned long data_len; }
if (libssh2_packet_ask(session, SSH_MSG_REQUEST_SUCCESS, &data, &data_len, 1) == 0) { if (data[0] == SSH_MSG_REQUEST_SUCCESS) {
LIBSSH2_LISTENER *listener; LIBSSH2_LISTENER *listener;
listener = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_LISTENER));
if (!listener) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0);
LIBSSH2_FREE(session, data);
return NULL;
}
memset(listener, 0, sizeof(LIBSSH2_LISTENER));
listener->session = session;
listener->host = LIBSSH2_ALLOC(session, host_len + 1);
if (!listener->host) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0);
LIBSSH2_FREE(session, listener);
LIBSSH2_FREE(session, data);
return NULL;
}
memcpy(listener->host, host ? host : "0.0.0.0", host_len);
listener->host[host_len] = 0;
if (data_len >= 5 && !port) {
listener->port = libssh2_ntohu32(data + 1);
} else {
listener->port = port;
}
listener->queue_size = 0;
listener->queue_maxsize = queue_maxsize;
listener->next = session->listeners;
listener->prev = NULL;
if (session->listeners) {
session->listeners->prev = listener;
}
session->listeners = listener;
if (bound_port) {
*bound_port = listener->port;
}
listener = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_LISTENER));
if (!listener) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0);
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
return listener;
}
if (libssh2_packet_ask(session, SSH_MSG_REQUEST_FAILURE, &data, &data_len, 0) == 0) {
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED, "Unable to complete request for forward-listen", 0);
return NULL; return NULL;
} }
memset(listener, 0, sizeof(LIBSSH2_LISTENER));
listener->session = session;
listener->host = LIBSSH2_ALLOC(session, host_len + 1);
if (!listener->host) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0);
LIBSSH2_FREE(session, listener);
LIBSSH2_FREE(session, data);
return NULL;
}
memcpy(listener->host, host ? host : "0.0.0.0", host_len);
listener->host[host_len] = 0;
if (data_len >= 5 && !port) {
listener->port = libssh2_ntohu32(data + 1);
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Dynamic tcpip-forward port allocated: %d", listener->port);
#endif
} else {
listener->port = port;
}
listener->queue_size = 0;
listener->queue_maxsize = queue_maxsize;
listener->next = session->listeners;
listener->prev = NULL;
if (session->listeners) {
session->listeners->prev = listener;
}
session->listeners = listener;
if (bound_port) {
*bound_port = listener->port;
}
LIBSSH2_FREE(session, data);
return listener;
}
if (data[0] == SSH_MSG_REQUEST_FAILURE) {
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED, "Unable to complete request for forward-listen", 0);
return NULL;
} }
return NULL; return NULL;
@@ -364,6 +375,10 @@ LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
unsigned long packet_len = host_len + 14 + sizeof("cancel-tcpip-forward") - 1; unsigned long packet_len = host_len + 14 + sizeof("cancel-tcpip-forward") - 1;
/* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */ /* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Cancelling tcpip-forward session for %s:%d", listener->host, listener->port);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0);
@@ -451,10 +466,15 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_forward_accept(LIBSSH2_LISTENER *li
LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varname, int varname_len, char *value, int value_len) LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varname, int varname_len, char *value, int value_len)
{ {
LIBSSH2_SESSION *session = channel->session; LIBSSH2_SESSION *session = channel->session;
unsigned char *s, *packet; unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4];
unsigned long data_len;
unsigned long packet_len = varname_len + value_len + 21; /* packet_type(1) + channel_id(4) + request_len(4) + request(3)"env" + unsigned long packet_len = varname_len + value_len + 21; /* packet_type(1) + channel_id(4) + request_len(4) + request(3)"env" +
want_reply(1) + varname_len(4) + value_len(4) */ want_reply(1) + varname_len(4) + value_len(4) */
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Setting remote environment variable: %s=%s on channel %lu/%lu", varname, value, channel->local.id, channel->remote.id);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0);
@@ -481,26 +501,18 @@ LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varnam
} }
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
while (1) { libssh2_htonu32(local_channel, channel->local.id);
unsigned char *data; if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) {
unsigned long data_len; return -1;
unsigned char local_channel[4];
libssh2_htonu32(local_channel, channel->local.id);
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_SUCCESS, &data, &data_len, 1, local_channel, 4, 1) == 0) {
LIBSSH2_FREE(session, data);
return 0;
}
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_FAILURE, &data, &data_len, 1, local_channel, 4, 0) == 0) {
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-setenv", 0);
return -1;
}
} }
/* Never reached, just giving the compiler something to not complain about */ if (data[0] == SSH_MSG_CHANNEL_SUCCESS) {
LIBSSH2_FREE(session, data);
return 0;
}
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-setenv", 0);
return -1; return -1;
} }
/* }}} */ /* }}} */
@@ -514,10 +526,15 @@ LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, char *t
int width_px, int height_px) int width_px, int height_px)
{ {
LIBSSH2_SESSION *session = channel->session; LIBSSH2_SESSION *session = channel->session;
unsigned char *s, *packet; unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4];
unsigned long data_len;
unsigned long packet_len = term_len + modes_len + 41; /* packet_type(1) + channel(4) + pty_req_len(4) + "pty_req"(7) + want_reply(1) + unsigned long packet_len = term_len + modes_len + 41; /* packet_type(1) + channel(4) + pty_req_len(4) + "pty_req"(7) + want_reply(1) +
term_len(4) + width(4) + height(4) + width_px(4) + height_px(4) + modes_len(4) */ term_len(4) + width(4) + height(4) + width_px(4) + height_px(4) + modes_len(4) */
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Allocating tty on channel %lu/%lu", channel->local.id, channel->remote.id);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0);
@@ -553,26 +570,18 @@ LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, char *t
} }
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
while (1) { libssh2_htonu32(local_channel, channel->local.id);
unsigned char *data; if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) {
unsigned long data_len; return -1;
unsigned char local_channel[4];
libssh2_htonu32(local_channel, channel->local.id);
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_SUCCESS, &data, &data_len, 1, local_channel, 4, 1) == 0) {
LIBSSH2_FREE(session, data);
return 0;
}
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_FAILURE, &data, &data_len, 1, local_channel, 4, 1) == 0) {
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel request-pty", 0);
return -1;
}
} }
/* Never reached, just giving the compiler something to not complain about */ if (data[0] == SSH_MSG_CHANNEL_SUCCESS) {
LIBSSH2_FREE(session, data);
return 0;
}
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel request-pty", 0);
return -1; return -1;
} }
/* }}} */ /* }}} */
@@ -585,12 +594,20 @@ LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, char *t
LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection, char *auth_proto, char *auth_cookie, int screen_number) LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection, char *auth_proto, char *auth_cookie, int screen_number)
{ {
LIBSSH2_SESSION *session = channel->session; LIBSSH2_SESSION *session = channel->session;
unsigned char *s, *packet; unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4];
unsigned long data_len;
unsigned long proto_len = auth_proto ? strlen(auth_proto) : (sizeof("MIT-MAGIC-COOKIE-1") - 1); unsigned long proto_len = auth_proto ? strlen(auth_proto) : (sizeof("MIT-MAGIC-COOKIE-1") - 1);
unsigned long cookie_len = auth_cookie ? strlen(auth_cookie) : LIBSSH2_X11_RANDOM_COOKIE_LEN; unsigned long cookie_len = auth_cookie ? strlen(auth_cookie) : LIBSSH2_X11_RANDOM_COOKIE_LEN;
unsigned long packet_len = proto_len + cookie_len + 41; /* packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) + want_reply(1) + unsigned long packet_len = proto_len + cookie_len + 41; /* packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) + want_reply(1) +
single_cnx(4) + proto_len(4) + cookie_len(4) + screen_num(4) */ single_cnx(4) + proto_len(4) + cookie_len(4) + screen_num(4) */
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting x11-req for channel %lu/%lu: single=%d proto=%s cookie=%s screen=%d",
channel->local.id, channel->remote.id, single_connection,
auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1",
auth_cookie ? auth_cookie : "<random>", screen_number);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0);
@@ -632,26 +649,14 @@ LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_
} }
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
while (1) { libssh2_htonu32(local_channel, channel->local.id);
unsigned char *data; if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) {
unsigned long data_len; LIBSSH2_FREE(session, data);
unsigned char local_channel[4]; return 0;
libssh2_htonu32(local_channel, channel->local.id);
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_SUCCESS, &data, &data_len, 1, local_channel, 4, 1) == 0) {
LIBSSH2_FREE(session, data);
return 0;
}
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_FAILURE, &data, &data_len, 1, local_channel, 4, 1) == 0) {
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel x11-req", 0);
return -1;
}
} }
/* Never reached, just giving the compiler something to not complain about */ LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel x11-req", 0);
return -1; return -1;
} }
/* }}} */ /* }}} */
@@ -662,13 +667,18 @@ LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_
LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, char *request, int request_len, char *message, int message_len) LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, char *request, int request_len, char *message, int message_len)
{ {
LIBSSH2_SESSION *session = channel->session; LIBSSH2_SESSION *session = channel->session;
unsigned char *s, *packet; unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4];
unsigned long data_len;
unsigned long packet_len = request_len + 10; /* packet_type(1) + channel(4) + request_len(4) + want_reply(1) */ unsigned long packet_len = request_len + 10; /* packet_type(1) + channel(4) + request_len(4) + want_reply(1) */
if (message) { if (message) {
packet_len += message_len + 4; packet_len += message_len + 4;
} }
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "starting request(%s) on channel %lu/%lu, message=%s", request, channel->local.id, channel->remote.id, message);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for channel-process request", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for channel-process request", 0);
@@ -694,26 +704,18 @@ LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, char *
} }
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
while (1) { libssh2_htonu32(local_channel, channel->local.id);
unsigned char *data; if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) {
unsigned long data_len; return -1;
unsigned char local_channel[4];
libssh2_htonu32(local_channel, channel->local.id);
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_SUCCESS, &data, &data_len, 1, local_channel, 4, 1) == 0) {
LIBSSH2_FREE(session, data);
return 0;
}
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_FAILURE, &data, &data_len, 1, local_channel, 4, 0) == 0) {
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-process-startup", 0);
return -1;
}
} }
/* Never reached, just giving the compiler something to not complain about */ if (data[0] == SSH_MSG_CHANNEL_SUCCESS) {
LIBSSH2_FREE(session, data);
return 0;
}
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-process-startup", 0);
return -1; return -1;
} }
/* }}} */ /* }}} */
@@ -723,6 +725,9 @@ LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, char *
*/ */
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking) LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking)
{ {
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Setting blocking mode on channel %lu/%lu to %d", channel->local.id, channel->remote.id, blocking);
#endif
channel->blocking = blocking; channel->blocking = blocking;
} }
/* }}} */ /* }}} */
@@ -743,13 +748,19 @@ LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid)
if (((packet_type == SSH_MSG_CHANNEL_DATA) || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) && if (((packet_type == SSH_MSG_CHANNEL_DATA) || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) &&
(libssh2_ntohu32(packet->data + 1) == channel->local.id)) { (libssh2_ntohu32(packet->data + 1) == channel->local.id)) {
/* It's our channel at least */ /* It's our channel at least */
unsigned long packet_stream_id = (packet_type == SSH_MSG_CHANNEL_DATA) ? 0 : libssh2_ntohu32(packet->data + 5);
if ((streamid == LIBSSH2_CHANNEL_FLUSH_ALL) || if ((streamid == LIBSSH2_CHANNEL_FLUSH_ALL) ||
((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA) && ((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) || (streamid = libssh2_ntohu32(packet->data + 5)))) || ((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA) && ((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) || (streamid == packet_stream_id))) ||
((packet_type == SSH_MSG_CHANNEL_DATA) && (streamid == 0))) { ((packet_type == SSH_MSG_CHANNEL_DATA) && (streamid == 0))) {
int bytes_to_flush = packet->data_len - packet->data_head;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Flushing %d bytes of data from stream %lu on channel %lu/%lu", bytes_to_flush,
packet_stream_id, channel->local.id, channel->remote.id);
#endif
/* It's one of the streams we wanted to flush */ /* It's one of the streams we wanted to flush */
refund_bytes += packet->data_len - 13; refund_bytes += packet->data_len - 13;
flush_bytes += packet->data_len - packet->data_head; flush_bytes += bytes_to_flush;
LIBSSH2_FREE(channel->session, packet->data); LIBSSH2_FREE(channel->session, packet->data);
if (packet->prev) { if (packet->prev) {
@@ -768,26 +779,60 @@ LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid)
packet = next; packet = next;
} }
if (refund_bytes && channel->remote.window_size_initial) { if (refund_bytes) {
unsigned char adjust[9]; /* packet_type(1) + channel(4) + adjustment(4) */ libssh2_channel_receive_window_adjust(channel, refund_bytes, 0);
/* Adjust the window based on the block we just freed */
adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST;
libssh2_htonu32(adjust + 1, channel->remote.id);
libssh2_htonu32(adjust + 5, refund_bytes);
if (libssh2_packet_write(channel->session, adjust, 9)) {
libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet", 0);
return -1;
} else {
channel->remote.window_size += refund_bytes;
}
} }
return flush_bytes; return flush_bytes;
} }
/* }}} */ /* }}} */
/* {{{ libssh2_channel_receive_window_adjust
* Adjust the receive window for a channel by adjustment bytes
* If the amount to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0
* The adjustment amount will be queued for a later packet
*
* Returns the new size of the receive window (as understood by remote end)
*/
LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force)
{
unsigned char adjust[9]; /* packet_type(1) + channel(4) + adjustment(4) */
if (!force && (adjustment + channel->adjust_queue < LIBSSH2_CHANNEL_MINADJUST)) {
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Queing %lu bytes for receive window adjustment for channel %lu/%lu", adjustment, channel->local.id, channel->remote.id);
#endif
channel->adjust_queue += adjustment;
return channel->remote.window_size;
}
if (!adjustment && !channel->adjust_queue) {
return channel->remote.window_size;
}
adjustment += channel->adjust_queue;
channel->adjust_queue = 0;
/* Adjust the window based on the block we just freed */
adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST;
libssh2_htonu32(adjust + 1, channel->remote.id);
libssh2_htonu32(adjust + 5, adjustment);
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Adjusting window %lu bytes for data flushed from channel %lu/%lu", adjustment, channel->local.id, channel->remote.id);
#endif
if (libssh2_packet_write(channel->session, adjust, 9)) {
libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet, deferring", 0);
channel->adjust_queue = adjustment;
} else {
channel->remote.window_size += adjustment;
}
return channel->remote.window_size;
}
/* }}} */
/* {{{ libssh2_channel_handle_extended_data /* {{{ libssh2_channel_handle_extended_data
* How should extended data look to the calling app? * How should extended data look to the calling app?
* Keep it in separate channels[_read() _read_stdder()]? (NORMAL) * Keep it in separate channels[_read() _read_stdder()]? (NORMAL)
@@ -796,6 +841,9 @@ LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid)
*/ */
LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode) LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode)
{ {
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Setting channel %lu/%lu handle_extended_data mode to %d", channel->local.id, channel->remote.id, ignore_mode);
#endif
channel->remote.extended_data_ignore_mode = ignore_mode; channel->remote.extended_data_ignore_mode = ignore_mode;
if (ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) { if (ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) {
@@ -812,6 +860,9 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
LIBSSH2_SESSION *session = channel->session; LIBSSH2_SESSION *session = channel->session;
int bytes_read = 0, blocking_read = 0; int bytes_read = 0, blocking_read = 0;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Attempting to read %d bytes from channel %lu/%lu stream #%d", (int)buflen, channel->local.id, channel->remote.id, stream_id);
#endif
do { do {
LIBSSH2_PACKET *packet; LIBSSH2_PACKET *packet;
@@ -838,13 +889,14 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
unlink_packet = 1; unlink_packet = 1;
} }
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Reading %d of buffered data from %lu/%lu/%d", want, channel->local.id, channel->remote.id, stream_id);
#endif
memcpy(buf + bytes_read, packet->data + packet->data_head, want); memcpy(buf + bytes_read, packet->data + packet->data_head, want);
packet->data_head += want; packet->data_head += want;
bytes_read += want; bytes_read += want;
if (unlink_packet) { if (unlink_packet) {
unsigned char adjust[9]; /* packet_type(1) + channel(4) + adjustment(4) */
if (packet->prev) { if (packet->prev) {
packet->prev->next = packet->next; packet->prev->next = packet->next;
} else { } else {
@@ -857,18 +909,10 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
} }
LIBSSH2_FREE(session, packet->data); LIBSSH2_FREE(session, packet->data);
/* Adjust the window based on the block we just freed */ #ifdef LIBSSH2_DEBUG_CONNECTION
adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST; _libssh2_debug(session, LIBSSH2_DBG_CONN, "Unlinking empty packet buffer from channel %lu/%lu", channel->local.id, channel->remote.id);
libssh2_htonu32(adjust + 1, channel->remote.id); #endif
libssh2_htonu32(adjust + 5, packet->data_len - (stream_id ? 13 : 9)); libssh2_channel_receive_window_adjust(channel, packet->data_len - (stream_id ? 13 : 9), 0);
if (libssh2_packet_write(session, adjust, 9)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet", 0);
} else {
/* Don't forget to acknowledge the adjust on this end */
channel->remote.window_size += (packet->data_len - (stream_id ? 13 : 9));
}
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
} }
} }
@@ -891,9 +935,12 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen) LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen)
{ {
LIBSSH2_SESSION *session = channel->session; LIBSSH2_SESSION *session = channel->session;
unsigned char *packet, *s; unsigned char *packet;
unsigned long packet_len; unsigned long packet_len, bufwrote = 0;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Writing %d bytes on channel %lu/%lu, stream #%d", (int)buflen, channel->local.id, channel->remote.id, stream_id);
#endif
if (channel->local.close) { if (channel->local.close) {
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "We've already closed this channel", 0); libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "We've already closed this channel", 0);
return -1; return -1;
@@ -903,48 +950,78 @@ LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_EOF_SENT, "EOF has already been sight, data might be ignored", 0); libssh2_error(session, LIBSSH2_ERROR_CHANNEL_EOF_SENT, "EOF has already been sight, data might be ignored", 0);
} }
if (channel->blocking && (channel->local.window_size <= 0)) { if (!channel->blocking && (channel->local.window_size <= 0)) {
/* twiddle our thumbs until there's window space available */ /* Can't write anything */
if (libssh2_packet_read(session, 1) < 0) { return 0;
/* Error occured, disconnect? */
return -1;
}
} }
packet_len = buflen + (stream_id ? 13 : 9); /* packet_type(1) + channelno(4) [ + streamid(4) ] + buflen(4) */ packet_len = buflen + (stream_id ? 13 : 9); /* packet_type(1) + channelno(4) [ + streamid(4) ] + buflen(4) */
s = packet = LIBSSH2_ALLOC(session, packet_len); packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocte space for data transmission packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocte space for data transmission packet", 0);
return -1; return -1;
} }
*(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA; while (buflen > 0) {
libssh2_htonu32(s, channel->remote.id); s += 4; size_t bufwrite = buflen;
if (stream_id) { unsigned char *s = packet;
libssh2_htonu32(s, stream_id); s += 4;
}
/* Don't exceed the remote end's limits */ *(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA;
/* REMEMBER local means local as the SOURCE of the data */ libssh2_htonu32(s, channel->remote.id); s += 4;
if (buflen > channel->local.window_size) { if (stream_id) {
buflen = channel->local.window_size; libssh2_htonu32(s, stream_id); s += 4;
} }
if (buflen > channel->local.packet_size) {
buflen = channel->local.packet_size;
}
libssh2_htonu32(s, buflen); s += 4;
memcpy(s, buf, buflen); s += buflen;
if (libssh2_packet_write(session, packet, s - packet)) { /* twiddle our thumbs until there's window space available */
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel data", 0); while (channel->local.window_size <= 0) {
return -1; /* Don't worry -- This is never hit unless it's a blocking channel anyway */
if (libssh2_packet_read(session, 1) < 0) {
/* Error occured, disconnect? */
return -1;
}
}
/* Don't exceed the remote end's limits */
/* REMEMBER local means local as the SOURCE of the data */
if (bufwrite > channel->local.window_size) {
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Splitting write block due to %lu byte window_size on %lu/%lu/%d", channel->local.window_size, channel->local.id, channel->remote.id, stream_id);
#endif
bufwrite = channel->local.window_size;
}
if (bufwrite > channel->local.packet_size) {
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Splitting write block due to %lu byte packet_size on %lu/%lu/%d", channel->local.packet_size, channel->local.id, channel->remote.id, stream_id);
#endif
bufwrite = channel->local.packet_size;
}
libssh2_htonu32(s, bufwrite); s += 4;
memcpy(s, buf, bufwrite); s += bufwrite;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending %d bytes on channel %lu/%lu, stream_id=%d", (int)bufwrite, channel->local.id, channel->remote.id, stream_id);
#endif
if (libssh2_packet_write(session, packet, s - packet)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel data", 0);
LIBSSH2_FREE(session, packet);
return -1;
}
/* Shrink local window size */
channel->local.window_size -= bufwrite;
/* Adjust buf for next iteration */
buflen -= bufwrite;
buf += bufwrite;
bufwrote += bufwrite;
if (!channel->blocking) {
break;
}
} }
/* Shrink local window size */
channel->local.window_size -= buflen;
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
return buflen; return bufwrote;
} }
/* }}} */ /* }}} */
@@ -956,6 +1033,9 @@ LIBSSH2_API int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel)
LIBSSH2_SESSION *session = channel->session; LIBSSH2_SESSION *session = channel->session;
unsigned char packet[5]; /* packet_type(1) + channelno(4) */ unsigned char packet[5]; /* packet_type(1) + channelno(4) */
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending EOF on channel %lu/%lu",channel->local.id, channel->remote.id);
#endif
packet[0] = SSH_MSG_CHANNEL_EOF; packet[0] = SSH_MSG_CHANNEL_EOF;
libssh2_htonu32(packet + 1, channel->remote.id); libssh2_htonu32(packet + 1, channel->remote.id);
if (libssh2_packet_write(session, packet, 5)) { if (libssh2_packet_write(session, packet, 5)) {
@@ -1002,6 +1082,10 @@ LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel)
return 0; return 0;
} }
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Closing channel %lu/%lu", channel->local.id, channel->remote.id);
#endif
if (channel->close_cb) { if (channel->close_cb) {
LIBSSH2_CHANNEL_CLOSE(session, channel); LIBSSH2_CHANNEL_CLOSE(session, channel);
} }
@@ -1029,6 +1113,9 @@ LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel)
unsigned char channel_id[4], *data; unsigned char channel_id[4], *data;
unsigned long data_len; unsigned long data_len;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Freeing channel %lu/%lu resources", channel->local.id, channel->remote.id);
#endif
/* Allow channel freeing even when the socket has lost its connection */ /* Allow channel freeing even when the socket has lost its connection */
if (!channel->local.close && (session->socket_state == LIBSSH2_SOCKET_CONNECTED) && if (!channel->local.close && (session->socket_state == LIBSSH2_SOCKET_CONNECTED) &&
libssh2_channel_close(channel)) { libssh2_channel_close(channel)) {
@@ -1069,3 +1156,53 @@ LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel)
return 0; return 0;
} }
/* }}} */ /* }}} */
/* {{{ libssh2_channel_window_read_ex
* Check the status of the read window
* Returns the number of bytes which the remote end may send without overflowing the window limit
* read_avail (if passed) will be populated with the number of bytes actually available to be read
* window_size_initial (if passed) will be populated with the window_size_initial as defined by the channel_open request
*/
LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, unsigned long *read_avail, unsigned long *window_size_initial)
{
if (window_size_initial) {
*window_size_initial = channel->remote.window_size_initial;
}
if (read_avail) {
unsigned long bytes_queued = 0;
LIBSSH2_PACKET *packet = channel->session->packets.head;
while (packet) {
unsigned char packet_type = packet->data[0];
if (((packet_type == SSH_MSG_CHANNEL_DATA) || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) &&
(libssh2_ntohu32(packet->data + 1) == channel->local.id)) {
bytes_queued += packet->data_len - packet->data_head;
}
packet = packet->next;
}
*read_avail = bytes_queued;
}
return channel->remote.window_size;
}
/* }}} */
/* {{{ libssh2_channel_window_write_ex
* Check the status of the write window
* Returns the number of bytes which may be safely writen on the channel without blocking
* window_size_initial (if passed) will be populated with the size of the initial window as defined by the channel_open request
*/
LIBSSH2_API unsigned long libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, unsigned long *window_size_initial)
{
if (window_size_initial) {
/* For locally initiated channels this is very often 0, so it's not *that* useful as information goes */
*window_size_initial = channel->local.window_size_initial;
}
return channel->local.window_size;
}
/* }}} */

101
src/kex.c
View File

@@ -109,6 +109,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
BN_bn2bin(e, e_packet + 6); BN_bn2bin(e, e_packet + 6);
} }
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending KEX packet %d", (int)packet_type_init);
#endif
if (libssh2_packet_write(session, e_packet, e_packet_len)) { if (libssh2_packet_write(session, e_packet, e_packet_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEX init message", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEX init message", 0);
ret = -11; ret = -11;
@@ -143,7 +146,18 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
MD5_Update(&fingerprint_ctx, session->server_hostkey, session->server_hostkey_len); MD5_Update(&fingerprint_ctx, session->server_hostkey, session->server_hostkey_len);
MD5_Final(session->server_hostkey_md5, &fingerprint_ctx); MD5_Final(session->server_hostkey_md5, &fingerprint_ctx);
} }
#endif #ifdef LIBSSH2_DEBUG_KEX
{
char fingerprint[50], *fprint = fingerprint;
int i;
for(i = 0; i < 16; i++, fprint += 3) {
snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]);
}
*(--fprint) = '\0';
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Server's MD5 Fingerprint: %s", fingerprint);
}
#endif /* LIBSSH2_DEBUG_KEX */
#endif /* ! OPENSSL_NO_MD5 */
#ifndef OPENSSL_NO_SHA #ifndef OPENSSL_NO_SHA
{ {
@@ -153,7 +167,18 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
SHA1_Update(&fingerprint_ctx, session->server_hostkey, session->server_hostkey_len); SHA1_Update(&fingerprint_ctx, session->server_hostkey, session->server_hostkey_len);
SHA1_Final(session->server_hostkey_sha1, &fingerprint_ctx); SHA1_Final(session->server_hostkey_sha1, &fingerprint_ctx);
} }
#endif #ifdef LIBSSH2_DEBUG_KEX
{
char fingerprint[64], *fprint = fingerprint;
int i;
for(i = 0; i < 20; i++, fprint += 3) {
snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]);
}
*(--fprint) = '\0';
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Server's SHA1 Fingerprint: %s", fingerprint);
}
#endif /* LIBSSH2_DEBUG_KEX */
#endif /* ! OPENSSL_NO_SHA */
if (session->hostkey->init(session, session->server_hostkey, session->server_hostkey_len, &session->server_hostkey_abstract)) { if (session->hostkey->init(session, session->server_hostkey, session->server_hostkey_len, &session->server_hostkey_abstract)) {
libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, "Unable to initialize hostkey importer", 0); libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, "Unable to initialize hostkey importer", 0);
@@ -161,7 +186,6 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
goto clean_exit; goto clean_exit;
} }
f_value_len = libssh2_ntohu32(s); s += 4; f_value_len = libssh2_ntohu32(s); s += 4;
f_value = s; s += f_value_len; f_value = s; s += f_value_len;
BN_bin2bn(f_value, f_value_len, f); BN_bin2bn(f_value, f_value_len, f);
@@ -250,6 +274,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
goto clean_exit; goto clean_exit;
} }
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending NEWKEYS message");
#endif
c = SSH_MSG_NEWKEYS; c = SSH_MSG_NEWKEYS;
if (libssh2_packet_write(session, &c, 1)) { if (libssh2_packet_write(session, &c, 1)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send NEWKEYS message", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send NEWKEYS message", 0);
@@ -264,6 +291,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
} }
/* The first key exchange has been performed, switch to active crypt/comp/mac mode */ /* The first key exchange has been performed, switch to active crypt/comp/mac mode */
session->state |= LIBSSH2_STATE_NEWKEYS; session->state |= LIBSSH2_STATE_NEWKEYS;
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Received NEWKEYS message");
#endif
/* This will actually end up being just packet_type(1) for this packet type anyway */ /* This will actually end up being just packet_type(1) for this packet type anyway */
LIBSSH2_FREE(session, tmp); LIBSSH2_FREE(session, tmp);
@@ -276,6 +306,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
} }
memcpy(session->session_id, h_sig_comp, SHA_DIGEST_LENGTH); memcpy(session->session_id, h_sig_comp, SHA_DIGEST_LENGTH);
session->session_id_len = SHA_DIGEST_LENGTH; session->session_id_len = SHA_DIGEST_LENGTH;
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "session_id calculated");
#endif
} }
/* Calculate IV/Secret/Key for each direction */ /* Calculate IV/Secret/Key for each direction */
@@ -327,6 +360,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
LIBSSH2_FREE(session, secret); LIBSSH2_FREE(session, secret);
} }
} }
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Client to Server IV and Key calculated");
#endif
if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) {
if (session->remote.crypt_abstract) { if (session->remote.crypt_abstract) {
@@ -376,6 +412,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
LIBSSH2_FREE(session, secret); LIBSSH2_FREE(session, secret);
} }
} }
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client IV and Key calculated");
#endif
if (session->local.mac->dtor) { if (session->local.mac->dtor) {
session->local.mac->dtor(session, &session->local.mac_abstract); session->local.mac->dtor(session, &session->local.mac_abstract);
@@ -393,6 +432,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
LIBSSH2_FREE(session, key); LIBSSH2_FREE(session, key);
} }
} }
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Client to Server HMAC Key calculated");
#endif
if (session->remote.mac->dtor) { if (session->remote.mac->dtor) {
session->remote.mac->dtor(session, &session->remote.mac_abstract); session->remote.mac->dtor(session, &session->remote.mac_abstract);
@@ -410,6 +452,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
LIBSSH2_FREE(session, key); LIBSSH2_FREE(session, key);
} }
} }
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client HMAC Key calculated");
#endif
clean_exit: clean_exit:
BN_clear_free(x); BN_clear_free(x);
@@ -470,6 +515,9 @@ static int libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SE
BN_set_word(g, 2); BN_set_word(g, 2);
BN_bin2bn(p_value, 128, p); BN_bin2bn(p_value, 128, p);
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group1 Key Exchange");
#endif
ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0); ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0);
BN_clear_free(p); BN_clear_free(p);
@@ -526,6 +574,9 @@ static int libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_S
BN_set_word(g, 2); BN_set_word(g, 2);
BN_bin2bn(p_value, 256, p); BN_bin2bn(p_value, 256, p);
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group14 Key Exchange");
#endif
ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0); ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0);
BN_clear_free(p); BN_clear_free(p);
@@ -554,10 +605,16 @@ static int libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange(LI
libssh2_htonu32(request + 5, LIBSSH2_DH_GEX_OPTGROUP); libssh2_htonu32(request + 5, LIBSSH2_DH_GEX_OPTGROUP);
libssh2_htonu32(request + 9, LIBSSH2_DH_GEX_MAXGROUP); libssh2_htonu32(request + 9, LIBSSH2_DH_GEX_MAXGROUP);
request_len = 13; request_len = 13;
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (New Method)");
#endif
#else #else
request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD; request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD;
libssh2_htonu32(request + 1, LIBSSH2_DH_GEX_OPTGROUP); libssh2_htonu32(request + 1, LIBSSH2_DH_GEX_OPTGROUP);
request_len = 5; request_len = 5;
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (Old Method)");
#endif
#endif #endif
if (libssh2_packet_write(session, request, request_len)) { if (libssh2_packet_write(session, request, request_len)) {
@@ -746,6 +803,23 @@ static int libssh2_kexinit(LIBSSH2_SESSION *session)
*(s++) = 0; *(s++) = 0;
*(s++) = 0; *(s++) = 0;
#ifdef LIBSSH2_DEBUG_KEX
{
/* Funnily enough, they'll all "appear" to be '\0' terminated */
char *p = data + 21; /* type(1) + cookie(16) + len(4) */
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent KEX: %s", p); p += kex_len + 4;
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent HOSTKEY: %s", p); p += hostkey_len + 4;
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_CS: %s", p); p += crypt_cs_len + 4;
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_SC: %s", p); p += crypt_sc_len + 4;
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_CS: %s", p); p += mac_cs_len + 4;
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_SC: %s", p); p += mac_sc_len + 4;
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_CS: %s", p); p += comp_cs_len + 4;
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_SC: %s", p); p += comp_sc_len + 4;
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_CS: %s", p); p += lang_cs_len + 4;
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_SC: %s", p); p += lang_sc_len + 4;
}
#endif /* LIBSSH2_DEBUG_KEX */
if (libssh2_packet_write(session, data, data_len)) { if (libssh2_packet_write(session, data, data_len)) {
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEXINIT packet to remote host", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEXINIT packet to remote host", 0);
@@ -854,7 +928,7 @@ static int libssh2_kex_agree_hostkey(LIBSSH2_SESSION *session, unsigned long kex
return -1; return -1;
} }
while ((*hostkeyp)->name) { while (hostkeyp && (*hostkeyp)->name) {
s = libssh2_kex_agree_instr(hostkey, hostkey_len, (*hostkeyp)->name, strlen((*hostkeyp)->name)); s = libssh2_kex_agree_instr(hostkey, hostkey_len, (*hostkeyp)->name, strlen((*hostkeyp)->name));
if (s) { if (s) {
/* So far so good, but does it suit our purposes? (Encrypting vs Signing) */ /* So far so good, but does it suit our purposes? (Encrypting vs Signing) */
@@ -962,7 +1036,7 @@ static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session, libssh2_endpoint_da
return -1; return -1;
} }
while ((*cryptp)->name) { while (*cryptp && (*cryptp)->name) {
s = libssh2_kex_agree_instr(crypt, crypt_len, (*cryptp)->name, strlen((*cryptp)->name)); s = libssh2_kex_agree_instr(crypt, crypt_len, (*cryptp)->name, strlen((*cryptp)->name));
if (s) { if (s) {
endpoint->crypt = *cryptp; endpoint->crypt = *cryptp;
@@ -1007,7 +1081,7 @@ static int libssh2_kex_agree_mac(LIBSSH2_SESSION *session, libssh2_endpoint_data
return -1; return -1;
} }
while ((*macp)->name) { while (*macp && (*macp)->name) {
s = libssh2_kex_agree_instr(mac, mac_len, (*macp)->name, strlen((*macp)->name)); s = libssh2_kex_agree_instr(mac, mac_len, (*macp)->name, strlen((*macp)->name));
if (s) { if (s) {
endpoint->mac = *macp; endpoint->mac = *macp;
@@ -1052,7 +1126,7 @@ static int libssh2_kex_agree_comp(LIBSSH2_SESSION *session, libssh2_endpoint_dat
return -1; return -1;
} }
while ((*compp)->name) { while (*compp && (*compp)->name) {
s = libssh2_kex_agree_instr(comp, comp_len, (*compp)->name, strlen((*compp)->name)); s = libssh2_kex_agree_instr(comp, comp_len, (*compp)->name, strlen((*compp)->name));
if (s) { if (s) {
endpoint->comp = *compp; endpoint->comp = *compp;
@@ -1120,6 +1194,19 @@ static int libssh2_kex_agree_methods(LIBSSH2_SESSION *session, unsigned char *da
return -1; return -1;
} }
#ifdef LIBSSH2_DEBUG_KEX
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on KEX method: %s", session->kex->name);
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on HOSTKEY method: %s", session->hostkey->name);
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on CRYPT_CS method: %s", session->local.crypt->name);
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on CRYPT_SC method: %s", session->remote.crypt->name);
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on MAC_CS method: %s", session->local.mac->name);
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on MAC_SC method: %s", session->remote.mac->name);
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on COMP_CS method: %s", session->local.comp->name);
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on COMP_SC method: %s", session->remote.comp->name);
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on LANG_CS method:"); /* None yet */
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on LANG_SC method:"); /* None yet */
#endif
/* Initialize compression layer */ /* Initialize compression layer */
if (session->local.comp && session->local.comp->init && if (session->local.comp && session->local.comp->init &&
session->local.comp->init(session, 1, &session->local.comp_abstract)) { session->local.comp->init(session, 1, &session->local.comp_abstract)) {

View File

@@ -169,3 +169,29 @@ LIBSSH2_API int libssh2_base64_decode(LIBSSH2_SESSION *session, char **data, int
} }
/* }}} */ /* }}} */
#ifdef LIBSSH2_DEBUG_ENABLED
/* {{{ _libssh2_debug
* Internal debug logging facility
* Just writes to stderr until a good reason comes up to support anything else
*/
void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, ...)
{
char buffer[1536];
int len;
va_list vargs;
char *contexts[8] = { "Unknown", "Transport", "Key Exhange", "Userauth", "Connection", "scp", "SFTP", "Failure Event" };
if (context < 1 || context > 6) {
context = 0;
}
len = snprintf(buffer, 1535, "[libssh2] %s: ", contexts[context]);
va_start(vargs, format);
len += vsnprintf(buffer + len, 1535 - len, format, vargs);
buffer[len] = '\n';
va_end(vargs);
write(2, buffer, len + 1);
}
/* }}} */
#endif

View File

@@ -49,6 +49,19 @@
#include <sys/uio.h> #include <sys/uio.h>
#endif #endif
#ifdef HAVE_POLL
# include <sys/poll.h>
#else
# ifdef HAVE_SELECT
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
# else
# include <sys/time.h>
# include <sys/types.h>
# endif
# endif
#endif
/* {{{ libssh2_packet_queue_listener /* {{{ libssh2_packet_queue_listener
* Queue a connection request for a listener * Queue a connection request for a listener
*/ */
@@ -78,6 +91,9 @@ inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char
shost = s; s += shost_len; shost = s; s += shost_len;
sport = libssh2_ntohu32(s); s += 4; sport = libssh2_ntohu32(s); s += 4;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Remote received connection from %s:%ld to %s:%ld", shost, sport, host, port);
#endif
while (l) { while (l) {
if ((l->port == port) && if ((l->port == port) &&
(strlen(l->host) == host_len) && (strlen(l->host) == host_len) &&
@@ -89,6 +105,9 @@ inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char
(l->queue_maxsize <= l->queue_size)) { (l->queue_maxsize <= l->queue_size)) {
/* Queue is full */ /* Queue is full */
failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Listener queue full, ignoring");
#endif
break; break;
} }
@@ -121,6 +140,13 @@ inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char
channel->local.window_size = initial_window_size; channel->local.window_size = initial_window_size;
channel->local.packet_size = packet_size; channel->local.packet_size = packet_size;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Connection queued: channel %lu/%lu win %lu/%lu packet %lu/%lu",
channel->local.id, channel->remote.id,
channel->local.window_size, channel->remote.window_size,
channel->local.packet_size, channel->remote.packet_size);
#endif
p = packet; p = packet;
*(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
libssh2_htonu32(p, channel->remote.id); p += 4; libssh2_htonu32(p, channel->remote.id); p += 4;
@@ -195,6 +221,9 @@ inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data
shost = s; s += shost_len; shost = s; s += shost_len;
sport = libssh2_ntohu32(s); s += 4; sport = libssh2_ntohu32(s); s += 4;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection Received from %s:%ld on channel %lu", shost, sport, sender_channel);
#endif
if (session->x11) { if (session->x11) {
channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL));
if (!channel) { if (!channel) {
@@ -225,6 +254,12 @@ inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data
channel->local.window_size = initial_window_size; channel->local.window_size = initial_window_size;
channel->local.packet_size = packet_size; channel->local.packet_size = packet_size;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection established: channel %lu/%lu win %lu/%lu packet %lu/%lu",
channel->local.id, channel->remote.id,
channel->local.window_size, channel->remote.window_size,
channel->local.packet_size, channel->remote.packet_size);
#endif
p = packet; p = packet;
*(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
libssh2_htonu32(p, channel->remote.id); p += 4; libssh2_htonu32(p, channel->remote.id); p += 4;
@@ -281,6 +316,9 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
LIBSSH2_PACKET *packet; LIBSSH2_PACKET *packet;
unsigned long data_head = 0; unsigned long data_head = 0;
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Packet type %d received, length=%d", (int)data[0], (int)datalen);
#endif
if (macstate == LIBSSH2_MAC_INVALID) { if (macstate == LIBSSH2_MAC_INVALID) {
if (session->macerror) { if (session->macerror) {
if (LIBSSH2_MACERROR(session, data, datalen) == 0) { if (LIBSSH2_MACERROR(session, data, datalen) == 0) {
@@ -329,6 +367,9 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
if (session->ssh_msg_disconnect) { if (session->ssh_msg_disconnect) {
LIBSSH2_DISCONNECT(session, reason, message, message_len, language, language_len); LIBSSH2_DISCONNECT(session, reason, message, message_len, language, language_len);
} }
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnect(%d): %s(%s)", reason, message, language);
#endif
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
return -1; return -1;
@@ -369,6 +410,10 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
if (session->ssh_msg_debug) { if (session->ssh_msg_debug) {
LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len); LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len);
} }
#ifdef LIBSSH2_DEBUG_TRANSPORT
/* _libssh2_debug will actually truncate this for us so that it's not an inordinate about of data */
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Debug Packet: %s", message);
#endif
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
return 0; return 0;
} }
@@ -385,22 +430,27 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
return 0; return 0;
} }
#ifdef LIBSSH2_DEBUG_CONNECTION
{
unsigned long stream_id = 0;
if (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) {
stream_id = libssh2_ntohu32(data + 5);
}
_libssh2_debug(session, LIBSSH2_DBG_CONN, "%d bytes received for channel %lu/%lu stream #%lu", (int)(datalen - data_head), channel->local.id, channel->remote.id, stream_id);
}
#endif
if ((channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) && (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) { if ((channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) && (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) {
/* Pretend we didn't receive this */ /* Pretend we didn't receive this */
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
if (channel->remote.window_size_initial) { #ifdef LIBSSH2_DEBUG_CONNECTION
/* Adjust the window based on the block we just freed */ _libssh2_debug(session, LIBSSH2_DBG_CONN, "Ignoring extended data and refunding %d bytes", (int)(datalen - 13));
unsigned char adjust[9]; #endif
/* Adjust the window based on the block we just freed */
libssh2_channel_receive_window_adjust(channel, datalen - 13, 0);
adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST;
libssh2_htonu32(adjust + 1, channel->remote.id);
libssh2_htonu32(adjust + 5, datalen - 13);
if (libssh2_packet_write(channel->session, adjust, 9)) {
libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet", 0);
}
}
return 0; return 0;
} }
@@ -410,7 +460,7 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "Packet contains more data than we offered to receive, truncating", 0); libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "Packet contains more data than we offered to receive, truncating", 0);
datalen = channel->remote.packet_size + data_head; datalen = channel->remote.packet_size + data_head;
} }
if (channel->remote.window_size_initial && (channel->remote.window_size <= 0)) { if (channel->remote.window_size <= 0) {
/* Spec says we MAY ignore bytes sent beyond window_size */ /* Spec says we MAY ignore bytes sent beyond window_size */
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "The current receive window is full, data ignored", 0); libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "The current receive window is full, data ignored", 0);
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
@@ -419,7 +469,7 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
/* Reset EOF status */ /* Reset EOF status */
channel->remote.eof = 0; channel->remote.eof = 0;
if (channel->remote.window_size_initial && ((datalen - data_head) > channel->remote.window_size)) { if ((datalen - data_head) > channel->remote.window_size) {
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "Remote sent more data than current window allows, truncating", 0); libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "Remote sent more data than current window allows, truncating", 0);
datalen = channel->remote.window_size + data_head; datalen = channel->remote.window_size + data_head;
} else { } else {
@@ -438,6 +488,9 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
return 0; return 0;
} }
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "EOF received for channel %lu/%lu", channel->local.id, channel->remote.id);
#endif
channel->remote.eof = 1; channel->remote.eof = 1;
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
@@ -453,6 +506,9 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
return 0; return 0;
} }
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Close received for channel %lu/%lu", channel->local.id, channel->remote.id);
#endif
channel->remote.close = 1; channel->remote.close = 1;
/* TODO: Add a callback for this */ /* TODO: Add a callback for this */
@@ -487,6 +543,9 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
if (channel && bytestoadd) { if (channel && bytestoadd) {
channel->local.window_size += bytestoadd; channel->local.window_size += bytestoadd;
} }
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu", channel->local.id, channel->remote.id, bytestoadd, channel->local.window_size);
#endif
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
return 0; return 0;
@@ -519,6 +578,9 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
* Well, it's already in the brigade, * Well, it's already in the brigade,
* let's just call back into ourselves * let's just call back into ourselves
*/ */
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Renegotiating Keys");
#endif
libssh2_kex_exchange(session, 1); libssh2_kex_exchange(session, 1);
/* If there was a key reexchange failure, let's just hope we didn't send NEWKEYS yet, otherwise remote will drop us like a rock */ /* If there was a key reexchange failure, let's just hope we didn't send NEWKEYS yet, otherwise remote will drop us like a rock */
} }
@@ -533,7 +595,22 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
static int libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, size_t count) static int libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, size_t count)
{ {
size_t bytes_read = 0; size_t bytes_read = 0;
#if !defined(HAVE_POLL) && !defined(HAVE_SELECT)
int polls = 0; int polls = 0;
#endif
#ifndef WIN32
fcntl(session->socket_fd, F_SETFL, 0);
#else
{
u_long block = FALSE;
ioctlsocket(session->socket_fd, FIONBIO, &block);
}
#endif
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes", (int)count);
#endif
while (bytes_read < count) { while (bytes_read < count) {
int ret; int ret;
@@ -542,18 +619,42 @@ static int libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, s
if (ret < 0) { if (ret < 0) {
#ifdef WIN32 #ifdef WIN32
switch (WSAGetLastError()) { switch (WSAGetLastError()) {
case WSAEWOULDBLOCK: errno = EAGAIN; case WSAEWOULDBLOCK: errno = EAGAIN; break;
case WSAENOTCONN: case WSAENOTCONN:
case WSAENOTSOCK: case WSAENOTSOCK:
case WSAECONNABORTED: errno = EBADF; case WSAECONNABORTED: errno = EBADF; break;
case WSAEINTR: errno = EINTR; case WSAEINTR: errno = EINTR; break;
} }
#endif #endif
if (errno == EAGAIN) { if (errno == EAGAIN) {
#ifdef HAVE_POLL
struct pollfd read_socket;
read_socket.fd = session->socket_fd;
read_socket.events = POLLIN;
if (poll(&read_socket, 1, 30000) <= 0) {
return -1;
}
#elif defined(HAVE_SELECT)
fd_set read_socket;
struct timeval timeout;
FD_ZERO(&read_socket);
FD_SET(session->socket_fd, &read_socket);
timeout.tv_sec = 30;
timeout.tv_usec = 0;
if (select(session->socket_fd + 1, &read_socket, NULL, NULL, &timeout) <= 0) {
return -1;
}
#else
if (polls++ > LIBSSH2_SOCKET_POLL_MAXLOOPS) { if (polls++ > LIBSSH2_SOCKET_POLL_MAXLOOPS) {
return -1; return -1;
} }
usleep(LIBSSH2_SOCKET_POLL_UDELAY); usleep(LIBSSH2_SOCKET_POLL_UDELAY);
#endif /* POLL/SELECT/SLEEP */
continue; continue;
} }
if (errno == EINTR) { if (errno == EINTR) {
@@ -569,6 +670,10 @@ static int libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, s
bytes_read += ret; bytes_read += ret;
} }
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes actually read", (int)bytes_read);
#endif
return bytes_read; return bytes_read;
} }
/* }}} */ /* }}} */
@@ -595,6 +700,10 @@ int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
ioctlsocket(session->socket_fd, FIONBIO, &non_block); ioctlsocket(session->socket_fd, FIONBIO, &non_block);
} }
#endif #endif
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Checking for packet: will%s block", should_block ? "" : " not");
#endif
if (session->state & LIBSSH2_STATE_NEWKEYS) { if (session->state & LIBSSH2_STATE_NEWKEYS) {
/* Temporary Buffer /* Temporary Buffer
* The largest blocksize (currently) is 32, the largest MAC (currently) is 20 * The largest blocksize (currently) is 32, the largest MAC (currently) is 20
@@ -637,6 +746,9 @@ int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
packet_len = libssh2_ntohu32(block); packet_len = libssh2_ntohu32(block);
padding_len = block[4]; padding_len = block[4];
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing packet %lu bytes long (with %lu bytes padding)", packet_len, padding_len);
#endif
memcpy(tmp, block, 5); /* Use this for MAC later */ memcpy(tmp, block, 5); /* Use this for MAC later */
payload_len = packet_len - 1; /* padding_len(1) */ payload_len = packet_len - 1; /* padding_len(1) */
@@ -700,6 +812,9 @@ int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
LIBSSH2_FREE(session, payload); LIBSSH2_FREE(session, payload);
return -1; return -1;
} }
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Payload decompressed: %lu bytes(compressed) to %lu bytes(uncompressed)", data_len, payload_len);
#endif
if (free_payload) { if (free_payload) {
LIBSSH2_FREE(session, payload); LIBSSH2_FREE(session, payload);
payload = data; payload = data;
@@ -752,6 +867,9 @@ int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
} }
packet_length = libssh2_ntohu32(buf); packet_length = libssh2_ntohu32(buf);
padding_length = buf[4]; padding_length = buf[4];
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing plaintext packet %lu bytes long (with %lu bytes padding)", packet_length, padding_length);
#endif
payload_len = packet_length - padding_length - 1; /* padding_length(1) */ payload_len = packet_length - padding_length - 1; /* padding_length(1) */
payload = LIBSSH2_ALLOC(session, payload_len); payload = LIBSSH2_ALLOC(session, payload_len);
@@ -792,6 +910,9 @@ int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, u
return -1; return -1;
} }
} }
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Looking for packet of type: %d", (int)packet_type);
#endif
while (packet) { while (packet) {
if (packet->data[0] == packet_type && if (packet->data[0] == packet_type &&
(packet->data_len >= (match_ofs + match_len)) && (packet->data_len >= (match_ofs + match_len)) &&
@@ -821,9 +942,27 @@ int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, u
} }
/* }}} */ /* }}} */
/* {{{ libssh2_packet_askv
* Scan for any of a list of packet types in the brigade, optionally poll the socket for a packet first
*/
int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len,
unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket)
{
int i, packet_types_len = strlen(packet_types);
for(i = 0; i < packet_types_len; i++) {
if (0 == libssh2_packet_ask_ex(session, packet_types[i], data, data_len, match_ofs, match_buf, match_len, i ? 0 : poll_socket)) {
return 0;
}
}
return -1;
}
/* }}} */
/* {{{ libssh2_packet_require /* {{{ libssh2_packet_require
* Loops libssh2_packet_read() until the packet requested is available * Loops libssh2_packet_read() until the packet requested is available
* SSH_DISCONNECT will cause a bailout though * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
*/ */
int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len,
unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len) unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len)
@@ -833,6 +972,9 @@ int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_typ
return 0; return 0;
} }
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet of type %d becomes available", (int)packet_type);
#endif
while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
int ret = libssh2_packet_read(session, 1); int ret = libssh2_packet_read(session, 1);
if (ret < 0) { if (ret < 0) {
@@ -851,6 +993,39 @@ int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_typ
} }
/* }}} */ /* }}} */
/* {{{ libssh2_packet_requirev
* Loops libssh2_packet_read() until one of a list of packet types requested is available
* SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
* packet_types is a null terminated list of packet_type numbers
*/
int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len,
unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len)
{
if (libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0) == 0) {
/* One of the packets listed was available in the packet brigade */
return 0;
}
while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) {
int ret = libssh2_packet_read(session, 1);
if (ret < 0) {
return -1;
}
if (ret == 0) {
continue;
}
if (strchr(packet_types, ret)) {
/* Be lazy, let packet_ask pull it out of the brigade */
return libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0);
}
}
/* Only reached if the socket died */
return -1;
}
/* }}} */
/* {{{ libssh2_packet_write /* {{{ libssh2_packet_write
* Send a packet, encrypting it and adding a MAC code if necessary * Send a packet, encrypting it and adding a MAC code if necessary
* Returns 0 on success, non-zero on failure * Returns 0 on success, non-zero on failure
@@ -863,14 +1038,26 @@ int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned
unsigned long padding_length; unsigned long padding_length;
int free_data = 0; int free_data = 0;
unsigned char buf[246]; /* 6 byte header plus max padding size(240) */ unsigned char buf[246]; /* 6 byte header plus max padding size(240) */
int i;
#ifdef LIBSSH2_DEBUG_TRANSPORT
{
/* Show a hint of what's being sent */
char excerpt[32];
int ex_len = 0, db_ofs = 0;
for (; ex_len < 24 && db_ofs < data_len; ex_len += 3, db_ofs++) snprintf(excerpt + ex_len, 4, "%02X ", data[db_ofs]);
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet type %d, length=%lu, %s", (int)data[0], data_len, excerpt);
}
#endif
if ((session->state & LIBSSH2_STATE_NEWKEYS) && if ((session->state & LIBSSH2_STATE_NEWKEYS) &&
strcmp(session->local.comp->name, "none")) { strcmp(session->local.comp->name, "none")) {
if (session->local.comp->comp(session, 1, &data, &data_len, LIBSSH2_PACKET_MAXCOMP, &free_data, data, data_len, &session->local.comp_abstract)) { if (session->local.comp->comp(session, 1, &data, &data_len, LIBSSH2_PACKET_MAXCOMP, &free_data, data, data_len, &session->local.comp_abstract)) {
return -1; return -1;
} }
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Compressed payload to %lu bytes", data_len);
#endif
} }
#ifndef WIN32 #ifndef WIN32
@@ -892,11 +1079,9 @@ int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned
packet_length += padding_length; packet_length += padding_length;
libssh2_htonu32(buf, packet_length); libssh2_htonu32(buf, packet_length);
buf[4] = padding_length; buf[4] = padding_length;
#ifdef LIBSSH2_DEBUG_TRANSPORT
for (i = 0; i < padding_length; i++) { _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet with total length %lu (%lu bytes padding)", packet_length, padding_length);
/* Make random */ #endif
buf[5 + i] = '\0';
}
if (session->state & LIBSSH2_STATE_NEWKEYS) { if (session->state & LIBSSH2_STATE_NEWKEYS) {
/* Encryption is in effect */ /* Encryption is in effect */

View File

@@ -70,6 +70,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, char *pa
} }
command[command_len - 1] = '\0'; command[command_len - 1] = '\0';
#ifdef LIBSSH2_DEBUG_SCP
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP receive");
#endif
/* Allocate a channel */ /* Allocate a channel */
if ((channel = libssh2_channel_open_session(session)) == NULL) { if ((channel = libssh2_channel_open_session(session)) == NULL) {
LIBSSH2_FREE(session, command); LIBSSH2_FREE(session, command);
@@ -86,6 +89,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, char *pa
} }
LIBSSH2_FREE(session, command); LIBSSH2_FREE(session, command);
#ifdef LIBSSH2_DEBUG_SCP
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Sending initial wakeup");
#endif
/* SCP ACK */ /* SCP ACK */
response[0] = '\0'; response[0] = '\0';
if (libssh2_channel_write(channel, response, 1) != 1) { if (libssh2_channel_write(channel, response, 1) != 1) {
@@ -197,6 +203,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, char *pa
libssh2_channel_free(channel); libssh2_channel_free(channel);
return NULL; return NULL;
} }
#ifdef LIBSSH2_DEBUG_SCP
_libssh2_debug(session, LIBSSH2_DBG_SCP, "mtime = %ld, atime = %ld", mtime, atime);
#endif
/* We *should* check that atime.usec is valid, but why let that stop use? */ /* We *should* check that atime.usec is valid, but why let that stop use? */
break; break;
@@ -295,6 +304,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, char *pa
libssh2_channel_free(channel); libssh2_channel_free(channel);
return NULL; return NULL;
} }
#ifdef LIBSSH2_DEBUG_SCP
_libssh2_debug(session, LIBSSH2_DBG_SCP, "mod = 0%lo size = %ld", mode, size);
#endif
/* We *should* check that basename is valid, but why let that stop us? */ /* We *should* check that basename is valid, but why let that stop us? */
break; break;
@@ -344,6 +356,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, char
} }
command[command_len - 1] = '\0'; command[command_len - 1] = '\0';
#ifdef LIBSSH2_DEBUG_SCP
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP send");
#endif
/* Allocate a channel */ /* Allocate a channel */
if ((channel = libssh2_channel_open_session(session)) == NULL) { if ((channel = libssh2_channel_open_session(session)) == NULL) {
/* previous call set libssh2_session_last_error(), pass it through */ /* previous call set libssh2_session_last_error(), pass it through */
@@ -372,6 +387,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, char
/* Send mtime and atime to be used for file */ /* Send mtime and atime to be used for file */
if (mtime || atime) { if (mtime || atime) {
response_len = snprintf(response, LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n", mtime, atime); response_len = snprintf(response, LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n", mtime, atime);
#ifdef LIBSSH2_DEBUG_SCP
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", response);
#endif
if (libssh2_channel_write(channel, response, response_len) != response_len) { if (libssh2_channel_write(channel, response, response_len) != response_len) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send time data for SCP file", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send time data for SCP file", 0);
libssh2_channel_free(channel); libssh2_channel_free(channel);
@@ -394,6 +412,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, char
} }
response_len = snprintf(response, LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %lu %s\n", mode, (unsigned long)size, base); response_len = snprintf(response, LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %lu %s\n", mode, (unsigned long)size, base);
#ifdef LIBSSH2_DEBUG_SCP
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", response);
#endif
if (libssh2_channel_write(channel, response, response_len) != response_len) { if (libssh2_channel_write(channel, response, response_len) != response_len) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send core file data for SCP file", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send core file data for SCP file", 0);
libssh2_channel_free(channel); libssh2_channel_free(channel);

View File

@@ -128,6 +128,9 @@ static int libssh2_banner_receive(LIBSSH2_SESSION *session)
session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1); session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1);
memcpy(session->remote.banner, banner, banner_len); memcpy(session->remote.banner, banner, banner_len);
session->remote.banner[banner_len] = '\0'; session->remote.banner[banner_len] = '\0';
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Received Banner: %s", session->remote.banner);
#endif
return 0; return 0;
} }
/* }}} */ /* }}} */
@@ -145,6 +148,22 @@ static int libssh2_banner_send(LIBSSH2_SESSION *session)
banner_len = strlen(session->local.banner); banner_len = strlen(session->local.banner);
banner = session->local.banner; banner = session->local.banner;
} }
#ifdef LIBSSH2_DEBUG_TRANSPORT
{
/* Hack and slash to avoid sending CRLF in debug output */
char banner_dup[256];
if (banner_len < 256) {
memcpy(banner_dup, banner, banner_len - 2);
banner_dup[banner_len - 2] = '\0';
} else {
memcpy(banner_dup, banner, 255);
banner[255] = '\0';
}
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending Banner: %s", banner_dup);
}
#endif
return (send(session->socket_fd, banner, banner_len, LIBSSH2_SOCKET_SEND_FLAGS(session)) == banner_len) ? 0 : 1; return (send(session->socket_fd, banner, banner_len, LIBSSH2_SOCKET_SEND_FLAGS(session)) == banner_len) ? 0 : 1;
} }
@@ -173,6 +192,10 @@ LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, char *banner)
} }
memcpy(session->local.banner, banner, banner_len); memcpy(session->local.banner, banner, banner_len);
#ifdef LIBSSH2_DEBUG_TRANSPORT
session->local.banner[banner_len] = '\0';
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting local Banner: %s", session->local.banner);
#endif
session->local.banner[banner_len++] = '\r'; session->local.banner[banner_len++] = '\r';
session->local.banner[banner_len++] = '\n'; session->local.banner[banner_len++] = '\n';
session->local.banner[banner_len++] = '\0'; session->local.banner[banner_len++] = '\0';
@@ -208,6 +231,9 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(
session->free = local_free; session->free = local_free;
session->realloc = local_realloc; session->realloc = local_realloc;
session->abstract = abstract; session->abstract = abstract;
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "New session resource allocated");
#endif
return session; return session;
} }
@@ -248,6 +274,9 @@ LIBSSH2_API void* libssh2_session_callback_set(LIBSSH2_SESSION *session, int cbt
return oldcb; return oldcb;
break; break;
} }
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting Callback %d", cbtype);
#endif
return NULL; return NULL;
} }
@@ -266,6 +295,9 @@ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
unsigned char service[sizeof("ssh-userauth") + 5 - 1]; unsigned char service[sizeof("ssh-userauth") + 5 - 1];
unsigned long service_length; unsigned long service_length;
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "session_startup for socket %d", socket);
#endif
if (socket <= 0) { if (socket <= 0) {
/* Did we forget something? */ /* Did we forget something? */
libssh2_error(session, LIBSSH2_ERROR_SOCKET_NONE, "No socket provided", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_NONE, "No socket provided", 0);
@@ -291,6 +323,9 @@ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
return LIBSSH2_ERROR_KEX_FAILURE; return LIBSSH2_ERROR_KEX_FAILURE;
} }
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Requesting userauth service");
#endif
/* Request the userauth service */ /* Request the userauth service */
service[0] = SSH_MSG_SERVICE_REQUEST; service[0] = SSH_MSG_SERVICE_REQUEST;
libssh2_htonu32(service + 1, sizeof("ssh-userauth") - 1); libssh2_htonu32(service + 1, sizeof("ssh-userauth") - 1);
@@ -323,6 +358,9 @@ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
*/ */
LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session) LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session)
{ {
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Freeing session resource", session->remote.banner);
#endif
while (session->channels.head) { while (session->channels.head) {
LIBSSH2_CHANNEL *tmp = session->channels.head; LIBSSH2_CHANNEL *tmp = session->channels.head;
@@ -464,6 +502,9 @@ LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reas
unsigned char *s, *data; unsigned char *s, *data;
unsigned long data_len, descr_len = 0, lang_len = 0; unsigned long data_len, descr_len = 0, lang_len = 0;
#ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnecting: reason=%d, desc=%s, lang=%s", reason, description, lang);
#endif
if (description) { if (description) {
descr_len = strlen(description); descr_len = strlen(description);
} }
@@ -784,12 +825,21 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeou
((fds[i].revents & LIBSSH2_POLLFD_POLLOUT) == 0)) { /* Not yet known to be ready for write */ ((fds[i].revents & LIBSSH2_POLLFD_POLLOUT) == 0)) { /* Not yet known to be ready for write */
fds[i].revents |= libssh2_poll_channel_write(fds[i].fd.channel) ? LIBSSH2_POLLFD_POLLOUT : 0; fds[i].revents |= libssh2_poll_channel_write(fds[i].fd.channel) ? LIBSSH2_POLLFD_POLLOUT : 0;
} }
if (fds[i].fd.channel->remote.close || fds[i].fd.channel->local.close) {
fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED;
}
if (fds[i].fd.channel->session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
}
break; break;
case LIBSSH2_POLLFD_LISTENER: case LIBSSH2_POLLFD_LISTENER:
if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && /* Want a connection */ if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && /* Want a connection */
((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { /* No connections known of yet */ ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { /* No connections known of yet */
fds[i].revents |= libssh2_poll_listener_queued(fds[i].fd.listener) ? LIBSSH2_POLLFD_POLLIN : 0; fds[i].revents |= libssh2_poll_listener_queued(fds[i].fd.listener) ? LIBSSH2_POLLFD_POLLIN : 0;
} }
if (fds[i].fd.listener->session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
fds[i].revents |= LIBSSH2_POLLFD_LISTENER_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
}
break; break;
} }
} }
@@ -838,6 +888,9 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeou
/* Spin session until no data available */ /* Spin session until no data available */
while (libssh2_packet_read(fds[i].fd.channel->session, 0) > 0); while (libssh2_packet_read(fds[i].fd.channel->session, 0) > 0);
} }
if (sockets[i].revents & POLLHUP) {
fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
}
sockets[i].revents = 0; sockets[i].revents = 0;
break; break;
case LIBSSH2_POLLFD_LISTENER: case LIBSSH2_POLLFD_LISTENER:
@@ -845,6 +898,9 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeou
/* Spin session until no data available */ /* Spin session until no data available */
while (libssh2_packet_read(fds[i].fd.listener->session, 0) > 0); while (libssh2_packet_read(fds[i].fd.listener->session, 0) > 0);
} }
if (sockets[i].revents & POLLHUP) {
fds[i].revents |= LIBSSH2_POLLFD_LISTENER_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
}
sockets[i].revents = 0; sockets[i].revents = 0;
break; break;
} }

View File

@@ -89,6 +89,11 @@ struct _LIBSSH2_SFTP {
#define LIBSSH2_SFTP_HANDLE_FILE 0 #define LIBSSH2_SFTP_HANDLE_FILE 0
#define LIBSSH2_SFTP_HANDLE_DIR 1 #define LIBSSH2_SFTP_HANDLE_DIR 1
/* S_IFREG */
#define LIBSSH2_SFTP_ATTR_PFILETYPE_FILE 0100000
/* S_IFDIR */
#define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000
struct _LIBSSH2_SFTP_HANDLE { struct _LIBSSH2_SFTP_HANDLE {
LIBSSH2_SFTP *sftp; LIBSSH2_SFTP *sftp;
LIBSSH2_SFTP_HANDLE *prev, *next; LIBSSH2_SFTP_HANDLE *prev, *next;
@@ -118,6 +123,9 @@ static int libssh2_sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data, unsi
LIBSSH2_SESSION *session = sftp->channel->session; LIBSSH2_SESSION *session = sftp->channel->session;
LIBSSH2_PACKET *packet; LIBSSH2_PACKET *packet;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Received packet %d", (int)data[0]);
#endif
packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET));
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate datablock for SFTP packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate datablock for SFTP packet", 0);
@@ -151,8 +159,11 @@ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block)
LIBSSH2_SESSION *session = channel->session; LIBSSH2_SESSION *session = channel->session;
unsigned char buffer[4]; /* To store the packet length */ unsigned char buffer[4]; /* To store the packet length */
unsigned char *packet; unsigned char *packet;
unsigned long packet_len; unsigned long packet_len, packet_received;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Waiting for packet: %s block", should_block ? "will" : "willnot");
#endif
if (should_block) { if (should_block) {
libssh2_channel_set_blocking(channel, 1); libssh2_channel_set_blocking(channel, 1);
if (4 != libssh2_channel_read(channel, buffer, 4)) { if (4 != libssh2_channel_read(channel, buffer, 4)) {
@@ -172,6 +183,9 @@ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block)
} }
} }
packet_len = libssh2_ntohu32(buffer); packet_len = libssh2_ntohu32(buffer);
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Data begin - Packet Length: %lu", packet_len);
#endif
if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN) { if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN) {
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "SFTP packet too large", 0); libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "SFTP packet too large", 0);
return -1; return -1;
@@ -183,10 +197,16 @@ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block)
return -1; return -1;
} }
if (packet_len != libssh2_channel_read(channel, packet, packet_len)) { packet_received = 0;
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for SFTP packet", 0); while (packet_len > packet_received) {
LIBSSH2_FREE(session, packet); long bytes_received = libssh2_channel_read(channel, packet + packet_received, packet_len - packet_received);
return -1;
if (bytes_received < 0) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Receive error waiting for SFTP packet", 0);
LIBSSH2_FREE(session, packet);
return -1;
}
packet_received += bytes_received;
} }
if (libssh2_sftp_packet_add(sftp, packet, packet_len)) { if (libssh2_sftp_packet_add(sftp, packet, packet_len)) {
@@ -206,7 +226,11 @@ static int libssh2_sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type
LIBSSH2_SESSION *session = sftp->channel->session; LIBSSH2_SESSION *session = sftp->channel->session;
LIBSSH2_PACKET *packet = sftp->packets.head; LIBSSH2_PACKET *packet = sftp->packets.head;
unsigned char match_buf[5]; unsigned char match_buf[5];
int match_len = 5;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Asking for %d packet", (int)packet_type);
#endif
if (poll_channel) { if (poll_channel) {
if (libssh2_sftp_packet_read(sftp, 0) < 0) { if (libssh2_sftp_packet_read(sftp, 0) < 0) {
return -1; return -1;
@@ -214,10 +238,15 @@ static int libssh2_sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type
} }
match_buf[0] = packet_type; match_buf[0] = packet_type;
libssh2_htonu32(match_buf + 1, request_id); if (packet_type == SSH_FXP_VERSION) {
/* Special consideration when matching VERSION packet */
match_len = 1;
} else {
libssh2_htonu32(match_buf + 1, request_id);
}
while (packet) { while (packet) {
if (strncmp(packet->data, match_buf, 5) == 0) { if (strncmp(packet->data, match_buf, match_len) == 0) {
*data = packet->data; *data = packet->data;
*data_len = packet->data_len; *data_len = packet->data_len;
@@ -250,6 +279,9 @@ static int libssh2_sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_
{ {
LIBSSH2_SESSION *session = sftp->channel->session; LIBSSH2_SESSION *session = sftp->channel->session;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Requiring %d packet", (int)packet_type);
#endif
if (libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0) == 0) { if (libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0) == 0) {
/* A packet was available in the packet brigade */ /* A packet was available in the packet brigade */
return 0; return 0;
@@ -280,12 +312,21 @@ static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_respon
{ {
int i; int i;
/* Flush */
while (libssh2_sftp_packet_read(sftp, 0) > 0);
while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) { while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
int ret;
for(i = 0; i < num_valid_responses; i++) { for(i = 0; i < num_valid_responses; i++) {
if (libssh2_sftp_packet_ask(sftp, valid_responses[i], request_id, data, data_len, !i) == 0) { if (libssh2_sftp_packet_ask(sftp, valid_responses[i], request_id, data, data_len, 0) == 0) {
return 0; return 0;
} }
} }
ret = libssh2_sftp_packet_read(sftp, 1);
if (ret < 0) {
return -1;
}
if (ret == 0) continue;
} }
return -1; return -1;
@@ -408,9 +449,12 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
{ {
LIBSSH2_SFTP *sftp; LIBSSH2_SFTP *sftp;
LIBSSH2_CHANNEL *channel; LIBSSH2_CHANNEL *channel;
unsigned char *data, *s, buffer[13]; /* sftp_header(9) + version_id(4) */ unsigned char *data, *s, buffer[9]; /* sftp_header(5){excludes request_id} + version_id(4) */
unsigned long data_len, request_id; unsigned long data_len;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Initializing SFTP subsystem");
#endif
channel = libssh2_channel_open_session(session); channel = libssh2_channel_open_session(session);
if (!channel) { if (!channel) {
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0); libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0);
@@ -424,6 +468,8 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
libssh2_channel_set_blocking(channel, 1); libssh2_channel_set_blocking(channel, 1);
libssh2_channel_handle_extended_data(channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
sftp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP)); sftp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP));
if (!sftp) { if (!sftp) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new SFTP structure", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new SFTP structure", 0);
@@ -432,21 +478,23 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
} }
memset(sftp, 0, sizeof(LIBSSH2_SFTP)); memset(sftp, 0, sizeof(LIBSSH2_SFTP));
sftp->channel = channel; sftp->channel = channel;
sftp->request_id = 0;
request_id = sftp->request_id++; libssh2_htonu32(buffer, 5);
libssh2_htonu32(buffer, 4 + 5);
buffer[4] = SSH_FXP_INIT; buffer[4] = SSH_FXP_INIT;
libssh2_htonu32(buffer + 5, request_id); libssh2_htonu32(buffer + 5, LIBSSH2_SFTP_VERSION);
libssh2_htonu32(buffer + 9, 6);
if (13 != libssh2_channel_write(channel, buffer, 13)) { #ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending FXP_INIT packet advertising version %d support", (int)LIBSSH2_SFTP_VERSION);
#endif
if (9 != libssh2_channel_write(channel, buffer, 9)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SSH_FXP_INIT", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SSH_FXP_INIT", 0);
libssh2_channel_free(channel); libssh2_channel_free(channel);
LIBSSH2_FREE(session, sftp); LIBSSH2_FREE(session, sftp);
return NULL; return NULL;
} }
if (libssh2_sftp_packet_require(sftp, SSH_FXP_VERSION, request_id, &data, &data_len)) { if (libssh2_sftp_packet_require(sftp, SSH_FXP_VERSION, 0, &data, &data_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from SFTP subsystem", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from SFTP subsystem", 0);
libssh2_channel_free(channel); libssh2_channel_free(channel);
LIBSSH2_FREE(session, sftp); LIBSSH2_FREE(session, sftp);
@@ -462,8 +510,14 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
s = data + 1; s = data + 1;
sftp->version = libssh2_ntohu32(s); s += 4; sftp->version = libssh2_ntohu32(s); s += 4;
if (sftp->version > LIBSSH2_SFTP_VERSION) { if (sftp->version > LIBSSH2_SFTP_VERSION) {
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Truncating remote SFTP version from %lu", sftp->version);
#endif
sftp->version = LIBSSH2_SFTP_VERSION; sftp->version = LIBSSH2_SFTP_VERSION;
} }
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Enabling SFTP version %lu compatability", sftp->version);
#endif
while (s < (data + data_len)) { while (s < (data + data_len)) {
char *extension_name, *extension_data; char *extension_name, *extension_data;
unsigned long extname_len, extdata_len; unsigned long extname_len, extdata_len;
@@ -518,7 +572,8 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_REMOVE packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_REMOVE packet", 0);
return NULL; return NULL;
} }
attrs.permissions = mode; /* Filetype in SFTP 3 and earlier */
attrs.permissions = mode | ((open_type == LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE : LIBSSH2_SFTP_ATTR_PFILETYPE_DIR);
libssh2_htonu32(s, packet_len - 4); s += 4; libssh2_htonu32(s, packet_len - 4); s += 4;
*(s++) = (open_type == LIBSSH2_SFTP_OPENFILE) ? SSH_FXP_OPEN : SSH_FXP_OPENDIR; *(s++) = (open_type == LIBSSH2_SFTP_OPENFILE) ? SSH_FXP_OPEN : SSH_FXP_OPENDIR;
@@ -531,6 +586,9 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *
s += libssh2_sftp_attr2bin(s, &attrs); s += libssh2_sftp_attr2bin(s, &attrs);
} }
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending %s open request", (open_type == LIBSSH2_SFTP_OPENFILE) ? "file" : "directory");
#endif
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_REMOVE command", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_REMOVE command", 0);
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
@@ -583,6 +641,9 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *
fp->u.file.offset = 0; fp->u.file.offset = 0;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Open command successful");
#endif
return fp; return fp;
} }
/* }}} */ /* }}} */
@@ -601,6 +662,9 @@ LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
unsigned char read_responses[2] = { SSH_FXP_DATA, SSH_FXP_STATUS }; unsigned char read_responses[2] = { SSH_FXP_DATA, SSH_FXP_STATUS };
size_t bytes_read = 0; size_t bytes_read = 0;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading %lu bytes from SFTP handle", (unsigned long)buffer_maxlen);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0);
@@ -639,6 +703,9 @@ LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
if (bytes_read > (data_len - 9)) { if (bytes_read > (data_len - 9)) {
return -1; return -1;
} }
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu bytes returned", (unsigned long)bytes_read);
#endif
memcpy(buffer, data + 9, bytes_read); memcpy(buffer, data + 9, bytes_read);
handle->u.file.offset += bytes_read; handle->u.file.offset += bytes_read;
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
@@ -705,6 +772,9 @@ LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
libssh2_htonu32(s, handle->handle_len); s += 4; libssh2_htonu32(s, handle->handle_len); s += 4;
memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading entries from directory handle");
#endif
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0);
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
@@ -732,6 +802,9 @@ LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
} }
num_names = libssh2_ntohu32(data + 5); num_names = libssh2_ntohu32(data + 5);
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu entries returned", num_names);
#endif
if (num_names <= 0) { if (num_names <= 0) {
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
return (num_names == 0) ? 0 : -1; return (num_names == 0) ? 0 : -1;
@@ -776,6 +849,9 @@ LIBSSH2_API size_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *b
unsigned long packet_len = handle->handle_len + count + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + count(4) */ unsigned long packet_len = handle->handle_len + count + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + count(4) */
unsigned char *packet, *s, *data; unsigned char *packet, *s, *data;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Writing %lu bytes", (unsigned long)count);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_WRITE packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_WRITE packet", 0);
@@ -832,6 +908,9 @@ LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_
unsigned char *packet, *s, *data; unsigned char *packet, *s, *data;
unsigned char fstat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS }; unsigned char fstat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS };
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Issuing %s command", setstat ? "set-stat" : "stat");
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FSTAT/FSETSTAT packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FSTAT/FSETSTAT packet", 0);
@@ -912,6 +991,9 @@ LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle)
unsigned long packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ unsigned long packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
unsigned char *packet, *s, *data; unsigned char *packet, *s, *data;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Closing handle");
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0);
@@ -980,6 +1062,9 @@ LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, char *filename, int f
unsigned long packet_len = filename_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */ unsigned long packet_len = filename_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */
unsigned char *packet, *s, *data; unsigned char *packet, *s, *data;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Unlinking %s", filename);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_REMOVE packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_REMOVE packet", 0);
@@ -1032,6 +1117,14 @@ LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, char *source_filenam
source_filename_len(4) + dest_filename_len(4) + flags(4) */ source_filename_len(4) + dest_filename_len(4) + flags(4) */
unsigned char *packet, *s, *data; unsigned char *packet, *s, *data;
if (sftp->version < 2) {
libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Server does not support RENAME", 0);
return -1;
}
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Renaming %s to %s", source_filename, dest_filename);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_RENAME packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_RENAME packet", 0);
@@ -1100,12 +1193,16 @@ LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, char *path, int path_l
/* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
unsigned char *packet, *s, *data; unsigned char *packet, *s, *data;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Creating directory %s with mode 0%lo", path, mode);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0);
return -1; return -1;
} }
attrs.permissions = mode; /* Filetype in SFTP 3 and earlier */
attrs.permissions = mode | LIBSSH2_SFTP_ATTR_PFILETYPE_DIR;
libssh2_htonu32(s, packet_len - 4); s += 4; libssh2_htonu32(s, packet_len - 4); s += 4;
*(s++) = SSH_FXP_MKDIR; *(s++) = SSH_FXP_MKDIR;
@@ -1151,6 +1248,9 @@ LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, char *path, int path_l
unsigned long packet_len = path_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ unsigned long packet_len = path_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
unsigned char *packet, *s, *data; unsigned char *packet, *s, *data;
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Removing directory: %s", path);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0);
@@ -1202,6 +1302,9 @@ LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, char *path, int path_le
unsigned char *packet, *s, *data; unsigned char *packet, *s, *data;
unsigned char stat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS }; unsigned char stat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS };
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s", (stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" : (stat_type == LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path);
#endif
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0);
@@ -1275,12 +1378,22 @@ LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, in
unsigned char *packet, *s, *data; unsigned char *packet, *s, *data;
unsigned char link_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS }; unsigned char link_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS };
if ((sftp->version < 3) &&
(link_type != LIBSSH2_SFTP_REALPATH)) {
libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Server does not support SYMLINK or READLINK", 0);
return -1;
}
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for SYMLINK/READLINK/REALPATH packet", 0); libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for SYMLINK/READLINK/REALPATH packet", 0);
return -1; return -1;
} }
#ifdef LIBSSH2_DEBUG_SFTP
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s on %s", (link_type == LIBSSH2_SFTP_SYMLINK) ? "Creating" : "Reading",
(link_type == LIBSSH2_SFTP_REALPATH) ? "realpath" : "symlink", path);
#endif
libssh2_htonu32(s, packet_len - 4); s += 4; libssh2_htonu32(s, packet_len - 4); s += 4;
switch (link_type) { switch (link_type) {
case LIBSSH2_SFTP_REALPATH: case LIBSSH2_SFTP_REALPATH:

View File

@@ -51,6 +51,7 @@
*/ */
LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, char *username, int username_len) LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, char *username, int username_len)
{ {
unsigned char reply_codes[3] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 };
unsigned long data_len = username_len + 31; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" + unsigned long data_len = username_len + 31; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" +
method_len(4) + method(4)"none" */ method_len(4) + method(4)"none" */
unsigned long methods_len; unsigned long methods_len;
@@ -81,25 +82,23 @@ LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, char *username
} }
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
while (1) { if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_SUCCESS, &data, &data_len, 1) == 0) { return NULL;
/* Wow, who'dve thought... */ }
LIBSSH2_FREE(session, data);
session->state |= LIBSSH2_STATE_AUTHENTICATED;
return NULL;
}
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_FAILURE, &data, &data_len, 0) == 0) { if (data[0] == SSH_MSG_USERAUTH_SUCCESS) {
/* What we *actually* wanted to happen */ /* Wow, who'dve thought... */
break; LIBSSH2_FREE(session, data);
} session->state |= LIBSSH2_STATE_AUTHENTICATED;
/* TODO: Timeout? */ return NULL;
} }
methods_len = libssh2_ntohu32(data + 1); methods_len = libssh2_ntohu32(data + 1);
memcpy(data, data + 5, methods_len); memcpy(data, data + 5, methods_len);
data[methods_len] = '\0'; data[methods_len] = '\0';
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Permitted auth methods: %s", data);
#endif
return data; return data;
} }
/* }}} */ /* }}} */
@@ -121,7 +120,7 @@ LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, char *use
char *password, int password_len, char *password, int password_len,
LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))) LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)))
{ {
unsigned char *data, *s; unsigned char *data, *s, reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 0 };
unsigned long data_len = username_len + password_len + 40; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" + unsigned long data_len = username_len + password_len + 40; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" +
method_len(4) + method(8)"password" + chgpwdbool(1) + password_len(4) */ method_len(4) + method(8)"password" + chgpwdbool(1) + password_len(4) */
@@ -146,6 +145,9 @@ LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, char *use
libssh2_htonu32(s, password_len); s += 4; libssh2_htonu32(s, password_len); s += 4;
memcpy(s, password, password_len); s += password_len; memcpy(s, password, password_len); s += password_len;
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting to login using password authentication");
#endif
if (libssh2_packet_write(session, data, data_len)) { if (libssh2_packet_write(session, data, data_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password request", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password request", 0);
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
@@ -153,71 +155,78 @@ LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, char *use
} }
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
while (1) { password_response:
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_SUCCESS, &data, &data_len, 1) == 0) { if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
LIBSSH2_FREE(session, data); return -1;
session->state |= LIBSSH2_STATE_AUTHENTICATED;
return 0;
}
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_FAILURE, &data, &data_len, 0) == 0) {
LIBSSH2_FREE(session, data);
return -1;
}
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, &data, &data_len, 0) == 0) {
char *newpw = NULL;
int newpw_len = 0;
LIBSSH2_FREE(session, data);
if (passwd_change_cb) {
passwd_change_cb(session, &newpw, &newpw_len, &session->abstract);
if (!newpw) {
libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password expired, and callback failed", 0);
return -1;
}
data_len = username_len + password_len + 44 + newpw_len; /* basic data_len + newpw_len(4) */
s = data = LIBSSH2_ALLOC(session, data_len);
if (!data) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password-change request", 0);
return -1;
}
*(s++) = SSH_MSG_USERAUTH_REQUEST;
libssh2_htonu32(s, username_len); s += 4;
memcpy(s, username, username_len); s += username_len;
libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4;
memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1;
libssh2_htonu32(s, sizeof("password") - 1); s += 4;
memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1;
*s = 0xFF; s++;
libssh2_htonu32(s, password_len); s += 4;
memcpy(s, password, password_len); s += password_len;
libssh2_htonu32(s, newpw_len); s += 4;
memcpy(s, newpw, newpw_len); s += newpw_len;
if (libssh2_packet_write(session, data, data_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password-change request", 0);
LIBSSH2_FREE(session, data);
return -1;
}
LIBSSH2_FREE(session, data);
LIBSSH2_FREE(session, newpw);
/* TODO: Reset timeout? */
} else {
libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password Expired, and no callback specified", 0);
return -1;
}
}
/* TODO: Timeout? */
} }
return 0; if (data[0] == SSH_MSG_USERAUTH_SUCCESS) {
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password authentication successful");
#endif
LIBSSH2_FREE(session, data);
session->state |= LIBSSH2_STATE_AUTHENTICATED;
return 0;
}
if (data[0] == SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) {
char *newpw = NULL;
int newpw_len = 0;
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password change required");
#endif
LIBSSH2_FREE(session, data);
if (passwd_change_cb) {
passwd_change_cb(session, &newpw, &newpw_len, &session->abstract);
if (!newpw) {
libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password expired, and callback failed", 0);
return -1;
}
data_len = username_len + password_len + 44 + newpw_len; /* basic data_len + newpw_len(4) */
s = data = LIBSSH2_ALLOC(session, data_len);
if (!data) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password-change request", 0);
return -1;
}
*(s++) = SSH_MSG_USERAUTH_REQUEST;
libssh2_htonu32(s, username_len); s += 4;
memcpy(s, username, username_len); s += username_len;
libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4;
memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1;
libssh2_htonu32(s, sizeof("password") - 1); s += 4;
memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1;
*s = 0xFF; s++;
libssh2_htonu32(s, password_len); s += 4;
memcpy(s, password, password_len); s += password_len;
libssh2_htonu32(s, newpw_len); s += 4;
memcpy(s, newpw, newpw_len); s += newpw_len;
if (libssh2_packet_write(session, data, data_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password-change request", 0);
LIBSSH2_FREE(session, data);
return -1;
}
LIBSSH2_FREE(session, data);
LIBSSH2_FREE(session, newpw);
/* Ugliest use of goto ever. Blame it on the askN => requirev migration. */
goto password_response;
} else {
libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password Expired, and no callback specified", 0);
return -1;
}
}
/* FAILURE */
LIBSSH2_FREE(session, data);
return -1;
} }
/* }}} */ /* }}} */
@@ -232,6 +241,9 @@ static int libssh2_file_read_publickey(LIBSSH2_SESSION *session, unsigned char *
char *pubkey = NULL, c, *sp1, *sp2, *tmp; char *pubkey = NULL, c, *sp1, *sp2, *tmp;
int pubkey_len = 0, tmp_len; int pubkey_len = 0, tmp_len;
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Loading public key file: %s", pubkeyfile);
#endif
/* Read Public Key */ /* Read Public Key */
fd = fopen(pubkeyfile, "r"); fd = fopen(pubkeyfile, "r");
if (!fd) { if (!fd) {
@@ -307,6 +319,9 @@ static int libssh2_file_read_privatekey(LIBSSH2_SESSION *session, LIBSSH2_HOSTKE
{ {
LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail = libssh2_hostkey_methods(); LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail = libssh2_hostkey_methods();
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Loading private key file: %s", privkeyfile);
#endif
*hostkey_method = NULL; *hostkey_method = NULL;
*hostkey_abstract = NULL; *hostkey_abstract = NULL;
while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) { while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) {
@@ -344,8 +359,8 @@ LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session,
void *abstract; void *abstract;
unsigned char buf[5]; unsigned char buf[5];
struct iovec datavec[4]; struct iovec datavec[4];
unsigned char *method, *pubkeydata, *packet, *s, *sig; unsigned char *method, *pubkeydata, *packet, *s, *sig, *data, reply_codes[3] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 };
unsigned long method_len, pubkeydata_len, packet_len, sig_len; unsigned long method_len, pubkeydata_len, packet_len, sig_len, data_len;
if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) { if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) {
return -1; return -1;
@@ -431,6 +446,9 @@ LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session,
memcpy(s, sig, sig_len); s += sig_len; memcpy(s, sig, sig_len); s += sig_len;
LIBSSH2_FREE(session, sig); LIBSSH2_FREE(session, sig);
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting hostbased authentication");
#endif
if (libssh2_packet_write(session, packet, s - packet)) { if (libssh2_packet_write(session, packet, s - packet)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-hostbased request", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-hostbased request", 0);
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
@@ -438,27 +456,24 @@ LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session,
} }
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
while (1) { if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
unsigned char *data; return -1;
unsigned long data_len;
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_SUCCESS, &data, &data_len, 1) == 0) {
/* We are us and we've proved it. */
LIBSSH2_FREE(session, data);
session->state |= LIBSSH2_STATE_AUTHENTICATED;
return 0;
}
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_FAILURE, &data, &data_len, 0) == 0) {
/* This public key is not allowed for this user on this server */
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0);
return -1;
}
/* TODO: Timeout? */
} }
return 0; if (data[0] == SSH_MSG_USERAUTH_SUCCESS) {
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Hostbased authentication successful");
#endif
/* We are us and we've proved it. */
LIBSSH2_FREE(session, data);
session->state |= LIBSSH2_STATE_AUTHENTICATED;
return 0;
}
/* This public key is not allowed for this user on this server */
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0);
return -1;
} }
/* }}} */ /* }}} */
@@ -473,8 +488,9 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
void *abstract; void *abstract;
unsigned char buf[5]; unsigned char buf[5];
struct iovec datavec[4]; struct iovec datavec[4];
unsigned char *method, *pubkeydata, *packet, *s, *b, *sig; unsigned char *method, *pubkeydata, *packet, *s, *b, *sig, *data;
unsigned long method_len, pubkeydata_len, packet_len, sig_len; unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PK_OK, 0 };
unsigned long method_len, pubkeydata_len, packet_len, sig_len, data_len;
if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) { if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) {
return -1; return -1;
@@ -507,6 +523,9 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
libssh2_htonu32(s, pubkeydata_len); s += 4; libssh2_htonu32(s, pubkeydata_len); s += 4;
memcpy(s, pubkeydata, pubkeydata_len); s += pubkeydata_len; memcpy(s, pubkeydata, pubkeydata_len); s += pubkeydata_len;
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication");
#endif
if (libssh2_packet_write(session, packet, packet_len)) { if (libssh2_packet_write(session, packet, packet_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0);
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
@@ -515,45 +534,38 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
return -1; return -1;
} }
while (1) { if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
unsigned char *data; LIBSSH2_FREE(session, packet);
unsigned long data_len; LIBSSH2_FREE(session, method);
LIBSSH2_FREE(session, pubkeydata);
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_SUCCESS, &data, &data_len, 1) == 0) { return -1;
/* God help any SSH server that allows an UNVERIFIED public key to validate the user */
LIBSSH2_FREE(session, data);
LIBSSH2_FREE(session, packet);
LIBSSH2_FREE(session, method);
LIBSSH2_FREE(session, pubkeydata);
session->state |= LIBSSH2_STATE_AUTHENTICATED;
return 0;
}
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_FAILURE, &data, &data_len, 0) == 0) {
/* This public key is not allowed for this user on this server */
LIBSSH2_FREE(session, data);
LIBSSH2_FREE(session, packet);
LIBSSH2_FREE(session, method);
LIBSSH2_FREE(session, pubkeydata);
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED, "Username/PublicKey combination invalid", 0);
return -1;
}
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_PK_OK, &data, &data_len, 0) == 0) {
/* Semi-Success! */
if ((libssh2_ntohu32(data + 1) != method_len) ||
strncmp(data + 5, method, method_len) ||
(libssh2_ntohu32(data + 5 + method_len) != pubkeydata_len) ||
strncmp(data + 5 + method_len + 4, pubkeydata, pubkeydata_len)) {
/* Unlikely but possible, the server has responded to a different userauth public key request */
LIBSSH2_FREE(session, data);
continue;
}
LIBSSH2_FREE(session, data);
break;
}
/* TODO: Timeout? */
} }
if (data[0] == SSH_MSG_USERAUTH_SUCCESS) {
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Pubkey authentication prematurely successful");
#endif
/* God help any SSH server that allows an UNVERIFIED public key to validate the user */
LIBSSH2_FREE(session, data);
LIBSSH2_FREE(session, packet);
LIBSSH2_FREE(session, method);
LIBSSH2_FREE(session, pubkeydata);
session->state |= LIBSSH2_STATE_AUTHENTICATED;
return 0;
}
if (data[0] == SSH_MSG_USERAUTH_FAILURE) {
/* This public key is not allowed for this user on this server */
LIBSSH2_FREE(session, data);
LIBSSH2_FREE(session, packet);
LIBSSH2_FREE(session, method);
LIBSSH2_FREE(session, pubkeydata);
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED, "Username/PublicKey combination invalid", 0);
return -1;
}
/* Semi-Success! */
LIBSSH2_FREE(session, data);
LIBSSH2_FREE(session, pubkeydata); LIBSSH2_FREE(session, pubkeydata);
if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, method, method_len, privatekey, passphrase)) { if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, method, method_len, privatekey, passphrase)) {
@@ -607,6 +619,9 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
memcpy(s, sig, sig_len); s += sig_len; memcpy(s, sig, sig_len); s += sig_len;
LIBSSH2_FREE(session, sig); LIBSSH2_FREE(session, sig);
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication -- phase 2");
#endif
if (libssh2_packet_write(session, packet, s - packet)) { if (libssh2_packet_write(session, packet, s - packet)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0);
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
@@ -614,26 +629,26 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
} }
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
while (1) { /* PK_OK is no longer valid */
unsigned char *data; reply_codes[2] = 0;
unsigned long data_len;
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_SUCCESS, &data, &data_len, 1) == 0) { if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
/* We are us and we've proved it. */ return -1;
LIBSSH2_FREE(session, data);
session->state |= LIBSSH2_STATE_AUTHENTICATED;
return 0;
}
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_FAILURE, &data, &data_len, 0) == 0) {
/* This public key is not allowed for this user on this server */
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0);
return -1;
}
/* TODO: Timeout? */
} }
return 0; if (data[0] == SSH_MSG_USERAUTH_SUCCESS) {
#ifdef LIBSSH2_DEBUG_USERAUTH
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Publickey authentication successful");
#endif
/* We are us and we've proved it. */
LIBSSH2_FREE(session, data);
session->state |= LIBSSH2_STATE_AUTHENTICATED;
return 0;
}
/* This public key is not allowed for this user on this server */
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0);
return -1;
} }
/* }}} */ /* }}} */

View File

@@ -1,19 +0,0 @@
LIBS = -L../src -lssh2
CFLAGS = -c -I../include
CC = gcc -Wall -g
OBJS = main.o util.o methods.o auth.o forward.o
all: libssh2-test
libssh2-test: $(OBJS)
$(CC) -o libssh2-test $(OBJS) $(LIBS)
main.o: main.c libssh2-test.h
util.o: util.c libssh2-test.h
methods.o: methods.c libssh2-test.h
auth.o: auth.c libssh2-test.h
forward.o: forward.c libssh2-test.h
clean:
rm -f $(OBJS) libssh2-test

View File

@@ -1,58 +0,0 @@
This directory contains some code meant to test libssh2's protocol compliance
against other implementations, as well as providing a body of example code.
The following tests are done:
* methods.c:
Tries a connection + authentication with every possible key exchange,
hostkey, encryption, mac and compression method (see
libssh2_session_method_pref()).
The first connection is done with methods that are required to be supported
by server implementations according to the standard: key exchange method
"diffie-hellman-group1-sha1", hostkey method "ssh-dss", encryption method
"3des-cbc", mac method "hmac-sha1" and no compression.
Then every method is tried in turn, with all the other methods set to these
"known good" methods. Hence, the test will report exactly which method of
which method type failed to initialize a connection. A total of 26 tests
is performed.
* auth.c:
This test tries to authenticate with username+pasword, and public/private
key + passphrase. In addition, the hostkey is retrieved in both MD5 and SHA1
formats, and sanity-checked.
* forward.c:
This test tries to make the remote host listen on a port, then requests a
tunnel through the remote host to that port, accepting the connection
locally. A block of data is then sent through the tunnel, and this data is
then compared to the original when received.
This is tried four times: one each for every combination of the remote
host's bind address provided or not, and bind port provided or not.
If you want to add a test, you will need to do the following:
* create a new source file, and add it to the Makefile
* call your main function (runtest_yourtest) from main() in main.c
* at the top of your source file, put:
#include <libssh2.h>
#include "libssh2-test.h"
extern struct authdefs auth;
* call init_test("description of your test", number_of_steps)
* use new_socket() to get a connected socket, and log_line() to log
errors if your test fails.
* before every step in your test, call increase_progress()
* after every step, if it was successful, call step_successful()
* after your test, print a "\n".

View File

@@ -1,215 +0,0 @@
/*
* auth.c -- test authentication methods
*
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libssh2.h>
#include "libssh2-test.h"
extern struct authdefs auth;
static int auth_publickey(LIBSSH2_SESSION *session)
{
if(libssh2_userauth_publickey_fromfile(session, auth.username,
auth.pubkey, auth.privkey, auth.passphrase))
{
log_line(ERROR, "Public key authentication failed\n");
return(0);
}
if(!libssh2_userauth_authenticated(session))
{
log_line(ERROR, "Public key authentication succeeded, but authentication not set\n");
return(0);
}
return(1);
}
static int auth_password(LIBSSH2_SESSION *session)
{
if(libssh2_userauth_password(session, auth.username, auth.password))
{
log_line(ERROR, "Password authentication failed\n");
return(0);
}
if(!libssh2_userauth_authenticated(session))
{
log_line(ERROR, "Password authentication succeeded, but authentication not set\n");
return(0);
}
return(1);
}
static void all_auth(void)
{
LIBSSH2_SESSION *session;
int sock, size, res, sum, i;
unsigned char *hash;
char *errmsg, *_authlist, *authlist, *authmethod, *sep;
authlist = NULL;
authmethod = "";
while(authmethod)
{
sock = new_socket();
if(sock == -1)
{
log_line(ERROR, "new_socket() failed\n");
return;
}
session = libssh2_session_init();
res = libssh2_session_startup(session, sock);
if(res)
{
libssh2_session_last_error(session, &errmsg, &size, 0);
log_line(ERROR, "session_startup() failed: %s\n", errmsg);
close(sock);
return;
}
if(!authlist)
{
_authlist = libssh2_userauth_list(session, auth.username, strlen(auth.username));
if(_authlist == NULL)
{
libssh2_session_last_error(session, &errmsg, &size, 0);
log_line(ERROR, "userauth_list() failed: %s\n", errmsg);
libssh2_session_disconnect(session, "All done.");
libssh2_session_free(session);
close(sock);
return;
}
authlist = strdup(_authlist);
authmethod = authlist;
/* only need to check hostkey hashes once... might as well do that here */
increase_progress();
hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
if(hash)
{
sum = 0;
for(i = 0; i < 16; i++)
sum += hash[i];
if(sum > 0)
step_successful();
else
log_line(ERROR, "MD5 hostkey hash invalid\n");
}
else
log_line(ERROR, "MD5 hostkey hash failed\n");
increase_progress();
hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
if(hash)
{
sum = 0;
for(i = 0; i < 20; i++)
sum += hash[i];
if(sum > 0)
step_successful();
else
log_line(ERROR, "SHA1 hostkey hash invalid\n");
}
else
log_line(ERROR, "SHA1 hostkey hash failed\n");
}
if( (sep = strchr(authmethod, ',')) )
*sep++ = '\0';
if(!strcasecmp(authmethod, "publickey"))
{
increase_progress();
if(auth_publickey(session))
step_successful();
}
else if(!strcasecmp(authmethod, "password"))
{
increase_progress();
if(auth_password(session))
step_successful();
}
else if(!strcasecmp(authmethod, "keyboard-interactive"))
{
/* no idea how to test this */
}
else
{
log_line(DEBUG, "Unknown authentication method %s\n", authmethod);
}
authmethod = sep;
libssh2_session_disconnect(session, "All done.");
libssh2_session_free(session);
close(sock);
}
free(authlist);
printf("\n");
}
void runtest_auth(void)
{
init_test("authentication", 4);
all_auth();
}

View File

@@ -1,272 +0,0 @@
/*
* forward.c -- ssh2 port forwarding test
*
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <wordexp.h>
#include <libssh2.h>
#include "libssh2-test.h"
struct authdefs auth;
extern struct addrinfo *cur_ai;
#define LISTEN_PORT 22617
#define TESTBUF_SIZE 10
static int loopback(LIBSSH2_SESSION *session, int hostbind, int portbind)
{
LIBSSH2_CHANNEL *inbound, *outbound;
LIBSSH2_LISTENER *listener;
int listen_port, size, i, res;
char paramstr[64], ipstr[128], *errmsg, *host, *sendbuf, *recvbuf;
snprintf(paramstr, 64, "(%shost bind, %sport bind)", hostbind ? "" : "no ", portbind ? "" : "no ");
host = NULL;
if(hostbind)
{
if(getnameinfo(cur_ai->ai_addr, cur_ai->ai_addrlen, ipstr, sizeof(ipstr), NULL, 0, NI_NUMERICHOST))
{
log_line(ERROR, "getnameinfo() failed\n");
return(0);
}
host = ipstr;
}
listen_port = 0;
if(portbind)
listen_port = LISTEN_PORT;
listener = libssh2_channel_forward_listen_ex(session, host, listen_port, &listen_port, 2);
if(!listener)
{
libssh2_session_last_error(session, &errmsg, &size, 0);
log_line(ERROR, "Listen failed %s: %s\n", paramstr, errmsg);
return(0);
}
outbound = libssh2_channel_direct_tcpip(session, auth.hostname, listen_port);
if(!outbound)
{
libssh2_session_last_error(session, &errmsg, &size, 0);
log_line(ERROR, "Outbound channel setup failed %s: %s\n", paramstr, errmsg);
libssh2_channel_forward_cancel(listener);
return(0);
}
inbound = libssh2_channel_forward_accept(listener);
if(!inbound)
{
libssh2_session_last_error(session, &errmsg, &size, 0);
log_line(ERROR, "Forwarding channel accept failed %s: %s\n", paramstr, errmsg);
libssh2_channel_free(outbound);
libssh2_channel_forward_cancel(listener);
return(0);
}
sendbuf = malloc(TESTBUF_SIZE);
if(!sendbuf)
{
log_line(ERROR, "sendbuf malloc failed\n");
libssh2_channel_free(inbound);
libssh2_channel_free(outbound);
libssh2_channel_forward_cancel(listener);
return(0);
}
for(i = 0; i < TESTBUF_SIZE; i++)
sendbuf[i] = (char) random;
res = libssh2_channel_write(outbound, sendbuf, TESTBUF_SIZE);
if(res != TESTBUF_SIZE)
{
if(res == -1)
libssh2_session_last_error(session, &errmsg, &size, 0);
else
errmsg = NULL;
log_line(ERROR, "Unable to send %d bytes across tunnel %s%s%s\n",
TESTBUF_SIZE, paramstr, errmsg ? ": " : "", errmsg ? errmsg : "");
free(sendbuf);
libssh2_channel_free(inbound);
libssh2_channel_free(outbound);
libssh2_channel_forward_cancel(listener);
return(0);
}
recvbuf = malloc(TESTBUF_SIZE);
if(!recvbuf)
{
log_line(ERROR, "recvbuf malloc failed\n");
free(sendbuf);
libssh2_channel_free(inbound);
libssh2_channel_free(outbound);
libssh2_channel_forward_cancel(listener);
return(0);
}
res = libssh2_channel_read(inbound, recvbuf, TESTBUF_SIZE);
if(res != TESTBUF_SIZE)
{
if(res == -1)
libssh2_session_last_error(session, &errmsg, &size, 0);
else
errmsg = NULL;
log_line(ERROR, "Unable to receive %d bytes across tunnel %s%s%s\n",
TESTBUF_SIZE, paramstr, errmsg ? ": " : "", errmsg ? errmsg : "");
free(sendbuf);
free(recvbuf);
libssh2_channel_free(inbound);
libssh2_channel_free(outbound);
libssh2_channel_forward_cancel(listener);
return(0);
}
res = 1;
for(i = 0; i < TESTBUF_SIZE; i++)
{
if(recvbuf[i] != sendbuf[i])
{
log_line(ERROR, "Received data did not match sent data %s\n", paramstr);
res = 0;
break;
}
}
free(sendbuf);
free(recvbuf);
libssh2_channel_free(inbound);
libssh2_channel_free(outbound);
libssh2_channel_forward_cancel(listener);
return(res);
}
static void all_forward(void)
{
LIBSSH2_SESSION *session;
int sock, res, size;
char *errmsg;
sock = new_socket();
if(sock == -1)
{
log_line(ERROR, "Unable to open a socket\n");
return;
}
session = libssh2_session_init();
res = libssh2_session_startup(session, sock);
if(res)
{
libssh2_session_last_error(session, &errmsg, &size, 0);
log_line(ERROR, "Session startup failed: %s\n", errmsg);
close(sock);
return;
}
if(libssh2_userauth_password(session, auth.username, auth.password))
{
libssh2_session_last_error(session, &errmsg, &size, 0);
log_line(ERROR, "Authentication failed%s%s\n", errmsg[0] ? ": " : "", errmsg);
libssh2_session_disconnect(session, "All done.");
libssh2_session_free(session);
close(sock);
return;
}
increase_progress();
if(loopback(session, 1, 1))
step_successful();
increase_progress();
if(loopback(session, 1, 0))
step_successful();
increase_progress();
if(loopback(session, 0, 1))
step_successful();
increase_progress();
if(loopback(session, 0, 0))
step_successful();
libssh2_session_disconnect(session, "All done.");
libssh2_session_free(session);
close(sock);
printf("\n");
}
void runtest_forward(void)
{
init_test("TCP port forwarding/tunneling", 4);
all_forward();
}

View File

@@ -1,102 +0,0 @@
/*
* prototest.h
*
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#ifndef LIBSSH2_TEST_H
#define LIBSSH2_TEST_H 1
#define MAX_LOGLINE_LEN 256
void log_line(int priority, char *format, ...);
void init_test(char *msg, int num_items);
void increase_progress(void);
void step_successful(void);
void output_testresults(void);
int new_socket(void);
void runtest_methods(void);
void runtest_auth(void);
void runtest_forward(void);
struct authdefs {
char *hostname;
char *port;
char *username;
char *password;
char *privkey;
char *pubkey;
char *passphrase;
};
struct methodlist {
int method_type;
char *description;
char **list;
int cursor;
int done;
};
struct logentry {
int priority;
char *logline;
struct logentry *next;
};
struct logresults {
char *description;
int num_steps;
int success_steps;
int progress;
struct logentry *log;
struct logresults *next;
};
enum {
ERROR,
WARNING,
NORMAL,
DEBUG
};
#endif

View File

@@ -1,174 +0,0 @@
/*
* main.c -- ssh2 protocol compliance tester for libssh2
*
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <wordexp.h>
#include <libssh2.h>
#include "libssh2-test.h"
struct authdefs auth;
char *progress_message;
int progress, progress_max;
struct addrinfo *hostai = NULL, *cur_ai = NULL;
extern struct logresults *testlogs;
extern struct logentry *logfile;
void cleanup(void)
{
struct logresults *test, *nexttest;
struct logentry *logfile, *nextlog;
if(hostai)
freeaddrinfo(hostai);
test = testlogs;
while(test)
{
logfile = test->log;
while(logfile)
{
nextlog = logfile->next;
free(logfile->logline);
free(logfile);
logfile = nextlog;
}
nexttest = test->next;
free(test);
test = nexttest;
}
free(auth.hostname);
free(auth.port);
free(auth.username);
free(auth.password);
free(auth.privkey);
free(auth.pubkey);
free(auth.passphrase);
}
char *get_interactive(char *prompt, int size, char *default_value)
{
char *str;
if( !(str = malloc(size)) )
{
log_line(ERROR, "unable to malloc %d bytes for %s\n", size, prompt);
return(NULL);
}
printf("%s [%s]: ", prompt, default_value);
fgets(str, size, stdin);
if(str[strlen(str)-1] == '\n')
str[strlen(str)-1] = 0;
if(!str[0])
strncpy(str, default_value, size);
return(str);
}
char *resolve_tilde(char *path)
{
wordexp_t we;
if( (wordexp(path, &we, 0)) == 0 && we.we_wordc == 1)
{
free(path);
path = strdup(we.we_wordv[0]);
wordfree(&we);
}
return(path);
}
void get_auth(void)
{
auth.hostname = get_interactive("hostname", 64, "localhost");
auth.port = get_interactive("port", 6, "22");
// auth.username = get_interactive("username", 20, getenv("USER"));
// auth.password = get_interactive("password", 20, "");
auth.username = get_interactive("username", 20, "bert2");
auth.password = get_interactive("password", 20, "blinko");
auth.privkey = resolve_tilde(get_interactive("private key filename", 128, "~/.ssh/id_dsa"));
auth.pubkey = resolve_tilde(get_interactive("public key filename", 128, "~/.ssh/id_dsa.pub"));
auth.passphrase = get_interactive("passphrase", 256, "");
}
int main(int argc, char **argv)
{
get_auth();
if(!strlen(auth.username) || !strlen(auth.password))
{
printf("Not enough authentication info to continue.\n");
return(1);
}
runtest_methods();
runtest_auth();
runtest_forward();
output_testresults();
cleanup();
return(0);
}

View File

@@ -1,214 +0,0 @@
/*
* methods.c -- test all available key exchange, hostkey, encryption, mac
* and compression methods
*
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <unistd.h>
#include <libssh2.h>
#include "libssh2-test.h"
extern struct authdefs auth;
static char *kex_methods[] = {
"diffie-hellman-group1-sha1",
"diffie-hellman-group14-sha1",
"diffie-hellman-group-exchange-sha1",
NULL
};
static char *hostkey_methods[] = {
"ssh-dss",
"ssh-rsa",
NULL
};
static char *crypt_methods[] = {
"3des-cbc",
"aes256-cbc",
"aes192-cbc",
"aes128-cbc",
"blowfish-cbc",
"arcfour",
"cast128-cbc",
NULL
};
static char *mac_methods[] = {
"hmac-sha1",
"hmac-sha1-96",
"hmac-md5",
"hmac-md5-96",
"hmac-ripemd160",
NULL
};
static char *compression_methods[] = {
"none",
"zlib",
NULL
};
static struct methodlist methods[] = {
{ LIBSSH2_METHOD_KEX, "kex", kex_methods, 0, 0 },
{ LIBSSH2_METHOD_HOSTKEY, "hostkey", hostkey_methods, 0, 0 },
{ LIBSSH2_METHOD_CRYPT_CS, "crypt (cs)", crypt_methods, 0, 0 },
{ LIBSSH2_METHOD_CRYPT_SC, "crypt (sc)", crypt_methods, 0, 0 },
{ LIBSSH2_METHOD_MAC_CS, "MAC (cs)", mac_methods, 0, 0 },
{ LIBSSH2_METHOD_MAC_SC, "MAC (sc)", mac_methods, 0, 0 },
{ LIBSSH2_METHOD_COMP_CS, "compression (cs)", compression_methods, 0, 0 },
{ LIBSSH2_METHOD_COMP_SC, "compression (sc)", compression_methods, 0, 0 },
{ 0, NULL, NULL, 0, 0 }
};
/*
static void dump_methods(LIBSSH2_SESSION *session)
{
printf(" Key exchange methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_KEX));
printf(" Hostkey methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_HOSTKEY));
printf(" Crypt C->S methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_CS));
printf(" Crypt S->C methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_SC));
printf(" MAC C->S methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_MAC_CS));
printf(" MAC S->C methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_MAC_SC));
printf("Compression C->S methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_COMP_CS));
printf("Compression S->C methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_COMP_SC));
}
*/
static void cycle_methods(void)
{
LIBSSH2_SESSION *session;
int sock, size, res, method_type, method, i, methods_set;
char *errmsg;
method_type = 0;
method = 0;
while(methods[method_type].description)
{
while(methods[method_type].list[method])
{
increase_progress();
sock = new_socket();
if(sock == -1)
{
log_line(ERROR, "new_socket() failed");
return;
}
session = libssh2_session_init();
methods_set = 1;
for(i = 0; methods[i].description; i++)
{
res = libssh2_session_method_pref(session, methods[i].method_type,
methods[i].list[ i == method_type ? method : 0 ]);
if(res != 0)
{
libssh2_session_last_error(session, &errmsg, &size, 0);
log_line(ERROR, "%s method set to '%s' failed: %s\n",
methods[i].description,
methods[i].list[ i == method_type ? method : 0 ], errmsg);
methods_set = 0;
break;
}
i++;
}
if(methods_set)
{
res = libssh2_session_startup(session, sock);
if(res == 0)
{
if(libssh2_userauth_password(session, auth.username, auth.password))
{
log_line(ERROR, "Authentication failed\n");
}
else
step_successful();
}
else
{
libssh2_session_last_error(session, &errmsg, &size, 0);
log_line(ERROR, "Session startup for %s method %s failed: %s\n",
methods[method_type].description, methods[method_type].list[method], errmsg);
}
}
libssh2_session_disconnect(session, "All done.");
libssh2_session_free(session);
close(sock);
method++;
}
method_type++;
method = 1;
}
printf("\n");
}
void runtest_methods(void)
{
int i, j, num_steps;
num_steps = 0;
for(i = 0; methods[i].description; i++)
{
for(j = 0; methods[i].list[j]; j++)
;
num_steps += j - 1;
}
num_steps++;
init_test("kex/hostkey/crypt/mac/compression methods", num_steps);
cycle_methods();
}

View File

@@ -1,222 +0,0 @@
/*
* util.c
*
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdarg.h>
#include <errno.h>
#include <libssh2.h>
#include "libssh2-test.h"
extern struct addrinfo *hostai, *cur_ai;
struct logresults *testlogs = NULL, *cur_testlog;
struct logentry *logfile = NULL, *cur_logentry;
extern struct authdefs auth;
extern char *progress_message;
extern int progress, progress_max;
struct loglevel {
int priority;
char *descr;
} loglevels[] = {
{ ERROR, "ERROR" },
{ WARNING, "WARNING" },
{ NORMAL, "NORMAL" },
{ DEBUG, "DEBUG" }
};
void log_line(int priority, char *format, ...)
{
va_list args;
struct logentry *entry;
char line[MAX_LOGLINE_LEN];
va_start(args, format);
vsnprintf(line, MAX_LOGLINE_LEN, format, args);
va_end(args);
entry = malloc(sizeof(struct logentry));
entry->priority = priority;
entry->logline = malloc(strlen(line)+1);
strcpy(entry->logline, line);
entry->next = NULL;
if(!cur_testlog->log)
cur_testlog->log = entry;
else
cur_logentry->next = entry;
cur_logentry = entry;
}
void init_test(char *msg, int num_items)
{
struct logresults *newtest;
newtest = malloc(sizeof(struct logresults));
newtest->description = msg;
newtest->num_steps = num_items;
newtest->success_steps = 0;
newtest->progress = 0;
newtest->log = NULL;
newtest->next = NULL;
if(!testlogs)
testlogs = newtest;
else
cur_testlog->next = newtest;
cur_testlog = newtest;
}
void increase_progress(void)
{
cur_testlog->progress++;
printf("Testing %s... %3d/%d\r", cur_testlog->description, cur_testlog->progress, cur_testlog->num_steps);
fflush(stdout);
}
void step_successful(void)
{
cur_testlog->success_steps++;
}
void output_testresults(void)
{
struct logresults *test;
struct logentry *logfile;
int total_steps, total_success;
printf("\nTest results\n============\n");
total_steps = 0;
total_success = 0;
test = testlogs;
while(test)
{
total_steps += test->num_steps;
total_success += test->success_steps;
printf("Test: %s (%d/%d)\n", test->description, test->success_steps, test->num_steps);
logfile = test->log;
while(logfile)
{
printf(" %s", logfile->logline);
logfile = logfile->next;
}
test = test->next;
}
printf("%d/%d steps successful\n", total_success, total_steps);
}
int new_socket(void)
{
int sock, res;
struct addrinfo hints, *ai;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
if(!hostai)
{
res = getaddrinfo(auth.hostname, auth.port, &hints, &hostai);
if(res)
{
printf("unable to resolve %s: %s\n", auth.hostname, gai_strerror(res));
return(-1);
}
}
sock = 0;
ai = hostai;
while(ai)
{
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if(sock > 0)
{
res = connect(sock, ai->ai_addr, ai->ai_addrlen);
if(res == 0)
break;
close(sock);
sock = 0;
}
ai = ai->ai_next;
}
if(!sock)
{
printf("unable to connect: %s\n", strerror(errno));
close(sock);
sock = -1;
}
cur_ai = ai;
return(sock);
}