Compare commits

...

32 Commits

Author SHA1 Message Date
Daniel Stenberg
c8cc30c996 RELEASE-NOTES: updated for 1.4.1 release 2012-04-04 22:43:11 +02:00
Daniel Stenberg
acd9bd6104 always do "forced" window updates
When calling _libssh2_channel_receive_window_adjust() internally, we now
always use the 'force' option to prevent libssh2 to avoid sending the
update if the update isn't big enough.

It isn't fully analyzed but we have seen corner cases which made a
necessary window update not get send due to this and then the other side
doesn't send data our side then sits waiting for forever.
2012-04-03 22:36:19 +02:00
Daniel Stenberg
2ea40e63e8 channel_read: force window adjusts!
if there's not enough room to receive the data that's being requested,
the window adjustment needs to be sent to the remote and thus the force
option has to be used. _libssh2_channel_receive_window_adjust() would
otherwise "queue" small window adjustments for a later packet but that
is really terribly for the small buffer read that for example is the
final little piece of a very large file as then there is no logical next
packet!

Reported by: Armen Babakhanian
Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2012-03/0130.shtml
2012-03-19 22:34:04 +01:00
Paul Howarth
f4f2298ef3 aes: the init function fails when OpenSSL has AES support
The internal init function only worked fine when the configure script
didn't detect the OpenSSL AES_CTR function!

Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2012-03/0111.shtml
Reported by: Paul Howarth
2012-03-18 15:24:35 +01:00
Matthew Booth
cc4f9d5679 transport_send: Finish in-progress key exchange before sending data
_libssh2_channel_write() first reads outstanding packets before writing
new data. If it reads a key exchange request, it will immediately start
key re-exchange, which will require sending a response. If the output
socket is full, this will result in a return from
_libssh2_transport_read() of LIBSSH2_ERROR_EAGAIN. In order not to block
a write because there is no data to read, this error is explicitly
ignored and the code continues marshalling a packet for sending. When it
is sent, the remote end immediately drops the connection because it was
expecting a continuation of the key exchange, but got a data packet.

This change adds the same check for key exchange to
_libssh2_transport_send() that is in _libssh2_transport_read(). This
ensures that key exchange is completed before any data packet is sent.
2012-03-16 16:40:04 +01:00
Daniel Stenberg
fed0759720 channel_write: acknowledge transport errors
When draining data off the socket with _libssh2_transport_read() (which
in turn has to be done so that we can be sure to have read any possible
window-increasing packets), this code previously ignored errors which
could lead to nasty loops. Now all error codes except EAGAIN will cause
the error to be returned at once.

Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2012-03/0068.shtml
Reported by: Matthew Booth
2012-03-15 13:03:08 +01:00
Steven Dake
a46ef85a56 In examples/x11.c, Make sure sizeof passed to read operation is correct
sizeof(buf) expands to 8 or 4 (since its a pointer).  This variable may
have been static in the past, leading to this error.

Signed-off-by: Steven Dake <sdake@redhat.com>
2012-03-13 22:19:54 +01:00
Steven Dake
626f91da07 Fix suspicious sizeof usage in examples/x11.c
In the x11 example, sizeof(buf) = 8UL (on x86_64), when this should
probably represent the buffer size available.  I am not sure how to
test that this change is actually correct, however.

Signed-off-by: Steven Dake <sdake@redhat.com>
2012-03-13 22:19:18 +01:00
Daniel Stenberg
bf097e37b0 sftp_packet_read: follow-up fix for EAGAIN/window adjust
The commit in 7194a9bd7b wasn't complete. This change makes sure
variables are initialized properly before used in the EAGAIN and window
adjust cases.
2012-03-13 22:02:14 +01:00
Daniel Stenberg
a1c996cef1 sftp_packet_add: use named error code instead of number 2012-03-13 22:01:33 +01:00
Daniel Stenberg
8fc16beda9 sftp_packet_add: verify the packet before accepting it
In order to bail out as quickly as possible when things are wrong and
out of sync, make sure the SFTP message is one we understand.
2012-03-13 21:59:59 +01:00
Daniel Stenberg
9a42fac055 SFTP: preserve the original error code more
Lots of places in the code translated the original error into the more
generic LIBSSH2_ERROR_SOCKET_TIMEOUT but this turns out to distort the
original error reason a lot and makes tracking down the real origin of a
problem really hard. This change makes the original error code be
preserved to a larger extent when return up to the parent function.
2012-03-12 22:52:49 +01:00
Daniel Stenberg
7194a9bd7b sftp_packet_read: adjust window size as necessary
Commit 03ca902075 tried to simplify the window sizing logic but broke
SFTP readdir as there was no window sizing code left there so large
directory listings no longer worked.

This change introduces window sizing logic to the sftp_packet_read()
function so that it now tells the remote about the local size having a
window size that suffice when it is about to ask for directory data.

Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2012-03/0069.shtml
Reported by: Eric
2012-03-12 22:49:25 +01:00
Steven Dake
7e53949e66 Tell C compiler we don't care about return code of libssh2_init
The call of libssh2_init returns a return code, but nothing could be done
within the _libssh2_init_if_needed execution path.

Signed-off-by: Steven Dake <sdake@redhat.com>
2012-03-11 23:22:05 +01:00
Steven Dake
5672e8f4cf Add comment indicating a resource leak is not really a resource leak
While possibly obvious to those investigating the code, coverity complains
about this out of scope leak.

Signed-off-by: Steven Dake <sdake@redhat.com>
2012-03-06 23:25:46 +01:00
Steven Dake
4449905ea5 Use safer snprintf rather then sprintf in scp_send()
Signed-off-by: Steven Dake <sdake@redhat.com>
2012-03-06 23:24:32 +01:00
Steven Dake
8e0cddd01f Use safer snprintf rather then sprintf in scp_recv()
While the buffer is indeed allocated to a safe length, better safe then sorry.

Signed-off-by: Steven Dake <sdake@redhat.com>
2012-03-06 23:23:21 +01:00
Steven Dake
796a5195d3 use snprintf in knownhost_writeline() rather then sprintf
Although the function checks the length, if the code was in error, there
could potentially be a buffer overrun with the use of sprintf.  Instead replace
with snprintf.

Signed-off-by: Steven Dake <sdake@redhat.com>
2012-03-06 23:21:59 +01:00
Steven Dake
15aadb930d Add tracing to print packets left on session at libssh2_session_free
Signed-off-by: Steven Dake <sdake@redhat.com>
2012-03-05 20:04:57 +01:00
Peter Stuge
f28264c6a3 Define and use LIBSSH2_INVALID_SOCKET instead of INVALID_SOCKET
INVALID_SOCKET is a special value in Windows representing a
non-valid socket identifier. We were #defining this to -1 on
non-Windows platforms, causing unneccessary namespace pollution.
Let's have our own identifier instead.

Thanks to Matt Lawson for pointing this out.
2012-03-02 17:36:52 +01:00
Peter Stuge
179d2197dd nw/Makefile.netware: Fix project name typo to avoid needless confusion 2012-02-29 22:31:28 +01:00
Peter Stuge
aa8f2cbf33 example/x11: Set raw terminal mode manually instead of with cfmakeraw()
OpenSolaris has no cfmakeraw() so to make the example more portable
we simply do the equivalent operations on struct termios ourselves.

Thanks to Tom Weber for reporting this problem, and finding a solution.
2012-02-29 22:27:18 +01:00
Daniel Stenberg
e07342443f sftp_write: cannot return acked data *and* EAGAIN
Whenever we have acked data and is about to call a function that *MAY*
return EAGAIN we must return the number now and wait to get called
again. Our API only allows data *or* EAGAIN and we must never try to get
both.
2012-02-17 16:06:21 +01:00
Peter Stuge
4774d500e7 example/x11: Build only when sys/un.h is found by configure
The example can't be built on systems without AF_UNIX sockets.
2012-02-13 00:40:24 +01:00
Alexander Lamaison
1403847429 Simplified sftp_read.
Removed the total_read variable that originally must have tracked how
much data had been written to the buffer.  With non-blocking reads, we
must return straight away once we have read data into the buffer so this
variable served not purpose.

