Add the ratelimit ACL condition. This is mostly reasonably self-contained
authorTony Finch <dot@dot.at>
Mon, 23 May 2005 16:58:55 +0000 (16:58 +0000)
committerTony Finch <dot@dot.at>
Mon, 23 May 2005 16:58:55 +0000 (16:58 +0000)
except that it requires changes to most Makefiles in order to bring in the
maths library for the exp() function.

37 files changed:
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/OS/Makefile-AIX
src/OS/Makefile-BSDI
src/OS/Makefile-CYGWIN
src/OS/Makefile-DGUX
src/OS/Makefile-FreeBSD
src/OS/Makefile-GNU
src/OS/Makefile-GNUkFreeBSD
src/OS/Makefile-GNUkNetBSD
src/OS/Makefile-IRIX
src/OS/Makefile-IRIX6
src/OS/Makefile-IRIX632
src/OS/Makefile-IRIX65
src/OS/Makefile-Linux
src/OS/Makefile-NetBSD
src/OS/Makefile-NetBSD-a.out
src/OS/Makefile-OSF1
src/OS/Makefile-OpenUNIX
src/OS/Makefile-QNX
src/OS/Makefile-SCO
src/OS/Makefile-SCO_SV
src/OS/Makefile-SunOS5
src/OS/Makefile-SunOS5-hal
src/OS/Makefile-UNIX_SV
src/OS/Makefile-USG
src/OS/Makefile-Unixware7
src/OS/Makefile-mips
src/src/acl.c
src/src/dbstuff.h
src/src/exim.h
src/src/exim_dbutil.c
src/src/expand.c
src/src/globals.c
src/src/globals.h
src/src/smtp_in.c
src/src/string.c

index e2baa6130c1f3082b03289051e5ac6453ef67211..628e7eeea8dfaea1ce60718cddc4975fd7048347 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.140 2005/05/23 15:28:37 fanf2 Exp $
+$Cambridge: 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
 -------------------------------------------
 
 Change log file for Exim from version 4.21
 -------------------------------------------
@@ -35,6 +35,8 @@ PH/04 Installed a modified version of Tony Finch's patch to make submission
 
 TF/03 Added the control = fakedefer ACL modifier.
 
 
 TF/03 Added the control = fakedefer ACL modifier.
 
+TF/04 Added the ratelimit ACL condition. See NewStuff for details.
+
 
 Exim version 4.51
 -----------------
 
 Exim version 4.51
 -----------------
index ba1c5afc47f08037a33d476d3234a984d78231d2..e0d87c44abfad8319349adf4ec4339238f3fb301 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.44 2005/05/23 15:44:06 fanf2 Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.45 2005/05/23 16:58:55 fanf2 Exp $
 
 New Features in Exim
 --------------------
 
 New Features in Exim
 --------------------
@@ -121,6 +121,120 @@ TF/03 The control = fakereject ACL modifier now has a fakedefer counterpart,
       to be duplicated when the sender retries. Therefore you should not use
       fakedefer if the message will be delivered normally.
 
       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
 ------------
 
 Version 4.51
 ------------
index 489ffe3b125b91ba36a8b73e095060457754860a..e758f7409afd76efa0391ce23a38c40051875440 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-AIX,v 1.2 2005/02/16 16:40:22 ph10 Exp $
+# $Cambridge: 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@cimio.co.uk)
 
 # Exim: OS-specific make file for AIX
 # Written by Nick Waterman (nick@cimio.co.uk)
@@ -25,6 +25,6 @@ CFLAGS = -mcpu=power4 -maix64 -O3
 
 # Needed for vfork() and vfork() only?
 
 
 # Needed for vfork() and vfork() only?
 
-LIBS = -lbsd
+LIBS = -lbsd -lm
 
 # End
 
 # End
index 10ea69895bf6cd6c102c6fa261a70512d9d034b8..2538c707e475917ec922bd4216fe9653b98ba033 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-BSDI,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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.
 
 # Exim: OS-specific make file for BSDI. Its antique link editor
 # cannot handle the TextPop overriding.
