- Removed "utils" directory

- Created a "DNSUtils" class in filter.py

git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@274 a942ae1a-1317-0410-a47c-b1dcaea8d605
0.x
Cyril Jaquier 2006-08-20 20:39:45 +00:00
parent ec8f620f80
commit 535b9a6a36
7 changed files with 100 additions and 480 deletions

View File

@ -28,7 +28,6 @@ from failmanager import FailManager
from failmanager import FailManagerEmpty from failmanager import FailManagerEmpty
from failticket import FailTicket from failticket import FailTicket
from jailthread import JailThread from jailthread import JailThread
from utils.dns import *
import time, logging, os, re, sys, socket import time, logging, os, re, sys, socket
# Gets the instance of the logger. # Gets the instance of the logger.
@ -257,8 +256,8 @@ class Filter(JailThread):
s.insert(1, '32') s.insert(1, '32')
s[1] = long(s[1]) s[1] = long(s[1])
try: try:
a = cidr(s[0], s[1]) a = DNSUtils.cidr(s[0], s[1])
b = cidr(ip, s[1]) b = DNSUtils.cidr(ip, s[1])
except Exception: except Exception:
return False return False
if a == b: if a == b:
@ -379,7 +378,7 @@ class Filter(JailThread):
if timeMatch: if timeMatch:
date = self.getUnixTime(timeMatch.group()) date = self.getUnixTime(timeMatch.group())
try: try:
ipMatch = textToIp(match.group("host")) ipMatch = DNSUtils.textToIp(match.group("host"))
if ipMatch: if ipMatch:
for ip in ipMatch: for ip in ipMatch:
failList.append([ip, date]) failList.append([ip, date])
@ -457,4 +456,100 @@ class Filter(JailThread):
ret = [("Currently failed", self.failManager.size()), ret = [("Currently failed", self.failManager.size()),
("Total failed", self.failManager.getFailTotal())] ("Total failed", self.failManager.getFailTotal())]
return ret return ret
##
# Utils class for DNS and IP handling.
#
# This class contains only static methods used to handle DNS and IP
# addresses.
import socket, struct
class DNSUtils:
dnsRe = re.compile("(?:(?:\w|-)+\.){2,}\w+")
ipRe = re.compile("(?:\d{1,3}\.){3}\d{1,3}")
@staticmethod
def dnsToIp(dns):
""" Convert a DNS into an IP address using the Python socket module.
Thanks to Kevin Drapel.
"""
try:
return socket.gethostbyname_ex(dns)[2]
except socket.gaierror:
return list()
@staticmethod
def textToDns(text):
""" Search for possible DNS in an arbitrary text.
Thanks to Tom Pike.
"""
match = dnsRe.find(text)
if match:
return match
else:
return None
@staticmethod
def searchIP(text):
""" Search if an IP address if directly available and return
it.
"""
match = ipRe.find(text)
if match:
return match
else:
return None
@staticmethod
def isValidIP(str):
""" Return true if str is a valid IP
"""
s = str.split('/', 1)
try:
socket.inet_aton(s[0])
return True
except socket.error:
return False
@staticmethod
def textToIp(text):
""" Return the IP of DNS found in a given text.
"""
ipList = list()
# Search for plain IP
plainIP = DNSUtils.searchIP(text)
if not plainIP == None:
if isValidIP(element):
ipList.append(element)
if not ipList:
# Try to get IP from possible DNS
dnsList = DNSUtils.textToDns(text)
if not dnsList == None:
dns = DNSUtils.dnsToIp(element)
for e in dns:
ipList.append(e)
return ipList
@staticmethod
def cidr(i, n):
""" Convert an IP address string with a CIDR mask into a 32-bit
integer.
"""
# 32-bit IPv4 address mask
MASK = 0xFFFFFFFFL
return ~(MASK >> n) & MASK & DNSUtils.addr2bin(i)
@staticmethod
def addr2bin(str):
""" Convert a string IPv4 address into an unsigned integer.
"""
return struct.unpack("!L", socket.inet_aton(str))[0]
@staticmethod
def bin2addr(addr):
""" Convert a numeric IPv4 address into string n.n.n.n form.
"""
return socket.inet_ntoa(struct.pack("!L", addr))

