smtpd icon indicating copy to clipboard operation
smtpd copied to clipboard

Simple additions to support LMTP

Open luisemunoz opened this issue 8 years ago • 5 comments
trafficstars

Please accept these trivial changes that allow your library to be used for LMTP as well. Since the implementation of actual behaviors reside in the user of this library, I believe these changes are sufficient to implement a complete MDA.

luisemunoz avatar Dec 13 '16 05:12 luisemunoz

Unfortunately I don't think this change suffices to implement LMTP by itself. According to RFC 2033, in LTMP mode the receiving MTA must give multiple replies to the terminating . at the end of DATA, one for each RCPT TO (whether accepted, rejected, or temp-failed). In the current form, simply accepting a LHLO and then pretending it is a EHLO can only work with a single recipient; otherwise, compliant clients would hang (expecting additional DATA replies that they wouldn't get) and then report a timeout error.

(I'm not sure what the best way to handle this would be in the current code. In a general package it's clearly important to give callers the ability to report post-DATA problems for some of the accepted recipients, which adds complexity and probably calls for additional methods.)

siebenmann avatar Dec 13 '16 16:12 siebenmann

Thanks @siebenmann -- I was under the impression that the code using smtpd would be able to provide for these responses. In any case, I'll implement any required methods / interface and ping you back if needed.

luisemunoz avatar Dec 13 '16 19:12 luisemunoz

It depends on what level of smtpd the caller is operating on. At the level of calling ParseCmd(), simply having basic knowledge of LHLO is enough to enable callers to do things. However, I expect that most callers will use smtpd.Conn, which ensures RFC compliance, proper command sequencing, and so on. At the Conn level there's nothing exposed that would let users of the package send out multi-line replies on DATA; the closest is currently the (Accept|Reject|Tempfail)Msg() family, but they specifically send properly formed multi-line replies (with continuations before everything except the last line) where LTMP requires multiple separate replies.

(As the code stands you might be able to call eg AcceptMsg() multiple times and get multiple outputs, but this is a bug (or an unimplemented check), not an API feature. The API contract currently is that you call the Accept, Reject, or Tempfail family at most once per Conn.Next() event that has to be handled.)

I'm also not sure that the smtpd API currently provides enough information to users of the package to emit all of the DATA replies because Conn.Next() internally rejects a few RCPT TOs without telling the caller about them. I believe those rejected RCPT TOs probably still have to be rejected (again) after the DATA reply.

siebenmann avatar Dec 13 '16 19:12 siebenmann

In thinking about the current changes, I find myself nervous about how LMTP support currently interacts with a non-LMTP-aware caller. The problem is that in order to properly handle the LTMP protocol, the caller must be specially coded for it and be aware that LHLO has been used. After these changes, a non-LMTP aware caller can be switched into LTMP mode by a client using LHLO, after which everything will go off the rails.

I think this implies that callers should have to explicitly opt in to LTMP support by setting a new flag in their smtpd.Config structure. If they don't set this flag, a LHLO should be rejected. I suspect that it would also be good to have a LMTPOn boolean exposed in smtpd.Conn, and probably also more RCPT TO state tracking information (given the internal rejection RCPT TO problem).

siebenmann avatar Dec 15 '16 19:12 siebenmann

Yes, I'm thinking along the same lines. I was going for having this in the smtpd.Config

  ⋮
  LMTPMode bool
  ⋮

And then, within command handling code, only the appropriate HELO/EHLO vs LHLO will be accepted. This prevents messing with unaware SMTP clients.

That said, I think the LMTP... variants I introduced for Accept, TempFail and Reject cover the rest of what's needed for LMTP -- I think that actually executing LMTP delivery required more involved integration, so I'm happy with staying at this level of abstraction.

If you agree, I'll go ahead and add the LMTP flag to smtpd.Config.

luisemunoz avatar Dec 16 '16 01:12 luisemunoz