@@ -13,7 +13,7 @@ XINCLUDE=-I$(X11)/include
 XLFLAGS=-L$(X11)/lib
 X11_LD_LIB=$(X11)/lib
 
 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
 EXIMON_TEXTPOP=
 
 EXIWHAT_PS_ARG=-ax
index 52dd68456aaa8fde40c7f08e72ee9e64a176002e..8bf51216c87b984c1172277c44aeb7f8b2b2ac2f 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-CYGWIN,v 1.2 2004/11/10 10:36:48 ph10 Exp $
+# $Cambridge: exim/src/OS/Makefile-CYGWIN,v 1.3 2005/05/23 16:58:55 fanf2 Exp $
 
 # OS-specific file for Cygwin.
 
 
 # OS-specific file for Cygwin.
 
@@ -6,7 +6,7 @@
 
 HAVE_ICONV = yes
 CFLAGS= -g -Wall -O2
 
 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
 LIBS_EXIM= -liconv
 EXIWHAT_PS_ARG=-as
 EXIWHAT_KILL_SIGNAL=-USR1
index 4f0439b93eaf9f792744091972af73336a6cb9d1..11e5012f18bd19839986849344cc5c337f4bf1da 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-DGUX,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/src/OS/Makefile-DGUX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
 
 # Exim: OS-specific make file for DGUX
 #
 
 # Exim: OS-specific make file for DGUX
 #
@@ -25,7 +25,7 @@ PERL_COMMAND=/usr/local/bin/perl
 CFLAGS=-O2
 
 RANLIB=@true
 CFLAGS=-O2
 
 RANLIB=@true
-LIBS=-lsocket -lnsl
+LIBS=-lsocket -lnsl -lm
 LIBRESOLV=-lresolv
 DBMLIB=-ldbm
 
 LIBRESOLV=-lresolv
 DBMLIB=-ldbm
 
index 179cbacb40a35c4ac26cdfcdf947fd035aa892e4..0527e8be45f50c9a74aa8ac5a5f6a19a17577f4b 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-FreeBSD,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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
 
 # Exim: OS-specific make file for FreeBSD
 # There's no setting of CFLAGS here, to allow the system default
@@ -11,7 +11,7 @@ CHOWN_COMMAND=/usr/sbin/chown
 HAVE_SA_LEN=YES
 
 # crypt() is in a separate library
 HAVE_SA_LEN=YES
 
 # crypt() is in a separate library
-LIBS=-lcrypt
+LIBS=-lcrypt -lm
 
 # FreeBSD always ships with Berkeley DB
 USE_DB=yes
 
 # FreeBSD always ships with Berkeley DB
 USE_DB=yes
index ef71d90d32d3c6c152cc5f71f854203ab899ab54..6c8f30eae5e77d5cb84ff176c64df9054da72c73 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-GNU,v 1.3 2005/01/12 12:25:56 ph10 Exp $
+# $Cambridge: 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.
 
 
 # Exim: OS-specific make file for GNU and variants.
 
@@ -13,7 +13,7 @@ CFLAGS ?= -O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 DBMLIB = -ldb
 USE_DB = yes
 
 DBMLIB = -ldb
 USE_DB = yes
 
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
 LIBRESOLV = -lresolv
 
 X11=/usr/X11R6
 LIBRESOLV = -lresolv
 
 X11=/usr/X11R6
index 03a8ac9e8bb388fc8e18cb829d50e135b677c1d8..305640cca8df31bd9e17f7ac5b48ca74cf49d3d4 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-GNUkFreeBSD,v 1.1 2005/01/04 10:25:58 ph10 Exp $
+# $Cambridge: 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.
 
 
 # Exim: OS-specific make file for GNU and variants.
 
@@ -13,7 +13,7 @@ CFLAGS ?= -O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 DBMLIB = -ldb
 USE_DB = yes
 
 DBMLIB = -ldb
 USE_DB = yes
 
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
 LIBRESOLV = -lresolv
 
 X11=/usr/X11R6
 LIBRESOLV = -lresolv
 
 X11=/usr/X11R6
