fix: reject empty HELO/EHLO with 501 per RFC 5321
EHLO/HELO require a domain or address-literal argument. Previously the server accepted bare EHLO with no argument and responded 250, which deviates from the spec and makes the honeypot easier to fingerprint.
This commit is contained in:
@@ -142,6 +142,11 @@ class SMTPProtocol(asyncio.Protocol):
|
||||
args = parts[1] if len(parts) > 1 else ""
|
||||
|
||||
if cmd in ("EHLO", "HELO"):
|
||||
if not args:
|
||||
self._transport.write(
|
||||
f"501 5.5.4 Syntax: {cmd} hostname\r\n".encode()
|
||||
)
|
||||
return
|
||||
_log("ehlo", src=self._peer[0], domain=args)
|
||||
self._transport.write(
|
||||
f"250-{_SMTP_MTA}\r\n"
|
||||
|
||||
@@ -114,6 +114,20 @@ def test_ehlo_returns_250_multiline(relay_mod):
|
||||
assert "PIPELINING" in combined
|
||||
|
||||
|
||||
def test_ehlo_empty_domain_rejected(relay_mod):
|
||||
proto, _, written = _make_protocol(relay_mod)
|
||||
_send(proto, "EHLO")
|
||||
replies = _replies(written)
|
||||
assert any(r.startswith("501") for r in replies)
|
||||
|
||||
|
||||
def test_helo_empty_domain_rejected(relay_mod):
|
||||
proto, _, written = _make_protocol(relay_mod)
|
||||
_send(proto, "HELO")
|
||||
replies = _replies(written)
|
||||
assert any(r.startswith("501") for r in replies)
|
||||
|
||||
|
||||
# ── OPEN RELAY MODE ───────────────────────────────────────────────────────────
|
||||
|
||||
class TestOpenRelay:
|
||||
|
||||
Reference in New Issue
Block a user