noumenia/libmilterphp 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

noumenia/libmilterphp

Composer 安装命令:

composer require noumenia/libmilterphp

包简介

libMilterPHP is a Postfix/Sendmail Milter library implementation in PHP.

README 文档

README

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

libMilterPHP is a Postfix/Sendmail Milter library implementation in PHP.

Features

  • High-performance multi-process
  • Low memory footprint
  • Strict coding standards
  • Support for milter protocol v2, v3 and v4
  • Listen on IP address/port or UNIX sockets
  • Support for signals
  • Proper memory-resident daemon

Requirements

Install with RPM packages

You may install libMilterPHP via the copr repository, for Alma/Rocky/Oracle Enterprise Linux and Fedora, simply use:

dnf copr enable mksanthi/noumenia
dnf install libMilterPHP

Install with Composer

You may install libMilterPHP with composer, to get the latest version use:

composer require noumenia/libmilterphp

Install manually

Download the repository and copy the libMilterPHP project directory in the appropriate place within your project or within an accessibe location, for example under /usr/share/php.

How to use

Loading libraries (RPM package)

Load and initialize the library by loading the common.inc.php file, for each required library:

require_once("/usr/share/php/libLoggerPHP/controller/common.inc.php");
require_once("/usr/share/php/libSDManagerPHP/controller/common.inc.php");
require_once("/usr/share/php/libMilterPHP/controller/common.inc.php");

Loading libraries (composer)

require_once("./vendor/autoload.php");

PHP class

Create your custom class and extend the milter library \libMilterPHP\Milter:

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

Implement your callback functions

Implement all or some of the available callback functions, within your custom class, these functions are:

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
public function smficUnknown(array $message): void
public function smficData(array $message): void

Instantiate your custom class

Instantiate your custom class and pass the configuration parameters to the constructor. These parameters are:

// Create milter object
$milter = new MilterTestDaemon(
	"postfix",			// Daemon user owner
	"mail",				// Daemon 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")
	10,				// Socket/Stream timeout in seconds
	array(SMFIF_QUARANTINE),	// Supported actions (array of SMFIF_* constants)
	array(SMFIP_NOBODY)		// Ignored content (array of SMFIP_* constants)
);

Accept connections

Finally, call the acceptConnections() method, to start the daemon in the foreground:

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

Sample milter

The following is a sample milter daemon that checks the FROM and matches it with a known spammer address.

#!/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
 */

if(is_file(__DIR__ . "/vendor/autoload.php")) {

	// Autoload via composer
	require_once(__DIR__ . "/vendor/autoload.php");

} else {

	// Autoload via RPM packages
	require_once("/usr/share/php/libLoggerPHP/controller/common.inc.php");
	require_once("/usr/share/php/libSDManagerPHP/controller/common.inc.php");
	require_once("/usr/share/php/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

		// Use the helper function
		$this->replyContinue();

		// Or use the reply function directly
		//$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
	{

		// Use the helper function
		$this->replyContinue();

		// Or use the reply function directly
		//$reply = array('command' => SMFIR_CONTINUE, 'data' => array());
		//$this->reply($reply);

		// Here is an example for a rejection
		//$this->replyReject("Blocked by MilterTestDaemon");

	}

}

// Create milter object
$milter = new MilterTestDaemon(
	"miltertestuser",		// Daemon user owner
	"mail",				// Daemon 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")
	10,				// Socket/Stream timeout in seconds
	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,
		SMFIP_NOUNKNOWN,
		SMFIP_NODATA
	)
	// ,2 or ,3 etc			// You may set a lower milter protocol version
);

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

Notes about SMFIF_* actions

The supported actions array uses SMFIF_* constants, to define which actions MAY be taken by the milter. This is an essential part of the milter/MTA conversation, to define what actions the MTA will expect from the milter. All constants are defined within the libMilterPHP/controller/constants.inc.php file.

Notes about SMFIP_* ignored content

The ignored content array uses SMFIP_* constants, to inform the MTA which parts of the SMTP conversation should NOT be sent to the milter. This feature saves a lot of bandwidth and CPU usage by both the MTA and the milter. All constants are defined within the libMilterPHP/controller/constants.inc.php file.

Notes about SMFIR_* response codes

These constants define various responses that the milter can give to the MTA. The ones marked as "Modification" only make changes to the email data, after a modification the MTA expects the milter to provide another response. The ones marked as "Accept/reject" give a final indication about the email and allow the milter and MTA to end their conversation.

ConstantValueModificationAccept/rejectAsynchronous
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][ ]

Notes about the milter communication protocol

The milter communication protocol runs over UNIX sockets or over an IP/port connection. It uses the following binary format:

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

For example, the MTA could send a message that in hex looks like:

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

The libMilterPHP library will first read the first 4 bytes of the message and interpret it as a uint32 number, which indicates the size of the message that is expected. The library will then keep reading for the whole message and once done, it will separate the command from the data. The command will be used to call the equivalent callback function with the data as a parameter.

Notes about milter protocol versions

version 3

Version 3 adds support for unknown SMTP commands:

  • SMFIC_UNKNOWN
  • SMFIP_NOUNKNOWN

version 4

Version 4 adds support for the DATA command:

  • SMFIC_DATA
  • SMFIP_NODATA

Design diagram

The following diagram shows a simplified view of the libMilterPHP implementation.

+----------------+         +-------------------+
|                |         |                   |
| common.inc.php | +---+-> | constants.inc.php |
|                |     |   |                   |
+----------------+     |   +-------------------+
                       |
Constants              |   Set system constants
Autoloader             |   Set milter protocol constants
Initialize logging     |
                       |
                       |   +----------------------------------------+
                       |   |                                        |
                       +-> | NoumeniaLibMilterPHPAutoloader.inc.php |
                       |   |                                        |
                       |   +----------------------------------------+
                       |
                       |   Define the autoloader function
                       |
                       |   +-----------------+ use
                       |   |                 | <-----> libLoggerPHP\Log
                       +-> | loginit.inc.php | <-----> libLoggerPHP\LogDestinationNull
                           |                 | <-----> libLoggerPHP\LogDestinationSyslog
                           +-----------------+

                           Set log destination
                           Set message priority

+----------------+ extends
|                | <-----> libSDManager\SocketManager
| Milter.inc.php |
|                | use
+----------------+ +-----> +---------------------+
                           |                     |
__construct()              | ReplyHelper.inc.php |
reply()                    |                     |
processChild()             +---------------------+

                           replyAddRecipient()
                           replyDeleteRecipient()
                           replyAccept()
                           replyReplaceBodyChunk()
                           replyContinue()
                           replyDiscard()
                           replyAddHeader()
                           replyChangeHeader()
                           replyDeleteHeader()
                           replyProgress()
                           replyQuarantine()
                           replyReject()
                           replyTempFail()
                           replyReplyCode()

Related projects

noumenia/libmilterphp 适用场景与选型建议

noumenia/libmilterphp 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 108 次下载、GitHub Stars 达 2, 最近一次更新时间为 2022 年 12 月 08 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「php」 「postfix」 「milter」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 noumenia/libmilterphp 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 noumenia/libmilterphp 我们能提供哪些服务?
定制开发 / 二次开发

基于 noumenia/libmilterphp 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 108
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 19
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: GPL-3.0-only
  • 更新时间: 2022-12-08