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
 -------------------------------------------
@@ -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/04 Added the ratelimit ACL condition. See NewStuff for details.
+
 
 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
 --------------------
@@ -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.
 
+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 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)
@@ -25,6 +25,6 @@ CFLAGS = -mcpu=power4 -maix64 -O3
 
 # Needed for vfork() and vfork() only?
 
-LIBS = -lbsd
+LIBS = -lbsd -lm
 
 # 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.
@@ -13,7 +13,7 @@ XINCLUDE=-I$(X11)/include
 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 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.
 
@@ -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 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
 #
@@ -25,7 +25,7 @@ PERL_COMMAND=/usr/local/bin/perl
 CFLAGS=-O2
 
 RANLIB=@true
-LIBS=-lsocket -lnsl
+LIBS=-lsocket -lnsl -lm
 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
@@ -11,7 +11,7 @@ CHOWN_COMMAND=/usr/sbin/chown
 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 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.
 
@@ -13,7 +13,7 @@ CFLAGS ?= -O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 DBMLIB = -ldb
 USE_DB = yes
 
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
 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.
 
@@ -13,7 +13,7 @@ CFLAGS ?= -O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 DBMLIB = -ldb
 USE_DB = yes
 
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
 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.
 
@@ -13,7 +13,7 @@ CFLAGS ?= -O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 DBMLIB = -ldb
 USE_DB = yes
 
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
 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
 
@@ -6,7 +6,7 @@ HAVE_ICONV=yes
 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 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
 
@@ -6,7 +6,7 @@ HAVE_ICONV=yes
 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 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
@@ -10,7 +10,7 @@ HOSTNAME_COMMAND=/usr/bsd/hostname
 CFLAGS=-32
 LFLAGS=-32
 #LIBS=-lmld
-LIBS=-lelf
+LIBS=-lelf -lm
 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
 
@@ -10,7 +10,7 @@ CFLAGS=-O2 -OPT:Olimit=0
 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 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.
@@ -14,7 +14,7 @@ CFLAGS=-O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 DBMLIB = -ldb
 USE_DB = yes
 
-LIBS = -lnsl -lcrypt
+LIBS = -lnsl -lcrypt -lm
 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)
 
@@ -7,7 +7,7 @@ CFLAGS=-O
 
 HAVE_SA_LEN=YES
 HAVE_IPV6=YES
-LIBS=-lcrypt
+LIBS=-lcrypt -lm
 
 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)
 
@@ -7,7 +7,7 @@ CFLAGS=-O
 
 HAVE_SA_LEN=YES
 HAVE_IPV6=YES
-LIBS=-lcrypt
+LIBS=-lcrypt -lm
 
 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
-LIBS=-liconv
+LIBS=-liconv -lm
 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
 
@@ -6,7 +6,7 @@ CC=/usr/bin/cc
 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 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
 
@@ -21,7 +21,7 @@ LIBIDENTCFLAGS=
 RANLIB=@true
 DBMLIB=-ldb
 USE_DB=yes
-LIBS=-lsocket
+LIBS=-lsocket -lm
 
 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
 
@@ -10,7 +10,7 @@ CFLAGS=-b elf
 RANLIB=@true
 DBMLIB=-lndbm
 ERRNO_QUOTA=0
-LIBS=-lsocket
+LIBS=-lsocket -lm
 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)
@@ -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 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
 
@@ -10,7 +10,7 @@ BASENAME_COMMAND=look_for_it
 HOSTNAME_COMMAND=look_for_it
 
 RANLIB=@true
-LIBS=-lsocket -lnsl -lkstat
+LIBS=-lsocket -lnsl -lkstat -lm
 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
 
@@ -11,7 +11,7 @@ CFLAGS=-O -KV7
 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 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)
 #
@@ -17,7 +17,7 @@ CFLAGS=-O
 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 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
 #
@@ -25,7 +25,7 @@ RANLIB=@true
 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 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>
@@ -17,7 +17,7 @@ LFLAGS=-L/usr/local/lib
 
 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 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
 
@@ -6,7 +6,7 @@ HOSTNAME_COMMAND=/usr/ucb/hostname
 EXIT_FAILURE=1
 EXIT_SUCCESS=0
 LIBRESOLV=-lresolv
-LIBS=-liberty
+LIBS=-liberty -lm
 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    *
@@ -63,6 +63,7 @@ ACLC_CONDITION, ACLC_CONTROL,
 #ifdef WITH_CONTENT_SCAN
        ACLC_MIME_REGEX,
 #endif
+       ACLC_RATELIMIT,
        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
+  US"ratelimit",
   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
+  TRUE,    /* ratelimit */
   FALSE,   /* recipients */
 #ifdef WITH_CONTENT_SCAN
   TRUE,    /* regex */
@@ -227,6 +230,7 @@ static uschar cond_modifiers[] = {
 #ifdef WITH_CONTENT_SCAN
   FALSE,   /* mime_regex */
 #endif
+  FALSE,   /* ratelimit */
   FALSE,   /* recipients */
 #ifdef WITH_CONTENT_SCAN
   FALSE,   /* regex */
@@ -363,6 +367,8 @@ static unsigned int cond_forbids[] = {
   ~(1<<ACL_WHERE_MIME),                            /* mime_regex */
 #endif
 
+  0,                                               /* ratelimit */
+
   (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   *
 *************************************************/
@@ -2415,6 +2698,10 @@ for (; cb != NULL; cb = cb->next)
     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);
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    *
@@ -363,8 +363,8 @@ after reading data. */
 
 /* 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 */
@@ -631,4 +631,15 @@ typedef struct {
 } 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 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    *
@@ -49,6 +49,7 @@ making unique names. */
 
 #include <ctype.h>
 #include <locale.h>
+#include <math.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    *
@@ -63,10 +63,11 @@ not too much extra baggage. */
 
 /* 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);
-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 (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], "ratelimit") == 0) return type_ratelimit;
   }
 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_ratelimit *ratelimit;
   int count_bad = 0;
   int i, length;
   uschar *t;
@@ -661,6 +664,15 @@ while (key != NULL)
           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);
@@ -733,6 +745,7 @@ for(;;)
   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 @@ for(;;)
               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);
@@ -970,6 +1006,13 @@ for(;;)
             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    *
@@ -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_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 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    *
@@ -777,6 +777,8 @@ BOOL    queue_smtp             = FALSE;
 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_notsocket  = 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;
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    *
@@ -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 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_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 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 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    *
@@ -838,6 +838,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;
 
 /* 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    *
@@ -1070,9 +1070,11 @@ while (*fp != 0)
     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':