1
1

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.
Этот коммит содержится в:
Daniel Stenberg 2007-02-02 16:21:20 +00:00
родитель c63ef86075
Коммит 9d55db6501
22 изменённых файлов: 3632 добавлений и 2388 удалений

Просмотреть файл

@ -1,19 +1,10 @@
AUTOMAKE_OPTIONS = foreign nostdinc
SUBDIRS = src tests
SUBDIRS = src example tests
include_HEADERS = include/libssh2.h include/libssh2_publickey.h \
include/libssh2_sftp.h
# and a sample tool
noinst_PROGRAMS = ssh2_sample
INCLUDES = -I$(top_srcdir)/include
ssh2_sample_SOURCES = ssh2_sample.c
ssh2_sample_LDADD = src/libssh2.la
EXTRA_DIST = LICENSE win32
ACLOCAL_AMFLAGS = -I m4

Просмотреть файл

@ -224,7 +224,7 @@ AC_ARG_ENABLE(debug-errors,
AC_HELP_STRING([--enable-debug-errors],[Output failure events to stderr]),
[AC_DEFINE(LIBSSH2_DEBUG_ERRORS, 1, [Output failure events to stderr])])
AC_ARG_ENABLE(debug-all,
AC_HELP_STRING([--enable-debug-all],[Output debugging info for all layers to stderr]),
AC_HELP_STRING([--enable-debug],[Enable debug]),
[
AC_DEFINE(LIBSSH2_DEBUG_TRANSPORT, 1, [Output transport layer debugging info to stderr])
AC_DEFINE(LIBSSH2_DEBUG_KEX, 1, [Output Key Exchange debugging info to stderr])
@ -273,5 +273,7 @@ AC_C_INLINE
AC_CONFIG_FILES([Makefile
src/Makefile
tests/Makefile])
tests/Makefile
example/Makefile
example/simple/Makefile])
AC_OUTPUT

2
example/Makefile.am Обычный файл
Просмотреть файл

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

17
example/simple/Makefile.am Обычный файл
Просмотреть файл

@ -0,0 +1,17 @@
AUTOMAKE_OPTIONS = foreign nostdinc
# samples
noinst_PROGRAMS = ssh2 scp_nonblock sftp_nonblock sftp scp
INCLUDES = -I$(top_srcdir)/include
LDADD = $(top_builddir)/src/libssh2.la
ssh2_SOURCES = ssh2.c
sftp_SOURCES = sftp.c
scp_nonblock_SOURCES = scp_nonblock.c
sftp_nonblock_SOURCES = sftp_nonblock.c
scp_SOURCES = scp.c

193
example/simple/scp_nonblock.c Обычный файл
Просмотреть файл

