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