diff --git a/abck b/abck
new file mode 100755
index 0000000..fc7e055
--- /dev/null
+++ b/abck
@@ -0,0 +1,230 @@
+#!/usr/local/bin/python
+# Build a report of all unauthorized access attempts
+
+####################
+# Imports
+####################
+
+import commands
+import re
+import sys
+
+####################
+# Booleans
+####################
+
+FALSE = 0 == 1
+TRUE  = not FALSE
+
+DONE  = FALSE
+
+####################
+# Constants And Literals
+####################
+
+ANS  = ";; ANSWER SECTION:"
+AUTH = ";; AUTHORITY SECTION:"
+DIG  = "/usr/bin/dig -t ptr -x "
+LOG = "/var/log/messages"
+OUT = "./abusers"
+
+VERSION = "$Id: abck,v 1.1 2001/07/16 20:00:50 tundra Exp $"
+
+####################
+# Data Structures
+####################
+
+# Dictionary of keywords indicating attack, and position of host address/name
+# in their log records
+
+AttackKeys = {
+              "refused" : 8,
+              "unauthorized" : 7
+             }
+
+# Cache dictionary of all attacking hosts discovered this run of the program
+
+NameCache = {}
+
+####################
+# Regular Expression Handlers
+####################
+
+# Regular Expression which describes a legit IP quad address
+
+IPQuad   = r"^((\d{1,3}\.){3}(\d\d?\d?))$"
+
+####################
+# Function Definitions
+####################
+
+# Return the ending substring of a host name with 'depth' number of dots
+# in it
+
+def HostDepth(host, depth=2):
+
+    # Break the address down into components
+    components = host.split(".")
+
+    # And return the recombined pieces we want
+    return '.'.join(components[-depth:])
+
+####################
+
+# Check a name, see if it's an IP quad, and if so, return reverse.
+# If not, return the original name.
+#
+# This is better than a socket.gethostbyaddr() call because
+# this will return the authority information for an address
+# if no explicit reverse resolution is found.
+
+def CheckIPReverse(hostquad):
+    
+    # If it's an IP address in quad form, reverse resolve it
+    if re.match(IPQuad, hostquad):
+
+        DIGResults = commands.getoutput(DIG + hostquad).splitlines()
+
+        ansname = ""
+        authname = hostquad
+        # Results must either have an Answer or Authority record
+        if ( DIGResults.count(ANS) + DIGResults.count(AUTH)):
+
+            i = 0
+            while i < len(DIGResults):
+
+                if DIGResults[i].startswith(ANS):
+                    ansname = DIGResults[i+1].split()[4]
+
+                if DIGResults[i].startswith(AUTH):
+                    authname = DIGResults[i+1].split()[-2]
+
+                i += 1
+
+        if ansname:
+            hostname = ansname
+        else:
+            hostname = authname
+
+        # Get rid of trailing dot, if any
+        if hostname[-1] == '.':
+            hostname = hostname[:-1]
+
+    else:
+        hostname = hostquad
+        
+    return hostname
+
+
+####################
+
+# Paw through a log record, doing any reverse rosolution needed,
+# confirm with user, and return name of the host to notify about
+# the instrusion attempt.  A null return means the user want to
+# skip this record.
+
+
+def ProcessLogRecord(logrecord):
+
+    # Check for each known attack keyword
+
+    sendto = ""
+    for attackkey in AttackKeys.keys():
+
+        logfield = logrecord.split()
+        if logrecord.count(attackkey):
+
+            # Different attack records put the hostquad in different places
+            hostquad =  logfield[AttackKeys[attackkey]]
+            if hostquad[-1] == ',':
+                hostquad = hostquad[:-1]         # Strip trailing dots
+
+            # Go do a reverse resolution if we need to
+            hostname = CheckIPReverse(hostquad)
+
+            # Check for the case of getting a PTR record back
+            hostname = ReversePTR(hostname)
+
+            # Check if we've seen this abuser before
+            if NameCache.has_key(hostname):
+                sendto = NameCache[hostname]
+
+            # New one
+            else:
+                depth = 2
+                DONE=FALSE
+                while not DONE:
+
+                    # Set depth of default response
+                    default = HostDepth(hostname, depth)
+
+                    # Ask the user about it
+                    st = raw_input("Who Gets Message for: <%s>? %s [%s] " %
+                               (hostname[-40:],
+                                " " * (40 - len(hostname)),
+                                default))
+
+                    # Parse the response
+                    if st == "!":              # Skip this record
+                        sendto = ""
+                        DONE = TRUE
+
+                    elif st.lower() == "r":    # Less depth in recipient name
+                        if depth > 2:
+                            depth -= 1
+
+                    elif st.lower() == "l":    # More depth in recipient name
+                        if depth < len(hostname.split('.')):
+                             depth += 1
+
+                    else:
+                        if st:                 # User keyed in their own recipient
+                            sendto = st
+                        else:                  # User accepted the default
+                            sendto = default
+                        DONE = TRUE
+                        
+                NameCache[hostname] = sendto  # Cache it
+            
+    return sendto
+
+####################
+
+# Check the passed hostname and see if it looks like a PTR record.
+# If so, strip out the address portion, reverse it, and trying
+# doing another reverse lookup.  If not, just return the original hostname.
+
+def ReversePTR(hostname):
+
+    tmp = hostname.split("in-addr.arpa")
+
+    if len(tmp) > 1:                       # Looks like a PTR record
+
+        tmp = tmp[0].split('.')            # Get addr components
+        tmp.reverse()                      # and reverse their order
+        # Take the 1st four quads (some PTR records have more)
+        # and see if we can dig out a reverse.
+        hostname = CheckIPReverse('.'.join(tmp[1:5]))
+
+    return hostname
+
+
+#------------------------- Program Entry And Mail Loop -----------------------#
+
+logfile    = open(LOG)
+abuserfile = open(OUT, "w")
+
+# Read in the whole log logfileile
+for logrecord in logfile.read().splitlines():
+
+    # Go check the record in no command line constraint given
+    # or a constraint is given and exits in the record
+    if not sys.argv[1] or logrecord.count(sys.argv[1]):
+
+        sendto = ProcessLogRecord(logrecord)
+        if sendto:
+            abuserfile.write("abnot \"" + logrecord + "\" " +
+                                 sendto + "\n")
+
+logfile.close()
+abuserfile.close()