@ -0,0 +1,193 @@
/*
* $Id: scp_nonblock.c,v 1.1 2007/02/02 16:21:20 bagder Exp $
*
* Sample showing how to do SCP transfers in a non-blocking manner.
*/
#include <libssh2.h>
#ifndef WIN32
# include <netinet/in.h>
# include <sys/socket.h>
# include <unistd.h>
#else
# include <winsock2.h>
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
int sock, i, auth_pw = 1;
struct sockaddr_in sin;
const char *fingerprint;
LIBSSH2_SESSION *session;
LIBSSH2_CHANNEL *channel;
char *username=(char *)"username";
char *password=(char *)"password";
char *scppath=(char *)"/tmp/TEST";
struct stat fileinfo;
int rc;
off_t got=0;
#ifdef WIN32
WSADATA wsadata;
WSAStartup(WINSOCK_VERSION, &wsadata);
#endif
/* Ultra basic "connect to port 22 on localhost"
* Your code is responsible for creating the socket establishing the
* connection
*/
sock = socket(AF_INET, SOCK_STREAM, 0);
sin.sin_family = AF_INET;
sin.sin_port = htons(22);
sin.sin_addr.s_addr = htonl(0x7F000001);
if (connect(sock, (struct sockaddr*)(&sin),
sizeof(struct sockaddr_in)) != 0) {
fprintf(stderr, "failed to connect!\n");
return -1;
}
/* We set the socket non-blocking. We do it after the connect just to
simplify the example code. */
#ifdef F_SETFL
/* FIXME: this can/should be done in a more portable manner */
rc = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, rc | O_NONBLOCK);
#else
#error "add support for setting the socket non-blocking here"
#endif
/* Create a session instance
*/
session = libssh2_session_init();
if(!session)
return -1;
/* ... start it up. This will trade welcome banners, exchange keys,
* and setup crypto, compression, and MAC layers
*/
rc = libssh2_session_startup(session, sock);
if(rc) {
fprintf(stderr, "Failure establishing SSH session: %d\n", rc);
return -1;
}
/* At this point we havn't yet authenticated. The first thing to do
* is check the hostkey's fingerprint against our known hosts Your app
* may have it hard coded, may go to a file, may present it to the
* user, that's your call
*/
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
printf("Fingerprint: ");
for(i = 0; i < 16; i++) {
printf("%02X ", (unsigned char)fingerprint[i]);
}
printf("\n");
if(argc > 1) {
username = argv[1];
}
if(argc > 2) {
password = argv[2];
}
if(argc > 3) {
scppath = argv[3];
}
if (auth_pw) {
/* We could authenticate via password */
if (libssh2_userauth_password(session, username, password)) {
printf("Authentication by password failed.\n");
goto shutdown;
}
} else {
/* Or by public key */
if (libssh2_userauth_publickey_fromfile(session, username,
"/home/username/.ssh/id_rsa.pub",
"/home/username/.ssh/id_rsa",
password)) {
printf("\tAuthentication by public key failed\n");
goto shutdown;
}
}
/* Request a file via SCP */
channel = libssh2_scp_recv(session, scppath, &fileinfo);
if (!channel) {
fprintf(stderr, "Unable to open a session\n");
goto shutdown;
}
fprintf(stderr, "libssh2_scp_recv() is done, now receive data!\n");
while(got < fileinfo.st_size) {
char mem[1000];
struct timeval timeout;
int rc;
fd_set fd;
do {
int amount=sizeof(mem);
if((fileinfo.st_size -got) < amount) {
amount = fileinfo.st_size -got;
}
/* loop until we block */
rc = libssh2_channel_readnb(channel, mem, amount);
if(rc > 0) {
write(2, mem, rc);
got += rc;
}
} while (rc > 0);
if(rc == LIBSSH2CHANNEL_EAGAIN) {
/* this is due to blocking that would occur otherwise
so we loop on this condition */
timeout.tv_sec = 10;
timeout.tv_usec = 0;
FD_ZERO(&fd);
FD_SET(sock, &fd);
rc = select(sock+1, &fd, &fd, NULL, &timeout);
if(rc <= 0) {
/* negative is error
0 is timeout */
fprintf(stderr, "SCP timed out: %d\n", rc);
}
continue;
}
break;
}
libssh2_channel_free(channel);
channel = NULL;
shutdown:
libssh2_session_disconnect(session,
"Normal Shutdown, Thank you for playing");
libssh2_session_free(session);
#ifdef WIN32
Sleep(1000);
closesocket(sock);
#else
sleep(1);
close(sock);
#endif
printf("all done\n");
return 0;
}

Просмотреть файл

