xref: /netbsd-src/external/ibm-public/postfix/dist/src/anvil/anvil.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
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   */
299 int     var_anvil_time_unit;
300 int     var_anvil_stat_time;
301 
302  /*
303   * Global dynamic state.
304   */
305 static HTABLE *anvil_remote_map;	/* indexed by service+ remote client */
306 
307  /*
308   * Remote connection state, one instance for each (service, client) pair.
309   */
310 typedef 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   */
326 typedef 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   */
453 typedef 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   */
464 typedef struct {
465     int     value;			/* peak value */
466     char   *ident;			/* lookup key */
467     time_t  when;			/* time of peak value */
468 } ANVIL_MAX;
469 
470 static ANVIL_MAX max_conn_count;	/* peak connection count */
471 static ANVIL_MAX max_conn_rate;		/* peak connection rate */
472 static ANVIL_MAX max_mail_rate;		/* peak message rate */
473 static ANVIL_MAX max_rcpt_rate;		/* peak recipient rate */
474 static ANVIL_MAX max_ntls_rate;		/* peak new TLS session rate */
475 static ANVIL_MAX max_auth_rate;		/* peak AUTH request rate */
476 
477 static int max_cache_size;		/* peak cache size */
478 static 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 
521 static 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 
544 static 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 
590 static 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 
641 static 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 
670 static 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 
699 static 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 
728 static 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 
757 static 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 
786 static 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 
820 static 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 
856 static 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 
885 static 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 
903 static 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 
911 static 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 
979 static 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 
1004 MAIL_VERSION_STAMP_DECLARE;
1005 
1006 /* main - pass control to the multi-threaded skeleton */
1007 
1008 int     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