From 0d331aade18929deca68e2efc8add96c431c676e Mon Sep 17 00:00:00 2001 From: Simon Josefsson Date: Thu, 25 Feb 2010 15:58:52 +0100 Subject: [PATCH] Add keep-alive support. --- Makefile.inc | 2 +- include/libssh2.h | 30 ++++++++++++++ src/keepalive.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++ src/libssh2_priv.h | 5 +++ src/session.c | 13 ++++++- 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 src/keepalive.c diff --git a/Makefile.inc b/Makefile.inc index 47a6b7a5..6f1654b8 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -1,5 +1,5 @@ CSOURCES = channel.c comp.c crypt.c hostkey.c kex.c mac.c misc.c \ packet.c publickey.c scp.c session.c sftp.c userauth.c transport.c \ - version.c knownhost.c agent.c openssl.c libgcrypt.c pem.c + version.c knownhost.c agent.c openssl.c libgcrypt.c pem.c keepalive.c HHEADERS = libssh2_priv.h openssl.h libgcrypt.h transport.h channel.h comp.h mac.h misc.h diff --git a/include/libssh2.h b/include/libssh2.h index 5c24d26d..000118a3 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -1,5 +1,6 @@ /* Copyright (c) 2004-2009, Sara Golemon * Copyright (c) 2009 by Daniel Stenberg + * Copyright (c) 2010 Simon Josefsson * All rights reserved. * * Redistribution and use in source and binary forms, @@ -973,6 +974,35 @@ libssh2_agent_disconnect(LIBSSH2_AGENT *agent); LIBSSH2_API void libssh2_agent_free(LIBSSH2_AGENT *agent); + +/* + * libssh2_keepalive_config() + * + * Set how often keepalive messages should be sent. WANT_REPLY + * indicates whether the keepalive messages should request a response + * from the server. INTERVAL is number of seconds that can pass + * without any I/O, use 0 (the default) to disable keepalives. To + * avoid some busy-loop corner-cases, if you specify an interval of 1 + * it will be treated as 2. + * + * Note that non-blocking applications are responsible for sending the + * keepalive messages using libssh2_keepalive_send(). + */ +LIBSSH2_API void libssh2_keepalive_config (LIBSSH2_SESSION *session, + int want_reply, + unsigned interval); + +/* + * libssh2_keepalive_send() + * + * Send a keepalive message if needed. SECONDS_TO_NEXT indicates how + * many seconds you can sleep after this call before you need to call + * it again. Returns 0 on success, or LIBSSH2_ERROR_SOCKET_SEND on + * I/O errors. + */ +LIBSSH2_API int libssh2_keepalive_send (LIBSSH2_SESSION *session, + int *seconds_to_next); + /* NOTE NOTE NOTE libssh2_trace() has no function in builds that aren't built with debug enabled diff --git a/src/keepalive.c b/src/keepalive.c new file mode 100644 index 00000000..253bab3a --- /dev/null +++ b/src/keepalive.c @@ -0,0 +1,97 @@ +/* Copyright (C) 2010 Simon Josefsson + * Author: Simon Josefsson + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + */ + +#include "libssh2_priv.h" +#include "transport.h" /* _libssh2_transport_write */ + +/* Keep-alive stuff. */ + +LIBSSH2_API void +libssh2_keepalive_config (LIBSSH2_SESSION *session, + int want_reply, + unsigned interval) +{ + if (interval == 1) + session->keepalive_interval = 2; + else + session->keepalive_interval = interval; + session->keepalive_want_reply = want_reply ? 1 : 0; +} + +LIBSSH2_API int +libssh2_keepalive_send (LIBSSH2_SESSION *session, + int *seconds_to_next) +{ + time_t now; + + if (!session->keepalive_interval) { + if (seconds_to_next) + *seconds_to_next = 0; + return 0; + } + + now = time (NULL); + + if (session->keepalive_last_sent + session->keepalive_interval <= now) { +/* Format is "SSH_MSG_GLOBAL_REQUEST || 4-byte len || str || want-reply". */ + unsigned char keepalive_data[] + = "\x50\x00\x00\x00\x15keepalive@libssh2.orgW"; + size_t len = sizeof (keepalive_data) - 1; + int rc; + + keepalive_data[len - 1] = session->keepalive_want_reply; + + rc = _libssh2_transport_write(session, keepalive_data, len); +/* Silently ignore PACKET_EAGAIN here: if the write buffer is + already full, sending another keepalive is not useful. */ + if (rc && rc != PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send keepalive message", 0); + return rc; + } + + session->keepalive_last_sent = now; + if (seconds_to_next) + *seconds_to_next = session->keepalive_interval; + } else if (seconds_to_next) { + *seconds_to_next = session->keepalive_last_sent + + session->keepalive_interval - now; + } + + return 0; +} diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index 89fa1044..86aeed80 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -908,6 +908,11 @@ struct _LIBSSH2_SESSION char *scpSend_err_msg; long scpSend_err_len; LIBSSH2_CHANNEL *scpSend_channel; + + /* Keepalive variables used by keepalive.c. */ + int keepalive_interval; + int keepalive_want_reply; + time_t keepalive_last_sent; }; /* session.state bits */ diff --git a/src/session.c b/src/session.c index be623f25..f32924bd 100644 --- a/src/session.c +++ b/src/session.c @@ -1,5 +1,6 @@ /* Copyright (c) 2004-2007 Sara Golemon * Copyright (c) 2009 by Daniel Stenberg + * Copyright (c) 2010 Simon Josefsson * All rights reserved. * * Redistribution and use in source and binary forms, @@ -521,6 +522,15 @@ int _libssh2_wait_socket(LIBSSH2_SESSION *session) fd_set *readfd = NULL; int dir; int rc; + struct timeval tv; + int seconds_to_next; + + rc = libssh2_keepalive_send (session, &seconds_to_next); + if (rc < 0) + return rc; + + tv.tv_sec = seconds_to_next; + tv.tv_usec = 0; FD_ZERO(&fd); FD_SET(session->socket_fd, &fd); @@ -536,7 +546,8 @@ int _libssh2_wait_socket(LIBSSH2_SESSION *session) /* Note that this COULD be made to use a timeout that perhaps could be customizable by the app or something... */ - rc = select(session->socket_fd + 1, readfd, writefd, NULL, NULL); + rc = select(session->socket_fd + 1, readfd, writefd, NULL, + seconds_to_next ? &tv : NULL); if(rc <= 0) { /* timeout (or error), bail out with a timeout error */