(void *)offsetof(smtp_transport_options_block, gethostbyname) },
#ifdef SUPPORT_TLS
/* These are no longer honoured, as of Exim 4.80; for now, we silently
(void *)offsetof(smtp_transport_options_block, gethostbyname) },
#ifdef SUPPORT_TLS
/* These are no longer honoured, as of Exim 4.80; for now, we silently
these options, so that using them becomes an error. */
{ "gnutls_require_kx", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, gnutls_require_kx) },
these options, so that using them becomes an error. */
{ "gnutls_require_kx", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, gnutls_require_kx) },
(void *)offsetof(smtp_transport_options_block, hosts_override) },
{ "hosts_randomize", opt_bool,
(void *)offsetof(smtp_transport_options_block, hosts_randomize) },
(void *)offsetof(smtp_transport_options_block, hosts_override) },
{ "hosts_randomize", opt_bool,
(void *)offsetof(smtp_transport_options_block, hosts_randomize) },
{ "hosts_request_ocsp", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_request_ocsp) },
#endif
{ "hosts_require_auth", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_require_auth) },
#ifdef SUPPORT_TLS
{ "hosts_request_ocsp", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_request_ocsp) },
#endif
{ "hosts_require_auth", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_require_auth) },
#ifdef SUPPORT_TLS
(void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) },
{ "tls_try_verify_hosts", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_try_verify_hosts) },
(void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) },
{ "tls_try_verify_hosts", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_try_verify_hosts) },
{ "tls_verify_certificates", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_verify_certificates) },
{ "tls_verify_hosts", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_verify_hosts) }
#endif
{ "tls_verify_certificates", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_verify_certificates) },
{ "tls_verify_hosts", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_verify_hosts) }
#endif
- return;
-
-tpda_delivery_ip = string_copy(host->address);
-tpda_delivery_port = (host->port == PORT_NONE)? 25 : host->port;
-tpda_delivery_fqdn = string_copy(host->name);
-tpda_delivery_local_part = string_copy(addr->local_part);
-tpda_delivery_domain = string_copy(addr->domain);
-tpda_defer_errno = addr->basic_errno;
-
-tpda_defer_errstr = addr->message
- ? addr->basic_errno > 0
- ? string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno))
- : string_copy(addr->message)
- : addr->basic_errno > 0
- ? string_copy(US strerror(addr->basic_errno))
- : NULL;
-DEBUG(D_transport)
- debug_printf(" TPDA(host defer): tpda_host_defer_action=|%s| tpda_delivery_IP=%s\n",
- action, tpda_delivery_ip);
+save_domain = deliver_domain;
+save_local = deliver_localpart;
+
+/*XXX would ip & port already be set up? */
+deliver_host_address = string_copy(host->address);
+deliver_host_port = (host->port == PORT_NONE)? 25 : host->port;
+tpda_defer_errno = addr->basic_errno;
-if (!expand_string(action) && *expand_string_message)
- log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand tpda_defer_action in %s: %s\n",
- transport_name, expand_string_message);
+deliver_domain = addr->domain;
+deliver_localpart = addr->local_part;
+
+(void) tpda_raise_event(action, US"msg:host:defer",
+ addr->message
+ ? addr->basic_errno > 0
+ ? string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno))
+ : string_copy(addr->message)
+ : addr->basic_errno > 0
+ ? string_copy(US strerror(addr->basic_errno))
+ : NULL);
+
+deliver_localpart = save_local;
+deliver_domain = save_domain;
- smtp_authenticated = FALSE;
- client_authenticator = client_authenticated_id = client_authenticated_sender = NULL;
- require_auth = verify_check_this_host(&(ob->hosts_require_auth), NULL,
- host->name, host->address, NULL);
+smtp_authenticated = FALSE;
+client_authenticator = client_authenticated_id = client_authenticated_sender = NULL;
+require_auth = verify_check_this_host(&(ob->hosts_require_auth), NULL,
+ host->name, host->address, NULL);
- if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
- {
- uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]);
- expand_nmax = -1; /* reset */
+if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
+ {
+ uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]);
+ expand_nmax = -1; /* reset */
- if (require_auth == OK ||
- verify_check_this_host(&(ob->hosts_try_auth), NULL, host->name,
- host->address, NULL) == OK)
- {
- auth_instance *au;
- fail_reason = US"no common mechanisms were found";
+ if (require_auth == OK ||
+ verify_check_this_host(&(ob->hosts_try_auth), NULL, host->name,
+ host->address, NULL) == OK)
+ {
+ auth_instance *au;
+ fail_reason = US"no common mechanisms were found";
- /* Scan the configured authenticators looking for one which is configured
- for use as a client, which is not suppressed by client_condition, and
- whose name matches an authentication mechanism supported by the server.
- If one is found, attempt to authenticate by calling its client function.
- */
+ /* Scan the configured authenticators looking for one which is configured
+ for use as a client, which is not suppressed by client_condition, and
+ whose name matches an authentication mechanism supported by the server.
+ If one is found, attempt to authenticate by calling its client function.
+ */
- for (au = auths; !smtp_authenticated && au != NULL; au = au->next)
- {
- uschar *p = names;
- if (!au->client ||
- (au->client_condition != NULL &&
- !expand_check_condition(au->client_condition, au->name,
- US"client authenticator")))
- {
- DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n",
- au->name,
- (au->client)? "client_condition is false" :
- "not configured as a client");
- continue;
- }
+ for (au = auths; !smtp_authenticated && au != NULL; au = au->next)
+ {
+ uschar *p = names;
+ if (!au->client ||
+ (au->client_condition != NULL &&
+ !expand_check_condition(au->client_condition, au->name,
+ US"client authenticator")))
+ {
+ DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n",
+ au->name,
+ (au->client)? "client_condition is false" :
+ "not configured as a client");
+ continue;
+ }
- if (strncmpic(au->public_name, p, len) != 0 ||
- (p[len] != 0 && !isspace(p[len])))
- {
- while (*p != 0 && !isspace(*p)) p++;
- continue;
- }
+ if (strncmpic(au->public_name, p, len) != 0 ||
+ (p[len] != 0 && !isspace(p[len])))
+ {
+ while (*p != 0 && !isspace(*p)) p++;
+ continue;
+ }
- fail_reason = US"authentication attempt(s) failed";
- obp->authenticating = TRUE;
- rc = (au->info->clientcode)(au, ibp, obp,
- ob->command_timeout, buffer, bufsize);
- obp->authenticating = FALSE;
- DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n",
- au->name, rc);
+ fail_reason = US"authentication attempt(s) failed";
+ obp->authenticating = TRUE;
+ rc = (au->info->clientcode)(au, ibp, obp,
+ ob->command_timeout, buffer, bufsize);
+ obp->authenticating = FALSE;
+ DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n",
+ au->name, rc);
- /* A temporary authentication failure must hold up delivery to
- this host. After a permanent authentication failure, we carry on
- to try other authentication methods. If all fail hard, try to
- deliver the message unauthenticated unless require_auth was set. */
+ /* A temporary authentication failure must hold up delivery to
+ this host. After a permanent authentication failure, we carry on
+ to try other authentication methods. If all fail hard, try to
+ deliver the message unauthenticated unless require_auth was set. */
- switch(rc)
- {
- case OK:
- smtp_authenticated = TRUE; /* stops the outer loop */
- client_authenticator = au->name;
- if (au->set_client_id != NULL)
- client_authenticated_id = expand_string(au->set_client_id);
- break;
-
- /* Failure after writing a command */
-
- case FAIL_SEND:
- return FAIL_SEND;
-
- /* Failure after reading a response */
-
- case FAIL:
- if (errno != 0 || buffer[0] != '5') return FAIL;
- log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
- au->name, host->name, host->address, buffer);
- break;
-
- /* Failure by some other means. In effect, the authenticator
- decided it wasn't prepared to handle this case. Typically this
- is the result of "fail" in an expansion string. Do we need to
- log anything here? Feb 2006: a message is now put in the buffer
- if logging is required. */
-
- case CANCELLED:
- if (*buffer != 0)
- log_write(0, LOG_MAIN, "%s authenticator cancelled "
- "authentication H=%s [%s] %s", au->name, host->name,
- host->address, buffer);
- break;
-
- /* Internal problem, message in buffer. */
-
- case ERROR:
- set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE);
- return ERROR;
- }
+ switch(rc)
+ {
+ case OK:
+ smtp_authenticated = TRUE; /* stops the outer loop */
+ client_authenticator = au->name;
+ if (au->set_client_id != NULL)
+ client_authenticated_id = expand_string(au->set_client_id);
+ break;
+
+ /* Failure after writing a command */
+
+ case FAIL_SEND:
+ return FAIL_SEND;
+
+ /* Failure after reading a response */
+
+ case FAIL:
+ if (errno != 0 || buffer[0] != '5') return FAIL;
+ log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
+ au->name, host->name, host->address, buffer);
+ break;
+
+ /* Failure by some other means. In effect, the authenticator
+ decided it wasn't prepared to handle this case. Typically this
+ is the result of "fail" in an expansion string. Do we need to
+ log anything here? Feb 2006: a message is now put in the buffer
+ if logging is required. */
+
+ case CANCELLED:
+ if (*buffer != 0)
+ log_write(0, LOG_MAIN, "%s authenticator cancelled "
+ "authentication H=%s [%s] %s", au->name, host->name,
+ host->address, buffer);
+ break;
+
+ /* Internal problem, message in buffer. */
+
+ case ERROR:
+ set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE);
+ return ERROR;
+ }
- if (require_auth == OK && !smtp_authenticated)
- {
- set_errno(addrlist, ERRNO_AUTHFAIL,
- string_sprintf("authentication required but %s", fail_reason), DEFER,
- FALSE);
- return DEFER;
- }
+if (require_auth == OK && !smtp_authenticated)
+ {
+ set_errno(addrlist, ERRNO_AUTHFAIL,
+ string_sprintf("authentication required but %s", fail_reason), DEFER,
+ FALSE);
+ return DEFER;
+ }
inblock.sock = outblock.sock =
smtp_connect(host, host_af, port, interface, ob->connect_timeout,
inblock.sock = outblock.sock =
smtp_connect(host, host_af, port, interface, ob->connect_timeout,
/* Alas; be careful, since this goto is not an error-out, so conceivably
we might set data between here and the target which we assume to exist
and be usable. I can see this coming back to bite us. */
/* Alas; be careful, since this goto is not an error-out, so conceivably
we might set data between here and the target which we assume to exist
and be usable. I can see this coming back to bite us. */
tls_offered = esmtp &&
pcre_exec(regex_STARTTLS, NULL, CS buffer, Ustrlen(buffer), 0,
PCRE_EOPT, NULL, 0) >= 0;
tls_offered = esmtp &&
pcre_exec(regex_STARTTLS, NULL, CS buffer, Ustrlen(buffer), 0,
PCRE_EOPT, NULL, 0) >= 0;
prdr_offered = esmtp &&
(pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(buffer), 0,
PCRE_EOPT, NULL, 0) >= 0) &&
prdr_offered = esmtp &&
(pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(buffer), 0,
PCRE_EOPT, NULL, 0) >= 0) &&
+#ifdef EXPERIMENTAL_DSN
+ /* Note if the server supports DSN */
+ smtp_use_dsn = esmtp && pcre_exec(regex_DSN, NULL, CS buffer, (int)Ustrlen(CS buffer), 0,
+ PCRE_EOPT, NULL, 0) >= 0;
+ DEBUG(D_transport) debug_printf("use_dsn=%d\n", smtp_use_dsn);
+#endif
+
/* Note if the response to EHLO specifies support for the AUTH extension.
If it has, check that this host is one we want to authenticate to, and do
the business. The host name and address must be available when the
/* Note if the response to EHLO specifies support for the AUTH extension.
If it has, check that this host is one we want to authenticate to, and do
the business. The host name and address must be available when the
+#endif
+
+#ifdef EXPERIMENTAL_DSN
+/* check if all addresses have lasthop flag */
+/* do not send RET and ENVID if true */
+dsn_all_lasthop = TRUE;
+for (addr = first_addr;
+ address_count < max_rcpt && addr != NULL;
+ addr = addr->next)
+ if ((addr->dsn_flags & rf_dsnlasthop) != 1)
+ dsn_all_lasthop = FALSE;
+
+/* Add any DSN flags to the mail command */
+
+if ((smtp_use_dsn) && (dsn_all_lasthop == FALSE))
+ {
+ if (dsn_ret == dsn_ret_hdrs)
+ {
+ strcpy(p, " RET=HDRS");
+ while (*p) p++;
+ }
+ else if (dsn_ret == dsn_ret_full)
+ {
+ strcpy(p, " RET=FULL");
+ while (*p) p++;
+ }
+ if (dsn_envid != NULL)
+ {
+ string_format(p, sizeof(buffer) - (p-buffer), " ENVID=%s", dsn_envid);
+ while (*p) p++;
+ }
+ }
+#ifdef EXPERIMENTAL_DSN
+ /* Add any DSN flags to the rcpt command and add to the sent string */
+
+ p = buffer;
+ *p = 0;
+
+ if (smtp_use_dsn && (addr->dsn_flags & rf_dsnlasthop) != 1)
+ {
+ if ((addr->dsn_flags & rf_dsnflags) != 0)
+ {
+ int i;
+ BOOL first = TRUE;
+ strcpy(p, " NOTIFY=");
+ while (*p) p++;
+ for (i = 0; i < 4; i++)
+ if ((addr->dsn_flags & rf_list[i]) != 0)
+ {
+ if (!first) *p++ = ',';
+ first = FALSE;
+ strcpy(p, rf_names[i]);
+ while (*p) p++;
+ }
+ }
+
+ if (addr->dsn_orcpt != NULL)
+ {
+ string_format(p, sizeof(buffer) - (p-buffer), " ORCPT=%s",
+ addr->dsn_orcpt);
+ while (*p) p++;
+ }
+ }
+#endif
+
+
/* Now send the RCPT command, and process outstanding responses when
necessary. After a timeout on RCPT, we just end the function, leaving the
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. */
/* Now send the RCPT command, and process outstanding responses when
necessary. After a timeout on RCPT, we just end the function, leaving the
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>%s\r\n",
transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr);
count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s\r\n",
transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr);
if (rc == DEFER && first_addr->basic_errno == ERRNO_TLSFAILURE &&
ob->tls_tempfail_tryclear &&
verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
if (rc == DEFER && first_addr->basic_errno == ERRNO_TLSFAILURE &&
ob->tls_tempfail_tryclear &&
verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
expanded_hosts != NULL, &message_defer, TRUE);
if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL)
write_logs(first_addr, host);
expanded_hosts != NULL, &message_defer, TRUE);
if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL)
write_logs(first_addr, host);