destination. The default limit is taken from the
<b>default_destination_concurrency_limit</b> parameter.
+ NB: This limit is enforced by the queue manager.
+
<b>smtp_destination_recipient_limit</b>
Limit the number of recipients per message deliv-
ery. The default limit is taken from the
<b>default_destination_recipient_limit</b> parameter.
+ <b>smtp_mx_address_limit</b>
+ An upper bound on the number of MX (mail exchanger)
+ IP addresses that the SMTP client will try to con-
+ nect to, before giving up or sending the mail to a
+ fall-back relay host.
+
+ Specify zero to disable the limit.
+
+ <b>smtp_mx_session_limit</b>
+ An upper bound on the number of SMTP sessions that
+ the SMTP client will engage in before giving up or
+ sending the mail to a fall-back relay host.
+
+ Specify zero to disable the limit.
+
+ <b>smtp_backup_on_soft_error</b>
+ The types of recoverable error that qualify for
+ sending a recipient to a backup mail server or to a
+ fall-back relay host. Specify zero or more of <b>ses-</b>
+ <b>sion</b> (SMTP handshake failure, connection loss),
+ <b>message</b> (failure of MAIL FROM, DATA or "."), or
+ <b>recipient</b> (failure of RCPT TO).
+
+ Recipients that do not qualify are deferred.
+
<b>Timeout controls</b>
The default time unit is seconds; an explicit time unit
can be specified by appending a one-letter suffix to the
Limit the number of parallel deliveries to the same destination.
The default limit is taken from the
\fBdefault_destination_concurrency_limit\fR parameter.
+.sp
+NB: This limit is enforced by the queue manager.
.IP \fBsmtp_destination_recipient_limit\fR
Limit the number of recipients per message delivery.
The default limit is taken from the
\fBdefault_destination_recipient_limit\fR parameter.
+.IP \fBsmtp_mx_address_limit\fR
+An upper bound on the number of MX (mail exchanger) IP addresses
+that the SMTP client will try to connect to, before giving up or
+sending the mail to a fall-back relay host.
+.sp
+Specify zero to disable the limit.
+.IP \fBsmtp_mx_session_limit\fR
+An upper bound on the number of SMTP sessions that the SMTP client
+will engage in before giving up or sending the mail to a fall-back
+relay host.
+.sp
+Specify zero to disable the limit.
+.IP \fBsmtp_backup_on_soft_error\fR
+The types of recoverable error that qualify for sending a
+recipient to a backup mail server or to a fall-back relay host.
+Specify zero or more of \fBsession\fR (SMTP handshake failure,
+connection loss), \fBmessage\fR (failure of MAIL FROM, DATA or
+"."), or \fBrecipient\fR (failure of RCPT TO).
+.sp
+Recipients that do not qualify are deferred.
.SH "Timeout controls"
.ad
.fi
" " SMTP_BACKUP_MESSAGE \
" " SMTP_BACKUP_RECIPIENT
+#define VAR_SMTP_MXADDR_LIMIT "smtp_mx_address_limit"
+#define DEF_SMTP_MXADDR_LIMIT 0
+extern int var_smtp_mxaddr_limit;
+
+#define VAR_SMTP_MXSESS_LIMIT "smtp_mx_session_limit"
+#define DEF_SMTP_MXSESS_LIMIT 2
+extern int var_smtp_mxsess_limit;
+
/*
* Location of the mail queue directory tree.
*/
* 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 "20031219"
+#define MAIL_RELEASE_DATE "20031220"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE
--- /dev/null
+/*++
+/* NAME
+/* peer_name 3
+/* SUMMARY
+/* produce printable peer name and address
+/* SYNOPSIS
+/* #include <peer_name.h>
+/*
+/* typedef struct {
+/* .in +4
+/* int type;
+/* char name;
+/* char addr;
+/* .in -4
+/* } PEER_NAME;
+/*
+/* PEER_NAME *peer_name(sock)
+/* int sock;
+/* DESCRIPTION
+/* The \fIpeer_name\fR() routine attempts to produce a printable
+/* version of the peer name and address of the specified socket.
+/* The result is in static memory that will be overwritten.
+/* Make a copy if the result is to be used for an appreciable
+/* amount of time.
+/*
+/* Where information is unavailable, the name and/or address
+/* are set to "unknown".
+/* The \fItype\fR result field specifies how the name and address
+/* should be interpreted:
+/* .IP PEER_TYPE_INET
+/* The socket specifies a TCP/IP endpoint.
+/* The result is a hostname (from the DNS, a local hosts file or
+/* other); the address a dotted quad.
+/* .IP PEER_TYPE_LOCAL
+/* The socket argument specifies a local transport.
+/* The result name is "localhost"; the result address is "127.0.0.1".
+/* .IP PEER_TYPE_UNKNOWN
+/* The socket argument does not specify a socket.
+/* The result name is "localhost"; the result address is "127.0.0.1".
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <valid_hostname.h>
+#include <peer_name.h>
+
+/* peer_name - produce printable peer name and address */
+
+PEER_NAME *peer_name(int sock)
+{
+ static PEER_NAME peer;
+ struct sockaddr_in sin;
+ SOCKADDR_SIZE len = sizeof(sin);
+ struct hostent *hp;
+
+ if (getpeername(sock, (struct sockaddr *) & sin, &len) == 0) {
+ switch (sin.sin_family) {
+ case AF_INET:
+ peer.type = PEER_TYPE_INET;
+ hp = gethostbyaddr((char *) &(sin.sin_addr),
+ sizeof(sin.sin_addr), AF_INET);
+ peer.name = (hp && valid_hostname(hp->h_name, DO_GRIPE) ?
+ hp->h_name : "unknown");
+ peer.addr = inet_ntoa(sin.sin_addr);
+ return (&peer);
+ case AF_UNSPEC:
+ case AF_UNIX:
+ peer.type = PEER_TYPE_LOCAL;
+ peer.name = "localhost";
+ peer.addr = "127.0.0.1";
+ return (&peer);
+ }
+ }
+ peer.type = PEER_TYPE_UNKNOWN;
+ peer.name = "localhost";
+ peer.addr = "127.0.0.1";
+ return (&peer);
+
+}
+
+#ifdef TEST
+
+#include <unistd.h>
+
+int main(int unused_argc, char **unused_argv)
+{
+ PEER_NAME *peer;
+
+ peer = peer_name(STDIN_FILENO);
+ msg_info("name %s addr %s", peer->name, peer->addr);
+}
+
+#endif
--- /dev/null
+#ifndef _PEER_NAME_H_INCLUDED_
+#define _PEER_NAME_H_INCLUDED_
+
+/*++
+/* NAME
+/* peer_name 3h
+/* SUMMARY
+/* produce printable peer name and address
+/* SYNOPSIS
+/* #include <peer_name.h>
+/* DESCRIPTION
+
+ /*
+ * External interface.
+ */
+typedef struct {
+ int type; /* IPC type, see below */
+ char *name; /* peer official name */
+ char *addr; /* peer address */
+} PEER_NAME;
+
+#define PEER_TYPE_UNKNOWN 0
+#define PEER_TYPE_INET 1
+#define PEER_TYPE_LOCAL 2
+
+extern PEER_NAME *peer_name(int);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
void recipient_list_free(RECIPIENT_LIST *list)
{
- recipient_list_truncate(list, 0);
+ if (list->len > 0)
+ recipient_list_truncate(list, 0);
myfree((char *) list->info);
}
/* Limit the number of parallel deliveries to the same destination.
/* The default limit is taken from the
/* \fBdefault_destination_concurrency_limit\fR parameter.
+/* .sp
+/* NB: This limit is enforced by the queue manager.
/* .IP \fBsmtp_destination_recipient_limit\fR
/* Limit the number of recipients per message delivery.
/* The default limit is taken from the
/* \fBdefault_destination_recipient_limit\fR parameter.
+/* .IP \fBsmtp_mx_address_limit\fR
+/* An upper bound on the number of MX (mail exchanger) IP addresses
+/* that the SMTP client will try to connect to, before giving up or
+/* sending the mail to a fall-back relay host.
+/* .sp
+/* Specify zero to disable the limit.
+/* .IP \fBsmtp_mx_session_limit\fR
+/* An upper bound on the number of SMTP sessions that the SMTP client
+/* will engage in before giving up or sending the mail to a fall-back
+/* relay host.
+/* .sp
+/* Specify zero to disable the limit.
+/* .IP \fBsmtp_backup_on_soft_error\fR
+/* The types of recoverable error that qualify for sending a
+/* recipient to a backup mail server or to a fall-back relay host.
+/* Specify zero or more of \fBsession\fR (SMTP handshake failure,
+/* connection loss), \fBmessage\fR (failure of MAIL FROM, DATA or
+/* "."), or \fBrecipient\fR (failure of RCPT TO).
+/* .sp
+/* Recipients that do not qualify are deferred.
/* .SH "Timeout controls"
/* .ad
/* .fi
bool var_smtp_quote_821_env;
bool var_smtp_defer_mxaddr;
bool var_smtp_send_xforward;
+int var_smtp_mxaddr_limit;
+int var_smtp_mxsess_limit;
/*
* Global variables. smtp_errno is set by the address lookup routines and by
};
static CONFIG_INT_TABLE int_table[] = {
VAR_SMTP_LINE_LIMIT, DEF_SMTP_LINE_LIMIT, &var_smtp_line_limit, 0, 0,
+ VAR_SMTP_MXADDR_LIMIT, DEF_SMTP_MXADDR_LIMIT, &var_smtp_mxaddr_limit, 0, 0,
+ VAR_SMTP_MXSESS_LIMIT, DEF_SMTP_MXSESS_LIMIT, &var_smtp_mxsess_limit, 0, 0,
0,
};
static CONFIG_BOOL_TABLE bool_table[] = {
off_t size_limit; /* server limit or unknown */
int space_left; /* output length control */
struct MIME_STATE *mime_state; /* mime state machine */
+
+ /*
+ * Flags and counters to control the handling of mail delivery errors.
+ * There is some redundancy for sanity checking. At the end of an SMTP
+ * session all recipients should be marked one way or the other.
+ */
int final_server; /* final mail server */
+ int drop_count; /* recipients marked as drop */
+ int keep_count; /* recipients marked as keep */
} SMTP_STATE;
/*
#define SMTP_FEATURE_XFORWARD_HELO (1<<10)
/*
- * Application-specific per-recipient status.
+ * Application-specific per-recipient status. At the end of each delivery
+ * attempt each recipient is marked as DROP (remove from recipient list) or
+ * KEEP (deliver to backup mail server). The ones marked DROP are deleted
+ * before trying to deliver the remainder to a backup server. There's a bit
+ * of redundcancy to ensure that all recipients are marked.
+ *
+ * The single recipient list abstraction dates from the time that the SMTP
+ * client would give up after one SMTP session, so that each recipient was
+ * either bounced, delivered or deferred. Implicitly, all recipients were
+ * marked as DROP.
+ *
+ * This abstraction is less convenient when an SMTP client must be able to
+ * deliver left-over recipients to a backup host. It might be more natural
+ * to have an input list with recipients to deliver, and an output list with
+ * the left-over recipients.
*/
#define SMTP_RCPT_KEEP 1 /* send to backup host */
-#define SMTP_RCPT_DROP 2 /* remove from list */
+#define SMTP_RCPT_DROP 2 /* remove from request */
+
+#define SMTP_RCPT_MARK_INIT(state) do { \
+ (state)->drop_count = (state)->keep_count = 0; \
+ } while (0)
+
+#define SMTP_RCPT_MARK_DROP(state, rcpt) do { \
+ (rcpt)->status = SMTP_RCPT_DROP; (state)->drop_count++; \
+ } while (0)
+
+#define SMTP_RCPT_MARK_KEEP(state, rcpt) do { \
+ (rcpt)->status = SMTP_RCPT_KEEP; (state)->keep_count++; \
+ } while (0)
+
+#define SMTP_RCPT_MARK_ISSET(rcpt) ((rcpt)->status != 0)
+
+extern int smtp_rcpt_mark_finish(SMTP_STATE *);
/*
* smtp.c
* smtp_misc.c.
*/
extern void smtp_rcpt_done(SMTP_STATE *, const char *, RECIPIENT *);
-extern int smtp_weed_request(RECIPIENT_LIST *);
/*
* smtp_trouble.c
char **cpp;
DNS_RR *addr_list;
DNS_RR *addr;
+ DNS_RR *next;
+ int addr_count;
+ int sess_count;
/*
* First try to deliver to the indicated destination, then try to deliver
* to the optional fall-back relays.
*
* Future proofing: do a null destination sanity check in case we allow the
- * primary destination to be a list, as it could be just a bunch of
- * separators.
+ * primary destination to be a list (it could be just separators).
*/
sites = argv_alloc(1);
argv_add(sites, request->nexthop, (char *) 0);
if (sites->argc == 0)
msg_panic("null destination: \"%s\"", request->nexthop);
- if (*var_fallback_relay)
- argv_split_append(sites, var_fallback_relay, ", \t\r\n");
+ argv_split_append(sites, var_fallback_relay, ", \t\r\n");
/*
- * 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 a host lookup problem with a relayhost or with a
* fallback relay.
*
+ * Don't give up after a qualifying soft error until we have tried all
+ * qualifying backup mail servers.
+ *
* All this means that error handling and error reporting depends on whether
- * the error qualifies for trying more mail servers, or whether we're
- * looking up a relayhost or fallback relay.
+ * the error qualifies for trying to deliver to a backup mail server, or
+ * whether we're looking up a relayhost or fallback relay. The challenge
+ * then is to build this into the pre-existing SMTP client without
+ * getting lost in the complexity.
*/
for (cpp = sites->argv; (dest = *cpp) != 0; cpp++) {
state->final_server = (cpp[1] == 0);
+ smtp_errno = SMTP_NONE;
/*
* Parse the destination. Default is to use the SMTP port. Look up
myfree(dest_buf);
/*
- * Don't try the fall-back relay if mail loops to myself.
+ * Don't try any backup host if mail loops to myself. That would just
+ * make the problem worse.
*/
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, weed out the recipient list and if there are any
- * left, try again.
+ * At the start of an SMTP session, all recipients are unmarked. In the
+ * course of an SMTP session, recipients are marked as KEEP (deliver
+ * to backup mail server) or DROP (remove from recipient list). The
+ * marking policy is configurable with the smtp_backup_on_soft_error
+ * parameter. At the end of an SMTP session, weed out the recipient
+ * list. Unmark any left-over recipients and try to deliver them to a
+ * backup mail server.
*/
- for (addr = addr_list; addr; addr = addr->next) {
+ for (sess_count = addr_count = 0, addr = addr_list; addr; addr = next) {
+ next = addr->next;
+ if (++addr_count == var_smtp_mxaddr_limit)
+ next = 0;
if ((state->session = smtp_connect_addr(addr, port, why)) != 0) {
- state->final_server = (cpp[1] == 0 && addr->next == 0);
+ if (++sess_count == var_smtp_mxsess_limit)
+ next = 0;
+ SMTP_RCPT_MARK_INIT(state);
+ state->final_server = (cpp[1] == 0 && next == 0);
state->session->best = (addr->pref == addr_list->pref);
debug_peer_check(state->session->host, state->session->addr);
if (smtp_helo(state) == 0)
/* XXX smtp_xfer() may abort in the middle of DATA. */
smtp_session_free(state->session);
debug_peer_restore();
- if (smtp_weed_request(&request->rcpt_list) == 0)
+ if (smtp_rcpt_mark_finish(state) == 0)
break;
} else {
msg_info("%s (port %d)", vstring_str(why), ntohs(port));
}
/*
- * We still need to deliver, bounce or defer some recipients.
+ * We still need to deliver, bounce or defer some left-over recipients:
+ * either mail loops or some backup mail server was unavailable.
*
* 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) {
+ switch (smtp_errno) {
+
+ default:
+ msg_panic("smtp_connect: bad error indication %d", smtp_errno);
+
+ case SMTP_LOOP:
+ case SMTP_FAIL:
/*
* The fall-back destination did not resolve as expected, or it
- * is refusing to talk to us.
+ * is refusing to talk to us, or mail for it loops back to us.
*/
if (sites->argc > 1 && cpp > sites->argv) {
msg_warn("%s configuration problem", VAR_FALLBACK_RELAY);
/*
* The next-hop relayhost did not resolve as expected, or it is
- * refusing to talk to us.
+ * refusing to talk to us, or mail for it loops back to us.
*/
else if (strcmp(sites->argv[0], var_relayhost) == 0) {
msg_warn("%s configuration problem", VAR_RELAYHOST);
}
/*
- * Mail for the next-hop destination loops back to myself.
+ * Mail for the next-hop destination loops back to myself. Pass
+ * the mail to the best_mx_transport or bounce it.
*/
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;
+ break;
}
- }
+ /* FALLTHROUGH */
- /*
- * 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 (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");
+ case SMTP_RETRY:
+
+ /*
+ * We still need to bounce or defer some left-over recipients:
+ * either mail loops or some backup mail server was unavailable.
+ */
smtp_site_fail(state, smtp_errno == SMTP_RETRY ? 450 : 550,
"%s", vstring_str(why));
+ break;
}
}
+/*++
+/* NAME
+/* smtp_misc 3
+/* SUMMARY
+/* assorted routines
+/* SYNOPSIS
+/* #include <smtp.h>
+/*
+/* void smtp_rcpt_done(state, reply, rcpt)
+/* SMTP_STATE *state;
+/* const char *reply;
+/* RECIPIENT *rcpt;
+/*
+/* int smtp_rcpt_mark_finish(SMTP_STATE *state)
+/* SMTP_STATE *state;
+/* DESCRIPTION
+/* smtp_rcpt_done() logs that a recipient is completed and upon
+/* success it marks the recipient as done in the queue file.
+/* Finally, it marks the in-memory recipient as DROP.
+/*
+/* smtp_rcpt_mark_finish() cleans up the in-memory recipient list.
+/* It deletes recipients marked DROP, and unmarks recipients marked KEEP.
+/* It enforces the requirement that all recipients are marked one way
+/* or the other. The result value is the number of left-over recipients.
+/* DIAGNOSTICS
+/* Panic: interface violation.
+/*
+/* When a recipient can't be logged as completed, the recipient is
+/* logged as deferred instead.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
/* System library. */
#include <sys_defs.h>
-#include <stdlib.h> /* smtp_weed_request */
+#include <stdlib.h> /* smtp_rcpt_mark_finish */
/* Utility library. */
if (status == 0)
if (request->flags & DEL_REQ_FLAG_SUCCESS)
deliver_completed(state->src, rcpt->offset);
- rcpt->status = SMTP_RCPT_DROP;
+ SMTP_RCPT_MARK_DROP(state, rcpt);
state->status |= status;
}
-/* smtp_weed_request_callback - qsort callback */
+/* smtp_rcpt_mark_finish_callback - qsort callback */
-static int smtp_weed_request_callback(const void *a, const void *b)
+static int smtp_rcpt_mark_finish_callback(const void *a, const void *b)
{
return (((RECIPIENT *) a)->status - ((RECIPIENT *) b)->status);
}
-/* smtp_weed_request - purge completed recipients from request */
+/* smtp_rcpt_mark_finish - purge completed recipients from request */
-int smtp_weed_request(RECIPIENT_LIST *rcpt_list)
+int smtp_rcpt_mark_finish(SMTP_STATE *state)
{
+ RECIPIENT_LIST *rcpt_list = &state->request->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).
+ * Sanity checks.
+ */
+ if (state->drop_count + state->keep_count != rcpt_list->len)
+ msg_panic("smtp_rcpt_mark_finish: recipient count mismatch: %d+%d!=%d",
+ state->drop_count, state->keep_count, rcpt_list->len);
+
+ /*
+ * Recipients marked KEEP sort before recipients marked DROP. Skip the
+ * sorting in the common case that all recipients are marked the same.
*/
- if (rcpt_list->len > 1)
+ if (state->drop_count > 0 && state->keep_count > 0)
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);
+ sizeof(rcpt_list->info), smtp_rcpt_mark_finish_callback);
+
+ /*
+ * Truncate the recipient list and unmark the left-over recipients so
+ * that the result looks like a brand-new recipient list.
+ */
+ if (state->keep_count < rcpt_list->len)
+ recipient_list_truncate(rcpt_list, state->keep_count);
+ for (rcpt = rcpt_list->info; rcpt < rcpt_list->info + rcpt_list->len; rcpt++)
+ rcpt->status = 0;
+
+ return (rcpt_list->len);
}
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)
+ if (SMTP_RCPT_MARK_ISSET(request->rcpt_list.info))
msg_panic("smtp_xfer: bad recipient status: %d",
request->rcpt_list.info->status);
} else {
for (nrcpt = 0; nrcpt < recv_rcpt; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
- if (rcpt->status == 0)
+ if (!SMTP_RCPT_MARK_ISSET(rcpt))
smtp_rcpt_done(state, resp->str, rcpt);
}
}
/*++
/* NAME
-/* smtp_state 8
+/* smtp_state 3
/* SUMMARY
/* initialize/cleanup shared state
/* SYNOPSIS
state->size_limit = 0;
state->space_left = 0;
state->mime_state = 0;
- state->final_server = 0;
return (state);
}
/* 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. 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).
+/* updated. In any case, the recipient is marked as either KEEP
+/* (try again with a backup host) or 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
msg_info("%s: %s", request->queue_id, vstring_str(why));
for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
- if (rcpt->status != 0)
+ if (SMTP_RCPT_MARK_ISSET(rcpt))
continue;
- rcpt->status = SMTP_RCPT_KEEP;
+ SMTP_RCPT_MARK_KEEP(state, rcpt);
}
}
else {
for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
- if (rcpt->status != 0)
+ if (SMTP_RCPT_MARK_ISSET(rcpt))
continue;
status = (soft_error ? defer_append : bounce_append)
(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
request->arrival_time, "%s", vstring_str(why));
if (status == 0)
deliver_completed(state->src, rcpt->offset);
- rcpt->status = SMTP_RCPT_DROP;
+ SMTP_RCPT_MARK_DROP(state, rcpt);
state->status |= status;
}
/* XXX This assumes no fall-back relay. */
msg_info("%s: %s", request->queue_id, vstring_str(why));
for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
- if (rcpt->status != 0)
+ if (SMTP_RCPT_MARK_ISSET(rcpt))
continue;
- rcpt->status = SMTP_RCPT_KEEP;
+ SMTP_RCPT_MARK_KEEP(state, rcpt);
}
}
else {
for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
- if (rcpt->status != 0)
+ if (SMTP_RCPT_MARK_ISSET(rcpt))
continue;
status = (soft_error ? defer_append : bounce_append)
(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
"%s", vstring_str(why));
if (status == 0)
deliver_completed(state->src, rcpt->offset);
- rcpt->status = SMTP_RCPT_DROP;
+ SMTP_RCPT_MARK_DROP(state, rcpt);
state->status |= status;
}
}
int soft_error = SMTP_SOFT(code);
va_list ap;
+ /*
+ * Sanity check.
+ */
+ if (SMTP_RCPT_MARK_ISSET(rcpt))
+ msg_panic("smtp_rcpt_fail: recipient <%s> is marked", rcpt->address);
+
/*
* Don't defer this recipient record just yet when this error qualifies
* for trying other mail servers. Just log something informative to show
vstring_vsprintf(buf, format, ap);
va_end(ap);
msg_info("%s: %s", request->queue_id, vstring_str(buf));
- rcpt->status = SMTP_RCPT_KEEP;
+ SMTP_RCPT_MARK_KEEP(state, rcpt);
vstring_free(buf);
}
va_end(ap);
if (status == 0)
deliver_completed(state->src, rcpt->offset);
- rcpt->status = SMTP_RCPT_DROP;
+ SMTP_RCPT_MARK_DROP(state, rcpt);
state->status |= status;
}
smtp_check_code(state, code);
msg_info("%s: %s", request->queue_id, vstring_str(why));
for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
- if (rcpt->status != 0)
+ if (SMTP_RCPT_MARK_ISSET(rcpt))
continue;
- rcpt->status = SMTP_RCPT_KEEP;
+ SMTP_RCPT_MARK_KEEP(state, rcpt);
}
}
else {
for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
- if (rcpt->status != 0)
+ if (SMTP_RCPT_MARK_ISSET(rcpt))
continue;
state->status |= defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id,
rcpt->offset, session->namaddr,
request->arrival_time,
"%s", vstring_str(why));
- rcpt->status = SMTP_RCPT_DROP;
+ SMTP_RCPT_MARK_DROP(state, rcpt);
}
}
--- /dev/null
+/*++
+/* NAME
+/* smtpd_xclient 3
+/* SUMMARY
+/* maintain XCLIENT information
+/* SYNOPSIS
+/* #include "smtpd.h"
+/*
+/* void smtpd_xclient_init(state)
+/* SMTPD_STATE *state;
+/*
+/* void smtpd_xclient_reset(state)
+/* SMTPD_STATE *state;
+/* DESCRIPTION
+/* smtpd_xclient_init() zeroes the attributes for storage of XCLIENT
+/* FORWARD command parameters.
+/*
+/* smtpd_xclient_preset() takes the result from smtpd_xclient_init()
+/* and sets all fields to the same "unknown" value that regular
+/* client attributes would have.
+/*
+/* smtpd_xclient_reset() restores the state from smtpd_xclient_init().
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <msg.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+
+/* Application-specific. */
+
+#include <smtpd.h>
+
+/* smtpd_xclient_init - initialize XCLIENT attributes */
+
+void smtpd_xclient_init(SMTPD_STATE *state)
+{
+ state->xclient.used = 0;
+ state->xclient.name = 0;
+ state->xclient.addr = 0;
+ state->xclient.namaddr = 0;
+ state->xclient.peer_code = 0;
+ state->xclient.protocol = 0;
+ state->xclient.helo_name = 0;
+}
+
+/* smtpd_xclient_preset - set xclient attributes to "unknown" */
+
+void smtpd_xclient_preset(SMTPD_STATE *state)
+{
+
+ /*
+ * This is a temporary solution. Unknown forwarded attributes get the
+ * same values as unknown normal attributes, so that we don't break
+ * assumptions in pre-existing code.
+ */
+ state->xclient.used = 1;
+ state->xclient.name = mystrdup(CLIENT_NAME_UNKNOWN);
+ state->xclient.addr = mystrdup(CLIENT_ADDR_UNKNOWN);
+ state->xclient.namaddr = mystrdup(CLIENT_NAMADDR_UNKNOWN);
+ state->xclient.protocol = mystrdup(CLIENT_PROTO_UNKNOWN);
+}
+
+/* smtpd_xclient_reset - reset XCLIENT attributes */
+
+void smtpd_xclient_reset(SMTPD_STATE *state)
+{
+#define FREE_AND_WIPE(s) { if (s) myfree(s); s = 0; }
+
+ state->xclient.used = 0;
+ FREE_AND_WIPE(state->xclient.name);
+ FREE_AND_WIPE(state->xclient.addr);
+ FREE_AND_WIPE(state->xclient.namaddr);
+ state->xclient.peer_code = 0;
+ FREE_AND_WIPE(state->xclient.protocol);
+ FREE_AND_WIPE(state->xclient.helo_name);
+}
--- /dev/null
+/*++
+/* NAME
+/* intv 3
+/* SUMMARY
+/* integer array utilities
+/* SYNOPSIS
+/* #include <intv.h>
+/*
+/* INTV *intv_alloc(len)
+/* int len;
+/*
+/* INTV *intv_free(intvp)
+/* INTV *intvp;
+/*
+/* void intv_add(intvp, count, arg, ...)
+/* INTV *intvp;
+/* int count;
+/* int *arg;
+/* DESCRIPTION
+/* The functions in this module manipulate arrays of integers.
+/* An INTV structure contains the following members:
+/* .IP len
+/* The actual length of the \fIintv\fR array.
+/* .IP intc
+/* The number of \fIintv\fR elements used.
+/* .IP intv
+/* An array of integer values.
+/* .PP
+/* intv_alloc() returns an empty integer array of the requested
+/* length. The result is ready for use by intv_add().
+/*
+/* intv_add() copies zero or more integers and adds them to the
+/* specified integer array.
+/*
+/* intv_free() releases storage for an integer array, and conveniently
+/* returns a null pointer.
+/* SEE ALSO
+/* msg(3) diagnostics interface
+/* DIAGNOSTICS
+/* Fatal errors: memory allocation problem.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System libraries. */
+
+#include <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Application-specific. */
+
+#include "mymalloc.h"
+#include "msg.h"
+#include "intv.h"
+
+/* intv_free - destroy integer array */
+
+INTV *intv_free(INTV *intvp)
+{
+ myfree((char *) intvp->intv);
+ myfree((char *) intvp);
+ return (0);
+}
+
+/* intv_alloc - initialize integer array */
+
+INTV *intv_alloc(int len)
+{
+ INTV *intvp;
+
+ /*
+ * Sanity check.
+ */
+ if (len < 1)
+ msg_panic("intv_alloc: bad array length %d", len);
+
+ /*
+ * Initialize.
+ */
+ intvp = (INTV *) mymalloc(sizeof(*intvp));
+ intvp->len = 0;
+ intvp->intv = (int *) mymalloc(len * sizeof(intvp->intv[0]));
+ intvp->len = len;
+ intvp->intc = 0;
+ return (intvp);
+}
+
+/* intv_add - add integer to vector */
+
+void intv_add(INTV *intvp, int count,...)
+{
+ va_list ap;
+ int new_len;
+
+ /*
+ * Make sure that always intvp->intc < intvp->len.
+ */
+ va_start(ap, count);
+ while (count-- > 0) {
+ if (intvp->intc >= intvp->len) {
+ new_len = intvp->len * 2;
+ intvp->intv = (int *) myrealloc((char *) intvp->intv,
+ new_len * sizeof(int));
+ intvp->len = new_len;
+ }
+ intvp->intv[intvp->intc++] = va_arg(ap, int);
+ }
+ va_end(ap);
+}
--- /dev/null
+#ifndef _INTV_H_INCLUDED_
+#define _INTV_H_INCLUDED_
+
+/*++
+/* NAME
+/* intv 3h
+/* SUMMARY
+/* string array utilities
+/* SYNOPSIS
+/* #include "intv.h"
+ DESCRIPTION
+ .nf
+
+ /*
+ * External interface.
+ */
+typedef struct INTV {
+ int len; /* number of array elements */
+ int intc; /* array elements in use */
+ int *intv; /* integer array */
+} INTV;
+
+extern INTV *intv_alloc(int);
+extern void intv_add(INTV *, int,...);
+extern INTV *intv_free(INTV *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif