'EMSERVER' - EXIM4 GREYLISTING DAEMON
'Emserver' is a daemon program designed to work in conjunction with Exim 4.5-> It provides black/white/grey-listing and recipient verification with one simple 'call' from the Exim4 ACL. Incoming emails can be accordingly rejected at the smtp 'rcpt to:' stage (before any 'data' is received); avoiding bandwidth wastage. The daemon will:
a)reject mail for unknown recipients
b)reject mail from blacklisted [user@]domains
c)accept mail from whitelised [user@]domains.
d)reject an incoming emails on its initial receipt (and up to 10 mins thereafter), but thereafter accept ('greylisting').
INSTALLATION:
1) compile the programs:
  cc emserver.c -s -o /usr/local/sbin/emserver (or wherever !)
  cc emsclient.c -s -o /usr/local/sbin/emsclient       -   "    - 

note: only tested on SCO Openserver and Linux. SCO requires daemon.c also plus some additional compilation switches - see the sources for details. 2) edit a suitable initial input-file for emserver, which should simply contain valid users, eg
   Y>tim       )
   Y>fred      ) Note carefully, each user must be on a separate line<
   Y>john      ) with 'Y>' in the FIRST TWO characters positions and no spaces
   


3) execute emserver:
   /usr/local/sbin/emserver   input-file
(if no input file is required specify a non-existent file or /dev/null)

4) check the server is responding correctly by executing emsclient and typing 'manual' queries to the server, eg:
   C>tim     - response should be Y
   L         - response should be a list of table-entries


5) edit your existing rcpt_to acl file in Exim4 appropriately, using the sample provided as a guide (the sample is taken directly from a standard Debian acl - and you may simply be able to directly replace your existing acl file with it!)

6) restart Exim4

RECIPIENT VERIFICATION:
Emserver will maintain 'user' entries so that recipients may be verified at smtp time. Users can either be loaded from the initial table or added at any other time using emsclient. The format for adding a user is quite simple:
Y>user-name[@domain]


Care should be taken to always include the > character before the actual user-name, and there should bo no spaces in the string. The domain is optional and will depend on whether your Exim4 acl is set up to query using the full recipient@domain or simply (the default) the recipient. The square brackets are not part of the syntax - they simply indicate the optional element!

Users may be deleted at any time with:
D>user-name[@domain]


To check whether a given user is in the table:
C>user-name[@domain]

- the response will either be Y if found or X if not found

WHITE/BLACK-LISTING:
Emserver will maintain, in addition to 'greylisting' entries, white and black-listing entries. These entries must be made in a strict format, as follows:-
White-list entries:
Y[sender@]domain<sender.i.p.address

or Black-list entries:
N

If 'sender@' is omitted white/black listing is done on the whole domain, otherwise it is done on the specific sender only.

The sender.i.p.address may be 'wildcarded' to some degree:
A simple * will match any sender IP address
123.* will match any address commencing 123.
123.123.123.* will match any address commencing 123.123.123.
123.123.123.1* will match any address commencing 123.123.123.1
Note that matching is done on the literal IP address string, NOT its numeric value. Accordingly to match a range of IP addresses such as 123.123.123.49 - 123.123.123.51, you would need three separate entries. If you wish to white-list a given domain and do not know the sending IP address, you can simply use the 'domain<*' form. My personal experience is that most known sender domains can be white-listed in the 'domain<*' form - except for the ones that will inevitably be forged by spammers and scammers (eg hotmail.com, barclaysbank.co.uk etc etc). Likewise known senders@domain can nearly always be listed in the same form, as specific users@domain are generally not forged.

You will need to adopt the strategy most appropriate to your situation. It may be best to start with a fairly 'loose' white-listing strategy and 'tighten up' on any senders/domains that prove to be problematical.

In all probability if you are black-listing a domain, you will always want to use the same 'domain<*' form as there is little point in discriminating by IP address in this case.

IP-MASKING FOR GREYLISTING:
Note that the program creates greylist entries ordinarily with the full IP address of the sending machine. However, to cater for senders who may use a 'pool' of machines to send emails (each with a slightly different address within a nnn.nnn.nnn.0-254 range), the program can be compiled to 'mask' the last octet of all IP addresses before creating the greylist entry, eg IP address 123.123.123.123 would be greylisted as 123.123.123.0. To compile with this facility, specify -DIPMASK on the compile line. You can, of course, change the mask within the program source from FFFFFF00 to something more restrictive, eg FFFFFFF8, if you wish.

HOUSEKEEPING/MAINTENANCE:
Greylisting entries created by emserver are automatically flushed from the table after approximately 4 hours (assuming there are at least 50 to be flushed - to reduce cpu utilisation!), if they have not already been deleted as a result of a second successful delivery attempt. Accordingly the number of entries in the memory-resident table at any one time will be the sum of:
user(recipient) entries, and
white and black listing entries, and
greylisting entries which have not yet 'expired' and no second delivery attempt has yet been received.
Unless you receive inordinately high amounts of 'spam', the greylisting entries should not be too huge (and should in any event be limited to 4 hours worth of 'spam').

