From 88a2e39fc79c09ccaf3a8d044d6de2bf98a78811 Mon Sep 17 00:00:00 2001
From: Wietse Venema
This document covers the following topics:
diff --git a/postfix/html/anvil.8.html b/postfix/html/anvil.8.html index e55868714..b10dd5705 100644 --- a/postfix/html/anvil.8.html +++ b/postfix/html/anvil.8.html @@ -13,151 +13,167 @@ ANVIL(8) ANVIL(8) anvil [generic Postfix daemon options] DESCRIPTION - The Postfix anvil(8) server maintains short-term statis- - tics to defend against clients that hammer a server with - either too many simultaneous sessions, or with too many - successive requests within a configurable time interval. - This server is designed to run under control by the Post- - fix master(8) server. + The Postfix anvil(8) server maintains statistics about + client connection counts or client request rates. This + information can be used to defend against clients that + hammer a server with either too many simultaneous ses- + sions, or with too many successive requests within a con- + figurable time interval. This server is designed to run + under control by the Postfix master(8) server. - The anvil(8) server maintains no persistent database. - Standard library utilities do not meet Postfix performance - and robustness requirements. + In the following text, ident specifies a (service, client) + combination. The exact syntax of that information is + application-dependent; the anvil(8) server does not care. -CONNECTION COUNT/RATE LIMITING - When a remote client connects, a connection count (or - rate) limited server should send the following request to +CONNECTION COUNT/RATE CONTROL + To register a new connection send the following request to the anvil(8) server: request=connect ident=string - This registers a new connection for the (service, client) - combination specified with ident. The anvil(8) server - answers with the number of simultaneous connections and - the number of connections per unit time for that (service, - client) combination: + The anvil(8) server answers with the number of simultane- + ous connections and the number of connections per unit + time for the (service, client) combination specified with + ident: status=0 count=number rate=number - The rate is computed as the number of connections that - were registered in the current "time unit" interval. It - is left up to the server to decide if the remote client - exceeds the connection count (or rate) limit. - - When a remote client disconnects, a connection count (or - rate) limited server should send the following request to - the anvil(8) server: + To register a disconnect event send the following request + to the anvil(8) server: request=disconnect ident=string - This registers a disconnect event for the (service, - client) combination specified with ident. The anvil(8) - server replies with: + The anvil(8) server replies with: status=0 -MESSAGE RATE LIMITING - When a remote client sends a message delivery request, a - message rate limited server should send the following +MESSAGE RATE CONTROL + To register a message delivery request send the following request to the anvil(8) server: request=message ident=string - This registers a message delivery request for the (ser- - vice, client) combination specified with ident. The - anvil(8) server answers with the number of message deliv- - ery requests per unit time for that (service, client) com- - bination: + The anvil(8) server answers with the number of message + delivery requests per unit time for the (service, client) + combination specified with ident: status=0 rate=number - In order to prevent the anvil(8) server from discarding - client request rates too early or too late, a message rate - limited service should also register connect/disconnect - events. - -RECIPIENT RATE LIMITING - When a remote client sends a recipient address, a recipi- - ent rate limited server should send the following request +RECIPIENT RATE CONTROL + To register a recipient request send the following request to the anvil(8) server: request=recipient ident=string - This registers a recipient request for the (service, - client) combination specified with ident. The anvil(8) - server answers with the number of recipient addresses per - unit time for that (service, client) combination: + The anvil(8) server answers with the number of recipient + addresses per unit time for the (service, client) combina- + tion specified with ident: status=0 rate=number - In order to prevent the anvil(8) server from discarding - client request rates too early or too late, a recipient - rate limited service should also register connect/discon- - nect events. +TLS SESSION NEGOTIATION RATE CONTROL + The features described in this section are available with + Postfix 2.3 and later. + + To register a request for a new (i.e. not cached) TLS ses- + sion send the following request to the anvil(8) server: + + request=newtls + ident=string + + The anvil(8) server answers with the number of new TLS + session requests per unit time for the (service, client) + combination specified with ident: + + status=0 + rate=number + + To retrieve new TLS session request rate information with- + out updating the counter information, use: + + request=newtls_report + ident=string + + The anvil(8) server answers with the number of new TLS + session requests per unit time for the (service, client) + combination specified with ident. + + status=0 + rate=number SECURITY The anvil(8) server does not talk to the network or to local users, and can run chrooted at fixed low privilege. The anvil(8) server maintains an in-memory table with - information about recent clients of a connection count (or - rate) limited service. Although state is kept only tempo- - rarily, this may require a lot of memory on systems that - handle connections from many remote clients. To reduce - memory usage, reduce the time unit over which state is - kept. + information about recent clients requests. No persistent + state is kept because standard system library routines are + not sufficiently robust for update-intensive applications. + + Although the in-memory state is kept only temporarily, + this may require a lot of memory on systems that handle + connections from many remote clients. To reduce memory + usage, reduce the time unit over which state is kept. DIAGNOSTICS Problems and transactions are logged to syslogd(8). Upon exit, and every anvil_status_update_time seconds, the - server logs the maximal count and rate values measured, - together with (service, client) information and the time - of day associated with those events. In order to avoid - unnecessary overhead, no measurements are done for activ- + server logs the maximal count and rate values measured, + together with (service, client) information and the time + of day associated with those events. In order to avoid + unnecessary overhead, no measurements are done for activ- ity that isn't concurrency limited or rate limited. BUGS - Systems behind network address translating routers or + Systems behind network address translating routers or proxies appear to have the same client address and can run into connection count and/or rate limits falsely. In this preliminary implementation, a count (or rate) lim- - ited server can have only one remote client at a time. If - a server reports multiple simultaneous clients, all but + ited server can have only one remote client at a time. If + a server reports multiple simultaneous clients, all but the last reported client are ignored. -CONFIGURATION PARAMETERS - Changes to main.cf are picked up automatically as anvil(8) - processes run for only a limited amount of time. Use the - command "postfix reload" to speed up a change. + The anvil(8) server automatically discards client request + information after it expires. To prevent the anvil(8) + server from discarding client request rate information too + early or too late, a rate limited service should always + register connect/disconnect events even when it does not + explicitly limit them. - The text below provides only a parameter summary. See +CONFIGURATION PARAMETERS + On low-traffic mail systems, changes to main.cf are picked + up automatically as anvil(8) processes run for only a lim- + ited amount of time. On other mail systems, use the com- + mand "postfix reload" to speed up a change. + + The text below provides only a parameter summary. See postconf(5) for more details including examples. anvil_rate_time_unit (60s) - The time unit over which client connection rates + The time unit over which client connection rates and other rates are calculated. anvil_status_update_time (600s) - How frequently the anvil(8) connection and rate + How frequently the anvil(8) connection and rate limiting server logs peak usage information. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. daemon_timeout (18000s) - How much time a Postfix daemon process may take to - handle a request before it is terminated by a + How much time a Postfix daemon process may take to + handle a request before it is terminated by a built-in watchdog timer. ipc_timeout (3600s) @@ -165,28 +181,28 @@ ANVIL(8) ANVIL(8) over an internal communication channel. max_idle (100s) - The maximum amount of time that an idle Postfix - daemon process waits for the next service request + The maximum amount of time that an idle Postfix + daemon process waits for the next service request before exiting. max_use (100) - The maximal number of connection requests before a + The maximal number of connection requests before a Postfix daemon process terminates. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (postfix) - The mail system name that is prepended to the - process name in syslog records, so that "smtpd" + The mail system name that is prepended to the + process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". SEE ALSO @@ -198,7 +214,7 @@ ANVIL(8) ANVIL(8) TUNING_README, performance tuning LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. HISTORY diff --git a/postfix/html/local.8.html b/postfix/html/local.8.html index 5bb9b37a7..344e4613b 100644 --- a/postfix/html/local.8.html +++ b/postfix/html/local.8.html @@ -386,13 +386,6 @@ LOCAL(8) LOCAL(8) attempt; do not update the Delivered-To: address while expanding aliases or .forward files. - sticky_owner_alias (yes) - When expanding a local(8) alias that has an owner - alias (see owner-name discussion in aliases(5)), - use the owner information even when the expansion - invokes a subordinate alias that has no owner - alias. - DELIVERY METHOD CONTROLS The precedence of local(8) delivery methods from high to low is: aliases, .forward files, mailbox_transport, mail- diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index bccd8ed65..7b018576f 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -7205,6 +7205,47 @@ Example: + + ++The maximal number of new (i.e., uncached) TLS sessions that a +remote SMTP client is allowed to negotiate with this service per +time unit. The time unit is specified with the anvil_rate_time_unit +configuration parameter. +
+ ++By default, a remote SMTP client can negotiate as many new TLS +sessions per time unit as Postfix can accept. +
+ ++To disable this feature, specify a limit of 0. Otherwise, specify +a limit that is at least the per-client concurrent session limit, +or else legitimate client sessions may be rejected. +
+ ++WARNING: The purpose of this feature is to limit abuse. It must +not be used to regulate legitimate mail traffic. +
+ ++This feature is available in Postfix 2.3 and later. +
+ ++Example: +
+ ++smtpd_client_new_tls_session_rate_limit = 100 ++ +
Attempt to look up the SMTP client hostname, and verify that +the name matches the client IP address. A client name is set to +"unknown" when it cannot be looked up or verified, or when name +lookup is disabled. Turning off name lookup reduces delays due to +DNS lookup and increases the maximal inbound delivery rate.
+ +When expanding a local(8) alias that has an owner alias (see -owner-name discussion in aliases(5)), use the owner information -even when the expansion invokes a subordinate alias that has no -owner alias.
- -This feature is available in Postfix 2.3 and later. With older -Postfix releases, the behavior is as if this parameter is set to -"no". The old setting provides poorer error reporting with nested -aliases that only have an owner alias at the top level.
- -This document covers the following topics:
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index b37eadef6..adcf4124d 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -4399,6 +4399,43 @@ Example: smtpd_client_recipient_rate_limit = 1000 +%PARAM smtpd_client_new_tls_session_rate_limit 0 + ++The maximal number of new (i.e., uncached) TLS sessions that a +remote SMTP client is allowed to negotiate with this service per +time unit. The time unit is specified with the anvil_rate_time_unit +configuration parameter. +
+ ++By default, a remote SMTP client can negotiate as many new TLS +sessions per time unit as Postfix can accept. +
+ ++To disable this feature, specify a limit of 0. Otherwise, specify +a limit that is at least the per-client concurrent session limit, +or else legitimate client sessions may be rejected. +
+ ++WARNING: The purpose of this feature is to limit abuse. It must +not be used to regulate legitimate mail traffic. +
+ ++This feature is available in Postfix 2.3 and later. +
+ ++Example: +
+ ++smtpd_client_new_tls_session_rate_limit = 100 ++ %PARAM smtpd_client_restrictions
@@ -8542,18 +8579,6 @@ message_strip_characters = \0
This feature is available in Postfix 2.3 and later.
-%PARAM sticky_owner_alias yes - -When expanding a local(8) alias that has an owner alias (see -owner-name discussion in aliases(5)), use the owner information -even when the expansion invokes a subordinate alias that has no -owner alias.
- -This feature is available in Postfix 2.3 and later. With older -Postfix releases, the behavior is as if this parameter is set to -"no". The old setting provides poorer error reporting with nested -aliases that only have an owner alias at the top level.
- %PARAM frozen_delivered_to yesUpdate the local(8) delivery agent's idea of the Delivered-To: @@ -8567,3 +8592,11 @@ Postfix releases, the behavior is as if this parameter is set to or .forward files. When an alias or .forward file changes the Delivered-To: address, it ties up one queue file and one cleanup process instance while mail is being forwarded.
+ +%PARAM smtpd_peername_lookup yes + +Attempt to look up the SMTP client hostname, and verify that +the name matches the client IP address. A client name is set to +"unknown" when it cannot be looked up or verified, or when name +lookup is disabled. Turning off name lookup reduces delays due to +DNS lookup and increases the maximal inbound delivery rate.
diff --git a/postfix/src/anvil/anvil.c b/postfix/src/anvil/anvil.c index 43f7e54d6..9f8a08439 100644 --- a/postfix/src/anvil/anvil.c +++ b/postfix/src/anvil/anvil.c @@ -6,21 +6,23 @@ /* SYNOPSIS /* \fBanvil\fR [generic Postfix daemon options] /* DESCRIPTION -/* The Postfix \fBanvil\fR(8) server maintains short-term statistics -/* to defend against clients that hammer a server with either too -/* many simultaneous sessions, or with too many successive requests -/* within a configurable time interval. -/* This server is designed to run under control by the Postfix -/* \fBmaster\fR(8) server. +/* The Postfix \fBanvil\fR(8) server maintains statistics about +/* client connection counts or client request rates. This +/* information can be used to defend against clients that +/* hammer a server with either too many simultaneous sessions, +/* or with too many successive requests within a configurable +/* time interval. This server is designed to run under control +/* by the Postfix \fBmaster\fR(8) server. /* -/* The \fBanvil\fR(8) server maintains no persistent database. Standard -/* library utilities do not meet Postfix performance and robustness -/* requirements. -/* CONNECTION COUNT/RATE LIMITING +/* In the following text, \fBident\fR specifies a (service, +/* client) combination. The exact syntax of that information +/* is application-dependent; the \fBanvil\fR(8) server does +/* not care. +/* CONNECTION COUNT/RATE CONTROL /* .ad /* .fi -/* When a remote client connects, a connection count (or rate) limited -/* server should send the following request to the \fBanvil\fR(8) server: +/* To register a new connection send the following request to +/* the \fBanvil\fR(8) server: /* .PP /* .in +4 /* \fBrequest=connect\fR @@ -28,11 +30,10 @@ /* \fBident=\fIstring\fR /* .in /* .PP -/* This registers a new connection for the (service, client) -/* combination specified with \fBident\fR. The \fBanvil\fR(8) server -/* answers with the number of simultaneous connections and the -/* number of connections per unit time for that (service, client) -/* combination: +/* The \fBanvil\fR(8) server answers with the number of +/* simultaneous connections and the number of connections per +/* unit time for the (service, client) combination specified +/* with \fBident\fR: /* .PP /* .in +4 /* \fBstatus=0\fR @@ -42,13 +43,8 @@ /* \fBrate=\fInumber\fR /* .in /* .PP -/* The \fBrate\fR is computed as the number of connections -/* that were registered in the current "time unit" interval. -/* It is left up to the server to decide if the remote client -/* exceeds the connection count (or rate) limit. -/* .PP -/* When a remote client disconnects, a connection count (or rate) limited -/* server should send the following request to the \fBanvil\fR(8) server: +/* To register a disconnect event send the following request +/* to the \fBanvil\fR(8) server: /* .PP /* .in +4 /* \fBrequest=disconnect\fR @@ -56,17 +52,14 @@ /* \fBident=\fIstring\fR /* .in /* .PP -/* This registers a disconnect event for the (service, client) -/* combination specified with \fBident\fR. The \fBanvil\fR(8) -/* server replies with: +/* The \fBanvil\fR(8) server replies with: /* .PP /* .ti +4 /* \fBstatus=0\fR -/* MESSAGE RATE LIMITING +/* MESSAGE RATE CONTROL /* .ad /* .fi -/* When a remote client sends a message delivery request, a -/* message rate limited server should send the following +/* To register a message delivery request send the following /* request to the \fBanvil\fR(8) server: /* .PP /* .in +4 @@ -75,26 +68,20 @@ /* \fBident=\fIstring\fR /* .in /* .PP -/* This registers a message delivery request for the (service, client) -/* combination specified with \fBident\fR. The \fBanvil\fR(8) server -/* answers with the number of message delivery requests per unit time -/* for that (service, client) combination: +/* The \fBanvil\fR(8) server answers with the number of message +/* delivery requests per unit time for the (service, client) +/* combination specified with \fBident\fR: /* .PP /* .in +4 /* \fBstatus=0\fR /* .br /* \fBrate=\fInumber\fR /* .in -/* .PP -/* In order to prevent the \fBanvil\fR(8) server from discarding client -/* request rates too early or too late, a message rate limited -/* service should also register connect/disconnect events. -/* RECIPIENT RATE LIMITING +/* RECIPIENT RATE CONTROL /* .ad /* .fi -/* When a remote client sends a recipient address, a recipient -/* rate limited server should send the following request to -/* the \fBanvil\fR(8) server: +/* To register a recipient request send the following request +/* to the \fBanvil\fR(8) server: /* .PP /* .in +4 /* \fBrequest=recipient\fR @@ -102,10 +89,33 @@ /* \fBident=\fIstring\fR /* .in /* .PP -/* This registers a recipient request for the (service, client) -/* combination specified with \fBident\fR. The \fBanvil\fR(8) server -/* answers with the number of recipient addresses per unit time -/* for that (service, client) combination: +/* The \fBanvil\fR(8) server answers with the number of recipient +/* addresses per unit time for the (service, client) combination +/* specified with \fBident\fR: +/* .PP +/* .in +4 +/* \fBstatus=0\fR +/* .br +/* \fBrate=\fInumber\fR +/* .in +/* TLS SESSION NEGOTIATION RATE CONTROL +/* .ad +/* .fi +/* The features described in this section are available with +/* Postfix 2.3 and later. +/* +/* To register a request for a new (i.e. not cached) TLS session +/* send the following request to the \fBanvil\fR(8) server: +/* .PP +/* .in +4 +/* \fBrequest=newtls\fR +/* .br +/* \fBident=\fIstring\fR +/* .in +/* .PP +/* The \fBanvil\fR(8) server answers with the number of new +/* TLS session requests per unit time for the (service, client) +/* combination specified with \fBident\fR: /* .PP /* .in +4 /* \fBstatus=0\fR @@ -113,21 +123,39 @@ /* \fBrate=\fInumber\fR /* .in /* .PP -/* In order to prevent the \fBanvil\fR(8) server from discarding client -/* request rates too early or too late, a recipient rate limited -/* service should also register connect/disconnect events. +/* To retrieve new TLS session request rate information without +/* updating the counter information, use: +/* .PP +/* .in +4 +/* \fBrequest=newtls_report\fR +/* .br +/* \fBident=\fIstring\fR +/* .in +/* .PP +/* The \fBanvil\fR(8) server answers with the number of new +/* TLS session requests per unit time for the (service, client) +/* combination specified with \fBident\fR. +/* .PP +/* .in +4 +/* \fBstatus=0\fR +/* .br +/* \fBrate=\fInumber\fR +/* .in /* SECURITY /* .ad /* .fi /* The \fBanvil\fR(8) server does not talk to the network or to local /* users, and can run chrooted at fixed low privilege. /* -/* The \fBanvil\fR(8) server maintains an in-memory table with information -/* about recent clients of a connection count (or rate) limited service. -/* Although state is kept only temporarily, this may require a lot of -/* memory on systems that handle connections from many remote clients. -/* To reduce memory usage, reduce the time unit over which state -/* is kept. +/* The \fBanvil\fR(8) server maintains an in-memory table with +/* information about recent clients requests. No persistent +/* state is kept because standard system library routines are +/* not sufficiently robust for update-intensive applications. +/* +/* Although the in-memory state is kept only temporarily, this +/* may require a lot of memory on systems that handle connections +/* from many remote clients. To reduce memory usage, reduce +/* the time unit over which state is kept. /* DIAGNOSTICS /* Problems and transactions are logged to \fBsyslogd\fR(8). /* @@ -147,12 +175,20 @@ /* can have only one remote client at a time. If a server reports /* multiple simultaneous clients, all but the last reported client /* are ignored. +/* +/* The \fBanvil\fR(8) server automatically discards client +/* request information after it expires. To prevent the +/* \fBanvil\fR(8) server from discarding client request rate +/* information too early or too late, a rate limited service +/* should always register connect/disconnect events even when +/* it does not explicitly limit them. /* CONFIGURATION PARAMETERS /* .ad /* .fi -/* Changes to \fBmain.cf\fR are picked up automatically as \fBanvil\fR(8) -/* processes run for only a limited amount of time. Use the command -/* "\fBpostfix reload\fR" to speed up a change. +/* On low-traffic mail systems, changes to \fBmain.cf\fR are +/* picked up automatically as \fBanvil\fR(8) processes run for +/* only a limited amount of time. On other mail systems, use +/* the command "\fBpostfix reload\fR" to speed up a change. /* /* The text below provides only a parameter summary. See /* \fBpostconf\fR(5) for more details including examples. @@ -252,28 +288,53 @@ static HTABLE *anvil_remote_map; /* indexed by service+ remote client */ * Absent a real-time query interface, these are logged at process exit time * and at regular intervals. */ -static int max_count; -static char *max_count_user; -static time_t max_count_time; +typedef struct { + int value; /* peak value */ + char *ident; /* lookup key */ + time_t when; /* time of peak value */ +} ANVIL_MAX; -static int max_rate; -static char *max_rate_user; -static time_t max_rate_time; +static ANVIL_MAX max_conn_count; /* peak connection count */ +static ANVIL_MAX max_conn_rate; /* peak connection rate */ +static ANVIL_MAX max_mail_rate; /* peak message rate */ +static ANVIL_MAX max_rcpt_rate; /* peak recipient rate */ +static ANVIL_MAX max_ntls_rate; /* peak new TLS session rate */ -static int max_mail; -static char *max_mail_user; -static time_t max_mail_time; +static int max_cache_size; /* peak cache size */ +static time_t max_cache_time; /* time of peak size */ -static int max_rcpt; -static char *max_rcpt_user; -static time_t max_rcpt_time; +/* Update/report peak usage. */ -static int max_newtls; -static char *max_newtls_user; -static time_t max_newtls_time; +#define ANVIL_MAX_UPDATE(_max, _value, _ident) \ + do { \ + _max.value = _value; \ + if (_max.ident == 0) { \ + _max.ident = mystrdup(_ident); \ + } else if (!STREQ(_max.ident, _ident)) { \ + myfree(_max.ident); \ + _max.ident = mystrdup(_ident); \ + } \ + _max.when = event_time(); \ + } while (0) -static int max_cache; -static time_t max_cache_time; +#define ANVIL_MAX_RATE_REPORT(_max, _name) \ + do { \ + if (_max.value > 0) { \ + msg_info("statistics: max " _name " rate %d/%ds for (%s) at %.15s", \ + _max.value, var_anvil_time_unit, \ + _max.ident, ctime(&_max.when) + 4); \ + _max.value = 0; \ + } \ + } while (0); + +#define ANVIL_MAX_COUNT_REPORT(_max, _name) \ + do { \ + if (_max.value > 0) { \ + msg_info("statistics: max " _name " count %d for (%s) at %.15s", \ + _max.value, _max.ident, ctime(&_max.when) + 4); \ + _max.value = 0; \ + } \ + } while (0); /* * Remote connection state, one instance for each (service, client) pair. @@ -284,7 +345,7 @@ typedef struct { int rate; /* connection rate */ int mail; /* message rate */ int rcpt; /* recipient rate */ - int newtls; /* newtls rate */ + int ntls; /* new TLS session rate */ time_t start; /* time of first rate sample */ } ANVIL_REMOTE; @@ -296,12 +357,6 @@ typedef struct { ANVIL_REMOTE *anvil_remote; /* XXX should be list */ } ANVIL_LOCAL; - /* - * Silly little macros. - */ -#define STR(x) vstring_str(x) -#define STREQ(x,y) (strcmp((x), (y)) == 0) - /* * The following operations are implemented as macros with recognizable * names so that we don't lose sight of what the code is trying to do. @@ -312,14 +367,14 @@ typedef struct { /* Create new (service, client) state. */ -#define ANVIL_REMOTE_FIRST(remote, id) \ +#define ANVIL_REMOTE_FIRST_CONN(remote, id) \ do { \ (remote)->ident = mystrdup(id); \ (remote)->count = 1; \ (remote)->rate = 1; \ (remote)->mail = 0; \ (remote)->rcpt = 0; \ - (remote)->newtls = 0; \ + (remote)->ntls = 0; \ (remote)->start = event_time(); \ } while(0) @@ -331,66 +386,41 @@ typedef struct { myfree((char *) (remote)); \ } while(0) +/* Reset event rate counters and start of data collection interval. */ + +#define ANVIL_REMOTE_RSET_RATE(remote, _start) \ + do { \ + (remote)->rate = 0; \ + (remote)->mail = 0; \ + (remote)->rcpt = 0; \ + (remote)->ntls = 0; \ + (remote)->start = _start; \ + } while(0) + /* Add connection to (service, client) state. */ -#define ANVIL_REMOTE_NEXT(remote) \ +#define ANVIL_REMOTE_INCR_RATE(remote, _what) \ do { \ time_t _now = event_time(); \ - if ((remote)->start + var_anvil_time_unit < _now) { \ - (remote)->rate = 1; \ - (remote)->mail = 0; \ - (remote)->rcpt = 0; \ - (remote)->newtls = 0; \ - (remote)->start = _now; \ - } else if ((remote)->rate < INT_MAX) { \ - (remote)->rate += 1; \ - } \ + if ((remote)->start + var_anvil_time_unit < _now) \ + ANVIL_REMOTE_RSET_RATE((remote), _now); \ + if ((remote)->_what < INT_MAX) \ + (remote)->_what += 1; \ + } while(0) + +#define ANVIL_REMOTE_NEXT_CONN(remote) \ + do { \ + ANVIL_REMOTE_INCR_RATE((remote), rate); \ if ((remote)->count == 0) \ event_cancel_timer(anvil_remote_expire, (char *) remote); \ (remote)->count++; \ } while(0) -#define ANVIL_ADD_MAIL(remote) \ - do { \ - time_t _now = event_time(); \ - if ((remote)->start + var_anvil_time_unit < _now) { \ - (remote)->rate = 0; \ - (remote)->mail = 1; \ - (remote)->rcpt = 0; \ - (remote)->newtls = 0; \ - (remote)->start = _now; \ - } else if ((remote)->mail < INT_MAX) { \ - (remote)->mail += 1; \ - } \ - } while(0) +#define ANVIL_REMOTE_INCR_MAIL(remote) ANVIL_REMOTE_INCR_RATE((remote), mail) -#define ANVIL_ADD_RCPT(remote) \ - do { \ - time_t _now = event_time(); \ - if ((remote)->start + var_anvil_time_unit < _now) { \ - (remote)->rate = 0; \ - (remote)->mail = 0; \ - (remote)->rcpt = 1; \ - (remote)->newtls = 0; \ - (remote)->start = _now; \ - } else if ((remote)->rcpt < INT_MAX) { \ - (remote)->rcpt += 1; \ - } \ - } while(0) +#define ANVIL_REMOTE_INCR_RCPT(remote) ANVIL_REMOTE_INCR_RATE((remote), rcpt) -#define ANVIL_ADD_STARTTLS(remote) \ - do { \ - time_t _now = event_time(); \ - if ((remote)->start + var_anvil_time_unit < _now) { \ - (remote)->rate = 0; \ - (remote)->mail = 0; \ - (remote)->rcpt = 0; \ - (remote)->newtls = 1; \ - (remote)->start = _now; \ - } else if ((remote)->newtls < INT_MAX) { \ - (remote)->newtls += 1; \ - } \ - } while(0) +#define ANVIL_REMOTE_INCR_NTLS(remote) ANVIL_REMOTE_INCR_RATE((remote), ntls) /* Drop connection from (service, client) state. */ @@ -443,6 +473,20 @@ typedef struct { anvil_remote_disconnect((stream), (local)->anvil_remote->ident); \ } while (0) + /* + * Lookup table to map request names to action routines. + */ +typedef struct { + const char *name; + void (*action) (VSTREAM *, const char *); +} ANVIL_REQ_TABLE; + + /* + * Silly little macros. + */ +#define STR(x) vstring_str(x) +#define STREQ(x,y) (strcmp((x), (y)) == 0) + /* anvil_remote_expire - purge expired connection state */ static void anvil_remote_expire(int unused_event, char *context) @@ -472,8 +516,6 @@ static void anvil_remote_lookup(VSTREAM *client_stream, const char *ident) { ANVIL_REMOTE *anvil_remote; char *myname = "anvil_remote_lookup"; - HTABLE_INFO **ht_info; - HTABLE_INFO **ht; if (msg_verbose) msg_info("%s fd=%d stream=0x%lx ident=%s", @@ -483,39 +525,31 @@ static void anvil_remote_lookup(VSTREAM *client_stream, const char *ident) /* * Look up remote client information. */ - if (STREQ(ident, "*")) { - attr_print_plain(client_stream, ATTR_FLAG_MORE, - ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, - ATTR_TYPE_END); - ht_info = htable_list(anvil_remote_map); - for (ht = ht_info; *ht; ht++) { - anvil_remote = (ANVIL_REMOTE *) ht[0]->value; - attr_print_plain(client_stream, ATTR_FLAG_MORE, - ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ht[0]->key, - ATTR_TYPE_NUM, ANVIL_ATTR_COUNT, anvil_remote->count, - ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->rate, - ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, anvil_remote->mail, - ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, anvil_remote->rcpt, - ATTR_TYPE_END); - } - attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_END); - myfree((char *) ht_info); - } else if ((anvil_remote = - (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) { + if ((anvil_remote = + (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) { attr_print_plain(client_stream, ATTR_FLAG_NONE, - ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_FAIL, + ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, ATTR_TYPE_NUM, ANVIL_ATTR_COUNT, 0, ATTR_TYPE_NUM, ANVIL_ATTR_RATE, 0, ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, 0, ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, 0, + ATTR_TYPE_NUM, ANVIL_ATTR_NTLS, 0, ATTR_TYPE_END); } else { + + /* + * Do not report stale information. + */ + if (anvil_remote->start != 0 + && anvil_remote->start + var_anvil_time_unit < event_time()) + ANVIL_REMOTE_RSET_RATE(anvil_remote, 0); attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, ATTR_TYPE_NUM, ANVIL_ATTR_COUNT, anvil_remote->count, ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->rate, ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, anvil_remote->mail, ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, anvil_remote->rcpt, + ATTR_TYPE_NUM, ANVIL_ATTR_NTLS, anvil_remote->ntls, ATTR_TYPE_END); } } @@ -542,14 +576,14 @@ static ANVIL_REMOTE *anvil_remote_conn_update(VSTREAM *client_stream, const char if ((anvil_remote = (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) { anvil_remote = (ANVIL_REMOTE *) mymalloc(sizeof(*anvil_remote)); - ANVIL_REMOTE_FIRST(anvil_remote, ident); + ANVIL_REMOTE_FIRST_CONN(anvil_remote, ident); htable_enter(anvil_remote_map, ident, (char *) anvil_remote); - if (max_cache < anvil_remote_map->used) { - max_cache = anvil_remote_map->used; + if (max_cache_size < anvil_remote_map->used) { + max_cache_size = anvil_remote_map->used; max_cache_time = event_time(); } } else { - ANVIL_REMOTE_NEXT(anvil_remote); + ANVIL_REMOTE_NEXT_CONN(anvil_remote); } /* @@ -592,28 +626,12 @@ static void anvil_remote_connect(VSTREAM *client_stream, const char *ident) ATTR_TYPE_END); /* - * Update local statistics. + * Update peak statistics. */ - if (anvil_remote->rate > max_rate) { - max_rate = anvil_remote->rate; - if (max_rate_user == 0) { - max_rate_user = mystrdup(anvil_remote->ident); - } else if (!STREQ(max_rate_user, anvil_remote->ident)) { - myfree(max_rate_user); - max_rate_user = mystrdup(anvil_remote->ident); - } - max_rate_time = event_time(); - } - if (anvil_remote->count > max_count) { - max_count = anvil_remote->count; - if (max_count_user == 0) { - max_count_user = mystrdup(anvil_remote->ident); - } else if (!STREQ(max_count_user, anvil_remote->ident)) { - myfree(max_count_user); - max_count_user = mystrdup(anvil_remote->ident); - } - max_count_time = event_time(); - } + if (anvil_remote->rate > max_conn_rate.value) + ANVIL_MAX_UPDATE(max_conn_rate, anvil_remote->rate, anvil_remote->ident); + if (anvil_remote->count > max_conn_count.value) + ANVIL_MAX_UPDATE(max_conn_count, anvil_remote->count, anvil_remote->ident); } /* anvil_remote_mail - register message delivery request */ @@ -632,25 +650,17 @@ static void anvil_remote_mail(VSTREAM *client_stream, const char *ident) /* * Update message delivery request rate and respond to local client. */ - ANVIL_ADD_MAIL(anvil_remote); + ANVIL_REMOTE_INCR_MAIL(anvil_remote); attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->mail, ATTR_TYPE_END); /* - * Update local statistics. + * Update peak statistics. */ - if (anvil_remote->mail > max_mail) { - max_mail = anvil_remote->mail; - if (max_mail_user == 0) { - max_mail_user = mystrdup(anvil_remote->ident); - } else if (!STREQ(max_mail_user, anvil_remote->ident)) { - myfree(max_mail_user); - max_mail_user = mystrdup(anvil_remote->ident); - } - max_mail_time = event_time(); - } + if (anvil_remote->mail > max_mail_rate.value) + ANVIL_MAX_UPDATE(max_mail_rate, anvil_remote->mail, anvil_remote->ident); } /* anvil_remote_rcpt - register recipient address event */ @@ -669,25 +679,17 @@ static void anvil_remote_rcpt(VSTREAM *client_stream, const char *ident) /* * Update recipient address rate and respond to local client. */ - ANVIL_ADD_RCPT(anvil_remote); + ANVIL_REMOTE_INCR_RCPT(anvil_remote); attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->rcpt, ATTR_TYPE_END); /* - * Update local statistics. + * Update peak statistics. */ - if (anvil_remote->rcpt > max_rcpt) { - max_rcpt = anvil_remote->rcpt; - if (max_rcpt_user == 0) { - max_rcpt_user = mystrdup(anvil_remote->ident); - } else if (!STREQ(max_rcpt_user, anvil_remote->ident)) { - myfree(max_rcpt_user); - max_rcpt_user = mystrdup(anvil_remote->ident); - } - max_rcpt_time = event_time(); - } + if (anvil_remote->rcpt > max_rcpt_rate.value) + ANVIL_MAX_UPDATE(max_rcpt_rate, anvil_remote->rcpt, anvil_remote->ident); } /* anvil_remote_newtls - register newtls event */ @@ -706,25 +708,51 @@ static void anvil_remote_newtls(VSTREAM *client_stream, const char *ident) /* * Update newtls rate and respond to local client. */ - ANVIL_ADD_STARTTLS(anvil_remote); + ANVIL_REMOTE_INCR_NTLS(anvil_remote); attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, - ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->newtls, + ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->ntls, ATTR_TYPE_END); /* - * Update local statistics. + * Update peak statistics. */ - if (anvil_remote->newtls > max_newtls) { - max_newtls = anvil_remote->newtls; - if (max_newtls_user == 0) { - max_newtls_user = mystrdup(anvil_remote->ident); - } else if (!STREQ(max_newtls_user, anvil_remote->ident)) { - myfree(max_newtls_user); - max_newtls_user = mystrdup(anvil_remote->ident); - } - max_newtls_time = event_time(); + if (anvil_remote->ntls > max_ntls_rate.value) + ANVIL_MAX_UPDATE(max_ntls_rate, anvil_remote->ntls, anvil_remote->ident); +} + +/* anvil_remote_newtls_stat - report newtls stats */ + +static void anvil_remote_newtls_stat(VSTREAM *client_stream, const char *ident) +{ + ANVIL_REMOTE *anvil_remote; + int rate; + + /* + * Be prepared for "postfix reload" after "connect". + */ + if ((anvil_remote = + (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) { + rate = 0; } + + /* + * Do not report stale information. + */ + else { + if (anvil_remote->start != 0 + && anvil_remote->start + var_anvil_time_unit < event_time()) + ANVIL_REMOTE_RSET_RATE(anvil_remote, 0); + rate = anvil_remote->ntls; + } + + /* + * Respond to local client. + */ + attr_print_plain(client_stream, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, + ATTR_TYPE_NUM, ANVIL_ATTR_RATE, rate, + ATTR_TYPE_END); } /* anvil_remote_disconnect - report disconnect event */ @@ -796,8 +824,19 @@ static void anvil_service_done(VSTREAM *client_stream, char *unused_service, static void anvil_service(VSTREAM *client_stream, char *unused_service, char **argv) { - VSTRING *request = vstring_alloc(10); - VSTRING *ident = vstring_alloc(10); + static VSTRING *request; + static VSTRING *ident; + static ANVIL_REQ_TABLE request_table[] = { + ANVIL_REQ_CONN, anvil_remote_connect, + ANVIL_REQ_MAIL, anvil_remote_mail, + ANVIL_REQ_RCPT, anvil_remote_rcpt, + ANVIL_REQ_NTLS, anvil_remote_newtls, + ANVIL_REQ_DISC, anvil_remote_disconnect, + ANVIL_REQ_NTLS_STAT, anvil_remote_newtls_stat, + ANVIL_REQ_LOOKUP, anvil_remote_lookup, + 0, 0, + }; + ANVIL_REQ_TABLE *rp; /* * Sanity check. This service takes no command-line arguments. @@ -805,6 +844,14 @@ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **a if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); + /* + * Initialize. + */ + if (request == 0) { + request = vstring_alloc(10); + ident = vstring_alloc(10); + } + /* * This routine runs whenever a client connects to the socket dedicated * to the client connection rate management service. All @@ -818,23 +865,18 @@ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **a ATTR_TYPE_STR, ANVIL_ATTR_REQ, request, ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident, ATTR_TYPE_END) == 2) { - if (STREQ(STR(request), ANVIL_REQ_CONN)) { - anvil_remote_connect(client_stream, STR(ident)); - } else if (STREQ(STR(request), ANVIL_REQ_MAIL)) { - anvil_remote_mail(client_stream, STR(ident)); - } else if (STREQ(STR(request), ANVIL_REQ_RCPT)) { - anvil_remote_rcpt(client_stream, STR(ident)); - } else if (STREQ(STR(request), ANVIL_REQ_NEWTLS)) { - anvil_remote_newtls(client_stream, STR(ident)); - } else if (STREQ(STR(request), ANVIL_REQ_DISC)) { - anvil_remote_disconnect(client_stream, STR(ident)); - } else if (STREQ(STR(request), ANVIL_REQ_LOOKUP)) { - anvil_remote_lookup(client_stream, STR(ident)); - } else { - msg_warn("unrecognized request: \"%s\", ignored", STR(request)); - attr_print_plain(client_stream, ATTR_FLAG_NONE, + for (rp = request_table; /* see below */ ; rp++) { + if (rp->name == 0) { + msg_warn("unrecognized request: \"%s\", ignored", STR(request)); + attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_FAIL, - ATTR_TYPE_END); + ATTR_TYPE_END); + break; + } + if (STREQ(rp->name, STR(request))) { + rp->action(client_stream, STR(ident)); + break; + } } vstream_fflush(client_stream); } else { @@ -843,8 +885,6 @@ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **a } if (msg_verbose) msg_info("--- end request ---"); - vstring_free(ident); - vstring_free(request); } /* post_jail_init - post-jail initialization */ @@ -874,39 +914,16 @@ static void post_jail_init(char *unused_name, char **unused_argv) static void anvil_status_dump(char *unused_name, char **unused_argv) { - if (max_rate > 0) { - msg_info("statistics: max connection rate %d/%ds for (%s) at %.15s", - max_rate, var_anvil_time_unit, - max_rate_user, ctime(&max_rate_time) + 4); - max_rate = 0; - } - if (max_count > 0) { - msg_info("statistics: max connection count %d for (%s) at %.15s", - max_count, max_count_user, ctime(&max_count_time) + 4); - max_count = 0; - } - if (max_mail > 0) { - msg_info("statistics: max message rate %d/%ds for (%s) at %.15s", - max_mail, var_anvil_time_unit, - max_mail_user, ctime(&max_mail_time) + 4); - max_mail = 0; - } - if (max_rcpt > 0) { - msg_info("statistics: max recipient rate %d/%ds for (%s) at %.15s", - max_rcpt, var_anvil_time_unit, - max_rcpt_user, ctime(&max_rcpt_time) + 4); - max_rcpt = 0; - } - if (max_newtls > 0) { - msg_info("statistics: max newtls rate %d/%ds for (%s) at %.15s", - max_newtls, var_anvil_time_unit, - max_newtls_user, ctime(&max_newtls_time) + 4); - max_newtls = 0; - } - if (max_cache > 0) { + ANVIL_MAX_RATE_REPORT(max_conn_rate, "connection"); + ANVIL_MAX_COUNT_REPORT(max_conn_count, "connection"); + ANVIL_MAX_RATE_REPORT(max_mail_rate, "message"); + ANVIL_MAX_RATE_REPORT(max_rcpt_rate, "recipient"); + ANVIL_MAX_RATE_REPORT(max_ntls_rate, "newtls"); + + if (max_cache_size > 0) { msg_info("statistics: max cache size %d at %.15s", - max_cache, ctime(&max_cache_time) + 4); - max_cache = 0; + max_cache_size, ctime(&max_cache_time) + 4); + max_cache_size = 0; } } diff --git a/postfix/src/global/anvil_clnt.c b/postfix/src/global/anvil_clnt.c index c54b9500b..a0117cdc7 100644 --- a/postfix/src/global/anvil_clnt.c +++ b/postfix/src/global/anvil_clnt.c @@ -37,6 +37,12 @@ /* const char *addr; /* int *newtls; /* +/* int anvil_clnt_newtls_stat(anvil_clnt, service, addr, newtls) +/* ANVIL_CLNT *anvil_clnt; +/* const char *service; +/* const char *addr; +/* int *newtls; +/* /* int anvil_clnt_disconnect(anvil_clnt, service, addr) /* ANVIL_CLNT *anvil_clnt; /* const char *service; @@ -52,29 +58,36 @@ /* int *msgs; /* int *rcpts; /* DESCRIPTION -/* anvil_clnt_create() instantiates an anvil service client endpoint. +/* anvil_clnt_create() instantiates a local anvil service +/* client endpoint. /* /* anvil_clnt_connect() informs the anvil server that a -/* client has connected, and returns the current connection -/* count and connection rate for that client. +/* remote client has connected, and returns the current +/* connection count and connection rate for that remote client. /* -/* anvil_clnt_mail() registers a MAIL FROM event and returns -/* the current MAIL FROM rate for the specified client. +/* anvil_clnt_mail() registers a MAIL FROM event and +/* returns the current MAIL FROM rate for the specified remote +/* client. /* -/* anvil_clnt_rcpt() registers a RCPT TO event and returns -/* the current RCPT TO rate for the specified client. +/* anvil_clnt_rcpt() registers a RCPT TO event and +/* returns the current RCPT TO rate for the specified remote +/* client. /* -/* anvil_clnt_newtls() registers a request to negotiate a new -/* (uncached) TLS session and returns the current request rate -/* for the specified client. +/* anvil_clnt_newtls() registers a remote client request +/* to negotiate a new (uncached) TLS session and returns the +/* current newtls request rate for the specified remote client. /* -/* anvil_clnt_disconnect() informs the anvil server that a +/* anvil_clnt_newtls_stat() returns the current newtls request +/* rate for the specified remote client. +/* +/* anvil_clnt_disconnect() informs the anvil server that a remote /* client has disconnected. /* -/* anvil_clnt_lookup() looks up the current connection -/* count and connection rate for that client. +/* anvil_clnt_lookup() returns the current count and rate +/* information for the specified client. /* -/* anvil_clnt_free() destroys an anvil service client endpoint. +/* anvil_clnt_free() destroys a local anvil service client +/* endpoint. /* /* Arguments: /* .IP anvil_clnt @@ -99,7 +112,7 @@ /* Pointer to storage for the current "new TLS session" rate /* for this remote client. /* DIAGNOSTICS -/* anvil_clnt_connect() and anvil_clnt_disconnect() return +/* The update and status query routines return /* ANVIL_STAT_OK in case of success, ANVIL_STAT_FAIL otherwise /* (either the communication with the server is broken or the /* server experienced a problem). @@ -168,7 +181,7 @@ void anvil_clnt_free(ANVIL_CLNT *anvil_clnt) int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service, const char *addr, int *count, int *rate, - int *msgs, int *rcpts) + int *msgs, int *rcpts, int *newtls) { char *ident = ANVIL_IDENT(service, addr); int status; @@ -184,7 +197,8 @@ int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service, ATTR_TYPE_NUM, ANVIL_ATTR_RATE, rate, ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, msgs, ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, rcpts, - ATTR_TYPE_END) != 5) + ATTR_TYPE_NUM, ANVIL_ATTR_NTLS, newtls, + ATTR_TYPE_END) != 6) status = ANVIL_STAT_FAIL; else if (status != ANVIL_STAT_OK) status = ANVIL_STAT_FAIL; @@ -192,7 +206,7 @@ int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service, return (status); } -/* anvil_clnt_connect - heads-up and policy query */ +/* anvil_clnt_connect - heads-up and status query */ int anvil_clnt_connect(ANVIL_CLNT *anvil_clnt, const char *service, const char *addr, int *count, int *rate) @@ -217,7 +231,7 @@ int anvil_clnt_connect(ANVIL_CLNT *anvil_clnt, const char *service, return (status); } -/* anvil_clnt_mail - heads-up and policy query */ +/* anvil_clnt_mail - heads-up and status query */ int anvil_clnt_mail(ANVIL_CLNT *anvil_clnt, const char *service, const char *addr, int *msgs) @@ -241,7 +255,7 @@ int anvil_clnt_mail(ANVIL_CLNT *anvil_clnt, const char *service, return (status); } -/* anvil_clnt_rcpt - heads-up and policy query */ +/* anvil_clnt_rcpt - heads-up and status query */ int anvil_clnt_rcpt(ANVIL_CLNT *anvil_clnt, const char *service, const char *addr, int *rcpts) @@ -265,17 +279,41 @@ int anvil_clnt_rcpt(ANVIL_CLNT *anvil_clnt, const char *service, return (status); } -/* anvil_clnt_newtls - heads-up and policy query */ +/* anvil_clnt_newtls - heads-up and status query */ -int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service, - const char *addr, int *newtls) +int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service, + const char *addr, int *newtls) { char *ident = ANVIL_IDENT(service, addr); - int status; + int status; - if (attr_clnt_request((ATTR_CLNT *)anvil_clnt, + if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, ATTR_FLAG_NONE, /* Query attributes. */ - ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_NEWTLS, + ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_NTLS, + ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident, + ATTR_TYPE_END, + ATTR_FLAG_MISSING, /* Reply attributes. */ + ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, &status, + ATTR_TYPE_NUM, ANVIL_ATTR_RATE, newtls, + ATTR_TYPE_END) != 2) + status = ANVIL_STAT_FAIL; + else if (status != ANVIL_STAT_OK) + status = ANVIL_STAT_FAIL; + myfree(ident); + return (status); +} + +/* anvil_clnt_newtls_stat - status query */ + +int anvil_clnt_newtls_stat(ANVIL_CLNT *anvil_clnt, const char *service, + const char *addr, int *newtls) +{ + char *ident = ANVIL_IDENT(service, addr); + int status; + + if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, + ATTR_FLAG_NONE, /* Query attributes. */ + ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_NTLS_STAT, ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident, ATTR_TYPE_END, ATTR_FLAG_MISSING, /* Reply attributes. */ @@ -291,13 +329,13 @@ int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service, /* anvil_clnt_disconnect - heads-up only */ -int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service, - const char *addr) +int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service, + const char *addr) { char *ident = ANVIL_IDENT(service, addr); - int status; + int status; - if (attr_clnt_request((ATTR_CLNT *)anvil_clnt, + if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, ATTR_FLAG_NONE, /* Query attributes. */ ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_DISC, ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident, @@ -326,13 +364,17 @@ int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service, static void usage(void) { - vstream_printf("usage: %s service addr | %s service addr |" - " %s service addr | %s service addr\n", - ANVIL_REQ_CONN, ANVIL_REQ_DISC, - ANVIL_REQ_MAIL, ANVIL_REQ_RCPT); + vstream_printf("usage: " + ANVIL_REQ_CONN " service addr | " + ANVIL_REQ_DISC " service addr | " + ANVIL_REQ_MAIL " service addr | " + ANVIL_REQ_RCPT " service addr | " + ANVIL_REQ_NTLS " service addr | " + ANVIL_REQ_NTLS_STAT " service addr | " + ANVIL_REQ_LOOKUP " service addr\n"); } -int main(int unused_argc, char **argv) +int main(int unused_argc, char **argv) { VSTRING *inbuf = vstring_alloc(1); char *bufp; @@ -340,10 +382,11 @@ int main(int unused_argc, char **argv) ssize_t cmd_len; char *service; char *addr; - int count; - int rate; - int msgs; - int rcpts; + int count; + int rate; + int msgs; + int rcpts; + int newtls; ANVIL_CLNT *anvil; msg_vstream_init(argv[0], VSTREAM_ERR); @@ -384,6 +427,16 @@ int main(int unused_argc, char **argv) msg_warn("error!"); else vstream_printf("rate=%d\n", rcpts); + } else if (strncmp(cmd, ANVIL_REQ_NTLS, cmd_len) == 0) { + if (anvil_clnt_newtls(anvil, service, addr, &newtls) != ANVIL_STAT_OK) + msg_warn("error!"); + else + vstream_printf("rate=%d\n", newtls); + } else if (strncmp(cmd, ANVIL_REQ_NTLS_STAT, cmd_len) == 0) { + if (anvil_clnt_newtls_stat(anvil, service, addr, &newtls) != ANVIL_STAT_OK) + msg_warn("error!"); + else + vstream_printf("rate=%d\n", newtls); } else if (strncmp(cmd, ANVIL_REQ_DISC, cmd_len) == 0) { if (anvil_clnt_disconnect(anvil, service, addr) != ANVIL_STAT_OK) msg_warn("error!"); @@ -391,11 +444,11 @@ int main(int unused_argc, char **argv) vstream_printf("OK\n"); } else if (strncmp(cmd, ANVIL_REQ_LOOKUP, cmd_len) == 0) { if (anvil_clnt_lookup(anvil, service, addr, &count, &rate, - &msgs, &rcpts) != ANVIL_STAT_OK) + &msgs, &rcpts, &newtls) != ANVIL_STAT_OK) msg_warn("error!"); else - vstream_printf("count=%d, rate=%d msgs=%d rcpts=%d\n", - count, rate, msgs, rcpts); + vstream_printf("count=%d, rate=%d msgs=%d rcpts=%d newtls=%d\n", + count, rate, msgs, rcpts, newtls); } else { vstream_printf("bad command: \"%s\"\n", cmd); usage(); diff --git a/postfix/src/global/anvil_clnt.h b/postfix/src/global/anvil_clnt.h index dbdce6313..0e48a42dd 100644 --- a/postfix/src/global/anvil_clnt.h +++ b/postfix/src/global/anvil_clnt.h @@ -32,13 +32,15 @@ #define ANVIL_REQ_DISC "disconnect" #define ANVIL_REQ_MAIL "message" #define ANVIL_REQ_RCPT "recipient" -#define ANVIL_REQ_NEWTLS "newtls" +#define ANVIL_REQ_NTLS "newtls" +#define ANVIL_REQ_NTLS_STAT "newtls_status" #define ANVIL_REQ_LOOKUP "lookup" #define ANVIL_ATTR_IDENT "ident" #define ANVIL_ATTR_COUNT "count" #define ANVIL_ATTR_RATE "rate" #define ANVIL_ATTR_MAIL "mail" #define ANVIL_ATTR_RCPT "rcpt" +#define ANVIL_ATTR_NTLS "newtls" #define ANVIL_ATTR_STATUS "status" #define ANVIL_STAT_OK 0 @@ -54,7 +56,8 @@ extern int anvil_clnt_connect(ANVIL_CLNT *, const char *, const char *, int *, i extern int anvil_clnt_mail(ANVIL_CLNT *, const char *, const char *, int *); extern int anvil_clnt_rcpt(ANVIL_CLNT *, const char *, const char *, int *); extern int anvil_clnt_newtls(ANVIL_CLNT *, const char *, const char *, int *); -extern int anvil_clnt_lookup(ANVIL_CLNT *, const char *, const char *, int *, int *, int *, int *); +extern int anvil_clnt_newtls_stat(ANVIL_CLNT *, const char *, const char *, int *); +extern int anvil_clnt_lookup(ANVIL_CLNT *, const char *, const char *, int *, int *, int *, int *, int *); extern int anvil_clnt_disconnect(ANVIL_CLNT *, const char *, const char *); extern void anvil_clnt_free(ANVIL_CLNT *); diff --git a/postfix/src/global/deliver_request.c b/postfix/src/global/deliver_request.c index 26b4cb013..14ddda513 100644 --- a/postfix/src/global/deliver_request.c +++ b/postfix/src/global/deliver_request.c @@ -355,6 +355,7 @@ static DELIVER_REQUEST *deliver_request_alloc(void) request->sasl_username = 0; request->sasl_sender = 0; request->rewrite_context = 0; + request->dsn_envid = 0; return (request); } diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 8a22a1c6d..0ca0f9a1b 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -1728,6 +1728,10 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`\ abcdefghijklmnopqrstuvwxyz{|}~" extern char *var_smtpd_exp_filter; +#define VAR_SMTPD_PEERNAME_LOOKUP "smtpd_peername_lookup" +#define DEF_SMTPD_PEERNAME_LOOKUP 1 +extern bool var_smtpd_peername_lookup; + /* * Heuristic to reject unknown local recipients at the SMTP port. */ @@ -2301,6 +2305,10 @@ extern int var_smtpd_cmail_limit; #define DEF_SMTPD_CRCPT_LIMIT 0 extern int var_smtpd_crcpt_limit; +#define VAR_SMTPD_CNTLS_LIMIT "smtpd_client_new_tls_session_rate_limit" +#define DEF_SMTPD_CNTLS_LIMIT 0 +extern int var_smtpd_cntls_limit; + #define VAR_SMTPD_HOGGERS "smtpd_client_event_limit_exceptions" #define DEF_SMTPD_HOGGERS "${smtpd_client_connection_limit_exceptions:$" VAR_MYNETWORKS "}" extern char *var_smtpd_hoggers; @@ -2380,10 +2388,6 @@ extern char *var_msg_strip_chars; #define DEF_FROZEN_DELIVERED 1 extern bool var_frozen_delivered; -#define VAR_STICKY_OWNER_ALIAS "sticky_owner_alias" -#define DEF_STICKY_OWNER_ALIAS 1 -extern bool var_sticky_owner_alias; - /* LICENSE /* .ad /* .fi diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 9e1b0d5af..ac5579892 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20050923" +#define MAIL_RELEASE_DATE "20051010" #define MAIL_VERSION_NUMBER "2.3" #ifdef SNAPSHOT diff --git a/postfix/src/global/smtp_stream.c b/postfix/src/global/smtp_stream.c index d836e6736..fc83695f5 100644 --- a/postfix/src/global/smtp_stream.c +++ b/postfix/src/global/smtp_stream.c @@ -95,6 +95,9 @@ /* An I/O error happened, or the peer has disconnected unexpectedly. /* .IP SMTP_ERR_TIME /* The time limit specified to smtp_timeout_setup() was exceeded. +/* .IP SMTP_ERR_PROTO +/* This error is never generated by the smtp_stream(3) module, but +/* is defined for application-specific use. /* BUGS /* The timeout deadline affects all I/O on the named stream, not /* just the I/O done on behalf of this module. diff --git a/postfix/src/global/smtp_stream.h b/postfix/src/global/smtp_stream.h index 2fc2f5f66..3ca7a0452 100644 --- a/postfix/src/global/smtp_stream.h +++ b/postfix/src/global/smtp_stream.h @@ -28,6 +28,7 @@ */ #define SMTP_ERR_EOF 1 /* unexpected client disconnect */ #define SMTP_ERR_TIME 2 /* time out */ +#define SMTP_ERR_PROTO 3 /* protocol (application) */ extern void smtp_timeout_setup(VSTREAM *, int); extern void PRINTFLIKE(2, 3) smtp_printf(VSTREAM *, const char *,...); diff --git a/postfix/src/lmtp/lmtp_chat.c b/postfix/src/lmtp/lmtp_chat.c index 608b8a8f3..621d17d89 100644 --- a/postfix/src/lmtp/lmtp_chat.c +++ b/postfix/src/lmtp/lmtp_chat.c @@ -231,7 +231,21 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state) if (*cp == ' ' || *cp == 0) break; } + + /* + * XXX Do not ignore garbage when ESMTP command pipelining is turned + * on. After sending ".