index 05f4f56aa68b90d0d0670963e83b86843fbf10fa..45cfc8b521a8c63da938bdb7a8cbaf922ef43dab 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-GNUkNetBSD,v 1.1 2005/01/04 10:25:58 ph10 Exp $
+# $Cambridge: 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.
 
 
 # Exim: OS-specific make file for GNU and variants.
 
@@ -13,7 +13,7 @@ CFLAGS ?= -O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 DBMLIB = -ldb
 USE_DB = yes
 
 DBMLIB = -ldb
 USE_DB = yes
 
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
 LIBRESOLV = -lresolv
 
 X11=/usr/X11R6
 LIBRESOLV = -lresolv
 
 X11=/usr/X11R6
index bab1e9910d88db5e4a4b1833b3f30ac7681dd08f..6ffcf5eec372ac346b2c56061891882d75be4cec 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-IRIX,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/src/OS/Makefile-IRIX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
 
 # Exim: OS-specific make file for IRIX
 
 
 # Exim: OS-specific make file for IRIX
 
@@ -6,7 +6,7 @@ HAVE_ICONV=yes
 BASENAME_COMMAND=/sbin/basename
 HOSTNAME_COMMAND=/usr/bsd/hostname
 CFLAGS=-OPT:Olimit=1500
 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
 XINCLUDE=-I/usr/include/X11
 vfork=fork
 RANLIB=@true
index 3534e5d66708f9a183e0495bdc5694ded6d1b752..357edd030769a879706958843d8dbf319e4d6b0b 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-IRIX6,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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
 
 
 # Exim: OS-specific make file for IRIX6 on 64-bit systems
 
@@ -6,7 +6,7 @@ HAVE_ICONV=yes
 HOSTNAME_COMMAND=/usr/bsd/hostname
 CFLAGS=-O2 -n32 -OPT:Olimit=4000
 LFLAGS=-n32
 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
 XINCLUDE=-I/usr/include/X11
 XLFLAGS=
 vfork=fork
index 5b09364df4c7c3d021be7be29bf96656946bca57..757c4b8a51af6e0d548f15f09a6ebf6c016dd947 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-IRIX632,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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
 
 # 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 @@ HOSTNAME_COMMAND=/usr/bsd/hostname
 CFLAGS=-32
 LFLAGS=-32
 #LIBS=-lmld
 CFLAGS=-32
 LFLAGS=-32
 #LIBS=-lmld
-LIBS=-lelf
+LIBS=-lelf -lm
 XINCLUDE=-I/usr/include/X11
 vfork=fork
 RANLIB=@true
 XINCLUDE=-I/usr/include/X11
 vfork=fork
 RANLIB=@true
index b678e37aed0fc1a5f2f63b7d9e78ffceef9741c1..27476c5e214b510d420464897c589c40409d0730 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-IRIX65,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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
 
 
 # Exim: OS-specific make file for IRIX 6.5
 
@@ -10,7 +10,7 @@ CFLAGS=-O2 -OPT:Olimit=0
 LFLAGS=-Wl,-LD_MSG:off=85
 LFLAGS=
 # nlist has moved from libmld to libelf
 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
 XINCLUDE=-I/usr/include/X11
 vfork=fork
 RANLIB=@true
index d933c056a541d0ef3837aa42205dd9535ad7bc8c..94290f2b9912695873e90f02297e4c12937e432b 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-Linux,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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.
 
 # Exim: OS-specific make file for Linux. This is for modern Linuxes,
 # which use libc6.
@@ -14,7 +14,7 @@ CFLAGS=-O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 DBMLIB = -ldb
 USE_DB = yes
 
 DBMLIB = -ldb
 USE_DB = yes
 
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
 LIBRESOLV = -lresolv
 
 X11=/usr/X11R6
 LIBRESOLV = -lresolv
 
 X11=/usr/X11R6