@ -1,5 +1,5 @@
/*
* $Id: sftp.c,v 1.1 2007/01/24 14:15:36 bagder Exp $
* $Id: sftp.c,v 1.2 2007/02/02 16:21:20 bagder Exp $
*
* Sample showing how to do SFTP transfers.
*/
@ -109,6 +109,7 @@ int main(int argc, char *argv[])
}
}
fprintf(stderr, "libssh2_sftp_init()!\n");
sftp_session = libssh2_sftp_init(session);
if (!sftp_session) {
@ -116,6 +117,7 @@ int main(int argc, char *argv[])
goto shutdown;
}
fprintf(stderr, "libssh2_sftp_open()!\n");
/* Request a file via SFTP */
sftp_handle =
libssh2_sftp_open(sftp_session, sftppath, LIBSSH2_FXF_READ, 0);
@ -129,6 +131,7 @@ int main(int argc, char *argv[])
char mem[512];
/* loop until we fail */
fprintf(stderr, "libssh2_sftp_read()!\n");
rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem));
if(rc > 0) {
write(2, mem, rc);

281
example/simple/sftp_nonblock.c Обычный файл
Просмотреть файл

@ -0,0 +1,281 @@
/*
* $Id: sftp_nonblock.c,v 1.1 2007/02/02 16:21:20 bagder Exp $
*
* Sample showing how to do SFTP transfers in a non-blocking manner.
*
* It will first download a given source file, store it locally and then
* upload the file again to a given destination file.
*
* Using the SFTP server running on 127.0.0.1
*/
#include <libssh2.h>
#include <libssh2_sftp.h>
#ifndef WIN32
# include <netinet/in.h>
# include <sys/socket.h>
# include <unistd.h>
#else
# include <winsock2.h>
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#define STORAGE "/tmp/sftp-storage" /* this is the local file name this
example uses to store the downloaded
file in */
int main(int argc, char *argv[])
{
int sock, i, auth_pw = 1;
struct sockaddr_in sin;
const char *fingerprint;
LIBSSH2_SESSION *session;
char *username=(char *)"username";
char *password=(char *)"password";
char *sftppath=(char *)"/tmp/TEST"; /* source path */
char *dest=(char *)"/tmp/TEST2"; /* destination path */
int rc;
LIBSSH2_SFTP *sftp_session;
LIBSSH2_SFTP_HANDLE *sftp_handle;
FILE *tempstorage;
char mem[1000];
struct timeval timeout;
fd_set fd;
#ifdef WIN32
WSADATA wsadata;
WSAStartup(WINSOCK_VERSION, &wsadata);
#endif
/* Ultra basic "connect to port 22 on localhost"
* The application is responsible for creating the socket establishing
* the connection
*/
sock = socket(AF_INET, SOCK_STREAM, 0);
sin.sin_family = AF_INET;
sin.sin_port = htons(22);
sin.sin_addr.s_addr = htonl(0x7F000001);
if (connect(sock, (struct sockaddr*)(&sin),
sizeof(struct sockaddr_in)) != 0) {
fprintf(stderr, "failed to connect!\n");
return -1;
}
/* We set the socket non-blocking. We do it after the connect just to
simplify the example code. */
#ifdef F_SETFL
/* FIXME: this can/should be done in a more portable manner */
rc = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, rc | O_NONBLOCK);
#else
#error "add support for setting the socket non-blocking here"
#endif
/* Create a session instance
*/
session = libssh2_session_init();
if(!session)
return -1;
/* ... start it up. This will trade welcome banners, exchange keys,
* and setup crypto, compression, and MAC layers
*/
rc = libssh2_session_startup(session, sock);
if(rc) {
fprintf(stderr, "Failure establishing SSH session: %d\n", rc);
return -1;
}
/* At this point we havn't yet authenticated. The first thing to do
* is check the hostkey's fingerprint against our known hosts Your app
* may have it hard coded, may go to a file, may present it to the
* user, that's your call
*/
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
printf("Fingerprint: ");
for(i = 0; i < 16; i++) {
printf("%02X ", (unsigned char)fingerprint[i]);
}
printf("\n");
if(argc > 1) {
username = argv[1];
}
if(argc > 2) {
password = argv[2];
}
if(argc > 3) {
sftppath = argv[3];
}
if(argc > 4) {
dest = argv[4];
}
tempstorage = fopen(STORAGE, "wb");
if(!tempstorage) {
printf("Can't open temp storage file %s\n", STORAGE);
goto shutdown;
}
if (auth_pw) {
/* We could authenticate via password */
if (libssh2_userauth_password(session, username, password)) {
printf("Authentication by password failed.\n");
goto shutdown;
}
} else {
/* Or by public key */
if (libssh2_userauth_publickey_fromfile(session, username,
"/home/username/.ssh/id_rsa.pub",
"/home/username/.ssh/id_rsa",
password)) {
printf("\tAuthentication by public key failed\n");
goto shutdown;
}
}
sftp_session = libssh2_sftp_init(session);
if (!sftp_session) {
fprintf(stderr, "Unable to init SFTP session\n");
goto shutdown;
}
/* Request a file via SFTP */
sftp_handle =
libssh2_sftp_open(sftp_session, sftppath, LIBSSH2_FXF_READ, 0);
if (!sftp_handle) {
fprintf(stderr, "Unable to open file with SFTP\n");
goto shutdown;
}
fprintf(stderr, "libssh2_sftp_open() is done, now receive data!\n");
do {
do {
/* read in a loop until we block */
rc = libssh2_sftp_readnb(sftp_handle, mem,
sizeof(mem));
fprintf(stderr, "libssh2_sftp_read returned %d\n",
rc);
if(rc > 0) {
/* write to stderr */
write(2, mem, rc);
/* write to temporary storage area */
fwrite(mem, rc, 1, tempstorage);
}
} while (rc > 0);
if(rc != LIBSSH2SFTP_EAGAIN) {
/* error or end of file */
break;
}
timeout.tv_sec = 10;
timeout.tv_usec = 0;
FD_ZERO(&fd);
FD_SET(sock, &fd);
/* wait for readable or writeable */
rc = select(sock+1, &fd, &fd, NULL, &timeout);
if(rc <= 0) {
/* negative is error
0 is timeout */
fprintf(stderr, "SFTP download timed out: %d\n", rc);
break;
}
} while (1);
libssh2_sftp_close(sftp_handle);
fclose(tempstorage);
tempstorage = fopen(STORAGE, "rb");
if(!tempstorage) {
/* weird, we can't read the file we just wrote to... */
fprintf(stderr, "can't open %s for reading\n", STORAGE);
goto shutdown;
}
/* we're done downloading, now reverse the process and upload the
temporarily stored data to the destination path */
sftp_handle =
libssh2_sftp_open(sftp_session, dest,
LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT,
LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|
LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH);
if(sftp_handle) {
size_t nread;
char *ptr;
do {
nread = fread(mem, 1, sizeof(mem), tempstorage);
if(nread <= 0) {
/* end of file */
break;
}
ptr = mem;
do {
/* write data in a loop until we block */
rc = libssh2_sftp_writenb(sftp_handle, ptr,
nread);
ptr += rc;
nread -= nread;
} while (rc > 0);
if(rc != LIBSSH2SFTP_EAGAIN) {
/* error or end of file */
break;
}
timeout.tv_sec = 10;
timeout.tv_usec = 0;
FD_ZERO(&fd);
FD_SET(sock, &fd);
/* wait for readable or writeable */
rc = select(sock+1, &fd, &fd, NULL, &timeout);
if(rc <= 0) {
/* negative is error
0 is timeout */
fprintf(stderr, "SFTP upload timed out: %d\n",
rc);
break;
}
} while (1);
fprintf(stderr, "SFTP upload done!\n");
}
else {
fprintf(stderr, "SFTP failed to open destination path: %s\n",
dest);
}
libssh2_sftp_shutdown(sftp_session);
shutdown:
libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");
libssh2_session_free(session);
#ifdef WIN32
Sleep(1000);
closesocket(sock);
#else
sleep(1);
close(sock);
#endif
printf("all done\n");
return 0;
}

