anvil.c revision 1.4
1/*	$NetBSD: anvil.c,v 1.4 2022/10/08 16:12:44 christos Exp $	*/
2
3/*++
4/* NAME
5/*	anvil 8
6/* SUMMARY
7/*	Postfix session count and request rate control
8/* SYNOPSIS
9/*	\fBanvil\fR [generic Postfix daemon options]
10/* DESCRIPTION
11/*	The Postfix \fBanvil\fR(8) server maintains statistics about
12/*	client connection counts or client request rates. This
13/*	information can be used to defend against clients that
14/*	hammer a server with either too many simultaneous sessions,
15/*	or with too many successive requests within a configurable
16/*	time interval.  This server is designed to run under control
17/*	by the Postfix \fBmaster\fR(8) server.
18/*
19/*	In the following text, \fBident\fR specifies a (service,
20/*	client) combination. The exact syntax of that information
21/*	is application-dependent; the \fBanvil\fR(8) server does
22/*	not care.
23/* CONNECTION COUNT/RATE CONTROL
24/* .ad
25/* .fi
26/*	To register a new connection send the following request to
27/*	the \fBanvil\fR(8) server:
28/*
29/* .nf
30/*	    \fBrequest=connect\fR
31/*	    \fBident=\fIstring\fR
32/* .fi
33/*
34/*	The \fBanvil\fR(8) server answers with the number of
35/*	simultaneous connections and the number of connections per
36/*	unit time for the (service, client) combination specified
37/*	with \fBident\fR:
38/*
39/* .nf
40/*	    \fBstatus=0\fR
41/*	    \fBcount=\fInumber\fR
42/*	    \fBrate=\fInumber\fR
43/* .fi
44/*
45/*	To register a disconnect event send the following request
46/*	to the \fBanvil\fR(8) server:
47/*
48/* .nf
49/*	    \fBrequest=disconnect\fR
50/*	    \fBident=\fIstring\fR
51/* .fi
52/*
53/*	The \fBanvil\fR(8) server replies with:
54/*
55/* .nf
56/*	    \fBstatus=0\fR
57/* .fi
58/* MESSAGE RATE CONTROL
59/* .ad
60/* .fi
61/*	To register a message delivery request send the following
62/*	request to the \fBanvil\fR(8) server:
63/*
64/* .nf
65/*	    \fBrequest=message\fR
66/*	    \fBident=\fIstring\fR
67/* .fi
68/*
69/*	The \fBanvil\fR(8) server answers with the number of message
70/*	delivery requests per unit time for the (service, client)
71/*	combination specified with \fBident\fR:
72/*
73/* .nf
74/*	    \fBstatus=0\fR
75/*	    \fBrate=\fInumber\fR
76/* .fi
77/* RECIPIENT RATE CONTROL
78/* .ad
79/* .fi
80/*	To register a recipient request send the following request
81/*	to the \fBanvil\fR(8) server:
82/*
83/* .nf
84/*	    \fBrequest=recipient\fR
85/*	    \fBident=\fIstring\fR
86/* .fi
87/*
88/*	The \fBanvil\fR(8) server answers with the number of recipient
89/*	addresses per unit time for the (service, client) combination
90/*	specified with \fBident\fR:
91/*
92/* .nf
93/*	    \fBstatus=0\fR
94/*	    \fBrate=\fInumber\fR
95/* .fi
96/* TLS SESSION NEGOTIATION RATE CONTROL
97/* .ad
98/* .fi
99/*	The features described in this section are available with
100/*	Postfix 2.3 and later.
101/*
102/*	To register a request for a new (i.e. not cached) TLS session
103/*	send the following request to the \fBanvil\fR(8) server:
104/*
105/* .nf
106/*	    \fBrequest=newtls\fR
107/*	    \fBident=\fIstring\fR
108/* .fi
109/*
110/*	The \fBanvil\fR(8) server answers with the number of new
111/*	TLS session requests per unit time for the (service, client)
112/*	combination specified with \fBident\fR:
113/*
114/* .nf
115/*	    \fBstatus=0\fR
116/*	    \fBrate=\fInumber\fR
117/* .fi
118/*
119/*	To retrieve new TLS session request rate information without
120/*	updating the counter information, send:
121/*
122/* .nf
123/*	    \fBrequest=newtls_report\fR
124/*	    \fBident=\fIstring\fR
125/* .fi
126/*
127/*	The \fBanvil\fR(8) server answers with the number of new
128/*	TLS session requests per unit time for the (service, client)
129/*	combination specified with \fBident\fR:
130/*
131/* .nf
132/*	    \fBstatus=0\fR
133/*	    \fBrate=\fInumber\fR
134/* .fi
135/* AUTH RATE CONTROL
136/* .ad
137/* .fi
138/*	To register an AUTH request send the following request
139/*	to the \fBanvil\fR(8) server:
140/*
141/* .nf
142/*	    \fBrequest=auth\fR
143/*	    \fBident=\fIstring\fR
144/* .fi
145/*
146/*	The \fBanvil\fR(8) server answers with the number of auth
147/*	requests per unit time for the (service, client) combination
148/*	specified with \fBident\fR:
149/*
150/* .nf
151/*	    \fBstatus=0\fR
152/*	    \fBrate=\fInumber\fR
153/* .fi
154/* SECURITY
155/* .ad
156/* .fi
157/*	The \fBanvil\fR(8) server does not talk to the network or to local
158/*	users, and can run chrooted at fixed low privilege.
159/*
160/*	The \fBanvil\fR(8) server maintains an in-memory table with
161/*	information about recent clients requests.  No persistent
162/*	state is kept because standard system library routines are
163/*	not sufficiently robust for update-intensive applications.
164/*
165/*	Although the in-memory state is kept only temporarily, this
166/*	may require a lot of memory on systems that handle connections
167/*	from many remote clients.  To reduce memory usage, reduce
168/*	the time unit over which state is kept.
169/* DIAGNOSTICS
170/*	Problems and transactions are logged to \fBsyslogd\fR(8)
171/*	or \fBpostlogd\fR(8).
172/*
173/*	Upon exit, and every \fBanvil_status_update_time\fR
174/*	seconds, the server logs the maximal count and rate values measured,
175/*	together with (service, client) information and the time of day
176/*	associated with those events.
177/*	In order to avoid unnecessary overhead, no measurements
178/*	are done for activity that isn't concurrency limited or
179/*	rate limited.
180/* BUGS
181/*	Systems behind network address translating routers or proxies
182/*	appear to have the same client address and can run into connection
183/*	count and/or rate limits falsely.
184/*
185/*	In this preliminary implementation, a count (or rate) limited server
186/*	process can have only one remote client at a time. If a
187/*	server process reports
188/*	multiple simultaneous clients, state is kept only for the last
189/*	reported client.
190/*
191/*	The \fBanvil\fR(8) server automatically discards client
192/*	request information after it expires.  To prevent the
193/*	\fBanvil\fR(8) server from discarding client request rate
194/*	information too early or too late, a rate limited service
195/*	should always register connect/disconnect events even when
196/*	it does not explicitly limit them.
197/* CONFIGURATION PARAMETERS
198/* .ad
199/* .fi
200/*	On low-traffic mail systems, changes to \fBmain.cf\fR are
201/*	picked up automatically as \fBanvil\fR(8) processes run for
202/*	only a limited amount of time. On other mail systems, use
203/*	the command "\fBpostfix reload\fR" to speed up a change.
204/*
205/*	The text below provides only a parameter summary. See
206/*	\fBpostconf\fR(5) for more details including examples.
207/* .IP "\fBanvil_rate_time_unit (60s)\fR"
208/*	The time unit over which client connection rates and other rates
209/*	are calculated.
210/* .IP "\fBanvil_status_update_time (600s)\fR"
211/*	How frequently the \fBanvil\fR(8) connection and rate limiting server
212/*	logs peak usage information.
213/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
214/*	The default location of the Postfix main.cf and master.cf
215/*	configuration files.
216/* .IP "\fBdaemon_timeout (18000s)\fR"
217/*	How much time a Postfix daemon process may take to handle a
218/*	request before it is terminated by a built-in watchdog timer.
219/* .IP "\fBipc_timeout (3600s)\fR"
220/*	The time limit for sending or receiving information over an internal
221/*	communication channel.
222/* .IP "\fBmax_idle (100s)\fR"
223/*	The maximum amount of time that an idle Postfix daemon process waits
224/*	for an incoming connection before terminating voluntarily.
225/* .IP "\fBmax_use (100)\fR"
226/*	The maximal number of incoming connections that a Postfix daemon
227/*	process will service before terminating voluntarily.
228/* .IP "\fBprocess_id (read-only)\fR"
229/*	The process ID of a Postfix command or daemon process.
230/* .IP "\fBprocess_name (read-only)\fR"
231/*	The process name of a Postfix command or daemon process.
232/* .IP "\fBsyslog_facility (mail)\fR"
233/*	The syslog facility of Postfix logging.
234/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
235/*	A prefix that is prepended to the process name in syslog
236/*	records, so that, for example, "smtpd" becomes "prefix/smtpd".
237/* .PP
238/*	Available in Postfix 3.3 and later:
239/* .IP "\fBservice_name (read-only)\fR"
240/*	The master.cf service name of a Postfix daemon process.
241/* SEE ALSO
242/*	smtpd(8), Postfix SMTP server
243/*	postconf(5), configuration parameters
244/*	master(5), generic daemon options
245/* README FILES
246/* .ad
247/* .fi
248/*	Use "\fBpostconf readme_directory\fR" or
249/*	"\fBpostconf html_directory\fR" to locate this information.
250/* .na
251/* .nf
252/*	TUNING_README, performance tuning
253/* LICENSE
254/* .ad
255/* .fi
256/*	The Secure Mailer license must be distributed with this software.
257/* HISTORY
258/* .ad
259/* .fi
260/*	The anvil service is available in Postfix 2.2 and later.
261/* AUTHOR(S)
262/*	Wietse Venema
263/*	IBM T.J. Watson Research
264/*	P.O. Box 704
265/*	Yorktown Heights, NY 10598, USA
266/*
267/*	Wietse Venema
268/*	Google, Inc.
269/*	111 8th Avenue
270/*	New York, NY 10011, USA
271/*--*/
272
273/* System library. */
274
275#include <sys_defs.h>
276#include <sys/time.h>
277#include <limits.h>
278
279/* Utility library. */
280
281#include <msg.h>
282#include <mymalloc.h>
283#include <htable.h>
284#include <stringops.h>
285#include <events.h>
286
287/* Global library. */
288
289#include <mail_conf.h>
290#include <mail_params.h>
291#include <mail_version.h>
292#include <mail_proto.h>
293#include <anvil_clnt.h>
294
295/* Server skeleton. */
296
297#include <mail_server.h>
298
299/* Application-specific. */
300
301 /*
302  * Configuration parameters.
303  */
304int     var_anvil_time_unit;
305int     var_anvil_stat_time;
306
307 /*
308  * Global dynamic state.
309  */
310static HTABLE *anvil_remote_map;	/* indexed by service+ remote client */
311
312 /*
313  * Remote connection state, one instance for each (service, client) pair.
314  */
315typedef struct {
316    char   *ident;			/* lookup key */
317    int     count;			/* connection count */
318    int     rate;			/* connection rate */
319    int     mail;			/* message rate */
320    int     rcpt;			/* recipient rate */
321    int     ntls;			/* new TLS session rate */
322    int     auth;			/* AUTH request rate */
323    time_t  start;			/* time of first rate sample */
324} ANVIL_REMOTE;
325
326 /*
327  * Local server state, one instance per anvil client connection. This allows
328  * us to clean up remote connection state when a local server goes away
329  * without cleaning up.
330  */
331typedef struct {
332    ANVIL_REMOTE *anvil_remote;		/* XXX should be list */
333} ANVIL_LOCAL;
334
335 /*
336  * The following operations are implemented as macros with recognizable
337  * names so that we don't lose sight of what the code is trying to do.
338  *
339  * Related operations are defined side by side so that the code implementing
340  * them isn't pages apart.
341  */
342
343/* Create new (service, client) state. */
344
345#define ANVIL_REMOTE_FIRST_CONN(remote, id) \
346    do { \
347	(remote)->ident = mystrdup(id); \
348	(remote)->count = 1; \
349	(remote)->rate = 1; \
350	(remote)->mail = 0; \
351	(remote)->rcpt = 0; \
352	(remote)->ntls = 0; \
353	(remote)->auth = 0; \
354	(remote)->start = event_time(); \
355    } while(0)
356
357/* Destroy unused (service, client) state. */
358
359#define ANVIL_REMOTE_FREE(remote) \
360    do { \
361	myfree((remote)->ident); \
362	myfree((void *) (remote)); \
363    } while(0)
364
365/* Reset or update rate information for existing (service, client) state. */
366
367#define ANVIL_REMOTE_RSET_RATE(remote, _start) \
368    do { \
369	(remote)->rate = 0; \
370	(remote)->mail = 0; \
371	(remote)->rcpt = 0; \
372	(remote)->ntls = 0; \
373	(remote)->auth = 0; \
374	(remote)->start = _start; \
375    } while(0)
376
377#define ANVIL_REMOTE_INCR_RATE(remote, _what) \
378    do { \
379	time_t _now = event_time(); \
380	if ((remote)->start + var_anvil_time_unit < _now) \
381	    ANVIL_REMOTE_RSET_RATE((remote), _now); \
382	if ((remote)->_what < INT_MAX) \
383            (remote)->_what += 1; \
384    } while(0)
385
386/* Update existing (service, client) state. */
387
388#define ANVIL_REMOTE_NEXT_CONN(remote) \
389    do { \
390	ANVIL_REMOTE_INCR_RATE((remote), rate); \
391	if ((remote)->count == 0) \
392	    event_cancel_timer(anvil_remote_expire, (void *) remote); \
393	(remote)->count++; \
394    } while(0)
395
396#define ANVIL_REMOTE_INCR_MAIL(remote)	ANVIL_REMOTE_INCR_RATE((remote), mail)
397
398#define ANVIL_REMOTE_INCR_RCPT(remote)	ANVIL_REMOTE_INCR_RATE((remote), rcpt)
399
400#define ANVIL_REMOTE_INCR_NTLS(remote)	ANVIL_REMOTE_INCR_RATE((remote), ntls)
401
402#define ANVIL_REMOTE_INCR_AUTH(remote)	ANVIL_REMOTE_INCR_RATE((remote), auth)
403
404/* Drop connection from (service, client) state. */
405
406#define ANVIL_REMOTE_DROP_ONE(remote) \
407    do { \
408	if ((remote) && (remote)->count > 0) { \
409	    if (--(remote)->count == 0) \
410		event_request_timer(anvil_remote_expire, (void *) remote, \
411			var_anvil_time_unit); \
412	} \
413    } while(0)
414
415/* Create local server state. */
416
417#define ANVIL_LOCAL_INIT(local) \
418    do { \
419	(local)->anvil_remote = 0; \
420    } while(0)
421
422/* Add remote connection to local server. */
423
424#define ANVIL_LOCAL_ADD_ONE(local, remote) \
425    do { \
426	/* XXX allow multiple remote clients per local server. */ \
427	if ((local)->anvil_remote) \
428	    ANVIL_REMOTE_DROP_ONE((local)->anvil_remote); \
429	(local)->anvil_remote = (remote); \
430    } while(0)
431
432/* Test if this remote connection is listed for this local server. */
433
434#define ANVIL_LOCAL_REMOTE_LINKED(local, remote) \
435    ((local)->anvil_remote == (remote))
436
437/* Drop specific remote connection from local server. */
438
439#define ANVIL_LOCAL_DROP_ONE(local, remote) \
440    do { \
441	/* XXX allow multiple remote clients per local server. */ \
442	if ((local)->anvil_remote == (remote)) \
443	    (local)->anvil_remote = 0; \
444    } while(0)
445
446/* Drop all remote connections from local server. */
447
448#define ANVIL_LOCAL_DROP_ALL(stream, local) \
449    do { \
450	 /* XXX allow multiple remote clients per local server. */ \
451	if ((local)->anvil_remote) \
452	    anvil_remote_disconnect((stream), (local)->anvil_remote->ident); \
453    } while (0)
454
455 /*
456  * Lookup table to map request names to action routines.
457  */
458typedef struct {
459    const char *name;
460    void    (*action) (VSTREAM *, const char *);
461} ANVIL_REQ_TABLE;
462
463 /*
464  * Run-time statistics for maximal connection counts and event rates. These
465  * store the peak resource usage, remote connection, and time. Absent a
466  * query interface, this information is logged at process exit time and at
467  * configurable intervals.
468  */
469typedef struct {
470    int     value;			/* peak value */
471    char   *ident;			/* lookup key */
472    time_t  when;			/* time of peak value */
473} ANVIL_MAX;
474
475static ANVIL_MAX max_conn_count;	/* peak connection count */
476static ANVIL_MAX max_conn_rate;		/* peak connection rate */
477static ANVIL_MAX max_mail_rate;		/* peak message rate */
478static ANVIL_MAX max_rcpt_rate;		/* peak recipient rate */
479static ANVIL_MAX max_ntls_rate;		/* peak new TLS session rate */
480static ANVIL_MAX max_auth_rate;		/* peak AUTH request rate */
481
482static int max_cache_size;		/* peak cache size */
483static time_t max_cache_time;		/* time of peak size */
484
485/* Update/report peak usage. */
486
487#define ANVIL_MAX_UPDATE(_max, _value, _ident) \
488    do { \
489	_max.value = _value; \
490	if (_max.ident == 0) { \
491	    _max.ident = mystrdup(_ident); \
492	} else if (!STREQ(_max.ident, _ident)) { \
493	    myfree(_max.ident); \
494	    _max.ident = mystrdup(_ident); \
495	} \
496	_max.when = event_time(); \
497    } while (0)
498
499#define ANVIL_MAX_RATE_REPORT(_max, _name) \
500    do { \
501	if (_max.value > 0) { \
502	    msg_info("statistics: max " _name " rate %d/%ds for (%s) at %.15s", \
503		_max.value, var_anvil_time_unit, \
504		_max.ident, ctime(&_max.when) + 4); \
505	    _max.value = 0; \
506	} \
507    } while (0);
508
509#define ANVIL_MAX_COUNT_REPORT(_max, _name) \
510    do { \
511	if (_max.value > 0) { \
512	    msg_info("statistics: max " _name " count %d for (%s) at %.15s", \
513		_max.value, _max.ident, ctime(&_max.when) + 4); \
514	    _max.value = 0; \
515	} \
516    } while (0);
517
518 /*
519  * Silly little macros.
520  */
521#define STR(x)			vstring_str(x)
522#define STREQ(x,y)		(strcmp((x), (y)) == 0)
523
524/* anvil_remote_expire - purge expired connection state */
525
526static void anvil_remote_expire(int unused_event, void *context)
527{
528    ANVIL_REMOTE *anvil_remote = (ANVIL_REMOTE *) context;
529    const char *myname = "anvil_remote_expire";
530
531    if (msg_verbose)
532	msg_info("%s %s", myname, anvil_remote->ident);
533
534    if (anvil_remote->count != 0)
535	msg_panic("%s: bad connection count: %d",
536		  myname, anvil_remote->count);
537
538    htable_delete(anvil_remote_map, anvil_remote->ident,
539		  (void (*) (void *)) 0);
540    ANVIL_REMOTE_FREE(anvil_remote);
541
542    if (msg_verbose)
543	msg_info("%s: anvil_remote_map used=%ld",
544		 myname, (long) anvil_remote_map->used);
545}
546
547/* anvil_remote_lookup - dump address status */
548
549static void anvil_remote_lookup(VSTREAM *client_stream, const char *ident)
550{
551    ANVIL_REMOTE *anvil_remote;
552    const char *myname = "anvil_remote_lookup";
553
554    if (msg_verbose)
555	msg_info("%s fd=%d stream=0x%lx ident=%s",
556		 myname, vstream_fileno(client_stream),
557		 (unsigned long) client_stream, ident);
558
559    /*
560     * Look up remote client information.
561     */
562    if ((anvil_remote =
563	 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) {
564	attr_print_plain(client_stream, ATTR_FLAG_NONE,
565			 SEND_ATTR_INT(ANVIL_ATTR_STATUS, ANVIL_STAT_OK),
566			 SEND_ATTR_INT(ANVIL_ATTR_COUNT, 0),
567			 SEND_ATTR_INT(ANVIL_ATTR_RATE, 0),
568			 SEND_ATTR_INT(ANVIL_ATTR_MAIL, 0),
569			 SEND_ATTR_INT(ANVIL_ATTR_RCPT, 0),
570			 SEND_ATTR_INT(ANVIL_ATTR_NTLS, 0),
571			 SEND_ATTR_INT(ANVIL_ATTR_AUTH, 0),
572			 ATTR_TYPE_END);
573    } else {
574
575	/*
576	 * Do not report stale information.
577	 */
578	if (anvil_remote->start != 0
579	    && anvil_remote->start + var_anvil_time_unit < event_time())
580	    ANVIL_REMOTE_RSET_RATE(anvil_remote, 0);
581	attr_print_plain(client_stream, ATTR_FLAG_NONE,
582			 SEND_ATTR_INT(ANVIL_ATTR_STATUS, ANVIL_STAT_OK),
583		       SEND_ATTR_INT(ANVIL_ATTR_COUNT, anvil_remote->count),
584			 SEND_ATTR_INT(ANVIL_ATTR_RATE, anvil_remote->rate),
585			 SEND_ATTR_INT(ANVIL_ATTR_MAIL, anvil_remote->mail),
586			 SEND_ATTR_INT(ANVIL_ATTR_RCPT, anvil_remote->rcpt),
587			 SEND_ATTR_INT(ANVIL_ATTR_NTLS, anvil_remote->ntls),
588			 SEND_ATTR_INT(ANVIL_ATTR_AUTH, anvil_remote->auth),
589			 ATTR_TYPE_END);
590    }
591}
592
593/* anvil_remote_conn_update - instantiate or update connection info */
594
595static ANVIL_REMOTE *anvil_remote_conn_update(VSTREAM *client_stream, const char *ident)
596{
597    ANVIL_REMOTE *anvil_remote;
598    ANVIL_LOCAL *anvil_local;
599    const char *myname = "anvil_remote_conn_update";
600
601    if (msg_verbose)
602	msg_info("%s fd=%d stream=0x%lx ident=%s",
603		 myname, vstream_fileno(client_stream),
604		 (unsigned long) client_stream, ident);
605
606    /*
607     * Look up remote connection count information. Update remote connection
608     * rate information. Simply reset the counter every var_anvil_time_unit
609     * seconds. This is easier than maintaining a moving average and it gives
610     * a quicker response to tresspassers.
611     */
612    if ((anvil_remote =
613	 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) {
614	anvil_remote = (ANVIL_REMOTE *) mymalloc(sizeof(*anvil_remote));
615	ANVIL_REMOTE_FIRST_CONN(anvil_remote, ident);
616	htable_enter(anvil_remote_map, ident, (void *) anvil_remote);
617	if (max_cache_size < anvil_remote_map->used) {
618	    max_cache_size = anvil_remote_map->used;
619	    max_cache_time = event_time();
620	}
621    } else {
622	ANVIL_REMOTE_NEXT_CONN(anvil_remote);
623    }
624
625    /*
626     * Record this connection under the local server information, so that we
627     * can clean up all its connection state when the local server goes away.
628     */
629    if ((anvil_local = (ANVIL_LOCAL *) vstream_context(client_stream)) == 0) {
630	anvil_local = (ANVIL_LOCAL *) mymalloc(sizeof(*anvil_local));
631	ANVIL_LOCAL_INIT(anvil_local);
632	vstream_control(client_stream,
633			CA_VSTREAM_CTL_CONTEXT((void *) anvil_local),
634			CA_VSTREAM_CTL_END);
635    }
636    ANVIL_LOCAL_ADD_ONE(anvil_local, anvil_remote);
637    if (msg_verbose)
638	msg_info("%s: anvil_local 0x%lx",
639		 myname, (unsigned long) anvil_local);
640
641    return (anvil_remote);
642}
643
644/* anvil_remote_connect - report connection event, query address status */
645
646static void anvil_remote_connect(VSTREAM *client_stream, const char *ident)
647{
648    ANVIL_REMOTE *anvil_remote;
649
650    /*
651     * Update or instantiate connection info.
652     */
653    anvil_remote = anvil_remote_conn_update(client_stream, ident);
654
655    /*
656     * Respond to the local server.
657     */
658    attr_print_plain(client_stream, ATTR_FLAG_NONE,
659		     SEND_ATTR_INT(ANVIL_ATTR_STATUS, ANVIL_STAT_OK),
660		     SEND_ATTR_INT(ANVIL_ATTR_COUNT, anvil_remote->count),
661		     SEND_ATTR_INT(ANVIL_ATTR_RATE, anvil_remote->rate),
662		     ATTR_TYPE_END);
663
664    /*
665     * Update peak statistics.
666     */
667    if (anvil_remote->rate > max_conn_rate.value)
668	ANVIL_MAX_UPDATE(max_conn_rate, anvil_remote->rate, anvil_remote->ident);
669    if (anvil_remote->count > max_conn_count.value)
670	ANVIL_MAX_UPDATE(max_conn_count, anvil_remote->count, anvil_remote->ident);
671}
672
673/* anvil_remote_mail - register message delivery request */
674
675static void anvil_remote_mail(VSTREAM *client_stream, const char *ident)
676{
677    ANVIL_REMOTE *anvil_remote;
678
679    /*
680     * Be prepared for "postfix reload" after "connect".
681     */
682    if ((anvil_remote =
683	 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0)
684	anvil_remote = anvil_remote_conn_update(client_stream, ident);
685
686    /*
687     * Update message delivery request rate and respond to local server.
688     */
689    ANVIL_REMOTE_INCR_MAIL(anvil_remote);
690    attr_print_plain(client_stream, ATTR_FLAG_NONE,
691		     SEND_ATTR_INT(ANVIL_ATTR_STATUS, ANVIL_STAT_OK),
692		     SEND_ATTR_INT(ANVIL_ATTR_RATE, anvil_remote->mail),
693		     ATTR_TYPE_END);
694
695    /*
696     * Update peak statistics.
697     */
698    if (anvil_remote->mail > max_mail_rate.value)
699	ANVIL_MAX_UPDATE(max_mail_rate, anvil_remote->mail, anvil_remote->ident);
700}
701
702/* anvil_remote_rcpt - register recipient address event */
703
704static void anvil_remote_rcpt(VSTREAM *client_stream, const char *ident)
705{
706    ANVIL_REMOTE *anvil_remote;
707
708    /*
709     * Be prepared for "postfix reload" after "connect".
710     */
711    if ((anvil_remote =
712	 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0)
713	anvil_remote = anvil_remote_conn_update(client_stream, ident);
714
715    /*
716     * Update recipient address rate and respond to local server.
717     */
718    ANVIL_REMOTE_INCR_RCPT(anvil_remote);
719    attr_print_plain(client_stream, ATTR_FLAG_NONE,
720		     SEND_ATTR_INT(ANVIL_ATTR_STATUS, ANVIL_STAT_OK),
721		     SEND_ATTR_INT(ANVIL_ATTR_RATE, anvil_remote->rcpt),
722		     ATTR_TYPE_END);
723
724    /*
725     * Update peak statistics.
726     */
727    if (anvil_remote->rcpt > max_rcpt_rate.value)
728	ANVIL_MAX_UPDATE(max_rcpt_rate, anvil_remote->rcpt, anvil_remote->ident);
729}
730
731/* anvil_remote_auth - register auth request event */
732
733static void anvil_remote_auth(VSTREAM *client_stream, const char *ident)
734{
735    ANVIL_REMOTE *anvil_remote;
736
737    /*
738     * Be prepared for "postfix reload" after "connect".
739     */
740    if ((anvil_remote =
741	 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0)
742	anvil_remote = anvil_remote_conn_update(client_stream, ident);
743
744    /*
745     * Update recipient address rate and respond to local server.
746     */
747    ANVIL_REMOTE_INCR_AUTH(anvil_remote);
748    attr_print_plain(client_stream, ATTR_FLAG_NONE,
749		     SEND_ATTR_INT(ANVIL_ATTR_STATUS, ANVIL_STAT_OK),
750		     SEND_ATTR_INT(ANVIL_ATTR_RATE, anvil_remote->auth),
751		     ATTR_TYPE_END);
752
753    /*
754     * Update peak statistics.
755     */
756    if (anvil_remote->auth > max_auth_rate.value)
757	ANVIL_MAX_UPDATE(max_auth_rate, anvil_remote->auth, anvil_remote->ident);
758}
759
760/* anvil_remote_newtls - register newtls event */
761
762static void anvil_remote_newtls(VSTREAM *client_stream, const char *ident)
763{
764    ANVIL_REMOTE *anvil_remote;
765
766    /*
767     * Be prepared for "postfix reload" after "connect".
768     */
769    if ((anvil_remote =
770	 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0)
771	anvil_remote = anvil_remote_conn_update(client_stream, ident);
772
773    /*
774     * Update newtls rate and respond to local server.
775     */
776    ANVIL_REMOTE_INCR_NTLS(anvil_remote);
777    attr_print_plain(client_stream, ATTR_FLAG_NONE,
778		     SEND_ATTR_INT(ANVIL_ATTR_STATUS, ANVIL_STAT_OK),
779		     SEND_ATTR_INT(ANVIL_ATTR_RATE, anvil_remote->ntls),
780		     ATTR_TYPE_END);
781
782    /*
783     * Update peak statistics.
784     */
785    if (anvil_remote->ntls > max_ntls_rate.value)
786	ANVIL_MAX_UPDATE(max_ntls_rate, anvil_remote->ntls, anvil_remote->ident);
787}
788
789/* anvil_remote_newtls_stat - report newtls stats */
790
791static void anvil_remote_newtls_stat(VSTREAM *client_stream, const char *ident)
792{
793    ANVIL_REMOTE *anvil_remote;
794    int     rate;
795
796    /*
797     * Be prepared for "postfix reload" after "connect".
798     */
799    if ((anvil_remote =
800	 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) {
801	rate = 0;
802    }
803
804    /*
805     * Do not report stale information.
806     */
807    else {
808	if (anvil_remote->start != 0
809	    && anvil_remote->start + var_anvil_time_unit < event_time())
810	    ANVIL_REMOTE_RSET_RATE(anvil_remote, 0);
811	rate = anvil_remote->ntls;
812    }
813
814    /*
815     * Respond to local server.
816     */
817    attr_print_plain(client_stream, ATTR_FLAG_NONE,
818		     SEND_ATTR_INT(ANVIL_ATTR_STATUS, ANVIL_STAT_OK),
819		     SEND_ATTR_INT(ANVIL_ATTR_RATE, rate),
820		     ATTR_TYPE_END);
821}
822
823/* anvil_remote_disconnect - report disconnect event */
824
825static void anvil_remote_disconnect(VSTREAM *client_stream, const char *ident)
826{
827    ANVIL_REMOTE *anvil_remote;
828    ANVIL_LOCAL *anvil_local;
829    const char *myname = "anvil_remote_disconnect";
830
831    if (msg_verbose)
832	msg_info("%s fd=%d stream=0x%lx ident=%s",
833		 myname, vstream_fileno(client_stream),
834		 (unsigned long) client_stream, ident);
835
836    /*
837     * Update local and remote info if this remote connection is listed for
838     * this local server.
839     */
840    if ((anvil_local = (ANVIL_LOCAL *) vstream_context(client_stream)) != 0
841	&& (anvil_remote =
842	    (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) != 0
843	&& ANVIL_LOCAL_REMOTE_LINKED(anvil_local, anvil_remote)) {
844	ANVIL_REMOTE_DROP_ONE(anvil_remote);
845	ANVIL_LOCAL_DROP_ONE(anvil_local, anvil_remote);
846    }
847    if (msg_verbose)
848	msg_info("%s: anvil_local 0x%lx",
849		 myname, (unsigned long) anvil_local);
850
851    /*
852     * Respond to the local server.
853     */
854    attr_print_plain(client_stream, ATTR_FLAG_NONE,
855		     SEND_ATTR_INT(ANVIL_ATTR_STATUS, ANVIL_STAT_OK),
856		     ATTR_TYPE_END);
857}
858
859/* anvil_service_done - clean up */
860
861static void anvil_service_done(VSTREAM *client_stream, char *unused_service,
862			               char **unused_argv)
863{
864    ANVIL_LOCAL *anvil_local;
865    const char *myname = "anvil_service_done";
866
867    if (msg_verbose)
868	msg_info("%s fd=%d stream=0x%lx",
869		 myname, vstream_fileno(client_stream),
870		 (unsigned long) client_stream);
871
872    /*
873     * Look up the local server, and get rid of any remote connection state
874     * that we still have for this local server. Do not destroy remote client
875     * status information before it expires.
876     */
877    if ((anvil_local = (ANVIL_LOCAL *) vstream_context(client_stream)) != 0) {
878	if (msg_verbose)
879	    msg_info("%s: anvil_local 0x%lx",
880		     myname, (unsigned long) anvil_local);
881	ANVIL_LOCAL_DROP_ALL(client_stream, anvil_local);
882	myfree((void *) anvil_local);
883    } else if (msg_verbose)
884	msg_info("client socket not found for fd=%d",
885		 vstream_fileno(client_stream));
886}
887
888/* anvil_status_dump - log and reset extreme usage */
889
890static void anvil_status_dump(char *unused_name, char **unused_argv)
891{
892    ANVIL_MAX_RATE_REPORT(max_conn_rate, "connection");
893    ANVIL_MAX_COUNT_REPORT(max_conn_count, "connection");
894    ANVIL_MAX_RATE_REPORT(max_mail_rate, "message");
895    ANVIL_MAX_RATE_REPORT(max_rcpt_rate, "recipient");
896    ANVIL_MAX_RATE_REPORT(max_ntls_rate, "newtls");
897    ANVIL_MAX_RATE_REPORT(max_auth_rate, "auth");
898
899    if (max_cache_size > 0) {
900	msg_info("statistics: max cache size %d at %.15s",
901		 max_cache_size, ctime(&max_cache_time) + 4);
902	max_cache_size = 0;
903    }
904}
905
906/* anvil_status_update - log and reset extreme usage periodically */
907
908static void anvil_status_update(int unused_event, void *context)
909{
910    anvil_status_dump((char *) 0, (char **) 0);
911    event_request_timer(anvil_status_update, context, var_anvil_stat_time);
912}
913
914/* anvil_service - perform service for client */
915
916static void anvil_service(VSTREAM *client_stream, char *unused_service, char **argv)
917{
918    static VSTRING *request;
919    static VSTRING *ident;
920    static const ANVIL_REQ_TABLE request_table[] = {
921	ANVIL_REQ_CONN, anvil_remote_connect,
922	ANVIL_REQ_MAIL, anvil_remote_mail,
923	ANVIL_REQ_RCPT, anvil_remote_rcpt,
924	ANVIL_REQ_NTLS, anvil_remote_newtls,
925	ANVIL_REQ_DISC, anvil_remote_disconnect,
926	ANVIL_REQ_NTLS_STAT, anvil_remote_newtls_stat,
927	ANVIL_REQ_AUTH, anvil_remote_auth,
928	ANVIL_REQ_LOOKUP, anvil_remote_lookup,
929	0, 0,
930    };
931    const ANVIL_REQ_TABLE *rp;
932
933    /*
934     * Sanity check. This service takes no command-line arguments.
935     */
936    if (argv[0])
937	msg_fatal("unexpected command-line argument: %s", argv[0]);
938
939    /*
940     * Initialize.
941     */
942    if (request == 0) {
943	request = vstring_alloc(10);
944	ident = vstring_alloc(10);
945    }
946
947    /*
948     * This routine runs whenever a client connects to the socket dedicated
949     * to the client connection rate management service. All
950     * connection-management stuff is handled by the common code in
951     * multi_server.c.
952     */
953    if (msg_verbose)
954	msg_info("--- start request ---");
955    if (attr_scan_plain(client_stream,
956			ATTR_FLAG_MISSING | ATTR_FLAG_STRICT,
957			RECV_ATTR_STR(ANVIL_ATTR_REQ, request),
958			RECV_ATTR_STR(ANVIL_ATTR_IDENT, ident),
959			ATTR_TYPE_END) == 2) {
960	for (rp = request_table; /* see below */ ; rp++) {
961	    if (rp->name == 0) {
962		msg_warn("unrecognized request: \"%s\", ignored", STR(request));
963		attr_print_plain(client_stream, ATTR_FLAG_NONE,
964			  SEND_ATTR_INT(ANVIL_ATTR_STATUS, ANVIL_STAT_FAIL),
965				 ATTR_TYPE_END);
966		break;
967	    }
968	    if (STREQ(rp->name, STR(request))) {
969		rp->action(client_stream, STR(ident));
970		break;
971	    }
972	}
973	vstream_fflush(client_stream);
974    } else {
975	/* Note: invokes anvil_service_done() */
976	multi_server_disconnect(client_stream);
977    }
978    if (msg_verbose)
979	msg_info("--- end request ---");
980}
981
982/* post_jail_init - post-jail initialization */
983
984static void post_jail_init(char *unused_name, char **unused_argv)
985{
986
987    /*
988     * Dump and reset extreme usage every so often.
989     */
990    event_request_timer(anvil_status_update, (void *) 0, var_anvil_stat_time);
991
992    /*
993     * Initial client state tables.
994     */
995    anvil_remote_map = htable_create(1000);
996
997    /*
998     * Do not limit the number of client requests.
999     */
1000    var_use_limit = 0;
1001
1002    /*
1003     * Don't exit before the sampling interval ends.
1004     */
1005    if (var_idle_limit < var_anvil_time_unit)
1006	var_idle_limit = var_anvil_time_unit;
1007}
1008
1009MAIL_VERSION_STAMP_DECLARE;
1010
1011/* post_accept - announce our protocol */
1012
1013static void post_accept(VSTREAM *stream, char *unused_name,
1014			        char **unused_argv, HTABLE *unused_table)
1015{
1016
1017    /*
1018     * Announce the protocol.
1019     */
1020    attr_print_plain(stream, ATTR_FLAG_NONE,
1021		     SEND_ATTR_STR(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_ANVIL),
1022		     ATTR_TYPE_END);
1023    (void) vstream_fflush(stream);
1024}
1025
1026/* main - pass control to the multi-threaded skeleton */
1027
1028int     main(int argc, char **argv)
1029{
1030    static const CONFIG_TIME_TABLE time_table[] = {
1031	VAR_ANVIL_TIME_UNIT, DEF_ANVIL_TIME_UNIT, &var_anvil_time_unit, 1, 0,
1032	VAR_ANVIL_STAT_TIME, DEF_ANVIL_STAT_TIME, &var_anvil_stat_time, 1, 0,
1033	0,
1034    };
1035
1036    /*
1037     * Fingerprint executables and core dumps.
1038     */
1039    MAIL_VERSION_STAMP_ALLOCATE;
1040
1041    multi_server_main(argc, argv, anvil_service,
1042		      CA_MAIL_SERVER_TIME_TABLE(time_table),
1043		      CA_MAIL_SERVER_POST_INIT(post_jail_init),
1044		      CA_MAIL_SERVER_POST_ACCEPT(post_accept),
1045		      CA_MAIL_SERVER_SOLITARY,
1046		      CA_MAIL_SERVER_PRE_DISCONN(anvil_service_done),
1047		      CA_MAIL_SERVER_EXIT(anvil_status_dump),
1048		      0);
1049}
1050