From 870f6ba8a2945754a7f2f66097e3a64465fe1a04 Mon Sep 17 00:00:00 2001 From: Tony Finch Date: Mon, 23 May 2005 16:58:55 +0000 Subject: [PATCH] Add the ratelimit ACL condition. This is mostly reasonably self-contained except that it requires changes to most Makefiles in order to bring in the maths library for the exp() function. --- doc/doc-txt/ChangeLog | 4 +- doc/doc-txt/NewStuff | 116 +++++++++++++- src/OS/Makefile-AIX | 4 +- src/OS/Makefile-BSDI | 4 +- src/OS/Makefile-CYGWIN | 4 +- src/OS/Makefile-DGUX | 4 +- src/OS/Makefile-FreeBSD | 4 +- src/OS/Makefile-GNU | 4 +- src/OS/Makefile-GNUkFreeBSD | 4 +- src/OS/Makefile-GNUkNetBSD | 4 +- src/OS/Makefile-IRIX | 4 +- src/OS/Makefile-IRIX6 | 4 +- src/OS/Makefile-IRIX632 | 4 +- src/OS/Makefile-IRIX65 | 4 +- src/OS/Makefile-Linux | 4 +- src/OS/Makefile-NetBSD | 4 +- src/OS/Makefile-NetBSD-a.out | 4 +- src/OS/Makefile-OSF1 | 4 +- src/OS/Makefile-OpenUNIX | 4 +- src/OS/Makefile-QNX | 4 +- src/OS/Makefile-SCO | 4 +- src/OS/Makefile-SCO_SV | 4 +- src/OS/Makefile-SunOS5 | 4 +- src/OS/Makefile-SunOS5-hal | 4 +- src/OS/Makefile-UNIX_SV | 4 +- src/OS/Makefile-USG | 4 +- src/OS/Makefile-Unixware7 | 4 +- src/OS/Makefile-mips | 4 +- src/src/acl.c | 289 ++++++++++++++++++++++++++++++++++- src/src/dbstuff.h | 17 ++- src/src/exim.h | 3 +- src/src/exim_dbutil.c | 55 ++++++- src/src/expand.c | 5 +- src/src/globals.c | 7 +- src/src/globals.h | 7 +- src/src/smtp_in.c | 6 +- src/src/string.c | 10 +- 37 files changed, 550 insertions(+), 73 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index e2baa6130..628e7eeea 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -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 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index ba1c5afc4..e0d87c44a 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -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 = /

