Adding src/transport.c for the SECSH transport layer read/write in a non-
blocking way. The channel code is now responsible for enabling/disabling blocking status and to work with it. I've also modified indenting and fixed compiler warnings at places, and added a bunch of new examples in example/simple that I've used to verify that the code still runs like before. libssh2_channel_{read|write}nb_ex() and libssh2_sftp_{read|write}nb() are the four new functions that supposedly work non-blocking.
This commit is contained in:
parent
c63ef86075
commit
9d55db6501
11
Makefile.am
11
Makefile.am
@ -1,19 +1,10 @@
|
|||||||
AUTOMAKE_OPTIONS = foreign nostdinc
|
AUTOMAKE_OPTIONS = foreign nostdinc
|
||||||
|
|
||||||
SUBDIRS = src tests
|
SUBDIRS = src example tests
|
||||||
|
|
||||||
include_HEADERS = include/libssh2.h include/libssh2_publickey.h \
|
include_HEADERS = include/libssh2.h include/libssh2_publickey.h \
|
||||||
include/libssh2_sftp.h
|
include/libssh2_sftp.h
|
||||||
|
|
||||||
# and a sample tool
|
|
||||||
noinst_PROGRAMS = ssh2_sample
|
|
||||||
|
|
||||||
INCLUDES = -I$(top_srcdir)/include
|
|
||||||
|
|
||||||
ssh2_sample_SOURCES = ssh2_sample.c
|
|
||||||
|
|
||||||
ssh2_sample_LDADD = src/libssh2.la
|
|
||||||
|
|
||||||
EXTRA_DIST = LICENSE win32
|
EXTRA_DIST = LICENSE win32
|
||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I m4
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
|
@ -224,7 +224,7 @@ AC_ARG_ENABLE(debug-errors,
|
|||||||
AC_HELP_STRING([--enable-debug-errors],[Output failure events to stderr]),
|
AC_HELP_STRING([--enable-debug-errors],[Output failure events to stderr]),
|
||||||
[AC_DEFINE(LIBSSH2_DEBUG_ERRORS, 1, [Output failure events to stderr])])
|
[AC_DEFINE(LIBSSH2_DEBUG_ERRORS, 1, [Output failure events to stderr])])
|
||||||
AC_ARG_ENABLE(debug-all,
|
AC_ARG_ENABLE(debug-all,
|
||||||
AC_HELP_STRING([--enable-debug-all],[Output debugging info for all layers to stderr]),
|
AC_HELP_STRING([--enable-debug],[Enable debug]),
|
||||||
[
|
[
|
||||||
AC_DEFINE(LIBSSH2_DEBUG_TRANSPORT, 1, [Output transport layer debugging info to stderr])
|
AC_DEFINE(LIBSSH2_DEBUG_TRANSPORT, 1, [Output transport layer debugging info to stderr])
|
||||||
AC_DEFINE(LIBSSH2_DEBUG_KEX, 1, [Output Key Exchange debugging info to stderr])
|
AC_DEFINE(LIBSSH2_DEBUG_KEX, 1, [Output Key Exchange debugging info to stderr])
|
||||||
@ -273,5 +273,7 @@ AC_C_INLINE
|
|||||||
|
|
||||||
AC_CONFIG_FILES([Makefile
|
AC_CONFIG_FILES([Makefile
|
||||||
src/Makefile
|
src/Makefile
|
||||||
tests/Makefile])
|
tests/Makefile
|
||||||
|
example/Makefile
|
||||||
|
example/simple/Makefile])
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
2
example/Makefile.am
Normal file
2
example/Makefile.am
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
AUTOMAKE_OPTIONS = foreign nostdinc
|
||||||
|
SUBDIRS = simple
|
17
example/simple/Makefile.am
Normal file
17
example/simple/Makefile.am
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
AUTOMAKE_OPTIONS = foreign nostdinc
|
||||||
|
|
||||||
|
# samples
|
||||||
|
noinst_PROGRAMS = ssh2 scp_nonblock sftp_nonblock sftp scp
|
||||||
|
|
||||||
|
INCLUDES = -I$(top_srcdir)/include
|
||||||
|
LDADD = $(top_builddir)/src/libssh2.la
|
||||||
|
|
||||||
|
ssh2_SOURCES = ssh2.c
|
||||||
|
|
||||||
|
sftp_SOURCES = sftp.c
|
||||||
|
|
||||||
|
scp_nonblock_SOURCES = scp_nonblock.c
|
||||||
|
|
||||||
|
sftp_nonblock_SOURCES = sftp_nonblock.c
|
||||||
|
|
||||||
|
scp_SOURCES = scp.c
|
193
example/simple/scp_nonblock.c
Normal file
193
example/simple/scp_nonblock.c
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* $Id: scp_nonblock.c,v 1.1 2007/02/02 16:21:20 bagder Exp $
|
||||||
|
*
|
||||||
|
* Sample showing how to do SCP transfers in a non-blocking manner.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libssh2.h>
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
#else
|
||||||
|
# include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int sock, i, auth_pw = 1;
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
const char *fingerprint;
|
||||||
|
LIBSSH2_SESSION *session;
|
||||||
|
LIBSSH2_CHANNEL *channel;
|
||||||
|
char *username=(char *)"username";
|
||||||
|
char *password=(char *)"password";
|
||||||
|
char *scppath=(char *)"/tmp/TEST";
|
||||||
|
struct stat fileinfo;
|
||||||
|
int rc;
|
||||||
|
off_t got=0;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
WSADATA wsadata;
|
||||||
|
|
||||||
|
WSAStartup(WINSOCK_VERSION, &wsadata);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Ultra basic "connect to port 22 on localhost"
|
||||||
|
* Your code is responsible for creating the socket establishing the
|
||||||
|
* connection
|
||||||
|
*/
|
||||||
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
sin.sin_family = AF_INET;
|
||||||
|
sin.sin_port = htons(22);
|
||||||
|
sin.sin_addr.s_addr = htonl(0x7F000001);
|
||||||
|
if (connect(sock, (struct sockaddr*)(&sin),
|
||||||
|
sizeof(struct sockaddr_in)) != 0) {
|
||||||
|
fprintf(stderr, "failed to connect!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We set the socket non-blocking. We do it after the connect just to
|
||||||
|
simplify the example code. */
|
||||||
|
#ifdef F_SETFL
|
||||||
|
/* FIXME: this can/should be done in a more portable manner */
|
||||||
|
rc = fcntl(sock, F_GETFL, 0);
|
||||||
|
fcntl(sock, F_SETFL, rc | O_NONBLOCK);
|
||||||
|
#else
|
||||||
|
#error "add support for setting the socket non-blocking here"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Create a session instance
|
||||||
|
*/
|
||||||
|
session = libssh2_session_init();
|
||||||
|
if(!session)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* ... start it up. This will trade welcome banners, exchange keys,
|
||||||
|
* and setup crypto, compression, and MAC layers
|
||||||
|
*/
|
||||||
|
rc = libssh2_session_startup(session, sock);
|
||||||
|
if(rc) {
|
||||||
|
fprintf(stderr, "Failure establishing 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_MD5);
|
||||||
|
printf("Fingerprint: ");
|
||||||
|
for(i = 0; i < 16; i++) {
|
||||||
|
printf("%02X ", (unsigned char)fingerprint[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if(argc > 1) {
|
||||||
|
username = argv[1];
|
||||||
|
}
|
||||||
|
if(argc > 2) {
|
||||||
|
password = argv[2];
|
||||||
|
}
|
||||||
|
if(argc > 3) {
|
||||||
|
scppath = argv[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth_pw) {
|
||||||
|
/* We could authenticate via password */
|
||||||
|
if (libssh2_userauth_password(session, username, password)) {
|
||||||
|
printf("Authentication by password failed.\n");
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Or by public key */
|
||||||
|
if (libssh2_userauth_publickey_fromfile(session, username,
|
||||||
|
"/home/username/.ssh/id_rsa.pub",
|
||||||
|
"/home/username/.ssh/id_rsa",
|
||||||
|
password)) {
|
||||||
|
printf("\tAuthentication by public key failed\n");
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Request a file via SCP */
|
||||||
|
channel = libssh2_scp_recv(session, scppath, &fileinfo);
|
||||||
|
|
||||||
|
if (!channel) {
|
||||||
|
fprintf(stderr, "Unable to open a session\n");
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "libssh2_scp_recv() is done, now receive data!\n");
|
||||||
|
|
||||||
|
while(got < fileinfo.st_size) {
|
||||||
|
char mem[1000];
|
||||||
|
|
||||||
|
struct timeval timeout;
|
||||||
|
int rc;
|
||||||
|
fd_set fd;
|
||||||
|
|
||||||
|
do {
|
||||||
|
int amount=sizeof(mem);
|
||||||
|
|
||||||
|
if((fileinfo.st_size -got) < amount) {
|
||||||
|
amount = fileinfo.st_size -got;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loop until we block */
|
||||||
|
rc = libssh2_channel_readnb(channel, mem, amount);
|
||||||
|
if(rc > 0) {
|
||||||
|
write(2, mem, rc);
|
||||||
|
got += rc;
|
||||||
|
}
|
||||||
|
} while (rc > 0);
|
||||||
|
|
||||||
|
if(rc == LIBSSH2CHANNEL_EAGAIN) {
|
||||||
|
/* this is due to blocking that would occur otherwise
|
||||||
|
so we loop on this condition */
|
||||||
|
|
||||||
|
timeout.tv_sec = 10;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
|
FD_ZERO(&fd);
|
||||||
|
|
||||||
|
FD_SET(sock, &fd);
|
||||||
|
|
||||||
|
rc = select(sock+1, &fd, &fd, NULL, &timeout);
|
||||||
|
if(rc <= 0) {
|
||||||
|
/* negative is error
|
||||||
|
0 is timeout */
|
||||||
|
fprintf(stderr, "SCP timed out: %d\n", rc);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
libssh2_channel_free(channel);
|
||||||
|
channel = NULL;
|
||||||
|
|
||||||
|
shutdown:
|
||||||
|
|
||||||
|
libssh2_session_disconnect(session,
|
||||||
|
"Normal Shutdown, Thank you for playing");
|
||||||
|
libssh2_session_free(session);
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
Sleep(1000);
|
||||||
|
closesocket(sock);
|
||||||
|
#else
|
||||||
|
sleep(1);
|
||||||
|
close(sock);
|
||||||
|
#endif
|
||||||
|
printf("all done\n");
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* $Id: sftp.c,v 1.1 2007/01/24 14:15:36 bagder Exp $
|
* $Id: sftp.c,v 1.2 2007/02/02 16:21:20 bagder Exp $
|
||||||
*
|
*
|
||||||
* Sample showing how to do SFTP transfers.
|
* Sample showing how to do SFTP transfers.
|
||||||
*/
|
*/
|
||||||
@ -109,6 +109,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "libssh2_sftp_init()!\n");
|
||||||
sftp_session = libssh2_sftp_init(session);
|
sftp_session = libssh2_sftp_init(session);
|
||||||
|
|
||||||
if (!sftp_session) {
|
if (!sftp_session) {
|
||||||
@ -116,6 +117,7 @@ int main(int argc, char *argv[])
|
|||||||
goto shutdown;
|
goto shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "libssh2_sftp_open()!\n");
|
||||||
/* Request a file via SFTP */
|
/* Request a file via SFTP */
|
||||||
sftp_handle =
|
sftp_handle =
|
||||||
libssh2_sftp_open(sftp_session, sftppath, LIBSSH2_FXF_READ, 0);
|
libssh2_sftp_open(sftp_session, sftppath, LIBSSH2_FXF_READ, 0);
|
||||||
@ -129,6 +131,7 @@ int main(int argc, char *argv[])
|
|||||||
char mem[512];
|
char mem[512];
|
||||||
|
|
||||||
/* loop until we fail */
|
/* loop until we fail */
|
||||||
|
fprintf(stderr, "libssh2_sftp_read()!\n");
|
||||||
rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem));
|
rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem));
|
||||||
if(rc > 0) {
|
if(rc > 0) {
|
||||||
write(2, mem, rc);
|
write(2, mem, rc);
|
||||||
|
281
example/simple/sftp_nonblock.c
Normal file
281
example/simple/sftp_nonblock.c
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
/*
|
||||||
|
* $Id: sftp_nonblock.c,v 1.1 2007/02/02 16:21:20 bagder Exp $
|
||||||
|
*
|
||||||
|
* Sample showing how to do SFTP transfers in a non-blocking manner.
|
||||||
|
*
|
||||||
|
* It will first download a given source file, store it locally and then
|
||||||
|
* upload the file again to a given destination file.
|
||||||
|
*
|
||||||
|
* Using the SFTP server running on 127.0.0.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libssh2.h>
|
||||||
|
#include <libssh2_sftp.h>
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
#else
|
||||||
|
# include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#define STORAGE "/tmp/sftp-storage" /* this is the local file name this
|
||||||
|
example uses to store the downloaded
|
||||||
|
file in */
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int sock, i, auth_pw = 1;
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
const char *fingerprint;
|
||||||
|
LIBSSH2_SESSION *session;
|
||||||
|
char *username=(char *)"username";
|
||||||
|
char *password=(char *)"password";
|
||||||
|
char *sftppath=(char *)"/tmp/TEST"; /* source path */
|
||||||
|
char *dest=(char *)"/tmp/TEST2"; /* destination path */
|
||||||
|
int rc;
|
||||||
|
LIBSSH2_SFTP *sftp_session;
|
||||||
|
LIBSSH2_SFTP_HANDLE *sftp_handle;
|
||||||
|
FILE *tempstorage;
|
||||||
|
char mem[1000];
|
||||||
|
struct timeval timeout;
|
||||||
|
fd_set fd;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
WSADATA wsadata;
|
||||||
|
|
||||||
|
WSAStartup(WINSOCK_VERSION, &wsadata);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Ultra basic "connect to port 22 on localhost"
|
||||||
|
* The application is responsible for creating the socket establishing
|
||||||
|
* the connection
|
||||||
|
*/
|
||||||
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
sin.sin_family = AF_INET;
|
||||||
|
sin.sin_port = htons(22);
|
||||||
|
sin.sin_addr.s_addr = htonl(0x7F000001);
|
||||||
|
if (connect(sock, (struct sockaddr*)(&sin),
|
||||||
|
sizeof(struct sockaddr_in)) != 0) {
|
||||||
|
fprintf(stderr, "failed to connect!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We set the socket non-blocking. We do it after the connect just to
|
||||||
|
simplify the example code. */
|
||||||
|
#ifdef F_SETFL
|
||||||
|
/* FIXME: this can/should be done in a more portable manner */
|
||||||
|
rc = fcntl(sock, F_GETFL, 0);
|
||||||
|
fcntl(sock, F_SETFL, rc | O_NONBLOCK);
|
||||||
|
#else
|
||||||
|
#error "add support for setting the socket non-blocking here"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Create a session instance
|
||||||
|
*/
|
||||||
|
session = libssh2_session_init();
|
||||||
|
if(!session)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* ... start it up. This will trade welcome banners, exchange keys,
|
||||||
|
* and setup crypto, compression, and MAC layers
|
||||||
|
*/
|
||||||
|
rc = libssh2_session_startup(session, sock);
|
||||||
|
if(rc) {
|
||||||
|
fprintf(stderr, "Failure establishing 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_MD5);
|
||||||
|
printf("Fingerprint: ");
|
||||||
|
for(i = 0; i < 16; i++) {
|
||||||
|
printf("%02X ", (unsigned char)fingerprint[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if(argc > 1) {
|
||||||
|
username = argv[1];
|
||||||
|
}
|
||||||
|
if(argc > 2) {
|
||||||
|
password = argv[2];
|
||||||
|
}
|
||||||
|
if(argc > 3) {
|
||||||
|
sftppath = argv[3];
|
||||||
|
}
|
||||||
|
if(argc > 4) {
|
||||||
|
dest = argv[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
tempstorage = fopen(STORAGE, "wb");
|
||||||
|
if(!tempstorage) {
|
||||||
|
printf("Can't open temp storage file %s\n", STORAGE);
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth_pw) {
|
||||||
|
/* We could authenticate via password */
|
||||||
|
if (libssh2_userauth_password(session, username, password)) {
|
||||||
|
printf("Authentication by password failed.\n");
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Or by public key */
|
||||||
|
if (libssh2_userauth_publickey_fromfile(session, username,
|
||||||
|
"/home/username/.ssh/id_rsa.pub",
|
||||||
|
"/home/username/.ssh/id_rsa",
|
||||||
|
password)) {
|
||||||
|
printf("\tAuthentication by public key failed\n");
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sftp_session = libssh2_sftp_init(session);
|
||||||
|
|
||||||
|
if (!sftp_session) {
|
||||||
|
fprintf(stderr, "Unable to init SFTP session\n");
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Request a file via SFTP */
|
||||||
|
sftp_handle =
|
||||||
|
libssh2_sftp_open(sftp_session, sftppath, LIBSSH2_FXF_READ, 0);
|
||||||
|
|
||||||
|
if (!sftp_handle) {
|
||||||
|
fprintf(stderr, "Unable to open file with SFTP\n");
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "libssh2_sftp_open() is done, now receive data!\n");
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
/* read in a loop until we block */
|
||||||
|
rc = libssh2_sftp_readnb(sftp_handle, mem,
|
||||||
|
sizeof(mem));
|
||||||
|
fprintf(stderr, "libssh2_sftp_read returned %d\n",
|
||||||
|
rc);
|
||||||
|
|
||||||
|
if(rc > 0) {
|
||||||
|
/* write to stderr */
|
||||||
|
write(2, mem, rc);
|
||||||
|
/* write to temporary storage area */
|
||||||
|
fwrite(mem, rc, 1, tempstorage);
|
||||||
|
}
|
||||||
|
} while (rc > 0);
|
||||||
|
|
||||||
|
if(rc != LIBSSH2SFTP_EAGAIN) {
|
||||||
|
/* error or end of file */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout.tv_sec = 10;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
|
FD_ZERO(&fd);
|
||||||
|
|
||||||
|
FD_SET(sock, &fd);
|
||||||
|
|
||||||
|
/* wait for readable or writeable */
|
||||||
|
rc = select(sock+1, &fd, &fd, NULL, &timeout);
|
||||||
|
if(rc <= 0) {
|
||||||
|
/* negative is error
|
||||||
|
0 is timeout */
|
||||||
|
fprintf(stderr, "SFTP download timed out: %d\n", rc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
libssh2_sftp_close(sftp_handle);
|
||||||
|
fclose(tempstorage);
|
||||||
|
|
||||||
|
tempstorage = fopen(STORAGE, "rb");
|
||||||
|
if(!tempstorage) {
|
||||||
|
/* weird, we can't read the file we just wrote to... */
|
||||||
|
fprintf(stderr, "can't open %s for reading\n", STORAGE);
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we're done downloading, now reverse the process and upload the
|
||||||
|
temporarily stored data to the destination path */
|
||||||
|
sftp_handle =
|
||||||
|
libssh2_sftp_open(sftp_session, dest,
|
||||||
|
LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT,
|
||||||
|
LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|
|
||||||
|
LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH);
|
||||||
|
if(sftp_handle) {
|
||||||
|
size_t nread;
|
||||||
|
char *ptr;
|
||||||
|
do {
|
||||||
|
nread = fread(mem, 1, sizeof(mem), tempstorage);
|
||||||
|
if(nread <= 0) {
|
||||||
|
/* end of file */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ptr = mem;
|
||||||
|
|
||||||
|
do {
|
||||||
|
/* write data in a loop until we block */
|
||||||
|
rc = libssh2_sftp_writenb(sftp_handle, ptr,
|
||||||
|
nread);
|
||||||
|
ptr += rc;
|
||||||
|
nread -= nread;
|
||||||
|
} while (rc > 0);
|
||||||
|
|
||||||
|
if(rc != LIBSSH2SFTP_EAGAIN) {
|
||||||
|
/* error or end of file */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout.tv_sec = 10;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
|
FD_ZERO(&fd);
|
||||||
|
|
||||||
|
FD_SET(sock, &fd);
|
||||||
|
|
||||||
|
/* wait for readable or writeable */
|
||||||
|
rc = select(sock+1, &fd, &fd, NULL, &timeout);
|
||||||
|
if(rc <= 0) {
|
||||||
|
/* negative is error
|
||||||
|
0 is timeout */
|
||||||
|
fprintf(stderr, "SFTP upload timed out: %d\n",
|
||||||
|
rc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
fprintf(stderr, "SFTP upload done!\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "SFTP failed to open destination path: %s\n",
|
||||||
|
dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
libssh2_sftp_shutdown(sftp_session);
|
||||||
|
|
||||||
|
shutdown:
|
||||||
|
|
||||||
|
libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");
|
||||||
|
libssh2_session_free(session);
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
Sleep(1000);
|
||||||
|
closesocket(sock);
|
||||||
|
#else
|
||||||
|
sleep(1);
|
||||||
|
close(sock);
|
||||||
|
#endif
|
||||||
|
printf("all done\n");
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms,
|
* Redistribution and use in source and binary forms,
|
||||||
@ -347,9 +347,22 @@ LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const
|
|||||||
#define libssh2_channel_exec(channel, command) libssh2_channel_process_startup((channel), "exec", sizeof("exec") - 1, (command), strlen(command))
|
#define libssh2_channel_exec(channel, command) libssh2_channel_process_startup((channel), "exec", sizeof("exec") - 1, (command), strlen(command))
|
||||||
#define libssh2_channel_subsystem(channel, subsystem) libssh2_channel_process_startup((channel), "subsystem", sizeof("subsystem") - 1, (subsystem), strlen(subsystem))
|
#define libssh2_channel_subsystem(channel, subsystem) libssh2_channel_process_startup((channel), "subsystem", sizeof("subsystem") - 1, (subsystem), strlen(subsystem))
|
||||||
|
|
||||||
LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, size_t buflen);
|
LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel,
|
||||||
#define libssh2_channel_read(channel, buf, buflen) libssh2_channel_read_ex((channel), 0, (buf), (buflen))
|
int stream_id, char *buf,
|
||||||
|
size_t buflen);
|
||||||
|
LIBSSH2_API int libssh2_channel_readnb_ex(LIBSSH2_CHANNEL *channel,
|
||||||
|
int stream_id, char *buf,
|
||||||
|
size_t buflen);
|
||||||
|
|
||||||
|
/* This is a public error code from libssh2_channel_read() that is returned
|
||||||
|
when it would otherwise block. */
|
||||||
|
#define LIBSSH2CHANNEL_EAGAIN -2
|
||||||
|
|
||||||
|
#define libssh2_channel_read(channel, buf, buflen) \
|
||||||
|
libssh2_channel_read_ex((channel), 0, (buf), (buflen))
|
||||||
#define libssh2_channel_read_stderr(channel, buf, buflen) libssh2_channel_read_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen))
|
#define libssh2_channel_read_stderr(channel, buf, buflen) libssh2_channel_read_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen))
|
||||||
|
#define libssh2_channel_readnb(channel, buf, buflen) \
|
||||||
|
libssh2_channel_readnb_ex((channel), 0, (buf), (buflen))
|
||||||
|
|
||||||
LIBSSH2_API int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended);
|
LIBSSH2_API int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended);
|
||||||
|
|
||||||
@ -358,14 +371,25 @@ LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channe
|
|||||||
|
|
||||||
LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force);
|
LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force);
|
||||||
|
|
||||||
LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen);
|
LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel,
|
||||||
#define libssh2_channel_write(channel, buf, buflen) libssh2_channel_write_ex((channel), 0, (buf), (buflen))
|
int stream_id, const char *buf,
|
||||||
|
size_t buflen);
|
||||||
|
LIBSSH2_API int libssh2_channel_writenb_ex(LIBSSH2_CHANNEL *channel,
|
||||||
|
int stream_id, const char *buf,
|
||||||
|
size_t buflen);
|
||||||
|
|
||||||
|
#define libssh2_channel_write(channel, buf, buflen) \
|
||||||
|
libssh2_channel_write_ex((channel), 0, (buf), (buflen))
|
||||||
|
#define libssh2_channel_writenb(channel, buf, buflen) \
|
||||||
|
libssh2_channel_writenb_ex((channel), 0, (buf), (buflen))
|
||||||
#define libssh2_channel_write_stderr(channel, buf, buflen) libssh2_channel_write_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen))
|
#define libssh2_channel_write_stderr(channel, buf, buflen) libssh2_channel_write_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen))
|
||||||
|
|
||||||
LIBSSH2_API unsigned long libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, unsigned long *window_size_initial);
|
LIBSSH2_API unsigned long libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, unsigned long *window_size_initial);
|
||||||
#define libssh2_channel_window_write(channel) libssh2_channel_window_write_ex((channel), NULL)
|
#define libssh2_channel_window_write(channel) libssh2_channel_window_write_ex((channel), NULL)
|
||||||
|
|
||||||
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking);
|
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking);
|
||||||
|
LIBSSH2_API int libssh2_channel_get_blocking(LIBSSH2_CHANNEL *channel);
|
||||||
|
|
||||||
LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode);
|
LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode);
|
||||||
/* libssh2_channel_ignore_extended_data() is defined below for BC with version 0.1
|
/* libssh2_channel_ignore_extended_data() is defined below for BC with version 0.1
|
||||||
* Future uses should use libssh2_channel_handle_extended_data() directly
|
* Future uses should use libssh2_channel_handle_extended_data() directly
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms,
|
* Redistribution and use in source and binary forms,
|
||||||
@ -38,6 +38,8 @@
|
|||||||
#ifndef LIBSSH2_SFTP_H
|
#ifndef LIBSSH2_SFTP_H
|
||||||
#define LIBSSH2_SFTP_H 1
|
#define LIBSSH2_SFTP_H 1
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@ -179,9 +181,19 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *
|
|||||||
#define libssh2_sftp_open(sftp, filename, flags, mode) libssh2_sftp_open_ex((sftp), (filename), strlen(filename), (flags), (mode), LIBSSH2_SFTP_OPENFILE)
|
#define libssh2_sftp_open(sftp, filename, flags, mode) libssh2_sftp_open_ex((sftp), (filename), strlen(filename), (flags), (mode), LIBSSH2_SFTP_OPENFILE)
|
||||||
#define libssh2_sftp_opendir(sftp, path) libssh2_sftp_open_ex((sftp), (path), strlen(path), 0, 0, LIBSSH2_SFTP_OPENDIR)
|
#define libssh2_sftp_opendir(sftp, path) libssh2_sftp_open_ex((sftp), (path), strlen(path), 0, 0, LIBSSH2_SFTP_OPENDIR)
|
||||||
|
|
||||||
LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen);
|
/* This is a public error code from libssh2_sftp_read() that is returned
|
||||||
|
when it would otherwise block. */
|
||||||
|
#define LIBSSH2SFTP_EAGAIN -2
|
||||||
|
LIBSSH2_API ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle,
|
||||||
|
char *buffer, size_t buffer_maxlen);
|
||||||
|
LIBSSH2_API ssize_t libssh2_sftp_readnb(LIBSSH2_SFTP_HANDLE *handle,
|
||||||
|
char *buffer, size_t buffer_maxlen);
|
||||||
|
|
||||||
LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, LIBSSH2_SFTP_ATTRIBUTES *attrs);
|
LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, LIBSSH2_SFTP_ATTRIBUTES *attrs);
|
||||||
LIBSSH2_API size_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count);
|
LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle,
|
||||||
|
const char *buffer, size_t count);
|
||||||
|
LIBSSH2_API ssize_t libssh2_sftp_writenb(LIBSSH2_SFTP_HANDLE *handle,
|
||||||
|
const char *buffer, size_t count);
|
||||||
|
|
||||||
LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle);
|
LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle);
|
||||||
#define libssh2_sftp_close(handle) libssh2_sftp_close_handle(handle)
|
#define libssh2_sftp_close(handle) libssh2_sftp_close_handle(handle)
|
||||||
|
@ -2,7 +2,7 @@ AUTOMAKE_OPTIONS = foreign nostdinc
|
|||||||
|
|
||||||
libssh2_la_SOURCES = channel.c comp.c crypt.c hostkey.c kex.c mac.c \
|
libssh2_la_SOURCES = channel.c comp.c crypt.c hostkey.c kex.c mac.c \
|
||||||
misc.c packet.c publickey.c scp.c session.c sftp.c userauth.c \
|
misc.c packet.c publickey.c scp.c session.c sftp.c userauth.c \
|
||||||
libssh2_priv.h openssl.h libgcrypt.h pem.c
|
libssh2_priv.h openssl.h libgcrypt.h pem.c transport.c
|
||||||
|
|
||||||
if LIBGCRYPT
|
if LIBGCRYPT
|
||||||
libssh2_la_SOURCES += libgcrypt.c
|
libssh2_la_SOURCES += libgcrypt.c
|
||||||
|
433
src/channel.c
433
src/channel.c
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms,
|
* Redistribution and use in source and binary forms,
|
||||||
@ -38,8 +38,11 @@
|
|||||||
#include "libssh2_priv.h"
|
#include "libssh2_priv.h"
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
/* {{{ libssh2_channel_nextid
|
/* {{{ libssh2_channel_nextid
|
||||||
* Determine the next channel ID we can use at our end
|
* Determine the next channel ID we can use at our end
|
||||||
*/
|
*/
|
||||||
@ -89,14 +92,14 @@ LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long
|
|||||||
|
|
||||||
#define libssh2_channel_add(session, channel) \
|
#define libssh2_channel_add(session, channel) \
|
||||||
{ \
|
{ \
|
||||||
if ((session)->channels.tail) { \
|
if ((session)->channels.tail) { \
|
||||||
(session)->channels.tail->next = (channel); \
|
(session)->channels.tail->next = (channel); \
|
||||||
(channel)->prev = (session)->channels.tail; \
|
(channel)->prev = (session)->channels.tail; \
|
||||||
} else { \
|
} else { \
|
||||||
(session)->channels.head = (channel); \
|
(session)->channels.head = (channel); \
|
||||||
(channel)->prev = NULL; \
|
(channel)->prev = NULL; \
|
||||||
} \
|
} \
|
||||||
(channel)->next = NULL; \
|
(channel)->next = NULL; \
|
||||||
(session)->channels.tail = (channel); \
|
(session)->channels.tail = (channel); \
|
||||||
(channel)->session = (session); \
|
(channel)->session = (session); \
|
||||||
}
|
}
|
||||||
@ -104,81 +107,111 @@ LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long
|
|||||||
/* {{{ libssh2_channel_open_session
|
/* {{{ libssh2_channel_open_session
|
||||||
* Establish a generic session channel
|
* Establish a generic session channel
|
||||||
*/
|
*/
|
||||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *channel_type, unsigned int channel_type_len, unsigned int window_size,
|
LIBSSH2_API LIBSSH2_CHANNEL *
|
||||||
unsigned int packet_size, const char *message, unsigned int message_len)
|
libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *channel_type,
|
||||||
|
unsigned int channel_type_len,
|
||||||
|
unsigned int window_size,
|
||||||
|
unsigned int packet_size, const char *message,
|
||||||
|
unsigned int message_len)
|
||||||
{
|
{
|
||||||
unsigned char reply_codes[3] = { SSH_MSG_CHANNEL_OPEN_CONFIRMATION, SSH_MSG_CHANNEL_OPEN_FAILURE, 0 };
|
unsigned char reply_codes[3] = {
|
||||||
|
SSH_MSG_CHANNEL_OPEN_CONFIRMATION,
|
||||||
|
SSH_MSG_CHANNEL_OPEN_FAILURE,
|
||||||
|
0
|
||||||
|
};
|
||||||
LIBSSH2_CHANNEL *channel = NULL;
|
LIBSSH2_CHANNEL *channel = NULL;
|
||||||
unsigned long local_channel = libssh2_channel_nextid(session);
|
unsigned long local_channel = libssh2_channel_nextid(session);
|
||||||
unsigned char *s, *packet = NULL;
|
unsigned char *s, *packet = NULL;
|
||||||
unsigned long packet_len = channel_type_len + message_len + 17; /* packet_type(1) + channel_type_len(4) + sender_channel(4) +
|
unsigned long packet_len =
|
||||||
window_size(4) + packet_size(4) */
|
channel_type_len + message_len + 17; /* packet_type(1) +
|
||||||
|
channel_type_len(4) +
|
||||||
|
sender_channel(4) +
|
||||||
|
window_size(4) +
|
||||||
|
packet_size(4) */
|
||||||
unsigned char *data = NULL;
|
unsigned char *data = NULL;
|
||||||
unsigned long data_len;
|
unsigned long data_len;
|
||||||
|
|
||||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Opening Channel - win %d pack %d", window_size, packet_size);
|
_libssh2_debug(session, LIBSSH2_DBG_CONN,
|
||||||
|
"Opening Channel - win %d pack %d",
|
||||||
|
window_size, packet_size);
|
||||||
#endif
|
#endif
|
||||||
channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL));
|
channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL));
|
||||||
if (!channel) {
|
if (!channel) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for channel data", 0);
|
libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
||||||
|
"Unable to allocate space for channel data", 0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memset(channel, 0, sizeof(LIBSSH2_CHANNEL));
|
memset(channel, 0, sizeof(LIBSSH2_CHANNEL));
|
||||||
|
|
||||||
channel->channel_type_len = channel_type_len;
|
channel->channel_type_len = channel_type_len;
|
||||||
channel->channel_type = LIBSSH2_ALLOC(session, channel_type_len);
|
channel->channel_type = LIBSSH2_ALLOC(session, channel_type_len);
|
||||||
if (!channel->channel_type) {
|
if (!channel->channel_type) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating memory for channel type name", 0);
|
libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
||||||
|
"Failed allocating memory for channel type name", 0);
|
||||||
LIBSSH2_FREE(session, channel);
|
LIBSSH2_FREE(session, channel);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memcpy(channel->channel_type, channel_type, channel_type_len);
|
memcpy(channel->channel_type, channel_type, channel_type_len);
|
||||||
|
|
||||||
/* REMEMBER: local as in locally sourced */
|
/* REMEMBER: local as in locally sourced */
|
||||||
channel->local.id = local_channel;
|
channel->local.id = local_channel;
|
||||||
channel->remote.window_size = window_size;
|
channel->remote.window_size = window_size;
|
||||||
channel->remote.window_size_initial = window_size;
|
channel->remote.window_size_initial = window_size;
|
||||||
channel->remote.packet_size = packet_size;
|
channel->remote.packet_size = packet_size;
|
||||||
|
|
||||||
libssh2_channel_add(session, channel);
|
libssh2_channel_add(session, channel);
|
||||||
|
|
||||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||||
if (!packet) {
|
if (!packet) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate temporary space for packet", 0);
|
libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
||||||
|
"Unable to allocate temporary space for packet",
|
||||||
|
0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
*(s++) = SSH_MSG_CHANNEL_OPEN;
|
*(s++) = SSH_MSG_CHANNEL_OPEN;
|
||||||
libssh2_htonu32(s, channel_type_len); s += 4;
|
libssh2_htonu32(s, channel_type_len);
|
||||||
memcpy(s, channel_type, channel_type_len); s += channel_type_len;
|
s += 4;
|
||||||
|
|
||||||
libssh2_htonu32(s, local_channel); s += 4;
|
memcpy(s, channel_type, channel_type_len);
|
||||||
libssh2_htonu32(s, window_size); s += 4;
|
s += channel_type_len;
|
||||||
libssh2_htonu32(s, packet_size); s += 4;
|
|
||||||
|
libssh2_htonu32(s, local_channel);
|
||||||
|
s += 4;
|
||||||
|
|
||||||
|
libssh2_htonu32(s, window_size);
|
||||||
|
s += 4;
|
||||||
|
|
||||||
|
libssh2_htonu32(s, packet_size);
|
||||||
|
s += 4;
|
||||||
|
|
||||||
if (message && message_len) {
|
if (message && message_len) {
|
||||||
memcpy(s, message, message_len); s += message_len;
|
memcpy(s, message, message_len);
|
||||||
|
s += message_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libssh2_packet_write(session, packet, packet_len)) {
|
if (libssh2_packet_write(session, packet, packet_len)) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel-open request", 0);
|
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
||||||
|
"Unable to send channel-open request", 0);
|
||||||
goto channel_error;
|
goto channel_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, packet + 5 + channel_type_len, 4)) {
|
if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len,
|
||||||
|
1, packet + 5 + channel_type_len, 4)) {
|
||||||
goto channel_error;
|
goto channel_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
|
if (data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
|
||||||
channel->remote.id = libssh2_ntohu32(data + 5);
|
channel->remote.id = libssh2_ntohu32(data + 5);
|
||||||
channel->local.window_size = libssh2_ntohu32(data + 9);
|
channel->local.window_size = libssh2_ntohu32(data + 9);
|
||||||
channel->local.window_size_initial = libssh2_ntohu32(data + 9);
|
channel->local.window_size_initial = libssh2_ntohu32(data + 9);
|
||||||
channel->local.packet_size = libssh2_ntohu32(data + 13);
|
channel->local.packet_size = libssh2_ntohu32(data + 13);
|
||||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Connection Established - ID: %lu/%lu win: %lu/%lu pack: %lu/%lu",
|
_libssh2_debug(session, LIBSSH2_DBG_CONN,
|
||||||
channel->local.id, channel->remote.id,
|
"Connection Established - ID: %lu/%lu win: %lu/%lu pack: %lu/%lu",
|
||||||
channel->local.window_size, channel->remote.window_size,
|
channel->local.id, channel->remote.id,
|
||||||
channel->local.packet_size, channel->remote.packet_size);
|
channel->local.window_size, channel->remote.window_size,
|
||||||
|
channel->local.packet_size, channel->remote.packet_size);
|
||||||
#endif
|
#endif
|
||||||
LIBSSH2_FREE(session, packet);
|
LIBSSH2_FREE(session, packet);
|
||||||
LIBSSH2_FREE(session, data);
|
LIBSSH2_FREE(session, data);
|
||||||
@ -187,7 +220,8 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) {
|
if (data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Channel open failure", 0);
|
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
|
||||||
|
"Channel open failure", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_error:
|
channel_error:
|
||||||
@ -217,8 +251,12 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, c
|
|||||||
|
|
||||||
/* Clear out packets meant for this channel */
|
/* Clear out packets meant for this channel */
|
||||||
libssh2_htonu32(channel_id, channel->local.id);
|
libssh2_htonu32(channel_id, channel->local.id);
|
||||||
while ((libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0) ||
|
while ((libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_DATA,
|
||||||
(libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0)) {
|
&data, &data_len, 1, channel_id,
|
||||||
|
4, 1) >= 0) ||
|
||||||
|
(libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_DATA,
|
||||||
|
&data, &data_len, 1, channel_id,
|
||||||
|
4, 1) >= 0)) {
|
||||||
LIBSSH2_FREE(session, data);
|
LIBSSH2_FREE(session, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,10 +295,10 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *se
|
|||||||
libssh2_htonu32(s, sport); s += 4;
|
libssh2_htonu32(s, sport); s += 4;
|
||||||
|
|
||||||
channel = libssh2_channel_open_ex(session, "direct-tcpip",
|
channel = libssh2_channel_open_ex(session, "direct-tcpip",
|
||||||
sizeof("direct-tcpip") - 1,
|
sizeof("direct-tcpip") - 1,
|
||||||
LIBSSH2_CHANNEL_WINDOW_DEFAULT,
|
LIBSSH2_CHANNEL_WINDOW_DEFAULT,
|
||||||
LIBSSH2_CHANNEL_PACKET_DEFAULT,
|
LIBSSH2_CHANNEL_PACKET_DEFAULT,
|
||||||
(char *)message, message_len);
|
(char *)message, message_len);
|
||||||
LIBSSH2_FREE(session, message);
|
LIBSSH2_FREE(session, message);
|
||||||
|
|
||||||
return channel;
|
return channel;
|
||||||
@ -430,9 +468,18 @@ LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
|
|||||||
/* {{{ libssh2_channel_forward_accept
|
/* {{{ libssh2_channel_forward_accept
|
||||||
* Accept a connection
|
* Accept a connection
|
||||||
*/
|
*/
|
||||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener)
|
LIBSSH2_API LIBSSH2_CHANNEL *
|
||||||
|
libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener)
|
||||||
{
|
{
|
||||||
while (libssh2_packet_read(listener->session, 0) > 0);
|
libssh2pack_t rc;
|
||||||
|
int loop=-1;
|
||||||
|
do {
|
||||||
|
rc = libssh2_packet_read(listener->session);
|
||||||
|
loop++;
|
||||||
|
} while (rc > 0);
|
||||||
|
|
||||||
|
/* dast: now this might have returned with EAGAIN status which might
|
||||||
|
be somehow signalled to the caller... */
|
||||||
|
|
||||||
if (listener->queue) {
|
if (listener->queue) {
|
||||||
LIBSSH2_SESSION *session = listener->session;
|
LIBSSH2_SESSION *session = listener->session;
|
||||||
@ -515,7 +562,8 @@ LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varnam
|
|||||||
}
|
}
|
||||||
|
|
||||||
LIBSSH2_FREE(session, data);
|
LIBSSH2_FREE(session, data);
|
||||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-setenv", 0);
|
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED,
|
||||||
|
"Unable to complete request for channel-setenv", 0);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
@ -678,6 +726,7 @@ LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const
|
|||||||
unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4];
|
unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4];
|
||||||
unsigned long data_len;
|
unsigned long data_len;
|
||||||
unsigned long packet_len = request_len + 10; /* packet_type(1) + channel(4) + request_len(4) + want_reply(1) */
|
unsigned long packet_len = request_len + 10; /* packet_type(1) + channel(4) + request_len(4) + want_reply(1) */
|
||||||
|
libssh2pack_t rc;
|
||||||
|
|
||||||
if (message) {
|
if (message) {
|
||||||
packet_len += message_len + 4;
|
packet_len += message_len + 4;
|
||||||
@ -705,7 +754,8 @@ LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const
|
|||||||
memcpy(s, message, message_len); s += message_len;
|
memcpy(s, message, message_len); s += message_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libssh2_packet_write(session, packet, packet_len)) {
|
rc = libssh2_packet_write(session, packet, packet_len);
|
||||||
|
if(rc) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel request", 0);
|
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel request", 0);
|
||||||
LIBSSH2_FREE(session, packet);
|
LIBSSH2_FREE(session, packet);
|
||||||
return -1;
|
return -1;
|
||||||
@ -728,15 +778,61 @@ LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const
|
|||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
/* {{{ libssh2_channel_set_blocking
|
/* {{{ _libssh2_channel_set_blocking
|
||||||
* Set a channel's blocking mode on or off, similar to a socket's fcntl(fd, F_SETFL, O_NONBLOCK); type command
|
* Set a channel's blocking mode on or off, return the status when this
|
||||||
|
* function is called.
|
||||||
*/
|
*/
|
||||||
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking)
|
int _libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel,
|
||||||
|
int blocking)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
int bl = channel->blocking;
|
||||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||||
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Setting blocking mode on channel %lu/%lu to %d", channel->local.id, channel->remote.id, blocking);
|
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN,
|
||||||
|
"Setting blocking mode on channel %lu/%lu to %d",
|
||||||
|
channel->local.id, channel->remote.id, blocking);
|
||||||
#endif
|
#endif
|
||||||
|
if(blocking == channel->blocking) {
|
||||||
|
/* avoid if already correct */
|
||||||
|
return bl;
|
||||||
|
}
|
||||||
channel->blocking = blocking;
|
channel->blocking = blocking;
|
||||||
|
|
||||||
|
#ifdef F_SETFL
|
||||||
|
/* FIXME: this can/should be done in a more portable manner */
|
||||||
|
rc = fcntl(channel->session->socket_fd, F_GETFL, 0);
|
||||||
|
if(!blocking) {
|
||||||
|
rc |= O_NONBLOCK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rc &= ~O_NONBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
fcntl(channel->session->socket_fd, F_SETFL, rc);
|
||||||
|
#else
|
||||||
|
#error "add support for setting the socket non-blocking here"
|
||||||
|
#endif
|
||||||
|
return bl;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ libssh2_channel_set_blocking
|
||||||
|
* Set a channel's blocking mode on or off, similar to a socket's
|
||||||
|
* fcntl(fd, F_SETFL, O_NONBLOCK); type command
|
||||||
|
*/
|
||||||
|
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel,
|
||||||
|
int blocking)
|
||||||
|
{
|
||||||
|
(void)_libssh2_channel_set_blocking(channel, blocking);
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ libssh2_channel_get_blocking
|
||||||
|
* Returns a channel's blocking mode on or off
|
||||||
|
*/
|
||||||
|
int libssh2_channel_get_blocking(LIBSSH2_CHANNEL *channel)
|
||||||
|
{
|
||||||
|
return channel->blocking;
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
@ -753,14 +849,21 @@ LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid)
|
|||||||
LIBSSH2_PACKET *next = packet->next;
|
LIBSSH2_PACKET *next = packet->next;
|
||||||
unsigned char packet_type = packet->data[0];
|
unsigned char packet_type = packet->data[0];
|
||||||
|
|
||||||
if (((packet_type == SSH_MSG_CHANNEL_DATA) || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) &&
|
if (((packet_type == SSH_MSG_CHANNEL_DATA) ||
|
||||||
(libssh2_ntohu32(packet->data + 1) == channel->local.id)) {
|
(packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) &&
|
||||||
|
(libssh2_ntohu32(packet->data + 1) == channel->local.id)) {
|
||||||
/* It's our channel at least */
|
/* It's our channel at least */
|
||||||
unsigned long packet_stream_id = (packet_type == SSH_MSG_CHANNEL_DATA) ? 0 : libssh2_ntohu32(packet->data + 5);
|
long packet_stream_id =
|
||||||
|
(packet_type == SSH_MSG_CHANNEL_DATA) ?
|
||||||
|
0 : libssh2_ntohu32(packet->data + 5);
|
||||||
if ((streamid == LIBSSH2_CHANNEL_FLUSH_ALL) ||
|
if ((streamid == LIBSSH2_CHANNEL_FLUSH_ALL) ||
|
||||||
((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA) && ((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) || (streamid == packet_stream_id))) ||
|
((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA) &&
|
||||||
((packet_type == SSH_MSG_CHANNEL_DATA) && (streamid == 0))) {
|
((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) ||
|
||||||
int bytes_to_flush = packet->data_len - packet->data_head;
|
(streamid == packet_stream_id))) ||
|
||||||
|
((packet_type == SSH_MSG_CHANNEL_DATA) &&
|
||||||
|
(streamid == 0))) {
|
||||||
|
int bytes_to_flush =
|
||||||
|
packet->data_len - packet->data_head;
|
||||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||||
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Flushing %d bytes of data from stream %lu on channel %lu/%lu", bytes_to_flush,
|
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Flushing %d bytes of data from stream %lu on channel %lu/%lu", bytes_to_flush,
|
||||||
packet_stream_id, channel->local.id, channel->remote.id);
|
packet_stream_id, channel->local.id, channel->remote.id);
|
||||||
@ -870,39 +973,89 @@ LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel,
|
|||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
/* {{{ libssh2_channel_read_ex
|
/* {{{ _libssh2_channel_read_ex
|
||||||
* Read data from a channel
|
* Read data from a channel blocking or non-blocking depending on set state
|
||||||
|
*
|
||||||
|
* When this is done non-blocking, it is important to not return 0 until the
|
||||||
|
* currently read channel is complete. If we read stuff from the wire but it
|
||||||
|
* was no payload data to fill in the buffer with, we MUST make sure to return
|
||||||
|
* PACKET_EAGAIN.
|
||||||
*/
|
*/
|
||||||
LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, size_t buflen)
|
ssize_t _libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel,
|
||||||
|
int stream_id, char *buf, size_t buflen)
|
||||||
{
|
{
|
||||||
LIBSSH2_SESSION *session = channel->session;
|
LIBSSH2_SESSION *session = channel->session;
|
||||||
int bytes_read = 0, blocking_read = 0;
|
int bytes_read = 0;
|
||||||
|
LIBSSH2_PACKET *packet;
|
||||||
|
libssh2pack_t rc=0;
|
||||||
|
int bl;
|
||||||
|
int block=0;
|
||||||
|
|
||||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Attempting to read %d bytes from channel %lu/%lu stream #%d", (int)buflen, channel->local.id, channel->remote.id, stream_id);
|
_libssh2_debug(session, LIBSSH2_DBG_CONN,
|
||||||
|
"Attempting to read %d bytes from channel %lu/%lu stream #%d",
|
||||||
|
(int)buflen,
|
||||||
|
channel->local.id, channel->remote.id, stream_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* set non-blocking and remember previous state */
|
||||||
|
bl = _libssh2_channel_set_blocking(channel, 0);
|
||||||
|
|
||||||
|
/* process all incoming packets */
|
||||||
do {
|
do {
|
||||||
LIBSSH2_PACKET *packet;
|
rc = libssh2_packet_read(session);
|
||||||
|
} while (rc > 0);
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
/* Process any waiting packets */
|
/* restore blocking state */
|
||||||
while (libssh2_packet_read(session, blocking_read) > 0) blocking_read = 0;
|
_libssh2_channel_set_blocking(channel, bl);
|
||||||
packet = session->packets.head;
|
|
||||||
|
|
||||||
while (packet && (bytes_read < buflen)) {
|
packet = session->packets.head;
|
||||||
/* In case packet gets destroyed during this iteration */
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
if(block) {
|
||||||
|
/* in the second lap and onwards, do this */
|
||||||
|
rc = libssh2_packet_read(session);
|
||||||
|
packet = session->packets.head;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rc < 0) {
|
||||||
|
/* no packets available */
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (packet && (bytes_read < (int)buflen)) {
|
||||||
|
/* In case packet gets destroyed during this
|
||||||
|
iteration */
|
||||||
LIBSSH2_PACKET *next = packet->next;
|
LIBSSH2_PACKET *next = packet->next;
|
||||||
|
|
||||||
/* Either we asked for a specific extended data stream (and data was available),
|
uint32_t local_id = libssh2_ntohu32(packet->data + 1);
|
||||||
|
|
||||||
|
/* Either we asked for a specific extended data stream
|
||||||
|
* (and data was available),
|
||||||
* or the standard stream (and data was available),
|
* or the standard stream (and data was available),
|
||||||
* or the standard stream with extended_data_merge enabled and data was available
|
* or the standard stream with extended_data_merge
|
||||||
|
* enabled and data was available
|
||||||
*/
|
*/
|
||||||
if ((stream_id && (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && (channel->local.id == libssh2_ntohu32(packet->data + 1)) && (stream_id == libssh2_ntohu32(packet->data + 5))) ||
|
if ((stream_id &&
|
||||||
(!stream_id && (packet->data[0] == SSH_MSG_CHANNEL_DATA) && (channel->local.id == libssh2_ntohu32(packet->data + 1))) ||
|
(packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) &&
|
||||||
(!stream_id && (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && (channel->local.id == libssh2_ntohu32(packet->data + 1)) && (channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) {
|
(channel->local.id == local_id) &&
|
||||||
|
(stream_id == (int)libssh2_ntohu32(packet->data + 5))) ||
|
||||||
|
|
||||||
|
(!stream_id &&
|
||||||
|
(packet->data[0] == SSH_MSG_CHANNEL_DATA) &&
|
||||||
|
(channel->local.id == local_id)) ||
|
||||||
|
|
||||||
|
(!stream_id &&
|
||||||
|
(packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) &&
|
||||||
|
(channel->local.id == local_id) &&
|
||||||
|
(channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) {
|
||||||
|
|
||||||
int want = buflen - bytes_read;
|
int want = buflen - bytes_read;
|
||||||
int unlink_packet = 0;
|
int unlink_packet = 0;
|
||||||
|
|
||||||
if (want >= (packet->data_len - packet->data_head)) {
|
if (want >= (int)(packet->data_len - packet->data_head)) {
|
||||||
want = packet->data_len - packet->data_head;
|
want = packet->data_len - packet->data_head;
|
||||||
unlink_packet = 1;
|
unlink_packet = 1;
|
||||||
}
|
}
|
||||||
@ -936,21 +1089,78 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
|
|||||||
}
|
}
|
||||||
packet = next;
|
packet = next;
|
||||||
}
|
}
|
||||||
blocking_read = 1;
|
block=1;
|
||||||
} while (channel->blocking && (bytes_read == 0) && !channel->remote.close);
|
|
||||||
|
|
||||||
if (channel->blocking && (bytes_read == 0)) {
|
} while (channel->blocking && (bytes_read == 0) &&
|
||||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "Remote end has closed this channel", 0);
|
!channel->remote.close);
|
||||||
|
|
||||||
|
if (bytes_read == 0) {
|
||||||
|
if(channel->blocking) {
|
||||||
|
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED,
|
||||||
|
"Remote end has closed this channel", 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* when non-blocking, we must return PACKET_EAGAIN if
|
||||||
|
we haven't completed reading the channel */
|
||||||
|
if(!libssh2_channel_eof(channel)) {
|
||||||
|
return PACKET_EAGAIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes_read;
|
return bytes_read;
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
|
||||||
|
LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel,
|
||||||
|
int stream_id,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
ssize_t rc;
|
||||||
|
int bl;
|
||||||
|
|
||||||
|
/* set blocking */
|
||||||
|
bl = _libssh2_channel_set_blocking(channel, 1);
|
||||||
|
|
||||||
|
rc = _libssh2_channel_read_ex(channel, stream_id, buf, buflen);
|
||||||
|
|
||||||
|
/* restore state */
|
||||||
|
_libssh2_channel_set_blocking(channel, bl);
|
||||||
|
|
||||||
|
if(rc < 0) {
|
||||||
|
/* precent accidental returning of other return codes since
|
||||||
|
this API does not support/provide those */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBSSH2_API int libssh2_channel_readnb_ex(LIBSSH2_CHANNEL *channel,
|
||||||
|
int stream_id,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
ssize_t rc;
|
||||||
|
|
||||||
|
/* set non-blocking */
|
||||||
|
int bl = _libssh2_channel_set_blocking(channel, 0);
|
||||||
|
|
||||||
|
rc = _libssh2_channel_read_ex(channel, stream_id, buf, buflen);
|
||||||
|
|
||||||
|
/* restore state */
|
||||||
|
_libssh2_channel_set_blocking(channel, bl);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* {{{ libssh2_channel_write_ex
|
/* {{{ libssh2_channel_write_ex
|
||||||
* Send data to a channel
|
* Send data to a channel
|
||||||
*/
|
*/
|
||||||
LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen)
|
int _libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel,
|
||||||
|
int stream_id,
|
||||||
|
const char *buf, size_t buflen)
|
||||||
{
|
{
|
||||||
LIBSSH2_SESSION *session = channel->session;
|
LIBSSH2_SESSION *session = channel->session;
|
||||||
unsigned char *packet;
|
unsigned char *packet;
|
||||||
@ -983,6 +1193,7 @@ LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id
|
|||||||
while (buflen > 0) {
|
while (buflen > 0) {
|
||||||
size_t bufwrite = buflen;
|
size_t bufwrite = buflen;
|
||||||
unsigned char *s = packet;
|
unsigned char *s = packet;
|
||||||
|
libssh2pack_t rc;
|
||||||
|
|
||||||
*(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA;
|
*(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA;
|
||||||
libssh2_htonu32(s, channel->remote.id); s += 4;
|
libssh2_htonu32(s, channel->remote.id); s += 4;
|
||||||
@ -992,11 +1203,18 @@ LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id
|
|||||||
|
|
||||||
/* twiddle our thumbs until there's window space available */
|
/* twiddle our thumbs until there's window space available */
|
||||||
while (channel->local.window_size <= 0) {
|
while (channel->local.window_size <= 0) {
|
||||||
/* Don't worry -- This is never hit unless it's a blocking channel anyway */
|
/* Don't worry -- This is never hit unless it's a
|
||||||
if (libssh2_packet_read(session, 1) < 0) {
|
blocking channel anyway */
|
||||||
/* Error occured, disconnect? */
|
rc = libssh2_packet_read(session);
|
||||||
return -1;
|
|
||||||
|
if (rc < 0) {
|
||||||
|
/* Error or EAGAIN occured, disconnect? */
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME: (dast) if rc == 0 here then this busyloops
|
||||||
|
like hell until data arrives on the network which
|
||||||
|
seems like a very bad idea */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't exceed the remote end's limits */
|
/* Don't exceed the remote end's limits */
|
||||||
@ -1017,9 +1235,10 @@ LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id
|
|||||||
memcpy(s, buf, bufwrite); s += bufwrite;
|
memcpy(s, buf, bufwrite); s += bufwrite;
|
||||||
|
|
||||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending %d bytes on channel %lu/%lu, stream_id=%d", (int)bufwrite, channel->local.id, channel->remote.id, stream_id);
|
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending %d bytes on channel %lu/%lu, stream_id=%d", (int)bufwrite, channel->local.id, channel->remote.id, stream_id);
|
||||||
#endif
|
#endif
|
||||||
if (libssh2_packet_write(session, packet, s - packet)) {
|
rc = libssh2_packet_write(session, packet, s - packet);
|
||||||
|
if(rc) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel data", 0);
|
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel data", 0);
|
||||||
LIBSSH2_FREE(session, packet);
|
LIBSSH2_FREE(session, packet);
|
||||||
return -1;
|
return -1;
|
||||||
@ -1043,6 +1262,50 @@ LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id
|
|||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ libssh2_channel_write_ex
|
||||||
|
* Send data to a channel blocking
|
||||||
|
*/
|
||||||
|
LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel,
|
||||||
|
int stream_id,
|
||||||
|
const char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
ssize_t rc;
|
||||||
|
/* set blocking */
|
||||||
|
int bl = _libssh2_channel_set_blocking(channel, 1);
|
||||||
|
|
||||||
|
rc = _libssh2_channel_write_ex(channel, stream_id, buf, buflen);
|
||||||
|
|
||||||
|
/* restore state */
|
||||||
|
_libssh2_channel_set_blocking(channel, bl);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ libssh2_channel_writenb_ex
|
||||||
|
* Send data to a channel non-blocking
|
||||||
|
*/
|
||||||
|
LIBSSH2_API int libssh2_channel_writenb_ex(LIBSSH2_CHANNEL *channel,
|
||||||
|
int stream_id,
|
||||||
|
const char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
ssize_t rc;
|
||||||
|
int bl;
|
||||||
|
|
||||||
|
/* set non-blocking */
|
||||||
|
bl = _libssh2_channel_set_blocking(channel, 0);
|
||||||
|
|
||||||
|
rc = _libssh2_channel_write_ex(channel, stream_id, buf, buflen);
|
||||||
|
|
||||||
|
/* restore state */
|
||||||
|
(void)_libssh2_channel_set_blocking(channel, bl);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
|
||||||
/* {{{ libssh2_channel_send_eof
|
/* {{{ libssh2_channel_send_eof
|
||||||
* Send EOF on channel
|
* Send EOF on channel
|
||||||
*/
|
*/
|
||||||
@ -1143,7 +1406,7 @@ LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel)
|
|||||||
* Either or channel will be closed
|
* Either or channel will be closed
|
||||||
* or network timeout will occur
|
* or network timeout will occur
|
||||||
*/
|
*/
|
||||||
while (!channel->remote.close && libssh2_packet_read(session, 1) > 0)
|
while (!channel->remote.close && libssh2_packet_read(session) > 0)
|
||||||
;
|
;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -1176,7 +1439,7 @@ LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel)
|
|||||||
|
|
||||||
/* Clear out packets meant for this channel */
|
/* Clear out packets meant for this channel */
|
||||||
libssh2_htonu32(channel_id, channel->local.id);
|
libssh2_htonu32(channel_id, channel->local.id);
|
||||||
while ((libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0) ||
|
while ((libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0) ||
|
||||||
(libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0)) {
|
(libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0)) {
|
||||||
LIBSSH2_FREE(session, data);
|
LIBSSH2_FREE(session, data);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms,
|
* Redistribution and use in source and binary forms,
|
||||||
@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
#include "libssh2_priv.h"
|
#include "libssh2_priv.h"
|
||||||
|
|
||||||
#if LIBSSH2_CRYPT_NONE
|
#ifdef LIBSSH2_CRYPT_NONE
|
||||||
/* {{{ libssh2_crypt_none_crypt
|
/* {{{ libssh2_crypt_none_crypt
|
||||||
* Minimalist cipher: VERY secure *wink*
|
* Minimalist cipher: VERY secure *wink*
|
||||||
*/
|
*/
|
||||||
@ -93,6 +93,7 @@ static int init (LIBSSH2_SESSION *session,
|
|||||||
static int crypt(LIBSSH2_SESSION *session, unsigned char *block, void **abstract)
|
static int crypt(LIBSSH2_SESSION *session, unsigned char *block, void **abstract)
|
||||||
{
|
{
|
||||||
struct crypt_ctx *cctx = *(struct crypt_ctx **)abstract;
|
struct crypt_ctx *cctx = *(struct crypt_ctx **)abstract;
|
||||||
|
(void)session;
|
||||||
return _libssh2_cipher_crypt(&cctx->h, cctx->algo,
|
return _libssh2_cipher_crypt(&cctx->h, cctx->algo,
|
||||||
cctx->encrypt, block);
|
cctx->encrypt, block);
|
||||||
}
|
}
|
||||||
@ -234,7 +235,7 @@ static LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = {
|
|||||||
#if LIBSSH2_3DES
|
#if LIBSSH2_3DES
|
||||||
&libssh2_crypt_method_3des_cbc,
|
&libssh2_crypt_method_3des_cbc,
|
||||||
#endif /* LIBSSH2_DES */
|
#endif /* LIBSSH2_DES */
|
||||||
#if LIBSSH2_CRYPT_NONE
|
#ifdef LIBSSH2_CRYPT_NONE
|
||||||
&libssh2_crypt_method_none,
|
&libssh2_crypt_method_none,
|
||||||
#endif
|
#endif
|
||||||
NULL
|
NULL
|
||||||
|
45
src/kex.c
45
src/kex.c
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms,
|
* Redistribution and use in source and binary forms,
|
||||||
@ -79,6 +79,7 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
|||||||
unsigned char *s, *f_value, *k_value = NULL, *h_sig;
|
unsigned char *s, *f_value, *k_value = NULL, *h_sig;
|
||||||
unsigned long f_value_len, k_value_len, h_sig_len;
|
unsigned long f_value_len, k_value_len, h_sig_len;
|
||||||
libssh2_sha1_ctx exchange_hash;
|
libssh2_sha1_ctx exchange_hash;
|
||||||
|
int rc;
|
||||||
|
|
||||||
/* Generate x and e */
|
/* Generate x and e */
|
||||||
_libssh2_bn_rand(x, group_order, 0, -1);
|
_libssh2_bn_rand(x, group_order, 0, -1);
|
||||||
@ -108,7 +109,8 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
|||||||
#ifdef LIBSSH2_DEBUG_KEX
|
#ifdef LIBSSH2_DEBUG_KEX
|
||||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending KEX packet %d", (int)packet_type_init);
|
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending KEX packet %d", (int)packet_type_init);
|
||||||
#endif
|
#endif
|
||||||
if (libssh2_packet_write(session, e_packet, e_packet_len)) {
|
rc = libssh2_packet_write(session, e_packet, e_packet_len);
|
||||||
|
if (rc) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEX init message", 0);
|
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEX init message", 0);
|
||||||
ret = -11;
|
ret = -11;
|
||||||
goto clean_exit;
|
goto clean_exit;
|
||||||
@ -135,8 +137,11 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for KEX reply */
|
/* Wait for KEX reply */
|
||||||
if (libssh2_packet_require(session, packet_type_reply, &s_packet, &s_packet_len)) {
|
rc = libssh2_packet_require(session, packet_type_reply, &s_packet,
|
||||||
libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timed out waiting for KEX reply", 0);
|
&s_packet_len);
|
||||||
|
if (rc) {
|
||||||
|
libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
|
||||||
|
"Timed out waiting for KEX reply", 0);
|
||||||
ret = -1;
|
ret = -1;
|
||||||
goto clean_exit;
|
goto clean_exit;
|
||||||
}
|
}
|
||||||
@ -514,7 +519,7 @@ static int libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_S
|
|||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
||||||
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
|
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
|
||||||
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
||||||
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
|
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
|
||||||
0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
|
0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
|
||||||
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
|
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
|
||||||
@ -813,7 +818,7 @@ static int libssh2_kexinit(LIBSSH2_SESSION *session)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
/* {{{ libssh2_kex_agree_instr
|
/* {{{ libssh2_kex_agree_instr
|
||||||
* Kex specific variant of strstr()
|
* Kex specific variant of strstr()
|
||||||
@ -907,7 +912,9 @@ static int libssh2_kex_agree_hostkey(LIBSSH2_SESSION *session, unsigned long kex
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (hostkeyp && (*hostkeyp)->name) {
|
while (hostkeyp && (*hostkeyp)->name) {
|
||||||
s = libssh2_kex_agree_instr(hostkey, hostkey_len, (*hostkeyp)->name, strlen((*hostkeyp)->name));
|
s = libssh2_kex_agree_instr(hostkey, hostkey_len,
|
||||||
|
(unsigned char *)(*hostkeyp)->name,
|
||||||
|
strlen((*hostkeyp)->name));
|
||||||
if (s) {
|
if (s) {
|
||||||
/* So far so good, but does it suit our purposes? (Encrypting vs Signing) */
|
/* So far so good, but does it suit our purposes? (Encrypting vs Signing) */
|
||||||
if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) == 0) ||
|
if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) == 0) ||
|
||||||
@ -972,7 +979,9 @@ static int libssh2_kex_agree_kex_hostkey(LIBSSH2_SESSION *session, unsigned char
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (*kexp && (*kexp)->name) {
|
while (*kexp && (*kexp)->name) {
|
||||||
s = libssh2_kex_agree_instr(kex, kex_len, (*kexp)->name, strlen((*kexp)->name));
|
s = libssh2_kex_agree_instr(kex, kex_len,
|
||||||
|
(unsigned char *)(*kexp)->name,
|
||||||
|
strlen((*kexp)->name));
|
||||||
if (s) {
|
if (s) {
|
||||||
/* We've agreed on a key exchange method,
|
/* We've agreed on a key exchange method,
|
||||||
* Can we agree on a hostkey that works with this kex?
|
* Can we agree on a hostkey that works with this kex?
|
||||||
@ -1014,7 +1023,7 @@ static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session,
|
|||||||
|
|
||||||
if (libssh2_kex_agree_instr(crypt, crypt_len, s, method_len)) {
|
if (libssh2_kex_agree_instr(crypt, crypt_len, s, method_len)) {
|
||||||
LIBSSH2_CRYPT_METHOD *method =
|
LIBSSH2_CRYPT_METHOD *method =
|
||||||
(LIBSSH2_CRYPT_METHOD*)libssh2_get_method_by_name(s, method_len, (LIBSSH2_COMMON_METHOD**)cryptp);
|
(LIBSSH2_CRYPT_METHOD*)libssh2_get_method_by_name((char *)s, method_len, (LIBSSH2_COMMON_METHOD**)cryptp);
|
||||||
|
|
||||||
if (!method) {
|
if (!method) {
|
||||||
/* Invalid method -- Should never be reached */
|
/* Invalid method -- Should never be reached */
|
||||||
@ -1031,7 +1040,9 @@ static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (*cryptp && (*cryptp)->name) {
|
while (*cryptp && (*cryptp)->name) {
|
||||||
s = libssh2_kex_agree_instr(crypt, crypt_len, (*cryptp)->name, strlen((*cryptp)->name));
|
s = libssh2_kex_agree_instr(crypt, crypt_len,
|
||||||
|
(unsigned char *)(*cryptp)->name,
|
||||||
|
strlen((*cryptp)->name));
|
||||||
if (s) {
|
if (s) {
|
||||||
endpoint->crypt = *cryptp;
|
endpoint->crypt = *cryptp;
|
||||||
return 0;
|
return 0;
|
||||||
@ -1076,7 +1087,9 @@ static int libssh2_kex_agree_mac(LIBSSH2_SESSION *session, libssh2_endpoint_data
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (*macp && (*macp)->name) {
|
while (*macp && (*macp)->name) {
|
||||||
s = libssh2_kex_agree_instr(mac, mac_len, (*macp)->name, strlen((*macp)->name));
|
s = libssh2_kex_agree_instr(mac, mac_len,
|
||||||
|
(unsigned char *)(*macp)->name,
|
||||||
|
strlen((*macp)->name));
|
||||||
if (s) {
|
if (s) {
|
||||||
endpoint->mac = *macp;
|
endpoint->mac = *macp;
|
||||||
return 0;
|
return 0;
|
||||||
@ -1121,7 +1134,9 @@ static int libssh2_kex_agree_comp(LIBSSH2_SESSION *session, libssh2_endpoint_dat
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (*compp && (*compp)->name) {
|
while (*compp && (*compp)->name) {
|
||||||
s = libssh2_kex_agree_instr(comp, comp_len, (*compp)->name, strlen((*compp)->name));
|
s = libssh2_kex_agree_instr(comp, comp_len,
|
||||||
|
(unsigned char *)(*compp)->name,
|
||||||
|
strlen((*compp)->name));
|
||||||
if (s) {
|
if (s) {
|
||||||
endpoint->comp = *compp;
|
endpoint->comp = *compp;
|
||||||
return 0;
|
return 0;
|
||||||
@ -1261,7 +1276,7 @@ int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange) /* session->f
|
|||||||
}
|
}
|
||||||
session->local.kexinit = oldlocal;
|
session->local.kexinit = oldlocal;
|
||||||
session->local.kexinit_len = oldlocal_len;
|
session->local.kexinit_len = oldlocal_len;
|
||||||
return -1;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session->remote.kexinit) {
|
if (session->remote.kexinit) {
|
||||||
@ -1271,13 +1286,13 @@ int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange) /* session->f
|
|||||||
session->remote.kexinit_len = data_len;
|
session->remote.kexinit_len = data_len;
|
||||||
|
|
||||||
if (libssh2_kex_agree_methods(session, data, data_len)) {
|
if (libssh2_kex_agree_methods(session, data, data_len)) {
|
||||||
return -1;
|
return -3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session->kex->exchange_keys(session)) {
|
if (session->kex->exchange_keys(session)) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE, "Unrecoverable error exchanging keys", 0);
|
libssh2_error(session, LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE, "Unrecoverable error exchanging keys", 0);
|
||||||
return -1;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Done with kexinit buffers */
|
/* Done with kexinit buffers */
|
||||||
|
@ -54,6 +54,15 @@
|
|||||||
#include "openssl.h"
|
#include "openssl.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* RFC4253 section 6.1 Maximum Packet Length says:
|
||||||
|
*
|
||||||
|
* "All implementations MUST be able to process packets with
|
||||||
|
* uncompressed payload length of 32768 bytes or less and
|
||||||
|
* total packet size of 35000 bytes or less (including length,
|
||||||
|
* padding length, payload, padding, and MAC.)."
|
||||||
|
*/
|
||||||
|
#define MAX_SSH_PACKET_LEN 35000
|
||||||
|
|
||||||
#define LIBSSH2_ALLOC(session, count) session->alloc((count), &(session)->abstract)
|
#define LIBSSH2_ALLOC(session, count) session->alloc((count), &(session)->abstract)
|
||||||
#define LIBSSH2_REALLOC(session, ptr, count) ((ptr) ? session->realloc((ptr), (count), &(session)->abstract) : session->alloc((count), &(session)->abstract))
|
#define LIBSSH2_REALLOC(session, ptr, count) ((ptr) ? session->realloc((ptr), (count), &(session)->abstract) : session->alloc((count), &(session)->abstract))
|
||||||
#define LIBSSH2_FREE(session, ptr) session->free((ptr), &(session)->abstract)
|
#define LIBSSH2_FREE(session, ptr) session->free((ptr), &(session)->abstract)
|
||||||
@ -173,6 +182,43 @@ typedef struct _libssh2_endpoint_data {
|
|||||||
char *lang_prefs;
|
char *lang_prefs;
|
||||||
} libssh2_endpoint_data;
|
} libssh2_endpoint_data;
|
||||||
|
|
||||||
|
#define PACKETBUFSIZE 4096
|
||||||
|
|
||||||
|
struct transportpacket {
|
||||||
|
/* ------------- for incoming data --------------- */
|
||||||
|
unsigned char buf[PACKETBUFSIZE];
|
||||||
|
unsigned char init[5]; /* first 5 bytes of the incoming data stream,
|
||||||
|
still encrypted */
|
||||||
|
int writeidx; /* at what array index we do the next write into
|
||||||
|
the buffer */
|
||||||
|
int readidx; /* at what array index we do the next read from
|
||||||
|
the buffer */
|
||||||
|
int packet_length; /* the most recent packet_length as read from the
|
||||||
|
network data */
|
||||||
|
int padding_length; /* the most recent padding_length as read from the
|
||||||
|
network data */
|
||||||
|
int data_num; /* How much of the total package that has been read
|
||||||
|
so far. */
|
||||||
|
int total_num; /* How much a total package is supposed to be, in
|
||||||
|
number of bytes. A full package is
|
||||||
|
packet_length + padding_length + 4 +
|
||||||
|
mac_length. */
|
||||||
|
unsigned char *payload; /* this is a pointer to a LIBSSH2_ALLOC()
|
||||||
|
area to which we write decrypted data */
|
||||||
|
unsigned char *wptr; /* write pointer into the payload to where we
|
||||||
|
are currently writing decrypted data */
|
||||||
|
|
||||||
|
/* ------------- for outgoing data --------------- */
|
||||||
|
unsigned char *outbuf; /* pointer to a LIBSSH2_ALLOC() area for the
|
||||||
|
outgoing data */
|
||||||
|
int ototal_num; /* size of outbuf in number of bytes */
|
||||||
|
unsigned char *odata; /* original pointer to the data we stored in
|
||||||
|
outbuf */
|
||||||
|
unsigned long olen; /* original size of the data we stored in
|
||||||
|
outbuf */
|
||||||
|
unsigned long osent; /* number of bytes already sent */
|
||||||
|
};
|
||||||
|
|
||||||
struct _LIBSSH2_SESSION {
|
struct _LIBSSH2_SESSION {
|
||||||
/* Memory management callbacks */
|
/* Memory management callbacks */
|
||||||
void *abstract;
|
void *abstract;
|
||||||
@ -240,6 +286,9 @@ struct _LIBSSH2_SESSION {
|
|||||||
unsigned long err_msglen;
|
unsigned long err_msglen;
|
||||||
int err_should_free;
|
int err_should_free;
|
||||||
int err_code;
|
int err_code;
|
||||||
|
|
||||||
|
/* struct members for packet-level reading */
|
||||||
|
struct transportpacket packet;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* session.state bits */
|
/* session.state bits */
|
||||||
@ -345,7 +394,7 @@ void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, .
|
|||||||
if (session->err_msg && session->err_should_free) { \
|
if (session->err_msg && session->err_should_free) { \
|
||||||
LIBSSH2_FREE(session, session->err_msg); \
|
LIBSSH2_FREE(session, session->err_msg); \
|
||||||
} \
|
} \
|
||||||
session->err_msg = errmsg; \
|
session->err_msg = (char *)errmsg; \
|
||||||
session->err_msglen = strlen(errmsg); \
|
session->err_msglen = strlen(errmsg); \
|
||||||
session->err_should_free = should_free; \
|
session->err_should_free = should_free; \
|
||||||
session->err_code = errcode; \
|
session->err_code = errcode; \
|
||||||
@ -359,7 +408,7 @@ void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, .
|
|||||||
if (session->err_msg && session->err_should_free) { \
|
if (session->err_msg && session->err_should_free) { \
|
||||||
LIBSSH2_FREE(session, session->err_msg); \
|
LIBSSH2_FREE(session, session->err_msg); \
|
||||||
} \
|
} \
|
||||||
session->err_msg = errmsg; \
|
session->err_msg = (char *)errmsg; \
|
||||||
session->err_msglen = strlen(errmsg); \
|
session->err_msglen = strlen(errmsg); \
|
||||||
session->err_should_free = should_free; \
|
session->err_should_free = should_free; \
|
||||||
session->err_code = errcode; \
|
session->err_code = errcode; \
|
||||||
@ -440,7 +489,27 @@ libssh2_uint64_t libssh2_ntohu64(const unsigned char *buf);
|
|||||||
void libssh2_htonu32(unsigned char *buf, unsigned long val);
|
void libssh2_htonu32(unsigned char *buf, unsigned long val);
|
||||||
void libssh2_htonu64(unsigned char *buf, libssh2_uint64_t val);
|
void libssh2_htonu64(unsigned char *buf, libssh2_uint64_t val);
|
||||||
|
|
||||||
int libssh2_packet_read(LIBSSH2_SESSION *session, int block);
|
#define LIBSSH2_READ_TIMEOUT 60 /* generic timeout in seconds used when
|
||||||
|
waiting for more data to arrive */
|
||||||
|
int libssh2_waitsocket(LIBSSH2_SESSION *session, long seconds);
|
||||||
|
|
||||||
|
|
||||||
|
/* CAUTION: some of these error codes are returned in the public API and is
|
||||||
|
there known with other #defined names from the public header file. They
|
||||||
|
should not be changed. */
|
||||||
|
typedef int libssh2pack_t;
|
||||||
|
|
||||||
|
#define PACKET_TIMEOUT -7
|
||||||
|
#define PACKET_BADUSE -6
|
||||||
|
#define PACKET_COMPRESS -5
|
||||||
|
#define PACKET_TOOBIG -4
|
||||||
|
#define PACKET_ENOMEM -3
|
||||||
|
#define PACKET_EAGAIN -2
|
||||||
|
#define PACKET_FAIL -1
|
||||||
|
#define PACKET_NONE 0
|
||||||
|
|
||||||
|
libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session);
|
||||||
|
|
||||||
int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket);
|
int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket);
|
||||||
#define libssh2_packet_ask(session, packet_type, data, data_len, poll_socket) \
|
#define libssh2_packet_ask(session, packet_type, data, data_len, poll_socket) \
|
||||||
libssh2_packet_ask_ex((session), (packet_type), (data), (data_len), 0, NULL, 0, (poll_socket))
|
libssh2_packet_ask_ex((session), (packet_type), (data), (data_len), 0, NULL, 0, (poll_socket))
|
||||||
@ -448,16 +517,32 @@ int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, unsigned char *packet_types
|
|||||||
#define libssh2_packet_askv(session, packet_types, data, data_len, poll_socket) \
|
#define libssh2_packet_askv(session, packet_types, data, data_len, poll_socket) \
|
||||||
libssh2_packet_askv_ex((session), (packet_types), (data), (data_len), 0, NULL, 0, (poll_socket))
|
libssh2_packet_askv_ex((session), (packet_types), (data), (data_len), 0, NULL, 0, (poll_socket))
|
||||||
int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len);
|
int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len);
|
||||||
#define libssh2_packet_require(session, packet_type, data, data_len) \
|
#define libssh2_packet_require(session, packet_type, data, data_len) \
|
||||||
libssh2_packet_require_ex((session), (packet_type), (data), (data_len), 0, NULL, 0)
|
libssh2_packet_require_ex((session), (packet_type), (data), (data_len), 0, NULL, 0)
|
||||||
int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len);
|
int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len);
|
||||||
#define libssh2_packet_requirev(session, packet_types, data, data_len) \
|
#define libssh2_packet_requirev(session, packet_types, data, data_len) \
|
||||||
libssh2_packet_requirev_ex((session), (packet_types), (data), (data_len), 0, NULL, 0)
|
libssh2_packet_requirev_ex((session), (packet_types), (data), (data_len), 0, NULL, 0)
|
||||||
int libssh2_packet_burn(LIBSSH2_SESSION *session);
|
int libssh2_packet_burn(LIBSSH2_SESSION *session);
|
||||||
int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len);
|
int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len);
|
||||||
|
int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, size_t datalen, int macstate);
|
||||||
int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange);
|
int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange);
|
||||||
unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session);
|
unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session);
|
||||||
LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long channel_id);
|
LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long channel_id);
|
||||||
|
ssize_t _libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel,
|
||||||
|
int stream_id, char *buf, size_t buflen);
|
||||||
|
#define _libssh2_channel_read(channel, buf, buflen) \
|
||||||
|
_libssh2_channel_read_ex((channel), 0, (buf), (buflen))
|
||||||
|
#undef libssh2_channel_read /* never use this internally */
|
||||||
|
#define libssh2_channel_read fix this code
|
||||||
|
|
||||||
|
int _libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel,
|
||||||
|
int stream_id,
|
||||||
|
const char *buf, size_t buflen);
|
||||||
|
#define _libssh2_channel_write(channel, buf, buflen) \
|
||||||
|
_libssh2_channel_write_ex((channel), 0, (buf), (buflen))
|
||||||
|
|
||||||
|
/* this is the lib-internal set blocking function */
|
||||||
|
int _libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking);
|
||||||
|
|
||||||
/* Let crypt.c/hostkey.c/comp.c/mac.c expose their method structs */
|
/* Let crypt.c/hostkey.c/comp.c/mac.c expose their method structs */
|
||||||
LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void);
|
LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void);
|
||||||
|
18
src/misc.c
18
src/misc.c
@ -181,15 +181,15 @@ void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, .
|
|||||||
int len;
|
int len;
|
||||||
va_list vargs;
|
va_list vargs;
|
||||||
char *contexts[9] = { "Unknown",
|
char *contexts[9] = { "Unknown",
|
||||||
"Transport",
|
"Transport",
|
||||||
"Key Exhange",
|
"Key Exhange",
|
||||||
"Userauth",
|
"Userauth",
|
||||||
"Connection",
|
"Connection",
|
||||||
"scp",
|
"scp",
|
||||||
"SFTP Subsystem",
|
"SFTP Subsystem",
|
||||||
"Failure Event",
|
"Failure Event",
|
||||||
"Publickey Subsystem",
|
"Publickey Subsystem",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (context < 1 || context > 8) {
|
if (context < 1 || context > 8) {
|
||||||
context = 0;
|
context = 0;
|
||||||
|
1678
src/packet.c
1678
src/packet.c
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms,
|
* Redistribution and use in source and binary forms,
|
||||||
@ -147,7 +147,7 @@ static int libssh2_publickey_packet_receive(LIBSSH2_PUBLICKEY *pkey, unsigned ch
|
|||||||
unsigned long packet_len;
|
unsigned long packet_len;
|
||||||
unsigned char *packet;
|
unsigned char *packet;
|
||||||
|
|
||||||
if (libssh2_channel_read(channel, (char *)buffer, 4) != 4) {
|
if (_libssh2_channel_read(channel, (char *)buffer, 4) != 4) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid response from publickey subsystem", 0);
|
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid response from publickey subsystem", 0);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ static int libssh2_publickey_packet_receive(LIBSSH2_PUBLICKEY *pkey, unsigned ch
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libssh2_channel_read(channel, (char *)packet, packet_len) != packet_len) {
|
if (_libssh2_channel_read(channel, (char *)packet, packet_len) != packet_len) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for publickey subsystem response packet", 0);
|
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for publickey subsystem response packet", 0);
|
||||||
LIBSSH2_FREE(session, packet);
|
LIBSSH2_FREE(session, packet);
|
||||||
return -1;
|
return -1;
|
||||||
|
27
src/scp.c
27
src/scp.c
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms,
|
* Redistribution and use in source and binary forms,
|
||||||
@ -42,9 +42,12 @@
|
|||||||
#define LIBSSH2_SCP_RESPONSE_BUFLEN 256
|
#define LIBSSH2_SCP_RESPONSE_BUFLEN 256
|
||||||
|
|
||||||
/* {{{ libssh2_scp_recv
|
/* {{{ libssh2_scp_recv
|
||||||
|
* [BLOCKING]
|
||||||
* Open a channel and request a remote file via SCP
|
* Open a channel and request a remote file via SCP
|
||||||
*/
|
*/
|
||||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat *sb)
|
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session,
|
||||||
|
const char *path,
|
||||||
|
struct stat *sb)
|
||||||
{
|
{
|
||||||
int path_len = strlen(path);
|
int path_len = strlen(path);
|
||||||
unsigned char *command, response[LIBSSH2_SCP_RESPONSE_BUFLEN];
|
unsigned char *command, response[LIBSSH2_SCP_RESPONSE_BUFLEN];
|
||||||
@ -103,8 +106,10 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
|
|||||||
response_len = 0;
|
response_len = 0;
|
||||||
while (sb && (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN)) {
|
while (sb && (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN)) {
|
||||||
unsigned char *s, *p;
|
unsigned char *s, *p;
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (libssh2_channel_read(channel, response + response_len, 1) <= 0) {
|
rc = _libssh2_channel_read(channel, response + response_len, 1);
|
||||||
|
if(rc <= 0) {
|
||||||
/* Timeout, give up */
|
/* Timeout, give up */
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0);
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0);
|
||||||
libssh2_channel_free(channel);
|
libssh2_channel_free(channel);
|
||||||
@ -135,7 +140,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
|
|||||||
libssh2_channel_free(channel);
|
libssh2_channel_free(channel);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* Way too short to be an SCP response, or not done yet, short circuit */
|
/* Way too short to be an SCP response, or not done yet, short circuit */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +220,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
|
|||||||
while (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) {
|
while (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) {
|
||||||
char *s, *p, *e = NULL;
|
char *s, *p, *e = NULL;
|
||||||
|
|
||||||
if (libssh2_channel_read(channel, response + response_len, 1) <= 0) {
|
if (_libssh2_channel_read(channel, response + response_len, 1) <= 0) {
|
||||||
/* Timeout, give up */
|
/* Timeout, give up */
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0);
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0);
|
||||||
libssh2_channel_free(channel);
|
libssh2_channel_free(channel);
|
||||||
@ -245,7 +250,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
|
|||||||
libssh2_channel_free(channel);
|
libssh2_channel_free(channel);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* Way too short to be an SCP response, or not done yet, short circuit */
|
/* Way too short to be an SCP response, or not done yet, short circuit */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +367,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const
|
|||||||
#endif
|
#endif
|
||||||
/* Allocate a channel */
|
/* Allocate a channel */
|
||||||
if ((channel = libssh2_channel_open_session(session)) == NULL) {
|
if ((channel = libssh2_channel_open_session(session)) == NULL) {
|
||||||
/* previous call set libssh2_session_last_error(), pass it through */
|
/* previous call set libssh2_session_last_error(), pass it through */
|
||||||
LIBSSH2_FREE(session, command);
|
LIBSSH2_FREE(session, command);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -371,7 +376,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const
|
|||||||
|
|
||||||
/* Request SCP for the desired file */
|
/* Request SCP for the desired file */
|
||||||
if (libssh2_channel_process_startup(channel, "exec", sizeof("exec") - 1, command, command_len)) {
|
if (libssh2_channel_process_startup(channel, "exec", sizeof("exec") - 1, command, command_len)) {
|
||||||
/* previous call set libssh2_session_last_error(), pass it through */
|
/* previous call set libssh2_session_last_error(), pass it through */
|
||||||
LIBSSH2_FREE(session, command);
|
LIBSSH2_FREE(session, command);
|
||||||
libssh2_channel_free(channel);
|
libssh2_channel_free(channel);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -379,7 +384,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const
|
|||||||
LIBSSH2_FREE(session, command);
|
LIBSSH2_FREE(session, command);
|
||||||
|
|
||||||
/* Wait for ACK */
|
/* Wait for ACK */
|
||||||
if ((libssh2_channel_read(channel, response, 1) <= 0) || (response[0] != 0)) {
|
if ((_libssh2_channel_read(channel, response, 1) <= 0) || (response[0] != 0)) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
|
||||||
libssh2_channel_free(channel);
|
libssh2_channel_free(channel);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -397,7 +402,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* Wait for ACK */
|
/* Wait for ACK */
|
||||||
if ((libssh2_channel_read(channel, response, 1) <= 0) || (response[0] != 0)) {
|
if ((_libssh2_channel_read(channel, response, 1) <= 0) || (response[0] != 0)) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
|
||||||
libssh2_channel_free(channel);
|
libssh2_channel_free(channel);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -422,7 +427,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* Wait for ACK */
|
/* Wait for ACK */
|
||||||
if ((libssh2_channel_read(channel, response, 1) <= 0) || (response[0] != 0)) {
|
if ((_libssh2_channel_read(channel, response, 1) <= 0) || (response[0] != 0)) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
|
||||||
libssh2_channel_free(channel);
|
libssh2_channel_free(channel);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
119
src/session.c
119
src/session.c
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms,
|
* Redistribution and use in source and binary forms,
|
||||||
@ -66,6 +66,7 @@
|
|||||||
*/
|
*/
|
||||||
static LIBSSH2_ALLOC_FUNC(libssh2_default_alloc)
|
static LIBSSH2_ALLOC_FUNC(libssh2_default_alloc)
|
||||||
{
|
{
|
||||||
|
(void)abstract;
|
||||||
return malloc(count);
|
return malloc(count);
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
@ -74,6 +75,7 @@ static LIBSSH2_ALLOC_FUNC(libssh2_default_alloc)
|
|||||||
*/
|
*/
|
||||||
static LIBSSH2_FREE_FUNC(libssh2_default_free)
|
static LIBSSH2_FREE_FUNC(libssh2_default_free)
|
||||||
{
|
{
|
||||||
|
(void)abstract;
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
@ -82,6 +84,7 @@ static LIBSSH2_FREE_FUNC(libssh2_default_free)
|
|||||||
*/
|
*/
|
||||||
static LIBSSH2_REALLOC_FUNC(libssh2_default_realloc)
|
static LIBSSH2_REALLOC_FUNC(libssh2_default_realloc)
|
||||||
{
|
{
|
||||||
|
(void)abstract;
|
||||||
return realloc(ptr, count);
|
return realloc(ptr, count);
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
@ -96,8 +99,8 @@ static int libssh2_banner_receive(LIBSSH2_SESSION *session)
|
|||||||
char banner[256];
|
char banner[256];
|
||||||
int banner_len = 0;
|
int banner_len = 0;
|
||||||
|
|
||||||
while ((banner_len < sizeof(banner)) &&
|
while ((banner_len < (int)sizeof(banner)) &&
|
||||||
((banner_len == 0) || (banner[banner_len-1] != '\n'))) {
|
((banner_len == 0) || (banner[banner_len-1] != '\n'))) {
|
||||||
char c = '\0';
|
char c = '\0';
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -159,13 +162,13 @@ static int libssh2_banner_receive(LIBSSH2_SESSION *session)
|
|||||||
*/
|
*/
|
||||||
static int libssh2_banner_send(LIBSSH2_SESSION *session)
|
static int libssh2_banner_send(LIBSSH2_SESSION *session)
|
||||||
{
|
{
|
||||||
char *banner = LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF;
|
char *banner = (char *)LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF;
|
||||||
int banner_len = sizeof(LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF) - 1;
|
int banner_len = sizeof(LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF) - 1;
|
||||||
|
|
||||||
if (session->local.banner) {
|
if (session->local.banner) {
|
||||||
/* setopt_string will have given us our \r\n characters */
|
/* setopt_string will have given us our \r\n characters */
|
||||||
banner_len = strlen(session->local.banner);
|
banner_len = strlen((char *)session->local.banner);
|
||||||
banner = session->local.banner;
|
banner = (char *)session->local.banner;
|
||||||
}
|
}
|
||||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||||
{
|
{
|
||||||
@ -206,14 +209,16 @@ LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, const char *banner)
|
|||||||
|
|
||||||
session->local.banner = LIBSSH2_ALLOC(session, banner_len + 3);
|
session->local.banner = LIBSSH2_ALLOC(session, banner_len + 3);
|
||||||
if (!session->local.banner) {
|
if (!session->local.banner) {
|
||||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for local banner", 0);
|
libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
||||||
|
"Unable to allocate memory for local banner", 0);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(session->local.banner, banner, banner_len);
|
memcpy(session->local.banner, banner, banner_len);
|
||||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||||
session->local.banner[banner_len] = '\0';
|
session->local.banner[banner_len] = '\0';
|
||||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting local Banner: %s", session->local.banner);
|
_libssh2_debug(session, LIBSSH2_DBG_TRANS,
|
||||||
|
"Setting local Banner: %s", session->local.banner);
|
||||||
#endif
|
#endif
|
||||||
session->local.banner[banner_len++] = '\r';
|
session->local.banner[banner_len++] = '\r';
|
||||||
session->local.banner[banner_len++] = '\n';
|
session->local.banner[banner_len++] = '\n';
|
||||||
@ -235,14 +240,17 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(
|
|||||||
LIBSSH2_REALLOC_FUNC((*my_realloc)),
|
LIBSSH2_REALLOC_FUNC((*my_realloc)),
|
||||||
void *abstract)
|
void *abstract)
|
||||||
{
|
{
|
||||||
LIBSSH2_ALLOC_FUNC((*local_alloc)) = libssh2_default_alloc;
|
LIBSSH2_ALLOC_FUNC((*local_alloc)) = libssh2_default_alloc;
|
||||||
LIBSSH2_FREE_FUNC((*local_free)) = libssh2_default_free;
|
LIBSSH2_FREE_FUNC((*local_free)) = libssh2_default_free;
|
||||||
LIBSSH2_REALLOC_FUNC((*local_realloc)) = libssh2_default_realloc;
|
LIBSSH2_REALLOC_FUNC((*local_realloc)) = libssh2_default_realloc;
|
||||||
LIBSSH2_SESSION *session;
|
LIBSSH2_SESSION *session;
|
||||||
|
|
||||||
if (my_alloc) local_alloc = my_alloc;
|
if (my_alloc)
|
||||||
if (my_free) local_free = my_free;
|
local_alloc = my_alloc;
|
||||||
if (my_realloc) local_realloc = my_realloc;
|
if (my_free)
|
||||||
|
local_free = my_free;
|
||||||
|
if (my_realloc)
|
||||||
|
local_realloc = my_realloc;
|
||||||
|
|
||||||
session = local_alloc(sizeof(LIBSSH2_SESSION), abstract);
|
session = local_alloc(sizeof(LIBSSH2_SESSION), abstract);
|
||||||
memset(session, 0, sizeof(LIBSSH2_SESSION));
|
memset(session, 0, sizeof(LIBSSH2_SESSION));
|
||||||
@ -251,7 +259,8 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(
|
|||||||
session->realloc = local_realloc;
|
session->realloc = local_realloc;
|
||||||
session->abstract = abstract;
|
session->abstract = abstract;
|
||||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "New session resource allocated");
|
_libssh2_debug(session, LIBSSH2_DBG_TRANS,
|
||||||
|
"New session resource allocated");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
libssh2_crypto_init ();
|
libssh2_crypto_init ();
|
||||||
@ -264,7 +273,9 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(
|
|||||||
* Set (or reset) a callback function
|
* Set (or reset) a callback function
|
||||||
* Returns the prior address
|
* Returns the prior address
|
||||||
*/
|
*/
|
||||||
LIBSSH2_API void* libssh2_session_callback_set(LIBSSH2_SESSION *session, int cbtype, void *callback)
|
LIBSSH2_API void* libssh2_session_callback_set(LIBSSH2_SESSION *session,
|
||||||
|
int cbtype,
|
||||||
|
void *callback)
|
||||||
{
|
{
|
||||||
void *oldcb;
|
void *oldcb;
|
||||||
|
|
||||||
@ -273,27 +284,27 @@ LIBSSH2_API void* libssh2_session_callback_set(LIBSSH2_SESSION *session, int cbt
|
|||||||
oldcb = session->ssh_msg_ignore;
|
oldcb = session->ssh_msg_ignore;
|
||||||
session->ssh_msg_ignore = callback;
|
session->ssh_msg_ignore = callback;
|
||||||
return oldcb;
|
return oldcb;
|
||||||
break;
|
|
||||||
case LIBSSH2_CALLBACK_DEBUG:
|
case LIBSSH2_CALLBACK_DEBUG:
|
||||||
oldcb = session->ssh_msg_debug;
|
oldcb = session->ssh_msg_debug;
|
||||||
session->ssh_msg_debug = callback;
|
session->ssh_msg_debug = callback;
|
||||||
return oldcb;
|
return oldcb;
|
||||||
break;
|
|
||||||
case LIBSSH2_CALLBACK_DISCONNECT:
|
case LIBSSH2_CALLBACK_DISCONNECT:
|
||||||
oldcb = session->ssh_msg_disconnect;
|
oldcb = session->ssh_msg_disconnect;
|
||||||
session->ssh_msg_disconnect = callback;
|
session->ssh_msg_disconnect = callback;
|
||||||
return oldcb;
|
return oldcb;
|
||||||
break;
|
|
||||||
case LIBSSH2_CALLBACK_MACERROR:
|
case LIBSSH2_CALLBACK_MACERROR:
|
||||||
oldcb = session->macerror;
|
oldcb = session->macerror;
|
||||||
session->macerror = callback;
|
session->macerror = callback;
|
||||||
return oldcb;
|
return oldcb;
|
||||||
break;
|
|
||||||
case LIBSSH2_CALLBACK_X11:
|
case LIBSSH2_CALLBACK_X11:
|
||||||
oldcb = session->x11;
|
oldcb = session->x11;
|
||||||
session->x11 = callback;
|
session->x11 = callback;
|
||||||
return oldcb;
|
return oldcb;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting Callback %d", cbtype);
|
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting Callback %d", cbtype);
|
||||||
@ -306,8 +317,9 @@ LIBSSH2_API void* libssh2_session_callback_set(LIBSSH2_SESSION *session, int cbt
|
|||||||
/* {{{ proto libssh2_session_startup
|
/* {{{ proto libssh2_session_startup
|
||||||
* session: LIBSSH2_SESSION struct allocated and owned by the calling program
|
* session: LIBSSH2_SESSION struct allocated and owned by the calling program
|
||||||
* Returns: 0 on success, or non-zero on failure
|
* Returns: 0 on success, or non-zero on failure
|
||||||
* Any memory allocated by libssh2 will use alloc/realloc/free callbacks in session
|
* Any memory allocated by libssh2 will use alloc/realloc/free
|
||||||
* socket *must* be populated with an opened socket
|
* callbacks in session
|
||||||
|
* socket *must* be populated with an opened and connected socket.
|
||||||
*/
|
*/
|
||||||
LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
|
LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
|
||||||
{
|
{
|
||||||
@ -315,13 +327,17 @@ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
|
|||||||
unsigned long data_len;
|
unsigned long data_len;
|
||||||
unsigned char service[sizeof("ssh-userauth") + 5 - 1];
|
unsigned char service[sizeof("ssh-userauth") + 5 - 1];
|
||||||
unsigned long service_length;
|
unsigned long service_length;
|
||||||
|
int rc;
|
||||||
|
|
||||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "session_startup for socket %d", socket);
|
_libssh2_debug(session, LIBSSH2_DBG_TRANS,
|
||||||
|
"session_startup for socket %d", socket);
|
||||||
#endif
|
#endif
|
||||||
|
/* FIXME: on some platforms (like win32) sockets are unsigned */
|
||||||
if (socket < 0) {
|
if (socket < 0) {
|
||||||
/* Did we forget something? */
|
/* Did we forget something? */
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_NONE, "Bad socket provided", 0);
|
libssh2_error(session, LIBSSH2_ERROR_SOCKET_NONE,
|
||||||
|
"Bad socket provided", 0);
|
||||||
return LIBSSH2_ERROR_SOCKET_NONE;
|
return LIBSSH2_ERROR_SOCKET_NONE;
|
||||||
}
|
}
|
||||||
session->socket_fd = socket;
|
session->socket_fd = socket;
|
||||||
@ -329,42 +345,52 @@ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
|
|||||||
/* TODO: Liveness check */
|
/* TODO: Liveness check */
|
||||||
if (libssh2_banner_send(session)) {
|
if (libssh2_banner_send(session)) {
|
||||||
/* Unable to send banner? */
|
/* Unable to send banner? */
|
||||||
libssh2_error(session, LIBSSH2_ERROR_BANNER_SEND, "Error sending banner to remote host", 0);
|
libssh2_error(session, LIBSSH2_ERROR_BANNER_SEND,
|
||||||
|
"Error sending banner to remote host", 0);
|
||||||
return LIBSSH2_ERROR_BANNER_SEND;
|
return LIBSSH2_ERROR_BANNER_SEND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libssh2_banner_receive(session)) {
|
if (libssh2_banner_receive(session)) {
|
||||||
/* Unable to receive banner from remote */
|
/* Unable to receive banner from remote */
|
||||||
libssh2_error(session, LIBSSH2_ERROR_BANNER_NONE, "Timeout waiting for banner", 0);
|
libssh2_error(session, LIBSSH2_ERROR_BANNER_NONE,
|
||||||
|
"Timeout waiting for banner", 0);
|
||||||
return LIBSSH2_ERROR_BANNER_NONE;
|
return LIBSSH2_ERROR_BANNER_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libssh2_kex_exchange(session, 0)) {
|
rc = libssh2_kex_exchange(session, 0);
|
||||||
libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE, "Unable to exchange encryption keys", 0);
|
if(rc) {
|
||||||
|
libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE,
|
||||||
|
"Unable to exchange encryption keys", 0);
|
||||||
return LIBSSH2_ERROR_KEX_FAILURE;
|
return LIBSSH2_ERROR_KEX_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Requesting userauth service");
|
_libssh2_debug(session, LIBSSH2_DBG_TRANS,
|
||||||
|
"Requesting userauth service");
|
||||||
#endif
|
#endif
|
||||||
/* Request the userauth service */
|
/* Request the userauth service */
|
||||||
service[0] = SSH_MSG_SERVICE_REQUEST;
|
service[0] = SSH_MSG_SERVICE_REQUEST;
|
||||||
libssh2_htonu32(service + 1, sizeof("ssh-userauth") - 1);
|
libssh2_htonu32(service + 1, sizeof("ssh-userauth") - 1);
|
||||||
memcpy(service + 5, "ssh-userauth", sizeof("ssh-userauth") - 1);
|
memcpy(service + 5, "ssh-userauth", sizeof("ssh-userauth") - 1);
|
||||||
if (libssh2_packet_write(session, service, sizeof("ssh-userauth") + 5 - 1)) {
|
if (libssh2_packet_write(session, service,
|
||||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to ask for ssh-userauth service", 0);
|
sizeof("ssh-userauth") + 5 - 1)) {
|
||||||
|
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
||||||
|
"Unable to ask for ssh-userauth service", 0);
|
||||||
return LIBSSH2_ERROR_SOCKET_SEND;
|
return LIBSSH2_ERROR_SOCKET_SEND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libssh2_packet_require(session, SSH_MSG_SERVICE_ACCEPT, &data, &data_len)) {
|
rc = libssh2_packet_require(session, SSH_MSG_SERVICE_ACCEPT, &data,
|
||||||
|
&data_len);
|
||||||
|
if(rc) {
|
||||||
return LIBSSH2_ERROR_SOCKET_DISCONNECT;
|
return LIBSSH2_ERROR_SOCKET_DISCONNECT;
|
||||||
}
|
}
|
||||||
service_length = libssh2_ntohu32(data + 1);
|
service_length = libssh2_ntohu32(data + 1);
|
||||||
|
|
||||||
if ((service_length != (sizeof("ssh-userauth") - 1)) ||
|
if ((service_length != (sizeof("ssh-userauth") - 1)) ||
|
||||||
strncmp("ssh-userauth", data + 5, service_length)) {
|
strncmp("ssh-userauth", (char *)data + 5, service_length)) {
|
||||||
LIBSSH2_FREE(session, data);
|
LIBSSH2_FREE(session, data);
|
||||||
libssh2_error(session, LIBSSH2_ERROR_PROTO, "Invalid response received from server", 0);
|
libssh2_error(session, LIBSSH2_ERROR_PROTO,
|
||||||
|
"Invalid response received from server", 0);
|
||||||
return LIBSSH2_ERROR_PROTO;
|
return LIBSSH2_ERROR_PROTO;
|
||||||
}
|
}
|
||||||
LIBSSH2_FREE(session, data);
|
LIBSSH2_FREE(session, data);
|
||||||
@ -619,7 +645,9 @@ LIBSSH2_API void **libssh2_session_abstract(LIBSSH2_SESSION *session)
|
|||||||
* If want_buf is non-zero then the string placed into errmsg must be freed by the calling program
|
* If want_buf is non-zero then the string placed into errmsg must be freed by the calling program
|
||||||
* Otherwise it is assumed to be owned by libssh2
|
* Otherwise it is assumed to be owned by libssh2
|
||||||
*/
|
*/
|
||||||
LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg, int *errmsg_len, int want_buf)
|
LIBSSH2_API int
|
||||||
|
libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg,
|
||||||
|
int *errmsg_len, int want_buf)
|
||||||
{
|
{
|
||||||
/* No error to report */
|
/* No error to report */
|
||||||
if (!session->err_code) {
|
if (!session->err_code) {
|
||||||
@ -630,7 +658,7 @@ LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errm
|
|||||||
**errmsg = 0;
|
**errmsg = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*errmsg = "";
|
*errmsg = (char *)"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (errmsg_len) {
|
if (errmsg_len) {
|
||||||
@ -640,7 +668,8 @@ LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errm
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errmsg) {
|
if (errmsg) {
|
||||||
char *serrmsg = session->err_msg ? session->err_msg : "";
|
char *serrmsg = session->err_msg ? session->err_msg :
|
||||||
|
(char *)"";
|
||||||
int ownbuf = session->err_msg ? session->err_should_free : 0;
|
int ownbuf = session->err_msg ? session->err_should_free : 0;
|
||||||
|
|
||||||
if (want_buf) {
|
if (want_buf) {
|
||||||
@ -707,6 +736,9 @@ LIBSSH2_API int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended
|
|||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
inline int libssh2_poll_channel_write(LIBSSH2_CHANNEL *channel);
|
||||||
|
inline int libssh2_poll_listener_queued(LIBSSH2_LISTENER *listener);
|
||||||
|
|
||||||
/* {{{ libssh2_poll_channel_write
|
/* {{{ libssh2_poll_channel_write
|
||||||
* Returns 0 if writing to channel would block,
|
* Returns 0 if writing to channel would block,
|
||||||
* non-0 if data can be written without blocking
|
* non-0 if data can be written without blocking
|
||||||
@ -730,13 +762,18 @@ inline int libssh2_poll_listener_queued(LIBSSH2_LISTENER *listener)
|
|||||||
/* {{{ libssh2_poll
|
/* {{{ libssh2_poll
|
||||||
* Poll sockets, channels, and listeners for activity
|
* Poll sockets, channels, and listeners for activity
|
||||||
*/
|
*/
|
||||||
LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeout)
|
LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds,
|
||||||
|
long timeout)
|
||||||
{
|
{
|
||||||
long timeout_remaining;
|
long timeout_remaining;
|
||||||
int i, active_fds;
|
unsigned int i, active_fds;
|
||||||
#ifdef HAVE_POLL
|
#ifdef HAVE_POLL
|
||||||
LIBSSH2_SESSION *session = NULL;
|
LIBSSH2_SESSION *session = NULL;
|
||||||
struct pollfd sockets[nfds];
|
struct pollfd sockets[nfds];
|
||||||
|
/* FIXME: (dast) this is not C89 code! However, the prototype for this
|
||||||
|
function doesn't provide a session struct so we can't easily use
|
||||||
|
the user-provided malloc replacement here... I suggest we modify
|
||||||
|
the proto to make it possible. */
|
||||||
|
|
||||||
/* Setup sockets for polling */
|
/* Setup sockets for polling */
|
||||||
for(i = 0; i < nfds; i++) {
|
for(i = 0; i < nfds; i++) {
|
||||||
@ -893,7 +930,7 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeou
|
|||||||
case LIBSSH2_POLLFD_CHANNEL:
|
case LIBSSH2_POLLFD_CHANNEL:
|
||||||
if (sockets[i].events & POLLIN) {
|
if (sockets[i].events & POLLIN) {
|
||||||
/* Spin session until no data available */
|
/* Spin session until no data available */
|
||||||
while (libssh2_packet_read(fds[i].fd.channel->session, 0) > 0);
|
while (libssh2_packet_read(fds[i].fd.channel->session) > 0);
|
||||||
}
|
}
|
||||||
if (sockets[i].revents & POLLHUP) {
|
if (sockets[i].revents & POLLHUP) {
|
||||||
fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
|
fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
|
||||||
@ -903,7 +940,7 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeou
|
|||||||
case LIBSSH2_POLLFD_LISTENER:
|
case LIBSSH2_POLLFD_LISTENER:
|
||||||
if (sockets[i].events & POLLIN) {
|
if (sockets[i].events & POLLIN) {
|
||||||
/* Spin session until no data available */
|
/* Spin session until no data available */
|
||||||
while (libssh2_packet_read(fds[i].fd.listener->session, 0) > 0);
|
while (libssh2_packet_read(fds[i].fd.listener->session) > 0);
|
||||||
}
|
}
|
||||||
if (sockets[i].revents & POLLHUP) {
|
if (sockets[i].revents & POLLHUP) {
|
||||||
fds[i].revents |= LIBSSH2_POLLFD_LISTENER_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
|
fds[i].revents |= LIBSSH2_POLLFD_LISTENER_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
|
||||||
|
2143
src/sftp.c
2143
src/sftp.c
File diff suppressed because it is too large
Load Diff
706
src/transport.c
Normal file
706
src/transport.c
Normal file
@ -0,0 +1,706 @@
|
|||||||
|
/* Copyright (C) 2007 The Written Word, Inc. All rights reserved.
|
||||||
|
* Author: Daniel Stenberg <daniel@haxx.se>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms,
|
||||||
|
* with or without modification, are permitted provided
|
||||||
|
* that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above
|
||||||
|
* copyright notice, this list of conditions and the
|
||||||
|
* following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials
|
||||||
|
* provided with the distribution.
|
||||||
|
*
|
||||||
|
* Neither the name of the copyright holder nor the names
|
||||||
|
* of any other contributors may be used to endorse or
|
||||||
|
* promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||||
|
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||||
|
* OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This file handles reading and writing to the SECSH transport layer. RFC4253.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libssh2_priv.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define MAX_BLOCKSIZE 32 /* MUST fit biggest crypto block size we use/get */
|
||||||
|
#define MAX_MACSIZE 20 /* MUST fit biggest MAC length we support */
|
||||||
|
|
||||||
|
#ifdef LIBSSH2DEBUG
|
||||||
|
#define UNPRINTABLE_CHAR '.'
|
||||||
|
static void debugdump(const char *desc, unsigned char *ptr, unsigned long size)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
size_t c;
|
||||||
|
FILE *stream = stdout;
|
||||||
|
|
||||||
|
unsigned int width=0x10;
|
||||||
|
|
||||||
|
fprintf(stream, "=> %s (%d bytes)\n", desc, (int)size);
|
||||||
|
|
||||||
|
for(i=0; i<size; i+= width) {
|
||||||
|
|
||||||
|
fprintf(stream, "%04x: ", i);
|
||||||
|
|
||||||
|
/* hex not disabled, show it */
|
||||||
|
for(c = 0; c < width; c++) {
|
||||||
|
if(i+c < size)
|
||||||
|
fprintf(stream, "%02x ", ptr[i+c]);
|
||||||
|
else
|
||||||
|
fputs(" ", stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(c = 0; (c < width) && (i+c < size); c++) {
|
||||||
|
fprintf(stream, "%c",
|
||||||
|
(ptr[i+c]>=0x20) &&
|
||||||
|
(ptr[i+c]<0x80)?ptr[i+c]:UNPRINTABLE_CHAR);
|
||||||
|
}
|
||||||
|
fputc('\n', stream); /* newline */
|
||||||
|
}
|
||||||
|
fflush(stream);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define debugdump(x,y,z)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* decrypt() decrypts 'len' bytes from 'source' to 'dest'.
|
||||||
|
*
|
||||||
|
* returns PACKET_NONE on success and PACKET_FAIL on failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
static libssh2pack_t decrypt(LIBSSH2_SESSION *session, unsigned char *source,
|
||||||
|
unsigned char *dest, int len)
|
||||||
|
{
|
||||||
|
struct transportpacket *p = &session->packet;
|
||||||
|
int blocksize = session->remote.crypt->blocksize;
|
||||||
|
while(len >= blocksize) {
|
||||||
|
if (session->remote.crypt->crypt(session, source,
|
||||||
|
&session->remote.crypt_abstract)) {
|
||||||
|
libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
|
||||||
|
(char *)"Error decrypting packet", 0);
|
||||||
|
LIBSSH2_FREE(session, p->payload);
|
||||||
|
return PACKET_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if the crypt() function would write to a given address it
|
||||||
|
wouldn't have to memcpy() and we could avoid this memcpy()
|
||||||
|
too */
|
||||||
|
memcpy(dest, source, blocksize);
|
||||||
|
|
||||||
|
len -= blocksize; /* less bytes left */
|
||||||
|
dest += blocksize; /* advance write pointer */
|
||||||
|
source += blocksize; /* advance read pointer */
|
||||||
|
}
|
||||||
|
return PACKET_NONE; /* all is fine */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fullpacket() gets called when a full packet has been received and properly
|
||||||
|
* collected.
|
||||||
|
*/
|
||||||
|
static libssh2pack_t fullpacket(LIBSSH2_SESSION *session,
|
||||||
|
int encrypted /* 1 or 0 */)
|
||||||
|
{
|
||||||
|
unsigned char macbuf[MAX_MACSIZE];
|
||||||
|
struct transportpacket *p = &session->packet;
|
||||||
|
int payload_len = p->packet_length-1;
|
||||||
|
libssh2pack_t packet_type;
|
||||||
|
int macstate = LIBSSH2_MAC_CONFIRMED;
|
||||||
|
|
||||||
|
if(encrypted) {
|
||||||
|
|
||||||
|
/* Calculate MAC hash */
|
||||||
|
session->remote.mac->hash(session,
|
||||||
|
macbuf, /* store hash here */
|
||||||
|
session->remote.seqno,
|
||||||
|
p->init, 5,
|
||||||
|
p->payload, payload_len,
|
||||||
|
&session->remote.mac_abstract);
|
||||||
|
|
||||||
|
/* Compare the calculated hash with the MAC we just read from
|
||||||
|
* the network. The read one is at the very end of the payload
|
||||||
|
* buffer. Note that 'payload_len' here is the packet_length
|
||||||
|
* field which includes the padding but not the MAC.
|
||||||
|
*/
|
||||||
|
if(memcmp(macbuf, p->payload + payload_len,
|
||||||
|
session->remote.mac->mac_len)) {
|
||||||
|
macstate = LIBSSH2_MAC_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session->remote.seqno++;
|
||||||
|
|
||||||
|
/* ignore the padding */
|
||||||
|
payload_len -= p->padding_length;
|
||||||
|
|
||||||
|
/* Check for and deal with decompression */
|
||||||
|
if (session->remote.comp &&
|
||||||
|
strcmp(session->remote.comp->name, "none")) {
|
||||||
|
unsigned char *data;
|
||||||
|
unsigned long data_len;
|
||||||
|
int free_payload = 1;
|
||||||
|
|
||||||
|
if (session->remote.comp->comp(session, 0,
|
||||||
|
&data, &data_len,
|
||||||
|
LIBSSH2_PACKET_MAXDECOMP,
|
||||||
|
&free_payload,
|
||||||
|
p->payload, payload_len,
|
||||||
|
&session->remote.comp_abstract)) {
|
||||||
|
LIBSSH2_FREE(session, p->payload);
|
||||||
|
return PACKET_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (free_payload) {
|
||||||
|
LIBSSH2_FREE(session, p->payload);
|
||||||
|
p->payload = data;
|
||||||
|
payload_len = data_len;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (data == p->payload) {
|
||||||
|
/* It's not to be freed, because the
|
||||||
|
* compression layer reused payload, So let's
|
||||||
|
* do the same!
|
||||||
|
*/
|
||||||
|
payload_len = data_len;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* No comp_method actually lets this happen,
|
||||||
|
* but let's prepare for the future */
|
||||||
|
|
||||||
|
LIBSSH2_FREE(session, p->payload);
|
||||||
|
|
||||||
|
/* We need a freeable struct otherwise the
|
||||||
|
* brigade won't know what to do with it */
|
||||||
|
p->payload = LIBSSH2_ALLOC(session, data_len);
|
||||||
|
if (!p->payload) {
|
||||||
|
libssh2_error(session,
|
||||||
|
LIBSSH2_ERROR_ALLOC,
|
||||||
|
(char *)"Unable to allocate memory for copy of uncompressed data", 0);
|
||||||
|
return PACKET_ENOMEM;
|
||||||
|
}
|
||||||
|
memcpy(p->payload, data, data_len);
|
||||||
|
payload_len = data_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packet_type = p->payload[0];
|
||||||
|
|
||||||
|
debugdump("libssh2_packet_read() plain",
|
||||||
|
p->payload, payload_len);
|
||||||
|
libssh2_packet_add(session, p->payload, payload_len, macstate);
|
||||||
|
|
||||||
|
return packet_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* {{{ libssh2_packet_read
|
||||||
|
* Collect a packet into the input brigade
|
||||||
|
* block only controls whether or not to wait for a packet to start,
|
||||||
|
* Once a packet starts, libssh2 will block until it is complete
|
||||||
|
|
||||||
|
* Returns packet type added to input brigade (PACKET_NONE if nothing added),
|
||||||
|
* or PACKET_FAIL on failure and PACKET_EAGAIN if it couldn't process a full
|
||||||
|
* packet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function reads the binary stream as specified in chapter 6 of RFC4253
|
||||||
|
* "The Secure Shell (SSH) Transport Layer Protocol"
|
||||||
|
*/
|
||||||
|
|
||||||
|
libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session)
|
||||||
|
{
|
||||||
|
libssh2pack_t rc;
|
||||||
|
struct transportpacket *p = &session->packet;
|
||||||
|
int remainbuf;
|
||||||
|
int remainpack;
|
||||||
|
int numbytes;
|
||||||
|
int numdecrypt;
|
||||||
|
unsigned char block[MAX_BLOCKSIZE];
|
||||||
|
int blocksize;
|
||||||
|
int encrypted = 1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
|
||||||
|
return PACKET_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session->state & LIBSSH2_STATE_NEWKEYS) {
|
||||||
|
blocksize = session->remote.crypt->blocksize;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
encrypted = 0; /* not encrypted */
|
||||||
|
blocksize = 5; /* not strictly true, but we can use 5
|
||||||
|
here to make the checks below work
|
||||||
|
fine still */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read/use a whole big chunk into a temporary area stored in
|
||||||
|
the LIBSSH2_SESSION struct. We will decrypt data from that
|
||||||
|
buffer into the packet buffer so this temp one doesn't have
|
||||||
|
to be able to keep a whole SSH packet, just be large enough
|
||||||
|
so that we can read big chunks from the network layer. */
|
||||||
|
|
||||||
|
/* how much data there is remaining in the buffer to deal with
|
||||||
|
before we should read more from the network */
|
||||||
|
remainbuf = p->writeidx - p->readidx;
|
||||||
|
|
||||||
|
/* if remainbuf turns negative we have a bad internal error */
|
||||||
|
assert(remainbuf >= 0);
|
||||||
|
|
||||||
|
if(remainbuf < blocksize) {
|
||||||
|
/* If we have less than a blocksize left, it is too
|
||||||
|
little data to deal with, read more */
|
||||||
|
ssize_t nread;
|
||||||
|
|
||||||
|
/* move any remainder to the start of the buffer so
|
||||||
|
that we can do a full refill */
|
||||||
|
if(remainbuf) {
|
||||||
|
memmove(p->buf, &p->buf[p->readidx],
|
||||||
|
remainbuf);
|
||||||
|
p->readidx = 0;
|
||||||
|
p->writeidx = remainbuf;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* nothing to move, just zero the indexes */
|
||||||
|
p->readidx = p->writeidx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now read a big chunk from the network into the temp
|
||||||
|
buffer */
|
||||||
|
nread = recv(session->socket_fd, &p->buf[remainbuf],
|
||||||
|
PACKETBUFSIZE-remainbuf,
|
||||||
|
LIBSSH2_SOCKET_RECV_FLAGS(session));
|
||||||
|
if (nread <= 0) {
|
||||||
|
/* check if this is due to EAGAIN and return
|
||||||
|
the special return code if so, error out
|
||||||
|
normally otherwise */
|
||||||
|
if(errno == EAGAIN) {
|
||||||
|
return PACKET_EAGAIN;
|
||||||
|
}
|
||||||
|
return PACKET_FAIL;
|
||||||
|
}
|
||||||
|
debugdump("libssh2_packet_read() raw",
|
||||||
|
&p->buf[remainbuf], nread);
|
||||||
|
/* advance write pointer */
|
||||||
|
p->writeidx += nread;
|
||||||
|
|
||||||
|
/* update remainbuf counter */
|
||||||
|
remainbuf = p->writeidx - p->readidx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* how much data to deal with from the buffer */
|
||||||
|
numbytes = remainbuf;
|
||||||
|
|
||||||
|
if(numbytes < blocksize) {
|
||||||
|
/* we can't act on anything less than blocksize */
|
||||||
|
return PACKET_EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!p->total_num) {
|
||||||
|
/* No payload package area allocated yet. To know the
|
||||||
|
size of this payload, we need to decrypt the first
|
||||||
|
blocksize data. */
|
||||||
|
|
||||||
|
if(encrypted) {
|
||||||
|
rc = decrypt(session, &p->buf[p->readidx],
|
||||||
|
block, blocksize);
|
||||||
|
if(rc != PACKET_NONE) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
/* save the first 5 bytes of the decrypted
|
||||||
|
package, to be used in the hash calculation
|
||||||
|
later down. */
|
||||||
|
memcpy(p->init, &p->buf[p->readidx], 5);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* the data is plain, just copy it verbatim to
|
||||||
|
the working block buffer */
|
||||||
|
memcpy(block, &p->buf[p->readidx], blocksize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* advance the read pointer */
|
||||||
|
p->readidx += blocksize;
|
||||||
|
|
||||||
|
/* we now have the initial blocksize bytes decrypted,
|
||||||
|
and we can extract packet and padding length from it
|
||||||
|
*/
|
||||||
|
p->packet_length = libssh2_ntohu32(block);
|
||||||
|
p->padding_length = block[4];
|
||||||
|
|
||||||
|
/* total_num is the number of bytes following the
|
||||||
|
initial (5 bytes) packet length and padding length
|
||||||
|
fields */
|
||||||
|
p->total_num = p->packet_length -1 +
|
||||||
|
(encrypted?session->remote.mac->mac_len:0);
|
||||||
|
|
||||||
|
/* RFC4253 section 6.1 Maximum Packet Length says:
|
||||||
|
*
|
||||||
|
* "All implementations MUST be able to process
|
||||||
|
* packets with uncompressed payload length of 32768
|
||||||
|
* bytes or less and total packet size of 35000 bytes
|
||||||
|
* or less (including length, padding length, payload,
|
||||||
|
* padding, and MAC.)."
|
||||||
|
*/
|
||||||
|
if(p->total_num > LIBSSH2_PACKET_MAXPAYLOAD) {
|
||||||
|
return PACKET_TOOBIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a packet handle put data into. We get one to
|
||||||
|
hold all data, including padding and MAC. */
|
||||||
|
p->payload = LIBSSH2_ALLOC(session, p->total_num);
|
||||||
|
if(!p->payload) {
|
||||||
|
return PACKET_ENOMEM;
|
||||||
|
}
|
||||||
|
/* init write pointer to start of payload buffer */
|
||||||
|
p->wptr = p->payload;
|
||||||
|
|
||||||
|
if(blocksize > 5) {
|
||||||
|
/* copy the data from index 5 to the end of
|
||||||
|
the blocksize from the temporary buffer to
|
||||||
|
the start of the decrypted buffer */
|
||||||
|
memcpy(p->wptr, &block[5], blocksize-5);
|
||||||
|
p->wptr += blocksize-5; /* advance write
|
||||||
|
pointer */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* init the data_num field to the number of bytes of
|
||||||
|
the package read so far */
|
||||||
|
p->data_num = blocksize-5;
|
||||||
|
|
||||||
|
/* we already dealt with a blocksize worth of data */
|
||||||
|
numbytes -= blocksize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* how much there is left to add to the current payload
|
||||||
|
package */
|
||||||
|
remainpack = p->total_num - p->data_num;
|
||||||
|
|
||||||
|
if(numbytes > remainpack) {
|
||||||
|
/* if we have more data in the buffer than what is
|
||||||
|
going into this particular packet, we limit this
|
||||||
|
round to this packet only */
|
||||||
|
numbytes = remainpack;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(encrypted) {
|
||||||
|
/* At the end of the incoming stream, there is a MAC,
|
||||||
|
and we don't want to decrypt that since we need it
|
||||||
|
"raw". We MUST however decrypt the padding data
|
||||||
|
since it is used for the hash later on. */
|
||||||
|
int skip = session->remote.mac->mac_len;
|
||||||
|
|
||||||
|
/* if what we have plus numbytes is bigger than the
|
||||||
|
total minus the skip margin, we should lower the
|
||||||
|
amount to decrypt even more */
|
||||||
|
if((p->data_num + numbytes) > (p->total_num - skip)) {
|
||||||
|
numdecrypt = (p->total_num - skip) -
|
||||||
|
p->data_num;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
numdecrypt = numbytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* unencrypted data should not be decrypted at all */
|
||||||
|
numdecrypt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if there are bytes to decrypt, do that */
|
||||||
|
if(numdecrypt > 0) {
|
||||||
|
/* now decrypt the lot */
|
||||||
|
rc = decrypt(session, &p->buf[p->readidx],
|
||||||
|
p->wptr, numdecrypt);
|
||||||
|
if(rc != PACKET_NONE) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* advance the read pointer */
|
||||||
|
p->readidx += numdecrypt;
|
||||||
|
/* advance write pointer */
|
||||||
|
p->wptr += numdecrypt;
|
||||||
|
/* increse data_num */
|
||||||
|
p->data_num += numdecrypt;
|
||||||
|
|
||||||
|
/* bytes left to take care of without decryption */
|
||||||
|
numbytes -= numdecrypt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if there are bytes to copy that aren't decrypted, simply
|
||||||
|
copy them as-is to the target buffer */
|
||||||
|
if(numbytes > 0) {
|
||||||
|
memcpy(p->wptr, &p->buf[p->readidx], numbytes);
|
||||||
|
|
||||||
|
/* advance the read pointer */
|
||||||
|
p->readidx += numbytes;
|
||||||
|
/* advance write pointer */
|
||||||
|
p->wptr += numbytes;
|
||||||
|
/* increse data_num */
|
||||||
|
p->data_num += numbytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now check how much data there's left to read to finish the
|
||||||
|
current packet */
|
||||||
|
remainpack = p->total_num - p->data_num;
|
||||||
|
|
||||||
|
if(!remainpack) {
|
||||||
|
/* we have a full packet */
|
||||||
|
rc = fullpacket(session, encrypted);
|
||||||
|
|
||||||
|
p->total_num = 0; /* no packet buffer available */
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
} while (1); /* loop */
|
||||||
|
|
||||||
|
return PACKET_FAIL; /* we never reach this point */
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
#ifndef OLDSEND
|
||||||
|
|
||||||
|
static libssh2pack_t send_existing(LIBSSH2_SESSION *session,
|
||||||
|
unsigned char *data,
|
||||||
|
unsigned long data_len,
|
||||||
|
int *ret)
|
||||||
|
{
|
||||||
|
ssize_t rc;
|
||||||
|
ssize_t length;
|
||||||
|
struct transportpacket *p = &session->packet;
|
||||||
|
|
||||||
|
if(!p->outbuf) {
|
||||||
|
*ret = 0;
|
||||||
|
return PACKET_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send as much as possible of the existing packet */
|
||||||
|
if((data != p->odata) || (data_len != p->olen)) {
|
||||||
|
/* When we are about to complete the sending of a packet, it
|
||||||
|
is vital that the caller doesn't try to send a
|
||||||
|
new/different packet since we don't add this one up until
|
||||||
|
the previous one has been sent. To make the caller really
|
||||||
|
notice his/hers flaw, we return error for this case */
|
||||||
|
return PACKET_BADUSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret = 1; /* set to make our parent return */
|
||||||
|
|
||||||
|
/* number of bytes left to send */
|
||||||
|
length = p->ototal_num - p->osent;
|
||||||
|
|
||||||
|
rc = send(session->socket_fd,
|
||||||
|
&p->outbuf[p->osent],
|
||||||
|
length, LIBSSH2_SOCKET_SEND_FLAGS(session));
|
||||||
|
|
||||||
|
if(rc == length) {
|
||||||
|
/* the remainder of the package was sent */
|
||||||
|
LIBSSH2_FREE(session, p->outbuf);
|
||||||
|
p->outbuf = NULL;
|
||||||
|
p->ototal_num = 0;
|
||||||
|
}
|
||||||
|
else if(rc < 0) {
|
||||||
|
/* nothing was sent */
|
||||||
|
if(errno != EAGAIN) {
|
||||||
|
/* send failure! */
|
||||||
|
return PACKET_FAIL;
|
||||||
|
}
|
||||||
|
return PACKET_EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugdump("libssh2_packet_write send()",
|
||||||
|
&p->outbuf[p->osent], length);
|
||||||
|
p->osent += length; /* we sent away this much data */
|
||||||
|
|
||||||
|
return PACKET_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* {{{ libssh2_packet_write
|
||||||
|
* Send a packet, encrypting it and adding a MAC code if necessary
|
||||||
|
* Returns 0 on success, non-zero on failure.
|
||||||
|
*
|
||||||
|
* Returns PACKET_EAGAIN if it would block - and if it does so, you should
|
||||||
|
* call this function again as soon as it is likely that more data can be
|
||||||
|
* sent, and this function should then be called with the same argument set
|
||||||
|
* (same data pointer and same data_len) until zero or failure is returned.
|
||||||
|
*/
|
||||||
|
int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data,
|
||||||
|
unsigned long data_len)
|
||||||
|
{
|
||||||
|
int blocksize =
|
||||||
|
(session->state & LIBSSH2_STATE_NEWKEYS) ?
|
||||||
|
session->local.crypt->blocksize : 8;
|
||||||
|
int padding_length;
|
||||||
|
int packet_length;
|
||||||
|
int total_length;
|
||||||
|
int free_data=0;
|
||||||
|
#ifdef RANDOM_PADDING
|
||||||
|
int rand_max;
|
||||||
|
int seed = data[0]; /* FIXME: make this random */
|
||||||
|
#endif
|
||||||
|
struct transportpacket *p = &session->packet;
|
||||||
|
int encrypted;
|
||||||
|
int i;
|
||||||
|
ssize_t ret;
|
||||||
|
libssh2pack_t rc;
|
||||||
|
unsigned char *orgdata = data;
|
||||||
|
unsigned long orgdata_len = data_len;
|
||||||
|
|
||||||
|
debugdump("libssh2_packet_write plain", data, data_len);
|
||||||
|
|
||||||
|
/* FIRST, check if we have a pending write to complete */
|
||||||
|
rc = send_existing(session, data, data_len, &ret);
|
||||||
|
if(rc || ret)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
encrypted = (session->state & LIBSSH2_STATE_NEWKEYS)?1:0;
|
||||||
|
|
||||||
|
/* check if we should compress */
|
||||||
|
if (encrypted && strcmp(session->local.comp->name, "none")) {
|
||||||
|
if (session->local.comp->comp(session, 1, &data, &data_len,
|
||||||
|
LIBSSH2_PACKET_MAXCOMP,
|
||||||
|
&free_data, data, data_len,
|
||||||
|
&session->local.comp_abstract)) {
|
||||||
|
return PACKET_COMPRESS; /* compression failure */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RFC4253 says: Note that the length of the concatenation of
|
||||||
|
'packet_length', 'padding_length', 'payload', and 'random padding'
|
||||||
|
MUST be a multiple of the cipher block size or 8, whichever is
|
||||||
|
larger. */
|
||||||
|
|
||||||
|
/* Plain math: (4 + 1 + packet_length + padding_length) % blocksize ==
|
||||||
|
0 */
|
||||||
|
|
||||||
|
packet_length = data_len + 1 + 4; /* 1 is for padding_length field
|
||||||
|
4 for the packet_length field */
|
||||||
|
|
||||||
|
/* at this point we have it all except the padding */
|
||||||
|
|
||||||
|
/* first figure out our minimum padding amount to make it an even
|
||||||
|
block size */
|
||||||
|
padding_length = blocksize - (packet_length % blocksize);
|
||||||
|
|
||||||
|
/* if the padding becomes too small we add another blocksize worth
|
||||||
|
of it (taken from the original libssh2 where it didn't have any
|
||||||
|
real explanation) */
|
||||||
|
if (padding_length < 4) {
|
||||||
|
padding_length += blocksize;
|
||||||
|
}
|
||||||
|
#ifdef RANDOM_PADDING
|
||||||
|
/* FIXME: we can add padding here, but that also makes the packets
|
||||||
|
bigger etc */
|
||||||
|
|
||||||
|
/* now we can add 'blocksize' to the padding_length N number of times
|
||||||
|
(to "help thwart traffic analysis") but it must be less than 255 in
|
||||||
|
total */
|
||||||
|
rand_max = (255 - padding_length)/blocksize + 1;
|
||||||
|
padding_length += blocksize * (seed % rand_max);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
packet_length += padding_length;
|
||||||
|
|
||||||
|
/* append the MAC length to the total_length size */
|
||||||
|
total_length = packet_length +
|
||||||
|
(encrypted?session->local.mac->mac_len:0);
|
||||||
|
|
||||||
|
/* allocate memory to store the outgoing packet in, in case we can't
|
||||||
|
send the whole one and thus need to keep it after this function
|
||||||
|
returns. */
|
||||||
|
p->outbuf = LIBSSH2_ALLOC(session, total_length);
|
||||||
|
if(!p->outbuf) {
|
||||||
|
return PACKET_ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store packet_length, which is the size of the whole packet except
|
||||||
|
the MAC and the packet_length field itself */
|
||||||
|
libssh2_htonu32(p->outbuf, packet_length - 4);
|
||||||
|
/* store padding_length */
|
||||||
|
p->outbuf[4] = padding_length;
|
||||||
|
/* copy the payload data */
|
||||||
|
memcpy(p->outbuf + 5, data, data_len);
|
||||||
|
/* fill the padding area with random junk */
|
||||||
|
libssh2_random(p->outbuf + 5 + data_len, padding_length);
|
||||||
|
if (free_data) {
|
||||||
|
LIBSSH2_FREE(session, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(encrypted) {
|
||||||
|
/* Calculate MAC hash. Put the output at index packet_length,
|
||||||
|
since that size includes the whole packet. The MAC is
|
||||||
|
calculated on the entire unencrypted packet, including all
|
||||||
|
fields except the MAC field itself. */
|
||||||
|
session->local.mac->hash(session,
|
||||||
|
p->outbuf + packet_length,
|
||||||
|
session->local.seqno,
|
||||||
|
p->outbuf, packet_length,
|
||||||
|
NULL, 0,
|
||||||
|
&session->local.mac_abstract);
|
||||||
|
|
||||||
|
/* Encrypt the whole packet data, one block size at a time.
|
||||||
|
The MAC field is not encrypted. */
|
||||||
|
for(i=0; i < packet_length;
|
||||||
|
i += session->local.crypt->blocksize) {
|
||||||
|
unsigned char *ptr = &p->outbuf[i];
|
||||||
|
if(session->local.crypt->crypt(session, ptr,
|
||||||
|
&session->local.crypt_abstract))
|
||||||
|
return PACKET_FAIL; /* encryption failure */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session->local.seqno++;
|
||||||
|
|
||||||
|
ret = send(session->socket_fd, p->outbuf,
|
||||||
|
total_length, LIBSSH2_SOCKET_SEND_FLAGS(session));
|
||||||
|
|
||||||
|
if(ret != -1) {
|
||||||
|
debugdump("libssh2_packet_write send()",
|
||||||
|
p->outbuf, ret);
|
||||||
|
}
|
||||||
|
if(ret != total_length) {
|
||||||
|
if((ret > 0 ) ||
|
||||||
|
((ret == -1) && (errno == EAGAIN))) {
|
||||||
|
/* the whole packet could not be sent, save the rest */
|
||||||
|
p->odata = orgdata;
|
||||||
|
p->olen = orgdata_len;
|
||||||
|
p->osent = (ret == -1)?0:ret;
|
||||||
|
p->ototal_num = total_length;
|
||||||
|
return PACKET_EAGAIN;
|
||||||
|
}
|
||||||
|
return PACKET_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the whole thing got sent away */
|
||||||
|
p->odata = NULL;
|
||||||
|
p->olen = 0;
|
||||||
|
LIBSSH2_FREE(session, p->outbuf);
|
||||||
|
p->outbuf = NULL;
|
||||||
|
|
||||||
|
return PACKET_NONE; /* all is good */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* }}} */
|
||||||
|
#endif
|
152
ssh2_sample.c
152
ssh2_sample.c
@ -1,152 +0,0 @@
|
|||||||
#include "libssh2.h"
|
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
# include <netinet/in.h>
|
|
||||||
# include <sys/socket.h>
|
|
||||||
# include <unistd.h>
|
|
||||||
#else
|
|
||||||
# include <winsock2.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int sock, i, auth_pw = 1;
|
|
||||||
struct sockaddr_in sin;
|
|
||||||
const char *fingerprint;
|
|
||||||
LIBSSH2_SESSION *session;
|
|
||||||
LIBSSH2_CHANNEL *channel;
|
|
||||||
char *username=(char *)"username";
|
|
||||||
char *password=(char *)"password";
|
|
||||||
#ifdef WIN32
|
|
||||||
WSADATA wsadata;
|
|
||||||
|
|
||||||
WSAStartup(WINSOCK_VERSION, &wsadata);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Ultra basic "connect to port 22 on localhost"
|
|
||||||
* Your code is responsible for creating the socket establishing the connection
|
|
||||||
*/
|
|
||||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
#ifndef WIN32
|
|
||||||
fcntl(sock, F_SETFL, 0);
|
|
||||||
#endif
|
|
||||||
sin.sin_family = AF_INET;
|
|
||||||
sin.sin_port = htons(22);
|
|
||||||
sin.sin_addr.s_addr = htonl(0x7F000001);
|
|
||||||
if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) {
|
|
||||||
fprintf(stderr, "failed to connect!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create a session instance and start it up
|
|
||||||
* This will trade welcome banners, exchange keys, and setup crypto, compression, and MAC layers
|
|
||||||
*/
|
|
||||||
session = libssh2_session_init();
|
|
||||||
if (libssh2_session_startup(session, sock)) {
|
|
||||||
fprintf(stderr, "Failure establishing SSH session\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* At this point we havn't 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_MD5);
|
|
||||||
printf("Fingerprint: ");
|
|
||||||
for(i = 0; i < 16; i++) {
|
|
||||||
printf("%02X ", (unsigned char)fingerprint[i]);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
if(argc > 1) {
|
|
||||||
username = argv[1];
|
|
||||||
}
|
|
||||||
if(argc > 2) {
|
|
||||||
password = argv[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auth_pw) {
|
|
||||||
/* We could authenticate via password */
|
|
||||||
if (libssh2_userauth_password(session, username, password)) {
|
|
||||||
printf("Authentication by password failed.\n");
|
|
||||||
goto shutdown;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Or by public key */
|
|
||||||
if (libssh2_userauth_publickey_fromfile(session, username, "/home/username/.ssh/id_rsa.pub", "/home/username/.ssh/id_rsa", password)) {
|
|
||||||
printf("\tAuthentication by public key failed\n");
|
|
||||||
goto shutdown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Request a shell */
|
|
||||||
if (!(channel = libssh2_channel_open_session(session))) {
|
|
||||||
fprintf(stderr, "Unable to open a session\n");
|
|
||||||
goto shutdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Some environment variables may be set,
|
|
||||||
* It's up to the server which ones it'll allow though
|
|
||||||
*/
|
|
||||||
libssh2_channel_setenv(channel, (char *)"FOO", (char *)"bar");
|
|
||||||
|
|
||||||
/* Request a terminal with 'vanilla' terminal emulation
|
|
||||||
* See /etc/termcap for more options
|
|
||||||
*/
|
|
||||||
if (libssh2_channel_request_pty(channel, (char *)"vanilla")) {
|
|
||||||
fprintf(stderr, "Failed requesting pty\n");
|
|
||||||
goto skip_shell;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open a SHELL on that pty */
|
|
||||||
if (libssh2_channel_shell(channel)) {
|
|
||||||
fprintf(stderr, "Unable to request shell on allocated pty\n");
|
|
||||||
goto shutdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* At this point the shell can be interacted with using
|
|
||||||
* libssh2_channel_read()
|
|
||||||
* libssh2_channel_read_stderr()
|
|
||||||
* libssh2_channel_write()
|
|
||||||
* libssh2_channel_write_stderr()
|
|
||||||
*
|
|
||||||
* Blocking mode may be (en|dis)abled with: libssh2_channel_set_blocking()
|
|
||||||
* If the server send EOF, libssh2_channel_eof() will return non-0
|
|
||||||
* To send EOF to the server use: libssh2_channel_send_eof()
|
|
||||||
* A channel can be closed with: libssh2_channel_close()
|
|
||||||
* A channel can be freed with: libssh2_channel_free()
|
|
||||||
*/
|
|
||||||
|
|
||||||
skip_shell:
|
|
||||||
if (channel) {
|
|
||||||
libssh2_channel_free(channel);
|
|
||||||
channel = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Other channel types are supported via:
|
|
||||||
* libssh2_scp_send()
|
|
||||||
* libssh2_scp_recv()
|
|
||||||
* libssh2_channel_direct_tcpip()
|
|
||||||
*/
|
|
||||||
|
|
||||||
shutdown:
|
|
||||||
|
|
||||||
libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");
|
|
||||||
libssh2_session_free(session);
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
Sleep(1000);
|
|
||||||
closesocket(sock);
|
|
||||||
#else
|
|
||||||
sleep(1);
|
|
||||||
close(sock);
|
|
||||||
#endif
|
|
||||||
printf("all done\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user