--- src/global/mail_params.h.orig	Thu Jul 27 09:48:44 2006
+++ src/global/mail_params.h	Thu Aug 31 10:47:35 2006
@@ -1924,6 +1924,12 @@
 
 #define REJECT_UNAUTH_PIPE	"reject_unauth_pipelining"
 
+#define CONN_COUNT_LIMIT	"client_connection_count_limit"
+#define CONN_RATE_LIMIT		"client_connection_rate_limit"
+#define MAIL_RATE_LIMIT		"client_message_rate_limit"
+#define RCPT_RATE_LIMIT		"client_recipient_rate_limit"
+/*#define NTLS_RATE_LIMIT		"client_new_tls_session_rate_limit"*/
+
 #define VAR_SMTPD_NULL_KEY	"smtpd_null_access_lookup_key"
 #define DEF_SMTPD_NULL_KEY	"<>"
 extern char *var_smtpd_null_key;
--- src/smtpd/smtpd.c.orig	Tue Aug 22 22:53:52 2006
+++ src/smtpd/smtpd.c	Thu Aug 31 10:55:26 2006
@@ -1876,7 +1876,6 @@
     int     narg;
     char   *arg;
     char   *verp_delims = 0;
-    int     rate;
     int     dsn_envid = 0;
 
     state->encoding = 0;
@@ -1917,16 +1916,16 @@
     if (SMTPD_STAND_ALONE(state) == 0
 	&& !xclient_allowed
 	&& anvil_clnt
-	&& var_smtpd_cmail_limit > 0
 	&& !namadr_list_match(hogger_list, state->name, state->addr)
 	&& anvil_clnt_mail(anvil_clnt, state->service, state->addr,
-			   &rate) == ANVIL_STAT_OK
-	&& rate > var_smtpd_cmail_limit) {
+			   &state->mail_rate) == ANVIL_STAT_OK
+	&& var_smtpd_cmail_limit > 0
+	&& state->mail_rate > var_smtpd_cmail_limit) {
 	state->error_mask |= MAIL_ERROR_POLICY;
 	smtpd_chat_reply(state, "450 4.7.1 Error: too much mail from %s",
 			 state->addr);
 	msg_warn("Message delivery request rate limit exceeded: %d from %s for service %s",
-		 rate, state->namaddr, state->service);
+		 state->mail_rate, state->namaddr, state->service);
 	return (-1);
     }
     if (argv[2].tokval == SMTPD_TOK_ERROR) {
@@ -2168,7 +2167,6 @@
     const char *err;
     int     narg;
     char   *arg;
-    int     rate;
     const char *dsn_orcpt_addr = 0;
     ssize_t dsn_orcpt_addr_len = 0;
     const char *dsn_orcpt_type = 0;
@@ -2204,14 +2202,14 @@
     if (SMTPD_STAND_ALONE(state) == 0
 	&& !xclient_allowed
 	&& anvil_clnt
-	&& var_smtpd_crcpt_limit > 0
 	&& !namadr_list_match(hogger_list, state->name, state->addr)
 	&& anvil_clnt_rcpt(anvil_clnt, state->service, state->addr,
-			   &rate) == ANVIL_STAT_OK
-	&& rate > var_smtpd_crcpt_limit) {
+			   &state->rcpt_rate) == ANVIL_STAT_OK
+	&& var_smtpd_crcpt_limit > 0
+	&& state->rcpt_rate > var_smtpd_crcpt_limit) {
 	state->error_mask |= MAIL_ERROR_POLICY;
 	msg_warn("Recipient address rate limit exceeded: %d from %s for service %s",
-		 rate, state->namaddr, state->service);
+		 state->rcpt_rate, state->namaddr, state->service);
 	smtpd_chat_reply(state, "450 4.7.1 Error: too many recipients from %s",
 			 state->addr);
 	return (-1);
