Server : LiteSpeed System : Linux in-mum-web1949.main-hosting.eu 5.14.0-503.40.1.el9_5.x86_64 #1 SMP PREEMPT_DYNAMIC Mon May 5 06:06:04 EDT 2025 x86_64 User : u595547767 ( 595547767) PHP Version : 7.4.33 Disable Function : NONE Directory : /opt/alt/python27/lib/python2.7/site-packages/postomaat/ |
# -*- coding: UTF-8 -*-
# Copyright 2012-2018 Oli Schacher
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from postomaat.shared import DUNNO, Suspect, DEFER, REJECT, DISCARD
from postomaat.addrcheck import AddrException
from functools import reduce
import logging
import sys
import traceback
import time
import socket
class TrackTimings(object):
def __init__(self, enable=False):
"""
Constructor, setting function handles to real functions or dummies
Args:
enable (bool): enable/disable time tracker
"""
self.timings_logger = logging.getLogger("%s.Timings" % __package__)
# timings
self._enabletimetracker= False
self.enabletimetracker = enable
self.timetracker = time.time()
self.timings = []
@property
def enabletimetracker(self):
return self._enabletimetracker
@enabletimetracker.setter
def enabletimetracker(self, enabled):
if not enabled:
self._enabletimetracker = False
self.tracktime = self._dummy_noreturn
self.gettime = self._dummy_returnlist
self.sumtime = self._dummy_returnzero
self.report_timings = self._dummy_noreturn
self.report_plugintime = self._dummy_noreturn
else:
self._enabletimetracker = True
self.tracktime = self._tracktime
self.gettime = self._gettime
self.sumtime = self._sumtime
self.report_timings = self._report_timings
self.report_plugintime = self._report_plugintime
self.timetracker = time.time()
self.timings = []
def _tracktime(self, tagname, plugin=False):
"""
Given a tag name, track and store time since last call of same function
Args:
tagname (str): String with tag name
Keyword Args:
plugin (bool): tag belongs to plugin timing
"""
newtime = time.time()
difftime = newtime - self.timetracker
self.timetracker = newtime
self.timings.append((tagname, difftime, plugin))
def _dummy_noreturn(self, *args, **kwargs):
return
def _dummy_returnlist(self, *args, **kwargs):
return []
def _dummy_returnzero(self, *args, **kwargs):
return 0.0
def _gettime(self, plugins=None):
"""
Get a list of timings stored (all/only plugins/exclude plugins)
Keyword Args:
plugins (bool or None): For 'None' return all timings, True: only plugin timings, False: all but plugin timings
Returns:
list of tuples (name, time)
"""
if plugins is None:
timing_list = [a[:2] for a in self.timings]
else:
timing_list = [a[:2] for a in self.timings if a[2] == plugins]
return timing_list
def _sumtime(self, plugins=None):
"""
Get total time spent (all/only plugins/exclude plugins)
Keyword Args:
plugins (bool or None): For 'None' return total time, True: time spent in plugins, False: time spent outside plugins
Returns:
float: total time spent
"""
puretimings = [a[1] for a in self.gettime(plugins)]
if len(puretimings) == 0:
return 0.0
else:
return reduce(lambda x, y: (x + y), puretimings)
def _report_timings(self, suspectid):
"""
Report all the timings collected
Args:
suspectid (id): the suspect id
"""
self.timings_logger.info('id: %s, total: %.3f' % (suspectid, self.sumtime()))
self.timings_logger.info('id: %s, overhead: %.3f' % (suspectid, self.sumtime(plugins=False)))
all_plugintimes = self.gettime(plugins=True)
for plugintime in all_plugintimes:
self.timings_logger.info('id: %s, (PLG) %s: %.3f' % (suspectid, plugintime[0], plugintime[1]))
all_overheadtimes = self.gettime(plugins=False)
for overheadtime in all_overheadtimes:
self.timings_logger.debug('id: %s, %s: %.3f' % (suspectid, overheadtime[0], overheadtime[1]))
def _report_plugintime(self,suspectid,pluginname, end=True):
"""
Report timings inside plugin
Args:
suspectid (str): the id of the suspect
pluginname (str): plugin name
Keyword Args:
end (bool): if true another timing called 'end' will be created before the report
"""
if end:
self.tracktime('end')
all_timings = self.gettime()
for itime in all_timings:
self.timings_logger.debug('id: %s, [%s]-%s: %.3f' % (suspectid, pluginname, itime[0], itime[1]))
class SessionHandler(TrackTimings):
"""thread handling one message"""
def __init__(self, incomingsocket, config, plugins):
TrackTimings.__init__(self)
self.incomingsocket = incomingsocket
self.logger = logging.getLogger("%s.SessionHandler" % __package__)
self.action = DUNNO
self.arg = ""
self.config = config
self.plugins = plugins
self.workerthread = None
try:
self.enabletimetracker = config.getboolean('main', 'scantimelogger')
except Exception as e:
self.enabletimetracker = False
self.logger.debug("enabletimetracker = %s" % self.enabletimetracker)
def defer(self,message):
"""
Create a session and immediately close it sending a DEFER message to the server
Args:
message (str): additional information sent to the server
"""
try:
sess = PolicydSession(self.incomingsocket, self. config)
sess.endsession(DEFER, message)
except Exception as e:
pass
def set_threadinfo(self, status):
if self.workerthread is not None:
self.workerthread.threadinfo = status
def handlesession(self, workerthread=None):
self.workerthread = workerthread
sess = None
suspectid = 'undefinedundefinedundefinedundef'
try:
self.set_threadinfo('receiving message')
self.tracktime("SessionHandler-Setup")
sess = PolicydSession(self.incomingsocket, self. config)
success = sess. getrequest()
if not success:
self.logger.error('incoming request did not finish')
sess.closeconn()
self.tracktime("Message-Receive")
values = sess.values
suspect = Suspect(values)
suspectid = suspect.id
# store incoming port to tag, could be used to disable plugins
# based on port
try:
port = sess.socket . getsockname()[1]
if port is not None:
suspect.tags['incomingport'] = port
except Exception as e:
self.logger.warning('Could not get incoming port: %s' % str(e))
self.set_threadinfo("Handling message %s" % suspect)
starttime = time.time()
self.tracktime("Suspect-Init")
self.run_plugins(suspect, self.plugins)
# how long did it all take?
difftime = time.time() - starttime
suspect.tags['postomaat.scantime'] = "%.4f" % difftime
# checks done.. print out suspect status
self.logger.debug(suspect)
self.set_threadinfo("Finishing message %s" % suspect)
except KeyboardInterrupt:
sys.exit(0)
except AddrException as e:
# Error in envelope send/receive address
self.logger.warning('Address exception: %s' % str(e))
try:
address_compliance_fail_action = self.config.get('main','address_compliance_fail_action').lower()
except Exception:
address_compliance_fail_action = "defer"
try:
message = self.config.get('main','address_compliance_fail_message')
except Exception:
message = "invalid sender or recipient address"
if address_compliance_fail_action == "defer":
self.action = DEFER
elif address_compliance_fail_action == "reject":
self.action = REJECT
elif address_compliance_fail_action == "discard":
self.action = DISCARD
else:
self.action = DEFER
self.arg = message
except Exception as e:
self.logger.exception(e)
finally:
if sess is not None:
sess.endsession(self.action, self.arg)
self.logger.debug('Session finished')
# ---------------#
# report timings #
# ---------------#
self.tracktime("Ending")
self.report_timings(suspectid)
def run_plugins(self, suspect, pluglist):
"""Run scannerplugins on suspect"""
for plugin in pluglist:
try:
self.logger.debug('Running plugin %s' % plugin)
self.set_threadinfo(
"%s : Running Plugin %s" % (suspect, plugin))
ans = plugin.examine(suspect)
arg = None
if isinstance(ans, tuple):
result, arg = ans
else:
result = ans
if result is None:
result = DUNNO
else:
result = result.strip().lower()
self.action = result
self.arg = arg
suspect.tags['decisions'].append((str(plugin), result))
self.logger.debug('Plugin decission: %s (arg=%s)' % (result, arg))
if result != DUNNO:
self.logger.debug(
'Plugin makes a decision other than DUNNO - not running any other plugins')
break
except Exception:
exc = traceback. format_exc()
self.logger.error('Plugin %s failed: %s' % (str(plugin), exc))
finally:
self.tracktime(str(plugin),plugin=True)
class PolicydSession(object):
def __init__(self, socket, config):
self.config = config
self.socket = socket
self.logger = logging.getLogger("%s.policysession" % __package__)
self.file = self.socket.makefile('r')
self.values = {}
def endsession(self, action, arg):
ret = action
if arg is not None and arg.strip() != "":
ret = "%s %s" % (action, arg.strip())
self.socket.send(('action=%s\n\n' % ret).encode())
self.closeconn()
def closeconn(self):
try:
self.socket.shutdown(socket.SHUT_RDWR)
except (OSError, socket.error):
pass
finally:
self.socket.close()
def getrequest(self):
"""return true if mail got in, false on error Session will be kept open"""
while True:
line = self.file.readline()
line = line.strip()
if line == '':
return True
try:
key, val = line.split('=', 1)
self.values[key] = val
except Exception:
self.logger.error('Invalid Protocol line: %s' % line)
break
return False