noumenia/libmilterphp

libMilterPHP 是 PHP 中的 Postfix/Sendmail Milter 库实现。

1.9 2023-12-26 09:46 UTC

This package is auto-updated.

Last update: 2024-09-12 17:07:31 UTC


README

  _ _ _     __  __ _ _ _            ____  _   _ ____
 | (_) |__ |  \/  (_) | |_ ___ _ __|  _ \| | | |  _ \
 | | | '_ \| |\/| | | | __/ _ \ '__| |_) | |_| | |_) |
 | | | |_) | |  | | | | ||  __/ |  |  __/|  _  |  __/
 |_|_|_.__/|_|  |_|_|_|_| \___|_|  |_|   |_| |_|_|

libMilterPHP 是 PHP 中的 Postfix/Sendmail Milter 库实现。

特性

  • 高性能多进程
  • 低内存占用
  • 严格的编码标准
  • 支持所有 Milter 协议版本 2 命令
  • 监听 IP 地址/端口或 UNIX 套接字
  • 支持信号
  • 合适的内存驻留守护进程

要求

  • PHP 7.2, 7.3, 7.4, 8.0, 8.1, 8.2, 8.3
  • iconv 模块
  • posix 模块
  • sockets 模块

使用 RPM 软件包安装

您可以通过 copr 仓库安装 libMilterPHP,对于 Alma/Rocky/Oracle 企业 Linux 和 Fedora,只需使用

dnf copr enable mksanthi/noumenia
dnf install libMilterPHP

使用 Composer 安装

您可以使用 composer 安装 libMilterPHP,要获取最新版本,请使用

composer require noumenia/libmilterphp

手动安装

下载仓库并复制 libMilterPHP 项目目录到您的项目适当位置或可访问位置,例如在 /usr/share/php 之下。

如何使用

通过加载 common.inc.php 文件来加载和初始化库

require_once("/path/to/libMilterPHP/controller/common.inc.php");

创建自定义类并扩展 milter 库 \libMilterPHP\Milter

class MilterTestDaemon extends \libMilterPHP\Milter { ... }

定义所有或部分可用的回调,这些是

public function smficAbort(array $message): void	// Required
public function smficBody(array $message): void
public function smficConnect(array $message): void
public function smficMacro(array $message): void	// Required
public function smficBodyeob(array $message): void	// Required
public function smficHelo(array $message): void
public function smficHeader(array $message): void
public function smficMail(array $message): void
public function smficEoh(array $message): void
public function smficRcpt(array $message): void
public function smficQuit(array $message): void		// Required

实例化您的自定义类并将配置参数传递给构造函数。这些参数是

// Create milter object
$milter = new MilterTestDaemon(
	"postfix",			// Effective user owner
	"mail",				// Effective group owner
	"/run/daemon.pid",		// PID file
	1024,				// Process limit
	"unix:/run/daemon.sock",	// Connection string ("unix:/path/to/file.sock" or "inet:port@IP")
	array(SMFIF_QUARANTINE),	// Supported actions (array of SMFIF_* constants)
	array(SMFIP_NOBODY)		// Ignored content (array of SMFIP_* constants)
);

最后,调用 acceptConnections() 方法,在前台启动守护进程

// Accept connections
$milter->acceptConnections();

示例 milter

以下是一个示例 milter 守护进程,它会检查 FROM 并将其与已知的垃圾邮件地址匹配。

#!/usr/bin/env php
<?php
/**
 * Milter test daemon
 *
 * @copyright Noumenia (C) 2019 - All rights reserved - Software Development - www.noumenia.gr
 * @license GNU GPL v3.0
 * @package libMilterPHP
 * @subpackage miltertestdaemon
 */

require_once("./libMilterPHP/controller/common.inc.php");

class MilterTestDaemon extends \libMilterPHP\Milter {

