1/*	$NetBSD: srclimit.c,v 1.3 2021/04/19 14:40:15 christos Exp $	*/
2
3/*
4 * Copyright (c) 2020 Darren Tucker <dtucker@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18#include "includes.h"
19__RCSID("$NetBSD: srclimit.c,v 1.3 2021/04/19 14:40:15 christos Exp $");
20
21#include <sys/socket.h>
22#include <sys/types.h>
23
24#include <limits.h>
25#include <netdb.h>
26#include <stdio.h>
27#include <string.h>
28
29#include "addr.h"
30#include "canohost.h"
31#include "log.h"
32#include "misc.h"
33#include "srclimit.h"
34#include "xmalloc.h"
35
36static int max_children, max_persource, ipv4_masklen, ipv6_masklen;
37
38/* Per connection state, used to enforce unauthenticated connection limit. */
39static struct child_info {
40	int id;
41	struct xaddr addr;
42} *child;
43
44void
45srclimit_init(int max, int persource, int ipv4len, int ipv6len)
46{
47	int i;
48
49	max_children = max;
50	ipv4_masklen = ipv4len;
51	ipv6_masklen = ipv6len;
52	max_persource = persource;
53	if (max_persource == INT_MAX)	/* no limit */
54		return;
55	debug("%s: max connections %d, per source %d, masks %d,%d", __func__,
56	    max, persource, ipv4len, ipv6len);
57	if (max <= 0)
58		fatal("%s: invalid number of sockets: %d", __func__, max);
59	child = xcalloc(max_children, sizeof(*child));
60	for (i = 0; i < max_children; i++)
61		child[i].id = -1;
62}
63
64/* returns 1 if connection allowed, 0 if not allowed. */
65int
66srclimit_check_allow(int sock, int id)
67{
68	struct xaddr xa, xb, xmask;
69	struct sockaddr_storage addr;
70	socklen_t addrlen = sizeof(addr);
71	struct sockaddr *sa = (struct sockaddr *)&addr;
72	int i, bits, first_unused, count = 0;
73	char xas[NI_MAXHOST];
74
75	if (max_persource == INT_MAX)	/* no limit */
76		return 1;
77
78	debug("%s: sock %d id %d limit %d", __func__, sock, id, max_persource);
79	if (getpeername(sock, sa, &addrlen) != 0)
80		return 1;	/* not remote socket? */
81	if (addr_sa_to_xaddr(sa, addrlen, &xa) != 0)
82		return 1;	/* unknown address family? */
83
84	/* Mask address off address to desired size. */
85	bits = xa.af == AF_INET ? ipv4_masklen : ipv6_masklen;
86	if (addr_netmask(xa.af, bits, &xmask) != 0 ||
87	    addr_and(&xb, &xa, &xmask) != 0) {
88		debug3("%s: invalid mask %d bits", __func__, bits);
89		return 1;
90	}
91
92	first_unused = max_children;
93	/* Count matching entries and find first unused one. */
94	for (i = 0; i < max_children; i++) {
95		if (child[i].id == -1) {
96			if (i < first_unused)
97				first_unused = i;
98		} else if (addr_cmp(&child[i].addr, &xb) == 0) {
99			count++;
100		}
101	}
102	if (addr_ntop(&xa, xas, sizeof(xas)) != 0) {
103		debug3("%s: addr ntop failed", __func__);
104		return 1;
105	}
106	debug3("%s: new unauthenticated connection from %s/%d, at %d of %d",
107	    __func__, xas, bits, count, max_persource);
108
109	if (first_unused == max_children) { /* no free slot found */
110		debug3("%s: no free slot", __func__);
111		return 0;
112	}
113	if (first_unused < 0 || first_unused >= max_children)
114		fatal("%s: internal error: first_unused out of range",
115		    __func__);
116
117	if (count >= max_persource)
118		return 0;
119
120	/* Connection allowed, store masked address. */
121	child[first_unused].id = id;
122	memcpy(&child[first_unused].addr, &xb, sizeof(xb));
123	return 1;
124}
125
126void
127srclimit_done(int id)
128{
129	int i;
130
131	if (max_persource == INT_MAX)	/* no limit */
132		return;
133
134	debug("%s: id %d", __func__, id);
135	/* Clear corresponding state entry. */
136	for (i = 0; i < max_children; i++) {
137		if (child[i].id == id) {
138			child[i].id = -1;
139			return;
140		}
141	}
142}
143