1 /* $NetBSD: anvil.c,v 1.1.1.1 2009/06/23 10:08:42 tron 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 /* 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. 140 /* 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. 145 /* 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). 152 /* 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. 164 /* 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. 170 /* 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. 184 /* 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 /*--*/ 243 244 /* System library. */ 245 246 #include <sys_defs.h> 247 #include <sys/time.h> 248 #include <limits.h> 249 250 /* Utility library. */ 251 252 #include <msg.h> 253 #include <mymalloc.h> 254 #include <htable.h> 255 #include <stringops.h> 256 #include <events.h> 257 258 /* Global library. */ 259 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> 265 266 /* Server skeleton. */ 267 268 #include <mail_server.h> 269 270 /* Application-specific. */ 271 272 /* 273 * Configuration parameters. 274 */ 275 int var_anvil_time_unit; 276 int var_anvil_stat_time; 277 278 /* 279 * Global dynamic state. 280 */ 281 static HTABLE *anvil_remote_map; /* indexed by service+ remote client */ 282 283 /* 284 * Remote connection state, one instance for each (service, client) pair. 285 */ 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; 295 296 /* 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. 300 */ 301 typedef struct { 302 ANVIL_REMOTE *anvil_remote; /* XXX should be list */ 303 } ANVIL_LOCAL; 304 305 /* 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. 308 * 309 * Related operations are defined side by side so that the code implementing 310 * them isn't pages apart. 311 */ 312 313 /* Create new (service, client) state. */ 314 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) 325 326 /* Destroy unused (service, client) state. */ 327 328 #define ANVIL_REMOTE_FREE(remote) \ 329 do { \ 330 myfree((remote)->ident); \ 331 myfree((char *) (remote)); \ 332 } while(0) 333 334 /* Reset or update rate information for existing (service, client) state. */ 335 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) 344 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) 353 354 /* Update existing (service, client) state. */ 355 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) 363 364 #define ANVIL_REMOTE_INCR_MAIL(remote) ANVIL_REMOTE_INCR_RATE((remote), mail) 365 366 #define ANVIL_REMOTE_INCR_RCPT(remote) ANVIL_REMOTE_INCR_RATE((remote), rcpt) 367 368 #define ANVIL_REMOTE_INCR_NTLS(remote) ANVIL_REMOTE_INCR_RATE((remote), ntls) 369 370 /* Drop connection from (service, client) state. */ 371 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); \ 378 } \ 379 } while(0) 380 381 /* Create local server state. */ 382 383 #define ANVIL_LOCAL_INIT(local) \ 384 do { \ 385 (local)->anvil_remote = 0; \ 386 } while(0) 387 388 /* Add remote connection to local server. */ 389 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) 397 398 /* Test if this remote connection is listed for this local server. */ 399 400 #define ANVIL_LOCAL_REMOTE_LINKED(local, remote) \ 401 ((local)->anvil_remote == (remote)) 402 403 /* Drop specific remote connection from local server. */ 404 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) 411 412 /* Drop all remote connections from local server. */ 413 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) 420 421 /* 422 * Lookup table to map request names to action routines. 423 */ 424 typedef struct { 425 const char *name; 426 void (*action) (VSTREAM *, const char *); 427 } ANVIL_REQ_TABLE; 428 429 /* 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. 434 */ 435 typedef struct { 436 int value; /* peak value */ 437 char *ident; /* lookup key */ 438 time_t when; /* time of peak value */ 439 } ANVIL_MAX; 440 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 */ 446 447 static int max_cache_size; /* peak cache size */ 448 static time_t max_cache_time; /* time of peak size */ 449 450 /* Update/report peak usage. */ 451 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); \ 460 } \ 461 _max.when = event_time(); \ 462 } while (0) 463 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; \ 471 } \ 472 } while (0); 473 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; \ 480 } \ 481 } while (0); 482 483 /* 484 * Silly little macros. 485 */ 486 #define STR(x) vstring_str(x) 487 #define STREQ(x,y) (strcmp((x), (y)) == 0) 488 489 /* anvil_remote_expire - purge expired connection state */ 490 491 static void anvil_remote_expire(int unused_event, char *context) 492 { 493 ANVIL_REMOTE *anvil_remote = (ANVIL_REMOTE *) context; 494 const char *myname = "anvil_remote_expire"; 495 496 if (msg_verbose) 497 msg_info("%s %s", myname, anvil_remote->ident); 498 499 if (anvil_remote->count != 0) 500 msg_panic("%s: bad connection count: %d", 501 myname, anvil_remote->count); 502 503 htable_delete(anvil_remote_map, anvil_remote->ident, 504 (void (*) (char *)) 0); 505 ANVIL_REMOTE_FREE(anvil_remote); 506 507 if (msg_verbose) 508 msg_info("%s: anvil_remote_map used=%d", 509 myname, anvil_remote_map->used); 510 } 511 512 /* anvil_remote_lookup - dump address status */ 513 514 static void anvil_remote_lookup(VSTREAM *client_stream, const char *ident) 515 { 516 ANVIL_REMOTE *anvil_remote; 517 const char *myname = "anvil_remote_lookup"; 518 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); 523 524 /* 525 * Look up remote client information. 526 */ 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 { 538 539 /* 540 * Do not report stale information. 541 */ 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); 553 } 554 } 555 556 /* anvil_remote_conn_update - instantiate or update connection info */ 557 558 static ANVIL_REMOTE *anvil_remote_conn_update(VSTREAM *client_stream, const char *ident) 559 { 560 ANVIL_REMOTE *anvil_remote; 561 ANVIL_LOCAL *anvil_local; 562 const char *myname = "anvil_remote_conn_update"; 563 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); 568 569 /* 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. 574 */ 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(); 583 } 584 } else { 585 ANVIL_REMOTE_NEXT_CONN(anvil_remote); 586 } 587 588 /* 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. 591 */ 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); 598 } 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); 603 604 return (anvil_remote); 605 } 606 607 /* anvil_remote_connect - report connection event, query address status */ 608 609 static void anvil_remote_connect(VSTREAM *client_stream, const char *ident) 610 { 611 ANVIL_REMOTE *anvil_remote; 612 613 /* 614 * Update or instantiate connection info. 615 */ 616 anvil_remote = anvil_remote_conn_update(client_stream, ident); 617 618 /* 619 * Respond to the local server. 620 */ 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); 626 627 /* 628 * Update peak statistics. 629 */ 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); 634 } 635 636 /* anvil_remote_mail - register message delivery request */ 637 638 static void anvil_remote_mail(VSTREAM *client_stream, const char *ident) 639 { 640 ANVIL_REMOTE *anvil_remote; 641 642 /* 643 * Be prepared for "postfix reload" after "connect". 644 */ 645 if ((anvil_remote = 646 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) 647 anvil_remote = anvil_remote_conn_update(client_stream, ident); 648 649 /* 650 * Update message delivery request rate and respond to local server. 651 */ 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); 657 658 /* 659 * Update peak statistics. 660 */ 661 if (anvil_remote->mail > max_mail_rate.value) 662 ANVIL_MAX_UPDATE(max_mail_rate, anvil_remote->mail, anvil_remote->ident); 663 } 664 665 /* anvil_remote_rcpt - register recipient address event */ 666 667 static void anvil_remote_rcpt(VSTREAM *client_stream, const char *ident) 668 { 669 ANVIL_REMOTE *anvil_remote; 670 671 /* 672 * Be prepared for "postfix reload" after "connect". 673 */ 674 if ((anvil_remote = 675 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) 676 anvil_remote = anvil_remote_conn_update(client_stream, ident); 677 678 /* 679 * Update recipient address rate and respond to local server. 680 */ 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); 686 687 /* 688 * Update peak statistics. 689 */ 690 if (anvil_remote->rcpt > max_rcpt_rate.value) 691 ANVIL_MAX_UPDATE(max_rcpt_rate, anvil_remote->rcpt, anvil_remote->ident); 692 } 693 694 /* anvil_remote_newtls - register newtls event */ 695 696 static void anvil_remote_newtls(VSTREAM *client_stream, const char *ident) 697 { 698 ANVIL_REMOTE *anvil_remote; 699 700 /* 701 * Be prepared for "postfix reload" after "connect". 702 */ 703 if ((anvil_remote = 704 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) 705 anvil_remote = anvil_remote_conn_update(client_stream, ident); 706 707 /* 708 * Update newtls rate and respond to local server. 709 */ 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); 715 716 /* 717 * Update peak statistics. 718 */ 719 if (anvil_remote->ntls > max_ntls_rate.value) 720 ANVIL_MAX_UPDATE(max_ntls_rate, anvil_remote->ntls, anvil_remote->ident); 721 } 722 723 /* anvil_remote_newtls_stat - report newtls stats */ 724 725 static void anvil_remote_newtls_stat(VSTREAM *client_stream, const char *ident) 726 { 727 ANVIL_REMOTE *anvil_remote; 728 int rate; 729 730 /* 731 * Be prepared for "postfix reload" after "connect". 732 */ 733 if ((anvil_remote = 734 (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) { 735 rate = 0; 736 } 737 738 /* 739 * Do not report stale information. 740 */ 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; 746 } 747 748 /* 749 * Respond to local server. 750 */ 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); 755 } 756 757 /* anvil_remote_disconnect - report disconnect event */ 758 759 static void anvil_remote_disconnect(VSTREAM *client_stream, const char *ident) 760 { 761 ANVIL_REMOTE *anvil_remote; 762 ANVIL_LOCAL *anvil_local; 763 const char *myname = "anvil_remote_disconnect"; 764 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); 769 770 /* 771 * Update local and remote info if this remote connection is listed for 772 * this local server. 773 */ 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); 780 } 781 if (msg_verbose) 782 msg_info("%s: anvil_local 0x%lx", 783 myname, (unsigned long) anvil_local); 784 785 /* 786 * Respond to the local server. 787 */ 788 attr_print_plain(client_stream, ATTR_FLAG_NONE, 789 ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, 790 ATTR_TYPE_END); 791 } 792 793 /* anvil_service_done - clean up */ 794 795 static void anvil_service_done(VSTREAM *client_stream, char *unused_service, 796 char **unused_argv) 797 { 798 ANVIL_LOCAL *anvil_local; 799 const char *myname = "anvil_service_done"; 800 801 if (msg_verbose) 802 msg_info("%s fd=%d stream=0x%lx", 803 myname, vstream_fileno(client_stream), 804 (unsigned long) client_stream); 805 806 /* 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. 810 */ 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)); 820 } 821 822 /* anvil_status_dump - log and reset extreme usage */ 823 824 static void anvil_status_dump(char *unused_name, char **unused_argv) 825 { 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"); 831 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; 836 } 837 } 838 839 /* anvil_status_update - log and reset extreme usage periodically */ 840 841 static void anvil_status_update(int unused_event, char *context) 842 { 843 anvil_status_dump((char *) 0, (char **) 0); 844 event_request_timer(anvil_status_update, context, var_anvil_stat_time); 845 } 846 847 /* anvil_service - perform service for client */ 848 849 static void anvil_service(VSTREAM *client_stream, char *unused_service, char **argv) 850 { 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, 862 }; 863 const ANVIL_REQ_TABLE *rp; 864 865 /* 866 * Sanity check. This service takes no command-line arguments. 867 */ 868 if (argv[0]) 869 msg_fatal("unexpected command-line argument: %s", argv[0]); 870 871 /* 872 * Initialize. 873 */ 874 if (request == 0) { 875 request = vstring_alloc(10); 876 ident = vstring_alloc(10); 877 } 878 879 /* 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. 884 */ 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; 899 } 900 if (STREQ(rp->name, STR(request))) { 901 rp->action(client_stream, STR(ident)); 902 break; 903 } 904 } 905 vstream_fflush(client_stream); 906 } else { 907 /* Note: invokes anvil_service_done() */ 908 multi_server_disconnect(client_stream); 909 } 910 if (msg_verbose) 911 msg_info("--- end request ---"); 912 } 913 914 /* post_jail_init - post-jail initialization */ 915 916 static void post_jail_init(char *unused_name, char **unused_argv) 917 { 918 919 /* 920 * Dump and reset extreme usage every so often. 921 */ 922 event_request_timer(anvil_status_update, (char *) 0, var_anvil_stat_time); 923 924 /* 925 * Initial client state tables. 926 */ 927 anvil_remote_map = htable_create(1000); 928 929 /* 930 * Do not limit the number of client requests. 931 */ 932 var_use_limit = 0; 933 934 /* 935 * Don't exit before the sampling interval ends. 936 */ 937 if (var_idle_limit < var_anvil_time_unit) 938 var_idle_limit = var_anvil_time_unit; 939 } 940 941 MAIL_VERSION_STAMP_DECLARE; 942 943 /* main - pass control to the multi-threaded skeleton */ 944 945 int main(int argc, char **argv) 946 { 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, 950 0, 951 }; 952 953 /* 954 * Fingerprint executables and core dumps. 955 */ 956 MAIL_VERSION_STAMP_ALLOCATE; 957 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, 964 0); 965 } 966