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 */ 299 int var_anvil_time_unit; 300 int var_anvil_stat_time; 301 302 /* 303 * Global dynamic state. 304 */ 305 static HTABLE *anvil_remote_map; /* indexed by service+ remote client */ 306 307 /* 308 * Remote connection state, one instance for each (service, client) pair. 309 */ 310 typedef 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 */ 326 typedef 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 */ 453 typedef 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 */ 464 typedef struct { 465 int value; /* peak value */ 466 char *ident; /* lookup key */ 467 time_t when; /* time of peak value */ 468 } ANVIL_MAX; 469 470 static ANVIL_MAX max_conn_count; /* peak connection count */ 471 static ANVIL_MAX max_conn_rate; /* peak connection rate */ 472 static ANVIL_MAX max_mail_rate; /* peak message rate */ 473 static ANVIL_MAX max_rcpt_rate; /* peak recipient rate */ 474 static ANVIL_MAX max_ntls_rate; /* peak new TLS session rate */ 475 static ANVIL_MAX max_auth_rate; /* peak AUTH request rate */ 476 477 static int max_cache_size; /* peak cache size */ 478 static 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 521 static 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 544 static 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 590 static 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 641 static 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 670 static 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 699 static 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 728 static 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 757 static 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 786 static 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 820 static 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 856 static 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 885 static 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 903 static 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 911 static 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 979 static 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 1004 MAIL_VERSION_STAMP_DECLARE; 1005 1006 /* main - pass control to the multi-threaded skeleton */ 1007 1008 int 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