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.
Этот коммит содержится в:
родитель
c63ef86075
Коммит
9d55db6501
11
Makefile.am
11
Makefile.am
@ -1,19 +1,10 @@
|
||||
AUTOMAKE_OPTIONS = foreign nostdinc
|
||||
|
||||
SUBDIRS = src tests
|
||||
SUBDIRS = src example tests
|
||||
|
||||
include_HEADERS = include/libssh2.h include/libssh2_publickey.h \
|
||||
include/libssh2_sftp.h
|
||||
|
||||
# and a sample tool
|
||||
noinst_PROGRAMS = ssh2_sample
|
||||
|
||||
INCLUDES = -I$(top_srcdir)/include
|
||||
|
||||
ssh2_sample_SOURCES = ssh2_sample.c
|
||||
|
||||
ssh2_sample_LDADD = src/libssh2.la
|
||||
|
||||
EXTRA_DIST = LICENSE win32
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
@ -224,7 +224,7 @@ AC_ARG_ENABLE(debug-errors,
|
||||
AC_HELP_STRING([--enable-debug-errors],[Output failure events to stderr]),
|
||||
[AC_DEFINE(LIBSSH2_DEBUG_ERRORS, 1, [Output failure events to stderr])])
|
||||
AC_ARG_ENABLE(debug-all,
|
||||
AC_HELP_STRING([--enable-debug-all],[Output debugging info for all layers to stderr]),
|
||||
AC_HELP_STRING([--enable-debug],[Enable debug]),
|
||||
[
|
||||
AC_DEFINE(LIBSSH2_DEBUG_TRANSPORT, 1, [Output transport layer debugging info to stderr])
|
||||
AC_DEFINE(LIBSSH2_DEBUG_KEX, 1, [Output Key Exchange debugging info to stderr])
|
||||
@ -273,5 +273,7 @@ AC_C_INLINE
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
src/Makefile
|
||||
tests/Makefile])
|
||||
tests/Makefile
|
||||
example/Makefile
|
||||
example/simple/Makefile])
|
||||
AC_OUTPUT
|
||||
|
2
example/Makefile.am
Обычный файл
2
example/Makefile.am
Обычный файл
@ -0,0 +1,2 @@
|
||||
AUTOMAKE_OPTIONS = foreign nostdinc
|
||||
SUBDIRS = simple
|
17
example/simple/Makefile.am
Обычный файл
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
Обычный файл
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
Обычный файл
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
|
||||
|
433
src/channel.c
433
src/channel.c
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
||||
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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
|
||||
|
47
src/kex.c
47
src/kex.c
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
||||
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
@ -79,6 +79,7 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
unsigned char *s, *f_value, *k_value = NULL, *h_sig;
|
||||
unsigned long f_value_len, k_value_len, h_sig_len;
|
||||
libssh2_sha1_ctx exchange_hash;
|
||||
int rc;
|
||||
|
||||
/* Generate x and e */
|
||||
_libssh2_bn_rand(x, group_order, 0, -1);
|
||||
@ -108,7 +109,8 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending KEX packet %d", (int)packet_type_init);
|
||||
#endif
|
||||
if (libssh2_packet_write(session, e_packet, e_packet_len)) {
|
||||
rc = libssh2_packet_write(session, e_packet, e_packet_len);
|
||||
if (rc) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEX init message", 0);
|
||||
ret = -11;
|
||||
goto clean_exit;
|
||||
@ -135,8 +137,11 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
}
|
||||
|
||||
/* Wait for KEX reply */
|
||||
if (libssh2_packet_require(session, packet_type_reply, &s_packet, &s_packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timed out waiting for KEX reply", 0);
|
||||
rc = libssh2_packet_require(session, packet_type_reply, &s_packet,
|
||||
&s_packet_len);
|
||||
if (rc) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
|
||||
"Timed out waiting for KEX reply", 0);
|
||||
ret = -1;
|
||||
goto clean_exit;
|
||||
}
|
||||
@ -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);
|
||||
|
18
src/misc.c
18
src/misc.c
@ -181,15 +181,15 @@ void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, .
|
||||
int len;
|
||||
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;
|
||||
|
1682
src/packet.c
1682
src/packet.c
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
||||
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
@ -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;
|
||||
|
37
src/scp.c
37
src/scp.c
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
||||
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
@ -42,9 +42,12 @@
|
||||
#define LIBSSH2_SCP_RESPONSE_BUFLEN 256
|
||||
|
||||
/* {{{ libssh2_scp_recv
|
||||
* [BLOCKING]
|
||||
* Open a channel and request a remote file via SCP
|
||||
*/
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat *sb)
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session,
|
||||
const char *path,
|
||||
struct stat *sb)
|
||||
{
|
||||
int path_len = strlen(path);
|
||||
unsigned char *command, response[LIBSSH2_SCP_RESPONSE_BUFLEN];
|
||||
@ -103,8 +106,10 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
|
||||
response_len = 0;
|
||||
while (sb && (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN)) {
|
||||
unsigned char *s, *p;
|
||||
int rc;
|
||||
|
||||
if (libssh2_channel_read(channel, response + response_len, 1) <= 0) {
|
||||
rc = _libssh2_channel_read(channel, response + response_len, 1);
|
||||
if(rc <= 0) {
|
||||
/* Timeout, give up */
|
||||
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0);
|
||||
libssh2_channel_free(channel);
|
||||
@ -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;
|
||||
|
119
src/session.c
119
src/session.c
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
|
||||
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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
2149
src/sftp.c
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
706
src/transport.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
|
152
ssh2_sample.c
152
ssh2_sample.c
@ -1,152 +0,0 @@
|
||||
#include "libssh2.h"
|
||||
|
||||
#ifndef WIN32
|
||||
# include <netinet/in.h>
|
||||
# include <sys/socket.h>
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int sock, i, auth_pw = 1;
|
||||
struct sockaddr_in sin;
|
||||
const char *fingerprint;
|
||||
LIBSSH2_SESSION *session;
|
||||
LIBSSH2_CHANNEL *channel;
|
||||
char *username=(char *)"username";
|
||||
char *password=(char *)"password";
|
||||
#ifdef WIN32
|
||||
WSADATA wsadata;
|
||||
|
||||
WSAStartup(WINSOCK_VERSION, &wsadata);
|
||||
#endif
|
||||
|
||||
/* Ultra basic "connect to port 22 on localhost"
|
||||
* Your code is responsible for creating the socket establishing the connection
|
||||
*/
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
#ifndef WIN32
|
||||
fcntl(sock, F_SETFL, 0);
|
||||
#endif
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(22);
|
||||
sin.sin_addr.s_addr = htonl(0x7F000001);
|
||||
if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) {
|
||||
fprintf(stderr, "failed to connect!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create a session instance and start it up
|
||||
* This will trade welcome banners, exchange keys, and setup crypto, compression, and MAC layers
|
||||
*/
|
||||
session = libssh2_session_init();
|
||||
if (libssh2_session_startup(session, sock)) {
|
||||
fprintf(stderr, "Failure establishing SSH session\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* At this point we havn't authenticated,
|
||||
* The first thing to do is check the hostkey's fingerprint against our known hosts
|
||||
* Your app may have it hard coded, may go to a file, may present it to the user, that's your call
|
||||
*/
|
||||
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
|
||||
printf("Fingerprint: ");
|
||||
for(i = 0; i < 16; i++) {
|
||||
printf("%02X ", (unsigned char)fingerprint[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
if(argc > 1) {
|
||||
username = argv[1];
|
||||
}
|
||||
if(argc > 2) {
|
||||
password = argv[2];
|
||||
}
|
||||
|
||||
if (auth_pw) {
|
||||
/* We could authenticate via password */
|
||||
if (libssh2_userauth_password(session, username, password)) {
|
||||
printf("Authentication by password failed.\n");
|
||||
goto shutdown;
|
||||
}
|
||||
} else {
|
||||
/* Or by public key */
|
||||
if (libssh2_userauth_publickey_fromfile(session, username, "/home/username/.ssh/id_rsa.pub", "/home/username/.ssh/id_rsa", password)) {
|
||||
printf("\tAuthentication by public key failed\n");
|
||||
goto shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
/* Request a shell */
|
||||
if (!(channel = libssh2_channel_open_session(session))) {
|
||||
fprintf(stderr, "Unable to open a session\n");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
/* Some environment variables may be set,
|
||||
* It's up to the server which ones it'll allow though
|
||||
*/
|
||||
libssh2_channel_setenv(channel, (char *)"FOO", (char *)"bar");
|
||||
|
||||
/* Request a terminal with 'vanilla' terminal emulation
|
||||
* See /etc/termcap for more options
|
||||
*/
|
||||
if (libssh2_channel_request_pty(channel, (char *)"vanilla")) {
|
||||
fprintf(stderr, "Failed requesting pty\n");
|
||||
goto skip_shell;
|
||||
}
|
||||
|
||||
/* Open a SHELL on that pty */
|
||||
if (libssh2_channel_shell(channel)) {
|
||||
fprintf(stderr, "Unable to request shell on allocated pty\n");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
/* At this point the shell can be interacted with using
|
||||
* libssh2_channel_read()
|
||||
* libssh2_channel_read_stderr()
|
||||
* libssh2_channel_write()
|
||||
* libssh2_channel_write_stderr()
|
||||
*
|
||||
* Blocking mode may be (en|dis)abled with: libssh2_channel_set_blocking()
|
||||
* If the server send EOF, libssh2_channel_eof() will return non-0
|
||||
* To send EOF to the server use: libssh2_channel_send_eof()
|
||||
* A channel can be closed with: libssh2_channel_close()
|
||||
* A channel can be freed with: libssh2_channel_free()
|
||||
*/
|
||||
|
||||
skip_shell:
|
||||
if (channel) {
|
||||
libssh2_channel_free(channel);
|
||||
channel = NULL;
|
||||
}
|
||||
|
||||
/* Other channel types are supported via:
|
||||
* libssh2_scp_send()
|
||||
* libssh2_scp_recv()
|
||||
* libssh2_channel_direct_tcpip()
|
||||
*/
|
||||
|
||||
shutdown:
|
||||
|
||||
libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");
|
||||
libssh2_session_free(session);
|
||||
|
||||
#ifdef WIN32
|
||||
Sleep(1000);
|
||||
closesocket(sock);
|
||||
#else
|
||||
sleep(1);
|
||||
close(sock);
|
||||
#endif
|
||||
printf("all done\n");
|
||||
return 0;
|
||||
}
|
Загрузка…
Ссылка в новой задаче
Block a user