mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
396 lines
14 KiB
C
396 lines
14 KiB
C
/*
|
|
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 readiness 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_mode) )
|
|
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 */
|