I think it was still hanging around in case the initial processing of
'leftover' data meant we wrote to the buffer but this case, like the
others, must return immediately.  Now that it does, the last remaining
need for the variable is gone.
2012-02-10 16:31:38 +01:00
Alexander Lamaison
0d824e5702 Cleaned up sftp_read and added more explanation.
Replaced the gotos which were implementing the state machine with
a switch statement which makes the states more explicit.
2012-02-10 16:31:38 +01:00
Daniel Stenberg
9836b0889f sftp_read: avoid data *and* EAGAIN
Whenever we have data and is about to call a function that *MAY* return
EAGAIN we must return the data now and wait to get called again. Our API
only allows data *or* EAGAIN and we must never try to get both.
2012-02-10 16:31:38 +01:00
Peter Stuge
e394987911 Add a tcpip-forward example which demonstrates remote port forwarding 2012-02-02 17:25:45 +01:00
Peter Stuge
e95c7de453 libssh2.h: Add missing prototype for libssh2_session_banner_set() 2012-02-01 11:35:05 +01:00
Peter Stuge
effbb72192 example/subsystem_netconf.c: Return error when read buffer is too small
Also remove a little redundancy in the read loop condition.
2012-02-01 11:35:05 +01:00
Peter Stuge
0ebe6f44bd example/subsystem_netconf.c: Add a missing newline in an error message 2012-02-01 11:35:00 +01:00
Peter Stuge
b3ade9a63e Fix undefined reference to _libssh_error in libgcrypt backend
Commit 209de22299 introduced a function
call to a non-existing function, and since then the libgcrypt backend
has not been buildable.
2012-02-01 09:53:44 +01:00
22 changed files with 988 additions and 530 deletions

View File

@@ -1,30 +1,28 @@
libssh2 1.4.0
This release includes the following changes:
o Added libssh2_session_supported_algs()
o Added libssh2_session_banner_get()
o Added libssh2_sftp_get_channel()
o libssh2.h: bump the default window size to 256K
libssh2 1.4.1
This release includes the following bugfixes:
o sftp-seek: clear EOF flag
o userauth: Provide more informations if ssh pub key extraction fails
o ssh2_exec: skip error outputs for EAGAIN
o LIBSSH2_SFTP_PACKET_MAXLEN: increase to 80000
o knownhost_check(): Don't dereference ext if NULL is passed
o knownhost_add: Avoid dereferencing uninitialized memory on error path
o OpenSSL EVP: fix threaded use of structs
o _libssh2_channel_read: react on errors from receive_window_adjust
o sftp_read: cap the read ahead maximum amount
o _libssh2_channel_read: fix non-blocking window adjusting
o build error with gcrypt backend
o always do "forced" window updates to avoid corner case stalls
o aes: the init function fails when OpenSSL has AES support
o transport_send: Finish in-progress key exchange before sending data
o channel_write: acknowledge transport errors
o examples/x11.c: Make sure sizeof passed to read operation is correct
o examples/x11.c:,Fix suspicious sizeof usage
o sftp_packet_add: verify the packet before accepting it
o SFTP: preserve the original error code more
o sftp_packet_read: adjust window size as necessary
o Use safer snprintf rather then sprintf in several places
o Define and use LIBSSH2_INVALID_SOCKET instead of INVALID_SOCKET
o sftp_write: cannot return acked data *and* EAGAIN
o sftp_read: avoid data *and* EAGAIN
o libssh2.h: Add missing prototype for libssh2_session_banner_set()
This release would not have looked like this without help, code, reports and
advice from friends like these:
Gellule Xg, Jernej Kovacic, Peter Krempa, Vincent Torri, Kamil Dudka,
Peter Stuge, Guenter Knauf, John Engstrom, Alexander Lamaison,
Mikhail Gusarov
Armen Babakhanian, Paul Howarth, Matthew Booth, Steven Dake, Peter Stuge,
Matt Lawson, Tom Weber, Alexander Lamaison
Thanks! (and sorry if I forgot to mention someone)

View File

