Rmilter解析邮件的流程

本文详细介绍了Milter过滤器的数据结构及其工作流程。通过解析关键函数的作用,展示了如何通过四步启动过滤器,并深入探讨了各个过滤函数的功能,如连接信息、SMTP HELO命令等的处理过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

过滤数据结构

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
  • 等待接收返回
  • 获取结果
  • 关闭文件
  • 检查结果

Rmilter处理流程

Rmilter过滤邮件流程图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值