大麻 发表于 2007-07-25 16:33

qmail-remote 缺陷造成but_connection_died的补丁

最近发现 qmail 发送日志中存在大量的如下的日志记录:

delivery 10277: deferral: Connected_to_xxx.xxx.xxx.xxx_but_connection_died._(#4.4.2)/

经过实际跟踪的结果,发现原来 qmail-remote 投递程序中存在明显的缺陷, 该缺陷
表现为:

如果远程主机 remote 禁止本地连接(qmail 最常见的做法是在 tcpserver 中使用 tcp.smtp.cdb 来
deny 指定的主机), telnet 记录如下:

代码:
# telnet xxx.xxx.xxx.xxx 25
Trying xxx.xxx.xxx.xxx...
Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx).
Escape character is '^]'.
Connection closed by foreign host.


从上面跟踪可以得知, 远程主机判断来访的IP后, 随即关闭了连接. 而 qmail-remote 无法正
确识别改种情况, 如果发送的域名地址有多个 MX 记录, 则 qmail 总是试图连接该优先级别的
地址, 而不会自动转向更低一级别的 MX 记录, 从而造成邮件在队列中堆积.

解决方法及补丁:

通过分析 qmail-remote.c 程序发现, qmail-remote 调用了 timeoutconn() 函数,

代码:
for (i = 0;i < ip.len;++i) if (ip.ix.pref < prefme) {
    if (tcpto(&ip.ix.ip)) continue;

    smtpfd = socket(AF_INET,SOCK_STREAM,0);
    if (smtpfd == -1) temp_oserr();

    if (timeoutconn(smtpfd,&ip.ix.ip,(unsigned int) port,timeoutconnect) == 0) {
      tcpto_err(&ip.ix.ip,0);
      partner = ip.ix.ip;
#ifdef TLS
      partner_fqdn = ip.ix.fqdn;
#endif
      smtp(); /* does not return */
    }
    tcpto_err(&ip.ix.ip,errno == error_timeout);
    close(smtpfd);
}


为此, 可在执行前 smtp() 让程序判断 socket 是否内容可读, 考虑到通用性, 在 timeoutconn.c
更改如下:

代码:
if (FD_ISSET(s,&wfds)) {
    int dummy;
    dummy = sizeof(sin);
    if (getpeername(s,(struct sockaddr *) &sin,&dummy) == -1) {
      read(s,&ch,1);
      return -1;
    }
   ndelay_off(s);
    if (recv(s, &ch, 1, MSG_PEEK) <= 0) return -1; // 此行是增加的
    return 0;
}


在程序中增加 recv 从 socket 读取一个字符数据, 如果失败, 表示远程端已经断开连接,
直接返回失败 (-1). 由于 recv 设置了 MSG_PEEK 参数, 所以所读的数据不会中缓冲区移除,
从而不会影响后面程序正常的 SMTP 会话.

特别注意,本补丁对在 smtp 会话过程中的中断无效。

原文在:
http://bbs.igenus.org/phpBB2/viewtopic.php?p=8448#8448

[ 本帖最后由 大麻 于 2007-7-25 16:35 编辑 ]

alvis 发表于 2007-07-25 22:53

這個,能不能給做個 patch 文件阿

hmily36 发表于 2010-09-15 09:51

请教大麻
修改后是否需要重新编译qmail?

Plesk 发表于 2011-02-18 10:28

lz,你好。最近我也遇到这样的为你。因为对qmail不是很懂,你上面的方法怎么去操作,应用到qmail中呢?

borenbao 发表于 2013-09-27 14:21

回复 1# 大麻
加的有问题。。。。当连接到对方服务器,但是对方服务器没有任何回应时,recv 函数没有读到数据会一直阻塞下去。。。。。。肯定阻塞队列。。

   
页: [1]
查看完整版本: qmail-remote 缺陷造成but_connection_died的补丁