[Twisted-Python] Simple multiplex-relayer with twisted.protocols.smtp?

Mika Bostrom bostik at stinghorn.com
Fri Nov 26 04:04:44 EST 2004

  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
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

  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...



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):
    self.msg = []


class RelayProtocol(smtp.ESMTP):
  """Relayer; sucks the mail in"""
  def __init__(self):
    self.util = util
    # Normal operations
    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):
      self.sendCode(430, "Queue full. Try again later.")

  # 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()
reactor.listenTCP(10025, RelayFactory())


 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?

More information about the Twisted-Python mailing list