fanf2 2005/05/23 17:58:56 BST
Modified files:
exim-doc/doc-txt ChangeLog NewStuff
exim-src/OS Makefile-AIX Makefile-BSDI
Makefile-CYGWIN Makefile-DGUX
Makefile-FreeBSD Makefile-GNU
Makefile-GNUkFreeBSD Makefile-GNUkNetBSD
Makefile-IRIX Makefile-IRIX6
Makefile-IRIX632 Makefile-IRIX65
Makefile-Linux Makefile-NetBSD
Makefile-NetBSD-a.out Makefile-OSF1
Makefile-OpenUNIX Makefile-QNX
Makefile-SCO Makefile-SCO_SV
Makefile-SunOS5 Makefile-SunOS5-hal
Makefile-UNIX_SV Makefile-USG
Makefile-Unixware7 Makefile-mips
exim-src/src acl.c dbstuff.h exim.h exim_dbutil.c
expand.c globals.c globals.h smtp_in.c
string.c
Log:
Add the ratelimit ACL condition. This is mostly reasonably self-contained
except that it requires changes to most Makefiles in order to bring in the
maths library for the exp() function.
Revision Changes Path
1.141 +2 -0 exim/exim-doc/doc-txt/ChangeLog
1.45 +114 -0 exim/exim-doc/doc-txt/NewStuff
1.3 +1 -1 exim/exim-src/OS/Makefile-AIX
1.2 +1 -1 exim/exim-src/OS/Makefile-BSDI
1.3 +1 -1 exim/exim-src/OS/Makefile-CYGWIN
1.2 +1 -1 exim/exim-src/OS/Makefile-DGUX
1.2 +1 -1 exim/exim-src/OS/Makefile-FreeBSD
1.4 +1 -1 exim/exim-src/OS/Makefile-GNU
1.2 +1 -1 exim/exim-src/OS/Makefile-GNUkFreeBSD
1.2 +1 -1 exim/exim-src/OS/Makefile-GNUkNetBSD
1.2 +1 -1 exim/exim-src/OS/Makefile-IRIX
1.2 +1 -1 exim/exim-src/OS/Makefile-IRIX6
1.2 +1 -1 exim/exim-src/OS/Makefile-IRIX632
1.2 +1 -1 exim/exim-src/OS/Makefile-IRIX65
1.2 +1 -1 exim/exim-src/OS/Makefile-Linux
1.2 +1 -1 exim/exim-src/OS/Makefile-NetBSD
1.2 +1 -1 exim/exim-src/OS/Makefile-NetBSD-a.out
1.2 +1 -1 exim/exim-src/OS/Makefile-OSF1
1.2 +1 -1 exim/exim-src/OS/Makefile-OpenUNIX
1.2 +1 -1 exim/exim-src/OS/Makefile-QNX
1.2 +1 -1 exim/exim-src/OS/Makefile-SCO
1.2 +1 -1 exim/exim-src/OS/Makefile-SCO_SV
1.2 +1 -1 exim/exim-src/OS/Makefile-SunOS5
1.2 +1 -1 exim/exim-src/OS/Makefile-SunOS5-hal
1.2 +1 -1 exim/exim-src/OS/Makefile-UNIX_SV
1.2 +1 -1 exim/exim-src/OS/Makefile-USG
1.2 +1 -1 exim/exim-src/OS/Makefile-Unixware7
1.2 +1 -1 exim/exim-src/OS/Makefile-mips
1.34 +287 -0 exim/exim-src/src/acl.c
1.3 +13 -2 exim/exim-src/src/dbstuff.h
1.14 +1 -0 exim/exim-src/src/exim.h
1.4 +48 -5 exim/exim-src/src/exim_dbutil.c
1.22 +3 -0 exim/exim-src/src/expand.c
1.26 +5 -0 exim/exim-src/src/globals.c
1.18 +5 -0 exim/exim-src/src/globals.h
1.19 +4 -0 exim/exim-src/src/smtp_in.c
1.3 +5 -3 exim/exim-src/src/string.c
Index: ChangeLog
===================================================================
RCS file: /home/cvs/exim/exim-doc/doc-txt/ChangeLog,v
retrieving revision 1.140
retrieving revision 1.141
diff -u -u -r1.140 -r1.141
--- ChangeLog 23 May 2005 15:28:37 -0000 1.140
+++ ChangeLog 23 May 2005 16:58:55 -0000 1.141
@@ -1,4 +1,4 @@
-$Cambridge: exim/exim-doc/doc-txt/ChangeLog,v 1.140 2005/05/23 15:28:37 fanf2 Exp $
+$Cambridge: exim/exim-doc/doc-txt/ChangeLog,v 1.141 2005/05/23 16:58:55 fanf2 Exp $
Change log file for Exim from version 4.21
-------------------------------------------
@@ -34,6 +34,8 @@
in the header line.
TF/03 Added the control = fakedefer ACL modifier.
+
+TF/04 Added the ratelimit ACL condition. See NewStuff for details.
Exim version 4.51
Index: NewStuff
===================================================================
RCS file: /home/cvs/exim/exim-doc/doc-txt/NewStuff,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -u -r1.44 -r1.45
--- NewStuff 23 May 2005 15:44:06 -0000 1.44
+++ NewStuff 23 May 2005 16:58:55 -0000 1.45
@@ -1,4 +1,4 @@
-$Cambridge: exim/exim-doc/doc-txt/NewStuff,v 1.44 2005/05/23 15:44:06 fanf2 Exp $
+$Cambridge: exim/exim-doc/doc-txt/NewStuff,v 1.45 2005/05/23 16:58:55 fanf2 Exp $
New Features in Exim
--------------------
@@ -120,6 +120,120 @@
You must take care when using fakedefer because it will cause messages
to be duplicated when the sender retries. Therefore you should not use
fakedefer if the message will be delivered normally.
+
+TF/04 There is a new ratelimit ACL condition which can be used to measure
+ and control the rate at which clients can send email. This is more
+ powerful than the existing smtp_ratelimit_* options, because those
+ options only control the rate of commands in a single SMTP session,
+ whereas the new ratelimit condition works across all connections
+ (concurrent and sequential) to the same host.
+
+ The syntax of the ratelimit condition is:
+
+ ratelimit = <m> / <p> / <options> / <key>
+
+ If the average client sending rate is greater than m messages per time
+ period p then the condition is true, otherwise it is false.
+
+ The parameter p is the smoothing time constant, in the form of an Exim
+ time interval e.g. 8h for eight hours. A larger time constant means it
+ takes Exim longer to forget a client's past behaviour. The parameter m is
+ the maximum number of messages that a client can send in a fast burst. By
+ increasing both m and p but keeping m/p constant, you can allow a client
+ to send more messages in a burst without changing its overall sending
+ rate limit. Conversely, if m and p are both small then messages must be
+ sent at an even rate.
+
+ The key is used to look up the data used to calcluate the client's
+ average sending rate. This data is stored in a database maintained by
+ Exim in its spool directory alongside the retry database etc. For
+ example, you can limit the sending rate of each authenticated user,
+ independent of the computer they are sending from, by setting the key
+ to $authenticated_id. The default key is $sender_host_address.
+
+ Each ratelimit condition can have up to two options. The first option
+ specifies what Exim measures the rate of, and the second specifies how
+ Exim handles excessively fast clients.
+
+ The per_mail option means that it measures the client's rate of sending
+ messages. This is the default if none of the per_* options is specified.
+
+ The per_conn option means that it measures the client's connection rate.
+
+ The per_byte option limits the sender's email bandwidth. Note that it
+ is best to use this option in the DATA ACL; if it is used in an earlier
+ ACL it relies on the SIZE parameter on the MAIL command, which may be
+ inaccurate or completely missing. You can follow the limit m in the
+ configuration with K, M, or G to specify limits in kilobytes,
+ megabytes, or gigabytes respectively.
+
+ The per_cmd option means that Exim recomputes the rate every time the
+ condition is processed, which can be used to limit the SMTP command rate.
+ The alias per_rcpt is provided for use in the RCPT ACL instead of per_cmd
+ to make it clear that the effect is to limit the rate at which recipients
+ are accepted. Note that in this case the rate limiting engine will see a
+ message with many recipients as a large high-speed burst.
+
+ If a client's average rate is greater than the maximum, the rate
+ limiting engine can react in two possible ways, depending on the
+ presence of the strict or leaky options. This is independent of the
+ other counter-measures (e.g. rejecting the message) that may be
+ specified by the rest of the ACL. The default mode is leaky, which
+ avoids a sender's over-aggressive retry rate preventing it from getting
+ any email through.
+
+ The strict option means that the client's recorded rate is always
+ updated. The effect of this is that Exim measures the client's average
+ rate of attempts to send email, which can be much higher than the
+ maximum. If the client is over the limit it will be subjected to
+ counter-measures until it slows down below the maximum rate.
+
+ The leaky option means that the client's recorded rate is not updated
+ if it is above the limit. The effect of this is that Exim measures the
+ client's average rate of successfully sent email, which cannot be
+ greater than the maximum. If the client is over the limit it will
+ suffer some counter-measures, but it will still be able to send email
+ at the configured maximum rate, whatever the rate of its attempts.
+
+ As a side-effect, the ratelimit condition will set the expansion
+ variables $sender_rate containing the client's computed rate,
+ $sender_rate_limit containing the configured value of m, and
+ $sender_rate_period containing the configured value of p.
+
+ Exim's other ACL facilities are used to define what counter-measures
+ are taken when the rate limit is exceeded. This might be anything from
+ logging a warning (e.g. while measuring existing sending rates in order
+ to define our policy), through time delays to slow down fast senders,
+ up to rejecting the message. For example,
+
+ # Log all senders' rates
+ warn
+ ratelimit = 0 / 1h / strict
+ log_message = \
+ Sender rate $sender_rate > $sender_rate_limit / $sender_rate_period
+
+ # Slow down fast senders
+ warn
+ ratelimit = 100 / 1h / per_rcpt / strict
+ delay = ${eval: 10 * ($sender_rate - $sender_rate_limit) }
+
+ # Keep authenticated users under control
+ deny
+ ratelimit = 100 / 1d / strict / $authenticated_id
+
+ # System-wide rate limit
+ defer
+ message = Sorry, too busy. Try again later.
+ ratelimit = 10 / 1s / $primary_hostname
+
+ # Restrict incoming rate from each host, with a default rate limit
+ # set using a macro and special cases looked up in a table.
+ defer
+ message = Sender rate $sender_rate exceeds \
+ $sender_rate_limit messages per $sender_rate_period
+ ratelimit = ${lookup {$sender_host_address} \
+ cdb {DB/ratelimits.cdb} \
+ {$value} {RATELIMIT} }
Version 4.51
Index: Makefile-AIX
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-AIX,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -u -r1.2 -r1.3
--- Makefile-AIX 16 Feb 2005 16:40:22 -0000 1.2
+++ Makefile-AIX 23 May 2005 16:58:55 -0000 1.3
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-AIX,v 1.2 2005/02/16 16:40:22 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-AIX,v 1.3 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for AIX
# Written by Nick Waterman (nick@???)
@@ -25,6 +25,6 @@
# Needed for vfork() and vfork() only?
-LIBS = -lbsd
+LIBS = -lbsd -lm
# End
Index: Makefile-BSDI
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-BSDI,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-BSDI 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-BSDI 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-BSDI,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-BSDI,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for BSDI. Its antique link editor
# cannot handle the TextPop overriding.
@@ -13,7 +13,7 @@
XLFLAGS=-L$(X11)/lib
X11_LD_LIB=$(X11)/lib
-LIBS_EXIMON=-lSM -lICE -lipc
+LIBS_EXIMON=-lSM -lICE -lipc -lm
EXIMON_TEXTPOP=
EXIWHAT_PS_ARG=-ax
Index: Makefile-CYGWIN
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-CYGWIN,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -u -r1.2 -r1.3
--- Makefile-CYGWIN 10 Nov 2004 10:36:48 -0000 1.2
+++ Makefile-CYGWIN 23 May 2005 16:58:55 -0000 1.3
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-CYGWIN,v 1.2 2004/11/10 10:36:48 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-CYGWIN,v 1.3 2005/05/23 16:58:55 fanf2 Exp $
# OS-specific file for Cygwin.
@@ -6,7 +6,7 @@
HAVE_ICONV = yes
CFLAGS= -g -Wall -O2
-LIBS= -lcrypt -lresolv
+LIBS= -lcrypt -lresolv -lm
LIBS_EXIM= -liconv
EXIWHAT_PS_ARG=-as
EXIWHAT_KILL_SIGNAL=-USR1
Index: Makefile-DGUX
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-DGUX,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-DGUX 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-DGUX 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-DGUX,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-DGUX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for DGUX
#
@@ -25,7 +25,7 @@
CFLAGS=-O2
RANLIB=@true
-LIBS=-lsocket -lnsl
+LIBS=-lsocket -lnsl -lm
LIBRESOLV=-lresolv
DBMLIB=-ldbm
Index: Makefile-FreeBSD
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-FreeBSD,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-FreeBSD 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-FreeBSD 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-FreeBSD,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-FreeBSD,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for FreeBSD
# There's no setting of CFLAGS here, to allow the system default
@@ -11,7 +11,7 @@
HAVE_SA_LEN=YES
# crypt() is in a separate library
-LIBS=-lcrypt
+LIBS=-lcrypt -lm
# FreeBSD always ships with Berkeley DB
USE_DB=yes
Index: Makefile-GNU
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-GNU,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -u -r1.3 -r1.4
--- Makefile-GNU 12 Jan 2005 12:25:56 -0000 1.3
+++ Makefile-GNU 23 May 2005 16:58:55 -0000 1.4
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-GNU,v 1.3 2005/01/12 12:25:56 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-GNU,v 1.4 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for GNU and variants.
@@ -13,7 +13,7 @@
DBMLIB = -ldb
USE_DB = yes
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
LIBRESOLV = -lresolv
X11=/usr/X11R6
Index: Makefile-GNUkFreeBSD
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-GNUkFreeBSD,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-GNUkFreeBSD 4 Jan 2005 10:25:58 -0000 1.1
+++ Makefile-GNUkFreeBSD 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-GNUkFreeBSD,v 1.1 2005/01/04 10:25:58 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-GNUkFreeBSD,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for GNU and variants.
@@ -13,7 +13,7 @@
DBMLIB = -ldb
USE_DB = yes
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
LIBRESOLV = -lresolv
X11=/usr/X11R6
Index: Makefile-GNUkNetBSD
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-GNUkNetBSD,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-GNUkNetBSD 4 Jan 2005 10:25:58 -0000 1.1
+++ Makefile-GNUkNetBSD 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-GNUkNetBSD,v 1.1 2005/01/04 10:25:58 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-GNUkNetBSD,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for GNU and variants.
@@ -13,7 +13,7 @@
DBMLIB = -ldb
USE_DB = yes
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
LIBRESOLV = -lresolv
X11=/usr/X11R6
Index: Makefile-IRIX
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-IRIX,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-IRIX 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-IRIX 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-IRIX,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-IRIX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for IRIX
@@ -6,7 +6,7 @@
BASENAME_COMMAND=/sbin/basename
HOSTNAME_COMMAND=/usr/bsd/hostname
CFLAGS=-OPT:Olimit=1500
-LIBS=-lmld
+LIBS=-lmld -lm
XINCLUDE=-I/usr/include/X11
vfork=fork
RANLIB=@true
Index: Makefile-IRIX6
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-IRIX6,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-IRIX6 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-IRIX6 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-IRIX6,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-IRIX6,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for IRIX6 on 64-bit systems
@@ -6,7 +6,7 @@
HOSTNAME_COMMAND=/usr/bsd/hostname
CFLAGS=-O2 -n32 -OPT:Olimit=4000
LFLAGS=-n32
-LIBS=-lelf
+LIBS=-lelf -lm
XINCLUDE=-I/usr/include/X11
XLFLAGS=
vfork=fork
Index: Makefile-IRIX632
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-IRIX632,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-IRIX632 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-IRIX632 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-IRIX632,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-IRIX632,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for IRIX 6 on 32-bit systems.
# There seems to be some variation. The commented settings show
@@ -10,7 +10,7 @@
CFLAGS=-32
LFLAGS=-32
#LIBS=-lmld
-LIBS=-lelf
+LIBS=-lelf -lm
XINCLUDE=-I/usr/include/X11
vfork=fork
RANLIB=@true
Index: Makefile-IRIX65
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-IRIX65,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-IRIX65 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-IRIX65 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-IRIX65,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-IRIX65,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for IRIX 6.5
@@ -10,7 +10,7 @@
LFLAGS=-Wl,-LD_MSG:off=85
LFLAGS=
# nlist has moved from libmld to libelf
-LIBS=-lelf
+LIBS=-lelf -lm
XINCLUDE=-I/usr/include/X11
vfork=fork
RANLIB=@true
Index: Makefile-Linux
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-Linux,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-Linux 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-Linux 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-Linux,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-Linux,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for Linux. This is for modern Linuxes,
# which use libc6.
@@ -14,7 +14,7 @@
DBMLIB = -ldb
USE_DB = yes
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
LIBRESOLV = -lresolv
X11=/usr/X11R6
Index: Makefile-NetBSD
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-NetBSD,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-NetBSD 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-NetBSD 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-NetBSD,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-NetBSD,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for NetBSD (ELF object format)
@@ -7,7 +7,7 @@
HAVE_SA_LEN=YES
HAVE_IPV6=YES
-LIBS=-lcrypt
+LIBS=-lcrypt -lm
X11=/usr/X11R6
XINCLUDE=-I$(X11)/include
Index: Makefile-NetBSD-a.out
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-NetBSD-a.out,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-NetBSD-a.out 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-NetBSD-a.out 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-NetBSD-a.out,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-NetBSD-a.out,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for NetBSD (a.out/COFF object format)
@@ -7,7 +7,7 @@
HAVE_SA_LEN=YES
HAVE_IPV6=YES
-LIBS=-lcrypt
+LIBS=-lcrypt -lm
X11=/usr/X11R6
XINCLUDE=-I$(X11)/include
Index: Makefile-OSF1
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-OSF1,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-OSF1 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-OSF1 23 May 2005 16:58:55 -0000 1.2
@@ -1,9 +1,9 @@
-# $Cambridge: exim/exim-src/OS/Makefile-OSF1,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-OSF1,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for OSF1
CFLAGS=-O
-LIBS=-liconv
+LIBS=-liconv -lm
HAVE_CRYPT16=yes
HAVE_ICONV=yes
HOSTNAME_COMMAND=/usr/bin/hostname
Index: Makefile-OpenUNIX
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-OpenUNIX,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-OpenUNIX 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-OpenUNIX 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-OpenUNIX,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-OpenUNIX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for OpenUNIX
@@ -6,7 +6,7 @@
CFLAGS=-O -I/usr/local/include
LFLAGS=-L/usr/local/lib
-LIBS=-lsocket -lnsl -lelf -lgen -lresolv
+LIBS=-lsocket -lnsl -lelf -lgen -lresolv -lm
EXTRALIBS_EXIMON=-lICE -lSM
RANLIB=@true
Index: Makefile-QNX
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-QNX,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-QNX 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-QNX 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-QNX,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-QNX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific makefile for QNX
@@ -21,7 +21,7 @@
RANLIB=@true
DBMLIB=-ldb
USE_DB=yes
-LIBS=-lsocket
+LIBS=-lsocket -lm
X11=/usr/X11R6
XINCLUDE=-I$(X11)/include
Index: Makefile-SCO
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-SCO,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-SCO 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-SCO 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-SCO,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-SCO,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for SCO
@@ -10,7 +10,7 @@
RANLIB=@true
DBMLIB=-lndbm
ERRNO_QUOTA=0
-LIBS=-lsocket
+LIBS=-lsocket -lm
HAVE_ICONV=yes
X11=/usr/lib/X11
Index: Makefile-SCO_SV
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-SCO_SV,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-SCO_SV 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-SCO_SV 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-SCO_SV,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-SCO_SV,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for SCO_SV release 5 (tested on 5.0.5 & 5.0.5)
# (see the UNIX_SV files for SCO 4.2)
@@ -11,7 +11,7 @@
CFLAGS=-melf -O3 -m486
LFLAGS=-L/lib -L/usr/lib -L/usr/local/lib
-LIBS=-ltinfo -lm -lsocket
+LIBS=-ltinfo -lsocket -lm
HAVE_ICONV=yes
Index: Makefile-SunOS5
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-SunOS5,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-SunOS5 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-SunOS5 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-SunOS5,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-SunOS5,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for SunOS5
@@ -10,7 +10,7 @@
HOSTNAME_COMMAND=look_for_it
RANLIB=@true
-LIBS=-lsocket -lnsl -lkstat
+LIBS=-lsocket -lnsl -lkstat -lm
LIBRESOLV=-lresolv
EXIWHAT_MULTIKILL_CMD=pkill
Index: Makefile-SunOS5-hal
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-SunOS5-hal,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-SunOS5-hal 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-SunOS5-hal 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-SunOS5-hal,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-SunOS5-hal,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for SunOS5 on a HAL
@@ -11,7 +11,7 @@
LIBIDENTCFLAGS="-KV7 -O -DHAVE_ANSIHEADERS"
LIBIDENTNAME=sunos5
RANLIB=@true
-LIBS=-lsocket -lnsl -lkstat
+LIBS=-lsocket -lnsl -lkstat -lm
LIBRESOLV=-lresolv
X11=/usr/X11R6
XINCLUDE=-I$(X11)/include
Index: Makefile-UNIX_SV
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-UNIX_SV,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-UNIX_SV 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-UNIX_SV 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-UNIX_SV,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-UNIX_SV,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for SCO SVR4.2MP (and maybe Unixware)
#
@@ -17,7 +17,7 @@
RANLIB=@true
DBMLIB=-lgdbm -L/usr/local/lib
ERRNO_QUOTA=0
-LIBS=-lsocket -lelf -lgen -lnsl -lresolv
+LIBS=-lsocket -lelf -lgen -lnsl -lresolv -lm
X11=/usr/lib/X11
XINCLUDE=-I/usr/include/X11
Index: Makefile-USG
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-USG,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-USG 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-USG 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-USG,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-USG,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for Unixware 2.x
#
@@ -25,7 +25,7 @@
DBMLIB=-ldb -L/usr/local/lib
USE_DB=YES
ERRNO_QUOTA=0
-LIBS=-lsocket -lelf -lgen -lnsl -lresolv
+LIBS=-lsocket -lelf -lgen -lnsl -lresolv -lm
X11=/usr/lib/X11
XINCLUDE=-I/usr/include/X11
Index: Makefile-Unixware7
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-Unixware7,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-Unixware7 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-Unixware7 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-Unixware7,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-Unixware7,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for Unixware7
# Based on information from James FitzGibbon <james@???>
@@ -17,7 +17,7 @@
HAVE_ICONV=yes
-LIBS=-lsocket -lnsl -lelf -lgen -lresolv
+LIBS=-lsocket -lnsl -lelf -lgen -lresolv -lm
# Removed on the advice of Larry Rosenman
# EXTRALIBS=-lwrap
Index: Makefile-mips
===================================================================
RCS file: /home/cvs/exim/exim-src/OS/Makefile-mips,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- Makefile-mips 6 Oct 2004 15:07:39 -0000 1.1
+++ Makefile-mips 23 May 2005 16:58:55 -0000 1.2
@@ -1,4 +1,4 @@
-# $Cambridge: exim/exim-src/OS/Makefile-mips,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/exim-src/OS/Makefile-mips,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
# Exim: OS-specific make file for RiscOS4bsd
@@ -6,7 +6,7 @@
EXIT_FAILURE=1
EXIT_SUCCESS=0
LIBRESOLV=-lresolv
-LIBS=-liberty
+LIBS=-liberty -lm
XINCLUDE=-I/usr/X11R6/include
CFLAGS=-O
Index: acl.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/acl.c,v
retrieving revision 1.33
retrieving revision 1.34
diff -u -u -r1.33 -r1.34
--- acl.c 23 May 2005 15:28:38 -0000 1.33
+++ acl.c 23 May 2005 16:58:56 -0000 1.34
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/acl.c,v 1.33 2005/05/23 15:28:38 fanf2 Exp $ */
+/* $Cambridge: exim/exim-src/src/acl.c,v 1.34 2005/05/23 16:58:56 fanf2 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -63,6 +63,7 @@
#ifdef WITH_CONTENT_SCAN
ACLC_MIME_REGEX,
#endif
+ ACLC_RATELIMIT,
ACLC_RECIPIENTS,
#ifdef WITH_CONTENT_SCAN
ACLC_REGEX,
@@ -110,6 +111,7 @@
#ifdef WITH_CONTENT_SCAN
US"mime_regex",
#endif
+ US"ratelimit",
US"recipients",
#ifdef WITH_CONTENT_SCAN
US"regex",
@@ -171,6 +173,7 @@
#ifdef WITH_CONTENT_SCAN
TRUE, /* mime_regex */
#endif
+ TRUE, /* ratelimit */
FALSE, /* recipients */
#ifdef WITH_CONTENT_SCAN
TRUE, /* regex */
@@ -227,6 +230,7 @@
#ifdef WITH_CONTENT_SCAN
FALSE, /* mime_regex */
#endif
+ FALSE, /* ratelimit */
FALSE, /* recipients */
#ifdef WITH_CONTENT_SCAN
FALSE, /* regex */
@@ -363,6 +367,8 @@
~(1<<ACL_WHERE_MIME), /* mime_regex */
#endif
+ 0, /* ratelimit */
+
(unsigned int)
~(1<<ACL_WHERE_RCPT), /* recipients */
@@ -1884,6 +1890,283 @@
/*************************************************
+* Handle rate limiting *
+*************************************************/
+
+/* Called by acl_check_condition() below to calculate the result
+of the ACL ratelimit condition.
+
+Note that the return value might be slightly unexpected: if the
+sender's rate is above the limit then the result is OK. This is
+similar to the dnslists condition, and is so that you can write
+ACL clauses like: defer ratelimit = 15 / 1h
+
+Arguments:
+ arg the option string for ratelimit=
+ log_msgptr for error messages
+
+Returns: OK - Sender's rate is above limit
+ FAIL - Sender's rate is below limit
+ DEFER - Problem opening ratelimit database
+ ERROR - Syntax error in options.
+*/
+
+static int
+acl_ratelimit(uschar *arg, uschar **log_msgptr)
+{
+double limit, period;
+uschar *ss, *key = arg;
+int sep = '/';
+BOOL have_key = FALSE, leaky = FALSE, strict = FALSE;
+BOOL per_byte = FALSE, per_cmd = FALSE, per_conn = FALSE, per_mail = FALSE;
+int old_pool, rc;
+tree_node **anchor, *t;
+open_db dbblock, *dbm;
+dbdata_ratelimit *dbd;
+struct timeval tv;
+
+/* Parse the first two options and record their values in expansion
+variables. These variables allow the configuration to have informative
+error messages based on rate limits obtained from a table lookup. */
+
+/* First is the maximum number of messages per period and maximum burst
+size, which must be greater than or equal to zero. Zero is useful for
+rate measurement as opposed to rate limiting. */
+
+sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0);
+if (sender_rate_limit == NULL)
+ limit = -1.0;
+else
+ {
+ limit = Ustrtod(sender_rate_limit, &ss);
+ if (tolower(*ss) == 'k') { limit *= 1024.0; ss++; }
+ else if (tolower(*ss) == 'm') { limit *= 1024.0*1024.0; ss++; }
+ else if (tolower(*ss) == 'g') { limit *= 1024.0*1024.0*1024.0; ss++; }
+ }
+if (limit < 0.0 || *ss != 0)
+ {
+ *log_msgptr = string_sprintf("syntax error in argument for "
+ "\"ratelimit\" condition: \"%s\" is not a positive number",
+ sender_rate_limit);
+ return ERROR;
+ }
+
+/* Second is the rate measurement period and exponential smoothing time
+constant. This must be strictly greater than zero, because zero leads to
+run-time division errors. */
+
+sender_rate_period = string_nextinlist(&arg, &sep, NULL, 0);
+if (sender_rate_period == NULL) period = -1.0;
+else period = readconf_readtime(sender_rate_period, 0, FALSE);
+if (period <= 0.0)
+ {
+ *log_msgptr = string_sprintf("syntax error in argument for "
+ "\"ratelimit\" condition: \"%s\" is not a time value",
+ sender_rate_period);
+ return ERROR;
+ }
+
+/* Parse the other options. Should we check if the per_* options are being
+used in ACLs where they don't make sense, e.g. per_mail in the connect ACL? */
+
+while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))
+ != NULL)
+ {
+ if (strcmpic(ss, US"leaky") == 0) leaky = TRUE;
+ else if (strcmpic(ss, US"strict") == 0) strict = TRUE;
+ else if (strcmpic(ss, US"per_byte") == 0) per_byte = TRUE;
+ else if (strcmpic(ss, US"per_cmd") == 0) per_cmd = TRUE;
+ else if (strcmpic(ss, US"per_conn") == 0) per_conn = TRUE;
+ else if (strcmpic(ss, US"per_mail") == 0) per_mail = TRUE;
+ else if (strcmpic(ss, US"per_rcpt") == 0) per_cmd = TRUE; /* alias */
+ else have_key = TRUE;
+ }
+if (leaky + strict > 1 || per_byte + per_cmd + per_conn + per_mail > 1)
+ {
+ *log_msgptr = US"conflicting options for \"ratelimit\" condition";
+ return ERROR;
+ }
+
+/* Default option values */
+if (!strict) leaky = TRUE;
+if (!per_byte && !per_cmd && !per_conn) per_mail = TRUE;
+
+/* We use the whole of the argument list as the lookup key, because it doesn't
+make sense to use the same stored data if any of the arguments are different.
+If there is no explicit key, use the sender_host_address. If there is no
+sender_host_address (e.g. -bs or acl_not_smtp) then we simply omit it. */
+
+if (!have_key && sender_host_address != NULL)
+ key = string_sprintf("%s / %s", key, sender_host_address);
+
+HDEBUG(D_acl) debug_printf("ratelimit condition limit=%.0f period=%.0f key=%s\n",
+ limit, period, key);
+
+/* If we are dealing with rate limits per connection, per message, or per byte,
+see if we have already computed the rate by looking in the relevant tree. For
+per-connection rate limiting, store tree nodes and dbdata in the permanent pool
+so that they survive across resets. */
+
+anchor = NULL;
+old_pool = store_pool;
+
+if (per_conn)
+ {
+ anchor = &ratelimiters_conn;
+ store_pool = POOL_PERM;
+ }
+if (per_mail || per_byte)
+ anchor = &ratelimiters_mail;
+
+if (anchor != NULL && (t = tree_search(*anchor, key)) != NULL)
+ {
+ dbd = t->data.ptr;
+ /* The following few lines duplicate some of the code below. */
+ if (dbd->rate > limit) rc = OK;
+ else rc = FAIL;
+ store_pool = old_pool;
+ sender_rate = string_sprintf("%.1f", dbd->rate);
+ HDEBUG(D_acl)
+ debug_printf("ratelimit found pre-computed rate %s\n", sender_rate);
+ return rc;
+ }
+
+/* We aren't using a pre-computed rate, so get a previously recorded
+rate from the database, update it, and write it back. If there's no
+previous rate for this key, create one. */
+
+dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE);
+if (dbm == NULL)
+ {
+ store_pool = old_pool;
+ sender_rate = NULL;
+ HDEBUG(D_acl) debug_printf("ratelimit database not available\n");
+ *log_msgptr = US"ratelimit database not available";
+ return DEFER;
+ }
+dbd = dbfn_read(dbm, key);
+
+gettimeofday(&tv, NULL);
+
+if (dbd == NULL)
+ {
+ HDEBUG(D_acl) debug_printf("ratelimit initializing new key's data\n");
+ dbd = store_get(sizeof(dbdata_ratelimit));
+ dbd->time_stamp = tv.tv_sec;
+ dbd->time_usec = tv.tv_usec;
+ dbd->rate = 0.0;
+ }
+else
+ {
+ /* The smoothed rate is computed using an exponentially weighted moving
+ average adjusted for variable sampling intervals. The standard EWMA for
+ a fixed sampling interval is: f'(t) = (1 - a) * f(t) + a * f'(t - 1)
+ where f() is the measured value and f'() is the smoothed value.
+
+ Old data decays out of the smoothed value exponentially, such that data n
+ samples old is multiplied by a^n. The exponential decay time constant p
+ is defined such that data p samples old is multiplied by 1/e, which means
+ that a = exp(-1/p). We can maintain the same time constant for a variable
+ sampling interval i by using a = exp(-i/p).
+
+ The rate we are measuring is messages per period, suitable for directly
+ comparing with the limit. The average rate between now and the previous
+ message is period / interval, which we feed into the EWMA as the sample.
+
+ It turns out that the number of messages required for the smoothed rate
+ to reach the limit when they are sent in a burst is equal to the limit.
+ This can be seen by analysing the value of the smoothed rate after N
+ messages sent at even intervals. Let k = (1 - a) * p/i
+
+ rate_1 = (1 - a) * p/i + a * rate_0
+ = k + a * rate_0
+ rate_2 = k + a * rate_1
+ = k + a * k + a^2 * rate_0
+ rate_3 = k + a * k + a^2 * k + a^3 * rate_0
+ rate_N = rate_0 * a^N + k * SUM(x=0..N-1)(a^x)
+ = rate_0 * a^N + k * (1 - a^N) / (1 - a)
+ = rate_0 * a^N + p/i * (1 - a^N)
+
+ When N is large, a^N -> 0 so rate_N -> p/i as desired.
+
+ rate_N = p/i + (rate_0 - p/i) * a^N
+ a^N = (rate_N - p/i) / (rate_0 - p/i)
+ N * -i/p = log((rate_N - p/i) / (rate_0 - p/i))
+ N = p/i * log((rate_0 - p/i) / (rate_N - p/i))
+
+ Numerical analysis of the above equation, setting the computed rate to
+ increase from rate_0 = 0 to rate_N = limit, shows that for large sending
+ rates, p/i, the number of messages N = limit. So limit serves as both the
+ maximum rate measured in messages per period, and the maximum number of
+ messages that can be sent in a fast burst. */
+
+ double this_time = (double)tv.tv_sec
+ + (double)tv.tv_usec / 1000000.0;
+ double prev_time = (double)dbd->time_stamp
+ + (double)dbd->time_usec / 1000000.0;
+ double interval = this_time - prev_time;
+
+ double i_over_p = interval / period;
+ double a = exp(-i_over_p);
+
+ /* We must avoid division by zero, and deal gracefully with the clock going
+ backwards. If we blunder ahead when time is in reverse then the computed
+ rate will become bogusly huge. Clamp i/p to a very small number instead. */
+
+ if (i_over_p <= 0.0) i_over_p = 1e-9;
+
+ dbd->time_stamp = tv.tv_sec;
+ dbd->time_usec = tv.tv_usec;
+
+ /* If we are measuring the rate in bytes per period, multiply the
+ measured rate by the message size. If we don't know the message size
+ then it's safe to just use a value of zero and let the recorded rate
+ decay as if nothing happened. */
+
+ if (per_byte)
+ dbd->rate = (message_size < 0 ? 0.0 : (double)message_size)
+ * (1 - a) / i_over_p + a * dbd->rate;
+ else
+ dbd->rate = (1 - a) / i_over_p + a * dbd->rate;
+ }
+
+if (dbd->rate > limit) rc = OK;
+ else rc = FAIL;
+
+/* Update the state if the rate is low or if we are being strict. If we
+are in leaky mode and the sender's rate is too high, we do not update
+the recorded rate in order to avoid an over-aggressive sender's retry
+rate preventing them from getting any email through. */
+
+if (rc == FAIL || !leaky)
+ dbfn_write(dbm, key, dbd, sizeof(dbdata_ratelimit));
+dbfn_close(dbm);
+
+/* Store the result in the tree for future reference, if necessary. */
+
+if (anchor != NULL)
+ {
+ t = store_get(sizeof(tree_node) + Ustrlen(key));
+ t->data.ptr = dbd;
+ Ustrcpy(t->name, key);
+ (void)tree_insertnode(anchor, t);
+ }
+
+/* We create the formatted version of the sender's rate very late in
+order to ensure that it is done using the correct storage pool. */
+
+store_pool = old_pool;
+sender_rate = string_sprintf("%.1f", dbd->rate);
+
+HDEBUG(D_acl)
+ debug_printf("ratelimit computed rate %s\n", sender_rate);
+
+return rc;
+}
+
+
+
+/*************************************************
* Handle conditions/modifiers on an ACL item *
*************************************************/
@@ -2414,6 +2697,10 @@
rc = mime_regex(&arg);
break;
#endif
+
+ case ACLC_RATELIMIT:
+ rc = acl_ratelimit(arg, log_msgptr);
+ break;
case ACLC_RECIPIENTS:
rc = match_address_list(addr->address, TRUE, TRUE, &arg, NULL, -1, 0,
Index: dbstuff.h
===================================================================
RCS file: /home/cvs/exim/exim-src/src/dbstuff.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -u -r1.2 -r1.3
--- dbstuff.h 4 Jan 2005 10:00:42 -0000 1.2
+++ dbstuff.h 23 May 2005 16:58:56 -0000 1.3
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/dbstuff.h,v 1.2 2005/01/04 10:00:42 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/dbstuff.h,v 1.3 2005/05/23 16:58:56 fanf2 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -363,8 +363,8 @@
/* Basic DB type */
typedef struct {
- GDBM_FILE gdbm; /* Database */
- datum lkey; /* Last key, for scans */
+ GDBM_FILE gdbm; /* Database */
+ datum lkey; /* Last key, for scans */
} EXIM_DB;
/* Cursor type, not used with gdbm: just set up a dummy */
@@ -629,6 +629,17 @@
/*************/
int count; /* Reserved for possible connection count */
} dbdata_serialize;
+
+
+/* This structure records the information required for the ratelimit
+ACL condition. */
+
+typedef struct {
+ time_t time_stamp;
+ /*************/
+ int time_usec; /* Fractional part of time, from gettimeofday() */
+ double rate; /* Smoothed sending rate at that time */
+} dbdata_ratelimit;
/* End of dbstuff.h */
Index: exim.h
===================================================================
RCS file: /home/cvs/exim/exim-src/src/exim.h,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -u -r1.13 -r1.14
--- exim.h 10 May 2005 22:39:20 -0000 1.13
+++ exim.h 23 May 2005 16:58:56 -0000 1.14
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/exim.h,v 1.13 2005/05/10 22:39:20 tom Exp $ */
+/* $Cambridge: exim/exim-src/src/exim.h,v 1.14 2005/05/23 16:58:56 fanf2 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -49,6 +49,7 @@
#include <ctype.h>
#include <locale.h>
+#include <math.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
Index: exim_dbutil.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/exim_dbutil.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -u -r1.3 -r1.4
--- exim_dbutil.c 4 Jan 2005 10:00:42 -0000 1.3
+++ exim_dbutil.c 23 May 2005 16:58:56 -0000 1.4
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/exim_dbutil.c,v 1.3 2005/01/04 10:00:42 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/exim_dbutil.c,v 1.4 2005/05/23 16:58:56 fanf2 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -63,10 +63,11 @@
/* Identifiers for the different database types. */
-#define type_retry 1
-#define type_wait 2
-#define type_misc 3
-#define type_callout 4
+#define type_retry 1
+#define type_wait 2
+#define type_misc 3
+#define type_callout 4
+#define type_ratelimit 5
@@ -113,7 +114,7 @@
usage(uschar *name, uschar *options)
{
printf("Usage: exim_%s%s <spool-directory> <database-name>\n", name, options);
-printf(" <database-name> = retry | misc | wait-<transport-name> | callout\n");
+printf(" <database-name> = retry | misc | wait-<transport-name> | callout | ratelimit\n");
exit(1);
}
@@ -135,6 +136,7 @@
if (Ustrcmp(argv[2], "misc") == 0) return type_misc;
if (Ustrncmp(argv[2], "wait-", 5) == 0) return type_wait;
if (Ustrcmp(argv[2], "callout") == 0) return type_callout;
+ if (Ustrcmp(argv[2], "ratelimit") == 0) return type_ratelimit;
}
usage(name, options);
return -1; /* Never obeyed */
@@ -536,6 +538,7 @@
dbdata_retry *retry;
dbdata_wait *wait;
dbdata_callout_cache *callout;
+ dbdata_ratelimit *ratelimit;
int count_bad = 0;
int i, length;
uschar *t;
@@ -662,6 +665,15 @@
}
break;
+
+ case type_ratelimit:
+ ratelimit = (dbdata_ratelimit *)value;
+
+ printf("%s.%06d rate: %10.3f key: %s\n",
+ print_time(ratelimit->time_stamp), ratelimit->time_usec,
+ ratelimit->rate, keybuffer);
+
+ break;
}
store_reset(value);
}
@@ -733,6 +745,7 @@
dbdata_retry *retry;
dbdata_wait *wait;
dbdata_callout_cache *callout;
+ dbdata_ratelimit *ratelimit;
int i, oldlength;
uschar *t;
uschar field[256], value[256];
@@ -873,6 +886,29 @@
break;
}
break;
+
+ case type_ratelimit:
+ ratelimit = (dbdata_ratelimit *)value;
+ switch(fieldno)
+ {
+ case 0:
+ if ((tt = read_time(value)) > 0) ratelimit->time_stamp = tt;
+ else printf("bad time value\n");
+ break;
+
+ case 1:
+ ratelimit->time_usec = Uatoi(value);
+
+ case 2:
+ ratelimit->rate = Ustrtod(value, NULL);
+ break;
+
+ default:
+ printf("unknown field number\n");
+ verify = 0;
+ break;
+ }
+ break;
}
dbfn_write(dbm, name, record, length);
@@ -969,6 +1005,13 @@
printf("2 random: %s (%d)\n", print_cache(callout->random_result),
callout->random_result);
}
+ break;
+
+ case type_ratelimit:
+ ratelimit = (dbdata_ratelimit *)value;
+ printf("0 time stamp: %s\n", print_time(ratelimit->time_stamp));
+ printf("1 fract. time: .%06d\n", ratelimit->time_usec);
+ printf("2 sender rate: % .3f\n", ratelimit->rate);
break;
}
}
Index: expand.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/expand.c,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -u -r1.21 -r1.22
--- expand.c 10 May 2005 10:19:11 -0000 1.21
+++ expand.c 23 May 2005 16:58:56 -0000 1.22
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/expand.c,v 1.21 2005/05/10 10:19:11 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/expand.c,v 1.22 2005/05/23 16:58:56 fanf2 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -466,6 +466,9 @@
{ "sender_host_name", vtype_host_lookup, NULL },
{ "sender_host_port", vtype_int, &sender_host_port },
{ "sender_ident", vtype_stringptr, &sender_ident },
+ { "sender_rate", vtype_stringptr, &sender_rate },
+ { "sender_rate_limit", vtype_stringptr, &sender_rate_limit },
+ { "sender_rate_period", vtype_stringptr, &sender_rate_period },
{ "sender_rcvhost", vtype_stringptr, &sender_rcvhost },
{ "sender_verify_failure",vtype_stringptr, &sender_verify_failure },
{ "smtp_active_hostname", vtype_stringptr, &smtp_active_hostname },
Index: globals.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/globals.c,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -u -r1.25 -r1.26
--- globals.c 23 May 2005 15:28:38 -0000 1.25
+++ globals.c 23 May 2005 16:58:56 -0000 1.26
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/globals.c,v 1.25 2005/05/23 15:28:38 fanf2 Exp $ */
+/* $Cambridge: exim/exim-src/src/globals.c,v 1.26 2005/05/23 16:58:56 fanf2 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -777,6 +777,8 @@
uschar *queue_smtp_domains = NULL;
unsigned int random_seed = 0;
+tree_node *ratelimiters_conn = NULL;
+tree_node *ratelimiters_mail = NULL;
uschar *raw_active_hostname = NULL;
uschar *raw_sender = NULL;
uschar **raw_recipients = NULL;
@@ -964,6 +966,9 @@
BOOL sender_host_unknown = FALSE;
uschar *sender_ident = NULL;
BOOL sender_local = FALSE;
+uschar *sender_rate = NULL;
+uschar *sender_rate_limit = NULL;
+uschar *sender_rate_period = NULL;
uschar *sender_rcvhost = NULL;
BOOL sender_set_untrusted = FALSE;
uschar *sender_unqualified_hosts = NULL;
Index: globals.h
===================================================================
RCS file: /home/cvs/exim/exim-src/src/globals.h,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -u -r1.17 -r1.18
--- globals.h 23 May 2005 15:28:38 -0000 1.17
+++ globals.h 23 May 2005 16:58:56 -0000 1.18
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/globals.h,v 1.17 2005/05/23 15:28:38 fanf2 Exp $ */
+/* $Cambridge: exim/exim-src/src/globals.h,v 1.18 2005/05/23 16:58:56 fanf2 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -505,6 +505,8 @@
extern uschar *queue_smtp_domains; /* Ditto, for these domains */
extern unsigned int random_seed; /* Seed for random numbers */
+extern tree_node *ratelimiters_conn; /* Results of connection ratelimit checks */
+extern tree_node *ratelimiters_mail; /* Results of per-mail ratelimit checks */
extern uschar *raw_active_hostname; /* Pre-expansion */
extern uschar *raw_sender; /* Before rewriting */
extern uschar **raw_recipients; /* Before rewriting */
@@ -579,6 +581,9 @@
extern BOOL sender_host_unknown; /* TRUE for -bs and -bS except inetd */
extern uschar *sender_ident; /* Sender identity via RFC 1413 */
extern BOOL sender_local; /* TRUE for local senders */
+extern uschar *sender_rate; /* Sender rate computed by ACL */
+extern uschar *sender_rate_limit; /* Configured rate limit */
+extern uschar *sender_rate_period; /* Configured smoothing period */
extern uschar *sender_rcvhost; /* Host data for Received: */
extern BOOL sender_set_untrusted; /* Sender set by untrusted caller */
extern uschar *sender_unqualified_hosts; /* Permitted unqualified senders */
Index: smtp_in.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/smtp_in.c,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -u -r1.18 -r1.19
--- smtp_in.c 23 May 2005 15:28:38 -0000 1.18
+++ smtp_in.c 23 May 2005 16:58:56 -0000 1.19
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/smtp_in.c,v 1.18 2005/05/23 15:28:38 fanf2 Exp $ */
+/* $Cambridge: exim/exim-src/src/smtp_in.c,v 1.19 2005/05/23 16:58:56 fanf2 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -837,6 +837,10 @@
spf_smtp_comment = NULL;
#endif
body_linecount = body_zerocount = 0;
+
+sender_rate = sender_rate_limit = sender_rate_period = NULL;
+ratelimiters_mail = NULL; /* Updated by ratelimit ACL condition */
+ /* Note that ratelimiters_conn persists across resets. */
for (i = 0; i < ACL_M_MAX; i++) acl_var[ACL_C_MAX + i] = NULL;
Index: string.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/string.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -u -r1.2 -r1.3
--- string.c 4 Jan 2005 10:00:42 -0000 1.2
+++ string.c 23 May 2005 16:58:56 -0000 1.3
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/string.c,v 1.2 2005/01/04 10:00:42 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/string.c,v 1.3 2005/05/23 16:58:56 fanf2 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -1070,9 +1070,11 @@
break;
/* %f format is inherently insecure if the numbers that it may be
- handed are unknown (e.g. 1e300). However, in Exim, the only use of %f
- is for printing load averages, and these are actually stored as integers
- (load average * 1000) so the size of the numbers is constrained. */
+ handed are unknown (e.g. 1e300). However, in Exim, %f is used for
+ printing load averages, and these are actually stored as integers
+ (load average * 1000) so the size of the numbers is constrained.
+ It is also used for formatting sending rates, where the simplicity
+ of the format prevents overflow. */
case 'f':
case 'e':