#!/usr/bin/env python                                                                                       
# -*- coding: utf-8 -*-                                                                                     

#***************************************************************************
#*                                                                         *
#*   This program is free software; you can redistribute it and/or modify  *
#*   it under the terms of the GNU General Public License as published by  *
#*   the Free Software Foundation; either version 2 of the License, or     *
#*   (at your option) any later version.                                   *
#*                                                                         *
#***************************************************************************

""" 
    Written by Robert Penz <robert@penz.name>                                
"""                                                                          

import re
import sys
import time
import subprocess
import signal
import os

# Example
#Jul 26 15:24:53 mail courieresmtpd: error,relay=::ffff:189.87.47.237,from=<autopilots40502017@xxxxxxxx>,to=<autopilots40502017@xxxxxxxxx>: 511 http://www.spamhaus.org/query/bl?ip=189.87.47.237
#Jul 27 23:18:04 mail courieresmtpd: error,relay=::ffff:80.87.99.6,from=<anxiety40502017@xxxxxxxx>,to=<anxiety40502017@xxxxxxx>: 511 Blocked - see http://www.spamcop.net/bl.shtml?80.87.99.6
#511 Exploitable Server See: http://www.sorbs.net/lookup.shtml?190.6.198.52
#511 Spam sent to the mailhost mail.ixlab.de was detected by NiX Spam at Mon, 27 Jul 2009 22:22:57 +0200, see http://www.dnsbl.ma...     
regexLog = re.compile(r'^.*courieresmtpd: error,relay=::ffff:(?P<ip>[^,]+),from=.*: 511 .*http://.*$')
iptablesRecentFilename = "/proc/net/ipt_recent/spammer"

psCommand = "/bin/ps -A eo pid,ppid,command"

# Print usage message and exit
def usage(*args):             
    sys.stdout = sys.stderr   
    print __doc__             
    print 50*"-"              
    for msg in args: print msg
    sys.exit(2)    

def tail_f(file, startAtTheEnd = True, interval = 1.0):
    """ returns any new line """
    if startAtTheEnd:
        file.seek(0, 2)
    while True:
        where = file.tell()
        line = file.readline()
        if not line:
            time.sleep(interval)
            file.seek(where)
        else:
            yield line

def watch4spammers(filename):
    """ looks in the courier logfile if a mail has been bounced to an DNS RBL check if so
        add the IP to the iptables blocklist
    """
    for line in tail_f(open(filename)):
        found = regexLog.match(line)
        if found:
            print found.group("ip")
            add2iptables(found.group("ip"))           
            findAndKillSubmitProcess(found.group("ip"))

def add2iptables(ip):
    """ adds the provided IP to the iptableslist """
    open(iptablesRecentFilename, "a").write("+%s\n" % ip)

def findAndKillSubmitProcess(ip):
    """ search for the submit process with is responsible for the given ID """
    for line in subprocess.Popen(psCommand.split(), stdout=subprocess.PIPE).communicate()[0].splitlines():
        # example
        # 30319 30317 submit -src=smtp esmtp dns; 89.123.163.183 ([::ffff:89.123.163.183]) TLS_COMPRESSION
        pid, ppid, cmd = line.split(None, 2)
        if not cmd.startswith("submit"):
            continue
        s = line.split("[::ffff:")
        if len(s) == 2 and s[1].startswith(ip):
            print pid, ppid, ip
            try:
                os.kill(int(ppid), signal.SIGTERM)
            except:
                # process may be terminated already
                pass

# Main program: parse command line and start processing                                                           
def main():                                                                                                       
    watch4spammers(sys.argv[1])
    
if __name__ == '__main__':
    main()
