0

in PHP how can i determine if an IP address string is a localhost/loopback address or not? my attempt:

function isLocalhost(string $ip): bool
{
    if ($ip === "::1") {
        // IPv6 localhost (technically ::1/128 is the ipv6 loopback range, not sure how to check for that, bcmath probably)
        return true;
    }
    $long = @ip2long($ip);
    if ($long !== false && ($long >> 24) === 127) {
        // IPv4 loopback range 127.0.0.0/8
        return true;
    }
    return false;
}
hanshenrik
  • 19,904
  • 4
  • 43
  • 89
  • 1
    And what problem are you having, specifically? – ADyson Dec 01 '22 at 12:01
  • 3
    **localhost** is usually 127.0.0.1 (IP4). But nothing prevents you to have more interfaces on the machine. These are local yet not localhost as per common understanding. Also you should be able to to bind more to loopback as well, so the question really is - what is the problem you want to solve? – Marcin Orlowski Dec 01 '22 at 12:06
  • 1
    usually i store IPs data in .env files, i'd compare with those to determine if an IP is localhost or remote, or for example you specify in what environment you are by setting process env vars and you can determine if you are in local, test, development or prod environment... but your question is too general as other said – bLuke Dec 01 '22 at 12:08
  • $_SERVER['SERVER_ADDR'] doesn't show this? – jspit Dec 01 '22 at 12:19

1 Answers1

2

I use a function for that:

function is_local_ip($ip) {
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
        $ip = ip2long($ip);
        return ($ip >> 24) === 10 || ($ip >> 20) === 2752 || ($ip >> 16) === 49320 || ($ip >> 24) === 127;
    } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
        return substr($ip, 0, 2) === 'fc' || substr($ip, 0, 3) === 'fec' || substr($ip, 0, 4) === '2001';
    }
    return false;
}
// test the above application with a local ip and a public ip
echo is_local_ip('127.0.0.1') ? 'local' : 'public';
echo "<br />";
echo is_local_ip('41.29.18.31') ? 'local' : 'public';
echo "<br />";
echo is_local_ip('2001:db8::8c28:c929:72db:49fe') ? 'local' : 'public';

You can also use regex to catch ranges

function is_local_ip($ip) {
    if (preg_match('/^((127\.)|(192\.168\.)|(10\.)|(172\.1[6-9]\.)|(172\.2[0-9]\.)|(172\.3[0-1]\.)|(::1)|(fe80::))/', $ip)) {
        return true;
    } else {
        return false;
    }
}
Moudi
  • 525
  • 3
  • 12
  • What about `10.0.0.0 to 10.255.255.255`, `172.16.0.0 to 172.31.255.255` and `192.168.0.0 to 192.168.255.255`, because technically these aren't public, but private ranges – DarkBee Dec 01 '22 at 12:39
  • Actually, now that you mention it, this is my old function. I use regex now. Updated answer to include a regex version – Moudi Dec 01 '22 at 12:47