X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/14f4a80da26686a979d4ac4c7040cb3e6350a746..8f2401034e2ce4007b1f270cd389381753c814eb:/src/src/acl.c diff --git a/src/src/acl.c b/src/src/acl.c index 0f484f21d..3b00be570 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/acl.c,v 1.71 2007/02/06 11:16:21 ph10 Exp $ */ +/* $Cambridge: exim/src/src/acl.c,v 1.77 2007/06/20 14:13:39 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -53,6 +53,7 @@ enum { ACLC_ACL, ACLC_BMI_OPTIN, #endif ACLC_CONDITION, + ACLC_CONTINUE, ACLC_CONTROL, #ifdef WITH_CONTENT_SCAN ACLC_DECODE, @@ -101,10 +102,10 @@ enum { ACLC_ACL, #endif ACLC_VERIFY }; -/* ACL conditions/modifiers: "delay", "control", "endpass", "message", -"log_message", "log_reject_target", "logwrite", and "set" are modifiers that -look like conditions but always return TRUE. They are used for their side -effects. */ +/* ACL conditions/modifiers: "delay", "control", "continue", "endpass", +"message", "log_message", "log_reject_target", "logwrite", and "set" are +modifiers that look like conditions but always return TRUE. They are used for +their side effects. */ static uschar *conditions[] = { US"acl", @@ -114,6 +115,7 @@ static uschar *conditions[] = { US"bmi_optin", #endif US"condition", + US"continue", US"control", #ifdef WITH_CONTENT_SCAN US"decode", @@ -188,7 +190,8 @@ enum { CONTROL_FAKEREJECT, CONTROL_NO_MULTILINE, CONTROL_NO_PIPELINING, - CONTROL_NO_DELAY_FLUSH + CONTROL_NO_DELAY_FLUSH, + CONTROL_NO_CALLOUT_FLUSH }; /* ACL control names; keep in step with the table above! This list is used for @@ -218,9 +221,10 @@ static uschar *controls[] = { #endif US"fakedefer", US"fakereject", - US"no_multiline", + US"no_multiline_responses", US"no_pipelining", - US"no_delay_flush" + US"no_delay_flush", + US"no_callout_flush" }; /* Flags to indicate for which conditions/modifiers a string expansion is done @@ -235,6 +239,7 @@ static uschar cond_expand_at_top[] = { TRUE, /* bmi_optin */ #endif TRUE, /* condition */ + TRUE, /* continue */ TRUE, /* control */ #ifdef WITH_CONTENT_SCAN TRUE, /* decode */ @@ -294,6 +299,7 @@ static uschar cond_modifiers[] = { TRUE, /* bmi_optin */ #endif FALSE, /* condition */ + TRUE, /* continue */ TRUE, /* control */ #ifdef WITH_CONTENT_SCAN FALSE, /* decode */ @@ -343,9 +349,9 @@ static uschar cond_modifiers[] = { FALSE /* verify */ }; -/* Bit map vector of which conditions are not allowed at certain times. For -each condition, there's a bitmap of dis-allowed times. For some, it is easier -to specify the negation of a small number of allowed times. */ +/* Bit map vector of which conditions and modifiers are not allowed at certain +times. For each condition, there's a bitmap of dis-allowed times. For some, it +is easier to specify the negation of a small number of allowed times. */ static unsigned int cond_forbids[] = { 0, /* acl */ @@ -373,6 +379,8 @@ static unsigned int cond_forbids[] = { 0, /* condition */ + 0, /* continue */ + /* Certain types of control are always allowed, so we let it through always and check in the control processing itself. */ @@ -598,6 +606,9 @@ static unsigned int control_forbids[] = { (1< 1 || per_byte + per_cmd + per_conn + per_mail > 1) { *log_msgptr = US"conflicting options for \"ratelimit\" condition"; @@ -2191,21 +2203,32 @@ if (leaky + strict > 1 || per_byte + per_cmd + per_conn + per_mail > 1) } /* Default option values */ + if (!strict) leaky = TRUE; if (!per_byte && !per_cmd && !per_conn) per_mail = TRUE; -/* 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. */ +/* Create the lookup key. If there is no explicit key, use sender_host_address. +If there is no sender_host_address (e.g. -bs or acl_not_smtp) then we simply +omit it. The smoothing constant (sender_rate_period) and the per_xxx options +are added to the key because they alter the meaning of the stored data. */ + +if (key == NULL) + key = (sender_host_address == NULL)? US"" : sender_host_address; -if (!have_key && sender_host_address != NULL) - key = string_sprintf("%s / %s", key, sender_host_address); +key = string_sprintf("%s/%s/%s/%s", + sender_rate_period, + per_byte? US"per_byte" : + per_cmd? US"per_cmd" : + per_mail? US"per_mail" : US"per_conn", + strict? US"strict" : US"leaky", + key); HDEBUG(D_acl) debug_printf("ratelimit condition limit=%.0f period=%.0f key=%s\n", limit, period, key); -/* 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. */ +/* 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; @@ -2224,8 +2247,7 @@ 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 = FAIL; - else rc = OK; + rc = (dbd->rate < limit)? FAIL : OK; store_pool = old_pool; sender_rate = string_sprintf("%.1f", dbd->rate); HDEBUG(D_acl) @@ -2234,8 +2256,8 @@ if (anchor != NULL && (t = tree_search(*anchor, key)) != NULL) } /* 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. */ +rate from the database, update it, and write it back when required. If there's +no previous rate for this key, create one. */ dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE); if (dbm == NULL) @@ -2340,21 +2362,30 @@ matters for edge cases such the first message sent by a client (which gets the initial rate of 0.0) when the rate limit is zero (i.e. the client should be completely blocked). */ -if (dbd->rate < limit) rc = FAIL; - else rc = OK; +rc = (dbd->rate < limit)? FAIL : OK; /* 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. */ +rate preventing them from getting any email through. If noupdate is set, +do not do any updates. */ -if (rc == FAIL || !leaky) +if ((rc == FAIL || !leaky) && !noupdate) + { dbfn_write(dbm, key, dbd, sizeof(dbdata_ratelimit)); + HDEBUG(D_acl) debug_printf("ratelimit db updated\n"); + } +else + { + HDEBUG(D_acl) debug_printf("ratelimit db not updated: %s\n", + noupdate? "noupdate set" : "over the limit, but leaky"); + } + dbfn_close(dbm); /* Store the result in the tree for future reference, if necessary. */ -if (anchor != NULL) +if (anchor != NULL && !noupdate) { t = store_get(sizeof(tree_node) + Ustrlen(key)); t->data.ptr = dbd; @@ -2548,6 +2579,9 @@ for (; cb != NULL; cb = cb->next) *log_msgptr = string_sprintf("invalid \"condition\" value \"%s\"", arg); break; + case ACLC_CONTINUE: /* Always succeeds */ + break; + case ACLC_CONTROL: control_type = decode_control(arg, &p, where, log_msgptr); @@ -2615,6 +2649,10 @@ for (; cb != NULL; cb = cb->next) disable_delay_flush = TRUE; break; + case CONTROL_NO_CALLOUT_FLUSH: + disable_callout_flush = TRUE; + break; + case CONTROL_FAKEDEFER: case CONTROL_FAKEREJECT: fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL; @@ -3637,48 +3675,9 @@ if (rc == FAIL_DROP && where == ACL_WHERE_MAILAUTH) /* Before giving a response, take a look at the length of any user message, and split it up into multiple lines if possible. */ -if (*user_msgptr != NULL && Ustrlen(*user_msgptr) > 75) - { - uschar *s = *user_msgptr = string_copy(*user_msgptr); - uschar *ss = s; - - for (;;) - { - int i = 0; - while (i < 75 && *ss != 0 && *ss != '\n') ss++, i++; - if (*ss == 0) break; - if (*ss == '\n') - s = ++ss; - else - { - uschar *t = ss + 1; - uschar *tt = NULL; - while (--t > s + 35) - { - if (*t == ' ') - { - if (t[-1] == ':') { tt = t; break; } - if (tt == NULL) tt = t; - } - } - - if (tt == NULL) /* Can't split behind - try ahead */ - { - t = ss + 1; - while (*t != 0) - { - if (*t == ' ' || *t == '\n') - { tt = t; break; } - t++; - } - } - - if (tt == NULL) break; /* Can't find anywhere to split */ - *tt = '\n'; - s = ss = tt+1; - } - } - } +*user_msgptr = string_split_message(*user_msgptr); +if (fake_response != OK) + fake_response_text = string_split_message(fake_response_text); return rc; }