PHP Conference Japan 2024

gethostbyaddr

(PHP 4, PHP 5, PHP 7, PHP 8)

gethostbyaddr 获取与给定 IP 地址对应的 Internet 主机名

描述

gethostbyaddr(string $ip): string|false

返回由 ip 指定的 Internet 主机的主机名。

参数

ip

主机 IP 地址。

返回值

成功时返回主机名,失败时返回未修改的 ip,或输入格式错误时返回 false

范例

例 1 一个简单的 gethostbyaddr() 例子

<?php
$hostname
= gethostbyaddr($_SERVER['REMOTE_ADDR']);

echo
$hostname;
?>

参见

  • gethostbyname() - 获取与给定 Internet 主机名对应的 IPv4 地址
  • gethostbynamel() - 获取与给定 Internet 主机名对应的一系列 IPv4 地址

添加注释

用户贡献的注释 32 条注释

36
lukevb_at_iafrica.com
22 年前
有时,当使用 $_SERVER['HTTP_X_FORWARDED_FOR'] 或 $_SERVER['REMOTE_ADDR'] 时,会返回多个 IP 地址,例如 '155.240.132.261, 196.250.25.120'。当此字符串作为 gethostbyaddr() 的参数传递时,PHP 会给出以下错误:警告:地址不是有效的 IPv4 或 IPv6 地址 in...

为了解决这个问题,我使用以下代码从字符串中提取第一个 IP 地址并丢弃其余部分。(如果您希望使用其他 IP,它们将位于 $ips 数组的其他元素中)。

if (strstr($remoteIP, ', ')) {
$ips = explode(', ', $remoteIP);
$remoteIP = $ips[0];
}

希望这对某人有所帮助 :)
13
king dot macro at gmail dot com
20 年前
损坏的 DNS 服务器问题导致我遇到问题,因为我有一个用户统计页面需要进行大约 20 次反向 DNS 查询,即使多达 5/6 个损坏也会导致页面加载速度大大减慢。所以我编写了一个函数,它使用 UDP 套接字直接与 DNS 服务器通信(而不是通过正常的 gethostbyaddr 函数),这允许我设置超时。

唯一的要求是您的 DNS 服务器必须能够执行递归查找,如果被告知,它不会转到其他 DNS 服务器……当然,您需要知道您的 DNS 服务器的 IP 地址 :-)

<?
function gethostbyaddr_timeout($ip, $dns, $timeout=1000)
{
// 随机事务编号(用于路由器等将回复送回)
$data = rand(0, 99);
// 修剪到 2 个字节
$data = substr($data, 0, 2);
// 请求头
$data .= "\1\0\0\1\0\0\0\0\0\0";
// 分割 IP
$bits = explode(".", $ip);
// 错误检查
if (count($bits) != 4) return "ERROR";
// 可能有更好的方法来做这一部分……
// 循环遍历每个段
for ($x=3; $x>=0; $x--)
{
// 需要一个字节来指示请求的每个段的长度
switch (strlen($bits[$x]))
{
case 1: // 1 字节长的段
$data .= "\1"; break;
case 2: // 2 字节长的段
$data .= "\2"; break;
case 3: // 3 字节长的段
$data .= "\3"; break;
default: // 段太大,无效的 IP
return "INVALID";
}
// 和段本身
$data .= $bits[$x];
}
// 和请求的最后部分
$data .= "\7in-addr\4arpa\0\0\x0C\0\1";
// 创建 UDP 套接字
$handle = @fsockopen("udp://$dns", 53);
// 发送我们的请求(并存储请求大小以便稍后作弊)
$requestsize=@fwrite($handle, $data);

@socket_set_timeout($handle, $timeout - $timeout%1000, $timeout%1000);
// 希望我们得到回复
$response = @fread($handle, 1000);
@fclose($handle);
if ($response == "")
return $ip;
// 查找响应类型
$type = @unpack("s", substr($response, $requestsize+2));
if ($type[1] == 0x0C00) // 答案
{
// 设置我们的变量
$host="";
$len = 0;
// 将我们的指针设置在主机名的开头
// 使用前面请求的大小而不是计算它
$position=$requestsize+12;
// 重建主机名
do
{
// 获取段大小
$len = unpack("c", substr($response, $position));
// 以空字符结尾的字符串,所以长度为 0 表示完成
if ($len[1] == 0)
// 返回主机名,没有尾随的 .
return substr($host, 0, strlen($host) -1);
// 将段添加到我们的主机
$host .= substr($response, $position+1, $len[1]) . ".";
// 将指针移动到下一段
$position += $len[1] + 1;
}
while ($len != 0);
// 错误 - 返回我们构造的主机名(没有末尾的 .)
return $ip;
}
return $ip;
}
?>

