[English](#en) | [中文](#zh)

---

<a id="en"></a>

# smtp_recv : Secure, High-Performance SMTP Server

A complete, secure-by-default SMTP server implementation in Rust, designed for high performance and modern security standards.

## Table of Contents

- [Features](#features)
- [Usage](#usage)
- [Design](#design)
- [Tech Stack](#tech-stack)
- [Directory Structure](#directory-structure)
- [API Reference](#api-reference)
- [History](#history)

## Features

- **Implicit TLS (SMTPS)**: Enforces encryption from the start of the connection.
- **SNI Support**: Automatically selects the correct certificate based on the domain name.
- **Dynamic Certificate Loading**: Integrates with `cert_by_host` for efficient SSL certificate management in SaaS environments. Certificates are loaded asynchronously on-demand, significantly reducing memory usage when handling hundreds or thousands of domains.
- **Automatic Certificate Expiration**: Leverages `cert_by_host` to refresh certificates without service restarts. The cache automatically expires certificates based on their validity period, ensuring only valid certificates are served.
- **SMTP Authentication**: Supports `AUTH PLAIN` and `AUTH LOGIN`.
- **Pipeline Support**: Implements RFC 2920 SMTP command pipelining for efficiency.
- **Async I/O**: Built on `tokio` for high-performance asynchronous processing.
- **Extensible**: Custom email handling via the `Mailer` trait.

## Usage

Implement the `Mailer` trait to define how to handle received emails.

```rust
use smtp_recv::{run, Mailer, Result};
use mail_struct::UserMail;

struct MyMailer;

impl Mailer for MyMailer {
    async fn send(&self, user_mail: UserMail) -> anyhow::Result<()> {
        println!("Received mail from: {}", user_mail.mail.sender);
        Ok(())
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    // Run SMTP server on port 465
    // Requires implementation of Auth and CertByHost traits
    run(465, my_auth, MyMailer, my_ssl).await
}
```

## Design

The server follows a secure connection flow:

1.  **Connection**: Accepts TCP connection.
2.  **TLS Handshake**: Initiates Implicit TLS immediately.
3.  **SNI Extraction**: Extracts the server name from the ClientHello.
4.  **Certificate Selection**: Fetches the appropriate certificate using `CertByHost` with `cert_by_host` integration. This enables:
    - **Asynchronous Loading**: Certificates are loaded on-demand from Kvrocks, avoiding upfront loading of all certificates for SaaS platforms with hundreds or thousands of domains.
    - **Intelligent Caching**: `cert_by_host` provides a high-performance in-memory cache with automatic expiration based on certificate validity periods.
    - **Resource Efficiency**: Only active certificates are kept in memory, significantly reducing resource consumption compared to traditional approaches.
5.  **Session**: Establishes an SMTP session (`Session::run`).
6.  **Command Processing**: Handles SMTP commands (HELO, MAIL, RCPT, DATA) with pipelining support.
7.  **Authentication**: Verifies credentials using the `Auth` trait.
8.  **Delivery**: Delivers the email via the `Mailer` trait.

## Tech Stack

-   **Runtime**: `tokio`
-   **TLS**: `rustls`, `tokio-rustls`
-   **Certificate Management**: `cert_by_host` for dynamic SSL certificate loading
-   **Error Handling**: `anyhow`, `thiserror`
-   **Logging**: `log`

## Directory Structure

```
src/
├── lib.rs       # Library entry point, server run loop
├── error.rs     # Error definitions
├── mailer.rs    # Mailer trait definition
└── session.rs   # SMTP session handling logic
```

## API Reference

### `run`

```rust
pub async fn run<A: Auth, M: Mailer>(
    port: u16,
    auth: A,
    mailer: impl Into<Arc<M>>,
    ssl: impl CertByHost,
) -> Result<()>
```

Starts the SMTP server.
-   `port`: Listening port (usually 465).
-   `auth`: Authentication provider.
-   `mailer`: Email handler.
-   `ssl`: Certificate provider.

### `Mailer` Trait

```rust
pub trait Mailer: Send + Sync + 'static {
    fn send(&self, mail: UserMail) -> impl Future<Output = Result<()>> + Send;
}
```

Implement this to handle received emails.

### `UserMail` Struct

Contains the email data (`Mail`) and the user ID associated with the recipient.

## CertByHost Trait and Integration

`smtp_recv` uses the `ssl_trait::CertByHost` trait for dynamic SSL certificate loading. This design allows you to implement custom certificate providers or use the built-in `cert_by_host` integration.

### The `CertByHost` Trait

```rust
pub trait CertByHost: Send + Sync + 'static {
    type Item: Borrow<SslConfig>;
    async fn get(&self, host: &str) -> anyhow::Result<Option<Self::Item>>;
}
```

The trait provides an asynchronous method to fetch SSL certificates based on the hostname. This enables:
- **On-demand loading**: Certificates are loaded only when needed
- **Asynchronous operations**: Non-blocking certificate retrieval
- **Flexible backends**: Support for various storage systems (Kvrocks, databases, etc.)

### Using `cert_by_host` Integration

The `cert_by_host` crate provides a ready-to-use implementation that offers:
- **High-performance caching**: In-memory cache with concurrent reads
- **Automatic expiration**: Certificates that are about to expire will automatically re-access the redis/kvrocks database and reload
- **Resource efficiency**: Only active certificates are kept in memory

Here's how to use `cert_by_host` with `smtp_recv`:

```rust
use smtp_recv::{run, Mailer, Result};
use mail_struct::UserMail;
use cert_by_host::CertByHost as CertByHostImpl;

struct MyMailer;

impl Mailer for MyMailer {
    async fn send(&self, user_mail: UserMail) -> anyhow::Result<()> {
        println!("Received mail from: {}", user_mail.mail.sender);
        Ok(())
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    // Initialize cert_by_host system
    xboot::init().await?;

    // Create cert_by_host provider
    let ssl_provider = CertByHostImpl;

    // Run SMTP server with cert_by_host integration
    run(465, my_auth, MyMailer, ssl_provider).await
}
```

### Custom Certificate Provider Example

You can also implement your own certificate provider:

```rust
use ssl_trait::{CertByHost, SslConfig};
use rustls::pki_types::{PrivateKeyDer, CertificateDer};
use std::borrow::Borrow;

struct MyCertProvider;

impl CertByHost for MyCertProvider {
    type Item = SslConfig;
    
    async fn get(&self, host: &str) -> anyhow::Result<Option<Self::Item>> {
        // Your custom logic to load certificates
        // e.g., from database, filesystem, or external API
        if host == "example.com" {
            let key = PrivateKeyDer::Pkcs8(/* load key */);
            let cert = vec![CertificateDer::from(/* load cert */)];
            Ok(Some(SslConfig { key, cert }))
        } else {
            Ok(None)
        }
    }
}
```

### Benefits for SaaS Applications

When running a SaaS platform with hundreds or thousands of domains:
1. **Memory efficiency**: Only certificates for active domains are loaded
2. **Automatic refresh**: Certificates that are about to expire will automatically re-access the redis/kvrocks database and reload
3. **No service restart**: New certificates can be added without restarting the server
4. **Scalability**: Handles large numbers of domains with minimal resource usage

## History

**The Story of Port 465**

In 1997, port 465 was registered for "SMTPS" - SMTP over SSL. It was intended to be the secure equivalent of port 25, encrypting the connection from the very beginning (Implicit TLS). However, it was never officially standardized by the IETF.

In 1998, the IETF standardized STARTTLS on port 587, which starts as plain text and upgrades to TLS. Port 465 was reassigned and considered deprecated for SMTP.

Despite this, many major email providers (like Gmail) continued to support port 465 because it is often more robust against misconfigured firewalls or intermediaries that might strip the STARTTLS command. Today, port 465 with Implicit TLS has seen a resurgence and is widely recommended for secure email submission, offering a "secure or nothing" approach that prevents downgrade attacks.

---

## About

This project is an open-source component of [js0.site ⋅ Refactoring the Internet Plan](https://js0.site).

We are redefining the development paradigm of the Internet in a componentized way. Welcome to follow us:

* [Google Group](https://groups.google.com/g/js0-site)
* [js0site.bsky.social](https://bsky.app/profile/js0site.bsky.social)

---

<a id="zh"></a>

# smtp_recv : 安全高性能的 SMTP 服务器

一个基于 Rust 实现的、默认安全的完整 SMTP 服务器，专为高性能和现代安全标准而设计。

## 目录

- [功能特性](#功能特性)
- [使用演示](#使用演示)
- [设计思路](#设计思路)
- [技术堆栈](#技术堆栈)
- [目录结构](#目录结构)
- [API 参考](#api-参考)
- [历史背景](#历史背景)

## 功能特性

- **隐式 TLS (SMTPS)**: 连接建立伊始即强制加密，杜绝明文传输。
- **SNI 支持**: 根据客户端请求的域名自动选择对应的 SSL 证书。
- **动态证书加载**: 集成 `cert_by_host` 实现高效的 SSL 证书管理，特别适合 SaaS 环境。证书按需异步加载，在处理成百上千个域名时显著降低内存占用。
- **自动证书过期**: 利用 `cert_by_host` 实现不重启服务的证书自动刷新功能。缓存会根据证书有效期自动过期，确保只提供有效的证书。
- **SMTP 认证**: 支持 `AUTH PLAIN` 和 `AUTH LOGIN` 认证机制。
- **流水线支持**: 实现 RFC 2920 SMTP 命令流水线，提升传输效率。
- **异步 I/O**: 基于 `tokio` 构建，提供高性能的异步处理能力。
- **可扩展性**: 通过 `Mailer` trait 自定义邮件处理逻辑。

## 使用演示

实现 `Mailer` trait 来定义如何处理接收到的邮件。

```rust
use smtp_recv::{run, Mailer, Result};
use mail_struct::UserMail;

struct MyMailer;

impl Mailer for MyMailer {
    async fn send(&self, user_mail: UserMail) -> anyhow::Result<()> {
        println!("收到来自 {} 的邮件", user_mail.mail.sender);
        Ok(())
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    // 在 465 端口运行 SMTP 服务器
    // 需要提供 Auth 和 CertByHost trait 的实现
    run(465, my_auth, MyMailer, my_ssl).await
}
```

## 设计思路

服务器采用安全优先的连接处理流程：

1.  **连接建立**: 接受 TCP 连接。
2.  **TLS 握手**: 立即启动隐式 TLS 握手。
3.  **SNI 提取**: 从 ClientHello 中提取服务器名称。
4.  **证书选择**: 使用集成 `cert_by_host` 的 `CertByHost` 获取匹配的证书。这实现了：
    - **异步加载**: 证书按需从 Kvrocks 加载，避免了在 SaaS 平台有成百上千个域名时一次性加载所有证书。
    - **智能缓存**: `cert_by_host` 提供了基于证书有效期自动过期的高性能内存缓存。
    - **资源高效**: 只有活跃的证书保留在内存中，相比传统方法显著降低了资源消耗。
5.  **会话建立**: 创建 SMTP 会话 (`Session::run`)。
6.  **命令处理**: 处理 SMTP 命令 (HELO, MAIL, RCPT, DATA)，支持流水线。
7.  **身份验证**: 使用 `Auth` trait 验证用户凭据。
8.  **邮件投递**: 通过 `Mailer` trait 将邮件交给上层应用处理。

## 技术堆栈

-   **运行时**: `tokio`
-   **TLS**: `rustls`, `tokio-rustls`
-   **证书管理**: `cert_by_host` 用于动态 SSL 证书加载
-   **错误处理**: `anyhow`, `thiserror`
-   **日志**: `log`

## 目录结构

```
src/
├── lib.rs       # 库入口，包含服务器运行主循环
├── error.rs     # 错误类型定义
├── mailer.rs    # Mailer trait 定义
└── session.rs   # SMTP 会话处理逻辑
```

## API 参考

### `run`

```rust
pub async fn run<A: Auth, M: Mailer>(
    port: u16,
    auth: A,
    mailer: impl Into<Arc<M>>,
    ssl: impl CertByHost,
) -> Result<()>
```

启动 SMTP 服务器。
-   `port`: 监听端口（通常为 465）。
-   `auth`: 认证服务提供者。
-   `mailer`: 邮件处理器。
-   `ssl`: 证书提供者。

### `Mailer` Trait

```rust
pub trait Mailer: Send + Sync + 'static {
    fn send(&self, mail: UserMail) -> impl Future<Output = Result<()>> + Send;
}
```

实现此 trait 以处理接收到的邮件。

### `UserMail` Struct

包含邮件数据 (`Mail`) 和接收者的用户 ID。

## CertByHost Trait 与集成

`smtp_recv` 使用 `ssl_trait::CertByHost` trait 来实现动态 SSL 证书加载。这种设计允许您实现自定义的证书提供者，或使用内置的 `cert_by_host` 集成。

### `CertByHost` Trait

```rust
pub trait CertByHost: Send + Sync + 'static {
    type Item: Borrow<SslConfig>;
    async fn get(&self, host: &str) -> anyhow::Result<Option<Self::Item>>;
}
```

该 trait 提供了一个异步方法来根据主机名获取 SSL 证书。这实现了：
- **按需加载**: 证书仅在需要时加载
- **异步操作**: 非阻塞的证书获取
- **灵活的后端**: 支持各种存储系统（Kvrocks、数据库等）

### 使用 `cert_by_host` 集成

`cert_by_host` 库提供了一个开箱即用的实现，提供：
- **高性能缓存**: 支持并发读取的内存缓存
- **自动过期**: 将要过期的证书，会自动重新访问redis/kvrocks数据库重新加载
- **资源高效**: 只有活跃的证书保留在内存中

以下是如何将 `cert_by_host` 与 `smtp_recv` 结合使用：

```rust
use smtp_recv::{run, Mailer, Result};
use mail_struct::UserMail;
use cert_by_host::CertByHost as CertByHostImpl;

struct MyMailer;

impl Mailer for MyMailer {
    async fn send(&self, user_mail: UserMail) -> anyhow::Result<()> {
        println!("收到来自 {} 的邮件", user_mail.mail.sender);
        Ok(())
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    // 初始化 cert_by_host 系统
    xboot::init().await?;

    // 创建 cert_by_host 提供者
    let ssl_provider = CertByHostImpl;

    // 使用 cert_by_host 集成运行 SMTP 服务器
    run(465, my_auth, MyMailer, ssl_provider).await
}
```

### 自定义证书提供者示例

您也可以实现自己的证书提供者：

```rust
use ssl_trait::{CertByHost, SslConfig};
use rustls::pki_types::{PrivateKeyDer, CertificateDer};
use std::borrow::Borrow;

struct MyCertProvider;

impl CertByHost for MyCertProvider {
    type Item = SslConfig;

    async fn get(&self, host: &str) -> anyhow::Result<Option<Self::Item>> {
        // 您的自定义证书加载逻辑
        // 例如：从数据库、文件系统或外部 API 加载
        if host == "example.com" {
            let key = PrivateKeyDer::Pkcs8(/* 加载私钥 */);
            let cert = vec![CertificateDer::from(/* 加载证书 */)];
            Ok(Some(SslConfig { key, cert }))
        } else {
            Ok(None)
        }
    }
}
```

### 对 SaaS 应用的优势

当运行拥有成百上千个域名的 SaaS 平台时：
1. **内存高效**: 只有活跃域名的证书被加载
2. **自动刷新**: 将要过期的证书，会自动重新访问redis/kvrocks数据库重新加载
3. **无需重启服务**: 可以添加新证书而无需重启服务器
4. **可扩展性**: 以最小的资源使用处理大量域名

## 历史背景

**端口 465 的前世今生**

1997 年，端口 465 被注册用于 "SMTPS" —— 即基于 SSL 的 SMTP。它的初衷是作为端口 25 的安全版本，从连接一开始就进行加密（隐式 TLS）。然而，这种方式从未被 IETF 正式标准化。

1998 年，IETF 标准化了运行在 587 端口的 STARTTLS 协议，它允许连接以明文开始，然后升级到 TLS。端口 465 随即被重新分配，并在 SMTP 协议中被视为"已弃用"。

尽管如此，许多主流邮件服务商（如 Gmail）仍坚持支持端口 465。这是因为相比于 STARTTLS，隐式 TLS 更能抵抗配置错误的防火墙或中间人干扰（这些干扰可能会剥离 STARTTLS 命令）。如今，端口 465 及其隐式 TLS 模式迎来了复兴，被广泛推荐用于安全的邮件提交，它提供了一种"要么安全，要么不连"的强硬态度，有效防止了降级攻击。

---

## 关于

本项目为 [js0.site ⋅ 重构互联网计划](https://js0.site) 的开源组件。

我们正在以组件化的方式重新定义互联网的开发范式，欢迎关注：

* [谷歌邮件列表](https://groups.google.com/g/js0-site)
* [js0site.bsky.social](https://bsky.app/profile/js0site.bsky.social)
