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