aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2018-02-15 07:14:03 +0100
committerChristian Grothoff <christian@grothoff.org>2018-02-15 07:14:03 +0100
commite76b3a78104ab4d6b939a50fe1de446b34296916 (patch)
tree0bcd230c202d3fa3261c81cd32e9632e11048ca4
parent31dd55ae496a5582dd4c67f1231ee7e042f6b4e8 (diff)
downloadlibmicrohttpd-e76b3a78104ab4d6b939a50fe1de446b34296916.tar.gz
libmicrohttpd-e76b3a78104ab4d6b939a50fe1de446b34296916.zip
adding ip counting and connection_add logic
-rw-r--r--src/gnutls/setup_connection.c57
-rw-r--r--src/gnutls/teardown_connection.c4
-rw-r--r--src/include/microhttpd2.h146
-rw-r--r--src/include/microhttpd_tls.h4
-rw-r--r--src/lib/Makefile.am3
-rw-r--r--src/lib/connection_add.c978
-rw-r--r--src/lib/connection_add.h41
-rw-r--r--src/lib/daemon_add_connection.c63
-rw-r--r--src/lib/daemon_epoll.c2
-rw-r--r--src/lib/daemon_ip_limit.c280
-rw-r--r--src/lib/daemon_ip_limit.h60
-rw-r--r--src/lib/daemon_poll.c4
-rw-r--r--src/lib/daemon_select.c2
-rw-r--r--src/lib/internal.h19
14 files changed, 1564 insertions, 99 deletions
diff --git a/src/gnutls/setup_connection.c b/src/gnutls/setup_connection.c
new file mode 100644
index 00000000..c789613d
--- /dev/null
+++ b/src/gnutls/setup_connection.c
@@ -0,0 +1,57 @@
1
2
3setup_connection ()
4{
5 connection->tls_state = MHD_TLS_CONN_INIT;
6 MHD_set_https_callbacks (connection);
7 gnutls_init (&connection->tls_session,
8 GNUTLS_SERVER
9#if (GNUTLS_VERSION_NUMBER+0 >= 0x030402)
10 | GNUTLS_NO_SIGNAL
11#endif /* GNUTLS_VERSION_NUMBER >= 0x030402 */
12#if GNUTLS_VERSION_MAJOR >= 3
13 | GNUTLS_NONBLOCK
14#endif /* GNUTLS_VERSION_MAJOR >= 3*/
15 );
16 gnutls_priority_set (connection->tls_session,
17 daemon->priority_cache);
18 switch (daemon->cred_type)
19 {
20 /* set needed credentials for certificate authentication. */
21 case GNUTLS_CRD_CERTIFICATE:
22 gnutls_credentials_set (connection->tls_session,
23 GNUTLS_CRD_CERTIFICATE,
24 daemon->x509_cred);
25 break;
26 default:
27#ifdef HAVE_MESSAGES
28 MHD_DLOG (connection->daemon,
29 _("Failed to setup TLS credentials: unknown credential type %d\n"),
30 daemon->cred_type);
31#endif
32 MHD_socket_close_chk_ (client_socket);
33 MHD_ip_limit_del (daemon,
34 addr,
35 addrlen);
36 free (connection);
37 MHD_PANIC (_("Unknown credential type"));
38#if EINVAL
39 errno = EINVAL;
40#endif
41 return MHD_NO;
42 }
43#if (GNUTLS_VERSION_NUMBER+0 >= 0x030109) && !defined(_WIN64)
44 gnutls_transport_set_int (connection->tls_session, (int)(client_socket));
45#else /* GnuTLS before 3.1.9 or Win x64 */
46 gnutls_transport_set_ptr (connection->tls_session, (gnutls_transport_ptr_t)(intptr_t)(client_socket));
47#endif /* GnuTLS before 3.1.9 */
48#ifdef MHD_TLSLIB_NEED_PUSH_FUNC
49 gnutls_transport_set_push_function (connection->tls_session, MHD_tls_push_func_);
50#endif /* MHD_TLSLIB_NEED_PUSH_FUNC */
51 if (daemon->https_mem_trust)
52 gnutls_certificate_server_set_request (connection->tls_session,
53 GNUTLS_CERT_REQUEST);
54#else /* ! HTTPS_SUPPORT */
55 return NULL;
56
57}
diff --git a/src/gnutls/teardown_connection.c b/src/gnutls/teardown_connection.c
new file mode 100644
index 00000000..0156955b
--- /dev/null
+++ b/src/gnutls/teardown_connection.c
@@ -0,0 +1,4 @@
1teardown_connection ()
2{
3 gnutls_deinit (connection->tls_session);
4}
diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h
index ebb8f00a..91b0074b 100644
--- a/src/include/microhttpd2.h
+++ b/src/include/microhttpd2.h
@@ -348,6 +348,80 @@ enum MHD_StatusCode
348 */ 348 */
349 MHD_SC_NO_TIMEOUT = 10001, 349 MHD_SC_NO_TIMEOUT = 10001,
350 350
351 /**
352 * Informational event, we accepted a connection.
353 */
354 MHD_SC_CONNECTION_ACCEPTED = 10002,
355
356 /**
357 * Informational event, thread processing connection termiantes.
358 */
359 MHD_SC_THREAD_TERMINATING = 10003,
360
361
362 /**
363 * Resource limit in terms of number of parallel connections
364 * hit.
365 */
366 MHD_SC_LIMIT_CONNECTIONS_REACHED = 30000,
367
368 /**
369 * accept() returned transient error.
370 */
371 MHD_SC_ACCEPT_FAILED_EAGAIN = 30001,
372
373 /**
374 * We failed to allocate memory for poll() syscall.
375 * (May be transient.)
376 */
377 MHD_SC_POLL_MALLOC_FAILURE = 30002,
378
379 /**
380 * The operation failed because the respective
381 * daemon is already too deep inside of the shutdown
382 * activity.
383 */
384 MHD_SC_DAEMON_ALREADY_SHUTDOWN = 30003,
385
386 /**
387 * We failed to start a thread.
388 */
389 MHD_SC_THREAD_LAUNCH_FAILURE = 30004,
390
391 /**
392 * The operation failed because we either have no
393 * listen socket or were already quiesced.
394 */
395 MHD_SC_DAEMON_ALREADY_QUIESCED = 30005,
396
397 /**
398 * The operation failed because client disconnected
399 * faster than we could accept().
400 */
401 MHD_SC_ACCEPT_FAST_DISCONNECT = 30006,
402
403 /**
404 * Operating resource limits hit on accept().
405 */
406 MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED = 30007,
407
408 /**
409 * Connection was refused by accept policy callback.
410 */
411 MHD_SC_ACCEPT_POLICY_REJECTED = 30008,
412
413 /**
414 * We failed to allocate memory for the connection.
415 * (May be transient.)
416 */
417 MHD_SC_CONNECTION_MALLOC_FAILURE = 30009,
418
419 /**
420 * We failed to allocate memory for the connection's memory pool.
421 * (May be transient.)
422 */
423 MHD_SC_POOL_MALLOC_FAILURE = 30010,
424
351 425
352 /** 426 /**
353 * MHD does not support the requested combination of 427 * MHD does not support the requested combination of
@@ -362,21 +436,27 @@ enum MHD_StatusCode
362 MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 40001, 436 MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 40001,
363 437
364 /** 438 /**
365 * This build of MHD does not support TLS, but the application 439 * We failed to bind the listen socket.
366 * requested TLS.
367 */ 440 */
368 MHD_SC_TLS_DISABLED = 50000, 441 MHD_SC_LISTEN_SOCKET_BIND_FAILED = 40002,
369 442
370 /** 443 /**
371 * The application requested an unsupported TLS backend to be used. 444 * The application requested an unsupported TLS backend to be used.
372 */ 445 */
373 MHD_SC_TLS_BACKEND_UNSUPPORTED = 50001, 446 MHD_SC_TLS_BACKEND_UNSUPPORTED = 40003,
374 447
375 /** 448 /**
376 * The application requested a TLS cipher suite which is not 449 * The application requested a TLS cipher suite which is not
377 * supported by the selected backend. 450 * supported by the selected backend.
378 */ 451 */
379 MHD_SC_TLS_CIPHERS_INVALID = 50002, 452 MHD_SC_TLS_CIPHERS_INVALID = 40004,
453
454
455 /**
456 * This build of MHD does not support TLS, but the application
457 * requested TLS.
458 */
459 MHD_SC_TLS_DISABLED = 50000,
380 460
381 /** 461 /**
382 * The application attempted to setup TLS paramters before 462 * The application attempted to setup TLS paramters before
@@ -467,11 +547,6 @@ enum MHD_StatusCode
467 MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED = 50018, 547 MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED = 50018,
468 548
469 /** 549 /**
470 * We failed to bind the listen socket.
471 */
472 MHD_SC_LISTEN_SOCKET_BIND_FAILED = 50019,
473
474 /**
475 * Failed to enable TCP FAST OPEN option. 550 * Failed to enable TCP FAST OPEN option.
476 */ 551 */
477 MHD_SC_FAST_OPEN_FAILURE = 50020, 552 MHD_SC_FAST_OPEN_FAILURE = 50020,
@@ -519,12 +594,10 @@ enum MHD_StatusCode
519 MHD_SC_UPGRADE_ON_DAEMON_WITH_UPGRADE_DISALLOWED = 50028, 594 MHD_SC_UPGRADE_ON_DAEMON_WITH_UPGRADE_DISALLOWED = 50028,
520 595
521 /** 596 /**
522 * Queueing a response failed because the respective 597 * Failed to signal via ITC channel.
523 * daemon is already too deep inside of the shutdown
524 * activity and the reponse cannot be sent any longer.
525 */ 598 */
526 MHD_SC_DAEMON_ALREADY_SHUTDOWN = 50029, 599 MHD_SC_ITC_USE_FAILED = 50029,
527 600
528 /** 601 /**
529 * We failed to initialize the main thread for listening. 602 * We failed to initialize the main thread for listening.
530 */ 603 */
@@ -539,11 +612,6 @@ enum MHD_StatusCode
539 * We failed to add a socket to the epoll() set. 612 * We failed to add a socket to the epoll() set.
540 */ 613 */
541 MHD_SC_EPOLL_CTL_ADD_FAILED = 50032, 614 MHD_SC_EPOLL_CTL_ADD_FAILED = 50032,
542
543 /**
544 * We failed to start a thread.
545 */
546 MHD_SC_THREAD_LAUNCH_FAILURE = 50033,
547 615
548 /** 616 /**
549 * We failed to create control socket for the epoll(). 617 * We failed to create control socket for the epoll().
@@ -608,12 +676,13 @@ enum MHD_StatusCode
608 * (should never happen). 676 * (should never happen).
609 */ 677 */
610 MHD_SC_UNEXPECTED_POLL_ERROR = 50044, 678 MHD_SC_UNEXPECTED_POLL_ERROR = 50044,
611 679
612 /** 680 /**
613 * We failed to allocate memory for poll() syscall. 681 * We failed to configure accepted socket
682 * to not use a signal pipe.
614 */ 683 */
615 MHD_SC_POLL_MALLOC_FAILURE = 50045, 684 MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED = 50045,
616 685
617 /** 686 /**
618 * Encountered an unexpected error from epoll_wait() 687 * Encountered an unexpected error from epoll_wait()
619 * (should never happen). 688 * (should never happen).
@@ -625,6 +694,35 @@ enum MHD_StatusCode
625 */ 694 */
626 MHD_SC_EPOLL_FD_INVALID = 50047, 695 MHD_SC_EPOLL_FD_INVALID = 50047,
627 696
697 /**
698 * We failed to configure accepted socket
699 * to be non-inheritable.
700 */
701 MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED = 50048,
702
703 /**
704 * We failed to configure accepted socket
705 * to be non-blocking.
706 */
707 MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED = 50049,
708
709 /**
710 * accept() returned non-transient error.
711 */
712 MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY = 50050,
713
714 /**
715 * Operating resource limits hit on accept() while
716 * zero connections are active. Oopsie.
717 */
718 MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY = 50051,
719
720 /**
721 * Failed to add IP address to per-IP counter for
722 * some reason.
723 */
724 MHD_SC_IP_COUNTER_FAILURE = 50052,
725
628}; 726};
629 727
630 728
diff --git a/src/include/microhttpd_tls.h b/src/include/microhttpd_tls.h
index f8e5bd51..e59b983b 100644
--- a/src/include/microhttpd_tls.h
+++ b/src/include/microhttpd_tls.h
@@ -89,6 +89,10 @@ struct MHD_TLS_Plugin
89 struct MHD_TLS_ConnectionState * 89 struct MHD_TLS_ConnectionState *
90 (*setup_connection)(void *cls, 90 (*setup_connection)(void *cls,
91 ...); 91 ...);
92
93 void
94 (*teardown_connection)(void *cls,
95 struct MHD_TLS_ConnectionState *cs);
92 96
93 /** 97 /**
94 * TODO: More functions here.... 98 * TODO: More functions here....
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index e5ea0d13..a1241cc8 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -58,14 +58,15 @@ libmicrohttpd_la_SOURCES = \
58 action_parse_post.c \ 58 action_parse_post.c \
59 action_process_upload.c \ 59 action_process_upload.c \
60 action_suspend.c \ 60 action_suspend.c \
61 connection_add.c connection_add.h \
61 connection_info.c \ 62 connection_info.c \
62 connection_options.c \ 63 connection_options.c \
63 daemon_add_connection.c \
64 daemon_create.c \ 64 daemon_create.c \
65 daemon_destroy.c \ 65 daemon_destroy.c \
66 daemon_epoll.c daemon_epoll.h \ 66 daemon_epoll.c daemon_epoll.h \
67 daemon_get_timeout.c \ 67 daemon_get_timeout.c \
68 daemon_info.c \ 68 daemon_info.c \
69 daemon_ip_limit.c daemon_ip_limit.h \
69 daemon_options.c \ 70 daemon_options.c \
70 daemon_poll.c daemon_poll.h \ 71 daemon_poll.c daemon_poll.h \
71 daemon_run.c \ 72 daemon_run.c \
diff --git a/src/lib/connection_add.c b/src/lib/connection_add.c
new file mode 100644
index 00000000..8d4f12ad
--- /dev/null
+++ b/src/lib/connection_add.c
@@ -0,0 +1,978 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19/**
20 * @file lib/connection_add.c
21 * @brief functions to add connection to our active set
22 * @author Christian Grothoff
23 */
24#include "internal.h"
25#include "connection_add.h"
26#include "daemon_ip_limit.h"
27
28
29/**
30 * Main function of the thread that handles an individual
31 * connection when #MHD_USE_THREAD_PER_CONNECTION is set.
32 *
33 * @param data the `struct MHD_Connection` this thread will handle
34 * @return always 0
35 */
36static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
37thread_main_handle_connection (void *data)
38{
39 struct MHD_Connection *con = data;
40 struct MHD_Daemon *daemon = con->daemon;
41 int num_ready;
42 fd_set rs;
43 fd_set ws;
44 fd_set es;
45 MHD_socket maxsock;
46 struct timeval tv;
47 struct timeval *tvp;
48 time_t now;
49#if WINDOWS
50#ifdef HAVE_POLL
51 int extra_slot;
52#endif /* HAVE_POLL */
53#define EXTRA_SLOTS 1
54#else /* !WINDOWS */
55#define EXTRA_SLOTS 0
56#endif /* !WINDOWS */
57#ifdef HAVE_POLL
58 struct pollfd p[1 + EXTRA_SLOTS];
59#endif
60#undef EXTRA_SLOTS
61#ifdef HAVE_POLL
62 const bool use_poll = (MHD_ELS_POLL == daemon->event_loop_syscall);
63#else /* ! HAVE_POLL */
64 const bool use_poll = false;
65#endif /* ! HAVE_POLL */
66 bool was_suspended = false;
67
68 MHD_thread_init_(&con->pid);
69
70 while ( (! daemon->shutdown) &&
71 (MHD_REQUEST_CLOSED != con->request.state) )
72 {
73 const time_t timeout = daemon->connection_default_timeout;
74#ifdef UPGRADE_SUPPORT
75 struct MHD_UpgradeResponseHandle * const urh = con->request.urh;
76#else /* ! UPGRADE_SUPPORT */
77 static const void * const urh = NULL;
78#endif /* ! UPGRADE_SUPPORT */
79
80 if ( (con->suspended) &&
81 (NULL == urh) )
82 {
83 /* Connection was suspended, wait for resume. */
84 was_suspended = true;
85 if (! use_poll)
86 {
87 FD_ZERO (&rs);
88 if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc),
89 &rs,
90 NULL,
91 FD_SETSIZE))
92 {
93 #ifdef HAVE_MESSAGES
94 MHD_DLOG (con->daemon,
95 MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE,
96 _("Failed to add FD to fd_set\n"));
97 #endif
98 goto exit;
99 }
100 if (0 > MHD_SYS_select_ (MHD_itc_r_fd_ (daemon->itc) + 1,
101 &rs,
102 NULL,
103 NULL,
104 NULL))
105 {
106 const int err = MHD_socket_get_error_();
107
108 if (MHD_SCKT_ERR_IS_EINTR_(err))
109 continue;
110#ifdef HAVE_MESSAGES
111 MHD_DLOG (con->daemon,
112 MHD_SC_UNEXPECTED_SELECT_ERROR,
113 _("Error during select (%d): `%s'\n"),
114 err,
115 MHD_socket_strerr_ (err));
116#endif
117 break;
118 }
119 }
120#ifdef HAVE_POLL
121 else /* use_poll */
122 {
123 p[0].events = POLLIN;
124 p[0].fd = MHD_itc_r_fd_ (daemon->itc);
125 p[0].revents = 0;
126 if (0 > MHD_sys_poll_ (p,
127 1,
128 -1))
129 {
130 if (MHD_SCKT_LAST_ERR_IS_(MHD_SCKT_EINTR_))
131 continue;
132#ifdef HAVE_MESSAGES
133 MHD_DLOG (con->daemon,
134 MHD_SC_UNEXPECTED_POLL_ERROR,
135 _("Error during poll: `%s'\n"),
136 MHD_socket_last_strerr_ ());
137#endif
138 break;
139 }
140 }
141#endif /* HAVE_POLL */
142 MHD_itc_clear_ (daemon->itc);
143 continue; /* Check again for resume. */
144 } /* End of "suspended" branch. */
145
146 if (was_suspended)
147 {
148 MHD_update_last_activity_ (con); /* Reset timeout timer. */
149 /* Process response queued during suspend and update states. */
150 MHD_connection_handle_idle (con);
151 was_suspended = false;
152 }
153
154 tvp = NULL;
155
156 if ( (MHD_EVENT_LOOP_INFO_BLOCK == con->request.event_loop_info)
157#ifdef HTTPS_SUPPORT
158 || ( (con->tls_read_ready) &&
159 (MHD_EVENT_LOOP_INFO_READ == con->request.event_loop_info) )
160#endif /* HTTPS_SUPPORT */
161 )
162 {
163 /* do not block: more data may be inside of TLS buffers waiting or
164 * application must provide response data */
165 tv.tv_sec = 0;
166 tv.tv_usec = 0;
167 tvp = &tv;
168 }
169 if ( (NULL == tvp) &&
170 (timeout > 0) )
171 {
172 now = MHD_monotonic_sec_counter();
173 if (now - con->last_activity > timeout)
174 tv.tv_sec = 0;
175 else
176 {
177 const time_t seconds_left = timeout - (now - con->last_activity);
178#if !defined(_WIN32) || defined(__CYGWIN__)
179 tv.tv_sec = seconds_left;
180#else /* _WIN32 && !__CYGWIN__ */
181 if (seconds_left > TIMEVAL_TV_SEC_MAX)
182 tv.tv_sec = TIMEVAL_TV_SEC_MAX;
183 else
184 tv.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) seconds_left;
185#endif /* _WIN32 && ! __CYGWIN__ */
186 }
187 tv.tv_usec = 0;
188 tvp = &tv;
189 }
190 if (! use_poll)
191 {
192 /* use select */
193 bool err_state = false;
194
195 FD_ZERO (&rs);
196 FD_ZERO (&ws);
197 FD_ZERO (&es);
198 maxsock = MHD_INVALID_SOCKET;
199 switch (con->request.event_loop_info)
200 {
201 case MHD_EVENT_LOOP_INFO_READ:
202 if (! MHD_add_to_fd_set_ (con->socket_fd,
203 &rs,
204 &maxsock,
205 FD_SETSIZE))
206 err_state = true;
207 break;
208 case MHD_EVENT_LOOP_INFO_WRITE:
209 if (! MHD_add_to_fd_set_ (con->socket_fd,
210 &ws,
211 &maxsock,
212 FD_SETSIZE))
213 err_state = true;
214 break;
215 case MHD_EVENT_LOOP_INFO_BLOCK:
216 if (! MHD_add_to_fd_set_ (con->socket_fd,
217 &es,
218 &maxsock,
219 FD_SETSIZE))
220 err_state = true;
221 break;
222 case MHD_EVENT_LOOP_INFO_CLEANUP:
223 /* how did we get here!? */
224 goto exit;
225 }
226#if WINDOWS
227 if (MHD_ITC_IS_VALID_(daemon->itc) )
228 {
229 if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc),
230 &rs,
231 &maxsock,
232 FD_SETSIZE))
233 err_state = 1;
234 }
235#endif
236 if (err_state)
237 {
238#ifdef HAVE_MESSAGES
239 MHD_DLOG (con->daemon,
240 MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE,
241 _("Failed to add FD to fd_set\n"));
242#endif
243 goto exit;
244 }
245
246 num_ready = MHD_SYS_select_ (maxsock + 1,
247 &rs,
248 &ws,
249 NULL,
250 tvp);
251 if (num_ready < 0)
252 {
253 const int err = MHD_socket_get_error_();
254
255 if (MHD_SCKT_ERR_IS_EINTR_(err))
256 continue;
257#ifdef HAVE_MESSAGES
258 MHD_DLOG (con->daemon,
259 MHD_SC_UNEXPECTED_SELECT_ERROR,
260 _("Error during select (%d): `%s'\n"),
261 err,
262 MHD_socket_strerr_ (err));
263#endif
264 break;
265 }
266#if WINDOWS
267 /* Clear ITC before other processing so additional
268 * signals will trigger select() again */
269 if ( (MHD_ITC_IS_VALID_(daemon->itc)) &&
270 (FD_ISSET (MHD_itc_r_fd_ (daemon->itc),
271 &rs)) )
272 MHD_itc_clear_ (daemon->itc);
273#endif
274 if (MHD_NO ==
275 call_handlers (con,
276 FD_ISSET (con->socket_fd,
277 &rs),
278 FD_ISSET (con->socket_fd,
279 &ws),
280 FD_ISSET (con->socket_fd,
281 &es)) )
282 goto exit;
283 }
284#ifdef HAVE_POLL
285 else
286 {
287 /* use poll */
288 memset (&p,
289 0,
290 sizeof (p));
291 p[0].fd = con->socket_fd;
292 switch (con->request.event_loop_info)
293 {
294 case MHD_EVENT_LOOP_INFO_READ:
295 p[0].events |= POLLIN | MHD_POLL_EVENTS_ERR_DISC;
296 break;
297 case MHD_EVENT_LOOP_INFO_WRITE:
298 p[0].events |= POLLOUT | MHD_POLL_EVENTS_ERR_DISC;
299 break;
300 case MHD_EVENT_LOOP_INFO_BLOCK:
301 p[0].events |= MHD_POLL_EVENTS_ERR_DISC;
302 break;
303 case MHD_EVENT_LOOP_INFO_CLEANUP:
304 /* how did we get here!? */
305 goto exit;
306 }
307#if WINDOWS
308 extra_slot = 0;
309 if (MHD_ITC_IS_VALID_(daemon->itc))
310 {
311 p[1].events |= POLLIN;
312 p[1].fd = MHD_itc_r_fd_ (daemon->itc);
313 p[1].revents = 0;
314 extra_slot = 1;
315 }
316#endif
317 if (MHD_sys_poll_ (p,
318#if WINDOWS
319 1 + extra_slot,
320#else
321 1,
322#endif
323 (NULL == tvp) ? -1 : tv.tv_sec * 1000) < 0)
324 {
325 if (MHD_SCKT_LAST_ERR_IS_(MHD_SCKT_EINTR_))
326 continue;
327#ifdef HAVE_MESSAGES
328 MHD_DLOG (con->daemon,
329 MHD_SC_UNEXPECTED_POLL_ERROR,
330 _("Error during poll: `%s'\n"),
331 MHD_socket_last_strerr_ ());
332#endif
333 break;
334 }
335#if WINDOWS
336 /* Clear ITC before other processing so additional
337 * signals will trigger poll() again */
338 if ( (MHD_ITC_IS_VALID_(daemon->itc)) &&
339 (0 != (p[1].revents & (POLLERR | POLLHUP | POLLIN))) )
340 MHD_itc_clear_ (daemon->itc);
341#endif
342 if (MHD_NO ==
343 call_handlers (con,
344 0 != (p[0].revents & POLLIN),
345 0 != (p[0].revents & POLLOUT),
346 0 != (p[0].revents & (POLLERR | MHD_POLL_REVENTS_ERR_DISC))))
347 goto exit;
348 }
349#endif
350#ifdef UPGRADE_SUPPORT
351 if (MHD_REQUEST_UPGRADE == con->request.state)
352 {
353 /* Normal HTTP processing is finished,
354 * notify application. */
355 if ( (NULL != con->request.response->termination_cb) &&
356 (con->request.client_aware) )
357 con->request.response->termination_cb
358 (con->request.response->termination_cb_cls,
359 MHD_REQUEST_TERMINATED_COMPLETED_OK,
360 con->request.client_context);
361 con->request.client_aware = false;
362
363 thread_main_connection_upgrade (con);
364 /* MHD_connection_finish_forward_() was called by thread_main_connection_upgrade(). */
365
366 /* "Upgraded" data will not be used in this thread from this point. */
367 con->request.urh->clean_ready = true;
368 /* If 'urh->was_closed' set to true, connection will be
369 * moved immediately to cleanup list. Otherwise connection
370 * will stay in suspended list until 'urh' will be marked
371 * with 'was_closed' by application. */
372 MHD_resume_connection (con);
373
374 /* skip usual clean up */
375 return (MHD_THRD_RTRN_TYPE_) 0;
376 }
377#endif /* UPGRADE_SUPPORT */
378 }
379 if (MHD_REQUEST_IN_CLEANUP != con->request.state)
380 {
381#if DEBUG_CLOSE
382#ifdef HAVE_MESSAGES
383 MHD_DLOG (con->daemon,
384 MHD_SC_THREAD_TERMINATING,
385 _("Processing thread terminating. Closing connection\n"));
386#endif
387#endif
388 if (MHD_REQUEST_CLOSED != con->request.state)
389 MHD_connection_close_ (con,
390 (daemon->shutdown) ?
391 MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN:
392 MHD_REQUEST_TERMINATED_WITH_ERROR);
393 MHD_connection_handle_idle (con);
394 }
395exit:
396 if (NULL != con->request.response)
397 {
398 MHD_response_queue_for_destroy (con->request.response);
399 con->request.response = NULL;
400 }
401
402 if (MHD_INVALID_SOCKET != con->socket_fd)
403 {
404 shutdown (con->socket_fd,
405 SHUT_WR);
406 /* 'socket_fd' can be used in other thread to signal shutdown.
407 * To avoid data races, do not close socket here. Daemon will
408 * use more connections only after cleanup anyway. */
409 }
410 return (MHD_THRD_RTRN_TYPE_) 0;
411}
412
413
414/**
415 * Add another client connection to the set of connections
416 * managed by MHD. This API is usually not needed (since
417 * MHD will accept inbound connections on the server socket).
418 * Use this API in special cases, for example if your HTTP
419 * server is behind NAT and needs to connect out to the
420 * HTTP client.
421 *
422 * The given client socket will be managed (and closed!) by MHD after
423 * this call and must no longer be used directly by the application
424 * afterwards.
425 *
426 * @param daemon daemon that manages the connection
427 * @param client_socket socket to manage (MHD will expect
428 * to receive an HTTP request from this socket next).
429 * @param addr IP address of the client
430 * @param addrlen number of bytes in @a addr
431 * @param external_add perform additional operations needed due
432 * to the application calling us directly
433 * @param non_blck indicate that socket in non-blocking mode
434 * @return #MHD_SC_OK on success
435 */
436static enum MHD_StatusCode
437internal_add_connection (struct MHD_Daemon *daemon,
438 MHD_socket client_socket,
439 const struct sockaddr *addr,
440 socklen_t addrlen,
441 bool external_add,
442 bool non_blck)
443{
444 enum MHD_StatusCode sc;
445 struct MHD_Connection *connection;
446 int eno = 0;
447
448 /* Direct add to master daemon could happen only with "external" add mode. */
449 mhd_assert ( (NULL == daemon->worker_pool) ||
450 (external_add) );
451 if ( (external_add) &&
452 (NULL != daemon->worker_pool) )
453 {
454 unsigned int i;
455
456 /* have a pool, try to find a pool with capacity; we use the
457 socket as the initial offset into the pool for load
458 balancing */
459 for (i = 0; i < daemon->worker_pool_size; ++i)
460 {
461 struct MHD_Daemon * const worker =
462 &daemon->worker_pool[(i + client_socket) % daemon->worker_pool_size];
463 if (worker->connections < worker->global_connection_limit)
464 return internal_add_connection (worker,
465 client_socket,
466 addr,
467 addrlen,
468 true,
469 non_blck);
470 }
471 /* all pools are at their connection limit, must refuse */
472 MHD_socket_close_chk_ (client_socket);
473#if ENFILE
474 errno = ENFILE;
475#endif
476 return MHD_SC_LIMIT_CONNECTIONS_REACHED;
477 }
478
479 if ( (! MHD_SCKT_FD_FITS_FDSET_(client_socket,
480 NULL)) &&
481 (MHD_ELS_SELECT == daemon->event_loop_syscall) )
482 {
483#ifdef HAVE_MESSAGES
484 MHD_DLOG (daemon,
485 MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE,
486 _("Socket descriptor larger than FD_SETSIZE: %d > %d\n"),
487 (int) client_socket,
488 (int) FD_SETSIZE);
489#endif
490 MHD_socket_close_chk_ (client_socket);
491#if EINVAL
492 errno = EINVAL;
493#endif
494 return MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE;
495 }
496
497#ifdef MHD_socket_nosignal_
498 if (! MHD_socket_nosignal_ (client_socket))
499 {
500#ifdef HAVE_MESSAGES
501 MHD_DLOG (daemon,
502 MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED,
503 _("Failed to set SO_NOSIGPIPE on accepted socket: %s\n"),
504 MHD_socket_last_strerr_());
505#endif
506#ifndef MSG_NOSIGNAL
507 /* Cannot use socket as it can produce SIGPIPE. */
508#ifdef ENOTSOCK
509 errno = ENOTSOCK;
510#endif /* ENOTSOCK */
511 return MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED;
512#endif /* ! MSG_NOSIGNAL */
513 }
514#endif /* MHD_socket_nosignal_ */
515
516
517#ifdef HAVE_MESSAGES
518#if DEBUG_CONNECT
519 MHD_DLOG (daemon,
520 MHD_SC_CONNECTION_ACCEPTED,
521 _("Accepted connection on socket %d\n"),
522 client_socket);
523#endif
524#endif
525 if ( (daemon->connections == daemon->global_connection_limit) ||
526 (MHD_NO == MHD_ip_limit_add (daemon,
527 addr,
528 addrlen)) )
529 {
530 /* above connection limit - reject */
531#ifdef HAVE_MESSAGES
532 MHD_DLOG (daemon,
533 MHD_SC_LIMIT_CONNECTIONS_REACHED,
534 _("Server reached connection limit. Closing inbound connection.\n"));
535#endif
536 MHD_socket_close_chk_ (client_socket);
537#if ENFILE
538 errno = ENFILE;
539#endif
540 return MHD_SC_LIMIT_CONNECTIONS_REACHED;
541 }
542
543 /* apply connection acceptance policy if present */
544 if ( (NULL != daemon->accept_policy_cb) &&
545 (MHD_NO ==
546 daemon->accept_policy_cb (daemon->accept_policy_cb_cls,
547 addr,
548 addrlen)) )
549 {
550#if DEBUG_CLOSE
551#ifdef HAVE_MESSAGES
552 MHD_DLOG (daemon,
553 MHD_SC_ACCEPT_POLICY_REJECTED,
554 _("Connection rejected by application. Closing connection.\n"));
555#endif
556#endif
557 MHD_socket_close_chk_ (client_socket);
558 MHD_ip_limit_del (daemon,
559 addr,
560 addrlen);
561#if EACCESS
562 errno = EACCESS;
563#endif
564 return MHD_SC_ACCEPT_POLICY_REJECTED;
565 }
566
567 if (NULL ==
568 (connection = MHD_calloc_ (1,
569 sizeof (struct MHD_Connection))))
570 {
571 eno = errno;
572#ifdef HAVE_MESSAGES
573 MHD_DLOG (daemon,
574 MHD_SC_CONNECTION_MALLOC_FAILURE,
575 "Error allocating memory: %s\n",
576 MHD_strerror_ (errno));
577#endif
578 MHD_socket_close_chk_ (client_socket);
579 MHD_ip_limit_del (daemon,
580 addr,
581 addrlen);
582 errno = eno;
583 return MHD_SC_CONNECTION_MALLOC_FAILURE;
584 }
585 connection->request.pool
586 = MHD_pool_create (daemon->connection_memory_limit_b);
587 if (NULL == connection->request.pool)
588 {
589#ifdef HAVE_MESSAGES
590 MHD_DLOG (daemon,
591 MHD_SC_POOL_MALLOC_FAILURE,
592 _("Error allocating memory: %s\n"),
593 MHD_strerror_ (errno));
594#endif
595 MHD_socket_close_chk_ (client_socket);
596 MHD_ip_limit_del (daemon,
597 addr,
598 addrlen);
599 free (connection);
600#if ENOMEM
601 errno = ENOMEM;
602#endif
603 return MHD_SC_POOL_MALLOC_FAILURE;
604 }
605
606 connection->connection_timeout = daemon->connection_default_timeout;
607 memcpy (&connection->addr,
608 addr,
609 addrlen);
610 connection->addr_len = addrlen;
611 connection->socket_fd = client_socket;
612 connection->sk_nonblck = non_blck;
613 connection->daemon = daemon;
614 connection->last_activity = MHD_monotonic_sec_counter();
615
616#ifdef HTTPS_SUPPORT
617 if (NULL != daemon->tls_api)
618 {
619 connection->tls_cs
620 = daemon->tls_api->setup_connection (daemon->tls_api->cls,
621 NULL /* FIXME */);
622 if (NULL == connection->tls_cs)
623 {
624 eno = EINVAL;
625 sc = -1; // FIXME!
626 goto cleanup;
627 }
628 }
629 else
630#endif /* ! HTTPS_SUPPORT */
631 /* set default connection handlers */
632 MHD_set_http_callbacks_ (connection);
633
634 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
635 /* Firm check under lock. */
636 if (daemon->connections >= daemon->global_connection_limit)
637 {
638 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
639 /* above connection limit - reject */
640#ifdef HAVE_MESSAGES
641 MHD_DLOG (daemon,
642 MHD_SC_LIMIT_CONNECTIONS_REACHED,
643 _("Server reached connection limit. Closing inbound connection.\n"));
644#endif
645#if ENFILE
646 eno = ENFILE;
647#endif
648 sc = MHD_SC_LIMIT_CONNECTIONS_REACHED;
649 goto cleanup;
650 }
651 daemon->connections++;
652 if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model)
653 {
654 XDLL_insert (daemon->normal_timeout_head,
655 daemon->normal_timeout_tail,
656 connection);
657 }
658 DLL_insert (daemon->connections_head,
659 daemon->connections_tail,
660 connection);
661 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
662
663 if (NULL != daemon->notify_connection_cb)
664 daemon->notify_connection_cb (daemon->notify_connection_cb_cls,
665 connection,
666 MHD_CONNECTION_NOTIFY_STARTED);
667
668 /* attempt to create handler thread */
669 if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model)
670 {
671 if (! MHD_create_named_thread_ (&connection->pid,
672 "MHD-connection",
673 daemon->thread_stack_limit_b,
674 &thread_main_handle_connection,
675 connection))
676 {
677 eno = errno;
678#ifdef HAVE_MESSAGES
679 MHD_DLOG (daemon,
680 MHD_SC_THREAD_LAUNCH_FAILURE,
681 "Failed to create a thread: %s\n",
682 MHD_strerror_ (eno));
683#endif
684 sc = MHD_SC_THREAD_LAUNCH_FAILURE;
685 goto cleanup;
686 }
687 }
688 else
689 {
690 connection->pid = daemon->pid;
691 }
692#ifdef EPOLL_SUPPORT
693 if (MHD_ELS_EPOLL == daemon->event_loop_syscall)
694 {
695 if ( (! daemon->enable_turbo) ||
696 (external_add))
697 { /* Do not manipulate EReady DL-list in 'external_add' mode. */
698 struct epoll_event event;
699
700 event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
701 event.data.ptr = connection;
702 if (0 != epoll_ctl (daemon->epoll_fd,
703 EPOLL_CTL_ADD,
704 client_socket,
705 &event))
706 {
707 eno = errno;
708#ifdef HAVE_MESSAGES
709 MHD_DLOG (daemon,
710 MHD_SC_EPOLL_CTL_ADD_FAILED,
711 _("Call to epoll_ctl failed: %s\n"),
712 MHD_socket_last_strerr_ ());
713#endif
714 sc = MHD_SC_EPOLL_CTL_ADD_FAILED;
715 goto cleanup;
716 }
717 connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
718 }
719 else
720 {
721 connection->epoll_state |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY
722 | MHD_EPOLL_STATE_IN_EREADY_EDLL;
723 EDLL_insert (daemon->eready_head,
724 daemon->eready_tail,
725 connection);
726 }
727 }
728 else /* This 'else' is combined with next 'if'. */
729#endif
730 if ( (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) &&
731 (external_add) &&
732 (MHD_ITC_IS_VALID_(daemon->itc)) &&
733 (! MHD_itc_activate_ (daemon->itc,
734 "n")) )
735 {
736#ifdef HAVE_MESSAGES
737 MHD_DLOG (daemon,
738 MHD_SC_ITC_USE_FAILED,
739 _("Failed to signal new connection via inter-thread communication channel."));
740#endif
741 sc = MHD_SC_ITC_USE_FAILED;
742 }
743 return MHD_SC_OK;
744
745 cleanup:
746 if (NULL != daemon->notify_connection_cb)
747 daemon->notify_connection_cb (daemon->notify_connection_cb_cls,
748 connection,
749 MHD_CONNECTION_NOTIFY_CLOSED);
750#ifdef HTTPS_SUPPORT
751 if (NULL != connection->tls_cs)
752 daemon->tls_api->teardown_connection (daemon->tls_api->cls,
753 connection->tls_cs);
754#endif /* HTTPS_SUPPORT */
755 MHD_socket_close_chk_ (client_socket);
756 MHD_ip_limit_del (daemon,
757 addr,
758 addrlen);
759 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
760 if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model)
761 {
762 XDLL_remove (daemon->normal_timeout_head,
763 daemon->normal_timeout_tail,
764 connection);
765 }
766 DLL_remove (daemon->connections_head,
767 daemon->connections_tail,
768 connection);
769 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
770 MHD_pool_destroy (connection->request.pool);
771 free (connection);
772 if (0 != eno)
773 errno = eno;
774 else
775 errno = EINVAL;
776 return sc;
777}
778
779
780/**
781 * Add another client connection to the set of connections managed by
782 * MHD. This API is usually not needed (since MHD will accept inbound
783 * connections on the server socket). Use this API in special cases,
784 * for example if your HTTP server is behind NAT and needs to connect
785 * out to the HTTP client, or if you are building a proxy.
786 *
787 * If you use this API in conjunction with a internal select or a
788 * thread pool, you must set the option #MHD_USE_ITC to ensure that
789 * the freshly added connection is immediately processed by MHD.
790 *
791 * The given client socket will be managed (and closed!) by MHD after
792 * this call and must no longer be used directly by the application
793 * afterwards.
794 *
795 * @param daemon daemon that manages the connection
796 * @param client_socket socket to manage (MHD will expect
797 * to receive an HTTP request from this socket next).
798 * @param addr IP address of the client
799 * @param addrlen number of bytes in @a addr
800 * @return #MHD_YES on success, #MHD_NO if this daemon could
801 * not handle the connection (i.e. malloc() failed, etc).
802 * The socket will be closed in any case; `errno` is
803 * set to indicate further details about the error.
804 * @ingroup specialized
805 */
806enum MHD_StatusCode
807MHD_daemon_add_connection (struct MHD_Daemon *daemon,
808 MHD_socket client_socket,
809 const struct sockaddr *addr,
810 socklen_t addrlen)
811{
812 bool sk_nonbl;
813
814 if (! MHD_socket_nonblocking_ (client_socket))
815 {
816#ifdef HAVE_MESSAGES
817 MHD_DLOG (daemon,
818 MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED,
819 _("Failed to set nonblocking mode on new client socket: %s\n"),
820 MHD_socket_last_strerr_());
821#endif
822 sk_nonbl = false;
823 }
824 else
825 {
826 sk_nonbl = true;
827 }
828
829 if ( (daemon->enable_turbo) &&
830 (! MHD_socket_noninheritable_ (client_socket)) )
831 {
832#ifdef HAVE_MESSAGES
833 MHD_DLOG (daemon,
834 MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED,
835 _("Failed to set noninheritable mode on new client socket.\n"));
836#endif
837 }
838 return internal_add_connection (daemon,
839 client_socket,
840 addr,
841 addrlen,
842 true,
843 sk_nonbl);
844}
845
846
847/**
848 * Accept an incoming connection and create the MHD_Connection object
849 * for it. This function also enforces policy by way of checking with
850 * the accept policy callback. @remark To be called only from thread
851 * that process daemon's select()/poll()/etc.
852 *
853 * @param daemon handle with the listen socket
854 * @return #MHD_SC_OK on success
855 */
856enum MHD_StatusCode
857MHD_accept_connection_ (struct MHD_Daemon *daemon)
858{
859 struct sockaddr_storage addrstorage;
860 struct sockaddr *addr = (struct sockaddr *) &addrstorage;
861 socklen_t addrlen;
862 MHD_socket s;
863 MHD_socket fd;
864 bool sk_nonbl;
865
866 addrlen = sizeof (addrstorage);
867 memset (addr,
868 0,
869 sizeof (addrstorage));
870 if ( (MHD_INVALID_SOCKET == (fd = daemon->listen_socket)) ||
871 (daemon->was_quiesced) )
872 return MHD_SC_DAEMON_ALREADY_QUIESCED;
873#ifdef USE_ACCEPT4
874 s = accept4 (fd,
875 addr,
876 &addrlen,
877 MAYBE_SOCK_CLOEXEC | MAYBE_SOCK_NONBLOCK);
878 sk_nonbl = (0 != MAYBE_SOCK_NONBLOCK);
879#else /* ! USE_ACCEPT4 */
880 s = accept (fd,
881 addr,
882 &addrlen);
883 sk_nonbl = false;
884#endif /* ! USE_ACCEPT4 */
885 if ( (MHD_INVALID_SOCKET == s) ||
886 (addrlen <= 0) )
887 {
888 const int err = MHD_socket_get_error_ ();
889
890 /* This could be a common occurance with multiple worker threads */
891 if (MHD_SCKT_ERR_IS_ (err,
892 MHD_SCKT_EINVAL_))
893 return MHD_SC_DAEMON_ALREADY_SHUTDOWN; /* can happen during shutdown, let's hope this is the cause... */
894 if (MHD_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT_(err))
895 return MHD_SC_ACCEPT_FAST_DISCONNECT; /* do not print error if client just disconnected early */
896 if (MHD_SCKT_ERR_IS_EAGAIN_ (err) )
897 return MHD_SC_ACCEPT_FAILED_EAGAIN;
898 if (MHD_INVALID_SOCKET != s)
899 {
900 MHD_socket_close_chk_ (s);
901 }
902 if ( MHD_SCKT_ERR_IS_LOW_RESOURCES_ (err) )
903 {
904 /* system/process out of resources */
905 if (0 == daemon->connections)
906 {
907#ifdef HAVE_MESSAGES
908 /* Not setting 'at_limit' flag, as there is no way it
909 would ever be cleared. Instead trying to produce
910 bit fat ugly warning. */
911 MHD_DLOG (daemon,
912 MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY,
913 _("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"));
914#endif
915 return MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY;
916 }
917 else
918 {
919 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
920 daemon->at_limit = true;
921 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
922#ifdef HAVE_MESSAGES
923 MHD_DLOG (daemon,
924 MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED,
925 _("Hit process or system resource limit at %u connections, temporarily suspending accept(). Consider setting a lower MHD_OPTION_CONNECTION_LIMIT.\n"),
926 (unsigned int) daemon->connections);
927#endif
928 return MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED;
929 }
930 }
931#ifdef HAVE_MESSAGES
932 MHD_DLOG (daemon,
933 MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY,
934 _("Error accepting connection: %s\n"),
935 MHD_socket_strerr_(err));
936#endif
937 return MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY;
938 }
939#if !defined(USE_ACCEPT4) || !defined(HAVE_SOCK_NONBLOCK)
940 if (! MHD_socket_nonblocking_ (s))
941 {
942#ifdef HAVE_MESSAGES
943 MHD_DLOG (daemon,
944 MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED,
945 _("Failed to set nonblocking mode on incoming connection socket: %s\n"),
946 MHD_socket_last_strerr_());
947#endif
948 }
949 else
950 sk_nonbl = true;
951#endif /* !USE_ACCEPT4 || !HAVE_SOCK_NONBLOCK */
952#if !defined(USE_ACCEPT4) || !defined(SOCK_CLOEXEC)
953 if (! MHD_socket_noninheritable_ (s))
954 {
955#ifdef HAVE_MESSAGES
956 MHD_DLOG (daemon,
957 MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED,
958 _("Failed to set noninheritable mode on incoming connection socket.\n"));
959#endif
960 }
961#endif /* !USE_ACCEPT4 || !SOCK_CLOEXEC */
962#ifdef HAVE_MESSAGES
963#if DEBUG_CONNECT
964 MHD_DLOG (daemon,
965 MHD_SC_CONNECTION_ACCEPTED,
966 _("Accepted connection on socket %d\n"),
967 s);
968#endif
969#endif
970 return internal_add_connection (daemon,
971 s,
972 addr,
973 addrlen,
974 false,
975 sk_nonbl);
976}
977
978/* end of connection_add.c */
diff --git a/src/lib/connection_add.h b/src/lib/connection_add.h
new file mode 100644
index 00000000..6957fd77
--- /dev/null
+++ b/src/lib/connection_add.h
@@ -0,0 +1,41 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19/**
20 * @file lib/connection_add.h
21 * @brief functions to add connection to our active set
22 * @author Christian Grothoff
23 */
24
25#ifndef CONNECTION_ADD_H
26#define CONNECTION_ADD_H
27
28/**
29 * Accept an incoming connection and create the MHD_Connection object
30 * for it. This function also enforces policy by way of checking with
31 * the accept policy callback. @remark To be called only from thread
32 * that process daemon's select()/poll()/etc.
33 *
34 * @param daemon handle with the listen socket
35 * @return #MHD_SC_OK on success
36 */
37enum MHD_StatusCode
38MHD_accept_connection_ (struct MHD_Daemon *daemon)
39 MHD_NONNULL (1);
40
41#endif
diff --git a/src/lib/daemon_add_connection.c b/src/lib/daemon_add_connection.c
deleted file mode 100644
index 30492d7c..00000000
--- a/src/lib/daemon_add_connection.c
+++ /dev/null
@@ -1,63 +0,0 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
20/**
21 * @file lib/daemon_add_connection.c
22 * @brief main functions to add a connection to be managed by a daemon
23 * @author Christian Grothoff
24 */
25#include "internal.h"
26
27
28/**
29 * Add another client connection to the set of connections managed by
30 * MHD. This API is usually not needed (since MHD will accept inbound
31 * connections on the server socket). Use this API in special cases,
32 * for example if your HTTP server is behind NAT and needs to connect
33 * out to the HTTP client, or if you are building a proxy.
34 *
35 * If you use this API in conjunction with a internal select or a
36 * thread pool, you must set the option
37 * #MHD_USE_ITC to ensure that the freshly added
38 * connection is immediately processed by MHD.
39 *
40 * The given client socket will be managed (and closed!) by MHD after
41 * this call and must no longer be used directly by the application
42 * afterwards.
43 *
44 * @param daemon daemon that manages the connection
45 * @param client_socket socket to manage (MHD will expect
46 * to receive an HTTP request from this socket next).
47 * @param addr IP address of the client
48 * @param addrlen number of bytes in @a addr
49 * @return #MHD_SC_OK on success
50 * The socket will be closed in any case; `errno` is
51 * set to indicate further details about the error.
52 * @ingroup specialized
53 */
54_MHD_EXTERN enum MHD_StatusCode
55MHD_daemon_add_connection (struct MHD_Daemon *daemon,
56 MHD_socket client_socket,
57 const struct sockaddr *addr,
58 socklen_t addrlen)
59{
60 return -1;
61}
62
63/* end of daemon_add_connection.c */
diff --git a/src/lib/daemon_epoll.c b/src/lib/daemon_epoll.c
index 2acb3cc6..49f15307 100644
--- a/src/lib/daemon_epoll.c
+++ b/src/lib/daemon_epoll.c
@@ -385,7 +385,7 @@ MHD_daemon_epoll_ (struct MHD_Daemon *daemon,
385 * be accepted on next turn (level trigger is used for listen 385 * be accepted on next turn (level trigger is used for listen
386 * socket). */ 386 * socket). */
387 while ( (MHD_YES == 387 while ( (MHD_YES ==
388 MHD_accept_connection (daemon)) && 388 MHD_accept_connection_ (daemon)) &&
389 (series_length < 10) && 389 (series_length < 10) &&
390 (daemon->connections < daemon->global_connection_limit) && 390 (daemon->connections < daemon->global_connection_limit) &&
391 (! daemon->at_limit) ) 391 (! daemon->at_limit) )
diff --git a/src/lib/daemon_ip_limit.c b/src/lib/daemon_ip_limit.c
new file mode 100644
index 00000000..f33b8193
--- /dev/null
+++ b/src/lib/daemon_ip_limit.c
@@ -0,0 +1,280 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
20/**
21 * @file lib/daemon_ip_limit.c
22 * @brief counting of connections per IP
23 * @author Christian Grothoff
24 */
25#include "internal.h"
26#include "daemon_ip_limit.h"
27
28/**
29 * Maintain connection count for single address.
30 */
31struct MHD_IPCount
32{
33 /**
34 * Address family. AF_INET or AF_INET6 for now.
35 */
36 int family;
37
38 /**
39 * Actual address.
40 */
41 union
42 {
43 /**
44 * IPv4 address.
45 */
46 struct in_addr ipv4;
47#if HAVE_INET6
48 /**
49 * IPv6 address.
50 */
51 struct in6_addr ipv6;
52#endif
53 } addr;
54
55 /**
56 * Counter.
57 */
58 unsigned int count;
59};
60
61
62/**
63 * Lock shared structure for IP connection counts and connection DLLs.
64 *
65 * @param daemon handle to daemon where lock is
66 */
67static void
68MHD_ip_count_lock (struct MHD_Daemon *daemon)
69{
70 MHD_mutex_lock_chk_(&daemon->per_ip_connection_mutex);
71}
72
73
74/**
75 * Unlock shared structure for IP connection counts and connection DLLs.
76 *
77 * @param daemon handle to daemon where lock is
78 */
79static void
80MHD_ip_count_unlock (struct MHD_Daemon *daemon)
81{
82 MHD_mutex_unlock_chk_(&daemon->per_ip_connection_mutex);
83}
84
85
86/**
87 * Tree comparison function for IP addresses (supplied to tsearch() family).
88 * We compare everything in the struct up through the beginning of the
89 * 'count' field.
90 *
91 * @param a1 first address to compare
92 * @param a2 second address to compare
93 * @return -1, 0 or 1 depending on result of compare
94 */
95static int
96MHD_ip_addr_compare (const void *a1,
97 const void *a2)
98{
99 return memcmp (a1,
100 a2,
101 offsetof (struct MHD_IPCount,
102 count));
103}
104
105
106/**
107 * Parse address and initialize @a key using the address.
108 *
109 * @param addr address to parse
110 * @param addrlen number of bytes in @a addr
111 * @param key where to store the parsed address
112 * @return #MHD_YES on success and #MHD_NO otherwise (e.g., invalid address type)
113 */
114static int
115MHD_ip_addr_to_key (const struct sockaddr *addr,
116 socklen_t addrlen,
117 struct MHD_IPCount *key)
118{
119 memset(key,
120 0,
121 sizeof(*key));
122
123 /* IPv4 addresses */
124 if (sizeof (struct sockaddr_in) == addrlen)
125 {
126 const struct sockaddr_in *addr4 = (const struct sockaddr_in*) addr;
127
128 key->family = AF_INET;
129 memcpy (&key->addr.ipv4,
130 &addr4->sin_addr,
131 sizeof(addr4->sin_addr));
132 return MHD_YES;
133 }
134
135#if HAVE_INET6
136 /* IPv6 addresses */
137 if (sizeof (struct sockaddr_in6) == addrlen)
138 {
139 const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*) addr;
140
141 key->family = AF_INET6;
142 memcpy (&key->addr.ipv6,
143 &addr6->sin6_addr,
144 sizeof(addr6->sin6_addr));
145 return MHD_YES;
146 }
147#endif
148
149 /* Some other address */
150 return MHD_NO;
151}
152
153
154/**
155 * Check if IP address is over its limit in terms of the number
156 * of allowed concurrent connections. If the IP is still allowed,
157 * increments the connection counter.
158 *
159 * @param daemon handle to daemon where connection counts are tracked
160 * @param addr address to add (or increment counter)
161 * @param addrlen number of bytes in @a addr
162 * @return Return #MHD_YES if IP below limit, #MHD_NO if IP has surpassed limit.
163 * Also returns #MHD_NO if fails to allocate memory.
164 */
165int
166MHD_ip_limit_add (struct MHD_Daemon *daemon,
167 const struct sockaddr *addr,
168 socklen_t addrlen)
169{
170 struct MHD_IPCount *key;
171 void **nodep;
172 void *node;
173 int result;
174
175 daemon = MHD_get_master (daemon);
176 /* Ignore if no connection limit assigned */
177 if (0 == daemon->ip_connection_limit)
178 return MHD_YES;
179
180 if (NULL == (key = malloc (sizeof(*key))))
181 return MHD_NO;
182
183 /* Initialize key */
184 if (MHD_NO == MHD_ip_addr_to_key (addr,
185 addrlen,
186 key))
187 {
188 /* Allow unhandled address types through */
189 free (key);
190 return MHD_YES;
191 }
192 MHD_ip_count_lock (daemon);
193
194 /* Search for the IP address */
195 if (NULL == (nodep = tsearch (key,
196 &daemon->per_ip_connection_count,
197 &MHD_ip_addr_compare)))
198 {
199#ifdef HAVE_MESSAGES
200 MHD_DLOG (daemon,
201 MHD_SC_IP_COUNTER_FAILURE,
202 _("Failed to add IP connection count node\n"));
203#endif
204 MHD_ip_count_unlock (daemon);
205 free (key);
206 return MHD_NO;
207 }
208 node = *nodep;
209 /* If we got an existing node back, free the one we created */
210 if (node != key)
211 free(key);
212 key = (struct MHD_IPCount *) node;
213 /* Test if there is room for another connection; if so,
214 * increment count */
215 result = (key->count < daemon->ip_connection_limit) ? MHD_YES : MHD_NO;
216 if (MHD_YES == result)
217 ++key->count;
218
219 MHD_ip_count_unlock (daemon);
220 return result;
221}
222
223
224/**
225 * Decrement connection count for IP address, removing from table
226 * count reaches 0.
227 *
228 * @param daemon handle to daemon where connection counts are tracked
229 * @param addr address to remove (or decrement counter)
230 * @param addrlen number of bytes in @a addr
231 */
232void
233MHD_ip_limit_del (struct MHD_Daemon *daemon,
234 const struct sockaddr *addr,
235 socklen_t addrlen)
236{
237 struct MHD_IPCount search_key;
238 struct MHD_IPCount *found_key;
239 void **nodep;
240
241 daemon = MHD_get_master (daemon);
242 /* Ignore if no connection limit assigned */
243 if (0 == daemon->ip_connection_limit)
244 return;
245 /* Initialize search key */
246 if (MHD_NO == MHD_ip_addr_to_key (addr,
247 addrlen,
248 &search_key))
249 return;
250
251 MHD_ip_count_lock (daemon);
252
253 /* Search for the IP address */
254 if (NULL == (nodep = tfind (&search_key,
255 &daemon->per_ip_connection_count,
256 &MHD_ip_addr_compare)))
257 {
258 /* Something's wrong if we couldn't find an IP address
259 * that was previously added */
260 MHD_PANIC (_("Failed to find previously-added IP address\n"));
261 }
262 found_key = (struct MHD_IPCount *) *nodep;
263 /* Validate existing count for IP address */
264 if (0 == found_key->count)
265 {
266 MHD_PANIC (_("Previously-added IP address had counter of zero\n"));
267 }
268 /* Remove the node entirely if count reduces to 0 */
269 if (0 == --found_key->count)
270 {
271 tdelete (found_key,
272 &daemon->per_ip_connection_count,
273 &MHD_ip_addr_compare);
274 free (found_key);
275 }
276
277 MHD_ip_count_unlock (daemon);
278}
279
280/* end of daemon_ip_limit.c */
diff --git a/src/lib/daemon_ip_limit.h b/src/lib/daemon_ip_limit.h
new file mode 100644
index 00000000..5f22db05
--- /dev/null
+++ b/src/lib/daemon_ip_limit.h
@@ -0,0 +1,60 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19/**
20 * @file lib/daemon_ip_limit.h
21 * @brief counting of connections per IP
22 * @author Christian Grothoff
23 */
24
25#ifndef DAEMON_IP_LIMIT_H
26#define DAEMON_IP_LIMIT_H
27
28/**
29 * Check if IP address is over its limit in terms of the number
30 * of allowed concurrent connections. If the IP is still allowed,
31 * increments the connection counter.
32 *
33 * @param daemon handle to daemon where connection counts are tracked
34 * @param addr address to add (or increment counter)
35 * @param addrlen number of bytes in @a addr
36 * @return Return #MHD_YES if IP below limit, #MHD_NO if IP has surpassed limit.
37 * Also returns #MHD_NO if fails to allocate memory.
38 */
39int
40MHD_ip_limit_add (struct MHD_Daemon *daemon,
41 const struct sockaddr *addr,
42 socklen_t addrlen)
43 MHD_NONNULL (1,2);
44
45
46/**
47 * Decrement connection count for IP address, removing from table
48 * count reaches 0.
49 *
50 * @param daemon handle to daemon where connection counts are tracked
51 * @param addr address to remove (or decrement counter)
52 * @param addrlen number of bytes in @a addr
53 */
54void
55MHD_ip_limit_del (struct MHD_Daemon *daemon,
56 const struct sockaddr *addr,
57 socklen_t addrlen)
58 MHD_NONNULL (1,2);
59
60#endif
diff --git a/src/lib/daemon_poll.c b/src/lib/daemon_poll.c
index 118c0579..d5660715 100644
--- a/src/lib/daemon_poll.c
+++ b/src/lib/daemon_poll.c
@@ -333,7 +333,7 @@ MHD_daemon_poll_all_ (struct MHD_Daemon *daemon,
333 /* handle 'listen' FD */ 333 /* handle 'listen' FD */
334 if ( (-1 != poll_listen) && 334 if ( (-1 != poll_listen) &&
335 (0 != (p[poll_listen].revents & POLLIN)) ) 335 (0 != (p[poll_listen].revents & POLLIN)) )
336 (void) MHD_accept_connection (daemon); 336 (void) MHD_accept_connection_ (daemon);
337 337
338 free(p); 338 free(p);
339 } 339 }
@@ -418,7 +418,7 @@ MHD_daemon_poll_listen_socket_ (struct MHD_Daemon *daemon,
418 return MHD_SC_DAEMON_ALREADY_SHUTDOWN; 418 return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
419 if ( (-1 != poll_listen) && 419 if ( (-1 != poll_listen) &&
420 (0 != (p[poll_listen].revents & POLLIN)) ) 420 (0 != (p[poll_listen].revents & POLLIN)) )
421 (void) MHD_accept_connection (daemon); 421 (void) MHD_accept_connection_ (daemon);
422 return MHD_SC_OK; 422 return MHD_SC_OK;
423} 423}
424#endif 424#endif
diff --git a/src/lib/daemon_select.c b/src/lib/daemon_select.c
index f61ba36a..abd6eacf 100644
--- a/src/lib/daemon_select.c
+++ b/src/lib/daemon_select.c
@@ -425,7 +425,7 @@ internal_run_from_select (struct MHD_Daemon *daemon,
425 (! daemon->was_quiesced) && 425 (! daemon->was_quiesced) &&
426 (FD_ISSET (ds, 426 (FD_ISSET (ds,
427 read_fd_set)) ) 427 read_fd_set)) )
428 (void) MHD_accept_connection (daemon); 428 (void) MHD_accept_connection_ (daemon);
429 429
430 if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) 430 if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model)
431 { 431 {
diff --git a/src/lib/internal.h b/src/lib/internal.h
index 92b5b511..1e95698e 100644
--- a/src/lib/internal.h
+++ b/src/lib/internal.h
@@ -429,7 +429,7 @@ struct MHD_Request
429 * response (which maybe shared between requests) and the IP 429 * response (which maybe shared between requests) and the IP
430 * address (which persists across individual requests). 430 * address (which persists across individual requests).
431 */ 431 */
432 struct MemoryPool *pool; 432 struct MemoryPool *pool; // FIXME: keep with connnection!
433 433
434 /** 434 /**
435 * We allow the main application to associate some pointer with the 435 * We allow the main application to associate some pointer with the
@@ -515,12 +515,6 @@ struct MHD_Request
515#endif /* UPGRADE_SUPPORT */ 515#endif /* UPGRADE_SUPPORT */
516 516
517 /** 517 /**
518 * Thread handle for this connection (if we are using
519 * one thread per connection).
520 */
521 MHD_thread_handle_ID_ pid;
522
523 /**
524 * Size of @e read_buffer (in bytes). This value indicates 518 * Size of @e read_buffer (in bytes). This value indicates
525 * how many bytes we're willing to read into the buffer; 519 * how many bytes we're willing to read into the buffer;
526 * the real buffer is one byte longer to allow for 520 * the real buffer is one byte longer to allow for
@@ -760,6 +754,12 @@ struct MHD_Connection
760 struct MHD_Request request; 754 struct MHD_Request request;
761 755
762 /** 756 /**
757 * Thread handle for this connection (if we are using
758 * one thread per connection).
759 */
760 MHD_thread_handle_ID_ pid;
761
762 /**
763 * Foreign address (of length @e addr_len). 763 * Foreign address (of length @e addr_len).
764 */ 764 */
765 struct sockaddr_storage addr; 765 struct sockaddr_storage addr;
@@ -1218,6 +1218,11 @@ struct MHD_Daemon
1218 */ 1218 */
1219 struct MHD_Connection *cleanup_tail; 1219 struct MHD_Connection *cleanup_tail;
1220 1220
1221 /**
1222 * Table storing number of connections per IP
1223 */
1224 void *per_ip_connection_count;
1225
1221#ifdef EPOLL_SUPPORT 1226#ifdef EPOLL_SUPPORT
1222 /** 1227 /**
1223 * Head of EDLL of connections ready for processing (in epoll mode). 1228 * Head of EDLL of connections ready for processing (in epoll mode).