Просмотреть файл

@ -1,4 +1,4 @@
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
@ -347,9 +347,22 @@ LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const
#define libssh2_channel_exec(channel, command) libssh2_channel_process_startup((channel), "exec", sizeof("exec") - 1, (command), strlen(command))
#define libssh2_channel_subsystem(channel, subsystem) libssh2_channel_process_startup((channel), "subsystem", sizeof("subsystem") - 1, (subsystem), strlen(subsystem))
LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, size_t buflen);
#define libssh2_channel_read(channel, buf, buflen) libssh2_channel_read_ex((channel), 0, (buf), (buflen))
LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel,
int stream_id, char *buf,
size_t buflen);
LIBSSH2_API int libssh2_channel_readnb_ex(LIBSSH2_CHANNEL *channel,
int stream_id, char *buf,
size_t buflen);
/* This is a public error code from libssh2_channel_read() that is returned
when it would otherwise block. */
#define LIBSSH2CHANNEL_EAGAIN -2
#define libssh2_channel_read(channel, buf, buflen) \
libssh2_channel_read_ex((channel), 0, (buf), (buflen))
#define libssh2_channel_read_stderr(channel, buf, buflen) libssh2_channel_read_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen))
#define libssh2_channel_readnb(channel, buf, buflen) \
libssh2_channel_readnb_ex((channel), 0, (buf), (buflen))
LIBSSH2_API int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended);
@ -358,14 +371,25 @@ LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channe
LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force);
LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen);
#define libssh2_channel_write(channel, buf, buflen) libssh2_channel_write_ex((channel), 0, (buf), (buflen))
LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel,
int stream_id, const char *buf,
size_t buflen);
LIBSSH2_API int libssh2_channel_writenb_ex(LIBSSH2_CHANNEL *channel,
int stream_id, const char *buf,
size_t buflen);
#define libssh2_channel_write(channel, buf, buflen) \
libssh2_channel_write_ex((channel), 0, (buf), (buflen))
#define libssh2_channel_writenb(channel, buf, buflen) \
libssh2_channel_writenb_ex((channel), 0, (buf), (buflen))
#define libssh2_channel_write_stderr(channel, buf, buflen) libssh2_channel_write_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen))
LIBSSH2_API unsigned long libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, unsigned long *window_size_initial);
#define libssh2_channel_window_write(channel) libssh2_channel_window_write_ex((channel), NULL)
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking);
LIBSSH2_API int libssh2_channel_get_blocking(LIBSSH2_CHANNEL *channel);
LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode);
/* libssh2_channel_ignore_extended_data() is defined below for BC with version 0.1
* Future uses should use libssh2_channel_handle_extended_data() directly

Просмотреть файл

@ -1,4 +1,4 @@
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
@ -38,6 +38,8 @@
#ifndef LIBSSH2_SFTP_H
#define LIBSSH2_SFTP_H 1
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
@ -179,9 +181,19 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *
#define libssh2_sftp_open(sftp, filename, flags, mode) libssh2_sftp_open_ex((sftp), (filename), strlen(filename), (flags), (mode), LIBSSH2_SFTP_OPENFILE)
#define libssh2_sftp_opendir(sftp, path) libssh2_sftp_open_ex((sftp), (path), strlen(path), 0, 0, LIBSSH2_SFTP_OPENDIR)
LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen);
/* This is a public error code from libssh2_sftp_read() that is returned
when it would otherwise block. */
#define LIBSSH2SFTP_EAGAIN -2
LIBSSH2_API ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle,
char *buffer, size_t buffer_maxlen);
LIBSSH2_API ssize_t libssh2_sftp_readnb(LIBSSH2_SFTP_HANDLE *handle,
char *buffer, size_t buffer_maxlen);
LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, LIBSSH2_SFTP_ATTRIBUTES *attrs);
LIBSSH2_API size_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count);
LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle,
const char *buffer, size_t count);
LIBSSH2_API ssize_t libssh2_sftp_writenb(LIBSSH2_SFTP_HANDLE *handle,
const char *buffer, size_t count);
LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle);
#define libssh2_sftp_close(handle) libssh2_sftp_close_handle(handle)

