X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/83364d30f60965fc809f0340cb5cab5f17b6b1c9..f15132938afa3813819da28497bcabedc551dcf2:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index edcdc409d..42179898a 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/transports/smtp.c,v 1.8 2005/03/22 15:45:35 ph10 Exp $ */ +/* $Cambridge: exim/src/src/transports/smtp.c,v 1.15 2005/08/02 11:22:24 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -93,6 +93,8 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, interface) }, { "keepalive", opt_bool, (void *)offsetof(smtp_transport_options_block, keepalive) }, + { "lmtp_ignore_quota", opt_bool, + (void *)offsetof(smtp_transport_options_block, lmtp_ignore_quota) }, { "max_rcpt", opt_int | opt_public, (void *)offsetof(transport_instance, max_addresses) }, { "multi_domain", opt_bool | opt_public, @@ -163,6 +165,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { FALSE, /* hosts_override */ FALSE, /* hosts_randomize */ TRUE, /* keepalive */ + FALSE, /* lmtp_ignore_quota */ TRUE /* retry_include_ip_address */ #ifdef SUPPORT_TLS ,NULL, /* tls_certificate */ @@ -313,10 +316,11 @@ host_build_hostlist(&(ob->fallback_hostlist), ob->fallback_hosts, FALSE); status means that an address is not currently being processed. Arguments: - addrlist points to a chain of addresses - errno_value to put in each address's errno field - msg to put in each address's message field - rc to put in each address's transport_return field + addrlist points to a chain of addresses + errno_value to put in each address's errno field + msg to put in each address's message field + rc to put in each address's transport_return field + pass_message if TRUE, set the "pass message" flag in the address If errno_value has the special value ERRNO_CONNECTTIMEOUT, ETIMEDOUT is put in the errno field, and RTEF_CTOUT is ORed into the more_errno field, to indicate @@ -326,7 +330,8 @@ Returns: nothing */ static -void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc) +void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc, + BOOL pass_message) { address_item *addr; int orvalue = 0; @@ -340,7 +345,11 @@ for (addr = addrlist; addr != NULL; addr = addr->next) if (addr->transport_return < PENDING) continue; addr->basic_errno = errno_value; addr->more_errno |= orvalue; - if (msg != NULL) addr->message = msg; + if (msg != NULL) + { + addr->message = msg; + if (pass_message) setflag(addr, af_pass_message); + } addr->transport_return = rc; } } @@ -358,18 +367,19 @@ the yield variable. If no response was actually read, a suitable digit is chosen. Arguments: - host the current host, to get its name for messages - errno_value pointer to the errno value - more_errno from the top address for use with ERRNO_FILTER_FAIL - buffer the SMTP response buffer - yield where to put a one-digit SMTP response code - message where to put an errror message - -Returns: TRUE if an SMTP "QUIT" command should be sent, else FALSE + host the current host, to get its name for messages + errno_value pointer to the errno value + more_errno from the top address for use with ERRNO_FILTER_FAIL + buffer the SMTP response buffer + yield where to put a one-digit SMTP response code + message where to put an errror message + pass_message set TRUE if message is an SMTP response + +Returns: TRUE if an SMTP "QUIT" command should be sent, else FALSE */ static BOOL check_response(host_item *host, int *errno_value, int more_errno, - uschar *buffer, int *yield, uschar **message) + uschar *buffer, int *yield, uschar **message, BOOL *pass_message) { uschar *pl = US""; @@ -444,8 +454,9 @@ if (*errno_value == ERRNO_WRITEINCOMPLETE) if (buffer[0] != 0) { uschar *s = string_printing(buffer); - *message = US string_sprintf("SMTP error from remote mailer after %s%s: " + *message = US string_sprintf("SMTP error from remote mail server after %s%s: " "host %s [%s]: %s", pl, smtp_command, host->name, host->address, s); + *pass_message = TRUE; *yield = buffer[0]; return TRUE; } @@ -623,7 +634,7 @@ while (count-- > 0) uschar *message = string_sprintf("SMTP timeout while connected to %s [%s] " "after RCPT TO:<%s>", host->name, host->address, transport_rcpt_address(addr, include_affixes)); - set_errno(addrlist, save_errno, message, DEFER); + set_errno(addrlist, save_errno, message, DEFER, FALSE); retry_add_item(addr, addr->address_retry_key, 0); host->update_waiting = FALSE; return -1; @@ -646,9 +657,10 @@ while (count-- > 0) else { addr->message = - string_sprintf("SMTP error from remote mailer after RCPT TO:<%s>: " + string_sprintf("SMTP error from remote mail server after RCPT TO:<%s>: " "host %s [%s]: %s", transport_rcpt_address(addr, include_affixes), host->name, host->address, string_printing(buffer)); + setflag(addr, af_pass_message); deliver_msglog("%s %s\n", tod_stamp(tod_log), addr->message); /* The response was 5xx */ @@ -699,8 +711,9 @@ if (pending_DATA != 0 && { int code; uschar *msg; + BOOL pass_message; if (pending_DATA > 0 || (yield & 1) != 0) return -3; - (void)check_response(host, &errno, 0, buffer, &code, &msg); + (void)check_response(host, &errno, 0, buffer, &code, &msg, &pass_message); DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining " "is in use and there were no good recipients\n", msg); } @@ -782,9 +795,11 @@ BOOL setting_up = TRUE; BOOL completed_address = FALSE; BOOL esmtp = TRUE; BOOL pending_MAIL; +BOOL pass_message = FALSE; smtp_inblock inblock; smtp_outblock outblock; int max_rcpt = tblock->max_addresses; +uschar *igquotstr = US""; uschar *local_authenticated_sender = authenticated_sender; uschar *helo_data; uschar *message = NULL; @@ -822,7 +837,7 @@ if (helo_data == NULL) { uschar *message = string_sprintf("failed to expand helo_data: %s", expand_string_message); - set_errno(addrlist, 0, message, DEFER); + set_errno(addrlist, 0, message, DEFER, FALSE); return ERROR; } @@ -842,7 +857,7 @@ if (ob->authenticated_sender != NULL) { uschar *message = string_sprintf("failed to expand " "authenticated_sender: %s", expand_string_message); - set_errno(addrlist, 0, message, DEFER); + set_errno(addrlist, 0, message, DEFER, FALSE); return ERROR; } } @@ -861,7 +876,7 @@ if (continue_hostname == NULL) if (inblock.sock < 0) { set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno, - NULL, DEFER); + NULL, DEFER, FALSE); return DEFER; } @@ -936,6 +951,13 @@ goto SEND_QUIT; ob->command_timeout)) goto RESPONSE_FAILED; } + /* Set IGNOREQUOTA if the response to LHLO specifies support and the + lmtp_ignore_quota option was set. */ + + igquotstr = (lmtp && ob->lmtp_ignore_quota && + pcre_exec(regex_IGNOREQUOTA, NULL, CS buffer, Ustrlen(CS buffer), 0, + PCRE_EOPT, NULL, 0) >= 0)? US" IGNOREQUOTA" : US""; + /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */ #ifdef SUPPORT_TLS @@ -1020,8 +1042,11 @@ if (tls_offered && !suppress_tls && for (addr = addrlist; addr != NULL; addr = addr->next) { - addr->cipher = tls_cipher; - addr->peerdn = tls_peerdn; + if (addr->transport_return == PENDING_DEFER) + { + addr->cipher = tls_cipher; + addr->peerdn = tls_peerdn; + } } } } @@ -1067,6 +1092,13 @@ if (continue_hostname == NULL int require_auth; uschar *fail_reason = US"server did not advertise AUTH support"; + /* Set for IGNOREQUOTA if the response to LHLO specifies support and the + lmtp_ignore_quota option was set. */ + + igquotstr = (lmtp && ob->lmtp_ignore_quota && + pcre_exec(regex_IGNOREQUOTA, NULL, CS buffer, Ustrlen(CS buffer), 0, + PCRE_EOPT, NULL, 0) >= 0)? US" IGNOREQUOTA" : US""; + /* If the response to EHLO specified support for the SIZE parameter, note this, provided size_addition is non-negative. */ @@ -1183,7 +1215,7 @@ if (continue_hostname == NULL case ERROR: yield = ERROR; - set_errno(addrlist, 0, string_copy(buffer), DEFER); + set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE); goto SEND_QUIT; } @@ -1199,7 +1231,8 @@ if (continue_hostname == NULL { yield = DEFER; set_errno(addrlist, ERRNO_AUTHFAIL, - string_sprintf("authentication required but %s", fail_reason), DEFER); + string_sprintf("authentication required but %s", fail_reason), DEFER, + FALSE); goto SEND_QUIT; } } @@ -1225,7 +1258,8 @@ if (tblock->filter_command != NULL) if (!rc) { - set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER); + set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, + FALSE); yield = ERROR; goto SEND_QUIT; } @@ -1327,8 +1361,8 @@ for (addr = first_addr; yield as OK, because this error can often mean that there is a problem with just one address, so we don't want to delay the host. */ - count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>\r\n", - transport_rcpt_address(addr, tblock->rcpt_include_affixes)); + count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s\r\n", + transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr); if (count < 0) goto SEND_FAILED; if (count > 0) { @@ -1365,7 +1399,8 @@ if (mua_wrapper) } if (badaddr != NULL) { - set_errno(addrlist, 0, badaddr->message, FAIL); + set_errno(addrlist, 0, badaddr->message, FAIL, + testflag(badaddr, af_pass_message)); ok = FALSE; } } @@ -1594,7 +1629,7 @@ if (!ok) save_errno = errno; message = NULL; send_quit = check_response(host, &save_errno, addrlist->more_errno, - buffer, &code, &message); + buffer, &code, &message, &pass_message); goto FAILED; SEND_FAILED: @@ -1629,11 +1664,11 @@ if (!ok) { if (code == '5') { - set_errno(addrlist, save_errno, message, FAIL); + set_errno(addrlist, save_errno, message, FAIL, pass_message); } else { - set_errno(addrlist, save_errno, message, DEFER); + set_errno(addrlist, save_errno, message, DEFER, pass_message); yield = DEFER; } } @@ -1659,7 +1694,7 @@ if (!ok) { yield = (save_errno == ERRNO_CHHEADER_FAIL || save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER; - set_errno(addrlist, save_errno, message, DEFER); + set_errno(addrlist, save_errno, message, DEFER, pass_message); } /* Otherwise we have a message-specific error response from the remote @@ -1680,7 +1715,8 @@ if (!ok) { if (mua_wrapper) code = '5'; /* Force hard failure in wrapper mode */ - set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER); + set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER, + pass_message); /* If there's an errno, the message contains just the identity of the host. */ @@ -1744,6 +1780,7 @@ if (completed_address && ok && send_quit) )) { uschar *msg; + BOOL pass_message; if (send_rset) { @@ -1757,7 +1794,8 @@ if (completed_address && ok && send_quit) ob->command_timeout))) { int code; - send_quit = check_response(host, &errno, 0, buffer, &code, &msg); + send_quit = check_response(host, &errno, 0, buffer, &code, &msg, + &pass_message); if (!send_quit) { DEBUG(D_transport) debug_printf("%s\n", msg); @@ -1803,7 +1841,7 @@ if (completed_address && ok && send_quit) /* If RSET failed and there are addresses left, they get deferred. */ - else set_errno(first_addr, errno, msg, DEFER); + else set_errno(first_addr, errno, msg, DEFER, FALSE); } } @@ -1846,7 +1884,7 @@ writing RSET might have failed, or there may be other addresses whose hosts are specified in the transports, and therefore not visible at top level, in which case continue_more won't get set. */ -close(inblock.sock); +(void)close(inblock.sock); continue_transport = NULL; continue_hostname = NULL; return yield; @@ -1899,7 +1937,7 @@ outblock.authenticating = FALSE; (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n"); (void)smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout); -close(inblock.sock); +(void)close(inblock.sock); } @@ -2172,6 +2210,12 @@ for (cutoff_retry = 0; expired && uschar *retry_message_key = NULL; uschar *serialize_key = NULL; + /* Default next host is next host. :-) But this can vary if the + hosts_max_try limit is hit (see below). It may also be reset if a host + address is looked up here (in case the host was multihomed). */ + + nexthost = host->next; + /* Set the flag requesting that this host be added to the waiting database if the delivery fails temporarily or if we are running with queue_smtp or a 2-stage queue run. This gets unset for certain @@ -2278,10 +2322,8 @@ for (cutoff_retry = 0; expired && continue; /* With next host */ } - /* The default next host is the next host. :-) But this can vary if the - hosts_max_try limit is hit (see below). NOTE: we cannot put this setting - earlier than this, because a multihomed host whose addresses are not looked - up till just above will add to the host list. */ + /* Reset the default next host in case a multihomed host whose addresses + are not looked up till just above added to the host list. */ nexthost = host->next; @@ -2293,8 +2335,8 @@ for (cutoff_retry = 0; expired && doing a two-stage queue run, don't do this if forcing. */ if ((!deliver_force || queue_2stage) && (queue_smtp || - match_isinlist(addrlist->domain, &queue_smtp_domains, 0, NULL, NULL, - MCL_DOMAIN, TRUE, NULL) == OK)) + match_isinlist(addrlist->domain, &queue_smtp_domains, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)) { expired = FALSE; for (addr = addrlist; addr != NULL; addr = addr->next) @@ -2442,7 +2484,7 @@ for (cutoff_retry = 0; expired && if (dont_deliver) { host_item *host2; - set_errno(addrlist, 0, NULL, OK); + set_errno(addrlist, 0, NULL, OK, FALSE); for (addr = addrlist; addr != NULL; addr = addr->next) { addr->host_used = host; @@ -2764,6 +2806,7 @@ for (addr = addrlist; addr != NULL; addr = addr->next) } else if (expired) { + setflag(addr, af_pass_message); /* This is not a security risk */ addr->message = (ob->delay_after_cutoff)? US"retry time not reached for any host after a long failure period" : US"all hosts have been failing for a long time and were last tried "