diff options
author | Christian Grothoff <christian@grothoff.org> | 2018-02-15 07:14:03 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2018-02-15 07:14:03 +0100 |
commit | e76b3a78104ab4d6b939a50fe1de446b34296916 (patch) | |
tree | 0bcd230c202d3fa3261c81cd32e9632e11048ca4 | |
parent | 31dd55ae496a5582dd4c67f1231ee7e042f6b4e8 (diff) | |
download | libmicrohttpd-e76b3a78104ab4d6b939a50fe1de446b34296916.tar.gz libmicrohttpd-e76b3a78104ab4d6b939a50fe1de446b34296916.zip |
adding ip counting and connection_add logic
-rw-r--r-- | src/gnutls/setup_connection.c | 57 | ||||
-rw-r--r-- | src/gnutls/teardown_connection.c | 4 | ||||
-rw-r--r-- | src/include/microhttpd2.h | 146 | ||||
-rw-r--r-- | src/include/microhttpd_tls.h | 4 | ||||
-rw-r--r-- | src/lib/Makefile.am | 3 | ||||
-rw-r--r-- | src/lib/connection_add.c | 978 | ||||
-rw-r--r-- | src/lib/connection_add.h | 41 | ||||
-rw-r--r-- | src/lib/daemon_add_connection.c | 63 | ||||
-rw-r--r-- | src/lib/daemon_epoll.c | 2 | ||||
-rw-r--r-- | src/lib/daemon_ip_limit.c | 280 | ||||
-rw-r--r-- | src/lib/daemon_ip_limit.h | 60 | ||||
-rw-r--r-- | src/lib/daemon_poll.c | 4 | ||||
-rw-r--r-- | src/lib/daemon_select.c | 2 | ||||
-rw-r--r-- | src/lib/internal.h | 19 |
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 | |||
3 | setup_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 @@ | |||
1 | teardown_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 | */ | ||
36 | static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_ | ||
37 | thread_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 | } | ||
395 | exit: | ||
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 | */ | ||
436 | static enum MHD_StatusCode | ||
437 | internal_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 | */ | ||
806 | enum MHD_StatusCode | ||
807 | MHD_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 | */ | ||
856 | enum MHD_StatusCode | ||
857 | MHD_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 | */ | ||
37 | enum MHD_StatusCode | ||
38 | MHD_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 | ||
55 | MHD_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 | */ | ||
31 | struct 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 | */ | ||
67 | static void | ||
68 | MHD_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 | */ | ||
79 | static void | ||
80 | MHD_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 | */ | ||
95 | static int | ||
96 | MHD_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 | */ | ||
114 | static int | ||
115 | MHD_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 | */ | ||
165 | int | ||
166 | MHD_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 | */ | ||
232 | void | ||
233 | MHD_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 | */ | ||
39 | int | ||
40 | MHD_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 | */ | ||
54 | void | ||
55 | MHD_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). |