Various 'commands' can be issued to emserver via emsclient as follows:-
Ystring  
- add 'whitelist' entry to table (see above)
Nstring  
- add 'blacklist' entry to table (see above)
Ttime:string
- add 'temporary' entry to table (see below - 'debugging' only)
Dstring  
- delete entry from table, where string is the table entry of form '>recipient', or 'sender[@domain]>ip.address.
Cstring  
- return table-entry status (using same form as D above)
L        
- list permanent table entries (loaded or inserted as Y or N)
LX[Z]    
- list 'temporary' table entries [ and terminate ]
LZ[Z]    
- list all table entries [ and terminate ]
F[nnn]   
- flush expired 'temporary' entries [ nnn = minutes old or more ]
U        
- report 'memory usage'
Z0       
- debug off
Zn       
debug on (note this will have no effect unless emserver invoked with D option in the first place - as stdout will be redirected to /dev/null !) where n = 1 or 2


In all cases the square brackets are not part of the command, they simply delimit optional items.

F on its own will flush expired temporary entries using the compiled-in 'flush' time, whereas Fnnn will flush temporary entries of over nnn minutes 'old'.

T='temporary' entries must be added in the following form:
Tnnnnnnnnnn:sender@domain<sender.I.P.address>recipient
where nnnnn is Unix epoch time (seconds). This feature is really only used to reload the table from a file previously extracted with the LZ command.

Exim4 sends the following command, which may also be entered via emsclient for testing purposes:
Qsender@senderdomain<sender.i.p.address>recipient


Note that emsclient does NOT check the validity of any command/query you type. Invalid command letters will result in a response of X. The results of other invalid commands/queries are indeterminate. For exmple, if you enter the command:
Nxyz.com<    
(to create black-listing entry)
the command will be accepted and a table-entry created, but the entry will never be found upon a query since the string format is invalid (no IP address or wildcard *).

SAVING THE MEMORY TABLE AND RESTARTING:
It may be that, on occasions, it is necessary to stop the emserver daemon. In this event the memory table will clearly 'evaporate'. Any pending emails temporarily rejected on the first attempt would then also be temporariliy rejected on the second attempt, which is undesirable (although not disastrous!). Accordingly, the table can be effectively saved to a file by use of the emsclient program as follows:
emsclient LZZ > file

The table will be saved to the file and the daemon terminated before it can process any further enquires. All table-entries including recipients/users, black/white-listed senders/domains and greylist entries will be saved. If you have a 'master' list of entries, then the following could also be done:
emsclient LXZ > file;  cat master-list >> file

A fresh master-list can be created at any time using:
emsclient LZ > master-list   
the daemon will not be stopped).

RUNNING THE DAEMON IN 'SUPERVISED' MODE:
It is possible, if you wish to be ultra-safe, to run the emserver daemon in 'supervised' mode using the program mdaemon. In this event, you need to compile emserver.c with the -DNODAEMON flag. It will then not demonise itself. By running mdaemon (which is is simply compiled: cc mdaemon.c -s -o whatever), with the arguments of flag interval /usr/local/sbin/emserver file-name (where flag has the meaning as shown below and interval is the number of seconds between 'restarts' - must be 0 or more), mdaemon will daemonise itself and then execute emserver and 'supervise' it - ie restart it if it should terminate prematurely.
To be sure of not losing too many greylist entries in the event of a restart, you can also also run:
mdaemon 1  600 /usr/local/sbin/emsclient LZ  > file-name

which will result in an entire 'dump' of the emserver table to file-name every 10 minutes.
Other variations are of course possible, using mdaemon to start a shell-script which itself constructs a load file and then executes emserver, for example.

TESTING
The Z0, Z1 and Z2 commands set the debugging levels to 'none' 'some' or 'all'. However emserver must be invoked with the D 'flag' for these to be of any use, as without the 'D' flag the program will disconnect stdout and re-connect it to /dev/null (so you will bever see its output!). If you think you will need the debug output, simply do the following:
emserver  input-file  D > debug-output-file-or-device;  ems Z0
This will start the daemon in debug mode with stdout redirected suitably and immediately switch off the actual debug output.

PROCESSOR UTILISATION:
Emserver keeps its table in memory and accordingly the amount of memory used is dependent on the size of the table. However, memory is cheap and plentiful, so this should not be an issue! Memory consumed will be approximately the size of each 'string' entered into the table + 20 bytes for each entry (table and 'malloc' overhead). The size of each string is effectively rounded up to the nearest multiple of 4. As a very rough guide, 5000 entries would probably consume about 1/2 Megabyte. Actual CPU usage should be very low. On a Athlon 2800, a individual query to a large table of 5000 entries takes subtsantially less than 1 millisecond, using the standard compiled hashentry size of 839. Doubling the hashentry size would use up about another 3400 bytes approximately, and would probably speed up search time marginally - however it seems much of the CPU time is actually consumed on 'socket' operations (the socket must be opened and closed for each query) and accordingly increasing the hashentry size is not really worthwile. In this connection, it is a pity that Exim4 does not support UDP queries, as there is far less overhead with connectionless datagrams! But maybe I am being 'picky' !!

The programs use a Unix domain socket '/tmp/ems' by default - and therefore must run on the same machine as the mail server. However, they can be compiled (see the sources for details of the complie-time defines required) to use I.P. sockets - which would allow them to be run on different machines across a local network (serving for example more than one Exim MTA). Also in this configuration, emclient could exist and be run on all the different machines on the network if need be.