@@ -3563,11 +3561,11 @@
 	&& anvil_clnt
 	&& !namadr_list_match(hogger_list, state->name, state->addr)
 	&& anvil_clnt_newtls(anvil_clnt, state->service, state->addr,
-			     &rate) == ANVIL_STAT_OK
-	&& rate > var_smtpd_cntls_limit) {
+			     &state->newtls_rate) == ANVIL_STAT_OK
+	&& state->newtls_rate > var_smtpd_cntls_limit) {
 	state->error_mask |= MAIL_ERROR_POLICY;
 	msg_warn("New TLS session rate limit exceeded: %d from %s for service %s",
-		 rate, state->namaddr, state->service);
+		 state->newtls_rate, state->namaddr, state->service);
 	smtpd_chat_reply(state,
 		    "421 4.7.0 %s Error: too many new TLS sessions from %s",
 			 var_myhostname, state->namaddr);
@@ -3650,8 +3648,8 @@
 	&& anvil_clnt
 	&& !namadr_list_match(hogger_list, state->name, state->addr)
 	&& anvil_clnt_newtls_stat(anvil_clnt, state->service, state->addr,
-				  &rate) == ANVIL_STAT_OK
-	&& rate > var_smtpd_cntls_limit) {
+				  &state->newtls_rate) == ANVIL_STAT_OK
+	&& state->newtls_rate > var_smtpd_cntls_limit) {
 	state->error_mask |= MAIL_ERROR_POLICY;
 	msg_warn("Refusing STARTTLS request from %s for service %s",
 		 state->namaddr, state->service);
@@ -3749,7 +3747,6 @@
     int     argc;
     SMTPD_TOKEN *argv;
     SMTPD_CMD *cmdp;
-    int     tls_rate;
     const char *ehlo_words;
     const char *err;
     int     status;
@@ -3824,8 +3821,9 @@
 		&& anvil_clnt
 		&& !namadr_list_match(hogger_list, state->name, state->addr)
 		&& anvil_clnt_newtls_stat(anvil_clnt, state->service,
-				    state->addr, &tls_rate) == ANVIL_STAT_OK
-		&& tls_rate > var_smtpd_cntls_limit) {
+					  state->addr, &state->newtls_rate)
+		== ANVIL_STAT_OK
+		&& state->newtls_rate > var_smtpd_cntls_limit) {
 		state->error_mask |= MAIL_ERROR_POLICY;
 		msg_warn("Refusing TLS service request from %s for service %s",
 			 state->namaddr, state->service);
@@ -4121,6 +4119,15 @@
 	namadr_list_match(xforward_hosts, state.name, state.addr);
 
     /*
+     * rate control.
+     */
+    state.conn_count = 0;
+    state.conn_rate = 0;
+    state.mail_rate = 0;
+    state.rcpt_rate = 0;
+    state.newtls_rate = 0;
+
+    /*
      * See if we need to turn on verbose logging for this client.
      */
     debug_peer_check(state.name, state.addr);
@@ -4375,10 +4382,7 @@
     /*
      * Connection rate management.
      */
-    if (var_smtpd_crate_limit || var_smtpd_cconn_limit
-	|| var_smtpd_cmail_limit || var_smtpd_crcpt_limit
-	|| var_smtpd_cntls_limit)
-	anvil_clnt = anvil_clnt_create();
+    anvil_clnt = anvil_clnt_create();
 }
 
 /* main - the main program */
--- src/smtpd/smtpd.h.orig	Sat Jul 22 09:47:28 2006
+++ src/smtpd/smtpd.h	Thu Aug 31 10:29:27 2006
@@ -82,6 +82,9 @@
     int     reverse_name_status;	/* 2=ok 4=soft 5=hard */
     int     conn_count;			/* connections from this client */
     int     conn_rate;			/* connection rate for this client */
+    int     mail_rate;
+    int     rcpt_rate;
+    int     newtls_rate;
     int     error_count;		/* reset after DOT */
     int     error_mask;			/* client errors */
     int     notify_mask;		/* what to report to postmaster */
