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