如果您执行 gethostbyname() 并且域名后没有尾随点,并且该域名无法解析,则 nslookup 最终会将此域名附加到服务器 FQDN。
因此,如果您查找 nonexistentdomainname.be,您的服务器可能会返回 nonexistentdomainname.be.yourhostname.com 的 IP 地址,即服务器 IP 地址。
要避免此行为,只需在域名后添加尾随点;例如 gethostbyname('nonexistentdomainname.be.')
(PHP 4, PHP 5, PHP 7, PHP 8)
gethostbyname — 获取与给定互联网主机名对应的 IPv4 地址
hostname
主机名。
返回 IPv4 地址,如果失败则返回包含未修改的 hostname
的字符串。
示例 #1 一个简单的 gethostbyname() 示例
<?php
$ip = gethostbyname('www.example.com');
echo $ip;
?>
如果您执行 gethostbyname() 并且域名后没有尾随点,并且该域名无法解析,则 nslookup 最终会将此域名附加到服务器 FQDN。
因此,如果您查找 nonexistentdomainname.be,您的服务器可能会返回 nonexistentdomainname.be.yourhostname.com 的 IP 地址,即服务器 IP 地址。
要避免此行为,只需在域名后添加尾随点;例如 gethostbyname('nonexistentdomainname.be.')
此函数表示“返回 IPv4 地址,或者在失败时返回包含未修改的主机名的字符串”。
这并不完全正确,任何主机名中包含空字节都只会返回空字节之前的字符。
<?php
$hostname = "foo\0bar";
var_dump($hostname );
var_dump(gethostbyname($hostname ));
?>
结果
string 'foo�bar' (length=7)
string 'foo' (length=3)
重要说明:您应该避免在生产环境中使用它。
DNS 解析可能需要 0.5 到 4 秒,在此期间您的脚本不会执行。
您的客户可能会认为服务器速度慢,但实际上它只是在等待 DNS 解析响应。
您可以使用它,但如果您想要性能,则应避免使用它,或将其安排到某个 CRON 脚本中……
可以通过使用 RES_OPTIONS 环境变量来提供底层解析器函数的选项。(至少在 Linux 下,请参阅 man resolv.conf)
将超时和重试设置为 1,使 DNS 查找的最大执行时间为 1 秒
<?php
putenv('RES_OPTIONS=retrans:1 retry:1 timeout:1 attempts:1');
gethostbyname($something);
?>
您还应该使用以点结尾的完全限定域名。这可以防止解析器遍历所有搜索域并尝试附加搜索域的域名。
要使用此函数进行基本的 RBL(实时黑名单)查找,请执行以下操作
<?php
$host = '64.53.200.156';
$rbl = 'sbl-xbl.spamhaus.org';
// 有效的查询格式是:156.200.53.64.sbl-xbl.spamhaus.org
$rev = array_reverse(explode('.', $host));
$lookup = implode('.', $rev) . '.' . $rbl;
if ($lookup != gethostbyname($lookup)) {
echo "ip: $host is listed in $rbl\n";
} else {
echo "ip: $host NOT listed in $rbl\n";
}
?>
Tomas V.V.Cox
gethostbyname 和 gethostbynamel 不会请求 AAAA 记录。我已经编写了两个函数来实现此功能。gethostbyname6 和 gethostbynamel6。我不认为这个问题已经得到解决。
它们旨在替换 gethostbyname[l],如果 $try_a 为真,如果无法获取 AAAA 记录,它将回退到尝试获取 A 记录。
随意更正任何错误,我意识到它是在请求 *同时* 获取 A 和 AAAA 记录,这意味着两次 DNS 调用……如果它在发出查询之前检查 $try_a,可能会更高效,但这对我来说有效,所以我将把它留给其他人去在他们自己的工作中实现……提示已经发布了……
这是代码
function gethostbyname6($host, $try_a = false) {
// 获取 $host 的 AAAA 记录
// 如果 $try_a 为真,如果 AAAA 失败,则尝试获取 A 记录
// 返回找到的第一个匹配项
// 否则返回 false
$dns = gethostbynamel6($host, $try_a);
if ($dns == false) { return false; }
else { return $dns[0]; }
}
function gethostbynamel6($host, $try_a = false) {
// 获取 $host 的 AAAA 记录,
// 如果 $try_a 为真,如果 AAAA 失败,则尝试获取 A 记录
// 结果以找到的匹配类型的 ip 数组返回
// 否则返回 false
$dns6 = dns_get_record($host, DNS_AAAA);
if ($try_a == true) {
$dns4 = dns_get_record($host, DNS_A);
$dns = array_merge($dns4, $dns6);
}
else { $dns = $dns6; }
$ip6 = array();
$ip4 = array();
foreach ($dns as $record) {
if ($record["type"] == "A") {
$ip4[] = $record["ip"];
}
if ($record["type"] == "AAAA") {
$ip6[] = $record["ipv6"];
}
}
if (count($ip6) < 1) {
if ($try_a == true) {
if (count($ip4) < 1) {
return false;
}
else {
return $ip4;
}
}
else {
return false;
}
}
else {
return $ip6;
}
}
自2015年以来,为了解决CVE-2015-0235漏洞,主机名参数被任意限制为255个字符,这可能是为了与RFC1035中对域名字节数的限制相对应。
获取多个主机的IP地址
<?php
function getAddrByHost($hosts, $timeout = 3) {
$returnString = '';
foreach ($hosts as $host) {
$query = `nslookup -timeout=$timeout -retry=1 $host`;
if (preg_match('/\nAddress: (.*)\n/', $query, $matches))
$returnString .= trim($matches[1]) . '<br>';
$returnString .= $host . '<br>';
}
return $returnString;
}
$hostArray[] = 'www.domain1.com';
$hostArray[] = 'www.domain2.com';
//$hostArray[] = 'www.domain3.com';
//$hostArray[] = 'www.domain4.com';
$returnString = getAddrByHost($hostArray);
echo $returnString;
?>
在OpenBSD 3.2和Apache下,我很难让gethostbyname工作,直到我发现默认的Apache chroot导致了这个问题。
要使PHP的gethostbyname工作,你需要在/var/www/etc中(假设是默认安装目录)放置resolv.conf(可能还有hosts)。
在PHP4中可以使用gethostbyname(),但我发现当查找返回私有网络上的A记录的条目时,它不可靠。PHP5有一个更好的例程——dns_get_record()。如果你仍然使用PHP4或者不想升级,可以使用dig。
<?php
$ip = `/usr/bin/dig $host A +short`;
?>
mmucklo提出了一个有效的观点,但是最简单的函数应该是getmxrr(),或者如果你愿意,也可以使用checkdnsrr();getdnsrr()不存在。
另一方面,当使用while循环迭代时,gethostbyname()在失败时返回主机名而不是FALSE,这可能会很麻烦。你可以用一个简单的用户函数来“修正”这个问题。
<?php
function fixed_gethostbyname ($host) {
// 尝试正常的查找…
$ip = gethostbyname($host);
// …但是如果失败,则返回FALSE而不是未解析的主机
if ($ip != $host) { return $ip; } else return false;
}
?>
<?php
// 用于测试DNS传播时间的脚本
//(上面的脚本略作修改以显示微秒时间)
// 看起来非常快……我得到的最悪情况(badhost)时间是0.0055秒。
// 一个已知的良好DNS名称(我自己的)
$nametotest = "fuzzygroup.com";
// 调用地址测试函数
$time_start = getmicrotime();
testipaddress($nametotest);
$time_end = getmicrotime();
$time = $time_end - $time_start;
echo "良好主机搜索耗时 $time 秒<br><br>";
// 一个已知的错误名称(相信我)
$nametotest = "providence.mascot.com";
$time_start = getmicrotime();
testipaddress($nametotest);
$time_end = getmicrotime();
$time = $time_end - $time_start;
echo "错误主机搜索耗时 $time 秒<br>";
function getmicrotime(){
list($usec, $sec) = explode(" ",microtime());
return ((float)$usec + (float)$sec);
}
// IP地址检查函数
// 实际使用应该有返回值,但这是示例代码
function testipaddress ($nametotest) {
$ipaddress = $nametotest;
$ipaddress = gethostbyname($nametotest);
if ($ipaddress == $nametotest) {
echo "主机没有IP地址<br>";
}
else {
echo "良好主机名,$nametotest IP地址为 $ipaddress<br>";
}
}
// 建议用于SQL应用程序的修复:
// 将URL存储到临时表中
// 定期运行第二个进程来
// 检查URL并更新主表
?>
参考ralphbolton at mail2sexy dot com的评论
(至少在5.2.0 + djbdns-dnscache中)gethostbyname似乎并没有真正缓存条目。如果有人注意到在第二次查找相同域名后速度加快了——那很可能是你的DNS缓存本身,而不是某种PHP内部的DNS缓存。
它确实缓存了你/etc/resolv.conf中的条目(例如,使用哪个DNS),所以我同意他的说法,停止并启动Apache将重新加载resolv.conf。
顺便说一句,PHP(5.0.4,但也可能是其他版本)可以缓存gethostbyname信息。
简而言之,一旦PHP查找地址,它可能不会像你预期的那样执行另一次查找。在我的特定情况下(我认为)问题是resolv.conf的更改在PHP内部没有生效(尽管nslookup/ping等工作正常)。停止/启动Apache解决了这个问题(尽管简单的“重启”(kill -HUP)没有解决)。
简而言之,如果你更改了resolv.conf,请停止并重新启动Apache。
我正在对一组URL使用file_get_contents。其中一些URL无效(其结构没问题,但DNS主机无法解析它们),我一直收到烦人的警告。我想以某种方式检查DNS,但是PHP中现有的检查DNS函数在Windows中没有,而其他人提供的函数也不能100%地工作。
相反,使用此函数尝试解析主机。这不会引发任何警告,你只需要检查输出。你将使用fopen和fsockopen获得相同的警告。
提醒一下。我一直在我的网站上使用此函数来验证电子邮件主机地址。我以为一切都很顺利,直到一个潜在的客户联系我说他们无法正确注册。他们有有效的电子邮件地址和域名,但是此函数失败了。
祝你好运。
对于“dig”解决方案,这里有一个更好的方法
<?php
$IP = `/usr/bin/dig $host. A +short | /usr/bin/tail -n1`
?>
使用tail是因为dig,即使在+short形式下,也可能首先返回CNAME条目,也可能返回许多条目(参见“dig google.com”)。
在主机末尾使用点以不执行域名搜索。
当$host已经是IP地址时,这也适用。
如果解析失败(无效主机名),将返回空字符串。
这个逻辑解决方案用于检查DNS反向名称解析问题。
<?php
$ip = gethostbyname($host);
if(ip2long($ip) == -1 || ($ip == gethostbyaddr($ip) && preg_match("/.*\.[a-zA-Z]{2,3}$/",$host) == 0) ) {
echo '错误,主机名或IP地址不正确';
}
else {
echo '正确';
}
?>
一种自动发现您的IP地址的方法
<?php
// 需要使用 trim(),因为空格会干扰名称查找
$myIP = gethostbyname(trim(`hostname`));
echo $myIP;
?>
同时使用 gethostbynamel() 和 gethostbyname() 时,必须先执行 gethostbynamel(),否则它将始终从缓存中返回一个IP(或无), 而不是返回完整的查找结果。
<?php
// 用于查看主机是否在互联网上存在的脚本
// 针对上面关于主机名检查和SQL超时的问题的后续处理,运行此测试脚本
// 并查看第二次主机名检查失败需要多长时间
// 注意 -- 这不是PHP的错误 -- 这是DNS的特性
// 一个已知的良好DNS名称(我自己的)
$nametotest = "fuzzygroup.com";
// 调用地址测试函数
testipaddress($nametotest);
// 一个已知的错误名称(相信我)
$nametotest = "providence.mascot.com";
// 调用地址测试函数
testipaddress($nametotest);
// IP地址检查函数
// 对于实际使用应该有一个返回值,但这是示例代码
function testipaddress ($nametotest) {
$ipaddress = $nametotest;
$ipaddress = gethostbyname($nametotest);
if ($ipaddress == $nametotest) {
echo "主机没有IP地址,因此主机"
. "当前在DNS中不可用,并且"
. "可能已离线一段时间<BR>";
}
else {
echo "主机名正确,ip地址 = $ipaddress<BR>";
}
}
// 建议用于SQL应用程序的修复方案:
// 将URL存储到临时表中
// 定期运行第二个进程来
// 检查URL并更新主表
?>
始终在末尾添加根“. ”,否则您的服务器可能会添加它自己的后缀。
例如:如果不使用 abc.com,abc.com 可能会变为 abc.com.yourcompany.com。
我还发现此函数有点局限,因为它不会告诉您解析是否失败。
为了解决所有这些问题,我使用
function host2ip($host)
{
$host=trim($host.'.'); // 清理并添加根“.”
$ip= gethostbyname($host);
if($ip==$host) $ip='';// 如果没有IP,则IP为空
return $ip;
}
在 RedHat Linux 上的 chroot 环境中使用 PHP 和 Apache 时,我发现我需要绑定挂载 /var/run/nscd 才能使其工作。显然,该目录中的套接字对于所有 DNS 事务都是必需的。
关于使用 gethostbyname() 检查电子邮件地址域的一个说明
如果名称无法解析,请继续使用 getdnsrr() 并确保它们没有 MX 条目,然后再返回错误。
域名可能没有 A 记录,但仍然有 MX 条目。
如果使用 apache2、mod_chroot 和 php5 时名称解析失败,请添加
LoadFile /lib/libnss_dns.so.2
到 mod_chroot 配置中。