X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/83364d30f60965fc809f0340cb5cab5f17b6b1c9..7cd1141be4e551e80514c38662ec6e8209608205:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index edcdc409d..a503d8f13 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.17 2005/08/09 13:31:53 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 @@ -325,8 +329,9 @@ this particular type of timeout. Returns: nothing */ -static -void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc) +static 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; } @@ -494,14 +505,14 @@ if (addr->message != NULL) } else { - log_write(0, LOG_MAIN, "%s [%s]: %s", - host->name, - host->address, - strerror(addr->basic_errno)); - deliver_msglog("%s %s [%s]: %s\n", - tod_stamp(tod_log), - host->name, - host->address, + uschar *msg = + ((log_extra_selector & LX_outgoing_port) != 0)? + string_sprintf("%s [%s]:%d", host->name, host->address, + (host->port == PORT_NONE)? 25 : host->port) + : + string_sprintf("%s [%s]", host->name, host->address); + log_write(0, LOG_MAIN, "%s %s", msg, strerror(addr->basic_errno)); + deliver_msglog("%s %s %s\n", tod_stamp(tod_log), msg, strerror(addr->basic_errno)); } } @@ -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); } @@ -736,7 +749,7 @@ Arguments: failed by one of them. host host to deliver to host_af AF_INET or AF_INET6 - port TCP/IP port to use, in host byte order + port default TCP/IP port to use, in host byte order interface interface to bind to, or NULL tblock transport instance block copy_host TRUE if host set in addr->host_used must be copied, because @@ -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; } } @@ -1542,11 +1577,10 @@ if (!ok) ok = TRUE; else } /* SMTP, or success return from LMTP for this address. Pass back the - actual port used. */ + actual host that was used. */ addr->transport_return = OK; addr->more_errno = delivery_time; - thost->port = port; addr->host_used = thost; addr->special_action = flag; addr->message = conf; @@ -1594,7 +1628,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 +1663,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 +1693,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 +1714,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 +1779,7 @@ if (completed_address && ok && send_quit) )) { uschar *msg; + BOOL pass_message; if (send_rset) { @@ -1757,7 +1793,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 +1840,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 +1883,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 +1936,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); } @@ -2092,12 +2129,9 @@ else if (ob->hosts_randomize && hostlist->mx == MX_NONE && !continuing) } -/* Sort out the port. Set up a string for adding to the retry key if the port -number is not the standard SMTP port. */ +/* Sort out the default port. */ if (!smtp_get_port(ob->port, addrlist, &port, tid)) return FALSE; -pistring = string_sprintf(":%d", port); -if (Ustrcmp(pistring, ":25") == 0) pistring = US""; /* For each host-plus-IP-address on the list: @@ -2172,6 +2206,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 @@ -2195,6 +2235,8 @@ for (cutoff_retry = 0; expired && if (host->address == NULL) { + int new_port; + host_item *hh; uschar *canonical_name; if (host->status >= hstatus_unusable) @@ -2206,6 +2248,13 @@ for (cutoff_retry = 0; expired && DEBUG(D_transport) debug_printf("getting address for %s\n", host->name); + /* The host name is permitted to have an attached port. Find it, and + strip it from the name. Just remember it for now. */ + + new_port = host_item_get_port(host); + + /* Count hosts looked up */ + hosts_looked_up++; /* Find by name if so configured, or if it's an IP address. We don't @@ -2222,6 +2271,11 @@ for (cutoff_retry = 0; expired && &canonical_name, NULL); } + /* Update the host (and any additional blocks, resulting from + multihoming) with a host-specific port, if any. */ + + for (hh = host; hh != nexthost; hh = hh->next) hh->port = new_port; + /* Failure to find the host at this time (usually DNS temporary failure) is really a kind of routing failure rather than a transport failure. Therefore we add a retry item of the routing kind, not to stop us trying @@ -2278,10 +2332,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 +2345,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) @@ -2317,6 +2369,14 @@ for (cutoff_retry = 0; expired && deliver_host = host->name; deliver_host_address = host->address; + /* Set up a string for adding to the retry key if the port number is not + the standard SMTP port. A host may have its own port setting that overrides + the default. */ + + pistring = string_sprintf(":%d", (host->port == PORT_NONE)? + port : host->port); + if (Ustrcmp(pistring, ":25") == 0) pistring = US""; + /* Select IPv4 or IPv6, and choose an outgoing interface. If the interface string changes upon expansion, we must add it to the key that is used for retries, because connections to the same host from a different interface @@ -2442,7 +2502,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 +2824,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 "