diff --git a/Makefile.am b/Makefile.am index 3060578..b13f5b9 100644 --- a/Makefile.am +++ b/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 diff --git a/configure.in b/configure.in index b7d3ba2..bcea2ab 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/example/Makefile.am b/example/Makefile.am new file mode 100644 index 0000000..3f2227e --- /dev/null +++ b/example/Makefile.am @@ -0,0 +1,2 @@ +AUTOMAKE_OPTIONS = foreign nostdinc +SUBDIRS = simple diff --git a/example/simple/Makefile.am b/example/simple/Makefile.am new file mode 100644 index 0000000..b66ab69 --- /dev/null +++ b/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 diff --git a/example/simple/scp_nonblock.c b/example/simple/scp_nonblock.c new file mode 100644 index 0000000..d4a20b8 --- /dev/null +++ b/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; +} diff --git a/example/simple/sftp.c b/example/simple/sftp.c index 6a75fca..c9aa756 100644 --- a/example/simple/sftp.c +++ b/example/simple/sftp.c @@ -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); diff --git a/example/simple/sftp_nonblock.c b/example/simple/sftp_nonblock.c new file mode 100644 index 0000000..f24f4e2 --- /dev/null +++ b/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; +} diff --git a/include/libssh2.h b/include/libssh2.h index 70addd7..6bb3f73 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -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 diff --git a/include/libssh2_sftp.h b/include/libssh2_sftp.h index d65a044..12884c6 100644 --- a/include/libssh2_sftp.h +++ b/include/libssh2_sftp.h @@ -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) diff --git a/src/Makefile.am b/src/Makefile.am index 1940446..cdf1a31 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/channel.c b/src/channel.c index 6f7b674..131fe21 100644 --- a/src/channel.c +++ b/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); } diff --git a/src/crypt.c b/src/crypt.c index f562d50..b6efb57 100644 --- a/src/crypt.c +++ b/src/crypt.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, @@ -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 diff --git a/src/kex.c b/src/kex.c index 185e71b..4a0f138 100644 --- a/src/kex.c +++ b/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 */ diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index 400c88d..8891e84 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -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); diff --git a/src/misc.c b/src/misc.c index 12e4d00..5d22876 100644 --- a/src/misc.c +++ b/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; diff --git a/src/packet.c b/src/packet.c index fa5ebd3..15eba72 100644 --- a/src/packet.c +++ b/src/packet.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org> +/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org> * All rights reserved. * * Redistribution and use in source and binary forms, @@ -47,6 +47,9 @@ #include <sys/uio.h> #endif +#include <sys/time.h> +#include <sys/types.h> + #ifdef HAVE_POLL # include <sys/poll.h> #else @@ -62,1014 +65,729 @@ #include <inttypes.h> -/* RFC4253 section 6.1 Maximum Packet Length says: - * - * "All implementations MUST be able to process packets with - * uncompressed payload length of 32768 bytes or less and - * total packet size of 35000 bytes or less (including length, - * padding length, payload, padding, and MAC.)." - */ -#define MAX_SSH_PACKET_LEN 35000 - inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen); inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen); /* {{{ libssh2_packet_queue_listener * Queue a connection request for a listener */ -inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen) +inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, + unsigned char *data, + unsigned long datalen) { - /* Look for a matching listener */ - unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5; - /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ - unsigned long packet_len = 17 + (sizeof("Forward not requested") - 1); - unsigned char *p, packet[17 + (sizeof("Forward not requested") - 1)]; - LIBSSH2_LISTENER *l = session->listeners; - char failure_code = 1; /* SSH_OPEN_ADMINISTRATIVELY_PROHIBITED */ + /* Look for a matching listener */ + unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5; + /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ + unsigned long packet_len = 17 + (sizeof("Forward not requested") - 1); + unsigned char *p, packet[17 + (sizeof("Forward not requested") - 1)]; + LIBSSH2_LISTENER *l = session->listeners; + char failure_code = 1; /* SSH_OPEN_ADMINISTRATIVELY_PROHIBITED */ uint32_t sender_channel, initial_window_size, packet_size; - unsigned char *host, *shost; + unsigned char *host, *shost; uint32_t port, sport, host_len, shost_len; + (void)datalen; - sender_channel = libssh2_ntohu32(s); s += 4; + sender_channel = libssh2_ntohu32(s); s += 4; - initial_window_size = libssh2_ntohu32(s); s += 4; - packet_size = libssh2_ntohu32(s); s += 4; + initial_window_size = libssh2_ntohu32(s); s += 4; + packet_size = libssh2_ntohu32(s); s += 4; - host_len = libssh2_ntohu32(s); s += 4; - host = s; s += host_len; - port = libssh2_ntohu32(s); s += 4; + host_len = libssh2_ntohu32(s); s += 4; + host = s; s += host_len; + port = libssh2_ntohu32(s); s += 4; - shost_len = libssh2_ntohu32(s); s += 4; - shost = s; s += shost_len; - sport = libssh2_ntohu32(s); s += 4; + shost_len = libssh2_ntohu32(s); s += 4; + shost = s; s += shost_len; + sport = libssh2_ntohu32(s); s += 4; #ifdef LIBSSH2_DEBUG_CONNECTION - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Remote received connection from %s:%ld to %s:%ld", shost, sport, host, port); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Remote received connection from %s:%ld to %s:%ld", shost, sport, host, port); #endif - while (l) { - if ((l->port == port) && - (strlen(l->host) == host_len) && - (memcmp(l->host, host, host_len) == 0)) { - /* This is our listener */ - LIBSSH2_CHANNEL *channel, *last_queued = l->queue; - if (l->queue_maxsize && - (l->queue_maxsize <= l->queue_size)) { - /* Queue is full */ - failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + while (l) { + if ((l->port == (int)port) && + (strlen(l->host) == host_len) && + (memcmp(l->host, host, host_len) == 0)) { + /* This is our listener */ + LIBSSH2_CHANNEL *channel, *last_queued = l->queue; + + if (l->queue_maxsize && + (l->queue_maxsize <= l->queue_size)) { + /* Queue is full */ + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ #ifdef LIBSSH2_DEBUG_CONNECTION - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Listener queue full, ignoring"); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Listener queue full, ignoring"); #endif - break; - } + break; + } - channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); - if (!channel) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); - failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ - break; - } - memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); + channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + break; + } + memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); - channel->session = session; - channel->channel_type_len = sizeof("forwarded-tcpip") - 1; - channel->channel_type = LIBSSH2_ALLOC(session, channel->channel_type_len + 1); - if (!channel->channel_type) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); - LIBSSH2_FREE(session, channel); - failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ - break; - } - memcpy(channel->channel_type, "forwarded-tcpip", channel->channel_type_len + 1); + channel->session = session; + channel->channel_type_len = sizeof("forwarded-tcpip") - 1; + channel->channel_type = LIBSSH2_ALLOC(session, channel->channel_type_len + 1); + if (!channel->channel_type) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); + LIBSSH2_FREE(session, channel); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + break; + } + memcpy(channel->channel_type, "forwarded-tcpip", channel->channel_type_len + 1); - channel->remote.id = sender_channel; - channel->remote.window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT; - channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; - channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; + channel->remote.id = sender_channel; + channel->remote.window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; - channel->local.id = libssh2_channel_nextid(session); - channel->local.window_size_initial = initial_window_size; - channel->local.window_size = initial_window_size; - channel->local.packet_size = packet_size; + channel->local.id = libssh2_channel_nextid(session); + channel->local.window_size_initial = initial_window_size; + channel->local.window_size = initial_window_size; + channel->local.packet_size = packet_size; #ifdef LIBSSH2_DEBUG_CONNECTION - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Connection queued: channel %lu/%lu win %lu/%lu packet %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 queued: channel %lu/%lu win %lu/%lu packet %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 - p = packet; - *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; - libssh2_htonu32(p, channel->remote.id); p += 4; - libssh2_htonu32(p, channel->local.id); p += 4; - libssh2_htonu32(p, channel->remote.window_size_initial); p += 4; - libssh2_htonu32(p, channel->remote.packet_size); p += 4; + p = packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; + libssh2_htonu32(p, channel->remote.id); p += 4; + libssh2_htonu32(p, channel->local.id); p += 4; + libssh2_htonu32(p, channel->remote.window_size_initial); p += 4; + libssh2_htonu32(p, channel->remote.packet_size); p += 4; - if (libssh2_packet_write(session, packet, 17)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0); - return -1; - } + if (libssh2_packet_write(session, packet, 17)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0); + return -1; + } - /* Link the channel into the end of the queue list */ + /* Link the channel into the end of the queue list */ - if (!last_queued) { - l->queue = channel; - return 0; - } + if (!last_queued) { + l->queue = channel; + return 0; + } - while (last_queued->next) last_queued = last_queued->next; + while (last_queued->next) last_queued = last_queued->next; - last_queued->next = channel; - channel->prev = last_queued; + last_queued->next = channel; + channel->prev = last_queued; - l->queue_size++; + l->queue_size++; - return 0; - } + return 0; + } - l = l->next; - } + l = l->next; + } - /* We're not listening to you */ - { + /* We're not listening to you */ + { - p = packet; - *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; - libssh2_htonu32(p, sender_channel); p += 4; - libssh2_htonu32(p, failure_code); p += 4; - libssh2_htonu32(p, sizeof("Forward not requested") - 1); p += 4; - memcpy(s, "Forward not requested", sizeof("Forward not requested") - 1); p += sizeof("Forward not requested") - 1; - libssh2_htonu32(p, 0); + p = packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; + libssh2_htonu32(p, sender_channel); p += 4; + libssh2_htonu32(p, failure_code); p += 4; + libssh2_htonu32(p, sizeof("Forward not requested") - 1); p += 4; + memcpy(s, "Forward not requested", sizeof("Forward not requested") - 1); p += sizeof("Forward not requested") - 1; + libssh2_htonu32(p, 0); - if (libssh2_packet_write(session, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send open failure", 0); - return -1; - } - return 0; - } + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send open failure", 0); + return -1; + } + return 0; + } } /* }}} */ /* {{{ libssh2_packet_x11_open * Accept a forwarded X11 connection */ -inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen) +inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, + unsigned char *data, unsigned long datalen) { - int failure_code = 2; /* SSH_OPEN_CONNECT_FAILED */ - unsigned char *s = data + (sizeof("x11") - 1) + 5; - unsigned long packet_len = 17 + (sizeof("X11 Forward Unavailable") - 1); - unsigned char *p, packet[17 + (sizeof("X11 Forward Unavailable") - 1)]; - /* packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ - LIBSSH2_CHANNEL *channel; - unsigned long sender_channel, initial_window_size, packet_size; - unsigned char *shost; - unsigned long sport, shost_len; + int failure_code = 2; /* SSH_OPEN_CONNECT_FAILED */ + unsigned char *s = data + (sizeof("x11") - 1) + 5; + unsigned long packet_len = 17 + (sizeof("X11 Forward Unavailable") - 1); + unsigned char *p, packet[17 + (sizeof("X11 Forward Unavailable") - 1)]; + /* packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ + LIBSSH2_CHANNEL *channel; + unsigned long sender_channel, initial_window_size, packet_size; + unsigned char *shost; + unsigned long sport, shost_len; + (void)datalen; - sender_channel = libssh2_ntohu32(s); s += 4; - initial_window_size = libssh2_ntohu32(s); s += 4; - packet_size = libssh2_ntohu32(s); s += 4; - shost_len = libssh2_ntohu32(s); s += 4; - shost = s; s += shost_len; - sport = libssh2_ntohu32(s); s += 4; + sender_channel = libssh2_ntohu32(s); s += 4; + initial_window_size = libssh2_ntohu32(s); s += 4; + packet_size = libssh2_ntohu32(s); s += 4; + shost_len = libssh2_ntohu32(s); s += 4; + shost = s; s += shost_len; + sport = libssh2_ntohu32(s); s += 4; #ifdef LIBSSH2_DEBUG_CONNECTION - _libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection Received from %s:%ld on channel %lu", shost, sport, sender_channel); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection Received from %s:%ld on channel %lu", shost, sport, sender_channel); #endif - if (session->x11) { - channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); - if (!channel) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); - failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ - goto x11_exit; - } - memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); + if (session->x11) { + channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + goto x11_exit; + } + memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); - channel->session = session; - channel->channel_type_len = sizeof("x11") - 1; - channel->channel_type = LIBSSH2_ALLOC(session, channel->channel_type_len + 1); - if (!channel->channel_type) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); - LIBSSH2_FREE(session, channel); - failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ - goto x11_exit; - } - memcpy(channel->channel_type, "x11", channel->channel_type_len + 1); + channel->session = session; + channel->channel_type_len = sizeof("x11") - 1; + channel->channel_type = LIBSSH2_ALLOC(session, channel->channel_type_len + 1); + if (!channel->channel_type) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); + LIBSSH2_FREE(session, channel); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + goto x11_exit; + } + memcpy(channel->channel_type, "x11", channel->channel_type_len + 1); - channel->remote.id = sender_channel; - channel->remote.window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT; - channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; - channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; + channel->remote.id = sender_channel; + channel->remote.window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; - channel->local.id = libssh2_channel_nextid(session); - channel->local.window_size_initial = initial_window_size; - channel->local.window_size = initial_window_size; - channel->local.packet_size = packet_size; + channel->local.id = libssh2_channel_nextid(session); + channel->local.window_size_initial = initial_window_size; + channel->local.window_size = initial_window_size; + channel->local.packet_size = packet_size; #ifdef LIBSSH2_DEBUG_CONNECTION - _libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection established: channel %lu/%lu win %lu/%lu packet %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, "X11 Connection established: channel %lu/%lu win %lu/%lu packet %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 - p = packet; - *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; - libssh2_htonu32(p, channel->remote.id); p += 4; - libssh2_htonu32(p, channel->local.id); p += 4; - libssh2_htonu32(p, channel->remote.window_size_initial); p += 4; - libssh2_htonu32(p, channel->remote.packet_size); p += 4; + p = packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; + libssh2_htonu32(p, channel->remote.id); p += 4; + libssh2_htonu32(p, channel->local.id); p += 4; + libssh2_htonu32(p, channel->remote.window_size_initial); p += 4; + libssh2_htonu32(p, channel->remote.packet_size); p += 4; - if (libssh2_packet_write(session, packet, 17)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0); - return -1; - } + if (libssh2_packet_write(session, packet, 17)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0); + return -1; + } - /* Link the channel into the session */ - if (session->channels.tail) { - session->channels.tail->next = channel; - channel->prev = session->channels.tail; - } else { - session->channels.head = channel; - channel->prev = NULL; - } - channel->next = NULL; - session->channels.tail = channel; - - /* - * Pass control to the callback, they may turn right around and - * free the channel, or actually use it - */ - LIBSSH2_X11_OPEN(channel, (char *)shost, sport); - - return 0; - } else { - failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ - } + /* Link the channel into the session */ + if (session->channels.tail) { + session->channels.tail->next = channel; + channel->prev = session->channels.tail; + } else { + session->channels.head = channel; + channel->prev = NULL; + } + channel->next = NULL; + session->channels.tail = channel; + + /* + * Pass control to the callback, they may turn right around and + * free the channel, or actually use it + */ + LIBSSH2_X11_OPEN(channel, (char *)shost, sport); + + return 0; + } else { + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + } x11_exit: - p = packet; - *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; - libssh2_htonu32(p, sender_channel); p += 4; - libssh2_htonu32(p, failure_code); p += 4; - libssh2_htonu32(p, sizeof("X11 Forward Unavailable") - 1); p += 4; - memcpy(s, "X11 Forward Unavailable", sizeof("X11 Forward Unavailable") - 1); p += sizeof("X11 Forward Unavailable") - 1; - libssh2_htonu32(p, 0); + p = packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; + libssh2_htonu32(p, sender_channel); p += 4; + libssh2_htonu32(p, failure_code); p += 4; + libssh2_htonu32(p, sizeof("X11 Forward Unavailable") - 1); p += 4; + memcpy(s, "X11 Forward Unavailable", sizeof("X11 Forward Unavailable") - 1); p += sizeof("X11 Forward Unavailable") - 1; + libssh2_htonu32(p, 0); - if (libssh2_packet_write(session, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send open failure", 0); - return -1; - } - return 0; + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send open failure", 0); + return -1; + } + return 0; } /* }}} */ /* {{{ libssh2_packet_new * Create a new packet and attach it to the brigade */ -static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, size_t datalen, int macstate) +int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, size_t datalen, int macstate) { - LIBSSH2_PACKET *packet; - unsigned long data_head = 0; + LIBSSH2_PACKET *packet; + unsigned long data_head = 0; #ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Packet type %d received, length=%d", (int)data[0], (int)datalen); + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Packet type %d received, length=%d", (int)data[0], (int)datalen); #endif - if (macstate == LIBSSH2_MAC_INVALID) { - if (session->macerror) { - if (LIBSSH2_MACERROR(session, (char *)data, datalen) == 0) { - /* Calling app has given the OK, Process it anyway */ - macstate = LIBSSH2_MAC_CONFIRMED; - } else { - libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0); - if (session->ssh_msg_disconnect) { - LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0); - } - return -1; - } - } else { - libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0); - if (session->ssh_msg_disconnect) { - LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0); - } - return -1; - } - } + if (macstate == LIBSSH2_MAC_INVALID) { + if (session->macerror) { + if (LIBSSH2_MACERROR(session, (char *)data, datalen) == 0) { + /* Calling app has given the OK, Process it anyway */ + macstate = LIBSSH2_MAC_CONFIRMED; + } else { + libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0); + if (session->ssh_msg_disconnect) { + LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0); + } + return -1; + } + } else { + libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0); + if (session->ssh_msg_disconnect) { + LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0); + } + return -1; + } + } - /* A couple exceptions to the packet adding rule: */ - switch (data[0]) { - case SSH_MSG_DISCONNECT: - { - char *message, *language; - int reason, message_len, language_len; + /* A couple exceptions to the packet adding rule: */ + switch (data[0]) { + case SSH_MSG_DISCONNECT: + { + char *message, *language; + int reason, message_len, language_len; - reason = libssh2_ntohu32(data + 1); - message_len = libssh2_ntohu32(data + 5); - message = (char *)data + 9; /* packet_type(1) + reason(4) + message_len(4) */ - language_len = libssh2_ntohu32(data + 9 + message_len); - /* This is where we hack on the data a little, - * Use the MSB of language_len to to a terminating NULL (In all liklihood it is already) - * Shift the language tag back a byte (In all likelihood it's zero length anyway - * Store a NULL in the last byte of the packet to terminate the language string - * With the lengths passed this isn't *REALLY* necessary, but it's "kind" - */ - message[message_len] = '\0'; - language = (char *)data + 9 + message_len + 3; - if (language_len) { - memcpy(language, language + 1, language_len); - } - language[language_len] = '\0'; + reason = libssh2_ntohu32(data + 1); + message_len = libssh2_ntohu32(data + 5); + message = (char *)data + 9; /* packet_type(1) + reason(4) + message_len(4) */ + language_len = libssh2_ntohu32(data + 9 + message_len); + /* This is where we hack on the data a little, + * Use the MSB of language_len to to a terminating NULL (In all liklihood it is already) + * Shift the language tag back a byte (In all likelihood it's zero length anyway + * Store a NULL in the last byte of the packet to terminate the language string + * With the lengths passed this isn't *REALLY* necessary, but it's "kind" + */ + message[message_len] = '\0'; + language = (char *)data + 9 + message_len + 3; + if (language_len) { + memcpy(language, language + 1, language_len); + } + language[language_len] = '\0'; - if (session->ssh_msg_disconnect) { - LIBSSH2_DISCONNECT(session, reason, message, message_len, language, language_len); - } + if (session->ssh_msg_disconnect) { + LIBSSH2_DISCONNECT(session, reason, message, message_len, language, language_len); + } #ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnect(%d): %s(%s)", reason, message, language); + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnect(%d): %s(%s)", reason, message, language); #endif - LIBSSH2_FREE(session, data); - session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; - return -1; - } - break; - case SSH_MSG_IGNORE: - /* As with disconnect, back it up one and add a trailing NULL */ - memcpy(data + 4, data + 5, datalen - 5); - data[datalen] = '\0'; - if (session->ssh_msg_ignore) { - LIBSSH2_IGNORE(session, (char *)data + 4, datalen - 5); - } - LIBSSH2_FREE(session, (char *)data); - return 0; - break; - case SSH_MSG_DEBUG: - { - int always_display = data[0]; - char *message, *language; - int message_len, language_len; + LIBSSH2_FREE(session, data); + session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; + return -1; + } + break; + case SSH_MSG_IGNORE: + /* As with disconnect, back it up one and add a trailing NULL */ + memcpy(data + 4, data + 5, datalen - 5); + data[datalen] = '\0'; + if (session->ssh_msg_ignore) { + LIBSSH2_IGNORE(session, (char *)data + 4, datalen - 5); + } + LIBSSH2_FREE(session, (char *)data); + return 0; + break; + case SSH_MSG_DEBUG: + { + int always_display = data[0]; + char *message, *language; + int message_len, language_len; - message_len = libssh2_ntohu32(data + 2); - message = (char *)data + 6; /* packet_type(1) + display(1) + message_len(4) */ - language_len = libssh2_ntohu32(data + 6 + message_len); - /* This is where we hack on the data a little, - * Use the MSB of language_len to to a terminating NULL (In all liklihood it is already) - * Shift the language tag back a byte (In all likelihood it's zero length anyway - * Store a NULL in the last byte of the packet to terminate the language string - * With the lengths passed this isn't *REALLY* necessary, but it's "kind" - */ - message[message_len] = '\0'; - language = (char *)data + 6 + message_len + 3; - if (language_len) { - memcpy(language, language + 1, language_len); - } - language[language_len] = '\0'; + message_len = libssh2_ntohu32(data + 2); + message = (char *)data + 6; /* packet_type(1) + display(1) + message_len(4) */ + language_len = libssh2_ntohu32(data + 6 + message_len); + /* This is where we hack on the data a little, + * Use the MSB of language_len to to a terminating NULL (In all liklihood it is already) + * Shift the language tag back a byte (In all likelihood it's zero length anyway + * Store a NULL in the last byte of the packet to terminate the language string + * With the lengths passed this isn't *REALLY* necessary, but it's "kind" + */ + message[message_len] = '\0'; + language = (char *)data + 6 + message_len + 3; + if (language_len) { + memcpy(language, language + 1, language_len); + } + language[language_len] = '\0'; - if (session->ssh_msg_debug) { - LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len); - } + if (session->ssh_msg_debug) { + LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len); + } #ifdef LIBSSH2_DEBUG_TRANSPORT - /* _libssh2_debug will actually truncate this for us so that it's not an inordinate about of data */ - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Debug Packet: %s", message); + /* _libssh2_debug will actually truncate this for us so that it's not an inordinate about of data */ + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Debug Packet: %s", message); #endif - LIBSSH2_FREE(session, data); - return 0; - } - break; - case SSH_MSG_CHANNEL_EXTENDED_DATA: - data_head += 4; /* streamid(4) */ - case SSH_MSG_CHANNEL_DATA: - data_head += 9; /* packet_type(1) + channelno(4) + datalen(4) */ - { - LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); + LIBSSH2_FREE(session, data); + return 0; + } + break; + case SSH_MSG_CHANNEL_EXTENDED_DATA: + data_head += 4; /* streamid(4) */ + case SSH_MSG_CHANNEL_DATA: + data_head += 9; /* packet_type(1) + channelno(4) + datalen(4) */ + { + LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); - if (!channel) { - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN, "Packet received for unknown channel, ignoring", 0); - LIBSSH2_FREE(session, data); - return 0; - } + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN, "Packet received for unknown channel, ignoring", 0); + LIBSSH2_FREE(session, data); + return 0; + } #ifdef LIBSSH2_DEBUG_CONNECTION { - unsigned long stream_id = 0; + unsigned long stream_id = 0; - if (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) { - stream_id = libssh2_ntohu32(data + 5); - } + if (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) { + stream_id = libssh2_ntohu32(data + 5); + } - _libssh2_debug(session, LIBSSH2_DBG_CONN, "%d bytes received for channel %lu/%lu stream #%lu", (int)(datalen - data_head), channel->local.id, channel->remote.id, stream_id); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "%d bytes received for channel %lu/%lu stream #%lu", (int)(datalen - data_head), channel->local.id, channel->remote.id, stream_id); } #endif - if ((channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) && (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) { - /* Pretend we didn't receive this */ - LIBSSH2_FREE(session, data); + if ((channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) && (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) { + /* Pretend we didn't receive this */ + LIBSSH2_FREE(session, data); #ifdef LIBSSH2_DEBUG_CONNECTION - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Ignoring extended data and refunding %d bytes", (int)(datalen - 13)); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Ignoring extended data and refunding %d bytes", (int)(datalen - 13)); #endif - /* Adjust the window based on the block we just freed */ - libssh2_channel_receive_window_adjust(channel, datalen - 13, 0); + /* Adjust the window based on the block we just freed */ + libssh2_channel_receive_window_adjust(channel, datalen - 13, 0); - return 0; - } + return 0; + } - /* REMEMBER! remote means remote as source of data, NOT remote window! */ - if (channel->remote.packet_size < (datalen - data_head)) { - /* Spec says we MAY ignore bytes sent beyond packet_size */ - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "Packet contains more data than we offered to receive, truncating", 0); - datalen = channel->remote.packet_size + data_head; - } - if (channel->remote.window_size <= 0) { - /* Spec says we MAY ignore bytes sent beyond window_size */ - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "The current receive window is full, data ignored", 0); - LIBSSH2_FREE(session, data); - return 0; - } - /* Reset EOF status */ - channel->remote.eof = 0; + /* REMEMBER! remote means remote as source of data, NOT remote window! */ + if (channel->remote.packet_size < (datalen - data_head)) { + /* Spec says we MAY ignore bytes sent beyond packet_size */ + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "Packet contains more data than we offered to receive, truncating", 0); + datalen = channel->remote.packet_size + data_head; + } + if (channel->remote.window_size <= 0) { + /* Spec says we MAY ignore bytes sent beyond window_size */ + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "The current receive window is full, data ignored", 0); + LIBSSH2_FREE(session, data); + return 0; + } + /* Reset EOF status */ + channel->remote.eof = 0; - if ((datalen - data_head) > channel->remote.window_size) { - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "Remote sent more data than current window allows, truncating", 0); - datalen = channel->remote.window_size + data_head; - } else { - /* Now that we've received it, shrink our window */ - channel->remote.window_size -= datalen - data_head; - } - } - break; - case SSH_MSG_CHANNEL_EOF: - { - LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); + if ((datalen - data_head) > channel->remote.window_size) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "Remote sent more data than current window allows, truncating", 0); + datalen = channel->remote.window_size + data_head; + } else { + /* Now that we've received it, shrink our window */ + channel->remote.window_size -= datalen - data_head; + } + } + break; + case SSH_MSG_CHANNEL_EOF: + { + LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); - if (!channel) { - /* We may have freed already, just quietly ignore this... */ - LIBSSH2_FREE(session, data); - return 0; - } + if (!channel) { + /* We may have freed already, just quietly ignore this... */ + LIBSSH2_FREE(session, data); + return 0; + } #ifdef LIBSSH2_DEBUG_CONNECTION - _libssh2_debug(session, LIBSSH2_DBG_CONN, "EOF received for channel %lu/%lu", channel->local.id, channel->remote.id); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "EOF received for channel %lu/%lu", channel->local.id, channel->remote.id); #endif - channel->remote.eof = 1; + channel->remote.eof = 1; - LIBSSH2_FREE(session, data); - return 0; - } - break; - case SSH_MSG_CHANNEL_REQUEST: - { - if (libssh2_ntohu32(data+5) == sizeof("exit-status") - 1 - && !memcmp("exit-status", data + 9, sizeof("exit-status") - 1)) { + LIBSSH2_FREE(session, data); + return 0; + } + break; + case SSH_MSG_CHANNEL_REQUEST: + { + if (libssh2_ntohu32(data+5) == sizeof("exit-status") - 1 + && !memcmp("exit-status", data + 9, sizeof("exit-status") - 1)) { - /* we've got "exit-status" packet. Set the session value */ - LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data+1)); + /* we've got "exit-status" packet. Set the session value */ + LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data+1)); - if (channel) { - channel->exit_status = libssh2_ntohu32(data + 9 + sizeof("exit-status")); + if (channel) { + channel->exit_status = libssh2_ntohu32(data + 9 + sizeof("exit-status")); #ifdef LIBSSH2_DEBUG_CONNECTION - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Exit status %lu received for channel %lu/%lu", channel->exit_status, channel->local.id, channel->remote.id); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Exit status %lu received for channel %lu/%lu", channel->exit_status, channel->local.id, channel->remote.id); #endif - } + } - LIBSSH2_FREE(session, data); - return 0; - } - } - break; - case SSH_MSG_CHANNEL_CLOSE: - { - LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); + LIBSSH2_FREE(session, data); + return 0; + } + } + break; + case SSH_MSG_CHANNEL_CLOSE: + { + LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); - if (!channel) { - /* We may have freed already, just quietly ignore this... */ - LIBSSH2_FREE(session, data); - return 0; - } + if (!channel) { + /* We may have freed already, just quietly ignore this... */ + LIBSSH2_FREE(session, data); + return 0; + } #ifdef LIBSSH2_DEBUG_CONNECTION - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Close received for channel %lu/%lu", channel->local.id, channel->remote.id); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Close received for channel %lu/%lu", channel->local.id, channel->remote.id); #endif - channel->remote.close = 1; - channel->remote.eof = 1; - /* TODO: Add a callback for this */ + channel->remote.close = 1; + channel->remote.eof = 1; + /* TODO: Add a callback for this */ - LIBSSH2_FREE(session, data); - return 0; - } - break; - case SSH_MSG_CHANNEL_OPEN: - if ((datalen >= (sizeof("forwarded-tcpip") + 4)) && - ((sizeof("forwarded-tcpip")-1) == libssh2_ntohu32(data + 1)) && - (memcmp(data + 5, "forwarded-tcpip", sizeof("forwarded-tcpip") - 1) == 0)) { - int retval = libssh2_packet_queue_listener(session, data, datalen); + LIBSSH2_FREE(session, data); + return 0; + } + break; + case SSH_MSG_CHANNEL_OPEN: + if ((datalen >= (sizeof("forwarded-tcpip") + 4)) && + ((sizeof("forwarded-tcpip")-1) == libssh2_ntohu32(data + 1)) && + (memcmp(data + 5, "forwarded-tcpip", sizeof("forwarded-tcpip") - 1) == 0)) { + int retval = libssh2_packet_queue_listener(session, data, datalen); - LIBSSH2_FREE(session, data); - return retval; - } - if ((datalen >= (sizeof("x11") + 4)) && - ((sizeof("x11")-1) == libssh2_ntohu32(data + 1)) && - (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) { - int retval = libssh2_packet_x11_open(session, data, datalen); + LIBSSH2_FREE(session, data); + return retval; + } + if ((datalen >= (sizeof("x11") + 4)) && + ((sizeof("x11")-1) == libssh2_ntohu32(data + 1)) && + (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) { + int retval = libssh2_packet_x11_open(session, data, datalen); - LIBSSH2_FREE(session, data); - return retval; - } - break; - case SSH_MSG_CHANNEL_WINDOW_ADJUST: - { - LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); - unsigned long bytestoadd = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + return retval; + } + break; + case SSH_MSG_CHANNEL_WINDOW_ADJUST: + { + LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); + unsigned long bytestoadd = libssh2_ntohu32(data + 5); - if (channel && bytestoadd) { - channel->local.window_size += bytestoadd; - } + if (channel && bytestoadd) { + channel->local.window_size += bytestoadd; + } #ifdef LIBSSH2_DEBUG_CONNECTION - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu", channel->local.id, channel->remote.id, bytestoadd, channel->local.window_size); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu", channel->local.id, channel->remote.id, bytestoadd, channel->local.window_size); #endif - LIBSSH2_FREE(session, data); - return 0; - } - break; - } + LIBSSH2_FREE(session, data); + return 0; + } + break; + } - packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); - memset(packet, 0, sizeof(LIBSSH2_PACKET)); + packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); + memset(packet, 0, sizeof(LIBSSH2_PACKET)); - packet->data = data; - packet->data_len = datalen; - packet->data_head = data_head; - packet->mac = macstate; - packet->brigade = &session->packets; - packet->next = NULL; + packet->data = data; + packet->data_len = datalen; + packet->data_head = data_head; + packet->mac = macstate; + packet->brigade = &session->packets; + packet->next = NULL; - if (session->packets.tail) { - packet->prev = session->packets.tail; - packet->prev->next = packet; - session->packets.tail = packet; - } else { - session->packets.head = packet; - session->packets.tail = packet; - packet->prev = NULL; - } + if (session->packets.tail) { + packet->prev = session->packets.tail; + packet->prev->next = packet; + session->packets.tail = packet; + } else { + session->packets.head = packet; + session->packets.tail = packet; + packet->prev = NULL; + } - if (data[0] == SSH_MSG_KEXINIT && !(session->state & LIBSSH2_STATE_EXCHANGING_KEYS)) { - /* Remote wants new keys - * Well, it's already in the brigade, - * let's just call back into ourselves - */ + if (data[0] == SSH_MSG_KEXINIT && !(session->state & LIBSSH2_STATE_EXCHANGING_KEYS)) { + /* Remote wants new keys + * Well, it's already in the brigade, + * let's just call back into ourselves + */ #ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Renegotiating Keys"); + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Renegotiating Keys"); #endif - libssh2_kex_exchange(session, 1); - /* If there was a key reexchange failure, let's just hope we didn't send NEWKEYS yet, otherwise remote will drop us like a rock */ - } + libssh2_kex_exchange(session, 1); + /* If there was a key reexchange failure, let's just hope we didn't send NEWKEYS yet, otherwise remote will drop us like a rock */ + } - return 0; -} -/* }}} */ - -/* {{{ libssh2_blocking_read - * Force a blocking read, regardless of socket settings - */ -static ssize_t libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, size_t count) -{ - size_t bytes_read = 0; -#if !defined(HAVE_POLL) && !defined(HAVE_SELECT) - int polls = 0; -#endif - -#ifndef WIN32 - fcntl(session->socket_fd, F_SETFL, 0); -#else - { - u_long block = FALSE; - ioctlsocket(session->socket_fd, FIONBIO, &block); - } -#endif - -#ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes", (int)count); -#endif - - while (bytes_read < count) { - int ret; - - ret = recv(session->socket_fd, buf + bytes_read, count - bytes_read, LIBSSH2_SOCKET_RECV_FLAGS(session)); - if (ret < 0) { -#ifdef WIN32 - switch (WSAGetLastError()) { - case WSAEWOULDBLOCK: errno = EAGAIN; break; - case WSAENOTSOCK: errno = EBADF; break; - case WSAENOTCONN: - case WSAECONNABORTED: errno = ENOTCONN; break; - case WSAEINTR: errno = EINTR; break; - } -#endif - if (errno == EAGAIN) { -#ifdef HAVE_POLL - struct pollfd read_socket; - - read_socket.fd = session->socket_fd; - read_socket.events = POLLIN; - - if (poll(&read_socket, 1, 30000) <= 0) { - return -1; - } -#elif defined(HAVE_SELECT) - fd_set read_socket; - struct timeval timeout; - - FD_ZERO(&read_socket); - FD_SET(session->socket_fd, &read_socket); - - timeout.tv_sec = 30; - timeout.tv_usec = 0; - - if (select(session->socket_fd + 1, &read_socket, NULL, NULL, &timeout) <= 0) { - return -1; - } -#else - if (polls++ > LIBSSH2_SOCKET_POLL_MAXLOOPS) { - return -1; - } - usleep(LIBSSH2_SOCKET_POLL_UDELAY); -#endif /* POLL/SELECT/SLEEP */ - continue; - } - if (errno == EINTR) { - continue; - } - if ((errno == EBADF) || (errno == EIO) || (errno == ENOTCONN)) { - session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; - } - return -1; - } - if (ret == 0) continue; - - bytes_read += ret; - } - -#ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes actually read", (int)bytes_read); -#endif - - return bytes_read; -} -/* }}} */ - -/* {{{ libssh2_packet_read - * Collect a packet into the input brigade - * block only controls whether or not to wait for a packet to start, - * Once a packet starts, libssh2 will block until it is complete - * Returns packet type added to input brigade (0 if nothing added), or -1 on failure - */ -int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block) -{ - int packet_type = -1; - - if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) { - return 0; - } - -#ifndef WIN32 - fcntl(session->socket_fd, F_SETFL, O_NONBLOCK); -#else - { - u_long non_block = TRUE; - ioctlsocket(session->socket_fd, FIONBIO, &non_block); - } -#endif - -#ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Checking for packet: will%s block", should_block ? "" : " not"); -#endif - if (session->state & LIBSSH2_STATE_NEWKEYS) { - /* Temporary Buffer - * The largest blocksize (currently) is 32, the largest MAC (currently) is 20 - */ - unsigned char block[2 * 32], *payload, *s, *p, tmp[6]; - ssize_t read_len; - unsigned long blocksize = session->remote.crypt->blocksize; - unsigned long packet_len, payload_len; - int padding_len; - int macstate; - int free_payload = 1; - - /* Note: If we add any cipher with a blocksize less than 6 we'll need to get more creative with this - * For now, all blocksize sizes are 8+ - */ - if (should_block) { - read_len = libssh2_blocking_read(session, block, blocksize); - if(read_len <= 0) - return read_len; - } else { - ssize_t nread; - read_len = recv(session->socket_fd, block, 1, LIBSSH2_SOCKET_RECV_FLAGS(session)); - if (read_len <= 0) { - return 0; - } - nread = libssh2_blocking_read(session, block + read_len, blocksize - read_len); - if(nread <= 0) - return nread; - - read_len += nread; - } - if (read_len < blocksize) { - return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1; - } - - if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) { - libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0); - return -1; - } - - packet_len = libssh2_ntohu32(block); - - /* RFC4253 section 6.1 Maximum Packet Length says: - * - * "All implementations MUST be able to process packets with - * uncompressed payload length of 32768 bytes or less and - * total packet size of 35000 bytes or less (including length, - * padding length, payload, padding, and MAC.)." - */ - if(packet_len > MAX_SSH_PACKET_LEN) { - return -1; - } - - padding_len = block[4]; -#ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing packet %lu bytes long (with %lu bytes padding)", packet_len, padding_len); -#endif - memcpy(tmp, block, 5); /* Use this for MAC later */ - - payload_len = packet_len - 1; /* padding_len(1) */ - /* Sanity Check */ - if ((payload_len > LIBSSH2_PACKET_MAXPAYLOAD) || - ((packet_len + 4) % blocksize)) { - /* If something goes horribly wrong during the decryption phase, just bailout and die gracefully */ - session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; - libssh2_error(session, LIBSSH2_ERROR_PROTO, "Fatal protocol error, invalid payload size", 0); - return -1; - } - - s = payload = LIBSSH2_ALLOC(session, payload_len); - memcpy(s, block + 5, blocksize - 5); - s += blocksize - 5; - - p = s; - while (p - payload < payload_len) { - read_len = payload_len - (p - payload); - if (read_len > 4096) read_len = 4096; - - if (libssh2_blocking_read(session, p, read_len) < read_len) { - LIBSSH2_FREE(session, payload); - return -1; - } - - p += read_len; - - while (s < p) { - memcpy(block, s, blocksize); - - if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) { - libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0); - LIBSSH2_FREE(session, payload); - return -1; - } - memcpy(s, block, blocksize); - - s += blocksize; - } - } - - read_len = libssh2_blocking_read(session, block, session->remote.mac->mac_len); - if (read_len < session->remote.mac->mac_len) { - LIBSSH2_FREE(session, payload); - return -1; - } - - /* Calculate MAC hash */ - session->remote.mac->hash(session, block + session->remote.mac->mac_len, session->remote.seqno, tmp, 5, payload, payload_len, &session->remote.mac_abstract); - - macstate = (strncmp((char *)block, (char *)block + session->remote.mac->mac_len, session->remote.mac->mac_len) == 0) ? LIBSSH2_MAC_CONFIRMED : LIBSSH2_MAC_INVALID; - - session->remote.seqno++; - - /* Ignore padding */ - payload_len -= padding_len; - - if (session->remote.comp && - strcmp(session->remote.comp->name, "none")) { - /* Decompress */ - unsigned char *data; - unsigned long data_len; - - if (session->remote.comp->comp(session, 0, &data, &data_len, LIBSSH2_PACKET_MAXDECOMP, &free_payload, payload, payload_len, &session->remote.comp_abstract)) { - LIBSSH2_FREE(session, payload); - return -1; - } -#ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Payload decompressed: %lu bytes(compressed) to %lu bytes(uncompressed)", data_len, payload_len); -#endif - if (free_payload) { - LIBSSH2_FREE(session, payload); - payload = data; - payload_len = data_len; - } else { - if (data == payload) { - /* It's not to be freed, because the compression layer reused payload, - * So let's do the same! - */ - payload_len = data_len; - } else { - /* No comp_method actually lets this happen, but let's prepare for the future */ - - LIBSSH2_FREE(session, payload); - - /* We need a freeable struct otherwise the brigade won't know what to do with it */ - payload = LIBSSH2_ALLOC(session, data_len); - if (!payload) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of uncompressed data", 0); - return -1; - } - memcpy(payload, data, data_len); - payload_len = data_len; - } - } - } - - packet_type = payload[0]; - libssh2_packet_add(session, payload, payload_len, macstate); - - } else { /* No cipher active */ - unsigned char *payload; - unsigned char buf[24]; - ssize_t buf_len; - unsigned long payload_len; - uint32_t packet_length; - unsigned long padding_length; - - if (should_block) { - buf_len = libssh2_blocking_read(session, buf, 5); - } else { - ssize_t nread; - buf_len = recv(session->socket_fd, buf, 1, LIBSSH2_SOCKET_RECV_FLAGS(session)); - if (buf_len <= 0) { - return buf_len; - } - nread = libssh2_blocking_read(session, buf, 5 - buf_len); - if(nread <= 0) - return -1; - - buf_len += nread; - } - if (buf_len < 5) { - /* Something bad happened */ - return -1; - } - packet_length = libssh2_ntohu32(buf); - - /* RFC4253 section 6.1 Maximum Packet Length says: - * - * "All implementations MUST be able to process packets with - * uncompressed payload length of 32768 bytes or less and - * total packet size of 35000 bytes or less (including length, - * padding length, payload, padding, and MAC.)." - */ - if(packet_length > MAX_SSH_PACKET_LEN) { - return -1; - } - - padding_length = buf[4]; -#ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing plaintext packet %lu bytes long (with %lu bytes padding)", packet_length, padding_length); -#endif - - payload_len = packet_length - padding_length - 1; /* padding_length(1) */ - payload = LIBSSH2_ALLOC(session, payload_len); - if (!payload) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of plaintext data", 0); - return -1; - } - - if (libssh2_blocking_read(session, payload, payload_len) < payload_len) { - return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1; - } - while (padding_length) { - int l; - /* Flush padding */ - l = libssh2_blocking_read(session, buf, padding_length); - if (l > 0) - padding_length -= l; - else - break; - } - - packet_type = payload[0]; - - /* MACs don't exist in non-encrypted mode */ - libssh2_packet_add(session, payload, payload_len, LIBSSH2_MAC_CONFIRMED); - session->remote.seqno++; - } - return packet_type; + return 0; } /* }}} */ /* {{{ libssh2_packet_ask - * Scan the brigade for a matching packet type, optionally poll the socket for a packet first + * Scan the brigade for a matching packet type, optionally poll the socket for + * a packet first */ -int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, - unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket) +int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, + unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, + const unsigned char *match_buf, + unsigned long match_len, int poll_socket) { - LIBSSH2_PACKET *packet = session->packets.head; + LIBSSH2_PACKET *packet = session->packets.head; - if (poll_socket) { - if (libssh2_packet_read(session, 0) < 0) { - return -1; - } - } + if (poll_socket) { + libssh2pack_t rc = libssh2_packet_read(session); + if ((rc < 0) && !packet) { + return rc; + } + } #ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Looking for packet of type: %d", (int)packet_type); + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Looking for packet of type: %d", (int)packet_type); #endif - while (packet) { - if (packet->data[0] == packet_type && - (packet->data_len >= (match_ofs + match_len)) && - (!match_buf || (memcmp(packet->data + match_ofs, match_buf, match_len) == 0))) { - *data = packet->data; - *data_len = packet->data_len; + while (packet) { + if (packet->data[0] == packet_type && + (packet->data_len >= (match_ofs + match_len)) && + (!match_buf || + (memcmp(packet->data + match_ofs, match_buf, match_len) == 0))) { + *data = packet->data; + *data_len = packet->data_len; - if (packet->prev) { - packet->prev->next = packet->next; - } else { - session->packets.head = packet->next; - } + if (packet->prev) { + packet->prev->next = packet->next; + } else { + session->packets.head = packet->next; + } - if (packet->next) { - packet->next->prev = packet->prev; - } else { - session->packets.tail = packet->prev; - } + if (packet->next) { + packet->next->prev = packet->prev; + } else { + session->packets.tail = packet->prev; + } - LIBSSH2_FREE(session, packet); + LIBSSH2_FREE(session, packet); - return 0; - } - packet = packet->next; - } - return -1; + return 0; + } + packet = packet->next; + } + return -1; } /* }}} */ /* {{{ libssh2_packet_askv - * Scan for any of a list of packet types in the brigade, optionally poll the socket for a packet first + * Scan for any of a list of packet types in the brigade, optionally poll the + * socket for a packet first */ int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, - unsigned char *packet_types, - unsigned char **data, - unsigned long *data_len, - unsigned long match_ofs, - const unsigned char *match_buf, - unsigned long match_len, int poll_socket) + unsigned char *packet_types, + unsigned char **data, + unsigned long *data_len, + unsigned long match_ofs, + const unsigned char *match_buf, + unsigned long match_len, int poll_socket) { - int i, packet_types_len = strlen((char *)packet_types); + int i, packet_types_len = strlen((char *)packet_types); - for(i = 0; i < packet_types_len; i++) { - if (0 == libssh2_packet_ask_ex(session, packet_types[i], data, data_len, match_ofs, match_buf, match_len, i ? 0 : poll_socket)) { - return 0; - } - } + for(i = 0; i < packet_types_len; i++) { + if (0 == libssh2_packet_ask_ex(session, packet_types[i], + data, data_len, match_ofs, + match_buf, match_len, + i ? 0 : poll_socket)) { + return 0; + } + } - return -1; + return -1; } /* }}} */ +/* {{{ waitsocket + * Returns + * negative on error + * >0 on incoming data + * 0 on timeout + * + * FIXME: convert to use poll on systems that have it. + */ +int libssh2_waitsocket(LIBSSH2_SESSION *session, + long seconds) +{ + struct timeval timeout; + int rc; + fd_set fd; + + timeout.tv_sec = seconds; + timeout.tv_usec = 0; + + FD_ZERO(&fd); + + FD_SET(session->socket_fd, &fd); + + rc = select(session->socket_fd+1, &fd, NULL, NULL, &timeout); + + return rc; +} + /* {{{ libssh2_packet_require + * [BLOCKING] for LIBSSH2_READ_TIMEOUT seconds * Loops libssh2_packet_read() until the packet requested is available * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout + * + * Returns negative on error + * Returns 0 when it has taken care of the requested packet. */ -int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, - unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len) + +int libssh2_packet_require_ex(LIBSSH2_SESSION *session, + unsigned char packet_type, + unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, + const unsigned char *match_buf, + unsigned long match_len) { - if (libssh2_packet_ask_ex(session, packet_type, data, data_len, match_ofs, match_buf, match_len, 0) == 0) { - /* A packet was available in the packet brigade */ - return 0; - } + time_t start = time(NULL); + + if (libssh2_packet_ask_ex(session, packet_type, data, data_len, + match_ofs, match_buf, match_len, 0) == 0) { + /* A packet was available in the packet brigade */ + return 0; + } #ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet of type %d becomes available", (int)packet_type); + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "Blocking until packet of type %d becomes available", + (int)packet_type); #endif - while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - int ret = libssh2_packet_read(session, 1); - if (ret < 0) { - return -1; - } - if (ret == 0) continue; + while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + libssh2pack_t ret = libssh2_packet_read(session); + if ((ret < 0) && (ret != PACKET_EAGAIN)) { + /* an error which is not just because of blocking */ + return ret; + } + if (packet_type == ret) { + /* Be lazy, let packet_ask pull it out of the + brigade */ + ret = libssh2_packet_ask_ex(session, packet_type, + data, data_len, match_ofs, + match_buf, match_len, 0); + return ret; + } + else if (ret <= 0) { + /* nothing available, wait until data arrives or + we time out */ + long left = LIBSSH2_READ_TIMEOUT - + (time(NULL) - start); - if (packet_type == ret) { - /* Be lazy, let packet_ask pull it out of the brigade */ - return libssh2_packet_ask_ex(session, packet_type, data, data_len, match_ofs, match_buf, match_len, 0); - } - } + if((left <= 0) || + (libssh2_waitsocket(session, left) <= 0)) { + return PACKET_TIMEOUT; + } + } + } - /* Only reached if the socket died */ - return -1; + /* Only reached if the socket died */ + return -1; } /* }}} */ @@ -1079,203 +797,97 @@ int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_typ */ int libssh2_packet_burn(LIBSSH2_SESSION *session) { - unsigned char *data; - unsigned long data_len; - unsigned char all_packets[255]; - int i; - for(i = 1; i < 256; i++) all_packets[i - 1] = i; + unsigned char *data; + unsigned long data_len; + unsigned char all_packets[255]; + int i; + for(i = 1; i < 256; i++) all_packets[i - 1] = i; - if (libssh2_packet_askv_ex(session, all_packets, &data, &data_len, 0, NULL, 0, 0) == 0) { - i = data[0]; - /* A packet was available in the packet brigade, burn it */ - LIBSSH2_FREE(session, data); - return i; - } + if (libssh2_packet_askv_ex(session, all_packets, &data, &data_len, 0, + NULL, 0, 0) == 0) { + i = data[0]; + /* A packet was available in the packet brigade, burn it */ + LIBSSH2_FREE(session, data); + return i; + } #ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet becomes available to burn"); + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet becomes available to burn"); #endif - while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - int ret = libssh2_packet_read(session, 1); - if (ret < 0) { - return -1; - } - if (ret == 0) continue; + while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + int ret = libssh2_packet_read(session); + if (ret < 0) { + return ret; + } + if (ret == 0) { + /* FIXME: this might busyloop */ + continue; + } - /* Be lazy, let packet_ask pull it out of the brigade */ - if (0 == libssh2_packet_ask_ex(session, ret, &data, &data_len, 0, NULL, 0, 0)) { - /* Smoke 'em if you got 'em */ - LIBSSH2_FREE(session, data); - return ret; - } - } + /* Be lazy, let packet_ask pull it out of the brigade */ + if (0 == libssh2_packet_ask_ex(session, ret, &data, &data_len, + 0, NULL, 0, 0)) { + /* Smoke 'em if you got 'em */ + LIBSSH2_FREE(session, data); + return ret; + } + } - /* Only reached if the socket died */ - return -1; + /* Only reached if the socket died */ + return -1; } /* }}} */ /* {{{ libssh2_packet_requirev - * Loops libssh2_packet_read() until one of a list of packet types requested is available + * [BLOCKING] + * Loops libssh2_packet_read() until one of a list of packet types requested is + * available * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout * packet_types is a null terminated list of packet_type numbers */ -int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, - unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len) + +int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, + unsigned char *packet_types, + unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, + const unsigned char *match_buf, + unsigned long match_len) { - if (libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0) == 0) { - /* One of the packets listed was available in the packet brigade */ - return 0; - } + time_t start = time(NULL); - while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) { - int ret = libssh2_packet_read(session, 1); - if (ret < 0) { - return -1; - } - if (ret == 0) { - continue; - } + if (libssh2_packet_askv_ex(session, packet_types, data, data_len, + match_ofs, match_buf, match_len, 0) == 0) { + /* One of the packets listed was available in the packet + brigade */ + return 0; + } - if (strchr((char *)packet_types, ret)) { - /* Be lazy, let packet_ask pull it out of the brigade */ - return libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0); - } - } + while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) { + int ret = libssh2_packet_read(session); + if ((ret < 0) && (ret != PACKET_EAGAIN)) { + return ret; + } + if (ret <= 0) { + long left = LIBSSH2_READ_TIMEOUT - + (time(NULL) - start); - /* Only reached if the socket died */ - return -1; -} -/* }}} */ - -/* {{{ libssh2_packet_write - * Send a packet, encrypting it and adding a MAC code if necessary - * Returns 0 on success, non-zero on failure - */ -int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len) -{ - unsigned long packet_length = data_len + 1; - unsigned long block_size = (session->state & LIBSSH2_STATE_NEWKEYS) ? session->local.crypt->blocksize : 8; - /* At this point packet_length doesn't include the packet_len field itself */ - unsigned long padding_length; - int free_data = 0; - unsigned char buf[246]; /* 6 byte header plus max padding size(240) */ - -#ifdef LIBSSH2_DEBUG_TRANSPORT -{ - /* Show a hint of what's being sent */ - char excerpt[32]; - int ex_len = 0, db_ofs = 0; - - for (; ex_len < 24 && db_ofs < data_len; ex_len += 3, db_ofs++) snprintf(excerpt + ex_len, 4, "%02X ", data[db_ofs]); - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet type %d, length=%lu, %s", (int)data[0], data_len, excerpt); -} -#endif - if ((session->state & LIBSSH2_STATE_NEWKEYS) && - strcmp(session->local.comp->name, "none")) { - - if (session->local.comp->comp(session, 1, &data, &data_len, LIBSSH2_PACKET_MAXCOMP, &free_data, data, data_len, &session->local.comp_abstract)) { - return -1; - } -#ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Compressed payload to %lu bytes", data_len); -#endif - } - -#ifndef WIN32 - fcntl(session->socket_fd, F_SETFL, 0); -#else - { - u_long non_block = FALSE; - ioctlsocket(session->socket_fd, FIONBIO, &non_block); - } -#endif - - packet_length = data_len + 1; /* padding_length(1) -- MAC doesn't count -- Padding to be added soon */ - padding_length = block_size - ((packet_length + 4) % block_size); - if (padding_length < 4) { - padding_length += block_size; - } - /* TODO: Maybe add 1 or 2 times block_size to padding_length randomly -- shake things up a bit... */ - - packet_length += padding_length; - libssh2_htonu32(buf, packet_length); - buf[4] = padding_length; -#ifdef LIBSSH2_DEBUG_TRANSPORT - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet with total length %lu (%lu bytes padding)", packet_length, padding_length); -#endif - - if (session->state & LIBSSH2_STATE_NEWKEYS) { - /* Encryption is in effect */ - unsigned char *encbuf, *s; - int ret, size, written = 0; - - /* include packet_length(4) itself and room for the hash at the end */ - encbuf = LIBSSH2_ALLOC(session, 4 + packet_length + session->local.mac->mac_len); - if (!encbuf) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate encryption buffer", 0); - if (free_data) { - LIBSSH2_FREE(session, data); - } - return -1; - } - - /* Copy packet to encoding buffer */ - memcpy(encbuf, buf, 5); - memcpy(encbuf + 5, data, data_len); - libssh2_random(encbuf + 5 + data_len, padding_length); - if (free_data) { - LIBSSH2_FREE(session, data); - } - - /* Calculate MAC hash */ - session->local.mac->hash(session, encbuf + 4 + packet_length , session->local.seqno, encbuf, 4 + packet_length, NULL, 0, &session->local.mac_abstract); - - /* Encrypt data */ - for(s = encbuf; (s - encbuf) < (4 + packet_length) ; s += session->local.crypt->blocksize) { - session->local.crypt->crypt(session, s, &session->local.crypt_abstract); - } - - session->local.seqno++; - - /* Send It */ - size = 4 + packet_length + session->local.mac->mac_len; - written = 0; - - while(written < size) { - ret = send(session->socket_fd, encbuf + written, size - written, LIBSSH2_SOCKET_SEND_FLAGS(session)); - if(ret > 0) written += ret; - else break; - } - - ret = written == size ? 0 : -1; - - /* Cleanup environment */ - LIBSSH2_FREE(session, encbuf); - - return ret; - } else { /* LIBSSH2_ENDPOINT_CRYPT_NONE */ - /* Simplified write for non-encrypted mode */ - struct iovec data_vector[3]; - - /* Using vectors means we don't have to alloc a new buffer -- a byte saved is a byte earned - * No MAC during unencrypted phase - */ - data_vector[0].iov_base = buf; - data_vector[0].iov_len = 5; - data_vector[1].iov_base = (char*)data; - data_vector[1].iov_len = data_len; - data_vector[2].iov_base = buf + 5; - data_vector[2].iov_len = padding_length; - - session->local.seqno++; - - /* Ignore this, it can't actually happen :) */ - if (free_data) { - LIBSSH2_FREE(session, data); - } - - return ((packet_length + 4) == writev(session->socket_fd, data_vector, 3)) ? 0 : 1; - } + if((left <= 0) || + (libssh2_waitsocket(session, left) <= 0 )) { + return PACKET_TIMEOUT; + } + } + + if (strchr((char *)packet_types, ret)) { + /* Be lazy, let packet_ask pull it out of the + brigade */ + return libssh2_packet_askv_ex(session, packet_types, + data, data_len, + match_ofs, match_buf, + match_len, 0); + } + } + + /* Only reached if the socket died */ + return -1; } /* }}} */ diff --git a/src/publickey.c b/src/publickey.c index ff331f0..524d7ca 100644 --- a/src/publickey.c +++ b/src/publickey.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; diff --git a/src/scp.c b/src/scp.c index 95e4d3f..8866b06 100644 --- a/src/scp.c +++ b/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; diff --git a/src/session.c b/src/session.c index 9d42c2d..14bd382 100644 --- a/src/session.c +++ b/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; diff --git a/src/sftp.c b/src/sftp.c index dcdf563..a6a801c 100644 --- a/src/sftp.c +++ b/src/sftp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org> +/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org> * All rights reserved. * * Redistribution and use in source and binary forms, @@ -46,73 +46,73 @@ */ /* SFTP packet types */ -#define SSH_FXP_INIT 1 -#define SSH_FXP_VERSION 2 -#define SSH_FXP_OPEN 3 -#define SSH_FXP_CLOSE 4 -#define SSH_FXP_READ 5 -#define SSH_FXP_WRITE 6 -#define SSH_FXP_LSTAT 7 -#define SSH_FXP_FSTAT 8 -#define SSH_FXP_SETSTAT 9 -#define SSH_FXP_FSETSTAT 10 -#define SSH_FXP_OPENDIR 11 -#define SSH_FXP_READDIR 12 -#define SSH_FXP_REMOVE 13 -#define SSH_FXP_MKDIR 14 -#define SSH_FXP_RMDIR 15 -#define SSH_FXP_REALPATH 16 -#define SSH_FXP_STAT 17 -#define SSH_FXP_RENAME 18 -#define SSH_FXP_READLINK 19 -#define SSH_FXP_SYMLINK 20 -#define SSH_FXP_STATUS 101 -#define SSH_FXP_HANDLE 102 -#define SSH_FXP_DATA 103 -#define SSH_FXP_NAME 104 -#define SSH_FXP_ATTRS 105 -#define SSH_FXP_EXTENDED 200 -#define SSH_FXP_EXTENDED_REPLY 201 +#define SSH_FXP_INIT 1 +#define SSH_FXP_VERSION 2 +#define SSH_FXP_OPEN 3 +#define SSH_FXP_CLOSE 4 +#define SSH_FXP_READ 5 +#define SSH_FXP_WRITE 6 +#define SSH_FXP_LSTAT 7 +#define SSH_FXP_FSTAT 8 +#define SSH_FXP_SETSTAT 9 +#define SSH_FXP_FSETSTAT 10 +#define SSH_FXP_OPENDIR 11 +#define SSH_FXP_READDIR 12 +#define SSH_FXP_REMOVE 13 +#define SSH_FXP_MKDIR 14 +#define SSH_FXP_RMDIR 15 +#define SSH_FXP_REALPATH 16 +#define SSH_FXP_STAT 17 +#define SSH_FXP_RENAME 18 +#define SSH_FXP_READLINK 19 +#define SSH_FXP_SYMLINK 20 +#define SSH_FXP_STATUS 101 +#define SSH_FXP_HANDLE 102 +#define SSH_FXP_DATA 103 +#define SSH_FXP_NAME 104 +#define SSH_FXP_ATTRS 105 +#define SSH_FXP_EXTENDED 200 +#define SSH_FXP_EXTENDED_REPLY 201 struct _LIBSSH2_SFTP { - LIBSSH2_CHANNEL *channel; + LIBSSH2_CHANNEL *channel; - unsigned long request_id, version; + unsigned long request_id, version; - LIBSSH2_PACKET_BRIGADE packets; + LIBSSH2_PACKET_BRIGADE packets; - LIBSSH2_SFTP_HANDLE *handles; + LIBSSH2_SFTP_HANDLE *handles; - unsigned long last_errno; + unsigned long last_errno; }; -#define LIBSSH2_SFTP_HANDLE_FILE 0 -#define LIBSSH2_SFTP_HANDLE_DIR 1 +#define LIBSSH2_SFTP_HANDLE_FILE 0 +#define LIBSSH2_SFTP_HANDLE_DIR 1 /* S_IFREG */ -#define LIBSSH2_SFTP_ATTR_PFILETYPE_FILE 0100000 +#define LIBSSH2_SFTP_ATTR_PFILETYPE_FILE 0100000 /* S_IFDIR */ -#define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000 +#define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000 struct _LIBSSH2_SFTP_HANDLE { - LIBSSH2_SFTP *sftp; - LIBSSH2_SFTP_HANDLE *prev, *next; + LIBSSH2_SFTP *sftp; + LIBSSH2_SFTP_HANDLE *prev, *next; - char *handle; - int handle_len; + char *handle; + int handle_len; - char handle_type; + char handle_type; - union _libssh2_sftp_handle_data { - struct _libssh2_sftp_handle_file_data { - libssh2_uint64_t offset; - } file; - struct _libssh2_sftp_handle_dir_data { - unsigned long names_left; - void *names_packet; - char *next_name; - } dir; - } u; + union _libssh2_sftp_handle_data { + struct _libssh2_sftp_handle_file_data { + libssh2_uint64_t offset; + } file; + struct _libssh2_sftp_handle_dir_data { + unsigned long names_left; + void *names_packet; + char *next_name; + } dir; + } u; }; /* {{{ libssh2_sftp_packet_add @@ -120,33 +120,33 @@ struct _LIBSSH2_SFTP_HANDLE { */ static int libssh2_sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data, unsigned long data_len) { - LIBSSH2_SESSION *session = sftp->channel->session; - LIBSSH2_PACKET *packet; + LIBSSH2_SESSION *session = sftp->channel->session; + LIBSSH2_PACKET *packet; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Received packet %d", (int)data[0]); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Received packet %d", (int)data[0]); #endif - packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate datablock for SFTP packet", 0); - return -1; - } - memset(packet, 0, sizeof(LIBSSH2_PACKET)); + packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate datablock for SFTP packet", 0); + return -1; + } + memset(packet, 0, sizeof(LIBSSH2_PACKET)); - packet->data = data; - packet->data_len = data_len; - packet->data_head = 5; - packet->brigade = &sftp->packets; - packet->next = NULL; - packet->prev = sftp->packets.tail; - if (packet->prev) { - packet->prev->next = packet; - } else { - sftp->packets.head = packet; - } - sftp->packets.tail = packet; + packet->data = data; + packet->data_len = data_len; + packet->data_head = 5; + packet->brigade = &sftp->packets; + packet->next = NULL; + packet->prev = sftp->packets.tail; + if (packet->prev) { + packet->prev->next = packet; + } else { + sftp->packets.head = packet; + } + sftp->packets.tail = packet; - return 0; + return 0; } /* }}} */ @@ -155,66 +155,59 @@ static int libssh2_sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data, unsi */ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block) { - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned char buffer[4]; /* To store the packet length */ - unsigned char *packet; - unsigned long packet_len, packet_received; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned char buffer[4]; /* To store the packet length */ + unsigned char *packet; + unsigned long packet_len, packet_received; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Waiting for packet: %s block", should_block ? "will" : "willnot"); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Waiting for packet: %s block", should_block ? "will" : "willnot"); #endif - if (should_block) { - libssh2_channel_set_blocking(channel, 1); - if (4 != libssh2_channel_read(channel, buffer, 4)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for FXP packet", 0); - return -1; - } - } else { - libssh2_channel_set_blocking(channel, 0); - if (1 != libssh2_channel_read(channel, buffer, 1)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for FXP packet", 0); - return 0; - } - libssh2_channel_set_blocking(channel, 1); - if (3 != libssh2_channel_read(channel, buffer + 1, 3)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for FXP packet", 0); - return -1; - } + libssh2_channel_set_blocking(channel, should_block); + if (4 != _libssh2_channel_read(channel, (char *)buffer, 4)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for FXP packet", 0); + return -1; } - packet_len = libssh2_ntohu32(buffer); + + packet_len = libssh2_ntohu32(buffer); #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Data begin - Packet Length: %lu", packet_len); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "Data begin - Packet Length: %lu", packet_len); #endif - if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN) { - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "SFTP packet too large", 0); - return -1; - } + if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "SFTP packet too large", 0); + return -1; + } - packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate SFTP packet", 0); - return -1; - } + packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate SFTP packet", 0); + return -1; + } - packet_received = 0; - while (packet_len > packet_received) { - long bytes_received = libssh2_channel_read(channel, packet + packet_received, packet_len - packet_received); + packet_received = 0; + while (packet_len > packet_received) { + long bytes_received = + _libssh2_channel_read(channel, + (char *)packet + packet_received, + packet_len - packet_received); - if (bytes_received < 0) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Receive error waiting for SFTP packet", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - packet_received += bytes_received; - } + if (bytes_received < 0) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Receive error waiting for SFTP packet", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + packet_received += bytes_received; + } - if (libssh2_sftp_packet_add(sftp, packet, packet_len)) { - LIBSSH2_FREE(session, packet); - return -1; - } + if (libssh2_sftp_packet_add(sftp, packet, packet_len)) { + LIBSSH2_FREE(session, packet); + return -1; + } - return packet[0]; + return packet[0]; } /* }}} */ @@ -223,52 +216,53 @@ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block) */ static int libssh2_sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type, unsigned long request_id, unsigned char **data, unsigned long *data_len, int poll_channel) { - LIBSSH2_SESSION *session = sftp->channel->session; - LIBSSH2_PACKET *packet = sftp->packets.head; - unsigned char match_buf[5]; - int match_len = 5; + LIBSSH2_SESSION *session = sftp->channel->session; + LIBSSH2_PACKET *packet = sftp->packets.head; + unsigned char match_buf[5]; + int match_len = 5; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Asking for %d packet", (int)packet_type); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Asking for %d packet", (int)packet_type); #endif - if (poll_channel) { - if (libssh2_sftp_packet_read(sftp, 0) < 0) { - return -1; - } - } + if (poll_channel) { + if (libssh2_sftp_packet_read(sftp, 0) < 0) { + return -1; + } + } - match_buf[0] = packet_type; - if (packet_type == SSH_FXP_VERSION) { - /* Special consideration when matching VERSION packet */ - match_len = 1; - } else { - libssh2_htonu32(match_buf + 1, request_id); - } + match_buf[0] = packet_type; + if (packet_type == SSH_FXP_VERSION) { + /* Special consideration when matching VERSION packet */ + match_len = 1; + } else { + libssh2_htonu32(match_buf + 1, request_id); + } - while (packet) { - if (strncmp(packet->data, match_buf, match_len) == 0) { - *data = packet->data; - *data_len = packet->data_len; + while (packet) { + if (strncmp((char *)packet->data, (char *)match_buf, + match_len) == 0) { + *data = packet->data; + *data_len = packet->data_len; - if (packet->prev) { - packet->prev->next = packet->next; - } else { - sftp->packets.head = packet->next; - } + if (packet->prev) { + packet->prev->next = packet->next; + } else { + sftp->packets.head = packet->next; + } - if (packet->next) { - packet->next->prev = packet->prev; - } else { - sftp->packets.tail = packet->prev; - } + if (packet->next) { + packet->next->prev = packet->prev; + } else { + sftp->packets.tail = packet->prev; + } - LIBSSH2_FREE(session, packet); + LIBSSH2_FREE(session, packet); - return 0; - } - packet = packet->next; - } - return -1; + return 0; + } + packet = packet->next; + } + return -1; } /* }}} */ @@ -277,59 +271,77 @@ static int libssh2_sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type */ static int libssh2_sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_type, unsigned long request_id, unsigned char **data, unsigned long *data_len) { - LIBSSH2_SESSION *session = sftp->channel->session; + LIBSSH2_SESSION *session = sftp->channel->session; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Requiring %d packet", (int)packet_type); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Requiring %d packet", (int)packet_type); #endif - if (libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0) == 0) { - /* A packet was available in the packet brigade */ - return 0; - } + if (libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0) == 0) { + /* A packet was available in the packet brigade */ + return 0; + } - while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - int ret = libssh2_sftp_packet_read(sftp, 1); - if (ret < 0) { - return -1; - } - if (ret == 0) continue; + while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + int ret = libssh2_sftp_packet_read(sftp, 1); + if (ret <= 0) { + return -1; + } - if (packet_type == ret) { - /* Be lazy, let packet_ask pull it out of the brigade */ - return libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0); - } - } + if (packet_type == ret) { + /* Be lazy, let packet_ask pull it out of the brigade */ + return libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0); + } + } - /* Only reached if the socket died */ - return -1; + /* Only reached if the socket died */ + return -1; } /* }}} */ /* {{{ libssh2_sftp_packet_requirev * Requie one of N possible reponses */ -static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_responses, unsigned char *valid_responses, unsigned long request_id, unsigned char **data, unsigned long *data_len) +static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp, + int num_valid_responses, + unsigned char *valid_responses, + unsigned long request_id, + unsigned char **data, + unsigned long *data_len) { - int i; + int i; + time_t start = time(NULL); - /* Flush */ - while (libssh2_sftp_packet_read(sftp, 0) > 0); + /* Flush */ + while (libssh2_sftp_packet_read(sftp, 0) > 0); - while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - int ret; - for(i = 0; i < num_valid_responses; i++) { - if (libssh2_sftp_packet_ask(sftp, valid_responses[i], request_id, data, data_len, 0) == 0) { - return 0; - } - } - ret = libssh2_sftp_packet_read(sftp, 1); - if (ret < 0) { - return -1; - } - if (ret == 0) continue; - } + while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + int ret; + for(i = 0; i < num_valid_responses; i++) { + if (libssh2_sftp_packet_ask(sftp, valid_responses[i], + request_id, data, + data_len, 0) == 0) { + return 0; + } + } - return -1; + ret = libssh2_sftp_packet_read(sftp, 1); + if (ret < 0) { + return -1; + } + if (ret == 0) { + /* prevent busy-looping */ + long left = LIBSSH2_READ_TIMEOUT - + (time(NULL) - start); + + if((left <= 0) || + (libssh2_waitsocket(sftp->channel->session, + left)<=0)) { + return PACKET_TIMEOUT; + } + } + } + + return -1; } /* }}} */ @@ -338,18 +350,18 @@ static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_respon */ static int libssh2_sftp_attrsize(LIBSSH2_SFTP_ATTRIBUTES *attrs) { - int attrsize = 4; /* flags(4) */ + int attrsize = 4; /* flags(4) */ - if (!attrs) { - return attrsize; - } + if (!attrs) { + return attrsize; + } - if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) attrsize += 8; - if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) attrsize += 8; - if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) attrsize += 4; - if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) attrsize += 8; /* atime + mtime as u32 */ + if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) attrsize += 8; + if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) attrsize += 8; + if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) attrsize += 4; + if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) attrsize += 8; /* atime + mtime as u32 */ - return attrsize; + return attrsize; } /* }}} */ @@ -358,37 +370,37 @@ static int libssh2_sftp_attrsize(LIBSSH2_SFTP_ATTRIBUTES *attrs) */ static int libssh2_sftp_attr2bin(unsigned char *p, LIBSSH2_SFTP_ATTRIBUTES *attrs) { - unsigned char *s = p; - unsigned long flag_mask = LIBSSH2_SFTP_ATTR_SIZE | LIBSSH2_SFTP_ATTR_UIDGID | LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME; + unsigned char *s = p; + unsigned long flag_mask = LIBSSH2_SFTP_ATTR_SIZE | LIBSSH2_SFTP_ATTR_UIDGID | LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME; - /* TODO: When we add SFTP4+ functionality flag_mask can get additional bits */ + /* TODO: When we add SFTP4+ functionality flag_mask can get additional bits */ - if (!attrs) { - libssh2_htonu32(s, 0); - return 4; - } + if (!attrs) { + libssh2_htonu32(s, 0); + return 4; + } - libssh2_htonu32(s, attrs->flags & flag_mask); s += 4; + libssh2_htonu32(s, attrs->flags & flag_mask); s += 4; - if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { - libssh2_htonu64(s, attrs->filesize); s += 8; - } + if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { + libssh2_htonu64(s, attrs->filesize); s += 8; + } - if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { - libssh2_htonu32(s, attrs->uid); s += 4; - libssh2_htonu32(s, attrs->gid); s += 4; - } + if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { + libssh2_htonu32(s, attrs->uid); s += 4; + libssh2_htonu32(s, attrs->gid); s += 4; + } - if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { - libssh2_htonu32(s, attrs->permissions); s += 4; - } + if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { + libssh2_htonu32(s, attrs->permissions); s += 4; + } - if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { - libssh2_htonu32(s, attrs->atime); s += 4; - libssh2_htonu32(s, attrs->mtime); s += 4; - } + if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { + libssh2_htonu32(s, attrs->atime); s += 4; + libssh2_htonu32(s, attrs->mtime); s += 4; + } - return (s - p); + return (s - p); } /* }}} */ @@ -396,30 +408,30 @@ static int libssh2_sftp_attr2bin(unsigned char *p, LIBSSH2_SFTP_ATTRIBUTES *attr */ static int libssh2_sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES *attrs, unsigned char *p) { - unsigned char *s = p; + unsigned char *s = p; - memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - attrs->flags = libssh2_ntohu32(s); s += 4; + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + attrs->flags = libssh2_ntohu32(s); s += 4; - if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { - attrs->filesize = libssh2_ntohu64(s); s += 8; - } + if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { + attrs->filesize = libssh2_ntohu64(s); s += 8; + } - if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { - attrs->uid = libssh2_ntohu32(s); s += 4; - attrs->gid = libssh2_ntohu32(s); s += 4; - } + if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { + attrs->uid = libssh2_ntohu32(s); s += 4; + attrs->gid = libssh2_ntohu32(s); s += 4; + } - if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { - attrs->permissions = libssh2_ntohu32(s); s += 4; - } + if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { + attrs->permissions = libssh2_ntohu32(s); s += 4; + } - if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { - attrs->atime = libssh2_ntohu32(s); s += 4; - attrs->mtime = libssh2_ntohu32(s); s += 4; - } + if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { + attrs->atime = libssh2_ntohu32(s); s += 4; + attrs->mtime = libssh2_ntohu32(s); s += 4; + } - return (s - p); + return (s - p); } /* }}} */ @@ -427,18 +439,24 @@ static int libssh2_sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES *attrs, unsigned char * * SFTP API * ************ */ +LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor); + /* {{{ libssh2_sftp_dtor * Shutdown an SFTP stream when the channel closes */ -LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor) { - LIBSSH2_SFTP *sftp = (LIBSSH2_SFTP*)(*channel_abstract); +LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor) +{ + LIBSSH2_SFTP *sftp = (LIBSSH2_SFTP*)(*channel_abstract); - /* Loop through handles closing them */ - while (sftp->handles) { - libssh2_sftp_close_handle(sftp->handles); - } + (void)session_abstract; + (void)channel; - LIBSSH2_FREE(session, sftp); + /* Loop through handles closing them */ + while (sftp->handles) { + libssh2_sftp_close_handle(sftp->handles); + } + + LIBSSH2_FREE(session, sftp); } /* }}} */ @@ -447,104 +465,109 @@ LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor) { */ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session) { - LIBSSH2_SFTP *sftp; - LIBSSH2_CHANNEL *channel; - unsigned char *data, *s, buffer[9]; /* sftp_header(5){excludes request_id} + version_id(4) */ - unsigned long data_len; + LIBSSH2_SFTP *sftp; + LIBSSH2_CHANNEL *channel; + unsigned char *data, *s, buffer[9]; /* sftp_header(5){excludes request_id} + version_id(4) */ + unsigned long data_len; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Initializing SFTP subsystem"); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Initializing SFTP subsystem"); #endif - channel = libssh2_channel_open_session(session); - if (!channel) { - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0); - return NULL; - } - if (libssh2_channel_subsystem(channel, "sftp")) { - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request SFTP subsystem", 0); - libssh2_channel_free(channel); - return NULL; - } + channel = libssh2_channel_open_session(session); + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0); + return NULL; + } - libssh2_channel_set_blocking(channel, 1); + if (libssh2_channel_subsystem(channel, "sftp")) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request SFTP subsystem", 0); + libssh2_channel_free(channel); + return NULL; + } - libssh2_channel_handle_extended_data(channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); + libssh2_channel_set_blocking(channel, 1); - sftp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP)); - if (!sftp) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new SFTP structure", 0); - libssh2_channel_free(channel); - return NULL; - } - memset(sftp, 0, sizeof(LIBSSH2_SFTP)); - sftp->channel = channel; - sftp->request_id = 0; + libssh2_channel_handle_extended_data(channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); - libssh2_htonu32(buffer, 5); - buffer[4] = SSH_FXP_INIT; - libssh2_htonu32(buffer + 5, LIBSSH2_SFTP_VERSION); + sftp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP)); + if (!sftp) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new SFTP structure", 0); + libssh2_channel_free(channel); + return NULL; + } + memset(sftp, 0, sizeof(LIBSSH2_SFTP)); + sftp->channel = channel; + sftp->request_id = 0; + + libssh2_htonu32(buffer, 5); + buffer[4] = SSH_FXP_INIT; + libssh2_htonu32(buffer + 5, LIBSSH2_SFTP_VERSION); #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending FXP_INIT packet advertising version %d support", (int)LIBSSH2_SFTP_VERSION); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending FXP_INIT packet advertising version %d support", (int)LIBSSH2_SFTP_VERSION); #endif - if (9 != libssh2_channel_write(channel, buffer, 9)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SSH_FXP_INIT", 0); - libssh2_channel_free(channel); - LIBSSH2_FREE(session, sftp); - return NULL; - } + if (9 != libssh2_channel_write(channel, (char *)buffer, 9)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send SSH_FXP_INIT", 0); + libssh2_channel_free(channel); + LIBSSH2_FREE(session, sftp); + return NULL; + } - if (libssh2_sftp_packet_require(sftp, SSH_FXP_VERSION, 0, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from SFTP subsystem", 0); - libssh2_channel_free(channel); - LIBSSH2_FREE(session, sftp); - return NULL; - } - if (data_len < 5) { - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Invalid SSH_FXP_VERSION response", 0); - libssh2_channel_free(channel); - LIBSSH2_FREE(session, sftp); - return NULL; - } + if (libssh2_sftp_packet_require(sftp, SSH_FXP_VERSION, 0, + &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for response from SFTP subsystem", 0); + libssh2_channel_free(channel); + LIBSSH2_FREE(session, sftp); + return NULL; + } + if (data_len < 5) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Invalid SSH_FXP_VERSION response", 0); + libssh2_channel_free(channel); + LIBSSH2_FREE(session, sftp); + return NULL; + } - s = data + 1; - sftp->version = libssh2_ntohu32(s); s += 4; - if (sftp->version > LIBSSH2_SFTP_VERSION) { + s = data + 1; + sftp->version = libssh2_ntohu32(s); s += 4; + if (sftp->version > LIBSSH2_SFTP_VERSION) { #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Truncating remote SFTP version from %lu", sftp->version); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Truncating remote SFTP version from %lu", sftp->version); #endif - sftp->version = LIBSSH2_SFTP_VERSION; - } + sftp->version = LIBSSH2_SFTP_VERSION; + } #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Enabling SFTP version %lu compatability", sftp->version); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Enabling SFTP version %lu compatability", sftp->version); #endif - while (s < (data + data_len)) { - char *extension_name, *extension_data; - unsigned long extname_len, extdata_len; + while (s < (data + data_len)) { + unsigned char *extension_name, *extension_data; + unsigned long extname_len, extdata_len; - extname_len = libssh2_ntohu32(s); s += 4; - extension_name = s; s += extname_len; + extname_len = libssh2_ntohu32(s); s += 4; + extension_name = s; s += extname_len; - extdata_len = libssh2_ntohu32(s); s += 4; - extension_data = s; s += extdata_len; + extdata_len = libssh2_ntohu32(s); s += 4; + extension_data = s; s += extdata_len; - /* TODO: Actually process extensions */ - } - LIBSSH2_FREE(session, data); + /* TODO: Actually process extensions */ + } + LIBSSH2_FREE(session, data); - /* Make sure that when the channel gets closed, the SFTP service is shut down too */ - sftp->channel->abstract = sftp; - sftp->channel->close_cb = libssh2_sftp_dtor; + /* Make sure that when the channel gets closed, the SFTP service is shut down too */ + sftp->channel->abstract = sftp; + sftp->channel->close_cb = libssh2_sftp_dtor; - return sftp; + return sftp; } /* }}} */ /* {{{ libssh2_sftp_shutdown * Shutsdown the SFTP subsystem */ -LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp) { - return libssh2_channel_free(sftp->channel); +LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp) +{ + return libssh2_channel_free(sftp->channel); } /* }}} */ @@ -557,350 +580,467 @@ LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp) { */ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *filename, unsigned int filename_len, unsigned long flags, long mode, int open_type) { - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - LIBSSH2_SFTP_HANDLE *fp; - LIBSSH2_SFTP_ATTRIBUTES attrs = { LIBSSH2_SFTP_ATTR_PERMISSIONS }; - unsigned long data_len, packet_len = filename_len + 13 + ((open_type == LIBSSH2_SFTP_OPENFILE) ? (4 + libssh2_sftp_attrsize(&attrs)) : 0); - /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) + flags(4) */ - unsigned char *packet, *data, *s; - unsigned char fopen_responses[2] = { SSH_FXP_HANDLE, SSH_FXP_STATUS }; - unsigned long request_id; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_SFTP_HANDLE *fp; + LIBSSH2_SFTP_ATTRIBUTES attrs = { + LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0 + }; + unsigned long data_len; + ssize_t packet_len = filename_len + 13 + ((open_type == LIBSSH2_SFTP_OPENFILE) ? (4 + libssh2_sftp_attrsize(&attrs)) : 0); + /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) + flags(4) */ + unsigned char *packet, *data, *s; + unsigned char fopen_responses[2] = { SSH_FXP_HANDLE, SSH_FXP_STATUS }; + unsigned long request_id; - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_OPEN or FXP_OPENDIR packet", 0); - return NULL; - } - /* Filetype in SFTP 3 and earlier */ - attrs.permissions = mode | ((open_type == LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE : LIBSSH2_SFTP_ATTR_PFILETYPE_DIR); + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_OPEN or FXP_OPENDIR packet", 0); + return NULL; + } + /* Filetype in SFTP 3 and earlier */ + attrs.permissions = mode | ((open_type == LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE : LIBSSH2_SFTP_ATTR_PFILETYPE_DIR); - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = (open_type == LIBSSH2_SFTP_OPENFILE) ? SSH_FXP_OPEN : SSH_FXP_OPENDIR; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, filename_len); s += 4; - memcpy(s, filename, filename_len); s += filename_len; - if (open_type == LIBSSH2_SFTP_OPENFILE) { - libssh2_htonu32(s, flags); s += 4; - s += libssh2_sftp_attr2bin(s, &attrs); - } + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = (open_type == LIBSSH2_SFTP_OPENFILE) ? SSH_FXP_OPEN : SSH_FXP_OPENDIR; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, filename_len); s += 4; + memcpy(s, filename, filename_len); s += filename_len; + if (open_type == LIBSSH2_SFTP_OPENFILE) { + libssh2_htonu32(s, flags); s += 4; + s += libssh2_sftp_attr2bin(s, &attrs); + } #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending %s open request", (open_type == LIBSSH2_SFTP_OPENFILE) ? "file" : "directory"); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending %s open request", (open_type == LIBSSH2_SFTP_OPENFILE) ? "file" : "directory"); #endif - if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_OPEN or FXP_OPENDIR command", 0); - LIBSSH2_FREE(session, packet); - return NULL; - } - LIBSSH2_FREE(session, packet); + if (packet_len != _libssh2_channel_write(channel, (char *)packet, + packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_OPEN or FXP_OPENDIR command", 0); + LIBSSH2_FREE(session, packet); + return NULL; + } + LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_requirev(sftp, 2, fopen_responses, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return NULL; - } + if (libssh2_sftp_packet_requirev(sftp, 2, fopen_responses, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return NULL; + } - if (data[0] == SSH_FXP_STATUS) { - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Failed opening remote file", 0); - sftp->last_errno = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - return NULL; - } + if (data[0] == SSH_FXP_STATUS) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Failed opening remote file", 0); + sftp->last_errno = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + return NULL; + } - fp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_HANDLE)); - if (!fp) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate new SFTP handle structure", 0); - LIBSSH2_FREE(session, data); - return NULL; - } - memset(fp, 0, sizeof(LIBSSH2_SFTP_HANDLE)); - fp->handle_type = (open_type == LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_HANDLE_FILE : LIBSSH2_SFTP_HANDLE_DIR; + fp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_HANDLE)); + if (!fp) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate new SFTP handle structure", 0); + LIBSSH2_FREE(session, data); + return NULL; + } + memset(fp, 0, sizeof(LIBSSH2_SFTP_HANDLE)); + fp->handle_type = (open_type == LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_HANDLE_FILE : LIBSSH2_SFTP_HANDLE_DIR; - fp->handle_len = libssh2_ntohu32(data + 5); - if (fp->handle_len > 256) { - /* SFTP doesn't allow handles longer than 256 characters */ - fp->handle_len = 256; - } - fp->handle = LIBSSH2_ALLOC(session, fp->handle_len); - if (!fp->handle) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for SFTP file/dir handle", 0); - LIBSSH2_FREE(session, data); - LIBSSH2_FREE(session, fp); - return NULL; - } - memcpy(fp->handle, data + 9, fp->handle_len); - LIBSSH2_FREE(session, data); + fp->handle_len = libssh2_ntohu32(data + 5); + if (fp->handle_len > 256) { + /* SFTP doesn't allow handles longer than 256 characters */ + fp->handle_len = 256; + } + fp->handle = LIBSSH2_ALLOC(session, fp->handle_len); + if (!fp->handle) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for SFTP file/dir handle", 0); + LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, fp); + return NULL; + } + memcpy(fp->handle, data + 9, fp->handle_len); + LIBSSH2_FREE(session, data); - /* Link the file and the sftp session together */ - fp->next = sftp->handles; - if (fp->next) { - fp->next->prev = fp; - } - fp->sftp = sftp; + /* Link the file and the sftp session together */ + fp->next = sftp->handles; + if (fp->next) { + fp->next->prev = fp; + } + fp->sftp = sftp; - fp->u.file.offset = 0; + fp->u.file.offset = 0; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Open command successful"); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Open command successful"); #endif - return fp; + return fp; } /* }}} */ -/* {{{ libssh2_sftp_read - * Read from an SFTP file handle +/* {{{ _libssh2_sftp_read + * Read from an SFTP file handle blocking/non-blocking depending on state */ -LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen) + +static ssize_t _libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, + char *buffer, size_t buffer_maxlen) { - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned long data_len, request_id; - unsigned long packet_len = handle->handle_len + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + length(4) */ - unsigned char *packet, *s, *data; - unsigned char read_responses[2] = { SSH_FXP_DATA, SSH_FXP_STATUS }; - size_t bytes_read = 0; + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id; + ssize_t packet_len = handle->handle_len + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + length(4) */ + unsigned char *packet, *s, *data; + unsigned char read_responses[2] = { SSH_FXP_DATA, SSH_FXP_STATUS }; + size_t bytes_read = 0; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading %lu bytes from SFTP handle", (unsigned long)buffer_maxlen); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading %lu bytes from SFTP handle", (unsigned long)buffer_maxlen); #endif - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0); - return -1; - } + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0); + return -1; + } - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = SSH_FXP_READ; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, handle->handle_len); s += 4; - memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; - libssh2_htonu64(s, handle->u.file.offset); s += 8; - libssh2_htonu32(s, buffer_maxlen); s += 4; + libssh2_htonu32(s, packet_len - 4); + s += 4; + *(s++) = SSH_FXP_READ; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); + s += 4; + libssh2_htonu32(s, handle->handle_len); + s += 4; - if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + memcpy(s, handle->handle, handle->handle_len); + s += handle->handle_len; - if (libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } + libssh2_htonu64(s, handle->u.file.offset); + s += 8; - switch (data[0]) { - case SSH_FXP_STATUS: - sftp->last_errno = libssh2_ntohu32(data + 5); - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - LIBSSH2_FREE(session, data); - return -1; - case SSH_FXP_DATA: - bytes_read = libssh2_ntohu32(data + 5); - if (bytes_read > (data_len - 9)) { - return -1; - } + libssh2_htonu32(s, buffer_maxlen); + s += 4; + + if (packet_len != libssh2_channel_write(channel, (char *)packet, + packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send FXP_READ command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id, + &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + return -1; + } + + switch (data[0]) { + case SSH_FXP_STATUS: + sftp->last_errno = libssh2_ntohu32(data + 5); + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "SFTP Protocol Error", 0); + LIBSSH2_FREE(session, data); + return -1; + case SSH_FXP_DATA: + bytes_read = libssh2_ntohu32(data + 5); + if (bytes_read > (data_len - 9)) { + return -1; + } #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu bytes returned", (unsigned long)bytes_read); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "%lu bytes returned", + (unsigned long)bytes_read); #endif - memcpy(buffer, data + 9, bytes_read); - handle->u.file.offset += bytes_read; - LIBSSH2_FREE(session, data); - return bytes_read; + memcpy(buffer, data + 9, bytes_read); + handle->u.file.offset += bytes_read; + LIBSSH2_FREE(session, data); + return bytes_read; + } + + return -1; +} +/* {{{ libssh2_sftp_read + * Read from an SFTP file handle blocking + */ +LIBSSH2_API ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, + char *buffer, size_t buffer_maxlen) +{ + ssize_t rc; + LIBSSH2_CHANNEL *ch = handle->sftp->channel; + int bl = libssh2_channel_get_blocking(ch); + + /* set blocking */ + libssh2_channel_set_blocking(ch, 1); + + rc = _libssh2_sftp_read(handle, buffer, buffer_maxlen); + + /* restore state */ + libssh2_channel_set_blocking(ch, bl); + + if(rc < 0) { + /* precent accidental returning of other return codes since + this API does not support/provide those */ + return -1; } - return -1; + return rc; +} +/* }}} */ + +/* {{{ libssh2_sftp_readnb + * Read from an SFTP file handle non-blocking + */ +LIBSSH2_API ssize_t libssh2_sftp_readnb(LIBSSH2_SFTP_HANDLE *handle, + char *buffer, size_t buffer_maxlen) +{ + ssize_t rc; + LIBSSH2_CHANNEL *ch = handle->sftp->channel; + int bl = libssh2_channel_get_blocking(ch); + + /* set non-blocking */ + libssh2_channel_set_blocking(ch, 0); + + rc = _libssh2_sftp_read(handle, buffer, buffer_maxlen); + + /* restore state */ + libssh2_channel_set_blocking(ch, bl); + + return rc; } /* }}} */ /* {{{ libssh2_sftp_readdir * Read from an SFTP directory handle */ -LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, LIBSSH2_SFTP_ATTRIBUTES *attrs) +LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, LIBSSH2_SFTP_ATTRIBUTES *attrs) { - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - LIBSSH2_SFTP_ATTRIBUTES attrs_dummy; - unsigned long data_len, request_id, filename_len, num_names; - unsigned long packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ - unsigned char *packet, *s, *data; - unsigned char read_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS }; + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_SFTP_ATTRIBUTES attrs_dummy; + unsigned long data_len, request_id, filename_len, num_names; + ssize_t packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ + unsigned char *packet, *s, *data; + unsigned char read_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS }; - if (handle->u.dir.names_left) { - /* A prior request returned more than one directory entry, feed it back from the buffer */ - unsigned char *s = handle->u.dir.next_name; - unsigned long real_filename_len = libssh2_ntohu32(s); + if (handle->u.dir.names_left) { + /* A prior request returned more than one directory entry, feed it back from the buffer */ + unsigned char *s = (unsigned char *)handle->u.dir.next_name; + unsigned long real_filename_len = libssh2_ntohu32(s); - filename_len = real_filename_len; s += 4; - if (filename_len > buffer_maxlen) { - filename_len = buffer_maxlen; - } - memcpy(buffer, s, filename_len); s += real_filename_len; - - /* The filename is not null terminated, make it so if possible */ - if (filename_len < buffer_maxlen) { - buffer[filename_len] = '\0'; - } + filename_len = real_filename_len; s += 4; + if (filename_len > buffer_maxlen) { + filename_len = buffer_maxlen; + } + memcpy(buffer, s, filename_len); s += real_filename_len; - /* Skip longname */ - s += 4 + libssh2_ntohu32(s); + /* The filename is not null terminated, make it so if possible */ + if (filename_len < buffer_maxlen) { + buffer[filename_len] = '\0'; + } - if (attrs) { - memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - } - s += libssh2_sftp_bin2attr(attrs ? attrs : &attrs_dummy, s); + /* Skip longname */ + s += 4 + libssh2_ntohu32(s); - handle->u.dir.next_name = s; - if ((--handle->u.dir.names_left) == 0) { - LIBSSH2_FREE(session, handle->u.dir.names_packet); - } + if (attrs) { + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + } + s += libssh2_sftp_bin2attr(attrs ? attrs : &attrs_dummy, s); - return filename_len; - } + handle->u.dir.next_name = (char *)s; + if ((--handle->u.dir.names_left) == 0) { + LIBSSH2_FREE(session, handle->u.dir.names_packet); + } - /* Request another entry(entries?) */ + return filename_len; + } - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_READDIR packet", 0); - return -1; - } + /* Request another entry(entries?) */ - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = SSH_FXP_READDIR; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, handle->handle_len); s += 4; - memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_READDIR packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_READDIR; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, handle->handle_len); s += 4; + memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading entries from directory handle"); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading entries from directory handle"); #endif - if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + if (packet_len != libssh2_channel_write(channel, (char *)packet, + packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } + if (libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } - if (data[0] == SSH_FXP_STATUS) { - int retcode; + if (data[0] == SSH_FXP_STATUS) { + int retcode; - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_EOF) { - return 0; - } else { - sftp->last_errno = retcode; - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - return -1; - } - } + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_EOF) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } + } - num_names = libssh2_ntohu32(data + 5); + num_names = libssh2_ntohu32(data + 5); #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu entries returned", num_names); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu entries returned", num_names); #endif - if (num_names <= 0) { - LIBSSH2_FREE(session, data); - return (num_names == 0) ? 0 : -1; - } + if (num_names <= 0) { + LIBSSH2_FREE(session, data); + return (num_names == 0) ? 0 : -1; + } - if (num_names == 1) { - unsigned long real_filename_len = libssh2_ntohu32(data + 9); + if (num_names == 1) { + unsigned long real_filename_len = libssh2_ntohu32(data + 9); - filename_len = real_filename_len; - if (filename_len > buffer_maxlen) { - filename_len = buffer_maxlen; - } - memcpy(buffer, data + 13, filename_len); + filename_len = real_filename_len; + if (filename_len > buffer_maxlen) { + filename_len = buffer_maxlen; + } + memcpy(buffer, data + 13, filename_len); - /* The filename is not null terminated, make it so if possible */ - if (filename_len < buffer_maxlen) { - buffer[filename_len] = '\0'; - } - - if (attrs) { - memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - libssh2_sftp_bin2attr(attrs, data + 13 + real_filename_len + (4 + libssh2_ntohu32(data + 13 + real_filename_len))); - } - LIBSSH2_FREE(session, data); + /* The filename is not null terminated, make it so if possible */ + if (filename_len < buffer_maxlen) { + buffer[filename_len] = '\0'; + } - return filename_len; - } + if (attrs) { + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + libssh2_sftp_bin2attr(attrs, data + 13 + real_filename_len + (4 + libssh2_ntohu32(data + 13 + real_filename_len))); + } + LIBSSH2_FREE(session, data); - handle->u.dir.names_left = num_names; - handle->u.dir.names_packet = data; - handle->u.dir.next_name = data + 9; + return filename_len; + } - /* Be lazy, just use the name popping mechanism from the start of the function */ - return libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs); + handle->u.dir.names_left = num_names; + handle->u.dir.names_packet = data; + handle->u.dir.next_name = (char *)data + 9; + + /* Be lazy, just use the name popping mechanism from the start of the function */ + return libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs); +} +/* }}} */ + +/* {{{ _libssh2_sftp_write + * Write data to a file handle + */ +static ssize_t _libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, + const char *buffer, + size_t count) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id, retcode; + ssize_t packet_len = handle->handle_len + count + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + count(4) */ + unsigned char *packet, *s, *data; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Writing %lu bytes", (unsigned long)count); +#endif + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_WRITE packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_WRITE; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, handle->handle_len); s += 4; + memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; + libssh2_htonu64(s, handle->u.file.offset); s += 8; + libssh2_htonu32(s, count); s += 4; + memcpy(s, buffer, count); s += count; + + if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_WRITE command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode == LIBSSH2_FX_OK) { + handle->u.file.offset += count; + return count; + } + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + sftp->last_errno = retcode; + + return -1; } /* }}} */ /* {{{ libssh2_sftp_write - * Write data to a file handle + * Write data to a SFTP handle blocking */ -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_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned long data_len, request_id, retcode; - unsigned long packet_len = handle->handle_len + count + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + count(4) */ - unsigned char *packet, *s, *data; + ssize_t rc; + LIBSSH2_CHANNEL *ch = handle->sftp->channel; + int bl = libssh2_channel_get_blocking(ch); -#ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Writing %lu bytes", (unsigned long)count); -#endif - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_WRITE packet", 0); - return -1; - } + /* set blocking */ + libssh2_channel_set_blocking(ch, 1); - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = SSH_FXP_WRITE; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, handle->handle_len); s += 4; - memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; - libssh2_htonu64(s, handle->u.file.offset); s += 8; - libssh2_htonu32(s, count); s += 4; - memcpy(s, buffer, count); s += count; + rc = _libssh2_sftp_write(handle, buffer, count); - if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_WRITE command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + /* restore state */ + libssh2_channel_set_blocking(ch, bl); - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } + return rc; +} +/* }}} */ - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); +/* {{{ libssh2_sftp_write + * Write data to a SFTP handle non-blocking + */ +LIBSSH2_API ssize_t libssh2_sftp_writenb(LIBSSH2_SFTP_HANDLE *handle, + const char *buffer, size_t count) +{ + ssize_t rc; + LIBSSH2_CHANNEL *ch = handle->sftp->channel; + int bl = libssh2_channel_get_blocking(ch); - if (retcode == LIBSSH2_FX_OK) { - handle->u.file.offset += count; - return count; - } - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - sftp->last_errno = retcode; + /* set non-blocking */ + libssh2_channel_set_blocking(ch, 0); - return -1; + rc = _libssh2_sftp_write(handle, buffer, count); + + /* restore state */ + libssh2_channel_set_blocking(ch, bl); + + return rc; } /* }}} */ @@ -909,63 +1049,63 @@ LIBSSH2_API size_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *b */ LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat) { - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned long data_len, request_id; - unsigned long packet_len = handle->handle_len + 13 + (setstat ? libssh2_sftp_attrsize(attrs) : 0); - /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ - unsigned char *packet, *s, *data; - unsigned char fstat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS }; + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id; + ssize_t packet_len = handle->handle_len + 13 + (setstat ? libssh2_sftp_attrsize(attrs) : 0); + /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ + unsigned char *packet, *s, *data; + unsigned char fstat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS }; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Issuing %s command", setstat ? "set-stat" : "stat"); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Issuing %s command", setstat ? "set-stat" : "stat"); #endif - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FSTAT/FSETSTAT packet", 0); - return -1; - } + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FSTAT/FSETSTAT packet", 0); + return -1; + } - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = setstat ? SSH_FXP_FSETSTAT : SSH_FXP_FSTAT; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, handle->handle_len); s += 4; - memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; - if (setstat) { - s += libssh2_sftp_attr2bin(s, attrs); - } + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = setstat ? SSH_FXP_FSETSTAT : SSH_FXP_FSTAT; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, handle->handle_len); s += 4; + memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; + if (setstat) { + s += libssh2_sftp_attr2bin(s, attrs); + } - if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, setstat ? "Unable to send FXP_FSETSTAT" : "Unable to send FXP_FSTAT command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, setstat ? (char *)"Unable to send FXP_FSETSTAT" : (char *)"Unable to send FXP_FSTAT command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_requirev(sftp, 2, fstat_responses, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } + if (libssh2_sftp_packet_requirev(sftp, 2, fstat_responses, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } - if (data[0] == SSH_FXP_STATUS) { - int retcode; + if (data[0] == SSH_FXP_STATUS) { + int retcode; - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_OK) { - return 0; - } else { - sftp->last_errno = retcode; - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - return -1; - } - } + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } + } - libssh2_sftp_bin2attr(attrs, data + 5); + libssh2_sftp_bin2attr(attrs, data + 5); - return 0; + return 0; } /* }}} */ @@ -974,7 +1114,7 @@ LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_ */ LIBSSH2_API void libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset) { - handle->u.file.offset = offset; + handle->u.file.offset = offset; } /* }}} */ @@ -983,7 +1123,7 @@ LIBSSH2_API void libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset) */ LIBSSH2_API size_t libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle) { - return handle->u.file.offset; + return handle->u.file.offset; } /* }}} */ @@ -994,66 +1134,66 @@ LIBSSH2_API size_t libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle) */ LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle) { - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned long data_len, retcode, request_id; - unsigned long packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ - unsigned char *packet, *s, *data; + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode, request_id; + ssize_t packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ + unsigned char *packet, *s, *data; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Closing handle"); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Closing handle"); #endif - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0); - return -1; - } + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0); + return -1; + } - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = SSH_FXP_CLOSE; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, handle->handle_len); s += 4; - memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_CLOSE; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, handle->handle_len); s += 4; + memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; - if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_CLOSE command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_CLOSE command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); - if (retcode != LIBSSH2_FX_OK) { - sftp->last_errno = retcode; - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - return -1; - } + if (retcode != LIBSSH2_FX_OK) { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } - if (handle == sftp->handles) { - sftp->handles = handle->next; - } - if (handle->next) { - handle->next->prev = NULL; - } + if (handle == sftp->handles) { + sftp->handles = handle->next; + } + if (handle->next) { + handle->next->prev = NULL; + } - if ((handle->handle_type == LIBSSH2_SFTP_HANDLE_DIR) && - handle->u.dir.names_left) { - LIBSSH2_FREE(session, handle->u.dir.names_packet); - } + if ((handle->handle_type == LIBSSH2_SFTP_HANDLE_DIR) && + handle->u.dir.names_left) { + LIBSSH2_FREE(session, handle->u.dir.names_packet); + } - LIBSSH2_FREE(session, handle->handle); - LIBSSH2_FREE(session, handle); + LIBSSH2_FREE(session, handle->handle); + LIBSSH2_FREE(session, handle); - return 0; + return 0; } /* }}} */ @@ -1066,131 +1206,133 @@ LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle) */ LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, char *filename, unsigned int filename_len) { - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned long data_len, retcode, request_id; - unsigned long packet_len = filename_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */ - unsigned char *packet, *s, *data; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode, request_id; + ssize_t packet_len = filename_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */ + unsigned char *packet, *s, *data; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Unlinking %s", filename); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Unlinking %s", filename); #endif - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_REMOVE packet", 0); - return -1; - } + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_REMOVE packet", 0); + return -1; + } - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = SSH_FXP_REMOVE; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, filename_len); s += 4; - memcpy(s, filename, filename_len); s += filename_len; + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_REMOVE; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, filename_len); s += 4; + memcpy(s, filename, filename_len); s += filename_len; - if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_REMOVE command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + if (packet_len != libssh2_channel_write(channel, (char *)packet, + packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_REMOVE command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_OK) { - return 0; - } else { - sftp->last_errno = retcode; - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - return -1; - } + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } } /* }}} */ /* {{{ libssh2_sftp_rename_ex * Rename a file on the remote server */ -LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, char *source_filename, unsigned int source_filename_len, - char *dest_filename, unsigned int dest_filename_len, - long flags) +LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, char *source_filename, unsigned int source_filename_len, + char *dest_filename, unsigned int dest_filename_len, + long flags) { - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned long data_len, retcode = -1, request_id; - unsigned long packet_len = source_filename_len + dest_filename_len + 17 + (sftp->version >= 5 ? 4 : 0); - /* packet_len(4) + packet_type(1) + request_id(4) + - source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */ - unsigned char *packet, *s, *data; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode = -1, request_id; + ssize_t packet_len = source_filename_len + dest_filename_len + 17 + (sftp->version >= 5 ? 4 : 0); + /* packet_len(4) + packet_type(1) + request_id(4) + + source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */ + unsigned char *packet, *s, *data; - if (sftp->version < 2) { - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Server does not support RENAME", 0); - return -1; - } + if (sftp->version < 2) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Server does not support RENAME", 0); + return -1; + } #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Renaming %s to %s", source_filename, dest_filename); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Renaming %s to %s", source_filename, dest_filename); #endif - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_RENAME packet", 0); - return -1; - } + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_RENAME packet", 0); + return -1; + } - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = SSH_FXP_RENAME; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, source_filename_len); s += 4; - memcpy(s, source_filename, source_filename_len); s += source_filename_len; - libssh2_htonu32(s, dest_filename_len); s += 4; - memcpy(s, dest_filename, dest_filename_len); s += dest_filename_len; + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_RENAME; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, source_filename_len); s += 4; + memcpy(s, source_filename, source_filename_len); s += source_filename_len; + libssh2_htonu32(s, dest_filename_len); s += 4; + memcpy(s, dest_filename, dest_filename_len); s += dest_filename_len; - if (sftp->version >= 5) { - libssh2_htonu32(s, flags); s += 4; - } + if (sftp->version >= 5) { + libssh2_htonu32(s, flags); s += 4; + } - if (packet_len != libssh2_channel_write(channel, packet, s - packet)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_RENAME command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + if (packet_len != libssh2_channel_write(channel, (char *)packet, + s - packet)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_RENAME command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); - switch (retcode) { - case LIBSSH2_FX_OK: - retcode = 0; - break; - case LIBSSH2_FX_FILE_ALREADY_EXISTS: - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "File already exists and SSH_FXP_RENAME_OVERWRITE not specified", 0); - sftp->last_errno = retcode; - retcode = -1; - break; - case LIBSSH2_FX_OP_UNSUPPORTED: - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Operation Not Supported", 0); - sftp->last_errno = retcode; - retcode = -1; - break; - default: - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - sftp->last_errno = retcode; - retcode = -1; - } + switch (retcode) { + case LIBSSH2_FX_OK: + retcode = 0; + break; + case LIBSSH2_FX_FILE_ALREADY_EXISTS: + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "File already exists and SSH_FXP_RENAME_OVERWRITE not specified", 0); + sftp->last_errno = retcode; + retcode = -1; + break; + case LIBSSH2_FX_OP_UNSUPPORTED: + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Operation Not Supported", 0); + sftp->last_errno = retcode; + retcode = -1; + break; + default: + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + sftp->last_errno = retcode; + retcode = -1; + } - return retcode; + return retcode; } /* }}} */ @@ -1199,55 +1341,58 @@ LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, char *source_filenam */ LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, char *path, unsigned int path_len, long mode) { - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - LIBSSH2_SFTP_ATTRIBUTES attrs = { LIBSSH2_SFTP_ATTR_PERMISSIONS }; - unsigned long data_len, retcode, request_id; - unsigned long packet_len = path_len + 13 + libssh2_sftp_attrsize(&attrs); - /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ - unsigned char *packet, *s, *data; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_SFTP_ATTRIBUTES attrs = { + LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0 + }; + unsigned long data_len, retcode, request_id; + ssize_t packet_len = path_len + 13 + libssh2_sftp_attrsize(&attrs); + /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + unsigned char *packet, *s, *data; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Creating directory %s with mode 0%lo", path, mode); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Creating directory %s with mode 0%lo", path, mode); #endif - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); - return -1; - } - /* Filetype in SFTP 3 and earlier */ - attrs.permissions = mode | LIBSSH2_SFTP_ATTR_PFILETYPE_DIR; + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); + return -1; + } + /* Filetype in SFTP 3 and earlier */ + attrs.permissions = mode | LIBSSH2_SFTP_ATTR_PFILETYPE_DIR; - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = SSH_FXP_MKDIR; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, path_len); s += 4; - memcpy(s, path, path_len); s += path_len; - s += libssh2_sftp_attr2bin(s, &attrs); + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_MKDIR; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, path_len); s += 4; + memcpy(s, path, path_len); s += path_len; + s += libssh2_sftp_attr2bin(s, &attrs); - if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_MKDIR command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + if (packet_len != libssh2_channel_write(channel, (char *)packet, + packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_MKDIR command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_OK) { - return 0; - } else { - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - sftp->last_errno = retcode; - return -1; - } + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + sftp->last_errno = retcode; + return -1; + } } /* }}} */ @@ -1256,50 +1401,50 @@ LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, char *path, unsigned i */ LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, char *path, unsigned int path_len) { - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned long data_len, retcode, request_id; - unsigned long packet_len = path_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ - unsigned char *packet, *s, *data; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode, request_id; + ssize_t packet_len = path_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + unsigned char *packet, *s, *data; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Removing directory: %s", path); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Removing directory: %s", path); #endif - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); - return -1; - } + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); + return -1; + } - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = SSH_FXP_RMDIR; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, path_len); s += 4; - memcpy(s, path, path_len); s += path_len; + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_RMDIR; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, path_len); s += 4; + memcpy(s, path, path_len); s += path_len; - if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_MKDIR command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_MKDIR command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_OK) { - return 0; - } else { - sftp->last_errno = retcode; - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - return -1; - } + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } } /* }}} */ @@ -1308,74 +1453,75 @@ LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, char *path, unsigned i */ LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, char *path, unsigned int path_len, int stat_type, LIBSSH2_SFTP_ATTRIBUTES *attrs) { - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned long data_len, request_id; - unsigned long packet_len = path_len + 13 + ((stat_type == LIBSSH2_SFTP_SETSTAT) ? libssh2_sftp_attrsize(attrs) : 0); - /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ - unsigned char *packet, *s, *data; - unsigned char stat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS }; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id; + ssize_t packet_len = path_len + 13 + ((stat_type == LIBSSH2_SFTP_SETSTAT) ? libssh2_sftp_attrsize(attrs) : 0); + /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + unsigned char *packet, *s, *data; + unsigned char stat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS }; #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s", (stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" : (stat_type == LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s", (stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" : (stat_type == LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path); #endif - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); - return -1; - } + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); + return -1; + } - libssh2_htonu32(s, packet_len - 4); s += 4; - switch (stat_type) { - case LIBSSH2_SFTP_SETSTAT: - *(s++) = SSH_FXP_SETSTAT; - break; - case LIBSSH2_SFTP_LSTAT: - *(s++) = SSH_FXP_LSTAT; - break; - case LIBSSH2_SFTP_STAT: - default: - *(s++) = SSH_FXP_STAT; - } - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, path_len); s += 4; - memcpy(s, path, path_len); s += path_len; - if (stat_type == LIBSSH2_SFTP_SETSTAT) { - s += libssh2_sftp_attr2bin(s, attrs); - } + libssh2_htonu32(s, packet_len - 4); s += 4; + switch (stat_type) { + case LIBSSH2_SFTP_SETSTAT: + *(s++) = SSH_FXP_SETSTAT; + break; + case LIBSSH2_SFTP_LSTAT: + *(s++) = SSH_FXP_LSTAT; + break; + case LIBSSH2_SFTP_STAT: + default: + *(s++) = SSH_FXP_STAT; + } + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, path_len); s += 4; + memcpy(s, path, path_len); s += path_len; + if (stat_type == LIBSSH2_SFTP_SETSTAT) { + s += libssh2_sftp_attr2bin(s, attrs); + } - if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send STAT/LSTAT/SETSTAT command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + if (packet_len != libssh2_channel_write(channel, (char *)packet, + packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send STAT/LSTAT/SETSTAT command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_requirev(sftp, 2, stat_responses, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } + if (libssh2_sftp_packet_requirev(sftp, 2, stat_responses, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } - if (data[0] == SSH_FXP_STATUS) { - int retcode; + if (data[0] == SSH_FXP_STATUS) { + int retcode; - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_OK) { - return 0; - } else { - sftp->last_errno = retcode; - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - return -1; - } - } + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } + } - memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - libssh2_sftp_bin2attr(attrs, data + 5); - LIBSSH2_FREE(session, data); + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + libssh2_sftp_bin2attr(attrs, data + 5); + LIBSSH2_FREE(session, data); - return 0; + return 0; } /* }}} */ @@ -1384,92 +1530,93 @@ LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, char *path, unsigned in */ LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len, char *target, unsigned int target_len, int link_type) { - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned long data_len, request_id, link_len; - unsigned long packet_len = path_len + 13 + ((link_type == LIBSSH2_SFTP_SYMLINK) ? (4 + target_len) : 0); - /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ - unsigned char *packet, *s, *data; - unsigned char link_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS }; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id, link_len; + ssize_t packet_len = path_len + 13 + ((link_type == LIBSSH2_SFTP_SYMLINK) ? (4 + target_len) : 0); + /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + unsigned char *packet, *s, *data; + unsigned char link_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS }; - if ((sftp->version < 3) && - (link_type != LIBSSH2_SFTP_REALPATH)) { - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Server does not support SYMLINK or READLINK", 0); - return -1; - } + if ((sftp->version < 3) && + (link_type != LIBSSH2_SFTP_REALPATH)) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Server does not support SYMLINK or READLINK", 0); + return -1; + } - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for SYMLINK/READLINK/REALPATH packet", 0); - return -1; - } + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for SYMLINK/READLINK/REALPATH packet", 0); + return -1; + } #ifdef LIBSSH2_DEBUG_SFTP - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s on %s", (link_type == LIBSSH2_SFTP_SYMLINK) ? "Creating" : "Reading", - (link_type == LIBSSH2_SFTP_REALPATH) ? "realpath" : "symlink", path); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s on %s", (link_type == LIBSSH2_SFTP_SYMLINK) ? "Creating" : "Reading", + (link_type == LIBSSH2_SFTP_REALPATH) ? "realpath" : "symlink", path); #endif - libssh2_htonu32(s, packet_len - 4); s += 4; - switch (link_type) { - case LIBSSH2_SFTP_REALPATH: - *(s++) = SSH_FXP_REALPATH; - break; - case LIBSSH2_SFTP_SYMLINK: - *(s++) = SSH_FXP_SYMLINK; - break; - case LIBSSH2_SFTP_READLINK: - default: - *(s++) = SSH_FXP_READLINK; - } - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, path_len); s += 4; - memcpy(s, path, path_len); s += path_len; - if (link_type == LIBSSH2_SFTP_SYMLINK) { - libssh2_htonu32(s, target_len); s += 4; - memcpy(s, target, target_len); s += target_len; - } + libssh2_htonu32(s, packet_len - 4); s += 4; + switch (link_type) { + case LIBSSH2_SFTP_REALPATH: + *(s++) = SSH_FXP_REALPATH; + break; + case LIBSSH2_SFTP_SYMLINK: + *(s++) = SSH_FXP_SYMLINK; + break; + case LIBSSH2_SFTP_READLINK: + default: + *(s++) = SSH_FXP_READLINK; + } + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, path_len); s += 4; + memcpy(s, path, path_len); s += path_len; + if (link_type == LIBSSH2_SFTP_SYMLINK) { + libssh2_htonu32(s, target_len); s += 4; + memcpy(s, target, target_len); s += target_len; + } - if (packet_len != libssh2_channel_write(channel, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SYMLINK/READLINK command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + if (packet_len != libssh2_channel_write(channel, (char *)packet, + packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SYMLINK/READLINK command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_requirev(sftp, 2, link_responses, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } + if (libssh2_sftp_packet_requirev(sftp, 2, link_responses, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } - if (data[0] == SSH_FXP_STATUS) { - int retcode; + if (data[0] == SSH_FXP_STATUS) { + int retcode; - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_OK) { - return 0; - } else { - sftp->last_errno = retcode; - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - return -1; - } - } + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } + } - if (libssh2_ntohu32(data + 5) < 1) { - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Invalid READLINK/REALPATH response, no name entries", 0); - LIBSSH2_FREE(session, data); - return -1; - } + if (libssh2_ntohu32(data + 5) < 1) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Invalid READLINK/REALPATH response, no name entries", 0); + LIBSSH2_FREE(session, data); + return -1; + } - link_len = libssh2_ntohu32(data + 9); - if (link_len >= target_len) { - link_len = target_len - 1; - } - memcpy(target, data + 13, link_len); - target[link_len] = 0; - LIBSSH2_FREE(session, data); + link_len = libssh2_ntohu32(data + 9); + if (link_len >= target_len) { + link_len = target_len - 1; + } + memcpy(target, data + 13, link_len); + target[link_len] = 0; + LIBSSH2_FREE(session, data); - return link_len; + return link_len; } /* }}} */ @@ -1478,6 +1625,6 @@ LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, un */ LIBSSH2_API unsigned long libssh2_sftp_last_error(LIBSSH2_SFTP *sftp) { - return sftp->last_errno; + return sftp->last_errno; } /* }}} */ diff --git a/src/transport.c b/src/transport.c new file mode 100644 index 0000000..607362f --- /dev/null +++ b/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 diff --git a/ssh2_sample.c b/ssh2_sample.c deleted file mode 100644 index 4a3fab3..0000000 --- a/ssh2_sample.c +++ /dev/null @@ -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; -}