Compare commits
	
		
			1 Commits
		
	
	
		
			master
			...
			dynamic_li
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 87fc340f27 | 
| @@ -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 '#' | ||||
| @@ -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". | ||||
| @@ -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 | ||||
| @@ -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' | ||||
| @@ -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. | ||||
| @@ -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 | ||||
| @@ -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. | ||||
| @@ -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 | ||||
| @@ -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". | ||||
| @@ -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". | ||||
| @@ -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. | ||||
| @@ -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() | ||||
|  | ||||
| @@ -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>" | ||||
|      | ||||
| @@ -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) | ||||
| @@ -1,8 +0,0 @@ | ||||
| Cache.py | ||||
| Logging.py | ||||
| SendmailSocketMapHandler.py | ||||
| VerifierHandler.py | ||||
| setup.py | ||||
| smmapd | ||||
| smmapd.ini | ||||
| verifysender.m4 | ||||
| @@ -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 | ||||
|  | ||||
|          | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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: <> | ||||
|  | ||||
| [Verifier2] | ||||
| ContainerModule: VerifierHandler | ||||
| ContainerClass: VerifierHandlerContainer | ||||
| WorkerModule: VerifierHandler | ||||
| WorkerClass: VerifierHandlerWorker | ||||
| EnableCaching: 1 | ||||
| CacheExpiration: 20 | ||||
| SMTPTimeOut: 20 | ||||
| SMTPHeloParam: hottis.de | ||||
| SMTPCheckSender: <postmaster@hottis.de></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> | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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'])] | ||||
|      ) | ||||
| @@ -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") | ||||
|      | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
| @@ -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>" | ||||
		Reference in New Issue
	
	Block a user