View File

@ -1,25 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban 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.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"

View File

@ -1,102 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban 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.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import os, re, socket, struct
def dnsToIp(dns):
""" Convert a DNS into an IP address using the Python socket module.
Thanks to Kevin Drapel.
"""
try:
return socket.gethostbyname_ex(dns)[2]
except socket.gaierror:
return list()
def textToDns(text):
""" Search for possible DNS in an arbitrary text.
Thanks to Tom Pike.
"""
match = re.findall("(?:(?:\w|-)+\.){2,}\w+", text)
if match:
return match
else:
return []
def searchIP(text):
""" Search if an IP address if directly available and return
it.
"""
match = re.findall("(?:\d{1,3}\.){3}\d{1,3}", text)
if match:
return match
else:
return []
def isValidIP(str):
""" Return true if str is a valid IP
"""
s = str.split('/', 1)
try:
socket.inet_aton(s[0])
return True
except socket.error:
return False
def textToIp(text):
""" Return the IP of DNS found in a given text.
"""
ipList = list()
# Search for plain IP
plainIP = searchIP(text)
for element in plainIP:
if isValidIP(element):
ipList.append(element)
if not ipList:
# Try to get IP from possible DNS
dnsList = textToDns(text)
for element in dnsList:
dns = dnsToIp(element)
for e in dns:
ipList.append(e)
return ipList
def cidr(i, n):
""" Convert an IP address string with a CIDR mask into a 32-bit
integer.
"""
# 32-bit IPv4 address mask
MASK = 0xFFFFFFFFL
return ~(MASK >> n) & MASK & addr2bin(i)
def addr2bin(str):
""" Convert a string IPv4 address into an unsigned integer.
"""
return struct.unpack("!L", socket.inet_aton(str))[0]
def bin2addr(addr):
""" Convert a numeric IPv4 address into string n.n.n.n form.
"""
return socket.inet_ntoa(struct.pack("!L", addr))

View File

@ -1,91 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban 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.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging, smtplib, email.Utils
from utils.strings import replaceTag
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
class Mail:
""" Mailer class
"""
def __init__(self, host, port = 25):
self.host = host
self.port = port
self.localTimeFlag = False
def setFromAddr(self, fromAddr):
""" Set from: address
"""
self.fromAddr = fromAddr
def setUser(self, user):
""" Set smtpuser
"""
self.user = user
def setPassword(self, password):
""" Set smtppassword
"""
self.password = password
def setToAddr(self, toAddr):
""" Set to: address
"""
self.toAddr = toAddr.split()
def setLocalTimeFlag(self, localTimeFlag):
""" Set to: address
"""
self.localTimeFlag = localTimeFlag
def sendmail(self, subject, message, aInfo):
""" Send an email using smtplib
"""
subj = replaceTag(subject, aInfo)
msg = replaceTag(message, aInfo)
mail = ("From: %s\r\nTo: %s\r\nDate: %s\r\nSubject: %s\r\n\r\n" %
(self.fromAddr, ", ".join(self.toAddr),
email.Utils.formatdate(localtime = self.localTimeFlag),
subj)) + msg
try:
server = smtplib.SMTP(self.host, self.port)
#server.set_debuglevel(1)
if not self.user == '':
server.login(self.user, self.password)
server.sendmail(self.fromAddr, self.toAddr, mail)
logSys.debug("Email sent to " + `self.toAddr`)
server.quit()
except Exception, e:
logSys.error("Unable to send mail to " + self.host + ":" +
`self.port` + " from " + self.fromAddr + " to " +
`self.toAddr` + ": " + `e` + ": " + `e.args`)

View File

