1*8b3967e5Srillig /* $NetBSD: ratelimit.c,v 1.2 2021/10/12 22:51:28 rillig Exp $ */
2b19025f3Schristos
3b19025f3Schristos /*-
4b19025f3Schristos * Copyright (c) 2021 The NetBSD Foundation, Inc.
5b19025f3Schristos * All rights reserved.
6b19025f3Schristos *
7b19025f3Schristos * This code is derived from software contributed to The NetBSD Foundation
8b19025f3Schristos * by James Browning, Gabe Coffland, Alex Gavin, and Solomon Ritzow.
9b19025f3Schristos *
10b19025f3Schristos * Redistribution and use in source and binary forms, with or without
11b19025f3Schristos * modification, are permitted provided that the following conditions
12b19025f3Schristos * are met:
13b19025f3Schristos * 1. Redistributions of source code must retain the above copyright
14b19025f3Schristos * notice, this list of conditions and the following disclaimer.
15b19025f3Schristos * 2. Redistributions in binary form must reproduce the above copyright
16b19025f3Schristos * notice, this list of conditions and the following disclaimer in the
17b19025f3Schristos * documentation and/or other materials provided with the distribution.
18b19025f3Schristos *
19b19025f3Schristos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20b19025f3Schristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21b19025f3Schristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22b19025f3Schristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23b19025f3Schristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24b19025f3Schristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25b19025f3Schristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26b19025f3Schristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27b19025f3Schristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28b19025f3Schristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29b19025f3Schristos * POSSIBILITY OF SUCH DAMAGE.
30b19025f3Schristos */
31b19025f3Schristos #include <sys/cdefs.h>
32*8b3967e5Srillig __RCSID("$NetBSD: ratelimit.c,v 1.2 2021/10/12 22:51:28 rillig Exp $");
33b19025f3Schristos
34b19025f3Schristos #include <sys/queue.h>
35b19025f3Schristos
36b19025f3Schristos #include <arpa/inet.h>
37b19025f3Schristos
38b19025f3Schristos #include <stdio.h>
39b19025f3Schristos #include <stdlib.h>
40b19025f3Schristos #include <syslog.h>
41b19025f3Schristos #include <unistd.h>
42b19025f3Schristos #include <string.h>
43b19025f3Schristos #include <errno.h>
44b19025f3Schristos #include <stddef.h>
45b19025f3Schristos
46b19025f3Schristos #include "inetd.h"
47b19025f3Schristos
48b19025f3Schristos union addr {
49b19025f3Schristos struct in_addr ipv4_addr;
50b19025f3Schristos /* ensure aligned for comparison in rl_ipv6_eq (already is on 64-bit) */
51b19025f3Schristos #ifdef INET6
52b19025f3Schristos struct in6_addr ipv6_addr __attribute__((aligned(16)));
53b19025f3Schristos #endif
54b19025f3Schristos char other_addr[NI_MAXHOST];
55b19025f3Schristos };
56b19025f3Schristos
57b19025f3Schristos static void rl_reset(struct servtab *, time_t);
58b19025f3Schristos static time_t rl_time(void);
59b19025f3Schristos static void rl_get_name(struct servtab *, int, union addr *);
60b19025f3Schristos static void rl_drop_connection(struct servtab *, int);
61b19025f3Schristos static struct rl_ip_node *rl_add(struct servtab *, union addr *);
62b19025f3Schristos static struct rl_ip_node *rl_try_get_ip(struct servtab *, union addr *);
63b19025f3Schristos static bool rl_ip_eq(struct servtab *, union addr *, struct rl_ip_node *);
64b19025f3Schristos #ifdef INET6
65b19025f3Schristos static bool rl_ipv6_eq(struct in6_addr *, struct in6_addr *);
66b19025f3Schristos #endif
67b19025f3Schristos #ifdef DEBUG_ENABLE
68b19025f3Schristos static void rl_print_found_node(struct servtab *, struct rl_ip_node *);
69b19025f3Schristos #endif
70b19025f3Schristos static void rl_log_address_exceed(struct servtab *, struct rl_ip_node *);
71b19025f3Schristos static const char *rl_node_tostring(struct servtab *, struct rl_ip_node *, char[NI_MAXHOST]);
72b19025f3Schristos static bool rl_process_service_max(struct servtab *, int, time_t *);
73b19025f3Schristos static bool rl_process_ip_max(struct servtab *, int, time_t *);
74b19025f3Schristos
75b19025f3Schristos /* Return 0 on allow, -1 if connection should be blocked */
76b19025f3Schristos int
rl_process(struct servtab * sep,int ctrl)77b19025f3Schristos rl_process(struct servtab *sep, int ctrl)
78b19025f3Schristos {
79b19025f3Schristos time_t now = -1;
80b19025f3Schristos
81b19025f3Schristos DPRINTF(SERV_FMT ": processing rate-limiting",
82b19025f3Schristos SERV_PARAMS(sep));
83b19025f3Schristos DPRINTF(SERV_FMT ": se_service_max "
84b19025f3Schristos "%zu and se_count %zu", SERV_PARAMS(sep),
85b19025f3Schristos sep->se_service_max, sep->se_count);
86b19025f3Schristos
87b19025f3Schristos if (sep->se_count == 0) {
88b19025f3Schristos now = rl_time();
89b19025f3Schristos sep->se_time = now;
90b19025f3Schristos }
91b19025f3Schristos
92b19025f3Schristos if (!rl_process_service_max(sep, ctrl, &now)
93b19025f3Schristos || !rl_process_ip_max(sep, ctrl, &now)) {
94b19025f3Schristos return -1;
95b19025f3Schristos }
96b19025f3Schristos
97b19025f3Schristos DPRINTF(SERV_FMT ": running service ", SERV_PARAMS(sep));
98b19025f3Schristos
99b19025f3Schristos /* se_count is only incremented if rl_process will return 0 */
100b19025f3Schristos sep->se_count++;
101b19025f3Schristos return 0;
102b19025f3Schristos }
103b19025f3Schristos
104b19025f3Schristos /*
105b19025f3Schristos * Get the identifier for the remote peer based on sep->se_socktype and
106b19025f3Schristos * sep->se_family
107b19025f3Schristos */
108b19025f3Schristos static void
rl_get_name(struct servtab * sep,int ctrl,union addr * out)109b19025f3Schristos rl_get_name(struct servtab *sep, int ctrl, union addr *out)
110b19025f3Schristos {
111b19025f3Schristos union {
112b19025f3Schristos struct sockaddr_storage ss;
113b19025f3Schristos struct sockaddr sa;
114b19025f3Schristos struct sockaddr_in sin;
115b19025f3Schristos struct sockaddr_in6 sin6;
116b19025f3Schristos } addr;
117b19025f3Schristos
118b19025f3Schristos /* Get the sockaddr of socket ctrl */
119b19025f3Schristos switch (sep->se_socktype) {
120b19025f3Schristos case SOCK_STREAM: {
121b19025f3Schristos socklen_t len = sizeof(struct sockaddr_storage);
122b19025f3Schristos if (getpeername(ctrl, &addr.sa, &len) == -1) {
123b19025f3Schristos /* error, log it and skip ip rate limiting */
124b19025f3Schristos syslog(LOG_ERR,
125b19025f3Schristos SERV_FMT " failed to get peer name of the "
126b19025f3Schristos "connection", SERV_PARAMS(sep));
127b19025f3Schristos exit(EXIT_FAILURE);
128b19025f3Schristos }
129b19025f3Schristos break;
130b19025f3Schristos }
131b19025f3Schristos case SOCK_DGRAM: {
132b19025f3Schristos struct msghdr header = {
133b19025f3Schristos .msg_name = &addr.sa,
134b19025f3Schristos .msg_namelen = sizeof(struct sockaddr_storage),
135b19025f3Schristos /* scatter/gather and control info is null */
136b19025f3Schristos };
137b19025f3Schristos ssize_t count;
138b19025f3Schristos
139b19025f3Schristos /* Peek so service can still get the packet */
140b19025f3Schristos count = recvmsg(ctrl, &header, MSG_PEEK);
141b19025f3Schristos if (count == -1) {
142b19025f3Schristos syslog(LOG_ERR,
143b19025f3Schristos "failed to get dgram source address: %s; exiting",
144b19025f3Schristos strerror(errno));
145b19025f3Schristos exit(EXIT_FAILURE);
146b19025f3Schristos }
147b19025f3Schristos break;
148b19025f3Schristos }
149b19025f3Schristos default:
150b19025f3Schristos DPRINTF(SERV_FMT ": ip_max rate limiting not supported for "
151b19025f3Schristos "socktype", SERV_PARAMS(sep));
152b19025f3Schristos syslog(LOG_ERR, SERV_FMT
153b19025f3Schristos ": ip_max rate limiting not supported for socktype",
154b19025f3Schristos SERV_PARAMS(sep));
155b19025f3Schristos exit(EXIT_FAILURE);
156b19025f3Schristos }
157b19025f3Schristos
158b19025f3Schristos /* Convert addr to to rate limiting address */
159b19025f3Schristos switch (sep->se_family) {
160b19025f3Schristos case AF_INET:
161b19025f3Schristos out->ipv4_addr = addr.sin.sin_addr;
162b19025f3Schristos break;
163b19025f3Schristos #ifdef INET6
164b19025f3Schristos case AF_INET6:
165b19025f3Schristos out->ipv6_addr = addr.sin6.sin6_addr;
166b19025f3Schristos break;
167b19025f3Schristos #endif
168b19025f3Schristos default: {
169b19025f3Schristos int res = getnameinfo(&addr.sa,
170b19025f3Schristos (socklen_t)addr.sa.sa_len,
171b19025f3Schristos out->other_addr, NI_MAXHOST,
172b19025f3Schristos NULL, 0,
173b19025f3Schristos NI_NUMERICHOST
174b19025f3Schristos );
175b19025f3Schristos if (res != 0) {
176b19025f3Schristos syslog(LOG_ERR,
177b19025f3Schristos SERV_FMT ": failed to get name info of "
178b19025f3Schristos "the incoming connection: %s; exiting",
179b19025f3Schristos SERV_PARAMS(sep), gai_strerror(res));
180b19025f3Schristos exit(EXIT_FAILURE);
181b19025f3Schristos }
182b19025f3Schristos break;
183b19025f3Schristos }
184b19025f3Schristos }
185b19025f3Schristos }
186b19025f3Schristos
187b19025f3Schristos static void
rl_drop_connection(struct servtab * sep,int ctrl)188b19025f3Schristos rl_drop_connection(struct servtab *sep, int ctrl)
189b19025f3Schristos {
190b19025f3Schristos
191b19025f3Schristos if (sep->se_wait == 0 && sep->se_socktype == SOCK_STREAM) {
192b19025f3Schristos /*
193b19025f3Schristos * If the fd isn't a listen socket,
194b19025f3Schristos * close the individual connection too.
195b19025f3Schristos */
196b19025f3Schristos close(ctrl);
197b19025f3Schristos return;
198b19025f3Schristos }
199b19025f3Schristos if (sep->se_socktype != SOCK_DGRAM) {
200b19025f3Schristos return;
201b19025f3Schristos }
202b19025f3Schristos /*
203b19025f3Schristos * Drop the single datagram the service would have
204b19025f3Schristos * consumed if nowait. If this is a wait service, this
205b19025f3Schristos * will consume 1 datagram, and further received packets
206b19025f3Schristos * will be removed in the same way.
207b19025f3Schristos */
208b19025f3Schristos struct msghdr header = {
209b19025f3Schristos /* All fields null, just consume one message */
210b19025f3Schristos };
211b19025f3Schristos ssize_t count;
212b19025f3Schristos
213b19025f3Schristos count = recvmsg(ctrl, &header, 0);
214b19025f3Schristos if (count == -1) {
215b19025f3Schristos syslog(LOG_ERR,
216b19025f3Schristos SERV_FMT ": failed to consume nowait dgram: %s",
217b19025f3Schristos SERV_PARAMS(sep), strerror(errno));
218b19025f3Schristos exit(EXIT_FAILURE);
219b19025f3Schristos }
220b19025f3Schristos DPRINTF(SERV_FMT ": dropped dgram message",
221b19025f3Schristos SERV_PARAMS(sep));
222b19025f3Schristos }
223b19025f3Schristos
224b19025f3Schristos static time_t
rl_time(void)225b19025f3Schristos rl_time(void)
226b19025f3Schristos {
227b19025f3Schristos struct timespec ts;
228b19025f3Schristos if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {
229b19025f3Schristos syslog(LOG_ERR, "clock_gettime for rate limiting failed: %s; "
230b19025f3Schristos "exiting", strerror(errno));
231b19025f3Schristos /* Exit inetd if rate limiting fails */
232b19025f3Schristos exit(EXIT_FAILURE);
233b19025f3Schristos }
234b19025f3Schristos return ts.tv_sec;
235b19025f3Schristos }
236b19025f3Schristos
237b19025f3Schristos /* Add addr to IP tracking or return NULL if malloc fails */
238b19025f3Schristos static struct rl_ip_node *
rl_add(struct servtab * sep,union addr * addr)239b19025f3Schristos rl_add(struct servtab *sep, union addr *addr)
240b19025f3Schristos {
241b19025f3Schristos
242b19025f3Schristos struct rl_ip_node *node;
243b19025f3Schristos size_t node_size, bufsize;
244b19025f3Schristos #ifdef DEBUG_ENABLE
245b19025f3Schristos char buffer[NI_MAXHOST];
246b19025f3Schristos #endif
247b19025f3Schristos
248b19025f3Schristos switch(sep->se_family) {
249b19025f3Schristos case AF_INET:
250b19025f3Schristos /* ip_node to end of IPv4 address */
251b19025f3Schristos node_size = offsetof(struct rl_ip_node, ipv4_addr)
252b19025f3Schristos + sizeof(struct in_addr);
253b19025f3Schristos break;
254b19025f3Schristos case AF_INET6:
255b19025f3Schristos /* ip_node to end of IPv6 address */
256b19025f3Schristos node_size = offsetof(struct rl_ip_node, ipv6_addr)
257b19025f3Schristos + sizeof(struct in6_addr);
258b19025f3Schristos break;
259b19025f3Schristos default:
260b19025f3Schristos /* ip_node to other_addr plus size of string + NULL */
261b19025f3Schristos bufsize = strlen(addr->other_addr) + sizeof(char);
262b19025f3Schristos node_size = offsetof(struct rl_ip_node, other_addr) + bufsize;
263b19025f3Schristos break;
264b19025f3Schristos }
265b19025f3Schristos
266b19025f3Schristos node = malloc(node_size);
267b19025f3Schristos if (node == NULL) {
268b19025f3Schristos if (errno == ENOMEM) {
269b19025f3Schristos return NULL;
270b19025f3Schristos } else {
271b19025f3Schristos syslog(LOG_ERR, "malloc failed unexpectedly: %s",
272b19025f3Schristos strerror(errno));
273b19025f3Schristos exit(EXIT_FAILURE);
274b19025f3Schristos }
275b19025f3Schristos }
276b19025f3Schristos
277b19025f3Schristos node->count = 0;
278b19025f3Schristos
279b19025f3Schristos /* copy the data into the new allocation */
280b19025f3Schristos switch(sep->se_family) {
281b19025f3Schristos case AF_INET:
282b19025f3Schristos node->ipv4_addr = addr->ipv4_addr;
283b19025f3Schristos break;
284b19025f3Schristos case AF_INET6:
285b19025f3Schristos /* Hopefully this is inlined, means the same thing as memcpy */
286b19025f3Schristos __builtin_memcpy(&node->ipv6_addr, &addr->ipv6_addr,
287b19025f3Schristos sizeof(struct in6_addr));
288b19025f3Schristos break;
289b19025f3Schristos default:
290b19025f3Schristos strlcpy(node->other_addr, addr->other_addr, bufsize);
291b19025f3Schristos break;
292b19025f3Schristos }
293b19025f3Schristos
294b19025f3Schristos /* initializes 'entries' member to NULL automatically */
295b19025f3Schristos SLIST_INSERT_HEAD(&sep->se_rl_ip_list, node, entries);
296b19025f3Schristos
297b19025f3Schristos DPRINTF(SERV_FMT ": add '%s' to rate limit tracking (%zu byte record)",
298b19025f3Schristos SERV_PARAMS(sep), rl_node_tostring(sep, node, buffer), node_size);
299b19025f3Schristos
300b19025f3Schristos return node;
301b19025f3Schristos }
302b19025f3Schristos
303b19025f3Schristos static void
rl_reset(struct servtab * sep,time_t now)304b19025f3Schristos rl_reset(struct servtab *sep, time_t now)
305b19025f3Schristos {
306b19025f3Schristos DPRINTF(SERV_FMT ": %ji seconds passed; resetting rate limiting ",
307b19025f3Schristos SERV_PARAMS(sep), (intmax_t)(now - sep->se_time));
308b19025f3Schristos
309b19025f3Schristos sep->se_count = 0;
310b19025f3Schristos sep->se_time = now;
311b19025f3Schristos if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) {
312b19025f3Schristos rl_clear_ip_list(sep);
313b19025f3Schristos }
314b19025f3Schristos }
315b19025f3Schristos
316b19025f3Schristos void
rl_clear_ip_list(struct servtab * sep)317b19025f3Schristos rl_clear_ip_list(struct servtab *sep)
318b19025f3Schristos {
319b19025f3Schristos while (!SLIST_EMPTY(&sep->se_rl_ip_list)) {
320b19025f3Schristos struct rl_ip_node *node = SLIST_FIRST(&sep->se_rl_ip_list);
321b19025f3Schristos SLIST_REMOVE_HEAD(&sep->se_rl_ip_list, entries);
322b19025f3Schristos free(node);
323b19025f3Schristos }
324b19025f3Schristos }
325b19025f3Schristos
326b19025f3Schristos /* Get the node associated with addr, or NULL */
327b19025f3Schristos static struct rl_ip_node *
rl_try_get_ip(struct servtab * sep,union addr * addr)328b19025f3Schristos rl_try_get_ip(struct servtab *sep, union addr *addr)
329b19025f3Schristos {
330b19025f3Schristos
331b19025f3Schristos struct rl_ip_node *cur;
332b19025f3Schristos SLIST_FOREACH(cur, &sep->se_rl_ip_list, entries) {
333b19025f3Schristos if (rl_ip_eq(sep, addr, cur)) {
334b19025f3Schristos return cur;
335b19025f3Schristos }
336b19025f3Schristos }
337b19025f3Schristos
338b19025f3Schristos return NULL;
339b19025f3Schristos }
340b19025f3Schristos
341b19025f3Schristos /* Return true if passed service rate limiting checks, false if blocked */
342b19025f3Schristos static bool
rl_process_service_max(struct servtab * sep,int ctrl,time_t * now)343b19025f3Schristos rl_process_service_max(struct servtab *sep, int ctrl, time_t *now)
344b19025f3Schristos {
345b19025f3Schristos if (sep->se_count >= sep->se_service_max) {
346b19025f3Schristos if (*now == -1) {
347b19025f3Schristos /* Only get the clock time if we didn't already */
348b19025f3Schristos *now = rl_time();
349b19025f3Schristos }
350b19025f3Schristos
351b19025f3Schristos if (*now - sep->se_time > CNT_INTVL) {
352b19025f3Schristos rl_reset(sep, *now);
353b19025f3Schristos } else {
354b19025f3Schristos syslog(LOG_ERR, SERV_FMT
355b19025f3Schristos ": max spawn rate (%zu in %ji seconds) "
356b19025f3Schristos "already met; closing for %ju seconds",
357b19025f3Schristos SERV_PARAMS(sep),
358b19025f3Schristos sep->se_service_max,
359b19025f3Schristos (intmax_t)CNT_INTVL,
360b19025f3Schristos (uintmax_t)RETRYTIME);
361b19025f3Schristos DPRINTF(SERV_FMT
362b19025f3Schristos ": max spawn rate (%zu in %ji seconds) "
363b19025f3Schristos "already met; closing for %ju seconds",
364b19025f3Schristos SERV_PARAMS(sep),
365b19025f3Schristos sep->se_service_max,
366b19025f3Schristos (intmax_t)CNT_INTVL,
367b19025f3Schristos (uintmax_t)RETRYTIME);
368b19025f3Schristos
369b19025f3Schristos rl_drop_connection(sep, ctrl);
370b19025f3Schristos
371b19025f3Schristos /* Close the server for 10 minutes */
372b19025f3Schristos close_sep(sep);
373b19025f3Schristos if (!timingout) {
374b19025f3Schristos timingout = true;
375b19025f3Schristos alarm(RETRYTIME);
376b19025f3Schristos }
377b19025f3Schristos
378b19025f3Schristos return false;
379b19025f3Schristos }
380b19025f3Schristos }
381b19025f3Schristos return true;
382b19025f3Schristos }
383b19025f3Schristos
384b19025f3Schristos /* Return true if passed IP rate limiting checks, false if blocked */
385b19025f3Schristos static bool
rl_process_ip_max(struct servtab * sep,int ctrl,time_t * now)386b19025f3Schristos rl_process_ip_max(struct servtab *sep, int ctrl, time_t *now) {
387b19025f3Schristos if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) {
388b19025f3Schristos struct rl_ip_node *node;
389b19025f3Schristos union addr addr;
390b19025f3Schristos
391b19025f3Schristos rl_get_name(sep, ctrl, &addr);
392b19025f3Schristos node = rl_try_get_ip(sep, &addr);
393b19025f3Schristos if (node == NULL) {
394b19025f3Schristos node = rl_add(sep, &addr);
395b19025f3Schristos if (node == NULL) {
396b19025f3Schristos /* If rl_add can't allocate, reject request */
397b19025f3Schristos DPRINTF("Cannot allocate rl_ip_node");
398b19025f3Schristos return false;
399b19025f3Schristos }
400b19025f3Schristos }
401b19025f3Schristos #ifdef DEBUG_ENABLE
402b19025f3Schristos else {
403b19025f3Schristos /*
404b19025f3Schristos * in a separate function to prevent large stack
405b19025f3Schristos * frame
406b19025f3Schristos */
407b19025f3Schristos rl_print_found_node(sep, node);
408b19025f3Schristos }
409b19025f3Schristos #endif
410b19025f3Schristos
411b19025f3Schristos DPRINTF(
412b19025f3Schristos SERV_FMT ": se_ip_max %zu and ip_count %zu",
413b19025f3Schristos SERV_PARAMS(sep), sep->se_ip_max, node->count);
414b19025f3Schristos
415b19025f3Schristos if (node->count >= sep->se_ip_max) {
416b19025f3Schristos if (*now == -1) {
417b19025f3Schristos *now = rl_time();
418b19025f3Schristos }
419b19025f3Schristos
420b19025f3Schristos if (*now - sep->se_time > CNT_INTVL) {
421b19025f3Schristos rl_reset(sep, *now);
422b19025f3Schristos node = rl_add(sep, &addr);
423b19025f3Schristos if (node == NULL) {
424b19025f3Schristos DPRINTF("Cannot allocate rl_ip_node");
425b19025f3Schristos return false;
426b19025f3Schristos }
427b19025f3Schristos } else {
428b19025f3Schristos if (debug && node->count == sep->se_ip_max) {
429b19025f3Schristos /*
430b19025f3Schristos * Only log first failed request to
431b19025f3Schristos * prevent DoS attack writing to system
432b19025f3Schristos * log
433b19025f3Schristos */
434b19025f3Schristos rl_log_address_exceed(sep, node);
435b19025f3Schristos } else {
436b19025f3Schristos DPRINTF(SERV_FMT
437b19025f3Schristos ": service not started",
438b19025f3Schristos SERV_PARAMS(sep));
439b19025f3Schristos }
440b19025f3Schristos
441b19025f3Schristos rl_drop_connection(sep, ctrl);
442b19025f3Schristos /*
443b19025f3Schristos * Increment so debug-syslog message will
444b19025f3Schristos * trigger only once
445b19025f3Schristos */
446b19025f3Schristos if (node->count < SIZE_MAX) {
447b19025f3Schristos node->count++;
448b19025f3Schristos }
449b19025f3Schristos return false;
450b19025f3Schristos }
451b19025f3Schristos }
452b19025f3Schristos node->count++;
453b19025f3Schristos }
454b19025f3Schristos return true;
455b19025f3Schristos }
456b19025f3Schristos
457b19025f3Schristos static bool
rl_ip_eq(struct servtab * sep,union addr * addr,struct rl_ip_node * cur)458b19025f3Schristos rl_ip_eq(struct servtab *sep, union addr *addr, struct rl_ip_node *cur) {
459b19025f3Schristos switch(sep->se_family) {
460b19025f3Schristos case AF_INET:
461b19025f3Schristos if (addr->ipv4_addr.s_addr == cur->ipv4_addr.s_addr) {
462b19025f3Schristos return true;
463b19025f3Schristos }
464b19025f3Schristos break;
465b19025f3Schristos #ifdef INET6
466b19025f3Schristos case AF_INET6:
467b19025f3Schristos if (rl_ipv6_eq(&addr->ipv6_addr, &cur->ipv6_addr)) {
468b19025f3Schristos return true;
469b19025f3Schristos }
470b19025f3Schristos break;
471b19025f3Schristos #endif
472b19025f3Schristos default:
473b19025f3Schristos if (strncmp(cur->other_addr, addr->other_addr, NI_MAXHOST)
474b19025f3Schristos == 0) {
475b19025f3Schristos return true;
476b19025f3Schristos }
477b19025f3Schristos break;
478b19025f3Schristos }
479b19025f3Schristos return false;
480b19025f3Schristos }
481b19025f3Schristos
482b19025f3Schristos #ifdef INET6
483b19025f3Schristos static bool
rl_ipv6_eq(struct in6_addr * a,struct in6_addr * b)484b19025f3Schristos rl_ipv6_eq(struct in6_addr *a, struct in6_addr *b)
485b19025f3Schristos {
486b19025f3Schristos #if UINTMAX_MAX >= UINT64_MAX
487b19025f3Schristos { /* requires 8 byte aligned structs */
488b19025f3Schristos uint64_t *ap = (uint64_t *)a->s6_addr;
489b19025f3Schristos uint64_t *bp = (uint64_t *)b->s6_addr;
490b19025f3Schristos return (ap[0] == bp[0]) & (ap[1] == bp[1]);
491b19025f3Schristos }
492b19025f3Schristos #else
493b19025f3Schristos { /* requires 4 byte aligned structs */
494b19025f3Schristos uint32_t *ap = (uint32_t *)a->s6_addr;
495b19025f3Schristos uint32_t *bp = (uint32_t *)b->s6_addr;
496b19025f3Schristos return ap[0] == bp[0] && ap[1] == bp[1] &&
497b19025f3Schristos ap[2] == bp[2] && ap[3] == bp[3];
498b19025f3Schristos }
499b19025f3Schristos #endif
500b19025f3Schristos }
501b19025f3Schristos #endif
502b19025f3Schristos
503b19025f3Schristos static const char *
rl_node_tostring(struct servtab * sep,struct rl_ip_node * node,char buffer[NI_MAXHOST])504b19025f3Schristos rl_node_tostring(struct servtab *sep, struct rl_ip_node *node,
505b19025f3Schristos char buffer[NI_MAXHOST])
506b19025f3Schristos {
507b19025f3Schristos switch (sep->se_family) {
508b19025f3Schristos case AF_INET:
509b19025f3Schristos #ifdef INET6
510b19025f3Schristos case AF_INET6:
511b19025f3Schristos #endif
512b19025f3Schristos /* ipv4_addr/ipv6_addr share same address */
513b19025f3Schristos return inet_ntop(sep->se_family, (void*)&node->ipv4_addr,
514b19025f3Schristos (char*)buffer, NI_MAXHOST);
515b19025f3Schristos default:
516b19025f3Schristos return (char *)&node->other_addr;
517b19025f3Schristos }
518b19025f3Schristos }
519b19025f3Schristos
520b19025f3Schristos #ifdef DEBUG_ENABLE
521b19025f3Schristos /* Separate function due to large buffer size */
522b19025f3Schristos static void
rl_print_found_node(struct servtab * sep,struct rl_ip_node * node)523b19025f3Schristos rl_print_found_node(struct servtab *sep, struct rl_ip_node *node)
524b19025f3Schristos {
525b19025f3Schristos char buffer[NI_MAXHOST];
526b19025f3Schristos DPRINTF(SERV_FMT ": found record for address '%s'",
527b19025f3Schristos SERV_PARAMS(sep), rl_node_tostring(sep, node, buffer));
528b19025f3Schristos }
529b19025f3Schristos #endif
530b19025f3Schristos
531b19025f3Schristos /* Separate function due to large buffer sie */
532b19025f3Schristos static void
rl_log_address_exceed(struct servtab * sep,struct rl_ip_node * node)533b19025f3Schristos rl_log_address_exceed(struct servtab *sep, struct rl_ip_node *node)
534b19025f3Schristos {
535b19025f3Schristos char buffer[NI_MAXHOST];
536b19025f3Schristos const char * name = rl_node_tostring(sep, node, buffer);
537b19025f3Schristos syslog(LOG_ERR, SERV_FMT
538b19025f3Schristos ": max ip spawn rate (%zu in "
539b19025f3Schristos "%ji seconds) for "
540b19025f3Schristos "'%." TOSTRING(NI_MAXHOST) "s' "
541b19025f3Schristos "already met; service not started",
542b19025f3Schristos SERV_PARAMS(sep),
543b19025f3Schristos sep->se_ip_max,
544b19025f3Schristos (intmax_t)CNT_INTVL,
545b19025f3Schristos name);
546b19025f3Schristos DPRINTF(SERV_FMT
547b19025f3Schristos ": max ip spawn rate (%zu in "
548b19025f3Schristos "%ji seconds) for "
549b19025f3Schristos "'%." TOSTRING(NI_MAXHOST) "s' "
550b19025f3Schristos "already met; service not started",
551b19025f3Schristos SERV_PARAMS(sep),
552b19025f3Schristos sep->se_ip_max,
553b19025f3Schristos (intmax_t)CNT_INTVL,
554b19025f3Schristos name);
555b19025f3Schristos }
556