	/**
	 * SMFIC_ABORT - Abort current filter checks
	 * @param array{size: int, command: string, binary: string} $message Message array
	 * @return void
	 */
	public function smficAbort(array $message): void
	{

		// No-op

	}

	/**
	 * SMFIC_MAIL - MAIL FROM: information - use SMFIP_NOMAIL to suppress
	 * @param array{size: int, command: string, binary: string, args: array<string>} $message Message array
	 * @return void
	 */
	public function smficMail(array $message): void
	{

		// Mark this email for quarantine
		if(preg_match('/spameri@tiscali\.it/i', strval($message['args'][0])) === 1)
			$reply = array('command' => SMFIR_QUARANTINE, 'data' => array('reason' => "Spammer in quarantine!"));
		else
			$reply = array('command' => SMFIR_CONTINUE, 'data' => array());
		$this->reply($reply);

		// Since SMFIR_QUARANTINE is a "Modification" action, the MTA is waiting for further commands...

		// So continue processing
		$reply = array('command' => SMFIR_CONTINUE, 'data' => array());
		$this->reply($reply);

	}

	/**
	 * SMFIC_BODYEOB - End of body marker
	 * @param array{size: int, command: string, binary: string} $message Message array
	 * @return void
	 */
	public function smficBodyeob(array $message): void
	{

		// Default reply
		$reply = array(

			// Set the command (char)
			'command'	=> SMFIR_CONTINUE,

			// Set the data (chars of uint32 size)
			'data'		=> array()

		);

		$this->reply($reply);

	}

}

// Create milter object
$milter = new MilterTestDaemon(
	"miltertestuser",		// Effective user owner
	"mail",				// Effective group owner
	"/run/daemon.pid",		// PID file
	1024,				// Process limit
	"unix:/run/daemon.sock",	// Connection string ("unix:/path/to/file.sock" or "inet:port@IP")
	array(SMFIF_QUARANTINE),	// Supported actions (array of SMFIF_* constants)
	array(				// Ignored content (array of SMFIP_* constants)
		SMFIP_NOCONNECT,
		SMFIP_NOHELO,
		SMFIP_NORCPT,
		SMFIP_NOBODY,
		SMFIP_NOHDRS,
		SMFIP_NOEOH
	)
);

// Accept connections
$milter->acceptConnections();

关于 SMFIF_* 动作的说明

支持的动作数组使用 SMFIF_* 常量,以定义 milter 可能采取哪些操作。这是 milter/MTA 通信的重要组成部分,用于定义 MTA 期望从 milter 接收哪些操作。所有常量都在 libMilterPHP/controller/constants.inc.php 文件中定义。

关于 SMFIP_* 忽略内容的说明

忽略内容数组使用 SMFIP_* 常量,以通知 MTA 哪些 SMTP 通信部分不应该发送给 milter。这个特性通过 MTA 和 milter 的大量带宽和 CPU 使用节省了很多。所有常量都在 libMilterPHP/controller/constants.inc.php 文件中定义。

关于 SMFIR_* 响应代码的说明

这些常量定义了 milter 可以向 MTA 提供的各种响应。标记为 "修改" 的那些仅修改电子邮件数据,修改后 MTA 期望 milter 提供另一个响应。标记为 "接受/拒绝" 的那些给出了关于电子邮件的最终指示,允许 milter 和 MTA 结束他们的通信。

常量修改接受/拒绝异步
SMFIR_ADDRCPT+[X][ ][ ]
SMFIR_DELRCPT-[X][ ][ ]
SMFIR_ACCEPTa[ ][X][ ]
SMFIR_REPLBODYb[X][ ][ ]
SMFIR_CONTINUEc[ ][X][ ]
SMFIR_DISCARDd[ ][X][ ]
SMFIR_ADDHEADERh[X][ ][ ]
SMFIR_CHGHEADERm[X][ ][ ]
SMFIR_PROGRESSp[ ][ ][X]
SMFIR_QUARANTINEq[X][ ][ ]
SMFIR_REJECTr[ ][X][ ]
SMFIR_TEMPFAILt[ ][X][ ]
SMFIR_REPLYCODEy[ ][X][ ]

