Sending email using PHP mail() function fails via dma(8)

Setting up MediaWiki in a 14.3R jail with default `dma` mail config + `SMART_HOST`` set to host system (also 14.3R) running sendmail.

I couldn't get email to send. dma -> sendmail (SMTP) was rejecting with:

421 4.5.0 Bare carriage return (CR) not allowed

and leaving the message stuck in dma's queue.

Debugging with a command-line PHP (8.3.27) call to mail():

jexec mwjail php -r 'mail("me","Test Subject","Test Message");'

and getting a tcpdump on the "wire" between dma and sendmail there is a stray CR at the end of the "Test Message" string:

0d0a 0d0d 0a2e 0d0a

i.e. The end of the message string is followed by: <CR/LF><CR><CR/LF>.<CR/LF>

I couldn't find any useful information about a likely cause or how to fix and eventually just tried toggling PHP's configuration item:

mail.mixed_lf_and_crlf"0"INI_SYSTEM
INI_PERDIR
Added in PHP 8.2.4
mail.mixed_lf_and_crlf bool
Allows reverting the line separator for email headers and message bodies to LF (Line Feed), mimicking the non-compliant behavior of PHP 7. It is provided as a compatibility measure for certain non-compliant Mail Transfer Agents (MTAs) that fail to correctly process CRLF (Carriage Return + Line Feed) as a line separator in email headers and message content.


Which fixed (bodged?) it.

Curiously this is actually being done as a migration of MediaWiki from the host system to the jail. The host system is also (currently) running PHP 8.3.27 - having had numerous previous versions including prior to 8.2.4 and it has never been a problem there with PHP sending emails via sendmail, despite that PHP option reportedly defaulting to "Off".

I haven't looked into it any further yet but I could guess that PHP could be mis-handling its submission via sendmail (i.e. not SMTP) and sendmail(8) is being more tolerant? (sendmail "tolerant"? Maybe not?)

Anyway, not too worried since I have found a work-around, but I'm bothered by not having been able to find any extant reports of this. I'm wondering if anyone has any further insights to offer on this?
 
Bah! I relaxed too soon (motivated by sleep!). And I also forgot to mention that the original MW was a 1.39 install, the jailed MW is 1.43.

While the command-line PHP mail() test was successful, emails sent from MediaWiki are still being rejected by the SMART_HOST sendmail.

Here's what I've found:

php.ini: mail.mixed_lf_and_crlfmailer.conf (jail) = sendmailmailer.conf (jail) = dma
offPHP mail(): 🆗
MediaWiki: 🆗
PHP mail(): ❌
MediaWiki: ❌
onPHP mail(): 🆗
MediaWiki: 🆗
PHP mail(): 🆗
MediaWiki: ❌

PHP mail() test is: # jexec mwjail php -r 'mail("me","Test Subject","Test Message");'
MediaWiki test is initiating send of an "email address confirmation" email.

Host sendmail maillog error for the failed messages is:
sm-mta[##]: ##: collect: relay=<jail hostname>, from=<mwiki@<jail hostname>>, info=Bare carriage return (CR) not allowed, where=header, status=tempfail
Jail dma maillog error is:
dma[##.##][##]: remote delivery deferred: <host hostname> [<host IP address>] failed after final DATA: 421 4.5.0 Bare carriage return (CR) not allowed

The failed send attempts also leave these processes running:
Mediawiki:
www 97630 0.0 0.0 19548 7064 - IsJ 19:27 0:00.04 /usr/sbin/sendmail -t -i -f no-reply@<jail hostname> (dma)
PHP mail():
mailnull 6102 0.0 0.0 19548 7060 - SsJ 00:54 0:00.01 /usr/sbin/sendmail -t -i (dma)

At least there is still a work-around: switch the jail's mailer from dma to sendmail.

Any thoughts?
 
dma is buggy
and does not support lines with '\r\n'

at the first look in conf.c trim line should check if before \n is a \r and kill it too
basically when it sends the smtp DATA command reads each line, kills '\n', appends '\r\n' and sends
it will just duplicate every '\r' if the source has '\r\n'
so it screws every message line
 
Diff:
diff --git a/conf.c.orig b/conf.c
index 13cfac7..4849048 100644
--- a/conf.c.orig
+++ b/conf.c
@@ -55,10 +55,11 @@ trim_line(char *line)
 {
     size_t linelen;
     char *p;
-
-    if ((p = strchr(line, '\n')))
+    if ((p = strchr(line, '\n'))) {
         *p = (char)0;
-
+        if ((p != line) && (*(--p) == '\r'))
+            *p = (char)0;
+    }
     /* Escape leading dot in every case */
     linelen = strlen(line);
     if (line[0] == '.') {
diff --git a/net.c.orig b/net.c
index 0079875..4c792c1 100644
--- a/net.c.orig
+++ b/net.c
@@ -589,6 +589,7 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)

     error = 0;
     while (!feof(it->mailf)) {
+        int has_cr;
         if (fgets(line, sizeof(line), it->mailf) == NULL)
             break;
         linelen = strlen(line);
@@ -599,6 +600,10 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)
             goto out;
         }

+        if (linelen > 1 && line[linelen -2] == '\r')
+            has_cr = 1;
+        else
+            has_cr = 0;
         /* Remove trailing \n's and escape leading dots */
         trim_line(line);

@@ -608,8 +613,7 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)
         */
         if (line[0] == '.')
             linelen++;
-
-        if (send_remote_command(fd, "%s", line) != (ssize_t)linelen+1) {
+        if (send_remote_command(fd, "%s", line) != (ssize_t)linelen+1-has_cr) {
             syslog(LOG_NOTICE, "remote delivery deferred: write error");
             error = 1;
             goto out;
 
what bugs if how that nobody was hit by this until now since rfc2822 and superseding rfc 5322 say that mail lines should be terminated by cr+lf not just lf
so nobody is using a compliant mua with dma ?
 
I see a question here with no response - I think it is actually the same problem or related one:
 
Incidentally, I am experiencing some weirdness with dma serving periodic mails to my mail server - everything works with daily and daily security reports, but irregularly, not too often, weekly or monthly mails fails. There is some dma crash noted in log, mail is prepared in local queue, so when I see this, I just invoke
Code:
sendmail -q
command and the missing mail is then re-delivered.
 
Back
Top