X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/f358d5e0ef3cb10a0a6d107f70e9e0442087f6f2..7e3ce68e68ab9b8906a637d352993abf361554e2:/src/src/acl.c diff --git a/src/src/acl.c b/src/src/acl.c index 5c0de55e7..492e9bf17 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for handling Access Control Lists (ACLs) */ @@ -65,9 +65,6 @@ enum { ACLC_ACL, ACLC_DECODE, #endif ACLC_DELAY, -#ifdef WITH_OLD_DEMIME - ACLC_DEMIME, -#endif #ifndef DISABLE_DKIM ACLC_DKIM_SIGNER, ACLC_DKIM_STATUS, @@ -91,6 +88,7 @@ enum { ACLC_ACL, #ifdef WITH_CONTENT_SCAN ACLC_MIME_REGEX, #endif + ACLC_QUEUE, ACLC_RATELIMIT, ACLC_RECIPIENTS, #ifdef WITH_CONTENT_SCAN @@ -111,7 +109,7 @@ enum { ACLC_ACL, ACLC_VERIFY }; /* ACL conditions/modifiers: "delay", "control", "continue", "endpass", -"message", "log_message", "log_reject_target", "logwrite", and "set" are +"message", "log_message", "log_reject_target", "logwrite", "queue" and "set" are modifiers that look like conditions but always return TRUE. They are used for their side effects. */ @@ -132,9 +130,6 @@ static uschar *conditions[] = { US"decode", #endif US"delay", -#ifdef WITH_OLD_DEMIME - US"demime", -#endif #ifndef DISABLE_DKIM US"dkim_signers", US"dkim_status", @@ -158,13 +153,16 @@ static uschar *conditions[] = { #ifdef WITH_CONTENT_SCAN US"mime_regex", #endif + US"queue", US"ratelimit", US"recipients", #ifdef WITH_CONTENT_SCAN US"regex", #endif US"remove_header", - US"sender_domains", US"senders", US"set", + US"sender_domains", + US"senders", + US"set", #ifdef WITH_CONTENT_SCAN US"spam", #endif @@ -181,17 +179,17 @@ that follows! */ enum { CONTROL_AUTH_UNADVERTISED, - #ifdef EXPERIMENTAL_BRIGHTMAIL +#ifdef EXPERIMENTAL_BRIGHTMAIL CONTROL_BMI_RUN, - #endif +#endif CONTROL_DEBUG, - #ifndef DISABLE_DKIM +#ifndef DISABLE_DKIM CONTROL_DKIM_VERIFY, - #endif - #ifdef EXPERIMENTAL_DMARC +#endif +#ifdef EXPERIMENTAL_DMARC CONTROL_DMARC_VERIFY, CONTROL_DMARC_FORENSIC, - #endif +#endif CONTROL_DSCP, CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, @@ -203,11 +201,14 @@ enum { CONTROL_QUEUE_ONLY, CONTROL_SUBMISSION, CONTROL_SUPPRESS_LOCAL_FIXUPS, - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN CONTROL_NO_MBOX_UNSPOOL, - #endif +#endif CONTROL_FAKEDEFER, CONTROL_FAKEREJECT, +#ifdef SUPPORT_I18N + CONTROL_UTF8_DOWNCONVERT, +#endif CONTROL_NO_MULTILINE, CONTROL_NO_PIPELINING, CONTROL_NO_DELAY_FLUSH, @@ -221,17 +222,17 @@ and should be tidied up. */ static uschar *controls[] = { US"allow_auth_unadvertised", - #ifdef EXPERIMENTAL_BRIGHTMAIL +#ifdef EXPERIMENTAL_BRIGHTMAIL US"bmi_run", - #endif +#endif US"debug", - #ifndef DISABLE_DKIM +#ifndef DISABLE_DKIM US"dkim_disable_verify", - #endif - #ifdef EXPERIMENTAL_DMARC +#endif +#ifdef EXPERIMENTAL_DMARC US"dmarc_disable_verify", US"dmarc_enable_forensic", - #endif +#endif US"dscp", US"error", US"caseful_local_part", @@ -243,11 +244,14 @@ static uschar *controls[] = { US"queue_only", US"submission", US"suppress_local_fixups", - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN US"no_mbox_unspool", - #endif +#endif US"fakedefer", US"fakereject", +#ifdef SUPPORT_I18N + US"utf8_downconvert", +#endif US"no_multiline_responses", US"no_pipelining", US"no_delay_flush", @@ -275,9 +279,6 @@ static uschar cond_expand_at_top[] = { TRUE, /* decode */ #endif TRUE, /* delay */ -#ifdef WITH_OLD_DEMIME - TRUE, /* demime */ -#endif #ifndef DISABLE_DKIM TRUE, /* dkim_signers */ TRUE, /* dkim_status */ @@ -301,6 +302,7 @@ static uschar cond_expand_at_top[] = { #ifdef WITH_CONTENT_SCAN TRUE, /* mime_regex */ #endif + TRUE, /* queue */ TRUE, /* ratelimit */ FALSE, /* recipients */ #ifdef WITH_CONTENT_SCAN @@ -340,9 +342,6 @@ static uschar cond_modifiers[] = { FALSE, /* decode */ #endif TRUE, /* delay */ -#ifdef WITH_OLD_DEMIME - FALSE, /* demime */ -#endif #ifndef DISABLE_DKIM FALSE, /* dkim_signers */ FALSE, /* dkim_status */ @@ -366,6 +365,7 @@ static uschar cond_modifiers[] = { #ifdef WITH_CONTENT_SCAN FALSE, /* mime_regex */ #endif + TRUE, /* queue */ FALSE, /* ratelimit */ FALSE, /* recipients */ #ifdef WITH_CONTENT_SCAN @@ -447,15 +447,6 @@ static unsigned int cond_forbids[] = { (1<next)); @@ -1790,27 +1798,27 @@ switch(vp->value) test whether it was successful or not. (This is for optional verification; for mandatory verification, the connection doesn't last this long.) */ - if (tls_in.certificate_verified) return OK; - *user_msgptr = US"no verified certificate"; - return FAIL; + if (tls_in.certificate_verified) return OK; + *user_msgptr = US"no verified certificate"; + return FAIL; case VERIFY_HELO: /* We can test the result of optional HELO verification that might have occurred earlier. If not, we can attempt the verification now. */ - if (!helo_verified && !helo_verify_failed) smtp_verify_helo(); - return helo_verified? OK : FAIL; + if (!helo_verified && !helo_verify_failed) smtp_verify_helo(); + return helo_verified? OK : FAIL; case VERIFY_CSA: /* Do Client SMTP Authorization checks in a separate function, and turn the result code into user-friendly strings. */ - rc = acl_verify_csa(list); - *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s", + rc = acl_verify_csa(list); + *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s", csa_reason_string[rc]); - csa_status = csa_status_string[rc]; - DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status); - return csa_return_code[rc]; + csa_status = csa_status_string[rc]; + DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status); + return csa_return_code[rc]; case VERIFY_HDR_SYNTAX: /* Check that all relevant header lines have the correct syntax. If there is @@ -1819,8 +1827,11 @@ switch(vp->value) always). */ rc = verify_check_headers(log_msgptr); - if (rc != OK && smtp_return_error_details && *log_msgptr != NULL) - *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); + if (rc != OK && *log_msgptr) + if (smtp_return_error_details) + *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); + else + acl_verify_message = *log_msgptr; return rc; case VERIFY_HDR_NAMES_ASCII: @@ -2006,15 +2017,15 @@ message if giving out verification details. */ if (verify_header_sender) { int verrno; - rc = verify_check_header_address(user_msgptr, log_msgptr, callout, + + if ((rc = verify_check_header_address(user_msgptr, log_msgptr, callout, callout_overall, callout_connect, se_mailfrom, pm_mailfrom, verify_options, - &verrno); - if (rc != OK) + &verrno)) != OK) { *basic_errno = verrno; if (smtp_return_error_details) { - if (*user_msgptr == NULL && *log_msgptr != NULL) + if (!*user_msgptr && *log_msgptr) *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); if (rc == DEFER) acl_temp_details = TRUE; } @@ -2036,10 +2047,9 @@ Therefore, we always do a full sender verify when any kind of callout is specified. Caching elsewhere, for instance in the DNS resolver and in the callout handling, should ensure that this is not terribly inefficient. */ -else if (verify_sender_address != NULL) +else if (verify_sender_address) { - if ((verify_options & (vopt_callout_recipsender|vopt_callout_recippmaster)) - != 0) + if ((verify_options & (vopt_callout_recipsender|vopt_callout_recippmaster))) { *log_msgptr = US"use_sender or use_postmaster cannot be used for a " "sender verify callout"; @@ -2055,7 +2065,9 @@ else if (verify_sender_address != NULL) callout that was done previously). If the "routed" flag is not set, routing must have failed, so we use the saved return code. */ - if (testflag(sender_vaddr, af_verify_routed)) rc = OK; else + if (testflag(sender_vaddr, af_verify_routed)) + rc = OK; + else { rc = sender_vaddr->special_action; *basic_errno = sender_vaddr->basic_errno; @@ -2079,8 +2091,12 @@ else if (verify_sender_address != NULL) uschar *save_address_data = deliver_address_data; sender_vaddr = deliver_make_addr(verify_sender_address, TRUE); -#ifdef EXPERIMENTAL_INTERNATIONAL - sender_vaddr->p.utf8 = message_smtputf8; +#ifdef SUPPORT_I18N + if ((sender_vaddr->prop.utf8_msg = message_smtputf8)) + { + sender_vaddr->prop.utf8_downcvt = message_utf8_downconvert == 1; + sender_vaddr->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1; + } #endif if (no_details) setflag(sender_vaddr, af_sverify_told); if (verify_sender_address[0] != 0) @@ -2105,22 +2121,21 @@ else if (verify_sender_address != NULL) HDEBUG(D_acl) debug_printf("----------- end verify ------------\n"); - if (rc == OK) - { - if (Ustrcmp(sender_vaddr->address, verify_sender_address) != 0) - { - DEBUG(D_acl) debug_printf("sender %s verified ok as %s\n", - verify_sender_address, sender_vaddr->address); - } - else - { - DEBUG(D_acl) debug_printf("sender %s verified ok\n", - verify_sender_address); - } - } - else *basic_errno = sender_vaddr->basic_errno; + if (rc != OK) + *basic_errno = sender_vaddr->basic_errno; + else + DEBUG(D_acl) + { + if (Ustrcmp(sender_vaddr->address, verify_sender_address) != 0) + debug_printf("sender %s verified ok as %s\n", + verify_sender_address, sender_vaddr->address); + else + debug_printf("sender %s verified ok\n", + verify_sender_address); + } } - else rc = OK; /* Null sender */ + else + rc = OK; /* Null sender */ /* Cache the result code */ @@ -2138,7 +2153,7 @@ else if (verify_sender_address != NULL) /* Put the sender address_data value into $sender_address_data */ - sender_address_data = sender_vaddr->p.address_data; + sender_address_data = sender_vaddr->prop.address_data; } /* A recipient address just gets a straightforward verify; again we must handle @@ -2168,7 +2183,7 @@ else if (testflag((&addr2), af_pass_message)) acl_temp_details = TRUE; /* Make $address_data visible */ - deliver_address_data = addr2.p.address_data; + deliver_address_data = addr2.prop.address_data; } /* We have a result from the relevant test. Handle defer overrides first. */ @@ -2187,13 +2202,9 @@ sender_verified_failed to the address item that actually failed. */ if (rc != OK && verify_sender_address != NULL) { if (rc != DEFER) - { *log_msgptr = *user_msgptr = US"Sender verify failed"; - } else if (*basic_errno != ERRNO_CALLOUTDEFER) - { *log_msgptr = *user_msgptr = US"Could not complete sender verify"; - } else { *log_msgptr = US"Could not complete sender verify callout"; @@ -2356,17 +2367,13 @@ rate measurement as opposed to rate limiting. */ sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0); if (sender_rate_limit == NULL) - { - limit = -1.0; - ss = NULL; /* compiler quietening */ - } -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++; } - } + return ratelimit_error(log_msgptr, "sender rate limit not set"); + +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') return ratelimit_error(log_msgptr, "\"%s\" is not a positive number", sender_rate_limit); @@ -2981,8 +2988,6 @@ acl_check_condition(int verb, acl_condition_block *cb, int where, { uschar *user_message = NULL; uschar *log_message = NULL; -uschar *debug_tag = NULL; -uschar *debug_opts = NULL; int rc = OK; #ifdef WITH_CONTENT_SCAN int sep = -'/'; @@ -3334,24 +3339,39 @@ for (; cb != NULL; cb = cb->next) break; case CONTROL_DEBUG: - while (*p == '/') { - if (Ustrncmp(p, "/tag=", 5) == 0) - { - const uschar *pp = p + 5; - while (*pp != '\0' && *pp != '/') pp++; - debug_tag = string_copyn(p+5, pp-p-5); - p = pp; - } - else if (Ustrncmp(p, "/opts=", 6) == 0) + uschar * debug_tag = NULL; + uschar * debug_opts = NULL; + BOOL kill = FALSE; + + while (*p == '/') { - const uschar *pp = p + 6; - while (*pp != '\0' && *pp != '/') pp++; - debug_opts = string_copyn(p+6, pp-p-6); + const uschar * pp = p+1; + if (Ustrncmp(pp, "tag=", 4) == 0) + { + for (pp += 4; *pp && *pp != '/';) pp++; + debug_tag = string_copyn(p+5, pp-p-5); + } + else if (Ustrncmp(pp, "opts=", 5) == 0) + { + for (pp += 5; *pp && *pp != '/';) pp++; + debug_opts = string_copyn(p+6, pp-p-6); + } + else if (Ustrncmp(pp, "kill", 4) == 0) + { + for (pp += 4; *pp && *pp != '/';) pp++; + kill = TRUE; + } + else + while (*pp && *pp != '/') pp++; p = pp; } + + if (kill) + debug_logging_stop(); + else + debug_logging_activate(debug_tag, debug_opts); } - debug_logging_activate(debug_tag, debug_opts); break; case CONTROL_SUPPRESS_LOCAL_FIXUPS: @@ -3359,7 +3379,11 @@ for (; cb != NULL; cb = cb->next) break; case CONTROL_CUTTHROUGH_DELIVERY: +#ifndef DISABLE_PRDR if (prdr_requested) +#else + if (0) +#endif /* Too hard to think about for now. We might in future cutthrough the case where both sides handle prdr and this-node prdr acl is "accept" */ @@ -3381,6 +3405,47 @@ for (; cb != NULL; cb = cb->next) arg, *log_msgptr); } return ERROR; + +#ifdef SUPPORT_I18N + case CONTROL_UTF8_DOWNCONVERT: + if (*p == '/') + { + if (p[1] == '1') + { + message_utf8_downconvert = 1; + addr->prop.utf8_downcvt = TRUE; + addr->prop.utf8_downcvt_maybe = FALSE; + p += 2; + break; + } + if (p[1] == '0') + { + message_utf8_downconvert = 0; + addr->prop.utf8_downcvt = FALSE; + addr->prop.utf8_downcvt_maybe = FALSE; + p += 2; + break; + } + if (p[1] == '-' && p[2] == '1') + { + message_utf8_downconvert = -1; + addr->prop.utf8_downcvt = FALSE; + addr->prop.utf8_downcvt_maybe = TRUE; + p += 3; + break; + } + *log_msgptr = US"bad option value for control=utf8_downconvert"; + } + else + { + message_utf8_downconvert = 1; + addr->prop.utf8_downcvt = TRUE; + addr->prop.utf8_downcvt_maybe = FALSE; + break; + } + return ERROR; +#endif + } break; } @@ -3394,14 +3459,9 @@ for (; cb != NULL; cb = cb->next) /* Run the dcc backend. */ rc = dcc_process(&ss); /* Modify return code based upon the existance of options. */ - while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) - != NULL) { + while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) - { - /* FAIL so that the message is passed to the next ACL */ - rc = FAIL; - } - } + rc = FAIL; /* FAIL so that the message is passed to the next ACL */ } break; #endif @@ -3431,6 +3491,34 @@ for (; cb != NULL; cb = cb->next) debug_printf("delay skipped in -bh checking mode\n"); } + /* NOTE 1: Remember that we may be + dealing with stdin/stdout here, in addition to TCP/IP connections. + Also, delays may be specified for non-SMTP input, where smtp_out and + smtp_in will be NULL. Whatever is done must work in all cases. + + NOTE 2: The added feature of flushing the output before a delay must + apply only to SMTP input. Hence the test for smtp_out being non-NULL. + */ + + else + { + if (smtp_out != NULL && !disable_delay_flush) + mac_smtp_fflush(); + +#if !defined(NO_POLL_H) && defined (POLLRDHUP) + { + struct pollfd p; + nfds_t n = 0; + if (smtp_out) + { + p.fd = fileno(smtp_out); + p.events = POLLRDHUP; + n = 1; + } + if (poll(&p, n, delay*1000) > 0) + HDEBUG(D_acl) debug_printf("delay cancelled by peer close\n"); + } +#else /* It appears to be impossible to detect that a TCP/IP connection has gone away without reading from it. This means that we cannot shorten the delay below if the client goes away, because we cannot discover @@ -3440,38 +3528,22 @@ for (; cb != NULL; cb = cb->next) Exim process is not held up unnecessarily. However, it seems that we can't. The poll() function does not do the right thing, and in any case it is not always available. - - NOTE 1: If ever this state of affairs changes, remember that we may be - dealing with stdin/stdout here, in addition to TCP/IP connections. - Also, delays may be specified for non-SMTP input, where smtp_out and - smtp_in will be NULL. Whatever is done must work in all cases. - - NOTE 2: The added feature of flushing the output before a delay must - apply only to SMTP input. Hence the test for smtp_out being non-NULL. */ - else - { - if (smtp_out != NULL && !disable_delay_flush) mac_smtp_fflush(); while (delay > 0) delay = sleep(delay); +#endif } } } break; - #ifdef WITH_OLD_DEMIME - case ACLC_DEMIME: - rc = demime(&arg); - break; - #endif - #ifndef DISABLE_DKIM case ACLC_DKIM_SIGNER: if (dkim_cur_signer != NULL) rc = match_isinlist(dkim_cur_signer, &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); else - rc = FAIL; + rc = FAIL; break; case ACLC_DKIM_STATUS: @@ -3493,7 +3565,7 @@ for (; cb != NULL; cb = cb->next) #endif case ACLC_DNSLISTS: - rc = verify_check_dnsbl(&arg); + rc = verify_check_dnsbl(where, &arg, log_msgptr); break; case ACLC_DOMAINS: @@ -3533,7 +3605,8 @@ for (; cb != NULL; cb = cb->next) rc = verify_check_this_host(&arg, sender_host_cache, NULL, (sender_host_address == NULL)? US"" : sender_host_address, CUSS &host_data); - if (host_data != NULL) host_data = string_copy_malloc(host_data); + if (rc == DEFER) *log_msgptr = search_error_message; + if (host_data) host_data = string_copy_malloc(host_data); break; case ACLC_LOCAL_PARTS: @@ -3548,8 +3621,7 @@ for (; cb != NULL; cb = cb->next) int sep = 0; const uschar *s = arg; uschar *ss; - while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)) - != NULL) + while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size))) { if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN; else if (Ustrcmp(ss, "panic") == 0) logbits |= LOG_PANIC; @@ -3630,6 +3702,10 @@ for (; cb != NULL; cb = cb->next) break; #endif + case ACLC_QUEUE: + queue_name = string_copy_malloc(arg); + break; + case ACLC_RATELIMIT: rc = acl_ratelimit(arg, where, log_msgptr); break; @@ -3669,7 +3745,12 @@ for (; cb != NULL; cb = cb->next) case ACLC_SET: { int old_pool = store_pool; - if (cb->u.varname[0] == 'c') store_pool = POOL_PERM; + if ( cb->u.varname[0] == 'c' +#ifndef DISABLE_EVENT + || event_name /* An event is being delivered */ +#endif + ) + store_pool = POOL_PERM; acl_var_create(cb->u.varname)->data.ptr = string_copy(arg); store_pool = old_pool; } @@ -3717,7 +3798,8 @@ for (; cb != NULL; cb = cb->next) case ACLC_VERIFY: rc = acl_verify(where, addr, arg, user_msgptr, log_msgptr, basic_errno); - acl_verify_message = *user_msgptr; + if (*user_msgptr) + acl_verify_message = *user_msgptr; if (verb == ACL_WARN) *user_msgptr = NULL; break; @@ -4216,7 +4298,7 @@ while (acl != NULL) case ACL_WARN: if (cond == OK) acl_warn(where, *user_msgptr, *log_msgptr); - else if (cond == DEFER && (log_extra_selector & LX_acl_warn_skipped) != 0) + else if (cond == DEFER && LOGGING(acl_warn_skipped)) log_write(0, LOG_MAIN, "%s Warning: ACL \"warn\" statement skipped: " "condition test deferred%s%s", host_and_ident(TRUE), (*log_msgptr == NULL)? US"" : US": ", @@ -4372,9 +4454,9 @@ ratelimiters_cmd = NULL; log_reject_target = LOG_MAIN|LOG_REJECT; #ifndef DISABLE_PRDR -if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR) +if (where==ACL_WHERE_RCPT || where==ACL_WHERE_VRFY || where==ACL_WHERE_PRDR) #else -if (where == ACL_WHERE_RCPT) +if (where==ACL_WHERE_RCPT || where==ACL_WHERE_VRFY) #endif { adb = address_defaults; @@ -4385,8 +4467,12 @@ if (where == ACL_WHERE_RCPT) *log_msgptr = US"defer in percent_hack_domains check"; return DEFER; } -#ifdef EXPERIMENTAL_INTERNATIONAL - addr->p.utf8 = message_smtputf8; +#ifdef SUPPORT_I18N + if ((addr->prop.utf8_msg = message_smtputf8)) + { + addr->prop.utf8_downcvt = message_utf8_downconvert == 1; + addr->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1; + } #endif deliver_domain = addr->domain; deliver_localpart = addr->local_part; @@ -4401,8 +4487,8 @@ and WHERE_RCPT and not yet opened conn as result of recipient-verify, and rcpt acl returned accept, and first recipient (cancel on any subsequents) open one now and run it up to RCPT acceptance. -A failed verify should cancel cutthrough request. - +A failed verify should cancel cutthrough request, +and will pass the fail to the originator. Initial implementation: dual-write to spool. Assume the rxd datastream is now being copied byte-for-byte to an open cutthrough connection. @@ -4421,12 +4507,14 @@ case ACL_WHERE_RCPT: #ifndef DISABLE_PRDR case ACL_WHERE_PRDR: #endif - if (rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt) - open_cutthrough_connection(addr); + if (host_checking_callout) /* -bhc mode */ + cancel_cutthrough_connection("host-checking mode"); + else if (rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt) + rc = open_cutthrough_connection(addr); break; case ACL_WHERE_PREDATA: - if( rc == OK ) + if (rc == OK) cutthrough_predata(); else cancel_cutthrough_connection("predata acl not ok");