Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GMail 421 4.7.0 Try again later, closing connection

I'am trying to find out, why it fails to send mails from my server with GMail. For this I use SwiftMailer but I can enclose the problem to the following isolated code:

<?php

$sock = stream_socket_client('tcp://smtp.gmail.com:587', $errno, $errstr);
if (false == $sock) {
    die('error');
}
$server_response = fread($sock, 4096);
echo $server_response . PHP_EOL;
fclose($sock);

When I run this code on my local machine (Windows 10, XAMPP, PHP 5.6.24), the output is:

220 smtp.gmail.com ESMTP qp16sm1358626ejb.89 - gsmtp

So 220 means everything is fine.

But when I run the exact same code on my server (Debian Jessie, PHP 5.6.38), the output is:

421 4.7.0 Try again later, closing connection.

GMail's error reference https://support.google.com/a/answer/3726730 doesn't give any advice for this error message.

I also tried this on the server console without PHP and it works fine too:

> nc smtp.gmail.com 587
$ 220 smtp.gmail.com ESMTP n11sm3188821wmi.15 - gsmtp

So I can delimit the problem

  • It has nothing to do with any security settings of my GMail account (because no authentification is sent at this point)
  • My server is not blacklisted on GMail (because the nc command works fine)

Are there any settings of the php.ini that can cause this error?

UPDATE

Just for fun I set up a simple TCP server on port 587 on another machine and display the IP address of any connected client. There is no different if I use the above PHP client code or connecting via nc command. I don't know what could be the different for the GMail's SMTP server while conneting via the client PHP code and the nc command.

UPDATE 2

While enabling verbose mode in the nc command, could DNS fwd/rev mismatch be a problem?

> nc -v smtp.gmail.com 587
$ DNS fwd/rev mismatch: smtp.gmail.com != wr-in-f109.1e100.net
$ smtp.gmail.com [108.177.15.109] 587 (submission) open
$ 220 smtp.gmail.com ESMTP z15sm4348737wrn.89 - gsmtp

UPDATE 3

Run the above code with

$sock = stream_socket_client('tcp://wr-in-f109.1e100.net:587', $errno, $errstr);

It works, it responses with 220. But the problem is, SwiftMailer try to establish TLS security after with stream_socket_enable_crypto() and this fails with

Peer certificate CN=`smtp.gmail.com' did not match expected CN=`wr-in-f109.1e100.net'

UPDATE 4

It's getting me crazy. When I use Python's TCP sockets to connect to smtp.gmail.com, GMail responses with 220:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('smtp.gmail.com', 587))
data = s.recv(4096)
s.close()

print data

Output:

220 smtp.gmail.com ESMTP p6sm8392243wmg.0 - gsmtp

What is the difference between Python's sockets and PHP's stream_socket_client?

Edit: The same behavior with PHP's non-streaming socket function:

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!is_resource($socket)) onSocketFailure("Failed to create socket");
socket_connect($socket, "smtp.gmail.com", 587)
        or onSocketFailure("Failed to connect", $socket);

$line = socket_read($socket, 1024, PHP_NORMAL_READ);

var_dump($line);
socket_close($socket);

UPDATE 5

Now I think it is a problem with IPv6. I'am not a big C programmer but I tried to understand the PHP's implementation.

Have a look at

  • xp_socket.c the function php_tcp_sockop_connect uses php_network_connect_socket_to_host
  • network.c which is implemented here
  • It use php_network_getaddresses
  • and this function use getaddrinfo

php_network_connect_socket_to_host iterates of the list of host addresses and tries to connect to it. If its success, it stops with looping.

I don't tried it out with C but I think the following Python code does the same:

import socket
print socket.getaddrinfo("smtp.gmail.com", 587)

Which prints

[
 (10, 1, 6, '', ('2a00:1450:400c:c0c::6d', 587, 0, 0)),
 (10, 2, 17, '', ('2a00:1450:400c:c0c::6d', 587, 0, 0)),
 (10, 3, 0, '', ('2a00:1450:400c:c0c::6d', 587, 0, 0)),
 (2, 1, 6, '', ('108.177.15.109', 587)),
 (2, 2, 17, '', ('108.177.15.109', 587)),
 (2, 3, 0, '', ('108.177.15.109', 587))
]

(I formatted the line for better reading)

As you can see, there are three IPv6 addresses on the top of the list and php_network_connect_socket_to_host try these IPv6 adresses first before it comes to the IPv4 addresses.

So what happens when I try to connect directly to the first IPv6 address?

<?php

$sock = stream_socket_client('tcp://[2a00:1450:400c:c02::6c]:587', $errno, $errstr);
if (false == $sock) {
    die('error');
}
$server_response = fread($sock, 4096);
echo $server_response . PHP_EOL;
fclose($sock);

The output is:

421 4.7.0 Try again later, closing connection.

This SO question is related to this observation: Why I can't send emails using smtp.gmail.com:587 on ipv6?

like image 359
Vertex Avatar asked Aug 12 '20 11:08

Vertex


People also ask

What is SMTP server for Gmail?

The outgoing SMTP server, smtp.gmail.com , requires TLS. Use port 465 , or port 587 if your client begins with plain text before issuing the STARTTLS command.


Video Answer


1 Answers

After finding out, that the error message happens when the IPv6 address is used the hack is to prefer the IPv4 address.

Acording to https://serverfault.com/questions/511237/linux-block-ipv6-for-certain-applications-hostnames#answer-514397 the easiest way was to put the following line to etc/hosts:

108.177.15.109 smtp.gmail.com

The drawback of this solution is the static IP address. smtp.gmail.com might change the IP address. This is the cause why I don't mark this answer as accepted.

I don't know if its possible to configure /etc/gai.conf to prefer IPv4 over IPv6 for only smtp.gmail.com.

This article might be interesting to get a detailed look, how getaddrinfo works: https://jameshfisher.com/2018/02/03/what-does-getaddrinfo-do/

like image 159
Vertex Avatar answered Oct 17 '22 13:10

Vertex