index 0f00789cb35b2d43c27991b2ce3e7ddf18fe3e93..1e7cbc12780e313875135a7b115184147d0e07ab 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-NetBSD,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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)
 
 
 # Exim: OS-specific make file for NetBSD (ELF object format)
 
@@ -7,7 +7,7 @@ CFLAGS=-O
 
 HAVE_SA_LEN=YES
 HAVE_IPV6=YES
 
 HAVE_SA_LEN=YES
 HAVE_IPV6=YES
-LIBS=-lcrypt
+LIBS=-lcrypt -lm
 
 X11=/usr/X11R6
 XINCLUDE=-I$(X11)/include
 
 X11=/usr/X11R6
 XINCLUDE=-I$(X11)/include
index 1ef7a86c828ebc627f7ca0444b7428b04a38a305..350689d2eb382f225674e8774c9cf8b67cb5df8d 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-NetBSD-a.out,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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)
 
 
 # Exim: OS-specific make file for NetBSD (a.out/COFF object format)
 
@@ -7,7 +7,7 @@ CFLAGS=-O
 
 HAVE_SA_LEN=YES
 HAVE_IPV6=YES
 
 HAVE_SA_LEN=YES
 HAVE_IPV6=YES
-LIBS=-lcrypt
+LIBS=-lcrypt -lm
 
 X11=/usr/X11R6
 XINCLUDE=-I$(X11)/include
 
 X11=/usr/X11R6
 XINCLUDE=-I$(X11)/include
index b5a61fb196fefb1cfaccd4972e0a88dea93729c7..b513db88e28aa0a6dac74d3836e03bc223211b17 100644 (file)
@@ -1,9 +1,9 @@
-# $Cambridge: exim/src/OS/Makefile-OSF1,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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
 
 # 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
 HAVE_CRYPT16=yes
 HAVE_ICONV=yes
 HOSTNAME_COMMAND=/usr/bin/hostname
index 2d543b64f2edb9b863b360a566ffe67b3a797bdc..c0dbe420c9a37f037a0da8579d94560da85fdd51 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-OpenUNIX,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/src/OS/Makefile-OpenUNIX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
 
 # Exim: OS-specific make file for OpenUNIX
 
 
 # Exim: OS-specific make file for OpenUNIX
 
@@ -6,7 +6,7 @@ CC=/usr/bin/cc
 CFLAGS=-O -I/usr/local/include
 LFLAGS=-L/usr/local/lib
 
 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
 EXTRALIBS_EXIMON=-lICE -lSM
 
 RANLIB=@true
index d0f74608df06b8792a9a944346c38d0bcfb2b76f..8c7375f9ad5fb15264cfd3357ee135b9eea0d01f 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-QNX,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/src/OS/Makefile-QNX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
 
 # Exim: OS-specific makefile for QNX
 
 
 # Exim: OS-specific makefile for QNX
 
@@ -21,7 +21,7 @@ LIBIDENTCFLAGS=
 RANLIB=@true
 DBMLIB=-ldb
 USE_DB=yes
 RANLIB=@true
 DBMLIB=-ldb
 USE_DB=yes
-LIBS=-lsocket
+LIBS=-lsocket -lm
 
 X11=/usr/X11R6
 XINCLUDE=-I$(X11)/include
 
 X11=/usr/X11R6
 XINCLUDE=-I$(X11)/include
index 4366f387befc8c52c3b6e6f74dafbf913f51cb54..d328b57cdca1cc779a5b3dba93385c2638344e6f 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-SCO,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/src/OS/Makefile-SCO,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
 
 # Exim: OS-specific make file for SCO
 
 
 # Exim: OS-specific make file for SCO
 
@@ -10,7 +10,7 @@ CFLAGS=-b elf
 RANLIB=@true
 DBMLIB=-lndbm
 ERRNO_QUOTA=0
 RANLIB=@true
 DBMLIB=-lndbm
 ERRNO_QUOTA=0
