gethostbyaddr

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

gethostbyaddr 获取指定的IP地址对应的主机名

说明

gethostbyaddr ( string $ip_address ) : string

返回指定的IP地址(ip_address)对应的主机名。

参数

ip_address

主机的IP地址。

返回值

成功则返回主机名;失败则原样输出(输出IP地址);如果输入的格式不正常,则返回false

范例

Example #1 一个使用 gethostbyaddr() 的简单例子

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

echo 
$hostname;
?>

参见

User Contributed Notes

Anonymous 23-Oct-2020 08:41
There is 1 bug in gethostbyaddr_timeout() function that king dot macro at gmail dot com posted in the most important line of all, setting the timeout with socket_set_timeout(). It should be:

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

I'd also make the $dns parameter last and optional and parse it from /etc/resolve.conf (or something else) if not given.
chris at ocproducts dot com 06-Jun-2019 01:27
This function does respect overrides made in the 'hosts' file, at least on Linux.
Vincent 29-Jan-2019 07:11
I discovered that gethostbyaddr sometimes returned the same host name with some uppercase letters in it and sometimes with all lowercase letters.

example:

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

This is probably not the fault of gethostbyaddr, but this can be a problem when comparing or storing, because it will give two entries instead of one.

A simple solution to this is to use strtolower on the host name.
nisha dot com126 at gmail dot com 26-Oct-2017 11:47
gethostbyaddr('10.176.xx.xx') returns IP address only not the host name....

Could you please suggest any solution..
Mark in Sussex 28-Aug-2011 11:21
If you use gethostbyaddr() with a bad IP address then it will send an error message to the error log.
If you don't want your error log file getting too big then first check that the IP address is valid.

In the following example I first check if the IP number starts with a number,
if not then don't use gethostbyaddr('..')

<?php
  $IP
= "BadValue.123.123.123";
  if(
intval($IP)>0){
   
$ServerIP = gethostbyaddr($IP);
  } else {
   
$ServerIP = $IP; // A bad address.
 
}
?>
james at trnxs dot net 12-Jan-2011 12:45
You should be careful in the use of $_SERVER['HTTP_X_FORWARDED_FOR'], as I discovered, once using Amazon AWS's Elastic Load Balancer's, this value may be a comma separated list of IP addresses and will thusly not compare as conceived in almost every example I have seen posted by users in the comments.
dhjdhj at gmail dot com 25-Sep-2010 06:58
I have observed that there are problems with all approaches that use the existence of an IP address to verify that the name being looked up actually exists.

If you're using opendns, then a request to a non-existent server returns an IP address anyway, the address being one for an opendns server. This is process is presumably in place so that errant URLs in browser requests take you to a "legitimate" page, i.e, the openDNS website where they can notify you of a problem.

Unfortunately, that mechanism seems to occur for ANY non-existent hostname. Appending a single period to the hostname does not seem to help.
Stuart Macdonald 28-Aug-2010 05:07
Here's a simple function that uses Dig to obtain the hostname for a given IP address. If no hostname can be found it returns the IP again.

Works only on linux / unix, or whatever other platform with dig installed as a command line utility.

<?php
   
function tryGetHost($ip)
    {
       
$string = '';
       
exec("dig +short -x $ip 2>&1", $output, $retval);
        if (
$retval != 0)
        {
           
// there was an error performing the command
       
}
        else
        {
           
$x=0;
            while (
$x < (sizeof($output)))
            {
               
$string.= $output[$x];
               
$x++;
            }
        }
       
        if (empty(
$string))
           
$string = $ip;
        else
//remove the trailing dot
           
$string = substr($string, 0, -1);
       
        return
$string;
    }
?>
marco[DOT]ceppi[@T]seacrow[DOT]org 01-Nov-2009 07:24
Anonymous has a good point (though I don't agree with pushing to execution to shell unless I have to. However this is a faster example (explode then loop is a little too intensive for a simple check)

<?php
function gethost ($ip)
{
   
//Make sure the input is not going to do anything unexpected
    //IPs must be in the form x.x.x.x with each x as a number
   
   
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;
    }
}
?>

