过滤数据结构
struct smfiDesc smfilter =
{
"rmilter", /* filter name */
SMFI_VERSION, /* version code -- do not change */
SMFIF_ADDHDRS | SMFIF_CHGHDRS | SMFIF_ADDRCPT | SMFIF_DELRCPT, /* flags */
mlfi_connect, /* connection info filter */
mlfi_helo, /* SMTP HELO command filter */
mlfi_envfrom, /* envelope sender filter */
mlfi_envrcpt, /* envelope recipient filter */
mlfi_header, /* header filter */
mlfi_eoh, /* end of header */
mlfi_body, /* body block filter */
mlfi_eom, /* end of message */
mlfi_abort, /* message aborted */
mlfi_close, /* connection cleanup */
#if (SMFI_PROT_VERSION >= 4)
NULL, /* unknown situation */
mlfi_data, /* SMTP DATA callback */
NULL /* Negotiation callback */
#endif
};
struct mlfi_priv {
struct rmilter_inet_address priv_addr; //rmilter网络地址
char priv_ip[INET6_ADDRSTRLEN + 1];
char priv_hostname[ADDRLEN + 1]; //客户端主机名
char priv_helo[ADDRLEN + 1]; //helo/ehlo
char priv_from[ADDRLEN + 1]; //邮件来源
char priv_user[ADDRLEN + 1]; //用户
char message_id[ADDRLEN + 1];
struct rcpt *rcpts; //收件人地址
char *priv_subject;
int priv_rcptcount; //收件人地址数量
struct {
char *header_name;
char *header_value;
} priv_cur_header;
struct {
char *value;
size_t len;
} priv_cur_body;
char mlfi_id[14]; //milter_filterID
char queue_id[32];
char mta_tag[ADDRLEN + 1];
char reply_id[ADDRLEN + 33];
#ifdef HAVE_PATH_MAX
char file[PATH_MAX];
#elif defined(HAVE_MAXPATHLEN)
char file[MAXPATHLEN];
#else
#error "neither PATH_MAX nor MAXPATHLEN defined"
#endif
FILE *fileh;
int filed;
struct timeval conn_tm; //时间
struct rule* matched_rules[STAGE_MAX];
long eoh_pos;
short int strict;
/* Config serial */
short int serial; //重载配置的数
short int has_return_path;
short int complete_to_beanstalk;
short int has_whitelisted;
short int authenticated;
#ifdef WITH_DKIM
DKIM *dkim;
struct dkim_domain_entry *dkim_domain;
#endif
};
过滤启动
第一步 指定要使用的套接字,smfi_setconn
#main.c 321
smfi_setconn (cfg->sock_cred);
第二步 注册一个过滤器,smfi_register
#main.c 322-325
if (smfi_register (smfilter) == MI_FAILURE) {
msg_err("smfi_register failed");
exit (EX_UNAVAILABLE);
}
第三步 尝试创建接口套接字,smfi_opensocket
#main.c 327-330
if (smfi_opensocket (true) == MI_FAILURE) {
msg_err("Unable to open listening socket");
exit (EX_UNAVAILABLE);
}
第四步 控制移交给 libmilter 事件循环
#main.c 355
r = smfi_main ();
Rmilter过滤函数
mlfi_connect connection info(连接信息)
mlfi_helo SMTP HELO/EHLO command(SMTP HELO/ EHLO命令)
mlfi_envfrom envelope sender(信封发送方)
mlfi_envrcpt envelope recipient(信封发送方)
mlfi_data DATA command(信封发送方)
mlfi_unknown Unknown SMTP command(信封发送方)
mlfi_header header(信封发送方)
mlfi_eoh end of header(结束的头)
mlfi_body body block(主题)
mlfi_eom end of message(消息结束)
mlfi_abort message aborted(消息失败)
mlfi_close connection cleanup(连接清理)
check_clamscan clamav扫描
clamscan 发送文件到clamd,并检查返回值。
clamscan_socket 发送文件到指定主机进行扫描,并检查返回值
static sfsistat mlfi_connect(SMFICTX * ctx, char * hostname, _SOCK_ADDR * addr);——过滤连接。
- 申请mlfi_priv内存空间,获取相应数据内容。
- 判断连接协议类型
- 判断并获取ctx中“j”(?)的值
- 判断主机名
- 判断并获取ctx中”{daemon_name}”的值。
- 设置mlfi_priv专用数据指针。
static sfsistat mlfi_helo(SMFICTX *ctx, char *helostr)
- 访问ctx的特定数据结构
- 为mlfi_priv赋值
static sfsistat mlfi_envfrom(SMFICTX *ctx, char **envfrom)
- 访问ctx的特定数据结构
- 判断并获取ctx中“{mail_addr}”的值
- 格式化邮件地址
- 判断并获取ctx中“{client_name}”的值
- 判断并获取ctx中“{auth_authen}”的值
- 检查DKIM
static sfsistat mlfi_envrcpt(SMFICTX *ctx, char **envrcpt)
- 访问ctx的特定数据结构
- 获取recipient address
- 判断是否是白名单地址
- 检查速率限制
static sfsistat mlfi_data(SMFICTX *ctx)
- 访问ctx的特定数据结构
- 判断并获取ctx中“{i}”的值,设置队列ID
- 判断队列
- 验证身份
- 判断是否是灰名单
static sfsistat mlfi_header(SMFICTX * ctx, char *headerf, char *headerv)
- 访问ctx的特定数据结构
- 判断header内容
- 创建临时文件
- 检查DKIM
- 将header写入临时文件
- 用正则检查header
static sfsistat mlfi_eoh(SMFICTX * ctx)
- 访问ctx的特定数据结构
- 判断header的临时文件是否存在
- 检查DKIM
static sfsistat mlfi_eom(SMFICTX * ctx)
- 访问ctx的特定数据结构
- 设置队列ID
- 判断是否能否正常扫描邮件
- 设置实际的pos,将消息发送到beanstalk
- 检查文件大小
- 检查DCC
- 判断spam白名单,检查spam
- 判断clamav白名单,检查clamav
- 添加DKIM
- 检查DKIM
- 清理ctx
static sfsistat mlfi_close(SMFICTX * ctx)
- 访问ctx的特定数据结构
- 清理ctx
- 释放mlfi_priv空间
- 释放ctx空间
static sfsistat mlfi_abort(SMFICTX * ctx)
- 清理ctx
static sfsistat mlfi_cleanup(SMFICTX * ctx, bool ok)
- 访问ctx的特定数据结构
- 关闭header临时文件夹
- 关闭邮件文件连接
- 清理mlfi_priv数据
static sfsistat mlfi_body(SMFICTX * ctx, u_char * bodyp, size_t bodylen)
- 访问ctx的特定数据结构
- 判断header临时文件
- 向header临时文件写入数据
static int check_clamscan(void *ctx, struct mlfi_priv *priv, char *strres, size_t strres_len)
- 使用clamAV扫描
- 重置virusname为non-viruses
int clamscan (void *ctx, struct mlfi_priv *priv, struct config_file *cfg, char *strres, size_t strres_len)
- 保存开始扫描的时间
- 尝试使用可用的服务器进行扫描
- 将文件发送到clamAV服务器
- 检查扫描结果
static int clamscan_socket(const char *file, const struct clamav_server *srv, char *strres, size_t strres_len, struct config_file *cfg, struct mlfi_priv *priv)
- 判断服务器是否找到
- 连接服务器
- 打开文件,读取数据
- 向服务器发送数据数据
- 发送数据流
- 发送zero chunk
- 等待接收返回
- 获取结果
- 关闭文件
- 检查结果