-/* $Cambridge: exim/src/src/moan.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for sending messages to sender or to mailmaster. */
+/*************************************************
+* Write From: line for DSN *
+*************************************************/
+
+/* This function is called to write the From: line in automatically generated
+messages - bounces, warnings, etc. It expands a configuration item in order to
+get the text. If the expansion fails, a panic is logged and the default value
+for the option is used.
+
+Argument: the FILE to write to
+Returns: nothing
+*/
+
+void
+moan_write_from(FILE *f)
+{
+uschar *s = expand_string(dsn_from);
+if (!s)
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "Failed to expand dsn_from (using default): %s", expand_string_message);
+ s = expand_string(US DEFAULT_DSN_FROM);
+ }
+fprintf(f, "From: %s\n", s);
+}
+
+
+
/*************************************************
* Send error message *
*************************************************/
Returns: TRUE if message successfully sent
*/
-static BOOL
+BOOL
moan_send_message(uschar *recipient, int ident, error_block *eblock,
header_line *headers, FILE *message_file, uschar *firstline)
{
int status;
int count = 0;
int size_limit = bounce_return_size_limit;
-FILE *f;
-int pid = child_open_exim(&fd);
+FILE * fp;
+int pid;
+
+#ifdef EXPERIMENTAL_DMARC
+uschar * s, * s2;
+
+/* For DMARC if there is a specific sender set, expand the variable for the
+header From: and grab the address from that for the envelope FROM. */
+
+if ( ident == ERRMESS_DMARC_FORENSIC
+ && dmarc_forensic_sender
+ && (s = expand_string(dmarc_forensic_sender))
+ && *s
+ && (s2 = expand_string(string_sprintf("${address:%s}", s)))
+ && *s2
+ )
+ pid = child_open_exim2(&fd, s2, bounce_sender_authentication);
+else
+ {
+ s = NULL;
+ pid = child_open_exim(&fd);
+ }
-/* Creation of child failed */
+#else
+pid = child_open_exim(&fd);
+#endif
if (pid < 0)
{
/* Creation of child succeeded */
-f = fdopen(fd, "wb");
-if (errors_reply_to != NULL) fprintf(f, "Reply-To: %s\n", errors_reply_to);
-fprintf(f, "Auto_submitted: auto-generated\n");
-fprintf(f, "From: Mail Delivery System <Mailer-Daemon@%s>\n",
- qualify_domain_sender);
-fprintf(f, "To: %s\n", recipient);
+fp = fdopen(fd, "wb");
+if (errors_reply_to) fprintf(fp, "Reply-To: %s\n", errors_reply_to);
+fprintf(fp, "Auto-Submitted: auto-replied\n");
+
+#ifdef EXPERIMENTAL_DMARC
+if (s)
+ fprintf(fp, "From: %s\n", s);
+else
+#endif
+ moan_write_from(fp);
+
+fprintf(fp, "To: %s\n", recipient);
switch(ident)
{
case ERRMESS_BADARGADDRESS:
- fprintf(f,
- "Subject: Mail failure - malformed recipient address\n\n");
- fprintf(f,
- "A message that you sent contained a recipient address that was incorrectly\n"
- "constructed:\n\n");
- fprintf(f, " %s %s\n", eblock->text1, eblock->text2);
- count = Ustrlen(eblock->text1);
- if (count > 0 && eblock->text1[count-1] == '.')
- fprintf(f,
- "\nRecipient addresses must not end with a '.' character.\n");
- fprintf(f,
- "\nThe message has not been delivered to any recipients.\n");
- break;
+ fprintf(fp,
+ "Subject: Mail failure - malformed recipient address\n\n");
+ fprintf(fp,
+ "A message that you sent contained a recipient address that was incorrectly\n"
+ "constructed:\n\n");
+ fprintf(fp, " %s %s\n", eblock->text1, eblock->text2);
+ count = Ustrlen(eblock->text1);
+ if (count > 0 && eblock->text1[count-1] == '.')
+ fprintf(fp,
+ "\nRecipient addresses must not end with a '.' character.\n");
+ fprintf(fp,
+ "\nThe message has not been delivered to any recipients.\n");
+ break;
case ERRMESS_BADNOADDRESS:
case ERRMESS_BADADDRESS:
- fprintf(f,
- "Subject: Mail failure - malformed recipient address\n\n");
- fprintf(f,
- "A message that you sent contained one or more recipient addresses that were\n"
- "incorrectly constructed:\n\n");
+ fprintf(fp,
+ "Subject: Mail failure - malformed recipient address\n\n");
+ fprintf(fp,
+ "A message that you sent contained one or more recipient addresses that were\n"
+ "incorrectly constructed:\n\n");
- while (eblock != NULL)
- {
- fprintf(f, " %s: %s\n", eblock->text1, eblock->text2);
- count++;
- eblock = eblock->next;
- }
+ while (eblock != NULL)
+ {
+ fprintf(fp, " %s: %s\n", eblock->text1, eblock->text2);
+ count++;
+ eblock = eblock->next;
+ }
- fprintf(f, (count == 1)? "\nThis address has been ignored. " :
- "\nThese addresses have been ignored. ");
+ fprintf(fp, (count == 1)? "\nThis address has been ignored. " :
+ "\nThese addresses have been ignored. ");
- fprintf(f, (ident == ERRMESS_BADADDRESS)?
- "The other addresses in the message were\n"
- "syntactically valid and have been passed on for an attempt at delivery.\n" :
+ fprintf(fp, (ident == ERRMESS_BADADDRESS)?
+ "The other addresses in the message were\n"
+ "syntactically valid and have been passed on for an attempt at delivery.\n" :
- "There were no other addresses in your\n"
- "message, and so no attempt at delivery was possible.\n");
- break;
+ "There were no other addresses in your\n"
+ "message, and so no attempt at delivery was possible.\n");
+ break;
case ERRMESS_IGADDRESS:
- fprintf(f, "Subject: Mail failure - no recipient addresses\n\n");
- fprintf(f,
- "A message that you sent using the -t command line option contained no\n"
- "addresses that were not also on the command line, and were therefore\n"
- "suppressed. This left no recipient addresses, and so no delivery could\n"
- "be attempted.\n");
- break;
+ fprintf(fp, "Subject: Mail failure - no recipient addresses\n\n");
+ fprintf(fp,
+ "A message that you sent using the -t command line option contained no\n"
+ "addresses that were not also on the command line, and were therefore\n"
+ "suppressed. This left no recipient addresses, and so no delivery could\n"
+ "be attempted.\n");
+ break;
case ERRMESS_NOADDRESS:
- fprintf(f, "Subject: Mail failure - no recipient addresses\n\n");
- fprintf(f,
- "A message that you sent contained no recipient addresses, and therefore no\n"
- "delivery could be attempted.\n");
- break;
+ fprintf(fp, "Subject: Mail failure - no recipient addresses\n\n");
+ fprintf(fp,
+ "A message that you sent contained no recipient addresses, and therefore no\n"
+ "delivery could be attempted.\n");
+ break;
case ERRMESS_IOERR:
- fprintf(f, "Subject: Mail failure - system failure\n\n");
- fprintf(f,
- "A system failure was encountered while processing a message that you sent,\n"
- "so it has not been possible to deliver it. The error was:\n\n%s\n",
- eblock->text1);
- break;
+ fprintf(fp, "Subject: Mail failure - system failure\n\n");
+ fprintf(fp,
+ "A system failure was encountered while processing a message that you sent,\n"
+ "so it has not been possible to deliver it. The error was:\n\n%s\n",
+ eblock->text1);
+ break;
case ERRMESS_VLONGHEADER:
- fprintf(f, "Subject: Mail failure - overlong header section\n\n");
- fprintf(f,
- "A message that you sent contained a header section that was excessively\n"
- "long and could not be handled by the mail transmission software. The\n"
- "message has not been delivered to any recipients.\n");
- break;
+ fprintf(fp, "Subject: Mail failure - overlong header section\n\n");
+ fprintf(fp,
+ "A message that you sent contained a header section that was excessively\n"
+ "long and could not be handled by the mail transmission software. The\n"
+ "message has not been delivered to any recipients.\n");
+ break;
case ERRMESS_VLONGHDRLINE:
- fprintf(f, "Subject: Mail failure - overlong header line\n\n");
- fprintf(f,
- "A message that you sent contained a header line that was excessively\n"
- "long and could not be handled by the mail transmission software. The\n"
- "message has not been delivered to any recipients.\n");
- break;
+ fprintf(fp, "Subject: Mail failure - overlong header line\n\n");
+ fprintf(fp,
+ "A message that you sent contained a header line that was excessively\n"
+ "long and could not be handled by the mail transmission software. The\n"
+ "message has not been delivered to any recipients.\n");
+ break;
case ERRMESS_TOOBIG:
- fprintf(f, "Subject: Mail failure - message too big\n\n");
- fprintf(f,
- "A message that you sent was longer than the maximum size allowed on this\n"
- "system. It was not delivered to any recipients.\n");
- break;
+ fprintf(fp, "Subject: Mail failure - message too big\n\n");
+ fprintf(fp,
+ "A message that you sent was longer than the maximum size allowed on this\n"
+ "system. It was not delivered to any recipients.\n");
+ break;
case ERRMESS_TOOMANYRECIP:
- fprintf(f, "Subject: Mail failure - too many recipients\n\n");
- fprintf(f,
- "A message that you sent contained more recipients than allowed on this\n"
- "system. It was not delivered to any recipients.\n");
- break;
+ fprintf(fp, "Subject: Mail failure - too many recipients\n\n");
+ fprintf(fp,
+ "A message that you sent contained more recipients than allowed on this\n"
+ "system. It was not delivered to any recipients.\n");
+ break;
case ERRMESS_LOCAL_SCAN:
case ERRMESS_LOCAL_ACL:
- fprintf(f, "Subject: Mail failure - rejected by local scanning code\n\n");
- fprintf(f,
- "A message that you sent was rejected by the local scanning code that\n"
- "checks incoming messages on this system.");
- if (eblock->text1 != NULL)
- {
- fprintf(f,
- " The following error was given:\n\n %s", eblock->text1);
- }
- fprintf(f, "\n");
+ fprintf(fp, "Subject: Mail failure - rejected by local scanning code\n\n");
+ fprintf(fp,
+ "A message that you sent was rejected by the local scanning code that\n"
+ "checks incoming messages on this system.");
+ if (eblock->text1)
+ fprintf(fp, " The following error was given:\n\n %s", eblock->text1);
+ fprintf(fp, "\n");
break;
- default:
- fprintf(f, "Subject: Mail failure\n\n");
- fprintf(f,
- "A message that you sent has caused the error routine to be entered with\n"
- "an unknown error number (%d).\n", ident);
+#ifdef EXPERIMENTAL_DMARC
+ case ERRMESS_DMARC_FORENSIC:
+ bounce_return_message = TRUE;
+ bounce_return_body = FALSE;
+ fprintf(fp, "Subject: DMARC Forensic Report for %s from IP %s\n\n",
+ eblock ? eblock->text2 : US"Unknown",
+ sender_host_address);
+ fprintf(fp,
+ "A message claiming to be from you has failed the published DMARC\n"
+ "policy for your domain.\n\n");
+ while (eblock)
+ {
+ fprintf(fp, " %s: %s\n", eblock->text1, eblock->text2);
+ count++;
+ eblock = eblock->next;
+ }
break;
- }
+#endif
-/* Now copy the message - headers then the rest of the input if
-available, up to the configured limit. */
+ default:
+ fprintf(fp, "Subject: Mail failure\n\n");
+ fprintf(fp,
+ "A message that you sent has caused the error routine to be entered with\n"
+ "an unknown error number (%d).\n", ident);
+ break;
+ }
-if (size_limit == 0 || size_limit > thismessage_size_limit)
- size_limit = thismessage_size_limit;
+/* Now, if configured, copy the message; first the headers and then the rest of
+the input if available, up to the configured limit, if the option for including
+message bodies in bounces is set. */
-if (size_limit > 0 && size_limit < message_size)
+if (bounce_return_message)
{
- int x = size_limit;
- uschar *k = US"";
- if ((x & 1023) == 0)
+ if (bounce_return_body)
{
- k = US"K";
- x >>= 10;
+ fprintf(fp, "\n"
+ "------ This is a copy of your message, including all the headers.");
+ if (size_limit == 0 || size_limit > thismessage_size_limit)
+ size_limit = thismessage_size_limit;
+ if (size_limit > 0 && size_limit < message_size)
+ {
+ int x = size_limit;
+ uschar *k = US"";
+ if ((x & 1023) == 0)
+ {
+ k = US"K";
+ x >>= 10;
+ }
+ fprintf(fp, "\n"
+ "------ No more than %d%s characters of the body are included.\n\n",
+ x, k);
+ }
+ else fprintf(fp, " ------\n\n");
+ }
+ else
+ {
+ fprintf(fp, "\n"
+ "------ This is a copy of the headers that were received before the "
+ "error\n was detected.\n\n");
}
- fprintf(f, "\n"
- "------ This is a copy of your message, including all the headers.\n"
- "------ No more than %d%s characters of the body are included.\n\n", x, k);
- }
-else fprintf(f, "\n"
- "------ This is a copy of your message, including all the headers. ------"
- "\n\n");
-/* If the error occurred before the Received: header was created, its text
-field will still be NULL; just omit such a header line. */
+ /* If the error occurred before the Received: header was created, its text
+ field will still be NULL; just omit such a header line. */
-while (headers != NULL)
- {
- if (headers->text != NULL) fprintf(f, "%s", CS headers->text);
- headers = headers->next;
- }
+ while (headers)
+ {
+ if (headers->text != NULL) fprintf(fp, "%s", CS headers->text);
+ headers = headers->next;
+ }
-if (ident != ERRMESS_VLONGHEADER && ident != ERRMESS_VLONGHDRLINE)
- fputc('\n', f);
+ if (ident != ERRMESS_VLONGHEADER && ident != ERRMESS_VLONGHDRLINE)
+ fputc('\n', fp);
-/* After early detection of an error, the message file may be STDIN,
-in which case we might have to terminate on a line containing just "."
-as well as on EOF. We may already have the first line in memory. */
+ /* After early detection of an error, the message file may be STDIN,
+ in which case we might have to terminate on a line containing just "."
+ as well as on EOF. We may already have the first line in memory. */
-if (message_file != NULL)
- {
- int ch;
- int state = 1;
- BOOL enddot = dot_ends && message_file == stdin;
- if (firstline != NULL) fprintf(f, "%s", CS firstline);
- while ((ch = fgetc(message_file)) != EOF)
+ if (bounce_return_body && message_file)
{
- fputc(ch, f);
- if (size_limit > 0 && ++written > size_limit) break;
- if (enddot)
+ BOOL enddot = f.dot_ends && message_file == stdin;
+ uschar * buf = store_get(bounce_return_linesize_limit+2);
+
+ if (firstline) fprintf(fp, "%s", CS firstline);
+
+ while (fgets(CS buf, bounce_return_linesize_limit+2, message_file))
{
- if (state == 0) { if (ch == '\n') state = 1; }
- else if (state == 1)
- { if (ch == '.') state = 2; else if (ch != '\n') state = 0; }
- else
- { if (ch == '\n') break; else state = 0; }
+ int len;
+
+ if (enddot && *buf == '.' && buf[1] == '\n')
+ {
+ fputc('.', fp);
+ break;
+ }
+
+ len = Ustrlen(buf);
+ if (buf[len-1] != '\n')
+ { /* eat rest of partial line */
+ int ch;
+ while ((ch = fgetc(message_file)) != EOF && ch != '\n') ;
+ }
+
+ if (size_limit > 0 && len > size_limit - written)
+ {
+ buf[size_limit - written] = '\0';
+ fputs(CS buf, fp);
+ break;
+ }
+
+ fputs(CS buf, fp);
}
}
+#ifdef EXPERIMENTAL_DMARC
+ /* Overkill, but use exact test in case future code gets inserted */
+ else if (bounce_return_body && message_file == NULL)
+ {
+ /*XXX limit line length here? */
+ /* This doesn't print newlines, disable until can parse and fix
+ * output to be legible. */
+ fprintf(fp, "%s", expand_string(US"$message_body"));
+ }
+#endif
}
-
/* Close the file, which should send an EOF to the child process
that is receiving the message. Wait for it to finish, without a timeout. */
-fclose(f);
+(void)fclose(fp);
status = child_close(pid, 0); /* Waits for child to close */
if (status != 0)
{
uschar *firstline = NULL;
uschar *msg = US"Error while reading message with no usable sender address";
-if (message_reference != NULL)
+if (message_reference)
msg = string_sprintf("%s (R=%s)", msg, message_reference);
/* Find the sender from a From line if permitted and possible */
-if (check_sender && message_file != NULL && trusted_caller &&
+if (check_sender && message_file && f.trusted_caller &&
Ufgets(big_buffer, BIG_BUFFER_SIZE, message_file) != NULL)
{
uschar *new_sender = NULL;
if (regex_match_and_setup(regex_From, big_buffer, 0, -1))
new_sender = expand_string(uucp_from_sender);
- if (new_sender != NULL) sender_address = new_sender;
+ if (new_sender) sender_address = new_sender;
else firstline = big_buffer;
}
/* If viable sender address, send a message */
-if (sender_address != NULL && sender_address[0] != 0 && !local_error_message)
+if (sender_address && sender_address[0] && !f.local_error_message)
return moan_send_message(sender_address, ident, eblock, headers,
message_file, firstline);
*/
void
-moan_tell_someone(uschar *who, address_item *addr, uschar *subject,
- char *format, ...)
+moan_tell_someone(uschar *who, address_item *addr,
+ const uschar *subject, const char *format, ...)
{
FILE *f;
va_list ap;
}
f = fdopen(fd, "wb");
-fprintf(f, "Auto_submitted: auto-generated\n");
-fprintf(f, "From: Mail Delivery System <Mailer-Daemon@%s>\n",
- qualify_domain_sender);
+fprintf(f, "Auto-Submitted: auto-replied\n");
+moan_write_from(f);
fprintf(f, "To: %s\n", who);
fprintf(f, "Subject: %s\n\n", subject);
va_start(ap, format);
}
}
-fclose(f);
+(void)fclose(f);
child_close(pid, 0); /* Waits for child to close; no timeout */
}
*/
void
-moan_smtp_batch(uschar *cmd_buffer, char *format, ...)
+moan_smtp_batch(uschar *cmd_buffer, const char *format, ...)
{
va_list ap;
int yield = (receive_messagecount > 0)? 1 : 2;
fprintf(stderr, "The rest of the batch was abandoned.\n");
-exim_exit(yield);
+exim_exit(yield, US"batch");
}
moan_check_errorcopy(uschar *recipient)
{
uschar *item, *localpart, *domain;
-uschar *listptr = errors_copy;
+const uschar *listptr = errors_copy;
uschar *yield = NULL;
uschar buffer[256];
int sep = 0;
length of the local part. */
localpart = recipient;
-domain = Ustrchr(recipient, '@');
+domain = Ustrrchr(recipient, '@');
if (domain == NULL) return NULL; /* should not occur, but avoid crash */
llen = domain++ - recipient;
while ((item = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer)))
!= NULL)
{
- uschar *newaddress = item;
- uschar *pattern = string_dequote(&newaddress);
+ const uschar *newaddress = item;
+ const uschar *pattern = string_dequote(&newaddress);
/* If no new address found, just skip this item. */
if (match_address_list(recipient, TRUE, TRUE, &pattern, NULL, 0, UCHAR_MAX+1,
NULL) == OK)
{
- uschar temp[256];
- Ustrncpy(temp, localpart, llen);
- temp[llen] = 0;
- deliver_localpart = temp;
+ deliver_localpart = string_copyn(localpart, llen);
deliver_domain = domain;
yield = expand_string_copy(newaddress);
deliver_domain = deliver_localpart = NULL;
}
f = fdopen(fd, "wb");
-fprintf(f, "Auto_submitted: auto-generated\n");
-fprintf(f, "From: Mail Delivery System <Mailer-Daemon@%s>\n",
- qualify_domain_sender);
+fprintf(f, "Auto-Submitted: auto-replied\n");
+moan_write_from(f);
fprintf(f, "To: %s\n", s);
fprintf(f, "Subject: error(s) in forwarding or filtering\n\n");
else
fprintf(f, "No valid addresses were generated.\n");
-fclose(f);
+(void)fclose(f);
child_close(pid, 0); /* Waits for child to close; no timeout */
return TRUE;