xref: /netbsd-src/usr.sbin/inetd/ratelimit.c (revision 8b3967e586408c97b863e3e170f09419d68a80d8)
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