''' policy.py - SMTP policy service for SAGATOR. (c) 2005-2022 Jan ONDREJ (SAL) This program 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. ''' from aglib import * import stats __all__ = ['smtpd_policy', 'recipient_policy'] class smtpd_policy(ServiceTCPServer): ''' SMTP policy service. This service can be used as smtpd policy service for postfix. Usage: smtpd_polixy(scanners, dbc, host, port, max_children=200) Where: scanners is an array of policy scanners (see README.scanners for more info) dbc is an database connection host is a an ip address to bind port is a port to bind max_children is a number defining maximal number of childrens for this service Example: smtpd_policy(SCANNERS, db.sqlite(), '127.0.0.1', 29) Postfix configuration example: /etc/postfix/main.cf: smtpd_recipient_restrictions= ... check_policy_service inet:127.0.0.1:29 ... New in version 0.8.0. ''' name = 'smtpd_policy()' def __init__(self, scanners, dbc, host, port, max_children=200): self.max_children = max_children globals.DBC = dbc ServiceTCPServer.__init__( self, scanners, host, port, smtpd_policy_handler ) class smtpd_policy_handler(StreamRequestHandler): time1 = None def handle(self): while True: self.stats = stats.statistics() # generate ID self.time2 = time.strftime("%Y%m%d-%H%M%S", time.localtime(time.time())) if self.time2!=self.time1: self.time1, self.timec = self.time2, 1 globals.gen_id(self.time2, self.timec) mail.policy_request = {} mail.data = b'' try: while True: line = self.rfile.readline() if not line: break debug.echo(8, line) mail.data += line if line==b'\n' or line==b'\r\n': break try: key, value = line.rstrip(b'\r\n').split(b'=', 1) mail.policy_request[key] = value except ValueError: debug.echo(3, "%s: wrong line: '%s'" \ % (self.server.name, line.rstrip())) if not mail.data: break debug.echo(4, "%s: %s %s" \ % (self.server.name, time.strftime("%c", time.localtime()), tostr_dict(mail.policy_request))) self.wfile.write(b'action=%s\n\n' % checkpolicy(self.server.SCANNERS, False)) self.stats.policy_update() except socket.error as err: (ec, es) = err.args # ignore exception if connection is already down if (ec==107 # Transport endpoint is not connected or ec==104 # Connection reset by peer or ec==32): # Broken pipe debug.echo(3, "%s: %s" % (self.server.name, es)) return else: debug.traceback(3, "%s: " % self.server.name) self.wfile.write(b'action=warn %s\n\n' % tobytes(es)) except Exception as err: debug.traceback(3, "%s: Unknown exception" % self.server.name) self.wfile.write(b'action=warn %s\n\n' % tobytes(err)) class recipient_policy(service): ''' Virtual recipient policy. This policy check is invoked after an "RCPT TO:" smtp command is received. You can use an policy scanner combination as scanner. It is useable for postfix's before-queue policy filter or an policy filter for milter. This service must be defined before service, which want to use it. Usage: recipient_policy(scanners, dbc) Where: scanners is an array of policy scanners (see README.scanners for more info) dbc is an database connection Example: recipient_policy(POLICY_SCANNERS, db.sqlite()) New in version 0.8.0. ''' def __init__(self, scanners, dbc): globals.DBC = dbc globals.recipient_policy = scanners def start(self): return []