This commit was manufactured by cvs2svn to create tag

'dynamic_linking_works'.
This commit is contained in:
cvs2git 2004-10-11 10:16:30 +00:00
parent c89c9c8a86
commit 87fc340f27
24 changed files with 0 additions and 1901 deletions

View File

@ -1,13 +0,0 @@
# The "checkoutlist" file is used to support additional version controlled
# administrative files in $CVSROOT/CVSROOT, such as template files.
#
# The first entry on a line is a filename which will be checked out from
# the corresponding RCS file in the $CVSROOT/CVSROOT directory.
# The remainder of the line is an error message to use if the file cannot
# be checked out.
#
# File format:
#
# [<whitespace>]<filename>[<whitespace><error message>]<end-of-line>
#
# comment lines begin with '#'

View File

@ -1,15 +0,0 @@
# The "commitinfo" file is used to control pre-commit checks.
# The filter on the right is invoked with the repository and a list
# of files to check. A non-zero exit of the filter program will
# cause the commit to be aborted.
#
# The first entry on a line is a regular expression which is tested
# against the directory that the change is being committed to, relative
# to the $CVSROOT. For the first match that is found, then the remainder
# of the line is the name of the filter to run.
#
# If the repository name does not match any of the regular expressions in this
# file, the "DEFAULT" line is used, if it is specified.
#
# If the name "ALL" appears as a regular expression it is always used
# in addition to the first matching regex or "DEFAULT".

View File

@ -1,21 +0,0 @@
# Set this to "no" if pserver shouldn't check system users/passwords
#SystemAuth=no
# Put CVS lock files in this directory rather than directly in the repository.
#LockDir=/var/lock/cvs
# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top
# level of the new working directory when using the `cvs checkout'
# command.
#TopLevelAdmin=no
# Set `LogHistory' to `all' or `TOEFWUPCGMAR' to log all transactions to the
# history file, or a subset as needed (ie `TMAR' logs all write operations)
#LogHistory=TOEFWUPCGMAR
# Set `RereadLogAfterVerify' to `always' (the default) to allow the verifymsg
# script to change the log message. Set it to `stat' to force CVS to verify# that the file has changed before reading it (this can take up to an extra
# second per directory being committed, so it is not recommended for large
# repositories. Set it to `never' (the previous CVS behavior) to prevent
# verifymsg scripts from changing the log message.
#RereadLogAfterVerify=always

View File

@ -1,19 +0,0 @@
# This file affects handling of files based on their names.
#
# The -m option specifies whether CVS attempts to merge files.
#
# The -k option specifies keyword expansion (e.g. -kb for binary).
#
# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
#
# wildcard [option value][option value]...
#
# where option is one of
# -f from cvs filter value: path to filter
# -t to cvs filter value: path to filter
# -m update methodology value: MERGE or COPY
# -k expansion mode value: b, o, kkv, &c
#
# and value is a single-quote delimited value.
# For example:
#*.gif -k 'b'

View File

@ -1,21 +0,0 @@
# The "editinfo" file is used to allow verification of logging
# information. It works best when a template (as specified in the
# rcsinfo file) is provided for the logging procedure. Given a
# template with locations for, a bug-id number, a list of people who
# reviewed the code before it can be checked in, and an external
# process to catalog the differences that were code reviewed, the
# following test can be applied to the code:
#
# Making sure that the entered bug-id number is correct.
# Validating that the code that was reviewed is indeed the code being
# checked in (using the bug-id number or a seperate review
# number to identify this particular code set.).
#
# If any of the above test failed, then the commit would be aborted.
#
# Actions such as mailing a copy of the report to each reviewer are
# better handled by an entry in the loginfo file.
#
# One thing that should be noted is the the ALL keyword is not
# supported. There can be only one entry that matches a given
# repository.

View File

@ -1,27 +0,0 @@
# The "loginfo" file controls where "cvs commit" log information
# is sent. The first entry on a line is a regular expression which must match
# the directory that the change is being made to, relative to the
# $CVSROOT. If a match is found, then the remainder of the line is a filter
# program that should expect log information on its standard input.
#
# If the repository name does not match any of the regular expressions in this
# file, the "DEFAULT" line is used, if it is specified.
#
# If the name ALL appears as a regular expression it is always used
# in addition to the first matching regex or DEFAULT.
#
# You may specify a format string as part of the
# filter. The string is composed of a `%' followed
# by a single format character, or followed by a set of format
# characters surrounded by `{' and `}' as separators. The format
# characters are:
#
# s = file name
# V = old version number (pre-checkin)
# v = new version number (post-checkin)
# t = tag or branch name
#
# For example:
#DEFAULT (echo ""; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog
# or
#DEFAULT (echo ""; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog

View File

@ -1,26 +0,0 @@
# Three different line formats are valid:
# key -a aliases...
# key [options] directory
# key [options] directory files...
#
# Where "options" are composed of:
# -i prog Run "prog" on "cvs commit" from top-level of module.
# -o prog Run "prog" on "cvs checkout" of module.
# -e prog Run "prog" on "cvs export" of module.
# -t prog Run "prog" on "cvs rtag" of module.
# -u prog Run "prog" on "cvs update" of module.
# -d dir Place module in directory "dir" instead of module name.
# -l Top-level directory only -- do not recurse.
#
# NOTE: If you change any of the "Run" options above, you'll have to
# release and re-checkout any working directories of these modules.
#
# And "directory" is a path to a directory relative to $CVSROOT.
#
# The "-a" option specifies an alias. An alias is interpreted as if
# everything on the right of the "-a" had been typed on the command line.
#
# You can encode a module within a module by using the special '&'
# character to interpose another module into the current module. This
# can be useful for creating a module that consists of many directories
# spread out over the entire source repository.

View File

@ -1,12 +0,0 @@
# The "notify" file controls where notifications from watches set by
# "cvs watch add" or "cvs edit" are sent. The first entry on a line is
# a regular expression which is tested against the directory that the
# change is being made to, relative to the $CVSROOT. If it matches,
# then the remainder of the line is a filter program that should contain
# one occurrence of %s for the user to notify, and information on its
# standard input.
#
# "ALL" or "DEFAULT" can be used in place of the regular expression.
#
# For example:
#ALL mail -s "CVS notification" %s

View File

@ -1,13 +0,0 @@
# The "rcsinfo" file is used to control templates with which the editor
# is invoked on commit and import.
#
# The first entry on a line is a regular expression which is tested
# against the directory that the change is being made to, relative to the
# $CVSROOT. For the first match that is found, then the remainder of the
# line is the name of the file that contains the template.
#
# If the repository name does not match any of the regular expressions in this
# file, the "DEFAULT" line is used, if it is specified.
#
# If the name "ALL" appears as a regular expression it is always used
# in addition to the first matching regex or "DEFAULT".