--- src/smtpd/smtpd_check.c.orig	Sat Jul  8 05:32:43 2006
+++ src/smtpd/smtpd_check.c	Thu Aug 31 11:16:32 2006
@@ -3799,6 +3799,104 @@
 	}
 
 	/*
+	 * rate control.
+	 */
+	else if (strcasecmp(name, CONN_COUNT_LIMIT) == 0) {
+	    if (cpp[1] == 0 || alldig(cpp[1]) == 0) {
+		msg_warn("restriction %s must be followed by number", CONN_COUNT_LIMIT);
+		longjmp(smtpd_check_buf,
+			smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
+					   451, "4.3.5",
+					   "Server configuration error"));
+	    } else {
+		int limit;
+		limit = atoi(*++cpp);
+		if (limit > 0 && state->conn_count > limit) {
+		    msg_warn("Connection concurrency limit exceeded: %d from %s for service %s",
+			     state->conn_count, state->namaddr, state->service);
+		    if (strcmp(state->where, SMTPD_AFTER_CONNECT)) {
+			status = smtpd_check_reject(state, MAIL_ERROR_POLICY,
+						    450, "4.7.1",
+						    "Error: too many connections from %s",
+						    state->addr);
+		    } else {
+			status = smtpd_check_reject(state, MAIL_ERROR_POLICY,
+						    421, "4.7.1",
+						    "%s Error: too many connections from %s",
+						    var_myhostname, state->addr);
+		    }
+		}
+	    }
+	}
+	else if (strcasecmp(name, CONN_RATE_LIMIT) == 0) {
+	    if (cpp[1] == 0 || alldig(cpp[1]) == 0) {
+		msg_warn("restriction %s must be followed by number", CONN_RATE_LIMIT);
+		longjmp(smtpd_check_buf,
+			smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
+					   451, "4.3.5",
+					   "Server configuration error"));
+	    } else {
+		int limit;
+		limit = atoi(*++cpp);
+		if (limit > 0 && state->conn_rate > limit) {
+		    msg_warn("Connection rate limit exceeded: %d from %s for service %s",
+			     state->conn_rate, state->namaddr, state->service);
+		    if (strcmp(state->where, SMTPD_AFTER_CONNECT)) {
+			status = smtpd_check_reject(state, MAIL_ERROR_POLICY,
+						    450, "4.7.1",
+						    "Error: too many connections from %s",
+						    state->addr);
+		    } else {
+			status = smtpd_check_reject(state, MAIL_ERROR_POLICY,
+						    421, "4.7.1",
+						    "%s Error: too many connections from %s",
+						    var_myhostname, state->addr);
+		    }
+		}
+	    }
+	}
+	else if (strcasecmp(name, MAIL_RATE_LIMIT) == 0) {
+	    if (cpp[1] == 0 || alldig(cpp[1]) == 0) {
+		msg_warn("restriction %s must be followed by number", MAIL_RATE_LIMIT);
+		longjmp(smtpd_check_buf,
+			smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
+					   451, "4.3.5",
+					   "Server configuration error"));
+	    } else {
+		int limit;
+		limit = atoi(*++cpp);
+		if (limit > 0 && state->mail_rate > limit) {
+		    msg_warn("Message Delivery request rate limit exceeded: %d from %s for service %s",
+			     state->mail_rate, state->namaddr, state->service);
+		    status = smtpd_check_reject(state, MAIL_ERROR_POLICY,
+						450, "4.7.1",
+						"Error: too much mail from %s",
+						state->addr);
+		}
+	    }
+	}
+	else if (strcasecmp(name, RCPT_RATE_LIMIT) == 0) {
+	    if (cpp[1] == 0 || alldig(cpp[1]) == 0) {
+		msg_warn("restriction %s must be followed by number", RCPT_RATE_LIMIT);
+		longjmp(smtpd_check_buf,
+			smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
+					   451, "4.3.5",
+					   "Server configuration error"));
+	    } else {
+		int limit;
+		limit = atoi(*++cpp);
+		if (limit > 0 && state->rcpt_rate > limit) {
+		    msg_warn("Recipient address rate limit exceeded: %d from %s for service %s",
+			     state->rcpt_rate, state->namaddr, state->service);
+		    status = smtpd_check_reject(state, MAIL_ERROR_POLICY,
+						450, "4.7.1",
+						"Error: too many recipients from %s",
+						state->addr);
+		}
+	    }
+	}
+
+	/*
 	 * ETRN domain name restrictions.
 	 */
 	else if (is_map_command(state, name, CHECK_ETRN_ACL, &cpp)) {
