[exim] checking maildirs at SMTP time

Startseite
Nachricht löschen
Nachricht beantworten
Autor: Nathan Ollerenshaw
Datum:  
To: Exim Users Mailing List
Betreff: [exim] checking maildirs at SMTP time
Hi folks,

some of you might remember me asking about this a while ago.
Basically, I wanted a way of checking if a mailbox is over quota at
the "RCPT TO" stage, rather than at delivery time.

Philip H recommended a socket daemon approach, as did others. Some
recommended a periodic scan of all mailboxes, which writes to a file
to be used to :fail: particular mailboxes that are full. This is the
approach suggested and implemented by Tim Jackson (http://
www.timj.co.uk/linux/rcpt-time-quota-maildir.php).

I decided to implement the daemon approach.

You can find the result of my labour here: http://www.stupendous.net/
files/mailquotad

The script is basically a socket daemon that expects a maildir path
on its socket input, and it writes a 'yes' if the mailbox is over
quota and a 'no' if not back to exim.

It has several deficiencies:

1) It is not portable in its current state. It uses /proc to
determine if its already running. If you can suggest a better way to
check this that is portable, let me know. I didn't spend much time on
it.

2) It maintains a cache of all maildirs its ever checked. This cache
is not managed; if it grows too large, the daemon will slow down or
consume all available memory. Use at your own peril.

3) It is not multithreaded/multiprocess; it can only deal with one
request at a time. This is probably a feature.

Features:

1) Only recalculates the quota if it absolutely has to; it does a stat
() on the maildirsize file to determine if its been modified and only
then will it recalculate.

2) It will not recalculate if it has already done so within a
configurable time limit (set to 60 seconds by default). This is in
response to Tim's suggestion that a large spam attack might cause
problems with the daemon. I'm not convinced that this is a feature,
actually, because it makes the daemon 'sloppy'. If a message comes in
while the mailbox is over quota but the mailquotad hasn't realised it
yet, the message will be queued locally. I think I prefer it to check
quota for every delivery, which is easy to fix by setting $maxkeep to
-1. I'm not sure what effect this would have on a busy mailhost though.

3) if the maildirsize file doesn't exist, it will return that the
mailbox is not over quota, assuming that a message delivery will
create the maildirsize file.

Usage:

To use it, you will be required to have a variable that you can feed
to the ${readsocket call that contains the location of the maildir. I
use LDAP, and I only do a single LDAP lookup for each recipient
usually, and fill a variable with the result of that lookup, like:

GET_ADDRESS_DATA = ${lookup ldap {\
         ldap:///BASEDN??sub?(&(uid=${quote_ldap:$local_part}@$ 
{quote_ldap:$domain}))\
         }\
}


lookup:
driver = redirect
address_data = GET_ADDRESS_DATA
# data is intentionally left blank so that the router will decline
# we just want this router to do a lookup so the results are availble
# for the other routers.
data =

When this lookup is run, $address_data is filled with the LDAP entry
information, including the maildir location.

So, we run the lookup by having a 'require' clause in the ACL, and
then a defer if the mailbox is full. Or a deny, if you wish.

   require domains       = +local_domains
           verify        = recipient
   defer   domains       = +local_domains
           condition     = ${if eq\
                                 {${readsocket{/var/run/ 
mailquotad.sock}\
                                    {${extract{mailMessageStore} 
{$address_data}}/Maildir}\
                                    {2s}\
                                  }}\
                                  {yes}\
                                  {1}{0}\
                            }
           message       = mailbox full


Which brings me to a question, is it better to deny or to defer on a
full mailbox?

the mailquotad code is shared in the hope that it might be useful.
I'd love to hear any suggestions that people might have or any
constructive criticisms. It should be understood that I consider this
in a prototype state, and not suitable for a production mail system.
I do intend to rewrite it more thoroughly (with memory management and
better error handling) in C at some point soon.

Please let me know what you think :)

Regards,

Nathan.

--
Nathan Ollerenshaw / Systems Engineer
Systems Engineering
ValueCommerce Co., Ltd.

Tokyo Bldg 4F 3-32-7 Hongo Bunkyo-ku Tokyo 113-0033 Japan
Tel. +81.3.3817.8995 Fax. +81.3.3812.4051
mailto:nathan@valuecommerce.co.jp

"Propose to an Englishman any principle, or any instrument, however
admirable, and you will observe that the whole effort of the
English mind
is directed to find a difficulty, a defect, or an impossiblity in
it. If you speak
to him of a machine for peeling a potato, he will pronounce it
impossible:
if you peel a potato with it before his eyes, he will declare it
useless,
because it will not slice a pineapple." - Charles Babbage