View File

@ -1,20 +0,0 @@
# The "taginfo" file is used to control pre-tag checks.
# The filter on the right is invoked with the following arguments:
#
# $1 -- tagname
# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d
# $3 -- repository
# $4-> file revision [file revision ...]
#
# A non-zero exit of the filter program will cause the tag to be aborted.
#
# The first entry on a line is a regular expression which is tested
# against the directory that the change is being committed to, relative
# to the $CVSROOT. For the first match that is found, then the remainder
# of the line is the name of the filter to run.
#
# If the repository name does not match any of the regular expressions in this
# file, the "DEFAULT" line is used, if it is specified.
#
# If the name "ALL" appears as a regular expression it is always used
# in addition to the first matching regex or "DEFAULT".

View File

@ -1,21 +0,0 @@
# The "verifymsg" file is used to allow verification of logging
# information. It works best when a template (as specified in the
# rcsinfo file) is provided for the logging procedure. Given a
# template with locations for, a bug-id number, a list of people who
# reviewed the code before it can be checked in, and an external
# process to catalog the differences that were code reviewed, the
# following test can be applied to the code:
#
# Making sure that the entered bug-id number is correct.
# Validating that the code that was reviewed is indeed the code being
# checked in (using the bug-id number or a seperate review
# number to identify this particular code set.).
#
# If any of the above test failed, then the commit would be aborted.
#
# Actions such as mailing a copy of the report to each reviewer are
# better handled by an entry in the loginfo file.
#
# One thing that should be noted is the the ALL keyword is not
# supported. There can be only one entry that matches a given
# repository.

View File

@ -1,37 +0,0 @@
import threading
import time
from Logging import *
class Cache(object):
def __init__(self, expiration):
self.lock = threading.Lock()
self.expiration = expiration
self.cache = {}
def put(self, key, value):
self.lock.acquire()
self.cache[key] = (time.time(), value)
debug("cache.put(%s, %s)" % (str(key), str(value)))
self.lock.release()
def get(self, key):
try:
self.lock.acquire()
debug("cache.get(%s)" % str(key))
try:
timestamp, value = self.cache[key]
debug("cache.get found: %s" % value)
if (timestamp + self.expiration) < time.time():
debug("cache.get: expired")
del self.cache[key]
raise KeyError
return value
except KeyError:
debug("cache.get: found nothing")
return None
finally:
self.lock.release()

View File

@ -1,27 +0,0 @@
from Logging import *
from SendmailSocketMapHandler import smmapBaseHandlerWorker
from SendmailSocketMapHandler import smmapBaseHandlerContainer
from SendmailSocketMapHandler import MyPermanentVerifierException
from SendmailSocketMapHandler import MyTemporaryVerifierException
from VerifierHandler import MySMTP
class MyLMTP(MySMTP):
def lhlo(self, param):
return self.docmd("lhlo " + param)
class CyrusCheckerWorker(smmapBaseHandlerWorker):
OK = "OK"
NOK = "NOK"
TEMPNOK = "TEMPNOK"
def execute(self, data):
debug("data " + data)
host, address = data.split('|')
debug("host: (%s), address: (%s)" % (host, address))
return "<OK>"

View File

@ -1,14 +0,0 @@
import syslog
config = None
def log(data):
syslog.syslog(syslog.LOG_INFO, data)
def debug(data):
syslog.syslog(syslog.LOG_DEBUG, data)
def openlog(c):
config = c
syslog.openlog(config.get('Logging', 'ApplID'), syslog.LOG_PID, syslog.LOG_MAIL)

View File

@ -1,8 +0,0 @@
Cache.py
Logging.py
SendmailSocketMapHandler.py
VerifierHandler.py
setup.py
smmapd
smmapd.ini
verifysender.m4

View File

@ -1,190 +0,0 @@
import SocketServer
import time
from Logging import *
class NetStringError(ValueError): pass
def NetStringDecode(s):
try:
length, data = s.split(':')
except ValueError:
raise NetStringError, "Separator not found"
try:
length = int(length)
except ValueError:
raise NetStringError, "Can not read length"
if len(data) != length+1:
raise NetStringError, "Data has unexpected length"
if data[-1] != ',':
raise NetStringError, "End-delimiter not found"
return data[:-1]
def NetStringEncode(s):
return str(len(s)) + ":" + s + ","
class MyPermanentVerifierException(ValueError): pass
class MyTemporaryVerifierException(ValueError): pass
class MyBaseRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
debug("Connected from " + str(self.client_address))
self.localSetup()
while 1:
receivedData = self.request.recv(8192)
if (receivedData == None) or (len(receivedData) == 0): break
debug("Data: (%s)" % receivedData)
self.request.sendall(self.process(receivedData))
self.request.close();
self.localFinish()
debug("Disconnected")
def process(self, data):
debug("MyBaseRequestHandler.process")
return data
def localSetup(self): pass
def localfinish(self): pass
class SendmailAdaptor:
PERM = "PERM "
OK = "OK "
NOTFOUND = "NOTFOUND "
TEMP = "TEMP "
def preProcess(self, data):
try:
data = NetStringDecode(data)
klass, data = data.split(' ')
return klass, data
except NetStringError, arg:
raise MyPermanentVerifierException, arg
except ValueError:
raise MyPermanentVerifierException, "<class> <data> expected, only one found"
def postProcess(self, data):
return NetStringEncode(data)
def process(self, data):
startTime = time.time()
try:
klass, data2 = self.preProcess(data)
arg = self.execute(klass, data2)
code = SendmailAdaptor.OK
except MyPermanentVerifierException, arg:
code, arg = SendmailAdaptor.PERM, str(arg)
except MyTemporaryVerifierException, arg:
code, arg = SendmailAdaptor.TEMP, str(arg)
endTime = time.time()
log("Class: %s, Data: %s, Code: %s, Arg: %s, Delay: %f" % (klass, data2, code, arg, endTime-startTime))
return self.postProcess(code + arg)
def execute(self, data):
return data
class NullAdaptor(SendmailAdaptor):
def preProcess(self, data):
return re.compile(r'^(.*?)[\r\n]{1,2}$').match(data).group(1)
def postProcess(self, data):
return data + "\n"
class SendmailDispatcher(SendmailAdaptor, MyBaseRequestHandler):
pluginContainerObjects = {}
def registerAll(config):
for section in config.get('Daemon', 'Plugins').split(','):
SendmailDispatcher.register(section, config)
registerAll = staticmethod(registerAll)
def register(section, config):
cfg = Config(section, config)
className = cfg.get('ContainerClass')
moduleName = cfg.get('ContainerModule')
if className == None:
className = 'smmapBaseHandlerContainer'
else:
if moduleName == None:
moduleName == className
m = __import__(moduleName)
log("Registering %s, %s" % (section, className))
klass = eval("m.%s" % className)
containerObject = klass(cfg)
containerObject.setup()
SendmailDispatcher.pluginContainerObjects[section] = containerObject
register = staticmethod(register)
def localSetup(self):
self.pluginWorkerObjects = {}
def localFinish(self):
for o in self.pluginWorkerObjects.values():
o.finish()
def execute(self, klass, data):
if not self.pluginContainerObjects.has_key(klass):
raise MyPermanentVerifierException, "Class %s not implemented" % klass
elif not self.pluginWorkerObjects.has_key(klass):
debug("Instantiate worker %s" % klass)
self.pluginWorkerObjects[klass] = self.pluginContainerObjects[klass].getWorker()
return self.pluginWorkerObjects[klass].execute(data)
class Config(object):
def __init__(self, section, config):
self.section = section
self.config = config
def getSection(self):
return self.section
def get(self, item):
return self.config.get(self.section, item)
class smmapBaseHandlerWorker(object):
def __init__(self, container):
self.container = container
def setup(self): pass
def finish(self): pass
def execute(self, data):
raise NotImplementedError
class smmapBaseHandlerContainer(object):
def __init__(self, cfg):
self.config = cfg
workerClassName = cfg.get('WorkerClass')
workerModuleName = cfg.get('WorkerModule')
if workerModuleName == None:
workerModuleName = workerClassName
m = __import__(workerModuleName)
self.workerClass = eval("m.%s" % workerClassName)
def setup(self): pass
def finish(self): pass
def getWorker(self):
worker = self.workerClass(self)
worker.setup()
return worker

