Home | History | Annotate | Line # | Download | only in anvil
      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   */
    304 int     var_anvil_time_unit;
    305 int     var_anvil_stat_time;
    306 
    307  /*
    308   * Global dynamic state.
    309   */
    310 static HTABLE *anvil_remote_map;	/* indexed by service+ remote client */
    311 
    312  /*
    313   * Remote connection state, one instance for each (service, client) pair.
    314   */
    315 typedef 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   */
    331 typedef 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   */
    458 typedef 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   */
    469 typedef struct {
    470     int     value;			/* peak value */
    471     char   *ident;			/* lookup key */
    472     time_t  when;			/* time of peak value */
    473 } ANVIL_MAX;
    474 
    475 static ANVIL_MAX max_conn_count;	/* peak connection count */
    476 static ANVIL_MAX max_conn_rate;		/* peak connection rate */
    477 static ANVIL_MAX max_mail_rate;		/* peak message rate */
    478 static ANVIL_MAX max_rcpt_rate;		/* peak recipient rate */
    479 static ANVIL_MAX max_ntls_rate;		/* peak new TLS session rate */
    480 static ANVIL_MAX max_auth_rate;		/* peak AUTH request rate */
    481 
    482 static int max_cache_size;		/* peak cache size */
    483 static 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 
    526 static 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 
    549 static 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 
    595 static 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 
    646 static 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 
    675 static 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 
    704 static 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 
    733 static 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 
    762 static 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 
    791 static 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 
    825 static 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 
    861 static 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 
    890 static 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 
    908 static 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 
    916 static 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 
    984 static 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 
   1009 MAIL_VERSION_STAMP_DECLARE;
   1010 
   1011 /* post_accept - announce our protocol */
   1012 
   1013 static 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 
   1028 int     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