diff options
Diffstat (limited to 'src/lib/daemon_ip_limit.c')
-rw-r--r-- | src/lib/daemon_ip_limit.c | 280 |
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 | */ | ||
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 */ | ||