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