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
|
||||
|
||||
SUBDIRS = src tests
|
||||
SUBDIRS = src example tests
|
||||
|
||||
include_HEADERS = include/libssh2.h include/libssh2_publickey.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
|
||||
|
||||
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_DEFINE(LIBSSH2_DEBUG_ERRORS, 1, [Output failure events to stderr])])
|
||||
AC_ARG_ENABLE(debug-all,
|
||||
AC_HELP_STRING([--enable-debug-all],[Output debugging info for all layers to stderr]),
|
||||
AC_HELP_STRING([--enable-debug],[Enable debug]),
|
||||
[
|
||||
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])
|
||||
@ -273,5 +273,7 @@ AC_C_INLINE
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
src/Makefile
|
||||
tests/Makefile])
|
||||
tests/Makefile
|
||||
example/Makefile
|
||||
example/simple/Makefile])
|
||||
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.
|
||||
*/
|
||||
@ -109,6 +109,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "libssh2_sftp_init()!\n");
|
||||
sftp_session = libssh2_sftp_init(session);
|
||||
|
||||
if (!sftp_session) {
|
||||
@ -116,6 +117,7 @@ int main(int argc, char *argv[])
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
fprintf(stderr, "libssh2_sftp_open()!\n");
|
||||
/* Request a file via SFTP */
|
||||
sftp_handle =
|
||||
libssh2_sftp_open(sftp_session, sftppath, LIBSSH2_FXF_READ, 0);
|
||||
@ -129,6 +131,7 @@ int main(int argc, char *argv[])
|
||||
char mem[512];
|
||||
|
||||
/* loop until we fail */
|
||||
fprintf(stderr, "libssh2_sftp_read()!\n");
|
||||
rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem));
|
||||
if(rc > 0) {
|
||||
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.
|
||||
*
|
||||
* 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_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);
|
||||
#define libssh2_channel_read(channel, buf, buflen) libssh2_channel_read_ex((channel), 0, (buf), (buflen))
|
||||
LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel,
|
||||
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_readnb(channel, buf, buflen) \
|
||||
libssh2_channel_readnb_ex((channel), 0, (buf), (buflen))
|
||||
|
||||
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 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))
|
||||
LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel,
|
||||
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))
|
||||
|
||||
LIBSSH2_API unsigned long libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, unsigned long *window_size_initial);
|
||||
#define libssh2_channel_window_write(channel) libssh2_channel_window_write_ex((channel), NULL)
|
||||
|
||||
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking);
|
||||
LIBSSH2_API int libssh2_channel_get_blocking(LIBSSH2_CHANNEL *channel);
|
||||
|
||||
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
|
||||
* 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.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
@ -38,6 +38,8 @@
|
||||
#ifndef LIBSSH2_SFTP_H
|
||||
#define LIBSSH2_SFTP_H 1
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#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_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 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);
|
||||
#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 \
|
||||
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
|
||||
libssh2_la_SOURCES += libgcrypt.c
|
||||
|
389
src/channel.c
389
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.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
@ -38,8 +38,11 @@
|
||||
#include "libssh2_priv.h"
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/* {{{ libssh2_channel_nextid
|
||||
* Determine the next channel ID we can use at our end
|
||||
*/
|
||||
@ -104,24 +107,39 @@ LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long
|
||||
/* {{{ libssh2_channel_open_session
|
||||
* 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,
|
||||
unsigned int packet_size, const char *message, unsigned int message_len)
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *
|
||||
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;
|
||||
unsigned long local_channel = libssh2_channel_nextid(session);
|
||||
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) +
|
||||
window_size(4) + packet_size(4) */
|
||||
unsigned long packet_len =
|
||||
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 long data_len;
|
||||
|
||||
#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
|
||||
channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_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;
|
||||
}
|
||||
memset(channel, 0, sizeof(LIBSSH2_CHANNEL));
|
||||
@ -129,7 +147,8 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, c
|
||||
channel->channel_type_len = channel_type_len;
|
||||
channel->channel_type = LIBSSH2_ALLOC(session, channel_type_len);
|
||||
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);
|
||||
return NULL;
|
||||
}
|
||||
@ -145,27 +164,40 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, c
|
||||
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
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;
|
||||
}
|
||||
*(s++) = SSH_MSG_CHANNEL_OPEN;
|
||||
libssh2_htonu32(s, channel_type_len); s += 4;
|
||||
memcpy(s, channel_type, channel_type_len); s += channel_type_len;
|
||||
libssh2_htonu32(s, channel_type_len);
|
||||
s += 4;
|
||||
|
||||
libssh2_htonu32(s, local_channel); s += 4;
|
||||
libssh2_htonu32(s, window_size); s += 4;
|
||||
libssh2_htonu32(s, packet_size); s += 4;
|
||||
memcpy(s, channel_type, channel_type_len);
|
||||
s += channel_type_len;
|
||||
|
||||
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) {
|
||||
memcpy(s, message, message_len); s += message_len;
|
||||
memcpy(s, message, message_len);
|
||||
s += message_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -175,7 +207,8 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, c
|
||||
channel->local.window_size_initial = libssh2_ntohu32(data + 9);
|
||||
channel->local.packet_size = libssh2_ntohu32(data + 13);
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Connection Established - ID: %lu/%lu win: %lu/%lu pack: %lu/%lu",
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN,
|
||||
"Connection Established - ID: %lu/%lu win: %lu/%lu pack: %lu/%lu",
|
||||
channel->local.id, channel->remote.id,
|
||||
channel->local.window_size, channel->remote.window_size,
|
||||
channel->local.packet_size, channel->remote.packet_size);
|
||||
@ -187,7 +220,8 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, c
|
||||
}
|
||||
|
||||
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:
|
||||
@ -217,8 +251,12 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, c
|
||||
|
||||
/* Clear out packets meant for this channel */
|
||||
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) ||
|
||||
(libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_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_FREE(session, data);
|
||||
}
|
||||
|
||||
@ -430,9 +468,18 @@ LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
|
||||
/* {{{ libssh2_channel_forward_accept
|
||||
* 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) {
|
||||
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_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;
|
||||
}
|
||||
/* }}} */
|
||||
@ -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 long data_len;
|
||||
unsigned long packet_len = request_len + 10; /* packet_type(1) + channel(4) + request_len(4) + want_reply(1) */
|
||||
libssh2pack_t rc;
|
||||
|
||||
if (message) {
|
||||
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;
|
||||
}
|
||||
|
||||
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_FREE(session, packet);
|
||||
return -1;
|
||||
@ -728,15 +778,61 @@ LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ 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_channel_set_blocking
|
||||
* 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
|
||||
_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
|
||||
if(blocking == channel->blocking) {
|
||||
/* avoid if already correct */
|
||||
return bl;
|
||||
}
|
||||
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;
|
||||
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) ||
|
||||
(packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) &&
|
||||
(libssh2_ntohu32(packet->data + 1) == channel->local.id)) {
|
||||
/* 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) ||
|
||||
((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA) && ((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) || (streamid == packet_stream_id))) ||
|
||||
((packet_type == SSH_MSG_CHANNEL_DATA) && (streamid == 0))) {
|
||||
int bytes_to_flush = packet->data_len - packet->data_head;
|
||||
((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA) &&
|
||||
((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) ||
|
||||
(streamid == packet_stream_id))) ||
|
||||
((packet_type == SSH_MSG_CHANNEL_DATA) &&
|
||||
(streamid == 0))) {
|
||||
int bytes_to_flush =
|
||||
packet->data_len - packet->data_head;
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Flushing %d bytes of data from stream %lu on channel %lu/%lu", bytes_to_flush,
|
||||
packet_stream_id, channel->local.id, channel->remote.id);
|
||||
@ -870,39 +973,89 @@ LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel,
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_channel_read_ex
|
||||
* Read data from a channel
|
||||
/* {{{ _libssh2_channel_read_ex
|
||||
* 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;
|
||||
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
|
||||
_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
|
||||
do {
|
||||
LIBSSH2_PACKET *packet;
|
||||
|
||||
/* Process any waiting packets */
|
||||
while (libssh2_packet_read(session, blocking_read) > 0) blocking_read = 0;
|
||||
/* set non-blocking and remember previous state */
|
||||
bl = _libssh2_channel_set_blocking(channel, 0);
|
||||
|
||||
/* process all incoming packets */
|
||||
do {
|
||||
rc = libssh2_packet_read(session);
|
||||
} while (rc > 0);
|
||||
rc = 0;
|
||||
|
||||
/* restore blocking state */
|
||||
_libssh2_channel_set_blocking(channel, bl);
|
||||
|
||||
packet = session->packets.head;
|
||||
|
||||
while (packet && (bytes_read < buflen)) {
|
||||
/* 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;
|
||||
|
||||
/* 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 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))) ||
|
||||
(!stream_id && (packet->data[0] == SSH_MSG_CHANNEL_DATA) && (channel->local.id == libssh2_ntohu32(packet->data + 1))) ||
|
||||
(!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))) {
|
||||
if ((stream_id &&
|
||||
(packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) &&
|
||||
(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 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;
|
||||
unlink_packet = 1;
|
||||
}
|
||||
@ -936,21 +1089,78 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
|
||||
}
|
||||
packet = next;
|
||||
}
|
||||
blocking_read = 1;
|
||||
} while (channel->blocking && (bytes_read == 0) && !channel->remote.close);
|
||||
block=1;
|
||||
|
||||
if (channel->blocking && (bytes_read == 0)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "Remote end has closed this channel", 0);
|
||||
} while (channel->blocking && (bytes_read == 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;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
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
|
||||
* 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;
|
||||
unsigned char *packet;
|
||||
@ -983,6 +1193,7 @@ LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id
|
||||
while (buflen > 0) {
|
||||
size_t bufwrite = buflen;
|
||||
unsigned char *s = packet;
|
||||
libssh2pack_t rc;
|
||||
|
||||
*(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA;
|
||||
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 */
|
||||
while (channel->local.window_size <= 0) {
|
||||
/* Don't worry -- This is never hit unless it's a blocking channel anyway */
|
||||
if (libssh2_packet_read(session, 1) < 0) {
|
||||
/* Error occured, disconnect? */
|
||||
return -1;
|
||||
/* Don't worry -- This is never hit unless it's a
|
||||
blocking channel anyway */
|
||||
rc = libssh2_packet_read(session);
|
||||
|
||||
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 */
|
||||
@ -1019,7 +1237,8 @@ LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending %d bytes on channel %lu/%lu, stream_id=%d", (int)bufwrite, channel->local.id, channel->remote.id, stream_id);
|
||||
#endif
|
||||
if (libssh2_packet_write(session, packet, s - packet)) {
|
||||
rc = libssh2_packet_write(session, packet, s - packet);
|
||||
if(rc) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel data", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
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
|
||||
* Send EOF on channel
|
||||
*/
|
||||
@ -1143,7 +1406,7 @@ LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel)
|
||||
* Either or channel will be closed
|
||||
* 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;
|
||||
|
@ -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.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
@ -37,7 +37,7 @@
|
||||
|
||||
#include "libssh2_priv.h"
|
||||
|
||||
#if LIBSSH2_CRYPT_NONE
|
||||
#ifdef LIBSSH2_CRYPT_NONE
|
||||
/* {{{ libssh2_crypt_none_crypt
|
||||
* 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)
|
||||
{
|
||||
struct crypt_ctx *cctx = *(struct crypt_ctx **)abstract;
|
||||
(void)session;
|
||||
return _libssh2_cipher_crypt(&cctx->h, cctx->algo,
|
||||
cctx->encrypt, block);
|
||||
}
|
||||
@ -234,7 +235,7 @@ static LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = {
|
||||
#if LIBSSH2_3DES
|
||||
&libssh2_crypt_method_3des_cbc,
|
||||
#endif /* LIBSSH2_DES */
|
||||
#if LIBSSH2_CRYPT_NONE
|
||||
#ifdef LIBSSH2_CRYPT_NONE
|
||||
&libssh2_crypt_method_none,
|
||||
#endif
|
||||
NULL
|
||||
|
41
src/kex.c
41
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.
|
||||
*
|
||||
* 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 long f_value_len, k_value_len, h_sig_len;
|
||||
libssh2_sha1_ctx exchange_hash;
|
||||
int rc;
|
||||
|
||||
/* Generate x and e */
|
||||
_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
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending KEX packet %d", (int)packet_type_init);
|
||||
#endif
|
||||
if (libssh2_packet_write(session, e_packet, e_packet_len)) {
|
||||
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);
|
||||
ret = -11;
|
||||
goto clean_exit;
|
||||
@ -135,8 +137,11 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
}
|
||||
|
||||
/* Wait for KEX reply */
|
||||
if (libssh2_packet_require(session, packet_type_reply, &s_packet, &s_packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timed out waiting for KEX reply", 0);
|
||||
rc = libssh2_packet_require(session, packet_type_reply, &s_packet,
|
||||
&s_packet_len);
|
||||
if (rc) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
|
||||
"Timed out waiting for KEX reply", 0);
|
||||
ret = -1;
|
||||
goto clean_exit;
|
||||
}
|
||||
@ -907,7 +912,9 @@ static int libssh2_kex_agree_hostkey(LIBSSH2_SESSION *session, unsigned long kex
|
||||
}
|
||||
|
||||
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) {
|
||||
/* So far so good, but does it suit our purposes? (Encrypting vs Signing) */
|
||||
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) {
|
||||
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) {
|
||||
/* We've agreed on a key exchange method,
|
||||
* 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)) {
|
||||
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) {
|
||||
/* Invalid method -- Should never be reached */
|
||||
@ -1031,7 +1040,9 @@ static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session,
|
||||
}
|
||||
|
||||
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) {
|
||||
endpoint->crypt = *cryptp;
|
||||
return 0;
|
||||
@ -1076,7 +1087,9 @@ static int libssh2_kex_agree_mac(LIBSSH2_SESSION *session, libssh2_endpoint_data
|
||||
}
|
||||
|
||||
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) {
|
||||
endpoint->mac = *macp;
|
||||
return 0;
|
||||
@ -1121,7 +1134,9 @@ static int libssh2_kex_agree_comp(LIBSSH2_SESSION *session, libssh2_endpoint_dat
|
||||
}
|
||||
|
||||
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) {
|
||||
endpoint->comp = *compp;
|
||||
return 0;
|
||||
@ -1261,7 +1276,7 @@ int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange) /* session->f
|
||||
}
|
||||
session->local.kexinit = oldlocal;
|
||||
session->local.kexinit_len = oldlocal_len;
|
||||
return -1;
|
||||
return -2;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (libssh2_kex_agree_methods(session, data, data_len)) {
|
||||
return -1;
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
if (session->kex->exchange_keys(session)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE, "Unrecoverable error exchanging keys", 0);
|
||||
return -1;
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* Done with kexinit buffers */
|
||||
|
@ -54,6 +54,15 @@
|
||||
#include "openssl.h"
|
||||
#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_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)
|
||||
@ -173,6 +182,43 @@ typedef struct _libssh2_endpoint_data {
|
||||
char *lang_prefs;
|
||||
} 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 {
|
||||
/* Memory management callbacks */
|
||||
void *abstract;
|
||||
@ -240,6 +286,9 @@ struct _LIBSSH2_SESSION {
|
||||
unsigned long err_msglen;
|
||||
int err_should_free;
|
||||
int err_code;
|
||||
|
||||
/* struct members for packet-level reading */
|
||||
struct transportpacket packet;
|
||||
};
|
||||
|
||||
/* 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) { \
|
||||
LIBSSH2_FREE(session, session->err_msg); \
|
||||
} \
|
||||
session->err_msg = errmsg; \
|
||||
session->err_msg = (char *)errmsg; \
|
||||
session->err_msglen = strlen(errmsg); \
|
||||
session->err_should_free = should_free; \
|
||||
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) { \
|
||||
LIBSSH2_FREE(session, session->err_msg); \
|
||||
} \
|
||||
session->err_msg = errmsg; \
|
||||
session->err_msg = (char *)errmsg; \
|
||||
session->err_msglen = strlen(errmsg); \
|
||||
session->err_should_free = should_free; \
|
||||
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_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);
|
||||
#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))
|
||||
@ -455,9 +524,25 @@ int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_t
|
||||
libssh2_packet_requirev_ex((session), (packet_types), (data), (data_len), 0, NULL, 0)
|
||||
int libssh2_packet_burn(LIBSSH2_SESSION *session);
|
||||
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);
|
||||
unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session);
|
||||
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 */
|
||||
LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void);
|
||||
|
670
src/packet.c
670
src/packet.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.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
@ -47,6 +47,9 @@
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_POLL
|
||||
# include <sys/poll.h>
|
||||
#else
|
||||
@ -62,22 +65,15 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/* 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
|
||||
|
||||
inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen);
|
||||
inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen);
|
||||
|
||||
/* {{{ libssh2_packet_queue_listener
|
||||
* Queue a connection request for a listener
|
||||
*/
|
||||
inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen)
|
||||
inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session,
|
||||
unsigned char *data,
|
||||
unsigned long datalen)
|
||||
{
|
||||
/* Look for a matching listener */
|
||||
unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5;
|
||||
@ -89,6 +85,7 @@ inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char
|
||||
uint32_t sender_channel, initial_window_size, packet_size;
|
||||
unsigned char *host, *shost;
|
||||
uint32_t port, sport, host_len, shost_len;
|
||||
(void)datalen;
|
||||
|
||||
sender_channel = libssh2_ntohu32(s); s += 4;
|
||||
|
||||
@ -106,8 +103,9 @@ inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Remote received connection from %s:%ld to %s:%ld", shost, sport, host, port);
|
||||
#endif
|
||||
|
||||
while (l) {
|
||||
if ((l->port == port) &&
|
||||
if ((l->port == (int)port) &&
|
||||
(strlen(l->host) == host_len) &&
|
||||
(memcmp(l->host, host, host_len) == 0)) {
|
||||
/* This is our listener */
|
||||
@ -214,7 +212,8 @@ inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char
|
||||
/* {{{ libssh2_packet_x11_open
|
||||
* Accept a forwarded X11 connection
|
||||
*/
|
||||
inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen)
|
||||
inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session,
|
||||
unsigned char *data, unsigned long datalen)
|
||||
{
|
||||
int failure_code = 2; /* SSH_OPEN_CONNECT_FAILED */
|
||||
unsigned char *s = data + (sizeof("x11") - 1) + 5;
|
||||
@ -225,6 +224,7 @@ inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data
|
||||
unsigned long sender_channel, initial_window_size, packet_size;
|
||||
unsigned char *shost;
|
||||
unsigned long sport, shost_len;
|
||||
(void)datalen;
|
||||
|
||||
sender_channel = libssh2_ntohu32(s); s += 4;
|
||||
initial_window_size = libssh2_ntohu32(s); s += 4;
|
||||
@ -326,7 +326,7 @@ inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data
|
||||
/* {{{ libssh2_packet_new
|
||||
* Create a new packet and attach it to the brigade
|
||||
*/
|
||||
static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, size_t datalen, int macstate)
|
||||
int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, size_t datalen, int macstate)
|
||||
{
|
||||
LIBSSH2_PACKET *packet;
|
||||
unsigned long data_head = 0;
|
||||
@ -625,364 +625,22 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_blocking_read
|
||||
* Force a blocking read, regardless of socket settings
|
||||
*/
|
||||
static ssize_t libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, size_t count)
|
||||
{
|
||||
size_t bytes_read = 0;
|
||||
#if !defined(HAVE_POLL) && !defined(HAVE_SELECT)
|
||||
int polls = 0;
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
fcntl(session->socket_fd, F_SETFL, 0);
|
||||
#else
|
||||
{
|
||||
u_long block = FALSE;
|
||||
ioctlsocket(session->socket_fd, FIONBIO, &block);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes", (int)count);
|
||||
#endif
|
||||
|
||||
while (bytes_read < count) {
|
||||
int ret;
|
||||
|
||||
ret = recv(session->socket_fd, buf + bytes_read, count - bytes_read, LIBSSH2_SOCKET_RECV_FLAGS(session));
|
||||
if (ret < 0) {
|
||||
#ifdef WIN32
|
||||
switch (WSAGetLastError()) {
|
||||
case WSAEWOULDBLOCK: errno = EAGAIN; break;
|
||||
case WSAENOTSOCK: errno = EBADF; break;
|
||||
case WSAENOTCONN:
|
||||
case WSAECONNABORTED: errno = ENOTCONN; break;
|
||||
case WSAEINTR: errno = EINTR; break;
|
||||
}
|
||||
#endif
|
||||
if (errno == EAGAIN) {
|
||||
#ifdef HAVE_POLL
|
||||
struct pollfd read_socket;
|
||||
|
||||
read_socket.fd = session->socket_fd;
|
||||
read_socket.events = POLLIN;
|
||||
|
||||
if (poll(&read_socket, 1, 30000) <= 0) {
|
||||
return -1;
|
||||
}
|
||||
#elif defined(HAVE_SELECT)
|
||||
fd_set read_socket;
|
||||
struct timeval timeout;
|
||||
|
||||
FD_ZERO(&read_socket);
|
||||
FD_SET(session->socket_fd, &read_socket);
|
||||
|
||||
timeout.tv_sec = 30;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
if (select(session->socket_fd + 1, &read_socket, NULL, NULL, &timeout) <= 0) {
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
if (polls++ > LIBSSH2_SOCKET_POLL_MAXLOOPS) {
|
||||
return -1;
|
||||
}
|
||||
usleep(LIBSSH2_SOCKET_POLL_UDELAY);
|
||||
#endif /* POLL/SELECT/SLEEP */
|
||||
continue;
|
||||
}
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if ((errno == EBADF) || (errno == EIO) || (errno == ENOTCONN)) {
|
||||
session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (ret == 0) continue;
|
||||
|
||||
bytes_read += ret;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes actually read", (int)bytes_read);
|
||||
#endif
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ 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 (0 if nothing added), or -1 on failure
|
||||
*/
|
||||
int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
|
||||
{
|
||||
int packet_type = -1;
|
||||
|
||||
if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
fcntl(session->socket_fd, F_SETFL, O_NONBLOCK);
|
||||
#else
|
||||
{
|
||||
u_long non_block = TRUE;
|
||||
ioctlsocket(session->socket_fd, FIONBIO, &non_block);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Checking for packet: will%s block", should_block ? "" : " not");
|
||||
#endif
|
||||
if (session->state & LIBSSH2_STATE_NEWKEYS) {
|
||||
/* Temporary Buffer
|
||||
* The largest blocksize (currently) is 32, the largest MAC (currently) is 20
|
||||
*/
|
||||
unsigned char block[2 * 32], *payload, *s, *p, tmp[6];
|
||||
ssize_t read_len;
|
||||
unsigned long blocksize = session->remote.crypt->blocksize;
|
||||
unsigned long packet_len, payload_len;
|
||||
int padding_len;
|
||||
int macstate;
|
||||
int free_payload = 1;
|
||||
|
||||
/* Note: If we add any cipher with a blocksize less than 6 we'll need to get more creative with this
|
||||
* For now, all blocksize sizes are 8+
|
||||
*/
|
||||
if (should_block) {
|
||||
read_len = libssh2_blocking_read(session, block, blocksize);
|
||||
if(read_len <= 0)
|
||||
return read_len;
|
||||
} else {
|
||||
ssize_t nread;
|
||||
read_len = recv(session->socket_fd, block, 1, LIBSSH2_SOCKET_RECV_FLAGS(session));
|
||||
if (read_len <= 0) {
|
||||
return 0;
|
||||
}
|
||||
nread = libssh2_blocking_read(session, block + read_len, blocksize - read_len);
|
||||
if(nread <= 0)
|
||||
return nread;
|
||||
|
||||
read_len += nread;
|
||||
}
|
||||
if (read_len < blocksize) {
|
||||
return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1;
|
||||
}
|
||||
|
||||
if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
packet_len = libssh2_ntohu32(block);
|
||||
|
||||
/* 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(packet_len > MAX_SSH_PACKET_LEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
padding_len = block[4];
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing packet %lu bytes long (with %lu bytes padding)", packet_len, padding_len);
|
||||
#endif
|
||||
memcpy(tmp, block, 5); /* Use this for MAC later */
|
||||
|
||||
payload_len = packet_len - 1; /* padding_len(1) */
|
||||
/* Sanity Check */
|
||||
if ((payload_len > LIBSSH2_PACKET_MAXPAYLOAD) ||
|
||||
((packet_len + 4) % blocksize)) {
|
||||
/* If something goes horribly wrong during the decryption phase, just bailout and die gracefully */
|
||||
session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
|
||||
libssh2_error(session, LIBSSH2_ERROR_PROTO, "Fatal protocol error, invalid payload size", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = payload = LIBSSH2_ALLOC(session, payload_len);
|
||||
memcpy(s, block + 5, blocksize - 5);
|
||||
s += blocksize - 5;
|
||||
|
||||
p = s;
|
||||
while (p - payload < payload_len) {
|
||||
read_len = payload_len - (p - payload);
|
||||
if (read_len > 4096) read_len = 4096;
|
||||
|
||||
if (libssh2_blocking_read(session, p, read_len) < read_len) {
|
||||
LIBSSH2_FREE(session, payload);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p += read_len;
|
||||
|
||||
while (s < p) {
|
||||
memcpy(block, s, blocksize);
|
||||
|
||||
if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0);
|
||||
LIBSSH2_FREE(session, payload);
|
||||
return -1;
|
||||
}
|
||||
memcpy(s, block, blocksize);
|
||||
|
||||
s += blocksize;
|
||||
}
|
||||
}
|
||||
|
||||
read_len = libssh2_blocking_read(session, block, session->remote.mac->mac_len);
|
||||
if (read_len < session->remote.mac->mac_len) {
|
||||
LIBSSH2_FREE(session, payload);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Calculate MAC hash */
|
||||
session->remote.mac->hash(session, block + session->remote.mac->mac_len, session->remote.seqno, tmp, 5, payload, payload_len, &session->remote.mac_abstract);
|
||||
|
||||
macstate = (strncmp((char *)block, (char *)block + session->remote.mac->mac_len, session->remote.mac->mac_len) == 0) ? LIBSSH2_MAC_CONFIRMED : LIBSSH2_MAC_INVALID;
|
||||
|
||||
session->remote.seqno++;
|
||||
|
||||
/* Ignore padding */
|
||||
payload_len -= padding_len;
|
||||
|
||||
if (session->remote.comp &&
|
||||
strcmp(session->remote.comp->name, "none")) {
|
||||
/* Decompress */
|
||||
unsigned char *data;
|
||||
unsigned long data_len;
|
||||
|
||||
if (session->remote.comp->comp(session, 0, &data, &data_len, LIBSSH2_PACKET_MAXDECOMP, &free_payload, payload, payload_len, &session->remote.comp_abstract)) {
|
||||
LIBSSH2_FREE(session, payload);
|
||||
return -1;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Payload decompressed: %lu bytes(compressed) to %lu bytes(uncompressed)", data_len, payload_len);
|
||||
#endif
|
||||
if (free_payload) {
|
||||
LIBSSH2_FREE(session, payload);
|
||||
payload = data;
|
||||
payload_len = data_len;
|
||||
} else {
|
||||
if (data == 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, payload);
|
||||
|
||||
/* We need a freeable struct otherwise the brigade won't know what to do with it */
|
||||
payload = LIBSSH2_ALLOC(session, data_len);
|
||||
if (!payload) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of uncompressed data", 0);
|
||||
return -1;
|
||||
}
|
||||
memcpy(payload, data, data_len);
|
||||
payload_len = data_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packet_type = payload[0];
|
||||
libssh2_packet_add(session, payload, payload_len, macstate);
|
||||
|
||||
} else { /* No cipher active */
|
||||
unsigned char *payload;
|
||||
unsigned char buf[24];
|
||||
ssize_t buf_len;
|
||||
unsigned long payload_len;
|
||||
uint32_t packet_length;
|
||||
unsigned long padding_length;
|
||||
|
||||
if (should_block) {
|
||||
buf_len = libssh2_blocking_read(session, buf, 5);
|
||||
} else {
|
||||
ssize_t nread;
|
||||
buf_len = recv(session->socket_fd, buf, 1, LIBSSH2_SOCKET_RECV_FLAGS(session));
|
||||
if (buf_len <= 0) {
|
||||
return buf_len;
|
||||
}
|
||||
nread = libssh2_blocking_read(session, buf, 5 - buf_len);
|
||||
if(nread <= 0)
|
||||
return -1;
|
||||
|
||||
buf_len += nread;
|
||||
}
|
||||
if (buf_len < 5) {
|
||||
/* Something bad happened */
|
||||
return -1;
|
||||
}
|
||||
packet_length = libssh2_ntohu32(buf);
|
||||
|
||||
/* 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(packet_length > MAX_SSH_PACKET_LEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
padding_length = buf[4];
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing plaintext packet %lu bytes long (with %lu bytes padding)", packet_length, padding_length);
|
||||
#endif
|
||||
|
||||
payload_len = packet_length - padding_length - 1; /* padding_length(1) */
|
||||
payload = LIBSSH2_ALLOC(session, payload_len);
|
||||
if (!payload) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of plaintext data", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (libssh2_blocking_read(session, payload, payload_len) < payload_len) {
|
||||
return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1;
|
||||
}
|
||||
while (padding_length) {
|
||||
int l;
|
||||
/* Flush padding */
|
||||
l = libssh2_blocking_read(session, buf, padding_length);
|
||||
if (l > 0)
|
||||
padding_length -= l;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
packet_type = payload[0];
|
||||
|
||||
/* MACs don't exist in non-encrypted mode */
|
||||
libssh2_packet_add(session, payload, payload_len, LIBSSH2_MAC_CONFIRMED);
|
||||
session->remote.seqno++;
|
||||
}
|
||||
return packet_type;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_packet_ask
|
||||
* Scan the brigade for a matching packet type, optionally poll the socket for a packet first
|
||||
* Scan the brigade for a matching packet type, optionally poll the socket for
|
||||
* a packet first
|
||||
*/
|
||||
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)
|
||||
{
|
||||
LIBSSH2_PACKET *packet = session->packets.head;
|
||||
|
||||
if (poll_socket) {
|
||||
if (libssh2_packet_read(session, 0) < 0) {
|
||||
return -1;
|
||||
libssh2pack_t rc = libssh2_packet_read(session);
|
||||
if ((rc < 0) && !packet) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
@ -991,7 +649,8 @@ int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, u
|
||||
while (packet) {
|
||||
if (packet->data[0] == packet_type &&
|
||||
(packet->data_len >= (match_ofs + match_len)) &&
|
||||
(!match_buf || (memcmp(packet->data + match_ofs, match_buf, match_len) == 0))) {
|
||||
(!match_buf ||
|
||||
(memcmp(packet->data + match_ofs, match_buf, match_len) == 0))) {
|
||||
*data = packet->data;
|
||||
*data_len = packet->data_len;
|
||||
|
||||
@ -1018,7 +677,8 @@ int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, u
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_packet_askv
|
||||
* Scan for any of a list of packet types in the brigade, optionally poll the socket for a packet first
|
||||
* Scan for any of a list of packet types in the brigade, optionally poll the
|
||||
* socket for a packet first
|
||||
*/
|
||||
int libssh2_packet_askv_ex(LIBSSH2_SESSION *session,
|
||||
unsigned char *packet_types,
|
||||
@ -1031,7 +691,10 @@ int libssh2_packet_askv_ex(LIBSSH2_SESSION *session,
|
||||
int i, packet_types_len = strlen((char *)packet_types);
|
||||
|
||||
for(i = 0; i < packet_types_len; i++) {
|
||||
if (0 == libssh2_packet_ask_ex(session, packet_types[i], data, data_len, match_ofs, match_buf, match_len, i ? 0 : poll_socket)) {
|
||||
if (0 == libssh2_packet_ask_ex(session, packet_types[i],
|
||||
data, data_len, match_ofs,
|
||||
match_buf, match_len,
|
||||
i ? 0 : poll_socket)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1040,31 +703,86 @@ int libssh2_packet_askv_ex(LIBSSH2_SESSION *session,
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ waitsocket
|
||||
* Returns
|
||||
* negative on error
|
||||
* >0 on incoming data
|
||||
* 0 on timeout
|
||||
*
|
||||
* FIXME: convert to use poll on systems that have it.
|
||||
*/
|
||||
int libssh2_waitsocket(LIBSSH2_SESSION *session,
|
||||
long seconds)
|
||||
{
|
||||
struct timeval timeout;
|
||||
int rc;
|
||||
fd_set fd;
|
||||
|
||||
timeout.tv_sec = seconds;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
FD_ZERO(&fd);
|
||||
|
||||
FD_SET(session->socket_fd, &fd);
|
||||
|
||||
rc = select(session->socket_fd+1, &fd, NULL, NULL, &timeout);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* {{{ libssh2_packet_require
|
||||
* [BLOCKING] for LIBSSH2_READ_TIMEOUT seconds
|
||||
* Loops libssh2_packet_read() until the packet requested is available
|
||||
* SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
|
||||
*
|
||||
* Returns negative on error
|
||||
* Returns 0 when it has taken care of the requested packet.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
if (libssh2_packet_ask_ex(session, packet_type, data, data_len, match_ofs, match_buf, match_len, 0) == 0) {
|
||||
time_t start = time(NULL);
|
||||
|
||||
if (libssh2_packet_ask_ex(session, packet_type, data, data_len,
|
||||
match_ofs, match_buf, match_len, 0) == 0) {
|
||||
/* A packet was available in the packet brigade */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet of type %d becomes available", (int)packet_type);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS,
|
||||
"Blocking until packet of type %d becomes available",
|
||||
(int)packet_type);
|
||||
#endif
|
||||
while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
|
||||
int ret = libssh2_packet_read(session, 1);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
libssh2pack_t ret = libssh2_packet_read(session);
|
||||
if ((ret < 0) && (ret != PACKET_EAGAIN)) {
|
||||
/* an error which is not just because of blocking */
|
||||
return ret;
|
||||
}
|
||||
if (ret == 0) continue;
|
||||
|
||||
if (packet_type == ret) {
|
||||
/* Be lazy, let packet_ask pull it out of the brigade */
|
||||
return libssh2_packet_ask_ex(session, packet_type, data, data_len, match_ofs, match_buf, match_len, 0);
|
||||
/* Be lazy, let packet_ask pull it out of the
|
||||
brigade */
|
||||
ret = libssh2_packet_ask_ex(session, packet_type,
|
||||
data, data_len, match_ofs,
|
||||
match_buf, match_len, 0);
|
||||
return ret;
|
||||
}
|
||||
else if (ret <= 0) {
|
||||
/* nothing available, wait until data arrives or
|
||||
we time out */
|
||||
long left = LIBSSH2_READ_TIMEOUT -
|
||||
(time(NULL) - start);
|
||||
|
||||
if((left <= 0) ||
|
||||
(libssh2_waitsocket(session, left) <= 0)) {
|
||||
return PACKET_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1085,7 +803,8 @@ int libssh2_packet_burn(LIBSSH2_SESSION *session)
|
||||
int i;
|
||||
for(i = 1; i < 256; i++) all_packets[i - 1] = i;
|
||||
|
||||
if (libssh2_packet_askv_ex(session, all_packets, &data, &data_len, 0, NULL, 0, 0) == 0) {
|
||||
if (libssh2_packet_askv_ex(session, all_packets, &data, &data_len, 0,
|
||||
NULL, 0, 0) == 0) {
|
||||
i = data[0];
|
||||
/* A packet was available in the packet brigade, burn it */
|
||||
LIBSSH2_FREE(session, data);
|
||||
@ -1096,14 +815,18 @@ int libssh2_packet_burn(LIBSSH2_SESSION *session)
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet becomes available to burn");
|
||||
#endif
|
||||
while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
|
||||
int ret = libssh2_packet_read(session, 1);
|
||||
int ret = libssh2_packet_read(session);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
if (ret == 0) {
|
||||
/* FIXME: this might busyloop */
|
||||
continue;
|
||||
}
|
||||
if (ret == 0) continue;
|
||||
|
||||
/* Be lazy, let packet_ask pull it out of the brigade */
|
||||
if (0 == libssh2_packet_ask_ex(session, ret, &data, &data_len, 0, NULL, 0, 0)) {
|
||||
if (0 == libssh2_packet_ask_ex(session, ret, &data, &data_len,
|
||||
0, NULL, 0, 0)) {
|
||||
/* Smoke 'em if you got 'em */
|
||||
LIBSSH2_FREE(session, data);
|
||||
return ret;
|
||||
@ -1116,30 +839,51 @@ int libssh2_packet_burn(LIBSSH2_SESSION *session)
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_packet_requirev
|
||||
* Loops libssh2_packet_read() until one of a list of packet types requested is available
|
||||
* [BLOCKING]
|
||||
* Loops libssh2_packet_read() until one of a list of packet types requested is
|
||||
* available
|
||||
* SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
|
||||
* packet_types is a null terminated list of packet_type numbers
|
||||
*/
|
||||
int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len,
|
||||
unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len)
|
||||
|
||||
int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session,
|
||||
unsigned char *packet_types,
|
||||
unsigned char **data, unsigned long *data_len,
|
||||
unsigned long match_ofs,
|
||||
const unsigned char *match_buf,
|
||||
unsigned long match_len)
|
||||
{
|
||||
if (libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0) == 0) {
|
||||
/* One of the packets listed was available in the packet brigade */
|
||||
time_t start = time(NULL);
|
||||
|
||||
if (libssh2_packet_askv_ex(session, packet_types, data, data_len,
|
||||
match_ofs, match_buf, match_len, 0) == 0) {
|
||||
/* One of the packets listed was available in the packet
|
||||
brigade */
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) {
|
||||
int ret = libssh2_packet_read(session, 1);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
int ret = libssh2_packet_read(session);
|
||||
if ((ret < 0) && (ret != PACKET_EAGAIN)) {
|
||||
return ret;
|
||||
}
|
||||
if (ret <= 0) {
|
||||
long left = LIBSSH2_READ_TIMEOUT -
|
||||
(time(NULL) - start);
|
||||
|
||||
if((left <= 0) ||
|
||||
(libssh2_waitsocket(session, left) <= 0 )) {
|
||||
return PACKET_TIMEOUT;
|
||||
}
|
||||
if (ret == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strchr((char *)packet_types, ret)) {
|
||||
/* Be lazy, let packet_ask pull it out of the brigade */
|
||||
return libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0);
|
||||
/* Be lazy, let packet_ask pull it out of the
|
||||
brigade */
|
||||
return libssh2_packet_askv_ex(session, packet_types,
|
||||
data, data_len,
|
||||
match_ofs, match_buf,
|
||||
match_len, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1147,135 +891,3 @@ int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_t
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_packet_write
|
||||
* Send a packet, encrypting it and adding a MAC code if necessary
|
||||
* Returns 0 on success, non-zero on failure
|
||||
*/
|
||||
int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len)
|
||||
{
|
||||
unsigned long packet_length = data_len + 1;
|
||||
unsigned long block_size = (session->state & LIBSSH2_STATE_NEWKEYS) ? session->local.crypt->blocksize : 8;
|
||||
/* At this point packet_length doesn't include the packet_len field itself */
|
||||
unsigned long padding_length;
|
||||
int free_data = 0;
|
||||
unsigned char buf[246]; /* 6 byte header plus max padding size(240) */
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
{
|
||||
/* Show a hint of what's being sent */
|
||||
char excerpt[32];
|
||||
int ex_len = 0, db_ofs = 0;
|
||||
|
||||
for (; ex_len < 24 && db_ofs < data_len; ex_len += 3, db_ofs++) snprintf(excerpt + ex_len, 4, "%02X ", data[db_ofs]);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet type %d, length=%lu, %s", (int)data[0], data_len, excerpt);
|
||||
}
|
||||
#endif
|
||||
if ((session->state & LIBSSH2_STATE_NEWKEYS) &&
|
||||
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 -1;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Compressed payload to %lu bytes", data_len);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
fcntl(session->socket_fd, F_SETFL, 0);
|
||||
#else
|
||||
{
|
||||
u_long non_block = FALSE;
|
||||
ioctlsocket(session->socket_fd, FIONBIO, &non_block);
|
||||
}
|
||||
#endif
|
||||
|
||||
packet_length = data_len + 1; /* padding_length(1) -- MAC doesn't count -- Padding to be added soon */
|
||||
padding_length = block_size - ((packet_length + 4) % block_size);
|
||||
if (padding_length < 4) {
|
||||
padding_length += block_size;
|
||||
}
|
||||
/* TODO: Maybe add 1 or 2 times block_size to padding_length randomly -- shake things up a bit... */
|
||||
|
||||
packet_length += padding_length;
|
||||
libssh2_htonu32(buf, packet_length);
|
||||
buf[4] = padding_length;
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet with total length %lu (%lu bytes padding)", packet_length, padding_length);
|
||||
#endif
|
||||
|
||||
if (session->state & LIBSSH2_STATE_NEWKEYS) {
|
||||
/* Encryption is in effect */
|
||||
unsigned char *encbuf, *s;
|
||||
int ret, size, written = 0;
|
||||
|
||||
/* include packet_length(4) itself and room for the hash at the end */
|
||||
encbuf = LIBSSH2_ALLOC(session, 4 + packet_length + session->local.mac->mac_len);
|
||||
if (!encbuf) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate encryption buffer", 0);
|
||||
if (free_data) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy packet to encoding buffer */
|
||||
memcpy(encbuf, buf, 5);
|
||||
memcpy(encbuf + 5, data, data_len);
|
||||
libssh2_random(encbuf + 5 + data_len, padding_length);
|
||||
if (free_data) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
}
|
||||
|
||||
/* Calculate MAC hash */
|
||||
session->local.mac->hash(session, encbuf + 4 + packet_length , session->local.seqno, encbuf, 4 + packet_length, NULL, 0, &session->local.mac_abstract);
|
||||
|
||||
/* Encrypt data */
|
||||
for(s = encbuf; (s - encbuf) < (4 + packet_length) ; s += session->local.crypt->blocksize) {
|
||||
session->local.crypt->crypt(session, s, &session->local.crypt_abstract);
|
||||
}
|
||||
|
||||
session->local.seqno++;
|
||||
|
||||
/* Send It */
|
||||
size = 4 + packet_length + session->local.mac->mac_len;
|
||||
written = 0;
|
||||
|
||||
while(written < size) {
|
||||
ret = send(session->socket_fd, encbuf + written, size - written, LIBSSH2_SOCKET_SEND_FLAGS(session));
|
||||
if(ret > 0) written += ret;
|
||||
else break;
|
||||
}
|
||||
|
||||
ret = written == size ? 0 : -1;
|
||||
|
||||
/* Cleanup environment */
|
||||
LIBSSH2_FREE(session, encbuf);
|
||||
|
||||
return ret;
|
||||
} else { /* LIBSSH2_ENDPOINT_CRYPT_NONE */
|
||||
/* Simplified write for non-encrypted mode */
|
||||
struct iovec data_vector[3];
|
||||
|
||||
/* Using vectors means we don't have to alloc a new buffer -- a byte saved is a byte earned
|
||||
* No MAC during unencrypted phase
|
||||
*/
|
||||
data_vector[0].iov_base = buf;
|
||||
data_vector[0].iov_len = 5;
|
||||
data_vector[1].iov_base = (char*)data;
|
||||
data_vector[1].iov_len = data_len;
|
||||
data_vector[2].iov_base = buf + 5;
|
||||
data_vector[2].iov_len = padding_length;
|
||||
|
||||
session->local.seqno++;
|
||||
|
||||
/* Ignore this, it can't actually happen :) */
|
||||
if (free_data) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
}
|
||||
|
||||
return ((packet_length + 4) == writev(session->socket_fd, data_vector, 3)) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
@ -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.
|
||||
*
|
||||
* 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 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);
|
||||
return -1;
|
||||
}
|
||||
@ -159,7 +159,7 @@ static int libssh2_publickey_packet_receive(LIBSSH2_PUBLICKEY *pkey, unsigned ch
|
||||
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_FREE(session, packet);
|
||||
return -1;
|
||||
|
19
src/scp.c
19
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.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
@ -42,9 +42,12 @@
|
||||
#define LIBSSH2_SCP_RESPONSE_BUFLEN 256
|
||||
|
||||
/* {{{ libssh2_scp_recv
|
||||
* [BLOCKING]
|
||||
* 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);
|
||||
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;
|
||||
while (sb && (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN)) {
|
||||
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 */
|
||||
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0);
|
||||
libssh2_channel_free(channel);
|
||||
@ -215,7 +220,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
|
||||
while (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) {
|
||||
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 */
|
||||
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0);
|
||||
libssh2_channel_free(channel);
|
||||
@ -379,7 +384,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const
|
||||
LIBSSH2_FREE(session, command);
|
||||
|
||||
/* 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_channel_free(channel);
|
||||
return NULL;
|
||||
@ -397,7 +402,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const
|
||||
return NULL;
|
||||
}
|
||||
/* 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_channel_free(channel);
|
||||
return NULL;
|
||||
@ -422,7 +427,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const
|
||||
return NULL;
|
||||
}
|
||||
/* 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_channel_free(channel);
|
||||
return NULL;
|
||||
|
113
src/session.c
113
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.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
@ -66,6 +66,7 @@
|
||||
*/
|
||||
static LIBSSH2_ALLOC_FUNC(libssh2_default_alloc)
|
||||
{
|
||||
(void)abstract;
|
||||
return malloc(count);
|
||||
}
|
||||
/* }}} */
|
||||
@ -74,6 +75,7 @@ static LIBSSH2_ALLOC_FUNC(libssh2_default_alloc)
|
||||
*/
|
||||
static LIBSSH2_FREE_FUNC(libssh2_default_free)
|
||||
{
|
||||
(void)abstract;
|
||||
free(ptr);
|
||||
}
|
||||
/* }}} */
|
||||
@ -82,6 +84,7 @@ static LIBSSH2_FREE_FUNC(libssh2_default_free)
|
||||
*/
|
||||
static LIBSSH2_REALLOC_FUNC(libssh2_default_realloc)
|
||||
{
|
||||
(void)abstract;
|
||||
return realloc(ptr, count);
|
||||
}
|
||||
/* }}} */
|
||||
@ -96,7 +99,7 @@ static int libssh2_banner_receive(LIBSSH2_SESSION *session)
|
||||
char banner[256];
|
||||
int banner_len = 0;
|
||||
|
||||
while ((banner_len < sizeof(banner)) &&
|
||||
while ((banner_len < (int)sizeof(banner)) &&
|
||||
((banner_len == 0) || (banner[banner_len-1] != '\n'))) {
|
||||
char c = '\0';
|
||||
int ret;
|
||||
@ -159,13 +162,13 @@ static int libssh2_banner_receive(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;
|
||||
|
||||
if (session->local.banner) {
|
||||
/* setopt_string will have given us our \r\n characters */
|
||||
banner_len = strlen(session->local.banner);
|
||||
banner = session->local.banner;
|
||||
banner_len = strlen((char *)session->local.banner);
|
||||
banner = (char *)session->local.banner;
|
||||
}
|
||||
#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);
|
||||
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;
|
||||
}
|
||||
|
||||
memcpy(session->local.banner, banner, banner_len);
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
session->local.banner[banner_len] = '\0';
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting local Banner: %s", session->local.banner);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS,
|
||||
"Setting local Banner: %s", session->local.banner);
|
||||
#endif
|
||||
session->local.banner[banner_len++] = '\r';
|
||||
session->local.banner[banner_len++] = '\n';
|
||||
@ -240,9 +245,12 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(
|
||||
LIBSSH2_REALLOC_FUNC((*local_realloc)) = libssh2_default_realloc;
|
||||
LIBSSH2_SESSION *session;
|
||||
|
||||
if (my_alloc) local_alloc = my_alloc;
|
||||
if (my_free) local_free = my_free;
|
||||
if (my_realloc) local_realloc = my_realloc;
|
||||
if (my_alloc)
|
||||
local_alloc = my_alloc;
|
||||
if (my_free)
|
||||
local_free = my_free;
|
||||
if (my_realloc)
|
||||
local_realloc = my_realloc;
|
||||
|
||||
session = local_alloc(sizeof(LIBSSH2_SESSION), abstract);
|
||||
memset(session, 0, sizeof(LIBSSH2_SESSION));
|
||||
@ -251,7 +259,8 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(
|
||||
session->realloc = local_realloc;
|
||||
session->abstract = abstract;
|
||||
#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
|
||||
|
||||
libssh2_crypto_init ();
|
||||
@ -264,7 +273,9 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(
|
||||
* Set (or reset) a callback function
|
||||
* 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;
|
||||
|
||||
@ -273,27 +284,27 @@ LIBSSH2_API void* libssh2_session_callback_set(LIBSSH2_SESSION *session, int cbt
|
||||
oldcb = session->ssh_msg_ignore;
|
||||
session->ssh_msg_ignore = callback;
|
||||
return oldcb;
|
||||
break;
|
||||
|
||||
case LIBSSH2_CALLBACK_DEBUG:
|
||||
oldcb = session->ssh_msg_debug;
|
||||
session->ssh_msg_debug = callback;
|
||||
return oldcb;
|
||||
break;
|
||||
|
||||
case LIBSSH2_CALLBACK_DISCONNECT:
|
||||
oldcb = session->ssh_msg_disconnect;
|
||||
session->ssh_msg_disconnect = callback;
|
||||
return oldcb;
|
||||
break;
|
||||
|
||||
case LIBSSH2_CALLBACK_MACERROR:
|
||||
oldcb = session->macerror;
|
||||
session->macerror = callback;
|
||||
return oldcb;
|
||||
break;
|
||||
|
||||
case LIBSSH2_CALLBACK_X11:
|
||||
oldcb = session->x11;
|
||||
session->x11 = callback;
|
||||
return oldcb;
|
||||
break;
|
||||
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_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
|
||||
* session: LIBSSH2_SESSION struct allocated and owned by the calling program
|
||||
* Returns: 0 on success, or non-zero on failure
|
||||
* Any memory allocated by libssh2 will use alloc/realloc/free callbacks in session
|
||||
* socket *must* be populated with an opened socket
|
||||
* Any memory allocated by libssh2 will use alloc/realloc/free
|
||||
* callbacks in session
|
||||
* socket *must* be populated with an opened and connected 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 char service[sizeof("ssh-userauth") + 5 - 1];
|
||||
unsigned long service_length;
|
||||
int rc;
|
||||
|
||||
#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
|
||||
/* FIXME: on some platforms (like win32) sockets are unsigned */
|
||||
if (socket < 0) {
|
||||
/* 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;
|
||||
}
|
||||
session->socket_fd = socket;
|
||||
@ -329,42 +345,52 @@ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
|
||||
/* TODO: Liveness check */
|
||||
if (libssh2_banner_send(session)) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (libssh2_banner_receive(session)) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (libssh2_kex_exchange(session, 0)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE, "Unable to exchange encryption keys", 0);
|
||||
rc = libssh2_kex_exchange(session, 0);
|
||||
if(rc) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE,
|
||||
"Unable to exchange encryption keys", 0);
|
||||
return LIBSSH2_ERROR_KEX_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Requesting userauth service");
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS,
|
||||
"Requesting userauth service");
|
||||
#endif
|
||||
/* Request the userauth service */
|
||||
service[0] = SSH_MSG_SERVICE_REQUEST;
|
||||
libssh2_htonu32(service + 1, sizeof("ssh-userauth") - 1);
|
||||
memcpy(service + 5, "ssh-userauth", sizeof("ssh-userauth") - 1);
|
||||
if (libssh2_packet_write(session, service, sizeof("ssh-userauth") + 5 - 1)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to ask for ssh-userauth service", 0);
|
||||
if (libssh2_packet_write(session, service,
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
service_length = libssh2_ntohu32(data + 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_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;
|
||||
}
|
||||
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
|
||||
* 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 */
|
||||
if (!session->err_code) {
|
||||
@ -630,7 +658,7 @@ LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errm
|
||||
**errmsg = 0;
|
||||
}
|
||||
} else {
|
||||
*errmsg = "";
|
||||
*errmsg = (char *)"";
|
||||
}
|
||||
}
|
||||
if (errmsg_len) {
|
||||
@ -640,7 +668,8 @@ LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errm
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
* Returns 0 if writing to channel would block,
|
||||
* non-0 if data can be written without blocking
|
||||
@ -730,13 +762,18 @@ inline int libssh2_poll_listener_queued(LIBSSH2_LISTENER *listener)
|
||||
/* {{{ libssh2_poll
|
||||
* 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;
|
||||
int i, active_fds;
|
||||
unsigned int i, active_fds;
|
||||
#ifdef HAVE_POLL
|
||||
LIBSSH2_SESSION *session = NULL;
|
||||
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 */
|
||||
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:
|
||||
if (sockets[i].events & POLLIN) {
|
||||
/* 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) {
|
||||
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:
|
||||
if (sockets[i].events & POLLIN) {
|
||||
/* 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) {
|
||||
fds[i].revents |= LIBSSH2_POLLFD_LISTENER_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
|
||||
|
303
src/sftp.c
303
src/sftp.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.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
@ -164,27 +164,17 @@ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block)
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Waiting for packet: %s block", should_block ? "will" : "willnot");
|
||||
#endif
|
||||
if (should_block) {
|
||||
libssh2_channel_set_blocking(channel, 1);
|
||||
if (4 != libssh2_channel_read(channel, buffer, 4)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for FXP packet", 0);
|
||||
libssh2_channel_set_blocking(channel, should_block);
|
||||
if (4 != _libssh2_channel_read(channel, (char *)buffer, 4)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
|
||||
"Timeout waiting for FXP packet", 0);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
libssh2_channel_set_blocking(channel, 0);
|
||||
if (1 != libssh2_channel_read(channel, buffer, 1)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for FXP packet", 0);
|
||||
return 0;
|
||||
}
|
||||
libssh2_channel_set_blocking(channel, 1);
|
||||
if (3 != libssh2_channel_read(channel, buffer + 1, 3)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for FXP packet", 0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
packet_len = libssh2_ntohu32(buffer);
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Data begin - Packet Length: %lu", packet_len);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP,
|
||||
"Data begin - Packet Length: %lu", packet_len);
|
||||
#endif
|
||||
if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "SFTP packet too large", 0);
|
||||
@ -199,7 +189,10 @@ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block)
|
||||
|
||||
packet_received = 0;
|
||||
while (packet_len > packet_received) {
|
||||
long bytes_received = libssh2_channel_read(channel, packet + packet_received, packet_len - packet_received);
|
||||
long bytes_received =
|
||||
_libssh2_channel_read(channel,
|
||||
(char *)packet + packet_received,
|
||||
packet_len - packet_received);
|
||||
|
||||
if (bytes_received < 0) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Receive error waiting for SFTP packet", 0);
|
||||
@ -246,7 +239,8 @@ static int libssh2_sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type
|
||||
}
|
||||
|
||||
while (packet) {
|
||||
if (strncmp(packet->data, match_buf, match_len) == 0) {
|
||||
if (strncmp((char *)packet->data, (char *)match_buf,
|
||||
match_len) == 0) {
|
||||
*data = packet->data;
|
||||
*data_len = packet->data_len;
|
||||
|
||||
@ -289,10 +283,9 @@ static int libssh2_sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_
|
||||
|
||||
while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
|
||||
int ret = libssh2_sftp_packet_read(sftp, 1);
|
||||
if (ret < 0) {
|
||||
if (ret <= 0) {
|
||||
return -1;
|
||||
}
|
||||
if (ret == 0) continue;
|
||||
|
||||
if (packet_type == ret) {
|
||||
/* Be lazy, let packet_ask pull it out of the brigade */
|
||||
@ -308,9 +301,15 @@ static int libssh2_sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_
|
||||
/* {{{ libssh2_sftp_packet_requirev
|
||||
* Requie one of N possible reponses
|
||||
*/
|
||||
static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_responses, unsigned char *valid_responses, unsigned long request_id, unsigned char **data, unsigned long *data_len)
|
||||
static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp,
|
||||
int num_valid_responses,
|
||||
unsigned char *valid_responses,
|
||||
unsigned long request_id,
|
||||
unsigned char **data,
|
||||
unsigned long *data_len)
|
||||
{
|
||||
int i;
|
||||
time_t start = time(NULL);
|
||||
|
||||
/* Flush */
|
||||
while (libssh2_sftp_packet_read(sftp, 0) > 0);
|
||||
@ -318,15 +317,28 @@ static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_respon
|
||||
while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
|
||||
int ret;
|
||||
for(i = 0; i < num_valid_responses; i++) {
|
||||
if (libssh2_sftp_packet_ask(sftp, valid_responses[i], request_id, data, data_len, 0) == 0) {
|
||||
if (libssh2_sftp_packet_ask(sftp, valid_responses[i],
|
||||
request_id, data,
|
||||
data_len, 0) == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret = libssh2_sftp_packet_read(sftp, 1);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (ret == 0) continue;
|
||||
if (ret == 0) {
|
||||
/* prevent busy-looping */
|
||||
long left = LIBSSH2_READ_TIMEOUT -
|
||||
(time(NULL) - start);
|
||||
|
||||
if((left <= 0) ||
|
||||
(libssh2_waitsocket(sftp->channel->session,
|
||||
left)<=0)) {
|
||||
return PACKET_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -427,12 +439,18 @@ static int libssh2_sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES *attrs, unsigned char *
|
||||
* SFTP API *
|
||||
************ */
|
||||
|
||||
LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor);
|
||||
|
||||
/* {{{ libssh2_sftp_dtor
|
||||
* Shutdown an SFTP stream when the channel closes
|
||||
*/
|
||||
LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor) {
|
||||
LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor)
|
||||
{
|
||||
LIBSSH2_SFTP *sftp = (LIBSSH2_SFTP*)(*channel_abstract);
|
||||
|
||||
(void)session_abstract;
|
||||
(void)channel;
|
||||
|
||||
/* Loop through handles closing them */
|
||||
while (sftp->handles) {
|
||||
libssh2_sftp_close_handle(sftp->handles);
|
||||
@ -460,6 +478,7 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (libssh2_channel_subsystem(channel, "sftp")) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request SFTP subsystem", 0);
|
||||
libssh2_channel_free(channel);
|
||||
@ -487,15 +506,18 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending FXP_INIT packet advertising version %d support", (int)LIBSSH2_SFTP_VERSION);
|
||||
#endif
|
||||
if (9 != libssh2_channel_write(channel, buffer, 9)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SSH_FXP_INIT", 0);
|
||||
if (9 != libssh2_channel_write(channel, (char *)buffer, 9)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
||||
"Unable to send SSH_FXP_INIT", 0);
|
||||
libssh2_channel_free(channel);
|
||||
LIBSSH2_FREE(session, sftp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (libssh2_sftp_packet_require(sftp, SSH_FXP_VERSION, 0, &data, &data_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from SFTP subsystem", 0);
|
||||
if (libssh2_sftp_packet_require(sftp, SSH_FXP_VERSION, 0,
|
||||
&data, &data_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
|
||||
"Timeout waiting for response from SFTP subsystem", 0);
|
||||
libssh2_channel_free(channel);
|
||||
LIBSSH2_FREE(session, sftp);
|
||||
return NULL;
|
||||
@ -519,7 +541,7 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Enabling SFTP version %lu compatability", sftp->version);
|
||||
#endif
|
||||
while (s < (data + data_len)) {
|
||||
char *extension_name, *extension_data;
|
||||
unsigned char *extension_name, *extension_data;
|
||||
unsigned long extname_len, extdata_len;
|
||||
|
||||
extname_len = libssh2_ntohu32(s); s += 4;
|
||||
@ -543,7 +565,8 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
|
||||
/* {{{ libssh2_sftp_shutdown
|
||||
* Shutsdown the SFTP subsystem
|
||||
*/
|
||||
LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp) {
|
||||
LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp)
|
||||
{
|
||||
return libssh2_channel_free(sftp->channel);
|
||||
}
|
||||
/* }}} */
|
||||
@ -560,8 +583,11 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
LIBSSH2_SFTP_HANDLE *fp;
|
||||
LIBSSH2_SFTP_ATTRIBUTES attrs = { LIBSSH2_SFTP_ATTR_PERMISSIONS };
|
||||
unsigned long data_len, packet_len = filename_len + 13 + ((open_type == LIBSSH2_SFTP_OPENFILE) ? (4 + libssh2_sftp_attrsize(&attrs)) : 0);
|
||||
LIBSSH2_SFTP_ATTRIBUTES attrs = {
|
||||
LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
unsigned long data_len;
|
||||
ssize_t packet_len = filename_len + 13 + ((open_type == LIBSSH2_SFTP_OPENFILE) ? (4 + libssh2_sftp_attrsize(&attrs)) : 0);
|
||||
/* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) + flags(4) */
|
||||
unsigned char *packet, *data, *s;
|
||||
unsigned char fopen_responses[2] = { SSH_FXP_HANDLE, SSH_FXP_STATUS };
|
||||
@ -589,7 +615,8 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending %s open request", (open_type == LIBSSH2_SFTP_OPENFILE) ? "file" : "directory");
|
||||
#endif
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
if (packet_len != _libssh2_channel_write(channel, (char *)packet,
|
||||
packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_OPEN or FXP_OPENDIR command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return NULL;
|
||||
@ -648,16 +675,18 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_sftp_read
|
||||
* Read from an SFTP file handle
|
||||
/* {{{ _libssh2_sftp_read
|
||||
* Read from an SFTP file handle blocking/non-blocking depending on state
|
||||
*/
|
||||
LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen)
|
||||
|
||||
static ssize_t _libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle,
|
||||
char *buffer, size_t buffer_maxlen)
|
||||
{
|
||||
LIBSSH2_SFTP *sftp = handle->sftp;
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned long data_len, request_id;
|
||||
unsigned long packet_len = handle->handle_len + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + length(4) */
|
||||
ssize_t packet_len = handle->handle_len + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + length(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
unsigned char read_responses[2] = { SSH_FXP_DATA, SSH_FXP_STATUS };
|
||||
size_t bytes_read = 0;
|
||||
@ -671,31 +700,45 @@ LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
||||
return -1;
|
||||
}
|
||||
|
||||
libssh2_htonu32(s, packet_len - 4); s += 4;
|
||||
libssh2_htonu32(s, packet_len - 4);
|
||||
s += 4;
|
||||
*(s++) = SSH_FXP_READ;
|
||||
request_id = sftp->request_id++;
|
||||
libssh2_htonu32(s, request_id); s += 4;
|
||||
libssh2_htonu32(s, handle->handle_len); s += 4;
|
||||
memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len;
|
||||
libssh2_htonu64(s, handle->u.file.offset); s += 8;
|
||||
libssh2_htonu32(s, buffer_maxlen); s += 4;
|
||||
libssh2_htonu32(s, request_id);
|
||||
s += 4;
|
||||
libssh2_htonu32(s, handle->handle_len);
|
||||
s += 4;
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0);
|
||||
memcpy(s, handle->handle, handle->handle_len);
|
||||
s += handle->handle_len;
|
||||
|
||||
libssh2_htonu64(s, handle->u.file.offset);
|
||||
s += 8;
|
||||
|
||||
libssh2_htonu32(s, buffer_maxlen);
|
||||
s += 4;
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, (char *)packet,
|
||||
packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
||||
"Unable to send FXP_READ command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
|
||||
if (libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id, &data, &data_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0);
|
||||
if (libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id,
|
||||
&data, &data_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
|
||||
"Timeout waiting for status message", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (data[0]) {
|
||||
case SSH_FXP_STATUS:
|
||||
sftp->last_errno = libssh2_ntohu32(data + 5);
|
||||
libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0);
|
||||
libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
||||
"SFTP Protocol Error", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return -1;
|
||||
case SSH_FXP_DATA:
|
||||
@ -704,7 +747,9 @@ LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
||||
return -1;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu bytes returned", (unsigned long)bytes_read);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP,
|
||||
"%lu bytes returned",
|
||||
(unsigned long)bytes_read);
|
||||
#endif
|
||||
memcpy(buffer, data + 9, bytes_read);
|
||||
handle->u.file.offset += bytes_read;
|
||||
@ -714,6 +759,54 @@ LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
||||
|
||||
return -1;
|
||||
}
|
||||
/* {{{ libssh2_sftp_read
|
||||
* Read from an SFTP file handle blocking
|
||||
*/
|
||||
LIBSSH2_API ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle,
|
||||
char *buffer, size_t buffer_maxlen)
|
||||
{
|
||||
ssize_t rc;
|
||||
LIBSSH2_CHANNEL *ch = handle->sftp->channel;
|
||||
int bl = libssh2_channel_get_blocking(ch);
|
||||
|
||||
/* set blocking */
|
||||
libssh2_channel_set_blocking(ch, 1);
|
||||
|
||||
rc = _libssh2_sftp_read(handle, buffer, buffer_maxlen);
|
||||
|
||||
/* restore state */
|
||||
libssh2_channel_set_blocking(ch, bl);
|
||||
|
||||
if(rc < 0) {
|
||||
/* precent accidental returning of other return codes since
|
||||
this API does not support/provide those */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_sftp_readnb
|
||||
* Read from an SFTP file handle non-blocking
|
||||
*/
|
||||
LIBSSH2_API ssize_t libssh2_sftp_readnb(LIBSSH2_SFTP_HANDLE *handle,
|
||||
char *buffer, size_t buffer_maxlen)
|
||||
{
|
||||
ssize_t rc;
|
||||
LIBSSH2_CHANNEL *ch = handle->sftp->channel;
|
||||
int bl = libssh2_channel_get_blocking(ch);
|
||||
|
||||
/* set non-blocking */
|
||||
libssh2_channel_set_blocking(ch, 0);
|
||||
|
||||
rc = _libssh2_sftp_read(handle, buffer, buffer_maxlen);
|
||||
|
||||
/* restore state */
|
||||
libssh2_channel_set_blocking(ch, bl);
|
||||
|
||||
return rc;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_sftp_readdir
|
||||
@ -726,13 +819,13 @@ LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
LIBSSH2_SFTP_ATTRIBUTES attrs_dummy;
|
||||
unsigned long data_len, request_id, filename_len, num_names;
|
||||
unsigned long packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
|
||||
ssize_t packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
unsigned char read_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS };
|
||||
|
||||
if (handle->u.dir.names_left) {
|
||||
/* A prior request returned more than one directory entry, feed it back from the buffer */
|
||||
unsigned char *s = handle->u.dir.next_name;
|
||||
unsigned char *s = (unsigned char *)handle->u.dir.next_name;
|
||||
unsigned long real_filename_len = libssh2_ntohu32(s);
|
||||
|
||||
filename_len = real_filename_len; s += 4;
|
||||
@ -754,7 +847,7 @@ LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
||||
}
|
||||
s += libssh2_sftp_bin2attr(attrs ? attrs : &attrs_dummy, s);
|
||||
|
||||
handle->u.dir.next_name = s;
|
||||
handle->u.dir.next_name = (char *)s;
|
||||
if ((--handle->u.dir.names_left) == 0) {
|
||||
LIBSSH2_FREE(session, handle->u.dir.names_packet);
|
||||
}
|
||||
@ -780,7 +873,8 @@ LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading entries from directory handle");
|
||||
#endif
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
if (packet_len != libssh2_channel_write(channel, (char *)packet,
|
||||
packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
@ -840,23 +934,25 @@ LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
||||
|
||||
handle->u.dir.names_left = num_names;
|
||||
handle->u.dir.names_packet = data;
|
||||
handle->u.dir.next_name = data + 9;
|
||||
handle->u.dir.next_name = (char *)data + 9;
|
||||
|
||||
/* Be lazy, just use the name popping mechanism from the start of the function */
|
||||
return libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_sftp_write
|
||||
/* {{{ _libssh2_sftp_write
|
||||
* Write data to a file handle
|
||||
*/
|
||||
LIBSSH2_API size_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count)
|
||||
static ssize_t _libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle,
|
||||
const char *buffer,
|
||||
size_t count)
|
||||
{
|
||||
LIBSSH2_SFTP *sftp = handle->sftp;
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned long data_len, request_id, retcode;
|
||||
unsigned long packet_len = handle->handle_len + count + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + count(4) */
|
||||
ssize_t packet_len = handle->handle_len + count + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + count(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
@ -878,7 +974,7 @@ LIBSSH2_API size_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *b
|
||||
libssh2_htonu32(s, count); s += 4;
|
||||
memcpy(s, buffer, count); s += count;
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_WRITE command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
@ -904,6 +1000,50 @@ LIBSSH2_API size_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *b
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_sftp_write
|
||||
* Write data to a SFTP handle blocking
|
||||
*/
|
||||
LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle,
|
||||
const char *buffer, size_t count)
|
||||
{
|
||||
ssize_t rc;
|
||||
LIBSSH2_CHANNEL *ch = handle->sftp->channel;
|
||||
int bl = libssh2_channel_get_blocking(ch);
|
||||
|
||||
/* set blocking */
|
||||
libssh2_channel_set_blocking(ch, 1);
|
||||
|
||||
rc = _libssh2_sftp_write(handle, buffer, count);
|
||||
|
||||
/* restore state */
|
||||
libssh2_channel_set_blocking(ch, bl);
|
||||
|
||||
return rc;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_sftp_write
|
||||
* Write data to a SFTP handle non-blocking
|
||||
*/
|
||||
LIBSSH2_API ssize_t libssh2_sftp_writenb(LIBSSH2_SFTP_HANDLE *handle,
|
||||
const char *buffer, size_t count)
|
||||
{
|
||||
ssize_t rc;
|
||||
LIBSSH2_CHANNEL *ch = handle->sftp->channel;
|
||||
int bl = libssh2_channel_get_blocking(ch);
|
||||
|
||||
/* set non-blocking */
|
||||
libssh2_channel_set_blocking(ch, 0);
|
||||
|
||||
rc = _libssh2_sftp_write(handle, buffer, count);
|
||||
|
||||
/* restore state */
|
||||
libssh2_channel_set_blocking(ch, bl);
|
||||
|
||||
return rc;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_sftp_fstat_ex
|
||||
* Get or Set stat on a file
|
||||
*/
|
||||
@ -913,7 +1053,7 @@ LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned long data_len, request_id;
|
||||
unsigned long packet_len = handle->handle_len + 13 + (setstat ? libssh2_sftp_attrsize(attrs) : 0);
|
||||
ssize_t packet_len = handle->handle_len + 13 + (setstat ? libssh2_sftp_attrsize(attrs) : 0);
|
||||
/* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
unsigned char fstat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS };
|
||||
@ -937,8 +1077,8 @@ LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_
|
||||
s += libssh2_sftp_attr2bin(s, attrs);
|
||||
}
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, setstat ? "Unable to send FXP_FSETSTAT" : "Unable to send FXP_FSTAT command", 0);
|
||||
if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, setstat ? (char *)"Unable to send FXP_FSETSTAT" : (char *)"Unable to send FXP_FSTAT command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
@ -998,7 +1138,7 @@ LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle)
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned long data_len, retcode, request_id;
|
||||
unsigned long packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
|
||||
ssize_t packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
@ -1017,7 +1157,7 @@ LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle)
|
||||
libssh2_htonu32(s, handle->handle_len); s += 4;
|
||||
memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len;
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_CLOSE command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
@ -1069,7 +1209,7 @@ LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, char *filename, unsig
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned long data_len, retcode, request_id;
|
||||
unsigned long packet_len = filename_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */
|
||||
ssize_t packet_len = filename_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
@ -1088,7 +1228,8 @@ LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, char *filename, unsig
|
||||
libssh2_htonu32(s, filename_len); s += 4;
|
||||
memcpy(s, filename, filename_len); s += filename_len;
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
if (packet_len != libssh2_channel_write(channel, (char *)packet,
|
||||
packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_REMOVE command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
@ -1123,7 +1264,7 @@ LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, char *source_filenam
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned long data_len, retcode = -1, request_id;
|
||||
unsigned long packet_len = source_filename_len + dest_filename_len + 17 + (sftp->version >= 5 ? 4 : 0);
|
||||
ssize_t packet_len = source_filename_len + dest_filename_len + 17 + (sftp->version >= 5 ? 4 : 0);
|
||||
/* packet_len(4) + packet_type(1) + request_id(4) +
|
||||
source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */
|
||||
unsigned char *packet, *s, *data;
|
||||
@ -1155,7 +1296,8 @@ LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, char *source_filenam
|
||||
libssh2_htonu32(s, flags); s += 4;
|
||||
}
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, s - packet)) {
|
||||
if (packet_len != libssh2_channel_write(channel, (char *)packet,
|
||||
s - packet)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_RENAME command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
@ -1201,9 +1343,11 @@ LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, char *path, unsigned i
|
||||
{
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
LIBSSH2_SFTP_ATTRIBUTES attrs = { LIBSSH2_SFTP_ATTR_PERMISSIONS };
|
||||
LIBSSH2_SFTP_ATTRIBUTES attrs = {
|
||||
LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
unsigned long data_len, retcode, request_id;
|
||||
unsigned long packet_len = path_len + 13 + libssh2_sftp_attrsize(&attrs);
|
||||
ssize_t packet_len = path_len + 13 + libssh2_sftp_attrsize(&attrs);
|
||||
/* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
|
||||
@ -1226,7 +1370,8 @@ LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, char *path, unsigned i
|
||||
memcpy(s, path, path_len); s += path_len;
|
||||
s += libssh2_sftp_attr2bin(s, &attrs);
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
if (packet_len != libssh2_channel_write(channel, (char *)packet,
|
||||
packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_MKDIR command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
@ -1259,7 +1404,7 @@ LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, char *path, unsigned i
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned long data_len, retcode, request_id;
|
||||
unsigned long packet_len = path_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
|
||||
ssize_t packet_len = path_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
@ -1278,7 +1423,7 @@ LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, char *path, unsigned i
|
||||
libssh2_htonu32(s, path_len); s += 4;
|
||||
memcpy(s, path, path_len); s += path_len;
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_MKDIR command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
@ -1311,7 +1456,7 @@ LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, char *path, unsigned in
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned long data_len, request_id;
|
||||
unsigned long packet_len = path_len + 13 + ((stat_type == LIBSSH2_SFTP_SETSTAT) ? libssh2_sftp_attrsize(attrs) : 0);
|
||||
ssize_t packet_len = path_len + 13 + ((stat_type == LIBSSH2_SFTP_SETSTAT) ? libssh2_sftp_attrsize(attrs) : 0);
|
||||
/* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
unsigned char stat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS };
|
||||
@ -1345,7 +1490,8 @@ LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, char *path, unsigned in
|
||||
s += libssh2_sftp_attr2bin(s, attrs);
|
||||
}
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
if (packet_len != libssh2_channel_write(channel, (char *)packet,
|
||||
packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send STAT/LSTAT/SETSTAT command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
@ -1387,7 +1533,7 @@ LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, un
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned long data_len, request_id, link_len;
|
||||
unsigned long packet_len = path_len + 13 + ((link_type == LIBSSH2_SFTP_SYMLINK) ? (4 + target_len) : 0);
|
||||
ssize_t packet_len = path_len + 13 + ((link_type == LIBSSH2_SFTP_SYMLINK) ? (4 + target_len) : 0);
|
||||
/* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
unsigned char link_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS };
|
||||
@ -1429,7 +1575,8 @@ LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, un
|
||||
memcpy(s, target, target_len); s += target_len;
|
||||
}
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
if (packet_len != libssh2_channel_write(channel, (char *)packet,
|
||||
packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SYMLINK/READLINK command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
|
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