/ / + + 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 ------------ diff --git a/src/OS/Makefile-AIX b/src/OS/Makefile-AIX index 489ffe3b1..e758f7409 100644 --- a/src/OS/Makefile-AIX +++ b/src/OS/Makefile-AIX @@ -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 diff --git a/src/OS/Makefile-BSDI b/src/OS/Makefile-BSDI index 10ea69895..2538c707e 100644 --- a/src/OS/Makefile-BSDI +++ b/src/OS/Makefile-BSDI @@ -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 diff --git a/src/OS/Makefile-CYGWIN b/src/OS/Makefile-CYGWIN index 52dd68456..8bf51216c 100644 --- a/src/OS/Makefile-CYGWIN +++ b/src/OS/Makefile-CYGWIN @@ -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 diff --git a/src/OS/Makefile-DGUX b/src/OS/Makefile-DGUX index 4f0439b93..11e5012f1 100644 --- a/src/OS/Makefile-DGUX +++ b/src/OS/Makefile-DGUX @@ -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 diff --git a/src/OS/Makefile-FreeBSD b/src/OS/Makefile-FreeBSD index 179cbacb4..0527e8be4 100644 --- a/src/OS/Makefile-FreeBSD +++ b/src/OS/Makefile-FreeBSD @@ -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 diff --git a/src/OS/Makefile-GNU b/src/OS/Makefile-GNU index ef71d90d3..6c8f30eae 100644 --- a/src/OS/Makefile-GNU +++ b/src/OS/Makefile-GNU @@ -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 diff --git a/src/OS/Makefile-GNUkFreeBSD b/src/OS/Makefile-GNUkFreeBSD index 03a8ac9e8..305640cca 100644 --- a/src/OS/Makefile-GNUkFreeBSD +++ b/src/OS/Makefile-GNUkFreeBSD @@ -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 diff --git a/src/OS/Makefile-GNUkNetBSD b/src/OS/Makefile-GNUkNetBSD index 05f4f56aa..45cfc8b52 100644 --- a/src/OS/Makefile-GNUkNetBSD +++ b/src/OS/Makefile-GNUkNetBSD @@ -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 diff --git a/src/OS/Makefile-IRIX b/src/OS/Makefile-IRIX index bab1e9910..6ffcf5eec 100644 --- a/src/OS/Makefile-IRIX +++ b/src/OS/Makefile-IRIX @@ -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 diff --git a/src/OS/Makefile-IRIX6 b/src/OS/Makefile-IRIX6 index 3534e5d66..357edd030 100644 --- a/src/OS/Makefile-IRIX6 +++ b/src/OS/Makefile-IRIX6 @@ -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 diff --git a/src/OS/Makefile-IRIX632 b/src/OS/Makefile-IRIX632 index 5b09364df..757c4b8a5 100644 --- a/src/OS/Makefile-IRIX632 +++ b/src/OS/Makefile-IRIX632 @@ -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 diff --git a/src/OS/Makefile-IRIX65 b/src/OS/Makefile-IRIX65 index b678e37ae..27476c5e2 100644 --- a/src/OS/Makefile-IRIX65 +++ b/src/OS/Makefile-IRIX65 @@ -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 diff --git a/src/OS/Makefile-Linux b/src/OS/Makefile-Linux index d933c056a..94290f2b9 100644 --- a/src/OS/Makefile-Linux +++ b/src/OS/Makefile-Linux @@ -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 diff --git a/src/OS/Makefile-NetBSD b/src/OS/Makefile-NetBSD index 0f00789cb..1e7cbc127 100644 --- a/src/OS/Makefile-NetBSD +++ b/src/OS/Makefile-NetBSD @@ -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 diff --git a/src/OS/Makefile-NetBSD-a.out b/src/OS/Makefile-NetBSD-a.out index 1ef7a86c8..350689d2e 100644 --- a/src/OS/Makefile-NetBSD-a.out +++ b/src/OS/Makefile-NetBSD-a.out @@ -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 diff --git a/src/OS/Makefile-OSF1 b/src/OS/Makefile-OSF1 index b5a61fb19..b513db88e 100644 --- a/src/OS/Makefile-OSF1 +++ b/src/OS/Makefile-OSF1 @@ -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 diff --git a/src/OS/Makefile-OpenUNIX b/src/OS/Makefile-OpenUNIX index 2d543b64f..c0dbe420c 100644 --- a/src/OS/Makefile-OpenUNIX +++ b/src/OS/Makefile-OpenUNIX @@ -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 diff --git a/src/OS/Makefile-QNX b/src/OS/Makefile-QNX index d0f74608d..8c7375f9a 100644 --- a/src/OS/Makefile-QNX +++ b/src/OS/Makefile-QNX @@ -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 diff --git a/src/OS/Makefile-SCO b/src/OS/Makefile-SCO index 4366f387b..d328b57cd 100644 --- a/src/OS/Makefile-SCO +++ b/src/OS/Makefile-SCO @@ -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 diff --git a/src/OS/Makefile-SCO_SV b/src/OS/Makefile-SCO_SV index 48b5f2278..6d65862a3 100644 --- a/src/OS/Makefile-SCO_SV +++ b/src/OS/Makefile-SCO_SV @@ -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 diff --git a/src/OS/Makefile-SunOS5 b/src/OS/Makefile-SunOS5 index 1afef4d23..ebe436779 100644 --- a/src/OS/Makefile-SunOS5 +++ b/src/OS/Makefile-SunOS5 @@ -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 diff --git a/src/OS/Makefile-SunOS5-hal b/src/OS/Makefile-SunOS5-hal index 014bac1e1..8b4bcdfbf 100644 --- a/src/OS/Makefile-SunOS5-hal +++ b/src/OS/Makefile-SunOS5-hal @@ -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 diff --git a/src/OS/Makefile-UNIX_SV b/src/OS/Makefile-UNIX_SV index 1f56c59fa..79660ed31 100644 --- a/src/OS/Makefile-UNIX_SV +++ b/src/OS/Makefile-UNIX_SV @@ -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 diff --git a/src/OS/Makefile-USG b/src/OS/Makefile-USG index b49feecc9..421e9b94c 100644 --- a/src/OS/Makefile-USG +++ b/src/OS/Makefile-USG @@ -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 diff --git a/src/OS/Makefile-Unixware7 b/src/OS/Makefile-Unixware7 index 5c228cc7f..6c7fab46b 100644 --- a/src/OS/Makefile-Unixware7 +++ b/src/OS/Makefile-Unixware7 @@ -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 @@ -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 diff --git a/src/OS/Makefile-mips b/src/OS/Makefile-mips index 9df94e2d7..9cee5174b 100644 --- a/src/OS/Makefile-mips +++ b/src/OS/Makefile-mips @@ -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 diff --git a/src/src/acl.c b/src/src/acl.c index 5ea853521..8125e38c9 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -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<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); diff --git a/src/src/dbstuff.h b/src/src/dbstuff.h index bb429126e..82f7ecc2a 100644 --- a/src/src/dbstuff.h +++ b/src/src/dbstuff.h @@ -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 */ diff --git a/src/src/exim.h b/src/src/exim.h index bfe4819a2..44e4ab31d 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -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 #include +#include #include #include #include diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 09783a959..a8dbe61b8 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -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 \n", name, options); -printf(" = retry | misc | wait- | callout\n"); +printf(" = retry | misc | wait- | 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; } } diff --git a/src/src/expand.c b/src/src/expand.c index 8677ccb5b..b0c1d5340 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -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 }, diff --git a/src/src/globals.c b/src/src/globals.c index 8928e451f..9ad36daf1 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -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; diff --git a/src/src/globals.h b/src/src/globals.h index 40785173d..58538975d 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -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 */ diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index dfed7b8f5..02264d280 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -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 diff --git a/src/src/string.c b/src/src/string.c index 2f69cd494..9edcee567 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -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': -- 2.30.2