Compare commits
32 Commits
libssh2-1.
...
libssh2-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c8cc30c996 | ||
![]() |
acd9bd6104 | ||
![]() |
2ea40e63e8 | ||
![]() |
f4f2298ef3 | ||
![]() |
cc4f9d5679 | ||
![]() |
fed0759720 | ||
![]() |
a46ef85a56 | ||
![]() |
626f91da07 | ||
![]() |
bf097e37b0 | ||
![]() |
a1c996cef1 | ||
![]() |
8fc16beda9 | ||
![]() |
9a42fac055 | ||
![]() |
7194a9bd7b | ||
![]() |
7e53949e66 | ||
![]() |
5672e8f4cf | ||
![]() |
4449905ea5 | ||
![]() |
8e0cddd01f | ||
![]() |
796a5195d3 | ||
![]() |
15aadb930d | ||
![]() |
f28264c6a3 | ||
![]() |
179d2197dd | ||
![]() |
aa8f2cbf33 | ||
![]() |
e07342443f | ||
![]() |
4774d500e7 | ||
![]() |
1403847429 | ||
![]() |
0d824e5702 | ||
![]() |
9836b0889f | ||
![]() |
e394987911 | ||
![]() |
e95c7de453 | ||
![]() |
effbb72192 | ||
![]() |
0ebe6f44bd | ||
![]() |
b3ade9a63e |
@@ -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)
|
||||
|
||||
|
@@ -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
1
example/.gitignore
vendored
@@ -33,4 +33,5 @@ test-sftp_nonblock
|
||||
test-sftp_write
|
||||
test-sftp_write_nonblock
|
||||
test-ssh2_echo
|
||||
tcpip-forward
|
||||
x11
|
||||
|
@@ -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
|
||||
|
@@ -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
292
example/tcpip-forward.c
Normal 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;
|
||||
}
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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) >> $@
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -74,5 +74,5 @@ void
|
||||
_libssh2_init_if_needed(void)
|
||||
{
|
||||
if (_libssh2_initialized == 0)
|
||||
libssh2_init (0);
|
||||
(void)libssh2_init (0);
|
||||
}
|
||||
|
@@ -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");
|
||||
|
@@ -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");
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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 */
|
||||
|
459
src/sftp.c
459
src/sftp.c
@@ -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;
|
||||
|
10
src/sftp.h
10
src/sftp.h
@@ -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;
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user