王朝网络
分享
 
 
 

基于postfix实现邮件用户的分布式处理

王朝other·作者佚名  2008-05-31
宽屏版  字体: |||超大  

基于一些特殊的考虑,现在邮件系统经常面临把用户分散在几个不同服务器上的需求。这里谈一下我在使用postfix完成该功能时的一点认识和实践,需求所限,没有涉及到用户通过SMTP或者POP3登录收发邮件的部分。请各位朋友谅解并指正。

一般分布情况下,MX和用户所在的服务器是分离的;本文所描述的即是这种设计。下面把用户所在的服务器简称为单元(UNIT)。

MX

MX是接受邮件的关键,可以由一台或一组服务器组成。它对外开放SMTP端口,负责接受邮件并转投到收件人所在单元服务器。

简单的说,一封信投递到我们的系统中,首先会进入MX,MX检查收件人是否存在,然后根据用户的信息把信通过smtp协议转发到他所在的UNIT上。

由于用户分散在不同主机中,所以MX对用户进行查证的工作必须进行特殊的处理;这里大家可以集思广益,设计一两百种方案我看是没什么问题。不过,我们的任务是说明postfix如何处理用户分布的情况,所以我们假定已经有一个写好了的函数,调用它就可以查证用户了。我们利用它,就可以实现dict_unit这个map。利用选项 local_recipient_maps,使外部发信到我们的用户时,调用我们指定的接口对用户查证;输入为收件人的邮件地址。

local_recipient_maps = unit:smtpcheck

这里dict_unit的实现在后面详细解释。

用户查证是在访问者发出“RCPT ”指令时,如果postfix确认了该用户存在,就开始准备接收“DATA”指令发过来的邮件正文了。成功收到邮件后,通过配置好的 transport_maps,postfix可以询问我们的dict_unit,获得下一步处理的指令。比如,输入收件人邮件地址“user@my.com”,输出“smtp:unit12.my.com”,这就会让postfix通过smtp把信转投到unit12.my.com域名的这台机器上。

transport_maps = unit:transportmx

UNIT

邮件系统中的用户通过注册、转移等方式,被存放在不同的单元服务器中。他们负责接收从MX转投过来的信件,并最终放入存储系统中。

在这里,一样会使用local_recipient_maps检查收件人是否真实存在。但与MX不同的是,这是信件的最终目的地,transport_maps查询的结果就不能再是“smtp:unit12.my.com”,要设置成合适的本地处理程序,比如local或者指定mda的名称(在etc/master.cf中设置)等。

local_recipient_maps = unit:smtpcheck

transport_maps = unit:transportunit

自定义的map

这里是dict_unit的实现示意。通过这个map,我们可以把前面提到的用户查证、转发邮件、处理邮件的所有指引工作在一起完成;实际上,它相当于一个多向的阀门,可以让邮件流向不同的方向。

注意,这仅仅是示意的代码。有需要做开发的朋友请自行参考dict_pam[1]的实现,那是真正可执行的代码。请不要写信询问我如何编写并调试可用的dict,google[2]和postfix官方网站[3]上都可以查到。

这个函数负责处理用户查证的工作。

static int dict_unit_smtpcheck(const char *name, char *result)

{

char userhost[128] = {0};

if (msg_verbose)

msg_info("dict_unit: lookup smtp user: %s", name);

ret = getuser(name, %26amp;userhost);

if (ret != 0){

if (msg_verbose)

msg_warn("dict_unit: lookup smtp failed, name: %s", name);

return 1;

}

if (msg_verbose)

msg_info("dict_unit: lookup transport successed, name: %s, host: %s, path: %s",

name,

userhost);

strncpy(result, userhost, sizeof(userhost));

return 0;

}

这个函数负责控制MX服务中邮件的流向。

static int dict_unit_transportmx(const char *name, char *result)

{

char userhost[128] = {0};

if (msg_verbose)

msg_info("dict_unit: lookup transport user: %s", name);

ret = getuser(name, %26amp;userhost);

if (ret != 0){

if (msg_verbose)

msg_warn("dict_unit: lookup transport failed, name: %s", name);

return 1;

}

if (msg_verbose)

msg_info("dict_unit: lookup transport successed, name: %s, host: %s,",

name,

userhost);

/* result formal like: "smtp:unit12.my.com" */

strncpy(result, "smtp:", sizeof(userhost));

strncat(result, userhost, sizeof(userhost)-strlen("smtp:"));

return 0;

}

这个函数负责控制UNIT中的邮件的流向。值得一提的是,这里我们使用了var_myhostname来鉴别该用户是不是本机的(很简陋,演示嘛),要正确编译的话,在src/global/dict_unit.c中,必须加入这一行:

#include "mail_params.h"

要正确运行的话,则必须再在etc/main.cf中配置好正确的本机域名:

myhostname = unit12.my.com

最后,我们这里的是调用了一个定制的MDA程序“mymda”来完成最终信件的投递。要让它能正确运行,得配置etc/master.cf,请自行查阅手册。

static int dict_unit_transportunit(const char *name, char *result)

{

char userhost[128] = {0};

if (msg_verbose)

msg_info("dict_unit: lookup transport user: %s", name);

ret = getuser(name, %26amp;userhost);

if (ret != 0){

if (msg_verbose)

msg_warn("dict_unit: lookup transport failed, name: %s", name);

return 1;

}

if (msg_verbose)

msg_info("dict_unit: lookup transport successed, name: %s, host: %s",

name,

userhost);

/* for local user, result is: "mymda:"

* for remote user, result is: "smtp:mx.my.com"

*/

if (0==strnstr(userhost, var_myhostname, sizeof(userhost)))

/* MX can delivery it */

strncpy(result, "smtp:mx.my.com", sizeof(userhost));

else

/* local MDA is ok */

strncpy(result, "mymda:", sizeof(userhost));

return 0;

}

最后,为了让上面的三个lookup能真正工作起来,要在dict对外提供的接口处调用他们。这个分流是通过lookup时传入的dict-service来辨认的,也就是在main.cf中的配置“unit:smtpcheck”冒号后面的部分。

static const char *dict_unit_lookup(DICT *dict, const char *name)

{

DICT_unit *dp = (DICT_unit *) dict;

char result[128] = {0};

int ret;

if (0 == strcmp(vstring_str(dp-service), "smtpcheck"))

ret = dict_unit_smtpcheck(name, result);

else if (0 == strcmp(vstring_str(dp-service), "transportmx"))

ret = dict_unit_transportmx(name, result);

else if (0 == strcmp(vstring_str(dp-service), "transportunit"))

ret = dict_unit_transportunit(name, result);

else

{

if (msg_verbose)

msg_error("dict_unit: lookup unknown service %s, name %s.",

vstring_str(dp-service), name);

return (0);

}

if ((0 != ret) || (0 == result[0]))

return (0);

succ:

if (msg_verbose)

msg_info("dict_unit: service %s, name: %s, result: %s.",

vstring_str(dp-service), name, result);

vstring_strncpy(dp-result, result, sizeof(result));

return vstring_str(dp-result);

}

组装在一起,这个控制邮件流向的大阀门就算是完工了。

以上是利用postfix实现用户分布的一次实践。当然,方案并非只有一种,大家各有不同。请大家多多讨论,共同进步。

本文基于创作共用协议[4]之“署名-保持一致”[5]许可证发表,使用前请阅读该协议。-- xyb

参考

[1] http://d.scn.ru/proj/postfix/dict_pam/

[2] http://www.google.com/

[3] http://www.postfix.org/

[4] http://www.creativecommons.cn/

[5] http://creativecommons.cn/licenses/by-sa/1.0/

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有