initial upload
This commit is contained in:
parent
5de3c72eab
commit
6f06324736
37
smmapd_prototype/Cache.py
Normal file
37
smmapd_prototype/Cache.py
Normal file
@ -0,0 +1,37 @@
|
||||
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()
|
||||
|
27
smmapd_prototype/CyrusChecker.py
Normal file
27
smmapd_prototype/CyrusChecker.py
Normal file
@ -0,0 +1,27 @@
|
||||
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>"
|
||||
|
14
smmapd_prototype/Logging.py
Normal file
14
smmapd_prototype/Logging.py
Normal file
@ -0,0 +1,14 @@
|
||||
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)
|
8
smmapd_prototype/MANIFEST
Normal file
8
smmapd_prototype/MANIFEST
Normal file
@ -0,0 +1,8 @@
|
||||
Cache.py
|
||||
Logging.py
|
||||
SendmailSocketMapHandler.py
|
||||
VerifierHandler.py
|
||||
setup.py
|
||||
smmapd
|
||||
smmapd.ini
|
||||
verifysender.m4
|
190
smmapd_prototype/SendmailSocketMapHandler.py
Normal file
190
smmapd_prototype/SendmailSocketMapHandler.py
Normal file
@ -0,0 +1,190 @@
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
240
smmapd_prototype/VerifierHandler.py
Normal file
240
smmapd_prototype/VerifierHandler.py
Normal file
@ -0,0 +1,240 @@
|
||||
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
|
||||
|
327
smmapd_prototype/index.html
Normal file
327
smmapd_prototype/index.html
Normal file
@ -0,0 +1,327 @@
|
||||
<!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>
|
195
smmapd_prototype/index.wiki
Normal file
195
smmapd_prototype/index.wiki
Normal file
@ -0,0 +1,195 @@
|
||||
#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
|
||||
|
19
smmapd_prototype/setup.py
Normal file
19
smmapd_prototype/setup.py
Normal file
@ -0,0 +1,19 @@
|
||||
#!/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'])]
|
||||
)
|
65
smmapd_prototype/smmapd
Executable file
65
smmapd_prototype/smmapd
Executable file
@ -0,0 +1,65 @@
|
||||
#!/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")
|
||||
|
37
smmapd_prototype/smmapd.ini
Normal file
37
smmapd_prototype/smmapd.ini
Normal file
@ -0,0 +1,37 @@
|
||||
[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
|
||||
|
424
smmapd_prototype/timeoutsocket.py
Normal file
424
smmapd_prototype/timeoutsocket.py
Normal file
@ -0,0 +1,424 @@
|
||||
|
||||
####
|
||||
# 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
|
110
smmapd_prototype/verifysender.m4
Normal file
110
smmapd_prototype/verifysender.m4
Normal file
@ -0,0 +1,110 @@
|
||||
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>"
|
69
smmapdfw/Makefile
Normal file
69
smmapdfw/Makefile
Normal file
@ -0,0 +1,69 @@
|
||||
MAIN_OBJ = smmapd.o containers.o queue.o count.o config.o safe_write.o
|
||||
MOD_OBJS = test_workers.so
|
||||
ALLDEPEND = Makefile
|
||||
COMMON_CFLAGS = -g
|
||||
COMMON_LDFLAGS = -g
|
||||
OS = $(shell uname)
|
||||
RELEASE_ID = $(shell ./extract_release_id)
|
||||
|
||||
ifeq ($(OS), FreeBSD)
|
||||
BDB_INC ?= /usr/local/include/db41
|
||||
BDB_LIB ?=
|
||||
CFLAGS = $(COMMON_CFLAGS) -DFREEBSD -D_THREAD_SAFE -I$(BDB_INC)
|
||||
LDFLAGS = $(COMMON_LDFLAGS) -pthread
|
||||
SHARED = -shared
|
||||
endif
|
||||
|
||||
ifeq ($(OS), Linux)
|
||||
BDB_INC ?= /usr/include
|
||||
BDB_LIB ?= /usr/lib/libdb.a
|
||||
CFLAGS = $(COMMON_CFLAGS) -DLINUX -I$(BDB_INC)
|
||||
LDFLAGS = $(COMMON_LDFLAGS) -lresolv -pthread -ldl
|
||||
SHARED = -shared
|
||||
endif
|
||||
|
||||
ifeq ($(OS), SunOS)
|
||||
BDB_INC ?= /prod/BrklyDB4/current/include
|
||||
BDB_LIB ?= /prod/BrklyDB4/current/lib/libdb.a
|
||||
CFLAGS = $(COMMON_CFLAGS) -DSUNOS -D_REENTRANT -I$(BDB_INC)
|
||||
LDFLAGS = $(COMMON_LDFLAGS) -lresolv -lpthread -ldl -lsocket -lnsl
|
||||
SHARED = -G
|
||||
endif
|
||||
|
||||
|
||||
|
||||
LIBS =
|
||||
MOD_LIBS =
|
||||
CC = gcc
|
||||
|
||||
all: smmapd test_workers.so verify_worker.so cyrus_worker.so
|
||||
|
||||
smmapd: $(MAIN_OBJ)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
test_workers.so: test_workers.o config_public.o
|
||||
$(CC) $(LDFLAGS) $(SHARED) -o $@ $^
|
||||
|
||||
verify_worker.so: verify_worker.o config_public.o dns.o queue.o smtp.o $(BDB_LIB)
|
||||
$(CC) $(LDFLAGS) $(SHARED) -o $@ $^
|
||||
|
||||
cyrus_worker.so: cyrus_worker.o config_public.o smtp.o dns.o
|
||||
$(CC) $(LDFLAGS) $(SHARED) -o $@ $^
|
||||
|
||||
clean:
|
||||
-rm -f *o *so smmapd
|
||||
|
||||
distclean: clean
|
||||
-rm -r smmapd-$(RELEASE_ID)
|
||||
-rm smmapd-$(RELEASE_ID).tar.gz
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
tar:
|
||||
mkdir smmapd-$(RELEASE_ID) && \
|
||||
cp *.c *.h *.m4 smmapd.ini Makefile README smmapd-$(RELEASE_ID) && \
|
||||
chmod u+w smmapd-$(RELEASE_ID)/* && \
|
||||
tar -czf smmapd-$(RELEASE_ID).tar.gz smmapd-$(RELEASE_ID)
|
||||
|
||||
|
158
smmapdfw/README
Normal file
158
smmapdfw/README
Normal file
@ -0,0 +1,158 @@
|
||||
$Id$
|
||||
|
||||
|
||||
smmapd - A framework for workers for the sendmail socket map
|
||||
------------------------------------------------------------
|
||||
|
||||
|
||||
With release 8.13 sendmail introduces the socket map type. This is an
|
||||
easy-to-use interface to other processes, performing tasks using data
|
||||
provided by sendmail through the map and returning data to sendmail,
|
||||
influencing the routing process.
|
||||
|
||||
smmapd is completely written in C, after a prototype written in Python
|
||||
was used to prove the concept. (The prototype can be found here:
|
||||
http://www.hottis.de/web/verifier/index.html)
|
||||
|
||||
An interface to plugin workers is provided. These workers perform the
|
||||
"interesting" work.
|
||||
|
||||
The framework itself does actually nothing than communicating with
|
||||
sendmail. It receives requests from sendmail, dispatches the requests
|
||||
to the workers and sends back the data returned from the selected
|
||||
worker to sendmail.
|
||||
|
||||
Thereby, the workers can completely concentrate on their particular
|
||||
task and completely ignore the communication with sendmail, including
|
||||
server-stuff, communication protocol and so on.
|
||||
|
||||
Currently two plugins are included in the tarball: an address verifier
|
||||
(comparable to exim's or postfix's callback functionality) and a Cyrus
|
||||
IMAP mailbox verifier. For both plugins m4 files to be used in the
|
||||
sendmail configuration are included too.
|
||||
|
||||
|
||||
|
||||
Building smmapd
|
||||
---------------
|
||||
|
||||
Currently only a Makefile is available, no autoconf and so on.
|
||||
On Debian and FreeBSD it should build out-of-the-box, on Solaris it
|
||||
builds also. Have a look into the Makefile and adapt it to your
|
||||
environment.
|
||||
|
||||
The installation is currently not supported by the Makefile. Copy the
|
||||
smmapd binary, the *.so files (plugins) and the smmapd.ini
|
||||
configuration file into the desired directories.
|
||||
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
# every configuration not related to a particular
|
||||
# plugin is in the global section
|
||||
[global]
|
||||
|
||||
# shall we run in background
|
||||
do_fork = 1
|
||||
|
||||
# where to place the pidfile
|
||||
pid_file = smmapd.pid
|
||||
|
||||
# on which address shall we listen (currently
|
||||
# IN_ANYADDR can not be configured)
|
||||
address = 127.0.0.1
|
||||
|
||||
# on which port shall we listen
|
||||
port = 8887
|
||||
|
||||
# from shall we load plugins (currently only one
|
||||
# location)
|
||||
plugin_dir = /home/who/Sources/private/smmapd
|
||||
|
||||
# which plugins should be loaded
|
||||
plugins = test_worker1 test_worker2 verifier cyruscheck
|
||||
|
||||
# the section identifier of a plugin must match to the map
|
||||
# name in the sendmail.cf
|
||||
[verifier]
|
||||
|
||||
# the shared object file containing the plugin
|
||||
obj = verify_worker.so
|
||||
|
||||
# verifier specific configuration begins here
|
||||
|
||||
# how long should we wait for an result before
|
||||
# answering to sendmail (time in seconds)
|
||||
timeout_result = 5
|
||||
|
||||
# timeout for the smtp dialog when verifying an
|
||||
# address
|
||||
timeout_dialog = 20
|
||||
|
||||
# should results be cached
|
||||
cache_enabled = 1
|
||||
|
||||
# how long should an result be kept in the cache
|
||||
# (time in seconds)
|
||||
cache_expiry = 86400
|
||||
|
||||
# sender address for the smtp dialog (angle brackets
|
||||
# are required
|
||||
sender_address = <>
|
||||
|
||||
# argument for the ehlo in the smtp dialog
|
||||
ehlo_arg = local
|
||||
|
||||
# what is the smtp port?
|
||||
smtp_port = 25
|
||||
|
||||
# for each best MX of a domain one checker thread
|
||||
# will be started when verifying an address, how
|
||||
# many should be started at maximum
|
||||
max_checker_threads = 100
|
||||
|
||||
[cyruscheck]
|
||||
obj = cyrus_worker.so
|
||||
|
||||
# how long should we wait for an answer of the depot
|
||||
timeout = 10
|
||||
|
||||
# sender address for the lmtp dialog (angle brackets
|
||||
# are required)
|
||||
sender_address = <testsender>
|
||||
|
||||
# argument for the lhlo command
|
||||
lhlo_arg = local
|
||||
|
||||
# what is your lmtp port?
|
||||
lmtp_port = 24
|
||||
|
||||
|
||||
|
||||
Running smmapd
|
||||
--------------
|
||||
|
||||
The following commandline options are supported:
|
||||
|
||||
-F run in foreground, overwrites do_fork from configuration
|
||||
file
|
||||
-p location of the pidfile, overwrites pidfile option from
|
||||
the configuration file
|
||||
-f location of the configuration file
|
||||
-v print version and release information
|
||||
-h print help screen
|
||||
|
||||
|
||||
|
||||
Programming interface
|
||||
---------------------
|
||||
|
||||
will be documented later
|
||||
|
||||
|
||||
|
||||
|
||||
Wolfgang Hottgenroth <woho@hottis.de>
|
||||
|
262
smmapdfw/config.c
Normal file
262
smmapdfw/config.c
Normal file
@ -0,0 +1,262 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef SUNOS
|
||||
#include "sunos_comp.h"
|
||||
#endif
|
||||
|
||||
#define LF 10
|
||||
|
||||
#define STATE_START 0
|
||||
#define STATE_COMMENT 1
|
||||
#define STATE_SECTION 2
|
||||
#define STATE_NAME 3
|
||||
#define STATE_EQUAL 4
|
||||
#define STATE_VALUESTART 5
|
||||
#define STATE_VALUE 6
|
||||
|
||||
#define BUFSIZE 1024
|
||||
config_section_t *readcfg(char *cfgfile) {
|
||||
FILE *f;
|
||||
char buffer[BUFSIZE+1];
|
||||
char c;
|
||||
int state = STATE_START;
|
||||
int i = 0;
|
||||
char tmp;
|
||||
|
||||
config_section_t *cfg = NULL;
|
||||
config_section_t *cs_head;
|
||||
config_section_t *cs;
|
||||
config_item_t *ci_head;
|
||||
config_item_t *ci;
|
||||
|
||||
|
||||
f = fopen(cfgfile, "r");
|
||||
if (NULL == f) {
|
||||
syslog(LOG_ERR, "readcfg: unable to open config file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (EOF != (c = fgetc(f))) {
|
||||
/* printf("read: (%d) (%d) (%c)\n", state, c, c); */
|
||||
|
||||
switch (state) {
|
||||
case STATE_START:
|
||||
if ((';' == c) || ('#' == c)) {
|
||||
state = STATE_COMMENT;
|
||||
break;
|
||||
} else if ('[' == c) {
|
||||
state = STATE_SECTION;
|
||||
i = 0;
|
||||
break;
|
||||
} else if (LF == c) {
|
||||
break;
|
||||
} else {
|
||||
buffer[i++] = c;
|
||||
state = STATE_NAME;
|
||||
}
|
||||
|
||||
case STATE_COMMENT:
|
||||
if (LF == c)
|
||||
state = STATE_START;
|
||||
break;
|
||||
|
||||
case STATE_SECTION:
|
||||
if (']' == c) {
|
||||
buffer[i] = '\0';
|
||||
/* printf("section: (%s)\n", buffer); */
|
||||
state = STATE_START;
|
||||
i = 0;
|
||||
|
||||
cs = (config_section_t*) malloc(sizeof(config_section_t));
|
||||
cs->next = NULL;
|
||||
cs->name = (char*) malloc(strlen(buffer)+1);
|
||||
strcpy(cs->name, buffer);
|
||||
cs->item = NULL;
|
||||
|
||||
if (NULL == cfg) {
|
||||
cfg = cs;
|
||||
cs_head = cfg;
|
||||
} else {
|
||||
cs_head->next = cs;
|
||||
cs_head = cs;
|
||||
}
|
||||
|
||||
break;
|
||||
} else if (LF == c) {
|
||||
syslog(LOG_ERR, "readcfg: unexpected EOL in section");
|
||||
freecfg(cfg);
|
||||
fclose(f);
|
||||
return NULL;
|
||||
} else {
|
||||
buffer[i++] = c;
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_NAME:
|
||||
if (isblank(c)) {
|
||||
buffer[i] = '\0';
|
||||
/* printf("name: (%s)\n", buffer); */
|
||||
state = STATE_EQUAL;
|
||||
i = 0;
|
||||
|
||||
ci = (config_item_t*) malloc(sizeof(config_item_t));
|
||||
ci->next = NULL;
|
||||
ci->name = (char*) malloc(strlen(buffer)+1);
|
||||
strcpy(ci->name, buffer);
|
||||
ci->value = NULL;
|
||||
|
||||
if (NULL == cs->item) {
|
||||
cs->item = ci;
|
||||
ci_head = ci;
|
||||
} else {
|
||||
ci_head->next = ci;
|
||||
ci_head = ci;
|
||||
}
|
||||
|
||||
break;
|
||||
} else if (LF == c) {
|
||||
syslog(LOG_ERR, "readcfg: unexpected EOL in name");
|
||||
freecfg(cfg);
|
||||
fclose(f);
|
||||
return NULL;
|
||||
} else {
|
||||
buffer[i++] = c;
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_EQUAL:
|
||||
if ('=' == c) {
|
||||
state = STATE_VALUESTART;
|
||||
break;
|
||||
} else if (isblank(c)) {
|
||||
break;
|
||||
} else {
|
||||
syslog(LOG_ERR, "readcfg: unexpected character in equal");
|
||||
freecfg(cfg);
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
case STATE_VALUESTART:
|
||||
if (isblank(c)) {
|
||||
break;
|
||||
} else if (LF == c) {
|
||||
syslog(LOG_ERR, "readcfg: unexpected EOL in valuestart");
|
||||
freecfg(cfg);
|
||||
fclose(f);
|
||||
return NULL;
|
||||
} else {
|
||||
buffer[i++] = c;
|
||||
state = STATE_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_VALUE:
|
||||
if (LF == c) {
|
||||
buffer[i] = '\0';
|
||||
/* printf("value: (%s)\n", buffer); */
|
||||
state = STATE_START;
|
||||
i = 0;
|
||||
|
||||
ci->value = (char*) malloc(strlen(buffer)+1);
|
||||
strcpy(ci->value, buffer);
|
||||
|
||||
break;
|
||||
} else {
|
||||
buffer[i++] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
if (STATE_START != state) {
|
||||
syslog(LOG_ERR, "readcfg: unexpected EOF");
|
||||
freecfg(cfg);
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
|
||||
void printcfg(config_section_t *cfg) {
|
||||
config_section_t *cs;
|
||||
config_item_t *ci;
|
||||
|
||||
for (cs = cfg; cs != NULL; cs = cs->next) {
|
||||
printf("section: %s\n", cs->name);
|
||||
for (ci = cs->item; ci != NULL; ci = ci->next) {
|
||||
printf(" item: %s -> %s\n", ci->name, ci->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void freecfg(config_section_t *cfg) {
|
||||
config_section_t *cs, *csf;
|
||||
config_item_t *ci, *cif;
|
||||
|
||||
cs = cfg;
|
||||
while (NULL != cs) {
|
||||
ci = cs->item;
|
||||
|
||||
while (NULL != ci) {
|
||||
cif = ci;
|
||||
ci = ci->next;
|
||||
|
||||
free(cif->name);
|
||||
free(cif->value);
|
||||
free(cif);
|
||||
}
|
||||
|
||||
csf = cs;
|
||||
cs = cs->next;
|
||||
|
||||
free(csf->name);
|
||||
free(csf);
|
||||
}
|
||||
}
|
||||
|
||||
config_item_t *findcfgsection(config_section_t *cfg, char *section) {
|
||||
config_section_t *cs;
|
||||
|
||||
for (cs = cfg; cs != NULL; cs = cs->next)
|
||||
if (0 == strcmp(section, cs->name))
|
||||
return cs->item;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *findcfg(config_section_t *cfg, char *section, char *name) {
|
||||
config_item_t *ci;
|
||||
|
||||
for (ci = findcfgsection(cfg, section); ci != NULL; ci = ci->next)
|
||||
if (0 == strcmp(name, ci->name))
|
||||
return ci->value;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *findcfgx(config_section_t *cfg, char *section, char *name, char *default_value) {
|
||||
char *r = findcfg(cfg, section, name);
|
||||
return (NULL != r) ? r : default_value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef _TEST_MODE_
|
||||
int main() {
|
||||
config_section_t *cfg = readcfg("smmapd.ini");
|
||||
printcfg(cfg);
|
||||
printf("test1: %s\n", findcfg(cfg, "test_worker1", "obj"));
|
||||
printf("test2: %s\n", findcfg(cfg, "global", "port"));
|
||||
printf("test3: %s\n", findcfg(cfg, "nix", "obj"));
|
||||
printf("test4: %s\n", findcfg(cfg, "global", "nix"));
|
||||
printf("test5: %s\n", findcfgx(cfg, "global", "test", "test default"));
|
||||
|
||||
freecfg(cfg);
|
||||
}
|
||||
#endif /* _TEST_MODE_ */
|
37
smmapdfw/config.h
Normal file
37
smmapdfw/config.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef _CONFIG_H_
|
||||
#define _CONFIG_H_
|
||||
|
||||
|
||||
|
||||
|
||||
struct config_item_s {
|
||||
char *name;
|
||||
char *value;
|
||||
struct config_item_s *next;
|
||||
};
|
||||
|
||||
typedef struct config_item_s config_item_t;
|
||||
|
||||
struct config_section_s {
|
||||
char *name;
|
||||
config_item_t *item;
|
||||
struct config_section_s *next;
|
||||
};
|
||||
|
||||
typedef struct config_section_s config_section_t;
|
||||
typedef config_section_t cfg_t;
|
||||
typedef config_item_t cfgl_t;
|
||||
|
||||
|
||||
config_section_t *readcfg(char *cfgfile);
|
||||
void freecfg(config_section_t *cfg);
|
||||
char *findcfg(config_section_t *cfg, char *section, char *name);
|
||||
char *findcfgx(config_section_t *cfg, char *section, char *name, char *default_value);
|
||||
config_item_t *findcfgsection(config_section_t *cfg, char *section);
|
||||
extern char *findcfgl(config_item_t *cfg, char *name);
|
||||
extern char *findcfglx(config_item_t *cfg, char *name, char *default_value);
|
||||
|
||||
|
||||
#endif /* _CONFIG_H_ */
|
||||
|
||||
|
17
smmapdfw/config_public.c
Normal file
17
smmapdfw/config_public.c
Normal file
@ -0,0 +1,17 @@
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
|
||||
char *findcfgl(config_item_t *cfg, char *name) {
|
||||
config_item_t *ci;
|
||||
|
||||
for (ci = cfg; ci != NULL; ci = ci->next)
|
||||
if (0 == strcmp(name, ci->name))
|
||||
return ci->value;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *findcfglx(config_item_t *cfg, char *name, char *default_value) {
|
||||
char *r = findcfgl(cfg, name);
|
||||
return (NULL != r) ? r : default_value;
|
||||
}
|
||||
|
252
smmapdfw/containers.c
Normal file
252
smmapdfw/containers.c
Normal file
@ -0,0 +1,252 @@
|
||||
#include <syslog.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "containers.h"
|
||||
#include "config.h"
|
||||
#include "smmapd.h"
|
||||
|
||||
|
||||
#define CFG_SECTION_GLOBAL "global"
|
||||
#define CFG_NAME_PLUGIN_DIR "plugin_dir"
|
||||
#define CFG_NAME_PLUGINS "plugins"
|
||||
#define CFG_PLUGINS_DELIMITER " "
|
||||
#define CFG_NAME_OBJ "obj"
|
||||
|
||||
extern cfg_t *cfg;
|
||||
|
||||
classes_t classes_root = {NULL, 0, NULL, NULL};
|
||||
classes_t *classes_head = &classes_root;
|
||||
|
||||
|
||||
|
||||
int containers_setup(container_handle_t **ch) {
|
||||
*ch = (container_handle_t*)malloc(sizeof(container_handle_t));
|
||||
(*ch)->worker_handle_root.next = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int containers_destroy(container_handle_t *ch) {
|
||||
worker_handle_t *wh, *wh2;
|
||||
classes_t *classes;
|
||||
int err;
|
||||
|
||||
syslog(LOG_DEBUG, "containers_destroy: going to free container_handle, first the worker handles");
|
||||
|
||||
wh = ch->worker_handle_root.next;
|
||||
while (NULL != wh) {
|
||||
for (classes = classes_root.next; classes != NULL; classes = classes->next) {
|
||||
if (classes->id == wh->id) {
|
||||
if (NULL != classes->descr->work_destroy_function) {
|
||||
syslog(LOG_DEBUG, "containers_destroy: calling work_destroy_function for class %s (%d)",
|
||||
classes->descr->name, classes->id);
|
||||
err = (*classes->descr->work_destroy_function)(classes->handle, wh->handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
wh2 = wh;
|
||||
wh = wh->next;
|
||||
free(wh2);
|
||||
}
|
||||
|
||||
free(ch);
|
||||
syslog(LOG_DEBUG, "containers_destroy: all freed");
|
||||
}
|
||||
|
||||
|
||||
int containers_dispatcher(container_handle_t *ch, char *input, char *output) {
|
||||
char *class;
|
||||
char *data;
|
||||
classes_t *classes;
|
||||
int result, err;
|
||||
worker_handle_t *wh, *wh_last, *wh2;
|
||||
|
||||
syslog(LOG_DEBUG, "dispatcher: input: %s", input);
|
||||
|
||||
data = strchr(input, ' ');
|
||||
if (NULL == data) {
|
||||
syslog(LOG_ERR, "dispatcher: illegal input (%s)", input);
|
||||
return SMM_ILLEGAL_INPUT;
|
||||
}
|
||||
|
||||
data++; /* skip the blank */
|
||||
|
||||
class = input;
|
||||
class[strlen(class)-strlen(data)-1] = '\0';
|
||||
|
||||
syslog(LOG_DEBUG, "dispatcher: class: %s, data: %s", class, data);
|
||||
|
||||
for (classes = classes_root.next; classes != NULL; classes = classes->next) {
|
||||
if (0 == strcmp(class, classes->descr->name)) {
|
||||
syslog(LOG_DEBUG, "dispatcher: yes, we support it, it's id=%d", classes->id);
|
||||
for (wh = ch->worker_handle_root.next, wh_last = &ch->worker_handle_root, wh2 = NULL;
|
||||
wh != NULL;
|
||||
wh = wh->next) {
|
||||
wh_last = wh;
|
||||
if (wh->id == classes->id) {
|
||||
syslog(LOG_DEBUG, "dispatcher: we already have a worker handle");
|
||||
wh2 = wh;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((NULL == wh_last->next) && (NULL == wh2)) {
|
||||
syslog(LOG_DEBUG, "dispatcher: we haven't one, we create one");
|
||||
wh2 = (worker_handle_t*)malloc(sizeof(worker_handle_t));
|
||||
wh2->id = classes->id;
|
||||
if (NULL != classes->descr->work_setup_function) {
|
||||
err = (*classes->descr->work_setup_function)(classes->handle, &(wh2->handle));
|
||||
} else {
|
||||
wh2->handle = NULL;
|
||||
}
|
||||
wh2->next = NULL;
|
||||
wh_last->next = wh2;
|
||||
}
|
||||
|
||||
|
||||
result = (*classes->descr->work_function)(classes->handle, wh2->handle, data, output);
|
||||
syslog(LOG_DEBUG, "dispatcher: worker output: (%d, %s)", result, output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL == classes) {
|
||||
syslog(LOG_ERR, "dispatcher: unknown class: %s", class);
|
||||
return SMM_UNKNOWN_CLASS;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int register_class(int id, class_descriptor_t *class_descriptor) {
|
||||
int result = 0;
|
||||
cfgl_t *c;
|
||||
classes_t *w;
|
||||
|
||||
|
||||
syslog(LOG_DEBUG, "register_class: registering class %s", class_descriptor->name);
|
||||
|
||||
c = findcfgsection(cfg, class_descriptor->name);
|
||||
if (NULL == c) {
|
||||
syslog(LOG_ERR, "register_class: no configuration section for this plugin available");
|
||||
return -1;
|
||||
}
|
||||
|
||||
w = (classes_t *) malloc(sizeof(classes_t));
|
||||
if (NULL == w) {
|
||||
syslog(LOG_ERR, "register_class: unable to alloc memory");
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (NULL != class_descriptor->init_function) {
|
||||
syslog(LOG_DEBUG, "register_class: initializing class %s", class_descriptor->name);
|
||||
result = (*class_descriptor->init_function)(c, &w->handle);
|
||||
if (0 != result) {
|
||||
syslog(LOG_ERR, "register_class: unable to init class %s, exit", class_descriptor->name);
|
||||
return -3;
|
||||
}
|
||||
} else {
|
||||
w->handle = NULL;
|
||||
}
|
||||
|
||||
w->descr = class_descriptor;
|
||||
w->id = id;
|
||||
w->next = NULL;
|
||||
|
||||
classes_head->next = w;
|
||||
classes_head = w;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int unregister_class(classes_t *class) {
|
||||
int result = 0;
|
||||
|
||||
syslog(LOG_DEBUG, "unregister_class: unregistering class %s", class->descr->name);
|
||||
|
||||
if (NULL != class->descr->destroy_function) {
|
||||
syslog(LOG_DEBUG, "unregister_class: destroying class");
|
||||
result = (*class->descr->destroy_function)(&class->handle);
|
||||
syslog(LOG_ERR, "unregister_class: destroy function returns %d", result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unregister_all() {
|
||||
classes_t *classes, *cf;
|
||||
|
||||
classes = classes_root.next;
|
||||
while (NULL != classes) {
|
||||
cf = classes;
|
||||
classes = classes->next;
|
||||
|
||||
unregister_class(cf);
|
||||
free(cf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int register_all() {
|
||||
void *dl_handle;
|
||||
class_descriptor_t * class_descriptor;
|
||||
char *cfg_plugin_dir, *cfg_plugins, *cfg_plugin, *cfg_obj;
|
||||
char *obj_name;
|
||||
const char *err_msg;
|
||||
int err;
|
||||
int id = 0;
|
||||
|
||||
cfg_plugin_dir = findcfg(cfg, CFG_SECTION_GLOBAL, CFG_NAME_PLUGIN_DIR);
|
||||
if (NULL == cfg_plugin_dir) {
|
||||
syslog(LOG_ERR, "register_all: no plugin dir configured");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfg_plugins = findcfg(cfg, CFG_SECTION_GLOBAL, CFG_NAME_PLUGINS);
|
||||
if (NULL == cfg_plugins) {
|
||||
syslog(LOG_ERR, "register_all: no plugins configured");
|
||||
return -2;
|
||||
}
|
||||
|
||||
while (NULL != (cfg_plugin = strtok(cfg_plugins, CFG_PLUGINS_DELIMITER))) {
|
||||
cfg_plugins = NULL; /* this is for subsequence calls of strtok */
|
||||
dlerror(); /* this is to clear old errors */
|
||||
|
||||
cfg_obj = findcfg(cfg, cfg_plugin, CFG_NAME_OBJ);
|
||||
if (NULL == cfg_obj) {
|
||||
syslog(LOG_ERR, "register_all: obj for plugin %s not configured", cfg_plugin);
|
||||
return -3;
|
||||
}
|
||||
|
||||
obj_name = (char*) malloc(strlen(cfg_plugin_dir) + strlen(cfg_obj) + 5);
|
||||
obj_name[0] = '\0';
|
||||
strcat(obj_name, cfg_plugin_dir);
|
||||
strcat(obj_name, "/");
|
||||
strcat(obj_name, cfg_obj);
|
||||
|
||||
dl_handle = dlopen(obj_name, RTLD_NOW);
|
||||
if (NULL == dl_handle) {
|
||||
syslog(LOG_ERR, "register_all: error when dlopen: %s", dlerror());
|
||||
free(obj_name);
|
||||
return -4;
|
||||
}
|
||||
free(obj_name);
|
||||
|
||||
class_descriptor = (class_descriptor_t*) dlsym(dl_handle, cfg_plugin);
|
||||
if (NULL != (err_msg = dlerror())) {
|
||||
syslog(LOG_ERR, "register_all: plugin %s not found, error %s", cfg_plugin, err_msg);
|
||||
return -5;
|
||||
}
|
||||
|
||||
err = register_class(id++, class_descriptor);
|
||||
if (0 != err) {
|
||||
syslog(LOG_ERR, "register_all: unable to initialize plugin %s, error %d", cfg_plugin, err);
|
||||
return -6;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
39
smmapdfw/containers.h
Normal file
39
smmapdfw/containers.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef _CONTAINERS_H_
|
||||
#define _CONTAINERS_H_
|
||||
|
||||
#define _SMMAPD_
|
||||
#include "containers_public.h"
|
||||
|
||||
struct classes_s {
|
||||
class_descriptor_t *descr;
|
||||
int id;
|
||||
void *handle;
|
||||
struct classes_s *next;
|
||||
};
|
||||
|
||||
typedef struct classes_s classes_t;
|
||||
|
||||
struct worker_handle_s {
|
||||
int id;
|
||||
void *handle;
|
||||
struct worker_handle_s *next;
|
||||
};
|
||||
|
||||
typedef struct worker_handle_s worker_handle_t;
|
||||
|
||||
struct container_handle_s {
|
||||
worker_handle_t worker_handle_root;
|
||||
};
|
||||
|
||||
typedef struct container_handle_s container_handle_t;
|
||||
|
||||
int containers_setup(container_handle_t **ch);
|
||||
int containers_destroy(container_handle_t *ch);
|
||||
int containers_dispatcher(container_handle_t *ch, char *input, char *output);
|
||||
|
||||
int register_all();
|
||||
|
||||
|
||||
#endif /* _CONTAINERS_H_ */
|
||||
|
||||
|
19
smmapdfw/containers_public.h
Normal file
19
smmapdfw/containers_public.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef _CONTAINERS_PUBLIC_H_
|
||||
#define _CONTAINERS_PUBLIC_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
struct class_descriptor_s {
|
||||
char *name;
|
||||
int (*init_function)(cfgl_t *cfg, void **handle);
|
||||
int (*destroy_function)(void *handle);
|
||||
int (*work_setup_function)(void *handle, void **work_handle);
|
||||
int (*work_function)(void *handle, void *work_handle, char *input, char *output);
|
||||
int (*work_destroy_function)(void *handle, void *work_handle);
|
||||
};
|
||||
|
||||
typedef struct class_descriptor_s class_descriptor_t;
|
||||
|
||||
|
||||
|
||||
#endif /* _CONTAINERS_PUBLIC_H_ */
|
41
smmapdfw/count.c
Normal file
41
smmapdfw/count.c
Normal file
@ -0,0 +1,41 @@
|
||||
#include "count.h"
|
||||
|
||||
void count_init(count_t *c) {
|
||||
c->cnt = 0;
|
||||
|
||||
pthread_mutex_init(&c->mutex, NULL);
|
||||
}
|
||||
|
||||
void count_destroy(count_t *c) {
|
||||
pthread_mutex_destroy(&c->mutex);
|
||||
}
|
||||
|
||||
int count_inc(count_t *c) {
|
||||
int i;
|
||||
|
||||
pthread_mutex_lock(&c->mutex);
|
||||
i = ++c->cnt;
|
||||
pthread_mutex_unlock(&c->mutex);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int count_dec(count_t *c) {
|
||||
int i;
|
||||
|
||||
pthread_mutex_lock(&c->mutex);
|
||||
i = --c->cnt;
|
||||
pthread_mutex_unlock(&c->mutex);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int count_get(count_t *c) {
|
||||
int i;
|
||||
|
||||
pthread_mutex_lock(&c->mutex);
|
||||
i = c->cnt;
|
||||
pthread_mutex_unlock(&c->mutex);
|
||||
|
||||
return i;
|
||||
}
|
21
smmapdfw/count.h
Normal file
21
smmapdfw/count.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef _COUNT_H_
|
||||
#define _COUNT_H_
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
struct count_s {
|
||||
int cnt;
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
typedef struct count_s count_t;
|
||||
|
||||
void count_init(count_t *c);
|
||||
void count_destroy(count_t *c);
|
||||
int count_inc(count_t *c);
|
||||
int count_dec(count_t *c);
|
||||
int count_get(count_t *c);
|
||||
|
||||
#endif /* _COUNT_H_ */
|
||||
|
||||
|
230
smmapdfw/cyrus_worker.c
Normal file
230
smmapdfw/cyrus_worker.c
Normal file
@ -0,0 +1,230 @@
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "containers_public.h"
|
||||
#include "smmapd.h"
|
||||
|
||||
#include "smtp.h"
|
||||
#include "dns.h"
|
||||
|
||||
#define SMM_LOCAL_PERM_NOK 101
|
||||
#define SMM_LOCAL_TEMP_NOK 102
|
||||
#define SMM_LOCAL_OK 103
|
||||
|
||||
|
||||
|
||||
struct cyrus_container_handle_s {
|
||||
cfgl_t *cfg;
|
||||
int timeout;
|
||||
char *sender_address;
|
||||
char *lhlo_arg;
|
||||
int lmtp_port;
|
||||
};
|
||||
|
||||
typedef struct cyrus_container_handle_s cyrus_container_handle_t;
|
||||
|
||||
|
||||
struct cyrus_work_handle_s {
|
||||
int id;
|
||||
cyrus_container_handle_t *cch;
|
||||
};
|
||||
|
||||
typedef struct cyrus_work_handle_s cyrus_work_handle_t;
|
||||
|
||||
|
||||
|
||||
int cyrus_init(cfgl_t *cfg, void **handle);
|
||||
int cyrus_destroy(void *handle);
|
||||
/* int cyrus_work_setup(void *handle, void **work_handle); */
|
||||
int cyrus_work(void *handle, void *work_handle, char *input, char *output);
|
||||
/* int cyrus_work_destroy(void *handle, void *work_handle); */
|
||||
|
||||
|
||||
class_descriptor_t cyruscheck = {
|
||||
"cyruscheck",
|
||||
&cyrus_init,
|
||||
&cyrus_destroy,
|
||||
NULL, /* &verify_work_setup, */
|
||||
&cyrus_work,
|
||||
NULL /* &cyrus_work_destroy */
|
||||
};
|
||||
|
||||
|
||||
int cyrus_init(cfgl_t *cfg, void **handle) {
|
||||
cyrus_container_handle_t *cch;
|
||||
|
||||
cch = (cyrus_container_handle_t*) malloc(sizeof(cyrus_container_handle_t));
|
||||
cch->cfg = cfg;
|
||||
|
||||
cch->timeout = atoi(findcfglx(cch->cfg, "timeout", "5"));
|
||||
cch->sender_address = findcfglx(cch->cfg, "sender_address", "<>");
|
||||
cch->lhlo_arg = findcfglx(cch->cfg, "lhlo_arg", "local");
|
||||
cch->lmtp_port = atoi(findcfglx(cch->cfg, "lmtp_port", "24"));
|
||||
|
||||
*handle = cch;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cyrus_destroy(void *handle) {
|
||||
cyrus_container_handle_t *cch = (cyrus_container_handle_t*)handle;
|
||||
|
||||
free(cch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int cyrus_work(void *handle, void *work_handle, char *input, char *output) {
|
||||
static const char *DEFAULT_ANSWER = "default answer";
|
||||
static const char *ILLEGAL_INPUT = "illegal input (must be 'depot_uid depot_host')";
|
||||
static const char *UNEXPECTED_ERROR = "unexpected error in lmtp dialog";
|
||||
static const char *TIMEOUT_ERROR = "timeout on lmtp dialog";
|
||||
static const char *GO_AHEAD = "go ahead";
|
||||
static const char *DEPOT_DNS_ERROR = "depot could not be found in dns";
|
||||
|
||||
cyrus_container_handle_t *cch = (cyrus_container_handle_t*) handle;
|
||||
int result = SMM_TEMP_NOK;
|
||||
char *depot_uid, *depot_host, *response_text, *tmp_arg;
|
||||
|
||||
smtp_t *lmtp;
|
||||
a_rdata_t **a_rdata;
|
||||
int ip_address, done=0, err;
|
||||
|
||||
enum {
|
||||
CONNECT, LHLO, MAILFROM, RCPTTO, RSET, QUIT, END
|
||||
} state = CONNECT;
|
||||
|
||||
|
||||
|
||||
syslog(LOG_DEBUG, "cyrus_work: going to check %s", input);
|
||||
|
||||
depot_uid = input;
|
||||
if (NULL == (depot_host = strchr(depot_uid, ' '))) {
|
||||
snprintf(output, ANSWER_BUFSIZE, ILLEGAL_INPUT);
|
||||
return SMM_PERM_NOK;
|
||||
}
|
||||
|
||||
*depot_host = '\0';
|
||||
depot_host++;
|
||||
|
||||
|
||||
a_rdata = get_a_rrs(depot_host);
|
||||
if (NULL == a_rdata) {
|
||||
syslog(LOG_DEBUG, "cyrus_work: depot_host %s could not be found in dns",
|
||||
depot_host);
|
||||
snprintf(output, ANSWER_BUFSIZE, DEPOT_DNS_ERROR);
|
||||
return SMM_TEMP_NOK;
|
||||
}
|
||||
|
||||
ip_address = (*a_rdata)->address;
|
||||
free_rrs((void**)a_rdata);
|
||||
|
||||
|
||||
syslog(LOG_DEBUG, "cyrus_work: depot_uid %s, depot_host %s", depot_uid, depot_host);
|
||||
|
||||
|
||||
lmtp = smtp_init(ip_address, cch->lmtp_port, cch->timeout);
|
||||
|
||||
while ((END != state) && (0 == done)) {
|
||||
syslog(LOG_DEBUG, "cyrus_work, lmtp dialog state %d", state);
|
||||
switch(state) {
|
||||
case CONNECT:
|
||||
err = smtp_connect(lmtp);
|
||||
break;
|
||||
case LHLO:
|
||||
err = smtp_lhlo(lmtp, cch->lhlo_arg);
|
||||
break;
|
||||
case MAILFROM:
|
||||
err = smtp_mailfrom(lmtp, cch->sender_address);
|
||||
break;
|
||||
case RCPTTO:
|
||||
tmp_arg = (char*) malloc(sizeof(char) * (strlen(depot_uid)+5));
|
||||
*tmp_arg = '\0';
|
||||
strcat(tmp_arg, "<");
|
||||
strcat(tmp_arg, depot_uid);
|
||||
strcat(tmp_arg, ">");
|
||||
err = smtp_rcptto(lmtp, tmp_arg);
|
||||
free(tmp_arg);
|
||||
break;
|
||||
case RSET:
|
||||
err = smtp_rset(lmtp);
|
||||
break;
|
||||
case QUIT:
|
||||
err = smtp_quit(lmtp);
|
||||
break;
|
||||
}
|
||||
|
||||
state++;
|
||||
|
||||
switch(err) {
|
||||
case SMTP_TIMEOUT:
|
||||
syslog(LOG_DEBUG, "cyrus_work, timeout in lmtp dialog");
|
||||
result = SMM_LOCAL_TEMP_NOK;
|
||||
response_text = (char*)TIMEOUT_ERROR;
|
||||
done = 1;
|
||||
break;
|
||||
case 0:
|
||||
/* evaluate smtp_response, return or continue */
|
||||
err = smtp_response(lmtp, &response_text);
|
||||
if (-1 == err) {
|
||||
syslog(LOG_DEBUG, "cyrus_work, response could not be parsed");
|
||||
result = SMM_LOCAL_TEMP_NOK;
|
||||
response_text = (char*)UNEXPECTED_ERROR;
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
syslog(LOG_DEBUG, "cyrus_work, response: %d, %s", err, response_text);
|
||||
switch(err/100) {
|
||||
case 4:
|
||||
result = SMM_LOCAL_TEMP_NOK;
|
||||
done = 1;
|
||||
break;
|
||||
case 5:
|
||||
result = SMM_LOCAL_PERM_NOK;
|
||||
done = 1;
|
||||
break;
|
||||
case 2:
|
||||
if (END == state) {
|
||||
result = SMM_LOCAL_OK;
|
||||
response_text = (char*)GO_AHEAD;
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_DEBUG, "cyrus_work, unexpected error in lmtp dialog");
|
||||
result = SMM_LOCAL_TEMP_NOK;
|
||||
response_text = (char*)UNEXPECTED_ERROR;
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
smtp_close(lmtp);
|
||||
|
||||
|
||||
switch (result) {
|
||||
case SMM_LOCAL_TEMP_NOK:
|
||||
snprintf(output, ANSWER_BUFSIZE, "<TNOK><%s>", response_text);
|
||||
result = SMM_OK;
|
||||
break;
|
||||
case SMM_LOCAL_PERM_NOK:
|
||||
snprintf(output, ANSWER_BUFSIZE, "<NOK><%s>", response_text);
|
||||
result = SMM_OK;
|
||||
break;
|
||||
case SMM_LOCAL_OK:
|
||||
snprintf(output, ANSWER_BUFSIZE, "<OK><%s>", response_text);
|
||||
result = SMM_OK;
|
||||
break;
|
||||
default:
|
||||
snprintf(output, ANSWER_BUFSIZE, response_text);
|
||||
break;
|
||||
}
|
||||
|
||||
smtp_destroy(lmtp);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
54
smmapdfw/cyruscheck.m4
Normal file
54
smmapdfw/cyruscheck.m4
Normal file
@ -0,0 +1,54 @@
|
||||
VERSIONID(`$Id$')
|
||||
|
||||
|
||||
|
||||
divert(-1)
|
||||
|
||||
define(`_USAGE_', `dnl
|
||||
errprint(`*** ERROR: missing argument for FEATURE(cyruscheck):
|
||||
Usage: FEATURE(`cyruscheck', `_dummy_')
|
||||
_dummy_: active (actually returning errors) or dummy (just log what it would return)
|
||||
found: $1
|
||||
')')
|
||||
|
||||
ifelse(_ARG_, `dummy', `', `
|
||||
ifelse(_ARG_, `active', `', `
|
||||
_USAGE_(`_dummy_: ('_ARG_`)
|
||||
')
|
||||
')')
|
||||
|
||||
define(`_dummy_', _ARG_)
|
||||
|
||||
dnl errprint(`*** _dummy_: '_dummy_`
|
||||
dnl ')
|
||||
|
||||
|
||||
|
||||
|
||||
ifelse(defn(`confCYRUSCHECK_MAP'), `', `
|
||||
define(`_CYRUSCHECK_MAP_', `inet:8884@127.0.0.1')', `
|
||||
define(`_CYRUSCHECK_MAP_', confCYRUSCHECK_MAP)')
|
||||
|
||||
divert(0)
|
||||
|
||||
LOCAL_CONFIG
|
||||
# Adjust the port
|
||||
Kcyruscheck socket -T<temp> _CYRUSCHECK_MAP_
|
||||
Kcht_logger syslog
|
||||
|
||||
LOCAL_RULESETS
|
||||
# This ruleset can be used to test the verifier in -bt mode
|
||||
Scc
|
||||
R< $+ > < $+ > $: < $(cyruscheck $1 $2 $:none $) >
|
||||
|
||||
Scyruscheck
|
||||
R< $+ > < $+ > $: < $1 > < $2 > < $(cyruscheck $1 $2 $:none $) >
|
||||
ifelse(_dummy_, `dummy', `dnl
|
||||
dnl dummy
|
||||
R< $+ > < $+ > < $+ > $@ < ok > $(cht_logger $1 -- $2 --- $3 $)', `dnl
|
||||
dnl active
|
||||
R< $+ > < $+ > < $+ > $: < $3 >
|
||||
R< <OK> $* > $@ < ok >
|
||||
R< <NOK> $* > $#error $@ 5.0.0 $: "500 Depot returns error: " $1
|
||||
R< <TNOK> $* > $#error $@ 4.0.0 $: "400 Depot returns error: " $1
|
||||
R$* $#error $@ 4.7.1 $: "451 Local configuration error <cc1>"')
|
309
smmapdfw/dns.c
Normal file
309
smmapdfw/dns.c
Normal file
@ -0,0 +1,309 @@
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/nameser.h>
|
||||
#include <resolv.h>
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "dns.h"
|
||||
|
||||
extern int h_errno;
|
||||
/* extern struct state _res; */
|
||||
|
||||
union answer_u {
|
||||
HEADER hdr;
|
||||
u_char buf[PACKETSZ+1];
|
||||
};
|
||||
|
||||
typedef union answer_u answer_t;
|
||||
|
||||
|
||||
|
||||
#define get32_x(b, o) htonl((b[o+3] << 24) + (b[o+2] << 16) + (b[o+1] << 8) + b[o])
|
||||
#define get32(b, o) ((b[o] << 24) + (b[o+1] << 16) + (b[o+2] << 8) + b[o+3])
|
||||
#define get16(b, o) ((b[o] << 8) + b[o+1])
|
||||
|
||||
|
||||
|
||||
static int get_domain_name(answer_t *answer, int offset, char **name) {
|
||||
int start, len, i, offset2;
|
||||
char *name_buf, *name_buf2;
|
||||
name_buf = NULL;
|
||||
|
||||
|
||||
while (0 != (len = answer->buf[offset++])) {
|
||||
if (0xC0 == (len & 0xC0)) {
|
||||
if (NULL != name) { /* if we don't need the result, we don't need to recurse, since a ... */
|
||||
offset2 = ((len & ~0xC0) << 8) + answer->buf[offset++];
|
||||
get_domain_name(answer, offset2, &name_buf2);
|
||||
name_buf = (char*) realloc(name_buf, ((NULL != name_buf) ? strlen(name_buf) : 0) + strlen(name_buf2) + 1);
|
||||
strcat(name_buf, name_buf2);
|
||||
free(name_buf2);
|
||||
*name = name_buf;
|
||||
} else {
|
||||
offset++; /* ... a recursion pointer is always two octets long ... */
|
||||
}
|
||||
return offset; /* ... and is always the final part of a name */
|
||||
} else {
|
||||
start = offset;
|
||||
offset += len;
|
||||
if (NULL != name) {
|
||||
i = (NULL != name_buf) ? strlen(name_buf) : 0;
|
||||
name_buf = (char*) realloc(name_buf, i+len+2);
|
||||
strncpy(name_buf + i, answer->buf + start, len);
|
||||
name_buf[i+len] = '\0';
|
||||
strcat(name_buf, ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (NULL != name) {
|
||||
name_buf[strlen(name_buf)-1] = '\0'; /* remove the final dot */
|
||||
*name = name_buf;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int get_mx_rdata(answer_t *answer, int offset, int len, mx_rdata_t **resp) {
|
||||
*resp = (mx_rdata_t*) malloc(sizeof(mx_rdata_t));
|
||||
(*resp)->preference = get16(answer->buf, offset);
|
||||
get_domain_name(answer, offset+2, &(*resp)->exchange);
|
||||
(*resp)->type = T_MX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_mx_rdata(mx_rdata_t *resp) {
|
||||
free(resp->exchange);
|
||||
free(resp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int get_a_rdata(answer_t *answer, int offset, int len, a_rdata_t **resp) {
|
||||
*resp = (a_rdata_t*) malloc(sizeof(a_rdata_t));
|
||||
(*resp)->address = htonl(get32(answer->buf, offset));
|
||||
(*resp)->type = T_A;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_a_rdata(a_rdata_t *resp) {
|
||||
free(resp);
|
||||
}
|
||||
|
||||
static int get_rdata(answer_t *answer, int type, int offset, int len, void **resp) {
|
||||
switch (type) {
|
||||
case T_MX:
|
||||
get_mx_rdata(answer, offset, len, (mx_rdata_t**)resp);
|
||||
break;
|
||||
case T_A:
|
||||
get_a_rdata(answer, offset, len, (a_rdata_t**)resp);
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_ERR, "type %d unsupported\n", type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_rdata(void *resp) {
|
||||
rdata_t *d = (rdata_t*)resp;
|
||||
switch (d->type) {
|
||||
case T_MX:
|
||||
free_mx_rdata(resp);
|
||||
break;
|
||||
case T_A:
|
||||
free_a_rdata(resp);
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_ERR, "type %d unsupported\n", d->type);
|
||||
}
|
||||
}
|
||||
|
||||
void free_rrs(void **resp) {
|
||||
void **rdata;
|
||||
for (rdata = resp; *rdata != NULL; rdata++) {
|
||||
free_rdata(*rdata);
|
||||
}
|
||||
free(resp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void** get_rrs(char *domain, int qtype) {
|
||||
unsigned int res, i, cnt, len, offset, x, y, rdlength;
|
||||
answer_t answer;
|
||||
unsigned int class, type, ttl;
|
||||
char *name;
|
||||
void **rdata, **rdata2;
|
||||
|
||||
|
||||
|
||||
res = res_search(domain, C_IN, qtype, (u_char*) &answer, sizeof(answer_t));
|
||||
if (-1 == res) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
cnt = sizeof(HEADER);
|
||||
|
||||
/* query section */
|
||||
for (y = 0; y < ntohs(answer.hdr.qdcount); y++) {
|
||||
cnt = get_domain_name(&answer, cnt, NULL);
|
||||
type = get16(answer.buf, cnt);
|
||||
cnt += 2;
|
||||
class = get16(answer.buf, cnt);
|
||||
cnt += 2;
|
||||
}
|
||||
|
||||
/* answer section */
|
||||
rdata = (void**)malloc(sizeof(void*) * (ntohs(answer.hdr.ancount)+1));
|
||||
for (y = 0; y < ntohs(answer.hdr.ancount); y++) {
|
||||
cnt = get_domain_name(&answer, cnt, NULL);
|
||||
type = get16(answer.buf, cnt);
|
||||
assert(type==qtype);
|
||||
cnt += 2;
|
||||
class = get16(answer.buf, cnt);
|
||||
cnt += 2;
|
||||
ttl = get32(answer.buf, cnt);
|
||||
cnt += 4;
|
||||
rdlength = get16(answer.buf, cnt);
|
||||
cnt += 2;
|
||||
rdata2 = rdata+y;
|
||||
get_rdata(&answer, type, cnt, rdlength, rdata2);
|
||||
cnt += rdlength;
|
||||
}
|
||||
|
||||
rdata2 = rdata+y;
|
||||
*((void**)rdata2) = NULL;
|
||||
|
||||
return rdata;
|
||||
}
|
||||
|
||||
mx_rdata_t** get_mx_rrs(char *domain) {
|
||||
return (mx_rdata_t**) get_rrs(domain, T_MX);
|
||||
}
|
||||
|
||||
a_rdata_t** get_a_rrs(char *domain) {
|
||||
return (a_rdata_t**) get_rrs(domain, T_A);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#define min(A,B) ((A<B) ? A : B)
|
||||
mx_rdata_t** get_best_mx_rrs(char *domain) {
|
||||
mx_rdata_t **all_mx_rrs, **mx_rdata2, **best_mx_rrs;
|
||||
int min_pref = 10000000;
|
||||
int all_cnt = 0;
|
||||
int best_cnt = 0;
|
||||
int i = 0;
|
||||
|
||||
all_mx_rrs = get_mx_rrs(domain);
|
||||
if (NULL == all_mx_rrs)
|
||||
return NULL;
|
||||
|
||||
|
||||
/* how much are there at all and what is the minimum preference */
|
||||
for (mx_rdata2 = all_mx_rrs; *mx_rdata2 != NULL; mx_rdata2++) {
|
||||
all_cnt++;
|
||||
min_pref = min(min_pref, (*mx_rdata2)->preference);
|
||||
}
|
||||
|
||||
/* how much are there of the minimum preference */
|
||||
for (mx_rdata2 = all_mx_rrs; *mx_rdata2 != NULL; mx_rdata2++)
|
||||
if ((*mx_rdata2)->preference == min_pref)
|
||||
best_cnt++;
|
||||
|
||||
if (all_cnt == best_cnt) {
|
||||
/* all of them are minimum */
|
||||
return all_mx_rrs;
|
||||
} else {
|
||||
/* space for the minimum pref rr's */
|
||||
best_mx_rrs = (mx_rdata_t**) malloc(sizeof(mx_rdata_t*) * (best_cnt+1));
|
||||
for (mx_rdata2 = all_mx_rrs; *mx_rdata2 != NULL; mx_rdata2++) {
|
||||
if ((*mx_rdata2)->preference == min_pref) {
|
||||
/* is a minimum one, keep it */
|
||||
*(best_mx_rrs+i) = *mx_rdata2;
|
||||
i++;
|
||||
} else {
|
||||
/* it's not, free it */
|
||||
free_mx_rdata(*mx_rdata2);
|
||||
}
|
||||
}
|
||||
/* free the old container */
|
||||
free(all_mx_rrs);
|
||||
/* terminate the new container */
|
||||
*((int**)(best_mx_rrs+i)) = NULL;
|
||||
|
||||
return best_mx_rrs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* #define _TEST_MODE_ */
|
||||
#ifdef _TEST_MODE_
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char default_domain[] = "test.de";
|
||||
char *domain = default_domain;
|
||||
mx_rdata_t **mx_rdata, **mx_rdata2;
|
||||
a_rdata_t **a_rdata, **a_rdata2;
|
||||
int a;
|
||||
|
||||
if (argc > 1) {
|
||||
domain = argv[1];
|
||||
}
|
||||
|
||||
printf("before get_mx_rrs: %s\n", domain);
|
||||
|
||||
mx_rdata = get_mx_rrs(domain);
|
||||
for (mx_rdata2 = mx_rdata; *mx_rdata2 != NULL; mx_rdata2++) {
|
||||
printf("preference: %d, exchange: %s\n", (*mx_rdata2)->preference, (*mx_rdata2)->exchange);
|
||||
}
|
||||
|
||||
free_rrs((void**)mx_rdata);
|
||||
|
||||
|
||||
printf("------------------\n");
|
||||
|
||||
mx_rdata = get_best_mx_rrs(domain);
|
||||
|
||||
|
||||
for (mx_rdata2 = mx_rdata; *mx_rdata2 != NULL; mx_rdata2++) {
|
||||
printf("preference: %d, exchange: %s\n", (*mx_rdata2)->preference, (*mx_rdata2)->exchange);
|
||||
|
||||
a_rdata = get_a_rrs((*mx_rdata2)->exchange);
|
||||
for (a_rdata2 = a_rdata; *a_rdata2 != NULL; a_rdata2++) {
|
||||
a = (*a_rdata2)->address;
|
||||
printf("address: %04x\n", a);
|
||||
printf(" %d.%d.%d.%d\n", (a&0xff000000)>>24, (a&0x00ff0000)>>16, (a&0x0000ff00)>>8, a&0x000000ff);
|
||||
}
|
||||
free_rrs((void**)a_rdata);
|
||||
|
||||
}
|
||||
|
||||
free_rrs((void**)mx_rdata);
|
||||
|
||||
/* printf("------------------\n"); */
|
||||
|
||||
/* a_rdata = get_a_rrs("www.microsoft.com.nsatc.net"); */
|
||||
/* for (a_rdata2 = a_rdata; *a_rdata2 != NULL; a_rdata2++) { */
|
||||
/* a = (*a_rdata2)->address; */
|
||||
/* printf("address: %04x\n", a); */
|
||||
/* printf(" %d.%d.%d.%d\n", (a&0xff000000)>>24, (a&0x00ff0000)>>16, (a&0x0000ff00)>>8, a&0x000000ff); */
|
||||
/* } */
|
||||
/* free_rrs((void**)a_rdata); */
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif /* _TEST_MODE_ */
|
||||
|
40
smmapdfw/dns.h
Normal file
40
smmapdfw/dns.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef _DNS_H_
|
||||
#define _DNS_H_
|
||||
|
||||
struct mx_rdata_s {
|
||||
int type;
|
||||
int preference;
|
||||
char *exchange;
|
||||
};
|
||||
|
||||
typedef struct mx_rdata_s mx_rdata_t;
|
||||
|
||||
struct a_rdata_s {
|
||||
int type;
|
||||
unsigned int address;
|
||||
};
|
||||
|
||||
typedef struct a_rdata_s a_rdata_t;
|
||||
|
||||
struct rdata_s {
|
||||
int type;
|
||||
union {
|
||||
mx_rdata_t mx;
|
||||
a_rdata_t a;
|
||||
} data;
|
||||
};
|
||||
|
||||
typedef struct rdata_s rdata_t;
|
||||
|
||||
|
||||
void free_rrs(void **resp);
|
||||
mx_rdata_t** get_mx_rrs(char *domain);
|
||||
mx_rdata_t** get_best_mx_rrs(char *domain);
|
||||
a_rdata_t** get_a_rrs(char *domain);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* _DNS_H_ */
|
||||
|
||||
|
2
smmapdfw/extract_release_id
Executable file
2
smmapdfw/extract_release_id
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
cat smmapd.c | grep '^#define RELEASE_ID' | awk '{print $3}' | sed 's/\"//g'
|
253
smmapdfw/queue.c
Normal file
253
smmapdfw/queue.c
Normal file
@ -0,0 +1,253 @@
|
||||
#include "queue.h"
|
||||
|
||||
#ifdef _TEST_MODE_
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#endif /* _TEST_MODE_ */
|
||||
|
||||
void queue_init(ht_queue_t *q) {
|
||||
q->head = NULL;
|
||||
q->tail = NULL;
|
||||
|
||||
pthread_mutex_init(&q->mutex, NULL);
|
||||
|
||||
pthread_cond_init(&q->cond, NULL);
|
||||
}
|
||||
|
||||
void queue_destroy(ht_queue_t *q) {
|
||||
pthread_mutex_destroy(&q->mutex);
|
||||
pthread_cond_destroy(&q->cond);
|
||||
}
|
||||
|
||||
int queue_put(ht_queue_t *q, void *d) {
|
||||
queue_entry_t *entry;
|
||||
|
||||
entry = (queue_entry_t *) malloc(sizeof(queue_entry_t));
|
||||
if (NULL == entry)
|
||||
return -1;
|
||||
|
||||
entry->data = d;
|
||||
entry->next = NULL;
|
||||
entry->prev = NULL;
|
||||
|
||||
pthread_mutex_lock(&q->mutex);
|
||||
|
||||
if (NULL != q->tail) {
|
||||
entry->next = q->tail;
|
||||
q->tail->prev = entry;
|
||||
}
|
||||
|
||||
if (NULL == q->head) {
|
||||
q->head = entry;
|
||||
}
|
||||
|
||||
q->tail = entry;
|
||||
|
||||
pthread_cond_signal(&q->cond);
|
||||
pthread_mutex_unlock(&q->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *queue_get_wait(ht_queue_t *q) {
|
||||
queue_entry_t *entry = NULL;
|
||||
void *d = NULL;
|
||||
|
||||
pthread_mutex_lock(&q->mutex);
|
||||
|
||||
while ((NULL == q->head) && (NULL == q->tail)) {
|
||||
pthread_cond_wait(&q->cond, &q->mutex);
|
||||
}
|
||||
|
||||
entry = q->head;
|
||||
|
||||
if (NULL != q->head) {
|
||||
q->head = q->head->prev;
|
||||
}
|
||||
|
||||
if (NULL == q->head) {
|
||||
q->tail = NULL;
|
||||
} else {
|
||||
q->head->next = NULL;
|
||||
}
|
||||
|
||||
if (NULL != entry) {
|
||||
d = entry->data;
|
||||
free(entry);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&q->mutex);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _TEST_MODE_
|
||||
|
||||
#define TEST_STRING_1 "test1"
|
||||
#define TEST_STRING_2 "test2"
|
||||
#define TEST_STRING_3 "test2"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
ht_queue_t q;
|
||||
char *s;
|
||||
|
||||
printf("queue test\n");
|
||||
|
||||
printf("\none-item queue: put, get\n");
|
||||
queue_init(&q);
|
||||
|
||||
if (0 != queue_put(&q, (void*)TEST_STRING_1)) {
|
||||
printf("- queue_put failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_put passed\n");
|
||||
|
||||
s = (char*) queue_get_wait(&q);
|
||||
|
||||
if (0 != strcmp(s, TEST_STRING_1)) {
|
||||
printf("- queue_get_wait returned something wrong\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_get_wait for one-item queue passed\n");
|
||||
|
||||
queue_destroy(&q);
|
||||
|
||||
|
||||
printf("\ntwo-item queue: put, put, get, get\n");
|
||||
queue_init(&q);
|
||||
|
||||
if (0 != queue_put(&q, (void*)TEST_STRING_1)) {
|
||||
printf("- queue_put failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_put passed\n");
|
||||
|
||||
if (0 != queue_put(&q, (void*)TEST_STRING_2)) {
|
||||
printf("- queue_put failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_put passed\n");
|
||||
|
||||
s = (char*) queue_get_wait(&q);
|
||||
|
||||
if (0 != strcmp(s, TEST_STRING_1)) {
|
||||
printf("- queue_get_wait returned something wrong\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_get_wait (first) for two-item queue passed\n");
|
||||
|
||||
s = (char*) queue_get_wait(&q);
|
||||
|
||||
if (0 != strcmp(s, TEST_STRING_2)) {
|
||||
printf("- queue_get_wait returned something wrong\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_get_wait (second) for two-item queue passed\n");
|
||||
|
||||
queue_destroy(&q);
|
||||
|
||||
|
||||
printf("\nthree-item queue: put, put, put, get, get, get\n");
|
||||
queue_init(&q);
|
||||
|
||||
if (0 != queue_put(&q, (void*)TEST_STRING_1)) {
|
||||
printf("- queue_put failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_put passed\n");
|
||||
|
||||
if (0 != queue_put(&q, (void*)TEST_STRING_2)) {
|
||||
printf("- queue_put failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_put passed\n");
|
||||
|
||||
if (0 != queue_put(&q, (void*)TEST_STRING_3)) {
|
||||
printf("- queue_put failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_put passed\n");
|
||||
|
||||
s = (char*) queue_get_wait(&q);
|
||||
|
||||
if (0 != strcmp(s, TEST_STRING_1)) {
|
||||
printf("- queue_get_wait returned something wrong\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_get_wait (first) for three-item queue passed\n");
|
||||
|
||||
s = (char*) queue_get_wait(&q);
|
||||
|
||||
if (0 != strcmp(s, TEST_STRING_2)) {
|
||||
printf("- queue_get_wait returned something wrong\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_get_wait (second) for three-item queue passed\n");
|
||||
|
||||
s = (char*) queue_get_wait(&q);
|
||||
|
||||
if (0 != strcmp(s, TEST_STRING_3)) {
|
||||
printf("- queue_get_wait returned something wrong\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_get_wait (third) for three-item queue passed\n");
|
||||
|
||||
queue_destroy(&q);
|
||||
|
||||
|
||||
printf("\nthree-item queue, different sequence: put, get, put, put, get, get\n");
|
||||
queue_init(&q);
|
||||
|
||||
if (0 != queue_put(&q, (void*)TEST_STRING_1)) {
|
||||
printf("- queue_put failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_put passed\n");
|
||||
|
||||
s = (char*) queue_get_wait(&q);
|
||||
|
||||
if (0 != strcmp(s, TEST_STRING_1)) {
|
||||
printf("- queue_get_wait returned something wrong\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_get_wait (first) for three-item queue passed\n");
|
||||
|
||||
if (0 != queue_put(&q, (void*)TEST_STRING_2)) {
|
||||
printf("- queue_put failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_put passed\n");
|
||||
|
||||
if (0 != queue_put(&q, (void*)TEST_STRING_3)) {
|
||||
printf("- queue_put failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_put passed\n");
|
||||
|
||||
s = (char*) queue_get_wait(&q);
|
||||
|
||||
if (0 != strcmp(s, TEST_STRING_2)) {
|
||||
printf("- queue_get_wait returned something wrong\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_get_wait (second) for three-item queue passed\n");
|
||||
|
||||
s = (char*) queue_get_wait(&q);
|
||||
|
||||
if (0 != strcmp(s, TEST_STRING_3)) {
|
||||
printf("- queue_get_wait returned something wrong\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("+ queue_get_wait (third) for three-item queue passed\n");
|
||||
|
||||
queue_destroy(&q);
|
||||
|
||||
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
#endif /* _TEST_MODE_ */
|
31
smmapdfw/queue.h
Normal file
31
smmapdfw/queue.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef _QUEUE_H_
|
||||
#define _QUEUE_H_
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
struct queue_entry_s {
|
||||
void *data;
|
||||
struct queue_entry_s *next;
|
||||
struct queue_entry_s *prev;
|
||||
};
|
||||
|
||||
typedef struct queue_entry_s queue_entry_t;
|
||||
|
||||
struct queue_s {
|
||||
queue_entry_t *tail;
|
||||
queue_entry_t *head;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
};
|
||||
|
||||
typedef struct queue_s ht_queue_t;
|
||||
|
||||
void queue_init(ht_queue_t *q);
|
||||
void queue_destroy(ht_queue_t *q);
|
||||
int queue_put(ht_queue_t *q, void *d);
|
||||
void *queue_get_wait(ht_queue_t *q);
|
||||
|
||||
|
||||
#endif /* _QUEUE_H_ */
|
||||
|
||||
|
46
smmapdfw/safe_write.c
Normal file
46
smmapdfw/safe_write.c
Normal file
@ -0,0 +1,46 @@
|
||||
#include <syslog.h>
|
||||
#include <sys/select.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "safe_write.h"
|
||||
|
||||
|
||||
ssize_t safe_write(int fd, const void *buf, size_t count) {
|
||||
int res;
|
||||
fd_set rdfs, wrfs;
|
||||
struct timeval tv = {0, 0};
|
||||
char read_buf[_SAFE_WRITE_READ_BUF_SIZE+1];
|
||||
|
||||
FD_ZERO(&rdfs);
|
||||
FD_ZERO(&wrfs);
|
||||
FD_SET(fd, &rdfs);
|
||||
FD_SET(fd, &wrfs);
|
||||
|
||||
while (1) {
|
||||
res = select(fd+1, &rdfs, &wrfs, NULL, &tv);
|
||||
if (-1 == res) {
|
||||
syslog(LOG_DEBUG, "_safe_write: error in select: %d, %s", errno, strerror(errno));
|
||||
return -1;
|
||||
} else {
|
||||
if (FD_ISSET(fd, &rdfs)) {
|
||||
/* set to read: something is wrong */
|
||||
syslog(LOG_DEBUG, "_safe_write, set to read");
|
||||
res = read(fd, read_buf, _SAFE_WRITE_READ_BUF_SIZE);
|
||||
syslog(LOG_DEBUG, "_safe_write, read res: %d", res);
|
||||
if (0 == res) {
|
||||
syslog(LOG_DEBUG, "_safe_write, read returns 0, socket probably closed");
|
||||
return -1;
|
||||
}
|
||||
} else if (FD_ISSET(fd, &wrfs)) {
|
||||
/* set to write */
|
||||
syslog(LOG_DEBUG, "_safe_write, set to write");
|
||||
res = write(fd, buf, count);
|
||||
return res;
|
||||
} else {
|
||||
syslog(LOG_DEBUG, "_safe_write: whether read nor write ready, strange");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
smmapdfw/safe_write.h
Normal file
17
smmapdfw/safe_write.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef _SAFE_WRITE_H_
|
||||
#define _SAFE_WRITE_H_
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define _SAFE_WRITE_READ_BUF_SIZE 256
|
||||
|
||||
|
||||
/* _safe_write avoids SIGPIPE when writing but it skips a part of an possible
|
||||
in between input. This is safe here, since no pipelining is supported.
|
||||
*/
|
||||
ssize_t safe_write(int fd, const void *buf, size_t count);
|
||||
|
||||
#endif /* _SAFE_WRITE_H_ */
|
||||
|
||||
|
336
smmapdfw/smmapd.c
Normal file
336
smmapdfw/smmapd.c
Normal file
@ -0,0 +1,336 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "count.h"
|
||||
#include "containers.h"
|
||||
#include "config.h"
|
||||
#include "safe_write.h"
|
||||
|
||||
#define _SMMAPD_C_
|
||||
#include "smmapd.h"
|
||||
|
||||
|
||||
#ifdef SUNOS
|
||||
#include "sunos_comp.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define CFG_SECTION_GLOBAL "global"
|
||||
#define CFG_NAME_ADDRESS "address"
|
||||
#define CFG_NAME_PORT "port"
|
||||
|
||||
|
||||
#define DEFAULT_PORT "8888"
|
||||
|
||||
#define CVS_ID "$Id$"
|
||||
#define RELEASE_ID "0.9"
|
||||
|
||||
|
||||
struct networkerThread_s {
|
||||
int result;
|
||||
struct sockaddr_in clientAddr;
|
||||
socklen_t clientAddrLen;
|
||||
int clientSock;
|
||||
pthread_t pthread;
|
||||
};
|
||||
|
||||
typedef struct networkerThread_s networkerThread_t;
|
||||
|
||||
|
||||
ht_queue_t terminated_networker_queue;
|
||||
pthread_t cleanerThread;
|
||||
count_t thread_counter;
|
||||
|
||||
cfg_t *cfg;
|
||||
|
||||
|
||||
void * cleaner(void * arg) {
|
||||
networkerThread_t *t;
|
||||
int joinRes;
|
||||
|
||||
pthread_detach(pthread_self());
|
||||
|
||||
while (1) {
|
||||
syslog(LOG_DEBUG, "cleaner: Waiting for terminated networker, running threads: %d",
|
||||
count_get(&thread_counter));
|
||||
t = (networkerThread_t*) queue_get_wait(&terminated_networker_queue);
|
||||
count_dec(&thread_counter);
|
||||
|
||||
syslog(LOG_DEBUG, "cleaner: Networker serving %d (address: %s, port: %d), result %d",
|
||||
t->clientSock, inet_ntoa(t->clientAddr.sin_addr),
|
||||
ntohs(t->clientAddr.sin_port), t->result);
|
||||
|
||||
joinRes = pthread_join(t->pthread, NULL);
|
||||
syslog(LOG_DEBUG, "cleaner: joinRes %d", joinRes);
|
||||
|
||||
free(t);
|
||||
t = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#define OUTPUT_BUFSIZE ANSWER_BUFSIZE+25 /* a bit more than answer for the result text */
|
||||
void * networker(void * arg) {
|
||||
|
||||
char buffer[BUFSIZE+1];
|
||||
char *input;
|
||||
char answer[ANSWER_BUFSIZE+1];
|
||||
char output[OUTPUT_BUFSIZE+1];
|
||||
const char *answer_ptr;
|
||||
const char *result_text;
|
||||
int err, cnt, i = 0;
|
||||
unsigned int len;
|
||||
int dispatcher_result;
|
||||
container_handle_t *container_handle;
|
||||
|
||||
|
||||
networkerThread_t * thread = (networkerThread_t *) arg;
|
||||
thread->pthread = pthread_self();
|
||||
|
||||
syslog(LOG_DEBUG, "networker: serving %d (address: %s, port: %d) started",
|
||||
thread->clientSock, inet_ntoa(thread->clientAddr.sin_addr),
|
||||
ntohs(thread->clientAddr.sin_port));
|
||||
|
||||
containers_setup(&container_handle);
|
||||
syslog(LOG_DEBUG, "networker: got a container handle");
|
||||
|
||||
|
||||
while(0 != (cnt = read(thread->clientSock, buffer, BUFSIZE))) {
|
||||
/* temporary, until netstring is introduced */
|
||||
if ((buffer[cnt-1] == 10) && (buffer[cnt-2] == 13)) /* ^M */
|
||||
buffer[cnt-2] = '\0';
|
||||
else
|
||||
buffer[cnt] = '\0';
|
||||
|
||||
len = strtol(buffer, &input, 10);
|
||||
if ((0 == len) && (buffer == input)) {
|
||||
syslog(LOG_DEBUG, "networker: netstring unparsable, no length found: %s", buffer);
|
||||
dispatcher_result = SMM_NETSTRING_UNPARSABLE;
|
||||
} else if (*input++ != ':') {
|
||||
syslog(LOG_DEBUG, "networker: netstring unparsable, no colon found: %s", buffer);
|
||||
dispatcher_result = SMM_NETSTRING_UNPARSABLE;
|
||||
} else if (strlen(input)-1 != len) {
|
||||
syslog(LOG_DEBUG, "networker: netstring unparsable, length does not match: %s", buffer);
|
||||
dispatcher_result = SMM_NETSTRING_UNPARSABLE;
|
||||
} else if (input[strlen(input)-1] != ',') {
|
||||
syslog(LOG_DEBUG, "networker: netstring unparsable, no terminating comma: %s", buffer);
|
||||
dispatcher_result = SMM_NETSTRING_UNPARSABLE;
|
||||
} else {
|
||||
input[strlen(input)-1] = '\0'; /* strip off the comma */
|
||||
|
||||
answer[0] = '\0';
|
||||
dispatcher_result = containers_dispatcher(container_handle, input, answer);
|
||||
syslog(LOG_DEBUG, "networker: dispatcher result: %d, answer: %s", dispatcher_result, answer);
|
||||
}
|
||||
|
||||
result_text = (dispatcher_result > SMM_MAX_NUM) ?
|
||||
T_SMM_RESULTS[0] :
|
||||
T_SMM_RESULTS[dispatcher_result];
|
||||
|
||||
answer_ptr = (dispatcher_result > SMM_MAX_NUM) ?
|
||||
T_SMM_RESULT_INFOS[0] :
|
||||
T_SMM_RESULT_INFOS[dispatcher_result];
|
||||
|
||||
if (NULL == answer_ptr)
|
||||
answer_ptr = answer;
|
||||
|
||||
snprintf(output, OUTPUT_BUFSIZE, "%d:%s%s%s,",
|
||||
strlen(result_text) + strlen(answer_ptr) + ((strlen(answer_ptr) > 0) ? 1 : 0),
|
||||
result_text,
|
||||
(strlen(answer_ptr) > 0) ? " " : "",
|
||||
answer_ptr);
|
||||
|
||||
safe_write(thread->clientSock, output, strlen(output));
|
||||
}
|
||||
|
||||
close(thread->clientSock);
|
||||
|
||||
containers_destroy(container_handle);
|
||||
syslog(LOG_DEBUG, "networker: destroyed the container handle");
|
||||
|
||||
|
||||
thread->result = 1;
|
||||
queue_put(&terminated_networker_queue, thread);
|
||||
}
|
||||
|
||||
|
||||
int server() {
|
||||
int serverSock;
|
||||
int res;
|
||||
struct sockaddr_in servAddr;
|
||||
struct sockaddr_in clientAddr;
|
||||
socklen_t clientAddrLen = sizeof(clientAddr);
|
||||
int clientSock;
|
||||
networkerThread_t *thread;
|
||||
pthread_t tid;
|
||||
int optval = 1;
|
||||
|
||||
char *cfg_address, *cfg_port;
|
||||
|
||||
serverSock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (-1 == serverSock) {
|
||||
syslog(LOG_ERR, "server: failure when creating server socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = setsockopt(serverSock, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval));
|
||||
if (-1 == res) {
|
||||
syslog(LOG_ERR, "server: failure when setting socket options");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
memset(&servAddr, 0, sizeof(servAddr));
|
||||
servAddr.sin_family = AF_INET;
|
||||
|
||||
cfg_address = findcfg(cfg, CFG_SECTION_GLOBAL, CFG_NAME_ADDRESS);
|
||||
servAddr.sin_addr.s_addr = (NULL == cfg_address) ? htonl(INADDR_ANY) : inet_addr(cfg_address);
|
||||
|
||||
cfg_port = findcfgx(cfg, CFG_SECTION_GLOBAL, CFG_NAME_PORT, DEFAULT_PORT);
|
||||
servAddr.sin_port = htons(atoi(cfg_port));
|
||||
|
||||
res = bind(serverSock, (struct sockaddr *) &servAddr, sizeof(servAddr));
|
||||
if (-1 == res) {
|
||||
syslog(LOG_ERR, "server: failure when binding on server socket, errno: %d, errtxt: %s",
|
||||
errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = listen(serverSock, 32);
|
||||
if (-1 == res) {
|
||||
syslog(LOG_ERR, "server: failure when listening on server socket, errno: %d, errtxt: %s",
|
||||
errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
count_init(&thread_counter);
|
||||
queue_init(&terminated_networker_queue);
|
||||
pthread_create(&cleanerThread, NULL, &cleaner, NULL);
|
||||
|
||||
|
||||
while (1) {
|
||||
syslog(LOG_DEBUG, "server: Waiting for connection");
|
||||
|
||||
clientSock = accept(serverSock, (struct sockaddr *) &clientAddr, &clientAddrLen);
|
||||
syslog(LOG_DEBUG, "server: Got a connection %d", clientSock);
|
||||
|
||||
if (-1 == clientSock) {
|
||||
syslog(LOG_ERR, "server: failure when accepting connection");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
thread = (networkerThread_t *) malloc(sizeof(networkerThread_t)+10);
|
||||
if (NULL == thread) {
|
||||
syslog(LOG_ERR, "server: unable to alloc memory for networker");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
thread->clientSock = clientSock;
|
||||
thread->result = 0;
|
||||
thread->clientAddr = clientAddr;
|
||||
thread->clientAddrLen = clientAddrLen;
|
||||
|
||||
count_inc(&thread_counter);
|
||||
res = pthread_create(&tid, NULL, &networker, thread);
|
||||
if (0 != res) {
|
||||
syslog(LOG_ERR, "server: unable to start networker thread");
|
||||
free(thread);
|
||||
count_dec(&thread_counter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usage() {
|
||||
printf("smmapd [-F] [-p pidfile] [-f cfgfile] [-h]\n");
|
||||
printf(" -F run in foreground, overwrite smmapd.ini setting\n");
|
||||
printf(" -p pidfile, overwrite smmapd.ini setting\n");
|
||||
printf(" -f cfgfile\n");
|
||||
printf(" -v version and release info\n");
|
||||
printf(" -h print this help\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#define DEFAULT_SMMAPD_INI "smmapd.ini"
|
||||
|
||||
int err, pid, do_fork, foreground=0, c;
|
||||
char *pid_file=NULL, *cfg_file=NULL;
|
||||
|
||||
FILE *f;
|
||||
|
||||
while ((c = getopt(argc, argv,"Fp:f:hv")) != -1)
|
||||
switch (c) {
|
||||
case 'F':
|
||||
foreground = 1;
|
||||
break;
|
||||
case 'f':
|
||||
cfg_file = strdup(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
pid_file = strdup(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
printf("\nsmmapd - Wolfgang Hottgenroth <woho@hottis.de>\n");
|
||||
printf(" cvs_id: %s\n", CVS_ID);
|
||||
printf(" release_id: %s\n\n", RELEASE_ID);
|
||||
exit(1);
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
openlog("smmapd", LOG_PID, LOG_LOCAL2);
|
||||
syslog(LOG_INFO, "main: started");
|
||||
cfg = readcfg((cfg_file == NULL) ? DEFAULT_SMMAPD_INI : cfg_file);
|
||||
if (0 != (err = register_all())) {
|
||||
syslog(LOG_ERR, "main: register_all fails: %d", err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
do_fork = atoi(findcfgx(cfg, "global", "do_fork", "1"));
|
||||
pid = ((1 == do_fork) && (0 == foreground)) ? fork() : 0;
|
||||
|
||||
if (-1 == pid) {
|
||||
syslog(LOG_ERR, "Error when forking smmapd: %d, %s", errno, strerror(errno));
|
||||
exit(1);
|
||||
} else if (0 != pid) {
|
||||
syslog(LOG_INFO, "smmapd successfully forked, child's pid is %d", pid);
|
||||
if (NULL == pid_file)
|
||||
pid_file = findcfgx(cfg, "global", "pid_file", "smmapd.pid");
|
||||
if ((f = fopen(pid_file, "w")) == NULL) {
|
||||
syslog(LOG_ERR, "unable to open pid_file %s for pid %d", pid_file, pid);
|
||||
} else {
|
||||
fprintf(f, "%d", pid);
|
||||
fclose(f);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
if (0 != (err = server())) {
|
||||
syslog(LOG_ERR, "main: server fails: %d", err);
|
||||
exit(1);
|
||||
}
|
||||
syslog(LOG_INFO, "main: finished");
|
||||
closelog();
|
||||
}
|
49
smmapdfw/smmapd.h
Normal file
49
smmapdfw/smmapd.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef _SMMAPD_H_
|
||||
#define _SMMAPD_H_
|
||||
|
||||
|
||||
#define SMM_OK 1
|
||||
#define SMM_TEMP_NOK 2
|
||||
#define SMM_PERM_NOK 3
|
||||
#define SMM_NOT_FOUND_NOK 4
|
||||
#define SMM_ILLEGAL_INPUT 5
|
||||
#define SMM_UNKNOWN_CLASS 6
|
||||
#define SMM_TIMEOUT_NOK 7
|
||||
#define SMM_NETSTRING_UNPARSABLE 8
|
||||
#define SMM_MAX_NUM 9
|
||||
|
||||
#ifdef _SMMAPD_C_
|
||||
const char *T_SMM_RESULTS[] = {
|
||||
"PERM",
|
||||
"OK",
|
||||
"TEMP",
|
||||
"PERM",
|
||||
"NOTFOUND",
|
||||
"PERM",
|
||||
"PERM",
|
||||
"TIMEOUT",
|
||||
"PERM"
|
||||
};
|
||||
|
||||
const char *T_SMM_RESULT_INFOS[] = {
|
||||
"unsupported result code",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
"illegal input",
|
||||
"unknown class",
|
||||
NULL,
|
||||
"netstring unparsable"
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define BUFSIZE 2048
|
||||
#define ANSWER_BUFSIZE BUFSIZE
|
||||
|
||||
#endif /* _SMMAPD_H_ */
|
||||
|
||||
|
32
smmapdfw/smmapd.ini
Normal file
32
smmapdfw/smmapd.ini
Normal file
@ -0,0 +1,32 @@
|
||||
[global]
|
||||
do_fork = 1
|
||||
pid_file = smmapd.pid
|
||||
address = 127.0.0.1
|
||||
port = 8887
|
||||
plugin_dir = /home/who/Sources/private/smmapd
|
||||
plugins = test_worker1 test_worker2 verifier cyruscheck
|
||||
|
||||
[test_worker1]
|
||||
obj = test_workers.so
|
||||
text = test worker1 handle text
|
||||
|
||||
[test_worker2]
|
||||
obj = test_workers.so
|
||||
|
||||
[verifier]
|
||||
obj = verify_worker.so
|
||||
timeout_result = 5
|
||||
timeout_dialog = 20
|
||||
cache_enabled = 1
|
||||
cache_expiry = 86400
|
||||
sender_address = <>
|
||||
ehlo_arg = local
|
||||
smtp_port = 25
|
||||
max_checker_threads = 100
|
||||
|
||||
[cyruscheck]
|
||||
obj = cyrus_worker.so
|
||||
timeout = 10
|
||||
sender_address = <testsender>
|
||||
lhlo_arg = local
|
||||
lmtp_port = 24
|
404
smmapdfw/smtp.c
Normal file
404
smmapdfw/smtp.c
Normal file
@ -0,0 +1,404 @@
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <syslog.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "smtp.h"
|
||||
|
||||
|
||||
|
||||
#define BUFSIZE 1024
|
||||
#define BUFSIZE2 1024
|
||||
|
||||
|
||||
|
||||
smtp_t *smtp_init(unsigned int address, int port, int timeout) {
|
||||
smtp_t *handle = (smtp_t*) malloc(sizeof(smtp_t));
|
||||
|
||||
handle->address = address;
|
||||
handle->port = port;
|
||||
handle->timeout = timeout;
|
||||
handle->response_text = NULL;
|
||||
handle->response_code = 0;
|
||||
handle->got_timeout = 0;
|
||||
|
||||
handle->buffer = (char*) malloc(BUFSIZE+5);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
smtp_t *smtp_init2(char *host_address, int port, int timeout) {
|
||||
unsigned int a = inet_addr(host_address);
|
||||
if (-1 == a) {
|
||||
syslog(LOG_DEBUG, "smtp_init2: invalid host_address %s", host_address);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return smtp_init(a, port, timeout);
|
||||
}
|
||||
|
||||
void smtp_destroy(smtp_t *handle) {
|
||||
free(handle->buffer);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
static int _smtp_strip_crlf(char *s) {
|
||||
char *c;
|
||||
int cnt = 0;
|
||||
|
||||
if ((10 == s[strlen(s)-1]) && (13 == s[strlen(s)-2]))
|
||||
s[strlen(s)-2] = '\0';
|
||||
|
||||
/* for (c = s; *c != '\0'; c++) { */
|
||||
/* if ((10 == *c) || (13 == *c)) { */
|
||||
/* *c = 'X'; */
|
||||
/* cnt++; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
static int _smtp_read(smtp_t *handle) {
|
||||
int res;
|
||||
fd_set rdfs;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&rdfs);
|
||||
FD_SET(handle->socket, &rdfs);
|
||||
tv.tv_sec = handle->timeout;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
res = select(handle->socket+1, &rdfs, NULL, NULL, &tv);
|
||||
if (0 == res) {
|
||||
syslog(LOG_DEBUG, "_smtp_read: timeout in select");
|
||||
handle->got_timeout = 1;
|
||||
return SMTP_TIMEOUT;
|
||||
} else if (-1 == res) {
|
||||
syslog(LOG_DEBUG, "_smtp_read: error in select, errno: %d (%s)", errno, strerror(errno));
|
||||
return -1;
|
||||
} else {
|
||||
if (FD_ISSET(handle->socket, &rdfs)) {
|
||||
res = read(handle->socket, handle->buffer, BUFSIZE);
|
||||
if (-1 == res) {
|
||||
syslog(LOG_DEBUG, "read error: %d, %s\n", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
handle->buffer[res] = '\0';
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _smtp_write(smtp_t *handle) {
|
||||
int res;
|
||||
fd_set wrfs;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&wrfs);
|
||||
FD_SET(handle->socket, &wrfs);
|
||||
tv.tv_sec = handle->timeout;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
res = select(handle->socket+1, NULL, &wrfs, NULL, &tv);
|
||||
if (0 == res) {
|
||||
syslog(LOG_DEBUG, "_smtp_write: timeout in select");
|
||||
handle->got_timeout = 1;
|
||||
return SMTP_TIMEOUT;
|
||||
} else if (-1 == res) {
|
||||
syslog(LOG_DEBUG, "_smtp_write: error in select, errno: %d (%s)", errno, strerror(errno));
|
||||
return -1;
|
||||
} else {
|
||||
if (FD_ISSET(handle->socket, &wrfs)) {
|
||||
if (-1 == write(handle->socket, handle->buffer, strlen(handle->buffer))) {
|
||||
syslog(LOG_DEBUG, "read error: %d, %s\n", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _smtp_parse_response(smtp_t *handle) {
|
||||
char *first_space;
|
||||
char *last_crlf;
|
||||
|
||||
int l;
|
||||
|
||||
_smtp_strip_crlf(handle->buffer);
|
||||
|
||||
|
||||
if (NULL == (last_crlf = strrchr(handle->buffer, 10)))
|
||||
last_crlf = handle->buffer;
|
||||
|
||||
if (NULL == (first_space = strchr(last_crlf, ' ')))
|
||||
return -1;
|
||||
|
||||
*first_space = '\0';
|
||||
handle->response_code = atoi(last_crlf);
|
||||
|
||||
*first_space = ' ';
|
||||
handle->response_text = handle->buffer;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smtp_response(smtp_t *handle, char **response) {
|
||||
if (handle->got_timeout) {
|
||||
handle->got_timeout = 0;
|
||||
return SMTP_TIMEOUT;
|
||||
}
|
||||
|
||||
if (0 != _smtp_parse_response(handle)) {
|
||||
syslog(LOG_DEBUG, "smtp_response: failed to parse response\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NULL != response)
|
||||
*response = handle->response_text;
|
||||
return handle->response_code;
|
||||
}
|
||||
|
||||
int smtp_command(smtp_t *handle, char *command, char *arg) {
|
||||
int err;
|
||||
|
||||
*handle->buffer = '\0';
|
||||
strcat(handle->buffer, command);
|
||||
if (NULL != arg) {
|
||||
/* strcat(handle->buffer, " "); */
|
||||
strcat(handle->buffer, arg);
|
||||
}
|
||||
strcat(handle->buffer, "\r\n");
|
||||
|
||||
if (0 != (err = _smtp_write(handle))) {
|
||||
syslog(LOG_DEBUG, "smtp_command: failed to send %s\n", handle->buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (0 != (err = _smtp_read(handle))) {
|
||||
syslog(LOG_DEBUG, "smtp_command: failed to read response\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smtp_connect(smtp_t *handle) {
|
||||
int err;
|
||||
int c;
|
||||
int res;
|
||||
fd_set wrfs;
|
||||
struct timeval tv;
|
||||
|
||||
|
||||
handle->socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (-1 == handle->socket) {
|
||||
syslog(LOG_DEBUG, "smtp_connect: unable to create socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
c = fcntl(handle->socket, F_GETFL);
|
||||
if (-1 == c) {
|
||||
syslog(LOG_DEBUG, "smtp_connect: unable to get flags, errno: %d (%s)", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = fcntl(handle->socket, F_SETFL, c | O_NONBLOCK);
|
||||
if (-1 == err) {
|
||||
syslog(LOG_DEBUG, "smtp_connect: unable to set O_NONBLOCK flag, errno: %d (%s)", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
memset(&handle->addr, 0, sizeof(handle->addr));
|
||||
handle->addr.sin_addr.s_addr = handle->address;
|
||||
|
||||
handle->addr.sin_port = htons(handle->port);
|
||||
handle->addr.sin_family = AF_INET;
|
||||
|
||||
c = connect(handle->socket, (struct sockaddr *) &handle->addr, sizeof(handle->addr));
|
||||
if (-1 == c) {
|
||||
if (EINPROGRESS == errno) {
|
||||
FD_ZERO(&wrfs);
|
||||
FD_SET(handle->socket, &wrfs);
|
||||
tv.tv_sec = handle->timeout;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
res = select(handle->socket+1, NULL, &wrfs, NULL, &tv);
|
||||
if (0 == res) {
|
||||
syslog(LOG_DEBUG, "smtp_connect: timeout in select");
|
||||
handle->got_timeout = 1;
|
||||
return SMTP_TIMEOUT;
|
||||
} else if (-1 == res) {
|
||||
syslog(LOG_DEBUG, "smtp_connect: error in select, errno: %d (%s)", errno, strerror(errno));
|
||||
return -1;
|
||||
} else {
|
||||
if (FD_ISSET(handle->socket, &wrfs)) {
|
||||
// debug("smtp_connect: here we go");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
syslog(LOG_DEBUG, "smtp_connect: unable to connect to socket, errno: %d (%s)", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
err = _smtp_read(handle);
|
||||
if (0 != err)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smtp_helo(smtp_t *handle, char *arg) {
|
||||
return smtp_command(handle, "helo ", arg);
|
||||
}
|
||||
|
||||
int smtp_ehlo(smtp_t *handle, char *arg) {
|
||||
return smtp_command(handle, "ehlo ", arg);
|
||||
}
|
||||
|
||||
int smtp_lhlo(smtp_t *handle, char *arg) {
|
||||
return smtp_command(handle, "lhlo ", arg);
|
||||
}
|
||||
|
||||
int smtp_mailfrom(smtp_t *handle, char *arg) {
|
||||
return smtp_command(handle, "mail from:", arg);
|
||||
}
|
||||
|
||||
int smtp_rcptto(smtp_t *handle, char *arg) {
|
||||
return smtp_command(handle, "rcpt to:", arg);
|
||||
}
|
||||
|
||||
int smtp_data(smtp_t *handle) {
|
||||
return smtp_command(handle, "data", NULL);
|
||||
}
|
||||
|
||||
|
||||
int smtp_datasend(smtp_t *handle, char *data) {
|
||||
char *d = data;
|
||||
int cnt;
|
||||
int ssize;
|
||||
|
||||
while (d < data + strlen(data)) {
|
||||
ssize = (strlen(d) < BUFSIZE2) ? strlen(d) : BUFSIZE2;
|
||||
if (-1 == write(handle->socket, d, ssize))
|
||||
return -1;
|
||||
d += BUFSIZE2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smtp_dataend(smtp_t *handle) {
|
||||
return smtp_command(handle, "\r\n.", NULL);
|
||||
}
|
||||
|
||||
int smtp_rset(smtp_t *handle) {
|
||||
return smtp_command(handle, "rset", NULL);
|
||||
}
|
||||
|
||||
int smtp_quit(smtp_t *handle) {
|
||||
return smtp_command(handle, "quit", NULL);
|
||||
}
|
||||
|
||||
int smtp_close(smtp_t *handle) {
|
||||
close(handle->socket);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _TEST_MODE_
|
||||
int main() {
|
||||
int res;
|
||||
char *res_text;
|
||||
char data[] = "Hallo Wolfgang\r\n"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"abaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaadaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaafaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaagaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaahaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaajaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaakaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaamaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaaanaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaaaaoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaaaaapaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaaaaaaqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaaaaaaaraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaaaaaaaasaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaaaaaaaaataaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaaaaaaaaaauaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaaaaaaaaaaavaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaaaaaaaaaaaawaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
||||
"\r\n"
|
||||
"dies ist eine Testmail.\r\n"
|
||||
"\r\n";
|
||||
|
||||
|
||||
smtp_t *h = smtp_init2("213.70.191.212", 25, 10);
|
||||
printf("got handle\n");
|
||||
|
||||
res = smtp_connect(h);
|
||||
printf("connected: %d\n", res);
|
||||
|
||||
res = smtp_response(h, &res_text);
|
||||
printf("response: %d, %s\n", res, res_text);
|
||||
|
||||
res = smtp_helo(h, "local");
|
||||
printf("ehlo: %d\n", res);
|
||||
|
||||
res = smtp_response(h, &res_text);
|
||||
printf("response: %d, %s\n", res, res_text);
|
||||
|
||||
res = smtp_mailfrom(h, "<xyx@test.de>");
|
||||
printf("mail from: %d\n", res);
|
||||
|
||||
res = smtp_response(h, &res_text);
|
||||
printf("response: %d, %s\n", res, res_text);
|
||||
|
||||
res = smtp_rcptto(h, "<wn@kja-essen.de>");
|
||||
printf("rcpt to: %d\n", res);
|
||||
|
||||
res = smtp_response(h, &res_text);
|
||||
printf("response: %d, %s\n", res, res_text);
|
||||
|
||||
res = smtp_data(h);
|
||||
printf("data: %d\n", res);
|
||||
|
||||
res = smtp_response(h, &res_text);
|
||||
printf("response: %d, %s\n", res, res_text);
|
||||
|
||||
res = smtp_datasend(h, data);
|
||||
printf("datasend: %d\n", res);
|
||||
|
||||
res = smtp_dataend(h);
|
||||
printf("dataend: %d\n", res);
|
||||
|
||||
res = smtp_response(h, &res_text);
|
||||
printf("response: %d, %s\n", res, res_text);
|
||||
|
||||
|
||||
smtp_close(h);
|
||||
smtp_destroy(h);
|
||||
}
|
||||
|
||||
#endif /* _TEST_MODE_ */
|
||||
|
52
smmapdfw/smtp.h
Normal file
52
smmapdfw/smtp.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef _SMTP_H_
|
||||
#define _SMTP_H_
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
struct smtp_s {
|
||||
/* char *host_address; /\* IP address, no hostname!! *\/ */
|
||||
unsigned int address;
|
||||
int port;
|
||||
int timeout;
|
||||
|
||||
char *buffer;
|
||||
|
||||
char *response_text;
|
||||
int response_code;
|
||||
int got_timeout;
|
||||
|
||||
int socket;
|
||||
struct sockaddr_in addr;
|
||||
};
|
||||
|
||||
typedef struct smtp_s smtp_t;
|
||||
|
||||
|
||||
#define SMTP_TIMEOUT -99
|
||||
|
||||
smtp_t *smtp_init2(char *host_address, int port, int timeout);
|
||||
smtp_t *smtp_init(unsigned int address, int port, int timeout);
|
||||
void smtp_destroy(smtp_t *handle);
|
||||
|
||||
int smtp_response(smtp_t *handle, char **response);
|
||||
int smtp_command(smtp_t *handle, char *command, char *arg);
|
||||
|
||||
int smtp_connect(smtp_t *handle);
|
||||
int smtp_close(smtp_t *handle);
|
||||
|
||||
int smtp_helo(smtp_t *handle, char *arg);
|
||||
int smtp_ehlo(smtp_t *handle, char *arg);
|
||||
int smtp_lhlo(smtp_t *handle, char *arg);
|
||||
int smtp_mailfrom(smtp_t *handle, char *arg);
|
||||
int smtp_rcptto(smtp_t *handle, char *arg);
|
||||
int smtp_data(smtp_t *handle);
|
||||
int smtp_datasend(smtp_t *handle, char *data);
|
||||
int smtp_dataend(smtp_t *handle);
|
||||
int smtp_rset(smtp_t *handle);
|
||||
int smtp_quit(smtp_t *handle);
|
||||
|
||||
|
||||
|
||||
#endif /* _SMTP_H_ */
|
||||
|
||||
|
10
smmapdfw/sunos_comp.h
Normal file
10
smmapdfw/sunos_comp.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef _SUNOS_COMP_H_
|
||||
#define _SUNOS_COMP_H_
|
||||
|
||||
#define socklen_t size_t
|
||||
#define isblank(A) ((A==' ') || (A=='\t'))
|
||||
|
||||
|
||||
#endif /* _SUNOS_COMP_H_ */
|
||||
|
||||
|
69
smmapdfw/test_workers.c
Normal file
69
smmapdfw/test_workers.c
Normal file
@ -0,0 +1,69 @@
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
#include "containers_public.h"
|
||||
|
||||
int test_worker1_init(cfgl_t *cfg, void **handle);
|
||||
int test_worker1_setup(void *handle, void **work_handle);
|
||||
int test_worker1_destroy(void *handle, void *work_handle);
|
||||
int test_worker1_work(void *handle, void *work_handle, char *input, char *output);
|
||||
int test_worker2_work(void *handle, void *work_handle, char *input, char *output);
|
||||
|
||||
class_descriptor_t test_worker1 = {
|
||||
"test_worker1",
|
||||
&test_worker1_init,
|
||||
NULL,
|
||||
&test_worker1_setup,
|
||||
&test_worker1_work,
|
||||
&test_worker1_destroy
|
||||
};
|
||||
|
||||
class_descriptor_t test_worker2 = {
|
||||
"test_worker2",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&test_worker2_work,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct test_worker1_handle_s {
|
||||
int counter;
|
||||
};
|
||||
|
||||
typedef struct test_worker1_handle_s test_worker1_handle_t;
|
||||
|
||||
int test_worker1_init(cfgl_t *cfg, void **handle) {
|
||||
*handle = (char*) findcfgl(cfg, "text");
|
||||
syslog(LOG_DEBUG, "test_worker1_init: %s", *handle);
|
||||
if (NULL == *handle) {
|
||||
syslog(LOG_ERR, "test_worker1_init: text not configured");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_worker1_setup(void *handle, void **work_handle) {
|
||||
test_worker1_handle_t *twh = (test_worker1_handle_t*)malloc(sizeof(test_worker1_handle_t));
|
||||
twh->counter = 0;
|
||||
*work_handle = twh;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_worker1_destroy(void *handle, void *work_handle) {
|
||||
syslog(LOG_DEBUG, "test_worker1_destroy: freeing the worker handle");
|
||||
free(work_handle);
|
||||
}
|
||||
|
||||
int test_worker1_work(void *handle, void *work_handle, char *input, char *output) {
|
||||
sprintf(output, "Test-Worker 1 receives %s (handle %s) (called %d)\n",
|
||||
input, (char*)handle, (((test_worker1_handle_t*)work_handle)->counter)++);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_worker2_work(void *handle, void *work_handle, char *input, char *output) {
|
||||
sprintf(output, "Test-Worker 2 receives %s\n", input);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
745
smmapdfw/verify_worker.c
Normal file
745
smmapdfw/verify_worker.c
Normal file
@ -0,0 +1,745 @@
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#define DB_DBM_HSEARCH 1
|
||||
#include <db.h>
|
||||
|
||||
#include "containers_public.h"
|
||||
#include "smmapd.h"
|
||||
|
||||
#include "dns.h"
|
||||
#include "queue.h"
|
||||
#include "smtp.h"
|
||||
|
||||
|
||||
|
||||
#define SMM_LOCAL_PERM_NOK 101
|
||||
#define SMM_LOCAL_TEMP_NOK 102
|
||||
#define SMM_LOCAL_OK 103
|
||||
|
||||
|
||||
struct verify_container_handle_s {
|
||||
cfgl_t *cfg;
|
||||
int timeout_result;
|
||||
int timeout_dialog;
|
||||
int cache_enabled;
|
||||
int cache_expiry;
|
||||
pthread_mutex_t *cache_mutex;
|
||||
char *cache_file;
|
||||
char *sender_address;
|
||||
char *ehlo_arg;
|
||||
int smtp_port;
|
||||
int max_checker_threads;
|
||||
};
|
||||
|
||||
typedef struct verify_container_handle_s verify_container_handle_t;
|
||||
|
||||
struct verify_result_s {
|
||||
int id;
|
||||
int result;
|
||||
char *output;
|
||||
};
|
||||
|
||||
typedef struct verify_result_s verify_result_t;
|
||||
|
||||
|
||||
|
||||
struct verify_work_handle_s {
|
||||
int id;
|
||||
pthread_mutex_t *result_mutex;
|
||||
pthread_cond_t *result_cond;
|
||||
verify_result_t *result;
|
||||
ht_queue_t *terminator_queue;
|
||||
verify_container_handle_t *vch;
|
||||
};
|
||||
|
||||
typedef struct verify_work_handle_s verify_work_handle_t;
|
||||
|
||||
|
||||
|
||||
struct checker_thread_s {
|
||||
pthread_t thread;
|
||||
int id;
|
||||
int ip_address;
|
||||
char *email_address;
|
||||
char *output;
|
||||
int result;
|
||||
ht_queue_t *checker_terminator_queue;
|
||||
verify_work_handle_t *vwh;
|
||||
};
|
||||
|
||||
typedef struct checker_thread_s checker_thread_t;
|
||||
|
||||
|
||||
struct worker_thread_s {
|
||||
pthread_t thread;
|
||||
int id;
|
||||
char *input;
|
||||
char *output;
|
||||
pthread_mutex_t *mutex;
|
||||
pthread_cond_t *cond;
|
||||
verify_result_t *result;
|
||||
ht_queue_t *checker_terminator_queue;
|
||||
int checker_cnt;
|
||||
ht_queue_t *terminator_queue;
|
||||
verify_work_handle_t *vwh;
|
||||
};
|
||||
|
||||
typedef struct worker_thread_s worker_thread_t;
|
||||
|
||||
struct mydata_s {
|
||||
int result;
|
||||
time_t timestamp;
|
||||
char output[1];
|
||||
};
|
||||
|
||||
typedef struct mydata_s mydata_t;
|
||||
|
||||
|
||||
|
||||
int verify_init(cfgl_t *cfg, void **handle);
|
||||
int verify_destroy(void *handle);
|
||||
int verify_work_setup(void *handle, void **work_handle);
|
||||
int verify_work(void *handle, void *work_handle, char *input, char *output);
|
||||
int verify_work_destroy(void *handle, void *work_handle);
|
||||
|
||||
static void *worker_thread(void *arg);
|
||||
static unsigned int *get_mx_ip_addresses(char *domain);
|
||||
|
||||
|
||||
class_descriptor_t verifier = {
|
||||
"verifier",
|
||||
&verify_init,
|
||||
&verify_destroy,
|
||||
&verify_work_setup,
|
||||
&verify_work,
|
||||
&verify_work_destroy
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* verify_init will be called when the class is loaded, directly at start-up */
|
||||
/* It will be called definitely only once, there is definitely only one */
|
||||
/* container handle for each class in the application. */
|
||||
int verify_init(cfgl_t *cfg, void **handle) {
|
||||
verify_container_handle_t *vch;
|
||||
|
||||
vch = (verify_container_handle_t*) malloc(sizeof(verify_container_handle_t));
|
||||
vch->cfg = cfg;
|
||||
|
||||
vch->timeout_result = atoi(findcfglx(vch->cfg, "timeout_result", "5"));
|
||||
vch->timeout_dialog = atoi(findcfglx(vch->cfg, "timeout_dialog", "20"));
|
||||
vch->sender_address = findcfglx(vch->cfg, "sender_address", "<>");
|
||||
vch->ehlo_arg = findcfglx(vch->cfg, "ehlo_arg", "local");
|
||||
vch->smtp_port = atoi(findcfglx(vch->cfg, "smtp_port", "25"));
|
||||
vch->max_checker_threads = atoi(findcfglx(vch->cfg, "max_checker_threads", "25"));
|
||||
vch->cache_enabled = atoi(findcfglx(vch->cfg, "cache_enabled", "1"));
|
||||
vch->cache_expiry = atoi(findcfglx(vch->cfg, "cache_expiry", "86400"));
|
||||
vch->cache_file = findcfglx(vch->cfg, "cache_file", "verifier_cache");
|
||||
|
||||
if (1 == vch->cache_enabled) {
|
||||
vch->cache_mutex = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t));
|
||||
pthread_mutex_init(vch->cache_mutex, NULL);
|
||||
pthread_mutex_unlock(vch->cache_mutex);
|
||||
} else {
|
||||
vch->cache_mutex = NULL;
|
||||
}
|
||||
|
||||
|
||||
*handle = vch;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* currently this will be called never. It would be called when the server */
|
||||
/* is gracefully shutted down, which is currently not supported */
|
||||
int verify_destroy(void *handle) {
|
||||
verify_container_handle_t *vch = (verify_container_handle_t*)handle;
|
||||
|
||||
if (1 == vch->cache_enabled) {
|
||||
pthread_mutex_destroy(vch->cache_mutex);
|
||||
free(vch->cache_mutex);
|
||||
}
|
||||
|
||||
free(vch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int verify_work_setup(void *handle, void **work_handle) {
|
||||
verify_work_handle_t *vwh;
|
||||
|
||||
vwh = (verify_work_handle_t*)malloc(sizeof(verify_work_handle_t));
|
||||
vwh->id = 0;
|
||||
vwh->result = (verify_result_t*) malloc(sizeof(verify_result_t));
|
||||
vwh->result_mutex = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t));
|
||||
pthread_mutex_init(vwh->result_mutex, NULL);
|
||||
pthread_mutex_unlock(vwh->result_mutex);
|
||||
vwh->result_cond = (pthread_cond_t*) malloc(sizeof(pthread_cond_t));
|
||||
pthread_cond_init(vwh->result_cond, NULL);
|
||||
vwh->terminator_queue = (ht_queue_t*) malloc(sizeof(ht_queue_t));
|
||||
queue_init(vwh->terminator_queue);
|
||||
vwh->vch = (verify_container_handle_t*)handle;
|
||||
|
||||
*work_handle = vwh;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verify_work_destroy(void *handle, void *work_handle) {
|
||||
verify_work_handle_t *vwh = (verify_work_handle_t*)work_handle;
|
||||
worker_thread_t *wt;
|
||||
checker_thread_t *ct;
|
||||
int cnt, cnt2, err;
|
||||
|
||||
syslog(LOG_DEBUG, "verify_work_destroy: was %d times used", vwh->id);
|
||||
|
||||
/* The terminator_queue must be drained until id_counter is zero again */
|
||||
cnt = vwh->id;
|
||||
while (cnt != 0) {
|
||||
syslog(LOG_DEBUG, "verify_work_destroy, %d thread in queue, waiting for it", cnt);
|
||||
wt = (worker_thread_t*) queue_get_wait(vwh->terminator_queue);
|
||||
|
||||
cnt2 = wt->checker_cnt;
|
||||
while (cnt2 != 0) {
|
||||
ct = (checker_thread_t*) queue_get_wait(wt->checker_terminator_queue);
|
||||
/* clean up the checker stuff */
|
||||
|
||||
pthread_join(ct->thread, NULL);
|
||||
syslog(LOG_DEBUG, "verify_work_destroy, checker_thread (id=%d) joined", ct->id);
|
||||
|
||||
free(ct->output);
|
||||
free(ct);
|
||||
|
||||
cnt2--;
|
||||
}
|
||||
|
||||
err = pthread_join(wt->thread, NULL);
|
||||
syslog(LOG_DEBUG, "verify_work_destroy, worker_thread (id=%d) joined", wt->id);
|
||||
free(wt->input);
|
||||
|
||||
/* this will always be a pointer to something const or allocated, which
|
||||
will be freed somewhere else */
|
||||
/* free(wt->output); */
|
||||
|
||||
queue_destroy(wt->checker_terminator_queue);
|
||||
free(wt->checker_terminator_queue);
|
||||
|
||||
free(wt);
|
||||
|
||||
cnt--;
|
||||
}
|
||||
|
||||
/* Free the members of the work_handle */
|
||||
|
||||
pthread_mutex_destroy(vwh->result_mutex);
|
||||
free(vwh->result_mutex);
|
||||
pthread_cond_destroy(vwh->result_cond);
|
||||
free(vwh->result_cond);
|
||||
free(vwh->result);
|
||||
queue_destroy(vwh->terminator_queue);
|
||||
free(vwh->terminator_queue);
|
||||
free(vwh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void cache_insert(verify_container_handle_t *vch, const char *address, int result, const char *output) {
|
||||
DBM *cache;
|
||||
datum data, key;
|
||||
int ret;
|
||||
mydata_t *mydata;
|
||||
|
||||
if (1 == vch->cache_enabled) {
|
||||
syslog(LOG_DEBUG, "cache_insert: inserting %s -> %d, %s", address, result, output);
|
||||
key.dsize = strlen(address) + 1; /* one more for the terminating \0 */
|
||||
key.dptr = (char*) address;
|
||||
data.dsize = (sizeof(mydata_t) + (sizeof(char) * (strlen(output) + 1)));
|
||||
mydata = (mydata_t *) malloc(data.dsize);
|
||||
mydata->result = result;
|
||||
mydata->timestamp = time(NULL);
|
||||
strcpy(mydata->output, output);
|
||||
data.dptr = (char*) mydata;
|
||||
|
||||
pthread_mutex_lock(vch->cache_mutex);
|
||||
if (NULL != (cache = dbm_open(vch->cache_file, O_RDWR | O_CREAT, 0644))) {
|
||||
ret = dbm_store(cache, key, data, DBM_INSERT);
|
||||
if (ret != 0) {
|
||||
syslog(LOG_DEBUG, "cache_insert: couldn't insert record");
|
||||
} else {
|
||||
syslog(LOG_DEBUG, "cache_insert: record inserted");
|
||||
}
|
||||
dbm_close(cache);
|
||||
}
|
||||
pthread_mutex_unlock(vch->cache_mutex);
|
||||
|
||||
free(mydata);
|
||||
}
|
||||
}
|
||||
|
||||
int cache_lookup(verify_container_handle_t *vch, const char* address, int *result, char **output) {
|
||||
DBM *cache;
|
||||
datum data, key;
|
||||
mydata_t *mydata;
|
||||
int ret, res = -1;
|
||||
|
||||
if (1 == vch->cache_enabled) {
|
||||
syslog(LOG_DEBUG, "cache_lookup: looking up %s, expiry %d",
|
||||
address, vch->cache_expiry);
|
||||
|
||||
if (NULL != (cache = dbm_open(vch->cache_file, O_RDONLY, 0644))) {
|
||||
key.dsize = strlen(address) + 1; /* one more for the terminating \0 */
|
||||
key.dptr = (char*) address;
|
||||
data = dbm_fetch(cache, key);
|
||||
if (NULL == data.dptr) {
|
||||
syslog(LOG_DEBUG, "cache_lookup: nothing found");
|
||||
dbm_close(cache);
|
||||
} else {
|
||||
mydata = (mydata_t *) data.dptr;
|
||||
syslog(LOG_DEBUG, "cache_lookup: found: %s -> %d, %d, %s",
|
||||
address, mydata->result, mydata->timestamp, mydata->output);
|
||||
if ((mydata->timestamp + vch->cache_expiry) > time(NULL)) {
|
||||
syslog(LOG_DEBUG, "cache_lookup: not yet expired");
|
||||
*result = mydata->result;
|
||||
*output = (char*) malloc(sizeof(char) * (strlen(mydata->output) + 1));
|
||||
strcpy(*output, mydata->output);
|
||||
free(data.dptr);
|
||||
dbm_close(cache);
|
||||
res = 0;
|
||||
} else {
|
||||
syslog(LOG_DEBUG, "cache_lookup: expired, will be deleted from cache");
|
||||
free(data.dptr);
|
||||
dbm_close(cache);
|
||||
|
||||
pthread_mutex_lock(vch->cache_mutex);
|
||||
if (NULL != (cache = dbm_open(vch->cache_file, O_RDWR, 0644))) {
|
||||
ret = dbm_delete(cache, key);
|
||||
if (ret != 0) {
|
||||
syslog(LOG_DEBUG, "cache_insert: couldn't delete record");
|
||||
} else {
|
||||
syslog(LOG_DEBUG, "cache_insert: record deleted");
|
||||
}
|
||||
dbm_close(cache);
|
||||
}
|
||||
pthread_mutex_unlock(vch->cache_mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* Each time verify_work is called, it starts a worker thread. This
|
||||
thread gets the address to check, an id, a worker termination queue
|
||||
and a pointer the a result structure as argument.
|
||||
|
||||
The id is just a number which is increased each time a new worker
|
||||
thread is started. The id is also part of the result structure.
|
||||
|
||||
verify_work waits a certain amount of time for the result of a
|
||||
worker thread, afterwards it returns with temporary failure.
|
||||
|
||||
A worker thread returns its result by putting it into the result
|
||||
structure, but it does so only if the id in the result structure is
|
||||
equal to the id it go as argument. If a worker thread finds an id
|
||||
in the result structure different from its own one, verify_work has
|
||||
started a new worker thread in between and is not longer interested
|
||||
in the result of the current thread.
|
||||
|
||||
A thread puts its own argument structure into the termination
|
||||
queue, just before actually terminating.
|
||||
|
||||
verify_work_destroy will drain termination queue, joins each thread
|
||||
it takes out of it an frees the argument structure. It does so
|
||||
until the thread count (id) goes to zero.
|
||||
*/
|
||||
|
||||
#define PERM_NOK_RETURN(msg) \
|
||||
syslog(LOG_ERR, "verify_work: %s", msg); \
|
||||
snprintf(output, ANSWER_BUFSIZE, "verify_work: %s", msg); \
|
||||
return SMM_PERM_NOK;
|
||||
#define TEMP_NOK_RETURN(msg) \
|
||||
syslog(LOG_ERR, "verify_work: %s", msg); \
|
||||
snprintf(output, ANSWER_BUFSIZE, "verify_work: %s", msg); \
|
||||
return SMM_TEMP_NOK;
|
||||
|
||||
int verify_work(void *handle, void *work_handle, char *input, char *output) {
|
||||
int err;
|
||||
pthread_t tid;
|
||||
worker_thread_t *wt;
|
||||
verify_container_handle_t *vch = (verify_container_handle_t*) handle;
|
||||
verify_work_handle_t *vwh = (verify_work_handle_t*) work_handle;
|
||||
struct timespec ts;
|
||||
|
||||
|
||||
syslog(LOG_DEBUG, "verify_work: going to verify %s\n", input);
|
||||
|
||||
vwh->id += 1;
|
||||
|
||||
vwh->result->id = vwh->id;
|
||||
|
||||
wt = (worker_thread_t*) malloc(sizeof(worker_thread_t));
|
||||
wt->id = vwh->id;
|
||||
wt->terminator_queue = vwh->terminator_queue;
|
||||
wt->input = (char*) malloc(sizeof(char) * (strlen(input)+1));
|
||||
strcpy(wt->input, input);
|
||||
wt->output = NULL;
|
||||
wt->mutex = vwh->result_mutex;
|
||||
wt->cond = vwh->result_cond;
|
||||
wt->result = vwh->result;
|
||||
wt->checker_terminator_queue = (ht_queue_t*) malloc(sizeof(ht_queue_t));
|
||||
queue_init(wt->checker_terminator_queue);
|
||||
wt->checker_cnt = 0;
|
||||
wt->vwh = work_handle;
|
||||
|
||||
syslog(LOG_DEBUG, "verify_work: going to start worker thread, id=%d", vwh->id);
|
||||
err = pthread_create(&tid, NULL, &worker_thread, wt);
|
||||
if (-1 == err) {
|
||||
free(wt->input);
|
||||
free(wt);
|
||||
PERM_NOK_RETURN("unable to create worker thread");
|
||||
}
|
||||
|
||||
syslog(LOG_DEBUG, "verify_work: waiting for result");
|
||||
pthread_mutex_lock(vwh->result_mutex);
|
||||
ts.tv_sec = time(0) + vch->timeout_result;
|
||||
ts.tv_nsec = 0;
|
||||
err = pthread_cond_timedwait(vwh->result_cond, vwh->result_mutex, &ts);
|
||||
pthread_mutex_unlock(vwh->result_mutex);
|
||||
if (ETIMEDOUT == err) {
|
||||
TEMP_NOK_RETURN("worker thread timed out");
|
||||
}
|
||||
|
||||
snprintf(output, ANSWER_BUFSIZE, vwh->result->output);
|
||||
free(vwh->result->output);
|
||||
return vwh->result->result;
|
||||
}
|
||||
|
||||
|
||||
static unsigned int *get_mx_ip_addresses(char *domain) {
|
||||
mx_rdata_t **mx_rdata, **mx_rdata2;
|
||||
a_rdata_t **a_rdata, **a_rdata2;
|
||||
int i = 0;
|
||||
unsigned int *addresses = NULL;
|
||||
|
||||
mx_rdata = get_best_mx_rrs(domain);
|
||||
if (NULL == mx_rdata) {
|
||||
syslog(LOG_DEBUG, "get_mx_ip_address: no mx at all");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (mx_rdata2 = mx_rdata; *mx_rdata2 != NULL; mx_rdata2++) {
|
||||
syslog(LOG_DEBUG, "get_mx_ip_address: %s", (*mx_rdata2)->exchange);
|
||||
|
||||
a_rdata = get_a_rrs((*mx_rdata2)->exchange);
|
||||
if (NULL != a_rdata) {
|
||||
for (a_rdata2 = a_rdata; *a_rdata2 != NULL; a_rdata2++) {
|
||||
addresses = (unsigned int*)realloc(addresses, sizeof(unsigned int)*(i+2)); /* 2 since first i==0 and
|
||||
we always need one more
|
||||
for the termination */
|
||||
*(addresses+i) = (*a_rdata2)->address;
|
||||
i++;
|
||||
}
|
||||
free_rrs((void**)a_rdata);
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL != addresses)
|
||||
*(addresses+i) = 0; /* termination */
|
||||
|
||||
free_rrs((void**)mx_rdata);
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
|
||||
static void *checker_thread(void *arg) {
|
||||
static const char *UNEXPECTED_ERROR = "unexpected error in smtp dialog";
|
||||
static const char *TIMEOUT_ERROR = "timeout on smtp dialog";
|
||||
static const char *ADDRESS_VALID = "address is valid";
|
||||
|
||||
checker_thread_t *ct = (checker_thread_t*) arg;
|
||||
|
||||
int timeout = ct->vwh->vch->timeout_dialog;
|
||||
char *sender_address = ct->vwh->vch->sender_address;
|
||||
char *ehlo_arg = ct->vwh->vch->ehlo_arg;
|
||||
int port = ct->vwh->vch->smtp_port;
|
||||
|
||||
smtp_t *smtp;
|
||||
char *response_text, *tmp_arg;
|
||||
|
||||
int err, done = 0;
|
||||
|
||||
enum {
|
||||
CONNECT, EHLO, MAILFROM, RCPTTO, RSET, QUIT, END
|
||||
} state = CONNECT;
|
||||
|
||||
|
||||
|
||||
syslog(LOG_DEBUG, "checker_thread (id=%d) started, %08x %s", ct->id, ct->ip_address, ct->email_address);
|
||||
|
||||
ct->thread = pthread_self();
|
||||
|
||||
/*
|
||||
Connect to ct->ip_address using ct->email_address,
|
||||
put the result text in ct->output, TBD: by copy or pointer,
|
||||
evaluate whether it is SMM_TEMP_NOK, SMM_PERM_NOK or SMM_OK
|
||||
*/
|
||||
smtp = smtp_init(ct->ip_address, port, timeout);
|
||||
|
||||
|
||||
while ((END != state) && (0 == done)) {
|
||||
syslog(LOG_DEBUG, "checker_thread (id=%d), smtp dialog state %d", ct->id, state);
|
||||
switch(state) {
|
||||
case CONNECT:
|
||||
err = smtp_connect(smtp);
|
||||
break;
|
||||
case EHLO:
|
||||
err = smtp_ehlo(smtp, ehlo_arg);
|
||||
break;
|
||||
case MAILFROM:
|
||||
err = smtp_mailfrom(smtp, sender_address);
|
||||
break;
|
||||
case RCPTTO:
|
||||
tmp_arg = (char*) malloc(sizeof(char) * (strlen(ct->email_address)+5));
|
||||
*tmp_arg = '\0';
|
||||
strcat(tmp_arg, "<");
|
||||
strcat(tmp_arg, ct->email_address);
|
||||
strcat(tmp_arg, ">");
|
||||
err = smtp_rcptto(smtp, tmp_arg);
|
||||
free(tmp_arg);
|
||||
break;
|
||||
case RSET:
|
||||
err = smtp_rset(smtp);
|
||||
break;
|
||||
case QUIT:
|
||||
err = smtp_quit(smtp);
|
||||
break;
|
||||
}
|
||||
|
||||
state++;
|
||||
|
||||
switch(err) {
|
||||
case SMTP_TIMEOUT:
|
||||
syslog(LOG_DEBUG, "checker_thread (id=%d), timeout in smtp dialog", ct->id);
|
||||
ct->result = SMM_LOCAL_TEMP_NOK;
|
||||
response_text = (char*)TIMEOUT_ERROR;
|
||||
done = 1;
|
||||
break;
|
||||
case 0:
|
||||
/* evaluate smtp_response, return or continue */
|
||||
err = smtp_response(smtp, &response_text);
|
||||
if (-1 == err) {
|
||||
syslog(LOG_DEBUG, "checker_thread (id=%d), response could not be parsed", ct->id);
|
||||
ct->result = SMM_LOCAL_TEMP_NOK;
|
||||
response_text = (char*)UNEXPECTED_ERROR;
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
syslog(LOG_DEBUG, "checker_thread (id=%d), response: %d, %s (%d)", ct->id, err, response_text, strlen(response_text));
|
||||
switch(err/100) {
|
||||
case 4:
|
||||
ct->result = SMM_LOCAL_TEMP_NOK;
|
||||
done = 1;
|
||||
break;
|
||||
case 5:
|
||||
ct->result = SMM_LOCAL_PERM_NOK;
|
||||
done = 1;
|
||||
break;
|
||||
case 2:
|
||||
if (END == state) {
|
||||
ct->result = SMM_LOCAL_OK;
|
||||
response_text = (char*)ADDRESS_VALID;
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_DEBUG, "checker_thread (id=%d), unexpected error in smtp dialog", ct->id);
|
||||
ct->result = SMM_LOCAL_TEMP_NOK;
|
||||
response_text = (char*)UNEXPECTED_ERROR;
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
smtp_close(smtp);
|
||||
|
||||
ct->output = (char*) malloc(sizeof(char) * (strlen(response_text)+1));
|
||||
strcpy(ct->output, response_text);
|
||||
|
||||
smtp_destroy(smtp);
|
||||
|
||||
syslog(LOG_DEBUG, "checker_thread (id=%d) goes to terminator queue", ct->id);
|
||||
queue_put(ct->checker_terminator_queue, ct);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void *worker_thread(void *arg) {
|
||||
worker_thread_t *wt = (worker_thread_t*) arg;
|
||||
char *domain_part;
|
||||
unsigned int *mx_ip_addresses;
|
||||
int result = SMM_TEMP_NOK;
|
||||
int i, err;
|
||||
pthread_t tid;
|
||||
checker_thread_t *ct = NULL;
|
||||
char *cached_output = NULL;
|
||||
|
||||
static const char *NOT_AN_ADDRESS = "not an email-address, no @ in it";
|
||||
static const char *DEFAULT_ANSWER = "default answer";
|
||||
static const char *NO_MX_AVAILABLE = "no MX available";
|
||||
static const char *NO_PERM_RESULT = "no checker returned permanent result";
|
||||
|
||||
syslog(LOG_DEBUG, "worker_thread %d started, %s", wt->id, wt->input);
|
||||
|
||||
wt->thread = pthread_self();
|
||||
|
||||
|
||||
|
||||
|
||||
/* separate domain part, some sanity checks */
|
||||
domain_part = strchr(wt->input, '@');
|
||||
if (NULL == domain_part) {
|
||||
wt->output = (char*) NOT_AN_ADDRESS;
|
||||
result = SMM_LOCAL_PERM_NOK;
|
||||
} else {
|
||||
if (0 == cache_lookup(wt->vwh->vch, wt->input, &result, &cached_output)) {
|
||||
syslog(LOG_DEBUG, "worker_thread: got a cached result for %s -> %d, %s",
|
||||
wt->input, result, cached_output);
|
||||
wt->output = cached_output;
|
||||
} else {
|
||||
domain_part += 1;
|
||||
|
||||
syslog(LOG_DEBUG, "worker_thread: looking up %s", domain_part);
|
||||
|
||||
mx_ip_addresses = get_mx_ip_addresses(domain_part);
|
||||
if (NULL == mx_ip_addresses) {
|
||||
wt->output = (char*) NO_MX_AVAILABLE;
|
||||
result = SMM_LOCAL_PERM_NOK;
|
||||
} else {
|
||||
for (i = 0; (*(mx_ip_addresses+i) != 0) && (i < wt->vwh->vch->max_checker_threads); i++) {
|
||||
unsigned int address = *(mx_ip_addresses+i);
|
||||
syslog(LOG_DEBUG, "worker_thread: starting checker thread to %d.%d.%d.%d, for email-address %s",
|
||||
(address&0xff000000)>>24, (address&0x00ff0000)>>16,
|
||||
(address&0x0000ff00)>>8, (address&0x000000ff),
|
||||
wt->input);
|
||||
|
||||
ct = (checker_thread_t*) malloc(sizeof(checker_thread_t));
|
||||
ct->id = i+1; /* id of ct should start with 1, same as id of wt */
|
||||
ct->ip_address = address;
|
||||
ct->email_address = wt->input;
|
||||
ct->output = NULL;
|
||||
ct->result = SMM_TEMP_NOK;
|
||||
ct->checker_terminator_queue = wt->checker_terminator_queue;
|
||||
ct->vwh = wt->vwh;
|
||||
|
||||
err = pthread_create(&tid, NULL, &checker_thread, ct);
|
||||
if (-1 == err) {
|
||||
syslog(LOG_ERR, "worker_thread: unable to create checker thread");
|
||||
free(ct);
|
||||
} else {
|
||||
wt->checker_cnt += 1;
|
||||
}
|
||||
}
|
||||
|
||||
free(mx_ip_addresses);
|
||||
|
||||
while (wt->checker_cnt > 0) {
|
||||
ct = (checker_thread_t*) queue_get_wait(wt->checker_terminator_queue);
|
||||
wt->checker_cnt -= 1;
|
||||
syslog(LOG_DEBUG, "worker_thread: got checker result for %d.%d.%d.%d: %s -> %d, %s",
|
||||
((ct->ip_address)&0xff000000)>>24, ((ct->ip_address)&0x00ff0000)>>16,
|
||||
((ct->ip_address)&0x0000ff00)>>8, ((ct->ip_address)&0x000000ff),
|
||||
wt->input, ct->result, ct->output);
|
||||
|
||||
pthread_join(ct->thread, NULL);
|
||||
syslog(LOG_DEBUG, "worker_thread: checker thread joined");
|
||||
|
||||
if ((SMM_LOCAL_TEMP_NOK != ct->result) &&
|
||||
(SMM_TEMP_NOK != ct->result)) {
|
||||
syslog(LOG_DEBUG, "worker_thread: this is a permanent result, returning");
|
||||
wt->output = ct->output;
|
||||
result = ct->result;
|
||||
cache_insert(wt->vwh->vch, wt->input, ct->result, ct->output);
|
||||
/* ct will be freed later, since its output is needed below */
|
||||
break; /* exit from the ct-collecting while loop, leave the rest of the ct's
|
||||
for the cleanup */
|
||||
} else {
|
||||
syslog(LOG_DEBUG, "worker_thread: this is a temporary result, continue to wait");
|
||||
wt->output = (char*) NO_PERM_RESULT;
|
||||
result = SMM_LOCAL_TEMP_NOK;
|
||||
/* we've collected the ct but its output is not longer need, so free it here */
|
||||
free(ct->output);
|
||||
free(ct);
|
||||
ct = NULL;
|
||||
}
|
||||
} /* ct-collecting while */
|
||||
} /* else: cache lookup */
|
||||
} /* else: no mx available */
|
||||
} /* else: not an address */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
|
||||
syslog(LOG_DEBUG, "worker_thread %d waiting for mutex", wt->id);
|
||||
pthread_mutex_lock(wt->mutex);
|
||||
if (wt->result->id == wt->id) {
|
||||
syslog(LOG_DEBUG, "worker_thread %d returned result", wt->id);
|
||||
/* we can write the result */
|
||||
wt->result->output = (char*) malloc(sizeof(char) * (strlen(wt->output)+15)); /* enough for the output
|
||||
plus <><> and prefix */
|
||||
switch (result) {
|
||||
case SMM_LOCAL_TEMP_NOK:
|
||||
sprintf(wt->result->output, "<TNOK><%s>", wt->output);
|
||||
result = SMM_OK;
|
||||
break;
|
||||
case SMM_LOCAL_PERM_NOK:
|
||||
sprintf(wt->result->output, "<NOK><%s>", wt->output);
|
||||
result = SMM_OK;
|
||||
break;
|
||||
case SMM_LOCAL_OK:
|
||||
sprintf(wt->result->output, "<OK><%s>", wt->output);
|
||||
result = SMM_OK;
|
||||
break;
|
||||
default:
|
||||
strcpy(wt->result->output, wt->output);
|
||||
break;
|
||||
}
|
||||
|
||||
wt->result->result = result;
|
||||
pthread_cond_signal(wt->cond);
|
||||
} else {
|
||||
syslog(LOG_DEBUG, "worker_thread %d drops result", wt->id);
|
||||
/* result not longer interested */
|
||||
}
|
||||
pthread_mutex_unlock(wt->mutex);
|
||||
|
||||
/* we've collected the ct, so we need to free it.
|
||||
free only if not NULL to avoid double-free in case of
|
||||
no permenant result at all */
|
||||
if (NULL != ct) {
|
||||
free(ct->output);
|
||||
free(ct);
|
||||
}
|
||||
|
||||
/* if cached_output is not NULL, it was allocated for us and
|
||||
we need to free it */
|
||||
if (NULL != cached_output) {
|
||||
free(cached_output);
|
||||
}
|
||||
|
||||
syslog(LOG_DEBUG, "worker_thread %d goes to terminator queue", wt->id);
|
||||
queue_put(wt->terminator_queue, wt);
|
||||
}
|
||||
|
||||
|
129
smmapdfw/verifysender.m4
Normal file
129
smmapdfw/verifysender.m4
Normal file
@ -0,0 +1,129 @@
|
||||
VERSIONID(`$Id$')
|
||||
|
||||
|
||||
|
||||
divert(-1)
|
||||
|
||||
define(`_USAGE_', `dnl
|
||||
errprint(`*** ERROR: missing argument for FEATURE(verifysender):
|
||||
Usage: FEATURE(`verifysender', `_mode_', `_return_', `_dummy_')
|
||||
_mode_: black or white
|
||||
_return_: temp or perm
|
||||
_dummy_: active (actually returning errors) or dummy (just log what it would return)
|
||||
found: $1
|
||||
')')
|
||||
|
||||
ifelse(_ARG_, `black', `', `
|
||||
ifelse(_ARG_, `white', `', `
|
||||
_USAGE_(`_mode_: ('_ARG_`)
|
||||
')
|
||||
')')
|
||||
|
||||
ifelse(_ARG2_, `temp', `', `
|
||||
ifelse(_ARG2_, `perm', `', `
|
||||
_USAGE_(`_return_: ('_ARG2_`)
|
||||
')
|
||||
')')
|
||||
|
||||
ifelse(_ARG3_, `active', `', `
|
||||
ifelse(_ARG3_, `dummy', `', `
|
||||
_USAGE_(`_return_: ('_ARG3_`)
|
||||
')
|
||||
')')
|
||||
|
||||
define(`_mode_', _ARG_)
|
||||
define(`_return_', _ARG2_)
|
||||
define(`_dummy_', _ARG3_)
|
||||
|
||||
dnl errprint(`*** _mode_: '_mode_`
|
||||
dnl ')
|
||||
dnl errprint(`*** _return_: '_return_`
|
||||
dnl ')
|
||||
dnl errprint(`*** _dummy_: '_dummy_`
|
||||
dnl ')
|
||||
|
||||
|
||||
|
||||
|
||||
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_', `hash -o /etc/mail/verifier-black-list')', `
|
||||
define(`_VERIFIER_BLACKLIST_', confVERIFIER_BLACKLIST)')
|
||||
|
||||
ifelse(defn(`confVERIFIER_WHITELIST'), `', `
|
||||
define(`_VERIFIER_WHITELIST_', `hash -o /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 _VERIFIER_WHITELIST_', `dnl
|
||||
Kverifier_helper _VERIFIER_BLACKLIST_')
|
||||
Kvht_logger syslog
|
||||
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< $+ > $: < $1 > < $(verifier $1 $:none $) >
|
||||
ifelse(_dummy_, `dummy', `dnl
|
||||
dnl dummy
|
||||
R< $* > < $* > $@ < ok > $(vht_logger $1 --- $2 $)', `dnl
|
||||
dnl active
|
||||
R< $* > < $* > $: < $2 >
|
||||
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
|
||||
dnl if we get here, some is wrong with our code
|
||||
R$* $#error $@ 4.7.1 $: "451 Local configuration error <sv2>"
|
Loading…
x
Reference in New Issue
Block a user