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