aboutsummaryrefslogtreecommitdiff
path: root/src/lib/daemon_ip_limit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/daemon_ip_limit.c')
-rw-r--r--src/lib/daemon_ip_limit.c280
1 files changed, 280 insertions, 0 deletions
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 */