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:
Daniel Stenberg 2007-02-02 16:21:20 +00:00
parent c63ef86075
commit 9d55db6501
22 changed files with 3632 additions and 2388 deletions

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,2 @@
AUTOMAKE_OPTIONS = foreign nostdinc
SUBDIRS = simple

View 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

View 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;
}

View File

@ -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);

View 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;
}

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -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);

View File

@ -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;
}
}
/* }}} */

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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
View 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

View File

@ -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;
}