Source code for snare.net.iptables
# coding: utf-8
"""
iptables manipulation functions.
"""
import subprocess
import logging
import collections.abc
logger = logging.getLogger(__name__)
[docs]class BadRuleError(Exception):
"""
Raised when an iptables command failes with the "Bad rule" message
This will occur when trying to check or delete a rule which does not exist
"""
def __init__(self, rule):
self.rule = rule
def __str__(self):
return "rule specified by '{:s}' does not exist in chain {:s}".format(" ".join(self.rule.args()), self.rule.chain)
class IptablesRule:
exe = 'iptables'
badrulemsg = b'Bad rule'
def __init__(self, chain, rulenum=None, **kwargs):
self.chain = chain
self.rulenum = rulenum
self.options = kwargs
self.applied = False
def args(self):
result = []
for name, value in self.options.items():
if len(name) == 1:
result.append('-{:s}'.format(name))
else:
result.append('--{:s}'.format(name.replace('_', '-')))
# None is special to indicate the option has no value
if value is not None:
# iptables has options with more than one value following
if any(isinstance(value, t) for t in (str, bytes)):
result.append(value)
elif isinstance(value, collections.abc.Iterable):
result.extend(value)
else:
result.append(str(value))
return result
def _run(self, cmd, **kwargs):
try:
return subprocess.run(
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
**kwargs
)
except subprocess.CalledProcessError as e:
if self.badrulemsg in e.stderr:
raise BadRuleError(self)
else:
raise
def exists(self):
try:
self._run([self.exe, '-C', self.chain] + self.args())
except BadRuleError:
return False
return True
def apply(self):
if self.rulenum is not None:
self._run([self.exe, '-I', self.chain, str(self.rulenum)] + self.args())
else:
self._run([self.exe, '-A', self.chain] + self.args())
self.applied = True
def delete(self):
self._run([self.exe, '-D', self.chain] + self.args())
self.applied = False
def __enter__(self):
if not self.applied:
self.apply()
return self
def __exit__(self, *args, **kwargs):
if self.applied:
self.delete()