From 387cc5048a2d24203e5ecc84ae6f76b7a701c847 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 18 Mar 2014 13:27:18 +0100 Subject: [PATCH] irc: fix truncated read on socket with SSL (bug #41558) If there are still data on socket with SSL (if gnutls_record_check_pending(session) returns > 0), then call gnutls_record_recv() again to read all available data. The problem was that some data remained in the gnutls buffers and the recv callback was not called any more (nothing available on raw socket). --- ChangeLog.asciidoc | 1 + src/plugins/irc/irc-server.c | 114 +++++++++++++++++++++-------------- 2 files changed, 69 insertions(+), 46 deletions(-) diff --git a/ChangeLog.asciidoc b/ChangeLog.asciidoc index 658685ba8..cfb1107ad 100644 --- a/ChangeLog.asciidoc +++ b/ChangeLog.asciidoc @@ -70,6 +70,7 @@ http://weechat.org/files/releasenotes/ReleaseNotes-devel.html[release notes] * alias: change default command for alias /beep to "/print -beep" * exec: add exec plugin: new command /exec and file exec.conf * guile: fix module used after unload of a script +* irc: fix truncated read on socket with SSL (bug #41558) * irc: add "#" before any channel in /join, even /join 0 (closes #20) * irc: display output of CAP LIST in server buffer * irc: fix colors in message with CTCP reply sent to another user diff --git a/src/plugins/irc/irc-server.c b/src/plugins/irc/irc-server.c index 44605dd4e..eac3ba5a7 100644 --- a/src/plugins/irc/irc-server.c +++ b/src/plugins/irc/irc-server.c @@ -2602,72 +2602,94 @@ irc_server_recv_cb (void *data, int fd) { struct t_irc_server *server; static char buffer[4096 + 2]; - int num_read; + int num_read, msgq_flush, end_recv; /* make C compiler happy */ (void) fd; server = (struct t_irc_server *)data; - if (!server) return WEECHAT_RC_ERROR; -#ifdef HAVE_GNUTLS - if (server->ssl_connected) - num_read = gnutls_record_recv (server->gnutls_sess, buffer, - sizeof (buffer) - 2); - else -#endif - num_read = recv (server->sock, buffer, sizeof (buffer) - 2, 0); + msgq_flush = 0; + end_recv = 0; - if (num_read > 0) - { - buffer[num_read] = '\0'; - irc_server_msgq_add_buffer (server, buffer); - irc_server_msgq_flush (); - } - else + while (!end_recv) { + end_recv = 1; + #ifdef HAVE_GNUTLS if (server->ssl_connected) - { - if ((num_read == 0) - || ((num_read != GNUTLS_E_AGAIN) && (num_read != GNUTLS_E_INTERRUPTED))) - { - weechat_printf (server->buffer, - _("%s%s: reading data on socket: error %d %s"), - weechat_prefix ("error"), IRC_PLUGIN_NAME, - num_read, - (num_read == 0) ? _("(connection closed by peer)") : - gnutls_strerror (num_read)); - weechat_printf (server->buffer, - _("%s%s: disconnecting from server..."), - weechat_prefix ("network"), - IRC_PLUGIN_NAME); - irc_server_disconnect (server, !server->is_connected, 1); - } - } + num_read = gnutls_record_recv (server->gnutls_sess, buffer, + sizeof (buffer) - 2); else #endif + num_read = recv (server->sock, buffer, sizeof (buffer) - 2, 0); + + if (num_read > 0) { - if ((num_read == 0) - || ((errno != EAGAIN) && (errno != EWOULDBLOCK))) + buffer[num_read] = '\0'; + irc_server_msgq_add_buffer (server, buffer); + msgq_flush = 1; /* the flush will be done after the loop */ +#ifdef HAVE_GNUTLS + if (server->ssl_connected + && (gnutls_record_check_pending (server->gnutls_sess) > 0)) { - weechat_printf (server->buffer, - _("%s%s: reading data on socket: error %d %s"), - weechat_prefix ("error"), IRC_PLUGIN_NAME, - errno, - (num_read == 0) ? _("(connection closed by peer)") : - strerror (errno)); - weechat_printf (server->buffer, - _("%s%s: disconnecting from server..."), - weechat_prefix ("network"), - IRC_PLUGIN_NAME); - irc_server_disconnect (server, !server->is_connected, 1); + /* + * if there are unread data in the gnutls buffers, + * go on with recv + */ + end_recv = 0; + } +#endif + } + else + { +#ifdef HAVE_GNUTLS + if (server->ssl_connected) + { + if ((num_read == 0) + || ((num_read != GNUTLS_E_AGAIN) + && (num_read != GNUTLS_E_INTERRUPTED))) + { + weechat_printf (server->buffer, + _("%s%s: reading data on socket: error %d %s"), + weechat_prefix ("error"), IRC_PLUGIN_NAME, + num_read, + (num_read == 0) ? _("(connection closed by peer)") : + gnutls_strerror (num_read)); + weechat_printf (server->buffer, + _("%s%s: disconnecting from server..."), + weechat_prefix ("network"), + IRC_PLUGIN_NAME); + irc_server_disconnect (server, !server->is_connected, 1); + } + } + else +#endif + { + if ((num_read == 0) + || ((errno != EAGAIN) && (errno != EWOULDBLOCK))) + { + weechat_printf (server->buffer, + _("%s%s: reading data on socket: error %d %s"), + weechat_prefix ("error"), IRC_PLUGIN_NAME, + errno, + (num_read == 0) ? _("(connection closed by peer)") : + strerror (errno)); + weechat_printf (server->buffer, + _("%s%s: disconnecting from server..."), + weechat_prefix ("network"), + IRC_PLUGIN_NAME); + irc_server_disconnect (server, !server->is_connected, 1); + } } } } + if (msgq_flush) + irc_server_msgq_flush (); + return WEECHAT_RC_OK; }