我们最近遇到了与您类似的问题。我们发现file_get_contents()
最终调用getaddrinfo()
,它实现了一些来自RFC3484的排序算法。
来自php 源代码的片段
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...
}
我们仍在寻找更好的方法来解决这个问题。
编辑:
RFC3484 的第 6 节规定了目标地址选择算法,第 9 条规则与 IPv4 相关。
规则 9:使用最长匹配前缀。
当 DA 和 DB 属于同一个地址族时(都是 IPv6 或都是 IPv4):如果 CommonPrefixLen(DA, Source(DA)) > CommonPrefixLen(DB, Source(DB)),则首选 DA。同样,如果 CommonPrefixLen(DA, Source(DA)) < CommonPrefixLen(DB, Source(DB)),则首选 DB。
假设我们有源地址192.168.1.100/24
和四个候选目标地址192.168.1.33/24
, 192.168.1.44/24
, 192.168.2.55/24
, 192.168.2.66/24
。
上述地址以二进制格式表示
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
你可以看到
CommonPrefixLen(DA, S) == CommonPrefixLen(DB, S) == 25 >
CommonPrefixLen(DC, S) == CommonPrefixLen(DD, S) == 22
所以DA
or DB
,在原始 DNS 查询中最先出现的,将被选为glibc
实现中的最终目的地。