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
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
php_tcp_sockop_connect
uses php_network_connect_socket_to_host
php_network_getaddresses
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?
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.
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/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With