Просмотреть файл

@ -2,7 +2,7 @@ AUTOMAKE_OPTIONS = foreign nostdinc
libssh2_la_SOURCES = channel.c comp.c crypt.c hostkey.c kex.c mac.c \
misc.c packet.c publickey.c scp.c session.c sftp.c userauth.c \
libssh2_priv.h openssl.h libgcrypt.h pem.c
libssh2_priv.h openssl.h libgcrypt.h pem.c transport.c
if LIBGCRYPT
libssh2_la_SOURCES += libgcrypt.c

Просмотреть файл

@ -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
*/
@ -89,14 +92,14 @@ LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long
#define libssh2_channel_add(session, channel) \
{ \
if ((session)->channels.tail) { \
if ((session)->channels.tail) { \
(session)->channels.tail->next = (channel); \
(channel)->prev = (session)->channels.tail; \
} else { \
(session)->channels.head = (channel); \
(channel)->prev = NULL; \
(channel)->prev = NULL; \
} \
(channel)->next = NULL; \
(channel)->next = NULL; \
(session)->channels.tail = (channel); \
(channel)->session = (session); \
}
@ -104,81 +107,111 @@ 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));
channel->channel_type_len = channel_type_len;
channel->channel_type = LIBSSH2_ALLOC(session, channel_type_len);
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;
}
memcpy(channel->channel_type, channel_type, channel_type_len);
/* REMEMBER: local as in locally sourced */
channel->local.id = local_channel;
channel->remote.window_size = window_size;
channel->local.id = local_channel;
channel->remote.window_size = window_size;
channel->remote.window_size_initial = window_size;
channel->remote.packet_size = packet_size;
channel->remote.packet_size = packet_size;
libssh2_channel_add(session, channel);
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;
}
if (data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
channel->remote.id = libssh2_ntohu32(data + 5);
channel->local.window_size = libssh2_ntohu32(data + 9);
channel->local.window_size_initial = libssh2_ntohu32(data + 9);
channel->local.packet_size = libssh2_ntohu32(data + 13);
channel->remote.id = libssh2_ntohu32(data + 5);
channel->local.window_size = libssh2_ntohu32(data + 9);
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",
channel->local.id, channel->remote.id,
channel->local.window_size, channel->remote.window_size,
channel->local.packet_size, channel->remote.packet_size);
_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);
#endif
LIBSSH2_FREE(session, packet);
LIBSSH2_FREE(session, data);
@ -187,7 +220,8 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, c
}
if (data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) {
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);
}
@ -257,10 +295,10 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *se
libssh2_htonu32(s, sport); s += 4;
channel = libssh2_channel_open_ex(session, "direct-tcpip",
sizeof("direct-tcpip") - 1,
LIBSSH2_CHANNEL_WINDOW_DEFAULT,
LIBSSH2_CHANNEL_PACKET_DEFAULT,
(char *)message, message_len);
sizeof("direct-tcpip") - 1,
LIBSSH2_CHANNEL_WINDOW_DEFAULT,
LIBSSH2_CHANNEL_PACKET_DEFAULT,
(char *)message, message_len);
LIBSSH2_FREE(session, message);
return channel;
@ -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)) &&
(libssh2_ntohu32(packet->data + 1) == channel->local.id)) {
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
/* set non-blocking and remember previous state */
bl = _libssh2_channel_set_blocking(channel, 0);
/* process all incoming packets */
do {
LIBSSH2_PACKET *packet;
rc = libssh2_packet_read(session);
} while (rc > 0);
rc = 0;
/* Process any waiting packets */
while (libssh2_packet_read(session, blocking_read) > 0) blocking_read = 0;
packet = session->packets.head;
/* restore blocking state */
_libssh2_channel_set_blocking(channel, bl);
while (packet && (bytes_read < buflen)) {
/* In case packet gets destroyed during this iteration */
packet = session->packets.head;
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 */
@ -1017,9 +1235,10 @@ LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id
memcpy(s, buf, bufwrite); s += bufwrite;
#ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending %d bytes on channel %lu/%lu, stream_id=%d", (int)bufwrite, channel->local.id, channel->remote.id, stream_id);
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending %d bytes on channel %lu/%lu, stream_id=%d", (int)bufwrite, channel->local.id, channel->remote.id, stream_id);
#endif
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;
@ -1176,7 +1439,7 @@ LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel)
/* 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) ||
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);
}

Просмотреть файл

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

Просмотреть файл

@ -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;
}
@ -514,7 +519,7 @@ static int libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_S
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
@ -599,7 +604,7 @@ static int libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange(LI
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send Group Exchange Request", 0);
ret = -1;
goto dh_gex_clean_exit;
}
}
if (libssh2_packet_require(session, SSH_MSG_KEX_DH_GEX_GROUP, &data, &data_len)) {
libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timeout waiting for GEX_GROUP reply", 0);
@ -813,7 +818,7 @@ static int libssh2_kexinit(LIBSSH2_SESSION *session)
return 0;
}
/* }}} */
/* }}} */
/* {{{ libssh2_kex_agree_instr
* Kex specific variant of strstr()
@ -907,7 +912,9 @@ static int libssh2_kex_agree_hostkey(LIBSSH2_SESSION *session, unsigned long kex
}
while (hostkeyp && (*hostkeyp)->name) {
s = libssh2_kex_agree_instr(hostkey, hostkey_len, (*hostkeyp)->name, strlen((*hostkeyp)->name));
s = libssh2_kex_agree_instr(hostkey, hostkey_len,
(unsigned char *)(*hostkeyp)->name,
strlen((*hostkeyp)->name));
if (s) {
/* So far so good, but does it suit our purposes? (Encrypting vs Signing) */
if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) == 0) ||
@ -972,7 +979,9 @@ static int libssh2_kex_agree_kex_hostkey(LIBSSH2_SESSION *session, unsigned char
}
while (*kexp && (*kexp)->name) {
s = libssh2_kex_agree_instr(kex, kex_len, (*kexp)->name, strlen((*kexp)->name));
s = libssh2_kex_agree_instr(kex, kex_len,
(unsigned char *)(*kexp)->name,
strlen((*kexp)->name));
if (s) {
/* We've agreed on a key exchange method,
* Can we agree on a hostkey that works with this kex?
@ -1014,7 +1023,7 @@ static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session,
if (libssh2_kex_agree_instr(crypt, crypt_len, s, method_len)) {
LIBSSH2_CRYPT_METHOD *method =
(LIBSSH2_CRYPT_METHOD*)libssh2_get_method_by_name(s, method_len, (LIBSSH2_COMMON_METHOD**)cryptp);
(LIBSSH2_CRYPT_METHOD*)libssh2_get_method_by_name((char *)s, method_len, (LIBSSH2_COMMON_METHOD**)cryptp);
if (!method) {
/* Invalid method -- Should never be reached */
@ -1031,7 +1040,9 @@ static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session,
}
while (*cryptp && (*cryptp)->name) {
s = libssh2_kex_agree_instr(crypt, crypt_len, (*cryptp)->name, strlen((*cryptp)->name));
s = libssh2_kex_agree_instr(crypt, crypt_len,
(unsigned char *)(*cryptp)->name,
strlen((*cryptp)->name));
if (s) {
endpoint->crypt = *cryptp;
return 0;
@ -1076,7 +1087,9 @@ static int libssh2_kex_agree_mac(LIBSSH2_SESSION *session, libssh2_endpoint_data
}
while (*macp && (*macp)->name) {
s = libssh2_kex_agree_instr(mac, mac_len, (*macp)->name, strlen((*macp)->name));
s = libssh2_kex_agree_instr(mac, mac_len,
(unsigned char *)(*macp)->name,
strlen((*macp)->name));
if (s) {
endpoint->mac = *macp;
return 0;
@ -1121,7 +1134,9 @@ static int libssh2_kex_agree_comp(LIBSSH2_SESSION *session, libssh2_endpoint_dat
}
while (*compp && (*compp)->name) {
s = libssh2_kex_agree_instr(comp, comp_len, (*compp)->name, strlen((*compp)->name));
s = libssh2_kex_agree_instr(comp, comp_len,
(unsigned char *)(*compp)->name,
strlen((*compp)->name));
if (s) {
endpoint->comp = *compp;
return 0;
@ -1261,7 +1276,7 @@ int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange) /* session->f
}
session->local.kexinit = oldlocal;
session->local.kexinit_len = oldlocal_len;
return -1;
return -2;
}
if (session->remote.kexinit) {
@ -1271,13 +1286,13 @@ int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange) /* session->f
session->remote.kexinit_len = data_len;
if (libssh2_kex_agree_methods(session, data, data_len)) {
return -1;
return -3;
}
}
if (session->kex->exchange_keys(session)) {
libssh2_error(session, LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE, "Unrecoverable error exchanging keys", 0);
return -1;
return -4;
}
/* Done with kexinit buffers */

Просмотреть файл

@ -54,6 +54,15 @@
#include "openssl.h"
#endif
/* RFC4253 section 6.1 Maximum Packet Length says:
*
* "All implementations MUST be able to process packets with
* uncompressed payload length of 32768 bytes or less and
* total packet size of 35000 bytes or less (including length,
* padding length, payload, padding, and MAC.)."
*/
#define MAX_SSH_PACKET_LEN 35000
#define LIBSSH2_ALLOC(session, count) session->alloc((count), &(session)->abstract)
#define LIBSSH2_REALLOC(session, ptr, count) ((ptr) ? session->realloc((ptr), (count), &(session)->abstract) : session->alloc((count), &(session)->abstract))
#define LIBSSH2_FREE(session, ptr) session->free((ptr), &(session)->abstract)
@ -173,6 +182,43 @@ typedef struct _libssh2_endpoint_data {
char *lang_prefs;
} libssh2_endpoint_data;
#define PACKETBUFSIZE 4096
struct transportpacket {
/* ------------- for incoming data --------------- */
unsigned char buf[PACKETBUFSIZE];
unsigned char init[5]; /* first 5 bytes of the incoming data stream,
still encrypted */
int writeidx; /* at what array index we do the next write into
the buffer */
int readidx; /* at what array index we do the next read from
the buffer */
int packet_length; /* the most recent packet_length as read from the
network data */
int padding_length; /* the most recent padding_length as read from the
network data */
int data_num; /* How much of the total package that has been read
so far. */
int total_num; /* How much a total package is supposed to be, in
number of bytes. A full package is
packet_length + padding_length + 4 +
mac_length. */
unsigned char *payload; /* this is a pointer to a LIBSSH2_ALLOC()
area to which we write decrypted data */
unsigned char *wptr; /* write pointer into the payload to where we
are currently writing decrypted data */
/* ------------- for outgoing data --------------- */
unsigned char *outbuf; /* pointer to a LIBSSH2_ALLOC() area for the
outgoing data */
int ototal_num; /* size of outbuf in number of bytes */
unsigned char *odata; /* original pointer to the data we stored in
outbuf */
unsigned long olen; /* original size of the data we stored in
outbuf */
unsigned long osent; /* number of bytes already sent */
};
struct _LIBSSH2_SESSION {
/* Memory management callbacks */
void *abstract;
@ -240,6 +286,9 @@ struct _LIBSSH2_SESSION {
unsigned long err_msglen;
int err_should_free;
int err_code;
/* struct members for packet-level reading */
struct transportpacket packet;
};
/* session.state bits */
@ -345,7 +394,7 @@ void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, .
if (session->err_msg && session->err_should_free) { \
LIBSSH2_FREE(session, session->err_msg); \
} \
session->err_msg = errmsg; \
session->err_msg = (char *)errmsg; \
session->err_msglen = strlen(errmsg); \
session->err_should_free = should_free; \
session->err_code = errcode; \
@ -359,7 +408,7 @@ void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, .
if (session->err_msg && session->err_should_free) { \
LIBSSH2_FREE(session, session->err_msg); \
} \
session->err_msg = errmsg; \
session->err_msg = (char *)errmsg; \
session->err_msglen = strlen(errmsg); \
session->err_should_free = should_free; \
session->err_code = errcode; \
@ -440,7 +489,27 @@ libssh2_uint64_t libssh2_ntohu64(const unsigned char *buf);
void libssh2_htonu32(unsigned char *buf, unsigned long val);
void libssh2_htonu64(unsigned char *buf, libssh2_uint64_t val);
int libssh2_packet_read(LIBSSH2_SESSION *session, int block);
#define LIBSSH2_READ_TIMEOUT 60 /* generic timeout in seconds used when
waiting for more data to arrive */
int libssh2_waitsocket(LIBSSH2_SESSION *session, long seconds);
/* CAUTION: some of these error codes are returned in the public API and is
there known with other #defined names from the public header file. They
should not be changed. */
typedef int libssh2pack_t;
#define PACKET_TIMEOUT -7
#define PACKET_BADUSE -6
#define PACKET_COMPRESS -5
#define PACKET_TOOBIG -4
#define PACKET_ENOMEM -3
#define PACKET_EAGAIN -2
#define PACKET_FAIL -1
#define PACKET_NONE 0
libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session);
int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket);
#define libssh2_packet_ask(session, packet_type, data, data_len, poll_socket) \
libssh2_packet_ask_ex((session), (packet_type), (data), (data_len), 0, NULL, 0, (poll_socket))
@ -448,16 +517,32 @@ int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, unsigned char *packet_types
#define libssh2_packet_askv(session, packet_types, data, data_len, poll_socket) \
libssh2_packet_askv_ex((session), (packet_types), (data), (data_len), 0, NULL, 0, (poll_socket))
int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len);
#define libssh2_packet_require(session, packet_type, data, data_len) \
libssh2_packet_require_ex((session), (packet_type), (data), (data_len), 0, NULL, 0)
#define libssh2_packet_require(session, packet_type, data, data_len) \
libssh2_packet_require_ex((session), (packet_type), (data), (data_len), 0, NULL, 0)
int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len);
#define libssh2_packet_requirev(session, packet_types, data, data_len) \
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);

Просмотреть файл

@ -181,15 +181,15 @@ void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, .
int len;
va_list vargs;
char *contexts[9] = { "Unknown",
"Transport",
"Key Exhange",
"Userauth",
"Connection",
"scp",
"SFTP Subsystem",
"Failure Event",
"Publickey Subsystem",
};
"Transport",
"Key Exhange",
"Userauth",
"Connection",
"scp",
"SFTP Subsystem",
"Failure Event",
"Publickey Subsystem",
};
if (context < 1 || context > 8) {
context = 0;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,4 +1,4 @@
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
@ -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;

Просмотреть файл

@ -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);
@ -118,9 +123,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
return NULL;
}
if ((response_len > 1) &&
((response[response_len-1] < '0') || (response[response_len-1] > '9')) &&
(response[response_len-1] != ' ') &&
if ((response_len > 1) &&
((response[response_len-1] < '0') || (response[response_len-1] > '9')) &&
(response[response_len-1] != ' ') &&
(response[response_len-1] != '\r') &&
(response[response_len-1] != '\n')) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0);
@ -135,7 +140,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
libssh2_channel_free(channel);
return NULL;
}
/* Way too short to be an SCP response, or not done yet, short circuit */
/* Way too short to be an SCP response, or not done yet, short circuit */
continue;
}
@ -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);
@ -229,7 +234,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
return NULL;
}
if ((response_len > 1) &&
if ((response_len > 1) &&
(response[response_len-1] != '\r') &&
(response[response_len-1] != '\n') &&
((response[response_len-1] < 32) || (response[response_len-1] > 126))) {
@ -245,7 +250,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
libssh2_channel_free(channel);
return NULL;
}
/* Way too short to be an SCP response, or not done yet, short circuit */
/* Way too short to be an SCP response, or not done yet, short circuit */
continue;
}
@ -261,7 +266,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
}
s = response + 1;
p = strchr(s, ' ');
if (!p || ((p - s) <= 0)) {
/* No spaces or space in the wrong spot */
@ -362,7 +367,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const
#endif
/* Allocate a channel */
if ((channel = libssh2_channel_open_session(session)) == NULL) {
/* previous call set libssh2_session_last_error(), pass it through */
/* previous call set libssh2_session_last_error(), pass it through */
LIBSSH2_FREE(session, command);
return NULL;
}
@ -371,7 +376,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const
/* Request SCP for the desired file */
if (libssh2_channel_process_startup(channel, "exec", sizeof("exec") - 1, command, command_len)) {
/* previous call set libssh2_session_last_error(), pass it through */
/* previous call set libssh2_session_last_error(), pass it through */
LIBSSH2_FREE(session, command);
libssh2_channel_free(channel);
return NULL;
@ -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;

Просмотреть файл

@ -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,8 +99,8 @@ static int libssh2_banner_receive(LIBSSH2_SESSION *session)
char banner[256];
int banner_len = 0;
while ((banner_len < sizeof(banner)) &&
((banner_len == 0) || (banner[banner_len-1] != '\n'))) {
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';
@ -235,14 +240,17 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(
LIBSSH2_REALLOC_FUNC((*my_realloc)),
void *abstract)
{
LIBSSH2_ALLOC_FUNC((*local_alloc)) = libssh2_default_alloc;
LIBSSH2_FREE_FUNC((*local_free)) = libssh2_default_free;
LIBSSH2_ALLOC_FUNC((*local_alloc)) = libssh2_default_alloc;
LIBSSH2_FREE_FUNC((*local_free)) = libssh2_default_free;
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;

2149
src/sftp.c

Разница между файлами не показана из-за своего большого размера Загрузить разницу

706
src/transport.c Обычный файл
Просмотреть файл

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

Просмотреть файл

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