From 4f47f84bdcf1449c0f5816718b02de026dfd8e4a Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Fri, 19 Dec 2003 00:00:00 -0500 Subject: [PATCH] postfix-2.0.16-20031219 --- postfix/conf/sample-auth.cf | 6 +- postfix/src/global/mail_params.h | 9 ++ postfix/src/global/mail_version.h | 2 +- postfix/src/global/recipient_list.c | 35 +++++-- postfix/src/global/recipient_list.h | 2 + postfix/src/smtp/Makefile.in | 35 ++++--- postfix/src/smtp/smtp.c | 35 ++++++- postfix/src/smtp/smtp.h | 62 ++++++++---- postfix/src/smtp/smtp_addr.c | 129 +++++++++--------------- postfix/src/smtp/smtp_addr.h | 4 +- postfix/src/smtp/smtp_connect.c | 150 +++++++++++++++++++++------- postfix/src/smtp/smtp_misc.c | 81 +++++++++++++++ postfix/src/smtp/smtp_proto.c | 45 +++------ postfix/src/smtp/smtp_state.c | 1 - postfix/src/smtp/smtp_trouble.c | 143 ++++++++++++++------------ postfix/src/util/sys_defs.h | 1 + 16 files changed, 474 insertions(+), 266 deletions(-) create mode 100644 postfix/src/smtp/smtp_misc.c diff --git a/postfix/conf/sample-auth.cf b/postfix/conf/sample-auth.cf index e44ec0abc..83e22aeaf 100644 --- a/postfix/conf/sample-auth.cf +++ b/postfix/conf/sample-auth.cf @@ -72,11 +72,11 @@ smtpd_sasl_security_options = noanonymous # The smtpd_sasl_local_domain parameter specifies the name of the # local authentication realm. # -# By default, the local authentication realm name is the name of the -# machine. +# By default, the local authentication realm name is the null string. # #smtpd_sasl_local_domain = $mydomain -smtpd_sasl_local_domain = $myhostname +#smtpd_sasl_local_domain = $myhostname +smtpd_sasl_local_domain = # The smtpd_sasl_exceptions_networks parameter controls what SMTP # clients Postfix will not offer AUTH support. diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 8c0f6f1f6..dc503c7cd 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -180,6 +180,15 @@ extern bool var_disable_dns; #define DEF_SMTP_HOST_LOOKUP SMTP_HOST_LOOKUP_DNS extern int var_smtp_dns_lookup; +#define SMTP_BACKUP_SESSION "session" +#define SMTP_BACKUP_MESSAGE "message" +#define SMTP_BACKUP_RECIPIENT "recipient" + +#define VAR_SMTP_BACKUP_MASK "smtp_backup_on_soft_error" +#define DEF_SMTP_BACKUP_MASK SMTP_BACKUP_SESSION \ + " " SMTP_BACKUP_MESSAGE \ + " " SMTP_BACKUP_RECIPIENT + /* * Location of the mail queue directory tree. */ diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index d83a0dda2..386f333bc 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change the patchlevel and the release date. Snapshots change the * release date only, unless they include the same bugfix as a patch release. */ -#define MAIL_RELEASE_DATE "20031217" +#define MAIL_RELEASE_DATE "20031219" #define VAR_MAIL_VERSION "mail_version" #define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE diff --git a/postfix/src/global/recipient_list.c b/postfix/src/global/recipient_list.c index 92d37f597..84e1e1061 100644 --- a/postfix/src/global/recipient_list.c +++ b/postfix/src/global/recipient_list.c @@ -11,6 +11,7 @@ /* long offset; /* char *orig_addr; /* char *address; +/* int status; /* .in -4 /* } RECIPIENT; /* @@ -30,12 +31,18 @@ /* const char *orig_rcpt; /* const char *recipient; /* +/* void recipient_list_truncate(list) +/* RECIPIENT_LIST *list; +/* /* void recipient_list_free(list) /* RECIPIENT_LIST *list; /* DESCRIPTION /* This module maintains lists of recipient structures. Each /* recipient is characterized by a destination address and /* by the queue file offset of its delivery status record. +/* The per-recipient status is initialized to zero, and exists +/* solely for the convenience of the application. It is not used +/* by the recipient_list module itself. /* /* recipient_list_init() creates an empty recipient structure list. /* The list argument is initialized such that it can be given to @@ -44,6 +51,9 @@ /* recipient_list_add() adds a recipient to the specified list. /* The recipient address is copied with mystrdup(). /* +/* recipient_list_truncate() truncates the specified list to +/* the specified length. +/* /* recipient_list_free() releases memory for the specified list /* of recipient structures. /* @@ -72,6 +82,7 @@ /* System library. */ #include +#include /* Utility library. */ @@ -106,18 +117,30 @@ void recipient_list_add(RECIPIENT_LIST *list, long offset, list->info[list->len].orig_addr = mystrdup(orig_rcpt); list->info[list->len].address = mystrdup(rcpt); list->info[list->len].offset = offset; + list->info[list->len].status = 0; list->len++; } +/* recipient_list_truncate - release memory for unused recipient structures */ + +void recipient_list_truncate(RECIPIENT_LIST *list, int new_len) +{ + RECIPIENT *rcpt; + + if (new_len < 0 || new_len > list->len) + msg_panic("recipient_list_truncate: bad length %d", new_len); + + for (rcpt = list->info + new_len; rcpt < list->info + list->len; rcpt++) { + myfree(rcpt->orig_addr); + myfree(rcpt->address); + } + list->len = new_len; +} + /* recipient_list_free - release memory for in-core recipient structure */ void recipient_list_free(RECIPIENT_LIST *list) { - RECIPIENT *rcpt; - - for (rcpt = list->info; rcpt < list->info + list->len; rcpt++) { - myfree(rcpt->orig_addr); - myfree(rcpt->address); - } + recipient_list_truncate(list, 0); myfree((char *) list->info); } diff --git a/postfix/src/global/recipient_list.h b/postfix/src/global/recipient_list.h index 0059b71ce..b85752b18 100644 --- a/postfix/src/global/recipient_list.h +++ b/postfix/src/global/recipient_list.h @@ -21,6 +21,7 @@ typedef struct RECIPIENT { long offset; /* REC_TYPE_RCPT byte */ char *orig_addr; /* null or original recipient */ char *address; /* complete address */ + int status; /* Application specific. */ } RECIPIENT; typedef struct RECIPIENT_LIST { @@ -31,6 +32,7 @@ typedef struct RECIPIENT_LIST { extern void recipient_list_init(RECIPIENT_LIST *); extern void recipient_list_add(RECIPIENT_LIST *, long, const char *, const char *); +extern void recipient_list_truncate(RECIPIENT_LIST *, int); extern void recipient_list_free(RECIPIENT_LIST *); /* LICENSE diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in index 2ae10116b..529e8e337 100644 --- a/postfix/src/smtp/Makefile.in +++ b/postfix/src/smtp/Makefile.in @@ -1,9 +1,9 @@ SHELL = /bin/sh SRCS = smtp.c smtp_connect.c smtp_proto.c smtp_chat.c smtp_session.c \ - smtp_addr.c smtp_trouble.c smtp_state.c \ + smtp_addr.c smtp_trouble.c smtp_state.c smtp_misc.c \ smtp_sasl_proto.c smtp_sasl_glue.c OBJS = smtp.o smtp_connect.o smtp_proto.o smtp_chat.o smtp_session.o \ - smtp_addr.o smtp_trouble.o smtp_state.o \ + smtp_addr.o smtp_trouble.o smtp_state.o smtp_misc.o \ smtp_sasl_proto.o smtp_sasl_glue.o HDRS = smtp.h smtp_sasl.h TESTSRC = @@ -89,16 +89,12 @@ smtp_addr.o: ../../include/stringops.h smtp_addr.o: ../../include/myrand.h smtp_addr.o: ../../include/mail_params.h smtp_addr.o: ../../include/own_inet_addr.h -smtp_addr.o: ../../include/deliver_pass.h -smtp_addr.o: ../../include/deliver_request.h -smtp_addr.o: ../../include/vstream.h -smtp_addr.o: ../../include/recipient_list.h -smtp_addr.o: ../../include/mail_proto.h -smtp_addr.o: ../../include/iostuff.h -smtp_addr.o: ../../include/attr.h smtp_addr.o: ../../include/dns.h smtp_addr.o: smtp.h +smtp_addr.o: ../../include/vstream.h smtp_addr.o: ../../include/argv.h +smtp_addr.o: ../../include/deliver_request.h +smtp_addr.o: ../../include/recipient_list.h smtp_addr.o: smtp_addr.h smtp_chat.o: smtp_chat.c smtp_chat.o: ../../include/sys_defs.h @@ -137,14 +133,29 @@ smtp_connect.o: ../../include/sane_connect.h smtp_connect.o: ../../include/mail_params.h smtp_connect.o: ../../include/own_inet_addr.h smtp_connect.o: ../../include/debug_peer.h +smtp_connect.o: ../../include/deliver_pass.h +smtp_connect.o: ../../include/deliver_request.h +smtp_connect.o: ../../include/recipient_list.h +smtp_connect.o: ../../include/mail_proto.h +smtp_connect.o: ../../include/attr.h smtp_connect.o: ../../include/mail_error.h smtp_connect.o: ../../include/name_mask.h smtp_connect.o: ../../include/dns.h smtp_connect.o: smtp.h smtp_connect.o: ../../include/argv.h -smtp_connect.o: ../../include/deliver_request.h -smtp_connect.o: ../../include/recipient_list.h smtp_connect.o: smtp_addr.h +smtp_misc.o: smtp_misc.c +smtp_misc.o: ../../include/sys_defs.h +smtp_misc.o: ../../include/msg.h +smtp_misc.o: ../../include/deliver_request.h +smtp_misc.o: ../../include/vstring.h +smtp_misc.o: ../../include/vbuf.h +smtp_misc.o: ../../include/vstream.h +smtp_misc.o: ../../include/recipient_list.h +smtp_misc.o: ../../include/deliver_completed.h +smtp_misc.o: ../../include/sent.h +smtp_misc.o: smtp.h +smtp_misc.o: ../../include/argv.h smtp_proto.o: smtp_proto.c smtp_proto.o: ../../include/sys_defs.h smtp_proto.o: ../../include/msg.h @@ -162,10 +173,8 @@ smtp_proto.o: ../../include/smtp_stream.h smtp_proto.o: ../../include/mail_queue.h smtp_proto.o: ../../include/recipient_list.h smtp_proto.o: ../../include/deliver_request.h -smtp_proto.o: ../../include/deliver_completed.h smtp_proto.o: ../../include/defer.h smtp_proto.o: ../../include/bounce.h -smtp_proto.o: ../../include/sent.h smtp_proto.o: ../../include/record.h smtp_proto.o: ../../include/rec_type.h smtp_proto.o: ../../include/off_cvt.h diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index d39fb17e2..364121031 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -310,14 +310,18 @@ int var_smtp_pix_delay; int var_smtp_line_limit; char *var_smtp_helo_name; char *var_smtp_host_lookup; +char *var_smtp_backup_mask; bool var_smtp_quote_821_env; bool var_smtp_defer_mxaddr; bool var_smtp_send_xforward; /* - * Global variables. + * Global variables. smtp_errno is set by the address lookup routines and by + * the connection management routines. */ +int smtp_errno; int smtp_host_lookup_mask; +int smtp_backup_mask; /* deliver_message - deliver message with extreme prejudice */ @@ -392,15 +396,21 @@ static void smtp_service(VSTREAM *client_stream, char *unused_service, char **ar } } -/* pre_init - pre-jail initialization */ +/* post_init - post-jail initialization */ -static void pre_init(char *unused_name, char **unused_argv) +static void post_init(char *unused_name, char **unused_argv) { static NAME_MASK lookup_masks[] = { SMTP_HOST_LOOKUP_DNS, SMTP_MASK_DNS, SMTP_HOST_LOOKUP_NATIVE, SMTP_MASK_NATIVE, 0, }; + static NAME_MASK backup_masks[] = { + SMTP_BACKUP_SESSION, SMTP_BACKUP_SESSION_FAILURE, + SMTP_BACKUP_MESSAGE, SMTP_BACKUP_MESSAGE_FAILURE, + SMTP_BACKUP_RECIPIENT, SMTP_BACKUP_RECIPIENT_FAILURE, + 0, + }; /* * Turn on per-peer debugging. @@ -420,6 +430,23 @@ static void pre_init(char *unused_name, char **unused_argv) str_name_mask(VAR_SMTP_HOST_LOOKUP, lookup_masks, smtp_host_lookup_mask)); + /* + * When to choose a backup host after a temporary failure. + */ + smtp_backup_mask = name_mask(VAR_SMTP_BACKUP_MASK, backup_masks, + var_smtp_backup_mask); + if (msg_verbose) + msg_info("when to try backup host: %s", + str_name_mask(VAR_SMTP_BACKUP_MASK, backup_masks, + smtp_backup_mask)); + +} + +/* pre_init - pre-jail initialization */ + +static void pre_init(char *unused_name, char **unused_argv) +{ + /* * SASL initialization. */ @@ -468,6 +495,7 @@ int main(int argc, char **argv) VAR_SMTP_BIND_ADDR, DEF_SMTP_BIND_ADDR, &var_smtp_bind_addr, 0, 0, VAR_SMTP_HELO_NAME, DEF_SMTP_HELO_NAME, &var_smtp_helo_name, 1, 0, VAR_SMTP_HOST_LOOKUP, DEF_SMTP_HOST_LOOKUP, &var_smtp_host_lookup, 1, 0, + VAR_SMTP_BACKUP_MASK, DEF_SMTP_BACKUP_MASK, &var_smtp_backup_mask, 0, 0, 0, }; static CONFIG_TIME_TABLE time_table[] = { @@ -510,6 +538,7 @@ int main(int argc, char **argv) MAIL_SERVER_STR_TABLE, str_table, MAIL_SERVER_BOOL_TABLE, bool_table, MAIL_SERVER_PRE_INIT, pre_init, + MAIL_SERVER_POST_INIT, post_init, MAIL_SERVER_PRE_ACCEPT, pre_accept, MAIL_SERVER_EXIT, pre_exit, 0); diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index f713f8b4c..04337966a 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -56,29 +56,53 @@ typedef struct SMTP_STATE { int space_left; /* output length control */ struct MIME_STATE *mime_state; /* mime state machine */ int final_server; /* final mail server */ - int backup_server; /* relayhost or fallback relay */ } SMTP_STATE; -#define SMTP_FEATURE_ESMTP (1<<0) -#define SMTP_FEATURE_8BITMIME (1<<1) -#define SMTP_FEATURE_PIPELINING (1<<2) -#define SMTP_FEATURE_SIZE (1<<3) -#define SMTP_FEATURE_STARTTLS (1<<4) -#define SMTP_FEATURE_AUTH (1<<5) -#define SMTP_FEATURE_MAYBEPIX (1<<6) /* PIX smtp fixup mode */ -#define SMTP_FEATURE_XFORWARD_NAME (1<<7) -#define SMTP_FEATURE_XFORWARD_ADDR (1<<8) -#define SMTP_FEATURE_XFORWARD_PROTO (1<<9) -#define SMTP_FEATURE_XFORWARD_HELO (1<<10) + /* + * Server features. + */ +#define SMTP_FEATURE_ESMTP (1<<0) +#define SMTP_FEATURE_8BITMIME (1<<1) +#define SMTP_FEATURE_PIPELINING (1<<2) +#define SMTP_FEATURE_SIZE (1<<3) +#define SMTP_FEATURE_STARTTLS (1<<4) +#define SMTP_FEATURE_AUTH (1<<5) +#define SMTP_FEATURE_MAYBEPIX (1<<6) /* PIX smtp fixup mode */ +#define SMTP_FEATURE_XFORWARD_NAME (1<<7) +#define SMTP_FEATURE_XFORWARD_ADDR (1<<8) +#define SMTP_FEATURE_XFORWARD_PROTO (1<<9) +#define SMTP_FEATURE_XFORWARD_HELO (1<<10) + + /* + * Application-specific per-recipient status. + */ +#define SMTP_RCPT_KEEP 1 /* send to backup host */ +#define SMTP_RCPT_DROP 2 /* remove from list */ /* * smtp.c */ +extern int smtp_errno; /* XXX can we get rid of this? */ + +#define SMTP_NONE 0 /* no error */ +#define SMTP_FAIL 1 /* permanent error */ +#define SMTP_RETRY 2 /* temporary error */ +#define SMTP_LOOP 3 /* MX loop */ + extern int smtp_host_lookup_mask; /* host lookup methods to use */ #define SMTP_MASK_DNS (1<<0) #define SMTP_MASK_NATIVE (1<<1) + /* + * What soft errors qualify for going to a backup host. + */ +extern int smtp_backup_mask; /* when to try backup host */ + +#define SMTP_BACKUP_SESSION_FAILURE (1<<0) +#define SMTP_BACKUP_MESSAGE_FAILURE (1<<1) +#define SMTP_BACKUP_RECIPIENT_FAILURE (1<<2) + /* * smtp_session.c */ @@ -119,6 +143,12 @@ extern SMTP_RESP *smtp_chat_resp(SMTP_STATE *); extern void smtp_chat_reset(SMTP_STATE *); extern void smtp_chat_notify(SMTP_STATE *); + /* + * smtp_misc.c. + */ +extern void smtp_rcpt_done(SMTP_STATE *, const char *, RECIPIENT *); +extern int smtp_weed_request(RECIPIENT_LIST *); + /* * smtp_trouble.c */ @@ -140,14 +170,6 @@ extern VSTRING *smtp_unalias_addr(VSTRING *, const char *); extern SMTP_STATE *smtp_state_alloc(void); extern void smtp_state_free(SMTP_STATE *); - /* - * Status codes. Errors must have negative codes so that they do not - * interfere with useful counts of work done. - */ -#define SMTP_OK 0 /* so far, so good */ -#define SMTP_RETRY (-1) /* transient error */ -#define SMTP_FAIL (-2) /* hard error */ - /* LICENSE /* .ad /* .fi diff --git a/postfix/src/smtp/smtp_addr.c b/postfix/src/smtp/smtp_addr.c index 0bf04b827..da701da67 100644 --- a/postfix/src/smtp/smtp_addr.c +++ b/postfix/src/smtp/smtp_addr.c @@ -6,13 +6,13 @@ /* SYNOPSIS /* #include "smtp_addr.h" /* -/* DNS_RR *smtp_domain_addr(state, name) -/* SMTP_STATE *state; +/* DNS_RR *smtp_domain_addr(name, why) /* char *name; +/* VSTRING *why; /* -/* DNS_RR *smtp_host_addr(state, name) -/* SMTP_STATE *state; +/* DNS_RR *smtp_host_addr(name, why) /* char *name; +/* VSTRING *why; /* DESCRIPTION /* This module implements Internet address lookups. By default, /* lookups are done via the Internet domain name service (DNS). @@ -24,10 +24,7 @@ /* exchanger hosts listed for the named domain. Addresses are /* returned in most-preferred first order. The result is truncated /* so that it contains only hosts that are more preferred than the -/* local mail server itself. When the "best MX is local" feature -/* is enabled, the local system is allowed to be the best mail -/* exchanger, and mail is delivered accordingly. Otherwise, -/* mailer loops are treated as an error. +/* local mail server itself. /* /* When no mail exchanger is listed in the DNS for \fIname\fR, the /* request is passed to smtp_host_addr(). @@ -46,7 +43,16 @@ /* when DNS lookups are explicitly disabled. /* /* All routines either return a DNS_RR pointer, or return a null -/* pointer and report any problems via the smtp_trouble(3) module. +/* pointer and set the \fIsmtp_errno\fR global variable accordingly: +/* .IP SMTP_RETRY +/* The request failed due to a soft error, and should be retried later. +/* .IP SMTP_FAIL +/* The request attempt failed due to a hard error. +/* .IP SMTP_LOOP +/* The local machine is the best mail exchanger. +/* .PP +/* In addition, a textual description of the problem is made available +/* via the \fIwhy\fR argument. /* LICENSE /* .ad /* .fi @@ -109,7 +115,6 @@ static int h_errno = TRY_AGAIN; #include #include -#include /* DNS library. */ @@ -120,9 +125,6 @@ static int h_errno = TRY_AGAIN; #include "smtp.h" #include "smtp_addr.h" -#define ERROR_CLASS_RETRY 450 -#define ERROR_CLASS_FAIL 550 - /* smtp_print_addr - print address list */ static void smtp_print_addr(char *what, DNS_RR *addr_list) @@ -146,8 +148,7 @@ static void smtp_print_addr(char *what, DNS_RR *addr_list) /* smtp_addr_one - address lookup for one host name */ -static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, - VSTRING *why, int *error_class) +static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, VSTRING *why) { char *myname = "smtp_addr_one"; struct in_addr inaddr; @@ -180,13 +181,13 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, addr_list = dns_rr_append(addr_list, addr); return (addr_list); default: - *error_class = ERROR_CLASS_RETRY; + smtp_errno = SMTP_RETRY; return (addr_list); case DNS_FAIL: - *error_class = ERROR_CLASS_FAIL; + smtp_errno = SMTP_FAIL; return (addr_list); case DNS_NOTFOUND: - *error_class = ERROR_CLASS_FAIL; + smtp_errno = SMTP_FAIL; /* maybe gethostbyname() will succeed */ break; } @@ -199,13 +200,12 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, memset((char *) &fixed, 0, sizeof(fixed)); if ((hp = gethostbyname(host)) == 0) { vstring_sprintf(why, "%s: %s", host, HSTRERROR(h_errno)); - *error_class = (h_errno == TRY_AGAIN ? - ERROR_CLASS_RETRY : ERROR_CLASS_FAIL); + smtp_errno = (h_errno == TRY_AGAIN ? SMTP_RETRY : SMTP_FAIL); } else if (hp->h_addrtype != AF_INET) { vstring_sprintf(why, "%s: host not found", host); msg_warn("%s: unknown address family %d for %s", myname, hp->h_addrtype, host); - *error_class = ERROR_CLASS_FAIL; + smtp_errno = SMTP_FAIL; } else { while (hp->h_addr_list[0]) { addr_list = dns_rr_append(addr_list, @@ -226,7 +226,7 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, /* smtp_addr_list - address lookup for a list of mail exchangers */ -static DNS_RR *smtp_addr_list(DNS_RR *mx_names, VSTRING *why, int *error_class) +static DNS_RR *smtp_addr_list(DNS_RR *mx_names, VSTRING *why) { DNS_RR *addr_list = 0; DNS_RR *rr; @@ -236,7 +236,7 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, VSTRING *why, int *error_class) * with DNS lookups (except if we're backup MX, and all the better MX * hosts can't be found). * - * XXX 2821: update error_class (0->FAIL upon unrecoverable lookup error, + * XXX 2821: update smtp_errno (0->FAIL upon unrecoverable lookup error, * any->RETRY upon temporary lookup error) so that we can correctly * handle the case of no resolvable MX host. Currently this is always * treated as a soft error. RFC 2821 wants a more precise response. @@ -244,8 +244,7 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, VSTRING *why, int *error_class) for (rr = mx_names; rr; rr = rr->next) { if (rr->type != T_MX) msg_panic("smtp_addr_list: bad resource type: %d", rr->type); - addr_list = smtp_addr_one(addr_list, (char *) rr->data, rr->pref, - why, error_class); + addr_list = smtp_addr_one(addr_list, (char *) rr->data, rr->pref, why); } return (addr_list); } @@ -328,16 +327,13 @@ static int smtp_compare_pref(DNS_RR *a, DNS_RR *b) /* smtp_domain_addr - mail exchanger address lookup */ -DNS_RR *smtp_domain_addr(SMTP_STATE *state, char *name) +DNS_RR *smtp_domain_addr(char *name, VSTRING *why) { - DELIVER_REQUEST *request = state->request; DNS_RR *mx_names; DNS_RR *addr_list = 0; DNS_RR *self = 0; unsigned best_pref; unsigned best_found; - int error_class; - VSTRING *why = vstring_alloc(1); /* * Preferences from DNS use 0..32767, fall-backs use 32768+. @@ -391,40 +387,26 @@ DNS_RR *smtp_domain_addr(SMTP_STATE *state, char *name) * that an IP address is listed only under one hostname. However, looking * at hostnames provides a partial solution for MX hosts behind a NAT * gateway. - * - * Defer host lookup errors if a) there are more mail servers or b) we are - * looking up a relayhost or fallback relay. */ -#define DEFER_HOST_LOOKUP_ERROR(s) \ - ((s)->final_server == 0 || (s)->backup_server) - switch (dns_lookup(name, T_MX, 0, &mx_names, (VSTRING *) 0, why)) { default: + smtp_errno = SMTP_RETRY; if (var_ign_mx_lookup_err) - addr_list = smtp_host_addr(state, name); - else - smtp_site_fail(state, ERROR_CLASS_RETRY, - "%s: %s", request->queue_id, vstring_str(why)); + addr_list = smtp_host_addr(name, why); break; case DNS_FAIL: + smtp_errno = SMTP_FAIL; if (var_ign_mx_lookup_err) - addr_list = smtp_host_addr(state, name); - else { - smtp_site_fail(state, DEFER_HOST_LOOKUP_ERROR(state) ? - ERROR_CLASS_RETRY : ERROR_CLASS_FAIL, - "%s: %s", request->queue_id, vstring_str(why)); - } + addr_list = smtp_host_addr(name, why); break; case DNS_OK: mx_names = dns_rr_sort(mx_names, smtp_compare_pref); best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE); - addr_list = smtp_addr_list(mx_names, why, &error_class); + addr_list = smtp_addr_list(mx_names, why); dns_rr_free(mx_names); if (addr_list == 0) { - if (var_smtp_defer_mxaddr || DEFER_HOST_LOOKUP_ERROR(state)) - error_class = ERROR_CLASS_RETRY; - smtp_site_fail(state, error_class, - "%s: %s", request->queue_id, vstring_str(why)); + if (var_smtp_defer_mxaddr) + smtp_errno = SMTP_RETRY; msg_warn("no MX host for %s has a valid A record", name); break; } @@ -435,23 +417,14 @@ DNS_RR *smtp_domain_addr(SMTP_STATE *state, char *name) addr_list = smtp_truncate_self(addr_list, self->pref); if (addr_list == 0) { if (best_pref != best_found) { - smtp_site_fail(state, ERROR_CLASS_RETRY, - "%s: unable to find primary relay for %s", - request->queue_id, name); - } else if (*var_bestmx_transp != 0) { /* we're best MX */ - state->status = - deliver_pass_all(MAIL_CLASS_PRIVATE, var_bestmx_transp, - request); - state->final_server = 1; + vstring_sprintf(why, "unable to find primary relay for %s", + name); + smtp_errno = SMTP_RETRY; } else { - msg_warn("%s is best MX host for %s but no local, virtual " - "or remote delivery is configured for that domain", - var_myhostname, request->nexthop); - smtp_site_fail(state, ERROR_CLASS_FAIL, - "%s: mail for %s loops back to myself", - request->queue_id, name); + vstring_sprintf(why, "mail for %s loops back to myself", + name); + smtp_errno = SMTP_LOOP; } - break; } } if (addr_list && addr_list->next && var_smtp_rand_addr) { @@ -460,43 +433,31 @@ DNS_RR *smtp_domain_addr(SMTP_STATE *state, char *name) } break; case DNS_NOTFOUND: - addr_list = smtp_host_addr(state, name); + addr_list = smtp_host_addr(name, why); break; } /* * Clean up. */ - vstring_free(why); return (addr_list); } /* smtp_host_addr - direct host lookup */ -DNS_RR *smtp_host_addr(SMTP_STATE *state, char *host) +DNS_RR *smtp_host_addr(char *host, VSTRING *why) { - DELIVER_REQUEST *request = state->request; DNS_RR *addr_list; - int error_class; - VSTRING *why = vstring_alloc(1); /* * If the host is specified by numerical address, just convert the * address to internal form. Otherwise, the host is specified by name. */ #define PREF0 0 - addr_list = smtp_addr_one((DNS_RR *) 0, host, PREF0, why, &error_class); - if (addr_list == 0) { - if (DEFER_HOST_LOOKUP_ERROR(state)) - error_class = ERROR_CLASS_RETRY; - smtp_site_fail(state, error_class, - "%s: %s", request->queue_id, vstring_str(why)); - } else { - if (addr_list->next && var_smtp_rand_addr) - addr_list = dns_rr_shuffle(addr_list); - if (msg_verbose) - smtp_print_addr(host, addr_list); - } - vstring_free(why); + addr_list = smtp_addr_one((DNS_RR *) 0, host, PREF0, why); + if (addr_list && addr_list->next && var_smtp_rand_addr) + addr_list = dns_rr_shuffle(addr_list); + if (msg_verbose) + smtp_print_addr(host, addr_list); return (addr_list); } diff --git a/postfix/src/smtp/smtp_addr.h b/postfix/src/smtp/smtp_addr.h index d9f72833f..d0dabba49 100644 --- a/postfix/src/smtp/smtp_addr.h +++ b/postfix/src/smtp/smtp_addr.h @@ -16,8 +16,8 @@ /* * Internal interfaces. */ -extern DNS_RR *smtp_host_addr(SMTP_STATE *state, char *); -extern DNS_RR *smtp_domain_addr(SMTP_STATE *state, char *); +extern DNS_RR *smtp_host_addr(char *, VSTRING *); +extern DNS_RR *smtp_domain_addr(char *, VSTRING *); /* LICENSE /* .ad diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c index 547f88e4c..9ed095c83 100644 --- a/postfix/src/smtp/smtp_connect.c +++ b/postfix/src/smtp/smtp_connect.c @@ -82,6 +82,7 @@ #include #include #include +#include #include /* DNS library. */ @@ -95,8 +96,8 @@ /* smtp_connect_addr - connect to explicit address */ -static SMTP_SESSION *smtp_connect_addr(SMTP_STATE *state, DNS_RR *addr, - unsigned port) +static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port, + VSTRING *why) { char *myname = "smtp_connect_addr"; struct sockaddr_in sin; @@ -111,8 +112,11 @@ static SMTP_SESSION *smtp_connect_addr(SMTP_STATE *state, DNS_RR *addr, /* * Sanity checks. */ - if (addr->data_len > sizeof(sin.sin_addr)) - msg_panic("%s: unexpected address length %d", myname, addr->data_len); + if (addr->data_len > sizeof(sin.sin_addr)) { + msg_warn("%s: skip address with length %d", myname, addr->data_len); + smtp_errno = SMTP_RETRY; + return (0); + } /* * Initialize. @@ -174,8 +178,9 @@ static SMTP_SESSION *smtp_connect_addr(SMTP_STATE *state, DNS_RR *addr, conn_stat = sane_connect(sock, (struct sockaddr *) & sin, sizeof(sin)); } if (conn_stat < 0) { - smtp_site_fail(state, 450, "connect to %s[%s] port %u: %m", - addr->name, inet_ntoa(sin.sin_addr), ntohs(port)); + vstring_sprintf(why, "connect to %s[%s]: %m", + addr->name, inet_ntoa(sin.sin_addr)); + smtp_errno = SMTP_RETRY; close(sock); return (0); } @@ -184,8 +189,9 @@ static SMTP_SESSION *smtp_connect_addr(SMTP_STATE *state, DNS_RR *addr, * Skip this host if it takes no action within some time limit. */ if (read_wait(sock, var_smtp_helo_tmout) < 0) { - smtp_site_fail(state, 450, "connect to %s[%s] port %u: read timeout", - addr->name, inet_ntoa(sin.sin_addr), ntohs(port)); + vstring_sprintf(why, "connect to %s[%s]: read timeout", + addr->name, inet_ntoa(sin.sin_addr)); + smtp_errno = SMTP_RETRY; close(sock); return (0); } @@ -195,14 +201,35 @@ static SMTP_SESSION *smtp_connect_addr(SMTP_STATE *state, DNS_RR *addr, */ stream = vstream_fdopen(sock, O_RDWR); if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) { - smtp_site_fail(state, 450, "connect to %s[%s] port %u: " - "server dropped connection without sending the initial SMTP greeting", - addr->name, inet_ntoa(sin.sin_addr), ntohs(port)); + vstring_sprintf(why, "connect to %s[%s]: server dropped connection without sending the initial SMTP greeting", + addr->name, inet_ntoa(sin.sin_addr)); + smtp_errno = SMTP_RETRY; vstream_fclose(stream); return (0); } vstream_ungetc(stream, ch); + /* + * Skip this host if it sends a 4xx greeting. + */ + if (ch == '4' && var_smtp_skip_4xx_greeting) { + vstring_sprintf(why, "connect to %s[%s]: server refused mail service", + addr->name, inet_ntoa(sin.sin_addr)); + smtp_errno = SMTP_RETRY; + vstream_fclose(stream); + return (0); + } + + /* + * Skip this host if it sends a 5xx greeting. + */ + if (ch == '5' && var_smtp_skip_5xx_greeting) { + vstring_sprintf(why, "connect to %s[%s]: server refused mail service", + addr->name, inet_ntoa(sin.sin_addr)); + smtp_errno = SMTP_RETRY; + vstream_fclose(stream); + return (0); + } return (smtp_session_alloc(stream, addr->name, inet_ntoa(sin.sin_addr))); } @@ -246,6 +273,7 @@ static char *smtp_parse_destination(char *destination, char *def_service, int smtp_connect(SMTP_STATE *state) { DELIVER_REQUEST *request = state->request; + VSTRING *why = vstring_alloc(10); char *dest_buf; char *host; unsigned port; @@ -272,22 +300,21 @@ int smtp_connect(SMTP_STATE *state) argv_split_append(sites, var_fallback_relay, ", \t\r\n"); /* - * Don't give up after any soft error until we have tried all servers. + * Don't give up after a qualifying soft error until we have tried all + * qualifying mail servers. * * Don't give up after a hard host lookup error until we have tried the * fallback relay servers. * - * Don't bounce mail after host lookup problems with a relayhost or with - * fallback relays. + * Don't bounce mail after a host lookup problem with a relayhost or with a + * fallback relay. * * All this means that error handling and error reporting depends on whether - * there are more mail servers (state->final_server), or whether we're - * looking up a relayhost or fallback relay (state->backup_server). + * the error qualifies for trying more mail servers, or whether we're + * looking up a relayhost or fallback relay. */ for (cpp = sites->argv; (dest = *cpp) != 0; cpp++) { state->final_server = (cpp[1] == 0); - state->backup_server = - (cpp > sites->argv || strcmp(request->nexthop, var_relayhost) == 0); /* * Parse the destination. Default is to use the SMTP port. Look up @@ -295,37 +322,36 @@ int smtp_connect(SMTP_STATE *state) * specified, or when DNS lookups are disabled. */ dest_buf = smtp_parse_destination(dest, def_service, &host, &port); + + /* + * Resolve an SMTP server. Skip mail exchanger lookups when a quoted + * host is specified, or when DNS lookups are disabled. + */ if (msg_verbose) - msg_info("connecting to \"%s\" port \"%d\"", host, ntohs(port)); + msg_info("connecting to %s port %d", host, ntohs(port)); if (var_disable_dns || *dest == '[') { - addr_list = smtp_host_addr(state, host); + addr_list = smtp_host_addr(host, why); } else { - addr_list = smtp_domain_addr(state, host); + addr_list = smtp_domain_addr(host, why); } myfree(dest_buf); /* - * No address list. The mail has / has not been delivered. What to do - * next (skip remaining hosts / try to deliver) is recorded in the - * state->final_server attribute. + * Don't try the fall-back relay if mail loops to myself. */ - if (addr_list == 0) { - if (state->backup_server) - msg_warn("%s or %s configuration problem", - VAR_RELAYHOST, VAR_FALLBACK_RELAY); - } + if (addr_list == 0 && smtp_errno == SMTP_LOOP) + break; /* * Connect to an SMTP server. XXX Limit the number of addresses that * we're willing to try for a non-fallback destination. * - * After a soft error, log deferrals and update delivery status values - * only when there are no further attempts. + * After a soft error, weed out the recipient list and if there are any + * left, try again. */ for (addr = addr_list; addr; addr = addr->next) { - if ((state->session = smtp_connect_addr(state, addr, port)) != 0) { + if ((state->session = smtp_connect_addr(addr, port, why)) != 0) { state->final_server = (cpp[1] == 0 && addr->next == 0); - state->status = 0; state->session->best = (addr->pref == addr_list->pref); debug_peer_check(state->session->host, state->session->addr); if (smtp_helo(state) == 0) @@ -337,23 +363,71 @@ int smtp_connect(SMTP_STATE *state) /* XXX smtp_xfer() may abort in the middle of DATA. */ smtp_session_free(state->session); debug_peer_restore(); - if (state->status == 0 || state->final_server) + if (smtp_weed_request(&request->rcpt_list) == 0) break; + } else { + msg_info("%s (port %d)", vstring_str(why), ntohs(port)); } } dns_rr_free(addr_list); + } + + /* + * We still need to deliver, bounce or defer some recipients. + * + * Pay attention to what could be configuration problems, and pretend that + * these are recoverable rather than bouncing the mail. + */ + if (request->rcpt_list.len > 0) { + if (smtp_errno != SMTP_RETRY) { + + /* + * The fall-back destination did not resolve as expected, or it + * is refusing to talk to us. + */ + if (sites->argc > 1 && cpp > sites->argv) { + msg_warn("%s configuration problem", VAR_FALLBACK_RELAY); + smtp_errno = SMTP_RETRY; + } + + /* + * The next-hop relayhost did not resolve as expected, or it is + * refusing to talk to us. + */ + else if (strcmp(sites->argv[0], var_relayhost) == 0) { + msg_warn("%s configuration problem", VAR_RELAYHOST); + smtp_errno = SMTP_RETRY; + } + + /* + * Mail for the next-hop destination loops back to myself. + */ + else if (smtp_errno == SMTP_LOOP && *var_bestmx_transp) { + state->status = deliver_pass_all(MAIL_CLASS_PRIVATE, + var_bestmx_transp, + request); + smtp_errno = SMTP_NONE; + } + } /* - * In case someone has raised the "final server" flag before we have - * tried all fallback servers. + * We still need to bounce or defer some recipients. Do it now or + * else they would silently disappear due to lack of error + * indication. */ - if (state->final_server) - break; + if (smtp_errno != SMTP_NONE) { + if (!state->final_server) + msg_panic("smtp_connect: we have left-over recipients but " + "we did not try to connect to the final server"); + smtp_site_fail(state, smtp_errno == SMTP_RETRY ? 450 : 550, + "%s", vstring_str(why)); + } } /* * Cleanup. */ argv_free(sites); + vstring_free(why); return (state->status); } diff --git a/postfix/src/smtp/smtp_misc.c b/postfix/src/smtp/smtp_misc.c new file mode 100644 index 000000000..f9b2a3d7e --- /dev/null +++ b/postfix/src/smtp/smtp_misc.c @@ -0,0 +1,81 @@ +/* System library. */ + +#include +#include /* smtp_weed_request */ + +/* Utility library. */ + +#include + +/* Global library. */ + +#include /* smtp_rcpt_done */ +#include /* smtp_rcpt_done */ +#include /* smtp_rcpt_done */ + +/* Application-specific. */ + +#include + +/* smtp_rcpt_done - mark recipient as done or else */ + +void smtp_rcpt_done(SMTP_STATE *state, const char *reply, RECIPIENT *rcpt) +{ + DELIVER_REQUEST *request = state->request; + SMTP_SESSION *session = state->session; + int status; + + /* + * Report success and delete the recipient from the delivery request. + * Defer if the success can't be reported. + */ + status = sent(DEL_REQ_TRACE_FLAGS(request->flags), + request->queue_id, rcpt->orig_addr, + rcpt->address, rcpt->offset, + session->namaddr, + request->arrival_time, + "%s", reply); + if (status == 0) + if (request->flags & DEL_REQ_FLAG_SUCCESS) + deliver_completed(state->src, rcpt->offset); + rcpt->status = SMTP_RCPT_DROP; + state->status |= status; +} + +/* smtp_weed_request_callback - qsort callback */ + +static int smtp_weed_request_callback(const void *a, const void *b) +{ + return (((RECIPIENT *) a)->status - ((RECIPIENT *) b)->status); +} + +/* smtp_weed_request - purge completed recipients from request */ + +int smtp_weed_request(RECIPIENT_LIST *rcpt_list) +{ + RECIPIENT *rcpt; + int nrcpt; + + /* + * Status codes one can expect to find: SMTP_RCPT_KEEP (try recipient + * another time), SMTP_RCPT_DROP (remove recipient from request) and zero + * (error: after delivery attempt, recipient status should be either KEEP + * or DROP). + */ + if (rcpt_list->len > 1) + qsort((void *) rcpt_list->info, rcpt_list->len, + sizeof(rcpt_list->info), smtp_weed_request_callback); + + for (nrcpt = 0; nrcpt < rcpt_list->len; nrcpt++) { + rcpt = rcpt_list->info + nrcpt; + if (rcpt->status == SMTP_RCPT_KEEP) + rcpt->status = 0; + if (rcpt->status == SMTP_RCPT_DROP) + break; + else + msg_panic("smtp_weed_request: bad status: %d for <%s>", + rcpt->status, rcpt->address); + } + recipient_list_truncate(rcpt_list, nrcpt); + return (nrcpt); +} diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 849c31f79..438cffe76 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -93,10 +93,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -197,13 +195,10 @@ int smtp_helo(SMTP_STATE *state) /* * Read and parse the server's SMTP greeting banner. */ - if ((resp = smtp_chat_resp(state))->code / 100 != 2) { - if (var_smtp_skip_5xx_greeting && resp->code / 100 == '5') - resp->code -= 100; + if ((resp = smtp_chat_resp(state))->code / 100 != 2) return (smtp_site_fail(state, resp->code, "host %s refused to talk to me: %s", session->namaddr, translit(resp->str, "\n", " "))); - } /* * XXX Some PIX firewall versions require flush before "." so it @@ -436,6 +431,16 @@ int smtp_xfer(SMTP_STATE *state) #define SENDING_MAIL \ (recv_state <= SMTP_STATE_DOT) + /* + * Sanity check. Recipients should be unmarked at this point. + */ + if (request->rcpt_list.len <= 0) + msg_panic("smtp_xfer: bad recipient count: %d", + request->rcpt_list.len); + if (request->rcpt_list.info->status != 0) + msg_panic("smtp_xfer: bad recipient status: %d", + request->rcpt_list.info->status); + /* * See if we should even try to send this message at all. This code sits * here rather than in the EHLO processing code, because of future SMTP @@ -763,24 +768,14 @@ int smtp_xfer(SMTP_STATE *state) if (resp->code / 100 == 2) { ++nrcpt; /* If trace-only, mark the recipient done. */ - if (DEL_REQ_TRACE_ONLY(request->flags) - && sent(DEL_REQ_TRACE_FLAGS(request->flags), - request->queue_id, rcpt->orig_addr, - rcpt->address, rcpt->offset, - session->namaddr, request->arrival_time, - "%s", - translit(resp->str, "\n", " ")) == 0) { - if (request->flags & DEL_REQ_FLAG_SUCCESS) - deliver_completed(state->src, rcpt->offset); - rcpt->offset = 0; /* in case deferred */ - } + if (DEL_REQ_TRACE_ONLY(request->flags)) + smtp_rcpt_done(state, resp->str, rcpt); } else { smtp_rcpt_fail(state, resp->code, rcpt, "host %s said: %s (in reply to %s)", session->namaddr, translit(resp->str, "\n", " "), xfer_request[SMTP_STATE_RCPT]); - rcpt->offset = 0; /* in case deferred */ } } /* If trace-only, send RSET instead of DATA. */ @@ -827,18 +822,8 @@ int smtp_xfer(SMTP_STATE *state) } else { for (nrcpt = 0; nrcpt < recv_rcpt; nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; - if (rcpt->offset) { - if (sent(DEL_REQ_TRACE_FLAGS(request->flags), - request->queue_id, rcpt->orig_addr, - rcpt->address, rcpt->offset, - session->namaddr, - request->arrival_time, - "%s", resp->str) == 0) { - if (request->flags & DEL_REQ_FLAG_SUCCESS) - deliver_completed(state->src, rcpt->offset); - rcpt->offset = 0; - } - } + if (rcpt->status == 0) + smtp_rcpt_done(state, resp->str, rcpt); } } } diff --git a/postfix/src/smtp/smtp_state.c b/postfix/src/smtp/smtp_state.c index ac63d27f6..726feb5ce 100644 --- a/postfix/src/smtp/smtp_state.c +++ b/postfix/src/smtp/smtp_state.c @@ -73,7 +73,6 @@ SMTP_STATE *smtp_state_alloc(void) state->space_left = 0; state->mime_state = 0; state->final_server = 0; - state->backup_server = 0; return (state); } diff --git a/postfix/src/smtp/smtp_trouble.c b/postfix/src/smtp/smtp_trouble.c index 53a1b1ec2..628195597 100644 --- a/postfix/src/smtp/smtp_trouble.c +++ b/postfix/src/smtp/smtp_trouble.c @@ -33,7 +33,9 @@ /* the problem, delivery of a single message is deferred, delivery /* of all messages to the same domain is deferred, or one or more /* recipients are given up as non-deliverable and a bounce log is -/* updated. +/* updated. In any case, the recipient status is updated to either +/* SMTP_RCPT_KEEP (try again with a backup host) or SMTP_RCPT_DROP +/* (delete recipient from delivery request). /* /* In addition, when an unexpected response code is seen such /* as 3xx where only 4xx or 5xx are expected, or any error code @@ -43,15 +45,11 @@ /* what appear to be configuration errors - very likely, they /* would suffer the same problem and just cause more trouble. /* -/* In case of a soft error, action depends on whether there are -/* more mail servers (log an informational record only and try -/* the other servers) or whether this is the final server (log -/* recipient delivery status records). -/* -/* In the case of a hard error that affects all recipients, -/* recipient delivery status records are logged, and the -/* final server flag is raised so that any remaining mail -/* servers are skipped. +/* In case of a soft error, action depends on whether the error +/* qualifies for trying the request with other mail servers (log +/* an informational record only and try the a backup server) or +/* whether this is the final server (log recipient delivery status +/* records and delete the recipient from the request). /* /* smtp_site_fail() handles the case where the program fails to /* complete the initial SMTP handshake: the server is not reachable, @@ -60,9 +58,8 @@ /* argument gives a textual description. /* The policy is: soft error, non-final server: log an informational /* record why the host is being skipped; soft error, final server: -/* defer delivery of all remaining recipients; hard error: bounce all -/* remaining recipients and set the "final server" flag so that any -/* remaining mail servers will be skipped. +/* defer delivery of all remaining recipients and mark the destination +/* a problematic; hard error: bounce all remaining recipients. /* The result is non-zero. /* /* smtp_mesg_fail() handles the case where the smtp server @@ -70,8 +67,7 @@ /* The policy is: soft error, non-final server: log an informational /* record why the host is being skipped; soft error, final server: /* defer delivery of all remaining recipients; hard error: bounce all -/* remaining recipients and set the "final server" flag so that any -/* remaining mail servers will be skipped. +/* remaining recipients. /* The result is non-zero. /* /* smtp_rcpt_fail() handles the case where a recipient is not @@ -80,7 +76,7 @@ /* The policy is: soft error, non-final server: log an informational /* record why the recipient is being skipped; soft error, final server: /* defer delivery of this recipient; hard error: bounce this -/* recipient. This routine does not change the "final server" flag. +/* recipient. /* /* smtp_stream_except() handles the exceptions generated by /* the smtp_stream(3) module (i.e. timeouts and I/O errors). @@ -159,7 +155,7 @@ static void smtp_check_code(SMTP_STATE *state, int code) state->error_mask |= MAIL_ERROR_PROTOCOL; } -/* smtp_site_fail - skip site, defer all recipients, or bounce all recipients */ +/* smtp_site_fail - skip site, defer or bounce all recipients */ int smtp_site_fail(SMTP_STATE *state, int code, char *format,...) { @@ -180,38 +176,44 @@ int smtp_site_fail(SMTP_STATE *state, int code, char *format,...) va_end(ap); /* - * Don't defer the recipients just yet when there are still more mail - * servers. Just log something informative to show why we're skipping - * this host. + * Don't defer the recipients just yet when this error qualifies them for + * delivery to a backup server. Just log something informative to show + * why we're skipping this host. */ - if (soft_error && state->final_server == 0) { + if (soft_error && state->final_server == 0 + && (smtp_backup_mask & SMTP_BACKUP_SESSION_FAILURE)) { msg_info("%s: %s", request->queue_id, vstring_str(why)); - state->status |= -1; + for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { + rcpt = request->rcpt_list.info + nrcpt; + if (rcpt->status != 0) + continue; + rcpt->status = SMTP_RCPT_KEEP; + } } /* - * Defer or bounce all the remaining recipients and raise the final mail - * server flag. + * Defer or bounce all the remaining recipients, and delete them from the + * delivery request. If a bounce fails, defer instead and do not qualify + * the recipient for delivery to a backup server. */ else { for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; - if (rcpt->offset == 0) + if (rcpt->status != 0) continue; status = (soft_error ? defer_append : bounce_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, rcpt->orig_addr, rcpt->address, rcpt->offset, session ? session->namaddr : "none", request->arrival_time, "%s", vstring_str(why)); - if (status == 0) { + if (status == 0) deliver_completed(state->src, rcpt->offset); - rcpt->offset = 0; - } + rcpt->status = SMTP_RCPT_DROP; state->status |= status; } + /* XXX This assumes no fall-back relay. */ if (soft_error && request->hop_status == 0) request->hop_status = mystrdup(vstring_str(why)); - state->final_server = 1; } smtp_check_code(state, code); @@ -243,36 +245,41 @@ int smtp_mesg_fail(SMTP_STATE *state, int code, char *format,...) va_end(ap); /* - * Don't defer the recipients just yet when there are still more mail - * servers. Just log something informative to show why we're skipping - * this host. + * Don't defer the recipients just yet when this error qualifies them for + * delivery to a backup server. Just log something informative to show + * why we're skipping this host. */ - if (soft_error && state->final_server == 0) { + if (soft_error && state->final_server == 0 + && (smtp_backup_mask & SMTP_BACKUP_MESSAGE_FAILURE)) { msg_info("%s: %s", request->queue_id, vstring_str(why)); - state->status |= -1; + for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { + rcpt = request->rcpt_list.info + nrcpt; + if (rcpt->status != 0) + continue; + rcpt->status = SMTP_RCPT_KEEP; + } } /* - * Defer or bounce all the remaining recipients and raise the final mail - * server flag. + * Defer or bounce all the remaining recipients, and delete them from the + * delivery request. If a bounce fails, defer instead and do not qualify + * the recipient for delivery to a backup server. */ else { for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; - if (rcpt->offset == 0) + if (rcpt->status != 0) continue; status = (soft_error ? defer_append : bounce_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, rcpt->orig_addr, rcpt->address, rcpt->offset, session->namaddr, request->arrival_time, "%s", vstring_str(why)); - if (status == 0) { + if (status == 0) deliver_completed(state->src, rcpt->offset); - rcpt->offset = 0; - } + rcpt->status = SMTP_RCPT_DROP; state->status |= status; } - state->final_server = 1; } smtp_check_code(state, code); @@ -295,31 +302,29 @@ void smtp_rcpt_fail(SMTP_STATE *state, int code, RECIPIENT *rcpt, va_list ap; /* - * Don't defer this recipient record just yet when there are still more - * mail servers. Just log something informative to show why we're - * skipping this recipient now. + * Don't defer this recipient record just yet when this error qualifies + * for trying other mail servers. Just log something informative to show + * why we're skipping this recipient now. */ - if (soft_error && state->final_server == 0) { - VSTRING *buf = vstring_alloc(10); + if (soft_error && state->final_server == 0 + && (smtp_backup_mask & SMTP_BACKUP_RECIPIENT_FAILURE)) { + VSTRING *buf = vstring_alloc(100); va_start(ap, format); vstring_vsprintf(buf, format, ap); va_end(ap); msg_info("%s: %s", request->queue_id, vstring_str(buf)); + rcpt->status = SMTP_RCPT_KEEP; vstring_free(buf); - status = -1; } /* - * Defer or bounce this specific recipient. + * Defer or bounce this recipient, and delete from the delivery request. + * If the bounce fails, defer instead and do not qualify the recipient + * for delivery to a backup server. * - * If this is a hard error, we must not raise the final mail server flag. We - * may still make another SMTP connection to deliver deferred recipients. - * - * If this is a soft error, we got here because the final mail server flag - * was already set. - * - * So don't touch that final mail server flag! + * Note: we may still make an SMTP connection to deliver other recipients + * that did qualify for delivery to a backup server. */ else { va_start(ap, format); @@ -328,13 +333,12 @@ void smtp_rcpt_fail(SMTP_STATE *state, int code, RECIPIENT *rcpt, rcpt->orig_addr, rcpt->address, rcpt->offset, session->namaddr, request->arrival_time, format, ap); va_end(ap); - if (status == 0) { + if (status == 0) deliver_completed(state->src, rcpt->offset); - rcpt->offset = 0; - } + rcpt->status = SMTP_RCPT_DROP; + state->status |= status; } smtp_check_code(state, code); - state->status |= status; } /* smtp_stream_except - defer domain after I/O problem */ @@ -364,21 +368,29 @@ int smtp_stream_except(SMTP_STATE *state, int code, char *description) } /* - * Don't defer the recipients just yet when there are still more mail - * servers. Just log why we're abandoning this host. + * Don't defer the recipients just yet when this error qualifies them for + * delivery to a backup server. Just log something informative to show + * why we're skipping this host. */ - if (state->final_server == 0) { + if (state->final_server == 0 + && (smtp_backup_mask & SMTP_BACKUP_SESSION_FAILURE)) { msg_info("%s: %s", request->queue_id, vstring_str(why)); - state->status |= -1; + for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { + rcpt = request->rcpt_list.info + nrcpt; + if (rcpt->status != 0) + continue; + rcpt->status = SMTP_RCPT_KEEP; + } } /* - * Final server. Defer all the remaining recipients. + * Defer all the remaining recipients and drop them from the delivery + * request. */ else { for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; - if (rcpt->offset == 0) + if (rcpt->status != 0) continue; state->status |= defer_append(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, @@ -386,6 +398,7 @@ int smtp_stream_except(SMTP_STATE *state, int code, char *description) rcpt->offset, session->namaddr, request->arrival_time, "%s", vstring_str(why)); + rcpt->status = SMTP_RCPT_DROP; } } diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index b9913e715..649c1dd55 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -875,6 +875,7 @@ extern int h_errno; #define STATVFS_IN_SYS_STATVFS_H #define UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT #define MISSING_SETENV +#define STRCASECMP_IN_STRINGS_H /* SCO5 misses just S_ISSOCK, the others are there * Use C_ISSOCK definition from cpio.h. */