View File

@ -1,240 +0,0 @@
import threading
import socket
import Queue
import re
import time
#import timeoutsocket
import DNS
from Logging import *
from Cache import Cache
from SendmailSocketMapHandler import smmapBaseHandlerWorker
from SendmailSocketMapHandler import smmapBaseHandlerContainer
from SendmailSocketMapHandler import MyPermanentVerifierException
from SendmailSocketMapHandler import MyTemporaryVerifierException
class VerifierHandlerContainer(smmapBaseHandlerContainer):
def setup(self):
DNS.ParseResolvConf()
if self.config.get('EnableCaching').lower() in ('true', 'yes', '1'):
debug("enabling cache")
self.cache = Cache(int(self.config.get('CacheExpiration')))
else:
debug("disabling cache")
self.cache = None
class VerifierHandlerWorker(smmapBaseHandlerWorker):
OK = "OK"
NOK = "NOK"
TEMPNOK = "TEMPNOK"
def setup(self):
self.zombies = []
class checker(threading.Thread):
def __init__(self, ready, config, host, address):
threading.Thread.__init__(self)
self.ready = ready
self.config = config
self.host = host
self.address = address
def checkAddressAvailability(self):
try:
debug("Trying " + self.host)
s = MySMTP(self.host, float(self.config.get('SMTPTimeOut')))
s.helo(self.config.get('SMTPHeloParam'))
s.mail(self.config.get('SMTPCheckSender'))
s.rcpt(self.address.getAddress())
s.quit()
result = VerifierHandlerWorker.OK
except MySMTPTemporaryException:
result = VerifierHandlerWorker.TEMPNOK
except MySMTPPermanentException:
result = VerifierHandlerWorker.NOK
except socket.timeout:
result = VerifierHandlerWorker.TEMPNOK
except socket.error:
result = VerifierHandlerWorker.TEMPNOK
return result
def run(self):
self.result = self.checkAddressAvailability()
self.ready.put(self.getName())
debug("NOTIFIED Host %s, Result %s" % (self.host, self.result))
def getResult(self):
return self.result
def getHost(self):
return self.host
def getAddress(self):
return self.address
def checkAvailability(self, mxes, address):
ready = Queue.Queue()
checkerThreads = {}
for m in mxes:
checkerThread = VerifierHandlerWorker.checker(ready, self.container.config, m, address)
checkerThread.start()
checkerThreads[checkerThread.getName()] = checkerThread
result = VerifierHandlerWorker.TEMPNOK
while 1:
debug("%i threads left" % len(checkerThreads))
if len(checkerThreads) == 0:
debug("no threads left ...")
break
if result != VerifierHandlerWorker.TEMPNOK:
debug("got a permanent result ...")
break
debug("Waiting for results ...")
name = ready.get()
checkerThread = checkerThreads[name]
checkerThread.join()
tempResult = checkerThread.getResult()
debug("success, result is " + str(tempResult))
if [VerifierHandlerWorker.OK, VerifierHandlerWorker.NOK].count(tempResult) != 0:
result = tempResult
del checkerThreads[name]
self.zombies.extend(checkerThreads.values())
return result
def finish(self):
while 1:
debug("finish: %i zombies left" % len(self.zombies))
for z in self.zombies:
if not z.isAlive():
debug("finish: thread %s for %s, %s terminated" % (z.getName(), z.getHost(), z.getAddress().getAddress()))
self.zombies.remove(z)
for z in self.zombies:
debug("finish: left over %s for %s, %s" % (z.getName(), z.getHost(), z.getAddress().getAddress()))
if len(self.zombies) == 0:
debug("finish: no zombie left ...")
break
debug("finish: WAITING")
time.sleep(5)
debug("finish: CONTINUE")
debug("finish: all threads terminated")
def execute(self, address):
debug("address " + address)
address = EMailAddress(address)
bestmxes = address.getBestMX()
if not bestmxes:
return "<NOK> <no bestmx found>"
if self.container.cache == None:
debug("no caching")
result = self.checkAvailability(bestmxes, address)
else:
result = self.container.cache.get(address.getAddress())
if result == None:
debug("not found in cache")
result = self.checkAvailability(bestmxes, address)
if result != VerifierHandlerWorker.TEMPNOK:
self.container.cache.put(address.getAddress(), result)
else:
debug("found in cache")
if result == VerifierHandlerWorker.OK:
return "<OK>"
elif result == VerifierHandlerWorker.NOK:
return "<NOK> <home server sent a permanent negative answer>"
else:
raise MyTemporaryVerifierException, "no mx reachable"
class MySMTPPermanentException(ValueError): pass
class MySMTPTemporaryException(ValueError): pass
class MySMTP(object):
def __init__(self, host, timeout, port=25):
self.host = host
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(timeout)
self.socket.connect((host, port))
self.socket.recv(8192)
self.resPattern = re.compile(r'[\w\W]*?^(\d{3,3}) (.*?)[\r\n]{1,2}$', re.MULTILINE)
def checkResult(self, r):
code, text = r
code = code / 100
if code == 2:
return;
elif code == 4:
raise MySMTPTemporaryException, text
elif code == 5:
raise MySMTPPermanentException, text
else:
raise MySMTPPermanentException, "unknown code: " + str(code) + ", text: " + str(text)
def docmd(self, cmd):
debug("docmd: %s, cmd: %s " % (self.host, cmd))
self.socket.sendall(cmd + "\r\n")
res = self.socket.recv(8192)
debug("docmd: result: (%s)" % res)
m = self.resPattern.match(res)
return self.checkResult((int(m.group(1)), m.group(2)))
def helo(self, param):
return self.docmd("helo " + param)
def mail(self, sender):
if sender[0] != '<' and sender[-1] != '>': sender = '<' + sender + '>'
return self.docmd("mail from:" + sender)
def rcpt(self, recipient):
return self.docmd("rcpt to:<%s>" % recipient)
def quit(self):
self.docmd("quit")
self.socket.close()
class EMailAddress(object):
def __init__(self, address):
self.address = address
if self.address[0] == '<' and self.address[-1] == '>': self.address = self.address[1:-1]
try:
self.userpart, self.domain = self.address.split('@')
except ValueError:
raise MyPermanentVerifierException, "excepted email address, found not at-sign"
def getUserPart(self):
return self.userpart
def getDomain(self):
return self.domain
def getAddress(self):
return self.address
def getBestMX(self):
if self.domain[0] == '[' and self.domain[-1] == ']':
bestmx2 = [self.domain[1:-1]]
else:
bestmx = DNS.mxlookup(self.domain)
pref = None
bestmx2 = []
for mx in bestmx:
if pref == None: pref = mx[0]
if pref == mx[0]:
bestmx2.append(mx[1])
else:
break
debug("bestmx " + str(bestmx2))
return bestmx2