@@ -220,7 +220,8 @@ AC_HELP_STRING([--disable-hidden-symbols],[Leave all symbols with default visibi
AC_CHECK_HEADERS([errno.h fcntl.h stdio.h stdlib.h unistd.h sys/uio.h])
AC_CHECK_HEADERS([sys/select.h sys/socket.h sys/ioctl.h sys/time.h])
AC_CHECK_HEADERS([arpa/inet.h netinet/in.h])
AC_CHECK_HEADERS([sys/un.h])
AC_CHECK_HEADERS([sys/un.h], [have_sys_un_h=yes], [have_sys_un_h=no])
AM_CONDITIONAL([HAVE_SYS_UN_H], test "x$have_sys_un_h" = xyes)
case $host in
*-*-cygwin* | *-*-cegcc*)

1
example/.gitignore vendored
View File

@@ -33,4 +33,5 @@ test-sftp_nonblock
test-sftp_write
test-sftp_write_nonblock
test-ssh2_echo
tcpip-forward
x11

View File

@@ -7,7 +7,11 @@ noinst_PROGRAMS = direct_tcpip ssh2 scp scp_nonblock scp_write \
scp_write_nonblock sftp sftp_nonblock sftp_write sftp_write_nonblock \
sftp_mkdir sftp_mkdir_nonblock sftp_RW_nonblock sftp_write_sliding \
sftpdir sftpdir_nonblock ssh2_exec ssh2_agent ssh2_echo sftp_append \
subsystem_netconf x11
subsystem_netconf tcpip-forward
if HAVE_SYS_UN_H
noinst_PROGRAMS += x11
endif
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/example
LDADD = $(top_builddir)/src/libssh2.la

View File

@@ -61,7 +61,8 @@ static int netconf_write(LIBSSH2_CHANNEL *channel, const char *buf, size_t len)
static int netconf_read_until(LIBSSH2_CHANNEL *channel, const char *endtag,
char *buf, size_t buflen)
{
ssize_t len, rd = 0;
ssize_t len;
size_t rd = 0;
char *endreply = NULL, *specialsequence = NULL;
memset(buf, 0, buflen);
@@ -71,7 +72,7 @@ static int netconf_read_until(LIBSSH2_CHANNEL *channel, const char *endtag,
if (LIBSSH2_ERROR_EAGAIN == len)
continue;
else if (len < 0) {
fprintf(stderr, "libssh2_channel_read: %d", (int)len);
fprintf(stderr, "libssh2_channel_read: %d\n", (int)len);
return -1;
}
rd += len;
@@ -85,7 +86,12 @@ static int netconf_read_until(LIBSSH2_CHANNEL *channel, const char *endtag,
if (endreply)
specialsequence = strstr(endreply, "]]>]]>");
} while (!endreply || !specialsequence);
} while (!specialsequence && rd < buflen);
if (!specialsequence) {
fprintf(stderr, "%s: ]]>]]> not found! read buffer too small?\n", __func__);
return -1;
}
/* discard the special sequence so that only XML is returned */
rd = specialsequence - buf;

292
example/tcpip-forward.c Normal file
View File

@@ -0,0 +1,292 @@
#include "libssh2_config.h"
#include <libssh2.h>
#ifdef WIN32
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#endif
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifndef INADDR_NONE
#define INADDR_NONE (in_addr_t)-1
#endif
const char *keyfile1 = "/home/username/.ssh/id_rsa.pub";
const char *keyfile2 = "/home/username/.ssh/id_rsa";
const char *username = "username";
const char *password = "";
const char *server_ip = "127.0.0.1";
const char *remote_listenhost = "localhost"; /* resolved by the server */
unsigned int remote_wantport = 2222;
unsigned int remote_listenport;
const char *local_destip = "127.0.0.1";
unsigned int local_destport = 22;
enum {
AUTH_NONE = 0,
AUTH_PASSWORD,
AUTH_PUBLICKEY
};
int main(int argc, char *argv[])
{
int rc, sock = -1, forwardsock = -1, i, auth = AUTH_NONE;
struct sockaddr_in sin;
socklen_t sinlen = sizeof(sin);
const char *fingerprint;
char *userauthlist;
LIBSSH2_SESSION *session;
LIBSSH2_LISTENER *listener = NULL;
LIBSSH2_CHANNEL *channel = NULL;
const char *shost;
unsigned int sport;
fd_set fds;
struct timeval tv;
ssize_t len, wr;
char buf[16384];
#ifdef WIN32
char sockopt;
WSADATA wsadata;
WSAStartup(MAKEWORD(2,0), &wsadata);
#else
int sockopt;
#endif
if (argc > 1)
server_ip = argv[1];
if (argc > 2)
username = argv[2];
if (argc > 3)
password = argv[3];
if (argc > 4)
remote_listenhost = argv[4];
if (argc > 5)
remote_wantport = atoi(argv[5]);
if (argc > 6)
local_destip = argv[6];
if (argc > 7)
local_destport = atoi(argv[7]);
rc = libssh2_init (0);
if (rc != 0) {
fprintf (stderr, "libssh2 initialization failed (%d)\n", rc);
return 1;
}
/* Connect to SSH server */
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
sin.sin_family = AF_INET;
if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(server_ip))) {
perror("inet_addr");
return -1;
}
sin.sin_port = htons(22);
if (connect(sock, (struct sockaddr*)(&sin),
sizeof(struct sockaddr_in)) != 0) {
fprintf(stderr, "failed to connect!\n");
return -1;
}
/* Create a session instance */
session = libssh2_session_init();
if(!session) {
fprintf(stderr, "Could not initialize SSH session!\n");
return -1;
}
/* ... start it up. This will trade welcome banners, exchange keys,
* and setup crypto, compression, and MAC layers
*/
rc = libssh2_session_handshake(session, sock);
if(rc) {
fprintf(stderr, "Error when starting up SSH session: %d\n", rc);
return -1;
}
/* At this point we havn't yet authenticated. The first thing to do
* is check the hostkey's fingerprint against our known hosts Your app
* may have it hard coded, may go to a file, may present it to the
* user, that's your call
*/
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
fprintf(stderr, "Fingerprint: ");
for(i = 0; i < 20; i++)
fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]);
fprintf(stderr, "\n");
/* check what authentication methods are available */
userauthlist = libssh2_userauth_list(session, username, strlen(username));
printf("Authentication methods: %s\n", userauthlist);
if (strstr(userauthlist, "password"))
auth |= AUTH_PASSWORD;
if (strstr(userauthlist, "publickey"))
auth |= AUTH_PUBLICKEY;
/* check for options */
if(argc > 8) {
if ((auth & AUTH_PASSWORD) && !strcasecmp(argv[8], "-p"))
auth = AUTH_PASSWORD;
if ((auth & AUTH_PUBLICKEY) && !strcasecmp(argv[8], "-k"))
auth = AUTH_PUBLICKEY;
}
if (auth & AUTH_PASSWORD) {
if (libssh2_userauth_password(session, username, password)) {
fprintf(stderr, "Authentication by password failed.\n");
goto shutdown;
}
} else if (auth & AUTH_PUBLICKEY) {
if (libssh2_userauth_publickey_fromfile(session, username, keyfile1,
keyfile2, password)) {
printf("\tAuthentication by public key failed!\n");
goto shutdown;
}
printf("\tAuthentication by public key succeeded.\n");
} else {
printf("No supported authentication methods found!\n");
goto shutdown;
}
printf("Asking server to listen on remote %s:%d\n", remote_listenhost,
remote_wantport);
listener = libssh2_channel_forward_listen_ex(session, remote_listenhost,
remote_wantport, &remote_listenport, 1);
if (!listener) {
fprintf(stderr, "Could not start the tcpip-forward listener!\n"
"(Note that this can be a problem at the server!"
" Please review the server logs.)\n");
goto shutdown;
}
printf("Server is listening on %s:%d\n", remote_listenhost,
remote_listenport);
printf("Waiting for remote connection\n");
channel = libssh2_channel_forward_accept(listener);
if (!channel) {
fprintf(stderr, "Could not accept connection!\n"
"(Note that this can be a problem at the server!"
" Please review the server logs.)\n");
goto shutdown;
}
printf("Accepted remote connection. Connecting to local server %s:%d\n",
local_destip, local_destport);
forwardsock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
sin.sin_family = AF_INET;
sin.sin_port = htons(local_destport);
if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(local_destip))) {
perror("inet_addr");
goto shutdown;
}
if (-1 == connect(forwardsock, (struct sockaddr *)&sin, sinlen)) {
perror("connect");
goto shutdown;
}
printf("Forwarding connection from remote %s:%d to local %s:%d\n",
remote_listenhost, remote_listenport, local_destip, local_destport);
/* Must use non-blocking IO hereafter due to the current libssh2 API */
libssh2_session_set_blocking(session, 0);
while (1) {
FD_ZERO(&fds);
FD_SET(forwardsock, &fds);
tv.tv_sec = 0;
tv.tv_usec = 100000;
rc = select(forwardsock + 1, &fds, NULL, NULL, &tv);
if (-1 == rc) {
perror("select");
goto shutdown;
}
if (rc && FD_ISSET(forwardsock, &fds)) {
len = recv(forwardsock, buf, sizeof(buf), 0);
if (len < 0) {
perror("read");
goto shutdown;
} else if (0 == len) {
printf("The local server at %s:%d disconnected!\n",
local_destip, local_destport);
goto shutdown;
}
wr = 0;
do {
i = libssh2_channel_write(channel, buf, len);
if (i < 0) {
fprintf(stderr, "libssh2_channel_write: %d\n", i);
goto shutdown;
}
wr += i;
} while(i > 0 && wr < len);
}
while (1) {
len = libssh2_channel_read(channel, buf, sizeof(buf));
if (LIBSSH2_ERROR_EAGAIN == len)
break;
else if (len < 0) {
fprintf(stderr, "libssh2_channel_read: %d", (int)len);
goto shutdown;
}
wr = 0;
while (wr < len) {
i = send(forwardsock, buf + wr, len - wr, 0);
if (i <= 0) {
perror("write");
goto shutdown;
}
wr += i;
}
if (libssh2_channel_eof(channel)) {
printf("The remote client at %s:%d disconnected!\n",
remote_listenhost, remote_listenport);
goto shutdown;
}
}
}
shutdown:
#ifdef WIN32
closesocket(forwardsock);
#else
close(forwardsock);
#endif
if (channel)
libssh2_channel_free(channel);
if (listener)
libssh2_channel_forward_cancel(listener);
libssh2_session_disconnect(session, "Client disconnecting normally");
libssh2_session_free(session);
#ifdef WIN32
closesocket(sock);
#else
close(sock);
#endif
libssh2_exit();
return 0;
}

View File

