/* makes the open_sink()/close_sink() pair non-reentrant */
static int lmtp_responses;
+void smtp_close(struct query *ctl, int sayquit)
+/* close the socket to SMTP server */
+{
+ if (ctl->smtp_socket != -1)
+ {
+ if (sayquit)
+ SMTP_quit(ctl->smtp_socket);
+ SockClose(ctl->smtp_socket);
+ ctl->smtp_socket = -1;
+ }
+ batchcount = 0;
+}
+
int smtp_open(struct query *ctl)
/* try to open a socket to the appropriate SMTP server for this query */
{
char *parsed_host = NULL;
/* maybe it's time to close the socket in order to force delivery */
- if (NUM_NONZERO(ctl->batchlimit) && (ctl->smtp_socket != -1) && ++batchcount == ctl->batchlimit)
- {
- SockClose(ctl->smtp_socket);
- ctl->smtp_socket = -1;
- batchcount = 0;
+ if (NUM_NONZERO(ctl->batchlimit)) {
+ if (batchcount == ctl->batchlimit)
+ smtp_close(ctl, 1);
+ batchcount++;
}
/* if no socket to any SMTP host is already set up, try to open one */
/* return immediately for ODMR */
if (ctl->server.protocol == P_ODMR)
+ {
+ set_timeout(0);
+ phase = oldphase;
return(ctl->smtp_socket); /* success */
+ }
/* are we doing SMTP or LMTP? */
SMTP_setmode(ctl->listener);
/* first, probe for ESMTP */
if (SMTP_ok(ctl->smtp_socket) == SM_OK &&
- SMTP_ehlo(ctl->smtp_socket, id_me,
- &ctl->server.esmtp_options) == SM_OK)
+ SMTP_ehlo(ctl->smtp_socket, id_me,
+ ctl->server.esmtp_name, ctl->server.esmtp_password,
+ &ctl->server.esmtp_options) == SM_OK)
break; /* success */
/*
* 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.
*/
- SockClose(ctl->smtp_socket);
- ctl->smtp_socket = -1;
+ smtp_close(ctl, 0);
/* if opening for ESMTP failed, try SMTP */
if ((ctl->smtp_socket = SockOpen(parsed_host,portnum,NULL,
SMTP_helo(ctl->smtp_socket, id_me) == SM_OK)
break; /* success */
- SockClose(ctl->smtp_socket);
- ctl->smtp_socket = -1;
+ smtp_close(ctl, 0);
}
set_timeout(0);
phase = oldphase;
strcat(daemon_name, host_fqdn());
/* we need only SMTP for this purpose */
- if ((sock = SockOpen("localhost", SMTP_PORT, NULL, NULL)) == -1
- || SMTP_ok(sock) != SM_OK
- || SMTP_helo(sock, fetchmailhost) != SM_OK
- || SMTP_from(sock, daemon_name, (char *)NULL) != SM_OK
- || SMTP_rcpt(sock, bounce_to) != SM_OK
- || SMTP_data(sock) != SM_OK)
+ if ((sock = SockOpen("localhost", SMTP_PORT, NULL, NULL)) == -1)
return(FALSE);
+ if (SMTP_ok(sock) != SM_OK)
+ {
+ SockClose(sock);
+ return FALSE;
+ }
+
+ if (SMTP_helo(sock, fetchmailhost) != SM_OK
+ || SMTP_from(sock, daemon_name, (char *)NULL) != SM_OK
+ || SMTP_rcpt(sock, bounce_to) != SM_OK
+ || SMTP_data(sock) != SM_OK)
+ {
+ SMTP_quit(sock);
+ SockClose(sock);
+ return(FALSE);
+ }
+
/* our first duty is to keep the sacred foo counters turning... */
#ifdef HAVE_SNPRINTF
snprintf(boundary, sizeof(boundary),
if (nerrors == 1)
/* one error applies to all users */
error = errors[0];
- else if (nerrors > nusers)
+ else if (nerrors <= nusers)
{
SockPrintf(sock, "Internal error: SMTP error count doesn't match number of recipients.\r\n");
break;
SockPrintf(sock, "--%s--\r\n", boundary);
if (SMTP_eom(sock) != SM_OK || SMTP_quit(sock))
+ {
+ SockClose(sock);
return(FALSE);
+ }
SockClose(sock);
*
*/
if (run.spambounce)
+ {
+ char rejmsg[160];
+#ifdef HAVE_SNPRINTF
+ snprintf(rejmsg, sizeof(rejmsg),
+#else
+ sprintf(rejmsg,
+#endif /* HAVE_SNPRINTF */
+ "spam filter or virus scanner rejected message because:\r\n"
+ "%s\r\n", responses[0]);
+
send_bouncemail(ctl, msg, XMIT_ACCEPT,
- "Our spam filter rejected this transaction.\r\n",
- 1, responses);
+ rejmsg, 1, responses);
+ }
return(PS_REFUSED);
}
* ESMTP server. Don't try to ship the message,
* and allow it to be deleted.
*/
- send_bouncemail(ctl, msg, XMIT_ACCEPT,
+ if (run.bouncemail)
+ send_bouncemail(ctl, msg, XMIT_ACCEPT,
"This message was too large (SMTP error 552).\r\n",
1, responses);
- return(run.bouncemail ? PS_REFUSED : PS_TRANSIENT);
+ return(PS_REFUSED);
case 553: /* invalid sending domain */
/*
* (b) we wouldn't want spammers to get confirmation that
* this address is live, anyway.
*/
- send_bouncemail(ctl, msg, XMIT_ACCEPT,
- "Invalid address in MAIL FROM (SMTP error 553).\r\n",
+#ifdef __DONT_FEED_THE_SPAMMERS__
+ if (run.bouncemail)
+ send_bouncemail(ctl, msg, XMIT_ACCEPT,
+ "Invalid address in MAIL FROM/RCPT TO (SMTP error 553).\r\n",
1, responses);
+#endif /* __DONT_FEED_THE_SPAMMERS__ */
return(PS_REFUSED);
default:
*
* Bouncemail *might* be appropriate here as a delay
* notification (note; if we ever add this, we must make
- * sure the RFC1894 Action field is "delayed" rather thwn
+ * sure the RFC1894 Action field is "delayed" rather than
* "failed"). But it's not really necessary because
* these are not actual failures, we're very likely to be
* able to recover on the next cycle.
}
}
+static int handle_smtp_report_without_bounce(struct query *ctl, struct msgblk *msg)
+/* handle SMTP errors based on the content of SMTP_response */
+/* atleast one PS_TRANSIENT: do not send the bounce mail, keep the mail;
+ * no PS_TRANSIENT, atleast one PS_SUCCESS: send the bounce mail, delete the mail;
+ * no PS_TRANSIENT, no PS_SUCCESS: do not send the bounce mail, delete the mail */
+{
+ int smtperr = atoi(smtp_response);
+
+ if (str_find(&ctl->antispam, smtperr))
+ {
+ if (run.spambounce)
+ return(PS_SUCCESS);
+ return(PS_REFUSED);
+ }
+
+ if (smtperr >= 400)
+ report(stderr, GT_("%cMTP error: %s\n"),
+ ctl->listener,
+ smtp_response);
+
+ switch (smtperr)
+ {
+ case 552: /* message exceeds fixed maximum message size */
+ if (run.bouncemail)
+ return(PS_SUCCESS);
+ return(PS_REFUSED);
+
+ case 553: /* invalid sending domain */
+#ifdef __DONT_FEED_THE_SPAMMERS__
+ if (run.bouncemail)
+ return(PS_SUCCESS);
+#endif /* __DONT_FEED_THE_SPAMMERS__ */
+ return(PS_REFUSED);
+
+ default:
+ /* bounce non-transient errors back to the sender */
+ if (smtperr >= 500 && smtperr <= 599)
+ return(PS_SUCCESS);
+ return(PS_TRANSIENT);
+ }
+}
+
/* these are shared by open_sink and stuffline */
static FILE *sinkfp;
/* this is experimental and will be removed if double bounces are reported */
#define EXPLICIT_BOUNCE_ON_BAD_ADDRESS
+
+static const char *is_quad(const char *q)
+/* Check if the string passed in points to what could be one quad of a
+ * dotted-quad IP address. Requirements are that the string is not a
+ * NULL pointer, begins with a period (which is skipped) or a digit
+ * and ends with a period or a NULL. If these requirements are met, a
+ * pointer to the last character (the period or the NULL character) is
+ * returned; otherwise NULL.
+ */
+{
+ const char *r;
+
+ if (!q || !*q)
+ return NULL;
+ if (*q == '.')
+ q++;
+ for(r=q;isdigit(*r);r++)
+ ;
+ if ( ((*r) && (*r != '.')) || ((r-q) < 1) || ((r-q)>3) )
+ return NULL;
+ /* Make sure quad is < 255 */
+ if ( (r-q) == 3)
+ {
+ if (*q > '2')
+ return NULL;
+ else if (*q == '2')
+ {
+ if (*(q+1) > '5')
+ return NULL;
+ else if (*(q+1) == '5')
+ {
+ if (*(q+2) > '5')
+ return NULL;
+ }
+ }
+ }
+ return r;
+}
+
+static int is_dottedquad(const char *hostname)
+/* Returns a true value if the passed in string looks like an IP
+ * address in dotted-quad form, and a false value otherwise.
+ */
+
+{
+ return ((hostname=is_quad(is_quad(is_quad(is_quad(hostname))))) != NULL) &&
+ (*hostname == '\0');
+}
+
static int open_smtp_sink(struct query *ctl, struct msgblk *msg,
int *good_addresses, int *bad_addresses)
/* open an SMTP stream */
char **from_responses;
#endif /* EXPLICIT_BOUNCE_ON_BAD_ADDRESS */
int total_addresses;
+ int force_transient_error = 0;
/*
* Compute ESMTP options.
* path equal to "@". Ghod knows why anyone does this, but
* it's been reported to happen in mail from Amazon.com and
* Motorola.
+ *
+ * Also, if the hostname is a dotted quad, wrap it in square brackets.
+ * Apparently this is required by RFC2821, section 4.1.3.
*/
if (!msg->return_path[0] || (0 == strcmp(msg->return_path, "@")))
{
+ if (is_dottedquad(ctl->server.truename))
+ {
+#ifdef HAVE_SNPRINTF
+ snprintf(addr, sizeof(addr),
+#else
+ sprintf(addr,
+#endif /* HAVE_SNPRINTF */
+ "%s@[%s]", ctl->remotename, ctl->server.truename);
+ }
+ else
+ {
#ifdef HAVE_SNPRINTF
snprintf(addr, sizeof(addr),
#else
sprintf(addr,
#endif /* HAVE_SNPRINTF */
"%s@%s", ctl->remotename, ctl->server.truename);
+ }
ap = addr;
}
else if (strchr(msg->return_path,'@') || strchr(msg->return_path,'!'))
ap = msg->return_path;
else /* in case Return-Path existed but was local */
{
+ if (is_dottedquad(ctl->server.truename))
+ {
+#ifdef HAVE_SNPRINTF
+ snprintf(addr, sizeof(addr),
+#else
+ sprintf(addr,
+#endif /* HAVE_SNPRINTF */
+ "%s@[%s]", msg->return_path, ctl->server.truename);
+ }
+ else
+ {
#ifdef HAVE_SNPRINTF
snprintf(addr, sizeof(addr),
#else
sprintf(addr,
#endif /* HAVE_SNPRINTF */
"%s@%s", msg->return_path, ctl->server.truename);
+ }
ap = addr;
}
else {
if (ctl->smtpname) {
#ifdef HAVE_SNPRINTF
- snprintf(addr, sizeof(addr)-1, "%s", ctl->smtpname);
+ snprintf(addr, sizeof(addr), "%s", ctl->smtpname);
#else
sprintf(addr, "%s", ctl->smtpname);
#endif /* HAVE_SNPRINTF */
} else {
#ifdef HAVE_SNPRINTF
- snprintf(addr, sizeof(addr)-1, "%s@%s", idp->id, ctl->destaddr);
+ snprintf(addr, sizeof(addr), "%s@%s", idp->id, ctl->destaddr);
#else
sprintf(addr, "%s@%s", idp->id, ctl->destaddr);
#endif /* HAVE_SNPRINTF */
(*good_addresses)++;
else
{
-#ifdef EXPLICIT_BOUNCE_ON_BAD_ADDRESS
- char errbuf[POPBUFSIZE];
-#endif /* EXPLICIT_BOUNCE_ON_BAD_ADDRESS */
- handle_smtp_report(ctl, msg);
+ switch (handle_smtp_report_without_bounce(ctl, msg))
+ {
+ case PS_TRANSIENT:
+ force_transient_error = 1;
+ break;
+ case PS_SUCCESS:
#ifdef EXPLICIT_BOUNCE_ON_BAD_ADDRESS
-#ifdef HAVE_SNPRINTF
- snprintf(errbuf, sizeof(errbuf), "%s: %s",
- idp->id, smtp_response);
-#else
- strncpy(errbuf, idp->id, sizeof(errbuf));
- strcat(errbuf, ": ");
- strcat(errbuf, smtp_response);
-#endif /* HAVE_SNPRINTF */
-
- xalloca(from_responses[*bad_addresses],
- char *,
- strlen(errbuf)+1);
- strcpy(from_responses[*bad_addresses], errbuf);
+ xalloca(from_responses[*bad_addresses],
+ char *,
+ strlen(smtp_response)+1);
+ strcpy(from_responses[*bad_addresses], smtp_response);
#endif /* EXPLICIT_BOUNCE_ON_BAD_ADDRESS */
- (*bad_addresses)++;
- idp->val.status.mark = XMIT_RCPTBAD;
- if (outlevel >= O_VERBOSE)
- report(stderr,
- GT_("%cMTP listener doesn't like recipient address `%s'\n"),
- ctl->listener, addr);
+ (*bad_addresses)++;
+ idp->val.status.mark = XMIT_RCPTBAD;
+ if (outlevel >= O_VERBOSE)
+ report(stderr,
+ GT_("%cMTP listener doesn't like recipient address `%s'\n"),
+ ctl->listener, addr);
+ break;
+
+ case PS_REFUSED:
+ if (outlevel >= O_VERBOSE)
+ report(stderr,
+ GT_("%cMTP listener doesn't really like recipient address `%s'\n"),
+ ctl->listener, addr);
+ break;
+ }
}
}
+ if (force_transient_error) {
+ /* do not risk dataloss due to overengineered multidrop
+ * crap. If one of the recipients returned PS_TRANSIENT,
+ * we return exactly that.
+ */
+ SMTP_rset(ctl->smtp_socket); /* required by RFC1870 */
+ return(PS_TRANSIENT);
+ }
#ifdef EXPLICIT_BOUNCE_ON_BAD_ADDRESS
/*
* This should not be necessary, because the SMTP listener itself
else
{
#ifdef HAVE_SNPRINTF
- snprintf(addr, sizeof(addr)-1, "%s@%s", run.postmaster, ctl->destaddr);
+ snprintf(addr, sizeof(addr), "%s@%s", run.postmaster, ctl->destaddr);
#else
sprintf(addr, "%s@%s", run.postmaster, ctl->destaddr);
#endif /* HAVE_SNPRINTF */
return(handle_smtp_report(ctl, msg));
}
+ /*
+ * We need to stash this away in order to know how many
+ * response lines to expect after the LMTP end-of-message.
+ */
+ lmtp_responses = *good_addresses;
+
return(PS_SUCCESS);
}
#ifdef HAVE_SIGACTION
struct sigaction sa_new;
#endif /* HAVE_SIGACTION */
+#ifdef HAVE_SETEUID
+ uid_t orig_uid;
+#endif /* HAVE_SETEUID */
struct idlist *idp;
int length = 0, fromlen = 0, nameslen = 0;
char *names = NULL, *before, *after, *from = NULL;
* MDA creates properly. (The seteuid call is available
* under all BSDs and Linux)
*/
+ orig_uid = getuid();
seteuid(ctl->uid);
#endif /* HAVE_SETEUID */
#ifdef HAVE_SETEUID
/* this will fail quietly if we didn't start as root */
- seteuid(0);
+ seteuid(orig_uid);
#endif /* HAVE_SETEUID */
if (!sinkfp)
/*
* User was delivering locally. We have a fallback MDA.
* Latch it in place, logging the error, and fall through.
+ * Set stripcr as we would if MDA had been the initial transport
*/
ctl->mda = FALLBACK_MDA;
+ if (!ctl->forcecr)
+ ctl->stripcr = TRUE;
report(stderr, GT_("can't raise the listener; falling back to %s"),
FALLBACK_MDA);
if (ctl->mda) /* must deliver through an MDA */
return(open_mda_sink(ctl, msg, good_addresses, bad_addresses));
- /*
- * We need to stash this away in order to know how many
- * response lines to expect after the LMTP end-of-message.
- */
- lmtp_responses = *good_addresses;
-
return(PS_SUCCESS);
}
/* release the per-message output sink, whether it's a pipe or SMTP socket */
{
if (ctl->bsmtp && sinkfp)
- fclose(sinkfp);
+ {
+ if (strcmp(ctl->bsmtp, "-"))
+ {
+ fclose(sinkfp);
+ sinkfp = (FILE *)NULL;
+ }
+ }
else if (ctl->mda)
{
if (sinkfp)
fputs(".\r\n", sinkfp);
error = ferror(sinkfp);
if (strcmp(ctl->bsmtp, "-"))
+ {
if (fclose(sinkfp) == EOF) error = 1;
+ sinkfp = (FILE *)NULL;
+ }
if (error)
{
report(stderr,
struct msgblk reply = {NULL, NULL, "FETCHMAIL-DAEMON@", 0};
int status;
- strcat(reply.return_path, fetchmailhost);
+ strcat(reply.return_path, ctl->smtpaddress ? ctl->smtpaddress :
+ fetchmailhost);
if (!MULTIDROP(ctl)) /* send to calling user */
{