--- blockhosts.py	2006-12-28 21:15:52.000000000 -0500
+++ blockhosts.erik.py	2006-12-28 21:27:22.000000000 -0500
@@ -68,6 +68,10 @@
 datetime, optparse.
 
 ====
+Modified 29/12/06 by Erik Ljungström to allow for email alerts being
+sent whenever a host is blocked.
+
+====
 BlockHosts Script License
 This work is hereby released into the Public Domain.
 To view a copy of the public domain dedication, visit
@@ -110,7 +114,11 @@
 # and no config file provided
 HC_OPTIONS = {
     "HOSTS_BLOCKFILE": "/etc/hosts.allow",
-
+    "SMTP_SERVER": "localhost",
+    "NOTIFY_ADDRESS": '',
+    "SMTP_USER":'',
+    "SMTP_PASSWD":'',
+    "SENDER_ADDRESS": "BlockHosts <blockhosts@localhost>",    
     "LOGFILES": ( "/var/log/secure", ),
         # default list of logs to process, multiple files can be listed
 
@@ -221,7 +229,7 @@
     import time
     import errno
     import fcntl
-
+    import smtplib
     import datetime
     from optparse import OptionParser, OptionGroup
     import ConfigParser
@@ -467,9 +475,37 @@
 
 
 #######################################################################
-
+class MailAlert:
+    def __init__(self, address=["root@localhost.localdomain"], sender_address="blockhosts@localhost.localdomain", smtp_server="localhost", smtp_user='', smtp_passwd='', host=''):
+        self.__address = address.replace('\@', '@')
+        self.__sender_address = sender_address.replace('\@', '@')
+        self.__smtp_server = smtp_server
+        # If smtp_user and passwd is empty, assume no authentication is necessary
+        self.__smtp_user = smtp_user.replace('\@', '@')
+        self.__smtp_passwd = smtp_passwd
+        self.__host = host
+    def sendMail(self):
+        if len(self.__address) == 0:
+            return
+        session = smtplib.SMTP(self.__smtp_server)
+        time = datetime.datetime.now().strftime("%a %d/%m/%Y %H:%M:%S")
+        message = "To: " + self.__address
+        message += "\nFrom: "+ self.__sender_address
+        message += "\nSubject: Host blocked!\n\n"
+        message += time
+        message += "\nAdded abusive host: " + self.__host + " to the naughty list"
+        if len(self.__smtp_user) > 0:
+            session.login(self.__smtp_user, self.__smtp_passwd)
+        smtpresult = session.sendmail(self.__sender_address, self.__address, message)
+        if smtpresult:
+            errstr = ""
+            for recip in smtpresult.keys():
+                errstr = """Unable to deliver mail to: %s Server responded: %s %s %s"""\
+                         % (recip, smtpresult[recip][0], smtpresult[recip][1], errstr)
+                raise smtplib.SMTPException, errstr
+        
 class BlockHosts:
-    def __init__(self, blockfile, block_services="ALL"):
+    def __init__(self, blockfile, block_services="ALL",address=["root@localhost.localdomain"], sender_address="blockhosts@localhost.localdomain", smtp_server="localhost", smtp_user='', smtp_passwd=''):
         self.__abusive_hosts = {} # hosts -> HostData [count, last seen]
         self.__offset_first_marker = -1L
         self.__remaining_lines = [] # all lines after the 2nd end marker
@@ -477,6 +513,12 @@
         self.__block_services = block_services
         self.__found_first_marker = False
         self.__found_second_marker = False
+        #Mail alert variables - purely for passing on to the MailAlert class
+        self.__address = address
+        self.__sender_address = sender_address
+        self.__smtp_server = smtp_server
+        self.__smtp_user = smtp_user
+        self.__smtp_passwd = smtp_passwd
 
 #######
     def load_hosts_blockfile(self, logoffsets):
@@ -644,10 +686,10 @@
         # first collect all the lines that will go in the blockhosts
         # section of blockfile - this is stored in lines[]
         status = False
-
         lines.append("%s\n" % HOSTS_MARKER_LINE) # first marker line
-
         for host in new_hosts:
+            mailer = MailAlert(self.__address, self.__sender_address, self.__smtp_server,self.__smtp_user, self.__smtp_passwd, host)
+            mailer.sendMail()
             lines.append("%s: %15s : deny\n" % (self.__block_services, host))
 
         if new_hosts: lines.append("\n")
@@ -733,7 +775,7 @@
     except NoSectionError, e:
         traceback.print_exc()
         die("Config file missing 'constants' section", e)
-
+        
     keys = allitems.keys()
     for key in keys:
         if options.has_key(key):
@@ -759,10 +801,15 @@
 
     config_options = copy.copy(HC_OPTIONS)
     get_config(config_options)
-
+#DEFAULTS
     oparser.set_defaults(verbose=1,
         ignore_offset=False,
         dry_run=False,
+        notify_address=config_options["NOTIFY_ADDRESS"],
+        sender_address=config_options["SENDER_ADDRESS"],
+        smtp_server=config_options["SMTP_SERVER"],
+        smtp_user=config_options["SMTP_USER"],
+        smtp_passwd=config_options["SMTP_PASSWD"],
         logfiles=",".join(config_options["LOGFILES"]),
         blockfile=config_options["HOSTS_BLOCKFILE"],
         block_services=config_options["BLOCK_SERVICES"],
@@ -822,9 +869,24 @@
     oconfig.add_option("--lockfile", metavar="FILE",
         help="Prevent multiple instances from running - open this file for locking and writing (\"%s\")" % defaults.lockfile)
 
+    oconfig.add_option("--notify-address", metavar="ADDRESS",
+        help="Address to send notification emails to (Leave blank to disable email alerts altogether) (\"%s\")" % defaults.notify_address)
+
+    oconfig.add_option("--sender-address", metavar="ADDRESS",
+        help="Address appearing as sender address in notification emails (\"%s\")" % defaults.sender_address)
+
+    oconfig.add_option("--smtp-server", metavar="HOST",
+        help="SMTP server to send notification emails through (\"%s\")" % defaults.smtp_server)
+
+    oconfig.add_option("--smtp-user", metavar="USERNAME",
+        help="If SMTP authentication is required, use this username (\"%s\")" % defaults.smtp_user)
+    oconfig.add_option("--smtp-passwd", metavar="PASSWORD",
+        help="If SMTP authentication is required, use this password (\"%s\")" % defaults.smtp_passwd)
+
     oconfig.add_option("--echo", type="string", metavar="TAG",
         help="Prints TAG on stderr and syslog, may be used to identify a run of blockhosts (%s)" % defaults.echo)
 
+                                
     oparser.add_option_group(oconfig)
 
     (options, remaining_args) = oparser.parse_args()
@@ -873,8 +935,8 @@
     prune_date = NOW_DATETIME - datetime.timedelta(0, options.discard*60*60)
 
     info(" ... will discard all host entries older than ", prune_date.strftime("%Y-%m-%d %H:%M"))
-
-    dh = BlockHosts(options.blockfile, options.block_services)
+# Had to do this.. options:: not reachable from within BlockHosts?
+    dh = BlockHosts(options.blockfile, options.block_services,options.notify_address, options.sender_address, options.smtp_server, options.smtp_user, options.smtp_passwd )
     prev_logoffsets = {}
     new_logoffsets = {}
     ip_pid = {}
@@ -947,9 +1009,8 @@
         debug(" ------- finished looking into log file: ", logfile)
 
     debug(" ------- collecting block file updates --- ")
-
     dh.update_hosts_blockfile(options.blockcount, prune_date, new_logoffsets, options.dry_run)
-
+    
     if not options.dry_run: lock.unlock()
 
 #######################################################################
