source: npl/mailserver/transparantspam/amavis2dspam

Last change on this file was c5c522c, checked in by Edwin Eefting <edwin@datux.nl>, 8 years ago

initial commit, transferred from cleaned syn3 svn tree

  • Property mode set to 100644
File size: 8.0 KB
RevLine 
[c5c522c]1#!/usr/bin/php
2<?
3/***
4        DatuX recipient adress verifier for SYN-3 transparant mail filter.
5
6        This will connect the SMPT servers found in qmails smtproutes and verify if the receipient adress really exists. (results are cached)
7
8        Mails to nonexisting adresses will be used to train dspam.
9
10*/
11
12require 'Net/SMTP.php';
13
14DEFINE ("CACHEDIR", "/tmp/smtpcheck");
15DEFINE ("CACHETIME", 600);
16DEFINE ("DSPAM_SOURCE", "corpus");
17//DEFINE ("DSPAM_SOURCE", "inoculation");
18
19DEFINE ("BACKUP", "spamfilter@vrieling.nl");
20
21function abort($txt)
22{
23        logError($txt);
24        exit(111); //always return temporary error
25}
26
27function logVerbose($txt)
28{
29        syslog(LOG_MAIL|LOG_INFO,getmypid().": $txt");
30};
31
32function logError($txt)
33{
34        syslog(LOG_MAIL|LOG_ERR, getmypid().": $txt");
35};
36
37define("CHECK_OK", 0);          //address exists accoding to server
38define("CHECK_REJECT",1);       //adresss was rejected by server
39define("CHECK_ERROR",2);    //some kind of other error occured (dont cache it)
40
41//connects to smtp server to check the receipient is ok
42function checkRcpt($host, $port, $from, $rcpt)
43{
44        logVerbose("Connecting to $host to check $rcpt from $from");
45       
46        /* Create a new Net_SMTP object. */
47        if (! ($smtp = new Net_SMTP($host,$port)))
48        {
49                logError("Unable to instantiate Net_SMTP object");
50                return (CHECK_ERROR);
51        }
52
53        /* Connect to the SMTP server. */
54        if (PEAR::isError($e = $smtp->connect(10)))
55        {
56                logError($e->getMessage());
57                return (CHECK_ERROR);
58        }
59
60        /* Send the 'MAIL FROM:' SMTP command. */
61        if (PEAR::isError($smtp->mailFrom($from)))
62        {
63                logError("Unable to set sender to <$from>");
64                return (CHECK_ERROR);
65        }
66
67        /* Address the message to each of the recipients. */
68        if (PEAR::isError($res = $smtp->rcptTo($rcpt)))
69        {
70                logVerbose("Address REJECTED (" . $res->getMessage().")");
71                return (CHECK_REJECT);
72        }
73       
74        //dont send anything offcourse, just disconnect and assume the adress exists at this point.
75
76        /* Disconnect from the SMTP server. */
77        $smtp->disconnect();
78       
79        logVerbose("Address OK");
80       
81        return (CHECK_OK);
82}
83
84
85
86function cacheGet($id)
87{
88        $cachefile=CACHEDIR."/$id";
89        $cachedata=array();
90        if (file_exists($cachefile))
91        {
92                $cachedata=unserialize(file_get_contents($cachefile));
93
94                //cache entry expired?
95                if (time()-$cachedata["time"]> CACHETIME)
96                {
97                        logVerbose("Cache: $id expired");
98                        $cachedata=array();
99                }
100                else
101                {
102                        logVerbose("Cache: $id loaded");
103                }
104        }
105        else
106        {
107                logVerbose("Cache: $id not cached yet");
108        }
109        return ($cachedata);
110}
111
112function cachePut($id, $cachedata)
113{
114        if (!file_exists(CACHEDIR))
115                mkdir(CACHEDIR);
116       
117        $cachefile=CACHEDIR."/$id";
118
119        $cachedata["time"]=time();
120        file_put_contents($cachefile, serialize($cachedata));
121        logVerbose("Cache: stored $id");
122}
123
124
125function cachedCheckRcpt($host, $port, $from, $rcpt)
126{
127        logVerbose("Checking host $host to $rcpt from $from");
128
129        $cachedata=cacheGet($rcpt);
130       
131        //not yet checked or last time was error?
132        if (!isset($cachedata["checkRcpt"]) || $cachedata["checkRcpt"]==CHECK_ERROR)
133        {
134                //check it
135                $cachedata["checkRcpt"]=checkRcpt($host, $port, $from, $rcpt);
136                cachePut($rcpt, $cachedata);
137        }
138       
139        return ($cachedata["checkRcpt"]);       
140}
141
142function callDspam($args, $tmpfh)
143{
144        $cmd="/usr/bin/dspam $args";
145        logVerbose("Executing: $cmd");
146        $pipes=array();
147        $proc=proc_open ( $cmd,
148                array (0=>$tmpfh),
149                $pipes);
150        if (!is_resource($proc))
151        {
152                logError("Error while executing");
153        }
154        return(proc_close($proc));
155}
156
157
158
159
160//checks data against all filters and returns descriptive string if a match is found.
161$filters=array();
162//return: 1=blacklist 0=no match -1=whitelist
163function filterCheck($user, $data)
164{
165        global $filters;
166        if (!$filters)
167        {
168                $filters=eval('return '.file_get_contents("/etc/amavis2dspam.filter").';');
169        }
170
171        //check whitelists:
172        foreach ($filters as $filter)
173        {
174                if ($filter["mode"]=="allow")
175                {
176                        if (strtolower($filter["username"])=="global" || strtolower($filter["username"])==strtolower($user))
177                        {
178                                if  (preg_match("/".$filter["regex"]."/im",$data,$matches))
179                                {
180                                        logVerbose("FILTER: Whitelisted by user ".$filter["username"].", regex '".$filter["regex"]."' on data: ".$matches[0]); 
181                                        return -1;
182                                }
183                        }
184                }
185        }
186        //check blacklists
187        foreach ($filters as $filter)
188        {
189                if ($filter["mode"]=="deny")
190                {
191                        if (strtolower($filter["username"])=="global" || strtolower($filter["username"])==strtolower($user))
192                        {
193                                if  (preg_match("/".$filter["regex"]."/im",$data,$matches))
194                                {
195                                        logVerbose("FILTER: Blacklisted by user ".$filter["username"].", regex '".$filter["regex"]."' on data: ".$matches[0]); 
196                                        return 1;
197                                }
198                        }
199                }
200        }
201        return (0);
202}
203
204//determine senders and receviers
205$sender=$argv[1];
206$sender=strtolower($sender);
207$sender=str_replace("'","_",$sender);
208array_shift($argv);
209array_shift($argv);
210$rcpts=$argv;
211
212//read qmail smtp routes
213$smtproutes=array();
214$smtproutelines=file("/var/qmail/control/smtproutes");
215foreach ($smtproutelines as $smtprouteline)
216{
217        $fields=explode(":",$smtprouteline);
218        $smtproutes[strtolower($fields[0])]["host"]=$fields[1];
219        $smtproutes[strtolower($fields[0])]["port"]=$fields[2];
220}
221if (!$smtproutes)
222        abort("error reading smtproutes");
223
224
225$filtered=array();
226
227//read mail from stdin, into temporary file
228$tmpfh=tmpfile();
229$firstblock="";
230while (!feof(STDIN))
231{
232        $data=fread(STDIN, 65535);
233        if (is_string($data))
234        {
235                if (!$firstblock)
236                        $firstblock=$data;
237                if (fwrite($tmpfh, $data)!=strlen($data))
238                {
239                        abort("error while writing mail to temporary file");
240                }
241        }
242        else
243        {
244                abort("error while reading mail from input");
245        }
246}
247
248
249$inoculatedglobal=false;
250$inoculateduser=array();
251$users_str="";
252$rcpts_str="";
253$backedup=false;
254
255//traverse all the rcpts
256foreach ($rcpts as $rcpt)
257{
258        //filter rcpt
259        $rcpt=strtolower($rcpt);
260        $rcpt=str_replace("'","_",$rcpt);
261        list($bla,$user)=explode("@", $rcpt);
262
263        logVerbose("Processing mail from $sender to $rcpt");
264
265        //reset tmp file
266        fseek($tmpfh,0);
267        unset($pipes);
268
269        $smtproute=$smtproutes[$user];
270        if (!$smtproute)
271        {
272                logError("No smtp route found for $user!");
273                if (!$inoculatedglobal)
274                {
275                        logError("INOCULATING global filter.");
276                        $inoculatedglobal=(callDspam("--user global --deliver=summary --source=".DSPAM_SOURCE." --class=spam" , $tmpfh)==0);
277                }
278                else
279                {
280                        logError("IGNORING mail.");
281                }
282        }
283        //route exists
284        else
285        {
286                //check filter for this user
287                $filtered=filtercheck($user, $firstblock);
288
289                //whitelisted?
290                if ($filtered==-1)
291                {
292                        logVerbose("INOCULATING as ham $rcpt");
293                        callDspam("--user '$user' --deliver=summary  --mail-from='$sender' --rcpt-to '$rcpt' --source=".DSPAM_SOURCE." --class=innocent" , $tmpfh);
294                        logVerbose("DELIVERING to $rcpt");
295                        $users_str.="'$user' ";
296                        $rcpts_str.="'$rcpt' ";
297                }
298
299                //is it blacklisted? does the final smtp server reject the mail?
300                else if ($filtered==1 || cachedCheckRcpt($smtproute["host"], $smtproute["port"], $sender, $rcpt)==CHECK_REJECT)
301                {
302                        if (!$inoculateduser[$user])
303                        {
304                                logVerbose("INOCULATING as spam $rcpt");
305                                if (callDspam("--user '$user' --deliver=summary  --mail-from='$sender' --rcpt-to '$rcpt' --source=".DSPAM_SOURCE." --class=spam" , $tmpfh)==0)
306//                              if (callDspam("--user '$user' --mail-from='$sender' --rcpt-to '$rcpt' --source=".DSPAM_SOURCE." --class=spam" , $tmpfh)==0)
307                                {
308                                        $inoculateduser[$user]=true;
309                                }
310
311                        }
312                        else
313                        {
314                                logVerbose("IGNORING $rcpt ($user already inoculated)");
315                        }
316
317                        //backup 1 mail to special mailbox to not lose filtered mail:
318                        if (!$backedup)
319                        {
320                                logVerbose("Backing up filtered mail to ".BACKUP);
321                                fseek($tmpfh,0);
322                                unset($pipes);
323                                $pipes=array();
324                                $proc=proc_open ( "/var/qmail/bin/qmail-inject -f '$sender' ".BACKUP,
325                                        array (0=>$tmpfh),
326                                        $pipes);
327                                        if (!is_resource($proc))
328                                        {
329                                                logError("Error while executing");
330                                        }
331                                proc_close($proc);
332                                $backedup=true;
333                        }
334                }
335
336                //normal mail
337                else
338                {
339                        logVerbose("DELIVERING to $rcpt");
340                        $users_str.="'$user' ";
341                        $rcpts_str.="'$rcpt' ";
342                }
343        }
344}
345
346//reset tmp file
347fseek($tmpfh,0);
348unset($pipes);
349
350//something left to actually try to deliver?
351if ($users_str)
352{
353        logVerbose("Calling dspam to deliver mails");
354        exit(callDspam("--user $users_str --deliver=innocent --mail-from='$sender' --rcpt-to $rcpts_str", $tmpfh));
355}
356else
357{
358        logVerbose("(No mails left to deliver to dspam)");
359        exit(0);
360}
361
362
Note: See TracBrowser for help on using the repository browser.