-LIBS=-lsocket
+LIBS=-lsocket -lm
 HAVE_ICONV=yes
 
 X11=/usr/lib/X11
 HAVE_ICONV=yes
 
 X11=/usr/lib/X11
index 48b5f22781b13b260e94ee75ce75ee8a9964e8e2..6d65862a398cc9e92d7e176ed7fdf61f47b599b6 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-SCO_SV,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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)
 
 # 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
 
 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
 
 
 HAVE_ICONV=yes
 
index 1afef4d2351e26d4e1b9f4666799eb88d1e9ffc4..ebe43677989bed1b105f163135369e794c4a9b7a 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-SunOS5,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/src/OS/Makefile-SunOS5,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
 
 # Exim: OS-specific make file for SunOS5
 
 
 # Exim: OS-specific make file for SunOS5
 
@@ -10,7 +10,7 @@ BASENAME_COMMAND=look_for_it
 HOSTNAME_COMMAND=look_for_it
 
 RANLIB=@true
 HOSTNAME_COMMAND=look_for_it
 
 RANLIB=@true
-LIBS=-lsocket -lnsl -lkstat
+LIBS=-lsocket -lnsl -lkstat -lm
 LIBRESOLV=-lresolv
 
 EXIWHAT_MULTIKILL_CMD=pkill
 LIBRESOLV=-lresolv
 
 EXIWHAT_MULTIKILL_CMD=pkill
index 014bac1e1b9afa8f66c6b10ce421180506a8ce27..8b4bcdfbf8c4003a76962b85d5ddc5069ccd0dcc 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-SunOS5-hal,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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
 
 
 # Exim: OS-specific make file for SunOS5 on a HAL
 
@@ -11,7 +11,7 @@ CFLAGS=-O -KV7
 LIBIDENTCFLAGS="-KV7 -O -DHAVE_ANSIHEADERS"
 LIBIDENTNAME=sunos5
 RANLIB=@true
 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
 LIBRESOLV=-lresolv
 X11=/usr/X11R6
 XINCLUDE=-I$(X11)/include
index 1f56c59fa11c4c7a34cc2ba82c89d0b4cdd068cc..79660ed31f8cc652c3be23f9be181ef575c505c6 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-UNIX_SV,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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)
 #
 
 # Exim: OS-specific make file for SCO SVR4.2MP (and maybe Unixware)
 #
@@ -17,7 +17,7 @@ CFLAGS=-O
 RANLIB=@true
 DBMLIB=-lgdbm -L/usr/local/lib
 ERRNO_QUOTA=0
 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
 
 X11=/usr/lib/X11
 XINCLUDE=-I/usr/include/X11
index b49feecc9f38cb5652c3fd7c3d7cfdad8b43ed0d..421e9b94c3b73be89e2a8b2a83df4e4dfdd4bcc2 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-USG,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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
 #
 
 # Exim: OS-specific make file for Unixware 2.x
 #
@@ -25,7 +25,7 @@ RANLIB=@true
 DBMLIB=-ldb -L/usr/local/lib
 USE_DB=YES
 ERRNO_QUOTA=0
 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
 
 X11=/usr/lib/X11
 XINCLUDE=-I/usr/include/X11
index 5c228cc7f176501f17b2a2f1beedb726c2fd0db3..6c7fab46b738427fe1edb3728fbfcca3dbee2570 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-Unixware7,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: 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@ehlo.com>
 
 # Exim: OS-specific make file for Unixware7
 # Based on information from James FitzGibbon <james@ehlo.com>
@@ -17,7 +17,7 @@ LFLAGS=-L/usr/local/lib
 
 HAVE_ICONV=yes
 
 
 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
 
 # Removed on the advice of Larry Rosenman
 # EXTRALIBS=-lwrap
index 9df94e2d7e385c40dd4fa4df015b3c16781fd5b6..9cee5174b900c22f2b969ee6c5fef871eb38c9c6 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-mips,v 1.1 2004/10/06 15:07:39 ph10 Exp $
+# $Cambridge: exim/src/OS/Makefile-mips,v 1.2 2005/05/23 16:58:55 fanf2 Exp $
 
 # Exim: OS-specific make file for RiscOS4bsd
 
 
 # Exim: OS-specific make file for RiscOS4bsd
 
