time out problem with gethostbyaddr() function in php - php

recently i noticed that on my new server the gethostbyaddr() function which is used on my site to get the referes' hosts, it makes the page load 5 times slower.
and when I removed it the time out problem is gone
what is wrong with this function and my new Centos linux server config.
and what else i can use instead of this php function to get the host name of my referers.

It may be a temporary slowness in DNS resolution and it may clear itself up later. There really isn't any alternative to gethostbyaddr() other than to make a system call like the following from PHP. (gethostbyaddr() does essentially this anyway)
`nslookup $ip_address`
// Or
`host $ip_address`
You can test to see if resolutions are slow from the command line with :
# choose an IP address you know the resolution of...
$ host 123.123.123.123
If this doesn't return quickly you may have a DNS problem.
Check the contents of /etc/resolv.conf and if you have an alternate DNS server you can point it to, try that one instead.

I wrote this useful replacement for gethostbyaddr():
//faster alternative to gethostbyaddr()
private 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 -s -W 1 $ip`;
$host = ( $host ? end( explode( ' ', trim( trim( $host ), '.' ) ) ) : $ip );
if( in_array( $host, Array( 'reached', 'record', '2(SERVFAIL)', '3(NXDOMAIN)' ) ) )
{
return sprintf( '(error fetching domain name for %s)', $ip );
}
else
{
return $host;
}
}
else
{
return '(invalid IP address)';
}
}
The trick is to use the host function, but you have to check if the $ip is a real IP to avoid exploits.
I set the timout to 1 second.
It's much faster than using gethostbyaddr, although it is still slow when used in a loop.

What kind of web hosting do you have? Shared, VPS or dedicated. gethostbyaddr() performance is dependent on how effective your web server DNS resolution is. There are some scenarios which might decrease its performance but you would still need to say at least what type of hosting you are using.

I think you should just implement some type of caching system for gethostbyaddr() results (Well, most of DNS servers have built-in caching system, but your can malfunction for some reason) or try to use alternative DNS servers(with system call or better with PHP's sockets).

Unfortunately some isp's don't maintain reverse dns records for some ip addresses, and therefore a dns lookup will always fail. The problem is how long to wait before giving it up.
Some hosting services set the gethostbyaddr function timeout real short, and if a lookup isn't successful, the ip address will be substituted for the host name. This speeds things up, preventing long delays in displaying pages that call this function.
You should always scrutinize log entries that don't show host names, as they rather frequently indicate that the visitor is from an ip address with a reputation for nefarious Internet activity.

As gethostbyaddr() does not support timeouts and my hosting setup does not allow shell commands I decided to use a local API:
<?php
$ip = isset($_GET['ip']) ? filter_var($_GET['ip'], FILTER_VALIDATE_IP) : false;
if ($ip) {
echo json_encode(gethostbyaddr($ip));
}
?>
And now I send a request to this hostAPI.php through file_get_contents() as follows:
<?php
// prepare host url
function isSecure() {
return ((isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443) ? 's' : '';
}
$protocol_host = 'http' . isSecure() . '://' . filter_input(INPUT_SERVER, 'HTTP_HOST', FILTER_SANITIZE_URL);
// dns request
$context = stream_context_create(array(
'http' => array(
'timeout' => 0.4,// 400 ms
)
));
$ip = long2ip(mt_rand(0, "4294967295"));
// note: the host url needs public access!
$host = json_decode(#file_get_contents($protocol_host . '/hostAPI.php?ip=' . $ip, false, $context), true);
// output
echo 'IP: ' . $ip . "<br>\n";
echo 'HOST: ' . ($host ? $host : 'Timeout!') . "<br>\n";
echo 'Execution Time: ' . round((microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"]) * 1000) . ' ms';
?>
As you can see I set the timeout through stream_context_create().
Sample output:
IP: 56.152.200.204
HOST: Timeout!
Execution Time: 801 ms
IP: 81.139.131.130
HOST: host81-139-131-130.range81-139.btcentralplus.com
Execution Time: 52 ms
I do not know why, but on my setup the 0.4 timeout kicks in after > 800 ms so I needed to double the value to get the final timeout. Maybe someone else can comment that.
Warning
This creates one additional http request on your server for every request on your main script!

After upgrading from XP to 7 it effected PHP's handling of IPv4 versus IPv6 (e.g. 127.0.0.1 is returned as ::1) I had adapted some code to temporarily address this issue. It turns out that I was using gethostbyaddr on the IPv6 patch and thus it was getting stuck timing out for PHP.
Before...
$ip = ip2long6($ip);
$host = gethostbyaddr($ip);
After...
$host = gethostbyaddr($_SERVER['REMOTE_ADDR']);
You can use Xdebug and WinCacheGrind (I use XAMPP for local testing) to find what is timing out, here is a video tutorial for a quick run-through...
http://www.warpconduit.net/2012/09/01/quick-tip-profiling-php-applications-with-xdebug-wincachegrind-xampp-for-windows/
Hope this saves someone some sanity. :-)

Related

how to get server ip4 *and* ip6 using php [duplicate]

I have a script which sends a request to another server but problem is that IPv6 does not supported so if I send IPv6 then give error so i need this one of two:
Get IPv4 address all time
or
Get both IPv4 and IPv6 addresses
I use this code to get the IP address
function getRealIP()
{
if (isset($_SERVER["HTTP_CLIENT_IP"])) {
$ip = $_SERVER["HTTP_CLIENT_IP"];
} elseif (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) {
$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
} elseif (isset($_SERVER["HTTP_X_FORWARDED"])) {
$ip = $_SERVER["HTTP_X_FORWARDED"];
} elseif (isset($_SERVER["HTTP_FORWARDED_FOR"])) {
$ip = $_SERVER["HTTP_FORWARDED_FOR"];
} elseif (isset($_SERVER["HTTP_FORWARDED"])) {
$ip = $_SERVER["HTTP_FORWARDED"];
} else {
$ip = $_SERVER["REMOTE_ADDR"];
}
// Strip any secondary IP etc from the IP address
if (strpos($ip, ',') > 0) {
$ip = substr($ip, 0, strpos($ip, ','));
}
return $ip;
}
But this function only returns one IPv address. How can I get all time IPv4 or get both addresses?
A client will send a request to your server using only one protocol. It doesn't send the request using both IPv4 and IPv6 at the same time, and there's no way to interleave both protocols, and IPv4 addresses also don't translate into equivalent IPv6 addresses. If the client sent the request using IPv4, then you'll get the IPv4 address. If they sent the request using IPv6, you'll get the IPv6 address. Period. End of story.
If you require an IPv4 address, then you have to disable IPv6 support on your server/DNS entry, so all clients are forced to use IPv4 as the only available protocol. But that would also be a terrible step backwards in this day and age.
You can't.
Only the IP address the request came from is available.
There's no reliable way to identify other IP addresses (my laptop currently has 12 IP addresses) that route to the same computer.
This is just a thought but maybe the trick is how said client queries the server.
Its what I use to gain my Public WAN_IPv4 & Public WAN_IPv6 with a simple Cron job to fire every xx mins and update one of my DNS records
Why not go up a layer and query the hostname NS A and AAAA records itself?
$ dig dom.domain -A | grep "ip ..." [with some some regex to extract specific pattern.]
Or a combination of approaches, maybe even employ curl to grab the -ipv4 address and then use a direct call to "ping" the myip6.php script for IPv6 return.?
Or keep it simple via dns resolving on interface with cURL
$ curl --help all
...
--dns-interface <interface> Interface to use for DNS requests
--dns-ipv4-addr <address> IPv4 address to use for DNS requests
--dns-ipv6-addr <address> IPv6 address to use for DNS requests
--dns-servers <addresses> DNS server addrs to use
...
--interface <name> Use network INTERFACE (or address)
-4, --ipv4 Resolve names to IPv`enter code here`4 addresses
-6, --ipv6 Resolve names to IPv6 addresses
...
if using curl you can specify option -4 and -6 to indicate which IP stack to return its result
$ curl -4 dom.domain
OUTPUT:
> 255.255.255.255
The same can be done for IPv6 by using -6
$ curl -6 dom.domin
OUTPUT:
> 20a1:efef:ffff::0
Above can then of course can be piped out to either console or stored inside a _VAR for later use.
I mean this is not a direct method to answer your specific question BUT it does give you options when managing dual-ipstack machines and its what I do.
There is also a very good example on how you could use curl for this exact job [name-resolve-tricks-with-c-ares][1]
[1]: https://everything.curl.dev/usingcurl/connections/name#name-resolve-tricks-with-c-ares

Get real ip address using CloudFlare [duplicate]

I'm trying to track and log users/visitors that are accessing my website using PHP's $_SERVER['REMOTE_ADDR'] to do so. A typical method for IP address tracking in PHP.
However, I am using CloudFlare for caching and such and receiving their IP addresses as CloudFlare's:
108.162.212.* - 108.162.239.*
What would be a correct method of retrieving the actual users/visitors IP address while still using CloudFlare?
Extra server variables that are available to cloud flare are:
$_SERVER["HTTP_CF_CONNECTING_IP"] real visitor ip address, this is what you want
$_SERVER["HTTP_CF_IPCOUNTRY"] country of visitor
$_SERVER["HTTP_CF_RAY"]
$_SERVER["HTTP_CF_VISITOR"] this can help you know if its http or https
you can use it like this:
if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
}
If you do this, and the validity of the visiting IP address is important, you might need to verify that the $_SERVER["REMOTE_ADDR"] contains an actual valid cloudflare IP address, because anyone can fake the header if he was able to connect directly to the server IP.
Update: CloudFlare has released a module mod_cloudflare for apache, the module will log and display the actual visitor IP Addresses rather than those accessed by cloudflare! https://www.cloudflare.com/resources-downloads#mod_cloudflare (Answer by: olimortimer)
If you dont have access to the apache runtime you can use the script below, this will allow you to check if the connection was through cloudflare and get the users ip.
I am rewriting my answer i used for another question "CloudFlare DNS / direct IP identifier"
Cloudflare's ips are stored in public so you can go view them here then check if the ip is from cloudflare (this will allow us to get the real ip from the http header HTTP_CF_CONNECTING_IP).
If you are using this to disable all non cf connections or vice versa, i recommend you to have a single php script file that gets called before every other script such as a common.php or pagestart.php etc.
function ip_in_range($ip, $range) {
if (strpos($range, '/') == false)
$range .= '/32';
// $range is in IP/CIDR format eg 127.0.0.1/24
list($range, $netmask) = explode('/', $range, 2);
$range_decimal = ip2long($range);
$ip_decimal = ip2long($ip);
$wildcard_decimal = pow(2, (32 - $netmask)) - 1;
$netmask_decimal = ~ $wildcard_decimal;
return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
}
function _cloudflare_CheckIP($ip) {
$cf_ips = array(
'199.27.128.0/21',
'173.245.48.0/20',
'103.21.244.0/22',
'103.22.200.0/22',
'103.31.4.0/22',
'141.101.64.0/18',
'108.162.192.0/18',
'190.93.240.0/20',
'188.114.96.0/20',
'197.234.240.0/22',
'198.41.128.0/17',
'162.158.0.0/15',
'104.16.0.0/12',
);
$is_cf_ip = false;
foreach ($cf_ips as $cf_ip) {
if (ip_in_range($ip, $cf_ip)) {
$is_cf_ip = true;
break;
}
} return $is_cf_ip;
}
function _cloudflare_Requests_Check() {
$flag = true;
if(!isset($_SERVER['HTTP_CF_CONNECTING_IP'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_IPCOUNTRY'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_RAY'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_VISITOR'])) $flag = false;
return $flag;
}
function isCloudflare() {
$ipCheck = _cloudflare_CheckIP($_SERVER['REMOTE_ADDR']);
$requestCheck = _cloudflare_Requests_Check();
return ($ipCheck && $requestCheck);
}
// Use when handling ip's
function getRequestIP() {
$check = isCloudflare();
if($check) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
} else {
return $_SERVER['REMOTE_ADDR'];
}
}
To use the script it's quite simple:
$ip = getRequestIP();
$cf = isCloudflare();
if($cf) echo "Connection is through cloudflare: <br>";
else echo "Connection is not through cloudflare: <br>";
echo "Your actual ip address is: ". $ip;
echo "The server remote address header is: ". $_SERVER['REMOTE_ADDR'];
This script will show you the real ip address and if the request is CF or not!
Cloudflare sends some additional request headers to your server including CF-Connecting-IP which we can store into $user_ip, if defined, using this simple one-liner:
$user_ip = $_SERVER["HTTP_CF_CONNECTING_IP"] ?? $_SERVER['REMOTE_ADDR'];
Since this question was asked and answered, CloudFlare has released mod_cloudflare for Apache, which logs & displays the actual visitor IP address rather than the CloudFlare address:
https://www.cloudflare.com/resources-downloads#mod_cloudflare
It would be hard to convert HTTP_CF_CONNECTING_IP to REMOTE_ADDR. So you can use apache (.htaccess) auto prepending to do that. So that you do not need to think about whether the $_SERVER['REMOTE_ADDR'] has the correct value in all the PHP scripts.
.htaccess code
php_value auto_prepend_file "/path/to/file.php"
php code (file.php)
<?php
define('CLIENT_IP', isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : $_SERVER['REMOTE_ADDR']);
Learn More here
HTTP_CF_CONNECTING_IP is only working if you are using cloudflare maybe you transfer your site or remove cloudflare you will forget the value so use this code .
$ip=$_SERVER["HTTP_CF_CONNECTING_IP"];
if (!isset($ip)) {
$ip = $_SERVER['REMOTE_ADDR'];
}
When you are using CloudFlare all your requests between your server and users are routed through CloudFlare servers.
In this case there are two methods to get User's Real IP Address:
Through Extra Server Headers added by CloudFlare Servers
Adding a CloudFlare Apache/NGINX Module on your server.
Method 1: Get IP though extra Server Headers
You can use the following code to get user's IP Address:
$user_ip = (isset($_SERVER["HTTP_CF_CONNECTING_IP"]) $_SERVER["HTTP_CF_CONNECTING_IP"]:$_SERVER['REMOTE_ADDR']);
How it is working?
CloudFlare adds some extra server variables in the request as follows:
$_SERVER["HTTP_CF_CONNECTING_IP"] - Real IP Address of user
$_SERVER["HTTP_CF_IPCOUNTRY"] - ISO2 Country of the User
$_SERVER["HTTP_CF_RAY"] A Special string for loggin purpose
In the above code, we are checking if $_SERVER["HTTP_CF_CONNECTING_IP"] is set or not. If it is there we will consider that as user's IP Address else we will use the default code as $_SERVER['REMOTE_ADDR']
Method 2: Installing Cloudflare Module on your server
Installing CloudFlare Module on Apache Server
Installing CloudFlare Module on NGINX Server
Installing CloudFlare Module on EasyApache+CPanel
In Laravel add the following line to AppServiceProvider:
...
use Symfony\Component\HttpFoundation\Request;
...
public function boot()
{
Request::setTrustedProxies(['REMOTE_ADDR'], Request::HEADER_X_FORWARDED_FOR);
}
Now you can get real client IP using request()->ip().
Read more here.
Cloudflare has an option to use a "Pseudo IPv4" address in the headers on the Network Management page for the domain. You have the option of adding or overwriting the headers that are sent to your server.
From the documentation:
What is Pseudo IPv4?
As a stopgap to accelerate the adoption of IPv6, Cloudflare offers Pseudo IPv4 which supports IPv6 addresses in legacy applications expecting IPv4 addresses. The goal is to provide a nearly unique IPv4 address for each IPv6 address, using Class E IPv4 address space, which is designated as experimental and would not normally see traffic. To learn more see here.
Options
Add header: Add additional Cf-Pseudo-IPv4 header only
Overwrite headers: Overwrite the existing Cf-Connecting-IP and X-Forwarded-For headers with a pseudo IPv4 address.
Note: We recommend leaving this set to “Off” unless you have a specific need.
You can learn more about this feature from this article from Cloudflare, where it goes into a little more detail about how they try to accommodate the 128-bit address space of IPv6 in the 32-bit space found in IPv4.
In the event that you would like to know the IPv6 address along with the pseudo IPv4, Cloudflare adds a Cf-Connecting-IPv6 header when Pseudo IPv4 is enabled. This can be used to measure how much of your traffic is originating from devices that are on IPv6 networks, which can be useful if you need a solid number to show management before investments are made in updating systems that are still bound to the IPv4 limitations.
It's better to check cloudflare ip ranges online (because it may change anytime) then check it's from cloudflare or not.
Cloudflare IP txt
If the source is Cloudflare you can use $_SERVER['HTTP_CF_CONNECTING_IP'] to get your client request ip-address but it's not safe to use it for all requests because it can send by any user in request header to trick you.
You can use following code to get real ip-address of your client request:
function _getUserRealIP() {
$ipaddress = '';
if(isset($_SERVER['REMOTE_ADDR']))
$ipaddress = $_SERVER['REMOTE_ADDR'];
else if (isset($_SERVER['HTTP_CLIENT_IP']))
$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_X_FORWARDED']))
$ipaddress = $_SERVER['HTTP_X_FORWARDED'];
else if(isset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
$ipaddress = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
else if(isset($_SERVER['HTTP_FORWARDED_FOR']))
$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_FORWARDED']))
$ipaddress = $_SERVER['HTTP_FORWARDED'];
else
$ipaddress = 'UNKNOWN';
return $ipaddress;
}
function _readCloudflareIps()
{
$file = file("https://www.cloudflare.com/ips-v4",FILE_IGNORE_NEW_LINES);
return $file;
}
function _checkIpInRange($ip, $range) {
if (strpos($range, '/') == false)
$range .= '/32';
// $range is in IP/CIDR format eg 127.0.0.1/24
list($range, $netmask) = explode('/', $range, 2);
$range_decimal = ip2long($range);
$ip_decimal = ip2long($ip);
$wildcard_decimal = pow(2, (32 - $netmask)) - 1;
$netmask_decimal = ~ $wildcard_decimal;
return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
}
function _checkIsCloudflare($ip) {
$cf_ips = _readCloudflareIps();
$is_cf_ip = false;
foreach ($cf_ips as $cf_ip) {
if (_checkIpInRange($ip, $cf_ip)) {
$is_cf_ip = true;
break;
}
}
return $is_cf_ip;
}
function getRealIp()
{
$httpIp = _getUserRealIP();
$check = _checkIsCloudflare($httpIp);
if ($check) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}else{
return $httpIp;
}
}
After importing this functions to your code you just need to call getRealIp() function like:
$userIp = getRealIp();
echo $userIp();
For magento 1.x users (I haven't try magento 2.0 yet), check https://tall-paul.co.uk/2012/03/13/magento-show-remote-ip-in-cloudflare-the-right-way/ which needs to change app/etc/local.xml and add:
HTTP_CF_CONNECTING_IP
another way to get it in Laravel is simply by reading HTTP_CF_CONNECTING_IP header
$request->server('HTTP_CF_CONNECTING_IP')
you can read more about it here:
How to get real client IP behind Cloudflare in Laravel / PHP

CloudFlare and logging visitor IP addresses via in PHP

I'm trying to track and log users/visitors that are accessing my website using PHP's $_SERVER['REMOTE_ADDR'] to do so. A typical method for IP address tracking in PHP.
However, I am using CloudFlare for caching and such and receiving their IP addresses as CloudFlare's:
108.162.212.* - 108.162.239.*
What would be a correct method of retrieving the actual users/visitors IP address while still using CloudFlare?
Extra server variables that are available to cloud flare are:
$_SERVER["HTTP_CF_CONNECTING_IP"] real visitor ip address, this is what you want
$_SERVER["HTTP_CF_IPCOUNTRY"] country of visitor
$_SERVER["HTTP_CF_RAY"]
$_SERVER["HTTP_CF_VISITOR"] this can help you know if its http or https
you can use it like this:
if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
}
If you do this, and the validity of the visiting IP address is important, you might need to verify that the $_SERVER["REMOTE_ADDR"] contains an actual valid cloudflare IP address, because anyone can fake the header if he was able to connect directly to the server IP.
Update: CloudFlare has released a module mod_cloudflare for apache, the module will log and display the actual visitor IP Addresses rather than those accessed by cloudflare! https://www.cloudflare.com/resources-downloads#mod_cloudflare (Answer by: olimortimer)
If you dont have access to the apache runtime you can use the script below, this will allow you to check if the connection was through cloudflare and get the users ip.
I am rewriting my answer i used for another question "CloudFlare DNS / direct IP identifier"
Cloudflare's ips are stored in public so you can go view them here then check if the ip is from cloudflare (this will allow us to get the real ip from the http header HTTP_CF_CONNECTING_IP).
If you are using this to disable all non cf connections or vice versa, i recommend you to have a single php script file that gets called before every other script such as a common.php or pagestart.php etc.
function ip_in_range($ip, $range) {
if (strpos($range, '/') == false)
$range .= '/32';
// $range is in IP/CIDR format eg 127.0.0.1/24
list($range, $netmask) = explode('/', $range, 2);
$range_decimal = ip2long($range);
$ip_decimal = ip2long($ip);
$wildcard_decimal = pow(2, (32 - $netmask)) - 1;
$netmask_decimal = ~ $wildcard_decimal;
return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
}
function _cloudflare_CheckIP($ip) {
$cf_ips = array(
'199.27.128.0/21',
'173.245.48.0/20',
'103.21.244.0/22',
'103.22.200.0/22',
'103.31.4.0/22',
'141.101.64.0/18',
'108.162.192.0/18',
'190.93.240.0/20',
'188.114.96.0/20',
'197.234.240.0/22',
'198.41.128.0/17',
'162.158.0.0/15',
'104.16.0.0/12',
);
$is_cf_ip = false;
foreach ($cf_ips as $cf_ip) {
if (ip_in_range($ip, $cf_ip)) {
$is_cf_ip = true;
break;
}
} return $is_cf_ip;
}
function _cloudflare_Requests_Check() {
$flag = true;
if(!isset($_SERVER['HTTP_CF_CONNECTING_IP'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_IPCOUNTRY'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_RAY'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_VISITOR'])) $flag = false;
return $flag;
}
function isCloudflare() {
$ipCheck = _cloudflare_CheckIP($_SERVER['REMOTE_ADDR']);
$requestCheck = _cloudflare_Requests_Check();
return ($ipCheck && $requestCheck);
}
// Use when handling ip's
function getRequestIP() {
$check = isCloudflare();
if($check) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
} else {
return $_SERVER['REMOTE_ADDR'];
}
}
To use the script it's quite simple:
$ip = getRequestIP();
$cf = isCloudflare();
if($cf) echo "Connection is through cloudflare: <br>";
else echo "Connection is not through cloudflare: <br>";
echo "Your actual ip address is: ". $ip;
echo "The server remote address header is: ". $_SERVER['REMOTE_ADDR'];
This script will show you the real ip address and if the request is CF or not!
Cloudflare sends some additional request headers to your server including CF-Connecting-IP which we can store into $user_ip, if defined, using this simple one-liner:
$user_ip = $_SERVER["HTTP_CF_CONNECTING_IP"] ?? $_SERVER['REMOTE_ADDR'];
Since this question was asked and answered, CloudFlare has released mod_cloudflare for Apache, which logs & displays the actual visitor IP address rather than the CloudFlare address:
https://www.cloudflare.com/resources-downloads#mod_cloudflare
It would be hard to convert HTTP_CF_CONNECTING_IP to REMOTE_ADDR. So you can use apache (.htaccess) auto prepending to do that. So that you do not need to think about whether the $_SERVER['REMOTE_ADDR'] has the correct value in all the PHP scripts.
.htaccess code
php_value auto_prepend_file "/path/to/file.php"
php code (file.php)
<?php
define('CLIENT_IP', isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : $_SERVER['REMOTE_ADDR']);
Learn More here
HTTP_CF_CONNECTING_IP is only working if you are using cloudflare maybe you transfer your site or remove cloudflare you will forget the value so use this code .
$ip=$_SERVER["HTTP_CF_CONNECTING_IP"];
if (!isset($ip)) {
$ip = $_SERVER['REMOTE_ADDR'];
}
When you are using CloudFlare all your requests between your server and users are routed through CloudFlare servers.
In this case there are two methods to get User's Real IP Address:
Through Extra Server Headers added by CloudFlare Servers
Adding a CloudFlare Apache/NGINX Module on your server.
Method 1: Get IP though extra Server Headers
You can use the following code to get user's IP Address:
$user_ip = (isset($_SERVER["HTTP_CF_CONNECTING_IP"]) $_SERVER["HTTP_CF_CONNECTING_IP"]:$_SERVER['REMOTE_ADDR']);
How it is working?
CloudFlare adds some extra server variables in the request as follows:
$_SERVER["HTTP_CF_CONNECTING_IP"] - Real IP Address of user
$_SERVER["HTTP_CF_IPCOUNTRY"] - ISO2 Country of the User
$_SERVER["HTTP_CF_RAY"] A Special string for loggin purpose
In the above code, we are checking if $_SERVER["HTTP_CF_CONNECTING_IP"] is set or not. If it is there we will consider that as user's IP Address else we will use the default code as $_SERVER['REMOTE_ADDR']
Method 2: Installing Cloudflare Module on your server
Installing CloudFlare Module on Apache Server
Installing CloudFlare Module on NGINX Server
Installing CloudFlare Module on EasyApache+CPanel
In Laravel add the following line to AppServiceProvider:
...
use Symfony\Component\HttpFoundation\Request;
...
public function boot()
{
Request::setTrustedProxies(['REMOTE_ADDR'], Request::HEADER_X_FORWARDED_FOR);
}
Now you can get real client IP using request()->ip().
Read more here.
Cloudflare has an option to use a "Pseudo IPv4" address in the headers on the Network Management page for the domain. You have the option of adding or overwriting the headers that are sent to your server.
From the documentation:
What is Pseudo IPv4?
As a stopgap to accelerate the adoption of IPv6, Cloudflare offers Pseudo IPv4 which supports IPv6 addresses in legacy applications expecting IPv4 addresses. The goal is to provide a nearly unique IPv4 address for each IPv6 address, using Class E IPv4 address space, which is designated as experimental and would not normally see traffic. To learn more see here.
Options
Add header: Add additional Cf-Pseudo-IPv4 header only
Overwrite headers: Overwrite the existing Cf-Connecting-IP and X-Forwarded-For headers with a pseudo IPv4 address.
Note: We recommend leaving this set to “Off” unless you have a specific need.
You can learn more about this feature from this article from Cloudflare, where it goes into a little more detail about how they try to accommodate the 128-bit address space of IPv6 in the 32-bit space found in IPv4.
In the event that you would like to know the IPv6 address along with the pseudo IPv4, Cloudflare adds a Cf-Connecting-IPv6 header when Pseudo IPv4 is enabled. This can be used to measure how much of your traffic is originating from devices that are on IPv6 networks, which can be useful if you need a solid number to show management before investments are made in updating systems that are still bound to the IPv4 limitations.
It's better to check cloudflare ip ranges online (because it may change anytime) then check it's from cloudflare or not.
Cloudflare IP txt
If the source is Cloudflare you can use $_SERVER['HTTP_CF_CONNECTING_IP'] to get your client request ip-address but it's not safe to use it for all requests because it can send by any user in request header to trick you.
You can use following code to get real ip-address of your client request:
function _getUserRealIP() {
$ipaddress = '';
if(isset($_SERVER['REMOTE_ADDR']))
$ipaddress = $_SERVER['REMOTE_ADDR'];
else if (isset($_SERVER['HTTP_CLIENT_IP']))
$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_X_FORWARDED']))
$ipaddress = $_SERVER['HTTP_X_FORWARDED'];
else if(isset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
$ipaddress = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
else if(isset($_SERVER['HTTP_FORWARDED_FOR']))
$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_FORWARDED']))
$ipaddress = $_SERVER['HTTP_FORWARDED'];
else
$ipaddress = 'UNKNOWN';
return $ipaddress;
}
function _readCloudflareIps()
{
$file = file("https://www.cloudflare.com/ips-v4",FILE_IGNORE_NEW_LINES);
return $file;
}
function _checkIpInRange($ip, $range) {
if (strpos($range, '/') == false)
$range .= '/32';
// $range is in IP/CIDR format eg 127.0.0.1/24
list($range, $netmask) = explode('/', $range, 2);
$range_decimal = ip2long($range);
$ip_decimal = ip2long($ip);
$wildcard_decimal = pow(2, (32 - $netmask)) - 1;
$netmask_decimal = ~ $wildcard_decimal;
return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
}
function _checkIsCloudflare($ip) {
$cf_ips = _readCloudflareIps();
$is_cf_ip = false;
foreach ($cf_ips as $cf_ip) {
if (_checkIpInRange($ip, $cf_ip)) {
$is_cf_ip = true;
break;
}
}
return $is_cf_ip;
}
function getRealIp()
{
$httpIp = _getUserRealIP();
$check = _checkIsCloudflare($httpIp);
if ($check) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}else{
return $httpIp;
}
}
After importing this functions to your code you just need to call getRealIp() function like:
$userIp = getRealIp();
echo $userIp();
For magento 1.x users (I haven't try magento 2.0 yet), check https://tall-paul.co.uk/2012/03/13/magento-show-remote-ip-in-cloudflare-the-right-way/ which needs to change app/etc/local.xml and add:
HTTP_CF_CONNECTING_IP
another way to get it in Laravel is simply by reading HTTP_CF_CONNECTING_IP header
$request->server('HTTP_CF_CONNECTING_IP')
you can read more about it here:
How to get real client IP behind Cloudflare in Laravel / PHP

XERO-API is working on live server, but failing on test server

Okay so the basically I'm building an application to connect to xero on the activecollab framework. And I'm testing the xeroapi php script created by David Pitman. And I'm just trying to find out why my browser responds with The connection to the server was reset while the page was loading. (but doesn't generated any liveheaders nor does firebug pick anything up)...
(source: iforce.co.nz)
Here is a snippet of code, that is being used. (Everything has been setup prior using the XERO Api previewer and openssl.)
define('XERO_KEY','my-key-here'); //hidden for privacy reasons
define('XERO_SECRET','my-key-here'); //hidden for privacy reasons
define('XERO_PUBLIC_KEY_PATH', 'path/to/public.key');
define('XERO_PRIVATE_KEY_PATH', 'path/to/privatekey.pem');
$xero = new Xero(XERO_KEY, XERO_SECRET, XERO_PUBLIC_KEY_PATH, XERO_PRIVATE_KEY_PATH, 'xml' );
$organisation = $xero->organisation;
//echo the results back
if ( is_object($organisation) ) {
//use this to see the source code if the $format option is "xml"
echo htmlentities($organisation->asXML()) . "<hr />";
} else {
//use this to see the source code if the $format option is "json" or not specified
echo json_encode($organisation) . "<hr />";
}
And my problem is... that the error_log (php) doesn't display any errors a part from a warning:
2012-07-23 21:59:42 Notice : Undefined index: port (at C:\xampp\htdocs\ac3\activecollab\3.1.10\modules\xero_invoice_manager\lib\xero\xero.class.php on 644 line)
The code on xero.class.php line 644
/**
* parses the url and rebuilds it to be
* scheme://host/path
*/
public function get_normalized_http_url() {
$parts = parse_url($this->http_url);
$port = #$parts['port']; //this line says its undefined
$scheme = $parts['scheme'];
$host = $parts['host'];
$path = #$parts['path'];
$port or $port = ($scheme == 'https') ? '443' : '80';
if (($scheme == 'https' && $port != '443')
|| ($scheme == 'http' && $port != '80')) {
$host = "$host:$port";
}
return "$scheme://$host$path";
}
From investigating I've found on print_r the result of $parts in a preformatted tag is..
Array
(
[scheme] => https
[host] => api.xero.com
[path] => /api.xro/2.0/Organisation
)
The same information is used on a live server (for the past couple of months). but the xeroapi class is not working on the test server, does anyone have any advice as to why it isn't connecting? I'm running XAMPP Control Panel with apache on port 80 and PHP Version 5.3.8.
I'm not sure about your port issue.
However, the Xero API require an OAuth set-up, which is presumably done for you in the Xero class. Part of the OAuth stuff is setting a callback domain, this requires you register a callback domain with Xero. Xero allows you to use subdomains of the registered domain and I assume that the Xero class uses request information to set the correct domain.
On localhost, that domain is localhost, which is not a subdomain. You can register localhost, or do what I have done (because I don't have access to the Application account), and set a special local subdomain in your hosts file.
So if you use example.com, then a good 'local' domain is local.example.com. Hope that helps.

local host and port number in Server variable

I have the following snippet that I am stuck at how to figure out getting the port number so the result might "turn" out correct as expected.
$server_list = array(
"localhost:8282" => array("test" => FALSE, "site_id" => "flyToHeaven"),
);
$server_name = $_SERVER["SERVER_NAME"];
if (!isset($server_list[$server_name])) {
$server_name = "unknown";
print "unknown server name!!";
exit();
i will always get "Unknown Server Name" in the output. If I delete the port number after localhost then the error is different, which means it passed this 'if' condition. Any help please ....
I believe what you're looking for is a combination of $_SERVER['SERVER_NAME'] and $_SERVER['SERVER_PORT'], which defaults to port 80.
Try this:
$server_name = $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'];
Source: http://www.php.net/manual/en/reserved.variables.server.php
I am using $_SERVER['HTTP_HOST'] which gives me server-name::port-number. The documentation says that this is "contents of the Host: header from the current request, if there is one".
My runtime environment is apache running on Linux and PHP 5.4 and this has always worked.
$_SERVER['SERVER_NAME'] will only return the name of the server.
If you're typing in http://localhost:8282 in the browser,
you'll get 'localhost' as the value of $_SERVER['SERVER_NAME'].
If you want to get the request port, you want to use $_SERVER['SERVER_PORT']. If you combine them both like so:
$server = $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'];
then you will get something closer to what you want.
I would suggest you look at the $_SERVER manual for all the details.

Categories