Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / anvil / anvil.c
blob3cf29503405162250a75623bf48cde0552920a7f
1 /* $NetBSD$ */
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.
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:
29 /* .nf
30 /* \fBrequest=connect\fR
31 /* \fBident=\fIstring\fR
32 /* .fi
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:
39 /* .nf
40 /* \fBstatus=0\fR
41 /* \fBcount=\fInumber\fR
42 /* \fBrate=\fInumber\fR
43 /* .fi
45 /* To register a disconnect event send the following request
46 /* to the \fBanvil\fR(8) server:
48 /* .nf
49 /* \fBrequest=disconnect\fR
50 /* \fBident=\fIstring\fR
51 /* .fi
53 /* The \fBanvil\fR(8) server replies with:
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:
64 /* .nf
65 /* \fBrequest=message\fR
66 /* \fBident=\fIstring\fR
67 /* .fi
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:
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:
83 /* .nf
84 /* \fBrequest=recipient\fR
85 /* \fBident=\fIstring\fR
86 /* .fi
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:
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.
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:
105 /* .nf
106 /* \fBrequest=newtls\fR
107 /* \fBident=\fIstring\fR
108 /* .fi
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:
114 /* .nf
115 /* \fBstatus=0\fR
116 /* \fBrate=\fInumber\fR
117 /* .fi
119 /* To retrieve new TLS session request rate information without
120 /* updating the counter information, send:
122 /* .nf
123 /* \fBrequest=newtls_report\fR
124 /* \fBident=\fIstring\fR
125 /* .fi
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:
131 /* .nf
132 /* \fBstatus=0\fR
133 /* \fBrate=\fInumber\fR
134 /* .fi
135 /* SECURITY
136 /* .ad
137 /* .fi
138 /* The \fBanvil\fR(8) server does not talk to the network or to local
139 /* users, and can run chrooted at fixed low privilege.
141 /* The \fBanvil\fR(8) server maintains an in-memory table with
142 /* information about recent clients requests. No persistent
143 /* state is kept because standard system library routines are
144 /* not sufficiently robust for update-intensive applications.
146 /* Although the in-memory state is kept only temporarily, this
147 /* may require a lot of memory on systems that handle connections
148 /* from many remote clients. To reduce memory usage, reduce
149 /* the time unit over which state is kept.
150 /* DIAGNOSTICS
151 /* Problems and transactions are logged to \fBsyslogd\fR(8).
153 /* Upon exit, and every \fBanvil_status_update_time\fR
154 /* seconds, the server logs the maximal count and rate values measured,
155 /* together with (service, client) information and the time of day
156 /* associated with those events.
157 /* In order to avoid unnecessary overhead, no measurements
158 /* are done for activity that isn't concurrency limited or
159 /* rate limited.
160 /* BUGS
161 /* Systems behind network address translating routers or proxies
162 /* appear to have the same client address and can run into connection
163 /* count and/or rate limits falsely.
165 /* In this preliminary implementation, a count (or rate) limited server
166 /* process can have only one remote client at a time. If a
167 /* server process reports
168 /* multiple simultaneous clients, state is kept only for the last
169 /* reported client.
171 /* The \fBanvil\fR(8) server automatically discards client
172 /* request information after it expires. To prevent the
173 /* \fBanvil\fR(8) server from discarding client request rate
174 /* information too early or too late, a rate limited service
175 /* should always register connect/disconnect events even when
176 /* it does not explicitly limit them.
177 /* CONFIGURATION PARAMETERS
178 /* .ad
179 /* .fi
180 /* On low-traffic mail systems, changes to \fBmain.cf\fR are
181 /* picked up automatically as \fBanvil\fR(8) processes run for
182 /* only a limited amount of time. On other mail systems, use
183 /* the command "\fBpostfix reload\fR" to speed up a change.
185 /* The text below provides only a parameter summary. See
186 /* \fBpostconf\fR(5) for more details including examples.
187 /* .IP "\fBanvil_rate_time_unit (60s)\fR"
188 /* The time unit over which client connection rates and other rates
189 /* are calculated.
190 /* .IP "\fBanvil_status_update_time (600s)\fR"
191 /* How frequently the \fBanvil\fR(8) connection and rate limiting server
192 /* logs peak usage information.
193 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
194 /* The default location of the Postfix main.cf and master.cf
195 /* configuration files.
196 /* .IP "\fBdaemon_timeout (18000s)\fR"
197 /* How much time a Postfix daemon process may take to handle a
198 /* request before it is terminated by a built-in watchdog timer.
199 /* .IP "\fBipc_timeout (3600s)\fR"
200 /* The time limit for sending or receiving information over an internal
201 /* communication channel.
202 /* .IP "\fBmax_idle (100s)\fR"
203 /* The maximum amount of time that an idle Postfix daemon process waits
204 /* for an incoming connection before terminating voluntarily.
205 /* .IP "\fBmax_use (100)\fR"
206 /* The maximal number of incoming connections that a Postfix daemon
207 /* process will service before terminating voluntarily.
208 /* .IP "\fBprocess_id (read-only)\fR"
209 /* The process ID of a Postfix command or daemon process.
210 /* .IP "\fBprocess_name (read-only)\fR"
211 /* The process name of a Postfix command or daemon process.
212 /* .IP "\fBsyslog_facility (mail)\fR"
213 /* The syslog facility of Postfix logging.
214 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
215 /* The mail system name that is prepended to the process name in syslog
216 /* records, so that "smtpd" becomes, for example, "postfix/smtpd".
217 /* SEE ALSO
218 /* smtpd(8), Postfix SMTP server
219 /* postconf(5), configuration parameters
220 /* master(5), generic daemon options
221 /* README FILES
222 /* .ad
223 /* .fi
224 /* Use "\fBpostconf readme_directory\fR" or
225 /* "\fBpostconf html_directory\fR" to locate this information.
226 /* .na
227 /* .nf
228 /* TUNING_README, performance tuning
229 /* LICENSE
230 /* .ad
231 /* .fi
232 /* The Secure Mailer license must be distributed with this software.
233 /* HISTORY
234 /* .ad
235 /* .fi
236 /* The anvil service is available in Postfix 2.2 and later.
237 /* AUTHOR(S)
238 /* Wietse Venema
239 /* IBM T.J. Watson Research
240 /* P.O. Box 704
241 /* Yorktown Heights, NY 10598, USA
242 /*--*/
244 /* System library. */
246 #include <sys_defs.h>
247 #include <sys/time.h>
248 #include <limits.h>
250 /* Utility library. */
252 #include <msg.h>
253 #include <mymalloc.h>
254 #include <htable.h>
255 #include <stringops.h>
256 #include <events.h>
258 /* Global library. */
260 #include <mail_conf.h>
261 #include <mail_params.h>
262 #include <mail_version.h>
263 #include <mail_proto.h>
264 #include <anvil_clnt.h>
266 /* Server skeleton. */
268 #include <mail_server.h>
270 /* Application-specific. */
273 * Configuration parameters.
275 int var_anvil_time_unit;
276 int var_anvil_stat_time;
279 * Global dynamic state.
281 static HTABLE *anvil_remote_map; /* indexed by service+ remote client */
284 * Remote connection state, one instance for each (service, client) pair.
286 typedef struct {
287 char *ident; /* lookup key */
288 int count; /* connection count */
289 int rate; /* connection rate */
290 int mail; /* message rate */
291 int rcpt; /* recipient rate */
292 int ntls; /* new TLS session rate */
293 time_t start; /* time of first rate sample */
294 } ANVIL_REMOTE;
297 * Local server state, one instance per anvil client connection. This allows
298 * us to clean up remote connection state when a local server goes away
299 * without cleaning up.
301 typedef struct {
302 ANVIL_REMOTE *anvil_remote; /* XXX should be list */
303 } ANVIL_LOCAL;
306 * The following operations are implemented as macros with recognizable
307 * names so that we don't lose sight of what the code is trying to do.
309 * Related operations are defined side by side so that the code implementing
310 * them isn't pages apart.
313 /* Create new (service, client) state. */
315 #define ANVIL_REMOTE_FIRST_CONN(remote, id) \
316 do { \
317 (remote)->ident = mystrdup(id); \
318 (remote)->count = 1; \
319 (remote)->rate = 1; \
320 (remote)->mail = 0; \
321 (remote)->rcpt = 0; \
322 (remote)->ntls = 0; \
323 (remote)->start = event_time(); \
324 } while(0)
326 /* Destroy unused (service, client) state. */
328 #define ANVIL_REMOTE_FREE(remote) \
329 do { \
330 myfree((remote)->ident); \
331 myfree((char *) (remote)); \
332 } while(0)
334 /* Reset or update rate information for existing (service, client) state. */
336 #define ANVIL_REMOTE_RSET_RATE(remote, _start) \
337 do { \
338 (remote)->rate = 0; \
339 (remote)->mail = 0; \
340 (remote)->rcpt = 0; \
341 (remote)->ntls = 0; \
342 (remote)->start = _start; \
343 } while(0)
345 #define ANVIL_REMOTE_INCR_RATE(remote, _what) \
346 do { \
347 time_t _now = event_time(); \
348 if ((remote)->start + var_anvil_time_unit < _now) \
349 ANVIL_REMOTE_RSET_RATE((remote), _now); \
350 if ((remote)->_what < INT_MAX) \
351 (remote)->_what += 1; \
352 } while(0)
354 /* Update existing (service, client) state. */
356 #define ANVIL_REMOTE_NEXT_CONN(remote) \
357 do { \
358 ANVIL_REMOTE_INCR_RATE((remote), rate); \
359 if ((remote)->count == 0) \
360 event_cancel_timer(anvil_remote_expire, (char *) remote); \
361 (remote)->count++; \
362 } while(0)
364 #define ANVIL_REMOTE_INCR_MAIL(remote) ANVIL_REMOTE_INCR_RATE((remote), mail)
366 #define ANVIL_REMOTE_INCR_RCPT(remote) ANVIL_REMOTE_INCR_RATE((remote), rcpt)
368 #define ANVIL_REMOTE_INCR_NTLS(remote) ANVIL_REMOTE_INCR_RATE((remote), ntls)
370 /* Drop connection from (service, client) state. */
372 #define ANVIL_REMOTE_DROP_ONE(remote) \
373 do { \
374 if ((remote) && (remote)->count > 0) { \
375 if (--(remote)->count == 0) \
376 event_request_timer(anvil_remote_expire, (char *) remote, \
377 var_anvil_time_unit); \
379 } while(0)
381 /* Create local server state. */
383 #define ANVIL_LOCAL_INIT(local) \
384 do { \
385 (local)->anvil_remote = 0; \
386 } while(0)
388 /* Add remote connection to local server. */
390 #define ANVIL_LOCAL_ADD_ONE(local, remote) \
391 do { \
392 /* XXX allow multiple remote clients per local server. */ \
393 if ((local)->anvil_remote) \
394 ANVIL_REMOTE_DROP_ONE((local)->anvil_remote); \
395 (local)->anvil_remote = (remote); \
396 } while(0)
398 /* Test if this remote connection is listed for this local server. */
400 #define ANVIL_LOCAL_REMOTE_LINKED(local, remote) \
401 ((local)->anvil_remote == (remote))
403 /* Drop specific remote connection from local server. */
405 #define ANVIL_LOCAL_DROP_ONE(local, remote) \
406 do { \
407 /* XXX allow multiple remote clients per local server. */ \
408 if ((local)->anvil_remote == (remote)) \
409 (local)->anvil_remote = 0; \
410 } while(0)
412 /* Drop all remote connections from local server. */
414 #define ANVIL_LOCAL_DROP_ALL(stream, local) \
415 do { \
416 /* XXX allow multiple remote clients per local server. */ \
417 if ((local)->anvil_remote) \
418 anvil_remote_disconnect((stream), (local)->anvil_remote->ident); \
419 } while (0)
422 * Lookup table to map request names to action routines.
424 typedef struct {
425 const char *name;
426 void (*action) (VSTREAM *, const char *);
427 } ANVIL_REQ_TABLE;
430 * Run-time statistics for maximal connection counts and event rates. These
431 * store the peak resource usage, remote connection, and time. Absent a
432 * query interface, this information is logged at process exit time and at
433 * configurable intervals.
435 typedef struct {
436 int value; /* peak value */
437 char *ident; /* lookup key */
438 time_t when; /* time of peak value */
439 } ANVIL_MAX;
441 static ANVIL_MAX max_conn_count; /* peak connection count */
442 static ANVIL_MAX max_conn_rate; /* peak connection rate */
443 static ANVIL_MAX max_mail_rate; /* peak message rate */
444 static ANVIL_MAX max_rcpt_rate; /* peak recipient rate */
445 static ANVIL_MAX max_ntls_rate; /* peak new TLS session rate */
447 static int max_cache_size; /* peak cache size */
448 static time_t max_cache_time; /* time of peak size */
450 /* Update/report peak usage. */
452 #define ANVIL_MAX_UPDATE(_max, _value, _ident) \
453 do { \
454 _max.value = _value; \
455 if (_max.ident == 0) { \
456 _max.ident = mystrdup(_ident); \
457 } else if (!STREQ(_max.ident, _ident)) { \
458 myfree(_max.ident); \
459 _max.ident = mystrdup(_ident); \
461 _max.when = event_time(); \
462 } while (0)
464 #define ANVIL_MAX_RATE_REPORT(_max, _name) \
465 do { \
466 if (_max.value > 0) { \
467 msg_info("statistics: max " _name " rate %d/%ds for (%s) at %.15s", \
468 _max.value, var_anvil_time_unit, \
469 _max.ident, ctime(&_max.when) + 4); \
470 _max.value = 0; \
472 } while (0);
474 #define ANVIL_MAX_COUNT_REPORT(_max, _name) \
475 do { \
476 if (_max.value > 0) { \
477 msg_info("statistics: max " _name " count %d for (%s) at %.15s", \
478 _max.value, _max.ident, ctime(&_max.when) + 4); \
479 _max.value = 0; \
481 } while (0);
484 * Silly little macros.
486 #define STR(x) vstring_str(x)
487 #define STREQ(x,y) (strcmp((x), (y)) == 0)
489 /* anvil_remote_expire - purge expired connection state */
491 static void anvil_remote_expire(int unused_event, char *context)
493 ANVIL_REMOTE *anvil_remote = (ANVIL_REMOTE *) context;
494 const char *myname = "anvil_remote_expire";
496 if (msg_verbose)
497 msg_info("%s %s", myname, anvil_remote->ident);
499 if (anvil_remote->count != 0)
500 msg_panic("%s: bad connection count: %d",
501 myname, anvil_remote->count);
503 htable_delete(anvil_remote_map, anvil_remote->ident,
504 (void (*) (char *)) 0);
505 ANVIL_REMOTE_FREE(anvil_remote);
507 if (msg_verbose)
508 msg_info("%s: anvil_remote_map used=%d",
509 myname, anvil_remote_map->used);
512 /* anvil_remote_lookup - dump address status */
514 static void anvil_remote_lookup(VSTREAM *client_stream, const char *ident)
516 ANVIL_REMOTE *anvil_remote;
517 const char *myname = "anvil_remote_lookup";
519 if (msg_verbose)
520 msg_info("%s fd=%d stream=0x%lx ident=%s",
521 myname, vstream_fileno(client_stream),
522 (unsigned long) client_stream, ident);
525 * Look up remote client information.
527 if ((anvil_remote =
528 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) {
529 attr_print_plain(client_stream, ATTR_FLAG_NONE,
530 ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
531 ATTR_TYPE_INT, ANVIL_ATTR_COUNT, 0,
532 ATTR_TYPE_INT, ANVIL_ATTR_RATE, 0,
533 ATTR_TYPE_INT, ANVIL_ATTR_MAIL, 0,
534 ATTR_TYPE_INT, ANVIL_ATTR_RCPT, 0,
535 ATTR_TYPE_INT, ANVIL_ATTR_NTLS, 0,
536 ATTR_TYPE_END);
537 } else {
540 * Do not report stale information.
542 if (anvil_remote->start != 0
543 && anvil_remote->start + var_anvil_time_unit < event_time())
544 ANVIL_REMOTE_RSET_RATE(anvil_remote, 0);
545 attr_print_plain(client_stream, ATTR_FLAG_NONE,
546 ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
547 ATTR_TYPE_INT, ANVIL_ATTR_COUNT, anvil_remote->count,
548 ATTR_TYPE_INT, ANVIL_ATTR_RATE, anvil_remote->rate,
549 ATTR_TYPE_INT, ANVIL_ATTR_MAIL, anvil_remote->mail,
550 ATTR_TYPE_INT, ANVIL_ATTR_RCPT, anvil_remote->rcpt,
551 ATTR_TYPE_INT, ANVIL_ATTR_NTLS, anvil_remote->ntls,
552 ATTR_TYPE_END);
556 /* anvil_remote_conn_update - instantiate or update connection info */
558 static ANVIL_REMOTE *anvil_remote_conn_update(VSTREAM *client_stream, const char *ident)
560 ANVIL_REMOTE *anvil_remote;
561 ANVIL_LOCAL *anvil_local;
562 const char *myname = "anvil_remote_conn_update";
564 if (msg_verbose)
565 msg_info("%s fd=%d stream=0x%lx ident=%s",
566 myname, vstream_fileno(client_stream),
567 (unsigned long) client_stream, ident);
570 * Look up remote connection count information. Update remote connection
571 * rate information. Simply reset the counter every var_anvil_time_unit
572 * seconds. This is easier than maintaining a moving average and it gives
573 * a quicker response to tresspassers.
575 if ((anvil_remote =
576 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) {
577 anvil_remote = (ANVIL_REMOTE *) mymalloc(sizeof(*anvil_remote));
578 ANVIL_REMOTE_FIRST_CONN(anvil_remote, ident);
579 htable_enter(anvil_remote_map, ident, (char *) anvil_remote);
580 if (max_cache_size < anvil_remote_map->used) {
581 max_cache_size = anvil_remote_map->used;
582 max_cache_time = event_time();
584 } else {
585 ANVIL_REMOTE_NEXT_CONN(anvil_remote);
589 * Record this connection under the local server information, so that we
590 * can clean up all its connection state when the local server goes away.
592 if ((anvil_local = (ANVIL_LOCAL *) vstream_context(client_stream)) == 0) {
593 anvil_local = (ANVIL_LOCAL *) mymalloc(sizeof(*anvil_local));
594 ANVIL_LOCAL_INIT(anvil_local);
595 vstream_control(client_stream,
596 VSTREAM_CTL_CONTEXT, (void *) anvil_local,
597 VSTREAM_CTL_END);
599 ANVIL_LOCAL_ADD_ONE(anvil_local, anvil_remote);
600 if (msg_verbose)
601 msg_info("%s: anvil_local 0x%lx",
602 myname, (unsigned long) anvil_local);
604 return (anvil_remote);
607 /* anvil_remote_connect - report connection event, query address status */
609 static void anvil_remote_connect(VSTREAM *client_stream, const char *ident)
611 ANVIL_REMOTE *anvil_remote;
614 * Update or instantiate connection info.
616 anvil_remote = anvil_remote_conn_update(client_stream, ident);
619 * Respond to the local server.
621 attr_print_plain(client_stream, ATTR_FLAG_NONE,
622 ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
623 ATTR_TYPE_INT, ANVIL_ATTR_COUNT, anvil_remote->count,
624 ATTR_TYPE_INT, ANVIL_ATTR_RATE, anvil_remote->rate,
625 ATTR_TYPE_END);
628 * Update peak statistics.
630 if (anvil_remote->rate > max_conn_rate.value)
631 ANVIL_MAX_UPDATE(max_conn_rate, anvil_remote->rate, anvil_remote->ident);
632 if (anvil_remote->count > max_conn_count.value)
633 ANVIL_MAX_UPDATE(max_conn_count, anvil_remote->count, anvil_remote->ident);
636 /* anvil_remote_mail - register message delivery request */
638 static void anvil_remote_mail(VSTREAM *client_stream, const char *ident)
640 ANVIL_REMOTE *anvil_remote;
643 * Be prepared for "postfix reload" after "connect".
645 if ((anvil_remote =
646 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0)
647 anvil_remote = anvil_remote_conn_update(client_stream, ident);
650 * Update message delivery request rate and respond to local server.
652 ANVIL_REMOTE_INCR_MAIL(anvil_remote);
653 attr_print_plain(client_stream, ATTR_FLAG_NONE,
654 ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
655 ATTR_TYPE_INT, ANVIL_ATTR_RATE, anvil_remote->mail,
656 ATTR_TYPE_END);
659 * Update peak statistics.
661 if (anvil_remote->mail > max_mail_rate.value)
662 ANVIL_MAX_UPDATE(max_mail_rate, anvil_remote->mail, anvil_remote->ident);
665 /* anvil_remote_rcpt - register recipient address event */
667 static void anvil_remote_rcpt(VSTREAM *client_stream, const char *ident)
669 ANVIL_REMOTE *anvil_remote;
672 * Be prepared for "postfix reload" after "connect".
674 if ((anvil_remote =
675 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0)
676 anvil_remote = anvil_remote_conn_update(client_stream, ident);
679 * Update recipient address rate and respond to local server.
681 ANVIL_REMOTE_INCR_RCPT(anvil_remote);
682 attr_print_plain(client_stream, ATTR_FLAG_NONE,
683 ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
684 ATTR_TYPE_INT, ANVIL_ATTR_RATE, anvil_remote->rcpt,
685 ATTR_TYPE_END);
688 * Update peak statistics.
690 if (anvil_remote->rcpt > max_rcpt_rate.value)
691 ANVIL_MAX_UPDATE(max_rcpt_rate, anvil_remote->rcpt, anvil_remote->ident);
694 /* anvil_remote_newtls - register newtls event */
696 static void anvil_remote_newtls(VSTREAM *client_stream, const char *ident)
698 ANVIL_REMOTE *anvil_remote;
701 * Be prepared for "postfix reload" after "connect".
703 if ((anvil_remote =
704 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0)
705 anvil_remote = anvil_remote_conn_update(client_stream, ident);
708 * Update newtls rate and respond to local server.
710 ANVIL_REMOTE_INCR_NTLS(anvil_remote);
711 attr_print_plain(client_stream, ATTR_FLAG_NONE,
712 ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
713 ATTR_TYPE_INT, ANVIL_ATTR_RATE, anvil_remote->ntls,
714 ATTR_TYPE_END);
717 * Update peak statistics.
719 if (anvil_remote->ntls > max_ntls_rate.value)
720 ANVIL_MAX_UPDATE(max_ntls_rate, anvil_remote->ntls, anvil_remote->ident);
723 /* anvil_remote_newtls_stat - report newtls stats */
725 static void anvil_remote_newtls_stat(VSTREAM *client_stream, const char *ident)
727 ANVIL_REMOTE *anvil_remote;
728 int rate;
731 * Be prepared for "postfix reload" after "connect".
733 if ((anvil_remote =
734 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) {
735 rate = 0;
739 * Do not report stale information.
741 else {
742 if (anvil_remote->start != 0
743 && anvil_remote->start + var_anvil_time_unit < event_time())
744 ANVIL_REMOTE_RSET_RATE(anvil_remote, 0);
745 rate = anvil_remote->ntls;
749 * Respond to local server.
751 attr_print_plain(client_stream, ATTR_FLAG_NONE,
752 ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
753 ATTR_TYPE_INT, ANVIL_ATTR_RATE, rate,
754 ATTR_TYPE_END);
757 /* anvil_remote_disconnect - report disconnect event */
759 static void anvil_remote_disconnect(VSTREAM *client_stream, const char *ident)
761 ANVIL_REMOTE *anvil_remote;
762 ANVIL_LOCAL *anvil_local;
763 const char *myname = "anvil_remote_disconnect";
765 if (msg_verbose)
766 msg_info("%s fd=%d stream=0x%lx ident=%s",
767 myname, vstream_fileno(client_stream),
768 (unsigned long) client_stream, ident);
771 * Update local and remote info if this remote connection is listed for
772 * this local server.
774 if ((anvil_local = (ANVIL_LOCAL *) vstream_context(client_stream)) != 0
775 && (anvil_remote =
776 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) != 0
777 && ANVIL_LOCAL_REMOTE_LINKED(anvil_local, anvil_remote)) {
778 ANVIL_REMOTE_DROP_ONE(anvil_remote);
779 ANVIL_LOCAL_DROP_ONE(anvil_local, anvil_remote);
781 if (msg_verbose)
782 msg_info("%s: anvil_local 0x%lx",
783 myname, (unsigned long) anvil_local);
786 * Respond to the local server.
788 attr_print_plain(client_stream, ATTR_FLAG_NONE,
789 ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
790 ATTR_TYPE_END);
793 /* anvil_service_done - clean up */
795 static void anvil_service_done(VSTREAM *client_stream, char *unused_service,
796 char **unused_argv)
798 ANVIL_LOCAL *anvil_local;
799 const char *myname = "anvil_service_done";
801 if (msg_verbose)
802 msg_info("%s fd=%d stream=0x%lx",
803 myname, vstream_fileno(client_stream),
804 (unsigned long) client_stream);
807 * Look up the local server, and get rid of any remote connection state
808 * that we still have for this local server. Do not destroy remote client
809 * status information before it expires.
811 if ((anvil_local = (ANVIL_LOCAL *) vstream_context(client_stream)) != 0) {
812 if (msg_verbose)
813 msg_info("%s: anvil_local 0x%lx",
814 myname, (unsigned long) anvil_local);
815 ANVIL_LOCAL_DROP_ALL(client_stream, anvil_local);
816 myfree((char *) anvil_local);
817 } else if (msg_verbose)
818 msg_info("client socket not found for fd=%d",
819 vstream_fileno(client_stream));
822 /* anvil_status_dump - log and reset extreme usage */
824 static void anvil_status_dump(char *unused_name, char **unused_argv)
826 ANVIL_MAX_RATE_REPORT(max_conn_rate, "connection");
827 ANVIL_MAX_COUNT_REPORT(max_conn_count, "connection");
828 ANVIL_MAX_RATE_REPORT(max_mail_rate, "message");
829 ANVIL_MAX_RATE_REPORT(max_rcpt_rate, "recipient");
830 ANVIL_MAX_RATE_REPORT(max_ntls_rate, "newtls");
832 if (max_cache_size > 0) {
833 msg_info("statistics: max cache size %d at %.15s",
834 max_cache_size, ctime(&max_cache_time) + 4);
835 max_cache_size = 0;
839 /* anvil_status_update - log and reset extreme usage periodically */
841 static void anvil_status_update(int unused_event, char *context)
843 anvil_status_dump((char *) 0, (char **) 0);
844 event_request_timer(anvil_status_update, context, var_anvil_stat_time);
847 /* anvil_service - perform service for client */
849 static void anvil_service(VSTREAM *client_stream, char *unused_service, char **argv)
851 static VSTRING *request;
852 static VSTRING *ident;
853 static const ANVIL_REQ_TABLE request_table[] = {
854 ANVIL_REQ_CONN, anvil_remote_connect,
855 ANVIL_REQ_MAIL, anvil_remote_mail,
856 ANVIL_REQ_RCPT, anvil_remote_rcpt,
857 ANVIL_REQ_NTLS, anvil_remote_newtls,
858 ANVIL_REQ_DISC, anvil_remote_disconnect,
859 ANVIL_REQ_NTLS_STAT, anvil_remote_newtls_stat,
860 ANVIL_REQ_LOOKUP, anvil_remote_lookup,
861 0, 0,
863 const ANVIL_REQ_TABLE *rp;
866 * Sanity check. This service takes no command-line arguments.
868 if (argv[0])
869 msg_fatal("unexpected command-line argument: %s", argv[0]);
872 * Initialize.
874 if (request == 0) {
875 request = vstring_alloc(10);
876 ident = vstring_alloc(10);
880 * This routine runs whenever a client connects to the socket dedicated
881 * to the client connection rate management service. All
882 * connection-management stuff is handled by the common code in
883 * multi_server.c.
885 if (msg_verbose)
886 msg_info("--- start request ---");
887 if (attr_scan_plain(client_stream,
888 ATTR_FLAG_MISSING | ATTR_FLAG_STRICT,
889 ATTR_TYPE_STR, ANVIL_ATTR_REQ, request,
890 ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
891 ATTR_TYPE_END) == 2) {
892 for (rp = request_table; /* see below */ ; rp++) {
893 if (rp->name == 0) {
894 msg_warn("unrecognized request: \"%s\", ignored", STR(request));
895 attr_print_plain(client_stream, ATTR_FLAG_NONE,
896 ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_FAIL,
897 ATTR_TYPE_END);
898 break;
900 if (STREQ(rp->name, STR(request))) {
901 rp->action(client_stream, STR(ident));
902 break;
905 vstream_fflush(client_stream);
906 } else {
907 /* Note: invokes anvil_service_done() */
908 multi_server_disconnect(client_stream);
910 if (msg_verbose)
911 msg_info("--- end request ---");
914 /* post_jail_init - post-jail initialization */
916 static void post_jail_init(char *unused_name, char **unused_argv)
920 * Dump and reset extreme usage every so often.
922 event_request_timer(anvil_status_update, (char *) 0, var_anvil_stat_time);
925 * Initial client state tables.
927 anvil_remote_map = htable_create(1000);
930 * Do not limit the number of client requests.
932 var_use_limit = 0;
935 * Don't exit before the sampling interval ends.
937 if (var_idle_limit < var_anvil_time_unit)
938 var_idle_limit = var_anvil_time_unit;
941 MAIL_VERSION_STAMP_DECLARE;
943 /* main - pass control to the multi-threaded skeleton */
945 int main(int argc, char **argv)
947 static const CONFIG_TIME_TABLE time_table[] = {
948 VAR_ANVIL_TIME_UNIT, DEF_ANVIL_TIME_UNIT, &var_anvil_time_unit, 1, 0,
949 VAR_ANVIL_STAT_TIME, DEF_ANVIL_STAT_TIME, &var_anvil_stat_time, 1, 0,
954 * Fingerprint executables and core dumps.
956 MAIL_VERSION_STAMP_ALLOCATE;
958 multi_server_main(argc, argv, anvil_service,
959 MAIL_SERVER_TIME_TABLE, time_table,
960 MAIL_SERVER_POST_INIT, post_jail_init,
961 MAIL_SERVER_SOLITARY,
962 MAIL_SERVER_PRE_DISCONN, anvil_service_done,
963 MAIL_SERVER_EXIT, anvil_status_dump,