aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--driver.c99
-rw-r--r--fetchmail.c40
-rw-r--r--fetchmail.h2
-rw-r--r--fetchmail.man4
-rw-r--r--options.c2
-rw-r--r--rcfile_y.y11
7 files changed, 82 insertions, 78 deletions
diff --git a/NEWS b/NEWS
index 31d7ec4d..dacf3f74 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,8 @@ fetchmail-3.7 ()
features --
+* You can now specify a hunt list of SMTP forwarding hosts.
+
* Treat unexpected EOF as a protocol error.
bugs --
diff --git a/driver.c b/driver.c
index a27f9d8f..3007bd61 100644
--- a/driver.c
+++ b/driver.c
@@ -341,62 +341,75 @@ char *parse_received(struct query *ctl, char *bufp)
static FILE *smtp_open(struct query *ctl)
/* try to open a socket to the appropriate SMTP server for this query */
{
- struct query *lead;
-
- lead = ctl->lead_smtp; /* go to the SMTP leader for this query */
+ struct idlist *idp;
/* maybe it's time to close the socket in order to force delivery */
- if (ctl->batchlimit && lead->smtp_sockfp && batchcount++==ctl->batchlimit)
+ if (ctl->batchlimit && ctl->smtp_sockfp && batchcount++ == ctl->batchlimit)
{
- fclose(lead->smtp_sockfp);
- lead->smtp_sockfp = (FILE *)NULL;
+ fclose(ctl->smtp_sockfp);
+ ctl->smtp_sockfp = (FILE *)NULL;
batchcount = 0;
}
- /*
- * RFC 1123 requires that the domain name in HELO address is a
- * "valid principal domain name" for the client host. We
- * violate this with malice aforethought in order to make the
- * Received headers and logging look right.
- *
- * In fact this code relies on the RFC1123 requirement that the
- * SMTP listener must accept messages even if verification of the
- * HELO name fails (RFC1123 section 5.2.5, paragraph 2).
- */
-
- /* if no socket to this host is already set up, try to open ESMTP */
- if (lead->smtp_sockfp == (FILE *)NULL)
+ /* run down the SMTP hunt list looking for a server that's up */
+ for (idp = ctl->smtphunt; idp; idp = idp->next)
{
- if ((lead->smtp_sockfp = SockOpen(lead->smtphost, SMTP_PORT)) == (FILE *)NULL)
- return((FILE *)NULL);
- else if (SMTP_ok(lead->smtp_sockfp) != SM_OK
- || SMTP_ehlo(lead->smtp_sockfp,
- ctl->server.names->id,
- &lead->server.esmtp_options) != SM_OK)
+
+ /*
+ * RFC 1123 requires that the domain name in HELO address is a
+ * "valid principal domain name" for the client host. We
+ * violate this with malice aforethought in order to make the
+ * Received headers and logging look right.
+ *
+ * In fact this code relies on the RFC1123 requirement that the
+ * SMTP listener must accept messages even if verification of the
+ * HELO name fails (RFC1123 section 5.2.5, paragraph 2).
+ */
+
+ /* if no socket to this host is already set up, try to open ESMTP */
+ if (ctl->smtp_sockfp == (FILE *)NULL)
{
- /*
- * RFC 1869 warns that some listeners hang up on a failed EHLO,
- * so it's safest not to assume the socket will still be good.
- */
- fclose(lead->smtp_sockfp);
- lead->smtp_sockfp = (FILE *)NULL;
+ if ((ctl->smtp_sockfp = SockOpen(idp->id,SMTP_PORT))==(FILE *)NULL)
+ return((FILE *)NULL);
+ else if (SMTP_ok(ctl->smtp_sockfp) != SM_OK
+ || SMTP_ehlo(ctl->smtp_sockfp,
+ ctl->server.names->id,
+ &ctl->server.esmtp_options) != SM_OK)
+ {
+ /*
+ * RFC 1869 warns that some listeners hang up on a failed EHLO,
+ * so it's safest not to assume the socket will still be good.
+ */
+ fclose(ctl->smtp_sockfp);
+ ctl->smtp_sockfp = (FILE *)NULL;
+ }
+ else
+ {
+ ctl->smtphost = idp->id;
+ break;
+ }
}
- }
- /* if opening for ESMTP failed, try SMTP */
- if (lead->smtp_sockfp == (FILE *)NULL)
- {
- if ((lead->smtp_sockfp = SockOpen(lead->smtphost, SMTP_PORT)) == (FILE *)NULL)
- return((FILE *)NULL);
- else if (SMTP_ok(lead->smtp_sockfp) != SM_OK
- || SMTP_helo(lead->smtp_sockfp, ctl->server.names->id) != SM_OK)
+ /* if opening for ESMTP failed, try SMTP */
+ if (ctl->smtp_sockfp == (FILE *)NULL)
{
- fclose(lead->smtp_sockfp);
- lead->smtp_sockfp = (FILE *)NULL;
+ if ((ctl->smtp_sockfp = SockOpen(idp->id,SMTP_PORT))==(FILE *)NULL)
+ return((FILE *)NULL);
+ else if (SMTP_ok(ctl->smtp_sockfp) != SM_OK
+ || SMTP_helo(ctl->smtp_sockfp, ctl->server.names->id) != SM_OK)
+ {
+ fclose(ctl->smtp_sockfp);
+ ctl->smtp_sockfp = (FILE *)NULL;
+ }
+ else
+ {
+ ctl->smtphost = idp->id;
+ break;
+ }
}
}
- return(lead->smtp_sockfp);
+ return(ctl->smtp_sockfp);
}
static int gen_readmsg(sockfp, len, delimited, ctl, realname)
@@ -672,7 +685,7 @@ char *realname; /* real name of host */
if (!ctl->mda && ((sinkfp = smtp_open(ctl)) == NULL))
{
free_str_list(&xmit_names);
- error(0, 0, "SMTP connect failed");
+ error(0, 0, "SMTP connect to %s failed", ctl->smtphost);
if (return_path)
free(return_path);
return(PS_SMTP);
diff --git a/fetchmail.c b/fetchmail.c
index a351e84e..bd31e0a0 100644
--- a/fetchmail.c
+++ b/fetchmail.c
@@ -526,7 +526,7 @@ static int load_params(int argc, char **argv, int optind)
def_opts.server.protocol = P_AUTO;
def_opts.server.timeout = CLIENT_TIMEOUT;
def_opts.remotename = user;
- def_opts.smtphost = fetchmailhost;
+ save_str(&def_opts.smtphunt, -1, fetchmailhost);
/* this builds the host list */
if (prc_parse_file(rcfile) != 0)
@@ -608,32 +608,7 @@ static int load_params(int argc, char **argv, int optind)
}
#endif /* !HAVE_GETHOSTBYNAME || !HAVE_RES_SEARCH */
- /*
- * Assign SMTP leaders. We want to allow all query blocks
- * sharing the same server/SMTP-host pair to use the same
- * SMTP connection. To accomplish this, we initialize
- * each query block's leader field to point to the first
- * block in the list with a matching server/SMTP-host pair.
- *
- * In the typical case, there will be only one SMTP host (the
- * client machine) and thus just one SMTP leader (and one listener
- * process) through the entire poll cycle.
- */
- if (!ctl->mda)
- {
- ctl->smtp_sockfp = (FILE *)NULL;
- for (mp = querylist; mp && mp != ctl; mp = mp->next)
- if (!strcmp(mp->server.names->id, ctl->server.names->id)
- && !strcmp(mp->smtphost, ctl->smtphost))
- {
- ctl->lead_smtp = mp->lead_smtp;
- goto no_new_leader;
- }
- ctl->lead_smtp = ctl;
- no_new_leader:;
- }
-
- /* similarly, compute server leaders for queries */
+ /* compute server leaders for queries */
for (mp = querylist; mp && mp != ctl; mp = mp->next)
if (strcmp(mp->server.names->id, ctl->server.names->id) == 0)
{
@@ -713,7 +688,7 @@ void termhook(int sig)
else
/* terminate all SMTP connections cleanly */
for (ctl = querylist; ctl; ctl = ctl->next)
- if (ctl->lead_smtp == ctl && ctl->smtp_sockfp != (FILE *)NULL)
+ if (ctl->smtp_sockfp != (FILE *)NULL)
SMTP_quit(ctl->smtp_sockfp);
if (!check_only)
@@ -877,7 +852,14 @@ void dump_params (struct query *ctl)
if (ctl->mda)
printf(" Messages will be delivered with '%s.'\n", visbuf(ctl->mda));
else
- printf(" Messages will be SMTP-forwarded to '%s'.\n", visbuf(ctl->smtphost));
+ {
+ struct idlist *idp;
+
+ printf(" Messages will be SMTP-forwarded to:");
+ for (idp = ctl->smtphunt; idp; idp = idp->next)
+ printf(" %s", idp->id);
+ printf("\n");
+ }
if (ctl->preconnect)
printf(" Server connection will be preinitialized with '%s.'\n", visbuf(ctl->preconnect));
else if (outlevel == O_VERBOSE)
diff --git a/fetchmail.h b/fetchmail.h
index eed7b1ea..063b2a16 100644
--- a/fetchmail.h
+++ b/fetchmail.h
@@ -102,6 +102,7 @@ struct query
char *remotename;
char *password;
char *mailbox;
+ struct idlist *smtphunt;
char *smtphost;
char *mda;
char *preconnect;
@@ -123,7 +124,6 @@ struct query
int active;
int errcount; /* count transient errors in last pass */
struct query *next; /* next query control block in chain */
- struct query *lead_smtp; /* pointer to this query's SMTP leader */
FILE *smtp_sockfp; /* socket descriptor for SMTP connection */
unsigned int uid; /* UID of user to deliver to */
char digest [DIGESTLEN]; /* md5 digest buffer */
diff --git a/fetchmail.man b/fetchmail.man
index fcd5c350..2546a800 100644
--- a/fetchmail.man
+++ b/fetchmail.man
@@ -166,8 +166,10 @@ under POP3 or ETRN.
.SS Delivery Control Options
.TP
.B \-S host, --smtphost host
-Specify a host to forward mail to (other than localhost).
+Specify a hunt list of hosts to forward mail to.
In ETRN mode, set the host that the mailserver is asked to ship mail to.
+Hosts are tried in list order; the first one that is up becomes the
+forwarding or ETRN target for the current run.
.TP
.B \-m, \--mda
You can force mail to be passed to an MDA directly (rather than
diff --git a/options.c b/options.c
index a723a7cd..7081ee84 100644
--- a/options.c
+++ b/options.c
@@ -247,7 +247,7 @@ struct query *ctl; /* option record to be initialized */
break;
case 'S':
case LA_SMTPHOST:
- ctl->smtphost = xstrdup(optarg);
+ save_str(&ctl->smtphunt, -1, optarg);
ocount++;
break;
case 'b':
diff --git a/rcfile_y.y b/rcfile_y.y
index 029b84de..199e57aa 100644
--- a/rcfile_y.y
+++ b/rcfile_y.y
@@ -176,6 +176,10 @@ mapping : STRING
{save_str_pair(&current.localnames, $1, $3);}
;
+smtphunt : STRING {save_str(&current.smtphunt, -1, $1);}
+ | smtphunt STRING {save_str(&current.smtphunt, -1, $2);}
+ ;
+
user_option : TO localnames HERE
| TO localnames
| IS localnames HERE
@@ -184,7 +188,7 @@ user_option : TO localnames HERE
| IS STRING THERE {current.remotename = xstrdup($2);}
| PASSWORD STRING {current.password = xstrdup($2);}
| FOLDER STRING {current.mailbox = xstrdup($2);}
- | SMTPHOST STRING {current.smtphost = xstrdup($2);}
+ | SMTPHOST smtphunt
| MDA STRING {current.mda = xstrdup($2);}
| PRECONNECT STRING {current.preconnect = xstrdup($2);}
@@ -350,7 +354,8 @@ static void record_current(void)
FLAG_FORCE(remotename);
FLAG_FORCE(password);
FLAG_FORCE(mailbox);
- FLAG_FORCE(smtphost);
+ if (cmd_opts.smtphunt)
+ save_str(&current.smtphunt, -1, cmd_opts.smtphunt->id);
FLAG_FORCE(mda);
FLAG_FORCE(preconnect);
@@ -373,6 +378,7 @@ void optmerge(struct query *h2, struct query *h1)
{
append_str_list(&h2->server.localdomains, &h1->server.localdomains);
append_str_list(&h2->localnames, &h1->localnames);
+ append_str_list(&h2->smtphunt, &h1->smtphunt);
#define FLAG_MERGE(fld) if (!h2->fld) h2->fld = h1->fld
FLAG_MERGE(server.protocol);
@@ -392,7 +398,6 @@ void optmerge(struct query *h2, struct query *h1)
FLAG_MERGE(remotename);
FLAG_MERGE(password);
FLAG_MERGE(mailbox);
- FLAG_MERGE(smtphost);
FLAG_MERGE(mda);
FLAG_MERGE(preconnect);