@@ -79,7 +79,12 @@ static int _raw_mode(void)
rc = tcgetattr(fileno(stdin), &tio);
if (rc != -1) {
_saved_tio = tio;
cfmakeraw(&tio);
/* do the equivalent of cfmakeraw() manually, to build on Solaris */
tio.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
tio.c_oflag &= ~OPOST;
tio.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
tio.c_cflag &= ~(CSIZE|PARENB);
tio.c_cflag |= CS8;
rc = tcsetattr(fileno(stdin), TCSADRAIN, &tio);
}
return rc;
@@ -203,7 +208,7 @@ static int x11_send_receive(LIBSSH2_CHANNEL *channel, int sock)
rc = libssh2_poll(fds, nfds, 0);
if (rc >0) {
rc = libssh2_channel_read(channel, buf,sizeof(buf));
rc = libssh2_channel_read(channel, buf, bufsize);
rc = write(sock, buf, rc);
}
@@ -212,7 +217,7 @@ static int x11_send_receive(LIBSSH2_CHANNEL *channel, int sock)
memset((void *)buf,0,bufsize);
/* Data in sock*/
rc = read(sock, buf,sizeof(buf));
rc = read(sock, buf, bufsize);
if (rc > 0)
rc = libssh2_channel_write(channel,buf, rc);
else

View File

@@ -135,9 +135,10 @@ typedef long long libssh2_int64_t;
#ifdef WIN32
typedef SOCKET libssh2_socket_t;
#define LIBSSH2_INVALID_SOCKET INVALID_SOCKET
#else /* !WIN32 */
typedef int libssh2_socket_t;
#define INVALID_SOCKET -1
#define LIBSSH2_INVALID_SOCKET -1
#endif /* WIN32 */
/* Part of every banner, user specified or not */
@@ -466,6 +467,8 @@ LIBSSH2_API void **libssh2_session_abstract(LIBSSH2_SESSION *session);
LIBSSH2_API void *libssh2_session_callback_set(LIBSSH2_SESSION *session,
int cbtype, void *callback);
LIBSSH2_API int libssh2_session_banner_set(LIBSSH2_SESSION *session,
const char *banner);
LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session,
const char *banner);

View File

@@ -566,7 +566,7 @@ $(DEVLDIR)/readme.txt: Makefile.netware
@echo Creating $@
@echo $(DL)This is a development distribution for NetWare platform.$(DL) > $@
@echo $(DL)libssh2 version $(LIBSSH2_VERSION_STR)$(DL) >> $@
@echo $(DL)Please download the complete libssh package for$(DL) >> $@
@echo $(DL)Please download the complete libssh2 package for$(DL) >> $@
@echo $(DL)any further documentation:$(DL) >> $@
@echo $(DL)$(WWWURL)$(DL) >> $@

View File

@@ -338,7 +338,7 @@ agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
static int
agent_disconnect_pageant(LIBSSH2_AGENT *agent)
{
agent->fd = INVALID_SOCKET;
agent->fd = LIBSSH2_INVALID_SOCKET;
return 0;
}
@@ -771,7 +771,7 @@ libssh2_agent_userauth(LIBSSH2_AGENT *agent,
LIBSSH2_API int
libssh2_agent_disconnect(LIBSSH2_AGENT *agent)
{
if (agent->ops && agent->fd != INVALID_SOCKET)
if (agent->ops && agent->fd != LIBSSH2_INVALID_SOCKET)
return agent->ops->disconnect(agent);
return 0;
}
@@ -785,7 +785,7 @@ libssh2_agent_disconnect(LIBSSH2_AGENT *agent)
LIBSSH2_API void
libssh2_agent_free(LIBSSH2_AGENT *agent) {
/* Allow connection freeing when the socket has lost its connection */
if (agent->fd != INVALID_SOCKET) {
if (agent->fd != LIBSSH2_INVALID_SOCKET) {
libssh2_agent_disconnect(agent);
}
agent_free_identities(agent);

View File

@@ -1418,7 +1418,7 @@ _libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid)
rc = _libssh2_channel_receive_window_adjust(channel,
channel->flush_refund_bytes,
0, NULL);
1, NULL);
if (rc == LIBSSH2_ERROR_EAGAIN)
return rc;
}
@@ -1898,7 +1898,7 @@ libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf,
if(buflen > recv_window) {
BLOCK_ADJUST(rc, channel->session,
_libssh2_channel_receive_window_adjust(channel, buflen,
0, NULL));
1, NULL));
}
BLOCK_ADJUST(rc, channel->session,
@@ -2008,6 +2008,9 @@ _libssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id,
rc = _libssh2_transport_read(session);
while (rc > 0);
if((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN))
return rc;
if(channel->local.window_size <= 0)
/* there's no room for data so we stop */
return (rc==LIBSSH2_ERROR_EAGAIN?rc:0);

View File

@@ -74,5 +74,5 @@ void
_libssh2_init_if_needed(void)
{
if (_libssh2_initialized == 0)
libssh2_init (0);
(void)libssh2_init (0);
}

View File

@@ -997,10 +997,10 @@ knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
if(nlen <= buflen)
if(node->comment)
sprintf(buf, "|1|%s|%s%s %s %s\n", saltalloc, namealloc,
snprintf(buf, buflen, "|1|%s|%s%s %s %s\n", saltalloc, namealloc,
keytype, node->key, node->comment);
else
sprintf(buf, "|1|%s|%s%s %s\n", saltalloc, namealloc,
snprintf(buf, buflen, "|1|%s|%s%s %s\n", saltalloc, namealloc,
keytype, node->key);
else
rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
@@ -1016,10 +1016,10 @@ knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
if(nlen <= buflen)
/* these types have the plain name */
if(node->comment)
sprintf(buf, "%s%s %s %s\n", node->name, keytype, node->key,
snprintf(buf, buflen, "%s%s %s %s\n", node->name, keytype, node->key,
node->comment);
else
sprintf(buf, "%s%s %s\n", node->name, keytype, node->key);
snprintf(buf, buflen, "%s%s %s\n", node->name, keytype, node->key);
else
rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"Known-host write buffer too small");

View File

@@ -581,7 +581,7 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
const char *privatekey,
const char *passphrase)
{
return _libssh_error(session, LIBSSH2_ERROR_FILE,
return _libssh2_error(session, LIBSSH2_ERROR_FILE,
"Unable to extract public key from private key file: "
"Method unimplemented in libgcrypt backend");
}

View File

@@ -201,7 +201,7 @@ _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
return ret == 1 ? 0 : 1;
}
#if LIBSSH2_AES_CTR && !defined(HAVE_EVP_AES_128_CTR)
#if LIBSSH2_AES_CTR
#include <openssl/aes.h>
#include <openssl/evp.h>
@@ -217,6 +217,10 @@ static int
aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
const unsigned char *iv, int enc) /* init key */
{
/*
* variable "c" is leaked from this scope, but is later freed
* in aes_ctr_cleanup
*/
aes_ctr_ctx *c = malloc(sizeof(*c));
const EVP_CIPHER *aes_cipher;
(void) enc;
@@ -358,6 +362,8 @@ void _libssh2_init_aes_ctr(void)
_libssh2_EVP_aes_256_ctr();
}
#else
void _libssh2_init_aes_ctr(void) {}
#endif /* LIBSSH2_AES_CTR */
/* TODO: Optionally call a passphrase callback specified by the

View File

@@ -148,15 +148,9 @@ void libssh2_md5(const unsigned char *message, unsigned long len, unsigned char
#define _libssh2_cipher_aes256 EVP_aes_256_cbc
#define _libssh2_cipher_aes192 EVP_aes_192_cbc
#define _libssh2_cipher_aes128 EVP_aes_128_cbc
#ifdef HAVE_EVP_AES_128_CTR
#define _libssh2_cipher_aes128ctr EVP_aes_128_ctr
#define _libssh2_cipher_aes192ctr EVP_aes_192_ctr
#define _libssh2_cipher_aes256ctr EVP_aes_256_ctr
#else
#define _libssh2_cipher_aes128ctr _libssh2_EVP_aes_128_ctr
#define _libssh2_cipher_aes192ctr _libssh2_EVP_aes_192_ctr
#define _libssh2_cipher_aes256ctr _libssh2_EVP_aes_256_ctr
#endif
#define _libssh2_cipher_blowfish EVP_bf_cbc
#define _libssh2_cipher_arcfour EVP_rc4
#define _libssh2_cipher_cast5 EVP_cast5_cbc

View File

@@ -661,7 +661,7 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
rc = _libssh2_channel_receive_window_adjust(session->
packAdd_channelp,
datalen - 13,
0, NULL);
1, NULL);
if (rc == LIBSSH2_ERROR_EAGAIN)
return rc;

View File

@@ -294,8 +294,8 @@ scp_recv(LIBSSH2_SESSION * session, const char *path, struct stat * sb)
return NULL;
}
/* sprintf() is fine here since we allocated a large enough buffer */
sprintf((char *)session->scpRecv_command, "scp -%sf ", sb?"p":"");
snprintf((char *)session->scpRecv_command,
session->scpRecv_command_len, "scp -%sf ", sb?"p":"");
cmd_len = strlen((char *)session->scpRecv_command);
@@ -796,8 +796,8 @@ scp_send(LIBSSH2_SESSION * session, const char *path, int mode,
return NULL;
}
sprintf((char *)session->scpSend_command, "scp -%st ",
(mtime || atime)?"p":"");
snprintf((char *)session->scpSend_command, session->scpSend_command_len,
"scp -%st ", (mtime || atime)?"p":"");
cmd_len = strlen((char *)session->scpSend_command);

View File

@@ -675,7 +675,7 @@ session_startup(LIBSSH2_SESSION *session, libssh2_socket_t sock)
if (session->startup_state == libssh2_NB_state_idle) {
_libssh2_debug(session, LIBSSH2_TRACE_TRANS,
"session_startup for socket %d", sock);
if (INVALID_SOCKET == sock) {
if (LIBSSH2_INVALID_SOCKET == sock) {
/* Did we forget something? */
return _libssh2_error(session, LIBSSH2_ERROR_BAD_SOCKET,
"Bad socket provided");
@@ -828,6 +828,7 @@ session_free(LIBSSH2_SESSION *session)
LIBSSH2_PACKET *pkg;
LIBSSH2_CHANNEL *ch;
LIBSSH2_LISTENER *l;
int packets_left = 0;
if (session->free_state == libssh2_NB_state_idle) {
_libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Freeing session resource",
@@ -1018,6 +1019,9 @@ session_free(LIBSSH2_SESSION *session)
/* Cleanup all remaining packets */
while ((pkg = _libssh2_list_first(&session->packets))) {
packets_left++;
_libssh2_debug(session, LIBSSH2_TRACE_TRANS,
"packet left with id %d", pkg->data[0]);
/* unlink the node */
_libssh2_list_remove(&pkg->node);
@@ -1025,6 +1029,8 @@ session_free(LIBSSH2_SESSION *session)
LIBSSH2_FREE(session, pkg->data);
LIBSSH2_FREE(session, pkg);
}
_libssh2_debug(session, LIBSSH2_TRACE_TRANS,
"Extra packets left %d", packets_left);
if(session->socket_prev_blockstate)
/* if the socket was previously blocking, put it back so */

View File

@@ -1,6 +1,6 @@
/* Copyright (c) 2004-2008, Sara Golemon <sarag@libssh2.org>
* Copyright (c) 2007 Eli Fant <elifantu@mail.ru>
* Copyright (c) 2009-2011 by Daniel Stenberg
* Copyright (c) 2009-2012 by Daniel Stenberg
* All rights reserved.
*
* Redistribution and use in source and binary forms,
@@ -146,6 +146,48 @@ sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data,
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Received packet %d (len %d)",
(int) data[0], data_len);
/*
* Experience shows that if we mess up EAGAIN handling somewhere or
* otherwise get out of sync with the channel, this is where we first get
* a wrong byte and if so we need to bail out at once to aid tracking the
* problem better.
*/
switch(data[0]) {
case SSH_FXP_INIT:
case SSH_FXP_VERSION:
case SSH_FXP_OPEN:
case SSH_FXP_CLOSE:
case SSH_FXP_READ:
case SSH_FXP_WRITE:
case SSH_FXP_LSTAT:
case SSH_FXP_FSTAT:
case SSH_FXP_SETSTAT:
case SSH_FXP_FSETSTAT:
case SSH_FXP_OPENDIR:
case SSH_FXP_READDIR:
case SSH_FXP_REMOVE:
case SSH_FXP_MKDIR:
case SSH_FXP_RMDIR:
case SSH_FXP_REALPATH:
case SSH_FXP_STAT:
case SSH_FXP_RENAME:
case SSH_FXP_READLINK:
case SSH_FXP_SYMLINK:
case SSH_FXP_STATUS:
case SSH_FXP_HANDLE:
case SSH_FXP_DATA:
case SSH_FXP_NAME:
case SSH_FXP_ATTRS:
case SSH_FXP_EXTENDED:
case SSH_FXP_EXTENDED_REPLY:
break;
default:
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
"Out of sync with the world");
}
packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_PACKET));
if (!packet) {
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
@@ -158,7 +200,7 @@ sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data,
_libssh2_list_add(&sftp->packets, &packet->node);
return 0;
return LIBSSH2_ERROR_NONE;
}
/*
@@ -171,25 +213,35 @@ sftp_packet_read(LIBSSH2_SFTP *sftp)
{
LIBSSH2_CHANNEL *channel = sftp->channel;
LIBSSH2_SESSION *session = channel->session;
unsigned char *packet;
size_t packet_received;
unsigned char *packet = NULL;
ssize_t rc;
uint32_t packet_len; /* 32bits on the wire */
unsigned long recv_window;
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "recv packet");
/* If there was a previous partial, start using it */
if (sftp->partial_packet) {
switch(sftp->packet_state) {
case libssh2_NB_state_sent: /* EAGAIN from window adjusting */
sftp->packet_state = libssh2_NB_state_idle;
packet = sftp->partial_packet;
goto window_adjust;
case libssh2_NB_state_sent1: /* EAGAIN from channel read */
sftp->packet_state = libssh2_NB_state_idle;
packet = sftp->partial_packet;
packet_len = sftp->partial_len;
packet_received = sftp->partial_received;
sftp->partial_packet = NULL;
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
"partial read cont, len: %lu", packet_len);
}
else {
"partial read cont, len: %lu", sftp->partial_len);
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
"partial read cont, already recvd: %lu",
sftp->partial_received);
/* fall-through */
default:
if(!packet) {
/* only do this if there's not already a packet buffer allocated
to use */
/* each packet starts with a 32 bit length field */
rc = _libssh2_channel_read(channel, 0,
(char *)&sftp->partial_size[
@@ -206,51 +258,73 @@ sftp_packet_read(LIBSSH2_SFTP *sftp)
/* we got a short read for the length part */
return LIBSSH2_ERROR_EAGAIN;
packet_len = _libssh2_ntohu32(sftp->partial_size);
sftp->partial_len = _libssh2_ntohu32(sftp->partial_size);
/* make sure we don't proceed if the packet size is unreasonably
large */
if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN)
if (sftp->partial_len > LIBSSH2_SFTP_PACKET_MAXLEN)
return _libssh2_error(session,
LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED,
"SFTP packet too large");
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
"Data begin - Packet Length: %lu", packet_len);
packet = LIBSSH2_ALLOC(session, packet_len);
"Data begin - Packet Length: %lu",
sftp->partial_len);
packet = LIBSSH2_ALLOC(session, sftp->partial_len);
if (!packet)
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate SFTP packet");
sftp->partial_size_len = 0;
packet_received = 0;
sftp->partial_received = 0; /* how much of the packet already
received */
sftp->partial_packet = packet;
window_adjust:
recv_window = libssh2_channel_window_read_ex(channel, NULL, NULL);
if(sftp->partial_len > recv_window) {
/* ask for twice the data amount we need at once */
rc = _libssh2_channel_receive_window_adjust(channel,
sftp->partial_len*2,
1, NULL);
/* store the state so that we continue with the correct
operation at next invoke */
sftp->packet_state = (rc == LIBSSH2_ERROR_EAGAIN)?
libssh2_NB_state_sent:
libssh2_NB_state_idle;
if(rc == LIBSSH2_ERROR_EAGAIN)
return rc;
}
}
/* Read as much of the packet as we can */
while (packet_len > packet_received) {
while (sftp->partial_len > sftp->partial_received) {
rc = _libssh2_channel_read(channel, 0,
(char *) packet + packet_received,
packet_len - packet_received);
(char *)&packet[sftp->partial_received],
sftp->partial_len -
sftp->partial_received);
if (rc == LIBSSH2_ERROR_EAGAIN) {
/*
* We received EAGAIN, save what we have and return EAGAIN to the
* caller
* We received EAGAIN, save what we have and return EAGAIN to
* the caller. Set 'partial_packet' so that this function
* knows how to continue on the next invoke.
*/
sftp->partial_packet = packet;
sftp->partial_len = packet_len;
sftp->partial_received = packet_received;
sftp->packet_state = libssh2_NB_state_sent1;
return rc;
}
else if (rc < 0) {
LIBSSH2_FREE(session, packet);
sftp->partial_packet = NULL;
return _libssh2_error(session, rc,
"Error waiting for SFTP packet");
}
packet_received += rc;
sftp->partial_received += rc;
}
sftp->partial_packet = NULL;
rc = sftp_packet_add(sftp, packet, packet_len);
rc = sftp_packet_add(sftp, packet, sftp->partial_len);
if (rc) {
LIBSSH2_FREE(session, packet);
return rc;
@@ -258,6 +332,8 @@ sftp_packet_read(LIBSSH2_SFTP *sftp)
return packet[0];
}
/* WON'T REACH */
}
/*
* sftp_packetlist_flush
*
@@ -676,12 +752,10 @@ static LIBSSH2_SFTP *sftp_init(LIBSSH2_SESSION *session)
rc = sftp_packet_require(sftp_handle, SSH_FXP_VERSION,
0, &data, &data_len);
if (rc == LIBSSH2_ERROR_EAGAIN) {
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
"Would block waiting for response from SFTP subsystem");
if (rc == LIBSSH2_ERROR_EAGAIN)
return NULL;
} else if (rc) {
_libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
else if (rc) {
_libssh2_error(session, rc,
"Timeout waiting for response from SFTP subsystem");
goto sftp_init_error;
}
@@ -1085,69 +1159,99 @@ static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
struct sftp_pipeline_chunk *chunk;
struct sftp_pipeline_chunk *next;
ssize_t rc;
size_t eagain = 0;
size_t total_read = 0;
struct _libssh2_sftp_handle_file_data *filep =
&handle->u.file;
/* Number of bytes asked for that haven't been acked yet */
size_t already = (filep->offset_sent - filep->offset);
/* This function can be interrupted in three different places where it
might need to wait for data from the network. It returns EAGAIN to
allow non-blocking clients to do other work but these client are
expected to call this function again (possibly many times) to finish
the operation.
The tricky part is that if we previously aborted a sftp_read due to
EAGAIN, we must continue at the same spot to continue the previously
interrupted operation. This is done using a state machine to record
what phase of execution we were at. The state is stored in
sftp->read_state.
libssh2_NB_state_idle: The first phase is where we prepare multiple
FXP_READ packets to do optimistic read-ahead. We send off as many as
possible in the second phase without waiting for a response to each
one; this is the key to fast reads. But we may have to adjust the
channel window size to do this which may interrupt this function while
waiting. The state machine saves the phase as libssh2_NB_state_idle so
it returns here on the next call.
libssh2_NB_state_sent: The second phase is where we send the FXP_READ
packets. Writing them to the channel can be interrupted with EAGAIN
but the state machine ensures we skip the first phase on the next call
and resume sending.
libssh2_NB_state_sent2: In the third phase (indicated by ) we read the
data from the responses that have arrived so far. Reading can be
interrupted with EAGAIN but the state machine ensures we skip the first
and second phases on the next call and resume sending.
*/
switch (sftp->read_state) {
case libssh2_NB_state_idle:
/* Some data may already have been read from the server in the
previous call but didn't fit in the buffer at the time. If so, we
return that now as we can't risk being interrupted later with data
partially written to the buffer. */
if(filep->data_left) {
/* data left from previous call */
size_t copy = MIN(buffer_size, filep->data_left);
memcpy(buffer, &filep->data[ filep->data_len - filep->data_left],
copy);
total_read += copy;
filep->data_left -= copy;
filep->offset += copy;
if(filep->data_left)
return total_read;
if(!filep->data_left) {
LIBSSH2_FREE(session, filep->data);
filep->data = NULL;
}
/* if we previously aborted a channel_write due to EAGAIN, we must
continue that writing so that we don't risk trying to send another
channel_write here to enlarge the receive window */
if(sftp->read_state == libssh2_NB_state_sent)
goto send_read_requests;
return copy;
}
/* We allow a number of bytes being requested at any given time without
having been acked - until we reach EOF. */
/* We allow a number of bytes being requested at any given time
without having been acked - until we reach EOF. */
if(!filep->eof) {
/* Number of bytes asked for that haven't been acked yet */
size_t already = (filep->offset_sent - filep->offset);
size_t max_read_ahead = buffer_size*4;
unsigned long recv_window;
if(max_read_ahead > LIBSSH2_CHANNEL_WINDOW_DEFAULT*4)
max_read_ahead = LIBSSH2_CHANNEL_WINDOW_DEFAULT*4;
/* if the buffer_size passed in now is smaller than what has already
been sent, we risk getting count become a very large number */
/* if the buffer_size passed in now is smaller than what has
already been sent, we risk getting count become a very large
number */
if(max_read_ahead > already)
count = max_read_ahead - already;
/* 'count' is how much more data to ask for, and 'already' is how much
data that already has been asked for but not yet returned.
Specificly, 'count' means how much data that have or will be asked
for by the nodes that are already added to the linked list. Some of
those read requests may not actually have been sent off
successfully yet.
/* 'count' is how much more data to ask for, and 'already' is how
much data that already has been asked for but not yet returned.
Specificly, 'count' means how much data that have or will be
asked for by the nodes that are already added to the linked
list. Some of those read requests may not actually have been
sent off successfully yet.
If 'already' is very large it should be perfectly fine to have
count set to 0 as then we don't have to ask for more data (right
now).
count set to 0 as then we don't have to ask for more data
(right now).
buffer_size*4 is just picked more or less out of the air. The idea
is that when reading SFTP from a remote server, we send away
multiple read requests guessing that the client will read more than
only this 'buffer_size' amount of memory. So we ask for maximum
buffer_size*4 amount of data so that we can return them very fast
in subsequent calls.
buffer_size*4 is just picked more or less out of the air. The
idea is that when reading SFTP from a remote server, we send
away multiple read requests guessing that the client will read
more than only this 'buffer_size' amount of memory. So we ask
for maximum buffer_size*4 amount of data so that we can return
them very fast in subsequent calls.
*/
recv_window = libssh2_channel_window_read_ex(sftp->channel,
@@ -1156,16 +1260,13 @@ static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
/* more data will be asked for than what the window currently
allows, expand it! */
if(total_read)
/* since we risk getting EAGAIN below, we return here if
there is data available */
return total_read;
rc = _libssh2_channel_receive_window_adjust(sftp->channel,
max_read_ahead*8,
0, NULL);
1, NULL);
/* if this returns EAGAIN, we will get back to this function
at next call */
assert(rc != LIBSSH2_ERROR_EAGAIN || !filep->data_left);
assert(rc != LIBSSH2_ERROR_EAGAIN || !filep->eof);
if (rc)
return rc;
}
@@ -1208,26 +1309,23 @@ static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
to create more packets */
}
send_read_requests:
/* move through the READ packets that haven't been sent and send as many
as possible - remember that we don't block */
chunk = _libssh2_list_first(&handle->packet_list);
case libssh2_NB_state_sent:
sftp->read_state = libssh2_NB_state_idle;
/* move through the READ packets that haven't been sent and send as
many as possible - remember that we don't block */
chunk = _libssh2_list_first(&handle->packet_list);
while(chunk) {
if(chunk->lefttosend) {
rc = _libssh2_channel_write(channel, 0,
&chunk->packet[chunk->sent],
chunk->lefttosend);
if(rc < 0) {
if(rc != LIBSSH2_ERROR_EAGAIN)
/* error */
return rc;
eagain++;
sftp->read_state = libssh2_NB_state_sent;
break;
return rc;
}
/* remember where to continue sending the next time */
@@ -1243,6 +1341,10 @@ static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
chunk = _libssh2_list_next(&chunk->node);
}
case libssh2_NB_state_sent2:
sftp->read_state = libssh2_NB_state_idle;
/*
* Count all ACKed packets and act on the contents of them.
*/
@@ -1257,29 +1359,26 @@ static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
};
if(chunk->lefttosend)
/* if the chunk still has data left to send, we shouldn't wait for
an ACK for it just yet */
/* if the chunk still has data left to send, we shouldn't wait
for an ACK for it just yet */
break;
rc = sftp_packet_requirev(sftp, 2, read_responses,
chunk->request_id, &data, &data_len);
if (rc == LIBSSH2_ERROR_EAGAIN) {
eagain++;
break;
if (rc < 0) {
sftp->read_state = libssh2_NB_state_sent2;
return rc;
}
else if (rc)
return _libssh2_error(session, rc,
"Error waiting for FXP_READ ACK");
/*
* We get DATA or STATUS back. STATUS can be error, or it is FX_EOF
* when we reach the end of the file.
* We get DATA or STATUS back. STATUS can be error, or it is
* FX_EOF when we reach the end of the file.
*/
switch (data[0]) {
case SSH_FXP_STATUS:
/* we must remove all outstanding READ requests, as either we got
an error or we're at end of file */
/* we must remove all outstanding READ requests, as either we
got an error or we're at end of file */
sftp_packetlist_flush(handle);
rc32 = _libssh2_ntohu32(data + 5);
@@ -1287,7 +1386,7 @@ static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
if (rc32 == LIBSSH2_FX_EOF) {
filep->eof = TRUE;
return total_read;
return 0;
}
else {
sftp->last_errno = rc32;
@@ -1303,19 +1402,19 @@ static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
"SFTP Protocol badness");
if(rc32 != chunk->len) {
/* a short read does not imply end of file, but we must adjust
the offset_sent since it was advanced with a full
chunk->len before */
/* a short read does not imply end of file, but we must
adjust the offset_sent since it was advanced with a
full chunk->len before */
filep->offset_sent -= (chunk->len - rc32);
}
if(total_read + rc32 > buffer_size) {
if(rc32 > buffer_size) {
/* figure out the overlap amount */
filep->data_left = (total_read + rc32) - buffer_size;
filep->data_left = rc32 - buffer_size;
/* getting the full packet would overflow the buffer, so
only get the correct amount and keep the remainder */
rc32 = (uint32_t)(buffer_size - total_read);
rc32 = (uint32_t)buffer_size;
/* store data to keep for next call */
filep->data = data;
@@ -1324,40 +1423,47 @@ static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
else
filep->data_len = 0;
/* copy the received data from the received FXP_DATA packet to the
buffer at the correct index */
memcpy(buffer + total_read, data + 9, rc32);
/* copy the received data from the received FXP_DATA packet to
the buffer at the correct index */
memcpy(buffer, data + 9, rc32);
filep->offset += rc32;
total_read += rc32;
if(!filep->data_len)
if(filep->data_len == 0)
/* free the allocated data if not stored to keep */
LIBSSH2_FREE(session, data);
else {
/* force the loop to end since the receive buffer is full
already, but remove this chunk from the list first */
_libssh2_list_remove(&chunk->node); /* remove from list */
LIBSSH2_FREE(session, chunk); /* free memory */
chunk = NULL;
continue;
}
break;
}
/* remove the chunk we just processed keeping track of the
* next one in case we need it */
next = _libssh2_list_next(&chunk->node);
_libssh2_list_remove(&chunk->node);
LIBSSH2_FREE(session, chunk);
chunk = NULL;
_libssh2_list_remove(&chunk->node); /* remove from list */
LIBSSH2_FREE(session, chunk); /* free memory */
if(rc32 > 0) {
/* we must return as we wrote some data to the buffer */
return rc32;
} else {
/* A zero-byte read is not necessarily EOF so we must not
* return 0 (that would signal EOF to the caller) so
* instead we carry on to the next chunk */
chunk = next;
}
if(total_read)
return total_read;
else if(eagain)
return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
"Would block sftp_read");
break;
default:
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
"SFTP Protocol badness: unrecognised "
"read request response");
}
}
break;
default:
assert(!"State machine error; unrecognised read state");
}
return 0;
}
@@ -1502,7 +1608,7 @@ static ssize_t sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
return retcode;
else if (retcode) {
sftp->readdir_state = libssh2_NB_state_idle;
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
return _libssh2_error(session, retcode,
"Timeout waiting for status message");
}
@@ -1611,16 +1717,20 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
struct sftp_pipeline_chunk *next;
size_t acked = 0;
size_t org_count = count;
size_t eagain = 0;
size_t already;
/* Number of bytes sent off that haven't been acked and therefor we will
get passed in here again.
switch(sftp->write_state) {
default:
case libssh2_NB_state_idle:
Also, add up the number of bytes that actually already have been acked
but we haven't been able to return as such yet, so we will get that
data as well passed in here again.
/* Number of bytes sent off that haven't been acked and therefor we
will get passed in here again.
Also, add up the number of bytes that actually already have been
acked but we haven't been able to return as such yet, so we will
get that data as well passed in here again.
*/
size_t already = (handle->u.file.offset_sent - handle->u.file.offset)+
already = (handle->u.file.offset_sent - handle->u.file.offset)+
handle->u.file.acked;
if(count >= already) {
@@ -1632,9 +1742,10 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
/* there is more data already fine than what we got in this call */
count = 0;
sftp->write_state = libssh2_NB_state_idle;
while(count) {
/* TODO: Possibly this should have some logic to prevent a very very
small fraction to be left but lets ignore that for now */
/* TODO: Possibly this should have some logic to prevent a very
very small fraction to be left but lets ignore that for now */
uint32_t size = MIN(MAX_SFTP_OUTGOING_SIZE, count);
uint32_t request_id;
@@ -1681,13 +1792,9 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
rc = _libssh2_channel_write(channel, 0,
&chunk->packet[chunk->sent],
chunk->lefttosend);
if(rc < 0) {
if(rc != LIBSSH2_ERROR_EAGAIN)
/* error */
if(rc < 0)
/* remain in idle state */
return rc;
eagain++;
break;
}
/* remember where to continue sending the next time */
chunk->lefttosend -= rc;
@@ -1702,6 +1809,10 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
chunk = _libssh2_list_next(&chunk->node);
}
/* fall-through */
case libssh2_NB_state_sent:
sftp->write_state = libssh2_NB_state_idle;
/*
* Count all ACKed packets
*/
@@ -1709,20 +1820,24 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
while(chunk) {
if(chunk->lefttosend)
/* if the chunk still has data left to send, we shouldn't wait for
an ACK for it just yet */
/* if the chunk still has data left to send, we shouldn't wait
for an ACK for it just yet */
break;
else if(acked)
/* if we have sent data that is acked, we must return that
info before we call a function that might return EAGAIN */
break;
/* we check the packets in order */
rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
chunk->request_id, &data, &data_len);
if (rc == LIBSSH2_ERROR_EAGAIN) {
eagain++;
break;
}
else if (rc) {
return _libssh2_error(session, rc, "Waiting for SFTP status");
if (rc < 0) {
if (rc == LIBSSH2_ERROR_EAGAIN)
sftp->write_state = libssh2_NB_state_sent;
return rc;
}
retcode = _libssh2_ntohu32(data + 5);
LIBSSH2_FREE(session, data);
@@ -1763,6 +1878,8 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
"FXP write failed");
}
}
break;
}
/* if there were acked data in a previous call that wasn't returned then,
add that up and try to return it all now. This can happen if the app
@@ -1781,9 +1898,7 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
return ret;
}
else if(eagain)
return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
"Would block sftp_write");
else
return 0; /* nothing was acked, and no EAGAIN was received! */
}
@@ -1870,11 +1985,11 @@ static int sftp_fstat(LIBSSH2_SFTP_HANDLE *handle,
rc = sftp_packet_requirev(sftp, 2, fstat_responses,
sftp->fstat_request_id, &data,
&data_len);
if (rc == LIBSSH2_ERROR_EAGAIN) {
if (rc == LIBSSH2_ERROR_EAGAIN)
return rc;
} else if (rc) {
else if (rc) {
sftp->fstat_state = libssh2_NB_state_idle;
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
return _libssh2_error(session, rc,
"Timeout waiting for status message");
}
@@ -2059,8 +2174,8 @@ sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle)
return rc;
} else if (rc) {
handle->close_state = libssh2_NB_state_idle;
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
"Timeout waiting for status message");
return _libssh2_error(session, rc,
"Error waiting for status message");
}
handle->close_state = libssh2_NB_state_sent1;
@@ -2177,8 +2292,8 @@ static int sftp_unlink(LIBSSH2_SFTP *sftp, const char *filename,
}
else if (rc) {
sftp->unlink_state = libssh2_NB_state_idle;
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
"Timeout waiting for status message");
return _libssh2_error(session, rc,
"Error waiting for FXP STATUS");
}
sftp->unlink_state = libssh2_NB_state_idle;
@@ -2287,8 +2402,8 @@ static int sftp_rename(LIBSSH2_SFTP *sftp, const char *source_filename,
return rc;
} else if (rc) {
sftp->rename_state = libssh2_NB_state_idle;
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
"Timeout waiting for status message");
return _libssh2_error(session, rc,
"Error waiting for FXP STATUS");
}
sftp->rename_state = libssh2_NB_state_idle;
@@ -2410,8 +2525,8 @@ static int sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_STATVFS *st)
return rc;
} else if (rc) {
sftp->fstatvfs_state = libssh2_NB_state_idle;
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
"Timeout waiting for status message");
return _libssh2_error(session, rc,
"Error waiting for FXP EXTENDED REPLY");
} else if (data_len < 93) {
LIBSSH2_FREE(session, data);
sftp->fstatvfs_state = libssh2_NB_state_idle;
@@ -2523,8 +2638,8 @@ static int sftp_statvfs(LIBSSH2_SFTP *sftp, const char *path,
return rc;
} else if (rc) {
sftp->statvfs_state = libssh2_NB_state_idle;
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
"Timeout waiting for status message");
return _libssh2_error(session, rc,
"Error waiting for FXP EXTENDED REPLY");
} else if (data_len < 93) {
LIBSSH2_FREE(session, data);
sftp->fstatvfs_state = libssh2_NB_state_idle;
@@ -2642,8 +2757,8 @@ static int sftp_mkdir(LIBSSH2_SFTP *sftp, const char *path,
return rc;
} else if (rc) {
sftp->mkdir_state = libssh2_NB_state_idle;
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
"Timeout waiting for status message");
return _libssh2_error(session, rc,
"Error waiting for FXP STATUS");
}
sftp->mkdir_state = libssh2_NB_state_idle;
@@ -2736,8 +2851,8 @@ static int sftp_rmdir(LIBSSH2_SFTP *sftp, const char *path,
return rc;
} else if (rc) {
sftp->rmdir_state = libssh2_NB_state_idle;
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
"Timeout waiting for status message");
return _libssh2_error(session, rc,
"Error waiting for FXP STATUS");
}
sftp->rmdir_state = libssh2_NB_state_idle;
@@ -2845,11 +2960,11 @@ static int sftp_stat(LIBSSH2_SFTP *sftp, const char *path,
rc = sftp_packet_requirev(sftp, 2, stat_responses,
sftp->stat_request_id, &data, &data_len);
if (rc == LIBSSH2_ERROR_EAGAIN) {
if (rc == LIBSSH2_ERROR_EAGAIN)
return rc;
} else if (rc) {
else if (rc) {
sftp->stat_state = libssh2_NB_state_idle;
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
return _libssh2_error(session, rc,
"Timeout waiting for status message");
}
@@ -2980,8 +3095,8 @@ static int sftp_symlink(LIBSSH2_SFTP *sftp, const char *path,
return retcode;
else if (retcode) {
sftp->symlink_state = libssh2_NB_state_idle;
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
"Timeout waiting for status message");
return _libssh2_error(session, retcode,
"Error waiting for status message");
}
sftp->symlink_state = libssh2_NB_state_idle;

View File

@@ -1,7 +1,7 @@
#ifndef _LIBSSH2_SFTP_H
#define _LIBSSH2_SFTP_H
/*
* Copyright (C) 2010, 2011 by Daniel Stenberg
* Copyright (C) 2010 - 2012 by Daniel Stenberg
* Author: Daniel Stenberg <daniel@haxx.se>
*
* Redistribution and use in source and binary forms,
@@ -158,9 +158,15 @@ struct _LIBSSH2_SFTP
size_t open_packet_sent;
uint32_t open_request_id;
/* State variables used in libssh2_sftp_read() */
/* State variable used in sftp_read() */
libssh2_nonblocking_states read_state;
/* State variable used in sftp_packet_read() */
libssh2_nonblocking_states packet_state;
/* State variable used in sftp_write() */
libssh2_nonblocking_states write_state;
/* State variables used in libssh2_sftp_readdir() */
libssh2_nonblocking_states readdir_state;
unsigned char *readdir_packet;

View File

@@ -296,7 +296,7 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
* is done!
*/
_libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Redirecting into the"
" key re-exchange");
" key re-exchange from _libssh2_transport_read");
rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state);
if (rc)
return rc;
@@ -687,6 +687,24 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
const unsigned char *orgdata = data;
size_t orgdata_len = data_len;
/*
* If the last read operation was interrupted in the middle of a key
* exchange, we must complete that key exchange before continuing to write
* further data.
*
* See the similar block in _libssh2_transport_read for more details.
*/
if (session->state & LIBSSH2_STATE_EXCHANGING_KEYS &&
!(session->state & LIBSSH2_STATE_KEX_ACTIVE)) {
/* Don't write any new packets if we're still in the middle of a key
* exchange. */
_libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Redirecting into the"
" key re-exchange from _libssh2_transport_send");
rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state);
if (rc)
return rc;
}
debugdump(session, "libssh2_transport_write plain", data, data_len);
if(data2)
debugdump(session, "libssh2_transport_write plain2", data2, data2_len);