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