PHP Conference Japan 2024

gethostbyaddr

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

gethostbyaddr 取得給定 IP 位址對應的網際網路主機名稱

描述

gethostbyaddr(string $ip): string|false

傳回由 ip 指定的網際網路主機的主機名稱。

參數

ip

主機 IP 位址。

傳回值

成功時傳回主機名稱,失敗時傳回未修改的 ip,或輸入格式錯誤時傳回 false

範例

範例 1:一個簡單的 gethostbyaddr() 範例

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

echo
$hostname;
?>

參見

  • gethostbyname() - 取得與給定的網際網路主機名稱對應的 IPv4 位址
  • gethostbynamel() - 取得與給定的網際網路主機名稱對應的 IPv4 位址清單

新增註記

使用者貢獻註記 32 個註記

lukevb_at_iafrica.com
22 年前
有時在使用 $_SERVER['HTTP_X_FORWARDED_FOR'] 或 $_SERVER['REMOTE_ADDR'] 時,會傳回多個 IP 位址,例如 '155.240.132.261, 196.250.25.120'。當此字串作為 gethostbyaddr() 的引數傳遞時,PHP 會出現以下錯誤:Warning: Address is not a valid IPv4 or IPv6 address in...

為了解決這個問題,我使用以下程式碼從字串中提取第一個 IP 位址,並捨棄其餘的。(如果您希望使用其他 IP,它們將在 $ips 陣列的其他元素中)。

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

希望這對某人有幫助:)
king dot macro at gmail dot com
20 年前
損壞的 DNS 伺服器問題一直困擾著我,因為我有一個使用者統計頁面,需要執行大約 20 次反向 DNS 查詢,即使其中多達 5/6 個損壞,也會導致頁面載入的巨大延遲。因此,我編寫了一個函數,使用 UDP socket 直接與 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 socket
$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));
// 以 null 終止的字串,因此長度 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,所以如果任何事情沒有以*建議的*方式完成,請忽略它 :-)
tom
19 年前
請小心使用此函數 - 如果經常呼叫,它會使伺服器速度變慢,而且速度變慢不會反映在任何明顯的地方,例如 CPU 使用率、apache 請求、SQL 等。當您使用它時,請特別注意位置!
Vincent
5 年前
我發現 gethostbyaddr 有時會傳回相同的主機名稱,其中包含一些大寫字母,有時則全部小寫字母。

範例

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

這可能不是 gethostbyaddr 的錯,但這在比較或儲存時可能會成為問題,因為它會給出兩個條目而不是一個。

一個簡單的解決方案是在主機名稱上使用 strtolower。
Anonymous
4 年前
king dot macro at gmail dot com 發佈的 gethostbyaddr_timeout() 函數中存在 1 個錯誤,在最重要的設定逾時的行中,使用 socket_set_timeout()。它應該是

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

我也會將 $dns 參數放在最後且為可選,如果未給定,則從 /etc/resolve.conf(或其他內容)剖析它。
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' 的指令可以執行,但它不是我使用的平台。
marco[DOT]ceppi[@T]seacrow[DOT]org
15 年前
Anonymous 說得有道理(雖然我不同意非必要時就執行 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;
}
?>
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 查詢失敗。' : return $myisp;
}
?>
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 "位址不是有效的 IPv6 位址";
$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]);
phalkon at nospam dot home dot com
23 年前
當查詢許多主機名稱時要小心。如果您的 DNS 伺服器回應速度緩慢,您可能必須提高腳本的最大執行時間,否則會逾時。我發現,即使是 3 個無法解析的主機也會造成 30 秒的處理延遲。
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;
}
?>
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;
}
james at trnxs dot net
13 年前
您應該小心使用 $_SERVER['HTTP_X_FORWARDED_FOR'],因為我發現,一旦使用 Amazon AWS 的 Elastic Load Balancer 時,這個值可能會是以逗號分隔的 IP 位址清單,因此無法像我在評論中看到的使用者發布的幾乎每個範例中所構想的那樣進行比較。
dhjdhj at gmail dot com
14 年前
我發現所有使用 IP 位址的存在來驗證被查詢的名稱實際存在的做法都有問題。

如果您使用 opendns,那麼對不存在的伺服器的請求仍然會回傳 IP 位址,該位址是 opendns 伺服器的位址。這個處理流程可能是在瀏覽器請求中發生錯誤的 URL 會將您帶到「合法的」頁面,也就是 openDNS 網站,他們可以在那裡通知您問題。