@ -1,109 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban 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.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import os, logging
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
class PIDLock:
""" Manages the PID lock file.
The following class shows how to implement the singleton pattern[1] in
Python. A singleton is a class that makes sure only one instance of it
is ever created. Typically such classes are used to manage resources
that by their very nature can only exist once.
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52558
"""
class __impl:
""" Implementation of the singleton interface """
def setPath(self, path):
""" Set PID lock file path.
"""
self.path = path
def create(self):
""" Create PID lock.
"""
try:
fileHandler = open(self.path, mode='w')
pid = os.getpid()
fileHandler.write(`pid` + '\n')
fileHandler.close()
logSys.debug("Created PID lock (" + `pid` + ") in " + self.path)
return True
except:
logSys.error("Unable to create PID lock " + self.path)
return False
def remove(self):
""" Remove PID lock.
"""
try:
os.remove(self.path)
logSys.debug("Removed PID lock " + self.path)
except OSError:
logSys.error("Unable to remove PID lock " + self.path)
except AttributeError:
# AttributeError if self.path wasn't specified yet
logSys.debug("PID lock not removed because not defined yet")
def exists(self):
""" Returns the current PID if Fail2Ban is running or False
if no instance found.
"""
try:
fileHandler = open(self.path)
pid = fileHandler.readline()
fileHandler.close()
return pid
except IOError:
return False
# storage for the instance reference
__instance = None
def __init__(self):
""" Create singleton instance """
# Check whether we already have an instance
if PIDLock.__instance is None:
# Create and remember instance
PIDLock.__instance = PIDLock.__impl()
# Store instance reference as the only member in the handle
self.__dict__['_PIDLock__instance'] = PIDLock.__instance
def __getattr__(self, attr):
""" Delegate access to implementation """
return getattr(self.__instance, attr)
def __setattr__(self, attr, value):
""" Delegate access to implementation """
return setattr(self.__instance, attr, value)

View File

@ -1,108 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban 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.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import os, logging, signal
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
class ExternalError(UserWarning):
""" Exception to warn about failed fwcheck or fwban command
"""
pass
def createDaemon():
""" Detach a process from the controlling terminal and run it in the
background as a daemon.
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731
"""
try:
# Fork a child process so the parent can exit. This will return control
# to the command line or shell. This is required so that the new process
# is guaranteed not to be a process group leader. We have this guarantee
# because the process GID of the parent is inherited by the child, but
# the child gets a new PID, making it impossible for its PID to equal its
# PGID.
pid = os.fork()
except OSError, e:
return((e.errno, e.strerror)) # ERROR (return a tuple)
if pid == 0: # The first child.
# Next we call os.setsid() to become the session leader of this new
# session. The process also becomes the process group leader of the
# new process group. Since a controlling terminal is associated with a
# session, and this new session has not yet acquired a controlling
# terminal our process now has no controlling terminal. This shouldn't
# fail, since we're guaranteed that the child is not a process group
# leader.
os.setsid()
# When the first child terminates, all processes in the second child
# are sent a SIGHUP, so it's ignored.
signal.signal(signal.SIGHUP, signal.SIG_IGN)
try:
# Fork a second child to prevent zombies. Since the first child is
# a session leader without a controlling terminal, it's possible for
# it to acquire one by opening a terminal in the future. This second
# fork guarantees that the child is no longer a session leader, thus
# preventing the daemon from ever acquiring a controlling terminal.
pid = os.fork() # Fork a second child.
except OSError, e:
return((e.errno, e.strerror)) # ERROR (return a tuple)
if (pid == 0): # The second child.
# Ensure that the daemon doesn't keep any directory in use. Failure
# to do this could make a filesystem unmountable.
os.chdir("/")
else:
os._exit(0) # Exit parent (the first child) of the second child.
else:
os._exit(0) # Exit parent of the first child.
# Close all open files. Try the system configuration variable, SC_OPEN_MAX,
# for the maximum number of open files to close. If it doesn't exist, use
# the default value (configurable).
try:
maxfd = os.sysconf("SC_OPEN_MAX")
except (AttributeError, ValueError):
maxfd = 256 # default maximum
for fd in range(0, maxfd):
try:
os.close(fd)
except OSError: # ERROR (ignore)
pass
# Redirect the standard file descriptors to /dev/null.
os.open("/dev/null", os.O_RDONLY) # standard input (0)
os.open("/dev/null", os.O_RDWR) # standard output (1)
os.open("/dev/null", os.O_RDWR) # standard error (2)
return True

View File

@ -1,40 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban 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.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
def replaceTag(query, aInfo):
""" Replace tags in query
"""
string = query
for tag in aInfo:
string = string.replace('<'+tag+'>', `aInfo[tag]`)
# New line
string = string.replace('<br>', '\n')
return string