I have problem with making PHP to respect RoundRobin-enabled DNS entry. The entry (say for example it's domain.example.com) has three possible IP addresses assigned. RoundRobin works (tested with ping, telnet, wget, etc.). Unfortunately, when using PHP SOAP extension, and even plain file_get_contents it always connects to the first IP address specified in DNS. Surprisingly, gethostbyname function sees RoundRobin perfectly fine. I've put a file outputing 1 or 2 or 3 in each server and executed the script on another server few times:
var_dump(file_get_contents('http://domain.example.com/test.html'));
var_dump(gethostbyname('domain.example.com'));
The first line always prints "1" (from the first IP address). The second line randomly outputs one of three possible IP addresses.
The question: Does anyone had similar problem? How can I force PHP to respect RoundRobin in DNS, at least when making a SOAP requests?
EDIT There is no DNS cache present, and no proxy. As mentioned, ping, telnet, wget etc. works OK on the same server where the test script is placed.
We have recently encountered a similar problem as yours. We found out that file_get_contents()
eventually calls getaddrinfo()
, which implements some sorting algorithms from RFC3484.
The piece of snippet from php source code
PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string)
{
// skip...
if ((n = getaddrinfo(host, NULL, &hints, &res))) {
if (error_string) {
*error_string = strpprintf(0, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(*error_string));
} else {
php_error_docref(NULL, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
}
return 0;
}
//skip...
}
We are still figuring out what is a better approach to deal with the problem.
Edit:
Section 6 of RFC3484 specifies the destination address selection algorithm, and rule 9 is related to IPv4.
Rule 9: Use longest matching prefix.
When DA and DB belong to the same address family (both are IPv6 or both are IPv4): If CommonPrefixLen(DA, Source(DA)) > CommonPrefixLen(DB, Source(DB)), then prefer DA. Similarly, if CommonPrefixLen(DA, Source(DA)) < CommonPrefixLen(DB, Source(DB)), then prefer DB.
Lets say we have the source address 192.168.1.100/24
and four candidate destination addresses 192.168.1.33/24
, 192.168.1.44/24
, 192.168.2.55/24
, 192.168.2.66/24
.
With the above addresses representing in binary format
S 192.168.1.100 11000000.10101000.00000001.01100100
-------------------------------------------------------
DA 192.168.1.33 11000000.10101000.00000001.00100001
DB 192.168.1.44 11000000.10101000.00000001.00101100
DC 192.168.2.55 11000000.10101000.00000010.00110111
DD 192.168.2.66 11000000.10101000.00000010.01000010
You can see that
CommonPrefixLen(DA, S) == CommonPrefixLen(DB, S) == 25 >
CommonPrefixLen(DC, S) == CommonPrefixLen(DD, S) == 22
So DA
or DB
, which ever comes first in the original DNS query, would be chosen as the final destination in glibc
implementation.