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