View File

@ -1,327 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<title>Sender Address Verifier for Sendmail</title>
<meta name="generator" content="emacs-wiki.el">
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1">
<link rev="made" href="mailto:woho@hottis.de">
<link rel="stylesheet" type="text/css" href="/web/default.css" />
</head>
<body>
<h1>Sender Address Verifier for Sendmail</h1>
<!-- Page published by Emacs Wiki begins here -->
<p>
Author: Wolfgang Hottgenroth <<a href="mailto:woho@hottis.de">woho@hottis.de</a>>, 2004-05-17
</p>
<p>
This is the prototype of a sender address verifier for sendmail-8.13.
</p>
<p>
It consists of a m4 file containing a FEATURE to be included in your
<code>sendmail.mc</code> and a verifier daemon in a bit of python code.
</p>
<p>
By including the FEATURE in your sendmail.mc file and running the
verifier daemon, sendmail file verify either
</p>
<ul>
<li>all sender addresses (with certain exceptions) or
</li>
<li>only certain sender addresses
</li>
</ul>
<p>
This will be done by connecting to the best MX servers of the
particular domain, trying to send a mail to the particular address and
collect the replies.
</p>
<p>
Actually only the <code>HELO</code>, <code>MAIL</code> and <code>RCPT</code> commands are issued.
</p>
<p>
If a positive reply was found, the mail is considered as valid.
</p>
<p>
If a permanent negative reply was found, the mail is considered as
invalid.
</p>
<p>
If no MX entry was found, the mail is considered as invalid.
</p>
<p>
If a temporary negative reply was found, the mail is considered as
temporary invalid.
</p>
<p>
If there is more than one best MX server all of these servers are
connected in parallel and the first permanent reply (either positive
or negative) is returned.
</p>
<h3>Download</h3>
<p>
The complete sources: <a href="./download/">download</a>
</p>
<p>
Have a look into the sources: <a href="http://www.hottis.de/cgi-bin/cvsweb.cgi/sender_verifier/">sources</a>
</p>
<h3>Requirements</h3>
<h4>sendmail</h4>
<p>
sendmail-8.13 is required, since this thing uses the fresh introduced
socket map.
</p>
<p>
Find it <a href="http://www.sendmail.org">here</a> on the sendmail homepage.
</p>
<p>
Your need to build sendmail with support for the socket map. Include
</p>
<pre class="example">
APPENDDEF(`confMAPDEF',`-DSOCKETMAP')
</pre>
<p>
in your <code>site.config.m4</code>.
</p>
<h4>Python</h4>
<p>
Python 2.2 or 2.3 is required. If you have Python 2.3 you must delete
the <code>import timeoutsocket</code> line from <code>verifier.py</code>.
</p>
<p>
Additionally the python package <code>python-dns</code> is required. Find it
<a href="http://pydns.sourceforge.net/">http://pydns.sourceforge.net</a>.
</p>
<h3>Configuration of sendmail</h3>
<p>
Include the FEATURE in your <code>sendmail.mc</code> file. You need to give two
parameters:
</p>
<pre class="example">
FEATURE(`verifysender', `mode', `return')
</pre>
<p>
For <code>mode</code> you must give either <code>white</code> or <code>black</code>.
</p>
<dl>
<dt><code>white</code></dt>
<dd>
All sender addresses but those mentioned in the whitelist
file are verified. Complete addresses or just domains can be listed in
the file. The default location of the whitelist is
<code>/etc/mail/verify-white-list</code>. If you need a different location,
define it to <code>confVERIFIER_WHITELIST</code>.
</dd>
<dt><code>black</code></dt>
<dd>
only addresses or addresses within domains listed in the
blacklist file are verified. It is obviously only useful to mention
domains in the blacklist. The default location of the blacklist is
<code>/etc/mail/verify-black-list</code>. If you need a different location,
define it to <code>confVERIFIER_BLACKLIST</code>.
</dd>
</dl>
<p>
Both the blacklist and the whitelist file are maps, they must be
created with <code>makemap</code>. Therefore the entries need a LHS (the address
or domain) and a RHS. The actual content of the RHS has NO meaning at
all.
</p>
<p>
The FEATURE defines a socket map. The default target of the map is
<code>inet:8884@127.0.0.1</code>, according to the default setting in
<code>Config.py</code>. If you need something different, define it to
<code>confVERIFIER_MAP</code>, but don't forget to also adjust <code>Config.py</code>.
</p>
<h3>Configuration of the verification daemon</h3>
<p>
The configuration of the daemon is done in the file <code>Config.py</code>.
</p>
<p>
This is the default of this file:
</p>
<pre class="example">
[Daemon]
Address: 127.0.0.1
Port: 8884
PidFile: smmapd.pid
Plugins: Verifier,Verifier2
[Logging]
ApplId: smmapd
[Verifier]
ContainerModule: VerifierHandler
ContainerClass: VerifierHandlerContainer
WorkerModule: VerifierHandler
WorkerClass: VerifierHandlerWorker
EnableCaching: 1
CacheExpiration: 20
SMTPTimeOut: 20
SMTPHeloParam: local
SMTPCheckSender: &lt;&gt;
[Verifier2]
ContainerModule: VerifierHandler
ContainerClass: VerifierHandlerContainer
WorkerModule: VerifierHandler
WorkerClass: VerifierHandlerWorker
EnableCaching: 1
CacheExpiration: 20
SMTPTimeOut: 20
SMTPHeloParam: hottis.de
SMTPCheckSender: &lt;postmaster@hottis.de&gt;</pre>
<p>
<code>Port</code> and <code>Address</code> are specifying the socket the daemon should
listen to for communication with sendmail. These settings must be
reflected in the <code>confVERIFIER_MAP</code> if you change it.
</p>
<p>
<code>SMTPTimeOut</code> is the timeout for the communication with the MX servers
when verifying addresses.
</p>
<p>
<code>SMTPHeloParam</code> is the parameter the verifier will use with the <code>HELO</code>
command when verifying.
</p>
<p>
<code>SMTPCheckSender</code> is the sender address used during
verifications. You should not change it unless you know what you do to
avoid verification loops.
</p>
<p>
Since the verification is a time and resource consuming process,
results can be cached, which is enabled by default. Set
<code>EnableCaching</code> to 0 to disable it.
</p>
<p>
<code>CacheExpiration</code> is the time in seconds an entry in the cache is
considered as valid. It should be much higher.
</p>
<h3>Operation</h3>
<p>
Configure sendmail and the daemon according to your needs. Start the
daemon:
</p>
<pre class="example">
./verifier.py
</pre>
<h3>Changes</h3>
<ul>
<li>According to a comment in comp.mail.sendmail I've introduced a class
<code>verifier_fix_white</code> in the FEATURE file, currently containing only
the string <code>postmaster</code>. Addresses with userpart in this class will
never ever be verified to avoid infinite verifying loops.
</li>
</ul>
<h4>2004-05-17</h4>
<ul>
<li>support plugins
</li>
<li>separate container and worker object, thereby enable multiple
instances of the same plugins
</li>
</ul>
<!-- Page published by Emacs Wiki ends here -->
<div class="navfoot">
<hr>
<table width="100%" border="0" summary="Footer navigation">
<tr>
<td width="33%" align="left">
<span class="footdate">UPDATED: 2004-05-17</span>
</td>
<td width="34%" align="center">
<span class="foothome">
</span>
</td>
<td width="33%" align="right">
</td>
</tr>
</table>
</div>
</body>
</html>

View File

@ -1,195 +0,0 @@
#title Sender Address Verifier for Sendmail
#style /web/default.css
Author: Wolfgang Hottgenroth <woho@hottis.de>, 2004-05-17
This is the prototype of a sender address verifier for sendmail-8.13.
It consists of a m4 file containing a FEATURE to be included in your
=sendmail.mc= and a verifier daemon in a bit of python code.
By including the FEATURE in your sendmail.mc file and running the
verifier daemon, sendmail file verify either
- all sender addresses (with certain exceptions) or
- only certain sender addresses
This will be done by connecting to the best MX servers of the
particular domain, trying to send a mail to the particular address and
collect the replies.
Actually only the =HELO=, =MAIL= and =RCPT= commands are issued.
If a positive reply was found, the mail is considered as valid.
If a permanent negative reply was found, the mail is considered as
invalid.
If no MX entry was found, the mail is considered as invalid.
If a temporary negative reply was found, the mail is considered as
temporary invalid.
If there is more than one best MX server all of these servers are
connected in parallel and the first permanent reply (either positive
or negative) is returned.
** Download
The complete sources: [[./download/][download]]
Have a look into the sources: [[http://www.hottis.de/cgi-bin/cvsweb.cgi/sender_verifier/][sources]]
** Requirements
*** sendmail
sendmail-8.13 is required, since this thing uses the fresh introduced
socket map.
Find it [[http://www.sendmail.org][here]] on the sendmail homepage.
Your need to build sendmail with support for the socket map. Include
<example>
APPENDDEF(`confMAPDEF',`-DSOCKETMAP')
</example>
in your =site.config.m4=.
*** Python
Python 2.2 or 2.3 is required. If you have Python 2.3 you must delete
the =import timeoutsocket= line from =verifier.py=.
Additionally the python package =python-dns= is required. Find it
[[http://pydns.sourceforge.net/][http://pydns.sourceforge.net]].
** Configuration of sendmail
Include the FEATURE in your =sendmail.mc= file. You need to give two
parameters:
<example>
FEATURE(`verifysender', `mode', `return')
</example>
For =mode= you must give either =white= or =black=.
=white= :: All sender addresses but those mentioned in the whitelist
file are verified. Complete addresses or just domains can be listed in
the file. The default location of the whitelist is
=/etc/mail/verify-white-list=. If you need a different location,
define it to =confVERIFIER_WHITELIST=.
=black= :: only addresses or addresses within domains listed in the
blacklist file are verified. It is obviously only useful to mention
domains in the blacklist. The default location of the blacklist is
=/etc/mail/verify-black-list=. If you need a different location,
define it to =confVERIFIER_BLACKLIST=.
Both the blacklist and the whitelist file are maps, they must be
created with =makemap=. Therefore the entries need a LHS (the address
or domain) and a RHS. The actual content of the RHS has NO meaning at
all.
The FEATURE defines a socket map. The default target of the map is
=inet:8884@127.0.0.1=, according to the default setting in
=Config.py=. If you need something different, define it to
=confVERIFIER_MAP=, but don't forget to also adjust =Config.py=.
** Configuration of the verification daemon
The configuration of the daemon is done in the file =Config.py=.
This is the default of this file:
<example>
[Daemon]
Address: 127.0.0.1
Port: 8884
PidFile: smmapd.pid
Plugins: Verifier,Verifier2
[Logging]
ApplId: smmapd
[Verifier]
ContainerModule: VerifierHandler
ContainerClass: VerifierHandlerContainer
WorkerModule: VerifierHandler
WorkerClass: VerifierHandlerWorker
EnableCaching: 1
CacheExpiration: 20
SMTPTimeOut: 20
SMTPHeloParam: local
SMTPCheckSender: <>
[Verifier2]
ContainerModule: VerifierHandler
ContainerClass: VerifierHandlerContainer
WorkerModule: VerifierHandler
WorkerClass: VerifierHandlerWorker
EnableCaching: 1
CacheExpiration: 20
SMTPTimeOut: 20
SMTPHeloParam: hottis.de
SMTPCheckSender: <postmaster@hottis.de></example>
=Port= and =Address= are specifying the socket the daemon should
listen to for communication with sendmail. These settings must be
reflected in the =confVERIFIER_MAP= if you change it.
=SMTPTimeOut= is the timeout for the communication with the MX servers
when verifying addresses.
=SMTPHeloParam= is the parameter the verifier will use with the =HELO=
command when verifying.
=SMTPCheckSender= is the sender address used during
verifications. You should not change it unless you know what you do to
avoid verification loops.
Since the verification is a time and resource consuming process,
results can be cached, which is enabled by default. Set
=EnableCaching= to 0 to disable it.
=CacheExpiration= is the time in seconds an entry in the cache is
considered as valid. It should be much higher.
** Operation
Configure sendmail and the daemon according to your needs. Start the
daemon:
<example>
./verifier.py
</example>
** Changes
- According to a comment in comp.mail.sendmail I've introduced a class
=verifier_fix_white= in the FEATURE file, currently containing only
the string =postmaster=. Addresses with userpart in this class will
never ever be verified to avoid infinite verifying loops.
*** 2004-05-17
- support plugins
- separate container and worker object, thereby enable multiple
instances of the same plugins

View File

@ -1,19 +0,0 @@
#!/usr/bin/env python
from distutils.core import setup
setup(name="smmapd",
version="0.1",
description="Framework for sendmail SocketMap handlers",
long_description = """
A framework to build handlers for the sendmail 8.13 SocketMap,
together with an implementation of a sender-address verifier,
together we a sendmail m4 feature file""",
author="Wolfgang Hottgenroth",
author_email="woho@hottis.de",
url="http://www.hottis.de/web/verifier/index.html",
py_modules = [ 'SendmailSocketMapHandler', 'Cache', 'Logging', 'VerifierHandler' ],
scripts = [ 'smmapd' ],
data_files = [('config', ['smmapd.ini']),
('cf/feature', ['verifysender.m4'])]
)

View File

@ -1,65 +0,0 @@
#!/usr/bin/python
import SocketServer
import socket
import os
import ConfigParser
import getopt
import sys
from Logging import *
from SendmailSocketMapHandler import SendmailDispatcher
def usage():
print "Usage"
try:
opts, args = getopt.getopt(sys.argv[1:], "hC:F", ["help", "ConfigFile=", "ForeGround"])
except getopt.GetoptError:
usage()
sys.exit(2)
configFile = '/etc/smmapd.ini'
foreGround = None
for o, a in opts:
if o in ("-h", "--help"):
usage()
sys.exit()
if o in ("-C", "--ConfigFile"):
configFile = a
if o in ("-F", "--ForeGround"):
foreGround = 1
config = ConfigParser.ConfigParser()
config.read(configFile)
openlog(config)
if foreGround:
pid = 0
else:
pid = os.fork()
if pid:
pidFile = file(config.get('Daemon', 'PidFile'), mode='w')
pidFile.write("%i\n" % pid)
pidFile.close()
print "daemon started with pid ", pid
else:
log("daemon started")
SendmailDispatcher.registerAll(config)
try:
address = config.get('Daemon', 'Address')
port = int(config.get('Daemon', 'Port'))
srv = SocketServer.ThreadingTCPServer((address, port), SendmailDispatcher)
srv.serve_forever()
except socket.error, arg:
log("got a socket error: %s" % str(arg))
log("daemon died")

View File

@ -1,37 +0,0 @@
[Daemon]
Address: 127.0.0.1
Port: 8884
PidFile: smmapd.pid
Plugins: verifier,verifier2,cyrusChecker
[Logging]
ApplId: smmapd
[verifier]
ContainerModule: VerifierHandler
ContainerClass: VerifierHandlerContainer
WorkerModule: VerifierHandler
WorkerClass: VerifierHandlerWorker
EnableCaching: yes
CacheExpiration: 20
SMTPTimeOut: 20
SMTPHeloParam: local
SMTPCheckSender: <>
[verifier2]
ContainerModule: VerifierHandler
ContainerClass: VerifierHandlerContainer
WorkerModule: VerifierHandler
WorkerClass: VerifierHandlerWorker
EnableCaching: yes
CacheExpiration: 20
SMTPTimeOut: 20
SMTPHeloParam: hottis.de
SMTPCheckSender: <postmaster@hottis.de>
[cyrusChecker]
ContainerModule: SendmailSocketMapHandler
ContainerClass: smmapBaseHandlerContainer
WorkerModule: CyrusChecker
WorkerClass: CyrusCheckerWorker

View File

@ -1,424 +0,0 @@
####
# Copyright 2000,2001 by Timothy O'Malley <timo@alum.mit.edu>
#
# All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software
# and its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of
# Timothy O'Malley not be used in advertising or publicity
# pertaining to distribution of the software without specific, written
# prior permission.
#
# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
####
"""Timeout Socket
This module enables a timeout mechanism on all TCP connections. It
does this by inserting a shim into the socket module. After this module
has been imported, all socket creation goes through this shim. As a
result, every TCP connection will support a timeout.
The beauty of this method is that it immediately and transparently
enables the entire python library to support timeouts on TCP sockets.
As an example, if you wanted to SMTP connections to have a 20 second
timeout:
import timeoutsocket
import smtplib
timeoutsocket.setDefaultSocketTimeout(20)
The timeout applies to the socket functions that normally block on
execution: read, write, connect, and accept. If any of these
operations exceeds the specified timeout, the exception Timeout
will be raised.
The default timeout value is set to None. As a result, importing
this module does not change the default behavior of a socket. The
timeout mechanism only activates when the timeout has been set to
a numeric value. (This behavior mimics the behavior of the
select.select() function.)
This module implements two classes: TimeoutSocket and TimeoutFile.
The TimeoutSocket class defines a socket-like object that attempts to
avoid the condition where a socket may block indefinitely. The
TimeoutSocket class raises a Timeout exception whenever the
current operation delays too long.
The TimeoutFile class defines a file-like object that uses the TimeoutSocket
class. When the makefile() method of TimeoutSocket is called, it returns
an instance of a TimeoutFile.
Each of these objects adds two methods to manage the timeout value:
get_timeout() --> returns the timeout of the socket or file
set_timeout() --> sets the timeout of the socket or file
As an example, one might use the timeout feature to create httplib
connections that will timeout after 30 seconds:
import timeoutsocket
import httplib
H = httplib.HTTP("www.python.org")
H.sock.set_timeout(30)
Note: When used in this manner, the connect() routine may still
block because it happens before the timeout is set. To avoid
this, use the 'timeoutsocket.setDefaultSocketTimeout()' function.
Good Luck!
"""
__version__ = "$Revision$"
__author__ = "Timothy O'Malley <timo@alum.mit.edu>"
#
# Imports
#
import select, string
import socket
if not hasattr(socket, "_no_timeoutsocket"):
_socket = socket.socket
else:
_socket = socket._no_timeoutsocket
#
# Set up constants to test for Connected and Blocking operations.
# We delete 'os' and 'errno' to keep our namespace clean(er).
# Thanks to Alex Martelli and G. Li for the Windows error codes.
#
import os
if os.name == "nt":
_IsConnected = ( 10022, 10056 )
_ConnectBusy = ( 10035, )
_AcceptBusy = ( 10035, )
else:
import errno
_IsConnected = ( errno.EISCONN, )
_ConnectBusy = ( errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK )
_AcceptBusy = ( errno.EAGAIN, errno.EWOULDBLOCK )
del errno
del os
#
# Default timeout value for ALL TimeoutSockets
#
_DefaultTimeout = None
def setDefaultSocketTimeout(timeout):
global _DefaultTimeout
_DefaultTimeout = timeout
def getDefaultSocketTimeout():
return _DefaultTimeout
#
# Exceptions for socket errors and timeouts
#
Error = socket.error
class Timeout(Exception):
pass
#
# Factory function
#
from socket import AF_INET, SOCK_STREAM
def timeoutsocket(family=AF_INET, type=SOCK_STREAM, proto=None):
if family != AF_INET or type != SOCK_STREAM:
if proto:
return _socket(family, type, proto)
else:
return _socket(family, type)
return TimeoutSocket( _socket(family, type), _DefaultTimeout )
# end timeoutsocket
#
# The TimeoutSocket class definition
#
class TimeoutSocket:
"""TimeoutSocket object
Implements a socket-like object that raises Timeout whenever
an operation takes too long.
The definition of 'too long' can be changed using the
set_timeout() method.
"""
_copies = 0
_blocking = 1
def __init__(self, sock, timeout):
self._sock = sock
self._timeout = timeout
# end __init__
def __getattr__(self, key):
return getattr(self._sock, key)
# end __getattr__
def get_timeout(self):
return self._timeout
# end set_timeout
def set_timeout(self, timeout=None):
self._timeout = timeout
# end set_timeout
def setblocking(self, blocking):
self._blocking = blocking
return self._sock.setblocking(blocking)
# end set_timeout
def connect_ex(self, addr):
errcode = 0
try:
self.connect(addr)
except Error, why:
errcode = why[0]
return errcode
# end connect_ex
def connect(self, addr, port=None, dumbhack=None):
# In case we were called as connect(host, port)
if port != None: addr = (addr, port)
# Shortcuts
sock = self._sock
timeout = self._timeout
blocking = self._blocking
# First, make a non-blocking call to connect
try:
sock.setblocking(0)
sock.connect(addr)
sock.setblocking(blocking)
return
except Error, why:
# Set the socket's blocking mode back
sock.setblocking(blocking)
# If we are not blocking, re-raise
if not blocking:
raise
# If we are already connected, then return success.
# If we got a genuine error, re-raise it.
errcode = why[0]
if dumbhack and errcode in _IsConnected:
return
elif errcode not in _ConnectBusy:
raise
# Now, wait for the connect to happen
# ONLY if dumbhack indicates this is pass number one.
# If select raises an error, we pass it on.
# Is this the right behavior?
if not dumbhack:
r,w,e = select.select([], [sock], [], timeout)
if w:
return self.connect(addr, dumbhack=1)
# If we get here, then we should raise Timeout
raise Timeout("Attempted connect to %s timed out." % str(addr) )
# end connect
def accept(self, dumbhack=None):
# Shortcuts
sock = self._sock
timeout = self._timeout
blocking = self._blocking
# First, make a non-blocking call to accept
# If we get a valid result, then convert the
# accept'ed socket into a TimeoutSocket.
# Be carefult about the blocking mode of ourselves.
try:
sock.setblocking(0)
newsock, addr = sock.accept()
sock.setblocking(blocking)
timeoutnewsock = self.__class__(newsock, timeout)
timeoutnewsock.setblocking(blocking)
return (timeoutnewsock, addr)
except Error, why:
# Set the socket's blocking mode back
sock.setblocking(blocking)
# If we are not supposed to block, then re-raise
if not blocking:
raise
# If we got a genuine error, re-raise it.
errcode = why[0]
if errcode not in _AcceptBusy:
raise
# Now, wait for the accept to happen
# ONLY if dumbhack indicates this is pass number one.
# If select raises an error, we pass it on.
# Is this the right behavior?
if not dumbhack:
r,w,e = select.select([sock], [], [], timeout)
if r:
return self.accept(dumbhack=1)
# If we get here, then we should raise Timeout
raise Timeout("Attempted accept timed out.")
# end accept
def send(self, data, flags=0):
sock = self._sock
if self._blocking:
r,w,e = select.select([],[sock],[], self._timeout)
if not w:
raise Timeout("Send timed out")
return sock.send(data, flags)
# end send
def recv(self, bufsize, flags=0):
sock = self._sock
if self._blocking:
r,w,e = select.select([sock], [], [], self._timeout)
if not r:
raise Timeout("Recv timed out")
return sock.recv(bufsize, flags)
# end recv
def makefile(self, flags="r", bufsize=-1):
self._copies = self._copies +1
return TimeoutFile(self, flags, bufsize)
# end makefile
def close(self):
if self._copies <= 0:
self._sock.close()
else:
self._copies = self._copies -1
# end close
# end TimeoutSocket
class TimeoutFile:
"""TimeoutFile object
Implements a file-like object on top of TimeoutSocket.
"""
def __init__(self, sock, mode="r", bufsize=4096):
self._sock = sock
self._bufsize = 4096
if bufsize > 0: self._bufsize = bufsize
if not hasattr(sock, "_inqueue"): self._sock._inqueue = ""
# end __init__
def __getattr__(self, key):
return getattr(self._sock, key)
# end __getattr__
def close(self):
self._sock.close()
self._sock = None
# end close
def write(self, data):
self.send(data)
# end write
def read(self, size=-1):
_sock = self._sock
_bufsize = self._bufsize
while 1:
datalen = len(_sock._inqueue)
if datalen >= size >= 0:
break
bufsize = _bufsize
if size > 0:
bufsize = min(bufsize, size - datalen )
buf = self.recv(bufsize)
if not buf:
break
_sock._inqueue = _sock._inqueue + buf
data = _sock._inqueue
_sock._inqueue = ""
if size > 0 and datalen > size:
_sock._inqueue = data[size:]
data = data[:size]
return data
# end read
def readline(self, size=-1):
_sock = self._sock
_bufsize = self._bufsize
while 1:
idx = string.find(_sock._inqueue, "\n")
if idx >= 0:
break
datalen = len(_sock._inqueue)
if datalen >= size >= 0:
break
bufsize = _bufsize
if size > 0:
bufsize = min(bufsize, size - datalen )
buf = self.recv(bufsize)
if not buf:
break
_sock._inqueue = _sock._inqueue + buf
data = _sock._inqueue
_sock._inqueue = ""
if idx >= 0:
idx = idx + 1
_sock._inqueue = data[idx:]
data = data[:idx]
elif size > 0 and datalen > size:
_sock._inqueue = data[size:]
data = data[:size]
return data
# end readline
def readlines(self, sizehint=-1):
result = []
data = self.read()
while data:
idx = string.find(data, "\n")
if idx >= 0:
idx = idx + 1
result.append( data[:idx] )
data = data[idx:]
else:
result.append( data )
data = ""
return result
# end readlines
def flush(self): pass
# end TimeoutFile
#
# Silently replace the socket() builtin function with
# our timeoutsocket() definition.
#
if not hasattr(socket, "_no_timeoutsocket"):
socket._no_timeoutsocket = socket.socket
socket.socket = timeoutsocket
del socket
socket = timeoutsocket
# Finis

View File

@ -1,110 +0,0 @@
VERSIONID(`$Id$')
divert(-1)
define(`_USAGE_', `dnl
errprint(`*** ERROR: missing argument for FEATURE(verifysender):
Usage: FEATURE(`verifysender', `_mode_', `_return_')
_mode_: black or white
_return_: temp or perm
found: $1
')')
ifelse(_ARG_, `black', `', `
ifelse(_ARG_, `white', `', `
_USAGE_(`_mode_: ('_ARG_`)
')
')')
ifelse(_ARG2_, `temp', `', `
ifelse(_ARG2_, `perm', `', `
_USAGE_(`_return_: ('_ARG2_`)
')
')')
define(`_mode_', _ARG_)
define(`_return_', _ARG2_)
errprint(`*** _mode_: '_mode_`
')
errprint(`*** _return_: '_return_`
')
define(`_T_DSN_', `4.1.0')
define(`_T_REPLY', `451')
ifelse(_return_, `temp', `
define(`_DSN_', _T_DSN_)
define(`_REPLY_', _T_REPLY)', `dnl
define(`_DSN_', `5.1.0')
define(`_REPLY_', `550')')
ifelse(defn(`confVERIFIER_MAP'), `', `
define(`_VERIFIER_MAP_', `inet:8884@127.0.0.1')', `
define(`_VERIFIER_MAP_', confVERIFIER_MAP)')
ifelse(defn(`confVERIFIER_BLACKLIST'), `', `
define(`_VERIFIER_BLACKLIST_', `/etc/mail/verifier-black-list')', `
define(`_VERIFIER_BLACKLIST_', confVERIFIER_BLACKLIST)')
ifelse(defn(`confVERIFIER_WHITELIST'), `', `
define(`_VERIFIER_WHITELIST_', `/etc/mail/verifier-white-list')', `
define(`_VERIFIER_WHITELIST_', confVERIFIER_WHITELIST)')
divert(0)
LOCAL_CONFIG
# Adjust the port
Kverifier socket -T<temp> _VERIFIER_MAP_
ifelse(_mode_, `white', `dnl
Kverifier_helper hash -o _VERIFIER_WHITELIST_', `dnl
Kverifier_helper hash _VERIFIER_BLACKLIST_')
C{verifier_fix_white} postmaster
LOCAL_RULESETS
# This ruleset can be used to test the verifier in -bt mode
Svt
R$+ $: < $(verifier $1 $:none $) >
Sverifier0
R< $={verifier_fix_white} @ $+ > $@ < ok >
R< $+ @ $+ > $: < $2 > < $(verifier_helper $1 @ $2 $: $) >
R< $+ > < > $: < $(verifier_helper $1 $: $) >
ifelse(_mode_, `white', `dnl
dnl if we found nothing in the whitelist, we continue with checking
R< > $@ < cont >
dnl if we found something in the whitelist, we skip further verifications
R< $+ > $@ < ok >', `dnl
dnl if we found nothing in the blacklist, we skip further verifications
R< > $@ < ok >
dnl if we found something in the blacklist, we continue with checking
R< $+ > $@ < cont >')
Sverifier1
R< $+ > $: < $(verifier $1 $:none $) >
R< $* < temp > > $#error $@ _T_DSN_ $: "_T_REPLY_ Sender Address could currently not be verified (1)"
R< none > $#error $@ _T_DSN_ $: "_T_REPLY_ Sender Address could currently not be verified (2)"
R< <OK> $* > $@ < ok >
R< <NOK> < $* > > $#error $@ _DSN_ $: "_REPLY_ Sender Address was verified as bad: " $1
R< <TNOK> < $* > > $#error $@ _T_DSN_ $: "_T_REPLY_ Sender Address could currently not be verified (3): " $1
dnl if we get here, some is wrong with our code
R$* $#error $@ 4.7.1 $: "451 Local configuration error <sv1>"
SLocal_check_mail
dnl MAILER-DAEMON address must not be verified
R<> $@ <>
dnl try to focus
R$+ $: <> $>3 $1
R<> $+ < @ $+ . > $: < $1 @ $2 >
R<> $+ < @ $+ > $: < $1 @ $2 >
dnl if unable to focus, rest of check_mail should take care (may be we should reject)
R<> $* $@ OK
R< $+ @ $+ > $: < $1 @ $2 > $>verifier0 < $1 @ $2 >
R< $+ @ $+ > < cont > $: < $1 @ $2 > $>verifier1 < $1 @ $2 >
R< $+ @ $+ > $# $* $# $3
R< $+ @ $+ > < ok > $@ OK
dnl if we get here, some is wrong with our code
R$* $#error $@ 4.7.1 $: "451 Local configuration error <sv2>"