diff --git a/docs/Makefile.am b/docs/Makefile.am index d657cb2..4a985fc 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -1,4 +1,4 @@ -# $Id: Makefile.am,v 1.5 2007/04/22 19:51:53 jehousley Exp $ +# $Id: Makefile.am,v 1.6 2007/06/06 12:34:06 jehousley Exp $ EXTRA_DIST = template.3 @@ -10,4 +10,5 @@ dist_man_MANS = libssh2_channel_forward_accept.3 \ libssh2_session_free.3 libssh2_poll.3 libssh2_poll_channel_read.3 \ libssh2_sftp_read.3 libssh2_sftp_readnb.3 libssh2_sftp_readdir.3 \ libssh2_sftp_readdirnb.3 libssh2_sftp_mkdir_ex.3 \ - libssh2_sftp_mkdirnb_ex.3 + libssh2_sftp_mkdirnb_ex.3 libssh2_session_disconnect_ex.3 \ + libssh2_channel_wait_eof.3 diff --git a/docs/libssh2_channel_forward_accept.3 b/docs/libssh2_channel_forward_accept.3 index d1d998a..b40b46c 100644 --- a/docs/libssh2_channel_forward_accept.3 +++ b/docs/libssh2_channel_forward_accept.3 @@ -1,6 +1,6 @@ -.\" $Id: libssh2_channel_forward_accept.3,v 1.1 2006/12/21 14:09:12 bagder Exp $ +.\" $Id: libssh2_channel_forward_accept.3,v 1.2 2007/06/06 12:34:06 jehousley Exp $ .\" -.TH libssh2_channel_forward_accept 3 "14 Dec 2006" "libssh2 0.15" "libssh2 manual" +.TH libssh2_channel_forward_accept 3 "1 June 2007" "libssh2 0.15" "libssh2 manual" .SH NAME libssh2_channel_forward_accept - accept a queued connection .SH SYNOPSIS @@ -12,5 +12,8 @@ libssh2_channel_forward_accept - accept a queued connection \fBlibssh2_channel_forward_listen(3)\fP. .SH RETURN VALUE A newly allocated channel instance or NULL on failure. +.SH ERRORS +LIBSSH2_ERROR_EAGAIN +Marked for non-blocking I/O but the call would block. .SH "SEE ALSO" .BI libssh2_channel_forward_listen(3) diff --git a/docs/libssh2_channel_forward_listen_ex.3 b/docs/libssh2_channel_forward_listen_ex.3 index a80a1fb..0e24087 100644 --- a/docs/libssh2_channel_forward_listen_ex.3 +++ b/docs/libssh2_channel_forward_listen_ex.3 @@ -1,6 +1,6 @@ -.\" $Id: libssh2_channel_forward_listen_ex.3,v 1.2 2007/04/12 21:30:03 dfandrich Exp $ +.\" $Id: libssh2_channel_forward_listen_ex.3,v 1.3 2007/06/06 12:34:06 jehousley Exp $ .\" -.TH libssh2_channel_forward_listen_ex 3 "14 Dec 2006" "libssh2 0.15" "libssh2 manual" +.TH libssh2_channel_forward_listen_ex 3 "1 June 2007" "libssh2 0.15" "libssh2 manual" .SH NAME libssh2_channel_forward_listen_ex - listen to inbound connections .SH SYNOPSIS @@ -36,5 +36,8 @@ rejecting further attempts. \fIlibssh2_channel_forward_listen(3)\fP is a macro. .SH RETURN VALUE A newly allocated LIBSSH2_LISTENER instance or NULL on failure. +.SH ERRORS +LIBSSH2_ERROR_EAGAIN +Marked for non-blocking I/O but the call would block. .SH "SEE ALSO" .BI libssh2_channel_forward_accept(3) diff --git a/docs/libssh2_channel_read_ex.3 b/docs/libssh2_channel_read_ex.3 index 2c12bbd..e4b06eb 100644 --- a/docs/libssh2_channel_read_ex.3 +++ b/docs/libssh2_channel_read_ex.3 @@ -1,6 +1,6 @@ -.\" $Id: libssh2_channel_read_ex.3,v 1.5 2007/02/23 10:20:56 bagder Exp $ +.\" $Id: libssh2_channel_read_ex.3,v 1.6 2007/06/06 12:34:06 jehousley Exp $ .\" -.TH libssh2_channel_read_ex 3 "14 Dec 2006" "libssh2 0.15" "libssh2 manual" +.TH libssh2_channel_read_ex 3 "1 June 2007" "libssh2 0.15" "libssh2 manual" .SH NAME libssh2_channel_read_ex - read data from a channel stream .SH SYNOPSIS @@ -31,6 +31,8 @@ currently defines a stream ID of 1 to be the stderr substream. \fIlibssh2_channel_read(3)\fP and \fIlibssh2_channel_read_stderr(3)\fP are macros. .SH RETURN VALUE -Actual number of bytes read or negative on failure. +Actual number of bytes read or negative on failure. It returns +LIBSSH2CHANNEL_EAGAIN when it would otherwise block. While +LIBSSH2CHANNEL_EAGAIN is a negative number, it isn't really a failure per se. .SH "SEE ALSO" .BR libssh2_poll_channel_read(3) diff --git a/docs/libssh2_channel_readnb_ex.3 b/docs/libssh2_channel_readnb_ex.3 index ec2b839..5b6f072 100644 --- a/docs/libssh2_channel_readnb_ex.3 +++ b/docs/libssh2_channel_readnb_ex.3 @@ -1,6 +1,6 @@ -.\" $Id: libssh2_channel_readnb_ex.3,v 1.2 2007/02/23 10:20:56 bagder Exp $ +.\" $Id: libssh2_channel_readnb_ex.3,v 1.3 2007/06/06 12:34:06 jehousley Exp $ .\" -.TH libssh2_channel_read_ex 3 "14 Dec 2006" "libssh2 0.15" "libssh2 manual" +.TH libssh2_channel_read_ex 3 "1 June 2007" "libssh2 0.15" "libssh2 manual" .SH NAME libssh2_channel_read_ex - read data from a channel stream .SH SYNOPSIS diff --git a/docs/libssh2_channel_wait_eof.3 b/docs/libssh2_channel_wait_eof.3 new file mode 100644 index 0000000..f6824e5 --- /dev/null +++ b/docs/libssh2_channel_wait_eof.3 @@ -0,0 +1,19 @@ +.\" $Id: libssh2_channel_wait_eof.3,v 1.1 2007/06/06 12:34:06 jehousley Exp $ +.\" +.TH libssh2_channel_wait_eof 3 "1 June 2007" "libssh2 0.15" "libssh2 manual" +.SH NAME +libssh2_channel_wait_eof - wait for the remote to reply to an EOF request +.SH SYNOPSIS +#include + +int libssh2_channel_wait_eof(LIBSSH2_CHANNEL *channel); + +.SH DESCRIPTION +Wait for the remote end to acknowledge an EOF request. + +.SH RETURN VALUE +Return 0 on success or negative on failure. It returns +LIBSSH2CHANNEL_EAGAIN when it would otherwise block. While +LIBSSH2CHANNEL_EAGAIN is a negative number, it isn't really a failure per se. +.SH "SEE ALSO" +.BI libssh2_channel_send_eof(3), libssh2_channel_eof(3) diff --git a/docs/libssh2_channel_write_ex.3 b/docs/libssh2_channel_write_ex.3 index f2b0cdc..9fbf798 100644 --- a/docs/libssh2_channel_write_ex.3 +++ b/docs/libssh2_channel_write_ex.3 @@ -1,19 +1,19 @@ -.\" $Id: libssh2_channel_write_ex.3,v 1.1 2007/02/23 10:20:56 bagder Exp $ +.\" $Id: libssh2_channel_write_ex.3,v 1.2 2007/06/06 12:34:06 jehousley Exp $ .\" -.TH libssh2_channel_write_ex 3 "6 Feb 2007" "libssh2 0.15" "libssh2 manual" +.TH libssh2_channel_write_ex 3 "1 June 2007" "libssh2 0.15" "libssh2 manual" .SH NAME libssh2_channel_write_ex - write data to a channel stream blocking .SH SYNOPSIS #include -int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, - char *buf, size_t buflen); +size_t libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, + char *buf, size_t buflen); -int libssh2_channel_write(LIBSSH2_CHANNEL *channel, char *buf, - size_t buflen); +size_t libssh2_channel_write(LIBSSH2_CHANNEL *channel, char *buf, + size_t buflen); -int libssh2_channel_write_stderr(LIBSSH2_CHANNEL *channel, char *buf, - size_t buflen); +size_t libssh2_channel_write_stderr(LIBSSH2_CHANNEL *channel, char *buf, + size_t buflen); .SH DESCRIPTION Write data to a channel stream. All channel streams have one standard I/O substream (stream_id == 0), and may have up to 2^32 extended data streams as @@ -32,6 +32,8 @@ defines a stream ID of 1 to be the stderr substream. macros. .SH RETURN VALUE Actual number of bytes written or negative on failure. +LIBSSH2CHANNEL_EAGAIN when it would otherwise block. While +LIBSSH2CHANNEL_EAGAIN is a negative number, it isn't really a failure per se. .SH "SEE ALSO" .BR libssh2_channel_open_session(3) .BR libssh2_channel_read(3) diff --git a/docs/libssh2_session_disconnect_ex.3 b/docs/libssh2_session_disconnect_ex.3 new file mode 100644 index 0000000..bc5d5a4 --- /dev/null +++ b/docs/libssh2_session_disconnect_ex.3 @@ -0,0 +1,20 @@ +.\" $Id: libssh2_session_disconnect_ex.3,v 1.1 2007/06/06 12:34:07 jehousley Exp $ +.\" +.TH libssh2_session_disconnect_ex 3 "1 June 2007" "libssh2 0.15" "libssh2 manual" +.SH NAME +libssh2_session_disconnect_ex - terminate transport layer +.SH SYNOPSIS +#include + +int ibssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, const char *description, const char *lang); +.SH DESCRIPTION +Terminates the transport layer connection with the remote host. +Note that all authentication and connection layer objects become unusable +at this point and should be explicitly freed prior to disconnection. +.SH RETURN VALUE +0 on success, \-1 on failure +.SH ERRORS +LIBSSH2_ERROR_EAGAIN +Marked for non-blocking I/O but the call would block. +.SH "SEE ALSO" +.BI libssh2_session_init(3) diff --git a/docs/libssh2_session_init.3 b/docs/libssh2_session_init.3 index 6d51d12..0ec5146 100644 --- a/docs/libssh2_session_init.3 +++ b/docs/libssh2_session_init.3 @@ -1,6 +1,6 @@ -.\" $Id: libssh2_session_init.3,v 1.2 2007/04/12 21:30:03 dfandrich Exp $ +.\" $Id: libssh2_session_init.3,v 1.3 2007/06/06 12:34:07 jehousley Exp $ .\" -.TH libssh2_session_init 3 "14 Dec 2006" "libssh2 0.15" "libssh2 manual" +.TH libssh2_session_init 3 "1 June 2007" "libssh2 0.15" "libssh2 manual" .SH NAME libssh2_session_init - initializes an SSH session object .SH SYNOPSIS @@ -24,6 +24,9 @@ This method must be called first, prior to configuring session options or starting up an SSH session with a remote server. .SH RETURN VALUE Pointer to a newly allocated LIBSSH2_SESSION instance, or NULL on errors. +.SH ERRORS +LIBSSH2_ERROR_EAGAIN +Marked for non-blocking I/O but the call would block. .SH "SEE ALSO" .BI libssh2_session_free(3), .BI libssh2_session_startup(3) diff --git a/docs/libssh2_session_startup.3 b/docs/libssh2_session_startup.3 index 43e2d5b..eb2747f 100644 --- a/docs/libssh2_session_startup.3 +++ b/docs/libssh2_session_startup.3 @@ -1,6 +1,6 @@ -.\" $Id: libssh2_session_startup.3,v 1.2 2007/01/02 05:47:00 gusarov Exp $ +.\" $Id: libssh2_session_startup.3,v 1.3 2007/06/06 12:34:07 jehousley Exp $ .\" -.TH libssh2_session_startup 3 "14 Dec 2006" "libssh2 0.15" "libssh2 manual" +.TH libssh2_session_startup 3 "1 June 2007" "libssh2 0.15" "libssh2 manual" .SH NAME libssh2_session_startup - begin transport layer .SH SYNOPSIS @@ -11,6 +11,27 @@ int libssh2_session_startup(LIBSSH2_SESSION *session, int socket); Begin transport layer protocol negotiation with the connected host. .SH RETURN VALUE 0 on success, \-1 on failure +.SH ERRORS +LIBSSH2_ERROR_SOCKET_NONE +Bad socket provided. +.br +LIBSSH2_ERROR_BANNER_SEND +Error sending banner to remote host. +.br +LIBSSH2_ERROR_KEX_FAILURE +Unable to exchange encryption keys. +.br +LIBSSH2_ERROR_SOCKET_SEND +Unable to ask for ssh-userauth service. +.br +LIBSSH2_ERROR_SOCKET_DISCONNECT +Connection was lost. +.br +LIBSSH2_ERROR_PROTO +Invalid response received from server. +.br +LIBSSH2_ERROR_EAGAIN +Marked for non-blocking I/O but the call would block. .SH "SEE ALSO" .BI libssh2_session_free(3), .BI libssh2_session_init(3) diff --git a/docs/libssh2_sftp_init.3 b/docs/libssh2_sftp_init.3 index b4528c4..13b4bef 100644 --- a/docs/libssh2_sftp_init.3 +++ b/docs/libssh2_sftp_init.3 @@ -1,6 +1,6 @@ -.\" $Id: libssh2_sftp_init.3,v 1.2 2007/04/22 17:18:03 jehousley Exp $ +.\" $Id: libssh2_sftp_init.3,v 1.3 2007/06/06 12:34:07 jehousley Exp $ .\" -.TH libssh2_sftp_init 3 "23 Jan 2007" "libssh2 0.15" "libssh2 manual" +.TH libssh2_sftp_init 3 "1 June 2007" "libssh2 0.15" "libssh2 manual" .SH NAME libssh2_sftp_init - .SH SYNOPSIS @@ -17,5 +17,8 @@ session is complete, it must be destroyed using the \fIlibssh2_sftp_shutdown(3)\fP function. .SH RETURN VALUE A pointer to the newly allocated SFTP instance or NULL on failure. +.SH ERRORS +LIBSSH2_ERROR_EAGAIN +Marked for non-blocking I/O but the call would block. .SH "SEE ALSO" .BI libssh2_sftp_shutdown(3), libssh2_sftp_open_ex(3) diff --git a/example/simple/Makefile.am b/example/simple/Makefile.am index 254e41f..326c6f6 100644 --- a/example/simple/Makefile.am +++ b/example/simple/Makefile.am @@ -3,6 +3,7 @@ AUTOMAKE_OPTIONS = foreign nostdinc # samples noinst_PROGRAMS = ssh2 \ scp scp_nonblock \ + scp_write scp_write_nonblock \ sftp sftp_nonblock \ sftp_write sftp_write_nonblock \ sftp_mkdir sftp_mkdir_nonblock \ @@ -18,6 +19,10 @@ scp_SOURCES = scp.c scp_nonblock_SOURCES = scp_nonblock.c +scp_write_SOURCES = scp_write.c + +scp_write_nonblock_SOURCES = scp_write_nonblock.c + sftp_SOURCES = sftp.c sftp_nonblock_SOURCES = sftp_nonblock.c diff --git a/example/simple/scp.c b/example/simple/scp.c index e3c0910..a2c9cdc 100644 --- a/example/simple/scp.c +++ b/example/simple/scp.c @@ -1,5 +1,5 @@ /* - * $Id: scp.c,v 1.6 2007/04/26 23:59:14 gknauf Exp $ + * $Id: scp.c,v 1.7 2007/06/06 12:34:08 jehousley Exp $ * * Sample showing how to do a simple SCP transfer. */ @@ -19,6 +19,9 @@ # ifdef HAVE_UNISTD_H #include #endif +#ifdef HAVE_ARPA_INET_H +# include +#endif #ifdef HAVE_SYS_TIME_H # include #endif @@ -31,140 +34,142 @@ 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; + unsigned long hostaddr; + 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; + WSADATA wsadata; - WSAStartup(WINSOCK_VERSION, &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); + if (argc > 1) { + hostaddr = inet_addr(argv[1]); + } else { + hostaddr = htonl(0x7F000001); + } + if (argc > 2) { + username = argv[2]; + } + if (argc > 3) { + password = argv[3]; + } + if (argc > 4) { + scppath = argv[4]; + } + + /* 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; - } + sin.sin_family = AF_INET; + sin.sin_port = htons(22); + sin.sin_addr.s_addr = hostaddr; + if (connect(sock, (struct sockaddr*)(&sin), + sizeof(struct sockaddr_in)) != 0) { + fprintf(stderr, "failed to connect!\n"); + return -1; + } - /* Create a session instance - */ - session = libssh2_session_init(); - if(!session) - return -1; + /* Create a session instance + */ + session = libssh2_session_init(); + if(!session) + return -1; - /* trace transport layer stuff*/ - libssh2_trace(session, LIBSSH2_TRACE_TRANS); + /* ... 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; + } - /* ... 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); + fprintf(stderr, "Fingerprint: "); + for(i = 0; i < 16; i++) { + fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); + } + fprintf(stderr, "\n"); - /* 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 (auth_pw) { + /* We could authenticate via password */ + if (libssh2_userauth_password(session, username, password)) { + fprintf(stderr, "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)) { + fprintf(stderr, "\tAuthentication by public key failed\n"); + goto shutdown; + } + } - if(argc > 1) { - username = argv[1]; - } - if(argc > 2) { - password = argv[2]; - } - if(argc > 3) { - scppath = argv[3]; - } + /* Request a file via SCP */ + channel = libssh2_scp_recv(session, scppath, &fileinfo); - 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; - } + if (!channel) { + fprintf(stderr, "Unable to open a session\n"); + goto shutdown; + } - while(got < fileinfo.st_size) { - char mem[1024]; - int amount=sizeof(mem); + while(got < fileinfo.st_size) { + char mem[1024]; + int amount=sizeof(mem); - if((fileinfo.st_size -got) < amount) { - amount = fileinfo.st_size -got; - } + if((fileinfo.st_size -got) < amount) { + amount = fileinfo.st_size -got; + } - rc = libssh2_channel_read(channel, mem, amount); - if(rc == amount) { - write(2, mem, rc); - } - else { - fprintf(stderr, "libssh2_channel_read() failed: %d\n", - rc); - break; - } - got += rc; - } + rc = libssh2_channel_read(channel, mem, amount); + if(rc == amount) { + write(1, mem, rc); + } + else { + fprintf(stderr, "libssh2_channel_read() failed: %d\n", rc); + break; + } + got += rc; + } - libssh2_channel_free(channel); - channel = NULL; + libssh2_channel_free(channel); + channel = NULL; shutdown: - libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); - libssh2_session_free(session); + libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); + libssh2_session_free(session); #ifdef WIN32 - Sleep(1000); - closesocket(sock); + Sleep(1000); + closesocket(sock); #else - sleep(1); - close(sock); + sleep(1); + close(sock); #endif -printf("all done\n"); - return 0; + fprintf(stderr, "all done\n"); + return 0; } diff --git a/example/simple/scp_nonblock.c b/example/simple/scp_nonblock.c index 0753ad6..88e4618 100644 --- a/example/simple/scp_nonblock.c +++ b/example/simple/scp_nonblock.c @@ -1,5 +1,5 @@ /* - * $Id: scp_nonblock.c,v 1.3 2007/04/26 23:59:14 gknauf Exp $ + * $Id: scp_nonblock.c,v 1.4 2007/06/06 12:34:08 jehousley Exp $ * * Sample showing how to do SCP transfers in a non-blocking manner. */ @@ -19,6 +19,9 @@ # ifdef HAVE_UNISTD_H #include #endif +#ifdef HAVE_ARPA_INET_H +# include +#endif #ifdef HAVE_SYS_TIME_H # include #endif @@ -31,172 +34,183 @@ 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; - + unsigned long hostaddr; + 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); + 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. */ + + if (argc > 1) { + hostaddr = inet_addr(argv[1]); + } else { + hostaddr = htonl(0x7F000001); + } + if (argc > 2) { + username = argv[2]; + } + if (argc > 3) { + password = argv[3]; + } + if (argc > 4) { + scppath = argv[4]; + } + + /* 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 = hostaddr; + 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); + /* 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); - + + /* Create a session instance */ + session = libssh2_session_init(); + if (!session) + return -1; + + /* Since we have set non-blocking, tell libssh2 we are non-blocking */ + libssh2_session_set_blocking(session, 0); + + /* ... start it up. This will trade welcome banners, exchange keys, + * and setup crypto, compression, and MAC layers + */ + while ((rc = libssh2_session_startup(session, sock)) == LIBSSH2_ERROR_EAGAIN); + 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); + fprintf(stderr, "Fingerprint: "); + for(i = 0; i < 16; i++) { + fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); + } + fprintf(stderr, "\n"); + + if (auth_pw) { + /* We could authenticate via password */ + while ((rc = libssh2_userauth_password(session, username, password)) == LIBSSH2CHANNEL_EAGAIN); + if (rc) { + fprintf(stderr, "Authentication by password failed.\n"); + goto shutdown; + } + } else { + /* Or by public key */ + while ((rc = libssh2_userauth_publickey_fromfile(session, username, + "/home/username/.ssh/id_rsa.pub", + "/home/username/.ssh/id_rsa", + password)) == LIBSSH2CHANNEL_EAGAIN); + if (rc) { + fprintf(stderr, "\tAuthentication by public key failed\n"); + goto shutdown; + } + } + + /* Request a file via SCP */ + fprintf(stderr, "libssh2_scp_recv()!\n"); + do { + channel = libssh2_scp_recv(session, scppath, &fileinfo); + + if ((!channel) && (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { + fprintf(stderr, "Unable to open a session\n"); + goto shutdown; + } + } while (!channel); + 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_read(channel, mem, amount); + if (rc > 0) { + write(1, 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); + Sleep(1000); + closesocket(sock); #else - sleep(1); - close(sock); + sleep(1); + close(sock); #endif -printf("all done\n"); - return 0; + fprintf(stderr, "all done\n"); + return 0; } diff --git a/example/simple/scp_write.c b/example/simple/scp_write.c new file mode 100644 index 0000000..dd83e66 --- /dev/null +++ b/example/simple/scp_write.c @@ -0,0 +1,201 @@ +/* + * $Id: scp_write.c,v 1.1 2007/06/06 12:34:08 jehousley Exp $ + * + * Sample showing how to do a simple SCP transfer. + */ + +#include +#include + +#ifdef HAVE_WINSOCK2_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +# ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + unsigned long hostaddr; + 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 *loclfile=(char *)"scp_write.c"; + char *scppath=(char *)"/tmp/TEST"; + FILE *local; + int rc; + char mem[1024]; + size_t nread; + char *ptr; + struct stat fileinfo; + +#ifdef WIN32 + WSADATA wsadata; + + WSAStartup(WINSOCK_VERSION, &wsadata); +#endif + + if (argc > 1) { + hostaddr = inet_addr(argv[1]); + } else { + hostaddr = htonl(0x7F000001); + } + if (argc > 2) { + username = argv[2]; + } + if (argc > 3) { + password = argv[3]; + } + if(argc > 4) { + loclfile = argv[4]; + } + if (argc > 5) { + scppath = argv[5]; + } + + local = fopen(loclfile, "rb"); + if (!local) { + fprintf(stderr, "Can't local file %s\n", loclfile); + goto shutdown; + } + + stat(loclfile, &fileinfo); + + /* 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 = hostaddr; + if (connect(sock, (struct sockaddr*)(&sin), + sizeof(struct sockaddr_in)) != 0) { + fprintf(stderr, "failed to connect!\n"); + return -1; + } + + /* 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); + fprintf(stderr, "Fingerprint: "); + for(i = 0; i < 16; i++) { + fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); + } + fprintf(stderr, "\n"); + + if (auth_pw) { + /* We could authenticate via password */ + if (libssh2_userauth_password(session, username, password)) { + fprintf(stderr, "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)) { + fprintf(stderr, "\tAuthentication by public key failed\n"); + goto shutdown; + } + } + + //libssh2_trace(session, 0xFFFF); + + /* Request a file via SCP */ + channel = libssh2_scp_send(session, scppath, 0x1FF & fileinfo.st_mode, (unsigned long)fileinfo.st_size); + + if (!channel) { + fprintf(stderr, "Unable to open a session\n"); + goto shutdown; + } + + fprintf(stderr, "SCP session waiting to send file\n"); + do { + nread = fread(mem, 1, sizeof(mem), local); + if (nread <= 0) { + /* end of file */ + break; + } + ptr = mem; + + do { + /* write data in a loop until we block */ + rc = libssh2_channel_write(channel, ptr, nread); + ptr += rc; + nread -= nread; + } while (rc > 0); + } while (1); + + fprintf(stderr, "Sending EOF\n"); + libssh2_channel_send_eof(channel); + + fprintf(stderr, "Waiting for EOF\n"); + libssh2_channel_wait_eof(channel); + + fprintf(stderr, "Waiting for channel to close\n"); + libssh2_channel_wait_closed(channel); + +// fprintf(stderr, "Closing channel\n"); +// libssh2_channel_close(channel); + + 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 + fprintf(stderr, "all done\n"); + return 0; +} diff --git a/example/simple/scp_write_nonblock.c b/example/simple/scp_write_nonblock.c new file mode 100644 index 0000000..7394703 --- /dev/null +++ b/example/simple/scp_write_nonblock.c @@ -0,0 +1,222 @@ +/* + * $Id: scp_write_nonblock.c,v 1.1 2007/06/06 12:34:08 jehousley Exp $ + * + * Sample showing how to do a simple SCP transfer. + */ + +#include +#include + +#ifdef HAVE_WINSOCK2_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +# ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + unsigned long hostaddr; + 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 *loclfile=(char *)"scp_write.c"; + char *scppath=(char *)"/tmp/TEST"; + FILE *local; + int rc; + char mem[1024]; + size_t nread; + char *ptr; + struct stat fileinfo; + +#ifdef WIN32 + WSADATA wsadata; + + WSAStartup(WINSOCK_VERSION, &wsadata); +#endif + + if (argc > 1) { + hostaddr = inet_addr(argv[1]); + } else { + hostaddr = htonl(0x7F000001); + } + if (argc > 2) { + username = argv[2]; + } + if (argc > 3) { + password = argv[3]; + } + if(argc > 4) { + loclfile = argv[4]; + } + if (argc > 5) { + scppath = argv[5]; + } + + local = fopen(loclfile, "rb"); + if (!local) { + fprintf(stderr, "Can't local file %s\n", loclfile); + goto shutdown; + } + + stat(loclfile, &fileinfo); + + /* 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 = hostaddr; + 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; + + /* Since we have set non-blocking, tell libssh2 we are non-blocking */ + libssh2_session_set_blocking(session, 0); + + /* ... start it up. This will trade welcome banners, exchange keys, + * and setup crypto, compression, and MAC layers + */ + while ((rc = libssh2_session_startup(session, sock)) + == LIBSSH2_ERROR_EAGAIN); + 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); + fprintf(stderr, "Fingerprint: "); + for(i = 0; i < 16; i++) { + fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); + } + fprintf(stderr, "\n"); + + if (auth_pw) { + /* We could authenticate via password */ + while ((rc = libssh2_userauth_password(session, username, password)) == LIBSSH2CHANNEL_EAGAIN); + if (rc) { + fprintf(stderr, "Authentication by password failed.\n"); + goto shutdown; + } + } else { + /* Or by public key */ + while ((rc = libssh2_userauth_publickey_fromfile(session, username, + "/home/username/.ssh/id_rsa.pub", + "/home/username/.ssh/id_rsa", + password)) == LIBSSH2CHANNEL_EAGAIN); + if (rc) { + fprintf(stderr, "\tAuthentication by public key failed\n"); + goto shutdown; + } + } + +// libssh2_trace(session, 0xFF7D); + + /* Request a file via SCP */ + do { + channel = libssh2_scp_send(session, scppath, 0x1FF & fileinfo.st_mode, (unsigned long)fileinfo.st_size); + + if ((!channel) && (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { + fprintf(stderr, "Unable to open a session\n"); + goto shutdown; + } + } while (!channel); + + fprintf(stderr, "SCP session waiting to send file\n"); + do { + nread = fread(mem, 1, sizeof(mem), local); + if (nread <= 0) { + /* end of file */ + break; + } + ptr = mem; + + do { + /* write data in a loop until we block */ + while ((rc = libssh2_channel_write(channel, ptr, nread)) == LIBSSH2CHANNEL_EAGAIN); + if (rc < 0) { + fprintf(stderr, "ERROR %d\n", rc); + } + ptr += rc; + nread -= rc; + } while (nread > 0); + } while (1); + + fprintf(stderr, "Sending EOF\n"); + while (libssh2_channel_send_eof(channel) == LIBSSH2CHANNEL_EAGAIN); + + fprintf(stderr, "Waiting for EOF\n"); + while (libssh2_channel_wait_eof(channel) == LIBSSH2CHANNEL_EAGAIN); + + fprintf(stderr, "Waiting for channel to close\n"); + while (libssh2_channel_wait_closed(channel) == LIBSSH2CHANNEL_EAGAIN); + +// fprintf(stderr, "Closing channel\n"); +// while (libssh2_channel_close(channel) == LIBSSH2CHANNEL_EAGAIN); + + libssh2_channel_free(channel); + channel = NULL; + + shutdown: + + while ((rc = libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing")) == LIBSSH2CHANNEL_EAGAIN); + libssh2_session_free(session); + +#ifdef WIN32 + Sleep(1000); + closesocket(sock); +#else + sleep(1); + close(sock); +#endif + fprintf(stderr, "all done\n"); + return 0; +} diff --git a/example/simple/sftp.c b/example/simple/sftp.c index eba4042..553ec6c 100644 --- a/example/simple/sftp.c +++ b/example/simple/sftp.c @@ -1,5 +1,5 @@ /* - * $Id: sftp.c,v 1.7 2007/04/26 23:59:14 gknauf Exp $ + * $Id: sftp.c,v 1.8 2007/06/06 12:34:08 jehousley Exp $ * * Sample showing how to do SFTP transfers. * @@ -94,6 +94,9 @@ int main(int argc, char *argv[]) if(!session) return -1; + /* Since we have set non-blocking, tell libssh2 we are blocking */ + libssh2_session_set_blocking(session, 1); + /* ... start it up. This will trade welcome banners, exchange keys, * and setup crypto, compression, and MAC layers */ @@ -103,22 +106,25 @@ int main(int argc, char *argv[]) return -1; } + /* Since we have not set non-blocking, tell libssh2 we are blocking */ + libssh2_session_set_blocking(session, 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: "); + fprintf(stderr, "Fingerprint: "); for(i = 0; i < 16; i++) { - printf("%02X ", (unsigned char)fingerprint[i]); + fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); } - printf("\n"); + fprintf(stderr, "\n"); if (auth_pw) { /* We could authenticate via password */ if (libssh2_userauth_password(session, username, password)) { - printf("Authentication by password failed.\n"); + fprintf(stderr, "Authentication by password failed.\n"); goto shutdown; } } else { @@ -127,7 +133,7 @@ int main(int argc, char *argv[]) "/home/username/.ssh/id_rsa.pub", "/home/username/.ssh/id_rsa", password)) { - printf("\tAuthentication by public key failed\n"); + fprintf(stderr, "\tAuthentication by public key failed\n"); goto shutdown; } } @@ -141,7 +147,7 @@ int main(int argc, char *argv[]) } /* Since we have not set non-blocking, tell libssh2 we are blocking */ - libssh2_sftp_set_blocking(sftp_session, 1); + libssh2_session_set_blocking(session, 1); fprintf(stderr, "libssh2_sftp_open()!\n"); /* Request a file via SFTP */ @@ -160,7 +166,7 @@ int main(int argc, char *argv[]) fprintf(stderr, "libssh2_sftp_read()!\n"); rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem)); if (rc > 0) { - write(2, mem, rc); + write(1, mem, rc); } else { break; } @@ -181,6 +187,6 @@ int main(int argc, char *argv[]) sleep(1); close(sock); #endif -printf("all done\n"); +fprintf(stderr, "all done\n"); return 0; } diff --git a/example/simple/sftp_RW_nonblock.c b/example/simple/sftp_RW_nonblock.c index d76f3a1..df87ccc 100644 --- a/example/simple/sftp_RW_nonblock.c +++ b/example/simple/sftp_RW_nonblock.c @@ -1,5 +1,5 @@ /* - * $Id: sftp_RW_nonblock.c,v 1.3 2007/04/26 23:59:15 gknauf Exp $ + * $Id: sftp_RW_nonblock.c,v 1.4 2007/06/06 12:34:08 jehousley Exp $ * * Sample showing how to do SFTP transfers in a non-blocking manner. * @@ -106,6 +106,8 @@ int main(int argc, char *argv[]) return -1; } + libssh2_session_set_blocking(session, 0); + /* 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 diff --git a/example/simple/sftp_mkdir.c b/example/simple/sftp_mkdir.c index d83ed43..ad46000 100644 --- a/example/simple/sftp_mkdir.c +++ b/example/simple/sftp_mkdir.c @@ -1,5 +1,5 @@ /* - * $Id: sftp_mkdir.c,v 1.3 2007/04/26 23:59:15 gknauf Exp $ + * $Id: sftp_mkdir.c,v 1.4 2007/06/06 12:34:08 jehousley Exp $ * * Sample showing how to do SFTP mkdir * @@ -138,7 +138,7 @@ int main(int argc, char *argv[]) } /* Since we have not set non-blocking, tell libssh2 we are blocking */ - libssh2_sftp_set_blocking(sftp_session, 1); + libssh2_session_set_blocking(session, 1); fprintf(stderr, "libssh2_sftp_mkdir()!\n"); /* Make a directory via SFTP */ diff --git a/example/simple/sftp_mkdir_nonblock.c b/example/simple/sftp_mkdir_nonblock.c index d62d606..6351aef 100644 --- a/example/simple/sftp_mkdir_nonblock.c +++ b/example/simple/sftp_mkdir_nonblock.c @@ -1,5 +1,5 @@ /* - * $Id: sftp_mkdir_nonblock.c,v 1.3 2007/04/26 23:59:15 gknauf Exp $ + * $Id: sftp_mkdir_nonblock.c,v 1.4 2007/06/06 12:34:09 jehousley Exp $ * * Sample showing how to do SFTP non-blocking mkdir. * @@ -148,7 +148,7 @@ int main(int argc, char *argv[]) } /* Since we have set non-blocking, tell libssh2 we are non-blocking */ - libssh2_sftp_set_blocking(sftp_session, 0); + libssh2_session_set_blocking(session, 0); fprintf(stderr, "libssh2_sftp_mkdirnb()!\n"); /* Make a directory via SFTP */ diff --git a/example/simple/sftp_nonblock.c b/example/simple/sftp_nonblock.c index 289ef2e..43e993c 100644 --- a/example/simple/sftp_nonblock.c +++ b/example/simple/sftp_nonblock.c @@ -1,5 +1,5 @@ /* - * $Id: sftp_nonblock.c,v 1.7 2007/04/26 23:59:15 gknauf Exp $ + * $Id: sftp_nonblock.c,v 1.8 2007/06/06 12:34:09 jehousley Exp $ * * Sample showing how to do SFTP non-blocking transfers. * @@ -40,159 +40,162 @@ int main(int argc, char *argv[]) { - unsigned long hostaddr; - 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"; - int rc; - LIBSSH2_SFTP *sftp_session; - LIBSSH2_SFTP_HANDLE *sftp_handle; - + unsigned long hostaddr; + 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"; + int rc; + LIBSSH2_SFTP *sftp_session; + LIBSSH2_SFTP_HANDLE *sftp_handle; + #ifdef WIN32 - WSADATA wsadata; - - WSAStartup(WINSOCK_VERSION, &wsadata); + WSADATA wsadata; + + WSAStartup(WINSOCK_VERSION, &wsadata); #endif - - if (argc > 1) { - hostaddr = inet_addr(argv[1]); - } else { - hostaddr = htonl(0x7F000001); - } - - if(argc > 2) { - username = argv[2]; - } - if(argc > 3) { - password = argv[3]; - } - if(argc > 4) { - sftppath = argv[4]; - } - /* - * The application code is responsible for creating the socket - * and 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 = hostaddr; - 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. */ + + if (argc > 1) { + hostaddr = inet_addr(argv[1]); + } else { + hostaddr = htonl(0x7F000001); + } + + if (argc > 2) { + username = argv[2]; + } + if (argc > 3) { + password = argv[3]; + } + if (argc > 4) { + sftppath = argv[4]; + } + /* + * The application code is responsible for creating the socket + * and 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 = hostaddr; + 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); + /* 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; + + /* Since we have set non-blocking, tell libssh2 we are non-blocking */ + libssh2_session_set_blocking(session, 0); + + /* ... start it up. This will trade welcome banners, exchange keys, + * and setup crypto, compression, and MAC layers + */ + while ((rc = libssh2_session_startup(session, sock)) == LIBSSH2_ERROR_EAGAIN); + 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); + fprintf(stderr, "Fingerprint: "); + for(i = 0; i < 16; i++) { + fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); + } + fprintf(stderr, "\n"); + + if (auth_pw) { + /* We could authenticate via password */ + while ((rc = libssh2_userauth_password(session, username, password)) == LIBSSH2CHANNEL_EAGAIN); + if (rc) { + fprintf(stderr, "Authentication by password failed.\n"); + goto shutdown; + } + } else { + /* Or by public key */ + while ((rc = libssh2_userauth_publickey_fromfile(session, username, + "/home/username/.ssh/id_rsa.pub", + "/home/username/.ssh/id_rsa", + password)) == LIBSSH2CHANNEL_EAGAIN); + if (rc) { + fprintf(stderr, "\tAuthentication by public key failed\n"); + goto shutdown; + } + } + + fprintf(stderr, "libssh2_sftp_init()!\n"); + do { + sftp_session = libssh2_sftp_init(session); - /* 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 (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; - } - } - - fprintf(stderr, "libssh2_sftp_init()!\n"); - sftp_session = libssh2_sftp_init(session); - - if (!sftp_session) { - fprintf(stderr, "Unable to init SFTP session\n"); - goto shutdown; - } - - /* Since we have set non-blocking, tell libssh2 we are non-blocking */ - libssh2_sftp_set_blocking(sftp_session, 0); - - fprintf(stderr, "libssh2_sftp_open()!\n"); - /* 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 { - char mem[1024]; - - /* loop until we fail */ - fprintf(stderr, "libssh2_sftp_readnb()!\n"); + if ((!sftp_session) && (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { + fprintf(stderr, "Unable to init SFTP session\n"); + goto shutdown; + } + } while (!sftp_session); + + fprintf(stderr, "libssh2_sftp_open()!\n"); + /* 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 { + char mem[1024]; + + /* loop until we fail */ + fprintf(stderr, "libssh2_sftp_readnb()!\n"); while ((rc = libssh2_sftp_readnb(sftp_handle, mem, sizeof(mem))) == LIBSSH2SFTP_EAGAIN) { ; } - if (rc > 0) { - write(2, mem, rc); - } else { - break; + if (rc > 0) { + write(1, mem, rc); + } else { + break; } - } while (1); - - libssh2_sftp_close(sftp_handle); - libssh2_sftp_shutdown(sftp_session); - - shutdown: - - libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); - libssh2_session_free(session); - + } while (1); + + libssh2_sftp_close(sftp_handle); + libssh2_sftp_shutdown(sftp_session); + +shutdown: + + while ((rc = libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing")) == LIBSSH2_ERROR_EAGAIN); + libssh2_session_free(session); + #ifdef WIN32 - Sleep(1000); - closesocket(sock); + Sleep(1000); + closesocket(sock); #else - sleep(1); - close(sock); + sleep(1); + close(sock); #endif -printf("all done\n"); - return 0; + fprintf(stderr, "all done\n"); + return 0; } diff --git a/example/simple/sftp_write.c b/example/simple/sftp_write.c index 6e8dd5c..7a1c5df 100644 --- a/example/simple/sftp_write.c +++ b/example/simple/sftp_write.c @@ -1,5 +1,5 @@ /* - * $Id: sftp_write.c,v 1.3 2007/04/26 23:59:15 gknauf Exp $ + * $Id: sftp_write.c,v 1.4 2007/06/06 12:34:09 jehousley Exp $ * * Sample showing how to do SFTP write transfers. * @@ -37,47 +37,47 @@ int main(int argc, char *argv[]) { - unsigned long hostaddr; - int sock, i, auth_pw = 1; - struct sockaddr_in sin; - const char *fingerprint; - LIBSSH2_SESSION *session; - char *username=(char *)"username"; - char *password=(char *)"password"; + unsigned long hostaddr; + 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 *loclfile=(char *)"sftp_write.c"; - char *sftppath=(char *)"/tmp/TEST"; - int rc; + char *sftppath=(char *)"/tmp/TEST"; + int rc; FILE *local; - LIBSSH2_SFTP *sftp_session; - LIBSSH2_SFTP_HANDLE *sftp_handle; + LIBSSH2_SFTP *sftp_session; + LIBSSH2_SFTP_HANDLE *sftp_handle; char mem[1024]; size_t nread; char *ptr; #ifdef WIN32 - WSADATA wsadata; + WSADATA wsadata; - WSAStartup(WINSOCK_VERSION, &wsadata); + WSAStartup(WINSOCK_VERSION, &wsadata); #endif - if (argc > 1) { - hostaddr = inet_addr(argv[1]); - } else { - hostaddr = htonl(0x7F000001); - } + if (argc > 1) { + hostaddr = inet_addr(argv[1]); + } else { + hostaddr = htonl(0x7F000001); + } - if(argc > 2) { - username = argv[2]; - } - if(argc > 3) { - password = argv[3]; - } - if(argc > 4) { - loclfile = argv[4]; - } - if(argc > 5) { - sftppath = argv[5]; - } + if(argc > 2) { + username = argv[2]; + } + if(argc > 3) { + password = argv[3]; + } + if(argc > 4) { + loclfile = argv[4]; + } + if(argc > 5) { + sftppath = argv[5]; + } local = fopen(loclfile, "rb"); if (!local) { @@ -85,90 +85,95 @@ int main(int argc, char *argv[]) goto shutdown; } - /* - * The application code is responsible for creating the socket - * and establishing the connection - */ - sock = socket(AF_INET, SOCK_STREAM, 0); + /* + * The application code is responsible for creating the socket + * and 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 = hostaddr; - if (connect(sock, (struct sockaddr*)(&sin), - sizeof(struct sockaddr_in)) != 0) { - fprintf(stderr, "failed to connect!\n"); - return -1; - } + sin.sin_family = AF_INET; + sin.sin_port = htons(22); + sin.sin_addr.s_addr = hostaddr; + if (connect(sock, (struct sockaddr*)(&sin), + sizeof(struct sockaddr_in)) != 0) { + fprintf(stderr, "failed to connect!\n"); + return -1; + } - /* Create a session instance - */ - session = libssh2_session_init(); - if(!session) - return -1; + /* 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; - } + /* Since we have set non-blocking, tell libssh2 we are blocking */ + libssh2_session_set_blocking(session, 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"); + /* 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 (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; - } - } + 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; + } + } - fprintf(stderr, "libssh2_sftp_init()!\n"); - sftp_session = libssh2_sftp_init(session); + //libssh2_trace(session, 0xFFFF); - if (!sftp_session) { - fprintf(stderr, "Unable to init SFTP session\n"); - goto shutdown; - } + fprintf(stderr, "libssh2_sftp_init()!\n"); + sftp_session = libssh2_sftp_init(session); - /* Since we have not set non-blocking, tell libssh2 we are blocking */ - libssh2_sftp_set_blocking(sftp_session, 1); + if (!sftp_session) { + fprintf(stderr, "Unable to init SFTP session\n"); + goto shutdown; + } + + /* Since we have not set non-blocking, tell libssh2 we are blocking */ + libssh2_session_set_blocking(session, 1); - fprintf(stderr, "libssh2_sftp_open()!\n"); - /* Request a file via SFTP */ - sftp_handle = + fprintf(stderr, "libssh2_sftp_open()!\n"); + /* Request a file via SFTP */ + sftp_handle = libssh2_sftp_open(sftp_session, sftppath, - LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT, + LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR| LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH); - if (!sftp_handle) { - fprintf(stderr, "Unable to open file with SFTP\n"); - goto shutdown; - } - fprintf(stderr, "libssh2_sftp_open() is done, now send data!\n"); - do { + if (!sftp_handle) { + fprintf(stderr, "Unable to open file with SFTP\n"); + goto shutdown; + } + fprintf(stderr, "libssh2_sftp_open() is done, now send data!\n"); + do { nread = fread(mem, 1, sizeof(mem), local); if (nread <= 0) { /* end of file */ @@ -182,24 +187,24 @@ int main(int argc, char *argv[]) ptr += rc; nread -= nread; } while (rc > 0); - } while (1); + } while (1); fclose(local); - libssh2_sftp_close(sftp_handle); - libssh2_sftp_shutdown(sftp_session); + libssh2_sftp_close(sftp_handle); + libssh2_sftp_shutdown(sftp_session); shutdown: - libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); - libssh2_session_free(session); + libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); + libssh2_session_free(session); #ifdef WIN32 - Sleep(1000); - closesocket(sock); + Sleep(1000); + closesocket(sock); #else - sleep(1); - close(sock); + sleep(1); + close(sock); #endif printf("all done\n"); - return 0; + return 0; } diff --git a/example/simple/sftp_write_nonblock.c b/example/simple/sftp_write_nonblock.c index 1b20b84..647607d 100644 --- a/example/simple/sftp_write_nonblock.c +++ b/example/simple/sftp_write_nonblock.c @@ -1,5 +1,5 @@ /* - * $Id: sftp_write_nonblock.c,v 1.3 2007/04/26 23:59:15 gknauf Exp $ + * $Id: sftp_write_nonblock.c,v 1.4 2007/06/06 12:34:09 jehousley Exp $ * * Sample showing how to do SFTP non-blocking write transfers. * @@ -53,29 +53,29 @@ int main(int argc, char *argv[]) char mem[1024]; size_t nread; char *ptr; - + #ifdef WIN32 WSADATA wsadata; - + WSAStartup(WINSOCK_VERSION, &wsadata); #endif - + if (argc > 1) { hostaddr = inet_addr(argv[1]); } else { hostaddr = htonl(0x7F000001); } - - if(argc > 2) { + + if (argc > 2) { username = argv[2]; } - if(argc > 3) { + if (argc > 3) { password = argv[3]; } - if(argc > 4) { + if (argc > 4) { loclfile = argv[4]; } - if(argc > 5) { + if (argc > 5) { sftppath = argv[5]; } @@ -90,18 +90,18 @@ int main(int argc, char *argv[]) * and 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 = hostaddr; if (connect(sock, (struct sockaddr*)(&sin), - sizeof(struct sockaddr_in)) != 0) { + 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. */ + simplify the example code. */ #ifdef F_SETFL /* FIXME: this can/should be done in a more portable manner */ rc = fcntl(sock, F_GETFL, 0); @@ -109,69 +109,77 @@ int main(int argc, char *argv[]) #else #error "add support for setting the socket non-blocking here" #endif - + /* Create a session instance - */ + */ session = libssh2_session_init(); - if(!session) + if (!session) return -1; - + + /* Since we have set non-blocking, tell libssh2 we are non-blocking */ + libssh2_session_set_blocking(session, 0); + /* ... 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) { + * and setup crypto, compression, and MAC layers + */ + while ((rc = libssh2_session_startup(session, sock)) + == LIBSSH2_ERROR_EAGAIN); + 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 - */ + * 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 (auth_pw) { /* We could authenticate via password */ - if (libssh2_userauth_password(session, username, password)) { + while ((rc = libssh2_userauth_password(session, username, password)) == LIBSSH2CHANNEL_EAGAIN); + if (rc) { 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)) { + while ((rc = libssh2_userauth_publickey_fromfile(session, username, + "/home/username/.ssh/id_rsa.pub", + "/home/username/.ssh/id_rsa", + password)) == LIBSSH2CHANNEL_EAGAIN); + if (rc) { printf("\tAuthentication by public key failed\n"); goto shutdown; } } - + fprintf(stderr, "libssh2_sftp_init()!\n"); - sftp_session = libssh2_sftp_init(session); - - if (!sftp_session) { - fprintf(stderr, "Unable to init SFTP session\n"); - goto shutdown; - } - + do { + sftp_session = libssh2_sftp_init(session); + + if ((!sftp_session) && (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { + fprintf(stderr, "Unable to init SFTP session\n"); + goto shutdown; + } + } while (!sftp_session); + /* Since we have set non-blocking, tell libssh2 we are non-blocking */ - libssh2_sftp_set_blocking(sftp_session, 0); + libssh2_session_set_blocking(session, 0); fprintf(stderr, "libssh2_sftp_open()!\n"); /* Request a file via SFTP */ sftp_handle = libssh2_sftp_open(sftp_session, sftppath, - LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT, - LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR| - LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH); + LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, + LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR| + LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH); if (!sftp_handle) { fprintf(stderr, "Unable to open file with SFTP\n"); @@ -195,16 +203,16 @@ int main(int argc, char *argv[]) nread -= nread; } while (rc > 0); } while (1); - + fclose(local); libssh2_sftp_close(sftp_handle); libssh2_sftp_shutdown(sftp_session); - - shutdown: - - libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); + +shutdown: + + while ((rc = libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing")) == LIBSSH2_ERROR_EAGAIN); libssh2_session_free(session); - + #ifdef WIN32 Sleep(1000); closesocket(sock); @@ -212,6 +220,6 @@ int main(int argc, char *argv[]) sleep(1); close(sock); #endif -printf("all done\n"); + printf("all done\n"); return 0; } diff --git a/example/simple/sftpdir.c b/example/simple/sftpdir.c index 287df26..b4b4a39 100644 --- a/example/simple/sftpdir.c +++ b/example/simple/sftpdir.c @@ -1,5 +1,5 @@ /* - * $Id: sftpdir.c,v 1.4 2007/04/26 23:59:15 gknauf Exp $ + * $Id: sftpdir.c,v 1.5 2007/06/06 12:34:09 jehousley Exp $ * * Sample doing an SFTP directory listing. * @@ -138,7 +138,7 @@ int main(int argc, char *argv[]) } /* Since we have not set non-blocking, tell libssh2 we are blocking */ - libssh2_sftp_set_blocking(sftp_session, 1); + libssh2_session_set_blocking(session, 1); fprintf(stderr, "libssh2_sftp_opendir()!\n"); /* Request a dir listing via SFTP */ diff --git a/example/simple/sftpdir_nonblock.c b/example/simple/sftpdir_nonblock.c index e577bff..de745ec 100644 --- a/example/simple/sftpdir_nonblock.c +++ b/example/simple/sftpdir_nonblock.c @@ -1,5 +1,5 @@ /* - * $Id: sftpdir_nonblock.c,v 1.4 2007/04/26 23:59:15 gknauf Exp $ + * $Id: sftpdir_nonblock.c,v 1.5 2007/06/06 12:34:09 jehousley Exp $ * * Sample doing an SFTP directory listing. * @@ -148,7 +148,7 @@ int main(int argc, char *argv[]) } /* Since we have set non-blocking, tell libssh2 we are non-blocking */ - libssh2_sftp_set_blocking(sftp_session, 0); + libssh2_session_set_blocking(session, 0); fprintf(stderr, "libssh2_sftp_opendir()!\n"); /* Request a dir listing via SFTP */ diff --git a/include/libssh2.h b/include/libssh2.h index e4f186f..77fed6b 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -76,7 +76,7 @@ typedef long long libssh2_int64_t; #endif #define LIBSSH2_VERSION "0.15-CVS" -#define LIBSSH2_APINO 200507211326 +#define LIBSSH2_APINO 200706012030 /* Part of every banner, user specified or not */ #define LIBSSH2_SSH_BANNER "SSH-2.0-libssh2_" LIBSSH2_VERSION @@ -222,6 +222,7 @@ typedef struct _LIBSSH2_POLLFD { #define SSH_DISCONNECT_ILLEGAL_USER_NAME 15 /* Error Codes (defined by libssh2) */ +#define LIBSSH2_ERROR_NONE 0 #define LIBSSH2_ERROR_SOCKET_NONE -1 #define LIBSSH2_ERROR_BANNER_NONE -2 #define LIBSSH2_ERROR_BANNER_SEND -3 @@ -258,6 +259,7 @@ typedef struct _LIBSSH2_POLLFD { #define LIBSSH2_ERROR_INVAL -34 #define LIBSSH2_ERROR_INVALID_POLL_TYPE -35 #define LIBSSH2_ERROR_PUBLICKEY_PROTOCOL -36 +#define LIBSSH2_ERROR_EAGAIN -37 /* Session API */ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)), LIBSSH2_FREE_FUNC((*my_free)), LIBSSH2_REALLOC_FUNC((*my_realloc)), void *abstract); @@ -277,6 +279,7 @@ LIBSSH2_API const char *libssh2_hostkey_hash(LIBSSH2_SESSION *session, int hash_ LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session, int method_type, const char *prefs); LIBSSH2_API const char *libssh2_session_methods(LIBSSH2_SESSION *session, int method_type); LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg, int *errmsg_len, int want_buf); +LIBSSH2_API int libssh2_session_last_errno(LIBSSH2_SESSION *session); LIBSSH2_API int libssh2_session_flag(LIBSSH2_SESSION *session, int flag, int value); @@ -351,50 +354,45 @@ 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); -LIBSSH2_API int libssh2_channel_readnb_ex(LIBSSH2_CHANNEL *channel, - int stream_id, char *buf, - size_t buflen); +LIBSSH2_API 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)) +#define libssh2_channel_read_stderr(channel, buf, buflen) \ + libssh2_channel_read_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (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); LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, unsigned long *read_avail, unsigned long *window_size_initial); -#define libssh2_channel_window_read(channel) libssh2_channel_window_read_ex((channel), NULL, NULL) +#define libssh2_channel_window_read(channel) \ + libssh2_channel_window_read_ex((channel), NULL, NULL) 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); -LIBSSH2_API int libssh2_channel_writenb_ex(LIBSSH2_CHANNEL *channel, - int stream_id, const char *buf, - size_t buflen); +LIBSSH2_API size_t 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)) -#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_channel_write_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_session_set_blocking(LIBSSH2_SESSION* session, int blocking); +LIBSSH2_API int libssh2_session_get_blocking(LIBSSH2_SESSION* session); + 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_API int libssh2_channel_handle_extended_data2(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 * if LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE is passed, extended data will be read (FIFO) from the standard data channel @@ -411,6 +409,7 @@ LIBSSH2_API int libssh2_channel_get_exit_status(LIBSSH2_CHANNEL* channel); LIBSSH2_API int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel); LIBSSH2_API int libssh2_channel_eof(LIBSSH2_CHANNEL *channel); +LIBSSH2_API int libssh2_channel_wait_eof(LIBSSH2_CHANNEL *channel); LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel); LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel); LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel); diff --git a/include/libssh2_sftp.h b/include/libssh2_sftp.h index 5587da9..633d92f 100644 --- a/include/libssh2_sftp.h +++ b/include/libssh2_sftp.h @@ -166,8 +166,10 @@ struct _LIBSSH2_SFTP_ATTRIBUTES { #define LIBSSH2_FX_NO_MEDIA 13 #define LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM 14 #define LIBSSH2_FX_QUOTA_EXCEEDED 15 -#define LIBSSH2_FX_UNKNOWN_PRINCIPLE 16 -#define LIBSSH2_FX_LOCK_CONFlICT 17 +#define LIBSSH2_FX_UNKNOWN_PRINCIPLE 16 /* Initial mis-spelling */ +#define LIBSSH2_FX_UNKNOWN_PRINCIPAL 16 +#define LIBSSH2_FX_LOCK_CONFlICT 17 /* Initial mis-spelling */ +#define LIBSSH2_FX_LOCK_CONFLICT 17 #define LIBSSH2_FX_DIR_NOT_EMPTY 18 #define LIBSSH2_FX_NOT_A_DIRECTORY 19 #define LIBSSH2_FX_INVALID_FILENAME 20 @@ -244,9 +246,6 @@ LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, un #define libssh2_sftp_readlink(sftp, path, target, maxlen) libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), LIBSSH2_SFTP_READLINK) #define libssh2_sftp_realpath(sftp, path, target, maxlen) libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), LIBSSH2_SFTP_REALPATH) -LIBSSH2_API void libssh2_sftp_set_blocking(LIBSSH2_SFTP *session, int blocking); -LIBSSH2_API int libssh2_sftp_get_blocking(LIBSSH2_SFTP *session); - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/channel.c b/src/channel.c index 348bf7d..c8c4d4e 100644 --- a/src/channel.c +++ b/src/channel.c @@ -45,75 +45,6 @@ #endif -/* - * _libssh2_nonblock() sets the given socket to either blocking or - * non-blocking mode based on the 'nonblock' boolean argument. This function - * is copied from the libcurl sources with permission. - */ -static int _libssh2_nonblock(int sockfd, /* operate on this */ - int nonblock /* TRUE or FALSE */) -{ -#undef SETBLOCK -#define SETBLOCK 0 -#ifdef HAVE_O_NONBLOCK - /* most recent unix versions */ - int flags; - - flags = fcntl(sockfd, F_GETFL, 0); - if (nonblock) - return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); - else - return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); -#undef SETBLOCK -#define SETBLOCK 1 -#endif - -#if defined(HAVE_FIONBIO) && (SETBLOCK == 0) - /* older unix versions */ - int flags; - - flags = nonblock; - return ioctl(sockfd, FIONBIO, &flags); -#undef SETBLOCK -#define SETBLOCK 2 -#endif - -#if defined(HAVE_IOCTLSOCKET) && (SETBLOCK == 0) - /* Windows? */ - unsigned long flags; - flags = nonblock; - - return ioctlsocket(sockfd, FIONBIO, &flags); -#undef SETBLOCK -#define SETBLOCK 3 -#endif - -#if defined(HAVE_IOCTLSOCKET_CASE) && (SETBLOCK == 0) - /* presumably for Amiga */ - return IoctlSocket(sockfd, FIONBIO, (long)nonblock); -#undef SETBLOCK -#define SETBLOCK 4 -#endif - -#if defined(HAVE_SO_NONBLOCK) && (SETBLOCK == 0) - /* BeOS */ - long b = nonblock ? 1 : 0; - return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); -#undef SETBLOCK -#define SETBLOCK 5 -#endif - -#ifdef HAVE_DISABLED_NONBLOCKING - return 0; /* returns success */ -#undef SETBLOCK -#define SETBLOCK 6 -#endif - -#if (SETBLOCK == 0) -#error "no non-blocking method was found/used/set" -#endif -} - /* {{{ libssh2_channel_nextid * Determine the next channel ID we can use at our end */ @@ -173,161 +104,177 @@ LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long (channel)->session = (session); \ } -/* {{{ libssh2_channel_open_session +/* {{{ libssh2_channel_open_ex * 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_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) { static const 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 char *data = NULL; - unsigned long data_len; + unsigned char *s; + int rc; - _libssh2_debug(session, LIBSSH2_DBG_CONN, - "Opening Channel - win %d pack %d", - window_size, packet_size); - channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); - if (!channel) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate space for channel data", 0); - return NULL; - } - memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); + if (session->open_state == libssh2_NB_state_idle) { + session->open_channel = NULL; + session->open_packet = NULL; + session->open_data = NULL; + /* 17 = packet_type(1) + channel_type_len(4) + sender_channel(4) + window_size(4) + packet_size(4) */ + session->open_packet_len = channel_type_len + message_len + 17; + session->open_local_channel = libssh2_channel_nextid(session); - 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_FREE(session, channel); - return NULL; - } - memcpy(channel->channel_type, channel_type, channel_type_len); + /* Zero the whole thing out */ + memset(&session->open_packet_requirev_state, 0, sizeof(session->open_packet_requirev_state)); - /* REMEMBER: local as in locally sourced */ - channel->local.id = local_channel; - channel->remote.window_size = window_size; - channel->remote.window_size_initial = window_size; - channel->remote.packet_size = packet_size; + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Opening Channel - win %d pack %d", window_size, packet_size); + session->open_channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); + if (!session->open_channel) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for channel data", 0); + return NULL; + } + memset(session->open_channel, 0, sizeof(LIBSSH2_CHANNEL)); - libssh2_channel_add(session, channel); + session->open_channel->channel_type_len = channel_type_len; + session->open_channel->channel_type = LIBSSH2_ALLOC(session, channel_type_len); + if (!session->open_channel->channel_type) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating memory for channel type name", 0); + LIBSSH2_FREE(session, session->open_channel); + session->open_channel = NULL; + return NULL; + } + memcpy(session->open_channel->channel_type, channel_type, channel_type_len); - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate temporary space for packet", - 0); - goto channel_error; - } - *(s++) = SSH_MSG_CHANNEL_OPEN; - libssh2_htonu32(s, channel_type_len); - s += 4; + /* REMEMBER: local as in locally sourced */ + session->open_channel->local.id = session->open_local_channel; + session->open_channel->remote.window_size = window_size; + session->open_channel->remote.window_size_initial = window_size; + session->open_channel->remote.packet_size = packet_size; - memcpy(s, channel_type, channel_type_len); - s += channel_type_len; + libssh2_channel_add(session, session->open_channel); - libssh2_htonu32(s, local_channel); - s += 4; + s = session->open_packet = LIBSSH2_ALLOC(session, session->open_packet_len); + if (!session->open_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate temporary space for packet", 0); + goto channel_error; + } + *(s++) = SSH_MSG_CHANNEL_OPEN; + libssh2_htonu32(s, channel_type_len); s += 4; - libssh2_htonu32(s, window_size); - s += 4; + memcpy(s, channel_type, channel_type_len); s += channel_type_len; - libssh2_htonu32(s, packet_size); - s += 4; + libssh2_htonu32(s, session->open_local_channel); s += 4; - if (message && message_len) { - memcpy(s, message, message_len); - s += message_len; + 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; + } + + session->open_state = libssh2_NB_state_created; } - if (libssh2_packet_write(session, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send channel-open request", 0); - goto channel_error; + if (session->open_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->open_packet, session->open_packet_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending channel-open request", 0); + return NULL; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel-open request", 0); + goto channel_error; + } + + session->open_state = libssh2_NB_state_sent; } - if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, - 1, packet + 5 + channel_type_len, 4)) { - goto channel_error; + if (session->open_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &session->open_data, &session->open_data_len, 1, + session->open_packet + 5 + channel_type_len, 4, &session->open_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block", 0); + return NULL; + } + else if (rc) { + goto channel_error; + } + + if (session->open_data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) { + session->open_channel->remote.id = libssh2_ntohu32(session->open_data + 5); + session->open_channel->local.window_size = libssh2_ntohu32(session->open_data + 9); + session->open_channel->local.window_size_initial = libssh2_ntohu32(session->open_data + 9); + session->open_channel->local.packet_size = libssh2_ntohu32(session->open_data + 13); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Connection Established - ID: %lu/%lu win: %lu/%lu pack: %lu/%lu", + session->open_channel->local.id, session->open_channel->remote.id, + session->open_channel->local.window_size, session->open_channel->remote.window_size, + session->open_channel->local.packet_size, session->open_channel->remote.packet_size); + LIBSSH2_FREE(session, session->open_packet); + session->open_packet = NULL; + LIBSSH2_FREE(session, session->open_data); + session->open_data = NULL; + + session->open_state = libssh2_NB_state_idle; + return session->open_channel; + } + + if (session->open_data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Channel open failure", 0); + } } - 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); - _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_FREE(session, packet); - LIBSSH2_FREE(session, data); +channel_error: - return channel; + if (session->open_data) { + LIBSSH2_FREE(session, session->open_data); + session->open_data = NULL; } - - if (data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) { - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, - "Channel open failure", 0); + if (session->open_packet) { + LIBSSH2_FREE(session, session->open_packet); + session->open_packet = NULL; } - - channel_error: - - if (data) { - LIBSSH2_FREE(session, data); - } - if (packet) { - LIBSSH2_FREE(session, packet); - } - if (channel) { + if (session->open_channel) { unsigned char channel_id[4]; - LIBSSH2_FREE(session, channel->channel_type); + LIBSSH2_FREE(session, session->open_channel->channel_type); - if (channel->next) { - channel->next->prev = channel->prev; + if (session->open_channel->next) { + session->open_channel->next->prev = session->open_channel->prev; } - if (channel->prev) { - channel->prev->next = channel->next; + if (session->open_channel->prev) { + session->open_channel->prev->next = session->open_channel->next; } - if (session->channels.head == channel) { - session->channels.head = channel->next; + if (session->channels.head == session->open_channel) { + session->channels.head = session->open_channel->next; } - if (session->channels.tail == channel) { - session->channels.tail = channel->prev; + if (session->channels.tail == session->open_channel) { + session->channels.tail = session->open_channel->prev; } /* 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, 0) >= 0) || - (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_DATA, - &data, &data_len, 1, channel_id, - 4, 0) >= 0)) { - LIBSSH2_FREE(session, data); + libssh2_htonu32(channel_id, session->open_channel->local.id); + while ((libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_DATA, &session->open_data, &session->open_data_len, + 1, channel_id, 4, 0) >= 0) || + (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &session->open_data, &session->open_data_len, + 1, channel_id, 4, 0) >= 0)) { + LIBSSH2_FREE(session, session->open_data); + session->open_data = NULL; } - LIBSSH2_FREE(session, channel); + /* Free any state variables still holding data */ + if (session->open_channel->write_packet) { + LIBSSH2_FREE(session, session->open_channel->write_packet); + session->open_channel->write_packet = NULL; + } + + LIBSSH2_FREE(session, session->open_channel); + session->open_channel = NULL; } + session->open_state = libssh2_NB_state_idle; return NULL; } /* }}} */ @@ -335,34 +282,52 @@ libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *channel_type, /* {{{ libssh2_channel_direct_tcpip_ex * Tunnel TCP/IP connect through the SSH session to direct host/port */ -LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, const char *host, int port, const char *shost, int sport) +LIBSSH2_API LIBSSH2_CHANNEL * +libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, const char *host, int port, const char *shost, int sport) { LIBSSH2_CHANNEL *channel; - unsigned char *message, *s; - unsigned long host_len = strlen(host), shost_len = strlen(shost); - unsigned long message_len = host_len + shost_len + 16; /* host_len(4) + port(4) + shost_len(4) + sport(4) */ + unsigned char *s; - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting direct-tcpip session to from %s:%d to %s:%d", shost, sport, host, port); + if (session->direct_state == libssh2_NB_state_idle) { + session->direct_host_len = strlen(host); + session->direct_shost_len = strlen(shost); + /* host_len(4) + port(4) + shost_len(4) + sport(4) */ + session->direct_message_len = session->direct_host_len + session->direct_shost_len + 16; - s = message = LIBSSH2_ALLOC(session, message_len); - if (!message) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for direct-tcpip connection", 0); - return NULL; + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting direct-tcpip session to from %s:%d to %s:%d", + shost, sport, host, port); + + s = session->direct_message = LIBSSH2_ALLOC(session, session->direct_message_len); + if (!session->direct_message) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for direct-tcpip connection", 0); + return NULL; + } + libssh2_htonu32(s, session->direct_host_len); s += 4; + memcpy(s, host, session->direct_host_len); s += session->direct_host_len; + libssh2_htonu32(s, port); s += 4; + + libssh2_htonu32(s, session->direct_shost_len); s += 4; + memcpy(s, shost, session->direct_shost_len); s += session->direct_shost_len; + libssh2_htonu32(s, sport); s += 4; + + session->direct_state = libssh2_NB_state_created; } - libssh2_htonu32(s, host_len); s += 4; - memcpy(s, host, host_len); s += host_len; - libssh2_htonu32(s, port); s += 4; - libssh2_htonu32(s, shost_len); s += 4; - memcpy(s, shost, shost_len); s += shost_len; - 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 *)session->direct_message, session->direct_message_len); + if (!channel) { + if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) { + /* The error code is still set to LIBSSH2_ERROR_EAGAIN */ + return NULL; + } else { + LIBSSH2_FREE(session, session->direct_message); + session->direct_message = NULL; + return NULL; + } + } - channel = libssh2_channel_open_ex(session, "direct-tcpip", - sizeof("direct-tcpip") - 1, - LIBSSH2_CHANNEL_WINDOW_DEFAULT, - LIBSSH2_CHANNEL_PACKET_DEFAULT, - (char *)message, message_len); - LIBSSH2_FREE(session, message); + LIBSSH2_FREE(session, session->direct_message); + session->direct_message = NULL; return channel; } @@ -371,96 +336,132 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *se /* {{{ libssh2_channel_forward_listen_ex * Bind a port on the remote host and listen for connections */ -LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, const char *host, int port, int *bound_port, int queue_maxsize) +LIBSSH2_API LIBSSH2_LISTENER * +libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, const char *host, int port, int *bound_port, int queue_maxsize) { - unsigned char *packet, *s, *data; + unsigned char *s, *data; static const unsigned char reply_codes[3] = { SSH_MSG_REQUEST_SUCCESS, SSH_MSG_REQUEST_FAILURE, 0 }; unsigned long data_len; - unsigned long host_len = (host ? strlen(host) : (sizeof("0.0.0.0") - 1)); - unsigned long packet_len = host_len + (sizeof("tcpip-forward") - 1) + 14; - /* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */ + int rc; - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting tcpip-forward session for %s:%d", host, port); + if (session->fwdLstn_state == libssh2_NB_state_idle) { + session->fwdLstn_host_len = (host ? strlen(host) : (sizeof("0.0.0.0") - 1)); + /* 14 = packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */ + session->fwdLstn_packet_len = session->fwdLstn_host_len + (sizeof("tcpip-forward") - 1) + 14; - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); - return NULL; - } + /* Zero the whole thing out */ + memset(&session->fwdLstn_packet_requirev_state, 0, sizeof(session->fwdLstn_packet_requirev_state)); - *(s++) = SSH_MSG_GLOBAL_REQUEST; - libssh2_htonu32(s, sizeof("tcpip-forward") - 1); s += 4; - memcpy(s, "tcpip-forward", sizeof("tcpip-forward") - 1); s += sizeof("tcpip-forward") - 1; - *(s++) = 0xFF; /* want_reply */ + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting tcpip-forward session for %s:%d", host, port); - libssh2_htonu32(s, host_len); s += 4; - memcpy(s, host ? host : "0.0.0.0", host_len); s += host_len; - libssh2_htonu32(s, port); s += 4; - - if (libssh2_packet_write(session, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send global-request packet for forward listen request", 0); - LIBSSH2_FREE(session, packet); - return NULL; - } - LIBSSH2_FREE(session, packet); - - if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { - return NULL; - } - - if (data[0] == SSH_MSG_REQUEST_SUCCESS) { - LIBSSH2_LISTENER *listener; - - listener = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_LISTENER)); - if (!listener) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0); - LIBSSH2_FREE(session, data); + s = session->fwdLstn_packet = LIBSSH2_ALLOC(session, session->fwdLstn_packet_len); + if (!session->fwdLstn_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); return NULL; } - memset(listener, 0, sizeof(LIBSSH2_LISTENER)); - listener->session = session; - listener->host = LIBSSH2_ALLOC(session, host_len + 1); - if (!listener->host) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0); - LIBSSH2_FREE(session, listener); - LIBSSH2_FREE(session, data); + + *(s++) = SSH_MSG_GLOBAL_REQUEST; + libssh2_htonu32(s, sizeof("tcpip-forward") - 1); s += 4; + memcpy(s, "tcpip-forward", sizeof("tcpip-forward") - 1); s += sizeof("tcpip-forward") - 1; + *(s++) = 0xFF; /* want_reply */ + + libssh2_htonu32(s, session->fwdLstn_host_len); s += 4; + memcpy(s, host ? host : "0.0.0.0", session->fwdLstn_host_len); s += session->fwdLstn_host_len; + libssh2_htonu32(s, port); s += 4; + + session->fwdLstn_state = libssh2_NB_state_created; + } + + if (session->fwdLstn_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->fwdLstn_packet, session->fwdLstn_packet_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending global-request packet for forward listen request", 0); return NULL; } - memcpy(listener->host, host ? host : "0.0.0.0", host_len); - listener->host[host_len] = 0; - if (data_len >= 5 && !port) { - listener->port = libssh2_ntohu32(data + 1); - _libssh2_debug(session, LIBSSH2_DBG_CONN, - "Dynamic tcpip-forward port allocated: %d", - listener->port); - } else { - listener->port = port; + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send global-request packet for forward listen request", 0); + LIBSSH2_FREE(session, session->fwdLstn_packet); + session->fwdLstn_packet = NULL; + session->fwdLstn_state = libssh2_NB_state_idle; + return NULL; } + LIBSSH2_FREE(session, session->fwdLstn_packet); + session->fwdLstn_packet = NULL; - listener->queue_size = 0; - listener->queue_maxsize = queue_maxsize; - - listener->next = session->listeners; - listener->prev = NULL; - if (session->listeners) { - session->listeners->prev = listener; - } - session->listeners = listener; - - if (bound_port) { - *bound_port = listener->port; - } - - LIBSSH2_FREE(session, data); - return listener; + session->fwdLstn_state = libssh2_NB_state_sent; } - if (data[0] == SSH_MSG_REQUEST_FAILURE) { - LIBSSH2_FREE(session, data); - libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED, "Unable to complete request for forward-listen", 0); - return NULL; + if (session->fwdLstn_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 0, NULL, 0, + &session->fwdLstn_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block", 0); + return NULL; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_PROTO, "Unknown", 0); + session->fwdLstn_state = libssh2_NB_state_idle; + return NULL; + } + + if (data[0] == SSH_MSG_REQUEST_SUCCESS) { + LIBSSH2_LISTENER *listener; + + listener = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_LISTENER)); + if (!listener) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0); + LIBSSH2_FREE(session, data); + session->fwdLstn_state = libssh2_NB_state_idle; + return NULL; + } + memset(listener, 0, sizeof(LIBSSH2_LISTENER)); + listener->session = session; + listener->host = LIBSSH2_ALLOC(session, session->fwdLstn_host_len + 1); + if (!listener->host) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0); + LIBSSH2_FREE(session, listener); + LIBSSH2_FREE(session, data); + session->fwdLstn_state = libssh2_NB_state_idle; + return NULL; + } + memcpy(listener->host, host ? host : "0.0.0.0", session->fwdLstn_host_len); + listener->host[session->fwdLstn_host_len] = 0; + if (data_len >= 5 && !port) { + listener->port = libssh2_ntohu32(data + 1); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Dynamic tcpip-forward port allocated: %d", listener->port); + } else { + listener->port = port; + } + + listener->queue_size = 0; + listener->queue_maxsize = queue_maxsize; + + listener->next = session->listeners; + listener->prev = NULL; + if (session->listeners) { + session->listeners->prev = listener; + } + session->listeners = listener; + + if (bound_port) { + *bound_port = listener->port; + } + + LIBSSH2_FREE(session, data); + session->fwdLstn_state = libssh2_NB_state_idle; + return listener; + } + + if (data[0] == SSH_MSG_REQUEST_FAILURE) { + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED, "Unable to complete request for forward-listen", 0); + session->fwdLstn_state = libssh2_NB_state_idle; + return NULL; + } } + session->fwdLstn_state = libssh2_NB_state_idle; + return NULL; } /* }}} */ @@ -468,6 +469,8 @@ LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION /* {{{ libssh2_channel_forward_cancel * Stop listening on a remote port and free the listener * Toss out any pending (un-accept()ed) connections + * + * Return 0 on success, PACKET_EAGAIN if would block, -1 on error */ LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener) { @@ -475,32 +478,45 @@ LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener) LIBSSH2_CHANNEL *queued = listener->queue; unsigned char *packet, *s; unsigned long host_len = strlen(listener->host); + /* 14 = packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */ unsigned long packet_len = host_len + 14 + sizeof("cancel-tcpip-forward") - 1; - /* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */ + int rc; - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Cancelling tcpip-forward session for %s:%d", listener->host, listener->port); + if (listener->chanFwdCncl_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Cancelling tcpip-forward session for %s:%d", listener->host, listener->port); - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); - return -1; + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); + return -1; + } + + *(s++) = SSH_MSG_GLOBAL_REQUEST; + libssh2_htonu32(s, sizeof("cancel-tcpip-forward") - 1); s += 4; + memcpy(s, "cancel-tcpip-forward", sizeof("cancel-tcpip-forward") - 1); s += sizeof("cancel-tcpip-forward") - 1; + *(s++) = 0x00; /* want_reply */ + + libssh2_htonu32(s, host_len); s += 4; + memcpy(s, listener->host, host_len); s += host_len; + libssh2_htonu32(s, listener->port); s += 4; + + listener->chanFwdCncl_state = libssh2_NB_state_created; + } else { + packet = listener->chanFwdCncl_data; } - *(s++) = SSH_MSG_GLOBAL_REQUEST; - libssh2_htonu32(s, sizeof("cancel-tcpip-forward") - 1); s += 4; - memcpy(s, "cancel-tcpip-forward", sizeof("cancel-tcpip-forward") - 1); s += sizeof("cancel-tcpip-forward") - 1; - *(s++) = 0x00; /* want_reply */ - - libssh2_htonu32(s, host_len); s += 4; - memcpy(s, listener->host, host_len); s += host_len; - libssh2_htonu32(s, listener->port); s += 4; - - if (libssh2_packet_write(session, packet, packet_len)) { + rc = libssh2_packet_write(session, packet, packet_len); + if (rc == PACKET_EAGAIN) { + listener->chanFwdCncl_data = packet; + } + else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send global-request packet for forward listen request", 0); LIBSSH2_FREE(session, packet); + listener->chanFwdCncl_state = libssh2_NB_state_idle; return -1; } LIBSSH2_FREE(session, packet); + listener->chanFwdCncl_state = libssh2_NB_state_idle; while (queued) { LIBSSH2_CHANNEL *next = queued->next; @@ -532,15 +548,15 @@ LIBSSH2_API LIBSSH2_CHANNEL * libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener) { libssh2pack_t rc; - int loop=-1; + do { rc = libssh2_packet_read(listener->session); - loop++; + if (rc == PACKET_EAGAIN) { + libssh2_error(listener->session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for packet", 0); + return NULL; + } } 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; LIBSSH2_CHANNEL *channel; @@ -573,56 +589,91 @@ libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener) /* {{{ libssh2_channel_setenv_ex * Set an environment variable prior to requesting a shell/program/subsystem */ -LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varname, unsigned int varname_len, const char *value, unsigned int value_len) +LIBSSH2_API int +libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varname, unsigned int varname_len, const char *value, + unsigned int value_len) { LIBSSH2_SESSION *session = channel->session; - unsigned char *s, *packet, *data, local_channel[4]; + unsigned char *s, *data; static const unsigned char reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; unsigned long data_len; - unsigned long packet_len = varname_len + value_len + 21; /* packet_type(1) + channel_id(4) + request_len(4) + request(3)"env" + - want_reply(1) + varname_len(4) + value_len(4) */ + int rc; - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Setting remote environment variable: %s=%s on channel %lu/%lu", varname, value, channel->local.id, channel->remote.id); + if (channel->setenv_state == libssh2_NB_state_idle) { + /* 21 = packet_type(1) + channel_id(4) + request_len(4) + + * request(3)"env" + want_reply(1) + varname_len(4) + value_len(4) */ + channel->setenv_packet_len = varname_len + value_len + 21; - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); - return -1; + /* Zero the whole thing out */ + memset(&channel->setenv_packet_requirev_state, 0, sizeof(channel->setenv_packet_requirev_state)); + + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Setting remote environment variable: %s=%s on channel %lu/%lu", + varname, value, channel->local.id, channel->remote.id); + + s = channel->setenv_packet = LIBSSH2_ALLOC(session, channel->setenv_packet_len); + if (!channel->setenv_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); + return -1; + } + + *(s++) = SSH_MSG_CHANNEL_REQUEST; + libssh2_htonu32(s, channel->remote.id); s += 4; + libssh2_htonu32(s, sizeof("env") - 1); s += 4; + memcpy(s, "env", sizeof("env") - 1); s += sizeof("env") - 1; + + *(s++) = 0xFF; + + libssh2_htonu32(s, varname_len); s += 4; + memcpy(s, varname, varname_len); s += varname_len; + + libssh2_htonu32(s, value_len); s += 4; + memcpy(s, value, value_len); s += value_len; + + channel->setenv_state = libssh2_NB_state_created; } - *(s++) = SSH_MSG_CHANNEL_REQUEST; - libssh2_htonu32(s, channel->remote.id); s += 4; - libssh2_htonu32(s, sizeof("env") - 1); s += 4; - memcpy(s, "env", sizeof("env") - 1); s += sizeof("env") - 1; + if (channel->setenv_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, channel->setenv_packet, channel->setenv_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel-request packet for setenv request", 0); + LIBSSH2_FREE(session, channel->setenv_packet); + channel->setenv_packet = NULL; + channel->setenv_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, channel->setenv_packet); + channel->setenv_packet = NULL; - *(s++) = 0xFF; + libssh2_htonu32(channel->setenv_local_channel, channel->local.id); - libssh2_htonu32(s, varname_len); s += 4; - memcpy(s, varname, varname_len); s += varname_len; - - libssh2_htonu32(s, value_len); s += 4; - memcpy(s, value, value_len); s += value_len; - - if (libssh2_packet_write(session, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel-request packet for setenv request", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); - - libssh2_htonu32(local_channel, channel->local.id); - if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) { - return -1; + channel->setenv_state = libssh2_NB_state_sent; } - if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + if (channel->setenv_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, channel->setenv_local_channel, 4, + &channel->setenv_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + if (rc) { + channel->setenv_state = libssh2_NB_state_idle; + return -1; + } + + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + channel->setenv_state = libssh2_NB_state_idle; + return 0; + } + LIBSSH2_FREE(session, data); - return 0; } - 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); + channel->setenv_state = libssh2_NB_state_idle; return -1; } /* }}} */ @@ -630,142 +681,208 @@ LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varnam /* {{{ libssh2_channel_request_pty_ex * Duh... Request a PTY */ -LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, const char *term, unsigned int term_len, - const char *modes, unsigned int modes_len, - int width, int height, - int width_px, int height_px) +LIBSSH2_API int +libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, const char *term, unsigned int term_len, const char *modes, + unsigned int modes_len, int width, int height, int width_px, int height_px) { LIBSSH2_SESSION *session = channel->session; - unsigned char *s, *packet, *data, local_channel[4]; + unsigned char *s, *data; static const unsigned char reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; unsigned long data_len; - unsigned long packet_len = term_len + modes_len + 41; /* packet_type(1) + channel(4) + pty_req_len(4) + "pty_req"(7) + want_reply(1) + - term_len(4) + width(4) + height(4) + width_px(4) + height_px(4) + modes_len(4) */ + int rc; - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Allocating tty on channel %lu/%lu", channel->local.id, channel->remote.id); + if (channel->reqPTY_state == libssh2_NB_state_idle) { + /* 41 = packet_type(1) + channel(4) + pty_req_len(4) + "pty_req"(7) + + * want_reply(1) + term_len(4) + width(4) + height(4) + width_px(4) + + * height_px(4) + modes_len(4) */ + channel->reqPTY_packet_len = term_len + modes_len + 41; - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0); - return -1; + /* Zero the whole thing out */ + memset(&channel->reqPTY_packet_requirev_state, 0, sizeof(channel->reqPTY_packet_requirev_state)); + + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Allocating tty on channel %lu/%lu", channel->local.id, channel->remote.id); + + s = channel->reqPTY_packet = LIBSSH2_ALLOC(session, channel->reqPTY_packet_len); + if (!channel->reqPTY_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0); + return -1; + } + + *(s++) = SSH_MSG_CHANNEL_REQUEST; + libssh2_htonu32(s, channel->remote.id); s += 4; + libssh2_htonu32(s, sizeof("pty-req") - 1); s += 4; + memcpy(s, "pty-req", sizeof("pty-req") - 1); s += sizeof("pty-req") - 1; + + *(s++) = 0xFF; + + libssh2_htonu32(s, term_len); s += 4; + if (term) { + memcpy(s, term, term_len); s += term_len; + } + + libssh2_htonu32(s, width); s += 4; + libssh2_htonu32(s, height); s += 4; + libssh2_htonu32(s, width_px); s += 4; + libssh2_htonu32(s, height_px); s += 4; + + libssh2_htonu32(s, modes_len); s += 4; + if (modes) { + memcpy(s, modes, modes_len); s += modes_len; + } + + channel->reqPTY_state = libssh2_NB_state_created; } - *(s++) = SSH_MSG_CHANNEL_REQUEST; - libssh2_htonu32(s, channel->remote.id); s += 4; - libssh2_htonu32(s, sizeof("pty-req") - 1); s += 4; - memcpy(s, "pty-req", sizeof("pty-req") - 1); s += sizeof("pty-req") - 1; + if (channel->reqPTY_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, channel->reqPTY_packet, channel->reqPTY_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send pty-request packet", 0); + LIBSSH2_FREE(session, channel->reqPTY_packet); + channel->reqPTY_packet = NULL; + channel->reqPTY_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, channel->reqPTY_packet); + channel->reqPTY_packet = NULL; - *(s++) = 0xFF; + libssh2_htonu32(channel->reqPTY_local_channel, channel->local.id); - libssh2_htonu32(s, term_len); s += 4; - if (term) { - memcpy(s, term, term_len); s += term_len; + channel->reqPTY_state = libssh2_NB_state_sent; } - libssh2_htonu32(s, width); s += 4; - libssh2_htonu32(s, height); s += 4; - libssh2_htonu32(s, width_px); s += 4; - libssh2_htonu32(s, height_px); s += 4; + if (channel->reqPTY_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, channel->reqPTY_local_channel, 4, + &channel->reqPTY_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + channel->reqPTY_state = libssh2_NB_state_idle; + return -1; + } - libssh2_htonu32(s, modes_len); s += 4; - if (modes) { - memcpy(s, modes, modes_len); s += modes_len; - } - - if (libssh2_packet_write(session, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send pty-request packet", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); - - libssh2_htonu32(local_channel, channel->local.id); - if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) { - return -1; - } - - if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { - LIBSSH2_FREE(session, data); - return 0; + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + channel->reqPTY_state = libssh2_NB_state_idle; + return 0; + } } LIBSSH2_FREE(session, data); libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel request-pty", 0); + channel->reqPTY_state = libssh2_NB_state_idle; return -1; } /* }}} */ /* Keep this an even number */ #define LIBSSH2_X11_RANDOM_COOKIE_LEN 32 + /* {{{ libssh2_channel_x11_req_ex * Request X11 forwarding */ -LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection, const char *auth_proto, const char *auth_cookie, int screen_number) +LIBSSH2_API int +libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection, const char *auth_proto, const char *auth_cookie, + int screen_number) { LIBSSH2_SESSION *session = channel->session; - unsigned char *s, *packet, *data, local_channel[4]; + unsigned char *s, *data; static const unsigned char reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; unsigned long data_len; unsigned long proto_len = auth_proto ? strlen(auth_proto) : (sizeof("MIT-MAGIC-COOKIE-1") - 1); unsigned long cookie_len = auth_cookie ? strlen(auth_cookie) : LIBSSH2_X11_RANDOM_COOKIE_LEN; - unsigned long packet_len = proto_len + cookie_len + 30; /* packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) + want_reply(1) + - single_cnx(1) + proto_len(4) + cookie_len(4) + screen_num(4) */ + int rc; - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting x11-req for channel %lu/%lu: single=%d proto=%s cookie=%s screen=%d", - channel->local.id, channel->remote.id, single_connection, - auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", - auth_cookie ? auth_cookie : "", screen_number); + if (channel->reqX11_state == libssh2_NB_state_idle) { + /* 30 = packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) + + * want_reply(1) + single_cnx(1) + proto_len(4) + cookie_len(4) + + * screen_num(4) */ + channel->reqX11_packet_len = proto_len + cookie_len + 30; - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0); - return -1; - } + /* Zero the whole thing out */ + memset(&channel->reqX11_packet_requirev_state, 0, + sizeof(channel->reqX11_packet_requirev_state)); - *(s++) = SSH_MSG_CHANNEL_REQUEST; - libssh2_htonu32(s, channel->remote.id); s += 4; - libssh2_htonu32(s, sizeof("x11-req") - 1); s += 4; - memcpy(s, "x11-req", sizeof("x11-req") - 1); s += sizeof("x11-req") - 1; + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting x11-req for channel %lu/%lu: single=%d proto=%s cookie=%s screen=%d", + channel->local.id, channel->remote.id, single_connection, auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", + auth_cookie ? auth_cookie : "", screen_number); - *(s++) = 0xFF; /* want_reply */ - *(s++) = single_connection ? 0xFF : 0x00; - - libssh2_htonu32(s, proto_len); s += 4; - memcpy(s, auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", proto_len); - s += proto_len; - - libssh2_htonu32(s, cookie_len); s += 4; - if (auth_cookie) { - memcpy(s, auth_cookie, cookie_len); - } else { - int i; - unsigned char buffer[LIBSSH2_X11_RANDOM_COOKIE_LEN / 2]; - - libssh2_random(buffer, LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); - for (i = 0; i < (LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); i++) { - snprintf((char *)s + (i * 2), 2, "%02X", buffer[i]); + s = channel->reqX11_packet = LIBSSH2_ALLOC(session, channel->reqX11_packet_len); + if (!channel->reqX11_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0); + return -1; } - } - s += cookie_len; - libssh2_htonu32(s, screen_number); s += 4; + *(s++) = SSH_MSG_CHANNEL_REQUEST; + libssh2_htonu32(s, channel->remote.id); s += 4; + libssh2_htonu32(s, sizeof("x11-req") - 1); s += 4; + memcpy(s, "x11-req", sizeof("x11-req") - 1); s += sizeof("x11-req") - 1; - if (libssh2_packet_write(session, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send x11-req packet", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + *(s++) = 0xFF; /* want_reply */ + *(s++) = single_connection ? 0xFF : 0x00; - libssh2_htonu32(local_channel, channel->local.id); + libssh2_htonu32(s, proto_len); s += 4; + memcpy(s, auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", proto_len); + s += proto_len; - if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) { - return -1; + libssh2_htonu32(s, cookie_len); s += 4; + if (auth_cookie) { + memcpy(s, auth_cookie, cookie_len); + } else { + int i; + unsigned char buffer[LIBSSH2_X11_RANDOM_COOKIE_LEN / 2]; + + libssh2_random(buffer, LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); + for (i = 0; i < (LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); i++) { + snprintf((char *)s + (i * 2), 2, "%02X", buffer[i]); + } + } + s += cookie_len; + + libssh2_htonu32(s, screen_number); s += 4; + + channel->reqX11_state = libssh2_NB_state_created; } - if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { - LIBSSH2_FREE(session, data); - return 0; + if (channel->reqX11_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, channel->reqX11_packet, channel->reqX11_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send x11-req packet", 0); + LIBSSH2_FREE(session, channel->reqX11_packet); + channel->reqX11_packet = NULL; + channel->reqX11_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, channel->reqX11_packet); + channel->reqX11_packet = NULL; + + libssh2_htonu32(channel->reqX11_local_channel, channel->local.id); + + channel->reqX11_state = libssh2_NB_state_sent; + } + + if (channel->reqX11_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, channel->reqX11_local_channel, 4, + &channel->reqX11_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + channel->reqX11_state = libssh2_NB_state_idle; + return -1; + } + + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + channel->reqX11_state = libssh2_NB_state_idle; + return 0; + } } LIBSSH2_FREE(session, data); @@ -777,102 +894,103 @@ LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_ /* {{{ libssh2_channel_process_startup * Primitive for libssh2_channel_(shell|exec|subsystem) */ -LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const char *request, unsigned int request_len, const char *message, unsigned int message_len) +LIBSSH2_API int +libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const char *request, unsigned int request_len, const char *message, + unsigned int message_len) { LIBSSH2_SESSION *session = channel->session; - unsigned char *s, *packet, *data, local_channel[4]; + unsigned char *s, *data; static const unsigned char reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; 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; + if (channel->process_state == libssh2_NB_state_idle) { + /* 10 = packet_type(1) + channel(4) + request_len(4) + want_reply(1) */ + channel->process_packet_len = request_len + 10; + + /* Zero the whole thing out */ + memset(&channel->process_packet_requirev_state, 0, sizeof(channel->process_packet_requirev_state)); + + if (message) { + channel->process_packet_len += message_len + 4; + } + + _libssh2_debug(session, LIBSSH2_DBG_CONN, "starting request(%s) on channel %lu/%lu, message=%s", + request, channel->local.id, channel->remote.id, message); + s = channel->process_packet = LIBSSH2_ALLOC(session, channel->process_packet_len); + if (!channel->process_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for channel-process request", 0); + return -1; + } + + *(s++) = SSH_MSG_CHANNEL_REQUEST; + libssh2_htonu32(s, channel->remote.id); s += 4; + libssh2_htonu32(s, request_len); s += 4; + memcpy(s, request, request_len); s += request_len; + + *(s++) = 0xFF; + + if (message) { + libssh2_htonu32(s, message_len); s += 4; + memcpy(s, message, message_len); s += message_len; + } + + channel->process_state = libssh2_NB_state_created; } - _libssh2_debug(session, LIBSSH2_DBG_CONN, "starting request(%s) on channel %lu/%lu, message=%s", request, channel->local.id, channel->remote.id, message); - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for channel-process request", 0); - return -1; + if (channel->process_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, channel->process_packet, channel->process_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel request", 0); + LIBSSH2_FREE(session, channel->process_packet); + channel->process_packet = NULL; + channel->process_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, channel->process_packet); + channel->process_packet = NULL; + + libssh2_htonu32(channel->process_local_channel, channel->local.id); + + channel->process_state = libssh2_NB_state_sent; } - *(s++) = SSH_MSG_CHANNEL_REQUEST; - libssh2_htonu32(s, channel->remote.id); s += 4; - libssh2_htonu32(s, request_len); s += 4; - memcpy(s, request, request_len); s += request_len; + if (channel->process_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, channel->process_local_channel, 4, + &channel->process_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + channel->process_state = libssh2_NB_state_idle; + return -1; + } - *(s++) = 0xFF; - - if (message) { - libssh2_htonu32(s, message_len); s += 4; - memcpy(s, message, message_len); s += message_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; - } - LIBSSH2_FREE(session, packet); - - libssh2_htonu32(local_channel, channel->local.id); - if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) { - return -1; - } - - if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { - LIBSSH2_FREE(session, data); - return 0; + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + channel->process_state = libssh2_NB_state_idle; + return 0; + } } LIBSSH2_FREE(session, data); libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-process-startup", 0); + channel->process_state = libssh2_NB_state_idle; return -1; } /* }}} */ -/* {{{ _libssh2_channel_set_blocking - * Set a channel's blocking mode on or off, return the status when this - * function is called. - */ -int _libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, - int blocking) -{ - int bl = channel->blocking; - _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, - "Setting blocking mode on channel %lu/%lu to %d", - channel->local.id, channel->remote.id, blocking); - if(blocking == channel->blocking) { - /* avoid if already correct */ - return bl; - } - channel->blocking = blocking; - - _libssh2_nonblock(channel->session->socket_fd, !blocking); - - 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) +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; + (void)_libssh2_session_set_blocking(channel->session, blocking); } /* }}} */ @@ -883,62 +1001,64 @@ int libssh2_channel_get_blocking(LIBSSH2_CHANNEL *channel) LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid) { LIBSSH2_PACKET *packet = channel->session->packets.head; - unsigned long refund_bytes = 0, flush_bytes = 0; - while (packet) { - LIBSSH2_PACKET *next = packet->next; - unsigned char packet_type = packet->data[0]; + if (channel->flush_state == libssh2_NB_state_idle) { + channel->flush_refund_bytes = 0; + channel->flush_flush_bytes = 0; - 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 */ - 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; - _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); + while (packet) { + LIBSSH2_PACKET *next = packet->next; + unsigned char packet_type = packet->data[0]; - /* It's one of the streams we wanted to flush */ - refund_bytes += packet->data_len - 13; - flush_bytes += bytes_to_flush; + 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 */ + 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; - LIBSSH2_FREE(channel->session, packet->data); - if (packet->prev) { - packet->prev->next = packet->next; - } else { - channel->session->packets.head = packet->next; + _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); + + /* It's one of the streams we wanted to flush */ + channel->flush_refund_bytes += packet->data_len - 13; + channel->flush_flush_bytes += bytes_to_flush; + + LIBSSH2_FREE(channel->session, packet->data); + if (packet->prev) { + packet->prev->next = packet->next; + } else { + channel->session->packets.head = packet->next; + } + if (packet->next) { + packet->next->prev = packet->prev; + } else { + channel->session->packets.tail = packet->prev; + } + LIBSSH2_FREE(channel->session, packet); } - if (packet->next) { - packet->next->prev = packet->prev; - } else { - channel->session->packets.tail = packet->prev; - } - LIBSSH2_FREE(channel->session, packet); } + packet = next; } - packet = next; + + channel->flush_state = libssh2_NB_state_created; } - if (refund_bytes) { - libssh2_channel_receive_window_adjust(channel, refund_bytes, 0); + if (channel->flush_refund_bytes) { + int rc; + + rc = libssh2_channel_receive_window_adjust(channel, channel->flush_refund_bytes, 0); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } } - return flush_bytes; + channel->flush_state = libssh2_NB_state_idle; + + return channel->flush_flush_bytes; } /* }}} */ @@ -954,17 +1074,21 @@ LIBSSH2_API int libssh2_channel_get_exit_status(LIBSSH2_CHANNEL* channel) /* {{{ libssh2_channel_receive_window_adjust * Adjust the receive window for a channel by adjustment bytes - * If the amount to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 - * The adjustment amount will be queued for a later packet + * If the amount to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and + * force is 0 the adjustment amount will be queued for a later packet * * Returns the new size of the receive window (as understood by remote end) */ -LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force) +LIBSSH2_API unsigned long +libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force) { unsigned char adjust[9]; /* packet_type(1) + channel(4) + adjustment(4) */ + int rc; - if (!force && (adjustment + channel->adjust_queue < LIBSSH2_CHANNEL_MINADJUST)) { - _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Queueing %lu bytes for receive window adjustment for channel %lu/%lu", adjustment, channel->local.id, channel->remote.id); + if (!force && + (adjustment + channel->adjust_queue < LIBSSH2_CHANNEL_MINADJUST)) { + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Queueing %lu bytes for receive window adjustment for channel %lu/%lu", + adjustment, channel->local.id, channel->remote.id); channel->adjust_queue += adjustment; return channel->remote.window_size; } @@ -981,10 +1105,16 @@ LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST; libssh2_htonu32(adjust + 1, channel->remote.id); libssh2_htonu32(adjust + 5, adjustment); - _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Adjusting window %lu bytes for data flushed from channel %lu/%lu", adjustment, channel->local.id, channel->remote.id); + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Adjusting window %lu bytes for data flushed from channel %lu/%lu", + adjustment, channel->local.id, channel->remote.id); - if (libssh2_packet_write(channel->session, adjust, 9)) { - libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet, deferring", 0); + rc = libssh2_packet_write(channel->session, adjust, 9); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet, deferring", + 0); channel->adjust_queue = adjustment; } else { channel->remote.window_size += adjustment; @@ -1000,18 +1130,38 @@ LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * Merge the extended data to the standard data? [everything via _read()]? (MERGE) * Ignore it entirely [toss out packets as they come in]? (IGNORE) */ -LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode) +LIBSSH2_API void +libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode) { - _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Setting channel %lu/%lu handle_extended_data mode to %d", channel->local.id, channel->remote.id, ignore_mode); - channel->remote.extended_data_ignore_mode = ignore_mode; + while (libssh2_channel_handle_extended_data2(channel, ignore_mode) == PACKET_EAGAIN); +} - if (ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) { - libssh2_channel_flush_ex(channel, LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA); +LIBSSH2_API int +libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL *channel, int ignore_mode) +{ + if (channel->extData2_state == libssh2_NB_state_idle) { + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Setting channel %lu/%lu handle_extended_data mode to %d", + channel->local.id, channel->remote.id, ignore_mode); + channel->remote.extended_data_ignore_mode = ignore_mode; + + channel->extData2_state = libssh2_NB_state_created; } + + if (channel->extData2_state == libssh2_NB_state_idle) { + if (ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) { + if (libssh2_channel_flush_ex(channel, LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + } + } + + channel->extData2_state = libssh2_NB_state_idle; + return 0; } /* }}} */ -/* {{{ _libssh2_channel_read_ex +/* + * {{{ _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 @@ -1019,329 +1169,289 @@ LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, * was no payload data to fill in the buffer with, we MUST make sure to return * PACKET_EAGAIN. */ -ssize_t _libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, - int stream_id, char *buf, size_t buflen) +LIBSSH2_API 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; - LIBSSH2_PACKET *packet; libssh2pack_t rc=0; - int bl; - int block=0; - _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); + if (channel->read_state == libssh2_NB_state_idle) { + _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); - /* set non-blocking and remember previous state */ - bl = _libssh2_channel_set_blocking(channel, 0); + /* process all incoming packets */ + do { + if (libssh2_waitsocket(session, 0) > 0) { + rc = libssh2_packet_read(session); + } else { + /* Set for PACKET_EAGAIN so we continue */ + rc = PACKET_EAGAIN; + } + } while (rc > 0); - /* process all incoming packets */ - do { - rc = libssh2_packet_read(session); - } while (rc > 0); + if ((rc < 0) && (rc != PACKET_EAGAIN)) { + fprintf(stderr, "return rc = %d\n", rc); + return rc; + } + channel->read_bytes_read = 0; + channel->read_block = 0; - if((rc < 0) && (rc != PACKET_EAGAIN)) { - return rc; + rc = 0; + + channel->read_packet = session->packets.head; + + channel->read_state = libssh2_NB_state_created; } - rc = 0; - - /* restore blocking state */ - _libssh2_channel_set_blocking(channel, bl); - - packet = session->packets.head; - + + /* + * =============================== NOTE =============================== + * I know this is very ugly and not a really good use of "goto", but + * this case statement would be even uglier to do it any other way + */ + if (channel->read_state == libssh2_NB_state_jump1) { + goto channel_read_ex_point1; + } + do { - - if(block) { + if (channel->read_block) { /* in the second lap and onwards, do this */ rc = libssh2_packet_read(session); - packet = session->packets.head; + channel->read_packet = session->packets.head; } - if(rc < 0) { + if (rc < 0) { + if (rc != PACKET_EAGAIN) { + channel->read_state = libssh2_NB_state_idle; + } /* no packets available */ return rc; } - while (packet && (bytes_read < (int)buflen)) { - /* In case packet gets destroyed during this - iteration */ - LIBSSH2_PACKET *next = packet->next; + while (channel->read_packet && (channel->read_bytes_read < (int)buflen)) { + /* In case packet gets destroyed during this iteration */ + channel->read_next = channel->read_packet->next; - uint32_t local_id = libssh2_ntohu32(packet->data + 1); + channel->read_local_id = libssh2_ntohu32(channel->read_packet->data + 1); - /* Either we asked for a specific extended data stream + /* + * 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 */ - if ((stream_id && - (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && - (channel->local.id == local_id) && - (stream_id == (int)libssh2_ntohu32(packet->data + 5))) || + if ((stream_id && (channel->read_packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && + (channel->local.id == channel->read_local_id) && + (stream_id == (int)libssh2_ntohu32(channel->read_packet->data + 5))) || - (!stream_id && - (packet->data[0] == SSH_MSG_CHANNEL_DATA) && - (channel->local.id == local_id)) || + (!stream_id && (channel->read_packet->data[0] == SSH_MSG_CHANNEL_DATA) && + (channel->local.id == channel->read_local_id)) || - (!stream_id && - (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && - (channel->local.id == local_id) && + (!stream_id && (channel->read_packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && + (channel->local.id == channel->read_local_id) && (channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) { - int want = buflen - bytes_read; + int want = buflen - channel->read_bytes_read; int unlink_packet = 0; - if (want >= (int)(packet->data_len - packet->data_head)) { - want = packet->data_len - packet->data_head; + if (want >= (int)(channel->read_packet->data_len - channel->read_packet->data_head)) { + want = channel->read_packet->data_len - channel->read_packet->data_head; unlink_packet = 1; } - _libssh2_debug(session, LIBSSH2_DBG_CONN, - "Reading %d of buffered data from %lu/%lu/%d", - want, channel->local.id, - channel->remote.id, stream_id); - memcpy(buf + bytes_read, packet->data + packet->data_head, want); - packet->data_head += want; - bytes_read += want; + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Reading %d of buffered data from %lu/%lu/%d", + want, channel->local.id, channel->remote.id, stream_id); + memcpy(buf + channel->read_bytes_read, channel->read_packet->data + channel->read_packet->data_head, want); + channel->read_packet->data_head += want; + channel->read_bytes_read += want; if (unlink_packet) { - if (packet->prev) { - packet->prev->next = packet->next; + if (channel->read_packet->prev) { + channel->read_packet->prev->next = channel->read_packet->next; } else { - session->packets.head = packet->next; + session->packets.head = channel->read_packet->next; } - if (packet->next) { - packet->next->prev = packet->prev; + if (channel->read_packet->next) { + channel->read_packet->next->prev = channel->read_packet->prev; } else { - session->packets.tail = packet->prev; + session->packets.tail = channel->read_packet->prev; } - LIBSSH2_FREE(session, packet->data); + LIBSSH2_FREE(session, channel->read_packet->data); - _libssh2_debug(session, - LIBSSH2_DBG_CONN, - "Unlinking empty packet buffer from channel %lu/%lu", - channel->local.id, channel->remote.id); - - libssh2_channel_receive_window_adjust(channel, packet->data_len - (stream_id ? 13 : 9), 0); - LIBSSH2_FREE(session, packet); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Unlinking empty packet buffer from channel %lu/%lu", + channel->local.id, channel->remote.id); +channel_read_ex_point1: + channel->read_state = libssh2_NB_state_jump1; + rc = libssh2_channel_receive_window_adjust(channel, channel->read_packet->data_len - (stream_id ? 13 : 9), 0); + if (rc == PACKET_EAGAIN){ + return PACKET_EAGAIN; + } + channel->read_state = libssh2_NB_state_created; + LIBSSH2_FREE(session, channel->read_packet); + channel->read_packet = NULL; } } - packet = next; + channel->read_packet = channel->read_next; } - block=1; + channel->read_block = 1; + } while (channel->session->socket_block && (channel->read_bytes_read == 0) && !channel->remote.close); - } 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)) { + if (channel->read_bytes_read == 0) { + if (channel->session->socket_block) { + 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; + channel->read_state = libssh2_NB_state_idle; + return channel->read_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 */ -int _libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, - int stream_id, - const char *buf, size_t buflen) +LIBSSH2_API size_t libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen) { LIBSSH2_SESSION *session = channel->session; - unsigned char *packet; - unsigned long packet_len, bufwrote = 0; + libssh2pack_t rc; - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Writing %d bytes on channel %lu/%lu, stream #%d", (int)buflen, channel->local.id, channel->remote.id, stream_id); - if (channel->local.close) { - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "We've already closed this channel", 0); - return -1; - } + if (channel->write_state == libssh2_NB_state_idle) { + channel->write_bufwrote = 0; - if (channel->local.eof) { - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_EOF_SENT, "EOF has already been sight, data might be ignored", 0); - } + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Writing %d bytes on channel %lu/%lu, stream #%d", + (int)buflen, channel->local.id, channel->remote.id, stream_id); - if (!channel->blocking && (channel->local.window_size <= 0)) { - /* Can't write anything */ - return 0; - } + if (channel->local.close) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "We've already closed this channel", 0); + return -1; + } - packet_len = buflen + (stream_id ? 13 : 9); /* packet_type(1) + channelno(4) [ + streamid(4) ] + buflen(4) */ - packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocte space for data transmission packet", 0); - return -1; + if (channel->local.eof) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_EOF_SENT, "EOF has already been sight, data might be ignored", 0); + } + + if (!channel->session->socket_block && + (channel->local.window_size <= 0)) { + /* Can't write anything */ + return 0; + } + + /* [13] 9 = packet_type(1) + channelno(4) [ + streamid(4) ] + buflen(4) */ + channel->write_packet_len = buflen + (stream_id ? 13 : 9); + channel->write_packet = LIBSSH2_ALLOC(session, channel->write_packet_len); + if (!channel->write_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocte space for data transmission packet", 0); + return -1; + } + + channel->write_state = libssh2_NB_state_allocated; } while (buflen > 0) { - size_t bufwrite = buflen; - unsigned char *s = packet; - libssh2pack_t rc; + if (channel->write_state == libssh2_NB_state_allocated) { + channel->write_bufwrite = buflen; + channel->write_s = channel->write_packet; - *(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA; - libssh2_htonu32(s, channel->remote.id); s += 4; - if (stream_id) { - libssh2_htonu32(s, stream_id); s += 4; - } - - /* 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 */ - rc = libssh2_packet_read(session); - - if (rc < 0) { - /* Error or EAGAIN occurred, disconnect? */ - return rc; + *(channel->write_s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA; + libssh2_htonu32(channel->write_s, channel->remote.id); channel->write_s += 4; + if (stream_id) { + libssh2_htonu32(channel->write_s, stream_id); channel->write_s += 4; } - /* FIXME: (dast) if rc == 0 here then this busyloops - like hell until data arrives on the network which - seems like a very bad idea */ - } + /* 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 */ + rc = libssh2_packet_read(session); - /* Don't exceed the remote end's limits */ - /* REMEMBER local means local as the SOURCE of the data */ - if (bufwrite > channel->local.window_size) { - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Splitting write block due to %lu byte window_size on %lu/%lu/%d", channel->local.window_size, channel->local.id, channel->remote.id, stream_id); - bufwrite = channel->local.window_size; - } - if (bufwrite > channel->local.packet_size) { - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Splitting write block due to %lu byte packet_size on %lu/%lu/%d", channel->local.packet_size, channel->local.id, channel->remote.id, stream_id); - bufwrite = channel->local.packet_size; - } - libssh2_htonu32(s, bufwrite); s += 4; - memcpy(s, buf, bufwrite); s += bufwrite; + if (rc < 0) { + /* Error or EAGAIN occurred, disconnect? */ + if (rc != PACKET_EAGAIN) { + channel->write_state = libssh2_NB_state_idle; + } + return rc; + } - _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); - 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; + if ((rc == 0) && (session->socket_block == 0)) { + /* + * if rc == 0 and in non-blocking, then fake EAGAIN + * to prevent busyloops until data arriaves on the network + * which seemed like a very bad idea + */ + return PACKET_EAGAIN; + } + } + + /* Don't exceed the remote end's limits */ + /* REMEMBER local means local as the SOURCE of the data */ + if (channel->write_bufwrite > channel->local.window_size) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Splitting write block due to %lu byte window_size on %lu/%lu/%d", + channel->local.window_size, channel->local.id, channel->remote.id, stream_id); + channel->write_bufwrite = channel->local.window_size; + } + if (channel->write_bufwrite > channel->local.packet_size) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Splitting write block due to %lu byte packet_size on %lu/%lu/%d", + channel->local.packet_size, channel->local.id, channel->remote.id, stream_id); + channel->write_bufwrite = channel->local.packet_size; + } + libssh2_htonu32(channel->write_s, channel->write_bufwrite); channel->write_s += 4; + memcpy(channel->write_s, buf, channel->write_bufwrite); channel->write_s += channel->write_bufwrite; + + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending %d bytes on channel %lu/%lu, stream_id=%d", + (int)channel->write_bufwrite, channel->local.id, channel->remote.id, stream_id); + + channel->write_state = libssh2_NB_state_created; } - /* Shrink local window size */ - channel->local.window_size -= bufwrite; + + if (channel->write_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, channel->write_packet, channel->write_s - channel->write_packet); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel data", 0); + LIBSSH2_FREE(session, channel->write_packet); + channel->write_packet = NULL; + channel->write_state = libssh2_NB_state_idle; + return -1; + } + /* Shrink local window size */ + channel->local.window_size -= channel->write_bufwrite; - /* Adjust buf for next iteration */ - buflen -= bufwrite; - buf += bufwrite; - bufwrote += bufwrite; + /* Adjust buf for next iteration */ + buflen -= channel->write_bufwrite; + buf += channel->write_bufwrite; + channel->write_bufwrote += channel->write_bufwrite; - if (!channel->blocking) { - break; + channel->write_state = libssh2_NB_state_allocated; + + /* + * Not sure this is still wanted + if (!channel->session->socket_block) { + break; + } + */ } } - LIBSSH2_FREE(session, packet); + LIBSSH2_FREE(session, channel->write_packet); + channel->write_packet = NULL; - return bufwrote; + channel->write_state = libssh2_NB_state_idle; + + return channel->write_bufwrote; } /* }}} */ -/* {{{ 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 */ @@ -1349,11 +1459,16 @@ LIBSSH2_API int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel) { LIBSSH2_SESSION *session = channel->session; unsigned char packet[5]; /* packet_type(1) + channelno(4) */ + int rc; - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending EOF on channel %lu/%lu",channel->local.id, channel->remote.id); + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending EOF on channel %lu/%lu", channel->local.id, channel->remote.id); packet[0] = SSH_MSG_CHANNEL_EOF; libssh2_htonu32(packet + 1, channel->remote.id); - if (libssh2_packet_write(session, packet, 5)) { + rc = libssh2_packet_write(session, packet, 5); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send EOF on channel", 0); return -1; } @@ -1384,49 +1499,108 @@ LIBSSH2_API int libssh2_channel_eof(LIBSSH2_CHANNEL *channel) } /* }}} */ +/* {{{ libssh2_channel_wait_closed +* Awaiting channel EOF +*/ +LIBSSH2_API int libssh2_channel_wait_eof(LIBSSH2_CHANNEL *channel) +{ + LIBSSH2_SESSION* session = channel->session; + int rc; + + if (channel->wait_eof_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Awaiting close of channel %lu/%lu", channel->local.id, channel->remote.id); + + channel->wait_eof_state = libssh2_NB_state_created; + } + + /* + * While channel is not eof, read more packets from the network. + * Either the EOF will be set or network timeout will occur. + */ + do { + if (channel->remote.eof) { + break; + } + rc = libssh2_packet_read(session); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc < 0) { + channel->wait_eof_state = libssh2_NB_state_idle; + return -1; + } + } while (1); + + channel->wait_eof_state = libssh2_NB_state_idle; + + return 0; +} +/* }}} */ + + /* {{{ libssh2_channel_close * Close a channel */ LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel) { LIBSSH2_SESSION *session = channel->session; - unsigned char packet[5]; int rc = 0; + int retcode; if (channel->local.close) { - /* Already closed, act like we sent another close, even though we didn't... shhhhhh */ + /* Already closed, act like we sent another close, + * even though we didn't... shhhhhh */ + channel->close_state = libssh2_NB_state_idle; return 0; } - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Closing channel %lu/%lu", channel->local.id, channel->remote.id); + if (channel->close_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Closing channel %lu/%lu", channel->local.id, channel->remote.id); - if (channel->close_cb) { - LIBSSH2_CHANNEL_CLOSE(session, channel); - } - channel->local.close = 1; + if (channel->close_cb) { + LIBSSH2_CHANNEL_CLOSE(session, channel); + } + channel->local.close = 1; - packet[0] = SSH_MSG_CHANNEL_CLOSE; - libssh2_htonu32(packet + 1, channel->remote.id); - if (libssh2_packet_write(session, packet, 5)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send close-channel request", 0); - return -1; + channel->close_packet[0] = SSH_MSG_CHANNEL_CLOSE; + libssh2_htonu32(channel->close_packet + 1, channel->remote.id); + + channel->close_state = libssh2_NB_state_created; } - /* We must wait for the remote SSH_MSG_CHANNEL_CLOSE message */ - if (!channel->remote.close) { - libssh2pack_t ret; - /* set blocking mode */ - int bl = _libssh2_channel_set_blocking(channel, 1); - do { - ret = libssh2_packet_read(session); - if ((ret < 0) && (ret != PACKET_EAGAIN)) { - rc = -1; - } - } while (ret != SSH_MSG_CHANNEL_CLOSE && rc == 0); + if (channel->close_state == libssh2_NB_state_created) { + retcode = libssh2_packet_write(session, channel->close_packet, 5); + if (retcode == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (retcode) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send close-channel request", 0); + channel->close_state = libssh2_NB_state_idle; + return -1; + } - _libssh2_channel_set_blocking(channel, bl); + channel->close_state = libssh2_NB_state_sent; } + if (channel->close_state == libssh2_NB_state_sent) { + /* We must wait for the remote SSH_MSG_CHANNEL_CLOSE message */ + if (!channel->remote.close) { + libssh2pack_t ret; + + do { + ret = libssh2_packet_read(session); + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (ret < 0) { + rc = -1; + } + } while ((ret != SSH_MSG_CHANNEL_CLOSE) && (rc == 0)); + } + } + + channel->close_state = libssh2_NB_state_idle; + return rc; } /* }}} */ @@ -1437,21 +1611,37 @@ LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel) LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel) { LIBSSH2_SESSION* session = channel->session; + int rc; if (!libssh2_channel_eof(channel)) { - libssh2_error(session, LIBSSH2_ERROR_INVAL, "libssh2_channel_wait_closed() invoked when channel is not in EOF state", 0); + libssh2_error(session, LIBSSH2_ERROR_INVAL, "libssh2_channel_wait_closed() invoked when channel is not in EOF state", 0); return -1; } - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Awaiting close of channel %lu/%lu", channel->local.id, channel->remote.id); + if (channel->wait_closed_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Awaiting close of channel %lu/%lu", channel->local.id, channel->remote.id); - /* while channel is not closed, read more - * packets from the network. - * Either or channel will be closed - * or network timeout will occur + channel->wait_closed_state = libssh2_NB_state_created; + } + + /* + * While channel is not closed, read more packets from the network. + * Either the channel will be closed or network timeout will occur. */ - while (!channel->remote.close && libssh2_packet_read(session) > 0) - ; + do { + if (!channel->remote.close) { + break; + } + rc = libssh2_packet_read(session); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc <= 0) { + break; + } + } while (1); + + channel->wait_closed_state = libssh2_NB_state_idle; return 1; } @@ -1459,30 +1649,34 @@ LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel) /* {{{ libssh2_channel_free - * Make sure a channel is closed, then remove the channel from the session and free its resource(s) + * Make sure a channel is closed, then remove the channel from the session + * and free its resource(s) + * + * Returns 0 on success, -1 on failure */ LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel) { LIBSSH2_SESSION *session = channel->session; - unsigned char channel_id[4], *data; + unsigned char channel_id[4]; + unsigned char *data; unsigned long data_len; _libssh2_debug(session, LIBSSH2_DBG_CONN, "Freeing channel %lu/%lu resources", channel->local.id, channel->remote.id); /* Allow channel freeing even when the socket has lost its connection */ - if (!channel->local.close && (session->socket_state == LIBSSH2_SOCKET_CONNECTED) && - libssh2_channel_close(channel)) { + if (!channel->local.close && (session->socket_state == LIBSSH2_SOCKET_CONNECTED) && libssh2_channel_close(channel)) { return -1; } - /* channel->remote.close *might* not be set yet, Well... + /* + * channel->remote.close *might* not be set yet, Well... * We've sent the close packet, what more do you want? * Just let packet_add ignore it when it finally arrives */ /* 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, 0) >= 0) || - (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, &data_len, 1, channel_id, 4, 0) >= 0)) { + while ((libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_DATA, &data, &data_len, 1, channel_id, 4, 0) >= 0) || + (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, &data_len, 1, channel_id, 4, 0) >= 0)) { LIBSSH2_FREE(session, data); } diff --git a/src/kex.c b/src/kex.c index 7c1e6e1..0208640 100644 --- a/src/kex.c +++ b/src/kex.c @@ -39,438 +39,510 @@ /* TODO: Switch this to an inline and handle alloc() failures */ /* Helper macro called from libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange */ -#define LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(value, reqlen, version) \ -{ \ - libssh2_sha1_ctx hash; \ - unsigned long len = 0; \ - if (!(value)) { \ - value = LIBSSH2_ALLOC(session, reqlen + SHA_DIGEST_LENGTH); \ - } \ - if (value) \ - while (len < reqlen) { \ - libssh2_sha1_init(&hash); \ - libssh2_sha1_update(hash, k_value, k_value_len); \ - libssh2_sha1_update(hash, h_sig_comp, SHA_DIGEST_LENGTH); \ - if (len > 0) { \ - libssh2_sha1_update(hash, value, len); \ - } else { \ - libssh2_sha1_update(hash, (version), 1); \ - libssh2_sha1_update(hash, session->session_id, session->session_id_len); \ - } \ - libssh2_sha1_final(hash, (value) + len); \ - len += SHA_DIGEST_LENGTH; \ - } \ +#define LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(value, reqlen, version) \ +{ \ + libssh2_sha1_ctx hash; \ + unsigned long len = 0; \ + if (!(value)) { \ + value = LIBSSH2_ALLOC(session, reqlen + SHA_DIGEST_LENGTH); \ + } \ + if (value) \ + while (len < (unsigned long)reqlen) { \ + libssh2_sha1_init(&hash); \ + libssh2_sha1_update(hash, exchange_state->k_value, \ + exchange_state->k_value_len); \ + libssh2_sha1_update(hash, exchange_state->h_sig_comp, \ + SHA_DIGEST_LENGTH); \ + if (len > 0) { \ + libssh2_sha1_update(hash, value, len); \ + } else { \ + libssh2_sha1_update(hash, (version), 1); \ + libssh2_sha1_update(hash, session->session_id, \ + session->session_id_len); \ + } \ + libssh2_sha1_final(hash, (value) + len); \ + len += SHA_DIGEST_LENGTH; \ + } \ } /* {{{ libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange * Diffie Hellman Key Exchange, Group Agnostic */ -static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_SESSION *session, _libssh2_bn *g, _libssh2_bn *p, int group_order, - unsigned char packet_type_init, unsigned char packet_type_reply, - unsigned char *midhash, unsigned long midhash_len) +static int +libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_SESSION *session, _libssh2_bn *g, _libssh2_bn *p, + int group_order, unsigned char packet_type_init, + unsigned char packet_type_reply, unsigned char *midhash, + unsigned long midhash_len, kmdhgGPsha1kex_state_t *exchange_state) { - unsigned char *e_packet = NULL, *s_packet = NULL, *tmp, h_sig_comp[SHA_DIGEST_LENGTH], c; - unsigned long e_packet_len, s_packet_len, tmp_len; int ret = 0; - _libssh2_bn_ctx *ctx = _libssh2_bn_ctx_new(); - _libssh2_bn *x = _libssh2_bn_init(); /* Random from client */ - _libssh2_bn *e = _libssh2_bn_init(); /* g^x mod p */ - _libssh2_bn *f = _libssh2_bn_init(); /* g^(Random from server) mod p */ - _libssh2_bn *k = _libssh2_bn_init(); /* The shared secret: f^x mod p */ - 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); - _libssh2_bn_mod_exp(e, g, x, p, ctx); - - /* Send KEX init */ - e_packet_len = _libssh2_bn_bytes(e) + 6; /* packet_type(1) + String Length(4) + leading 0(1) */ - if (_libssh2_bn_bits(e) % 8) { - /* Leading 00 not needed */ - e_packet_len--; - } - e_packet = LIBSSH2_ALLOC(session, e_packet_len); - if (!e_packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Out of memory error", 0); - ret = -1; - goto clean_exit; - } - e_packet[0] = packet_type_init; - libssh2_htonu32(e_packet + 1, e_packet_len - 5); - if (_libssh2_bn_bits(e) % 8) { - _libssh2_bn_to_bin(e, e_packet + 5); - } else { - e_packet[5] = 0; - _libssh2_bn_to_bin(e, e_packet + 6); - } - - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending KEX packet %d", (int)packet_type_init); - 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; - } - - if (session->burn_optimistic_kexinit) { - /* The first KEX packet to come along will be the guess initially sent by the server - * That guess turned out to be wrong so we need to silently ignore it */ - int burn_type; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Waiting for badly guessed KEX packet (to be ignored)"); - burn_type = libssh2_packet_burn(session); - if (burn_type <= 0) { - /* Failed to receive a packet */ + if (exchange_state->state == libssh2_NB_state_idle) { + /* Setup initial values */ + exchange_state->e_packet = NULL; + exchange_state->s_packet = NULL; + exchange_state->k_value = NULL; + exchange_state->ctx = _libssh2_bn_ctx_new(); + exchange_state->x = _libssh2_bn_init(); /* Random from client */ + exchange_state->e = _libssh2_bn_init(); /* g^x mod p */ + exchange_state->f = _libssh2_bn_init(); /* g^(Random from server) mod p */ + exchange_state->k = _libssh2_bn_init(); /* The shared secret: f^x mod p */ + + /* Zero the whole thing out */ + memset(&exchange_state->req_state, 0, sizeof(packet_require_state_t)); + + /* Generate x and e */ + _libssh2_bn_rand(exchange_state->x, group_order, 0, -1); + _libssh2_bn_mod_exp(exchange_state->e, g, exchange_state->x, p, exchange_state->ctx); + + /* Send KEX init */ + /* packet_type(1) + String Length(4) + leading 0(1) */ + exchange_state->e_packet_len = _libssh2_bn_bytes(exchange_state->e) + 6; + if (_libssh2_bn_bits(exchange_state->e) % 8) { + /* Leading 00 not needed */ + exchange_state->e_packet_len--; + } + + exchange_state->e_packet = LIBSSH2_ALLOC(session, exchange_state->e_packet_len); + if (!exchange_state->e_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Out of memory error", 0); ret = -1; goto clean_exit; } - session->burn_optimistic_kexinit = 0; - - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Burnt packet of type: %02x", (unsigned int)burn_type); + exchange_state->e_packet[0] = packet_type_init; + libssh2_htonu32(exchange_state->e_packet + 1, exchange_state->e_packet_len - 5); + if (_libssh2_bn_bits(exchange_state->e) % 8) { + _libssh2_bn_to_bin(exchange_state->e, exchange_state->e_packet + 5); + } else { + exchange_state->e_packet[5] = 0; + _libssh2_bn_to_bin(exchange_state->e, exchange_state->e_packet + 6); + } + + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending KEX packet %d", + (int)packet_type_init); + exchange_state->state = libssh2_NB_state_created; } - - /* Wait for KEX reply */ - 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; + + if (exchange_state->state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, exchange_state->e_packet, exchange_state->e_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEX init message", 0); + ret = -1; + goto clean_exit; + } + exchange_state->state = libssh2_NB_state_sent; } - - /* Parse KEXDH_REPLY */ - s = s_packet + 1; - - session->server_hostkey_len = libssh2_ntohu32(s); s += 4; - session->server_hostkey = LIBSSH2_ALLOC(session, session->server_hostkey_len); - if (!session->server_hostkey) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for a copy of the host key", 0); - ret = -1; - goto clean_exit; + + if (exchange_state->state == libssh2_NB_state_sent) { + if (session->burn_optimistic_kexinit) { + /* The first KEX packet to come along will be the guess initially + * sent by the server. That guess turned out to be wrong so we + * need to silently ignore it */ + int burn_type; + + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Waiting for badly guessed KEX packet (to be ignored)"); + burn_type = libssh2_packet_burn(session, &exchange_state->burn_state); + if (burn_type == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (burn_type <= 0) { + /* Failed to receive a packet */ + ret = -1; + goto clean_exit; + } + session->burn_optimistic_kexinit = 0; + + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Burnt packet of type: %02x", (unsigned int)burn_type); + } + + exchange_state->state = libssh2_NB_state_sent1; } - memcpy(session->server_hostkey, s, session->server_hostkey_len); - s += session->server_hostkey_len; - + + if (exchange_state->state == libssh2_NB_state_sent1) { + /* Wait for KEX reply */ + rc = libssh2_packet_require_ex(session, packet_type_reply, &exchange_state->s_packet, &exchange_state->s_packet_len, + 0, NULL, 0, &exchange_state->req_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + if (rc) { + libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timed out waiting for KEX reply", 0); + ret = -1; + goto clean_exit; + } + + /* Parse KEXDH_REPLY */ + exchange_state->s = exchange_state->s_packet + 1; + + session->server_hostkey_len = libssh2_ntohu32(exchange_state->s); + exchange_state->s += 4; + session->server_hostkey = LIBSSH2_ALLOC(session, session->server_hostkey_len); + if (!session->server_hostkey) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for a copy of the host key", 0); + ret = -1; + goto clean_exit; + } + memcpy(session->server_hostkey, exchange_state->s, session->server_hostkey_len); + exchange_state->s += session->server_hostkey_len; + #if LIBSSH2_MD5 -{ - libssh2_md5_ctx fingerprint_ctx; - - libssh2_md5_init(&fingerprint_ctx); - libssh2_md5_update(fingerprint_ctx, session->server_hostkey, session->server_hostkey_len); - libssh2_md5_final(fingerprint_ctx, session->server_hostkey_md5); -} + { + libssh2_md5_ctx fingerprint_ctx; + + libssh2_md5_init(&fingerprint_ctx); + libssh2_md5_update(fingerprint_ctx, session->server_hostkey, session->server_hostkey_len); + libssh2_md5_final(fingerprint_ctx, session->server_hostkey_md5); + } #ifdef LIBSSH2DEBUG -{ - char fingerprint[50], *fprint = fingerprint; - int i; - for(i = 0; i < 16; i++, fprint += 3) { - snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]); - } - *(--fprint) = '\0'; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server's MD5 Fingerprint: %s", fingerprint); -} + { + char fingerprint[50], *fprint = fingerprint; + int i; + for(i = 0; i < 16; i++, fprint += 3) { + snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]); + } + *(--fprint) = '\0'; + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Server's MD5 Fingerprint: %s", fingerprint); + } #endif /* LIBSSH2DEBUG */ #endif /* ! LIBSSH2_MD5 */ - -{ - libssh2_sha1_ctx fingerprint_ctx; - - libssh2_sha1_init(&fingerprint_ctx); - libssh2_sha1_update (fingerprint_ctx, session->server_hostkey, session->server_hostkey_len); - libssh2_sha1_final(fingerprint_ctx, session->server_hostkey_sha1); -} + + { + libssh2_sha1_ctx fingerprint_ctx; + + libssh2_sha1_init(&fingerprint_ctx); + libssh2_sha1_update (fingerprint_ctx, session->server_hostkey, session->server_hostkey_len); + libssh2_sha1_final(fingerprint_ctx, session->server_hostkey_sha1); + } #ifdef LIBSSH2DEBUG -{ - char fingerprint[64], *fprint = fingerprint; - int i; - for(i = 0; i < 20; i++, fprint += 3) { - snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]); - } - *(--fprint) = '\0'; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server's SHA1 Fingerprint: %s", fingerprint); -} + { + char fingerprint[64], *fprint = fingerprint; + int i; + + for(i = 0; i < 20; i++, fprint += 3) { + snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]); + } + *(--fprint) = '\0'; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server's SHA1 Fingerprint: %s", fingerprint); + } #endif /* LIBSSH2DEBUG */ - - if (session->hostkey->init(session, session->server_hostkey, session->server_hostkey_len, &session->server_hostkey_abstract)) { - libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, "Unable to initialize hostkey importer", 0); - ret = -1; - goto clean_exit; - } - - f_value_len = libssh2_ntohu32(s); s += 4; - f_value = s; s += f_value_len; - _libssh2_bn_from_bin(f, f_value_len, f_value); - - h_sig_len = libssh2_ntohu32(s); s += 4; - h_sig = s; - - /* Compute the shared secret */ - _libssh2_bn_mod_exp(k, f, x, p, ctx); - k_value_len = _libssh2_bn_bytes(k) + 5; - if (_libssh2_bn_bits(k) % 8) { - /* don't need leading 00 */ - k_value_len--; - } - k_value = LIBSSH2_ALLOC(session, k_value_len); - if (!k_value) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate buffer for K", 0); - ret = -1; - goto clean_exit; - } - libssh2_htonu32(k_value, k_value_len - 4); - if (_libssh2_bn_bits(k) % 8) { - _libssh2_bn_to_bin(k, k_value + 4); - } else { - k_value[4] = 0; - _libssh2_bn_to_bin(k, k_value + 5); - } - - libssh2_sha1_init(&exchange_hash); - if (session->local.banner) { - libssh2_htonu32(h_sig_comp, - strlen((char *)session->local.banner) - 2); - libssh2_sha1_update(exchange_hash, h_sig_comp, 4); - libssh2_sha1_update(exchange_hash, (char *)session->local.banner, - strlen((char *)session->local.banner) - 2); - } else { - libssh2_htonu32(h_sig_comp, sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); - libssh2_sha1_update(exchange_hash, h_sig_comp, 4); - libssh2_sha1_update(exchange_hash, LIBSSH2_SSH_DEFAULT_BANNER, - sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); - } - - libssh2_htonu32(h_sig_comp, strlen((char *)session->remote.banner)); - libssh2_sha1_update(exchange_hash, h_sig_comp, 4); - libssh2_sha1_update(exchange_hash, session->remote.banner, - strlen((char *)session->remote.banner)); - - libssh2_htonu32(h_sig_comp, session->local.kexinit_len); - libssh2_sha1_update(exchange_hash, h_sig_comp, 4); - libssh2_sha1_update(exchange_hash, session->local.kexinit, session->local.kexinit_len); - - libssh2_htonu32(h_sig_comp, session->remote.kexinit_len); - libssh2_sha1_update(exchange_hash, h_sig_comp, 4); - libssh2_sha1_update(exchange_hash, session->remote.kexinit, session->remote.kexinit_len); - - libssh2_htonu32(h_sig_comp, session->server_hostkey_len); - libssh2_sha1_update(exchange_hash, h_sig_comp, 4); - libssh2_sha1_update(exchange_hash, session->server_hostkey, session->server_hostkey_len); - - if (packet_type_init == SSH_MSG_KEX_DH_GEX_INIT) { - /* diffie-hellman-group-exchange hashes additional fields */ + + if (session->hostkey->init(session, session->server_hostkey, session->server_hostkey_len, + &session->server_hostkey_abstract)) { + libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, "Unable to initialize hostkey importer", 0); + ret = -1; + goto clean_exit; + } + + exchange_state->f_value_len = libssh2_ntohu32(exchange_state->s); + exchange_state->s += 4; + exchange_state->f_value = exchange_state->s; + exchange_state->s += exchange_state->f_value_len; + _libssh2_bn_from_bin(exchange_state->f, exchange_state->f_value_len, exchange_state->f_value); + + exchange_state->h_sig_len = libssh2_ntohu32(exchange_state->s); + exchange_state->s += 4; + exchange_state->h_sig = exchange_state->s; + + /* Compute the shared secret */ + _libssh2_bn_mod_exp(exchange_state->k, exchange_state->f, exchange_state->x, p, exchange_state->ctx); + exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5; + if (_libssh2_bn_bits(exchange_state->k) % 8) { + /* don't need leading 00 */ + exchange_state->k_value_len--; + } + exchange_state->k_value = LIBSSH2_ALLOC(session, exchange_state->k_value_len); + if (!exchange_state->k_value) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate buffer for K", 0); + ret = -1; + goto clean_exit; + } + libssh2_htonu32(exchange_state->k_value, exchange_state->k_value_len - 4); + if (_libssh2_bn_bits(exchange_state->k) % 8) { + _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4); + } else { + exchange_state->k_value[4] = 0; + _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5); + } + + libssh2_sha1_init(&exchange_state->exchange_hash); + if (session->local.banner) { + libssh2_htonu32(exchange_state->h_sig_comp, strlen((char *)session->local.banner) - 2); + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, (char *)session->local.banner, + strlen((char *)session->local.banner) - 2); + } else { + libssh2_htonu32(exchange_state->h_sig_comp, sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, LIBSSH2_SSH_DEFAULT_BANNER, + sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); + } + + libssh2_htonu32(exchange_state->h_sig_comp, strlen((char *)session->remote.banner)); + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, session->remote.banner, + strlen((char *)session->remote.banner)); + + libssh2_htonu32(exchange_state->h_sig_comp, session->local.kexinit_len); + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, session->local.kexinit, session->local.kexinit_len); + + libssh2_htonu32(exchange_state->h_sig_comp, session->remote.kexinit_len); + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, session->remote.kexinit, session->remote.kexinit_len); + + libssh2_htonu32(exchange_state->h_sig_comp, session->server_hostkey_len); + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, session->server_hostkey, session->server_hostkey_len); + + if (packet_type_init == SSH_MSG_KEX_DH_GEX_INIT) { + /* diffie-hellman-group-exchange hashes additional fields */ #ifdef LIBSSH2_DH_GEX_NEW - libssh2_htonu32(h_sig_comp, LIBSSH2_DH_GEX_MINGROUP); - libssh2_htonu32(h_sig_comp + 4, LIBSSH2_DH_GEX_OPTGROUP); - libssh2_htonu32(h_sig_comp + 8, LIBSSH2_DH_GEX_MAXGROUP); - libssh2_sha1_update(exchange_hash, h_sig_comp, 12); + libssh2_htonu32(exchange_state->h_sig_comp, LIBSSH2_DH_GEX_MINGROUP); + libssh2_htonu32(exchange_state->h_sig_comp+4, LIBSSH2_DH_GEX_OPTGROUP); + libssh2_htonu32(exchange_state->h_sig_comp+8, LIBSSH2_DH_GEX_MAXGROUP); + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->h_sig_comp, 12); #else - libssh2_htonu32(h_sig_comp, LIBSSH2_DH_GEX_OPTGROUP); - libssh2_sha1_update(exchange_hash, h_sig_comp, 4); + libssh2_htonu32(exchange_state->h_sig_comp, LIBSSH2_DH_GEX_OPTGROUP); + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->h_sig_comp, 4); #endif + } + + if (midhash) { + libssh2_sha1_update(exchange_state->exchange_hash, midhash, midhash_len); + } + + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->e_packet + 1, exchange_state->e_packet_len - 1); + + libssh2_htonu32(exchange_state->h_sig_comp, exchange_state->f_value_len); + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->f_value, exchange_state->f_value_len); + + libssh2_sha1_update(exchange_state->exchange_hash, exchange_state->k_value, exchange_state->k_value_len); + + libssh2_sha1_final(exchange_state->exchange_hash, exchange_state->h_sig_comp); + + if (session->hostkey->sig_verify(session, exchange_state->h_sig, exchange_state->h_sig_len, + exchange_state->h_sig_comp, 20, &session->server_hostkey_abstract)) { + libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN, "Unable to verify hostkey signature", 0); + ret = -1; + goto clean_exit; + } + + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending NEWKEYS message"); + exchange_state->c = SSH_MSG_NEWKEYS; + + exchange_state->state = libssh2_NB_state_sent2; } - if (midhash) { - libssh2_sha1_update(exchange_hash, midhash, midhash_len); + if (exchange_state->state == libssh2_NB_state_sent2) { + rc = libssh2_packet_write(session, &exchange_state->c, 1); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send NEWKEYS message", 0); + ret = -1; + goto clean_exit; + } + + exchange_state->state = libssh2_NB_state_sent3; } - - libssh2_sha1_update(exchange_hash, e_packet + 1, e_packet_len - 1); - - libssh2_htonu32(h_sig_comp, f_value_len); - libssh2_sha1_update(exchange_hash, h_sig_comp, 4); - libssh2_sha1_update(exchange_hash, f_value, f_value_len); - - libssh2_sha1_update(exchange_hash, k_value, k_value_len); - - libssh2_sha1_final(exchange_hash, h_sig_comp); - - if (session->hostkey->sig_verify(session, h_sig, h_sig_len, h_sig_comp, 20, &session->server_hostkey_abstract)) { - libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN, "Unable to verify hostkey signature", 0); - ret = -1; - goto clean_exit; - } - - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending NEWKEYS message"); - c = SSH_MSG_NEWKEYS; - if (libssh2_packet_write(session, &c, 1)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send NEWKEYS message", 0); - ret = -1; - goto clean_exit; - } - - if (libssh2_packet_require(session, SSH_MSG_NEWKEYS, &tmp, &tmp_len)) { - libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timed out waiting for NEWKEYS", 0); - ret = -1; - goto clean_exit; - } - /* The first key exchange has been performed, switch to active crypt/comp/mac mode */ - session->state |= LIBSSH2_STATE_NEWKEYS; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Received NEWKEYS message"); - - /* This will actually end up being just packet_type(1) for this packet type anyway */ - LIBSSH2_FREE(session, tmp); - - if (!session->session_id) { - session->session_id = LIBSSH2_ALLOC(session, SHA_DIGEST_LENGTH); + + if (exchange_state->state == libssh2_NB_state_sent3) { + rc = libssh2_packet_require_ex(session, SSH_MSG_NEWKEYS, &exchange_state->tmp, &exchange_state->tmp_len, 0, NULL, 0, + &exchange_state->req_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timed out waiting for NEWKEYS", 0); + ret = -1; + goto clean_exit; + } + /* The first key exchange has been performed, + switch to active crypt/comp/mac mode */ + session->state |= LIBSSH2_STATE_NEWKEYS; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Received NEWKEYS message"); + + /* This will actually end up being just packet_type(1) + for this packet type anyway */ + LIBSSH2_FREE(session, exchange_state->tmp); + if (!session->session_id) { - ret = -1; - goto clean_exit; + session->session_id = LIBSSH2_ALLOC(session, SHA_DIGEST_LENGTH); + if (!session->session_id) { + ret = -1; + goto clean_exit; + } + memcpy(session->session_id, exchange_state->h_sig_comp, SHA_DIGEST_LENGTH); + session->session_id_len = SHA_DIGEST_LENGTH; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "session_id calculated"); } - memcpy(session->session_id, h_sig_comp, SHA_DIGEST_LENGTH); - session->session_id_len = SHA_DIGEST_LENGTH; - _libssh2_debug(session, LIBSSH2_DBG_KEX, - "session_id calculated"); - } - - /* Cleanup any existing cipher */ - if (session->local.crypt->dtor) { - session->local.crypt->dtor(session, &session->local.crypt_abstract); - } - - /* Calculate IV/Secret/Key for each direction */ - if (session->local.crypt->init) { - unsigned char *iv = NULL, *secret = NULL; - int free_iv = 0, free_secret = 0; - - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, session->local.crypt->iv_len, "A"); - if (!iv) { - ret = -1; - goto clean_exit; - } - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, session->local.crypt->secret_len, "C"); - if (!secret) { - LIBSSH2_FREE(session, iv); - ret = -1; - goto clean_exit; - } - if (session->local.crypt->init(session, session->local.crypt, iv, &free_iv, secret, &free_secret, 1, &session->local.crypt_abstract)) { - LIBSSH2_FREE(session, iv); - LIBSSH2_FREE(session, secret); - ret = -1; - goto clean_exit; - } - - if (free_iv) { - memset(iv, 0, session->local.crypt->iv_len); - LIBSSH2_FREE(session, iv); - } - - if (free_secret) { - memset(secret, 0, session->local.crypt->secret_len); - LIBSSH2_FREE(session, secret); - } - } - _libssh2_debug(session, LIBSSH2_DBG_KEX, - "Client to Server IV and Key calculated"); - - if (session->remote.crypt->dtor) { + /* Cleanup any existing cipher */ - session->remote.crypt->dtor(session, &session->remote.crypt_abstract); - } - - if (session->remote.crypt->init) { - unsigned char *iv = NULL, *secret = NULL; - int free_iv = 0, free_secret = 0; - - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, session->remote.crypt->iv_len, "B"); - if (!iv) { - ret = -1; - goto clean_exit; + if (session->local.crypt->dtor) { + session->local.crypt->dtor(session, &session->local.crypt_abstract); } - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, session->remote.crypt->secret_len, "D"); - if (!secret) { - LIBSSH2_FREE(session, iv); - ret = -1; - goto clean_exit; + + /* Calculate IV/Secret/Key for each direction */ + if (session->local.crypt->init) { + unsigned char *iv = NULL, *secret = NULL; + int free_iv = 0, free_secret = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, session->local.crypt->iv_len, "A"); + if (!iv) { + ret = -1; + goto clean_exit; + } + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, session->local.crypt->secret_len, "C"); + if (!secret) { + LIBSSH2_FREE(session, iv); + ret = -1; + goto clean_exit; + } + if (session->local.crypt->init(session, session->local.crypt, iv, &free_iv, secret, &free_secret, 1, + &session->local.crypt_abstract)) { + LIBSSH2_FREE(session, iv); + LIBSSH2_FREE(session, secret); + ret = -1; + goto clean_exit; + } + + if (free_iv) { + memset(iv, 0, session->local.crypt->iv_len); + LIBSSH2_FREE(session, iv); + } + + if (free_secret) { + memset(secret, 0, session->local.crypt->secret_len); + LIBSSH2_FREE(session, secret); + } } - if (session->remote.crypt->init(session, session->remote.crypt, iv, &free_iv, secret, &free_secret, 0, &session->remote.crypt_abstract)) { - LIBSSH2_FREE(session, iv); - LIBSSH2_FREE(session, secret); - ret = -1; - goto clean_exit; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Client to Server IV and Key calculated"); + + if (session->remote.crypt->dtor) { + /* Cleanup any existing cipher */ + session->remote.crypt->dtor(session, &session->remote.crypt_abstract); } - - if (free_iv) { - memset(iv, 0, session->remote.crypt->iv_len); - LIBSSH2_FREE(session, iv); + + if (session->remote.crypt->init) { + unsigned char *iv = NULL, *secret = NULL; + int free_iv = 0, free_secret = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, session->remote.crypt->iv_len, "B"); + if (!iv) { + ret = -1; + goto clean_exit; + } + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, session->remote.crypt->secret_len, "D"); + if (!secret) { + LIBSSH2_FREE(session, iv); + ret = -1; + goto clean_exit; + } + if (session->remote.crypt->init(session, session->remote.crypt, iv, &free_iv, secret, &free_secret, 0, + &session->remote.crypt_abstract)) { + LIBSSH2_FREE(session, iv); + LIBSSH2_FREE(session, secret); + ret = -1; + goto clean_exit; + } + + if (free_iv) { + memset(iv, 0, session->remote.crypt->iv_len); + LIBSSH2_FREE(session, iv); + } + + if (free_secret) { + memset(secret, 0, session->remote.crypt->secret_len); + LIBSSH2_FREE(session, secret); + } } - - if (free_secret) { - memset(secret, 0, session->remote.crypt->secret_len); - LIBSSH2_FREE(session, secret); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client IV and Key calculated"); + + if (session->local.mac->dtor) { + session->local.mac->dtor(session, &session->local.mac_abstract); } - } - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client IV and Key calculated"); - - if (session->local.mac->dtor) { - session->local.mac->dtor(session, &session->local.mac_abstract); - } - - if (session->local.mac->init) { - unsigned char *key = NULL; - int free_key = 0; - - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, session->local.mac->key_len, "E"); - if (!key) { - ret = -1; - goto clean_exit; + + if (session->local.mac->init) { + unsigned char *key = NULL; + int free_key = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, session->local.mac->key_len, "E"); + if (!key) { + ret = -1; + goto clean_exit; + } + session->local.mac->init(session, key, &free_key, &session->local.mac_abstract); + + if (free_key) { + memset(key, 0, session->local.mac->key_len); + LIBSSH2_FREE(session, key); + } } - session->local.mac->init(session, key, &free_key, &session->local.mac_abstract); - - if (free_key) { - memset(key, 0, session->local.mac->key_len); - LIBSSH2_FREE(session, key); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Client to Server HMAC Key calculated"); + + if (session->remote.mac->dtor) { + session->remote.mac->dtor(session, &session->remote.mac_abstract); } - } - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Client to Server HMAC Key calculated"); - - if (session->remote.mac->dtor) { - session->remote.mac->dtor(session, &session->remote.mac_abstract); - } - - if (session->remote.mac->init) { - unsigned char *key = NULL; - int free_key = 0; - - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, session->remote.mac->key_len, "F"); - if (!key) { - ret = -1; - goto clean_exit; - } - session->remote.mac->init(session, key, &free_key, &session->remote.mac_abstract); - - if (free_key) { - memset(key, 0, session->remote.mac->key_len); - LIBSSH2_FREE(session, key); + + if (session->remote.mac->init) { + unsigned char *key = NULL; + int free_key = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, session->remote.mac->key_len, "F"); + if (!key) { + ret = -1; + goto clean_exit; + } + session->remote.mac->init(session, key, &free_key, &session->remote.mac_abstract); + + if (free_key) { + memset(key, 0, session->remote.mac->key_len); + LIBSSH2_FREE(session, key); + } } + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client HMAC Key calculated"); } - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client HMAC Key calculated"); - - clean_exit: - _libssh2_bn_free(x); - _libssh2_bn_free(e); - _libssh2_bn_free(f); - _libssh2_bn_free(k); - _libssh2_bn_ctx_free(ctx); - - if (e_packet) { - LIBSSH2_FREE(session, e_packet); + +clean_exit: + _libssh2_bn_free(exchange_state->x); + exchange_state->x = NULL; + _libssh2_bn_free(exchange_state->e); + exchange_state->e = NULL; + _libssh2_bn_free(exchange_state->f); + exchange_state->f = NULL; + _libssh2_bn_free(exchange_state->k); + exchange_state->k = NULL; + _libssh2_bn_ctx_free(exchange_state->ctx); + exchange_state->ctx = NULL; + + if (exchange_state->e_packet) { + LIBSSH2_FREE(session, exchange_state->e_packet); + exchange_state->e_packet = NULL; } - - if (s_packet) { - LIBSSH2_FREE(session, s_packet); + + if (exchange_state->s_packet) { + LIBSSH2_FREE(session, exchange_state->s_packet); + exchange_state->s_packet = NULL; } - - if (k_value) { - LIBSSH2_FREE(session, k_value); + + if (exchange_state->k_value) { + LIBSSH2_FREE(session, exchange_state->k_value); + exchange_state->k_value = NULL; } - + if (session->server_hostkey) { LIBSSH2_FREE(session, session->server_hostkey); session->server_hostkey = NULL; } + + exchange_state->state = libssh2_NB_state_idle; return ret; } @@ -479,7 +551,8 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S /* {{{ libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange * Diffie-Hellman Group1 (Actually Group2) Key Exchange using SHA1 */ -static int libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SESSION *session) +static int +libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SESSION *session, key_exchange_state_low_t *key_state) { static const unsigned char p_value[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -498,21 +571,34 @@ static int libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SE 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - /* g == 2 */ - _libssh2_bn *p = _libssh2_bn_init(); /* SSH2 defined value (p_value) */ - _libssh2_bn *g = _libssh2_bn_init(); /* SSH2 defined value (2) */ + int ret; + + if (key_state->state == libssh2_NB_state_idle) { + /* g == 2 */ + key_state->p = _libssh2_bn_init(); /* SSH2 defined value (p_value) */ + key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */ - /* Initialize P and G */ - _libssh2_bn_set_word(g, 2); - _libssh2_bn_from_bin(p, 128, p_value); + /* Initialize P and G */ + _libssh2_bn_set_word(key_state->g, 2); + _libssh2_bn_from_bin(key_state->p, 128, p_value); - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group1 Key Exchange"); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group1 Key Exchange"); + + key_state->state = libssh2_NB_state_created; + } - ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, 128, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0); + ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, key_state->g, key_state->p, 128, SSH_MSG_KEXDH_INIT, + SSH_MSG_KEXDH_REPLY, NULL, 0, &key_state->exchange_state); + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } - _libssh2_bn_free(p); - _libssh2_bn_free(g); + _libssh2_bn_free(key_state->p); + key_state->p = NULL; + _libssh2_bn_free(key_state->g); + key_state->g = NULL; + key_state->state = libssh2_NB_state_idle; return ret; } @@ -521,7 +607,8 @@ static int libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SE /* {{{ libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange * Diffie-Hellman Group14 Key Exchange using SHA1 */ -static int libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_SESSION *session) +static int +libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_SESSION *session, key_exchange_state_low_t *key_state) { static const unsigned char p_value[256] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -556,20 +643,29 @@ static int libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_S 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - /* g == 2 */ - _libssh2_bn *p = _libssh2_bn_init(); /* SSH2 defined value (p_value) */ - _libssh2_bn *g = _libssh2_bn_init(); /* SSH2 defined value (2) */ int ret; + + if (key_state->state == libssh2_NB_state_idle) { + /* g == 2 */ + /* Initialize P and G */ + _libssh2_bn_set_word(key_state->g, 2); + _libssh2_bn_from_bin(key_state->p, 256, p_value); - /* Initialize P and G */ - _libssh2_bn_set_word(g, 2); - _libssh2_bn_from_bin(p, 256, p_value); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group14 Key Exchange"); + + key_state->state = libssh2_NB_state_created; + } + ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, key_state->g, key_state->p, 256, SSH_MSG_KEXDH_INIT, + SSH_MSG_KEXDH_REPLY, NULL, 0, &key_state->exchange_state); + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group14 Key Exchange"); - ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, 256, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0); - - _libssh2_bn_free(p); - _libssh2_bn_free(g); + key_state->state = libssh2_NB_state_idle; + _libssh2_bn_free(key_state->p); + key_state->p = NULL; + _libssh2_bn_free(key_state->g); + key_state->g = NULL; return ret; } @@ -579,55 +675,89 @@ static int libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_S * Diffie-Hellman Group Exchange Key Exchange using SHA1 * Negotiates random(ish) group for secret derivation */ -static int libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange(LIBSSH2_SESSION *session) +static int +libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange(LIBSSH2_SESSION *session, key_exchange_state_low_t *key_state) { - unsigned char request[13], *s, *data; - unsigned long data_len, p_len, g_len, request_len; - _libssh2_bn *p = _libssh2_bn_init (); - _libssh2_bn *g = _libssh2_bn_init (); + unsigned char *s; + unsigned long p_len, g_len; int ret; + int rc; + if (key_state->state == libssh2_NB_state_idle) { + key_state->p = _libssh2_bn_init (); + key_state->g = _libssh2_bn_init (); /* Ask for a P and G pair */ #ifdef LIBSSH2_DH_GEX_NEW - request[0] = SSH_MSG_KEX_DH_GEX_REQUEST; - libssh2_htonu32(request + 1, LIBSSH2_DH_GEX_MINGROUP); - libssh2_htonu32(request + 5, LIBSSH2_DH_GEX_OPTGROUP); - libssh2_htonu32(request + 9, LIBSSH2_DH_GEX_MAXGROUP); - request_len = 13; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (New Method)"); + key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST; + libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_MINGROUP); + libssh2_htonu32(key_state->request + 5, LIBSSH2_DH_GEX_OPTGROUP); + libssh2_htonu32(key_state->request + 9, LIBSSH2_DH_GEX_MAXGROUP); + key_state->request_len = 13; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (New Method)"); #else - request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD; - libssh2_htonu32(request + 1, LIBSSH2_DH_GEX_OPTGROUP); - request_len = 5; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (Old Method)"); + key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD; + libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_OPTGROUP); + key_state->request_len = 5; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (Old Method)"); #endif - - if (libssh2_packet_write(session, request, request_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send Group Exchange Request", 0); - ret = -1; - goto dh_gex_clean_exit; + + key_state->state = libssh2_NB_state_created; } - 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); - ret = -1; - goto dh_gex_clean_exit; + if (key_state->state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, key_state->request, key_state->request_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send Group Exchange Request", 0); + ret = -1; + goto dh_gex_clean_exit; + } + + key_state->state = libssh2_NB_state_sent; } - s = data + 1; - p_len = libssh2_ntohu32(s); s += 4; - _libssh2_bn_from_bin(p, p_len, s); s += p_len; + if (key_state->state == libssh2_NB_state_sent) { + rc = libssh2_packet_require_ex(session, SSH_MSG_KEX_DH_GEX_GROUP, &key_state->data, &key_state->data_len, 0, NULL, 0, + &key_state->req_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timeout waiting for GEX_GROUP reply", 0); + ret = -1; + goto dh_gex_clean_exit; + } + + key_state->state = libssh2_NB_state_sent1; + } - g_len = libssh2_ntohu32(s); s += 4; - _libssh2_bn_from_bin(g, g_len, s); s += g_len; + if (key_state->state == libssh2_NB_state_sent1) { + s = key_state->data + 1; + p_len = libssh2_ntohu32(s); s += 4; + _libssh2_bn_from_bin(key_state->p, p_len, s); s += p_len; - ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, p_len, SSH_MSG_KEX_DH_GEX_INIT, SSH_MSG_KEX_DH_GEX_REPLY, data + 1, data_len - 1); + g_len = libssh2_ntohu32(s); s += 4; + _libssh2_bn_from_bin(key_state->g, g_len, s); s += g_len; - LIBSSH2_FREE(session, data); + ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, key_state->g, key_state->p, p_len, + SSH_MSG_KEX_DH_GEX_INIT, SSH_MSG_KEX_DH_GEX_REPLY, + key_state->data + 1, key_state->data_len - 1, + &key_state->exchange_state); + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + + LIBSSH2_FREE(session, key_state->data); + } dh_gex_clean_exit: - _libssh2_bn_free(g); - _libssh2_bn_free(p); + key_state->state = libssh2_NB_state_idle; + _libssh2_bn_free(key_state->g); + key_state->p = NULL; + _libssh2_bn_free(key_state->p); + key_state->g = NULL; return ret; } @@ -727,97 +857,125 @@ static size_t libssh2_kex_method_list(unsigned char *buf, size_t list_strlen, LI */ static int libssh2_kexinit(LIBSSH2_SESSION *session) { - size_t data_len = 62; /* packet_type(1) + cookie(16) + first_packet_follows(1) + reserved(4) + length longs(40) */ - size_t kex_len, hostkey_len = 0; + /* 62 = packet_type(1) + cookie(16) + first_packet_follows(1) + + reserved(4) + length longs(40) */ + size_t data_len = 62; + size_t kex_len, hostkey_len = 0; size_t crypt_cs_len, crypt_sc_len; size_t comp_cs_len, comp_sc_len; size_t mac_cs_len, mac_sc_len; size_t lang_cs_len, lang_sc_len; unsigned char *data, *s; - - kex_len = LIBSSH2_METHOD_PREFS_LEN(session->kex_prefs, libssh2_kex_methods); - hostkey_len = LIBSSH2_METHOD_PREFS_LEN(session->hostkey_prefs, libssh2_hostkey_methods()); - crypt_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.crypt_prefs, libssh2_crypt_methods()); - crypt_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.crypt_prefs, libssh2_crypt_methods()); - mac_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.mac_prefs, libssh2_mac_methods()); - mac_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.mac_prefs, libssh2_mac_methods()); - comp_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.comp_prefs, libssh2_comp_methods()); - comp_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.comp_prefs, libssh2_comp_methods()); - lang_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.lang_prefs, NULL); - lang_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.lang_prefs, NULL); - - data_len += kex_len + hostkey_len + \ - crypt_cs_len + crypt_sc_len + \ - comp_cs_len + comp_sc_len + \ - mac_cs_len + mac_sc_len + \ - lang_cs_len + lang_sc_len; - - s = data = LIBSSH2_ALLOC(session, data_len); - if (!data) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory", 0); - return -1; - } - - *(s++) = SSH_MSG_KEXINIT; - - libssh2_random(s, 16); - s += 16; - - /* Ennumerating through these lists twice is probably (certainly?) inefficient from a CPU standpoint, but it saves multiple malloc/realloc calls */ - LIBSSH2_METHOD_PREFS_STR(s, kex_len, session->kex_prefs, libssh2_kex_methods); - LIBSSH2_METHOD_PREFS_STR(s, hostkey_len, session->hostkey_prefs, libssh2_hostkey_methods()); - LIBSSH2_METHOD_PREFS_STR(s, crypt_cs_len, session->local.crypt_prefs, libssh2_crypt_methods()); - LIBSSH2_METHOD_PREFS_STR(s, crypt_sc_len, session->remote.crypt_prefs, libssh2_crypt_methods()); - LIBSSH2_METHOD_PREFS_STR(s, mac_cs_len, session->local.mac_prefs, libssh2_mac_methods()); - LIBSSH2_METHOD_PREFS_STR(s, mac_sc_len, session->remote.mac_prefs, libssh2_mac_methods()); - LIBSSH2_METHOD_PREFS_STR(s, comp_cs_len, session->local.comp_prefs, libssh2_comp_methods()); - LIBSSH2_METHOD_PREFS_STR(s, comp_sc_len, session->remote.comp_prefs, libssh2_comp_methods()); - LIBSSH2_METHOD_PREFS_STR(s, lang_cs_len, session->local.lang_prefs, NULL); - LIBSSH2_METHOD_PREFS_STR(s, lang_sc_len, session->remote.lang_prefs, NULL); - - /* No optimistic KEX packet follows */ - /* Deal with optimistic packets - * session->flags |= KEXINIT_OPTIMISTIC - * session->flags |= KEXINIT_METHODSMATCH - */ - *(s++) = 0; - - /* Reserved == 0 */ - *(s++) = 0; - *(s++) = 0; - *(s++) = 0; - *(s++) = 0; - + int rc; + + if (session->kexinit_state == libssh2_NB_state_idle) { + kex_len = LIBSSH2_METHOD_PREFS_LEN(session->kex_prefs, libssh2_kex_methods); + hostkey_len = LIBSSH2_METHOD_PREFS_LEN(session->hostkey_prefs, libssh2_hostkey_methods()); + crypt_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.crypt_prefs, libssh2_crypt_methods()); + crypt_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.crypt_prefs, libssh2_crypt_methods()); + mac_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.mac_prefs, libssh2_mac_methods()); + mac_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.mac_prefs, libssh2_mac_methods()); + comp_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.comp_prefs, libssh2_comp_methods()); + comp_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.comp_prefs, libssh2_comp_methods()); + lang_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.lang_prefs, NULL); + lang_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.lang_prefs, NULL); + + data_len += kex_len + hostkey_len + crypt_cs_len + crypt_sc_len + \ + comp_cs_len + comp_sc_len + mac_cs_len + mac_sc_len + \ + lang_cs_len + lang_sc_len; + + s = data = LIBSSH2_ALLOC(session, data_len); + if (!data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory", 0); + return -1; + } + + *(s++) = SSH_MSG_KEXINIT; + + libssh2_random(s, 16); + s += 16; + + /* Ennumerating through these lists twice is probably (certainly?) + inefficient from a CPU standpoint, but it saves multiple + malloc/realloc calls */ + LIBSSH2_METHOD_PREFS_STR(s, kex_len, session->kex_prefs, libssh2_kex_methods); + LIBSSH2_METHOD_PREFS_STR(s, hostkey_len, session->hostkey_prefs, libssh2_hostkey_methods()); + LIBSSH2_METHOD_PREFS_STR(s, crypt_cs_len, session->local.crypt_prefs, libssh2_crypt_methods()); + LIBSSH2_METHOD_PREFS_STR(s, crypt_sc_len, session->remote.crypt_prefs, libssh2_crypt_methods()); + LIBSSH2_METHOD_PREFS_STR(s, mac_cs_len, session->local.mac_prefs, libssh2_mac_methods()); + LIBSSH2_METHOD_PREFS_STR(s, mac_sc_len, session->remote.mac_prefs, libssh2_mac_methods()); + LIBSSH2_METHOD_PREFS_STR(s, comp_cs_len, session->local.comp_prefs, libssh2_comp_methods()); + LIBSSH2_METHOD_PREFS_STR(s, comp_sc_len, session->remote.comp_prefs, libssh2_comp_methods()); + LIBSSH2_METHOD_PREFS_STR(s, lang_cs_len, session->local.lang_prefs, NULL); + LIBSSH2_METHOD_PREFS_STR(s, lang_sc_len, session->remote.lang_prefs, NULL); + + /* No optimistic KEX packet follows */ + /* Deal with optimistic packets + * session->flags |= KEXINIT_OPTIMISTIC + * session->flags |= KEXINIT_METHODSMATCH + */ + *(s++) = 0; + + /* Reserved == 0 */ + *(s++) = 0; + *(s++) = 0; + *(s++) = 0; + *(s++) = 0; + #ifdef LIBSSH2DEBUG -{ - /* Funnily enough, they'll all "appear" to be '\0' terminated */ - unsigned char *p = data + 21; /* type(1) + cookie(16) + len(4) */ - - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent KEX: %s", p); p += kex_len + 4; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent HOSTKEY: %s", p); p += hostkey_len + 4; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_CS: %s", p); p += crypt_cs_len + 4; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_SC: %s", p); p += crypt_sc_len + 4; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_CS: %s", p); p += mac_cs_len + 4; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_SC: %s", p); p += mac_sc_len + 4; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_CS: %s", p); p += comp_cs_len + 4; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_SC: %s", p); p += comp_sc_len + 4; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_CS: %s", p); p += lang_cs_len + 4; - _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_SC: %s", p); p += lang_sc_len + 4; -} + { + /* Funnily enough, they'll all "appear" to be '\0' terminated */ + unsigned char *p = data + 21; /* type(1) + cookie(16) + len(4) */ + + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent KEX: %s", p); + p += kex_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent HOSTKEY: %s", p); + p += hostkey_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_CS: %s", p); + p += crypt_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_SC: %s", p); + p += crypt_sc_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_CS: %s", p); + p += mac_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_SC: %s", p); + p += mac_sc_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_CS: %s", p); + p += comp_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_SC: %s", p); + p += comp_sc_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_CS: %s", p); + p += lang_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_SC: %s", p); + p += lang_sc_len + 4; + } #endif /* LIBSSH2DEBUG */ - if (libssh2_packet_write(session, data, data_len)) { + + session->kexinit_state = libssh2_NB_state_created; + } else { + data = session->kexinit_data; + data_len = session->kexinit_data_len; + } + + if ((rc = libssh2_packet_write(session, data, data_len)) == PACKET_EAGAIN) { + session->kexinit_data = data; + session->kexinit_data_len = data_len; + return PACKET_EAGAIN; + } else if (rc) { LIBSSH2_FREE(session, data); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEXINIT packet to remote host", 0); + session->kexinit_state = libssh2_NB_state_idle; return -1; } - + if (session->local.kexinit) { LIBSSH2_FREE(session, session->local.kexinit); } - + session->local.kexinit = data; session->local.kexinit_len = data_len; - + + session->kexinit_state = libssh2_NB_state_idle; + return 0; } /* }}} */ @@ -837,7 +995,7 @@ static unsigned char *libssh2_kex_agree_instr(unsigned char *haystack, unsigned } /* Needle at start of haystack */ - if ((strncmp(haystack, needle, needle_len) == 0) && + if ((strncmp((char *)haystack, (char *)needle, needle_len) == 0) && (needle_len == haystack_len || haystack[needle_len] == ',')) { return haystack; } @@ -845,10 +1003,10 @@ static unsigned char *libssh2_kex_agree_instr(unsigned char *haystack, unsigned s = haystack; /* Search until we run out of comas or we run out of haystack, whichever comes first */ - while ((s = strchr(s, ',')) && ((haystack_len - (s - haystack)) > needle_len)) { + while ((s = (unsigned char *)strchr((char *)s, ',')) && ((haystack_len - (s - haystack)) > needle_len)) { s++; /* Needle at X position */ - if ((strncmp(s, needle, needle_len) == 0) && + if ((strncmp((char *)s, (char *)needle, needle_len) == 0) && (((s - haystack) + needle_len) == haystack_len || s[needle_len] == ',')) { return s; } @@ -860,7 +1018,8 @@ static unsigned char *libssh2_kex_agree_instr(unsigned char *haystack, unsigned /* {{{ libssh2_get_method_by_name */ -static const LIBSSH2_COMMON_METHOD *libssh2_get_method_by_name(const char *name, int name_len, const LIBSSH2_COMMON_METHOD **methodlist) +static const +LIBSSH2_COMMON_METHOD *libssh2_get_method_by_name(const char *name, int name_len, const LIBSSH2_COMMON_METHOD **methodlist) { while (*methodlist) { if ((strlen((*methodlist)->name) == name_len) && @@ -876,19 +1035,22 @@ static const LIBSSH2_COMMON_METHOD *libssh2_get_method_by_name(const char *name, /* {{{ libssh2_kex_agree_hostkey * Agree on a Hostkey which works with this kex */ -static int libssh2_kex_agree_hostkey(LIBSSH2_SESSION *session, unsigned long kex_flags, unsigned char *hostkey, unsigned long hostkey_len) +static int +libssh2_kex_agree_hostkey(LIBSSH2_SESSION *session, unsigned long kex_flags, unsigned char *hostkey, unsigned long hostkey_len) { const LIBSSH2_HOSTKEY_METHOD **hostkeyp = libssh2_hostkey_methods(); unsigned char *s; if (session->hostkey_prefs) { - s = session->hostkey_prefs; + s = (unsigned char *)session->hostkey_prefs; while (s && *s) { - unsigned char *p = strchr(s, ','); - int method_len = (p ? (p - s) : strlen(s)); + unsigned char *p = (unsigned char *)strchr((char *)s, ','); + int method_len = (p ? (p - s) : strlen((char *)s)); if (libssh2_kex_agree_instr(hostkey, hostkey_len, s, method_len)) { - const LIBSSH2_HOSTKEY_METHOD *method = (const LIBSSH2_HOSTKEY_METHOD*)libssh2_get_method_by_name(s, method_len, (const LIBSSH2_COMMON_METHOD**)hostkeyp); + const LIBSSH2_HOSTKEY_METHOD *method = + (const LIBSSH2_HOSTKEY_METHOD*)libssh2_get_method_by_name((char *)s, method_len, + (const LIBSSH2_COMMON_METHOD**)hostkeyp); if (!method) { /* Invalid method -- Should never be reached */ @@ -914,9 +1076,7 @@ static int libssh2_kex_agree_hostkey(LIBSSH2_SESSION *session, unsigned long kex } while (hostkeyp && (*hostkeyp)->name) { - s = libssh2_kex_agree_instr(hostkey, hostkey_len, - (unsigned char *)(*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) || @@ -947,13 +1107,15 @@ static int libssh2_kex_agree_kex_hostkey(LIBSSH2_SESSION *session, unsigned char unsigned char *s; if (session->kex_prefs) { - s = session->kex_prefs; + s = (unsigned char *)session->kex_prefs; while (s && *s) { - unsigned char *q, *p = strchr(s, ','); - int method_len = (p ? (p - s) : strlen(s)); + unsigned char *q, *p = (unsigned char *)strchr((char *)s, ','); + int method_len = (p ? (p - s) : strlen((char *)s)); if ((q = libssh2_kex_agree_instr(kex, kex_len, s, method_len))) { - const LIBSSH2_KEX_METHOD *method = (const LIBSSH2_KEX_METHOD*)libssh2_get_method_by_name(s, method_len, (const LIBSSH2_COMMON_METHOD**)kexp); + const LIBSSH2_KEX_METHOD *method = + (const LIBSSH2_KEX_METHOD*)libssh2_get_method_by_name((char *)s, method_len, + (const LIBSSH2_COMMON_METHOD**)kexp); if (!method) { /* Invalid method -- Should never be reached */ @@ -981,9 +1143,7 @@ static int libssh2_kex_agree_kex_hostkey(LIBSSH2_SESSION *session, unsigned char } while (*kexp && (*kexp)->name) { - s = libssh2_kex_agree_instr(kex, kex_len, - (unsigned char *)(*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? @@ -1008,25 +1168,25 @@ static int libssh2_kex_agree_kex_hostkey(LIBSSH2_SESSION *session, unsigned char /* {{{ libssh2_kex_agree_crypt * Agree on a cipher algo */ -static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session, - libssh2_endpoint_data *endpoint, - unsigned char *crypt, - unsigned long crypt_len) +static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session, libssh2_endpoint_data *endpoint, unsigned char *crypt, + unsigned long crypt_len) { const LIBSSH2_CRYPT_METHOD **cryptp = libssh2_crypt_methods(); unsigned char *s; + (void)session; if (endpoint->crypt_prefs) { - s = endpoint->crypt_prefs; + s = (unsigned char *)endpoint->crypt_prefs; while (s && *s) { - unsigned char *p = strchr(s, ','); - int method_len = (p ? (p - s) : strlen(s)); + unsigned char *p = (unsigned char *)strchr((char *)s, ','); + int method_len = (p ? (p - s) : strlen((char *)s)); if (libssh2_kex_agree_instr(crypt, crypt_len, s, method_len)) { const LIBSSH2_CRYPT_METHOD *method = - (const LIBSSH2_CRYPT_METHOD*)libssh2_get_method_by_name((char *)s, method_len, (const LIBSSH2_COMMON_METHOD**)cryptp); + (const LIBSSH2_CRYPT_METHOD*)libssh2_get_method_by_name((char *)s, method_len, + (const LIBSSH2_COMMON_METHOD**)cryptp); if (!method) { /* Invalid method -- Should never be reached */ @@ -1043,9 +1203,7 @@ static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session, } while (*cryptp && (*cryptp)->name) { - s = libssh2_kex_agree_instr(crypt, crypt_len, - (unsigned char *)(*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; @@ -1060,21 +1218,24 @@ static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session, /* {{{ libssh2_kex_agree_mac * Agree on a message authentication hash */ -static int libssh2_kex_agree_mac(LIBSSH2_SESSION *session, libssh2_endpoint_data *endpoint, unsigned char *mac, unsigned long mac_len) +static int +libssh2_kex_agree_mac(LIBSSH2_SESSION *session, libssh2_endpoint_data *endpoint, unsigned char *mac, unsigned long mac_len) { const LIBSSH2_MAC_METHOD **macp = libssh2_mac_methods(); unsigned char *s; (void)session; if (endpoint->mac_prefs) { - s = endpoint->mac_prefs; + s = (unsigned char *)endpoint->mac_prefs; while (s && *s) { - unsigned char *p = strchr(s, ','); - int method_len = (p ? (p - s) : strlen(s)); + unsigned char *p = (unsigned char *)strchr((char *)s, ','); + int method_len = (p ? (p - s) : strlen((char *)s)); if (libssh2_kex_agree_instr(mac, mac_len, s, method_len)) { - const LIBSSH2_MAC_METHOD *method = (const LIBSSH2_MAC_METHOD*)libssh2_get_method_by_name(s, method_len, (const LIBSSH2_COMMON_METHOD**)macp); + const LIBSSH2_MAC_METHOD *method = + (const LIBSSH2_MAC_METHOD*)libssh2_get_method_by_name((char *)s, method_len, + (const LIBSSH2_COMMON_METHOD**)macp); if (!method) { /* Invalid method -- Should never be reached */ @@ -1091,9 +1252,7 @@ static int libssh2_kex_agree_mac(LIBSSH2_SESSION *session, libssh2_endpoint_data } while (*macp && (*macp)->name) { - s = libssh2_kex_agree_instr(mac, mac_len, - (unsigned char *)(*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; @@ -1108,21 +1267,24 @@ static int libssh2_kex_agree_mac(LIBSSH2_SESSION *session, libssh2_endpoint_data /* {{{ libssh2_kex_agree_comp * Agree on a compression scheme */ -static int libssh2_kex_agree_comp(LIBSSH2_SESSION *session, libssh2_endpoint_data *endpoint, unsigned char *comp, unsigned long comp_len) +static int +libssh2_kex_agree_comp(LIBSSH2_SESSION *session, libssh2_endpoint_data *endpoint, unsigned char *comp, unsigned long comp_len) { const LIBSSH2_COMP_METHOD **compp = libssh2_comp_methods(); unsigned char *s; (void)session; if (endpoint->comp_prefs) { - s = endpoint->comp_prefs; + s = (unsigned char *)endpoint->comp_prefs; while (s && *s) { - unsigned char *p = strchr(s, ','); - int method_len = (p ? (p - s) : strlen(s)); + unsigned char *p = (unsigned char *)strchr((char *)s, ','); + int method_len = (p ? (p - s) : strlen((char *)s)); if (libssh2_kex_agree_instr(comp, comp_len, s, method_len)) { - const LIBSSH2_COMP_METHOD *method = (const LIBSSH2_COMP_METHOD*)libssh2_get_method_by_name(s, method_len, (const LIBSSH2_COMMON_METHOD**)compp); + const LIBSSH2_COMP_METHOD *method = + (const LIBSSH2_COMP_METHOD*)libssh2_get_method_by_name((char *)s, method_len, + (const LIBSSH2_COMMON_METHOD**)compp); if (!method) { /* Invalid method -- Should never be reached */ @@ -1139,9 +1301,7 @@ static int libssh2_kex_agree_comp(LIBSSH2_SESSION *session, libssh2_endpoint_dat } while (*compp && (*compp)->name) { - s = libssh2_kex_agree_instr(comp, comp_len, - (unsigned char *)(*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; @@ -1163,7 +1323,8 @@ static int libssh2_kex_agree_comp(LIBSSH2_SESSION *session, libssh2_endpoint_dat static int libssh2_kex_agree_methods(LIBSSH2_SESSION *session, unsigned char *data, unsigned data_len) { unsigned char *kex, *hostkey, *crypt_cs, *crypt_sc, *comp_cs, *comp_sc, *mac_cs, *mac_sc, *lang_cs, *lang_sc; - size_t kex_len, hostkey_len, crypt_cs_len, crypt_sc_len, comp_cs_len, comp_sc_len, mac_cs_len, mac_sc_len, lang_cs_len, lang_sc_len; + size_t kex_len, hostkey_len, crypt_cs_len, crypt_sc_len, comp_cs_len; + size_t comp_sc_len, mac_cs_len, mac_sc_len, lang_cs_len, lang_sc_len; unsigned char *s = data; /* Skip packet_type, we know it already */ @@ -1247,60 +1408,96 @@ static int libssh2_kex_agree_methods(LIBSSH2_SESSION *session, unsigned char *da * Exchange keys * Returns 0 on success, non-zero on failure */ -int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange) /* session->flags |= SERVER */ +int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange, /* session->flags |= SERVER */ + key_exchange_state_t *key_state) { - unsigned char *data; - unsigned long data_len; int rc = 0; + int retcode; - /* Prevent loop in packet_add() */ - session->state |= LIBSSH2_STATE_EXCHANGING_KEYS; + if (key_state->state == libssh2_NB_state_idle) { + /* Prevent loop in packet_add() */ + session->state |= LIBSSH2_STATE_EXCHANGING_KEYS; - if (reexchange) { - session->kex = NULL; + if (reexchange) { + session->kex = NULL; - if (session->hostkey && session->hostkey->dtor) { - session->hostkey->dtor(session, &session->server_hostkey_abstract); + if (session->hostkey && session->hostkey->dtor) { + session->hostkey->dtor(session, &session->server_hostkey_abstract); + } + session->hostkey = NULL; } - session->hostkey = NULL; + + key_state->state = libssh2_NB_state_created; } if (!session->kex || !session->hostkey) { - /* Preserve in case of failure */ - unsigned char *oldlocal = session->local.kexinit; - unsigned long oldlocal_len = session->local.kexinit_len; - - session->local.kexinit = NULL; - if (libssh2_kexinit(session)) { - session->local.kexinit = oldlocal; - session->local.kexinit_len = oldlocal_len; - return -1; + if (key_state->state == libssh2_NB_state_created) { + /* Preserve in case of failure */ + key_state->oldlocal = session->local.kexinit; + key_state->oldlocal_len = session->local.kexinit_len; + + session->local.kexinit = NULL; + + key_state->state = libssh2_NB_state_sent; } - - if (libssh2_packet_require(session, SSH_MSG_KEXINIT, &data, &data_len)) { - if (session->local.kexinit) { - LIBSSH2_FREE(session, session->local.kexinit); + + if (key_state->state == libssh2_NB_state_sent) { + retcode = libssh2_kexinit(session); + if (retcode == PACKET_EAGAIN) { + return PACKET_EAGAIN; } - session->local.kexinit = oldlocal; - session->local.kexinit_len = oldlocal_len; - return -2; + else if (retcode) { + session->local.kexinit = key_state->oldlocal; + session->local.kexinit_len = key_state->oldlocal_len; + key_state->state = libssh2_NB_state_idle; + return -1; + } + + key_state->state = libssh2_NB_state_sent1; } - if (session->remote.kexinit) { - LIBSSH2_FREE(session, session->remote.kexinit); - } - session->remote.kexinit = data; - session->remote.kexinit_len = data_len; + if (key_state->state == libssh2_NB_state_sent1) { + retcode = libssh2_packet_require_ex(session, SSH_MSG_KEXINIT, &key_state->data, &key_state->data_len, 0, NULL, 0, + &key_state->req_state); + if (retcode == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (retcode) { + if (session->local.kexinit) { + LIBSSH2_FREE(session, session->local.kexinit); + } + session->local.kexinit = key_state->oldlocal; + session->local.kexinit_len = key_state->oldlocal_len; + key_state->state = libssh2_NB_state_idle; + return -1; + } - if (libssh2_kex_agree_methods(session, data, data_len)) { - rc = -3; + if (session->remote.kexinit) { + LIBSSH2_FREE(session, session->remote.kexinit); + } + session->remote.kexinit = key_state->data; + session->remote.kexinit_len = key_state->data_len; + + if (libssh2_kex_agree_methods(session, key_state->data, key_state->data_len)) { + rc = -1; + } + + key_state->state = libssh2_NB_state_sent2; } + } else { + key_state->state = libssh2_NB_state_sent2; } if (rc == 0) { - if (session->kex->exchange_keys(session)) { - libssh2_error(session, LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE, "Unrecoverable error exchanging keys", 0); - rc = -4; + if (key_state->state == libssh2_NB_state_sent2) { + retcode = session->kex->exchange_keys(session, &key_state->key_state_low); + if (retcode == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (retcode) { + libssh2_error(session, LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE, "Unrecoverable error exchanging keys", 0); + rc = -1; + } } } @@ -1315,6 +1512,8 @@ int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange) /* session->f } session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; + + key_state->state = libssh2_NB_state_idle; return rc; } @@ -1334,42 +1533,52 @@ LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session, int method prefvar = &session->kex_prefs; mlist = (const LIBSSH2_COMMON_METHOD**)libssh2_kex_methods; break; + case LIBSSH2_METHOD_HOSTKEY: prefvar = &session->hostkey_prefs; mlist = (const LIBSSH2_COMMON_METHOD**)libssh2_hostkey_methods(); break; + case LIBSSH2_METHOD_CRYPT_CS: prefvar = &session->local.crypt_prefs; mlist = (const LIBSSH2_COMMON_METHOD**)libssh2_crypt_methods(); break; + case LIBSSH2_METHOD_CRYPT_SC: prefvar = &session->remote.crypt_prefs; mlist = (const LIBSSH2_COMMON_METHOD**)libssh2_crypt_methods(); break; + case LIBSSH2_METHOD_MAC_CS: prefvar = &session->local.mac_prefs; mlist = (const LIBSSH2_COMMON_METHOD**)libssh2_mac_methods(); break; + case LIBSSH2_METHOD_MAC_SC: prefvar = &session->remote.mac_prefs; mlist = (const LIBSSH2_COMMON_METHOD**)libssh2_mac_methods(); break; + case LIBSSH2_METHOD_COMP_CS: prefvar = &session->local.comp_prefs; mlist = (const LIBSSH2_COMMON_METHOD**)libssh2_comp_methods(); break; + case LIBSSH2_METHOD_COMP_SC: prefvar = &session->remote.comp_prefs; mlist = (const LIBSSH2_COMMON_METHOD**)libssh2_comp_methods(); break; + case LIBSSH2_METHOD_LANG_CS: prefvar = &session->local.lang_prefs; mlist = NULL; break; + case LIBSSH2_METHOD_LANG_SC: prefvar = &session->remote.lang_prefs; mlist = NULL; break; + default: libssh2_error(session, LIBSSH2_ERROR_INVAL, "Invalid parameter specified for method_type", 0); return -1; diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index 9a5235d..d02cba6 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -65,6 +65,8 @@ #endif #include "libssh2.h" +#include "libssh2_publickey.h" +#include "libssh2_sftp.h" #include @@ -115,6 +117,112 @@ typedef struct _LIBSSH2_PACKET LIBSSH2_PACKET; typedef struct _LIBSSH2_PACKET_BRIGADE LIBSSH2_PACKET_BRIGADE; typedef struct _LIBSSH2_CHANNEL_BRIGADE LIBSSH2_CHANNEL_BRIGADE; +typedef int libssh2pack_t; + +typedef enum { + libssh2_NB_state_idle = 0, + libssh2_NB_state_allocated, + libssh2_NB_state_created, + libssh2_NB_state_sent, + libssh2_NB_state_sent1, + libssh2_NB_state_sent2, + libssh2_NB_state_sent3, + libssh2_NB_state_sent4, + libssh2_NB_state_sent5, + libssh2_NB_state_sent6, + libssh2_NB_state_sent7, + libssh2_NB_state_jump1, + libssh2_NB_state_jump2, + libssh2_NB_state_jump3 +} libssh2_nonblocking_states; + +typedef struct packet_require_state_t { + libssh2_nonblocking_states state; + time_t start; +} packet_require_state_t; + +typedef struct packet_requirev_state_t { + time_t start; +} packet_requirev_state_t; + +typedef struct kmdhgGPsha1kex_state_t { + libssh2_nonblocking_states state; + unsigned char *e_packet; + unsigned char *s_packet; + unsigned char *tmp; + unsigned char h_sig_comp[SHA_DIGEST_LENGTH]; + unsigned char c; + unsigned long e_packet_len; + unsigned long s_packet_len; + unsigned long tmp_len; + _libssh2_bn_ctx *ctx; + _libssh2_bn *x; + _libssh2_bn *e; + _libssh2_bn *f; + _libssh2_bn *k; + unsigned char *s; + unsigned char *f_value; + unsigned char *k_value; + unsigned char *h_sig; + unsigned long f_value_len; + unsigned long k_value_len; + unsigned long h_sig_len; + libssh2_sha1_ctx exchange_hash; + packet_require_state_t req_state; + libssh2_nonblocking_states burn_state; +} kmdhgGPsha1kex_state_t; + +typedef struct key_exchange_state_low_t { + libssh2_nonblocking_states state; + packet_require_state_t req_state; + kmdhgGPsha1kex_state_t exchange_state; + _libssh2_bn *p; /* SSH2 defined value (p_value) */ + _libssh2_bn *g; /* SSH2 defined value (2) */ + unsigned char request[13]; + unsigned char *data; + unsigned long request_len; + unsigned long data_len; +} key_exchange_state_low_t; + +typedef struct key_exchange_state_t { + libssh2_nonblocking_states state; + packet_require_state_t req_state; + key_exchange_state_low_t key_state_low; + unsigned char *data; + unsigned long data_len; + unsigned char *oldlocal; + unsigned long oldlocal_len; +} key_exchange_state_t; + +#define FwdNotReq "Forward not requested" + +typedef struct packet_queue_listener_state_t { + libssh2_nonblocking_states state; + unsigned char packet[17 + (sizeof(FwdNotReq) - 1)]; + unsigned char *host; + unsigned char *shost; + uint32_t sender_channel; + uint32_t initial_window_size; + uint32_t packet_size; + uint32_t port; + uint32_t sport; + uint32_t host_len; + uint32_t shost_len; +} packet_queue_listener_state_t; + +#define X11FwdUnAvil "X11 Forward Unavailable" + +typedef struct packet_x11_open_state_t { + libssh2_nonblocking_states state; + unsigned char packet[17 + (sizeof(X11FwdUnAvil) - 1)]; + unsigned char *shost; + uint32_t sender_channel; + uint32_t initial_window_size; + uint32_t packet_size; + uint32_t sport; + uint32_t shost_len; +} packet_x11_open_state_t; + struct _LIBSSH2_PACKET { unsigned char type; @@ -153,13 +261,12 @@ struct _LIBSSH2_CHANNEL { unsigned char *channel_type; unsigned channel_type_len; - int blocking; - /* channel's program exit status */ int exit_status; libssh2_channel_data local, remote; - unsigned long adjust_queue; /* Amount of bytes to be refunded to receive window (but not yet sent) */ + /* Amount of bytes to be refunded to receive window (but not yet sent) */ + unsigned long adjust_queue; LIBSSH2_SESSION *session; @@ -167,6 +274,68 @@ struct _LIBSSH2_CHANNEL { void *abstract; LIBSSH2_CHANNEL_CLOSE_FUNC((*close_cb)); + + /* State variables used in libssh2_channel_setenv_ex() */ + libssh2_nonblocking_states setenv_state; + unsigned char *setenv_packet; + unsigned long setenv_packet_len; + unsigned char setenv_local_channel[4]; + packet_requirev_state_t setenv_packet_requirev_state; + + /* State variables used in libssh2_channel_request_pty_ex() */ + libssh2_nonblocking_states reqPTY_state; + unsigned char *reqPTY_packet; + unsigned long reqPTY_packet_len; + unsigned char reqPTY_local_channel[4]; + packet_requirev_state_t reqPTY_packet_requirev_state; + + /* State variables used in libssh2_channel_x11_req_ex() */ + libssh2_nonblocking_states reqX11_state; + unsigned char *reqX11_packet; + unsigned long reqX11_packet_len; + unsigned char reqX11_local_channel[4]; + packet_requirev_state_t reqX11_packet_requirev_state; + + /* State variables used in libssh2_channel_process_startup() */ + libssh2_nonblocking_states process_state; + unsigned char *process_packet; + unsigned long process_packet_len; + unsigned char process_local_channel[4]; + packet_requirev_state_t process_packet_requirev_state; + + /* State variables used in libssh2_channel_flush_ex() */ + libssh2_nonblocking_states flush_state; + unsigned long flush_refund_bytes; + unsigned long flush_flush_bytes; + + /* State variables used in libssh2_channel_read_ex() */ + libssh2_nonblocking_states read_state; + LIBSSH2_PACKET *read_packet; + LIBSSH2_PACKET *read_next; + int read_block; + int read_bytes_read; + uint32_t read_local_id; + + /* State variables used in libssh2_channel_write_ex() */ + libssh2_nonblocking_states write_state; + unsigned char *write_packet; + unsigned char *write_s; + unsigned long write_packet_len; + unsigned long write_bufwrote; + size_t write_bufwrite; + + /* State variables used in libssh2_channel_close() */ + libssh2_nonblocking_states close_state; + unsigned char close_packet[5]; + + /* State variables used in libssh2_channel_wait_closedeof() */ + libssh2_nonblocking_states wait_eof_state; + + /* State variables used in libssh2_channel_wait_closed() */ + libssh2_nonblocking_states wait_closed_state; + + /* State variables used in libssh2_channel_handle_extended_data2() */ + libssh2_nonblocking_states extData2_state; }; struct _LIBSSH2_CHANNEL_BRIGADE { @@ -184,6 +353,11 @@ struct _LIBSSH2_LISTENER { int queue_maxsize; LIBSSH2_LISTENER *prev, *next; + + /* State variables used in libssh2_channel_forward_cancel() */ + libssh2_nonblocking_states chanFwdCncl_state; + unsigned char *chanFwdCncl_data; + size_t chanFwdCncl_data_len; }; typedef struct _libssh2_endpoint_data { @@ -246,6 +420,123 @@ struct transportpacket { unsigned long osent; /* number of bytes already sent */ }; +struct _LIBSSH2_PUBLICKEY { + LIBSSH2_CHANNEL *channel; + unsigned long version; + + /* State variables used in libssh2_publickey_packet_receive() */ + libssh2_nonblocking_states receive_state; + unsigned char *receive_packet; + unsigned long receive_packet_len; + + /* State variables used in libssh2_publickey_add_ex() */ + libssh2_nonblocking_states add_state; + unsigned char *add_packet; + unsigned char *add_s; + + /* State variables used in libssh2_publickey_remove_ex() */ + libssh2_nonblocking_states remove_state; + unsigned char *remove_packet; + unsigned char *remove_s; + + /* State variables used in libssh2_publickey_list_fetch() */ + libssh2_nonblocking_states listFetch_state; + unsigned char *listFetch_s; + unsigned char listFetch_buffer[12]; + unsigned char *listFetch_data; + unsigned long listFetch_data_len; +}; + +typedef enum { + sftp_read_idle = 0, + sftp_read_packet_allocated, + sftp_read_packet_created, + sftp_read_packet_sent +} libssh2_sftp_read_state; + +typedef enum { + sftp_readdir_idle = 0, + sftp_readdir_packet_created, + sftp_readdir_packet_sent +} libssh2_sftp_readdir_state; + +typedef enum { + sftp_write_idle = 0, + sftp_write_packet_created, + sftp_write_packet_sent +} libssh2_sftp_write_state; + +typedef enum { + sftp_mkdir_idle = 0, + sftp_mkdir_packet_created, + sftp_mkdir_packet_sent +} libssh2_sftp_mkdir_state; + +struct _LIBSSH2_SFTP_HANDLE { + LIBSSH2_SFTP *sftp; + LIBSSH2_SFTP_HANDLE *prev, *next; + + char *handle; + int handle_len; + + 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; +}; + +struct _LIBSSH2_SFTP { + LIBSSH2_CHANNEL *channel; + + unsigned long request_id, version; + + LIBSSH2_PACKET_BRIGADE packets; + + LIBSSH2_SFTP_HANDLE *handles; + + unsigned long last_errno; + + /* Holder for partial packet, use in libssh2_sftp_packet_read() */ + unsigned char *partial_packet; /* The data */ + unsigned long partial_len; /* Desired number of bytes */ + unsigned long partial_received; /* Bytes received so far */ + + /* Time that libssh2_sftp_packet_requirev() started reading */ + time_t requirev_start; + + /* State variables used in _libssh2_sftp_read() */ + libssh2_sftp_read_state read_state; + unsigned char *read_packet; + unsigned long read_request_id; + size_t read_total_read; + + /* State variables used in _libssh2_sftp_readdir() */ + libssh2_sftp_readdir_state readdir_state; + unsigned char *readdir_packet; + unsigned long readdir_request_id; + + /* State variables used in _libssh2_sftp_write() */ + libssh2_sftp_write_state write_state; + unsigned char *write_packet; + unsigned long write_request_id; + + /* State variables used in _libssh2_sftp_mkdir() */ + libssh2_sftp_mkdir_state mkdir_state; + unsigned char *mkdir_packet; + unsigned long mkdir_request_id; + +}; + +#define LIBSSH2_SCP_RESPONSE_BUFLEN 256 + struct _LIBSSH2_SESSION { /* Memory management callbacks */ void *abstract; @@ -319,6 +610,134 @@ struct _LIBSSH2_SESSION { #ifdef LIBSSH2DEBUG int showmask; /* what debug/trace messages to display */ #endif + + /* State variables used in libssh2_banner_send() */ + libssh2_nonblocking_states banner_TxRx_state; + char banner_TxRx_banner[256]; + ssize_t banner_TxRx_total_send; + + /* State variables used in libssh2_kexinit() */ + libssh2_nonblocking_states kexinit_state; + unsigned char *kexinit_data; + size_t kexinit_data_len; + + /* State variables used in libssh2_session_startup() */ + libssh2_nonblocking_states startup_state; + unsigned char *startup_data; + unsigned long startup_data_len; + unsigned char startup_service[sizeof("ssh-userauth") + 5 - 1]; + unsigned long startup_service_length; + packet_require_state_t startup_req_state; + key_exchange_state_t startup_key_state; + + /* State variables used in libssh2_session_disconnect_ex() */ + libssh2_nonblocking_states disconnect_state; + unsigned char *disconnect_data; + unsigned long disconnect_data_len; + + /* State variables used in libssh2_packet_read() */ + libssh2_nonblocking_states readPack_state; + int readPack_encrypted; + + /* + * State variables used in libssh2_userauth_list(), + * libssh2_userauth_password_ex(), libssh2_userauth_hostbased_fromfile_ex(), + * libssh2_userauth_publickey_fromfile_ex(), + * libssh2_userauth_keyboard_interactive_ex + */ + libssh2_nonblocking_states userauth_state; + unsigned char *userauth_data; + unsigned char userauth_data0; + unsigned long userauth_data_len; + char *userauth_newpw; + int userauth_newpw_len; + unsigned char *userauth_packet; + unsigned long userauth_packet_len; + unsigned char *userauth_method; + unsigned long userauth_method_len; + unsigned char *userauth_s; + unsigned char *userauth_b; + unsigned int userauth_auth_name_len; + char *userauth_auth_name; + unsigned userauth_auth_instruction_len; + char *userauth_auth_instruction; + unsigned int userauth_num_prompts; + int userauth_auth_failure; + LIBSSH2_USERAUTH_KBDINT_PROMPT *userauth_prompts; + LIBSSH2_USERAUTH_KBDINT_RESPONSE *userauth_responses; + packet_requirev_state_t userauth_packet_requirev_state; + + /* State variables used in libssh2_channel_open_ex() */ + libssh2_nonblocking_states open_state; + packet_requirev_state_t open_packet_requirev_state; + LIBSSH2_CHANNEL *open_channel; + unsigned char *open_packet; + unsigned long open_packet_len; + unsigned char *open_data; + unsigned long open_data_len; + unsigned long open_local_channel; + + /* State variables used in libssh2_channel_direct_tcpip_ex() */ + libssh2_nonblocking_states direct_state; + unsigned char *direct_message; + unsigned long direct_host_len; + unsigned long direct_shost_len; + unsigned long direct_message_len; + + /* State variables used in libssh2_channel_forward_listen_ex() */ + libssh2_nonblocking_states fwdLstn_state; + unsigned char *fwdLstn_packet; + unsigned long fwdLstn_host_len; + unsigned long fwdLstn_packet_len; + packet_requirev_state_t fwdLstn_packet_requirev_state; + + /* State variables used in libssh2_publickey_init() */ + libssh2_nonblocking_states pkeyInit_state; + LIBSSH2_PUBLICKEY *pkeyInit_pkey; + LIBSSH2_CHANNEL *pkeyInit_channel; + unsigned char *pkeyInit_data; + unsigned long pkeyInit_data_len; + + /* State variables used in libssh2_packet_add() */ + libssh2_nonblocking_states packAdd_state; + LIBSSH2_PACKET *packAdd_packet; + LIBSSH2_CHANNEL *packAdd_channel; + unsigned long packAdd_data_head; + key_exchange_state_t packAdd_key_state; + packet_queue_listener_state_t packAdd_Qlstn_state; + packet_x11_open_state_t packAdd_x11open_state; + + /* State variables used in fullpacket() */ + libssh2_nonblocking_states fullpacket_state; + int fullpacket_macstate; + int fullpacket_payload_len; + libssh2pack_t fullpacket_packet_type; + + /* State variables used in libssh2_sftp_init() */ + libssh2_nonblocking_states sftpInit_state; + LIBSSH2_SFTP *sftpInit_sftp; + LIBSSH2_CHANNEL *sftpInit_channel; + unsigned char sftpInit_buffer[9]; /* sftp_header(5){excludes request_id} + version_id(4) */ + + /* State variables used in libssh2_scp_recv() */ + libssh2_nonblocking_states scpRecv_state; + unsigned char *scpRecv_command; + unsigned long scpRecv_command_len; + unsigned char scpRecv_response[LIBSSH2_SCP_RESPONSE_BUFLEN]; + unsigned long scpRecv_response_len; + long scpRecv_mode; + long scpRecv_size; + long scpRecv_mtime; + long scpRecv_atime; + LIBSSH2_CHANNEL *scpRecv_channel; + + /* State variables used in libssh2_scp_send_ex() */ + libssh2_nonblocking_states scpSend_state; + unsigned char *scpSend_command; + unsigned long scpSend_command_len; + unsigned char scpSend_response[LIBSSH2_SCP_RESPONSE_BUFLEN]; + unsigned long scpSend_response_len; + LIBSSH2_CHANNEL *scpSend_channel; }; /* session.state bits */ @@ -342,7 +761,7 @@ struct _LIBSSH2_KEX_METHOD { const char *name; /* Key exchange, populates session->* and returns 0 on success, non-0 on error */ - int (*exchange_keys)(LIBSSH2_SESSION *session); + int (*exchange_keys)(LIBSSH2_SESSION *session, key_exchange_state_low_t *key_state); long flags; }; @@ -451,7 +870,7 @@ static inline void _libssh2_debug(LIBSSH2_SESSION *session, int context, session->err_code = errcode; \ } -#endif /* LIBSSH2_DEBUG_ENABLED */ +#endif /* ! LIBSSH2DEBUG */ #define LIBSSH2_SOCKET_UNKNOWN 1 @@ -527,59 +946,43 @@ void libssh2_htonu32(unsigned char *buf, unsigned long val); void libssh2_htonu64(unsigned char *buf, libssh2_uint64_t val); #define LIBSSH2_READ_TIMEOUT 60 /* generic timeout in seconds used when - waiting for more data to arrive */ + 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_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 +#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)) +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_askv_ex(LIBSSH2_SESSION *session, const 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); -#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) -int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, const 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_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, packet_require_state_t *state); +int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, const unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, packet_requirev_state_t *state); +int libssh2_packet_burn(LIBSSH2_SESSION *session, libssh2_nonblocking_states *state); 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); +int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange, key_exchange_state_t *state); 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); +int _libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking); /* Let crypt.c/hostkey.c/comp.c/mac.c expose their method structs */ const LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void); diff --git a/src/misc.c b/src/misc.c index e3c8b66..77af4c8 100644 --- a/src/misc.c +++ b/src/misc.c @@ -201,7 +201,7 @@ void _libssh2_debug(LIBSSH2_SESSION *session, int context, if (context < 1 || context > 8) { context = 0; } - if(!(session->showmask & (1<showmask & (1<listeners; + unsigned long packet_len = 17 + (sizeof(FwdNotReq) - 1); + unsigned char *p; + LIBSSH2_LISTENER *listen = session->listeners; char failure_code = 1; /* SSH_OPEN_ADMINISTRATIVELY_PROHIBITED */ - uint32_t sender_channel, initial_window_size, packet_size; - unsigned char *host, *shost; - uint32_t port, sport, host_len, shost_len; + int rc; + (void)datalen; - sender_channel = libssh2_ntohu32(s); s += 4; + if (listen_state->state == libssh2_NB_state_idle) { + listen_state->sender_channel = libssh2_ntohu32(s); s += 4; + + listen_state->initial_window_size = libssh2_ntohu32(s); s += 4; + listen_state->packet_size = libssh2_ntohu32(s); s += 4; + + listen_state->host_len = libssh2_ntohu32(s); s += 4; + listen_state->host = s; s += listen_state->host_len; + listen_state->port = libssh2_ntohu32(s); s += 4; + + listen_state->shost_len = libssh2_ntohu32(s); s += 4; + listen_state->shost = s; s += listen_state->shost_len; + listen_state->sport = libssh2_ntohu32(s); s += 4; + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Remote received connection from %s:%ld to %s:%ld", + listen_state->shost, listen_state->sport, + listen_state->host, listen_state->port); + + listen_state->state = libssh2_NB_state_allocated; + } - 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; - - shost_len = libssh2_ntohu32(s); s += 4; - shost = s; s += shost_len; - sport = libssh2_ntohu32(s); s += 4; - - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Remote received connection from %s:%ld to %s:%ld", shost, sport, host, port); - - 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 */ - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Listener queue full, ignoring"); - break; + if (listen_state->state != libssh2_NB_state_sent) { + while (listen) { + if ((listen->port == (int)listen_state->port) && + (strlen(listen->host) == listen_state->host_len) && + (memcmp(listen->host, listen_state->host, listen_state->host_len) == 0)) { + /* This is our listener */ + LIBSSH2_CHANNEL *channel, *last_queued = listen->queue; + + last_queued = listen->queue; + if (listen_state->state == libssh2_NB_state_allocated) { + if (listen->queue_maxsize && + (listen->queue_maxsize <= listen->queue_size)) { + /* Queue is full */ + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Listener queue full, ignoring"); + listen_state->state = libssh2_NB_state_sent; + 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 */ + listen_state->state = libssh2_NB_state_sent; + 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 */ + listen_state->state = libssh2_NB_state_sent; + break; + } + memcpy(channel->channel_type, "forwarded-tcpip", + channel->channel_type_len + 1); + + channel->remote.id = listen_state->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 = listen_state->initial_window_size; + channel->local.window_size = listen_state->initial_window_size; + channel->local.packet_size = listen_state->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); + + p = listen_state->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; + + listen_state->state = libssh2_NB_state_created; + } + + if (listen_state->state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, listen_state->packet, + 17); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send channel open confirmation", + 0); + listen_state->state = libssh2_NB_state_idle; + return -1; + } + + /* Link the channel into the end of the queue list */ + + if (!last_queued) { + listen->queue = channel; + listen_state->state = libssh2_NB_state_idle; + return 0; + } + + while (last_queued->next) { + last_queued = last_queued->next; + } + + last_queued->next = channel; + channel->prev = last_queued; + + listen->queue_size++; + + listen_state->state = libssh2_NB_state_idle; + return 0; + } } - 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->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; - - _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); - - 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; - } - - /* Link the channel into the end of the queue list */ - - if (!last_queued) { - l->queue = channel; - return 0; - } - - while (last_queued->next) last_queued = last_queued->next; - - last_queued->next = channel; - channel->prev = last_queued; - - l->queue_size++; - - return 0; + listen = listen->next; } - l = l->next; + listen_state->state = libssh2_NB_state_sent; } /* We're not listening to you */ { - - p = packet; + p = listen_state->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, listen_state->sender_channel); + p += 4; + libssh2_htonu32(p, failure_code); + p += 4; + libssh2_htonu32(p, sizeof(FwdNotReq) - 1); + p += 4; + memcpy(s, FwdNotReq, sizeof(FwdNotReq) - 1); + p += sizeof(FwdNotReq) - 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); + rc = libssh2_packet_write(session, listen_state->packet, packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send open failure", 0); + listen_state->state = libssh2_NB_state_idle; return -1; } + listen_state->state = libssh2_NB_state_idle; return 0; } } @@ -196,109 +255,147 @@ static inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, /* {{{ libssh2_packet_x11_open * Accept a forwarded X11 connection */ -static inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, - unsigned char *data, unsigned long datalen) +static inline int +libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data, + unsigned long datalen, + packet_x11_open_state_t *x11open_state) { 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) */ + /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ + unsigned long packet_len = 17 + (sizeof(X11FwdUnAvil) - 1); + unsigned char *p; LIBSSH2_CHANNEL *channel; - unsigned long sender_channel, initial_window_size, packet_size; - unsigned char *shost; - unsigned long sport, shost_len; + int rc; + (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; + if (x11open_state->state == libssh2_NB_state_idle) { + x11open_state->sender_channel = libssh2_ntohu32(s); s += 4; + x11open_state->initial_window_size = libssh2_ntohu32(s); s += 4; + x11open_state->packet_size = libssh2_ntohu32(s); s += 4; + x11open_state->shost_len = libssh2_ntohu32(s); s += 4; + x11open_state->shost = s; s += x11open_state->shost_len; + x11open_state->sport = libssh2_ntohu32(s); s += 4; + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "X11 Connection Received from %s:%ld on channel %lu", + x11open_state->shost, x11open_state->sport, + x11open_state->sender_channel); + + x11open_state->state = libssh2_NB_state_allocated; + } - _libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection Received from %s:%ld on channel %lu", shost, sport, sender_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->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; - - _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); - 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 (x11open_state->state == libssh2_NB_state_allocated) { + 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->remote.id = x11open_state->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 = x11open_state->initial_window_size; + channel->local.window_size = x11open_state->initial_window_size; + channel->local.packet_size = x11open_state->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); + p = x11open_state->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; + + x11open_state->state = libssh2_NB_state_created; } - /* 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; + if (x11open_state->state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, x11open_state->packet, 17); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send channel open confirmation", 0); + x11open_state->state = libssh2_NB_state_idle; + 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 *)x11open_state->shost, x11open_state->sport); + + x11open_state->state = libssh2_NB_state_idle; + return 0; } - 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 = x11open_state->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, x11open_state->sender_channel); p += 4; + libssh2_htonu32(p, failure_code); p += 4; + libssh2_htonu32(p, sizeof(X11FwdUnAvil) - 1); p += 4; + memcpy(s, X11FwdUnAvil, sizeof(X11FwdUnAvil) - 1); + p += sizeof(X11FwdUnAvil) - 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); + rc = libssh2_packet_write(session, x11open_state->packet, packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send open failure", 0); + x11open_state->state = libssh2_NB_state_idle; return -1; } + x11open_state->state = libssh2_NB_state_idle; return 0; } /* }}} */ @@ -306,291 +403,470 @@ x11_exit: /* {{{ libssh2_packet_new * Create a new packet and attach it to the brigade */ -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; + int rc; - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Packet type %d received, length=%d", (int)data[0], (int)datalen); - 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; + if (session->packAdd_state == libssh2_NB_state_idle) { + session->packAdd_data_head = 0; + + /* Zero the whole thing out */ + memset(&session->packAdd_key_state, 0, + sizeof(session->packAdd_key_state)); + + /* Zero the whole thing out */ + memset(&session->packAdd_Qlstn_state, 0, + sizeof(session->packAdd_Qlstn_state)); + + /* Zero the whole thing out */ + memset(&session->packAdd_x11open_state, 0, + sizeof(session->packAdd_x11open_state)); + + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "Packet type %d received, length=%d", + (int)data[0], (int)datalen); + 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); + } + LIBSSH2_FREE(session, data); + return -1; + } } else { - libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0); + 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); + LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, + "Invalid MAC received", + sizeof("Invalid MAC received") - 1, + "", 0); } LIBSSH2_FREE(session, data); 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); - } + } + + session->packAdd_state = libssh2_NB_state_allocated; + } + + /* + * =============================== NOTE =============================== + * I know this is very ugly and not a really good use of "goto", but + * this case statement would be even uglier to do it any other way + */ + if (session->packAdd_state == libssh2_NB_state_jump1) { + goto libssh2_packet_add_jump_point1; + } + else if (session->packAdd_state == libssh2_NB_state_jump2) { + goto libssh2_packet_add_jump_point2; + } + else if (session->packAdd_state == libssh2_NB_state_jump3) { + goto libssh2_packet_add_jump_point3; + } + + if (session->packAdd_state == libssh2_NB_state_allocated) { + /* 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); + /* 9 = packet_type(1) + reason(4) + message_len(4) */ + message = (char *)data + 9; + 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); + } + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "Disconnect(%d): %s(%s)", reason, + message, language); + LIBSSH2_FREE(session, data); + session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; + session->packAdd_state = libssh2_NB_state_idle; + 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, data); + session->packAdd_state = libssh2_NB_state_idle; + 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); + /* 6 = packet_type(1) + display(1) + message_len(4) */ + message = (char *)data + 6; + 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); + } + /* + * _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_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + break; + + case SSH_MSG_CHANNEL_EXTENDED_DATA: + /* streamid(4) */ + session->packAdd_data_head += 4; + case SSH_MSG_CHANNEL_DATA: + /* packet_type(1) + channelno(4) + datalen(4) */ + session->packAdd_data_head += 9; + { + session->packAdd_channel = libssh2_channel_locate(session, + libssh2_ntohu32(data + 1)); + + if (!session->packAdd_channel) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN, + "Packet received for unknown channel, ignoring", + 0); + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } +#ifdef LIBSSH2DEBUG + { + unsigned long stream_id = 0; + + 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 - session->packAdd_data_head), + session->packAdd_channel->local.id, + session->packAdd_channel->remote.id, + stream_id); + } +#endif + if ((session->packAdd_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); + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Ignoring extended data and refunding %d bytes", + (int)(datalen - 13)); + /* Adjust the window based on the block we just freed */ +libssh2_packet_add_jump_point1: + session->packAdd_state = libssh2_NB_state_jump1; + rc = libssh2_channel_receive_window_adjust(session->packAdd_channel, + datalen - 13, 0); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + + /* + * REMEMBER! remote means remote as source of data, + * NOT remote window! + */ + if (session->packAdd_channel->remote.packet_size < (datalen - session->packAdd_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 = session->packAdd_channel->remote.packet_size + session->packAdd_data_head; + } + if (session->packAdd_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); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + /* Reset EOF status */ + session->packAdd_channel->remote.eof = 0; + + if ((datalen - session->packAdd_data_head) > session->packAdd_channel->remote.window_size) { + libssh2_error(session, + LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, + "Remote sent more data than current window allows, truncating", + 0); + datalen = session->packAdd_channel->remote.window_size + session->packAdd_data_head; + } else { + /* Now that we've received it, shrink our window */ + session->packAdd_channel->remote.window_size -= datalen - session->packAdd_data_head; + } + } + break; + + case SSH_MSG_CHANNEL_EOF: + { + session->packAdd_channel = libssh2_channel_locate(session, + libssh2_ntohu32(data + 1)); + + if (!session->packAdd_channel) { + /* We may have freed already, just quietly ignore this... */ + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + + _libssh2_debug(session, + LIBSSH2_DBG_CONN, + "EOF received for channel %lu/%lu", + session->packAdd_channel->local.id, + session->packAdd_channel->remote.id); + session->packAdd_channel->remote.eof = 1; + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + 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 */ + session->packAdd_channel = libssh2_channel_locate(session, libssh2_ntohu32(data+1)); + + if (session->packAdd_channel) { + session->packAdd_channel->exit_status = libssh2_ntohu32(data + 9 + sizeof("exit-status")); + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Exit status %lu received for channel %lu/%lu", + session->packAdd_channel->exit_status, + session->packAdd_channel->local.id, + session->packAdd_channel->remote.id); + } + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + } + break; + + case SSH_MSG_CHANNEL_CLOSE: + { + session->packAdd_channel = libssh2_channel_locate(session, + libssh2_ntohu32(data + 1)); + + if (!session->packAdd_channel) { + /* We may have freed already, just quietly ignore this... */ + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Close received for channel %lu/%lu", + session->packAdd_channel->local.id, + session->packAdd_channel->remote.id); + + session->packAdd_channel->remote.close = 1; + session->packAdd_channel->remote.eof = 1; + /* TODO: Add a callback for this */ + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + 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)) { + +libssh2_packet_add_jump_point2: + session->packAdd_state = libssh2_NB_state_jump2; + rc = libssh2_packet_queue_listener(session, data, datalen, + &session->packAdd_Qlstn_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return rc; + } + if ((datalen >= (sizeof("x11") + 4)) && + ((sizeof("x11")-1) == libssh2_ntohu32(data + 1)) && + (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) { + +libssh2_packet_add_jump_point3: + session->packAdd_state = libssh2_NB_state_jump3; + rc = libssh2_packet_x11_open(session, data, datalen, + &session->packAdd_x11open_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return rc; + } + break; + + case SSH_MSG_CHANNEL_WINDOW_ADJUST: + { + session->packAdd_channel = libssh2_channel_locate(session, + libssh2_ntohu32(data + 1)); + unsigned long bytestoadd = libssh2_ntohu32(data + 5); + + if (session->packAdd_channel && bytestoadd) { + session->packAdd_channel->local.window_size += bytestoadd; + } + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu", + session->packAdd_channel->local.id, + session->packAdd_channel->remote.id, + bytestoadd, + session->packAdd_channel->local.window_size); + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + break; + } + + session->packAdd_state = libssh2_NB_state_sent; + } + + if (session->packAdd_state == libssh2_NB_state_sent) { + session->packAdd_packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); + if (!session->packAdd_packet) { + _libssh2_debug(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for LIBSSH2_PACKET"); LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; 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; - - 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); - } - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnect(%d): %s(%s)", reason, message, language); - 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, 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'; - - if (session->ssh_msg_debug) { - LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len); - } - /* _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_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; - } -#ifdef LIBSSH2DEBUG - { - unsigned long stream_id = 0; - - 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); - } -#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); - - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Ignoring extended data and refunding %d bytes", (int)(datalen - 13)); - /* Adjust the window based on the block we just freed */ - libssh2_channel_receive_window_adjust(channel, datalen - 13, 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; - - 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; - } - - _libssh2_debug(session, LIBSSH2_DBG_CONN, "EOF received for channel %lu/%lu", channel->local.id, channel->remote.id); - 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)) { - - /* 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")); - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Exit status %lu received for channel %lu/%lu", channel->exit_status, channel->local.id, channel->remote.id); - } - - 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; - } - _libssh2_debug(session, LIBSSH2_DBG_CONN, "Close received for channel %lu/%lu", channel->local.id, channel->remote.id); - - 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 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); - - if (channel && bytestoadd) { - channel->local.window_size += bytestoadd; - } - _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_FREE(session, data); - return 0; - } - break; + memset(session->packAdd_packet, 0, sizeof(LIBSSH2_PACKET)); + + session->packAdd_packet->data = data; + session->packAdd_packet->data_len = datalen; + session->packAdd_packet->data_head = session->packAdd_data_head; + session->packAdd_packet->mac = macstate; + session->packAdd_packet->brigade = &session->packets; + session->packAdd_packet->next = NULL; + + if (session->packets.tail) { + session->packAdd_packet->prev = session->packets.tail; + session->packAdd_packet->prev->next = session->packAdd_packet; + session->packets.tail = session->packAdd_packet; + } else { + session->packets.head = session->packAdd_packet; + session->packets.tail = session->packAdd_packet; + session->packAdd_packet->prev = NULL; } - - packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); - if (!packet) { - _libssh2_debug(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for LIBSSH2_PACKET"); - LIBSSH2_FREE(session, data); - return -1; - } - 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; - - 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; + + session->packAdd_state = libssh2_NB_state_sent1; } - 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)) || + (session->packAdd_state == libssh2_NB_state_sent2)) { + if (session->packAdd_state == libssh2_NB_state_sent1) { + /* + * Remote wants new keys + * Well, it's already in the brigade, + * let's just call back into ourselves + */ + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Renegotiating Keys"); + + session->packAdd_state = libssh2_NB_state_sent2; + } + /* + * 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_debug(session, LIBSSH2_DBG_TRANS, - "Renegotiating Keys"); - 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 */ + rc = libssh2_kex_exchange(session, 1, &session->packAdd_key_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } } + session->packAdd_state = libssh2_NB_state_idle; return 0; } /* }}} */ @@ -599,15 +875,18 @@ int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, size_t dat * Scan the brigade for a matching packet type, optionally poll the socket for * a packet first */ -int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, - unsigned char **data, unsigned long *data_len, - unsigned long match_ofs, - const unsigned char *match_buf, - unsigned long match_len, int poll_socket) +int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket) { LIBSSH2_PACKET *packet = session->packets.head; if (poll_socket) { + /* + * XXX CHECK *** + * When "poll_socket" is "1" libhss2_packet_read() can return + * PACKET_EAGAIN. I am not sure what should happen, but internally + * there is only one location that might do so, libssh2_packet_askv_ex() + */ libssh2pack_t rc = libssh2_packet_read(session); if ((rc < 0) && !packet) { return rc; @@ -617,10 +896,8 @@ int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, "Looking for packet of type: %d", (int)packet_type); 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))) { + 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; @@ -652,8 +929,7 @@ int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, */ int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, const unsigned char *packet_types, - unsigned char **data, - unsigned long *data_len, + unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket) @@ -661,10 +937,15 @@ int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, int i, packet_types_len = strlen((char *)packet_types); for(i = 0; i < packet_types_len; i++) { - if (0 == libssh2_packet_ask_ex(session, packet_types[i], - data, data_len, match_ofs, - match_buf, match_len, - i ? 0 : poll_socket)) { + /* + * XXX CHECK XXX + * When "poll_socket" is "1" libssh2_packet_ask_ex() could + * return PACKET_EAGAIN. Not sure the correct action, I + * think it is right as is. + */ + 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; } } @@ -681,75 +962,72 @@ int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, * * FIXME: convert to use poll on systems that have it. */ -int libssh2_waitsocket(LIBSSH2_SESSION *session, - long seconds) +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, packet_require_state_t *state) { - 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; + if (state->start == 0) { + 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; + } + + state->start = time(NULL); + + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "May block 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); - while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { libssh2pack_t ret = libssh2_packet_read(session); - if ((ret < 0) && (ret != PACKET_EAGAIN)) { + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if ((ret == 0) && (!session->socket_block)) { + /* If we are in non-blocking and there is no data, return that */ + return PACKET_EAGAIN; + } + else if (ret < 0) { + state->start = 0; /* 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); + else if (ret == packet_type) { + /* 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); + state->start = 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); + else if (ret == 0) { + /* nothing available, wait until data arrives or we time out */ + long left = LIBSSH2_READ_TIMEOUT - (time(NULL) - state->start); - if((left <= 0) || - (libssh2_waitsocket(session, left) <= 0)) { + if ((left <= 0) || (libssh2_waitsocket(session, left) <= 0)) { + state->start = 0; return PACKET_TIMEOUT; } } @@ -761,43 +1039,52 @@ int libssh2_packet_require_ex(LIBSSH2_SESSION *session, /* }}} */ /* {{{ libssh2_packet_burn - * Loops libssh2_packet_read() until any packet is available and promptly discards it + * Loops libssh2_packet_read() until any packet is available and promptly + * discards it * Used during KEX exchange to discard badly guessed KEX_INIT packets */ -int libssh2_packet_burn(LIBSSH2_SESSION *session) +int libssh2_packet_burn(LIBSSH2_SESSION *session, libssh2_nonblocking_states *state) { 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; + int ret; - 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 (*state == libssh2_NB_state_idle) { + 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; + } + + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet becomes available to burn"); + *state = libssh2_NB_state_created; } - - _libssh2_debug(session, LIBSSH2_DBG_TRANS, - "Blocking until packet becomes available to burn"); - + while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - int ret = libssh2_packet_read(session); - if (ret < 0) { + if ((ret = libssh2_packet_read(session)) == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (ret < 0) { + *state = libssh2_NB_state_idle; return ret; } - if (ret == 0) { + else 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)) { + 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); + *state = libssh2_NB_state_idle; return ret; } } @@ -807,56 +1094,56 @@ int libssh2_packet_burn(LIBSSH2_SESSION *session) } /* }}} */ -/* {{{ libssh2_packet_requirev - * [BLOCKING] +/* + * {{{ libssh2_packet_requirev + * * 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, - const 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, const unsigned char *packet_types, unsigned char **data, + unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, + unsigned long match_len, packet_requirev_state_t *state) { - time_t start = time(NULL); - - if (libssh2_packet_askv_ex(session, packet_types, data, data_len, - match_ofs, match_buf, match_len, 0) == 0) { + 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 */ + brigade */ + state->start = 0; return 0; } + if (state->start == 0) { + state->start = time(NULL); + } + while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) { int ret = libssh2_packet_read(session); if ((ret < 0) && (ret != PACKET_EAGAIN)) { + state->start = 0; return ret; } if (ret <= 0) { - long left = LIBSSH2_READ_TIMEOUT - - (time(NULL) - start); + long left = LIBSSH2_READ_TIMEOUT - (time(NULL) - state->start); - if((left <= 0) || - (libssh2_waitsocket(session, left) <= 0 )) { + if ((left <= 0) || (libssh2_waitsocket(session, left) <= 0 )) { + state->start = 0; return PACKET_TIMEOUT; } + else if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } } if (strchr((char *)packet_types, ret)) { - /* Be lazy, let packet_ask pull it out of the - brigade */ - return libssh2_packet_askv_ex(session, packet_types, - data, data_len, - match_ofs, match_buf, - match_len, 0); + /* Be lazy, let packet_ask pull it out of the brigade */ + return libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0); } } /* Only reached if the socket died */ + state->start = 0; return -1; } /* }}} */ diff --git a/src/publickey.c b/src/publickey.c index 3a7678e..cb593f6 100644 --- a/src/publickey.c +++ b/src/publickey.c @@ -38,11 +38,6 @@ #include "libssh2_priv.h" #include "libssh2_publickey.h" -struct _LIBSSH2_PUBLICKEY { - LIBSSH2_CHANNEL *channel; - unsigned long version; -}; - #define LIBSSH2_PUBLICKEY_VERSION 2 /* Numericised response codes -- Not IETF standard, just a local representation */ @@ -95,7 +90,9 @@ static const LIBSSH2_PUBLICKEY_CODE_LIST libssh2_publickey_status_codes[] = { #define LIBSSH2_PUBLICKEY_STATUS_TEXT_START "Publickey Subsystem Error: \"" #define LIBSSH2_PUBLICKEY_STATUS_TEXT_MID "\" Server Resports: \"" #define LIBSSH2_PUBLICKEY_STATUS_TEXT_END "\"" -static void libssh2_publickey_status_error(const LIBSSH2_PUBLICKEY *pkey, LIBSSH2_SESSION *session, int status, const unsigned char *message, int message_len) +static void +libssh2_publickey_status_error(const LIBSSH2_PUBLICKEY *pkey, LIBSSH2_SESSION *session, int status, + const unsigned char *message, int message_len) { const char *status_text; int status_text_len; @@ -139,34 +136,52 @@ static void libssh2_publickey_status_error(const LIBSSH2_PUBLICKEY *pkey, LIBSSH /* {{{ libssh2_publickey_packet_receive * Read a packet from the subsystem */ -static int libssh2_publickey_packet_receive(LIBSSH2_PUBLICKEY *pkey, unsigned char **data, unsigned long *data_len) +static int +libssh2_publickey_packet_receive(LIBSSH2_PUBLICKEY *pkey, unsigned char **data, unsigned long *data_len) { LIBSSH2_CHANNEL *channel = pkey->channel; LIBSSH2_SESSION *session = channel->session; unsigned char buffer[4]; - unsigned long packet_len; - unsigned char *packet; + int rc; - if (_libssh2_channel_read(channel, (char *)buffer, 4) != 4) { - libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid response from publickey subsystem", 0); - return -1; + if (pkey->receive_state == libssh2_NB_state_idle) { + rc = libssh2_channel_read_ex(channel, 0, (char *)buffer, 4); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc != 4) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid response from publickey subsystem", 0); + return -1; + } + + pkey->receive_packet_len = libssh2_ntohu32(buffer); + pkey->receive_packet = LIBSSH2_ALLOC(session, pkey->receive_packet_len); + if (!pkey->receive_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate publickey response buffer", 0); + return -1; + } + + pkey->receive_state = libssh2_NB_state_sent; } - packet_len = libssh2_ntohu32(buffer); - packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate publickey response buffer", 0); - return -1; - } + if (pkey->receive_state == libssh2_NB_state_sent) { + rc = libssh2_channel_read_ex(channel, 0, (char *)pkey->receive_packet, pkey->receive_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc != pkey->receive_packet_len) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for publickey subsystem response packet", 0); + LIBSSH2_FREE(session, pkey->receive_packet); + pkey->receive_packet = NULL; + pkey->receive_state = libssh2_NB_state_idle; + return -1; + } - 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; + *data = pkey->receive_packet; + *data_len = pkey->receive_packet_len; } - - *data = packet; - *data_len = packet_len; + + pkey->receive_state = libssh2_NB_state_idle; return 0; } @@ -214,9 +229,14 @@ static int libssh2_publickey_response_success(LIBSSH2_PUBLICKEY *pkey) unsigned char *data, *s; unsigned long data_len; int response; + int rc; while (1) { - if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) { + rc = libssh2_publickey_packet_receive(pkey, &data, &data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0); return -1; } @@ -278,114 +298,182 @@ static int libssh2_publickey_response_success(LIBSSH2_PUBLICKEY *pkey) */ LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session) { - LIBSSH2_PUBLICKEY *pkey = NULL; - LIBSSH2_CHANNEL *channel = NULL; + /* 19 = packet_len(4) + version_len(4) + "version"(7) + version_num(4) */ unsigned char buffer[19]; - /* packet_len(4) + - version_len(4) + - "version"(7) + - version_num(4) */ - unsigned char *s, *data = NULL; - unsigned long data_len; + unsigned char *s; int response; + int rc; - _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Initializing publickey subsystem"); - - channel = libssh2_channel_open_session(session); - if (!channel) { - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0); - goto err_exit; - } - if (libssh2_channel_subsystem(channel, "publickey")) { - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request publickey subsystem", 0); - goto err_exit; + if (session->pkeyInit_state == libssh2_NB_state_idle) { + session->pkeyInit_data = NULL; + session->pkeyInit_pkey = NULL; + session->pkeyInit_channel = NULL; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Initializing publickey subsystem"); + + session->pkeyInit_state = libssh2_NB_state_allocated; } - libssh2_channel_set_blocking(channel, 1); - libssh2_channel_handle_extended_data(channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); - - pkey = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PUBLICKEY)); - if (!pkey) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new publickey structure", 0); - goto err_exit; - } - pkey->channel = channel; - pkey->version = 0; - - s = buffer; - libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4); s += 4; - libssh2_htonu32(s, sizeof("version") - 1); s += 4; - memcpy(s, "version", sizeof("version") - 1); s += sizeof("version") - 1; - libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION); s += 4; - - _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey version packet advertising version %d support", (int)LIBSSH2_PUBLICKEY_VERSION); - if ((s - buffer) != libssh2_channel_write(channel, (char*)buffer, (s - buffer))) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey version packet", 0); - goto err_exit; - } - - while (1) { - if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0); - goto err_exit; - } - - s = data; - if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) { - libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0); - goto err_exit; - } - - switch (response) { - case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: - /* Error */ - { - unsigned long status, descr_len, lang_len; - unsigned char *descr, *lang; - - status = libssh2_ntohu32(s); s += 4; - descr_len = libssh2_ntohu32(s); s += 4; - descr = s; s += descr_len; - lang_len = libssh2_ntohu32(s); s += 4; - lang = s; s += lang_len; - - if (s > data + data_len) { - libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0); - goto err_exit; - } - - libssh2_publickey_status_error(NULL, session, status, descr, descr_len); + if (session->pkeyInit_state == libssh2_NB_state_allocated) { + do { + session->pkeyInit_channel = libssh2_channel_open_ex(session, "session", sizeof("session") - 1, + LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, + NULL, 0); + if (!session->pkeyInit_channel && (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN)) { + /* The error state is already set, so leave it */ + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block to startup channel", 0); + return NULL; + } + else if (!session->pkeyInit_channel && (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0); goto err_exit; } - case LIBSSH2_PUBLICKEY_RESPONSE_VERSION: - /* What we want */ - pkey->version = libssh2_ntohu32(s); - if (pkey->version > LIBSSH2_PUBLICKEY_VERSION) { - _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Truncating remote publickey version from %lu", pkey->version); - pkey->version = LIBSSH2_PUBLICKEY_VERSION; - } - _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Enabling publickey subsystem version %lu", pkey->version); - LIBSSH2_FREE(session, data); - return pkey; - default: - /* Unknown/Unexpected */ - libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0); - LIBSSH2_FREE(session, data); - data = NULL; + } while (!session->pkeyInit_channel); + + session->pkeyInit_state = libssh2_NB_state_sent; + } + + if (session->pkeyInit_state == libssh2_NB_state_sent) { + rc = libssh2_channel_process_startup(session->pkeyInit_channel, "subsystem", sizeof("subsystem") - 1, + "publickey", strlen("publickey")); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block starting publickkey subsystem", 0); + return NULL; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request publickey subsystem", 0); + goto err_exit; + } + + session->pkeyInit_state = libssh2_NB_state_sent1; + } + + if (session->pkeyInit_state == libssh2_NB_state_sent1) { + rc = libssh2_channel_handle_extended_data2(session->pkeyInit_channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block starting publickkey subsystem", 0); + return NULL; + } + + session->pkeyInit_pkey = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PUBLICKEY)); + if (!session->pkeyInit_pkey) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new publickey structure", 0); + goto err_exit; + } + memset(session->pkeyInit_pkey, 0, sizeof(LIBSSH2_PUBLICKEY)); + session->pkeyInit_pkey->channel = session->pkeyInit_channel; + session->pkeyInit_pkey->version = 0; + + s = buffer; + libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4); s += 4; + libssh2_htonu32(s, sizeof("version") - 1); s += 4; + memcpy(s, "version", sizeof("version") - 1); s += sizeof("version") - 1; + libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION); s += 4; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey version packet advertising version %d support", + (int)LIBSSH2_PUBLICKEY_VERSION); + + session->pkeyInit_state = libssh2_NB_state_sent2; + } + + if (session->pkeyInit_state == libssh2_NB_state_sent2) { + rc = libssh2_channel_write_ex(session->pkeyInit_channel, 0, (char*)buffer, (s - buffer)); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending publickkey version packet", 0); + return NULL; + } + else if ((s - buffer) != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey version packet", 0); + goto err_exit; + } + + session->pkeyInit_state = libssh2_NB_state_sent3; + } + + if (session->pkeyInit_state == libssh2_NB_state_sent3) { + while (1) { + rc = libssh2_publickey_packet_receive(session->pkeyInit_pkey, &session->pkeyInit_data, &session->pkeyInit_data_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response from publickey subsystem", 0); + return NULL; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0); + goto err_exit; + } + + s = session->pkeyInit_data; + if ((response = libssh2_publickey_response_id(&s, session->pkeyInit_data_len)) < 0) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0); + goto err_exit; + } + + switch (response) { + case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: + /* Error */ + { + unsigned long status, descr_len, lang_len; + unsigned char *descr, *lang; + + status = libssh2_ntohu32(s); s += 4; + descr_len = libssh2_ntohu32(s); s += 4; + descr = s; s += descr_len; + lang_len = libssh2_ntohu32(s); s += 4; + lang = s; s += lang_len; + + if (s > session->pkeyInit_data + session->pkeyInit_data_len) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0); + goto err_exit; + } + + libssh2_publickey_status_error(NULL, session, status, descr, descr_len); + goto err_exit; + } + + case LIBSSH2_PUBLICKEY_RESPONSE_VERSION: + /* What we want */ + session->pkeyInit_pkey->version = libssh2_ntohu32(s); + if (session->pkeyInit_pkey->version > LIBSSH2_PUBLICKEY_VERSION) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Truncating remote publickey version from %lu", + session->pkeyInit_pkey->version); + session->pkeyInit_pkey->version = LIBSSH2_PUBLICKEY_VERSION; + } + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Enabling publickey subsystem version %lu", + session->pkeyInit_pkey->version); + LIBSSH2_FREE(session, session->pkeyInit_data); + session->pkeyInit_data = NULL; + session->pkeyInit_state = libssh2_NB_state_idle; + return session->pkeyInit_pkey; + + default: + /* Unknown/Unexpected */ + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Unexpected publickey subsystem response, ignoring", 0); + LIBSSH2_FREE(session, session->pkeyInit_data); + session->pkeyInit_data = NULL; + } } } /* Never reached except by direct goto */ err_exit: - if (channel) { - libssh2_channel_close(channel); + session->pkeyInit_state = libssh2_NB_state_sent4; + if (session->pkeyInit_channel) { + rc = libssh2_channel_close(session->pkeyInit_channel); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block closing channel", 0); + return NULL; + } } - if (pkey) { - LIBSSH2_FREE(session, pkey); + if (session->pkeyInit_pkey) { + LIBSSH2_FREE(session, session->pkeyInit_pkey); + session->pkeyInit_pkey = NULL; } - if (data) { - LIBSSH2_FREE(session, data); + if (session->pkeyInit_data) { + LIBSSH2_FREE(session, session->pkeyInit_data); + session->pkeyInit_data = NULL; } + session->pkeyInit_state = libssh2_NB_state_idle; return NULL; } /* }}} */ @@ -393,178 +481,233 @@ LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session) /* {{{ libssh2_publickey_add_ex * Add a new public key entry */ -LIBSSH2_API int libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, - const unsigned char *blob, unsigned long blob_len, char overwrite, - unsigned long num_attrs, const libssh2_publickey_attribute attrs[]) +LIBSSH2_API int +libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, + const unsigned char *blob, unsigned long blob_len, char overwrite, unsigned long num_attrs, + const libssh2_publickey_attribute attrs[]) { LIBSSH2_CHANNEL *channel = pkey->channel; LIBSSH2_SESSION *session = channel->session; - unsigned char *packet = NULL, *s; + /* 19 = packet_len(4) + add_len(4) + "add"(3) + name_len(4) + {name} blob_len(4) + {blob} */ unsigned long i, packet_len = 19 + name_len + blob_len; unsigned char *comment = NULL; unsigned long comment_len = 0; - /* packet_len(4) + - add_len(4) + - "add"(3) + - name_len(4) + - {name} - blob_len(4) + - {blob} */ + int rc; - _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Adding %s pubickey", name); + if (pkey->add_state == libssh2_NB_state_idle) { + pkey->add_packet = NULL; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Adding %s pubickey", name); - if (pkey->version == 1) { - for(i = 0; i < num_attrs; i++) { - /* Search for a comment attribute */ - if (attrs[i].name_len == (sizeof("comment") - 1) && - strncmp(attrs[i].name, "comment", sizeof("comment") - 1) == 0) { - comment = (unsigned char *)attrs[i].value; - comment_len = attrs[i].value_len; - break; + if (pkey->version == 1) { + for(i = 0; i < num_attrs; i++) { + /* Search for a comment attribute */ + if (attrs[i].name_len == (sizeof("comment") - 1) && + strncmp(attrs[i].name, "comment", sizeof("comment") - 1) == 0) { + comment = (unsigned char *)attrs[i].value; + comment_len = attrs[i].value_len; + break; + } + } + packet_len += 4 + comment_len; + } else { + packet_len += 5; /* overwrite(1) + attribute_count(4) */ + for(i = 0; i < num_attrs; i++) { + packet_len += 9 + attrs[i].name_len + attrs[i].value_len; + /* name_len(4) + value_len(4) + mandatory(1) */ } } - packet_len += 4 + comment_len; - } else { - packet_len += 5; /* overwrite(1) + attribute_count(4) */ - for(i = 0; i < num_attrs; i++) { - packet_len += 9 + attrs[i].name_len + attrs[i].value_len; - /* name_len(4) + value_len(4) + mandatory(1) */ - } - } - packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey \"add\" packet", 0); - return -1; - } - - s = packet; - libssh2_htonu32(s, packet_len - 4); s += 4; - libssh2_htonu32(s, sizeof("add") - 1); s += 4; - memcpy(s, "add", sizeof("add") - 1); s += sizeof("add") - 1; - if (pkey->version == 1) { - libssh2_htonu32(s, comment_len); s += 4; - if (comment) { - memcpy(s, comment, comment_len); s += comment_len; + pkey->add_packet = LIBSSH2_ALLOC(session, packet_len); + if (!pkey->add_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey \"add\" packet", 0); + return -1; } - libssh2_htonu32(s, name_len); s += 4; - memcpy(s, name, name_len); s += name_len; - libssh2_htonu32(s, blob_len); s += 4; - memcpy(s, blob, blob_len); s += blob_len; - } else { - /* Version == 2 */ + pkey->add_s = pkey->add_packet; + libssh2_htonu32(pkey->add_s, packet_len - 4); pkey->add_s += 4; + libssh2_htonu32(pkey->add_s, sizeof("add") - 1); pkey->add_s += 4; + memcpy(pkey->add_s, "add", sizeof("add") - 1); pkey->add_s += sizeof("add") - 1; + if (pkey->version == 1) { + libssh2_htonu32(pkey->add_s, comment_len); pkey->add_s += 4; + if (comment) { + memcpy(pkey->add_s, comment, comment_len); pkey->add_s += comment_len; + } - libssh2_htonu32(s, name_len); s += 4; - memcpy(s, name, name_len); s += name_len; - libssh2_htonu32(s, blob_len); s += 4; - memcpy(s, blob, blob_len); s += blob_len; - *(s++) = overwrite ? 0xFF : 0; - libssh2_htonu32(s, num_attrs); s += 4; - for(i = 0; i < num_attrs; i++) { - libssh2_htonu32(s, attrs[i].name_len); s += 4; - memcpy(s, attrs[i].name, attrs[i].name_len); s += attrs[i].name_len; - libssh2_htonu32(s, attrs[i].value_len); s += 4; - memcpy(s, attrs[i].value, attrs[i].value_len); s += attrs[i].value_len; - *(s++) = attrs[i].mandatory ? 0xFF : 0; + libssh2_htonu32(pkey->add_s, name_len); pkey->add_s += 4; + memcpy(pkey->add_s, name, name_len); pkey->add_s += name_len; + libssh2_htonu32(pkey->add_s, blob_len); pkey->add_s += 4; + memcpy(pkey->add_s, blob, blob_len); pkey->add_s += blob_len; + } else { + /* Version == 2 */ + + libssh2_htonu32(pkey->add_s, name_len); pkey->add_s += 4; + memcpy(pkey->add_s, name, name_len); pkey->add_s += name_len; + libssh2_htonu32(pkey->add_s, blob_len); pkey->add_s += 4; + memcpy(pkey->add_s, blob, blob_len); pkey->add_s += blob_len; + *(pkey->add_s++) = overwrite ? 0xFF : 0; + libssh2_htonu32(pkey->add_s, num_attrs); pkey->add_s += 4; + for(i = 0; i < num_attrs; i++) { + libssh2_htonu32(pkey->add_s, attrs[i].name_len); pkey->add_s += 4; + memcpy(pkey->add_s, attrs[i].name, attrs[i].name_len); pkey->add_s += attrs[i].name_len; + libssh2_htonu32(pkey->add_s, attrs[i].value_len); pkey->add_s += 4; + memcpy(pkey->add_s, attrs[i].value, attrs[i].value_len); pkey->add_s += attrs[i].value_len; + *(pkey->add_s++) = attrs[i].mandatory ? 0xFF : 0; + } } + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"add\" packet: type=%s blob_len=%ld num_attrs=%ld", + name, blob_len, num_attrs); + + pkey->add_state = libssh2_NB_state_created; + } + + if (pkey->add_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *)pkey->add_packet, (pkey->add_s - pkey->add_packet)); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if ((pkey->add_s - pkey->add_packet) != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey add packet", 0); + LIBSSH2_FREE(session, pkey->add_packet); + pkey->add_packet = NULL; + return -1; + } + LIBSSH2_FREE(session, pkey->add_packet); + pkey->add_packet = NULL; + + pkey->add_state = libssh2_NB_state_sent; } - _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, - "Sending publickey \"add\" packet: type=%s blob_len=%ld num_attrs=%ld", - name, blob_len, num_attrs); - if ((s - packet) != libssh2_channel_write(channel, (char *)packet, (s - packet))) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey add packet", 0); - LIBSSH2_FREE(session, packet); - return -1; + rc = libssh2_publickey_response_success(pkey); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; } - LIBSSH2_FREE(session, packet); - packet = NULL; - - return libssh2_publickey_response_success(pkey); + + pkey->add_state = libssh2_NB_state_idle; + + return rc; } /* }}} */ /* {{{ libssh2_publickey_remove_ex * Remove an existing publickey so that authentication can no longer be performed using it */ -LIBSSH2_API int libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, - const unsigned char *blob, unsigned long blob_len) +LIBSSH2_API int +libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, + const unsigned char *blob, unsigned long blob_len) { LIBSSH2_CHANNEL *channel = pkey->channel; LIBSSH2_SESSION *session = channel->session; - unsigned char *s, *packet = NULL; + /* 22 = packet_len(4) + remove_len(4) + "remove"(6) + name_len(4) + {name} + blob_len(4) + {blob} */ unsigned long packet_len = 22 + name_len + blob_len; - /* packet_len(4) + - remove_len(4) + - "remove"(6) + - name_len(4) + - {name} - blob_len(4) + - {blob} */ + int rc; - packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey \"remove\" packet", 0); - return -1; + if (pkey->remove_state == libssh2_NB_state_idle) { + pkey->remove_packet = NULL; + + pkey->remove_packet = LIBSSH2_ALLOC(session, packet_len); + if (!pkey->remove_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey \"remove\" packet", 0); + return -1; + } + + pkey->remove_s = pkey->remove_packet; + libssh2_htonu32(pkey->remove_s, packet_len - 4); pkey->remove_s += 4; + libssh2_htonu32(pkey->remove_s, sizeof("remove") - 1); pkey->remove_s += 4; + memcpy(pkey->remove_s, "remove", sizeof("remove") - 1); pkey->remove_s += sizeof("remove") - 1; + libssh2_htonu32(pkey->remove_s, name_len); pkey->remove_s += 4; + memcpy(pkey->remove_s, name, name_len); pkey->remove_s += name_len; + libssh2_htonu32(pkey->remove_s, blob_len); pkey->remove_s += 4; + memcpy(pkey->remove_s, blob, blob_len); pkey->remove_s += blob_len; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"remove\" packet: type=%s blob_len=%ld", name, blob_len); + + pkey->remove_state = libssh2_NB_state_created; + } + + if (pkey->remove_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *)pkey->remove_packet, (pkey->remove_s - pkey->remove_packet)); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if ((pkey->remove_s - pkey->remove_packet) != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey remove packet", 0); + LIBSSH2_FREE(session, pkey->remove_packet); + pkey->remove_packet = NULL; + pkey->remove_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, pkey->remove_packet); + pkey->remove_packet = NULL; + + pkey->remove_state = libssh2_NB_state_sent; } - s = packet; - libssh2_htonu32(s, packet_len - 4); s += 4; - libssh2_htonu32(s, sizeof("remove") - 1); s += 4; - memcpy(s, "remove", sizeof("remove") - 1); s += sizeof("remove") - 1; - libssh2_htonu32(s, name_len); s += 4; - memcpy(s, name, name_len); s += name_len; - libssh2_htonu32(s, blob_len); s += 4; - memcpy(s, blob, blob_len); s += blob_len; - - _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"remove\" packet: type=%s blob_len=%ld", name, blob_len); - if ((s - packet) != libssh2_channel_write(channel, (char *)packet, (s - packet))) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey remove packet", 0); - LIBSSH2_FREE(session, packet); - return -1; + rc = libssh2_publickey_response_success(pkey); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; } - LIBSSH2_FREE(session, packet); - packet = NULL; - - return libssh2_publickey_response_success(pkey); + + pkey->remove_state = libssh2_NB_state_idle; + + return rc; } /* }}} */ /* {{{ libssh2_publickey_list_fetch * Fetch a list of supported public key from a server */ -LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned long *num_keys, libssh2_publickey_list **pkey_list) +LIBSSH2_API int +libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned long *num_keys, libssh2_publickey_list **pkey_list) { LIBSSH2_CHANNEL *channel = pkey->channel; LIBSSH2_SESSION *session = channel->session; libssh2_publickey_list *list = NULL; - unsigned char *s, buffer[12], *data = NULL; - unsigned long buffer_len = 12, keys = 0, max_keys = 0, data_len, i; - /* packet_len(4) + - list_len(4) + - "list"(4) */ + unsigned long buffer_len = 12, keys = 0, max_keys = 0, i; + /* 12 = packet_len(4) + list_len(4) + "list"(4) */ int response; + int rc; - s = buffer; - libssh2_htonu32(s, buffer_len - 4); s += 4; - libssh2_htonu32(s, sizeof("list") - 1); s += 4; - memcpy(s, "list", sizeof("list") - 1); s += sizeof("list") - 1; + if (pkey->listFetch_state == libssh2_NB_state_idle) { + pkey->listFetch_data = NULL; + + pkey->listFetch_s = pkey->listFetch_buffer; + libssh2_htonu32(pkey->listFetch_s, buffer_len - 4); pkey->listFetch_s += 4; + libssh2_htonu32(pkey->listFetch_s, sizeof("list") - 1); pkey->listFetch_s += 4; + memcpy(pkey->listFetch_s, "list", sizeof("list") - 1); pkey->listFetch_s += sizeof("list") - 1; - _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"list\" packet"); - if ((s - buffer) != libssh2_channel_write(channel, (char *)buffer, (s - buffer))) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey list packet", 0); - return -1; + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"list\" packet"); + + pkey->listFetch_state = libssh2_NB_state_created; + } + + if (pkey->listFetch_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *)pkey->listFetch_buffer, (pkey->listFetch_s - pkey->listFetch_buffer)); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if ((pkey->listFetch_s - pkey->listFetch_buffer) != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey list packet", 0); + pkey->listFetch_state = libssh2_NB_state_idle; + return -1; + } + + pkey->listFetch_state = libssh2_NB_state_sent; } while (1) { - if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) { + rc = libssh2_publickey_packet_receive(pkey, &pkey->listFetch_data, &pkey->listFetch_data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0); goto err_exit; } - s = data; - if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) { + pkey->listFetch_s = pkey->listFetch_data; + if ((response = libssh2_publickey_response_id(&pkey->listFetch_s, pkey->listFetch_data_len)) < 0) { libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0); goto err_exit; } @@ -576,21 +719,23 @@ LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned l unsigned long status, descr_len, lang_len; unsigned char *descr, *lang; - status = libssh2_ntohu32(s); s += 4; - descr_len = libssh2_ntohu32(s); s += 4; - descr = s; s += descr_len; - lang_len = libssh2_ntohu32(s); s += 4; - lang = s; s += lang_len; + status = libssh2_ntohu32(pkey->listFetch_s); pkey->listFetch_s += 4; + descr_len = libssh2_ntohu32(pkey->listFetch_s); pkey->listFetch_s += 4; + descr = pkey->listFetch_s; pkey->listFetch_s += descr_len; + lang_len = libssh2_ntohu32(pkey->listFetch_s); pkey->listFetch_s += 4; + lang = pkey->listFetch_s; pkey->listFetch_s += lang_len; - if (s > data + data_len) { + if (pkey->listFetch_s > pkey->listFetch_data + pkey->listFetch_data_len) { libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0); goto err_exit; } if (status == LIBSSH2_PUBLICKEY_SUCCESS) { - LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, pkey->listFetch_data); + pkey->listFetch_data = NULL; *pkey_list = list; *num_keys = keys; + pkey->listFetch_state = libssh2_NB_state_idle; return 0; } @@ -613,7 +758,7 @@ LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned l if (pkey->version == 1) { unsigned long comment_len; - comment_len = libssh2_ntohu32(s); s += 4; + comment_len = libssh2_ntohu32(pkey->listFetch_s); pkey->listFetch_s += 4; if (comment_len) { list[keys].num_attrs = 1; list[keys].attrs = LIBSSH2_ALLOC(session, sizeof(libssh2_publickey_attribute)); @@ -623,26 +768,26 @@ LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned l } list[keys].attrs[0].name = "comment"; list[keys].attrs[0].name_len = sizeof("comment") - 1; - list[keys].attrs[0].value = (char *)s; + list[keys].attrs[0].value = (char *)pkey->listFetch_s; list[keys].attrs[0].value_len = comment_len; list[keys].attrs[0].mandatory = 0; - s += comment_len; + pkey->listFetch_s += comment_len; } else { list[keys].num_attrs = 0; list[keys].attrs = NULL; } - list[keys].name_len = libssh2_ntohu32(s); s += 4; - list[keys].name = s; s += list[keys].name_len; - list[keys].blob_len = libssh2_ntohu32(s); s += 4; - list[keys].blob = s; s += list[keys].blob_len; + list[keys].name_len = libssh2_ntohu32(pkey->listFetch_s); pkey->listFetch_s += 4; + list[keys].name = pkey->listFetch_s; pkey->listFetch_s += list[keys].name_len; + list[keys].blob_len = libssh2_ntohu32(pkey->listFetch_s); pkey->listFetch_s += 4; + list[keys].blob = pkey->listFetch_s; pkey->listFetch_s += list[keys].blob_len; } else { /* Version == 2 */ - list[keys].name_len = libssh2_ntohu32(s); s += 4; - list[keys].name = s; s += list[keys].name_len; - list[keys].blob_len = libssh2_ntohu32(s); s += 4; - list[keys].blob = s; s += list[keys].blob_len; - list[keys].num_attrs = libssh2_ntohu32(s); s += 4; + list[keys].name_len = libssh2_ntohu32(pkey->listFetch_s); pkey->listFetch_s += 4; + list[keys].name = pkey->listFetch_s; pkey->listFetch_s += list[keys].name_len; + list[keys].blob_len = libssh2_ntohu32(pkey->listFetch_s); pkey->listFetch_s += 4; + list[keys].blob = pkey->listFetch_s; pkey->listFetch_s += list[keys].blob_len; + list[keys].num_attrs = libssh2_ntohu32(pkey->listFetch_s); pkey->listFetch_s += 4; if (list[keys].num_attrs) { list[keys].attrs = LIBSSH2_ALLOC(session, list[keys].num_attrs * sizeof(libssh2_publickey_attribute)); if (!list[keys].attrs) { @@ -650,37 +795,44 @@ LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned l goto err_exit; } for(i = 0; i < list[keys].num_attrs; i++) { - list[keys].attrs[i].name_len = libssh2_ntohu32(s); s += 4; - list[keys].attrs[i].name = (char *)s; s += list[keys].attrs[i].name_len; - list[keys].attrs[i].value_len = libssh2_ntohu32(s); s += 4; - list[keys].attrs[i].value = (char *)s; s += list[keys].attrs[i].value_len; + list[keys].attrs[i].name_len = libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + list[keys].attrs[i].name = (char *)pkey->listFetch_s; + pkey->listFetch_s += list[keys].attrs[i].name_len; + list[keys].attrs[i].value_len = libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + list[keys].attrs[i].value = (char *)pkey->listFetch_s; + pkey->listFetch_s += list[keys].attrs[i].value_len; list[keys].attrs[i].mandatory = 0; /* actually an ignored value */ } } else { list[keys].attrs = NULL; } } - list[keys].packet = data; /* To be FREEd in libssh2_publickey_list_free() */ + list[keys].packet = pkey->listFetch_data; /* To be FREEd in libssh2_publickey_list_free() */ keys++; list[keys].packet = NULL; /* Terminate the list */ - data = NULL; + pkey->listFetch_data = NULL; break; default: /* Unknown/Unexpected */ libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0); - LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, pkey->listFetch_data); + pkey->listFetch_data = NULL; } } /* Only reached via explicit goto */ err_exit: - if (data) { - LIBSSH2_FREE(session, data); + if (pkey->listFetch_data) { + LIBSSH2_FREE(session, pkey->listFetch_data); + pkey->listFetch_data = NULL; } if (list) { libssh2_publickey_list_free(pkey, list); } + pkey->listFetch_state = libssh2_NB_state_idle; return -1; } /* }}} */ diff --git a/src/scp.c b/src/scp.c index 112bf2d..5dbf7ef 100644 --- a/src/scp.c +++ b/src/scp.c @@ -39,392 +39,631 @@ #include #include -#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]; - unsigned long command_len = path_len + sizeof("scp -f "), response_len; - LIBSSH2_CHANNEL *channel; - long mode = 0, size = 0, mtime = 0, atime = 0; + int rc; - if (sb) { - command_len++; - } - - command = LIBSSH2_ALLOC(session, command_len); - if (!command) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for scp session", 0); - return NULL; - } - if (sb) { - memcpy(command, "scp -pf ", sizeof("scp -pf ") - 1); - memcpy(command + sizeof("scp -pf ") - 1, path, path_len); - } else { - memcpy(command, "scp -f ", sizeof("scp -f ") - 1); - memcpy(command + sizeof("scp -f ") - 1, path, path_len); - } - command[command_len - 1] = '\0'; - - _libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP receive"); - /* Allocate a channel */ - if ((channel = libssh2_channel_open_session(session)) == NULL) { - LIBSSH2_FREE(session, command); - return NULL; - } - /* Use blocking I/O for negotiation phase */ - libssh2_channel_set_blocking(channel, 1); - - /* Request SCP for the desired file */ - if (libssh2_channel_process_startup(channel, "exec", sizeof("exec") - 1, command, command_len)) { - LIBSSH2_FREE(session, command); - libssh2_channel_free(channel); - return NULL; - } - LIBSSH2_FREE(session, command); - - _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sending initial wakeup"); - /* SCP ACK */ - response[0] = '\0'; - if (libssh2_channel_write(channel, response, 1) != 1) { - libssh2_channel_free(channel); - return NULL; - } - - /* Parse SCP response */ - response_len = 0; - while (sb && (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN)) { - unsigned char *s, *p; - int rc; - - 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); - return NULL; - } - response_len++; - - if (response[0] != 'T') { - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response, missing Time data", 0); - libssh2_channel_free(channel); - return NULL; + if (session->scpRecv_state == libssh2_NB_state_idle) { + session->scpRecv_mode = 0; + session->scpRecv_size = 0; + session->scpRecv_mtime = 0; + session->scpRecv_atime = 0; + + session->scpRecv_command_len = path_len + sizeof("scp -f "); + + if (sb) { + session->scpRecv_command_len++; } - 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); - libssh2_channel_free(channel); + session->scpRecv_command = LIBSSH2_ALLOC(session, session->scpRecv_command_len); + if (!session->scpRecv_command) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for SCP session", 0); return NULL; } + if (sb) { + memcpy(session->scpRecv_command, "scp -pf ", sizeof("scp -pf ") - 1); + memcpy(session->scpRecv_command + sizeof("scp -pf ") - 1, path, path_len); + } else { + memcpy(session->scpRecv_command, "scp -f ", sizeof("scp -f ") - 1); + memcpy(session->scpRecv_command + sizeof("scp -f ") - 1, path, path_len); + } + session->scpRecv_command[session->scpRecv_command_len - 1] = '\0'; - if ((response_len < 9) || (response[response_len-1] != '\n')) { - if (response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) { - /* You had your chance */ - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0); - libssh2_channel_free(channel); - return NULL; + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP receive"); + + session->scpRecv_state = libssh2_NB_state_created; + } + + if (session->scpRecv_state == libssh2_NB_state_created) { + /* Allocate a channel */ + do { + session->scpRecv_channel = libssh2_channel_open_ex(session, "session", sizeof("session") - 1, + LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, + NULL, 0); + if (!session->scpRecv_channel) { + if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { + LIBSSH2_FREE(session, session->scpRecv_command); + session->scpRecv_command = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + else if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block starting up channel", 0); + return NULL; + } } - /* Way too short to be an SCP response, or not done yet, short circuit */ - continue; - } - - /* We're guaranteed not to go under response_len == 0 by the logic above */ - while ((response[response_len-1] == '\r') || (response[response_len-1] == '\n')) response_len--; - response[response_len] = '\0'; - - if (response_len < 8) { - /* EOL came too soon */ - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0); - libssh2_channel_free(channel); - return NULL; - } - - s = response + 1; - - p = strchr(s, ' '); - if (!p || ((p - s) <= 0)) { - /* No spaces or space in the wrong spot */ - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime", 0); - libssh2_channel_free(channel); - return NULL; - } - - *(p++) = '\0'; - /* Make sure we don't get fooled by leftover values */ - errno = 0; - mtime = strtol(s, NULL, 10); - if (errno) { - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mtime", 0); - libssh2_channel_free(channel); - return NULL; - } - s = strchr(p, ' '); - if (!s || ((s - p) <= 0)) { - /* No spaces or space in the wrong spot */ - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime.usec", 0); - libssh2_channel_free(channel); - return NULL; - } - - /* Ignore mtime.usec */ - s++; - p = strchr(s, ' '); - if (!p || ((p - s) <= 0)) { - /* No spaces or space in the wrong spot */ - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed", 0); - libssh2_channel_free(channel); - return NULL; - } - - *(p++) = '\0'; - /* Make sure we don't get fooled by leftover values */ - errno = 0; - atime = strtol(s, NULL, 10); - if (errno) { - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid atime", 0); - libssh2_channel_free(channel); - return NULL; - } - - /* SCP ACK */ - response[0] = '\0'; - if (libssh2_channel_write(channel, response, 1) != 1) { - libssh2_channel_free(channel); - return NULL; - } - - _libssh2_debug(session, LIBSSH2_DBG_SCP, "mtime = %ld, atime = %ld", mtime, atime); - - /* We *should* check that atime.usec is valid, but why let that stop use? */ - break; + } while (!session->scpRecv_channel); + + session->scpRecv_state = libssh2_NB_state_sent; } - response_len = 0; - while (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) { - char *s, *p, *e = NULL; - - 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); + if (session->scpRecv_state == libssh2_NB_state_sent) { + /* Request SCP for the desired file */ + rc = libssh2_channel_process_startup(session->scpRecv_channel, "exec", sizeof("exec") - 1, (char *)session->scpRecv_command, session->scpRecv_command_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block requesting SCP startup", 0); return NULL; } - response_len++; - - if (response[0] != 'C') { - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server", 0); - libssh2_channel_free(channel); - return NULL; - } - - if ((response_len > 1) && - (response[response_len-1] != '\r') && - (response[response_len-1] != '\n') && - ((response[response_len-1] < 32) || (response[response_len-1] > 126))) { - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0); - libssh2_channel_free(channel); - return NULL; - } - - if ((response_len < 7) || (response[response_len-1] != '\n')) { - if (response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) { - /* You had your chance */ - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0); - libssh2_channel_free(channel); - return NULL; - } - /* Way too short to be an SCP response, or not done yet, short circuit */ - continue; - } - - /* We're guaranteed not to go under response_len == 0 by the logic above */ - while ((response[response_len-1] == '\r') || (response[response_len-1] == '\n')) response_len--; - response[response_len] = '\0'; - - if (response_len < 6) { - /* EOL came too soon */ - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0); - libssh2_channel_free(channel); - return NULL; - } - - s = response + 1; - - p = strchr(s, ' '); - if (!p || ((p - s) <= 0)) { - /* No spaces or space in the wrong spot */ - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mode", 0); - libssh2_channel_free(channel); - return NULL; - } - - *(p++) = '\0'; - /* Make sure we don't get fooled by leftover values */ - errno = 0; - mode = strtol(s, &e, 8); - if ((e && *e) || errno) { - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mode", 0); - libssh2_channel_free(channel); - return NULL; - } - - s = strchr(p, ' '); - if (!s || ((s - p) <= 0)) { - /* No spaces or space in the wrong spot */ - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed", 0); - libssh2_channel_free(channel); - return NULL; - } - - *(s++) = '\0'; - /* Make sure we don't get fooled by leftover values */ - errno = 0; - size = strtol(p, &e, 10); - if ((e && *e) || errno) { - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid size", 0); - libssh2_channel_free(channel); + else if (rc) { + LIBSSH2_FREE(session, session->scpRecv_command); + session->scpRecv_command = NULL; + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; return NULL; } + LIBSSH2_FREE(session, session->scpRecv_command); + session->scpRecv_command = NULL; + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sending initial wakeup"); /* SCP ACK */ - response[0] = '\0'; - if (libssh2_channel_write(channel, response, 1) != 1) { - libssh2_channel_free(channel); + session->scpRecv_response[0] = '\0'; + + session->scpRecv_state = libssh2_NB_state_sent1; + } + + if (session->scpRecv_state == libssh2_NB_state_sent1) { + rc = libssh2_channel_write_ex(session->scpRecv_channel, 0, (char *)session->scpRecv_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending initial wakeup", 0); return NULL; } - _libssh2_debug(session, LIBSSH2_DBG_SCP, "mod = 0%lo size = %ld", mode, size); + else if (rc != 1) { + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + /* Parse SCP response */ + session->scpRecv_response_len = 0; + + session->scpRecv_state = libssh2_NB_state_sent2; + } + + if ((session->scpRecv_state == libssh2_NB_state_sent2) || (session->scpRecv_state == libssh2_NB_state_sent3)) { + while (sb && (session->scpRecv_response_len < LIBSSH2_SCP_RESPONSE_BUFLEN)) { + unsigned char *s, *p; - /* We *should* check that basename is valid, but why let that stop us? */ - break; + if (session->scpRecv_state == libssh2_NB_state_sent2) { + rc = libssh2_channel_read_ex(session->scpRecv_channel, 0, + (char *)session->scpRecv_response + session->scpRecv_response_len, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for SCP response", 0); + return NULL; + } + else if (rc <= 0) { + /* Timeout, give up */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + session->scpRecv_response_len++; + + if (session->scpRecv_response[0] != 'T') { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response, missing Time data", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + if ((session->scpRecv_response_len > 1) && + ((session->scpRecv_response[session->scpRecv_response_len-1] < '0') || + (session->scpRecv_response[session->scpRecv_response_len-1] > '9')) && + (session->scpRecv_response[session->scpRecv_response_len-1] != ' ') && + (session->scpRecv_response[session->scpRecv_response_len-1] != '\r') && + (session->scpRecv_response[session->scpRecv_response_len-1] != '\n')) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + if ((session->scpRecv_response_len < 9) || (session->scpRecv_response[session->scpRecv_response_len-1] != '\n')) { + if (session->scpRecv_response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) { + /* You had your chance */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + /* Way too short to be an SCP response, or not done yet, short circuit */ + continue; + } + + /* We're guaranteed not to go under response_len == 0 by the logic above */ + while ((session->scpRecv_response[session->scpRecv_response_len-1] == '\r') || (session->scpRecv_response[session->scpRecv_response_len-1] == '\n')) session->scpRecv_response_len--; + session->scpRecv_response[session->scpRecv_response_len] = '\0'; + + if (session->scpRecv_response_len < 8) { + /* EOL came too soon */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + s = session->scpRecv_response + 1; + + p = (unsigned char *)strchr((char *)s, ' '); + if (!p || ((p - s) <= 0)) { + /* No spaces or space in the wrong spot */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + *(p++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + session->scpRecv_mtime = strtol((char *)s, NULL, 10); + if (errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mtime", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + s = (unsigned char *)strchr((char *)p, ' '); + if (!s || ((s - p) <= 0)) { + /* No spaces or space in the wrong spot */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime.usec", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + /* Ignore mtime.usec */ + s++; + p = (unsigned char *)strchr((char *)s, ' '); + if (!p || ((p - s) <= 0)) { + /* No spaces or space in the wrong spot */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + *(p++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + session->scpRecv_atime = strtol((char *)s, NULL, 10); + if (errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid atime", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + /* SCP ACK */ + session->scpRecv_response[0] = '\0'; + + session->scpRecv_state = libssh2_NB_state_sent3; + } + + if (session->scpRecv_state == libssh2_NB_state_sent3) { + rc = libssh2_channel_write_ex(session->scpRecv_channel, 0, (char *)session->scpRecv_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting to send SCP ACK", 0); + return NULL; + } + else if (rc != 1) { + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + _libssh2_debug(session, LIBSSH2_DBG_SCP, "mtime = %ld, atime = %ld", session->scpRecv_mtime, session->scpRecv_atime); + + /* We *should* check that atime.usec is valid, but why let that stop use? */ + break; + } + } + + session->scpRecv_state = libssh2_NB_state_sent4; + } + + if (session->scpRecv_state == libssh2_NB_state_sent4) { + session->scpRecv_response_len = 0; + + session->scpRecv_state = libssh2_NB_state_sent5; + } + + if ((session->scpRecv_state == libssh2_NB_state_sent5) || (session->scpRecv_state == libssh2_NB_state_sent6)) { + while (session->scpRecv_response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) { + char *s, *p, *e = NULL; + + if (session->scpRecv_state == libssh2_NB_state_sent5) { + rc = libssh2_channel_read_ex(session->scpRecv_channel, 0, + (char *)session->scpRecv_response + session->scpRecv_response_len, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for SCP response", 0); + return NULL; + } + else if (rc <= 0) { + /* Timeout, give up */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + session->scpRecv_response_len++; + + if (session->scpRecv_response[0] != 'C') { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + if ((session->scpRecv_response_len > 1) && + (session->scpRecv_response[session->scpRecv_response_len-1] != '\r') && + (session->scpRecv_response[session->scpRecv_response_len-1] != '\n') && + ((session->scpRecv_response[session->scpRecv_response_len-1] < 32) || + (session->scpRecv_response[session->scpRecv_response_len-1] > 126))) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + if ((session->scpRecv_response_len < 7) || (session->scpRecv_response[session->scpRecv_response_len-1] != '\n')) { + if (session->scpRecv_response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) { + /* You had your chance */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + /* Way too short to be an SCP response, or not done yet, short circuit */ + continue; + } + + /* We're guaranteed not to go under response_len == 0 by the logic above */ + while ((session->scpRecv_response[session->scpRecv_response_len-1] == '\r') || + (session->scpRecv_response[session->scpRecv_response_len-1] == '\n')) { + session->scpRecv_response_len--; + } + session->scpRecv_response[session->scpRecv_response_len] = '\0'; + + if (session->scpRecv_response_len < 6) { + /* EOL came too soon */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + s = (char *)session->scpRecv_response + 1; + + p = strchr(s, ' '); + if (!p || ((p - s) <= 0)) { + /* No spaces or space in the wrong spot */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mode", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + *(p++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + session->scpRecv_mode = strtol(s, &e, 8); + if ((e && *e) || errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mode", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + s = strchr(p, ' '); + if (!s || ((s - p) <= 0)) { + /* No spaces or space in the wrong spot */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed", + 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + *(s++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + session->scpRecv_size = strtol(p, &e, 10); + if ((e && *e) || errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid size", 0); + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + + /* SCP ACK */ + session->scpRecv_response[0] = '\0'; + + session->scpRecv_state = libssh2_NB_state_sent6; + } + + if (session->scpRecv_state == libssh2_NB_state_sent6) { + rc = libssh2_channel_write_ex(session->scpRecv_channel, 0, (char *)session->scpRecv_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending SCP ACK", 0); + return NULL; + } + else if (rc != 1) { + libssh2_channel_free(session->scpRecv_channel); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } + _libssh2_debug(session, LIBSSH2_DBG_SCP, "mode = 0%lo size = %ld", session->scpRecv_mode, session->scpRecv_size); + + /* We *should* check that basename is valid, but why let that stop us? */ + break; + } + } + + session->scpRecv_state = libssh2_NB_state_sent7; } if (sb) { memset(sb, 0, sizeof(struct stat)); - sb->st_mtime = mtime; - sb->st_atime = atime; - sb->st_size = size; - sb->st_mode = mode; + sb->st_mtime = session->scpRecv_mtime; + sb->st_atime = session->scpRecv_atime; + sb->st_size = session->scpRecv_size; + sb->st_mode = session->scpRecv_mode; } - /* Revert to non-blocking and let the data BEGIN! */ - libssh2_channel_set_blocking(channel, 0); - return channel; + session->scpRecv_state = libssh2_NB_state_idle; + return session->scpRecv_channel; } /* }}} */ /* {{{ libssh2_scp_send_ex * Send a file using SCP */ -LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t size, long mtime, long atime) +LIBSSH2_API LIBSSH2_CHANNEL * +libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t size, long mtime, long atime) { int path_len = strlen(path); - unsigned char *command, response[LIBSSH2_SCP_RESPONSE_BUFLEN]; - unsigned long response_len, command_len = path_len + sizeof("scp -t "); unsigned const char *base; - LIBSSH2_CHANNEL *channel; + int rc; - if (mtime || atime) { - command_len++; + if (session->scpSend_state == libssh2_NB_state_idle) { + session->scpSend_command_len = path_len + sizeof("scp -t "); + + if (mtime || atime) { + session->scpSend_command_len++; + } + + session->scpSend_command = LIBSSH2_ALLOC(session, session->scpSend_command_len); + if (!session->scpSend_command) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for scp session", 0); + return NULL; + } + + if (mtime || atime) { + memcpy(session->scpSend_command, "scp -pt ", sizeof("scp -pt ") - 1); + memcpy(session->scpSend_command + sizeof("scp -pt ") - 1, path, path_len); + } else { + memcpy(session->scpSend_command, "scp -t ", sizeof("scp -t ") - 1); + memcpy(session->scpSend_command + sizeof("scp -t ") - 1, path, path_len); + } + session->scpSend_command[session->scpSend_command_len - 1] = '\0'; + + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP send"); + /* Allocate a channel */ + + session->scpSend_state = libssh2_NB_state_created; + } + + if (session->scpSend_state == libssh2_NB_state_created) { + session->scpSend_channel = libssh2_channel_open_ex(session, "session", sizeof("session") - 1, + LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0); + if (!session->scpSend_channel) { + if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { + /* previous call set libssh2_session_last_error(), pass it through */ + LIBSSH2_FREE(session, session->scpSend_command); + session->scpSend_command = NULL; + session->scpSend_state = libssh2_NB_state_idle; + return NULL; + } + else if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block starting up channel", 0); + return NULL; + } + } + + session->scpSend_state = libssh2_NB_state_sent; } - command = LIBSSH2_ALLOC(session, command_len); - if (!command) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for scp session", 0); - return NULL; + if (session->scpSend_state == libssh2_NB_state_sent) { + /* Request SCP for the desired file */ + rc = libssh2_channel_process_startup(session->scpSend_channel, "exec", sizeof("exec") - 1, + (char *)session->scpSend_command, session->scpSend_command_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block requesting SCP startup", 0); + return NULL; + } + else if (rc) { + /* previous call set libssh2_session_last_error(), pass it through */ + LIBSSH2_FREE(session, session->scpSend_command); + session->scpSend_command = NULL; + libssh2_channel_free(session->scpSend_channel); + session->scpSend_channel = NULL; + session->scpSend_state = libssh2_NB_state_idle; + return NULL; + } + LIBSSH2_FREE(session, session->scpSend_command); + session->scpSend_command = NULL; + + session->scpSend_state = libssh2_NB_state_sent1; } - if (mtime || atime) { - memcpy(command, "scp -pt ", sizeof("scp -pt ") - 1); - memcpy(command + sizeof("scp -pt ") - 1, path, path_len); - } else { - memcpy(command, "scp -t ", sizeof("scp -t ") - 1); - memcpy(command + sizeof("scp -t ") - 1, path, path_len); - } - command[command_len - 1] = '\0'; - - _libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP send"); - /* Allocate a channel */ - if ((channel = libssh2_channel_open_session(session)) == NULL) { - /* previous call set libssh2_session_last_error(), pass it through */ - LIBSSH2_FREE(session, command); - return NULL; - } - /* Use blocking I/O for negotiation phase */ - libssh2_channel_set_blocking(channel, 1); - - /* 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 */ - LIBSSH2_FREE(session, command); - libssh2_channel_free(channel); - return NULL; - } - LIBSSH2_FREE(session, command); - - /* Wait for ACK */ - 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; + if (session->scpSend_state == libssh2_NB_state_sent1) { + /* Wait for ACK */ + rc = libssh2_channel_read_ex(session->scpSend_channel, 0, (char *)session->scpSend_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response from remote", 0); + return NULL; + } + else if ((rc <= 0) || (session->scpSend_response[0] != 0)) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); + libssh2_channel_free(session->scpSend_channel); + session->scpSend_channel = NULL; + session->scpSend_state = libssh2_NB_state_idle; + return NULL; + } + + if (mtime || atime) { + /* Send mtime and atime to be used for file */ + session->scpSend_response_len = snprintf((char *)session->scpSend_response, LIBSSH2_SCP_RESPONSE_BUFLEN, + "T%ld 0 %ld 0\n", mtime, atime); + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", session->scpSend_response); + } + + session->scpSend_state = libssh2_NB_state_sent2; } /* Send mtime and atime to be used for file */ if (mtime || atime) { - response_len = snprintf(response, LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n", mtime, atime); - _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", response); - - if (libssh2_channel_write(channel, response, response_len) != response_len) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send time data for SCP file", 0); - libssh2_channel_free(channel); - return NULL; + if (session->scpSend_state == libssh2_NB_state_sent2) { + rc = libssh2_channel_write_ex(session->scpSend_channel, 0, (char *)session->scpSend_response, + session->scpSend_response_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending time data for SCP file", 0); + return NULL; + } + else if (rc != session->scpSend_response_len) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send time data for SCP file", 0); + libssh2_channel_free(session->scpSend_channel); + session->scpSend_channel = NULL; + session->scpSend_state = libssh2_NB_state_idle; + return NULL; + } + + session->scpSend_state = libssh2_NB_state_sent3; } - /* Wait for ACK */ - 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; + + if (session->scpSend_state == libssh2_NB_state_sent3) { + /* Wait for ACK */ + rc = libssh2_channel_read_ex(session->scpSend_channel, 0, (char *)session->scpSend_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response", 0); + return NULL; + } + else if ((rc <= 0) || (session->scpSend_response[0] != 0)) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); + libssh2_channel_free(session->scpSend_channel); + session->scpSend_channel = NULL; + session->scpSend_state = libssh2_NB_state_idle; + return NULL; + } + + session->scpSend_state = libssh2_NB_state_sent4; } - } - - /* Send mode, size, and basename */ - base = strrchr(path, '/'); - if (base) { - base++; } else { - base = path; + if (session->scpSend_state == libssh2_NB_state_sent2) { + session->scpSend_state = libssh2_NB_state_sent4; + } } - response_len = snprintf(response, LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %lu %s\n", mode, (unsigned long)size, base); - _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", response); - if (libssh2_channel_write(channel, response, response_len) != response_len) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send core file data for SCP file", 0); - libssh2_channel_free(channel); - return NULL; + if (session->scpSend_state == libssh2_NB_state_sent4) { + /* Send mode, size, and basename */ + base = (unsigned char *)strrchr(path, '/'); + if (base) { + base++; + } else { + base = (unsigned char *)path; + } + + session->scpSend_response_len = snprintf((char *)session->scpSend_response, LIBSSH2_SCP_RESPONSE_BUFLEN, + "C0%o %lu %s\n", mode, (unsigned long)size, base); + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", session->scpSend_response); + + session->scpSend_state = libssh2_NB_state_sent5; } + + if (session->scpSend_state == libssh2_NB_state_sent5) { + rc = libssh2_channel_write_ex(session->scpSend_channel, 0, (char *)session->scpSend_response, + session->scpSend_response_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block send core file data for SCP file", 0); + return NULL; + } + else if (rc != session->scpSend_response_len) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send core file data for SCP file", 0); + libssh2_channel_free(session->scpSend_channel); + session->scpSend_channel = NULL; + session->scpSend_state = libssh2_NB_state_idle; + return NULL; + } + + session->scpSend_state = libssh2_NB_state_sent6; + } + /* Wait for ACK */ - 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); + rc = libssh2_channel_read_ex(session->scpSend_channel, 0, (char *)session->scpSend_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response", 0); return NULL; } + else if ((rc <= 0) || (session->scpSend_response[0] != 0)) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); + libssh2_channel_free(session->scpSend_channel); + session->scpSend_channel = NULL; + session->scpSend_state = libssh2_NB_state_idle; + return NULL; + } + + session->scpSend_state = libssh2_NB_state_idle; - /* Ready to start, switch to non-blocking and let calling app send file */ - libssh2_channel_set_blocking(channel, 0); - - return channel; + return session->scpSend_channel; } /* }}} */ diff --git a/src/session.c b/src/session.c index 3ddacb9..d76e055 100644 --- a/src/session.c +++ b/src/session.c @@ -41,6 +41,7 @@ #include #endif #include +#include #ifdef HAVE_GETTIMEOFDAY #include @@ -76,17 +77,24 @@ static LIBSSH2_REALLOC_FUNC(libssh2_default_realloc) /* {{{ libssh2_banner_receive * Wait for a hello from the remote host * Allocate a buffer and store the banner in session->remote.banner - * Returns: 0 on success, 1 on failure + * Returns: 0 on success, PACKET_EAGAIN if read would block, 1 on failure */ static int libssh2_banner_receive(LIBSSH2_SESSION *session) { - char banner[256]; - int banner_len = 0; + int ret; + int banner_len; - while ((banner_len < (int)sizeof(banner)) && - ((banner_len == 0) || (banner[banner_len-1] != '\n'))) { + if (session->banner_TxRx_state == libssh2_NB_state_idle) { + banner_len = 0; + + session->banner_TxRx_state = libssh2_NB_state_created; + } else { + banner_len = session->banner_TxRx_total_send; + } + + while ((banner_len < (int)sizeof(session->banner_TxRx_banner)) && + ((banner_len == 0) || (session->banner_TxRx_banner[banner_len-1] != '\n'))) { char c = '\0'; - int ret; ret = recv(session->socket_fd, &c, 1, LIBSSH2_SOCKET_RECV_FLAGS(session)); @@ -96,20 +104,28 @@ static int libssh2_banner_receive(LIBSSH2_SESSION *session) case WSAEWOULDBLOCK: errno = EAGAIN; break; + case WSAENOTSOCK: errno = EBADF; break; + case WSAENOTCONN: case WSAECONNABORTED: errno = WSAENOTCONN; break; + case WSAEINTR: errno = EINTR; break; } #endif /* WIN32 */ - if (errno != EAGAIN) { - /* Some kinda error, but don't break for non-blocking issues */ + if (errno == EAGAIN) { + session->banner_TxRx_total_send = banner_len; + return PACKET_EAGAIN; + } else { + /* Some kinda error */ + session->banner_TxRx_state = libssh2_NB_state_idle; + session->banner_TxRx_total_send = 0; return 1; } } @@ -118,17 +134,24 @@ static int libssh2_banner_receive(LIBSSH2_SESSION *session) if (c == '\0') { /* NULLs are not allowed in SSH banners */ + session->banner_TxRx_state = libssh2_NB_state_idle; + session->banner_TxRx_total_send = 0; return 1; } - banner[banner_len++] = c; + session->banner_TxRx_banner[banner_len++] = c; } while (banner_len && - ((banner[banner_len-1] == '\n') || (banner[banner_len-1] == '\r'))) { + ((session->banner_TxRx_banner[banner_len-1] == '\n') || + (session->banner_TxRx_banner[banner_len-1] == '\r'))) { banner_len--; } + /* From this point on, we are done here */ + session->banner_TxRx_state = libssh2_NB_state_idle; + session->banner_TxRx_total_send = 0; + if (!banner_len) return 1; session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1); @@ -136,7 +159,7 @@ static int libssh2_banner_receive(LIBSSH2_SESSION *session) libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Error allocating space for remote banner", 0); return 1; } - memcpy(session->remote.banner, banner, banner_len); + memcpy(session->remote.banner, session->banner_TxRx_banner, banner_len); session->remote.banner[banner_len] = '\0'; _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Received Banner: %s", session->remote.banner); return 0; @@ -145,38 +168,179 @@ static int libssh2_banner_receive(LIBSSH2_SESSION *session) /* {{{ libssh2_banner_send * Send the default banner, or the one set via libssh2_setopt_string + * + * 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. */ static int libssh2_banner_send(LIBSSH2_SESSION *session) { 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((char *)session->local.banner); - banner = (char *)session->local.banner; - } + ssize_t ret; #ifdef LIBSSH2DEBUG -{ - /* Hack and slash to avoid sending CRLF in debug output */ char banner_dup[256]; - - if (banner_len < 256) { - memcpy(banner_dup, banner, banner_len - 2); - banner_dup[banner_len - 2] = '\0'; - } else { - memcpy(banner_dup, banner, 255); - banner[255] = '\0'; - } - - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending Banner: %s", banner_dup); -} #endif - return (send(session->socket_fd, banner, banner_len, LIBSSH2_SOCKET_SEND_FLAGS(session)) == banner_len) ? 0 : 1; + if (session->banner_TxRx_state == libssh2_NB_state_idle) { + if (session->local.banner) { + /* setopt_string will have given us our \r\n characters */ + banner_len = strlen((char *)session->local.banner); + banner = (char *)session->local.banner; + } + +#ifdef LIBSSH2DEBUG + /* Hack and slash to avoid sending CRLF in debug output */ + if (banner_len < 256) { + memcpy(banner_dup, banner, banner_len - 2); + banner_dup[banner_len - 2] = '\0'; + } else { + memcpy(banner_dup, banner, 255); + banner[255] = '\0'; + } + + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending Banner: %s", banner_dup); +#endif + + session->banner_TxRx_state = libssh2_NB_state_created; + } + + ret = send(session->socket_fd, banner+session->banner_TxRx_total_send, banner_len-session->banner_TxRx_total_send, + LIBSSH2_SOCKET_SEND_FLAGS(session)); + + if (ret != (banner_len-session->banner_TxRx_total_send)) { + if ((ret > 0 ) || ((ret == -1) && (errno == EAGAIN))) { + /* the whole packet could not be sent, save the what was */ + session->banner_TxRx_total_send += ret; + return PACKET_EAGAIN; + } + session->banner_TxRx_state = libssh2_NB_state_idle; + session->banner_TxRx_total_send = 0; + return PACKET_FAIL; + } + + /* Set the state back to idle */ + session->banner_TxRx_state = libssh2_NB_state_idle; + session->banner_TxRx_total_send = 0; + + return 0; } /* }}} */ +/* + * _libssh2_nonblock() sets the given socket to either blocking or + * non-blocking mode based on the 'nonblock' boolean argument. This function + * is copied from the libcurl sources with permission. + */ +static int _libssh2_nonblock(int sockfd, /* operate on this */ + int nonblock /* TRUE or FALSE */) +{ +#undef SETBLOCK +#define SETBLOCK 0 +#ifdef HAVE_O_NONBLOCK + /* most recent unix versions */ + int flags; + + flags = fcntl(sockfd, F_GETFL, 0); + if (nonblock) + return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + else + return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); +#undef SETBLOCK +#define SETBLOCK 1 +#endif + +#if defined(HAVE_FIONBIO) && (SETBLOCK == 0) + /* older unix versions */ + int flags; + + flags = nonblock; + return ioctl(sockfd, FIONBIO, &flags); +#undef SETBLOCK +#define SETBLOCK 2 +#endif + +#if defined(HAVE_IOCTLSOCKET) && (SETBLOCK == 0) + /* Windows? */ + unsigned long flags; + flags = nonblock; + + return ioctlsocket(sockfd, FIONBIO, &flags); +#undef SETBLOCK +#define SETBLOCK 3 +#endif + +#if defined(HAVE_IOCTLSOCKET_CASE) && (SETBLOCK == 0) + /* presumably for Amiga */ + return IoctlSocket(sockfd, FIONBIO, (long)nonblock); +#undef SETBLOCK +#define SETBLOCK 4 +#endif + +#if defined(HAVE_SO_NONBLOCK) && (SETBLOCK == 0) + /* BeOS */ + long b = nonblock ? 1 : 0; + return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); +#undef SETBLOCK +#define SETBLOCK 5 +#endif + +#ifdef HAVE_DISABLED_NONBLOCKING + return 0; /* returns success */ +#undef SETBLOCK +#define SETBLOCK 6 +#endif + +#if (SETBLOCK == 0) +#error "no non-blocking method was found/used/set" +#endif +} + +/* + * _libssh2_get_socket_nonblocking() gets the given blocking or non-blocking + * state of the socket. + */ +static int _libssh2_get_socket_nonblocking(int sockfd) /* operate on this */ +{ +#undef GETBLOCK +#define GETBLOCK 0 +#ifdef HAVE_O_NONBLOCK + /* most recent unix versions */ + int flags; + + if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) { + /* Assume blocking on error */ + return 1; + } + return (flags & O_NONBLOCK); +#undef GETBLOCK +#define GETBLOCK 1 +#endif + +#if defined(HAVE_SO_NONBLOCK) && (GETBLOCK == 0) + /* BeOS */ + long b; + if (getsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b))) { + /* Assume blocking on error */ + return 1; + } + return (int)b; +#undef GETBLOCK +#define GETBLOCK 5 +#endif + +#ifdef HAVE_DISABLED_NONBLOCKING + return 1; /* returns blocking */ +#undef GETBLOCK +#define GETBLOCK 6 +#endif + +#if (GETBLOCK == 0) +#error "no non-blocking method was found/used/get" +#endif +} + /* {{{ libssh2_banner_set * Set the local banner */ @@ -195,15 +359,13 @@ 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); 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); session->local.banner[banner_len++] = '\r'; session->local.banner[banner_len++] = '\n'; session->local.banner[banner_len++] = '\0'; @@ -229,12 +391,15 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex( LIBSSH2_REALLOC_FUNC((*local_realloc)) = libssh2_default_realloc; LIBSSH2_SESSION *session; - if (my_alloc) - local_alloc = my_alloc; - if (my_free) - local_free = my_free; - if (my_realloc) + 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); if (session) { @@ -243,8 +408,7 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex( session->free = local_free; session->realloc = local_realloc; session->abstract = abstract; - _libssh2_debug(session, LIBSSH2_DBG_TRANS, - "New session resource allocated"); + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "New session resource allocated"); libssh2_crypto_init (); } return session; @@ -255,9 +419,7 @@ 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; @@ -303,75 +465,129 @@ LIBSSH2_API void* libssh2_session_callback_set(LIBSSH2_SESSION *session, */ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket) { - unsigned char *data; - unsigned long data_len; - unsigned char service[sizeof("ssh-userauth") + 5 - 1]; - unsigned long service_length; int rc; - - _libssh2_debug(session, LIBSSH2_DBG_TRANS, - "session_startup for socket %d", socket); - /* 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); - return LIBSSH2_ERROR_SOCKET_NONE; + + if (session->startup_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "session_startup for socket %d", socket); + /* 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); + return LIBSSH2_ERROR_SOCKET_NONE; + } + session->socket_fd = socket; + + session->socket_block = !_libssh2_get_socket_nonblocking(session->socket_fd); + if (session->socket_block) { + /* + * Since we can't be sure that we are in blocking or there + * was an error detecting the state, so set to blocking to + * be sure + */ + _libssh2_nonblock(session->socket_fd, 0); + } + + session->startup_state = libssh2_NB_state_created; } - session->socket_fd = 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); - return LIBSSH2_ERROR_BANNER_SEND; + + if (session->startup_state == libssh2_NB_state_created) { + rc = libssh2_banner_send(session); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending banner to remote host", 0); + return LIBSSH2_ERROR_EAGAIN; + } + else if (rc) { + /* Unable to send banner? */ + libssh2_error(session, LIBSSH2_ERROR_BANNER_SEND, "Error sending banner to remote host", 0); + return LIBSSH2_ERROR_BANNER_SEND; + } + + session->startup_state = libssh2_NB_state_sent; } - - if (libssh2_banner_receive(session)) { - /* Unable to receive banner from remote */ - libssh2_error(session, LIBSSH2_ERROR_BANNER_NONE, - "Timeout waiting for banner", 0); - return LIBSSH2_ERROR_BANNER_NONE; + + if (session->startup_state == libssh2_NB_state_sent) { + rc = libssh2_banner_receive(session); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for banner", 0); + return LIBSSH2_ERROR_EAGAIN; + } + else if (rc) { + /* Unable to receive banner from remote */ + libssh2_error(session, LIBSSH2_ERROR_BANNER_NONE, "Timeout waiting for banner", 0); + return LIBSSH2_ERROR_BANNER_NONE; + } + + session->startup_state = libssh2_NB_state_sent1; } - - 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; + + if (session->startup_state == libssh2_NB_state_sent1) { + rc = libssh2_kex_exchange(session, 0, &session->startup_key_state); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block exchanging encryption keys", 0); + return LIBSSH2_ERROR_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE, "Unable to exchange encryption keys", 0); + return LIBSSH2_ERROR_KEX_FAILURE; + } + + session->startup_state = libssh2_NB_state_sent2; } - - _libssh2_debug(session, LIBSSH2_DBG_TRANS, - "Requesting userauth service"); - /* 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); - return LIBSSH2_ERROR_SOCKET_SEND; + + if (session->startup_state == libssh2_NB_state_sent2) { + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Requesting userauth service"); + + /* Request the userauth service */ + session->startup_service[0] = SSH_MSG_SERVICE_REQUEST; + libssh2_htonu32(session->startup_service + 1, sizeof("ssh-userauth") - 1); + memcpy(session->startup_service + 5, "ssh-userauth", sizeof("ssh-userauth") - 1); + + session->startup_state = libssh2_NB_state_sent3; } - - rc = libssh2_packet_require(session, SSH_MSG_SERVICE_ACCEPT, &data, - &data_len); - if(rc) { - return LIBSSH2_ERROR_SOCKET_DISCONNECT; + + if (session->startup_state == libssh2_NB_state_sent3) { + rc = libssh2_packet_write(session, session->startup_service, sizeof("ssh-userauth") + 5 - 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block asking for ssh-userauth service", 0); + return LIBSSH2_ERROR_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to ask for ssh-userauth service", 0); + return LIBSSH2_ERROR_SOCKET_SEND; + } + + session->startup_state = libssh2_NB_state_sent4; } - service_length = libssh2_ntohu32(data + 1); - - if ((service_length != (sizeof("ssh-userauth") - 1)) || - strncmp("ssh-userauth", (char *)data + 5, service_length)) { - LIBSSH2_FREE(session, data); - libssh2_error(session, LIBSSH2_ERROR_PROTO, - "Invalid response received from server", 0); - return LIBSSH2_ERROR_PROTO; + + if (session->startup_state == libssh2_NB_state_sent4) { + rc = libssh2_packet_require_ex(session, SSH_MSG_SERVICE_ACCEPT, &session->startup_data, &session->startup_data_len, + 0, NULL, 0, &session->startup_req_state); + if (rc == PACKET_EAGAIN) { + return LIBSSH2_ERROR_EAGAIN; + } + else if (rc) { + return LIBSSH2_ERROR_SOCKET_DISCONNECT; + } + session->startup_service_length = libssh2_ntohu32(session->startup_data + 1); + + if ((session->startup_service_length != (sizeof("ssh-userauth") - 1)) || + strncmp("ssh-userauth", (char *)session->startup_data + 5, session->startup_service_length)) { + LIBSSH2_FREE(session, session->startup_data); + libssh2_error(session, LIBSSH2_ERROR_PROTO, "Invalid response received from server", 0); + return LIBSSH2_ERROR_PROTO; + } + LIBSSH2_FREE(session, session->startup_data); + + session->startup_state = libssh2_NB_state_idle; + + return 0; } - LIBSSH2_FREE(session, data); - - return 0; + + /* just for safety return some error */ + return LIBSSH2_ERROR_INVAL; } /* }}} */ @@ -506,42 +722,53 @@ LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session) */ LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, const char *description, const char *lang) { - unsigned char *s, *data; - unsigned long data_len, descr_len = 0, lang_len = 0; + unsigned char *s; + unsigned long descr_len = 0, lang_len = 0; + int rc; - _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnecting: reason=%d, desc=%s, lang=%s", reason, description, lang); - if (description) { - descr_len = strlen(description); - } - if (lang) { - lang_len = strlen(lang); - } - data_len = descr_len + lang_len + 13; /* packet_type(1) + reason code(4) + descr_len(4) + lang_len(4) */ + if (session->disconnect_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnecting: reason=%d, desc=%s, lang=%s", reason, description, lang); + if (description) { + descr_len = strlen(description); + } + if (lang) { + lang_len = strlen(lang); + } + /* 13 = packet_type(1) + reason code(4) + descr_len(4) + lang_len(4) */ + session->disconnect_data_len = descr_len + lang_len + 13; - s = data = LIBSSH2_ALLOC(session, data_len); - if (!data) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for disconnect packet", 0); - return -1; + s = session->disconnect_data = LIBSSH2_ALLOC(session, session->disconnect_data_len); + if (!session->disconnect_data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for disconnect packet", 0); + session->disconnect_state = libssh2_NB_state_idle; + return -1; + } + + *(s++) = SSH_MSG_DISCONNECT; + libssh2_htonu32(s, reason); s += 4; + + libssh2_htonu32(s, descr_len); s += 4; + if (description) { + memcpy(s, description, descr_len); + s += descr_len; + } + + libssh2_htonu32(s, lang_len); s += 4; + if (lang) { + memcpy(s, lang, lang_len); + s += lang_len; + } + + session->disconnect_state = libssh2_NB_state_created; } - *(s++) = SSH_MSG_DISCONNECT; - libssh2_htonu32(s, reason); s += 4; - - libssh2_htonu32(s, descr_len); s += 4; - if (description) { - memcpy(s, description, descr_len); - s += descr_len; + rc = libssh2_packet_write(session, session->disconnect_data, session->disconnect_data_len); + if (rc == PACKET_EAGAIN) { + return LIBSSH2_ERROR_EAGAIN; } - libssh2_htonu32(s, lang_len); s += 4; - if (lang) { - memcpy(s, lang, lang_len); - s += lang_len; - } - - libssh2_packet_write(session, data, data_len); - - LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, session->disconnect_data); + session->disconnect_state = libssh2_NB_state_idle; return 0; } @@ -560,38 +787,48 @@ LIBSSH2_API const char *libssh2_session_methods(LIBSSH2_SESSION *session, int me switch(method_type) { case LIBSSH2_METHOD_KEX: method = session->kex; - break; + break; + case LIBSSH2_METHOD_HOSTKEY: method = (LIBSSH2_KEX_METHOD*)session->hostkey; - break; + break; + case LIBSSH2_METHOD_CRYPT_CS: method = (LIBSSH2_KEX_METHOD*)session->local.crypt; - break; + break; + case LIBSSH2_METHOD_CRYPT_SC: method = (LIBSSH2_KEX_METHOD*)session->remote.crypt; - break; + break; + case LIBSSH2_METHOD_MAC_CS: method = (LIBSSH2_KEX_METHOD*)session->local.mac; - break; + break; + case LIBSSH2_METHOD_MAC_SC: method = (LIBSSH2_KEX_METHOD*)session->remote.mac; - break; + break; + case LIBSSH2_METHOD_COMP_CS: method = (LIBSSH2_KEX_METHOD*)session->local.comp; - break; + break; + case LIBSSH2_METHOD_COMP_SC: method = (LIBSSH2_KEX_METHOD*)session->remote.comp; - break; + break; + case LIBSSH2_METHOD_LANG_CS: return ""; - break; + break; + case LIBSSH2_METHOD_LANG_SC: return ""; - break; + break; + default: libssh2_error(session, LIBSSH2_ERROR_INVAL, "Invalid parameter specified for method_type", 0); return NULL; - break; + break; } if (!method) { @@ -618,8 +855,7 @@ LIBSSH2_API void **libssh2_session_abstract(LIBSSH2_SESSION *session) * 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_session_last_error(LIBSSH2_SESSION *session, char **errmsg, int *errmsg_len, int want_buf) { /* No error to report */ if (!session->err_code) { @@ -670,6 +906,16 @@ libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg, } /* }}} */ +/* {{{ libssh2_session_last_error +* Returns error code +*/ +LIBSSH2_API int +libssh2_session_last_errno(LIBSSH2_SESSION *session) +{ + return session->err_code; +} +/* }}} */ + /* {{{ libssh2_session_flag * Set/Get session flags * Passing flag==0 will avoid changing session->flags while still returning its current value @@ -686,6 +932,45 @@ LIBSSH2_API int libssh2_session_flag(LIBSSH2_SESSION *session, int flag, int val } /* }}} */ +/* {{{ _libssh2_session_set_blocking + * Set a session's blocking mode on or off, return the previous status + * when this function is called. + */ +int _libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking) +{ + int bl = session->socket_block; + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Setting blocking mode on session %d", blocking); + if (blocking == session->socket_block) { + /* avoid if already correct */ + return bl; + } + session->socket_block = blocking; + + _libssh2_nonblock(session->socket_fd, !blocking); + + return bl; +} +/* }}} */ + +/* {{{ libssh2_session_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_session_set_blocking(LIBSSH2_SESSION *session, int blocking) +{ + (void)_libssh2_session_set_blocking(session, blocking); +} +/* }}} */ + +/* {{{ libssh2_session_get_blocking +* Returns a session's blocking mode on or off +*/ +LIBSSH2_API int libssh2_session_get_blocking(LIBSSH2_SESSION *session) +{ + return session->socket_block; +} +/* }}} */ + /* {{{ libssh2_poll_channel_read * Returns 0 if no data is waiting on channel, * non-0 if data is available @@ -696,8 +981,10 @@ LIBSSH2_API int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended LIBSSH2_PACKET *packet = session->packets.head; while (packet) { - if (((packet->data[0] == SSH_MSG_CHANNEL_DATA) && (extended == 0) && (channel->local.id == libssh2_ntohu32(packet->data + 1))) || - ((packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && (extended != 0) && (channel->local.id == libssh2_ntohu32(packet->data + 1)))) { + if (((packet->data[0] == SSH_MSG_CHANNEL_DATA) && (extended == 0) && + (channel->local.id == libssh2_ntohu32(packet->data + 1))) || + ((packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && (extended != 0) && + (channel->local.id == libssh2_ntohu32(packet->data + 1)))) { /* Found data waiting to be read */ return 1; } @@ -731,8 +1018,7 @@ static 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; unsigned int i, active_fds; @@ -743,7 +1029,7 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, 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++) { fds[i].revents = 0; @@ -753,20 +1039,24 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, sockets[i].events = fds[i].events; sockets[i].revents = 0; break; + case LIBSSH2_POLLFD_CHANNEL: sockets[i].fd = fds[i].fd.channel->session->socket_fd; sockets[i].events = POLLIN; sockets[i].revents = 0; if (!session) session = fds[i].fd.channel->session; break; + case LIBSSH2_POLLFD_LISTENER: sockets[i].fd = fds[i].fd.listener->session->socket_fd; sockets[i].events = POLLIN; sockets[i].revents = 0; if (!session) session = fds[i].fd.listener->session; break; + default: - if (session) libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, "Invalid descriptor passed to libssh2_poll()", 0); + if (session) libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, + "Invalid descriptor passed to libssh2_poll()", 0); return -1; } } @@ -791,18 +1081,22 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, if (fds[i].fd.socket > maxfd) maxfd = fds[i].fd.socket; } break; + case LIBSSH2_POLLFD_CHANNEL: FD_SET(fds[i].fd.channel->session->socket_fd, &rfds); if (fds[i].fd.channel->session->socket_fd > maxfd) maxfd = fds[i].fd.channel->session->socket_fd; if (!session) session = fds[i].fd.channel->session; break; + case LIBSSH2_POLLFD_LISTENER: FD_SET(fds[i].fd.listener->session->socket_fd, &rfds); if (fds[i].fd.listener->session->socket_fd > maxfd) maxfd = fds[i].fd.listener->session->socket_fd; if (!session) session = fds[i].fd.listener->session; break; + default: - if (session) libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, "Invalid descriptor passed to libssh2_poll()", 0); + if (session) libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, + "Invalid descriptor passed to libssh2_poll()", 0); return -1; } } @@ -845,6 +1139,7 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED; } break; + case LIBSSH2_POLLFD_LISTENER: if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && /* Want a connection */ ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { /* No connections known of yet */ @@ -955,12 +1250,14 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, active_fds++; } break; + case LIBSSH2_POLLFD_CHANNEL: if (FD_ISSET(fds[i].fd.channel->session->socket_fd, &rfds)) { /* Spin session until no data available */ while (libssh2_packet_read(fds[i].fd.channel->session) > 0); } break; + case LIBSSH2_POLLFD_LISTENER: if (FD_ISSET(fds[i].fd.listener->session->socket_fd, &rfds)) { /* Spin session until no data available */ diff --git a/src/sftp.c b/src/sftp.c index d04e77f..6494159 100644 --- a/src/sftp.c +++ b/src/sftp.c @@ -74,73 +74,6 @@ #define SSH_FXP_EXTENDED 200 #define SSH_FXP_EXTENDED_REPLY 201 -typedef enum { - sftp_read_idle = 0, - sftp_read_packet_allocated, - sftp_read_packet_created, - sftp_read_packet_sent -} libssh2_sftp_read_state; - -typedef enum { - sftp_readdir_idle = 0, - sftp_readdir_packet_created, - sftp_readdir_packet_sent -} libssh2_sftp_readdir_state; - -typedef enum { - sftp_write_idle = 0, - sftp_write_packet_created, - sftp_write_packet_sent -} libssh2_sftp_write_state; - -typedef enum { - sftp_mkdir_idle = 0, - sftp_mkdir_packet_created, - sftp_mkdir_packet_sent -} libssh2_sftp_mkdir_state; - -struct _LIBSSH2_SFTP { - LIBSSH2_CHANNEL *channel; - - unsigned long request_id, version; - - LIBSSH2_PACKET_BRIGADE packets; - - LIBSSH2_SFTP_HANDLE *handles; - - unsigned long last_errno; - - /* Holder for partial packet, use in libssh2_sftp_packet_read() */ - unsigned char *partial_packet; /* The data */ - unsigned long partial_len; /* Desired number of bytes */ - unsigned long partial_received; /* Bytes received so far */ - - /* Time that libssh2_sftp_packet_requirev() started reading */ - time_t requirev_start; - - /* State variables used in _libssh2_sftp_read() */ - libssh2_sftp_read_state read_state; - unsigned char *read_packet; - unsigned long read_request_id; - size_t read_total_read; - - /* State variables used in _libssh2_sftp_readdir() */ - libssh2_sftp_readdir_state readdir_state; - unsigned char *readdir_packet; - unsigned long readdir_request_id; - - /* State variables used in _libssh2_sftp_write() */ - libssh2_sftp_write_state write_state; - unsigned char *write_packet; - unsigned long write_request_id; - - /* State variables used in _libssh2_sftp_mkdir() */ - libssh2_sftp_mkdir_state mkdir_state; - unsigned char *mkdir_packet; - unsigned long mkdir_request_id; - -}; - #define LIBSSH2_SFTP_HANDLE_FILE 0 #define LIBSSH2_SFTP_HANDLE_DIR 1 @@ -149,31 +82,9 @@ struct _LIBSSH2_SFTP { /* S_IFDIR */ #define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000 -struct _LIBSSH2_SFTP_HANDLE { - LIBSSH2_SFTP *sftp; - LIBSSH2_SFTP_HANDLE *prev, *next; - - char *handle; - int handle_len; - - 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; -}; - /* {{{ libssh2_sftp_packet_add * Add a packet to the SFTP packet brigade */ -/* libssh2_sftp_packet_add - NB-SAFE */ static int libssh2_sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data, unsigned long data_len) { LIBSSH2_SESSION *session = sftp->channel->session; @@ -232,7 +143,7 @@ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block, int fl packet_received = sftp->partial_received; sftp->partial_packet = NULL; } else { - rc = _libssh2_channel_read(channel, (char *)buffer, 4); + rc = libssh2_channel_read_ex(channel, 0, (char *)buffer, 4); if (flush && (rc < 0)) { /* When flushing, exit quickly */ return -1; @@ -265,7 +176,7 @@ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block, int fl /* Read as much of the packet as we can */ while (packet_len > packet_received) { - long bytes_received = _libssh2_channel_read(channel, (char *)packet + packet_received, packet_len - packet_received); + long bytes_received = libssh2_channel_read_ex(channel, 0, (char *)packet + packet_received, packet_len - packet_received); if (flush && (rc < 0)) { /* When flushing, exit quickly */ @@ -374,7 +285,7 @@ static int libssh2_sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_ return 0; } - bl = libssh2_channel_get_blocking(sftp->channel); + bl = libssh2_session_get_blocking(session); while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { int ret = libssh2_sftp_packet_read(sftp, bl, 0); if (!bl && (ret == PACKET_EAGAIN)) { @@ -415,12 +326,12 @@ static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_respon sftp->requirev_start = time(NULL); /* Flush */ - bl = libssh2_channel_get_blocking(sftp->channel); + bl = libssh2_session_get_blocking(sftp->channel->session); while (libssh2_sftp_packet_read(sftp, 0, 1) > 0); libssh2_channel_set_blocking(sftp->channel, bl); } - bl = libssh2_channel_get_blocking(sftp->channel); + bl = libssh2_session_get_blocking(sftp->channel->session) ; while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) { int ret; @@ -436,6 +347,7 @@ static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_respon } } +#warning "XXX - The timeout is ignored if PACKET_EAGAIN is returned" ret = libssh2_sftp_packet_read(sftp, bl, 0); if (!bl && (ret == PACKET_EAGAIN)) { return PACKET_EAGAIN; @@ -597,108 +509,161 @@ LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor) /* {{{ libssh2_sftp_init * Startup an SFTP session */ -/* libssh2_sftp_init - NB-UNSAFE */ 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 char *data, *s; unsigned long data_len; - int bl; + int rc; - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Initializing SFTP subsystem"); - channel = libssh2_channel_open_session(session); - if (!channel) { - libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0); - return NULL; + if (session->sftpInit_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Initializing SFTP subsystem"); + + session->sftpInit_state = libssh2_NB_state_created; } - 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; + if (session->sftpInit_state == libssh2_NB_state_created) { + session->sftpInit_channel = libssh2_channel_open_ex(session, "session", sizeof("session") - 1, + LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, + NULL, 0); + if (!session->sftpInit_channel) { + if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block starting up channel", 0); + return NULL; + } + else if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0); + session->sftpInit_state = libssh2_NB_state_idle; + return NULL; + } + } + + session->sftpInit_state = libssh2_NB_state_sent; } - bl = libssh2_channel_get_blocking(channel); - - libssh2_channel_set_blocking(channel, 1); - - libssh2_channel_handle_extended_data(channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); - - 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; + if (session->sftpInit_state == libssh2_NB_state_sent) { + rc = libssh2_channel_process_startup(session->sftpInit_channel, "subsystem", sizeof("subsystem") - 1, + "sftp", strlen("sftp")); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block to request SFTP subsystem", 0); + return NULL; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request SFTP subsystem", 0); + libssh2_channel_free(session->sftpInit_channel); + session->sftpInit_channel = NULL; + session->sftpInit_state = libssh2_NB_state_idle; + return NULL; + } + + session->sftpInit_state = libssh2_NB_state_sent1; } - 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); + if (session->sftpInit_state == libssh2_NB_state_sent1) { + rc = libssh2_channel_handle_extended_data2(session->sftpInit_channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block requesting handle extended data", 0); + return NULL; + } + + session->sftpInit_sftp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP)); + if (!session->sftpInit_sftp) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new SFTP structure", 0); + libssh2_channel_free(session->sftpInit_channel); + session->sftpInit_channel = NULL; + session->sftpInit_state = libssh2_NB_state_idle; + return NULL; + } + memset(session->sftpInit_sftp, 0, sizeof(LIBSSH2_SFTP)); + session->sftpInit_sftp->channel = session->sftpInit_channel; + session->sftpInit_sftp->request_id = 0; + + libssh2_htonu32(session->sftpInit_buffer, 5); + session->sftpInit_buffer[4] = SSH_FXP_INIT; + libssh2_htonu32(session->sftpInit_buffer + 5, LIBSSH2_SFTP_VERSION); + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending FXP_INIT packet advertising version %d support", + (int)LIBSSH2_SFTP_VERSION); + + session->sftpInit_state = libssh2_NB_state_sent2; + } - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending FXP_INIT packet advertising version %d support", (int)LIBSSH2_SFTP_VERSION); - 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 (session->sftpInit_state == libssh2_NB_state_sent2) { + rc = libssh2_channel_write_ex(session->sftpInit_channel, 0, (char *)session->sftpInit_buffer, 9); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending SSH_FXP_INIT", 0); + return NULL; + } + else if (9 != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SSH_FXP_INIT", 0); + libssh2_channel_free(session->sftpInit_channel); + session->sftpInit_channel = NULL; + LIBSSH2_FREE(session, session->sftpInit_sftp); + session->sftpInit_sftp = NULL; + session->sftpInit_state = libssh2_NB_state_idle; + return NULL; + } + + session->sftpInit_state = libssh2_NB_state_sent3; } /* For initiallization we are requiring blocking, probably reasonable */ - 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); + rc = libssh2_sftp_packet_require(session->sftpInit_sftp, SSH_FXP_VERSION, 0, &data, &data_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response from SFTP subsystem", 0); + return NULL; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from SFTP subsystem", 0); + libssh2_channel_free(session->sftpInit_channel); + session->sftpInit_channel = NULL; + LIBSSH2_FREE(session, session->sftpInit_sftp); + session->sftpInit_sftp = NULL; + session->sftpInit_state = libssh2_NB_state_idle; 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); + libssh2_channel_free(session->sftpInit_channel); + session->sftpInit_channel = NULL; + LIBSSH2_FREE(session, session->sftpInit_sftp); + session->sftpInit_sftp = NULL; + session->sftpInit_state = libssh2_NB_state_idle; return NULL; } s = data + 1; - sftp->version = libssh2_ntohu32(s); s += 4; - if (sftp->version > LIBSSH2_SFTP_VERSION) { - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Truncating remote SFTP version from %lu", sftp->version); - sftp->version = LIBSSH2_SFTP_VERSION; + session->sftpInit_sftp->version = libssh2_ntohu32(s); s += 4; + if (session->sftpInit_sftp->version > LIBSSH2_SFTP_VERSION) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Truncating remote SFTP version from %lu", session->sftpInit_sftp->version); + session->sftpInit_sftp->version = LIBSSH2_SFTP_VERSION; } - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Enabling SFTP version %lu compatability", sftp->version); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Enabling SFTP version %lu compatability", session->sftpInit_sftp->version); 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; + extension_name = s; s += extname_len; extdata_len = libssh2_ntohu32(s); s += 4; - extension_data = s; s += extdata_len; + extension_data = s; s += extdata_len; /* 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; + session->sftpInit_sftp->channel->abstract = session->sftpInit_sftp; + session->sftpInit_sftp->channel->close_cb = libssh2_sftp_dtor; - /* Restore the blocking state of the channel */ - libssh2_channel_set_blocking(channel, bl); - - return sftp; + session->sftpInit_state = libssh2_NB_state_idle; + return session->sftpInit_sftp; } /* }}} */ /* {{{ libssh2_sftp_shutdown * Shutsdown the SFTP subsystem */ -/* libssh2_sftp_shutdown - NB-SAFE */ LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp) { return libssh2_channel_free(sftp->channel); @@ -749,18 +714,15 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, const } _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending %s open request", (open_type == LIBSSH2_SFTP_OPENFILE) ? "file" : "directory"); - if (packet_len != _libssh2_channel_write(channel, (char *)packet, - packet_len)) { + while ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN); + if (packet_len != rc) { 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); -/* #warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" */ - while ((rc = libssh2_sftp_packet_requirev(sftp, 2, fopen_responses, request_id, &data, &data_len)) == PACKET_EAGAIN) { - ; - } + while ((rc = libssh2_sftp_packet_requirev(sftp, 2, fopen_responses, request_id, &data, &data_len)) == PACKET_EAGAIN); if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return NULL; @@ -885,8 +847,9 @@ static ssize_t _libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, } if (sftp->read_state != sftp_read_packet_sent) { - if (libssh2_channel_get_blocking(channel)) { - if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + if (libssh2_session_get_blocking(channel->session)) { + while ((retcode = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN); + if (packet_len != retcode) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); LIBSSH2_FREE(session, packet); sftp->read_packet = NULL; @@ -894,7 +857,7 @@ static ssize_t _libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, return -1; } } else { - if ((retcode = libssh2_channel_writenb(channel, (char *)packet, packet_len)) == PACKET_EAGAIN) { + if ((retcode = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN) { sftp->read_packet = packet; sftp->read_request_id = request_id; sftp->read_total_read = total_read; @@ -979,7 +942,7 @@ LIBSSH2_API ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, { ssize_t rc; LIBSSH2_CHANNEL *ch = handle->sftp->channel; - int bl = libssh2_channel_get_blocking(ch); + int bl = libssh2_session_get_blocking(ch->session); /* set blocking */ libssh2_channel_set_blocking(ch, 1); @@ -989,7 +952,7 @@ LIBSSH2_API ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, /* restore state */ libssh2_channel_set_blocking(ch, bl); - if(rc < 0) { + if (rc < 0) { /* precent accidental returning of other return codes since this API does not support/provide those */ return -1; @@ -1008,7 +971,7 @@ LIBSSH2_API ssize_t libssh2_sftp_readnb(LIBSSH2_SFTP_HANDLE *handle, { ssize_t rc; LIBSSH2_CHANNEL *ch = handle->sftp->channel; - int bl = libssh2_channel_get_blocking(ch); + int bl = libssh2_session_get_blocking(ch->session); /* set non-blocking */ libssh2_channel_set_blocking(ch, 0); @@ -1110,15 +1073,16 @@ static int _libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size if (sftp->readdir_state != sftp_readdir_packet_sent) { _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading entries from directory handle"); - if (libssh2_channel_get_blocking(channel)) { - if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + if (libssh2_session_get_blocking(channel->session)) { + while ((retcode = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN); + if (packet_len != retcode) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); LIBSSH2_FREE(session, packet); sftp->readdir_state = sftp_readdir_idle; return -1; } } else { - if ((retcode = libssh2_channel_writenb(channel, (char *)packet, packet_len)) == PACKET_EAGAIN) { + if ((retcode = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN) { sftp->readdir_packet = packet; sftp->readdir_request_id = request_id; return PACKET_EAGAIN; @@ -1213,7 +1177,7 @@ LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, { int rc; LIBSSH2_CHANNEL *ch = handle->sftp->channel; - int bl = libssh2_channel_get_blocking(ch); + int bl = libssh2_session_get_blocking(ch->session); /* set blocking */ libssh2_channel_set_blocking(ch, 1); @@ -1223,7 +1187,7 @@ LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, /* restore state */ libssh2_channel_set_blocking(ch, bl); - if(rc < 0) { + if (rc < 0) { /* precent accidental returning of other return codes since this API does not support/provide those */ return -1; @@ -1242,7 +1206,7 @@ LIBSSH2_API int libssh2_sftp_readdirnb(LIBSSH2_SFTP_HANDLE *handle, char *buffer { int rc; LIBSSH2_CHANNEL *ch = handle->sftp->channel; - int bl = libssh2_channel_get_blocking(ch); + int bl = libssh2_session_get_blocking(ch->session); /* set non-blocking */ libssh2_channel_set_blocking(ch, 0); @@ -1296,15 +1260,16 @@ static ssize_t _libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buff } if (sftp->write_state != sftp_write_packet_sent) { - if (libssh2_channel_get_blocking(channel)) { - if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + if (libssh2_session_get_blocking(channel->session)) { + while ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN); + if (packet_len != rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); LIBSSH2_FREE(session, packet); sftp->write_state = sftp_write_idle; return -1; } } else { - if ((rc = libssh2_channel_writenb(channel, (char *)packet, packet_len)) == PACKET_EAGAIN) { + if ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN) { sftp->write_packet = packet; sftp->write_request_id = request_id; return PACKET_EAGAIN; @@ -1356,7 +1321,7 @@ LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, { ssize_t rc; LIBSSH2_CHANNEL *ch = handle->sftp->channel; - int bl = libssh2_channel_get_blocking(ch); + int bl = libssh2_session_get_blocking(ch->session); /* set blocking */ libssh2_channel_set_blocking(ch, 1); @@ -1366,7 +1331,7 @@ LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, /* restore state */ libssh2_channel_set_blocking(ch, bl); - if(rc < 0) { + if (rc < 0) { /* precent accidental returning of other return codes since this API does not support/provide those */ return -1; @@ -1385,7 +1350,7 @@ LIBSSH2_API ssize_t libssh2_sftp_writenb(LIBSSH2_SFTP_HANDLE *handle, { ssize_t rc; LIBSSH2_CHANNEL *ch = handle->sftp->channel; - int bl = libssh2_channel_get_blocking(ch); + int bl = libssh2_session_get_blocking(ch->session); /* set non-blocking */ libssh2_channel_set_blocking(ch, 0); @@ -1432,17 +1397,15 @@ LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_ s += libssh2_sftp_attr2bin(s, attrs); } - if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + while ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN); + if (packet_len != rc) { 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); -/* #warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" */ - while ((rc = libssh2_sftp_packet_requirev(sftp, 2, fstat_responses, request_id, &data, &data_len)) == PACKET_EAGAIN) { - ; - } + while ((rc = libssh2_sftp_packet_requirev(sftp, 2, fstat_responses, request_id, &data, &data_len)) == PACKET_EAGAIN); if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; @@ -1518,17 +1481,15 @@ LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle) libssh2_htonu32(s, handle->handle_len); s += 4; memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; - if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + while ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN); + if (packet_len != rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_CLOSE command", 0); LIBSSH2_FREE(session, packet); return -1; } LIBSSH2_FREE(session, packet); -/* #warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" */ - while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN) { - ; - } + while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN); if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; @@ -1593,18 +1554,15 @@ LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, const char *filename, libssh2_htonu32(s, filename_len); s += 4; memcpy(s, filename, filename_len); s += filename_len; - if (packet_len != libssh2_channel_write(channel, (char *)packet, - packet_len)) { + while ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN); + if (packet_len != rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_REMOVE command", 0); LIBSSH2_FREE(session, packet); return -1; } LIBSSH2_FREE(session, packet); -/* #warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" */ - while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN) { - ; - } + while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN); if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; @@ -1665,18 +1623,15 @@ LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, const char *source_f libssh2_htonu32(s, flags); s += 4; } - if (packet_len != libssh2_channel_write(channel, (char *)packet, - s - packet)) { + while ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, s - packet)) == PACKET_EAGAIN); + if (packet_len != rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_RENAME command", 0); LIBSSH2_FREE(session, packet); return -1; } LIBSSH2_FREE(session, packet); -/* #warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" */ - while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN) { - ; - } + while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN); if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; @@ -1751,15 +1706,16 @@ static int _libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path, unsigned } if (sftp->mkdir_state != sftp_mkdir_packet_sent) { - if (libssh2_channel_get_blocking(channel)) { - if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + if (libssh2_session_get_blocking(channel->session)) { + while ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN); + if (packet_len != rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); LIBSSH2_FREE(session, packet); sftp->mkdir_state = sftp_mkdir_idle; return -1; } } else { - if ((rc = libssh2_channel_writenb(channel, (char *)packet, packet_len)) == PACKET_EAGAIN) { + if ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN) { sftp->mkdir_packet = packet; sftp->mkdir_request_id = request_id; return PACKET_EAGAIN; @@ -1809,7 +1765,7 @@ LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path, unsi { ssize_t rc; LIBSSH2_CHANNEL *ch = sftp->channel; - int bl = libssh2_channel_get_blocking(ch); + int bl = libssh2_session_get_blocking(ch->session); /* set blocking */ libssh2_channel_set_blocking(ch, 1); @@ -1819,7 +1775,7 @@ LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path, unsi /* restore state */ libssh2_channel_set_blocking(ch, bl); - if(rc < 0) { + if (rc < 0) { /* precent accidental returning of other return codes since this API does not support/provide those */ return -1; @@ -1837,7 +1793,7 @@ LIBSSH2_API int libssh2_sftp_mkdirnb_ex(LIBSSH2_SFTP *sftp, const char *path, un { ssize_t rc; LIBSSH2_CHANNEL *ch = sftp->channel; - int bl = libssh2_channel_get_blocking(ch); + int bl = libssh2_session_get_blocking(ch->session); /* set non-blocking */ libssh2_channel_set_blocking(ch, 0); @@ -1878,17 +1834,15 @@ LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, const char *path, unsi libssh2_htonu32(s, path_len); s += 4; memcpy(s, path, path_len); s += path_len; - if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + while ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN); + if (packet_len != rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_MKDIR command", 0); LIBSSH2_FREE(session, packet); return -1; } LIBSSH2_FREE(session, packet); -/* #warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" */ - while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN) { - ; - } + while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN); if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; @@ -1949,18 +1903,15 @@ LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, const char *path, unsig s += libssh2_sftp_attr2bin(s, attrs); } - if (packet_len != libssh2_channel_write(channel, (char *)packet, - packet_len)) { + while ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN); + if (packet_len != rc) { 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); -/* #warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" */ - while ((rc = libssh2_sftp_packet_requirev(sftp, 2, stat_responses, request_id, &data, &data_len)) == PACKET_EAGAIN) { - ; - } + while ((rc = libssh2_sftp_packet_requirev(sftp, 2, stat_responses, request_id, &data, &data_len)) == PACKET_EAGAIN); if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; @@ -2038,18 +1989,15 @@ LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, un memcpy(s, target, target_len); s += target_len; } - if (packet_len != libssh2_channel_write(channel, (char *)packet, - packet_len)) { + while ((rc = libssh2_channel_write_ex(channel, 0, (char *)packet, packet_len)) == PACKET_EAGAIN); + if (packet_len != rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SYMLINK/READLINK command", 0); LIBSSH2_FREE(session, packet); return -1; } LIBSSH2_FREE(session, packet); -/* #warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" */ - while ((rc = libssh2_sftp_packet_requirev(sftp, 2, link_responses, request_id, &data, &data_len)) == PACKET_EAGAIN) { - ; - } + while ((rc = libssh2_sftp_packet_requirev(sftp, 2, link_responses, request_id, &data, &data_len)) == PACKET_EAGAIN); if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; @@ -2096,24 +2044,3 @@ LIBSSH2_API unsigned long libssh2_sftp_last_error(LIBSSH2_SFTP *sftp) return sftp->last_errno; } /* }}} */ - -/* {{{ libssh2_sftp_set_blocking - * Set a channel's blocking mode on or off, this is an accessor - * to the channel through the SFTP session handle - */ -/* libssh2_sftp_set_blocking - NB-SAFE */ -LIBSSH2_API void libssh2_sftp_set_blocking(LIBSSH2_SFTP *session, int blocking) { - libssh2_channel_set_blocking(session->channel, blocking); -} -/* }}} */ - -/* {{{ libssh2_sftp_get_blocking - * Returns a channel's blocking mode on or off, this is an accessor - * to the channel through the SFTP session handle - */ -/* libssh2_sftp_get_blocking - NB-SAFE */ -LIBSSH2_API int libssh2_sftp_get_blocking(LIBSSH2_SFTP *session) { - return libssh2_channel_get_blocking(session->channel); -} -/* }}} */ - diff --git a/src/transport.c b/src/transport.c index cef0581..dc22b9b 100644 --- a/src/transport.c +++ b/src/transport.c @@ -57,7 +57,7 @@ static void debugdump(LIBSSH2_SESSION *session, FILE *stream = stdout; unsigned int width=0x10; - if(!(session->showmask & (1<< LIBSSH2_DBG_TRANS))) { + if (!(session->showmask & (1<< LIBSSH2_DBG_TRANS))) { /* not asked for, bail out */ return; } @@ -66,11 +66,11 @@ static void debugdump(LIBSSH2_SESSION *session, for(i=0; ipacket; - int payload_len = p->packet_length-1; - libssh2pack_t packet_type; - int macstate = LIBSSH2_MAC_CONFIRMED; + int rc; - if(encrypted) { + if (session->fullpacket_state == libssh2_NB_state_idle) { + session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED; + session->fullpacket_payload_len = p->packet_length-1; - /* 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 (encrypted) { + + /* Calculate MAC hash */ + session->remote.mac->hash(session, + macbuf, /* store hash here */ + session->remote.seqno, + p->init, 5, + p->payload, session->fullpacket_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 + session->fullpacket_payload_len, + session->remote.mac->mac_len)) { + session->fullpacket_macstate = LIBSSH2_MAC_INVALID; + } } - 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; + session->remote.seqno++; + + /* ignore the padding */ + session->fullpacket_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, session->fullpacket_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; + session->fullpacket_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; + if (data == p->payload) { + /* It's not to be freed, because the + * compression layer reused payload, So let's + * do the same! + */ + session->fullpacket_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); + session->fullpacket_payload_len = data_len; } - memcpy(p->payload, data, data_len); - payload_len = data_len; } } + + session->fullpacket_packet_type = p->payload[0]; + + debugdump(session, "libssh2_packet_read() plain", + p->payload, session->fullpacket_payload_len); + + session->fullpacket_state = libssh2_NB_state_created; + } + + if (session->fullpacket_state == libssh2_NB_state_created) { + rc = libssh2_packet_add(session, p->payload, session->fullpacket_payload_len, session->fullpacket_macstate); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc < 0) { + return PACKET_FAIL; + } } - packet_type = p->payload[0]; + session->fullpacket_state = libssh2_NB_state_idle; - debugdump(session, "libssh2_packet_read() plain", - p->payload, payload_len); - if (libssh2_packet_add(session, p->payload, payload_len, macstate) < 0) - return PACKET_FAIL; - - return packet_type; + return session->fullpacket_packet_type; } @@ -231,7 +246,7 @@ static libssh2pack_t fullpacket(LIBSSH2_SESSION *session, * 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. @@ -241,7 +256,6 @@ static libssh2pack_t fullpacket(LIBSSH2_SESSION *session, * 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; @@ -255,20 +269,28 @@ libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session) int minimum; int encrypted = 1; - do { + /* + * =============================== NOTE =============================== + * I know this is very ugly and not a really good use of "goto", but + * this case statement would be even uglier to do it any other way + */ + if (session->readPack_state == libssh2_NB_state_jump1) { + session->readPack_state = libssh2_NB_state_idle; + encrypted = session->readPack_encrypted; + goto libssh2_packet_read_point1; + } + do { if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) { return PACKET_NONE; } if (session->state & LIBSSH2_STATE_NEWKEYS) { blocksize = session->remote.crypt->blocksize; - } - else { + } else { encrypted = 0; /* not encrypted */ - blocksize = 5; /* not strictly true, but we can use 5 - here to make the checks below work - fine still */ + blocksize = 5; /* not strictly true, but we can use 5 here to + make the checks below work fine still */ } minimum = p->total_num ? p->total_num - p->data_num : blocksize; @@ -285,28 +307,24 @@ libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session) /* if remainbuf turns negative we have a bad internal error */ assert(remainbuf >= 0); - while(remainbuf < minimum) { - /* While there is too little data to deal with, read - more */ + if (remainbuf < minimum) { + /* If we have less than a minimum 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); + if (remainbuf) { + memmove(p->buf, &p->buf[p->readidx], remainbuf); p->readidx = 0; p->writeidx = remainbuf; - } - else { + } 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, + /* 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 @@ -329,23 +347,25 @@ libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session) /* how much data to deal with from the buffer */ numbytes = remainbuf; - if(!p->total_num) { + 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) { + 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. */ + /* 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 { + } else { /* the data is plain, just copy it verbatim to the working block buffer */ memcpy(block, &p->buf[p->readidx], blocksize); @@ -355,16 +375,14 @@ libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session) p->readidx += blocksize; /* we now have the initial blocksize bytes decrypted, - and we can extract packet and padding length from it - */ + * 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); + /* 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: * @@ -374,26 +392,25 @@ libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session) * or less (including length, padding length, payload, * padding, and MAC.)." */ - if(p->total_num > LIBSSH2_PACKET_MAXPAYLOAD) { + 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) { + if (!p->payload) { return PACKET_ENOMEM; } /* init write pointer to start of payload buffer */ p->wptr = p->payload; - if(blocksize > 5) { + 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 */ + p->wptr += blocksize-5; /* advance write pointer */ } /* init the data_num field to the number of bytes of @@ -405,17 +422,16 @@ libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session) } /* how much there is left to add to the current payload - package */ + 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 */ + 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) { + 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 @@ -425,35 +441,31 @@ libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session) /* 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 { + if ((p->data_num + numbytes) > (p->total_num - skip)) { + numdecrypt = (p->total_num - skip) - p->data_num; + } else { int frac; numdecrypt = numbytes; frac = numdecrypt % blocksize; - if(frac) { + if (frac) { /* not an aligned amount of blocks, - align it */ + align it */ numdecrypt -= frac; /* and make it no unencrypted data - after it */ + after it */ numbytes = 0; } } - } - else { + } else { /* unencrypted data should not be decrypted at all */ numdecrypt = 0; } /* if there are bytes to decrypt, do that */ - if(numdecrypt > 0) { + if (numdecrypt > 0) { /* now decrypt the lot */ - rc = decrypt(session, &p->buf[p->readidx], - p->wptr, numdecrypt); - if(rc != PACKET_NONE) { + rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt); + if (rc != PACKET_NONE) { return rc; } @@ -469,8 +481,8 @@ libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session) } /* if there are bytes to copy that aren't decrypted, simply - copy them as-is to the target buffer */ - if(numbytes > 0) { + copy them as-is to the target buffer */ + if (numbytes > 0) { memcpy(p->wptr, &p->buf[p->readidx], numbytes); /* advance the read pointer */ @@ -482,12 +494,18 @@ libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session) } /* now check how much data there's left to read to finish the - current packet */ + current packet */ remainpack = p->total_num - p->data_num; - if(!remainpack) { + if (!remainpack) { /* we have a full packet */ +libssh2_packet_read_point1: rc = fullpacket(session, encrypted); + if (rc == PACKET_EAGAIN) { + session->readPack_encrypted = encrypted; + session->readPack_state = libssh2_NB_state_jump1; + return PACKET_EAGAIN; + } p->total_num = 0; /* no packet buffer available */ @@ -501,27 +519,24 @@ libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION *session) #ifndef OLDSEND -static libssh2pack_t send_existing(LIBSSH2_SESSION *session, - unsigned char *data, - unsigned long data_len, - ssize_t *ret) +static libssh2pack_t send_existing(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len, ssize_t *ret) { ssize_t rc; ssize_t length; struct transportpacket *p = &session->packet; - if(!p->outbuf) { + 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 */ + 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; } @@ -530,27 +545,24 @@ static libssh2pack_t send_existing(LIBSSH2_SESSION *session, /* 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)); + rc = send(session->socket_fd, &p->outbuf[p->osent], length, LIBSSH2_SOCKET_SEND_FLAGS(session)); - if(rc == length) { + 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) { + else if (rc < 0) { /* nothing was sent */ - if(errno != EAGAIN) { + if (errno != EAGAIN) { /* send failure! */ return PACKET_FAIL; } return PACKET_EAGAIN; } - debugdump(session, "libssh2_packet_write send()", - &p->outbuf[p->osent], length); + debugdump(session, "libssh2_packet_write send()", &p->outbuf[p->osent], length); p->osent += length; /* we sent away this much data */ return PACKET_NONE; @@ -565,12 +577,9 @@ static libssh2pack_t send_existing(LIBSSH2_SESSION *session, * 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 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 blocksize = (session->state & LIBSSH2_STATE_NEWKEYS) ? session->local.crypt->blocksize : 8; int padding_length; int packet_length; int total_length; @@ -591,17 +600,16 @@ int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, /* FIRST, check if we have a pending write to complete */ rc = send_existing(session, data, data_len, &ret); - if(rc || 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)) { + 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 */ } } @@ -611,11 +619,10 @@ int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, MUST be a multiple of the cipher block size or 8, whichever is larger. */ - /* Plain math: (4 + 1 + packet_length + padding_length) % blocksize == - 0 */ + /* 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 */ + 4 for the packet_length field */ /* at this point we have it all except the padding */ @@ -643,14 +650,13 @@ int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, packet_length += padding_length; /* append the MAC length to the total_length size */ - total_length = packet_length + - (encrypted?session->local.mac->mac_len:0); + 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) { + if (!p->outbuf) { return PACKET_ENOMEM; } @@ -667,41 +673,33 @@ int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, LIBSSH2_FREE(session, data); } - if(encrypted) { + 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); + 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) { + 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)) + 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)); + ret = send(session->socket_fd, p->outbuf, total_length, + LIBSSH2_SOCKET_SEND_FLAGS(session)); - if(ret != -1) { - debugdump(session, "libssh2_packet_write send()", - p->outbuf, ret); + if (ret != -1) { + debugdump(session, "libssh2_packet_write send()", p->outbuf, ret); } - if(ret != total_length) { - if((ret > 0 ) || - ((ret == -1) && (errno == EAGAIN))) { + 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; diff --git a/src/userauth.c b/src/userauth.c index 912f226..d13e880 100644 --- a/src/userauth.c +++ b/src/userauth.c @@ -55,52 +55,89 @@ LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, const char *username, unsigned int username_len) { static const unsigned char reply_codes[3] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; - unsigned long data_len = username_len + 31; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" + - method_len(4) + method(4)"none" */ + /* packet_type(1) + username_len(4) + service_len(4) + + service(14)"ssh-connection" + method_len(4) + method(4)"none" */ unsigned long methods_len; - unsigned char *data, *s; + unsigned char *s; + int rc; + + if (session->userauth_state == libssh2_NB_state_idle) { + /* Zero the whole thing out */ + memset(&session->userauth_packet_requirev_state, 0, sizeof(session->userauth_packet_requirev_state)); + + session->userauth_data_len = username_len + 31; + + s = session->userauth_data = LIBSSH2_ALLOC(session, session->userauth_data_len); + if (!session->userauth_data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth_list", 0); + return NULL; + } - s = data = LIBSSH2_ALLOC(session, data_len); - if (!data) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth_list", 0); - return NULL; + *(s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(s, username_len); s += 4; + if (username) { + memcpy(s, username, username_len); s += username_len; + } + + libssh2_htonu32(s, 14); s += 4; + memcpy(s, "ssh-connection", 14); s += 14; + + libssh2_htonu32(s, 4); s += 4; + memcpy(s, "none", 4); s += 4; + + session->userauth_state = libssh2_NB_state_created; } - *(s++) = SSH_MSG_USERAUTH_REQUEST; - libssh2_htonu32(s, username_len); s += 4; - if (username) { - memcpy(s, username, username_len); s += username_len; + if (session->userauth_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->userauth_data, session->userauth_data_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block requesting userauth list", 0); + return NULL; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-none request", 0); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + session->userauth_state = libssh2_NB_state_idle; + return NULL; + } + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + + session->userauth_state = libssh2_NB_state_sent; } - libssh2_htonu32(s, 14); s += 4; - memcpy(s, "ssh-connection", 14); s += 14; + if (session->userauth_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &session->userauth_data, &session->userauth_data_len, + 0, NULL, 0, &session->userauth_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block requesting userauth list", 0); + return NULL; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_NONE, "No error", 0); + session->userauth_state = libssh2_NB_state_idle; + return NULL; + } - libssh2_htonu32(s, 4); s += 4; - memcpy(s, "none", 4); s += 4; + if (session->userauth_data[0] == SSH_MSG_USERAUTH_SUCCESS) { + /* Wow, who'dve thought... */ + libssh2_error(session, LIBSSH2_ERROR_NONE, "No error", 0); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_state = libssh2_NB_state_idle; + return NULL; + } - if (libssh2_packet_write(session, data, data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-none request", 0); - LIBSSH2_FREE(session, data); - return NULL; + methods_len = libssh2_ntohu32(session->userauth_data + 1); + memcpy(session->userauth_data, session->userauth_data + 5, methods_len); + session->userauth_data[methods_len] = '\0'; + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Permitted auth methods: %s", session->userauth_data); } - LIBSSH2_FREE(session, data); - - if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { - return NULL; - } - - if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { - /* Wow, who'dve thought... */ - LIBSSH2_FREE(session, data); - session->state |= LIBSSH2_STATE_AUTHENTICATED; - return NULL; - } - - methods_len = libssh2_ntohu32(data + 1); - memcpy(data, data + 5, methods_len); - data[methods_len] = '\0'; - _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Permitted auth methods: %s", data); - return (char *)data; + + session->userauth_state = libssh2_NB_state_idle; + return (char *)session->userauth_data; } /* }}} */ @@ -117,113 +154,190 @@ LIBSSH2_API int libssh2_userauth_authenticated(LIBSSH2_SESSION *session) /* {{{ libssh2_userauth_password * Plain ol' login */ -LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, - const char *password, unsigned int password_len, - LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))) +LIBSSH2_API int +libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, const char *password, + unsigned int password_len, LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))) { - unsigned char *data, *s; + unsigned char *s; static const unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 0 }; - unsigned long data_len = username_len + password_len + 40; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" + - method_len(4) + method(8)"password" + chgpwdbool(1) + password_len(4) */ + int rc; + + if (session->userauth_state == libssh2_NB_state_idle) { + /* Zero the whole thing out */ + memset(&session->userauth_packet_requirev_state, 0, sizeof(session->userauth_packet_requirev_state)); - s = data = LIBSSH2_ALLOC(session, data_len); - if (!data) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password request", 0); - return -1; + /* + * 40 = acket_type(1) + username_len(4) + service_len(4) + + * service(14)"ssh-connection" + method_len(4) + method(8)"password" + + * chgpwdbool(1) + password_len(4) */ + session->userauth_data_len = username_len + password_len + 40; + + session->userauth_data0 = ~SSH_MSG_USERAUTH_PASSWD_CHANGEREQ; + + s = session->userauth_data = LIBSSH2_ALLOC(session, session->userauth_data_len); + if (!session->userauth_data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password request", 0); + return -1; + } + + *(s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(s, username_len); s += 4; + memcpy(s, username, username_len); s += username_len; + + libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; + memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; + + libssh2_htonu32(s, sizeof("password") - 1); s += 4; + memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1; + + *s = '\0'; s++; + + libssh2_htonu32(s, password_len); s += 4; + memcpy(s, password, password_len); s += password_len; + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting to login using password authentication"); + + session->userauth_state = libssh2_NB_state_created; } - - *(s++) = SSH_MSG_USERAUTH_REQUEST; - libssh2_htonu32(s, username_len); s += 4; - memcpy(s, username, username_len); s += username_len; - - libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; - memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; - - libssh2_htonu32(s, sizeof("password") - 1); s += 4; - memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1; - - *s = '\0'; s++; - - libssh2_htonu32(s, password_len); s += 4; - memcpy(s, password, password_len); s += password_len; - - _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting to login using password authentication"); - if (libssh2_packet_write(session, data, data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password request", 0); - LIBSSH2_FREE(session, data); - return -1; + + if (session->userauth_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->userauth_data, session->userauth_data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password request", 0); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + session->userauth_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + + session->userauth_state = libssh2_NB_state_sent; } - LIBSSH2_FREE(session, data); password_response: - if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { - return -1; - } - - if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { - _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password authentication successful"); - LIBSSH2_FREE(session, data); - session->state |= LIBSSH2_STATE_AUTHENTICATED; - return 0; - } - - if (data[0] == SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) { - char *newpw = NULL; - int newpw_len = 0; - - _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password change required"); - LIBSSH2_FREE(session, data); - if (passwd_change_cb) { - passwd_change_cb(session, &newpw, &newpw_len, &session->abstract); - if (!newpw) { - libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password expired, and callback failed", 0); - return -1; + + if ((session->userauth_state == libssh2_NB_state_sent) || (session->userauth_state == libssh2_NB_state_sent1) || + (session->userauth_state == libssh2_NB_state_sent2)) { + if (session->userauth_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &session->userauth_data, &session->userauth_data_len, + 0, NULL, 0, &session->userauth_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; } - data_len = username_len + password_len + 44 + newpw_len; /* basic data_len + newpw_len(4) */ - s = data = LIBSSH2_ALLOC(session, data_len); - if (!data) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password-change request", 0); - LIBSSH2_FREE(session, newpw); + else if (rc) { + session->userauth_state = libssh2_NB_state_idle; return -1; } - *(s++) = SSH_MSG_USERAUTH_REQUEST; - libssh2_htonu32(s, username_len); s += 4; - memcpy(s, username, username_len); s += username_len; + if (session->userauth_data[0] == SSH_MSG_USERAUTH_SUCCESS) { + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password authentication successful"); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_state = libssh2_NB_state_idle; + return 0; + } + + session->userauth_newpw = NULL; + session->userauth_newpw_len = 0; + + session->userauth_state = libssh2_NB_state_sent1; + } + + if ((session->userauth_data[0] == SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) || + (session->userauth_data0 == SSH_MSG_USERAUTH_PASSWD_CHANGEREQ)) { + session->userauth_data0 = SSH_MSG_USERAUTH_PASSWD_CHANGEREQ; + + if ((session->userauth_state == libssh2_NB_state_sent1) || (session->userauth_state == libssh2_NB_state_sent2)) { + if (session->userauth_state == libssh2_NB_state_sent1) { + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password change required"); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + } + if (passwd_change_cb) { + if (session->userauth_state == libssh2_NB_state_sent1) { + passwd_change_cb(session, &session->userauth_newpw, &session->userauth_newpw_len, &session->abstract); + if (!session->userauth_newpw) { + libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password expired, and callback failed", 0); + return -1; + } + + /* basic data_len + newpw_len(4) */ + session->userauth_data_len = username_len + password_len + 44 + session->userauth_newpw_len; + + s = session->userauth_data = LIBSSH2_ALLOC(session, session->userauth_data_len); + if (!session->userauth_data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for userauth-password-change request", 0); + LIBSSH2_FREE(session, session->userauth_newpw); + return -1; + } - libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; - memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; + *(s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(s, username_len); s += 4; + memcpy(s, username, username_len); s += username_len; - libssh2_htonu32(s, sizeof("password") - 1); s += 4; - memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1; + libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; + memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; - *s = 0xFF; s++; + libssh2_htonu32(s, sizeof("password") - 1); s += 4; + memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1; - libssh2_htonu32(s, password_len); s += 4; - memcpy(s, password, password_len); s += password_len; + *s = 0xFF; s++; - libssh2_htonu32(s, newpw_len); s += 4; - memcpy(s, newpw, newpw_len); s += newpw_len; + libssh2_htonu32(s, password_len); s += 4; + memcpy(s, password, password_len); s += password_len; - if (libssh2_packet_write(session, data, data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password-change request", 0); - LIBSSH2_FREE(session, data); - LIBSSH2_FREE(session, newpw); + libssh2_htonu32(s, session->userauth_newpw_len); s += 4; + memcpy(s, session->userauth_newpw, session->userauth_newpw_len); + s += session->userauth_newpw_len; + + session->userauth_state = libssh2_NB_state_sent2; + } + + if (session->userauth_state == libssh2_NB_state_sent2) { + rc = libssh2_packet_write(session, session->userauth_data, session->userauth_data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send userauth-password-change request", 0); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + LIBSSH2_FREE(session, session->userauth_newpw); + session->userauth_newpw = NULL; + return -1; + } + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + LIBSSH2_FREE(session, session->userauth_newpw); + session->userauth_newpw = NULL; + + /* + * Ugliest use of goto ever. Blame it on the + * askN => requirev migration. + */ + session->userauth_state = libssh2_NB_state_sent; + goto password_response; + } + } + } else { + libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password Expired, and no callback specified", 0); + session->userauth_state = libssh2_NB_state_idle; return -1; } - LIBSSH2_FREE(session, data); - LIBSSH2_FREE(session, newpw); - - /* Ugliest use of goto ever. Blame it on the askN => requirev migration. */ - goto password_response; - } else { - libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password Expired, and no callback specified", 0); - return -1; } } /* FAILURE */ - LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + session->userauth_state = libssh2_NB_state_idle; return -1; } /* }}} */ @@ -231,9 +345,9 @@ LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const cha /* {{{ libssh2_file_read_publickey * Read a public key from an id_???.pub style file */ -static int libssh2_file_read_publickey(LIBSSH2_SESSION *session, unsigned char **method, unsigned long *method_len, - unsigned char **pubkeydata, unsigned long *pubkeydata_len, - const char *pubkeyfile) +static int +libssh2_file_read_publickey(LIBSSH2_SESSION *session, unsigned char **method, unsigned long *method_len, + unsigned char **pubkeydata, unsigned long *pubkeydata_len, const char *pubkeyfile) { FILE *fd; char c; @@ -318,9 +432,9 @@ static int libssh2_file_read_publickey(LIBSSH2_SESSION *session, unsigned char * /* {{{ libssh2_file_read_privatekey * Read a PEM encoded private key from an id_??? style file */ -static int libssh2_file_read_privatekey(LIBSSH2_SESSION *session, const LIBSSH2_HOSTKEY_METHOD **hostkey_method, void **hostkey_abstract, - const unsigned char *method, int method_len, - const char *privkeyfile, const char *passphrase) +static int +libssh2_file_read_privatekey(LIBSSH2_SESSION *session, const LIBSSH2_HOSTKEY_METHOD **hostkey_method, void **hostkey_abstract, + const unsigned char *method, int method_len, const char *privkeyfile, const char *passphrase) { const LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail = libssh2_hostkey_methods(); @@ -328,8 +442,7 @@ static int libssh2_file_read_privatekey(LIBSSH2_SESSION *session, const LIBSSH *hostkey_method = NULL; *hostkey_abstract = NULL; while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) { - if ((*hostkey_methods_avail)->initPEM && - strncmp((*hostkey_methods_avail)->name, (const char *)method, method_len) == 0) { + if ((*hostkey_methods_avail)->initPEM && strncmp((*hostkey_methods_avail)->name, (const char *)method, method_len) == 0) { *hostkey_method = *hostkey_methods_avail; break; } @@ -340,7 +453,7 @@ static int libssh2_file_read_privatekey(LIBSSH2_SESSION *session, const LIBSSH return -1; } - if ((*hostkey_method)->initPEM(session, privkeyfile, passphrase, hostkey_abstract)) { + if ((*hostkey_method)->initPEM(session, privkeyfile, (unsigned char *)passphrase, hostkey_abstract)) { libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to initialize private key from file", 0); return -1; } @@ -352,136 +465,189 @@ static int libssh2_file_read_privatekey(LIBSSH2_SESSION *session, const LIBSSH /* {{{ libssh2_userauth_hostbased_fromfile_ex * Authenticate using a keypair found in the named files */ -LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, - const char *publickey, const char *privatekey, - const char *passphrase, - const char *hostname, unsigned int hostname_len, - const char *local_username, unsigned int local_username_len) +LIBSSH2_API int +libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, + const char *publickey, const char *privatekey, const char *passphrase, const char *hostname, + unsigned int hostname_len, const char *local_username, unsigned int local_username_len) { const LIBSSH2_HOSTKEY_METHOD *privkeyobj; void *abstract; unsigned char buf[5]; struct iovec datavec[4]; - unsigned char *method, *pubkeydata, *packet, *s, *sig, *data; + unsigned char *pubkeydata, *sig; static const unsigned char reply_codes[3] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; - unsigned long method_len, pubkeydata_len, packet_len, sig_len, data_len; + unsigned long pubkeydata_len, sig_len, data_len; + int rc; - if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) { - return -1; - } + if (session->userauth_state == libssh2_NB_state_idle) { + /* Zero the whole thing out */ + memset(&session->userauth_packet_requirev_state, 0, sizeof(session->userauth_packet_requirev_state)); + + if (libssh2_file_read_publickey(session, &session->userauth_method, &session->userauth_method_len, + &pubkeydata, &pubkeydata_len, publickey)) { + return -1; + } - packet_len = username_len + method_len + hostname_len + local_username_len + pubkeydata_len + 48; - /* packet_type(1) + username_len(4) + servicename_len(4) + service_name(14)"ssh-connection" + - * authmethod_len(4) + authmethod(9)"hostbased" + method_len(4) + pubkeydata_len(4) + - * local_username_len(4) - */ - /* Preallocate space for an overall length, method name again, - * and the signature, which won't be any larger than the size of the publickeydata itself */ - s = packet = LIBSSH2_ALLOC(session, packet_len + 4 + (4 + method_len) + (4 + pubkeydata_len)); - if (!packet) { - LIBSSH2_FREE(session, method); - return -1; - } + /* + * 48 = packet_type(1) + username_len(4) + servicename_len(4) + + * service_name(14)"ssh-connection" + authmethod_len(4) + + * authmethod(9)"hostbased" + method_len(4) + pubkeydata_len(4) + + * local_username_len(4) + */ + session->userauth_packet_len = username_len + session->userauth_method_len + hostname_len + + local_username_len + pubkeydata_len + 48; + + /* + * Preallocate space for an overall length, method name again, + * and the signature, which won't be any larger than the size of + * the publickeydata itself + */ + session->userauth_s = session->userauth_packet = + LIBSSH2_ALLOC(session, session->userauth_packet_len + 4 + (4 + session->userauth_method_len) +(4 + pubkeydata_len)); + if (!session->userauth_packet) { + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + return -1; + } - *(s++) = SSH_MSG_USERAUTH_REQUEST; - libssh2_htonu32(s, username_len); s += 4; - memcpy(s, username, username_len); s += username_len; + *(session->userauth_s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(session->userauth_s, username_len); session->userauth_s += 4; + memcpy(session->userauth_s, username, username_len); session->userauth_s += username_len; - libssh2_htonu32(s, 14); s += 4; - memcpy(s, "ssh-connection", 14); s += 14; + libssh2_htonu32(session->userauth_s, 14); session->userauth_s += 4; + memcpy(session->userauth_s, "ssh-connection", 14); session->userauth_s += 14; - libssh2_htonu32(s, 9); s += 4; - memcpy(s, "hostbased", 9); s += 9; + libssh2_htonu32(session->userauth_s, 9); session->userauth_s += 4; + memcpy(session->userauth_s, "hostbased", 9); session->userauth_s += 9; - libssh2_htonu32(s, method_len); s += 4; - memcpy(s, method, method_len); s += method_len; + libssh2_htonu32(session->userauth_s, session->userauth_method_len); session->userauth_s += 4; + memcpy(session->userauth_s, session->userauth_method, session->userauth_method_len); + session->userauth_s += session->userauth_method_len; - libssh2_htonu32(s, pubkeydata_len); s += 4; - memcpy(s, pubkeydata, pubkeydata_len); s += pubkeydata_len; + libssh2_htonu32(session->userauth_s, pubkeydata_len); session->userauth_s += 4; + memcpy(session->userauth_s, pubkeydata, pubkeydata_len); session->userauth_s += pubkeydata_len; - libssh2_htonu32(s, hostname_len); s += 4; - memcpy(s, hostname, hostname_len); s += hostname_len; + libssh2_htonu32(session->userauth_s, hostname_len); session->userauth_s += 4; + memcpy(session->userauth_s, hostname, hostname_len); session->userauth_s += hostname_len; - libssh2_htonu32(s, local_username_len); s += 4; - memcpy(s, local_username, local_username_len); s += local_username_len; + libssh2_htonu32(session->userauth_s, local_username_len); session->userauth_s += 4; + memcpy(session->userauth_s, local_username, local_username_len); session->userauth_s += local_username_len; - if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, method, method_len, privatekey, passphrase)) { - LIBSSH2_FREE(session, method); - LIBSSH2_FREE(session, packet); - return -1; - } + if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, session->userauth_method, + session->userauth_method_len, privatekey, passphrase)) { + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + return -1; + } - libssh2_htonu32(buf, session->session_id_len); - datavec[0].iov_base = buf; - datavec[0].iov_len = 4; - datavec[1].iov_base = session->session_id; - datavec[1].iov_len = session->session_id_len; - datavec[2].iov_base = packet; - datavec[2].iov_len = packet_len; + libssh2_htonu32(buf, session->session_id_len); + datavec[0].iov_base = buf; + datavec[0].iov_len = 4; + datavec[1].iov_base = session->session_id; + datavec[1].iov_len = session->session_id_len; + datavec[2].iov_base = session->userauth_packet; + datavec[2].iov_len = session->userauth_packet_len; + + if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + return -1; + } - if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { - LIBSSH2_FREE(session, method); - LIBSSH2_FREE(session, packet); if (privkeyobj->dtor) { privkeyobj->dtor(session, &abstract); } - return -1; - } - if (privkeyobj->dtor) { - privkeyobj->dtor(session, &abstract); - } + if (sig_len > pubkeydata_len) { + unsigned char *newpacket; + /* Should *NEVER* happen, but...well.. better safe than sorry */ + newpacket = LIBSSH2_REALLOC(session, session->userauth_packet, session->userauth_packet_len + 4 + + (4 + session->userauth_method_len) + (4 + sig_len)); /* PK sigblob */ + if (!newpacket) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC,"Failed allocating additional space for userauth-hostbased packet", 0); + LIBSSH2_FREE(session, sig); + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + return -1; + } + session->userauth_packet = newpacket; + } - if (sig_len > pubkeydata_len) { - unsigned char *newpacket; - /* Should *NEVER* happen, but...well.. better safe than sorry */ - newpacket = LIBSSH2_REALLOC(session, packet, packet_len + 4 + (4 + method_len) + (4 + sig_len)); /* PK sigblob */ - if (!newpacket) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating additional space for userauth-hostbased packet", 0); - LIBSSH2_FREE(session, sig); - LIBSSH2_FREE(session, packet); - LIBSSH2_FREE(session, method); + session->userauth_s = session->userauth_packet + session->userauth_packet_len; + + libssh2_htonu32(session->userauth_s, 4 + session->userauth_method_len + 4 + sig_len); + session->userauth_s += 4; + + libssh2_htonu32(session->userauth_s, session->userauth_method_len); session->userauth_s += 4; + memcpy(session->userauth_s, session->userauth_method, session->userauth_method_len); + session->userauth_s += session->userauth_method_len; + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + + libssh2_htonu32(session->userauth_s, sig_len); session->userauth_s += 4; + memcpy(session->userauth_s, sig, sig_len); session->userauth_s += sig_len; + LIBSSH2_FREE(session, sig); + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting hostbased authentication"); + + session->userauth_state = libssh2_NB_state_created; + } + + if (session->userauth_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->userauth_packet, session->userauth_s - session->userauth_packet); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-hostbased request", 0); + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + session->userauth_state = libssh2_NB_state_idle; return -1; } - packet = newpacket; + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + + session->userauth_state = libssh2_NB_state_sent; } - s = packet + packet_len; + if (session->userauth_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &session->userauth_data, &data_len, + 0, NULL, 0, &session->userauth_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + session->userauth_state = libssh2_NB_state_idle; + return -1; + } - libssh2_htonu32(s, 4 + method_len + 4 + sig_len); s += 4; - - libssh2_htonu32(s, method_len); s += 4; - memcpy(s, method, method_len); s += method_len; - LIBSSH2_FREE(session, method); - - libssh2_htonu32(s, sig_len); s += 4; - memcpy(s, sig, sig_len); s += sig_len; - LIBSSH2_FREE(session, sig); - - _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting hostbased authentication"); - if (libssh2_packet_write(session, packet, s - packet)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-hostbased request", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); - - if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { - return -1; - } - - if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { - _libssh2_debug(session, LIBSSH2_DBG_AUTH, - "Hostbased authentication successful"); - /* We are us and we've proved it. */ - LIBSSH2_FREE(session, data); - session->state |= LIBSSH2_STATE_AUTHENTICATED; - return 0; + if (session->userauth_data[0] == SSH_MSG_USERAUTH_SUCCESS) { + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Hostbased authentication successful"); + /* We are us and we've proved it. */ + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_state = libssh2_NB_state_idle; + return 0; + } } /* This public key is not allowed for this user on this server */ - LIBSSH2_FREE(session, data); - libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, + "Invalid signature for supplied public key, or bad username/public key combination", 0); + session->userauth_state = libssh2_NB_state_idle; return -1; } /* }}} */ @@ -489,172 +655,264 @@ LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, /* {{{ libssh2_userauth_publickey_fromfile_ex * Authenticate using a keypair found in the named files */ -LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, - const char *publickey, const char *privatekey, - const char *passphrase) +LIBSSH2_API int +libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, + const char *publickey, const char *privatekey, const char *passphrase) { const LIBSSH2_HOSTKEY_METHOD *privkeyobj; void *abstract; unsigned char buf[5]; struct iovec datavec[4]; - unsigned char *method, *pubkeydata, *packet, *s, *b, *sig, *data; + unsigned char *pubkeydata, *sig; unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PK_OK, 0 }; - unsigned long method_len, pubkeydata_len, packet_len, sig_len, data_len; + unsigned long pubkeydata_len, sig_len; + int rc; + + if (session->userauth_state == libssh2_NB_state_idle) { + /* Zero the whole thing out */ + memset(&session->userauth_packet_requirev_state, 0, sizeof(session->userauth_packet_requirev_state)); + + if (libssh2_file_read_publickey(session, &session->userauth_method, &session->userauth_method_len, + &pubkeydata, &pubkeydata_len, publickey)) { + return -1; + } - if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) { - return -1; - } + /* + * 45 = packet_type(1) + username_len(4) + servicename_len(4) + + * service_name(14)"ssh-connection" + authmethod_len(4) + + * authmethod(9)"publickey" + sig_included(1)'\0' + algmethod_len(4) + + * publickey_len(4) + */ + session->userauth_packet_len = username_len + session->userauth_method_len + pubkeydata_len + 45; + + /* + * Preallocate space for an overall length, method name again, and + * the signature, which won't be any larger than the size of the + * publickeydata itself + */ + session->userauth_s = session->userauth_packet = + LIBSSH2_ALLOC(session, session->userauth_packet_len + 4 + (4 + session->userauth_method_len) + (4 + pubkeydata_len)); + if (!session->userauth_packet) { + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + LIBSSH2_FREE(session, pubkeydata); + return -1; + } - packet_len = username_len + method_len + pubkeydata_len + 45; /* packet_type(1) + username_len(4) + servicename_len(4) + - service_name(14)"ssh-connection" + authmethod_len(4) + - authmethod(9)"publickey" + sig_included(1)'\0' + - algmethod_len(4) + publickey_len(4) */ - /* Preallocate space for an overall length, method name again, - * and the signature, which won't be any larger than the size of the publickeydata itself */ - s = packet = LIBSSH2_ALLOC(session, packet_len + 4 + (4 + method_len) + (4 + pubkeydata_len)); - if (!packet) { - LIBSSH2_FREE(session, method); + *(session->userauth_s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(session->userauth_s, username_len); session->userauth_s += 4; + memcpy(session->userauth_s, username, username_len); session->userauth_s += username_len; + + libssh2_htonu32(session->userauth_s, 14); session->userauth_s += 4; + memcpy(session->userauth_s, "ssh-connection", 14); session->userauth_s += 14; + + libssh2_htonu32(session->userauth_s, 9); session->userauth_s += 4; + memcpy(session->userauth_s, "publickey", 9); session->userauth_s += 9; + + session->userauth_b = session->userauth_s; + /* Not sending signature with *this* packet */ + *(session->userauth_s++) = 0; + + libssh2_htonu32(session->userauth_s, session->userauth_method_len); session->userauth_s += 4; + memcpy(session->userauth_s, session->userauth_method, session->userauth_method_len); + session->userauth_s += session->userauth_method_len; + + libssh2_htonu32(session->userauth_s, pubkeydata_len); session->userauth_s += 4; + memcpy(session->userauth_s, pubkeydata, pubkeydata_len); session->userauth_s += pubkeydata_len; LIBSSH2_FREE(session, pubkeydata); - return -1; + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication"); + + session->userauth_state = libssh2_NB_state_created; + } + + if (session->userauth_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->userauth_packet, session->userauth_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + session->userauth_state = libssh2_NB_state_idle; + return -1; + } + + session->userauth_state = libssh2_NB_state_sent; } - *(s++) = SSH_MSG_USERAUTH_REQUEST; - libssh2_htonu32(s, username_len); s += 4; - memcpy(s, username, username_len); s += username_len; + if (session->userauth_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &session->userauth_data, &session->userauth_data_len, 0, NULL, 0, + &session->userauth_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + session->userauth_state = libssh2_NB_state_idle; + return -1; + } - libssh2_htonu32(s, 14); s += 4; - memcpy(s, "ssh-connection", 14); s += 14; + if (session->userauth_data[0] == SSH_MSG_USERAUTH_SUCCESS) { + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Pubkey authentication prematurely successful"); + /* + * God help any SSH server that allows an UNVERIFIED + * public key to validate the user + */ + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_state = libssh2_NB_state_idle; + return 0; + } - libssh2_htonu32(s, 9); s += 4; - memcpy(s, "publickey", 9); s += 9; + if (session->userauth_data[0] == SSH_MSG_USERAUTH_FAILURE) { + /* This public key is not allowed for this user on this server */ + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED, "Username/PublicKey combination invalid", 0); + session->userauth_state = libssh2_NB_state_idle; + return -1; + } - b = s; - *(s++) = 0; /* Not sending signature with *this* packet */ + /* Semi-Success! */ + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; - libssh2_htonu32(s, method_len); s += 4; - memcpy(s, method, method_len); s += method_len; + if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, session->userauth_method, + session->userauth_method_len, privatekey, passphrase)) { + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + session->userauth_state = libssh2_NB_state_idle; + return -1; + } - libssh2_htonu32(s, pubkeydata_len); s += 4; - memcpy(s, pubkeydata, pubkeydata_len); s += pubkeydata_len; - LIBSSH2_FREE(session, pubkeydata); + *session->userauth_b = 0xFF; - _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication"); - if (libssh2_packet_write(session, packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); - LIBSSH2_FREE(session, packet); - LIBSSH2_FREE(session, method); - return -1; - } + libssh2_htonu32(buf, session->session_id_len); + datavec[0].iov_base = buf; + datavec[0].iov_len = 4; + datavec[1].iov_base = session->session_id; + datavec[1].iov_len = session->session_id_len; + datavec[2].iov_base = session->userauth_packet; + datavec[2].iov_len = session->userauth_packet_len; - if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { - LIBSSH2_FREE(session, packet); - LIBSSH2_FREE(session, method); - return -1; - } + if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + session->userauth_state = libssh2_NB_state_idle; + return -1; + } - if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { - _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Pubkey authentication prematurely successful"); - /* God help any SSH server that allows an UNVERIFIED public key to validate the user */ - LIBSSH2_FREE(session, data); - LIBSSH2_FREE(session, packet); - LIBSSH2_FREE(session, method); - session->state |= LIBSSH2_STATE_AUTHENTICATED; - return 0; - } - - if (data[0] == SSH_MSG_USERAUTH_FAILURE) { - /* This public key is not allowed for this user on this server */ - LIBSSH2_FREE(session, data); - LIBSSH2_FREE(session, packet); - LIBSSH2_FREE(session, method); - libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED, "Username/PublicKey combination invalid", 0); - return -1; - } - - /* Semi-Success! */ - LIBSSH2_FREE(session, data); - - if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, method, method_len, privatekey, passphrase)) { - LIBSSH2_FREE(session, method); - LIBSSH2_FREE(session, packet); - return -1; - } - - *b = 0xFF; - - libssh2_htonu32(buf, session->session_id_len); - datavec[0].iov_base = buf; - datavec[0].iov_len = 4; - datavec[1].iov_base = session->session_id; - datavec[1].iov_len = session->session_id_len; - datavec[2].iov_base = packet; - datavec[2].iov_len = packet_len; - - if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { - LIBSSH2_FREE(session, method); - LIBSSH2_FREE(session, packet); if (privkeyobj->dtor) { privkeyobj->dtor(session, &abstract); } - return -1; - } - if (privkeyobj->dtor) { - privkeyobj->dtor(session, &abstract); - } + if (sig_len > pubkeydata_len) { + unsigned char *newpacket; + /* Should *NEVER* happen, but...well.. better safe than sorry */ + newpacket = LIBSSH2_REALLOC(session, session->userauth_packet, session->userauth_packet_len + 4 + + (4 + session->userauth_method_len) + (4 + sig_len)); /* PK sigblob */ + if (!newpacket) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating additional space for userauth-publickey packet", 0); + LIBSSH2_FREE(session, sig); + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + session->userauth_state = libssh2_NB_state_idle; + return -1; + } + session->userauth_packet = newpacket; + } - if (sig_len > pubkeydata_len) { - unsigned char *newpacket; - /* Should *NEVER* happen, but...well.. better safe than sorry */ - newpacket = LIBSSH2_REALLOC(session, packet, packet_len + 4 + (4 + method_len) + (4 + sig_len)); /* PK sigblob */ - if (!newpacket) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating additional space for userauth-publickey packet", 0); - LIBSSH2_FREE(session, sig); - LIBSSH2_FREE(session, packet); - LIBSSH2_FREE(session, method); + session->userauth_s = session->userauth_packet + session->userauth_packet_len; + + libssh2_htonu32(session->userauth_s, 4 + session->userauth_method_len + 4 + sig_len); session->userauth_s += 4; + + libssh2_htonu32(session->userauth_s, session->userauth_method_len); session->userauth_s += 4; + memcpy(session->userauth_s, session->userauth_method, session->userauth_method_len); + session->userauth_s += session->userauth_method_len; + LIBSSH2_FREE(session, session->userauth_method); + session->userauth_method = NULL; + + libssh2_htonu32(session->userauth_s, sig_len); session->userauth_s += 4; + memcpy(session->userauth_s, sig, sig_len); session->userauth_s += sig_len; + LIBSSH2_FREE(session, sig); + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication -- phase 2"); + + session->userauth_state = libssh2_NB_state_sent1; + } + + if (session->userauth_state == libssh2_NB_state_sent1) { + rc = libssh2_packet_write(session, session->userauth_packet, session->userauth_s - session->userauth_packet); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + session->userauth_state = libssh2_NB_state_idle; return -1; } - packet = newpacket; + LIBSSH2_FREE(session, session->userauth_packet); + session->userauth_packet = NULL; + + session->userauth_state = libssh2_NB_state_sent2; } - - s = packet + packet_len; - - libssh2_htonu32(s, 4 + method_len + 4 + sig_len); s += 4; - - libssh2_htonu32(s, method_len); s += 4; - memcpy(s, method, method_len); s += method_len; - LIBSSH2_FREE(session, method); - - libssh2_htonu32(s, sig_len); s += 4; - memcpy(s, sig, sig_len); s += sig_len; - LIBSSH2_FREE(session, sig); - - _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication -- phase 2"); - if (libssh2_packet_write(session, packet, s - packet)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); - + /* PK_OK is no longer valid */ reply_codes[2] = 0; - - if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { + + rc = libssh2_packet_requirev_ex(session, reply_codes, &session->userauth_data, &session->userauth_data_len, 0, NULL, 0, + &session->userauth_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + session->userauth_state = libssh2_NB_state_idle; return -1; } - if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { + if (session->userauth_data[0] == SSH_MSG_USERAUTH_SUCCESS) { _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Publickey authentication successful"); /* We are us and we've proved it. */ - LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_state = libssh2_NB_state_idle; return 0; } /* This public key is not allowed for this user on this server */ - LIBSSH2_FREE(session, data); - libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, + "Invalid signature for supplied public key, or bad username/public key combination", 0); + session->userauth_state = libssh2_NB_state_idle; return -1; } /* }}} */ @@ -662,194 +920,263 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, /* {{{ libssh2_userauth_keyboard_interactive * Authenticate using a challenge-response authentication */ -LIBSSH2_API int libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, - LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))) +LIBSSH2_API int +libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, + LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))) { - unsigned char *s, *data; /* packet */ - unsigned long packet_len; - - packet_len = 1 /* byte SSH_MSG_USERAUTH_REQUEST */ - + 4 + username_len /* string user name (ISO-10646 UTF-8, as defined in [RFC-3629]) */ - + 4 + 14 /* string service name (US-ASCII) */ - + 4 + 20 /* string "keyboard-interactive" (US-ASCII) */ - + 4 + 0 /* string language tag (as defined in [RFC-3066]) */ - + 4 + 0 /* string submethods (ISO-10646 UTF-8) */ - ; - - if (!(data = s = LIBSSH2_ALLOC(session, packet_len))) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive authentication", 0); - return -1; - } - - *s++ = SSH_MSG_USERAUTH_REQUEST; - - /* user name */ - libssh2_htonu32(s, username_len); s += 4; - memcpy(s, username, username_len); s += username_len; - - /* service name */ - libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; - memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; - - /* "keyboard-interactive" */ - libssh2_htonu32(s, sizeof("keyboard-interactive") - 1); s += 4; - memcpy(s, "keyboard-interactive", sizeof("keyboard-interactive") - 1); s += sizeof("keyboard-interactive") - 1; - - /* language tag */ - libssh2_htonu32(s, 0); s += 4; - - /* submethods */ - libssh2_htonu32(s, 0); s += 4; - - _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting keyboard-interactive authentication"); - if (libssh2_packet_write(session, data, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send keyboard-interactive request", 0); - LIBSSH2_FREE(session, data); - return -1; - } - LIBSSH2_FREE(session, data); - - for (;;) { - static const unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_INFO_REQUEST, 0 }; - unsigned int auth_name_len; - char* auth_name = NULL; - unsigned auth_instruction_len; - char* auth_instruction = NULL; - unsigned int language_tag_len; - unsigned long data_len; - unsigned int num_prompts = 0; - unsigned int i; - int auth_failure = 1; - LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts = NULL; - LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses = NULL; - - if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { - return -1; - } - - if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { - _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Keyboard-interactive authentication successful"); - LIBSSH2_FREE(session, data); - session->state |= LIBSSH2_STATE_AUTHENTICATED; - return 0; - } - - if (data[0] == SSH_MSG_USERAUTH_FAILURE) { - LIBSSH2_FREE(session, data); - return -1; - } - - /* server requested PAM-like conversation */ - - s = data + 1; - - /* string name (ISO-10646 UTF-8) */ - auth_name_len = libssh2_ntohu32(s); s += 4; - if (!(auth_name = LIBSSH2_ALLOC(session, auth_name_len))) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive 'name' request field", 0); - goto cleanup; - } - memcpy(auth_name, s, auth_name_len); s += auth_name_len; - - /* string instruction (ISO-10646 UTF-8) */ - auth_instruction_len = libssh2_ntohu32(s); s += 4; - if (!(auth_instruction = LIBSSH2_ALLOC(session, auth_instruction_len))) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive 'instruction' request field", 0); - goto cleanup; - } - memcpy(auth_instruction, s, auth_instruction_len); s += auth_instruction_len; - - /* string language tag (as defined in [RFC-3066]) */ - language_tag_len = libssh2_ntohu32(s); s += 4; - /* ignoring this field as deprecated */ s += language_tag_len; - - /* int num-prompts */ - num_prompts = libssh2_ntohu32(s); s += 4; - - prompts = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * num_prompts); - if (!prompts) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive prompts array", 0); - goto cleanup; - } - memset(prompts, 0, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * num_prompts); - - responses = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * num_prompts); - if (!responses) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive responses array", 0); - goto cleanup; - } - memset(responses, 0, sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * num_prompts); - - for(i = 0; i != num_prompts; ++i) { - /* string prompt[1] (ISO-10646 UTF-8) */ - prompts[i].length = libssh2_ntohu32(s); s += 4; - if (!(prompts[i].text = LIBSSH2_ALLOC(session, prompts[i].length))) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive prompt message", 0); - goto cleanup; - } - memcpy(prompts[i].text, s, prompts[i].length); s += prompts[i].length; - - /* boolean echo[1] */ - prompts[i].echo = *s++; - } - - response_callback(auth_name, auth_name_len, auth_instruction, auth_instruction_len, num_prompts, prompts, responses, &session->abstract); - - _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Keyboard-interactive response callback function invoked"); - - packet_len = 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */ - + 4 /* int num-responses */ + unsigned char *s; + int rc; + + static const unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, + SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_INFO_REQUEST, 0 }; + unsigned int language_tag_len; + unsigned int i; + + if (session->userauth_state == libssh2_NB_state_idle) { + session->userauth_auth_name = NULL; + session->userauth_auth_instruction = NULL; + session->userauth_num_prompts = 0; + session->userauth_auth_failure = 1; + session->userauth_prompts = NULL; + session->userauth_responses = NULL; + + /* Zero the whole thing out */ + memset(&session->userauth_packet_requirev_state, 0, sizeof(session->userauth_packet_requirev_state)); + + session->userauth_packet_len = + 1 /* byte SSH_MSG_USERAUTH_REQUEST */ + + 4 + username_len /* string user name (ISO-10646 UTF-8, as defined in [RFC-3629]) */ + + 4 + 14 /* string service name (US-ASCII) */ + + 4 + 20 /* string "keyboard-interactive" (US-ASCII) */ + + 4 + 0 /* string language tag (as defined in [RFC-3066]) */ + + 4 + 0 /* string submethods (ISO-10646 UTF-8) */ ; - for (i = 0; i != num_prompts; ++i) { - packet_len += 4 + responses[i].length; /* string response[1] (ISO-10646 UTF-8) */ + session->userauth_data = s = LIBSSH2_ALLOC(session, session->userauth_packet_len); + if (s) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive authentication", 0); + return -1; } - if (!(data = s = LIBSSH2_ALLOC(session, packet_len))) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive response packet", 0); - goto cleanup; + *s++ = SSH_MSG_USERAUTH_REQUEST; + + /* user name */ + libssh2_htonu32(s, username_len); s += 4; + memcpy(s, username, username_len); s += username_len; + + /* service name */ + libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; + memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; + + /* "keyboard-interactive" */ + libssh2_htonu32(s, sizeof("keyboard-interactive") - 1); s += 4; + memcpy(s, "keyboard-interactive", sizeof("keyboard-interactive") - 1); s += sizeof("keyboard-interactive") - 1; + + /* language tag */ + libssh2_htonu32(s, 0); s += 4; + + /* submethods */ + libssh2_htonu32(s, 0); s += 4; + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting keyboard-interactive authentication"); + + session->userauth_state = libssh2_NB_state_created; + } + + if (session->userauth_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->userauth_data, session->userauth_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send keyboard-interactive request", 0); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + session->userauth_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + + session->userauth_state = libssh2_NB_state_sent; + } + + for (;;) { + if (session->userauth_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &session->userauth_data, &session->userauth_data_len, + 0, NULL, 0, &session->userauth_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + session->userauth_state = libssh2_NB_state_idle; + return -1; + } + + if (session->userauth_data[0] == SSH_MSG_USERAUTH_SUCCESS) { + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Keyboard-interactive authentication successful"); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_state = libssh2_NB_state_idle; + return 0; + } + + if (session->userauth_data[0] == SSH_MSG_USERAUTH_FAILURE) { + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; + session->userauth_state = libssh2_NB_state_idle; + return -1; + } + + /* server requested PAM-like conversation */ + + s = session->userauth_data + 1; + + /* string name (ISO-10646 UTF-8) */ + session->userauth_auth_name_len = libssh2_ntohu32(s); s += 4; + session->userauth_auth_name = LIBSSH2_ALLOC(session, session->userauth_auth_name_len); + if (!session->userauth_auth_name) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive 'name' request field", 0); + goto cleanup; + } + memcpy(session->userauth_auth_name, s, session->userauth_auth_name_len); s += session->userauth_auth_name_len; + + /* string instruction (ISO-10646 UTF-8) */ + session->userauth_auth_instruction_len = libssh2_ntohu32(s); s += 4; + session->userauth_auth_instruction = LIBSSH2_ALLOC(session, session->userauth_auth_instruction_len); + if (!session->userauth_auth_instruction) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive 'instruction' request field", 0); + goto cleanup; + } + memcpy(session->userauth_auth_instruction, s, session->userauth_auth_instruction_len); + s += session->userauth_auth_instruction_len; + + /* string language tag (as defined in [RFC-3066]) */ + language_tag_len = libssh2_ntohu32(s); s += 4; + /* ignoring this field as deprecated */ + s += language_tag_len; + + /* int num-prompts */ + session->userauth_num_prompts = libssh2_ntohu32(s); s += 4; + + session->userauth_prompts = + LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_num_prompts); + if (!session->userauth_prompts) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive prompts array", 0); + goto cleanup; + } + memset(session->userauth_prompts, 0, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_num_prompts); + + session->userauth_responses = + LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * session->userauth_num_prompts); + if (!session->userauth_responses) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive responses array", 0); + goto cleanup; + } + memset(session->userauth_responses, 0, sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * session->userauth_num_prompts); + + for(i = 0; i != session->userauth_num_prompts; ++i) { + /* string prompt[1] (ISO-10646 UTF-8) */ + session->userauth_prompts[i].length = libssh2_ntohu32(s); s += 4; + session->userauth_prompts[i].text = LIBSSH2_ALLOC(session, session->userauth_prompts[i].length); + if (!session->userauth_prompts[i].text) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive prompt message", 0); + goto cleanup; + } + memcpy(session->userauth_prompts[i].text, s, session->userauth_prompts[i].length); + s += session->userauth_prompts[i].length; + + /* boolean echo[1] */ + session->userauth_prompts[i].echo = *s++; + } + + response_callback(session->userauth_auth_name, session->userauth_auth_name_len, session->userauth_auth_instruction, + session->userauth_auth_instruction_len, session->userauth_num_prompts, session->userauth_prompts, + session->userauth_responses, &session->abstract); + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Keyboard-interactive response callback function invoked"); + + session->userauth_packet_len = + 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */ + + 4 /* int num-responses */ + ; + + for (i = 0; i != session->userauth_num_prompts; ++i) { + /* string response[1] (ISO-10646 UTF-8) */ + session->userauth_packet_len += 4 + session->userauth_responses[i].length; + } + + session->userauth_data = s = LIBSSH2_ALLOC(session, session->userauth_packet_len); + if (!s) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive response packet", 0); + goto cleanup; + } + + *s = SSH_MSG_USERAUTH_INFO_RESPONSE; s++; + libssh2_htonu32(s, session->userauth_num_prompts); s += 4; + + for (i = 0; i != session->userauth_num_prompts; ++i) { + libssh2_htonu32(s, session->userauth_responses[i].length); s += 4; + memcpy(s, session->userauth_responses[i].text, session->userauth_responses[i].length); + s += session->userauth_responses[i].length; + } + + session->userauth_state = libssh2_NB_state_sent1; } - *s = SSH_MSG_USERAUTH_INFO_RESPONSE; s++; - libssh2_htonu32(s, num_prompts); s += 4; + if (session->userauth_state == libssh2_NB_state_sent1) { + rc = libssh2_packet_write(session, session->userauth_data, session->userauth_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-keyboard-interactive request", 0); + goto cleanup; + } - for (i = 0; i != num_prompts; ++i) { - libssh2_htonu32(s, responses[i].length); s += 4; - memcpy(s, responses[i].text, responses[i].length); s += responses[i].length; + session->userauth_auth_failure = 0; } - if (libssh2_packet_write(session, data, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-keyboard-interactive request", 0); - goto cleanup; - } - - auth_failure = 0; - cleanup: - /* It's safe to clean all the data here, because unallocated pointers + /* + * It's safe to clean all the data here, because unallocated pointers * are filled by zeroes */ - LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, session->userauth_data); + session->userauth_data = NULL; - if (prompts) { - for (i = 0; i != num_prompts; ++i) { - LIBSSH2_FREE(session, prompts[i].text); + if (session->userauth_prompts) { + for (i = 0; i != session->userauth_num_prompts; ++i) { + LIBSSH2_FREE(session, session->userauth_prompts[i].text); + session->userauth_prompts[i].text = NULL; } } - if (responses) { - for (i = 0; i != num_prompts; ++i) { - LIBSSH2_FREE(session, responses[i].text); + if (session->userauth_responses) { + for (i = 0; i != session->userauth_num_prompts; ++i) { + LIBSSH2_FREE(session, session->userauth_responses[i].text); + session->userauth_responses[i].text = NULL; } } - LIBSSH2_FREE(session, prompts); - LIBSSH2_FREE(session, responses); + LIBSSH2_FREE(session, session->userauth_prompts); + session->userauth_prompts = NULL; + LIBSSH2_FREE(session, session->userauth_responses); + session->userauth_responses = NULL; - if (auth_failure) { + if (session->userauth_auth_failure) { + session->userauth_state = libssh2_NB_state_idle; return -1; } + + session->userauth_state = libssh2_NB_state_sent; } } /* }}} */