Though to be honest I would use:

<?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;
}
?>
webmaster at askapache dot com 22-Oct-2008 01:57
Here is a simple function I compiled from user-notes that works great.. any improvements?

<?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;
}
?>
robbakAgmail_com 05-Jun-2008 11:19
it appears that gethostbyname() fails if the domain name contains unicode. Example:

 $ 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 is listed as unicode compatable in version 6, so a fix may be in the works.
billyblue 17-Dec-2007 08:12
I just spent a dickens of a time trying to figure out why my gethostbyaddr's were simply failing halfway through.

I'm returning a log of page visits, and with each new IP, I wanted to pull the hostname of the IP. On each report page, I'm pulling 500 lines from my Db, but only maybe 25 IPs on average. Sometimes this report would generate in under 15 seconds, other times it would fail with a connection reset.

It turns out that several of the IPs in my Db looked like this: x.255.x.x. gethostbyaddr really hates that and simply dies when it reaches one of these IPs.

For my case, I purged the Db and prevented the logging of IPs that contain 255.
oryan at zareste dot com 28-Feb-2006 03:17
If all else fails, but you have shell access, Unix/Linux servers can use this for a timeout response:

shell_exec('host -W 2 0.0.0.0');

Where 0.0.0.0 is of course the IP, and '2' is the number of seconds for the timeout.  This returns a more detailed string of info, with some additional text which might vary depending on the system, so if you want a string with the hostname and nothing else, you'll have to do some substring cutting.  There should be an equivalent of 'host' for Windows users to execute, but it isn't my platform.
tom 07-Oct-2005 01:14
Be careful with the usage of this function - it will slow down a server to a crawl if called a lot and the slowness won't be reflected in any of the obvious places, like CPU usage, apache requests, SQL etc. When you do use it make a special note of where!
reinhard at ess dot co dot at 12-Apr-2005 02:27
tried out some of the examples below, but no one worked for me.
(
"host" returns something if domain-name wasn't found
"gethostbyaddr" has a too long  timeout when it fails
"the udp-example" returns some strange characters...
)
so i have changed the "host"-example a little bit. hope someone can need it. (maybe with little changes like without error-description)

<?
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;
}
?>
king dot macro at gmail dot com 26-Oct-2004 07:28
The problem of broken DNS servers was causing me a problem because i had a page for user statistics that required around 20 reverse dns lookups to be done, and even as many as 5/6 of them being broken was causing a huge delay in loading the page. so i wrote a function that uses a UDP socket to talk directly to the DNS server (instead of going via the normal gethostbyaddr function) this let me set a timeout.

The only requirement is that your DNS server must be able to do recursive lookups, it wont go to other DNS servers if its told to... and of course you need to know your DNS servers IP address :-)

