/* 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/connection_add.c * @brief functions to add connection to our active set * @author Christian Grothoff */ #include "internal.h" #include "connection_add.h" #include "connection_call_handlers.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" #include "daemon_poll.h" #include "mhd_sockets.h" #ifdef UPGRADE_SUPPORT /** * Main function of the thread that handles an individual connection * after it was "upgraded" when #MHD_USE_THREAD_PER_CONNECTION is set. * @remark To be called only from thread that process * connection's recv(), send() and response. * * @param con the connection this thread will handle */ static void thread_main_connection_upgrade (struct MHD_Connection *con) { #ifdef HTTPS_SUPPORT struct MHD_Daemon *daemon = con->daemon; /* Here, we need to bi-directionally forward until the application tells us that it is done with the socket; */ if ( (NULL != daemon->tls_api) && (MHD_ELS_POLL != daemon->event_loop_syscall) ) { MHD_daemon_upgrade_connection_with_select_ (con); } #ifdef HAVE_POLL else if (NULL != daemon->tls_api) { MHD_daemon_upgrade_connection_with_poll_ (con); } #endif /* end HTTPS */ #endif /* HTTPS_SUPPORT */ /* TLS forwarding was finished. Cleanup socketpair. */ MHD_connection_finish_forward_ (con); /* Do not set 'urh->clean_ready' yet as 'urh' will be used * in connection thread for a little while. */ } #endif /* UPGRADE_SUPPORT */ /** * Main function of the thread that handles an individual * connection when #MHD_USE_THREAD_PER_CONNECTION is set. * * @param data the `struct MHD_Connection` this thread will handle * @return always 0 */ static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_ thread_main_handle_connection (void *data) { struct MHD_Connection *con = data; struct MHD_Daemon *daemon = con->daemon; int num_ready; fd_set rs; fd_set ws; fd_set es; MHD_socket maxsock; struct timeval tv; struct timeval *tvp; time_t now; #if WINDOWS #ifdef HAVE_POLL int extra_slot; #endif /* HAVE_POLL */ #define EXTRA_SLOTS 1 #else /* !WINDOWS */ #define EXTRA_SLOTS 0 #endif /* !WINDOWS */ #ifdef HAVE_POLL struct pollfd p[1 + EXTRA_SLOTS]; #endif #undef EXTRA_SLOTS #ifdef HAVE_POLL const bool use_poll = (MHD_ELS_POLL == daemon->event_loop_syscall); #else /* ! HAVE_POLL */ const bool use_poll = false; #endif /* ! HAVE_POLL */ bool was_suspended = false; MHD_thread_init_(&con->pid); while ( (! daemon->shutdown) && (MHD_REQUEST_CLOSED != con->request.state) ) { const time_t timeout = daemon->connection_default_timeout; #ifdef UPGRADE_SUPPORT struct MHD_UpgradeResponseHandle * const urh = con->request.urh; #else /* ! UPGRADE_SUPPORT */ static const void * const urh = NULL; #endif /* ! UPGRADE_SUPPORT */ if ( (con->suspended) && (NULL == urh) ) { /* Connection was suspended, wait for resume. */ was_suspended = true; if (! use_poll) { FD_ZERO (&rs); if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc), &rs, NULL, FD_SETSIZE)) { #ifdef HAVE_MESSAGES MHD_DLOG (con->daemon, MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE, _("Failed to add FD to fd_set\n")); #endif goto exit; } if (0 > MHD_SYS_select_ (MHD_itc_r_fd_ (daemon->itc) + 1, &rs, NULL, NULL, NULL)) { const int err = MHD_socket_get_error_(); if (MHD_SCKT_ERR_IS_EINTR_(err)) continue; #ifdef HAVE_MESSAGES MHD_DLOG (con->daemon, MHD_SC_UNEXPECTED_SELECT_ERROR, _("Error during select (%d): `%s'\n"), err, MHD_socket_strerr_ (err)); #endif break; } } #ifdef HAVE_POLL else /* use_poll */ { p[0].events = POLLIN; p[0].fd = MHD_itc_r_fd_ (daemon->itc); p[0].revents = 0; if (0 > MHD_sys_poll_ (p, 1, -1)) { if (MHD_SCKT_LAST_ERR_IS_(MHD_SCKT_EINTR_)) continue; #ifdef HAVE_MESSAGES MHD_DLOG (con->daemon, MHD_SC_UNEXPECTED_POLL_ERROR, _("Error during poll: `%s'\n"), MHD_socket_last_strerr_ ()); #endif break; } } #endif /* HAVE_POLL */ MHD_itc_clear_ (daemon->itc); continue; /* Check again for resume. */ } /* End of "suspended" branch. */ if (was_suspended) { MHD_connection_update_last_activity_ (con); /* Reset timeout timer. */ /* Process response queued during suspend and update states. */ MHD_request_handle_idle_ (&con->request); was_suspended = false; } tvp = NULL; if ( (MHD_EVENT_LOOP_INFO_BLOCK == con->request.event_loop_info) #ifdef HTTPS_SUPPORT || ( (con->tls_read_ready) && (MHD_EVENT_LOOP_INFO_READ == con->request.event_loop_info) ) #endif /* HTTPS_SUPPORT */ ) { /* do not block: more data may be inside of TLS buffers waiting or * application must provide response data */ tv.tv_sec = 0; tv.tv_usec = 0; tvp = &tv; } if ( (NULL == tvp) && (timeout > 0) ) { now = MHD_monotonic_sec_counter(); if (now - con->last_activity > timeout) tv.tv_sec = 0; else { const time_t seconds_left = timeout - (now - con->last_activity); #if !defined(_WIN32) || defined(__CYGWIN__) tv.tv_sec = seconds_left; #else /* _WIN32 && !__CYGWIN__ */ if (seconds_left > TIMEVAL_TV_SEC_MAX) tv.tv_sec = TIMEVAL_TV_SEC_MAX; else tv.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) seconds_left; #endif /* _WIN32 && ! __CYGWIN__ */ } tv.tv_usec = 0; tvp = &tv; } if (! use_poll) { /* use select */ bool err_state = false; FD_ZERO (&rs); FD_ZERO (&ws); FD_ZERO (&es); maxsock = MHD_INVALID_SOCKET; switch (con->request.event_loop_info) { case MHD_EVENT_LOOP_INFO_READ: if (! MHD_add_to_fd_set_ (con->socket_fd, &rs, &maxsock, FD_SETSIZE)) err_state = true; break; case MHD_EVENT_LOOP_INFO_WRITE: if (! MHD_add_to_fd_set_ (con->socket_fd, &ws, &maxsock, FD_SETSIZE)) err_state = true; break; case MHD_EVENT_LOOP_INFO_BLOCK: if (! MHD_add_to_fd_set_ (con->socket_fd, &es, &maxsock, FD_SETSIZE)) err_state = true; break; case MHD_EVENT_LOOP_INFO_CLEANUP: /* how did we get here!? */ goto exit; } #if WINDOWS if (MHD_ITC_IS_VALID_(daemon->itc) ) { if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc), &rs, &maxsock, FD_SETSIZE)) err_state = 1; } #endif if (err_state) { #ifdef HAVE_MESSAGES MHD_DLOG (con->daemon, MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE, _("Failed to add FD to fd_set\n")); #endif goto exit; } num_ready = MHD_SYS_select_ (maxsock + 1, &rs, &ws, &es, tvp); if (num_ready < 0) { const int err = MHD_socket_get_error_(); if (MHD_SCKT_ERR_IS_EINTR_(err)) continue; #ifdef HAVE_MESSAGES MHD_DLOG (con->daemon, MHD_SC_UNEXPECTED_SELECT_ERROR, _("Error during select (%d): `%s'\n"), err, MHD_socket_strerr_ (err)); #endif break; } #if WINDOWS /* Clear ITC before other processing so additional * signals will trigger select() again */ if ( (MHD_ITC_IS_VALID_(daemon->itc)) && (FD_ISSET (MHD_itc_r_fd_ (daemon->itc), &rs)) ) MHD_itc_clear_ (daemon->itc); #endif if (MHD_NO == MHD_connection_call_handlers_ (con, FD_ISSET (con->socket_fd, &rs), FD_ISSET (con->socket_fd, &ws), FD_ISSET (con->socket_fd, &es)) ) goto exit; } #ifdef HAVE_POLL else { /* use poll */ memset (&p, 0, sizeof (p)); p[0].fd = con->socket_fd; switch (con->request.event_loop_info) { case MHD_EVENT_LOOP_INFO_READ: p[0].events |= POLLIN | MHD_POLL_EVENTS_ERR_DISC; break; case MHD_EVENT_LOOP_INFO_WRITE: p[0].events |= POLLOUT | MHD_POLL_EVENTS_ERR_DISC; break; case MHD_EVENT_LOOP_INFO_BLOCK: p[0].events |= MHD_POLL_EVENTS_ERR_DISC; break; case MHD_EVENT_LOOP_INFO_CLEANUP: /* how did we get here!? */ goto exit; } #if WINDOWS extra_slot = 0; if (MHD_ITC_IS_VALID_(daemon->itc)) { p[1].events |= POLLIN; p[1].fd = MHD_itc_r_fd_ (daemon->itc); p[1].revents = 0; extra_slot = 1; } #endif if (MHD_sys_poll_ (p, #if WINDOWS 1 + extra_slot, #else 1, #endif (NULL == tvp) ? -1 : tv.tv_sec * 1000) < 0) { if (MHD_SCKT_LAST_ERR_IS_(MHD_SCKT_EINTR_)) continue; #ifdef HAVE_MESSAGES MHD_DLOG (con->daemon, MHD_SC_UNEXPECTED_POLL_ERROR, _("Error during poll: `%s'\n"), MHD_socket_last_strerr_ ()); #endif break; } #if WINDOWS /* Clear ITC before other processing so additional * signals will trigger poll() again */ if ( (MHD_ITC_IS_VALID_(daemon->itc)) && (0 != (p[1].revents & (POLLERR | POLLHUP | POLLIN))) ) MHD_itc_clear_ (daemon->itc); #endif if (MHD_NO == MHD_connection_call_handlers_ (con, 0 != (p[0].revents & POLLIN), 0 != (p[0].revents & POLLOUT), 0 != (p[0].revents & (POLLERR | MHD_POLL_REVENTS_ERR_DISC)))) goto exit; } #endif #ifdef UPGRADE_SUPPORT if (MHD_REQUEST_UPGRADE == con->request.state) { /* Normal HTTP processing is finished, * notify application. */ if (NULL != con->request.response->termination_cb) con->request.response->termination_cb (con->request.response->termination_cb_cls, MHD_REQUEST_TERMINATED_COMPLETED_OK, con->request.client_context); thread_main_connection_upgrade (con); /* MHD_connection_finish_forward_() was called by thread_main_connection_upgrade(). */ /* "Upgraded" data will not be used in this thread from this point. */ con->request.urh->clean_ready = true; /* If 'urh->was_closed' set to true, connection will be * moved immediately to cleanup list. Otherwise connection * will stay in suspended list until 'urh' will be marked * with 'was_closed' by application. */ MHD_request_resume (&con->request); /* skip usual clean up */ return (MHD_THRD_RTRN_TYPE_) 0; } #endif /* UPGRADE_SUPPORT */ } #if DEBUG_CLOSE #ifdef HAVE_MESSAGES MHD_DLOG (con->daemon, MHD_SC_THREAD_TERMINATING, _("Processing thread terminating. Closing connection\n")); #endif #endif if (MHD_REQUEST_CLOSED != con->request.state) MHD_connection_close_ (con, (daemon->shutdown) ? MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN: MHD_REQUEST_TERMINATED_WITH_ERROR); MHD_request_handle_idle_ (&con->request); exit: if (NULL != con->request.response) { MHD_response_queue_for_destroy (con->request.response); con->request.response = NULL; } if (MHD_INVALID_SOCKET != con->socket_fd) { shutdown (con->socket_fd, SHUT_WR); /* 'socket_fd' can be used in other thread to signal shutdown. * To avoid data races, do not close socket here. Daemon will * use more connections only after cleanup anyway. */ } return (MHD_THRD_RTRN_TYPE_) 0; } /** * Callback for receiving data from the socket. * * @param connection the MHD connection structure * @param other where to write received data to * @param i maximum size of other (in bytes) * @return positive value for number of bytes actually received or * negative value for error number MHD_ERR_xxx_ */ static ssize_t recv_param_adapter (struct MHD_Connection *connection, void *other, size_t i) { ssize_t ret; if ( (MHD_INVALID_SOCKET == connection->socket_fd) || (MHD_REQUEST_CLOSED == connection->request.state) ) { return MHD_ERR_NOTCONN_; } if (i > MHD_SCKT_SEND_MAX_SIZE_) i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */ ret = MHD_recv_ (connection->socket_fd, other, i); if (0 > ret) { const int err = MHD_socket_get_error_ (); if (MHD_SCKT_ERR_IS_EAGAIN_ (err)) { #ifdef EPOLL_SUPPORT /* Got EAGAIN --- no longer read-ready */ connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; #endif /* EPOLL_SUPPORT */ return MHD_ERR_AGAIN_; } if (MHD_SCKT_ERR_IS_EINTR_ (err)) return MHD_ERR_AGAIN_; if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ECONNRESET_)) return MHD_ERR_CONNRESET_; /* Treat any other error as hard error. */ return MHD_ERR_NOTCONN_; } #ifdef EPOLL_SUPPORT else if (i > (size_t)ret) connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; #endif /* EPOLL_SUPPORT */ return ret; } /** * Callback for writing data to the socket. * * @param connection the MHD connection structure * @param other data to write * @param i number of bytes to write * @return positive value for number of bytes actually sent or * negative value for error number MHD_ERR_xxx_ */ static ssize_t send_param_adapter (struct MHD_Connection *connection, const void *other, size_t i) { ssize_t ret; if ( (MHD_INVALID_SOCKET == connection->socket_fd) || (MHD_REQUEST_CLOSED == connection->request.state) ) { return MHD_ERR_NOTCONN_; } if (i > MHD_SCKT_SEND_MAX_SIZE_) i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */ ret = MHD_send_ (connection->socket_fd, other, i); if (0 > ret) { const int err = MHD_socket_get_error_(); if (MHD_SCKT_ERR_IS_EAGAIN_(err)) { #ifdef EPOLL_SUPPORT /* EAGAIN --- no longer write-ready */ connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; #endif /* EPOLL_SUPPORT */ return MHD_ERR_AGAIN_; } if (MHD_SCKT_ERR_IS_EINTR_ (err)) return MHD_ERR_AGAIN_; if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ECONNRESET_)) return MHD_ERR_CONNRESET_; /* Treat any other error as hard error. */ return MHD_ERR_NOTCONN_; } #ifdef EPOLL_SUPPORT else if (i > (size_t)ret) connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; #endif /* EPOLL_SUPPORT */ return ret; } /** * Add another client connection to the set of connections * managed by MHD. This API is usually not needed (since * MHD will accept inbound connections on the server socket). * Use this API in special cases, for example if your HTTP * server is behind NAT and needs to connect out to the * HTTP client. * * The given client socket will be managed (and closed!) by MHD after * this call and must no longer be used directly by the application * afterwards. * * @param daemon daemon that manages the connection * @param client_socket socket to manage (MHD will expect * to receive an HTTP request from this socket next). * @param addr IP address of the client * @param addrlen number of bytes in @a addr * @param external_add perform additional operations needed due * to the application calling us directly * @param non_blck indicate that socket in non-blocking mode * @return #MHD_SC_OK on success */ static enum MHD_StatusCode internal_add_connection (struct MHD_Daemon *daemon, MHD_socket client_socket, const struct sockaddr *addr, socklen_t addrlen, bool external_add, bool non_blck) { enum MHD_StatusCode sc; struct MHD_Connection *connection; int eno = 0; /* Direct add to master daemon could happen only with "external" add mode. */ mhd_assert ( (NULL == daemon->worker_pool) || (external_add) ); if ( (external_add) && (NULL != daemon->worker_pool) ) { unsigned int i; /* have a pool, try to find a pool with capacity; we use the socket as the initial offset into the pool for load balancing */ for (i = 0; i < daemon->worker_pool_size; ++i) { struct MHD_Daemon * const worker = &daemon->worker_pool[(i + client_socket) % daemon->worker_pool_size]; if (worker->connections < worker->global_connection_limit) return internal_add_connection (worker, client_socket, addr, addrlen, true, non_blck); } /* all pools are at their connection limit, must refuse */ MHD_socket_close_chk_ (client_socket); #if ENFILE errno = ENFILE; #endif return MHD_SC_LIMIT_CONNECTIONS_REACHED; } if ( (! MHD_SCKT_FD_FITS_FDSET_(client_socket, NULL)) && (MHD_ELS_SELECT == daemon->event_loop_syscall) ) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE, _("Socket descriptor larger than FD_SETSIZE: %d > %d\n"), (int) client_socket, (int) FD_SETSIZE); #endif MHD_socket_close_chk_ (client_socket); #if EINVAL errno = EINVAL; #endif return MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; } #ifdef MHD_socket_nosignal_ if (! MHD_socket_nosignal_ (client_socket)) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED, _("Failed to set SO_NOSIGPIPE on accepted socket: %s\n"), MHD_socket_last_strerr_()); #endif #ifndef MSG_NOSIGNAL /* Cannot use socket as it can produce SIGPIPE. */ #ifdef ENOTSOCK errno = ENOTSOCK; #endif /* ENOTSOCK */ return MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED; #endif /* ! MSG_NOSIGNAL */ } #endif /* MHD_socket_nosignal_ */ #ifdef HAVE_MESSAGES #if DEBUG_CONNECT MHD_DLOG (daemon, MHD_SC_CONNECTION_ACCEPTED, _("Accepted connection on socket %d\n"), client_socket); #endif #endif if ( (daemon->connections == daemon->global_connection_limit) || (MHD_NO == MHD_ip_limit_add (daemon, addr, addrlen)) ) { /* above connection limit - reject */ #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_LIMIT_CONNECTIONS_REACHED, _("Server reached connection limit. Closing inbound connection.\n")); #endif MHD_socket_close_chk_ (client_socket); #if ENFILE errno = ENFILE; #endif return MHD_SC_LIMIT_CONNECTIONS_REACHED; } /* apply connection acceptance policy if present */ if ( (NULL != daemon->accept_policy_cb) && (MHD_NO == daemon->accept_policy_cb (daemon->accept_policy_cb_cls, addr, addrlen)) ) { #if DEBUG_CLOSE #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_ACCEPT_POLICY_REJECTED, _("Connection rejected by application. Closing connection.\n")); #endif #endif MHD_socket_close_chk_ (client_socket); MHD_ip_limit_del (daemon, addr, addrlen); #if EACCESS errno = EACCESS; #endif return MHD_SC_ACCEPT_POLICY_REJECTED; } if (NULL == (connection = MHD_calloc_ (1, sizeof (struct MHD_Connection)))) { eno = errno; #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_CONNECTION_MALLOC_FAILURE, "Error allocating memory: %s\n", MHD_strerror_ (errno)); #endif MHD_socket_close_chk_ (client_socket); MHD_ip_limit_del (daemon, addr, addrlen); errno = eno; return MHD_SC_CONNECTION_MALLOC_FAILURE; } connection->pool = MHD_pool_create (daemon->connection_memory_limit_b); if (NULL == connection->pool) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_POOL_MALLOC_FAILURE, _("Error allocating memory: %s\n"), MHD_strerror_ (errno)); #endif MHD_socket_close_chk_ (client_socket); MHD_ip_limit_del (daemon, addr, addrlen); free (connection); #if ENOMEM errno = ENOMEM; #endif return MHD_SC_POOL_MALLOC_FAILURE; } connection->connection_timeout = daemon->connection_default_timeout; memcpy (&connection->addr, addr, addrlen); connection->addr_len = addrlen; connection->socket_fd = client_socket; connection->sk_nonblck = non_blck; connection->daemon = daemon; connection->last_activity = MHD_monotonic_sec_counter(); #ifdef HTTPS_SUPPORT if (NULL != daemon->tls_api) { connection->tls_cs = daemon->tls_api->setup_connection (daemon->tls_api->cls, NULL /* FIXME */); if (NULL == connection->tls_cs) { eno = EINVAL; sc = -1; // FIXME! goto cleanup; } } else #endif /* ! HTTPS_SUPPORT */ { /* set default connection handlers */ connection->recv_cls = &recv_param_adapter; connection->send_cls = &send_param_adapter; } MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); /* Firm check under lock. */ if (daemon->connections >= daemon->global_connection_limit) { MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); /* above connection limit - reject */ #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_LIMIT_CONNECTIONS_REACHED, _("Server reached connection limit. Closing inbound connection.\n")); #endif #if ENFILE eno = ENFILE; #endif sc = MHD_SC_LIMIT_CONNECTIONS_REACHED; goto cleanup; } daemon->connections++; if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) { XDLL_insert (daemon->normal_timeout_head, daemon->normal_timeout_tail, connection); } DLL_insert (daemon->connections_head, daemon->connections_tail, connection); MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); if (NULL != daemon->notify_connection_cb) daemon->notify_connection_cb (daemon->notify_connection_cb_cls, connection, MHD_CONNECTION_NOTIFY_STARTED); /* attempt to create handler thread */ if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) { if (! MHD_create_named_thread_ (&connection->pid, "MHD-connection", daemon->thread_stack_limit_b, &thread_main_handle_connection, connection)) { eno = errno; #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_THREAD_LAUNCH_FAILURE, "Failed to create a thread: %s\n", MHD_strerror_ (eno)); #endif sc = MHD_SC_THREAD_LAUNCH_FAILURE; goto cleanup; } } else { connection->pid = daemon->pid; } #ifdef EPOLL_SUPPORT if (MHD_ELS_EPOLL == daemon->event_loop_syscall) { if ( (! daemon->enable_turbo) || (external_add)) { /* Do not manipulate EReady DL-list in 'external_add' mode. */ struct epoll_event event; event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET; event.data.ptr = connection; if (0 != epoll_ctl (daemon->epoll_fd, EPOLL_CTL_ADD, client_socket, &event)) { eno = errno; #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_EPOLL_CTL_ADD_FAILED, _("Call to epoll_ctl failed: %s\n"), MHD_socket_last_strerr_ ()); #endif sc = MHD_SC_EPOLL_CTL_ADD_FAILED; goto cleanup; } connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET; } else { connection->epoll_state |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY | MHD_EPOLL_STATE_IN_EREADY_EDLL; EDLL_insert (daemon->eready_head, daemon->eready_tail, connection); } } else /* This 'else' is combined with next 'if'. */ #endif if ( (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) && (external_add) && (MHD_ITC_IS_VALID_(daemon->itc)) && (! MHD_itc_activate_ (daemon->itc, "n")) ) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_ITC_USE_FAILED, _("Failed to signal new connection via inter-thread communication channel (not necessarily fatal, continuing anyway).")); #endif } return MHD_SC_OK; cleanup: if (NULL != daemon->notify_connection_cb) daemon->notify_connection_cb (daemon->notify_connection_cb_cls, connection, MHD_CONNECTION_NOTIFY_CLOSED); #ifdef HTTPS_SUPPORT if ( (NULL != daemon->tls_api) && (NULL != connection->tls_cs) ) daemon->tls_api->teardown_connection (daemon->tls_api->cls, connection->tls_cs); #endif /* HTTPS_SUPPORT */ MHD_socket_close_chk_ (client_socket); MHD_ip_limit_del (daemon, addr, addrlen); MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) { XDLL_remove (daemon->normal_timeout_head, daemon->normal_timeout_tail, connection); } DLL_remove (daemon->connections_head, daemon->connections_tail, connection); MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); MHD_pool_destroy (connection->pool); free (connection); if (0 != eno) errno = eno; else errno = EINVAL; return sc; } /** * Add another client connection to the set of connections managed by * MHD. This API is usually not needed (since MHD will accept inbound * connections on the server socket). Use this API in special cases, * for example if your HTTP server is behind NAT and needs to connect * out to the HTTP client, or if you are building a proxy. * * If you use this API in conjunction with a internal select or a * thread pool, you must set the option #MHD_USE_ITC to ensure that * the freshly added connection is immediately processed by MHD. * * The given client socket will be managed (and closed!) by MHD after * this call and must no longer be used directly by the application * afterwards. * * @param daemon daemon that manages the connection * @param client_socket socket to manage (MHD will expect * to receive an HTTP request from this socket next). * @param addr IP address of the client * @param addrlen number of bytes in @a addr * @return #MHD_YES on success, #MHD_NO if this daemon could * not handle the connection (i.e. malloc() failed, etc). * The socket will be closed in any case; `errno` is * set to indicate further details about the error. * @ingroup specialized */ enum MHD_StatusCode MHD_daemon_add_connection (struct MHD_Daemon *daemon, MHD_socket client_socket, const struct sockaddr *addr, socklen_t addrlen) { bool sk_nonbl; if (! MHD_socket_nonblocking_ (client_socket)) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, _("Failed to set nonblocking mode on new client socket: %s\n"), MHD_socket_last_strerr_()); #endif sk_nonbl = false; } else { sk_nonbl = true; } if ( (daemon->enable_turbo) && (! MHD_socket_noninheritable_ (client_socket)) ) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, _("Failed to set noninheritable mode on new client socket.\n")); #endif } return internal_add_connection (daemon, client_socket, addr, addrlen, true, sk_nonbl); } /** * Accept an incoming connection and create the MHD_Connection object * for it. This function also enforces policy by way of checking with * the accept policy callback. @remark To be called only from thread * that process daemon's select()/poll()/etc. * * @param daemon handle with the listen socket * @return #MHD_SC_OK on success */ enum MHD_StatusCode MHD_accept_connection_ (struct MHD_Daemon *daemon) { struct sockaddr_storage addrstorage; struct sockaddr *addr = (struct sockaddr *) &addrstorage; socklen_t addrlen; MHD_socket s; MHD_socket fd; bool sk_nonbl; addrlen = sizeof (addrstorage); memset (addr, 0, sizeof (addrstorage)); if ( (MHD_INVALID_SOCKET == (fd = daemon->listen_socket)) || (daemon->was_quiesced) ) return MHD_SC_DAEMON_ALREADY_QUIESCED; #ifdef USE_ACCEPT4 s = accept4 (fd, addr, &addrlen, MAYBE_SOCK_CLOEXEC | MAYBE_SOCK_NONBLOCK); sk_nonbl = (0 != MAYBE_SOCK_NONBLOCK); #else /* ! USE_ACCEPT4 */ s = accept (fd, addr, &addrlen); sk_nonbl = false; #endif /* ! USE_ACCEPT4 */ if ( (MHD_INVALID_SOCKET == s) || (addrlen <= 0) ) { const int err = MHD_socket_get_error_ (); /* This could be a common occurance with multiple worker threads */ if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINVAL_)) return MHD_SC_DAEMON_ALREADY_SHUTDOWN; /* can happen during shutdown, let's hope this is the cause... */ if (MHD_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT_(err)) return MHD_SC_ACCEPT_FAST_DISCONNECT; /* do not print error if client just disconnected early */ if (MHD_SCKT_ERR_IS_EAGAIN_ (err) ) return MHD_SC_ACCEPT_FAILED_EAGAIN; if (MHD_INVALID_SOCKET != s) { MHD_socket_close_chk_ (s); } if ( MHD_SCKT_ERR_IS_LOW_RESOURCES_ (err) ) { /* system/process out of resources */ if (0 == daemon->connections) { #ifdef HAVE_MESSAGES /* Not setting 'at_limit' flag, as there is no way it would ever be cleared. Instead trying to produce bit fat ugly warning. */ MHD_DLOG (daemon, MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY, _("Hit process or system resource limit at FIRST connection. This is really bad as there is no sane way to proceed. Will try busy waiting for system resources to become magically available.\n")); #endif return MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY; } else { MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); daemon->at_limit = true; MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED, _("Hit process or system resource limit at %u connections, temporarily suspending accept(). Consider setting a lower MHD_OPTION_CONNECTION_LIMIT.\n"), (unsigned int) daemon->connections); #endif return MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED; } } #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY, _("Error accepting connection: %s\n"), MHD_socket_strerr_(err)); #endif return MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY; } #if !defined(USE_ACCEPT4) || !defined(HAVE_SOCK_NONBLOCK) if (! MHD_socket_nonblocking_ (s)) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, _("Failed to set nonblocking mode on incoming connection socket: %s\n"), MHD_socket_last_strerr_()); #endif } else sk_nonbl = true; #endif /* !USE_ACCEPT4 || !HAVE_SOCK_NONBLOCK */ #if !defined(USE_ACCEPT4) || !defined(SOCK_CLOEXEC) if (! MHD_socket_noninheritable_ (s)) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, _("Failed to set noninheritable mode on incoming connection socket.\n")); #endif } #endif /* !USE_ACCEPT4 || !SOCK_CLOEXEC */ #ifdef HAVE_MESSAGES #if DEBUG_CONNECT MHD_DLOG (daemon, MHD_SC_CONNECTION_ACCEPTED, _("Accepted connection on socket %d\n"), s); #endif #endif return internal_add_connection (daemon, s, addr, addrlen, false, sk_nonbl); } /* end of connection_add.c */