Hello, I am using CentOS 4 and DSPAM Anti-Spam Suite 3.6.8
(agent/library)

Configuration parameters: --enable-daemon --enable-clamav --enable-
syslog --with-logdir=/var/log/dspam --with-storage-driver=mysql_drv
--enable-virtual-users --enable-preferences-extension --with-mysql-
libraries=/usr/lib/mysql --with-mysql-includes=/usr/include/mysql
--enable-large-scale --enable-long-usernames --with-dspam-home-
owner=dspam --with-dspam-home-group=dspam --with-dspam-owner=dspam
--with-dspam-group=dspam --enable-debug


Description of problem
----------------------

With notifications turned on, if dspam processes a first-time message to
a test user, with or without --client option:

dspam --user testuser --deliver=innocent --stdout < v1

I expect to see the message on stdout, preceded by the firstrun.txt
notice, but all I see is the message. Syslog reports, 'Error getting
exit status of delivery agent: : Broken pipe' and debug log reports,
'Opening pipe to LDA:'. I do see a firstrun timestamp file created
in testuser's home directory, so only the delivery of the notice
seems in error.

A similar problem has been reported twice in dspam-user:

In 2006-02-23, http://article.gmane.org/gmane.mail.spam.dspam.user/9833,
'When dspam tries to send firstrun.txt or firstspam.txt, the user
doesn't receive the message, and I see the error message in my
mail.info: mail1 dspam[4390]: Error getting exit status of delivery
agent: : Broken pipe.'

In 2006-09-27, http://article.gmane.org/gmane.mail.spam.dspam.user/11481,
'My debug output seems to indicate the DSPAM is calling a NULL local
delivery agent and thinks the message went out ok, when in fact, nothing
went out nor was received.'


Analysis
--------

If dspam can deliver a message to stdout, then why can't it similarly
deliver a notification? Rather, dspam tries a local delivery with null
delivery arguments.

Inspecting the source code, we see that an innocent message is delivered
by the following function call:

deliver_message(ATX, parse_message->data,
                (ATX->flags & DAF_STDOUT) ? NULL : ATX->mailer_args,
                node_nt->ptr,
                ATX->sockfd ? ATX->sockfd : stdout,
                DSR_ISINNOCENT);

However, a notification is delivered by send_notice() calling the same
function with slightly different arguments:

deliver_message(ATX, b->data,
                ATX->mailer_args,
                username,
                stdout,
                DSR_ISINNOCENT);

When the same arguments are used, we get the expected result: a
notification is sent along with the innocent message. Using --client
also works.

Testing now with --deliver=spam, we expect to see only the notification
on stdout, because the test message is innocent and won't be delivered
to stdout. This proves true without --client, but in client mode,
although the notification is sent, the LMTP session hangs with the line,
'250 2.6.0 <testuser> Message accepted for delivery: INNOCENT', and one
needs to send a TERM signal to break the session.


Analysis (client mode)
----------------------

If dspam can successfully terminate an LMTP session after sending a
notification followed by an innocent message, why can't it terminate
after sending only a notification?

Inspecting the code again, we see that send_notice() is not setting a
flag, relying on it being set elsewhere after calling deliver_message().
However, in this particular case, no other parts of the code will set it
because no other output will be delivered.

if (ATX->sockfd && ATX->flags & DAF_STDOUT) ATX->sockfd_output = 1;

The flag is used by daemon.c, as follows:

    /* Send a terminating '.' if --stdout in 'dspam' mode */

    if (ATX->sockfd_output) {
      if (!(ATX->flags & DAF_SUMMARY))
        if (send_socket(TTX, ".")<=0)
          goto CLOSE;

When the flag is set, we get the expected results for executing the
following command:

dspam --client --user testuser --deliver=spam --stdout < v1


One more problem
----------------

In 2006-08-06,
http://article.gmane.org/gmane.mail.spam.dspam.devel/2526, the poster
writes, '...quarantine, web frontend, log and web stats are all
disabled. I noticed that with this configuration, I cannot use the
notifications out-of-the-box as the folders under $DSPAM_HOME/data/ are
not created automatically (resulting in the notifications being sent
ever and ever).' He recommends, '_ds_prepare_path_for must be called...'

The function mentioned is responsible for creating the path for the
user's home directory if needed. The do_notifications() function, which
calls send_notice() once for each possible notification, presumes other
parts of the code will have created the user's home directory. But in
the configuration explored by the poster, no other features requiring a
user directory is enabled.

We can modify do_notifications() to call _ds_prepare_path_for() before
creating a timestamp file:

        _ds_prepare_path_for(filename); /* add this */
        file = fopen(filename, "w");


Two annoyances
--------------

1. Notification messages lack a 'From ' header, necessary for mail
systems using the mbox format and probably harmless for other formats.
The send_notice() function can add it to the outgoing notice with the
following lines of code:

   snprintf(buf, sizeof(buf), "From DSPAM %s", ctime(&now));
   buffer_cat(b, buf);

(Also suggested in
http://article.gmane.org/gmane.mail.spam.dspam.user/10121.)

2. If I omit any of the files containing a notice, dspam doesn't send
the notice, but it creates a corresponding timestamp file anyway. It
would be useful if a timestamp was created only if a notice was sent,
for example, if I decide to add a notification file later, its message
would get sent without my having to manually remove the timestamp file.

Inspecting send_notice() again, we see that it returns two
possible errors:

    LOG(LOG_ERR, ERR_IO_FILE_OPEN, filename, strerror(errno));
    return EFILE;
    ...
    LOG(LOG_CRIT, ERR_MEM_ALLOC);
    return EUNKNOWN;

The first error is failure to open the file containing the notice to
send. In do_notifications(), we can test for success from send_notice()
before creating a timestamp file, rather than creating it
unconditionally:

    if (!send_notice(ATX, "firstrun.txt", ATX->mailer_args, CTX->username)) {
      /* create timestamp file here */
    }


Patch
-----

A patch against version 3.6.8 is attached. It makes changes to file
dspam.c, in functions do_notifications() and send_notice().

The author or authors of this submission hereby release any and all
copyright interest in this code, documentation, or other materials
included to the DSPAM project and its primary governors. We intend this
relinquishment of copyright interest in perpetuity of all present and
future rights to said submission under copyright law.

-- 
Steven Chan
Vancouver Community Network

Reply via email to