<?
function gethostbyaddr_timeout($ip, $dns, $timeout=1000)
{
    // random transaction number (for routers etc to get the reply back)
    $data = rand(0, 99);
    // trim it to 2 bytes
    $data = substr($data, 0, 2);
    // request header
    $data .= "\1\0\0\1\0\0\0\0\0\0";
    // split IP up
    $bits = explode(".", $ip);
    // error checking
    if (count($bits) != 4) return "ERROR";
    // there is probably a better way to do this bit...
    // loop through each segment
    for ($x=3; $x>=0; $x--)
    {
        // needs a byte to indicate the length of each segment of the request
        switch (strlen($bits[$x]))
        {
            case 1: // 1 byte long segment
                $data .= "\1"; break;
            case 2: // 2 byte long segment
                $data .= "\2"; break;
            case 3: // 3 byte long segment
                $data .= "\3"; break;
            default: // segment is too big, invalid IP
                return "INVALID";
        }
        // and the segment itself
        $data .= $bits[$x];
    }
    // and the final bit of the request
    $data .= "\7in-addr\4arpa\0\0\x0C\0\1";
    // create UDP socket
    $handle = @fsockopen("udp://$dns", 53);
    // send our request (and store request size so we can cheat later)
    $requestsize=@fwrite($handle, $data);

    @socket_set_timeout($handle, $timeout - $timeout%1000, $timeout%1000);
    // hope we get a reply
    $response = @fread($handle, 1000);
    @fclose($handle);
    if ($response == "")
        return $ip;
    // find the response type
    $type = @unpack("s", substr($response, $requestsize+2));
    if ($type[1] == 0x0C00)  // answer
    {
        // set up our variables
        $host="";
        $len = 0;
        // set our pointer at the beginning of the hostname
        // uses the request size from earlier rather than work it out
        $position=$requestsize+12;
        // reconstruct hostname
        do
        {
            // get segment size
            $len = unpack("c", substr($response, $position));
            // null terminated string, so length 0 = finished
            if ($len[1] == 0)
                // return the hostname, without the trailing .
                return substr($host, 0, strlen($host) -1);
            // add segment to our host
            $host .= substr($response, $position+1, $len[1]) . ".";
            // move pointer on to the next segment
            $position += $len[1] + 1;
        }
        while ($len != 0);
        // error - return the hostname we constructed (without the . on the end)
        return $ip;
    }
    return $ip;
}
?>

This could be expanded quite a bit and improved but it works and i've seen quite a few people trying various methods to achieve something like this so i decided to post it here. on most servers it should also be more efficient than other methods such as calling nslookup because it doesn't need to run external programs

Note: I'm more a C person than a PHP person, so just ignore it if anything hasn't been done the *recomended* way :-)
Matt AKA Junkie 14-Mar-2004 08:29
Going through numerous tests, the following results are concluded:

<?
// If you're using a server on Windows, this is faster
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;
}

// If your server is on a *nix system, this is faster
function gethost ($ip) {
 $host = `host $ip`;
 return (($host ? end ( explode (' ', $host)) : $ip));
}

// be warned, however, that gethost() will issue a warning
// if safe mode is on with the use of backticked variables

?>
webmaster at script-tease dot net 14-Mar-2004 04:37
gethostbyaddr() tends to lag on various systems for whatever reason. Here are two functions that should prove their worth speedwise.

<?php
// For Linux...

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

// For Win32...

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

Pretty basic, but it should get the job done.
Matt AKA Junkie 29-Feb-2004 04:11
Since my little ISP thing isn't globally acceptable, here's an update.

<?
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;
}
?>
grimNOSPAMtraffic at hotNOSPAMmail dot com 31-Jul-2003 02:31
If you have found the host of the ip, the shortest way to cut it not to display the full hostname to the public would be:

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

P.S. strpos() can also be easily used if you want to put "*" for every simbol you ommit, like so:

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

--McTrafik
www.ad-rotator.com 28-Apr-2003 04:06
For ad-rotator.com, we need to do a lot of IP lookups, gethostbyaddr is very easy get timed-out and the script stucks there forever. Here is a fail-safe alternative, 1 sec max for timeout per IP.

function ar_gethostbyaddr($ip) {
  $output = `host -W 1 $ip`;
  if (ereg('.*pointer ([A-Za-z0-9.-]+)\..*',$output,$regs)) {
    return $regs[1];
  }
  return $ip;
}
jz at NOSPAM dot nplu dot kiev dot ua 11-Apr-2003 03:59
Just to fun that gethostbyadd() returns server machine name known in LAN rather then its DNS if I try to call it against my own host (from workstation), whether it would local (behind the proxy) or global (of proxy itself) IP or just loop 127.0.0.1.

And thus you can get LAN names of other workstations by its intranet IPs (I mean smth like 192.168.0.???)

Fun :)
kriek at jonkriek dot com 21-Mar-2003 08:39
In response to god at weaponzero dot f2s dot com: I found this much easier to write.

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

IP address <?=$ip?> | Host: <?=$host?>