关于 milter 通信协议的说明

milter 通信协议在 UNIX 套接字或 IP/端口连接上运行。它使用以下二进制格式

 size = uint32		(the size of the command+data)
 command = char		(a one character command)
 data = data[size-1]	(the data)

例如,MTA可以发送一条看起来像十六进制的消息

 +----------+---------+--------------------------------+
 | size     | command | data...                        |
 +----------+---------+----------+----------+----------+
 | 0000000d |      4f | 00000006 | 000001ff | 001fffff |
 +----------+---------+----------+----------+----------+

libMilterPHP库首先读取消息的前4个字节,并将其解释为uint32数字,该数字表示预期的消息大小。库将接着读取整个消息,一旦完成,它将命令和数据分开。命令将用于调用相应的回调函数,并将数据作为参数传递。

设计图

以下图展示了libMilterPHP实现的简化视图。

+----------------+         +-------------------+
|                |         |                   |
| common.inc.php | +---+-> | constants.inc.php |
|                |     |   |                   |
+----------------+     |   +-------------------+
                       |
Constants              |   Set system constants
Autoloader             |   Set milter protocol constants
Initialize logging     |
                       |
                       |   +----------------------------------------+
                       |   |                                        |
                       +-> | NoumeniaLibMilterPHPAutoloader.inc.php |
                       |   |                                        |
                       |   +----------------------------------------+
                       |
                       |   Define the autoloader function
                       |
                       |   +-----------------+
                       |   |                 |
                       +-> | loginit.inc.php |
                           |                 |
                           +-----------------+

                           Set log destination
                           Set message priority

                                                                +-------------------------+
                                                                |                         |
                                                                | SocketInterface.inc.php |
                                                     implements |                         |
+----------------+         +-----------------------+ +--------> +-------------------------+
|                | extends |                       |
| Milter.inc.php | <-----> | SocketManager.inc.php |
|                |         |                       | extends
+----------------+         +-----------------------+ <--------> +-----------------------+            +-------------------------+
                                                                |                       | implements |                         |
__construct()              __construct()                        | DaemonManager.inc.php | +--------> | DaemonInterface.inc.php |
reply()                    acceptConnections()                  |                       |            |                         |
processChild()             socketReadByHttp()                   +-----------------------+            +-------------------------+
                           socketWriteByHttp()
                           socketReadByNul()                    __construct()
                           socketWriteByNul()                   checkProcessLimit()
                           socketReadBySize()                   fork()
                           socketWriteBySize()                  signalHandler()
                           remoteConnect()
                           __destruct()

                           +-------------------------+
                           |                         |
                           | LoggerInterface.inc.php |
                implements |                         |
+-------------+ +--------> +-------------------------+
|             |
| Log.inc.php |            Abstract log destination interface
|             |
+-------------+ +------+-> +----------------------------+
                       |   |                            |
Event logging          |   | LogDestinationNull.inc.php | +----+
Display handler        |   |                            |      |
                       |   +----------------------------+      |
                       |                                       |
                       |   Log to null                         |
                       |                                       |
                       |   +-------------------------------+   |           +---------------------------------+
                       |   |                               |   |implements |                                 |
                       +-> | LogDestinationConsole.inc.php | +-+---------> | LogDestinationInterface.inc.php |
                       |   |                               |   |           |                                 |
                       |   +-------------------------------+   |           +---------------------------------+
                       |                                       |
                       |   Log to console                      |           Abstract log destination interface
                       |                                       |
                       |   +------------------------------+    |
                       |   |                              |    |
                       +-> | LogDestinationSyslog.inc.php | +--+
                           |                              |
                           +------------------------------+

                           Log to syslog

相关项目