Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SMTP send mail is not working for office365

Here is a peculiar problem. The intention is to send a mail via SMTP for office365.

I have been able to consistently send mail from my local laptop.

But when deployed on our server (behind a firewall), it does not succeed. Note: The port 587 for smtp.office365.com is accessible and confirmed on the server. Here are the properties via which it successfully works from my local computer.

Properties props = new Properties();
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.connectiontimeout", MAIL_TIMEOUT);
props.put("mail.smtp.timeout", MAIL_TIMEOUT);
props.put("mail.debug", true);
this.session = Session.getInstance(props);
session.setDebug(true);

Transport transport  = session.getTransport();
transport.connect("smtp.office365.com", 587, email, pass);

But fails on server. Here are the server debug logs:

DEBUG: setDebug: JavaMail version 1.6.2
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
DEBUG SMTP: useEhlo true, useAuth false
DEBUG SMTP: trying to connect to host "smtp.office365.com", port 587, isSSL false
220 PN1PR0101CA0017.outlook.office365.com Microsoft ESMTP MAIL Service ready at Fri, 28 Jun 2019 06:39:41 +0000
DEBUG SMTP: connected to host "smtp.office365.com", port: 587
EHLO appqa
250-PN1PR0101CA0017.outlook.office365.com Hello [182.73.191.100]
250-SIZE 157286400
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-STARTTLS
250-8BITMIME
250-BINARYMIME
250-CHUNKING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "157286400"
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "DSN", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "STARTTLS", arg ""
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "BINARYMIME", arg ""
DEBUG SMTP: Found extension "CHUNKING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
STARTTLS
220 2.0.0 SMTP server ready
Exception in thread "main" javax.mail.MessagingException: Could not convert socket to TLS;
  nested exception is:
java.net.SocketTimeoutException: Read timed out
at com.sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.java:2155)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:752)
at javax.mail.Service.connect(Service.java:366)
at com.company.app.MailReader.getTransport(MailReader.java:269)
at io.vavr.control.Try.of(Try.java:75)
at com.company.app.MailReader.<init>(MailReader.java:59)
at com.company.services.MailService.getNewMailReader(MailService.java:82)
at com.company.services.MailService.start(MailService.java:46)
at com.company.Main.main(Main.java:34)
Caused by: java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
at sun.security.ssl.InputRecord.readV3Record(InputRecord.java:593)
at sun.security.ssl.InputRecord.read(InputRecord.java:529)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:975)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
at com.sun.mail.util.SocketFetcher.configureSSLSocket(SocketFetcher.java:626)
at com.sun.mail.util.SocketFetcher.startTLS(SocketFetcher.java:553)
at com.sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.java:2150)
... 8 more
like image 303
Jatin Avatar asked Jun 28 '19 06:06

Jatin


2 Answers

Check whether the server has the same set of certificates as your local computer.

The 220 response from the server does not mean that the TLS session is already established, it just means that the client may start negotiating it:

After receiving a 220 response to a STARTTLS command, the client MUST start the TLS negotiation before giving any other SMTP commands. If, after having issued the STARTTLS command, the client finds out that some failure prevents it from actually starting a TLS handshake, then it SHOULD abort the connection. (from RFC 3207)

At this point, a missing certificate is the most likely problem.

like image 109
tquadrat Avatar answered Sep 22 '22 13:09

tquadrat


Check your JRE version on the server and compare it to the version of your local computer.

This is an environment related issue as the same code behaves differently on different machines. Without the full picture, I cannot answer with certainty. But I hope providing some insight for further investigation. My analysis follows:

  • First, I don't think it's a SSL certificate issue, the root cause error is clear:
Caused by: java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
...
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
...

this means the socket has been established, but the handshake phase of converting the socket to TLS has failed. If the certificate is not valid, it will be reported after the handshake, let's look at the code from the SocketFetcher.java class:

    /*
     * Force the handshake to be done now so that we can report any
     * errors (e.g., certificate errors) to the caller of the startTLS
     * method.
     */
    sslsocket.startHandshake();

    /*
     * Check server identity and trust.
     */
    boolean idCheck = PropUtil.getBooleanProperty(props,
                prefix + ".ssl.checkserveridentity", false);
    if (idCheck)
        checkServerIdentity(host, sslsocket);
    if (sf instanceof MailSSLSocketFactory) {
        MailSSLSocketFactory msf = (MailSSLSocketFactory)sf;
        if (!msf.isServerTrusted(host, sslsocket)) {
        throw cleanupAndThrow(sslsocket,
            new IOException("Server is not trusted: " + host));
        }
    }
    }

the socket encountered the timeout at this line: sslsocket.startHandshake(), which is before the certificate validation.

  • Second, you have already mentioned that firewalls are disabled, and we can see that the previous socket is correctly established so is the telnet command, so I don't think it's a firewall issue neither.

  • It seems like a protocol issue, mostly because this happened during the handshake phase, otherwise we should see different and more explicit error, such as certificate error, connection timeout, etc. This is a socketRead timeout, which indicates the client (your server) is expecting some information from the server (office365), but the server doesn't respond, it's like they are not talking together.

  • The compiled code is not the issue here, but some part of this process is environment related: the SSLSocketImpl.class class is from the JRE not from the compilation. And this is the exact code (decompiled) where the protocol is implemented:

private void performInitialHandshake() throws IOException {
        Object var1 = this.handshakeLock;
        synchronized(this.handshakeLock) {
            if (this.getConnectionState() == 1) {
                this.kickstartHandshake();
                if (this.inrec == null) {
                    this.inrec = new InputRecord();
                    this.inrec.setHandshakeHash(this.input.r.getHandshakeHash());
                    this.inrec.setHelloVersion(this.input.r.getHelloVersion());
                    this.inrec.enableFormatChecks();
                }

                this.readRecord(this.inrec, false);
                this.inrec = null;
            }

        }
    }

The above code is from JRE_1.8.0_181, your code or the code from your server may be different. This is way it's necessary to check your server's JRE version.

  • Using the same code you provided at the beginning, I could correctly connect to office365
like image 28
iyunbo Avatar answered Sep 25 '22 13:09

iyunbo