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 編輯 ]
《解決方案》
這個,能不能給做個 patch 文件阿
《解決方案》
請教大麻
修改後是否需要重新編譯qmail?
《解決方案》
lz,你好。最近我也遇到這樣的為你。因為對qmail不是很懂,你上面的方法怎麼去操作,應用到qmail中呢?
《解決方案》
回復 1# 大麻
加的有問題。。。。當連接到對方伺服器,但是對方伺服器沒有任何回應時,recv 函數沒有讀到數據會一直阻塞下去。。。。。。肯定阻塞隊列。。