You can still protect the IP of your visitors and only show the hostname or show them both.
lukevb_at_iafrica.com 09-Nov-2002 02:43
Sometimes when using $_SERVER['HTTP_X_FORWARDED_FOR'] OR $_SERVER['REMOTE_ADDR'] more than 1 IP address is returned, for example '155.240.132.261, 196.250.25.120'. When this string is passed as an argument for gethostbyaddr() PHP gives the following error: Warning: Address is not a valid IPv4 or IPv6 address in...

To work around this I use the following code to extract the first IP address from the string and discard the rest. (If you wish to use the other IPs they will be in the other elements of the $ips array).

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

Hope this helps someone :)
abe at abe2k dot net 07-Nov-2002 06:32
gethostbyaddr() doesn't seem to be able to resolve ip6.int
(ipv6) adresses, so I made a function that can, and works
just like the normal gethostbyaddr().

You need dig and ipv6calc, dig should come with most
distributions, if not, install bind from http://www.isc.org.
ipv6calc can be found at 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]);
dominique at vdx dot nl 30-Sep-2002 05:14
To convert an IP to a numeric value, just use the ip2long (...) function.

Vice versa; use: long2ip (...)
pulstar at mail dot com 20-Sep-2002 02:59
If you need to store an IP addresses in a database, you can convert and store it in an INT type column (4 bytes). The functions below can convert IP addresses to its integer decimal value and vice-versa.

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;
}
alexey at ozerov dot de 15-Sep-2002 10:15
This function seems to be very slow on IIS 4.0 Server (Win32). I use system call to NSLOOKUP instead to get PC-Hostname:

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";

Note by members: This is not portable to Windows platforms. so you would be better to stay with our function.
stephane at metacites dot net 10-Sep-2002 07:17
gethostbyaddr_with_cache()

As someone truely said upper in the forum, some unresolved addresses may slow down your script to the point it times out.

Althought I had thought gethostbyaddr() would use some kind of cache, it doesn't seem to when the IP is unresolved (at least on my win machine).

So, I've coded a little gethostbyaddr_with_cache() function that will greatly speed your page if you have many gethostbyaddr() to perform on the same page.

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;
  }
    }
inny at core dot fetchnet dot org 10-Aug-2002 09:58
Turning on the HostnameLookup function on in the apache configuration file will severely increase the loading times of all the pages serviced by the httpd-server.
It's mostly a better idea to just use gethostbyaddr($REMOTE_ADDR); instead of $REMOTE_HOST if you turned HostnameLookups On, unless you want the hostnames specified in apache's log file...
ven at PragaKhan dot com 16-Jul-2002 03:55
$REMOTE_HOST or $_SERVER['REMOTE_HOST'] will give you the reversed ip IF apache is setup to do hostname lookups.

HostnameLookups On
elpmille at indiana dot edu 30-May-2002 12:26
I previously used something very similar to what [email protected] posted but found it to be quite tedious for getting the 'nicehost'. This method below is a lot cleaner, and it also works for numeric addresses.

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));
    }
}
nothanks at nospam dot com 16-May-2002 03:30
This is also very useful if you'd like to ban these annoying people that keep spamming your forums. It requires a clever use of mySql but can be great:

First, make a table for the bans, then make the query:

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

If your results aren't null it means his host matched a banMask in your DB, so you can redirect him with header() to a banned.php page and prevent him from accessing any content on your page!
phalkon at nospam dot home dot com 08-Jun-2001 03:58
Be cautious when looking up many hostnames.  If your DNS server is slow to respond, you may have to pump up your Max execution time for your scripts, otherwise, it will timeout.  I found that even 3 unresolvable hosts can cause a 30 second delay in processing.
andy at occ dot nu 12-Jan-2001 06:48
if the user is sitting behind a proxy server, you can do this;

<?
    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; i use $HTTP_SERVER_VARS["something"] instead of just $something;
you can get most of the $HTTP_SERVER_VARS by just using there $something equivalent, see the manual for that (preserved variables)