xref: /freebsd/contrib/sendmail/src/ratectrl.c (revision d39bd2c1388b520fcba9abed1932acacead60fba)
1e92d3f3fSGregory Neil Shapiro /*
25dd76dd0SGregory Neil Shapiro  * Copyright (c) 2003 Proofpoint, Inc. and its suppliers.
3e92d3f3fSGregory Neil Shapiro  *	All rights reserved.
4e92d3f3fSGregory Neil Shapiro  *
5e92d3f3fSGregory Neil Shapiro  * By using this file, you agree to the terms and conditions set
6e92d3f3fSGregory Neil Shapiro  * forth in the LICENSE file which can be found at the top level of
7e92d3f3fSGregory Neil Shapiro  * the sendmail distribution.
8e92d3f3fSGregory Neil Shapiro  *
9e92d3f3fSGregory Neil Shapiro  * Contributed by Jose Marcio Martins da Cruz - Ecole des Mines de Paris
10e92d3f3fSGregory Neil Shapiro  *   Jose-Marcio.Martins@ensmp.fr
11e92d3f3fSGregory Neil Shapiro  */
12e92d3f3fSGregory Neil Shapiro 
13e92d3f3fSGregory Neil Shapiro /* a part of this code is based on inetd.c for which this copyright applies: */
14e92d3f3fSGregory Neil Shapiro /*
15e92d3f3fSGregory Neil Shapiro  * Copyright (c) 1983, 1991, 1993, 1994
16e92d3f3fSGregory Neil Shapiro  *      The Regents of the University of California.  All rights reserved.
17e92d3f3fSGregory Neil Shapiro  *
18e92d3f3fSGregory Neil Shapiro  * Redistribution and use in source and binary forms, with or without
19e92d3f3fSGregory Neil Shapiro  * modification, are permitted provided that the following conditions
20e92d3f3fSGregory Neil Shapiro  * are met:
21e92d3f3fSGregory Neil Shapiro  * 1. Redistributions of source code must retain the above copyright
22e92d3f3fSGregory Neil Shapiro  *    notice, this list of conditions and the following disclaimer.
23e92d3f3fSGregory Neil Shapiro  * 2. Redistributions in binary form must reproduce the above copyright
24e92d3f3fSGregory Neil Shapiro  *    notice, this list of conditions and the following disclaimer in the
25e92d3f3fSGregory Neil Shapiro  *    documentation and/or other materials provided with the distribution.
26e92d3f3fSGregory Neil Shapiro  * 3. All advertising materials mentioning features or use of this software
27e92d3f3fSGregory Neil Shapiro  *    must display the following acknowledgement:
28e92d3f3fSGregory Neil Shapiro  *      This product includes software developed by the University of
29e92d3f3fSGregory Neil Shapiro  *      California, Berkeley and its contributors.
30e92d3f3fSGregory Neil Shapiro  * 4. Neither the name of the University nor the names of its contributors
31e92d3f3fSGregory Neil Shapiro  *    may be used to endorse or promote products derived from this software
32e92d3f3fSGregory Neil Shapiro  *    without specific prior written permission.
33e92d3f3fSGregory Neil Shapiro  *
34e92d3f3fSGregory Neil Shapiro  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35e92d3f3fSGregory Neil Shapiro  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36e92d3f3fSGregory Neil Shapiro  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37e92d3f3fSGregory Neil Shapiro  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38e92d3f3fSGregory Neil Shapiro  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39e92d3f3fSGregory Neil Shapiro  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40e92d3f3fSGregory Neil Shapiro  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41e92d3f3fSGregory Neil Shapiro  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42e92d3f3fSGregory Neil Shapiro  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43e92d3f3fSGregory Neil Shapiro  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44e92d3f3fSGregory Neil Shapiro  * SUCH DAMAGE.
45e92d3f3fSGregory Neil Shapiro  */
46e92d3f3fSGregory Neil Shapiro 
475b0945b5SGregory Neil Shapiro #include <ratectrl.h>
484313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: ratectrl.c,v 8.14 2013-11-22 20:51:56 ca Exp $")
49e92d3f3fSGregory Neil Shapiro 
505b0945b5SGregory Neil Shapiro static int client_rate __P((time_t, SOCKADDR *, int));
51e92d3f3fSGregory Neil Shapiro static int total_rate __P((time_t, bool));
525b0945b5SGregory Neil Shapiro static unsigned int gen_hash __P((SOCKADDR *));
535b0945b5SGregory Neil Shapiro static void rate_init __P((void));
54e92d3f3fSGregory Neil Shapiro 
55e92d3f3fSGregory Neil Shapiro /*
56e92d3f3fSGregory Neil Shapiro **  CONNECTION_RATE_CHECK - updates connection history data
57e92d3f3fSGregory Neil Shapiro **      and computes connection rate for the given host
58e92d3f3fSGregory Neil Shapiro **
59e92d3f3fSGregory Neil Shapiro **	Parameters:
605b0945b5SGregory Neil Shapiro **		hostaddr -- IP address of SMTP client
61e92d3f3fSGregory Neil Shapiro **		e -- envelope
62e92d3f3fSGregory Neil Shapiro **
63e92d3f3fSGregory Neil Shapiro **	Returns:
645b0945b5SGregory Neil Shapiro **		none
65e92d3f3fSGregory Neil Shapiro **
66e92d3f3fSGregory Neil Shapiro **	Side Effects:
67e92d3f3fSGregory Neil Shapiro **		updates connection history
68e92d3f3fSGregory Neil Shapiro **
69e92d3f3fSGregory Neil Shapiro **	Warnings:
70e92d3f3fSGregory Neil Shapiro **		For each connection, this call shall be
71e92d3f3fSGregory Neil Shapiro **		done only once with the value true for the
72e92d3f3fSGregory Neil Shapiro **		update parameter.
73e92d3f3fSGregory Neil Shapiro **		Typically, this call is done with the value
74e92d3f3fSGregory Neil Shapiro **		true by the father, and once again with
75e92d3f3fSGregory Neil Shapiro **		the value false by the children.
76e92d3f3fSGregory Neil Shapiro */
77e92d3f3fSGregory Neil Shapiro 
785b0945b5SGregory Neil Shapiro void
connection_rate_check(hostaddr,e)79e92d3f3fSGregory Neil Shapiro connection_rate_check(hostaddr, e)
80e92d3f3fSGregory Neil Shapiro 	SOCKADDR *hostaddr;
81e92d3f3fSGregory Neil Shapiro 	ENVELOPE *e;
82e92d3f3fSGregory Neil Shapiro {
83e92d3f3fSGregory Neil Shapiro 	time_t now;
84e92d3f3fSGregory Neil Shapiro 	int totalrate, clientrate;
85e92d3f3fSGregory Neil Shapiro 	static int clientconn = 0;
86e92d3f3fSGregory Neil Shapiro 
87e92d3f3fSGregory Neil Shapiro 	now = time(NULL);
88e92d3f3fSGregory Neil Shapiro #if RATECTL_DEBUG
89e92d3f3fSGregory Neil Shapiro 	sm_syslog(LOG_INFO, NOQID, "connection_rate_check entering...");
905b0945b5SGregory Neil Shapiro #endif
91e92d3f3fSGregory Neil Shapiro 
92e92d3f3fSGregory Neil Shapiro 	/* update server connection rate */
93e92d3f3fSGregory Neil Shapiro 	totalrate = total_rate(now, e == NULL);
94e92d3f3fSGregory Neil Shapiro #if RATECTL_DEBUG
95e3793f76SGregory Neil Shapiro 	sm_syslog(LOG_INFO, NOQID, "global connection rate: %d", totalrate);
965b0945b5SGregory Neil Shapiro #endif
97e92d3f3fSGregory Neil Shapiro 
98e92d3f3fSGregory Neil Shapiro 	/* update client connection rate */
995b0945b5SGregory Neil Shapiro 	clientrate = client_rate(now, hostaddr, e == NULL ? SM_CLFL_UPDATE : SM_CLFL_NONE);
100e92d3f3fSGregory Neil Shapiro 
101e92d3f3fSGregory Neil Shapiro 	if (e == NULL)
102e92d3f3fSGregory Neil Shapiro 		clientconn = count_open_connections(hostaddr);
103e92d3f3fSGregory Neil Shapiro 
104e92d3f3fSGregory Neil Shapiro 	if (e != NULL)
105e92d3f3fSGregory Neil Shapiro 	{
106e92d3f3fSGregory Neil Shapiro 		char s[16];
107e92d3f3fSGregory Neil Shapiro 
108e92d3f3fSGregory Neil Shapiro 		sm_snprintf(s, sizeof(s), "%d", clientrate);
109e92d3f3fSGregory Neil Shapiro 		macdefine(&e->e_macro, A_TEMP, macid("{client_rate}"), s);
110e92d3f3fSGregory Neil Shapiro 		sm_snprintf(s, sizeof(s), "%d", totalrate);
111e92d3f3fSGregory Neil Shapiro 		macdefine(&e->e_macro, A_TEMP, macid("{total_rate}"), s);
112e92d3f3fSGregory Neil Shapiro 		sm_snprintf(s, sizeof(s), "%d", clientconn);
113e92d3f3fSGregory Neil Shapiro 		macdefine(&e->e_macro, A_TEMP, macid("{client_connections}"),
114e92d3f3fSGregory Neil Shapiro 				s);
115e92d3f3fSGregory Neil Shapiro 	}
1165b0945b5SGregory Neil Shapiro 	return;
117e92d3f3fSGregory Neil Shapiro }
118e92d3f3fSGregory Neil Shapiro 
119e92d3f3fSGregory Neil Shapiro /*
120e92d3f3fSGregory Neil Shapiro **  Data declarations needed to evaluate connection rate
121e92d3f3fSGregory Neil Shapiro */
122e92d3f3fSGregory Neil Shapiro 
123e92d3f3fSGregory Neil Shapiro static int CollTime = 60;
124e92d3f3fSGregory Neil Shapiro 
125e92d3f3fSGregory Neil Shapiro /*
126e92d3f3fSGregory Neil Shapiro **  time granularity: 10s (that's one "tick")
127e92d3f3fSGregory Neil Shapiro **  will be initialised to ConnectionRateWindowSize/CHTSIZE
128e92d3f3fSGregory Neil Shapiro **  before being used the first time
129e92d3f3fSGregory Neil Shapiro */
130e92d3f3fSGregory Neil Shapiro 
131e92d3f3fSGregory Neil Shapiro static int ChtGran = -1;
132e92d3f3fSGregory Neil Shapiro static CHash_T CHashAry[CPMHSIZE];
1335b0945b5SGregory Neil Shapiro static CTime_T srv_Times[CHTSIZE];
1345b0945b5SGregory Neil Shapiro 
1355b0945b5SGregory Neil Shapiro #ifndef MAX_CT_STEPS
1365b0945b5SGregory Neil Shapiro # define MAX_CT_STEPS	10
1375b0945b5SGregory Neil Shapiro #endif
138e92d3f3fSGregory Neil Shapiro 
139e92d3f3fSGregory Neil Shapiro /*
1405b0945b5SGregory Neil Shapiro **  RATE_INIT - initialize local data
141e92d3f3fSGregory Neil Shapiro **
142e92d3f3fSGregory Neil Shapiro **	Parameters:
1435b0945b5SGregory Neil Shapiro **		none
144e92d3f3fSGregory Neil Shapiro **
145e92d3f3fSGregory Neil Shapiro **	Returns:
1465b0945b5SGregory Neil Shapiro **		none
147e92d3f3fSGregory Neil Shapiro **
148e92d3f3fSGregory Neil Shapiro **	Side effects:
1495b0945b5SGregory Neil Shapiro **		initializes static global data
150e92d3f3fSGregory Neil Shapiro */
151e92d3f3fSGregory Neil Shapiro 
1525b0945b5SGregory Neil Shapiro static void
rate_init()1535b0945b5SGregory Neil Shapiro rate_init()
154e92d3f3fSGregory Neil Shapiro {
1555b0945b5SGregory Neil Shapiro 	if (ChtGran > 0)
1565b0945b5SGregory Neil Shapiro 		return;
157e92d3f3fSGregory Neil Shapiro 	ChtGran = ConnectionRateWindowSize / CHTSIZE;
158e92d3f3fSGregory Neil Shapiro 	if (ChtGran <= 0)
159e92d3f3fSGregory Neil Shapiro 		ChtGran = 10;
160e92d3f3fSGregory Neil Shapiro 	memset(CHashAry, 0, sizeof(CHashAry));
1615b0945b5SGregory Neil Shapiro 	memset(srv_Times, 0, sizeof(srv_Times));
1625b0945b5SGregory Neil Shapiro 	return;
163e92d3f3fSGregory Neil Shapiro }
164e92d3f3fSGregory Neil Shapiro 
1655b0945b5SGregory Neil Shapiro /*
1665b0945b5SGregory Neil Shapiro **  GEN_HASH - calculate a hash value
1675b0945b5SGregory Neil Shapiro **
1685b0945b5SGregory Neil Shapiro **	Parameters:
1695b0945b5SGregory Neil Shapiro **		saddr - client address
1705b0945b5SGregory Neil Shapiro **
1715b0945b5SGregory Neil Shapiro **	Returns:
1725b0945b5SGregory Neil Shapiro **		hash value
1735b0945b5SGregory Neil Shapiro */
1745b0945b5SGregory Neil Shapiro 
1755b0945b5SGregory Neil Shapiro static unsigned int
gen_hash(saddr)1765b0945b5SGregory Neil Shapiro gen_hash(saddr)
1775b0945b5SGregory Neil Shapiro 	SOCKADDR *saddr;
178e92d3f3fSGregory Neil Shapiro {
1795b0945b5SGregory Neil Shapiro 	unsigned int hv;
1805b0945b5SGregory Neil Shapiro 	int i;
181e92d3f3fSGregory Neil Shapiro 	int addrlen;
1825b0945b5SGregory Neil Shapiro 	char *p;
183e92d3f3fSGregory Neil Shapiro #if HASH_ALG != 1
184e92d3f3fSGregory Neil Shapiro 	int c, d;
1855b0945b5SGregory Neil Shapiro #endif
186e92d3f3fSGregory Neil Shapiro 
1875b0945b5SGregory Neil Shapiro 	hv = 0xABC3D20F;
188e92d3f3fSGregory Neil Shapiro 	switch (saddr->sa.sa_family)
189e92d3f3fSGregory Neil Shapiro 	{
190e92d3f3fSGregory Neil Shapiro #if NETINET
191e92d3f3fSGregory Neil Shapiro 	  case AF_INET:
192e92d3f3fSGregory Neil Shapiro 		p = (char *)&saddr->sin.sin_addr;
193e92d3f3fSGregory Neil Shapiro 		addrlen = sizeof(struct in_addr);
194e92d3f3fSGregory Neil Shapiro 		break;
195e92d3f3fSGregory Neil Shapiro #endif /* NETINET */
196e92d3f3fSGregory Neil Shapiro #if NETINET6
197e92d3f3fSGregory Neil Shapiro 	  case AF_INET6:
198e92d3f3fSGregory Neil Shapiro 		p = (char *)&saddr->sin6.sin6_addr;
199e92d3f3fSGregory Neil Shapiro 		addrlen = sizeof(struct in6_addr);
200e92d3f3fSGregory Neil Shapiro 		break;
201e92d3f3fSGregory Neil Shapiro #endif /* NETINET6 */
202e92d3f3fSGregory Neil Shapiro 	  default:
203e92d3f3fSGregory Neil Shapiro 		/* should not happen */
204e92d3f3fSGregory Neil Shapiro 		return -1;
205e92d3f3fSGregory Neil Shapiro 	}
206e92d3f3fSGregory Neil Shapiro 
207e92d3f3fSGregory Neil Shapiro 	/* compute hash value */
208e92d3f3fSGregory Neil Shapiro 	for (i = 0; i < addrlen; ++i, ++p)
209e92d3f3fSGregory Neil Shapiro #if HASH_ALG == 1
210e92d3f3fSGregory Neil Shapiro 		hv = (hv << 5) ^ (hv >> 23) ^ *p;
211e92d3f3fSGregory Neil Shapiro 	hv = (hv ^ (hv >> 16));
212e92d3f3fSGregory Neil Shapiro #elif HASH_ALG == 2
213e92d3f3fSGregory Neil Shapiro 	{
214e92d3f3fSGregory Neil Shapiro 		d = *p;
215e92d3f3fSGregory Neil Shapiro 		c = d;
216e92d3f3fSGregory Neil Shapiro 		c ^= c<<6;
217e92d3f3fSGregory Neil Shapiro 		hv += (c<<11) ^ (c>>1);
218e92d3f3fSGregory Neil Shapiro 		hv ^= (d<<14) + (d<<7) + (d<<4) + d;
219e92d3f3fSGregory Neil Shapiro 	}
220e92d3f3fSGregory Neil Shapiro #elif HASH_ALG == 3
221e92d3f3fSGregory Neil Shapiro 	{
222e92d3f3fSGregory Neil Shapiro 		hv = (hv << 4) + *p;
223e92d3f3fSGregory Neil Shapiro 		d = hv & 0xf0000000;
224e92d3f3fSGregory Neil Shapiro 		if (d != 0)
225e92d3f3fSGregory Neil Shapiro 		{
226e92d3f3fSGregory Neil Shapiro 			hv ^= (d >> 24);
227e92d3f3fSGregory Neil Shapiro 			hv ^= d;
228e92d3f3fSGregory Neil Shapiro 		}
229e92d3f3fSGregory Neil Shapiro 	}
230e92d3f3fSGregory Neil Shapiro #else /* HASH_ALG == 1 */
231*d39bd2c1SGregory Neil Shapiro # error "unsupported HASH_ALG"
2325b0945b5SGregory Neil Shapiro 	hv = ((hv << 1) ^ (*p & 0377)) % cctx->cc_size; ???
233e92d3f3fSGregory Neil Shapiro #endif /* HASH_ALG == 1 */
2345b0945b5SGregory Neil Shapiro 
2355b0945b5SGregory Neil Shapiro 	return hv;
236e92d3f3fSGregory Neil Shapiro }
237e92d3f3fSGregory Neil Shapiro 
2385b0945b5SGregory Neil Shapiro /*
2395b0945b5SGregory Neil Shapiro **  CONN_LIMIT - Evaluate connection limits
2405b0945b5SGregory Neil Shapiro **
2415b0945b5SGregory Neil Shapiro **	Parameters:
2425b0945b5SGregory Neil Shapiro **		e -- envelope (_FFR_OCC, for logging only)
2435b0945b5SGregory Neil Shapiro **		now - current time in secs
2445b0945b5SGregory Neil Shapiro **		saddr - client address
2455b0945b5SGregory Neil Shapiro **		clflags - update data / check only / ...
2465b0945b5SGregory Neil Shapiro **		hashary - hash array
2475b0945b5SGregory Neil Shapiro **		ratelimit - rate limit (_FFR_OCC only)
2485b0945b5SGregory Neil Shapiro **		conclimit - concurrency limit (_FFR_OCC only)
2495b0945b5SGregory Neil Shapiro **
2505b0945b5SGregory Neil Shapiro **	Returns:
2515b0945b5SGregory Neil Shapiro #if _FFR_OCC
2525b0945b5SGregory Neil Shapiro **		outgoing: limit exceeded?
2535b0945b5SGregory Neil Shapiro #endif
2545b0945b5SGregory Neil Shapiro **		incoming:
2555b0945b5SGregory Neil Shapiro **		  connection rate (connections / ConnectionRateWindowSize)
2565b0945b5SGregory Neil Shapiro */
2575b0945b5SGregory Neil Shapiro 
2585b0945b5SGregory Neil Shapiro int
conn_limits(e,now,saddr,clflags,hashary,ratelimit,conclimit)2595b0945b5SGregory Neil Shapiro conn_limits(e, now, saddr, clflags, hashary, ratelimit, conclimit)
2605b0945b5SGregory Neil Shapiro 	ENVELOPE *e;
2615b0945b5SGregory Neil Shapiro 	time_t now;
2625b0945b5SGregory Neil Shapiro 	SOCKADDR *saddr;
2635b0945b5SGregory Neil Shapiro 	int clflags;
2645b0945b5SGregory Neil Shapiro 	CHash_T hashary[];
2655b0945b5SGregory Neil Shapiro 	int ratelimit;
2665b0945b5SGregory Neil Shapiro 	int conclimit;
2675b0945b5SGregory Neil Shapiro {
2685b0945b5SGregory Neil Shapiro 	int i;
2695b0945b5SGregory Neil Shapiro 	int cnt;
2705b0945b5SGregory Neil Shapiro 	bool coll;
2715b0945b5SGregory Neil Shapiro 	CHash_T *chBest = NULL;
2725b0945b5SGregory Neil Shapiro 	CTime_T *ct = NULL;
2735b0945b5SGregory Neil Shapiro 	unsigned int ticks;
2745b0945b5SGregory Neil Shapiro 	unsigned int hv;
2755b0945b5SGregory Neil Shapiro #if _FFR_OCC
2765b0945b5SGregory Neil Shapiro 	bool exceeded = false;
2775b0945b5SGregory Neil Shapiro 	int *prv, *pcv;
2785b0945b5SGregory Neil Shapiro #endif
2795b0945b5SGregory Neil Shapiro #if RATECTL_DEBUG || _FFR_OCC
2805b0945b5SGregory Neil Shapiro 	bool logit = false;
2815b0945b5SGregory Neil Shapiro #endif
2825b0945b5SGregory Neil Shapiro 
2835b0945b5SGregory Neil Shapiro 	cnt = 0;
2845b0945b5SGregory Neil Shapiro 	hv = gen_hash(saddr);
2855b0945b5SGregory Neil Shapiro 	ticks = now / ChtGran;
2865b0945b5SGregory Neil Shapiro 
287e92d3f3fSGregory Neil Shapiro 	coll = true;
288e92d3f3fSGregory Neil Shapiro 	for (i = 0; i < MAX_CT_STEPS; ++i)
289e92d3f3fSGregory Neil Shapiro 	{
2905b0945b5SGregory Neil Shapiro 		CHash_T *ch = &hashary[(hv + i) & CPMHMASK];
291e92d3f3fSGregory Neil Shapiro 
292e92d3f3fSGregory Neil Shapiro #if NETINET
293e92d3f3fSGregory Neil Shapiro 		if (saddr->sa.sa_family == AF_INET &&
294e92d3f3fSGregory Neil Shapiro 		    ch->ch_Family == AF_INET &&
295e92d3f3fSGregory Neil Shapiro 		    (saddr->sin.sin_addr.s_addr == ch->ch_Addr4.s_addr ||
296e92d3f3fSGregory Neil Shapiro 		     ch->ch_Addr4.s_addr == 0))
297e92d3f3fSGregory Neil Shapiro 		{
298e92d3f3fSGregory Neil Shapiro 			chBest = ch;
299e92d3f3fSGregory Neil Shapiro 			coll = false;
300e92d3f3fSGregory Neil Shapiro 			break;
301e92d3f3fSGregory Neil Shapiro 		}
302e92d3f3fSGregory Neil Shapiro #endif /* NETINET */
303e92d3f3fSGregory Neil Shapiro #if NETINET6
304e92d3f3fSGregory Neil Shapiro 		if (saddr->sa.sa_family == AF_INET6 &&
305e92d3f3fSGregory Neil Shapiro 		    ch->ch_Family == AF_INET6 &&
306e92d3f3fSGregory Neil Shapiro 		    (IN6_ARE_ADDR_EQUAL(&saddr->sin6.sin6_addr,
307e92d3f3fSGregory Neil Shapiro 				       &ch->ch_Addr6) != 0 ||
308e92d3f3fSGregory Neil Shapiro 		     IN6_IS_ADDR_UNSPECIFIED(&ch->ch_Addr6)))
309e92d3f3fSGregory Neil Shapiro 		{
310e92d3f3fSGregory Neil Shapiro 			chBest = ch;
311e92d3f3fSGregory Neil Shapiro 			coll = false;
312e92d3f3fSGregory Neil Shapiro 			break;
313e92d3f3fSGregory Neil Shapiro 		}
314e92d3f3fSGregory Neil Shapiro #endif /* NETINET6 */
315e92d3f3fSGregory Neil Shapiro 		if (chBest == NULL || ch->ch_LTime == 0 ||
316e92d3f3fSGregory Neil Shapiro 		    ch->ch_LTime < chBest->ch_LTime)
317e92d3f3fSGregory Neil Shapiro 			chBest = ch;
318e92d3f3fSGregory Neil Shapiro 	}
319e92d3f3fSGregory Neil Shapiro 
320e92d3f3fSGregory Neil Shapiro 	/* Let's update data... */
3215b0945b5SGregory Neil Shapiro 	if ((clflags & (SM_CLFL_UPDATE|SM_CLFL_EXC)) != 0)
322e92d3f3fSGregory Neil Shapiro 	{
323e92d3f3fSGregory Neil Shapiro 		if (coll && (now - chBest->ch_LTime < CollTime))
324e92d3f3fSGregory Neil Shapiro 		{
325e92d3f3fSGregory Neil Shapiro 			/*
326e92d3f3fSGregory Neil Shapiro 			**  increment the number of collisions last
327e92d3f3fSGregory Neil Shapiro 			**  CollTime for this client
328e92d3f3fSGregory Neil Shapiro 			*/
329e92d3f3fSGregory Neil Shapiro 
330e92d3f3fSGregory Neil Shapiro 			chBest->ch_colls++;
331e92d3f3fSGregory Neil Shapiro 
332e92d3f3fSGregory Neil Shapiro 			/*
333e92d3f3fSGregory Neil Shapiro 			**  Maybe shall log if collision rate is too high...
334e92d3f3fSGregory Neil Shapiro 			**  and take measures to resize tables
335e92d3f3fSGregory Neil Shapiro 			**  if this is the case
336e92d3f3fSGregory Neil Shapiro 			*/
337e92d3f3fSGregory Neil Shapiro 		}
338e92d3f3fSGregory Neil Shapiro 
339e92d3f3fSGregory Neil Shapiro 		/*
340e92d3f3fSGregory Neil Shapiro 		**  If it's not a match, then replace the data.
341e92d3f3fSGregory Neil Shapiro 		**  Note: this purges the history of a colliding entry,
342e92d3f3fSGregory Neil Shapiro 		**  which may cause "overruns", i.e., if two entries are
343e92d3f3fSGregory Neil Shapiro 		**  "cancelling" each other out, then they may exceed
344e92d3f3fSGregory Neil Shapiro 		**  the limits that are set. This might be mitigated a bit
345e92d3f3fSGregory Neil Shapiro 		**  by the above "best of 5" function however.
346e92d3f3fSGregory Neil Shapiro 		**
347e92d3f3fSGregory Neil Shapiro 		**  Alternative approach: just use the old data, which may
348e92d3f3fSGregory Neil Shapiro 		**  cause false positives however.
3495b0945b5SGregory Neil Shapiro 		**  To activate this, deactivate the memset() call.
350e92d3f3fSGregory Neil Shapiro 		*/
351e92d3f3fSGregory Neil Shapiro 
352e92d3f3fSGregory Neil Shapiro 		if (coll)
353e92d3f3fSGregory Neil Shapiro 		{
354e92d3f3fSGregory Neil Shapiro #if NETINET
355e92d3f3fSGregory Neil Shapiro 			if (saddr->sa.sa_family == AF_INET)
356e92d3f3fSGregory Neil Shapiro 			{
357e92d3f3fSGregory Neil Shapiro 				chBest->ch_Family = AF_INET;
358e92d3f3fSGregory Neil Shapiro 				chBest->ch_Addr4 = saddr->sin.sin_addr;
359e92d3f3fSGregory Neil Shapiro 			}
360e92d3f3fSGregory Neil Shapiro #endif /* NETINET */
361e92d3f3fSGregory Neil Shapiro #if NETINET6
362e92d3f3fSGregory Neil Shapiro 			if (saddr->sa.sa_family == AF_INET6)
363e92d3f3fSGregory Neil Shapiro 			{
364e92d3f3fSGregory Neil Shapiro 				chBest->ch_Family = AF_INET6;
365e92d3f3fSGregory Neil Shapiro 				chBest->ch_Addr6 = saddr->sin6.sin6_addr;
366e92d3f3fSGregory Neil Shapiro 			}
367e92d3f3fSGregory Neil Shapiro #endif /* NETINET6 */
368e92d3f3fSGregory Neil Shapiro 			memset(chBest->ch_Times, '\0',
369e92d3f3fSGregory Neil Shapiro 			       sizeof(chBest->ch_Times));
370e92d3f3fSGregory Neil Shapiro 		}
371e92d3f3fSGregory Neil Shapiro 
372e92d3f3fSGregory Neil Shapiro 		chBest->ch_LTime = now;
3735b0945b5SGregory Neil Shapiro 		ct = &chBest->ch_Times[ticks % CHTSIZE];
374e92d3f3fSGregory Neil Shapiro 
375e92d3f3fSGregory Neil Shapiro 		if (ct->ct_Ticks != ticks)
376e92d3f3fSGregory Neil Shapiro 		{
377e92d3f3fSGregory Neil Shapiro 			ct->ct_Ticks = ticks;
378e92d3f3fSGregory Neil Shapiro 			ct->ct_Count = 0;
379e92d3f3fSGregory Neil Shapiro 		}
3805b0945b5SGregory Neil Shapiro 		if ((clflags & SM_CLFL_UPDATE) != 0)
381e92d3f3fSGregory Neil Shapiro 			++ct->ct_Count;
382e92d3f3fSGregory Neil Shapiro 	}
383e92d3f3fSGregory Neil Shapiro 
384e92d3f3fSGregory Neil Shapiro 	/* Now let's count connections on the window */
385e92d3f3fSGregory Neil Shapiro 	for (i = 0; i < CHTSIZE; ++i)
386e92d3f3fSGregory Neil Shapiro 	{
3875b0945b5SGregory Neil Shapiro 		CTime_T *cth;
388e92d3f3fSGregory Neil Shapiro 
3895b0945b5SGregory Neil Shapiro 		cth = &chBest->ch_Times[i];
3905b0945b5SGregory Neil Shapiro 		if (cth->ct_Ticks <= ticks && cth->ct_Ticks >= ticks - CHTSIZE)
3915b0945b5SGregory Neil Shapiro 			cnt += cth->ct_Count;
3925b0945b5SGregory Neil Shapiro 	}
3935b0945b5SGregory Neil Shapiro #if _FFR_OCC
3945b0945b5SGregory Neil Shapiro 	prv = pcv = NULL;
3955b0945b5SGregory Neil Shapiro 	if (ct != NULL && ((clflags & SM_CLFL_EXC) != 0))
3965b0945b5SGregory Neil Shapiro 	{
3975b0945b5SGregory Neil Shapiro 		if (ratelimit > 0)
3985b0945b5SGregory Neil Shapiro 		{
3995b0945b5SGregory Neil Shapiro 			if (cnt < ratelimit)
4005b0945b5SGregory Neil Shapiro 				prv = &(ct->ct_Count);
4015b0945b5SGregory Neil Shapiro 			else
4025b0945b5SGregory Neil Shapiro 				exceeded = true;
4035b0945b5SGregory Neil Shapiro 		}
4045b0945b5SGregory Neil Shapiro 		else if (ratelimit < 0 && ct->ct_Count > 0)
4055b0945b5SGregory Neil Shapiro 			--ct->ct_Count;
406e92d3f3fSGregory Neil Shapiro 	}
407e92d3f3fSGregory Neil Shapiro 
4085b0945b5SGregory Neil Shapiro 	if (chBest != NULL && ((clflags & SM_CLFL_EXC) != 0))
4095b0945b5SGregory Neil Shapiro 	{
4105b0945b5SGregory Neil Shapiro 		if (conclimit > 0)
4115b0945b5SGregory Neil Shapiro 		{
4125b0945b5SGregory Neil Shapiro 			if (chBest->ch_oc < conclimit)
4135b0945b5SGregory Neil Shapiro 				pcv = &(chBest->ch_oc);
4145b0945b5SGregory Neil Shapiro 			else
4155b0945b5SGregory Neil Shapiro 				exceeded = true;
4165b0945b5SGregory Neil Shapiro 		}
4175b0945b5SGregory Neil Shapiro 		else if (conclimit < 0 && chBest->ch_oc > 0)
4185b0945b5SGregory Neil Shapiro 			--chBest->ch_oc;
4195b0945b5SGregory Neil Shapiro 	}
4205b0945b5SGregory Neil Shapiro #endif
4215b0945b5SGregory Neil Shapiro 
422e92d3f3fSGregory Neil Shapiro #if RATECTL_DEBUG
4235b0945b5SGregory Neil Shapiro 	logit = true;
4245b0945b5SGregory Neil Shapiro #endif
4255b0945b5SGregory Neil Shapiro #if RATECTL_DEBUG || _FFR_OCC
4265b0945b5SGregory Neil Shapiro # if _FFR_OCC
4275b0945b5SGregory Neil Shapiro 	if (!exceeded)
4285b0945b5SGregory Neil Shapiro 	{
4295b0945b5SGregory Neil Shapiro 		if (prv != NULL)
4305b0945b5SGregory Neil Shapiro 			++*prv, ++cnt;
4315b0945b5SGregory Neil Shapiro 		if (pcv != NULL)
4325b0945b5SGregory Neil Shapiro 			++*pcv;
4335b0945b5SGregory Neil Shapiro 	}
4345b0945b5SGregory Neil Shapiro 	logit = exceeded || LogLevel > 11;
4355b0945b5SGregory Neil Shapiro # endif
4365b0945b5SGregory Neil Shapiro 	if (logit)
4375b0945b5SGregory Neil Shapiro 		sm_syslog(LOG_DEBUG, e != NULL ? e->e_id : NOQID,
4385b0945b5SGregory Neil Shapiro 			"conn_limits: addr=%s, flags=0x%x, rate=%d/%d, conc=%d/%d, exc=%d",
4395b0945b5SGregory Neil Shapiro 			saddr->sa.sa_family == AF_INET
4405b0945b5SGregory Neil Shapiro 				? inet_ntoa(saddr->sin.sin_addr) : "???",
4415b0945b5SGregory Neil Shapiro 			clflags, cnt, ratelimit,
4425b0945b5SGregory Neil Shapiro # if _FFR_OCC
4435b0945b5SGregory Neil Shapiro 			chBest != NULL ? chBest->ch_oc : -1
4445b0945b5SGregory Neil Shapiro # else
4455b0945b5SGregory Neil Shapiro 			-2
4465b0945b5SGregory Neil Shapiro # endif
4475b0945b5SGregory Neil Shapiro 			, conclimit
4485b0945b5SGregory Neil Shapiro # if _FFR_OCC
4495b0945b5SGregory Neil Shapiro 			, exceeded
4505b0945b5SGregory Neil Shapiro # else
4515b0945b5SGregory Neil Shapiro 			, 0
4525b0945b5SGregory Neil Shapiro # endif
4535b0945b5SGregory Neil Shapiro 			);
4545b0945b5SGregory Neil Shapiro #endif
4555b0945b5SGregory Neil Shapiro #if _FFR_OCC
4565b0945b5SGregory Neil Shapiro 	if ((clflags & SM_CLFL_EXC) != 0)
4575b0945b5SGregory Neil Shapiro 		return exceeded;
4585b0945b5SGregory Neil Shapiro #endif
459e92d3f3fSGregory Neil Shapiro 	return cnt;
460e92d3f3fSGregory Neil Shapiro }
461e92d3f3fSGregory Neil Shapiro 
462e92d3f3fSGregory Neil Shapiro /*
4635b0945b5SGregory Neil Shapiro **  CLIENT_RATE - Evaluate connection rate per SMTP client
4645b0945b5SGregory Neil Shapiro **
4655b0945b5SGregory Neil Shapiro **	Parameters:
4665b0945b5SGregory Neil Shapiro **		now - current time in secs
4675b0945b5SGregory Neil Shapiro **		saddr - client address
4685b0945b5SGregory Neil Shapiro **		clflags - update data / check only
4695b0945b5SGregory Neil Shapiro **
4705b0945b5SGregory Neil Shapiro **	Returns:
4715b0945b5SGregory Neil Shapiro **		connection rate (connections / ConnectionRateWindowSize)
4725b0945b5SGregory Neil Shapiro **
4735b0945b5SGregory Neil Shapiro **	Side effects:
4745b0945b5SGregory Neil Shapiro **		update static global data
4755b0945b5SGregory Neil Shapiro */
4765b0945b5SGregory Neil Shapiro 
4775b0945b5SGregory Neil Shapiro static int
client_rate(now,saddr,clflags)4785b0945b5SGregory Neil Shapiro client_rate(now, saddr, clflags)
4795b0945b5SGregory Neil Shapiro 	time_t now;
4805b0945b5SGregory Neil Shapiro 	SOCKADDR *saddr;
4815b0945b5SGregory Neil Shapiro 	int clflags;
4825b0945b5SGregory Neil Shapiro {
4835b0945b5SGregory Neil Shapiro 	rate_init();
4845b0945b5SGregory Neil Shapiro 	return conn_limits(NULL, now, saddr, clflags, CHashAry, 0, 0);
4855b0945b5SGregory Neil Shapiro }
4865b0945b5SGregory Neil Shapiro 
4875b0945b5SGregory Neil Shapiro /*
488e92d3f3fSGregory Neil Shapiro **  TOTAL_RATE - Evaluate global connection rate
489e92d3f3fSGregory Neil Shapiro **
490e92d3f3fSGregory Neil Shapiro **	Parameters:
491e92d3f3fSGregory Neil Shapiro **		now - current time in secs
492e92d3f3fSGregory Neil Shapiro **		update - update data / check only
493e92d3f3fSGregory Neil Shapiro **
494e92d3f3fSGregory Neil Shapiro **	Returns:
495e92d3f3fSGregory Neil Shapiro **		connection rate (connections / ConnectionRateWindowSize)
496e92d3f3fSGregory Neil Shapiro */
497e92d3f3fSGregory Neil Shapiro 
498e92d3f3fSGregory Neil Shapiro static int
total_rate(now,update)499e92d3f3fSGregory Neil Shapiro total_rate(now, update)
500e92d3f3fSGregory Neil Shapiro 	time_t now;
501e92d3f3fSGregory Neil Shapiro 	bool update;
502e92d3f3fSGregory Neil Shapiro {
503e92d3f3fSGregory Neil Shapiro 	int i;
504e92d3f3fSGregory Neil Shapiro 	int cnt = 0;
505e92d3f3fSGregory Neil Shapiro 	CTime_T *ct;
506e92d3f3fSGregory Neil Shapiro 	unsigned int ticks;
507e92d3f3fSGregory Neil Shapiro 
5085b0945b5SGregory Neil Shapiro 	rate_init();
509e92d3f3fSGregory Neil Shapiro 	ticks = now / ChtGran;
510e92d3f3fSGregory Neil Shapiro 
511e92d3f3fSGregory Neil Shapiro 	/* Let's update data */
512e92d3f3fSGregory Neil Shapiro 	if (update)
513e92d3f3fSGregory Neil Shapiro 	{
514e92d3f3fSGregory Neil Shapiro 		ct = &srv_Times[ticks % CHTSIZE];
515e92d3f3fSGregory Neil Shapiro 
516e92d3f3fSGregory Neil Shapiro 		if (ct->ct_Ticks != ticks)
517e92d3f3fSGregory Neil Shapiro 		{
518e92d3f3fSGregory Neil Shapiro 			ct->ct_Ticks = ticks;
519e92d3f3fSGregory Neil Shapiro 			ct->ct_Count = 0;
520e92d3f3fSGregory Neil Shapiro 		}
521e92d3f3fSGregory Neil Shapiro 		++ct->ct_Count;
522e92d3f3fSGregory Neil Shapiro 	}
523e92d3f3fSGregory Neil Shapiro 
524e92d3f3fSGregory Neil Shapiro 	/* Let's count connections on the window */
525e92d3f3fSGregory Neil Shapiro 	for (i = 0; i < CHTSIZE; ++i)
526e92d3f3fSGregory Neil Shapiro 	{
527e92d3f3fSGregory Neil Shapiro 		ct = &srv_Times[i];
528e92d3f3fSGregory Neil Shapiro 
529e92d3f3fSGregory Neil Shapiro 		if (ct->ct_Ticks <= ticks && ct->ct_Ticks >= ticks - CHTSIZE)
530e92d3f3fSGregory Neil Shapiro 			cnt += ct->ct_Count;
531e92d3f3fSGregory Neil Shapiro 	}
532e92d3f3fSGregory Neil Shapiro 
533e92d3f3fSGregory Neil Shapiro #if RATECTL_DEBUG
534e92d3f3fSGregory Neil Shapiro 	sm_syslog(LOG_WARNING, NOQID,
5355b0945b5SGregory Neil Shapiro 		"total: cnt=%d, CHTSIZE=%d, ChtGran=%d",
536e92d3f3fSGregory Neil Shapiro 		cnt, CHTSIZE, ChtGran);
5375b0945b5SGregory Neil Shapiro #endif
538e92d3f3fSGregory Neil Shapiro 
539e92d3f3fSGregory Neil Shapiro 	return cnt;
540e92d3f3fSGregory Neil Shapiro }
5415b0945b5SGregory Neil Shapiro 
5425b0945b5SGregory Neil Shapiro #if RATECTL_DEBUG || _FFR_OCC
5435b0945b5SGregory Neil Shapiro void
dump_ch(fp)5445b0945b5SGregory Neil Shapiro dump_ch(fp)
5455b0945b5SGregory Neil Shapiro 	SM_FILE_T *fp;
5465b0945b5SGregory Neil Shapiro {
5475b0945b5SGregory Neil Shapiro 	int i, j, cnt;
5485b0945b5SGregory Neil Shapiro 	unsigned int ticks;
5495b0945b5SGregory Neil Shapiro 
5505b0945b5SGregory Neil Shapiro 	ticks = time(NULL) / ChtGran;
5515b0945b5SGregory Neil Shapiro 	sm_io_fprintf(fp, SM_TIME_DEFAULT, "dump_ch\n");
5525b0945b5SGregory Neil Shapiro 	for (i = 0; i < CPMHSIZE; i++)
5535b0945b5SGregory Neil Shapiro 	{
5545b0945b5SGregory Neil Shapiro 		CHash_T *ch = &CHashAry[i];
5555b0945b5SGregory Neil Shapiro 		bool valid;
5565b0945b5SGregory Neil Shapiro 
5575b0945b5SGregory Neil Shapiro 		valid = false;
5585b0945b5SGregory Neil Shapiro # if NETINET
5595b0945b5SGregory Neil Shapiro 		valid = (ch->ch_Family == AF_INET);
5605b0945b5SGregory Neil Shapiro 		if (valid)
5615b0945b5SGregory Neil Shapiro 			sm_io_fprintf(fp, SM_TIME_DEFAULT, "ip=%s ",
5625b0945b5SGregory Neil Shapiro 				inet_ntoa(ch->ch_Addr4));
5635b0945b5SGregory Neil Shapiro # endif /* NETINET */
5645b0945b5SGregory Neil Shapiro # if NETINET6
5655b0945b5SGregory Neil Shapiro 		if (ch->ch_Family == AF_INET6)
5665b0945b5SGregory Neil Shapiro 		{
5675b0945b5SGregory Neil Shapiro 			char buf[64], *str;
5685b0945b5SGregory Neil Shapiro 
5695b0945b5SGregory Neil Shapiro 			valid = true;
5705b0945b5SGregory Neil Shapiro 			str = anynet_ntop(&ch->ch_Addr6, buf, sizeof(buf));
5715b0945b5SGregory Neil Shapiro 			if (str != NULL)
5725b0945b5SGregory Neil Shapiro 				sm_io_fprintf(fp, SM_TIME_DEFAULT, "ip=%s ",
5735b0945b5SGregory Neil Shapiro 					str);
5745b0945b5SGregory Neil Shapiro 		}
5755b0945b5SGregory Neil Shapiro # endif /* NETINET6 */
5765b0945b5SGregory Neil Shapiro 		if (!valid)
5775b0945b5SGregory Neil Shapiro 			continue;
5785b0945b5SGregory Neil Shapiro 
5795b0945b5SGregory Neil Shapiro 		cnt = 0;
5805b0945b5SGregory Neil Shapiro 		for (j = 0; j < CHTSIZE; ++j)
5815b0945b5SGregory Neil Shapiro 		{
5825b0945b5SGregory Neil Shapiro 			CTime_T *cth;
5835b0945b5SGregory Neil Shapiro 
5845b0945b5SGregory Neil Shapiro 			cth = &ch->ch_Times[j];
5855b0945b5SGregory Neil Shapiro 			if (cth->ct_Ticks <= ticks && cth->ct_Ticks >= ticks - CHTSIZE)
5865b0945b5SGregory Neil Shapiro 				cnt += cth->ct_Count;
5875b0945b5SGregory Neil Shapiro 		}
5885b0945b5SGregory Neil Shapiro 
5895b0945b5SGregory Neil Shapiro 		sm_io_fprintf(fp, SM_TIME_DEFAULT, "time=%ld cnt=%d ",
5905b0945b5SGregory Neil Shapiro 			(long) ch->ch_LTime, cnt);
5915b0945b5SGregory Neil Shapiro # if _FFR_OCC
5925b0945b5SGregory Neil Shapiro 		sm_io_fprintf(fp, SM_TIME_DEFAULT, "oc=%d", ch->ch_oc);
5935b0945b5SGregory Neil Shapiro # endif
5945b0945b5SGregory Neil Shapiro 		sm_io_fprintf(fp, SM_TIME_DEFAULT, "\n");
5955b0945b5SGregory Neil Shapiro 	}
5965b0945b5SGregory Neil Shapiro 	sm_io_flush(fp, SM_TIME_DEFAULT);
5975b0945b5SGregory Neil Shapiro }
5985b0945b5SGregory Neil Shapiro 
5995b0945b5SGregory Neil Shapiro #endif /* RATECTL_DEBUG || _FFR_OCC */
600