不幸的是,對於任何不存在的主機名稱,似乎都會發生這種機制。在主機名稱後面附加一個句點似乎沒有幫助。
chris at ocproducts dot com
5 年前
這個函式至少在 Linux 上會尊重在 'hosts' 檔案中所做的覆寫。
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,請參閱手冊(保留變數)
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
ven at PragaKhan dot com
22 年前
$REMOTE_HOST 或 $_SERVER['REMOTE_HOST'] 會在 apache 設定為執行主機名稱查詢時,回傳反向的 IP 位址。

HostnameLookups On
elpmille at indiana dot edu
22 年前
我之前使用過與 god@weaponzero.f2s.com 發佈的非常相似的東西,但發現它在取得 '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));
}
}
robbakAgmail_com
16 年前
如果網域名稱包含 Unicode,gethostbyname() 似乎會失敗。範例

$ host 10.10.10.128
128.10.10.10.in-addr.arpa 網域名稱指標 PC-de-S\130bastien.flexi.robbak.com.
$ php
<?php echo gethostbyaddr( '10.10.10.128' ); ?>
10.10.10.128

gethostbyaddr 在版本 6 中被列為與 Unicode 相容,因此可能正在進行修復。
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;
}
?>
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));
}
?>

相當基本,但它應該可以完成工作。
dominique at vdx dot nl
22 年前
要將 IP 轉換為數字值,只需使用 ip2long(...) 函數即可。

反之亦然;使用:long2ip(...)
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 平台。因此,您最好堅持使用我們的函數。
Mark in Sussex
13 年前
如果您使用 gethostbyaddr() 並提供錯誤的 IP 位址,它將會發送錯誤訊息到錯誤記錄檔。
如果您不希望您的錯誤記錄檔變得太大,請先檢查 IP 位址是否有效。

在以下範例中,我先檢查 IP 號碼是否以數字開頭,
如果不是,則不要使用 gethostbyaddr('..')

<?php
$IP
= "BadValue.123.123.123";
if(
intval($IP)>0){
$ServerIP = gethostbyaddr($IP);
} else {
$ServerIP = $IP; // 錯誤的位址。
}
?>
webmaster at askapache dot com
16 年前
這是我從使用者筆記中整理出來的一個簡單函式,效果很好...有任何改進嗎?

<?php
function get_ip( $host ){
$hostip = @gethostbyname( $host );
$ip = ( $hostip == $host ) ? $host : long2ip( ip2long( $hostip ) );
//echo sprintf("已將 %s 解析為 %s", $host, $ip);
return $ip;
}
?>
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 查詢失敗。';
return $myisp;
}

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

// 請注意,但是,如果安全模式開啟且使用了反引號變數,gethost() 將會發出警告
// 如果安全模式開啟且使用了反引號變數

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

正如論壇中某人所說,某些未解析的位址可能會使您的腳本速度變慢到逾時。

儘管我認為 gethostbyaddr() 會使用某種快取,但在 IP 未解析時似乎不會(至少在我的 Windows 機器上是這樣)。

因此,我編寫了一個小的 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;
}
}
billyblue
16 年前
我剛剛花了一大段時間試圖弄清楚為什麼我的 gethostbyaddr 會在中途失敗。

我正在返回頁面訪問的記錄,並且對於每個新的 IP,我都想提取 IP 的主機名稱。在每個報告頁面上,我從我的資料庫中提取 500 行,但平均只有 25 個 IP。有時此報告會在 15 秒內產生,其他時候則會因連線重設而失敗。

結果發現我的資料庫中的幾個 IP 看起來像這樣:x.255.x.x。 gethostbyaddr 非常討厭這種情況,並且當它到達其中一個 IP 時就會直接死掉。

就我的情況而言,我清除了資料庫並阻止記錄包含 255 的 IP。
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;
}
inny at core dot fetchnet dot org
22 年前
在 Apache 設定檔中啟用 HostnameLookup 函式將會嚴重增加 httpd 伺服器所服務的所有頁面的載入時間。
如果您啟用了 HostnameLookups,最好只使用 gethostbyaddr($REMOTE_ADDR);而不是 $REMOTE_HOST,除非您想要 Apache 的記錄檔中指定的主機名稱...
jz at NOSPAM dot nplu dot kiev dot ua
21 年前
只是好玩的是,如果我嘗試對自己的主機(從工作站)呼叫 gethostbyadd(),無論是本地的(在 Proxy 後面)還是全域的(Proxy 本身),或是只是迴路 127.0.0.1,它都會返回 LAN 中已知的伺服器機器名稱,而不是其 DNS。

因此,您可以透過其內部網路 IP 取得其他工作站的 LAN 名稱(我的意思是類似 192.168.0.??? 的東西)

有趣 :)
To Top