这可以扩展很多并改进,但它有效,我看到很多人尝试各种方法来实现类似的功能,所以我决定在这里发布它。在大多数服务器上,它也应该比其他方法(例如调用 nslookup)更有效,因为它不需要运行外部程序

注意:我更擅长 C 而不是 PHP,所以如果有什么没有以 *推荐* 的方式完成,请忽略它 :-)
2
tom
19 年前
小心使用此函数 - 如果调用很多次,它会使服务器速度慢到无法运行,并且速度减慢不会反映在任何明显的地方,例如 CPU 使用率、apache 请求、SQL 等。当您使用它时,请特别注意使用的地方!
1
Vincent
5 年前
我发现 gethostbyaddr 有时会返回相同的主机名,其中一些字母大写,有时全部小写。

例子

d54c34fa1.access.example.com
d54C34FA1.access.example.com

这可能不是 gethostbyaddr 的错误,但这在比较或存储时可能是一个问题,因为它会给出两个条目而不是一个。

一个简单的解决方法是使用 strtolower 函数处理主机名。
1
匿名
4年前
gethostbyaddr_timeout() 函数中存在一个 bug,king dot macro at gmail dot com 在最重要的一行中指出了这个问题,即使用 socket_set_timeout() 设置超时。它应该是

@socket_set_timeout($handle, intdiv($timeout_ms, 1000), $timeout_ms % 1000);

我还建议将 $dns 参数放在最后并设置为可选参数,如果未提供则从 /etc/resolve.conf(或其他文件)中解析。
1
oryan at zareste dot com
18年前
如果其他方法都失败了,但你有 shell 访问权限,Unix/Linux 服务器可以使用以下命令来获取超时响应

shell_exec('host -W 2 0.0.0.0');

