gethostbyaddr

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

gethostbyaddr 获取与给定 IP 地址对应的互联网主机名

说明

gethostbyaddr(string $ip): string|false

返回由 ip 指定的互联网主机的 hostname。

参数

ip

主机 IP 地址。

返回值

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

示例

示例 #1 一个简单的 gethostbyaddr() 示例

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

echo
$hostname;
?>

参见

添加笔记

用户贡献笔记 35 条笔记

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

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

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

希望这能帮助到某些人 :)
13
king dot macro at gmail dot com
19 年前
损坏的 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
18 年前
小心使用此函数 - 如果调用次数过多,它会使服务器运行速度变慢,而这种慢速不会反映在任何明显的地方,如 CPU 使用率、Apache 请求、SQL 等。当你使用它时,请特别注意使用的地方!
1
Vincent
5 年前
我发现 gethostbyaddr 有时返回相同的 hostname,其中包含一些大写字母,有时则包含所有小写字母。

例如

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

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

一个简单的解决方案是在主机名上使用 strtolower。
1
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;
}
?>
1
Anonymous
3 年前
gethostbyaddr_timeout() 函数中存在一个错误,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
14 年前
匿名用户提出了一个很好的观点(虽然我不赞成除非必须才将执行推送到 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
abe at abe2k dot net
21 年前
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
13 年前
以下是一个简单的函数,它使用 Dig 获取给定 IP 地址的主机名。如果找不到主机名,它将再次返回 IP 地址。

仅适用于安装了 dig 命令行工具的 Linux/Unix 或任何其他平台。

<?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
21 年前
如果需要在数据库中存储 IP 地址,可以将其转换为 INT 类型列(4 字节)并存储。

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'] 时要小心,因为我发现,一旦使用亚马逊 AWS 的弹性负载均衡器,此值可能是一个逗号分隔的 IP 地址列表,因此在几乎所有我见过的用户在评论中发布的示例中,它都不会按预期进行比较。
-1
dhjdhj at gmail dot com
13 年前
我发现所有使用 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
dominique at vdx dot nl
21 年前
要将 IP 地址转换为数值,只需使用 ip2long(...) 函数。

反之亦然;使用:long2ip(...)
-1
ven at PragaKhan dot com
22 年前
$REMOTE_HOST 或 $_SERVER['REMOTE_HOST'] 会返回反转的 IP 地址(如果 Apache 设置为执行主机名查找)。

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 年前
似乎 gethostbyname() 在域名包含 Unicode 字符时会失败。例如:

$ 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 中被列为与 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));
}
?>

非常基础,但应该能完成任务。
-3
alexey at ozerov dot de
21 年前
此函数在 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
12 年前
如果你使用 gethostbyaddr() 与一个错误的 IP 地址,它将向错误日志发送错误消息。
如果你不希望错误日志文件变得太大,那么首先检查 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
15 年前
这是一个简单的函数,我从用户笔记中整理而成,效果很好.. 有什么改进吗?

<?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
21 年前
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 的主机名。在每个报表页面,我从 Db 中提取 500 行数据,但平均只有 25 个 IP。有时报表会在 15 秒内生成,有时会因连接重置而失败。

事实证明,Db 中的几个 IP 类似于:x.255.x.x。gethostbyaddr 非常讨厌这种格式,当遇到这些 IP 时就会直接挂掉。

对于我的情况,我清除了 Db 并阻止了包含 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-server 服务的所有页面的加载时间。
如果你启用了 HostnameLookups,最好只使用 gethostbyaddr($REMOTE_ADDR);,而不是 $REMOTE_HOST,除非你想要在 apache 的日志文件中指定主机名...
-3
jz at NOSPAM dot nplu dot kiev dot ua
21 年前
有趣的是,当我对自己的主机(从工作站)进行调用时,gethostbyadd() 返回的是 LAN 中已知服务器的机器名,而不是它的 DNS,无论是本地(代理后面)还是全局(代理本身)IP,还是循环 127.0.0.1。

因此你可以通过其内网 IP 获取其他工作站的 LAN 名(我的意思是类似 192.168.0.?? 的东西)。

有趣 :D
-10
nisha dot com126 at gmail dot com
6 年前
gethostbyaddr('10.176.xx.xx') 只返回 IP 地址,而不是主机名....

你能否提供任何解决方案..
-13
nothanks at nospam dot com
22 年前
如果你想屏蔽那些不断在你的论坛上发布垃圾信息的人,这也很有用。这需要巧妙地使用 mySql,但效果很好。

首先,为屏蔽创建一个表,然后执行查询

"SELECT banReason from tBans where '". gethostbyaddr($REMOTE_ADDR) ."' LIKE banMask"

如果你的结果不为空,则表示他的主机与你数据库中的 banMask 相匹配,因此你可以使用 header() 将他重定向到 banned.php 页面,并阻止他访问你页面上的任何内容!
-7
kriek at jonkriek dot com
21 年前
针对 god at weaponzero dot f2s dot com 的回复:我发现这种写法更方便。

<?php
$ip
= $_SERVER['REMOTE_ADDR'];
$fullhost = gethostbyaddr($ip);
$host = preg_replace("/^[^.]+./", "*.", $fullhost);
?>

IP 地址 <?=$ip?> | 主机:<?=$host?>

你仍然可以保护访客的 IP,只显示主机名,或者同时显示两者。
To Top