@@ -6,7 +6,7 @@ HOSTNAME_COMMAND=/usr/ucb/hostname
 EXIT_FAILURE=1
 EXIT_SUCCESS=0
 LIBRESOLV=-lresolv
 EXIT_FAILURE=1
 EXIT_SUCCESS=0
 LIBRESOLV=-lresolv
-LIBS=-liberty
+LIBS=-liberty -lm
 XINCLUDE=-I/usr/X11R6/include
 
 CFLAGS=-O
 XINCLUDE=-I/usr/X11R6/include
 
 CFLAGS=-O
index 5ea8535219201b6f315c4530b318a825e7b3f44f..8125e38c90cde9a4464df69d1193e871ecc92649 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/acl.c,v 1.33 2005/05/23 15:28:38 fanf2 Exp $ */
+/* $Cambridge: exim/src/src/acl.c,v 1.34 2005/05/23 16:58:56 fanf2 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -63,6 +63,7 @@ ACLC_CONDITION, ACLC_CONTROL,
 #ifdef WITH_CONTENT_SCAN
        ACLC_MIME_REGEX,
 #endif
 #ifdef WITH_CONTENT_SCAN
        ACLC_MIME_REGEX,
 #endif
+       ACLC_RATELIMIT,
        ACLC_RECIPIENTS,
 #ifdef WITH_CONTENT_SCAN
        ACLC_REGEX,
        ACLC_RECIPIENTS,
 #ifdef WITH_CONTENT_SCAN
        ACLC_REGEX,
@@ -110,6 +111,7 @@ static uschar *conditions[] = { US"acl", US"authenticated",
 #ifdef WITH_CONTENT_SCAN
   US"mime_regex",
 #endif
 #ifdef WITH_CONTENT_SCAN
   US"mime_regex",
 #endif
+  US"ratelimit",
   US"recipients",
 #ifdef WITH_CONTENT_SCAN
   US"regex",
   US"recipients",
 #ifdef WITH_CONTENT_SCAN
   US"regex",
@@ -171,6 +173,7 @@ static uschar cond_expand_at_top[] = {
 #ifdef WITH_CONTENT_SCAN
   TRUE,    /* mime_regex */
 #endif
 #ifdef WITH_CONTENT_SCAN
   TRUE,    /* mime_regex */
 #endif
+  TRUE,    /* ratelimit */
   FALSE,   /* recipients */
 #ifdef WITH_CONTENT_SCAN
   TRUE,    /* regex */
   FALSE,   /* recipients */
 #ifdef WITH_CONTENT_SCAN
   TRUE,    /* regex */
@@ -227,6 +230,7 @@ static uschar cond_modifiers[] = {
 #ifdef WITH_CONTENT_SCAN
   FALSE,   /* mime_regex */
 #endif
 #ifdef WITH_CONTENT_SCAN
   FALSE,   /* mime_regex */
 #endif
+  FALSE,   /* ratelimit */
   FALSE,   /* recipients */
 #ifdef WITH_CONTENT_SCAN
   FALSE,   /* regex */
   FALSE,   /* recipients */
 #ifdef WITH_CONTENT_SCAN
   FALSE,   /* regex */
@@ -363,6 +367,8 @@ static unsigned int cond_forbids[] = {
   ~(1<<ACL_WHERE_MIME),                            /* mime_regex */
 #endif
 
   ~(1<<ACL_WHERE_MIME),                            /* mime_regex */
 #endif
 
+  0,                                               /* ratelimit */
+
   (unsigned int)
   ~(1<<ACL_WHERE_RCPT),                            /* recipients */
 
   (unsigned int)
   ~(1<<ACL_WHERE_RCPT),                            /* recipients */
 
@@ -1883,6 +1889,283 @@ return d->value;
 
 
 
 
 
 
+/*************************************************
+*            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   *
 *************************************************/
 /*************************************************
 *   Handle conditions/modifiers on an ACL item   *
 *************************************************/
@@ -2415,6 +2698,10 @@ for (; cb != NULL; cb = cb->next)
     break;
 #endif
 
     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,
       &recipient_data);
     case ACLC_RECIPIENTS:
     rc = match_address_list(addr->address, TRUE, TRUE, &arg, NULL, -1, 0,
       &recipient_data);
index bb429126e9f1c85bc93bc20b810e8a1aeb54e411..82f7ecc2adb9d9d0bf5219ff5da994e8b925680b 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/dbstuff.h,v 1.2 2005/01/04 10:00:42 ph10 Exp $ */
+/* $Cambridge: exim/src/src/dbstuff.h,v 1.3 2005/05/23 16:58:56 fanf2 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -363,8 +363,8 @@ after reading data. */
 
 /* Basic DB type */
 typedef struct {
 
 /* 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 */
 } EXIM_DB;
 
 /* Cursor type, not used with gdbm: just set up a dummy */
@@ -631,4 +631,15 @@ typedef struct {
 } dbdata_serialize;
 
 
 } 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 */
 /* End of dbstuff.h */