其中 0.0.0.0 是 IP 地址,“2” 是超时秒数。这将返回更详细的信息字符串,其中包含一些附加文本,这些文本可能因系统而异,因此,如果您想要一个只包含主机名而没有其他内容的字符串,则必须进行一些子字符串剪切。Windows 用户应该有与“host”等效的命令可以使用,但这并非我的平台。
0
marco[DOT]ceppi[@T]seacrow[DOT]org
15年前
匿名用户提出了一个很好的观点(尽管我不同意除非必要才将执行推送到 shell。但是这是一个更快的示例(explode 然后循环对于简单的检查来说有点过于密集)

<?php
function gethost ($ip)
{
//确保输入不会产生任何意外情况
//IP 地址必须采用 x.x.x.x 的形式,其中每个 x 都是一个数字

if( preg_match('/^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:[.]
(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/'
, $ip) )
{
$host = `host $ip`;
return ((
$host ? end ( explode (' ', $host)) : $ip));
}
else
{
return
false;
}
}
?>

老实说,我会使用

<?php
function gethost ($ip)
{
return (
preg_match('/^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:[.]
(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/'
, $ip) ) ? gethostbyaddr($ip) : false;
}
?>
0
Matt AKA Junkie
20 年前
由于我的小型 ISP 服务并非全球通用,因此这是一个更新。

<?
function getisp($ip='') {
if ($ip=='') $ip = $_SERVER['REMOTE_ADDR'];
$longisp = @gethostbyaddr($ip);
$isp = explode('.', $longisp);
$isp = array_reverse($isp);
$tmp = $isp[1];
if (preg_match("/\<(org?|com?|net)\>/i", $tmp)) {
$myisp = $isp[2].'.'.$isp[1].'.'.$isp[0];
} else {
$myisp = $isp[1].'.'.$isp[0];
}
preg_match("/[0-9]{1,3}\.[0-9]{1,3}/", $myisp) ? return 'ISP lookup failed.' : return $myisp;
}
?>
0
abe at abe2k dot net
22 年前
gethostbyaddr() 似乎无法解析 ip6.int
(ipv6) 地址,所以我创建了一个可以工作的函数
就像普通的 gethostbyaddr() 一样。

你需要 dig 和 ipv6calc,dig 大多数
发行版都应该自带,如果没有,请从 http://www.isc.org. 安装 bind。
ipv6calc 可在 http://www.bieringer.de/linux/IPv6/ipv6calc/index.html. 找到。

function gethostbyaddr6($ip6) {
$ipv6calc = "/bin/ipv6calc";
$dig = "/usr/bin/dig";
$file = popen($ipv6calc." --in ipv6addr --out revnibbles.int ".escapeshellarg($ip6), r);
$ip = fread($file, 128);
pclose($file);
if ((substr($ip, 0, 5) == "Error") || (!$ip)) return "Address is not a valid IPv6 address";
$file = popen($dig." ptr ".$ip, r);
while (!feof ($file)) {
$buffer = fgets($file, 128);
if (substr($buffer, 0, 1) == ";") continue;
$buffer = explode(" ", $buffer);
if ($buffer[3] == "PTR") {
$host = substr(trim($buffer[4]), 0, -1);
pclose($file);
return $host;
}
}
pclose($file);
return $ip6;
}

echo gethostbyaddr6($_SERVER[REMOTE_ADDR]);
0
phalkon at nospam dot home dot com
23年前
查找许多主机名时要小心。如果您的 DNS 服务器响应缓慢,您可能需要增加脚本的最大执行时间,否则它将超时。我发现即使是 3 个无法解析的主机也会导致处理延迟 30 秒。
-1
Stuart Macdonald
14年前
这是一个简单的函数,它使用 Dig 获取给定 IP 地址的主机名。如果找不到主机名,它将再次返回 IP 地址。

仅适用于 linux/unix 或其他安装了 dig 命令行实用程序的平台。

<?php
function tryGetHost($ip)
{
$string = '';
exec("dig +short -x $ip 2>&1", $output, $retval);
if (
$retval != 0)
{
// 执行命令时出错
}
else
{
$x=0;
while (
$x < (sizeof($output)))
{
$string.= $output[$x];
$x++;
}
}

if (empty(
$string))
$string = $ip;
else
// 删除尾随的点
$string = substr($string, 0, -1);

return
$string;
}
?>
-1
pulstar at mail dot com
22 年前
如果您需要在数据库中存储 IP 地址,您可以将其转换为 INT 类型列(4 字节)并存储。以下函数可以将 IP 地址转换为其十进制整数值,反之亦然。

function ip2dec($ipaddr) {
$base=explode(".",$ipaddr);
$decimal=(double) $base[0]*16777216;
$decimal+=$base[1]*65536;
$decimal+=$base[2]*256;
$decimal+=$base[3];
if($decimal>2147483647) {
$decimal-=4294967296;
}
return (int) $decimal;
}

function dec2ip($dec) {
if($dec<0) {
$dec=(double) 4294967296+$dec;
}
if($dec>16777215) {
$ip=$dec-(intval($dec/256)*256);
$dec=(double) intval($dec/256);
} else $ip="0";
if($dec>65535) {
$ip=($dec-(intval($dec/256)*256)).".".$ip;
$dec=(double) intval($dec/256);
} else $ip="0.".$ip;
if($dec>255) {
$ip=($dec-(intval($dec/256)*256)).".".$ip;
$dec=(double) intval($dec/256);
} else $ip="0.".$ip;
$ip=$dec.".".$ip;
return (string) $ip;
}
-1
james at trnxs dot net
13年前
您应该小心使用 $_SERVER['HTTP_X_FORWARDED_FOR'],因为我发现,一旦使用 Amazon AWS 的 Elastic Load Balancer,此值可能是一个用逗号分隔的 IP 地址列表,因此在几乎所有我见过的用户在评论中发布的示例中,它都不会像设想的那样进行比较。
-1
dhjdhj at gmail dot com
14年前
我观察到,所有使用 IP 地址的存在来验证正在查找的名称是否实际存在的方法都存在问题。

如果您使用的是 opendns,则对不存在的服务器的请求仍然会返回一个 IP 地址,该地址是 opendns 服务器的一个地址。此过程大概是为了确保浏览器请求中错误的 URL 会将您带到一个“合法”页面,即 openDNS 网站,在那里他们可以通知您出现问题。

不幸的是,这种机制似乎适用于任何不存在的主机名。在主机名后面附加一个句点似乎无济于事。
-1
chris at ocproducts dot com
5 年前
此函数至少在 Linux 上尊重在“hosts”文件中进行的覆盖。
-2
andy at occ dot nu
23年前
如果用户位于代理服务器后面,您可以这样做:

<?
if ($HTTP_SERVER_VARS["HTTP_X_FORWARDED_FOR"] != ""){
$IP = $HTTP_SERVER_VARS["HTTP_X_FORWARDED_FOR"];
$proxy = $HTTP_SERVER_VARS["REMOTE_ADDR"];
$host = @gethostbyaddr($HTTP_SERVER_VARS["HTTP_X_FORWARDED_FOR"]);
}else{
$IP = $HTTP_SERVER_VARS["REMOTE_ADDR"];
$host = @gethostbyaddr($HTTP_SERVER_VARS["REMOTE_ADDR"]);
}
?>

ps; 我使用 $HTTP_SERVER_VARS["something"] 而不是 $something;
您可以通过只使用其 $something 等价物来获取大部分 $HTTP_SERVER_VARS,请参阅手册了解详情(保留变量)
-1
grimNOSPAMtraffic at hotNOSPAMmail dot com
21年前
如果您找到了 IP 的主机名,最短的截断方法(不向公众显示完整主机名)是

$host = substr($host, strpos($host, ".") + 1);

附注:如果你想为每个省略的符号添加"*",也可以轻松地使用strpos()函数,如下所示

$os = strpos($host, ".");
$host = substr($host, $os);
$host = str_repeat("*", $os) . $host;

--McTrafik
-1
ven at PragaKhan dot com
22 年前
如果Apache配置为执行主机名查找,则$REMOTE_HOST或$_SERVER['REMOTE_HOST']将返回反向IP。

HostnameLookups On
-1
elpmille at indiana dot edu
22 年前
我之前使用过与[email protected]发布的非常类似的方法,但发现它对于获取“nicehost”相当繁琐。下面的方法更加简洁,而且也适用于数字地址。

function nicehost($host) {
if (ereg('^([0-9]{1,3}\.){3}[0-9]{1,3}$', $host)) {
return(ereg_replace('\.[0-9]{1,3}$', '.*', $host));
} else {
return(ereg_replace('^.{' . strpos($host, '.') . '}', '*', $host));
}
}
-4
robbakAgmail_com
16年前
如果域名包含Unicode字符,则gethostbyname()似乎会失败。示例

$ host 10.10.10.128
128.10.10.10.in-addr.arpa domain name pointer PC-de-S\130bastien.flexi.robbak.com.
$ php
<?php echo gethostbyaddr( '10.10.10.128' ); ?>
10.10.10.128

gethostbyaddr在6.0版本中被列为支持Unicode,所以修复可能正在进行中。
-2
reinhard at ess dot co dot at
19 年前
尝试了下面的一些示例,但没有一个对我有用。
(
"host"命令在找不到域名时会返回一些信息
"gethostbyaddr"函数在失败时超时时间过长
"UDP示例"返回一些奇怪的字符...
)
所以我修改了一下"host"示例。希望有人能用得上。(也许可以稍作修改,例如去掉错误描述)

<?
function gethost($ip)
{
$host = `host $ip`;
$host=end(explode(' ',$host));
$host=substr($host,0,strlen($host)-2);
$chk=split("\(",$host);
if($chk[1]) return $ip." (".$chk[1].")";
else return $host;
}
?>
-2
webmaster at script-tease dot net
20 年前
由于某种原因,gethostbyaddr()函数在各种系统上往往会延迟。以下两个函数在速度方面应该能证明它们的价值。

<?php
// 适用于Linux...

function gethost ($ip) {
$host = `host $ip`;
return ((
$host ? end ( explode (' ', $host)) : $ip));
}

// 适用于Win32...

function nslookup ($ip) {
$host = split('Name:',`nslookup $ip`);
return (
trim (isset($host[1]) ? str_replace ("\n".'Address: '.$ip, '', $host[1]) : $ip));
}
?>

非常基础,但它应该能完成工作。
-2
dominique at vdx dot nl
22 年前
要将IP转换为数值,只需使用ip2long(...)函数。

反之亦然;使用:long2ip(...)
-3
alexey at ozerov dot de
22 年前
此函数在IIS 4.0服务器(Win32)上速度非常慢。我改用系统调用NSLOOKUP来获取PC主机名。

unset ($execoutput);
exec ("nslookup $IPAdresse 2>nul",$execoutput,$nslookstatus);
if (isset ($execoutput[3]) && ereg ("^Name: *([A-Za-z0-9]{2,})\.",$execoutput[3],$regs))
$nslookname=strtoupper($regs[1]);
else $nslookname="Unknown";

成员说明:这不可移植到Windows平台。因此,最好坚持使用我们的函数。
-3
Mark in Sussex
13年前
如果使用无效的IP地址调用gethostbyaddr()函数,它会向错误日志发送错误消息。
如果你不想让你的错误日志文件变得太大,那么首先检查IP地址是否有效。

在下面的示例中,我首先检查IP号是否以数字开头,
如果不是,则不使用gethostbyaddr('..')

<?php
$IP
= "BadValue.123.123.123";
if(
intval($IP)>0){
$ServerIP = gethostbyaddr($IP);
} else {
$ServerIP = $IP; // 无效地址
}
?>
-3
webmaster at askapache dot com
16年前
这是我根据用户笔记编写的简单函数,效果很好……有什么改进建议吗?

<?php
function get_ip( $host ){
$hostip = @gethostbyname( $host );
$ip = ( $hostip == $host ) ? $host : long2ip( ip2long( $hostip ) );
//echo sprintf("Resolved %s to %s", $host, $ip);
return $ip;
}
?>
-3
Matt AKA Junkie
20 年前
经过多次测试,得出以下结果

<?
// 如果你使用的是Windows服务器,则此方法更快
function getisp($ip='') {
if ($ip=='') $ip = $_SERVER['REMOTE_ADDR'];
$longisp = @gethostbyaddr($ip);
$isp = explode('.', $longisp);
$isp = array_reverse($isp);
$tmp = $isp[1];
if (preg_match("/\<(org?|com?|net)\>/i", $tmp)) {
$myisp = $isp[2].'.'.$isp[1].'.'.$isp[0];
} else {
$myisp = $isp[1].'.'.$isp[0];
}
if (preg_match("/[0-9]{1,3}\.[0-9]{1,3}/", $myisp))
return 'ISP lookup failed.';
return $myisp;
}

// 如果你的服务器在*nix系统上,则此方法更快
function gethost ($ip) {
$host = `host $ip`;
return (($host ? end ( explode (' ', $host)) : $ip));
}

// 但是请注意,如果启用了安全模式并使用了反引号变量,则gethost()会发出警告
//

?>
-3
stephane at metacites dot net
22 年前
gethostbyaddr_with_cache()

正如论坛中有人所说,一些无法解析的地址可能会减慢脚本的速度,直到超时。

虽然我认为gethostbyaddr()会使用某种缓存,但在IP无法解析时(至少在我的Win机器上)似乎不会。

因此,我编写了一个gethostbyaddr_with_cache()函数,如果要在同一页面上执行许多gethostbyaddr()操作,它将大大加快页面速度。

function gethostbyaddr_with_cache($a) {
global $dns_cache;
if ($dns_cache[$a]) {
return $dns_cache[$a];
} else {
$temp = gethostbyaddr($a);
$dns_cache[$a] = $temp;
return $temp;
}
}
-2
billyblue
16年前
我花了很长时间才弄清楚为什么我的gethostbyaddr函数会在中途失败。

我正在返回页面访问日志,对于每个新的IP,我都想获取该IP的主机名。在每个报表页面上,我从数据库中提取500行,但平均只有大约25个IP。有时此报表会在15秒内生成,而其他时候则会因连接重置而失败。

事实证明,我的数据库中的几个IP看起来像这样:x.255.x.x。gethostbyaddr非常讨厌这种情况,当它遇到这些IP时会直接崩溃。

在我的情况下,我清除了数据库并阻止了包含255的IP的记录。
-2
www.ad-rotator.com
21年前
对于ad-rotator.com,我们需要进行大量的IP查找,gethostbyaddr很容易超时,并且脚本会永远卡在那里。这是一个故障安全的替代方案,每个IP的超时时间最多为1秒。

function ar_gethostbyaddr($ip) {
$output = `host -W 1 $ip`;
if (ereg('.*pointer ([A-Za-z0-9.-]+)\..*',$output,$regs)) {
return $regs[1];
}
return $ip;
}
-2
inny at core dot fetchnet dot org
22 年前
在Apache配置文件中启用HostnameLookup功能会严重增加httpd服务器处理的所有页面的加载时间。
如果你启用了HostnameLookups,最好使用gethostbyaddr($REMOTE_ADDR);代替$REMOTE_HOST,除非你想在Apache的日志文件中指定主机名……
-3
jz at NOSPAM dot nplu dot kiev dot ua
21年前
有趣的是,当我尝试针对我自己的主机(从工作站)调用gethostbyadd()时,它返回的是局域网中已知的服务器机器名,而不是其DNS名,无论它是本地(在代理后面)还是全局(代理本身)IP,还是只是循环127.0.0.1。

因此,你可以通过其内网IP(例如192.168.0.???)获取其他工作站的局域网名称。

有趣 :)
To Top