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