From 6450f55f0c18d7a4b2fac829d9eff5d567348d19 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 16 Feb 2018 06:30:29 +0100 Subject: add upgrade_process.c --- src/lib/Makefile.am | 1 + src/lib/connection_add.c | 3 +- src/lib/daemon_epoll.c | 4 +- src/lib/daemon_ip_limit.c | 6 + src/lib/daemon_poll.c | 5 +- src/lib/daemon_run.c | 19 +-- src/lib/daemon_select.c | 5 +- src/lib/upgrade_process.c | 388 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib/upgrade_process.h | 44 ++++++ 9 files changed, 460 insertions(+), 15 deletions(-) create mode 100644 src/lib/upgrade_process.c create mode 100644 src/lib/upgrade_process.h (limited to 'src/lib') diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index b53e00b4..1667978a 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -100,6 +100,7 @@ libmicrohttpd_la_SOURCES = \ request_info.c \ request_resume.c \ sysfdsetsize.c sysfdsetsize.h \ + upgrade_process.c upgrade_process.h \ panic.c \ version.c diff --git a/src/lib/connection_add.c b/src/lib/connection_add.c index eb5984e3..0aebe501 100644 --- a/src/lib/connection_add.c +++ b/src/lib/connection_add.c @@ -23,6 +23,8 @@ */ #include "internal.h" #include "connection_add.h" +#include "connection_close.h" +#include "connection_finish_forward.h" #include "connection_update_last_activity.h" #include "daemon_ip_limit.h" #include "daemon_select.h" @@ -42,7 +44,6 @@ static void thread_main_connection_upgrade (struct MHD_Connection *con) { #ifdef HTTPS_SUPPORT - struct MHD_UpgradeResponseHandle *urh = con->request.urh; struct MHD_Daemon *daemon = con->daemon; /* Here, we need to bi-directionally forward diff --git a/src/lib/daemon_epoll.c b/src/lib/daemon_epoll.c index d85d54e7..115bb9d0 100644 --- a/src/lib/daemon_epoll.c +++ b/src/lib/daemon_epoll.c @@ -24,7 +24,9 @@ */ #include "internal.h" #include "daemon_epoll.h" +#include "upgrade_process.h" #include "request_resume.h" +#include "connection_add.h" #include "connection_finish_forward.h" #ifdef EPOLL_SUPPORT @@ -170,7 +172,7 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon) while (NULL != (pos = prev)) { prev = pos->prevE; - process_urh (pos); + MHD_upgrade_response_handle_process_ (pos); if (! is_urh_ready(pos)) { EDLL_remove (daemon->eready_urh_head, diff --git a/src/lib/daemon_ip_limit.c b/src/lib/daemon_ip_limit.c index d368893b..4a131c92 100644 --- a/src/lib/daemon_ip_limit.c +++ b/src/lib/daemon_ip_limit.c @@ -24,6 +24,12 @@ */ #include "internal.h" #include "daemon_ip_limit.h" +#if HAVE_SEARCH_H +#include +#else +#include "tsearch.h" +#endif + /** * Maintain connection count for single address. diff --git a/src/lib/daemon_poll.c b/src/lib/daemon_poll.c index 63747bd0..39d22ccf 100644 --- a/src/lib/daemon_poll.c +++ b/src/lib/daemon_poll.c @@ -23,6 +23,7 @@ */ #include "internal.h" #include "daemon_poll.h" +#include "upgrade_process.h" #include "request_resume.h" #include "connection_add.h" #include "connection_finish_forward.h" @@ -315,7 +316,7 @@ MHD_daemon_poll_all_ (struct MHD_Daemon *daemon, urh_from_pollfd (urh, &(p[poll_server+i])); i += 2; - process_urh (urh); + MHD_upgrade_response_handle_process_ (urh); /* Finished forwarding? */ if ( (0 == urh->in_buffer_size) && (0 == urh->out_buffer_size) && @@ -508,7 +509,7 @@ MHD_daemon_upgrade_connection_with_poll_ (struct MHD_Connection *con) } urh_from_pollfd (urh, p); - process_urh (urh); + MHD_upgrade_response_handle_process_ (urh); } } #endif diff --git a/src/lib/daemon_run.c b/src/lib/daemon_run.c index c93a5f83..d95cff12 100644 --- a/src/lib/daemon_run.c +++ b/src/lib/daemon_run.c @@ -49,6 +49,8 @@ enum MHD_StatusCode MHD_daemon_run (struct MHD_Daemon *daemon) { + enum MHD_StatusCode sc; + if (daemon->shutdown) return MHD_SC_DAEMON_ALREADY_SHUTDOWN; if (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_model) @@ -56,22 +58,21 @@ MHD_daemon_run (struct MHD_Daemon *daemon) switch (daemon->event_loop_syscall) { case MHD_ELS_POLL: - MHD_daemon_poll_ (daemon, - MHD_NO); + sc = MHD_daemon_poll_ (daemon, + MHD_NO); MHD_cleanup_connections (daemon); - break; + return sc; #ifdef EPOLL_SUPPORT case MHD_ELS_EPOLL: - MHD_daemon_epoll_ (daemon, - MHD_NO); + sc = MHD_daemon_epoll_ (daemon, + MHD_NO); MHD_cleanup_connections (daemon); - break; + return sc; #endif case MHD_ELS_SELECT: - MHD_daemon_select_ (daemon, - MHD_NO); + return MHD_daemon_select_ (daemon, + MHD_NO); /* MHD_select does MHD_cleanup_connections already */ - break; default: return MHD_SC_CONFIGURATION_UNEXPECTED_ELS; } diff --git a/src/lib/daemon_select.c b/src/lib/daemon_select.c index 9c80812c..ec4da399 100644 --- a/src/lib/daemon_select.c +++ b/src/lib/daemon_select.c @@ -25,6 +25,7 @@ #include "internal.h" #include "daemon_select.h" #include "request_resume.h" +#include "upgrade_process.h" #include "connection_finish_forward.h" @@ -461,7 +462,7 @@ internal_run_from_select (struct MHD_Daemon *daemon, write_fd_set, except_fd_set); /* call generic forwarding function for passing data */ - process_urh (urh); + MHD_upgrade_response_handle_process_ (urh); /* Finished forwarding? */ if ( (0 == urh->in_buffer_size) && (0 == urh->out_buffer_size) && @@ -566,7 +567,7 @@ MHD_daemon_upgrade_connection_with_select_ (struct MHD_Connection *con) &rs, &ws, &es); - process_urh (urh); + MHD_upgrade_response_handle_process_ (urh); } } #endif diff --git a/src/lib/upgrade_process.c b/src/lib/upgrade_process.c new file mode 100644 index 00000000..318881ab --- /dev/null +++ b/src/lib/upgrade_process.c @@ -0,0 +1,388 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +/** + * @file lib/upgrade_process.c + * @brief function to process upgrade activity (over TLS) + * @author Christian Grothoff + */ +#include "internal.h" +#include "upgrade_process.h" + + +#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) +/** + * Performs bi-directional forwarding on upgraded HTTPS connections + * based on the readyness state stored in the @a urh handle. + * @remark To be called only from thread that process + * connection's recv(), send() and response. + * + * @param urh handle to process + */ +void +MHD_upgrade_response_handle_process_ (struct MHD_UpgradeResponseHandle *urh) +{ + /* Help compiler to optimize: + * pointers to 'connection' and 'daemon' are not changed + * during this processing, so no need to chain dereference + * each time. */ + struct MHD_Connection * const connection = urh->connection; + struct MHD_Daemon * const daemon = connection->daemon; + /* Prevent data races: use same value of 'was_closed' throughout + * this function. If 'was_closed' changed externally in the middle + * of processing - it will be processed on next iteration. */ + bool was_closed; + struct MHD_TLS_Plugin *tls = daemon->tls_api; + + if (daemon->shutdown) + { + /* Daemon shutting down, application will not receive any more data. */ +#ifdef HAVE_MESSAGES + if (! urh->was_closed) + { + MHD_DLOG (daemon, + MHD_SC_DAEMON_ALREADY_SHUTDOWN, + _("Initiated daemon shutdown while \"upgraded\" connection was not closed.\n")); + } +#endif + urh->was_closed = true; + } + was_closed = urh->was_closed; + if (was_closed) + { + /* Application was closed connections: no more data + * can be forwarded to application socket. */ + if (0 < urh->in_buffer_used) + { +#ifdef HAVE_MESSAGES + MHD_DLOG (daemon, + MHD_SC_UPGRADE_FORWARD_INCOMPLETE, + _("Failed to forward to application " MHD_UNSIGNED_LONG_LONG_PRINTF \ + " bytes of data received from remote side: application shut down socket\n"), + (MHD_UNSIGNED_LONG_LONG) urh->in_buffer_used); +#endif + + } + /* If application signaled MHD about socket closure then + * check for any pending data even if socket is not marked + * as 'ready' (signal may arrive after poll()/select()). + * Socketpair for forwarding is always in non-blocking mode + * so no risk that recv() will block the thread. */ + if (0 != urh->out_buffer_size) + urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY; + /* Discard any data received form remote. */ + urh->in_buffer_used = 0; + /* Do not try to push data to application. */ + urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY; + /* Reading from remote client is not required anymore. */ + urh->in_buffer_size = 0; + urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY; + connection->tls_read_ready = false; + } + + /* On some platforms (W32, possibly Darwin) failed send() (send() will always + * fail after remote disconnect was detected) may discard data in system + * buffers received by system but not yet read by recv(). + * So, before trying send() on any socket, recv() must be performed at first + * otherwise last part of incoming data may be lost. */ + + /* If disconnect or error was detected - try to read from socket + * to dry data possibly pending is system buffers. */ + if (0 != (MHD_EPOLL_STATE_ERROR & urh->app.celi)) + urh->app.celi |= MHD_EPOLL_STATE_READ_READY; + if (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) + urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY; + + /* + * handle reading from remote TLS client + */ + if ( ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) || + (connection->tls_read_ready) ) && + (urh->in_buffer_used < urh->in_buffer_size) ) + { + ssize_t res; + size_t buf_size; + + buf_size = urh->in_buffer_size - urh->in_buffer_used; + if (buf_size > SSIZE_MAX) + buf_size = SSIZE_MAX; + + connection->tls_read_ready = false; + res = tls->recv (tls->cls, + connection->tls_cs, + &urh->in_buffer[urh->in_buffer_used], + buf_size); + if (0 >= res) + { + // FIXME: define GNUTLS-independent error codes! + if (GNUTLS_E_INTERRUPTED != res) + { + urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY; + if (GNUTLS_E_AGAIN != res) + { + /* Unrecoverable error on socket was detected or + * socket was disconnected/shut down. */ + /* Stop trying to read from this TLS socket. */ + urh->in_buffer_size = 0; + } + } + } + else /* 0 < res */ + { + urh->in_buffer_used += res; + if (buf_size > (size_t)res) + urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY; + else if (0 < tls->check_record_pending (tls->cls, + connection->tls_cs)) + connection->tls_read_ready = true; + } + if (MHD_EPOLL_STATE_ERROR == + ((MHD_EPOLL_STATE_ERROR | MHD_EPOLL_STATE_READ_READY) & urh->app.celi)) + { + /* Unrecoverable error on socket was detected and all + * pending data was read from system buffers. */ + /* Stop trying to read from this TLS socket. */ + urh->in_buffer_size = 0; + } + } + + /* + * handle reading from application + */ + if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) && + (urh->out_buffer_used < urh->out_buffer_size) ) + { + ssize_t res; + size_t buf_size; + + buf_size = urh->out_buffer_size - urh->out_buffer_used; + if (buf_size > MHD_SCKT_SEND_MAX_SIZE_) + buf_size = MHD_SCKT_SEND_MAX_SIZE_; + + res = MHD_recv_ (urh->mhd.socket, + &urh->out_buffer[urh->out_buffer_used], + buf_size); + if (0 >= res) + { + const int err = MHD_socket_get_error_ (); + if ((0 == res) || + ((! MHD_SCKT_ERR_IS_EINTR_ (err)) && + (! MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)))) + { + urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY; + if ((0 == res) || + (was_closed) || + (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) || + (! MHD_SCKT_ERR_IS_EAGAIN_ (err))) + { + /* Socket disconnect/shutdown was detected; + * Application signaled about closure of 'upgraded' socket; + * or persistent / unrecoverable error. */ + /* Do not try to pull more data from application. */ + urh->out_buffer_size = 0; + } + } + } + else /* 0 < res */ + { + urh->out_buffer_used += res; + if (buf_size > (size_t)res) + urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY; + } + if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) && + ( (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) || + (was_closed) ) ) + { + /* Unrecoverable error on socket was detected and all + * pending data was read from system buffers. */ + /* Do not try to pull more data from application. */ + urh->out_buffer_size = 0; + } + } + + /* + * handle writing to remote HTTPS client + */ + if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) && + (urh->out_buffer_used > 0) ) + { + ssize_t res; + size_t data_size; + + data_size = urh->out_buffer_used; + if (data_size > SSIZE_MAX) + data_size = SSIZE_MAX; + + res = tls->send (tls->cls, + connection->tls_cs, + urh->out_buffer, + data_size); + if (0 >= res) + { + // FIXME: define GNUTLS-independent error codes! + if (GNUTLS_E_INTERRUPTED != res) + { + urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY; + if (GNUTLS_E_INTERRUPTED != res) + { + /* TLS connection shut down or + * persistent / unrecoverable error. */ +#ifdef HAVE_MESSAGES + MHD_DLOG (daemon, + MHD_SC_UPGRADE_FORWARD_INCOMPLETE, + _("Failed to forward to remote client " MHD_UNSIGNED_LONG_LONG_PRINTF \ + " bytes of data received from application: %s\n"), + (MHD_UNSIGNED_LONG_LONG) urh->out_buffer_used, + tls->strerror (tls->cls, + res)); +#endif + /* Discard any data unsent to remote. */ + urh->out_buffer_used = 0; + /* Do not try to pull more data from application. */ + urh->out_buffer_size = 0; + urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY; + } + } + } + else /* 0 < res */ + { + const size_t next_out_buffer_used = urh->out_buffer_used - res; + if (0 != next_out_buffer_used) + { + memmove (urh->out_buffer, + &urh->out_buffer[res], + next_out_buffer_used); + if (data_size > (size_t)res) + urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY; + } + urh->out_buffer_used = next_out_buffer_used; + } + if ( (0 == urh->out_buffer_used) && + (0 != (MHD_EPOLL_STATE_ERROR & urh->app.celi)) ) + { + /* Unrecoverable error on socket was detected and all + * pending data was sent to remote. */ + /* Do not try to send to remote anymore. */ + urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY; + /* Do not try to pull more data from application. */ + urh->out_buffer_size = 0; + urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY; + } + } + + /* + * handle writing to application + */ + if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) && + (urh->in_buffer_used > 0) ) + { + ssize_t res; + size_t data_size; + + data_size = urh->in_buffer_used; + if (data_size > MHD_SCKT_SEND_MAX_SIZE_) + data_size = MHD_SCKT_SEND_MAX_SIZE_; + + res = MHD_send_ (urh->mhd.socket, + urh->in_buffer, + data_size); + if (0 >= res) + { + const int err = MHD_socket_get_error_ (); + if ( (! MHD_SCKT_ERR_IS_EINTR_ (err)) && + (! MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)) ) + { + urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY; + if (! MHD_SCKT_ERR_IS_EAGAIN_ (err)) + { + /* Socketpair connection shut down or + * persistent / unrecoverable error. */ +#ifdef HAVE_MESSAGES + MHD_DLOG (daemon, + MHD_SC_UPGRADE_FORWARD_INCOMPLETE, + _("Failed to forward to application " MHD_UNSIGNED_LONG_LONG_PRINTF \ + " bytes of data received from remote side: %s\n"), + (MHD_UNSIGNED_LONG_LONG) urh->in_buffer_used, + MHD_socket_strerr_ (err)); +#endif + /* Discard any data received form remote. */ + urh->in_buffer_used = 0; + /* Reading from remote client is not required anymore. */ + urh->in_buffer_size = 0; + urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY; + connection->tls_read_ready = false; + } + } + } + else /* 0 < res */ + { + const size_t next_in_buffer_used = urh->in_buffer_used - res; + if (0 != next_in_buffer_used) + { + memmove (urh->in_buffer, + &urh->in_buffer[res], + next_in_buffer_used); + if (data_size > (size_t)res) + urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY; + } + urh->in_buffer_used = next_in_buffer_used; + } + if ( (0 == urh->in_buffer_used) && + (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) ) + { + /* Do not try to push data to application. */ + urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY; + /* Reading from remote client is not required anymore. */ + urh->in_buffer_size = 0; + urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY; + connection->tls_read_ready = false; + } + } + + /* Check whether data is present in TLS buffers + * and incoming forward buffer have some space. */ + if ( (connection->tls_read_ready) && + (urh->in_buffer_used < urh->in_buffer_size) && + (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) ) + daemon->data_already_pending = true; + + if ( (daemon->shutdown) && + ( (0 != urh->out_buffer_size) || + (0 != urh->out_buffer_used) ) ) + { + /* Daemon shutting down, discard any remaining forward data. */ +#ifdef HAVE_MESSAGES + if (0 < urh->out_buffer_used) + MHD_DLOG (daemon, + MHD_SC_UPGRADE_FORWARD_INCOMPLETE, + _("Failed to forward to remote client " MHD_UNSIGNED_LONG_LONG_PRINTF \ + " bytes of data received from application: daemon shut down\n"), + (MHD_UNSIGNED_LONG_LONG) urh->out_buffer_used); +#endif + /* Discard any data unsent to remote. */ + urh->out_buffer_used = 0; + /* Do not try to sent to remote anymore. */ + urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY; + /* Do not try to pull more data from application. */ + urh->out_buffer_size = 0; + urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY; + } +} +#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ + +/* end of upgrade_process.c */ diff --git a/src/lib/upgrade_process.h b/src/lib/upgrade_process.h new file mode 100644 index 00000000..36a7ddff --- /dev/null +++ b/src/lib/upgrade_process.h @@ -0,0 +1,44 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +/** + * @file lib/upgrade_process.h + * @brief function to process upgrade activity (over TLS) + * @author Christian Grothoff + */ +#ifndef UPGRADE_PROCESS_H +#define UPGRADE_PROCESS_H + + +#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) +/** + * Performs bi-directional forwarding on upgraded HTTPS connections + * based on the readyness state stored in the @a urh handle. + * @remark To be called only from thread that process + * connection's recv(), send() and response. + * + * @param urh handle to process + */ +void +MHD_upgrade_response_handle_process_ (struct MHD_UpgradeResponseHandle *urh) + MHD_NONNULL(1); + + +#endif + +#endif -- cgit v1.2.3