anvil.c revision 1.1
1/* $NetBSD: anvil.c,v 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 */ 275int var_anvil_time_unit; 276int var_anvil_stat_time; 277 278 /* 279 * Global dynamic state. 280 */ 281static HTABLE *anvil_remote_map; /* indexed by service+ remote client */ 282 283 /* 284 * Remote connection state, one instance for each (service, client) pair. 285 */ 286typedef 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 */ 301typedef 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 */ 424typedef 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 */ 435typedef struct { 436 int value; /* peak value */ 437 char *ident; /* lookup key */ 438 time_t when; /* time of peak value */ 439} ANVIL_MAX; 440 441static ANVIL_MAX max_conn_count; /* peak connection count */ 442static ANVIL_MAX max_conn_rate; /* peak connection rate */ 443static ANVIL_MAX max_mail_rate; /* peak message rate */ 444static ANVIL_MAX max_rcpt_rate; /* peak recipient rate */ 445static ANVIL_MAX max_ntls_rate; /* peak new TLS session rate */ 446 447static int max_cache_size; /* peak cache size */ 448static 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 491static 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 514static 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 558static 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 609static 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 638static 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 667static 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 696static 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 725static 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 759static 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 795static 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 824static 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 841static 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 849static 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 916static 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 941MAIL_VERSION_STAMP_DECLARE; 942 943/* main - pass control to the multi-threaded skeleton */ 944 945int 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