場景描述:
保存郵箱配置時自動探測郵箱配置參數(shù)是否正確,結(jié)果發(fā)現(xiàn)在探測SMTP時,系統(tǒng)報出如下異常:
javax.mail.MessagingException: 501 Command "HELO" requires an argument
at com.sun.mail.smtp.SMTPTransport.issueCommand(SMTPTransport.java:1363)
at com.sun.mail.smtp.SMTPTransport.helo(SMTPTransport.java:838)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:375)
at javax.mail.Service.connect(Service.java:275)
但是換一個windows服務(wù)器,發(fā)現(xiàn)就沒這樣的問題,僅在一臺Linux服務(wù)器上可以重現(xiàn),直觀感覺就是這臺Linux服務(wù)器某些配置有問題。
排查步驟
1. 找一臺同樣操作系統(tǒng)的Linux服務(wù)器,驗證郵箱配置,ok,排除Linux操作系統(tǒng)特殊性的問題
2. 直接在Linux服務(wù)器上使用telnet連接對端郵件服務(wù)器的SMTP端口,OK,排除該服務(wù)器的網(wǎng)絡(luò)問題
3. 查找HELO指令解釋
HELO
-- Initiates a conversation with the mail server. When using this command you can specify your domain name so that the mail server knows who you are. For example, HELO mailhost2. cf.ac.uk.
發(fā)現(xiàn)HELO指令后面需要跟一個發(fā)起者的主機(jī)名,告訴SMTP服務(wù)器這個消息來源是哪里。
再看異常信息是"501 Command "HELO" requires an argument",很明顯,程序在跟SMTP SERVER交互過程中沒有傳遞源主機(jī)域名。
4. 查看JAVA MAIL源碼
查找HELO指令,如下:
/**
* Issue the <code>HELO</code> command.
*
* @param domain
* our domain
*
* @since JavaMail 1.4.1
*/
protected void helo(String domain) throws MessagingException {
if (domain != null)
issueCommand("HELO " + domain, 250);
else
issueCommand("HELO", 250);
}
查找helo方法在哪里被調(diào)用,看看domain如何被傳遞過來的
if (useEhlo)
succeed = ehlo(getLocalHost());
if (!succeed)
helo(getLocalHost());
順理成章,接著找getLocalHost()方法,定義如下:
/**
* Get the name of the local host, for use in the EHLO and HELO commands.
* The property mail.smtp.localhost overrides mail.smtp.localaddress, which
* overrides what InetAddress would tell us.
*/
public synchronized String getLocalHost() {
try {
// get our hostname and cache it for future use
if (localHostName == null || localHostName.length() <= 0)
localHostName = session.getProperty("mail." + name + ".localhost");
if (localHostName == null || localHostName.length() <= 0)
localHostName = session.getProperty("mail." + name+ ".localaddress");
if (localHostName == null || localHostName.length() <= 0) {
InetAddress localHost = InetAddress.getLocalHost();
localHostName = localHost.getHostName();
// if we can't get our name, use local address literal
if (localHostName == null)
// XXX - not correct for IPv6
localHostName = "[" + localHost.getHostAddress() + "]";
}
} catch (UnknownHostException uhex) {
}
return localHostName;
}
可以看到hostname的獲取可以通過當(dāng)前連接的session屬性中獲取,也可以從當(dāng)前服務(wù)器的hosts配置中獲取,而我們程序是沒有在session中設(shè)置hostname的,因此原因肯定在于該臺Linux服務(wù)器的hosts文件被修改,造成JAVA程序無法自動獲得localhostName。
5. 查看/etc/hosts文件,情況如下:
127.0.0.1 localhost.localdomain localhost
::1 localhost6.localdomain6 localhost6
簡單的將hosts文件修改如下:
127.0.0.1 localhost
::1 localhost6.localdomain6 localhost6
6. 重新測試,問題解決了。
其實,這種情況也可以通過程序避免,即在session連接中加入當(dāng)前服務(wù)器的hostname屬性,程序示例:
public static void main(String[] args) {
try {
int smtpport = 25;
String smtpserver = "219.147.xxx.xxx";
Properties prop = System.getProperties();
prop.put("mail.smtp.auth", "true");
prop.put("mail.smtp.localhost", "myMailServer");
Session mailSession = Session.getInstance(prop, null);
Transport transport = mailSession.getTransport("smtp");
transport.connect(smtpserver,smtpport, "username", "password");
System.out.println("connect ok");
transport.close();
} catch (AuthenticationFailedException en) {
en.printStackTrace();
System.out.println("smtp服務(wù)器連接失敗,請檢查輸入信息是否正確");
} catch (NoSuchProviderException e) {
e.printStackTrace();
System.out.println("smtp服務(wù)器連接失敗,請檢查輸入信息是否正確");
} catch (MessagingException e) {
e.printStackTrace();
System.out.println("smtp服務(wù)器連接失敗,請檢查輸入信息是否正確");
}
}