Download app/validator/smtp_client.py from sitcod3/modul-x: direct link, hf CLI and curl.
- Browser
- Download file 6.96 kB
-
https://huggingface.co/spaces/sitcod3/modul-x/resolve/main/app/validator/smtp_client.py
- Command line
-
hf download hf://spaces/sitcod3/modul-x/app/validator/smtp_client.py
-
curl -L -o smtp_client.py https://huggingface.co/spaces/sitcod3/modul-x/resolve/main/app/validator/smtp_client.py
6.96 kB
| """Low-level SMTP client for mailbox probing (RFC 5321). | |
| This is the core of the autonomous engine. Unlike a one-shot probe that opens a | |
| TCP connection per address, `SmtpConnection` keeps a single session to a | |
| receiving MX open and issues many `RCPT TO` probes over it (RSET between each). | |
| Remote mail servers limit how often you may connect, and the TCP+TLS handshake | |
| dominates cost — so connection reuse is what makes deep verification of a 10k | |
| list feasible in minutes instead of hours. | |
| No message is ever transmitted: the dialogue stops after RCPT and issues RSET, | |
| so nothing can be delivered or marked as spam. | |
| Every failure mode is returned as data (`RcptOutcome`), never raised, so one | |
| dead host cannot abort a bulk job. | |
| """ | |
| from __future__ import annotations | |
| import socket | |
| from dataclasses import dataclass, field | |
| from typing import Callable, Optional | |
| from .mx_intel import is_greylist_code, is_permanent_reject, parse_rcpt_code | |
| CRLF = "\r\n" | |
| class RcptOutcome: | |
| """Result of one RCPT TO probe.""" | |
| code: Optional[int] = None | |
| message: str = "" | |
| transport_error: bool = False | |
| error: Optional[str] = None | |
| def accepted(self) -> bool: | |
| return self.code == 250 | |
| def permanent_reject(self) -> bool: | |
| return is_permanent_reject(self.code) | |
| def retryable(self) -> bool: | |
| return self.transport_error or is_greylist_code(self.code) | |
| def _default_connect(host: str, port: int, timeout: float): | |
| sock = socket.create_connection((host, port), timeout=timeout) | |
| sock.settimeout(timeout) | |
| return sock | |
| class SmtpConnection: | |
| """A single reusable SMTP session against one receiving host.""" | |
| def __init__( | |
| self, | |
| host: str, | |
| *, | |
| port: int = 25, | |
| timeout: float = 10.0, | |
| helo_domain: str = "validator.local", | |
| mail_from: str = "probe@validator.local", | |
| connect: Optional[Callable[[str, int, float], object]] = None, | |
| ): | |
| self.host = host | |
| self.port = port | |
| self.timeout = timeout | |
| self.helo_domain = helo_domain | |
| self.mail_from = mail_from | |
| self._connect = connect or _default_connect | |
| self._sock = None | |
| self.ready = False | |
| self.server_code: Optional[int] = None | |
| self.banner: str = "" | |
| self._supports_pipelining = False | |
| # ---- transport ------------------------------------------------- | |
| def open(self) -> bool: | |
| """Connect + banner + EHLO. Returns True when the session is usable.""" | |
| try: | |
| self._sock = self._connect(self.host, self.port, self.timeout) | |
| except Exception as exc: # connection refused / blocked / DNS | |
| self._sock = None | |
| self.banner = f"connect failed: {exc}" | |
| self.ready = False | |
| return False | |
| try: | |
| code, text = self._read_response() | |
| self.banner = text | |
| if code != 220: | |
| self.ready = False | |
| return False | |
| self._send(f"EHLO {self.helo_domain}") | |
| code, text = self._read_response() | |
| if code != 250: | |
| # Fall back to HELO for ancient servers. | |
| self._send(f"HELO {self.helo_domain}") | |
| code, text = self._read_response() | |
| if code != 250: | |
| self.ready = False | |
| return False | |
| self._supports_pipelining = "PIPELINING" in text.upper() | |
| self.server_code = code | |
| self.ready = True | |
| return True | |
| except Exception as exc: | |
| self.banner = f"handshake failed: {exc}" | |
| self.ready = False | |
| return False | |
| def _send(self, line: str) -> None: | |
| if self._sock is None: | |
| raise OSError("socket closed") | |
| self._sock.sendall((line + CRLF).encode("utf-8")) | |
| def _read_response(self) -> tuple[Optional[int], str]: | |
| """Read one (possibly multiline) SMTP response. | |
| Multiline replies use `250-first`, `250-more`, `250 last` — the code is | |
| repeated on every line and the final line has a space after the code. | |
| Reading only one line would desynchronise the whole session. | |
| """ | |
| if self._sock is None: | |
| raise OSError("socket closed") | |
| lines: list[str] = [] | |
| while True: | |
| raw = self._sock.recv(4096) | |
| if not raw: | |
| raise OSError("connection closed by peer") | |
| text = raw.decode("utf-8", "replace") | |
| for line in text.split(CRLF): | |
| if not line: | |
| continue | |
| lines.append(line) | |
| # "250-last" continues, "250 last" or a short line ends. | |
| if len(line) >= 4 and line[3] == "-": | |
| continue | |
| joined = "\n".join(lines) | |
| return parse_rcpt_code(line), joined | |
| # A chunk may end mid-line; keep reading. | |
| if lines and len(lines[-1]) >= 4 and lines[-1][3] == " ": | |
| joined = "\n".join(lines) | |
| return parse_rcpt_code(lines[-1]), joined | |
| # ---- probing --------------------------------------------------- | |
| def probe(self, email: str) -> RcptOutcome: | |
| """Ask the receiving server whether `email` is accepted.""" | |
| if not self.ready or self._sock is None: | |
| return RcptOutcome( | |
| transport_error=True, error="session not ready", retryable=True | |
| ) | |
| try: | |
| self._send(f"MAIL FROM:<{self.mail_from}>") | |
| code, text = self._read_response() | |
| if code is None or code >= 400: | |
| return RcptOutcome( | |
| code=code, | |
| message=text, | |
| transport_error=True, | |
| error=f"MAIL FROM rejected: {text[:120]}", | |
| ) | |
| self._send(f"RCPT TO:<{email}>") | |
| code, text = self._read_response() | |
| # Always reset so the next probe starts clean; never send DATA. | |
| try: | |
| self._send("RSET") | |
| self._read_response() | |
| except Exception: | |
| pass | |
| return RcptOutcome(code=code, message=text) | |
| except Exception as exc: | |
| return RcptOutcome(transport_error=True, error=str(exc)) | |
| def quit(self) -> None: | |
| try: | |
| if self._sock is not None: | |
| self._send("QUIT") | |
| except Exception: | |
| pass | |
| finally: | |
| self.close() | |
| def close(self) -> None: | |
| if self._sock is not None: | |
| try: | |
| self._sock.close() | |
| except Exception: | |
| pass | |
| self._sock = None | |
| self.ready = False | |
| def __enter__(self) -> "SmtpConnection": | |
| self.open() | |
| return self | |
| def __exit__(self, *exc) -> None: | |
| self.quit() |