index bfe4819a2d84a006c1fc19176265894c80413a95..44e4ab31d9045fe4f2c10943eca1c4d51beb9a02 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim.h,v 1.13 2005/05/10 22:39:20 tom Exp $ */
+/* $Cambridge: exim/src/src/exim.h,v 1.14 2005/05/23 16:58:56 fanf2 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -49,6 +49,7 @@ making unique names. */
 
 #include <ctype.h>
 #include <locale.h>
 
 #include <ctype.h>
 #include <locale.h>
+#include <math.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <stddef.h>
index 09783a9597251a519e47790d954ece31dc74b157..a8dbe61b804b81e8200b3fba8c51d4640bf21272 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim_dbutil.c,v 1.3 2005/01/04 10:00:42 ph10 Exp $ */
+/* $Cambridge: exim/src/src/exim_dbutil.c,v 1.4 2005/05/23 16:58:56 fanf2 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -63,10 +63,11 @@ not too much extra baggage. */
 
 /* Identifiers for the different database types. */
 
 
 /* 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 @@ static void
 usage(uschar *name, uschar *options)
 {
 printf("Usage: exim_%s%s  <spool-directory> <database-name>\n", name, options);
 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);
 }
 
 exit(1);
 }
 
@@ -135,6 +136,7 @@ if (argc == 3)
   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], "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 */
   }
 usage(name, options);
 return -1;              /* Never obeyed */
@@ -536,6 +538,7 @@ while (key != NULL)
   dbdata_retry *retry;
   dbdata_wait *wait;
   dbdata_callout_cache *callout;
   dbdata_retry *retry;
   dbdata_wait *wait;
   dbdata_callout_cache *callout;
+  dbdata_ratelimit *ratelimit;
   int count_bad = 0;
   int i, length;
   uschar *t;
   int count_bad = 0;
   int i, length;
   uschar *t;
@@ -661,6 +664,15 @@ while (key != NULL)
           print_cache(callout->random_result));
         }
 
           print_cache(callout->random_result));
         }
 
+      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);
       break;
       }
     store_reset(value);
@@ -733,6 +745,7 @@ for(;;)
   dbdata_retry *retry;
   dbdata_wait *wait;
   dbdata_callout_cache *callout;
   dbdata_retry *retry;
   dbdata_wait *wait;
   dbdata_callout_cache *callout;
+  dbdata_ratelimit *ratelimit;
   int i, oldlength;
   uschar *t;
   uschar field[256], value[256];
   int i, oldlength;
   uschar *t;
   uschar field[256], value[256];
@@ -873,6 +886,29 @@ for(;;)
               break;
               }
             break;
               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);
             }
 
           dbfn_write(dbm, name, record, length);
