Good day, hackers.
I'm trying to implement a rather simple, localhost-bound mail relay
with Twisted. The setup is follows:
Internet --- MTA -> multiplexer (*) -> parallel filters -> MTA -> dest.
Multiplexer marked with (*) is my doing. Filters are independent
systems which accept mail according to destination domain and do their
magic according to domain-wide settings. Different domains can have
completely separate configs and even backends.
I have read and tried to use existing sample from
http://twistedmatrix.com/pipermail/twisted-python/2003-October/005885.html
and the surrounding thread, but unfortunately have not been so far able
to get the system working.
Basically the system should be rather trivial: accept everything,
regardless of sender or recipient (frontal MTA has already bounced
incorrect mails). Check recipient and forward to matching filter set.
Use smtp.ESMTP for inbound, smtp.SMTPClient for outbound. However, the
code snippet below freaks out. It now accepts unconditionally both
sender and recipient but issuing DATA causes an immediate stacktrace
with the following last error:
File "/usr/lib/python2.3/site-packages/twisted/protocols/smtp.py",
line 625, in do_DATA
assert self.delivery
exceptions.AssertionError:
Tracing the problem, it is apparent that there is no delivery method
in use and incidentally, this was apparently the bugging reason in the
original (a year old now) problem. Call me stupid but I couldn't fix
this by following the example and responses.
Version of Twisted in use: 1.3, as packaged for Debian. (In time we'll
get 2.0 as well.) Any help will be appreaciated. The leap from very
simple Finger examples to smtp parts is rather long and steep...
Code:
[--snip--]
#!/usr/bin/python
from twisted.internet import reactor, protocol, defer
from twisted.protocols import smtp
from twisted.python import log
import sys
class RelayUtility:
"""Utility class for holding runtime values"""
def __init__(self):
self.maxconns = 20
self.active = 0
class RelayMessage(smtp.IMessage):
def __init__(self):
smtp.IMessage.__init__(self)
self.msg = []
class RelayProtocol(smtp.ESMTP):
"""Relayer; sucks the mail in"""
def __init__(self):
self.util = util
# Normal operations
smtp.ESMTP.__init__(self)
self.host = "nowhere.dot.invalid"
def connectionLost(self, reason):
self.util.active -= 1
def connectionMade(self):
# The easiest way. Increments upon connection, decrements
# upon disconnection; In case of full queue, just kick the client
self.util.active += 1
if (self.util.active <= self.util.maxconns):
smtp.ESMTP.connectionMade(self)
else:
self.sendCode(430, "Queue full. Try again later.")
self.transport.loseConnection()
# This can't be right
def validateFrom(self, helo, origin):
return smtp.Address(origin, None)
# This is certainly not right, DATA barks
def validateTo(self, user):
return RelayMessage
class RelayFactory(smtp.SMTPFactory):
protocol = RelayProtocol
util = RelayUtility()
log.startLogging(sys.stdout)
reactor.listenTCP(10025, RelayFactory())
reactor.run()
[--snap--]
--
Mika Boström \-/ "World peace will be achieved
Bostik at stinghorn.com X when the last man has killed
Software slave /-\ the second-to-last." -anon?