501 Command "HELO" requires an argument问题的解决方法

所属分类: 软件编程 / java 阅读数: 44
收藏 0 赞 0 分享

场景描述:
保存邮箱配置时自动探测邮箱配置参数是否正确,结果发现在探测SMTP时,系统报出如下异常:

复制代码 代码如下:

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服务器,发现就没这样的问题,仅在一台Linux服务器上可以重现,直观感觉就是这台Linux服务器某些配置有问题。

排查步骤
1. 找一台同样操作系统的Linux服务器,验证邮箱配置,ok,排除Linux操作系统特殊性的问题
2. 直接在Linux服务器上使用telnet连接对端邮件服务器的SMTP端口,OK,排除该服务器的网络问题
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.

发现HELO指令后面需要跟一个发起者的主机名,告诉SMTP服务器这个消息来源是哪里。
再看异常信息是"501 Command "HELO" requires an argument",很明显,程序在跟SMTP SERVER交互过程中没有传递源主机域名。

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方法在哪里被调用,看看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的获取可以通过当前连接的session属性中获取,也可以从当前服务器的hosts配置中获取,而我们程序是没有在session中设置hostname的,因此原因肯定在于该台Linux服务器的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连接中加入当前服务器的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服务器连接失败,请检查输入信息是否正确");
                } catch (NoSuchProviderException e) {
                        e.printStackTrace();
                        System.out.println("smtp服务器连接失败,请检查输入信息是否正确");
                } catch (MessagingException e) {
                        e.printStackTrace();
                        System.out.println("smtp服务器连接失败,请检查输入信息是否正确");
                }
        }

更多精彩内容其他人还在看

SpringBoot SpEL语法扫盲与查询手册的实现

这篇文章主要介绍了SpringBoot SpEL语法扫盲与查询手册的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
收藏 0 赞 0 分享

Java创建子线程的两种方法

这篇文章主要介绍了Java创建子线程的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
收藏 0 赞 0 分享

Spring Boot2.x集成JPA快速开发的示例代码

这篇文章主要介绍了Spring Boot2.x集成JPA快速开发,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
收藏 0 赞 0 分享

关于Java中的mysql时区问题详解

这篇文章主要给大家介绍了关于Java中mysql时区问题的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
收藏 0 赞 0 分享

JAVA参数传递方式实例浅析【按值传递与引用传递区别】

这篇文章主要介绍了JAVA参数传递方式,结合实例形式分析了java按值传递与引用传递区别及相关操作注意事项,需要的朋友可以参考下
收藏 0 赞 0 分享

Java中MessageDigest来实现数据加密的方法

这篇文章主要介绍了Java中MessageDigest来实现数据加密的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
收藏 0 赞 0 分享

spring 注解验证@NotNull等使用方法

这篇文章主要介绍了spring 注解验证@NotNull等使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
收藏 0 赞 0 分享

浅谈如何优雅地停止Spring Boot应用

这篇文章主要介绍了浅谈如何优雅地停止Spring Boot应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
收藏 0 赞 0 分享

Python如何使用@property @x.setter及@x.deleter

这篇文章主要介绍了Python如何使用@property @x.setter及@x.deleter,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
收藏 0 赞 0 分享

Java Jmeter全局变量设置过程图解

这篇文章主要介绍了Java Jmeter全局变量设置过程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多