@@ -970,6 +1006,13 @@ for(;;)
             callout->random_result);
         }
       break;
             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 8677ccb5b927c0d6b2ad0032741779486ac1bf30..b0c1d5340fd6aabac277963d86ba5d0b34d07c4d 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/expand.c,v 1.21 2005/05/10 10:19:11 ph10 Exp $ */
+/* $Cambridge: exim/src/src/expand.c,v 1.22 2005/05/23 16:58:56 fanf2 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -466,6 +466,9 @@ static var_entry var_table[] = {
   { "sender_host_name",    vtype_host_lookup, NULL },
   { "sender_host_port",    vtype_int,         &sender_host_port },
   { "sender_ident",        vtype_stringptr,   &sender_ident },
   { "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 },
   { "sender_rcvhost",      vtype_stringptr,   &sender_rcvhost },
   { "sender_verify_failure",vtype_stringptr,  &sender_verify_failure },
   { "smtp_active_hostname", vtype_stringptr,  &smtp_active_hostname },
index 8928e451fa54e34d5a79d0a37d18e0df6ca76a55..9ad36daf132c1abd47c4f15e374c8da4a72d9af8 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.c,v 1.25 2005/05/23 15:28:38 fanf2 Exp $ */
+/* $Cambridge: exim/src/src/globals.c,v 1.26 2005/05/23 16:58:56 fanf2 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -777,6 +777,8 @@ BOOL    queue_smtp             = FALSE;
 uschar *queue_smtp_domains     = NULL;
 
 unsigned int random_seed       = 0;
 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;
 uschar *raw_active_hostname    = NULL;
 uschar *raw_sender             = NULL;
 uschar **raw_recipients        = NULL;
@@ -964,6 +966,9 @@ BOOL    sender_host_notsocket  = FALSE;
 BOOL    sender_host_unknown    = FALSE;
 uschar *sender_ident           = NULL;
 BOOL    sender_local           = FALSE;
 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;
 uschar *sender_rcvhost         = NULL;
 BOOL    sender_set_untrusted   = FALSE;
 uschar *sender_unqualified_hosts = NULL;
index 40785173d4b1dafd282df329befb5c7e29c3810d..58538975d6154ff0c5fce95224cce188972cc7ea 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.h,v 1.17 2005/05/23 15:28:38 fanf2 Exp $ */
+/* $Cambridge: exim/src/src/globals.h,v 1.18 2005/05/23 16:58:56 fanf2 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -505,6 +505,8 @@ extern BOOL    queue_smtp;             /* Disable all immediate STMP (-odqs)*/
 extern uschar *queue_smtp_domains;     /* Ditto, for these domains */
 
 extern unsigned int random_seed;       /* Seed for random numbers */
 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 */
 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_notsocket;  /* Set for -bs and -bS */
 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 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 */
 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 dfed7b8f588d7edf6339c26afe51d85ac6608d20..02264d28091be922b2901d0a3b897de5f2c040dd 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/smtp_in.c,v 1.18 2005/05/23 15:28:38 fanf2 Exp $ */
+/* $Cambridge: exim/src/src/smtp_in.c,v 1.19 2005/05/23 16:58:56 fanf2 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -838,6 +838,10 @@ spf_smtp_comment = NULL;
 #endif
 body_linecount = body_zerocount = 0;
 
 #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;
 
 /* The message body variables use malloc store. They may be set if this is
 for (i = 0; i < ACL_M_MAX; i++) acl_var[ACL_C_MAX + i] = NULL;
 
 /* The message body variables use malloc store. They may be set if this is
index 2f69cd494ff5c1bcf57a04a7350a37759f5c5371..9edcee5679d9df876f76beb6dcb0e9c51939d13b 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/string.c,v 1.2 2005/01/04 10:00:42 ph10 Exp $ */
+/* $Cambridge: exim/src/src/string.c,v 1.3 2005/05/23 16:58:56 fanf2 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -1070,9 +1070,11 @@ while (*fp != 0)
     break;
 
     /* %f format is inherently insecure if the numbers that it may be
     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':
 
     case 'f':
     case 'e':