PHP: Tor check not working - php

I've installed a Tor relay and Nginx and created my .onion on my Linux server.
In torrc HiddenServicePort 80 127.0.0.1:8747
In nginx's default: listen 8747
I've modified TorDNSExitList's PHP Pear Net_DNS to use Net_DNS2. When I echo out the $ip, $myip, $myport I get:
ip = 127.0.0.1
my ip = 127.0.0.1
port = 8747
Thus it is picking the IP address as the local machine and not the Tor exit node's IP address. Is there another why to test if the page is access via the Tor network?
(I've also tried this suggestion)

The solution is to check for 127.0.0.1 IP address, seeing that torrc points to 127.0.0.1. This works when accessing the website via the .onion path. But the full check still needs to be done as the website can be access via the full URL, e.g. http:// [IP Address]:[Port] - using a "normal" or Tor browser. My changes to the function below:
<?php include("Net/DNS2.php");
// torel_check ($ip, $port, $destip) queries the Tor DNS Exit List server.
// The result of the query is one of the following:
// -1 : DNS lookup failed to get a response, or other error occurred.
// 0 : $ip does not appear to be a Tor exit.
// 1 : $ip is a known Tor exit for the provided destination IP / port.
function revaddr ($ip) {
list($a, $b, $c, $d) = split("[.]", $ip);
return("${d}.${c}.${b}.${a}");
}
function torel_qh ($ip, $port, $destip) {
$rsrcip = revaddr ($ip);
$rdstip = revaddr ($destip);
return("${rsrcip}.${port}.${rdstip}.ip-port.exitlist.torproject.org");
}
function torel_check ($ip, $port, $destip) {
try{
if($ip == "127.0.0.1") {
//TX: Access via .onion path
// is Tor exit
return (1);
}
//TX: Access web site directly
$ndr = new Net_DNS2_Resolver();
$qh = torel_qh($ip, $port, $destip);
// uncomment these two lines to query the server directly...
//$ns = "exitlist-ns.torproject.org";
//$ndr->nameservers( array($ns) );
// tune DNS params accordingly. this is just my preference.
$ndr->retrans = 2;
$ndr->retry = 3;
$ndr->usevc = 0;
// perform DNS query
// TX: Old Net_DNS check $ndr->search($qh)
if (! $pkt = $ndr->query($qh)) {
if (strcmp($ndr->errorstring, "NXDOMAIN") == 0) {
// response but no answer. does not appear to be Tor exit.
return (0);
}
// search failed: no response or other problem...
return(-1);
}
if (! isset($pkt->answer[0])) {
// response but no answer section. does not appear to be Tor exit.
// (this should only happen when authority sections are provided without answer)
return(0);
}
// is Tor exit
return(1);
} catch(Net_DNS2_Exception $e) {
return (-1);
}
}
// get client request parameters from Apache or equiv server:
$ip = $myip = $myport = 0;
if (isset ($_SERVER["REMOTE_ADDR"])) { $ip = $_SERVER["REMOTE_ADDR"]; }
if (isset ($_SERVER["SERVER_ADDR"])) { $myip = $_SERVER["SERVER_ADDR"]; }
if (isset ($_SERVER["SERVER_PORT"])) { $myport = $_SERVER["SERVER_PORT"]; }
$istor = torel_check($ip, $myport, $myip);
TX: is my comments

Related

php - check internet connect and DNS resolution

I know this may have been asked before, but I can't find anything that quite matches my specific requirements.
I'm loading a page on a local Linux server, when it loads I need to know does the server it is running on have Internet Access and is DNS resolving.
I've got this working, BUT... if there is no Internet connection the page takes a very long time to load, if there is a connection then it loads instantly.
I'm using the following to check for Internet Access:
$check1 = checkState('google-public-dns-a.google.com',53);
$check2 = checkState('resolver1.opendns.com',53);
if ($check1 == "YES" || $check2 == "YES"){
echo "Internet Available";
}
function checkState($site, $port) {
$state = array("NO", "YES");
$fp = #fsockopen($site, $port, $errno, $errstr, 2);
if (!$fp) {
return $state[0];
} else {
return $state[1];
}
}
and checking DNS resolution using:
$nameToIP = gethostbyname('www.google.com');
if (preg_match('/^\d/', $nameToIP) === 1) {
echo "DNS Resolves";
}
Can anyone recommend a better way ? so if there is no connection the page doesn't stall for a long time.
Thanks
You can use fsockopen
Following example works well and tells you whether you are connected to internet or not
function is_connected() {
$connected = #fsockopen("www.google.com", 80); //website, port (try 80 or 443)
if ($connected){
fclose($connected);
return true;
}
return false;
}
Reference : https://stackoverflow.com/a/4860432/2975952
Check DNS resolves here
function is_site_alive(){
$response = null;
system("ping -c 1 google.com", $response);
if($response == 0){
return true;
}
return false;
}
Reference : https://stackoverflow.com/a/4860429/2975952

Incorrect Client IP in PHP

I am getting a weird result for the client IP in PHP in some cases.
Result in Most Cases (Expected Result) :
192.123.132.123
Erroneous Result Type 1:
for="192.123.132.123"
Erroneous Result Type 2:
for="192.123.132.123:1232"
Code for getting the IP:
<?php
function getIP(){
$ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '-';
$proxy = false;
if (!empty($_SERVER['HTTP_VIA']) || !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$proxy = true;
} elseif (!empty($_SERVER['REMOTE_HOST'])) {
$aProxyHosts = array('proxy','cache','inktomi');
foreach ($aProxyHosts as $proxyName) {
if (strpos($_SERVER['REMOTE_HOST'], $proxyName) !== false) {
$proxy = true;
break;
}
}
}
// Has the viewer come via an HTTP proxy?
if ($proxy) {
// Try to find the "real" IP address the viewer has come from
$aHeaders = array('HTTP_FORWARDED','HTTP_FORWARDED_FOR','HTTP_X_FORWARDED','HTTP_X_FORWARDED_FOR','HTTP_CLIENT_IP');
foreach ($aHeaders as $header) {
if (!empty($_SERVER[$header])) {
$ip = $_SERVER[$header];
break;
}
}
}
if (!empty($ip)) {
// The "remote IP" may be a list, ensure that
// only the last item is used in that case
$ip = explode(',', $ip);
$ip = trim($ip[count($ip) - 1]);
}
return $ip;
}
?>
I know that I can clean the result to get the correct value (IP) but I am puzzled at why is this happening in the first place.
PS: 192.123.132.123 is an arbitrary IP used to explain the issue.
You're reading arbitrary HTTP headers... not all of them contain purely the IP, some are in the form of for=... and some include the port as well.
Using any HTTP header instead $_SERVER['REMOTE_ADDR'] means you're allowing anyone to mask/fake their IP address by simply sending an HTTP header. You should be perfectly aware of where such headers may be set, which usually means you know they're set by a proxy you control. In this case you obviously don't know where those headers are coming from, so you should not use them.
If you decide to use an HTTP header, you should know which one exactly you want to read and what format it's in. If its format is for=..., then parse that format correctly.

Redirect User if Internal IP Address

I am not familiar with PHP but I need to create a simple page to temporarily redirect internal users until a production problem is fixed.
If the user's IP address starts with "10.", "192.", or "172." then I need to redirect them to another server. If the user's IP address does not meet this criteria then I need to display a message telling the user the site is down for maintenance.
Can someone help me with this?
You can use preg_match() to see if the user's address ($_SERVER['REMOTE_ADDR']) starts with 10., 192., or 172.:
if(preg_match('/^(10|192|172)\./', $_SERVER['REMOTE_ADDR']))
{
header('Location: http://example.com');
die;
}
echo 'Site down for maintenance.';
$chunks = explode('.', $_SERVER['REMOTE_ADDR']);
$whitelist = array(10, 192, 172);
$server = "http://example.com";
if(in_array($chunks[0], $whitelist))
{
//redirect to another server
header("Location: " . $server);
die();
}
else
{
//Show maintenance message
die("The site is down for maintenance.");
}
You cannot reliably identify a local ip address via the first octet of an IPv4 address. Luckily, PHP has taken care of all of this for us. I know the OP was asking only about IPv4, but this solution covers IPv6 and reserved addresses as well.
/**
* Function returns true if IP Address is identified as private or reserved
*
* Uses REMOTE_ADDR, a reliable source as TCP handshake is required, most others can be spoofed
*
* FILTER_FLAG_NO_PRIV_RANGE:
* Fails validation for the following private IPv4 ranges: 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16.
* Fails validation for the IPv6 addresses starting with FD or FC.
*
* FILTER_FLAG_NO_RES_RANGE:
* Fails validation for the following reserved IPv4 ranges: 0.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24 and 224.0.0.0/4.
* This flag does not apply to IPv6 addresses.
*/
function isPrivateIp()
{
return !filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
}
/*Using function to get OP desired result*/
if (isPrivateIp() === true) {
$server = 'http://example.com';
//redirect to another server
header("Location: $server");
} else {
//Show maintenance message
echo 'The site is down for maintenance.';
}
exit;
Well,you can do this:
$ip = $_SERVER['REMOTE_ADDR']; //get IP address
$toRedirect = array(10,192,172);
$parts = explode('.', $ip);
$id = $parts[0];
if(in_array($id, $toRedirect)) {
//do redirect
}
<?php
// Settings
$toRedirect = array (10, 172, 192);
$redirectAddress = 'http://wherever.com/';
$maintenanceMessage = 'The site is down for maintenance';
// Split the IP address into octets
list($oct1, $oct2, $oct3, $oct4) = explode('.', $_SERVER['REMOTE_ADDR']);
// Send local clients to redirect address
if (in_array($oct1, $toRedirect)) {
header('HTTP/1.1 307 Temporary Redirect');
header('Location: '.$redirectAddress);
}
// Exit with the maintenance message.
// We can send this everyone in case the redirect fails
exit($maintenanceMessage);

Determine in php script if connected to internet?

How can I check if I'm connected to the internet from my PHP script which is running on my dev machine?
I run the script to download a set of files (which may or may not exist) using wget. If I try the download without being connected, wget proceeds to the next one thinking the file is not present.
<?php
function is_connected()
{
$connected = #fsockopen("www.example.com", 80);
//website, port (try 80 or 443)
if ($connected){
$is_conn = true; //action when connected
fclose($connected);
}else{
$is_conn = false; //action in connection failure
}
return $is_conn;
}
?>
You can always ping good 'ol trusty google:
$response = null;
system("ping -c 1 google.com", $response);
if($response == 0)
{
// this means you are connected
}
This code was failing in laravel 4.2 php framework with an internal server 500 error:
<?php
function is_connected()
{
$connected = #fsockopen("www.some_domain.com", 80);
//website, port (try 80 or 443)
if ($connected){
$is_conn = true; //action when connected
fclose($connected);
}else{
$is_conn = false; //action in connection failure
}
return $is_conn;
}
?>
Which I didn't want to stress myself to figure that out, hence I tried this code and it worked for me:
function is_connected()
{
$connected = fopen("http://www.google.com:80/","r");
if($connected)
{
return true;
} else {
return false;
}
}
Please note that: This is based upon the assumption that the connection to google.com is less prone to failure.
The accepted answer did not work for me. When the internet was disconnected it threw a php error. So I used it with a little modification which is below:
if(!$sock = #fsockopen('www.google.com', 80))
{
echo 'Not Connected';
}
else
{
echo 'Connected';
}
Why don't you fetch the return code from wget to determine whether or not the download was successful? The list of possible values can be found at wget exit status.
On the other hand, you could use php's curl functions as well, then you can do all error tracking from within PHP.
There are various factors that determine internet connection. The interface state, for example. But, regardles of those, due to the nature of the net, proper configuration does not meen you have a working connection.
So the best way is to try to download a file that you’re certain that exists. If you succeed, you may follow to next steps. If not, retry once and then fail.
Try to pick one at the destination host. If it’s not possible, choose some major website like google or yahoo.
Finally, just try checking the error code returned by wget. I bet those are different for 404-s and timeouts. You can use third parameter in exec call:
string exec ( string $command [, array &$output [, int &$return_var ]] )
/*
* Usage: is_connected('www.google.com')
*/
function is_connected($addr)
{
if (!$socket = #fsockopen($addr, 80, $num, $error, 5)) {
echo "OFF";
} else {
echo "ON";
}
}
Also note that fopen and fsockopen are different. fsockopen opens a socket depending on the protocol prefix. fopen opens a file or something else e.g file over HTTP, or a stream filter or something etc. Ultimately this affects the execution time.
You could ping to a popular site or to the site you're wgetting from (like www.google.nl) then parse the result to see if you can connect to it.
<?php
$ip = '127.0.0.1'; //some ip
exec("ping -n 4 $ip 2>&1", $output, $retval);
if ($retval != 0) {
echo "no!";
}
else
{
echo "yes!"; }
?>
Just check the result of wget. A status code of 4 indicates a network problem, a status code of 8 indicates a server error (such as a 404). This only works if you call wget for each file in sequence, rather than once for all the files.
You can also use libcurl with PHP, instead of calling wget. Something like:
foreach (...) {
$c = curl_init($url);
$f = fopen($filepath, "w")
curl_setopt($c, CURLOPT_FILE, $f);
curl_setopt($c, CURLOPT_HEADER, 0);
if (curl_exec($c)) {
if (curl_getinfo($c, CURLINFO_HTTP_CODE) == 200) {
// success
} else {
// 404 or something, delete file
unlink($filepath);
}
} else {
// network error or server down
break; // abort
}
curl_close($c);
}
This function handles what you need
function isConnected()
{
// use 80 for http or 443 for https protocol
$connected = #fsockopen("www.example.com", 80);
if ($connected){
fclose($connected);
return true;
}
return false;
}
You can use this by adding this inside a class:
private $api_domain = 'google.com';
private function serverAliveOrNot()
{
if($pf = #fsockopen($this->api_domain, 443)) {
fclose($pf);
$_SESSION['serverAliveOrNot'] = true;
return true;
} else {
$_SESSION['serverAliveOrNot'] = false;
return false;
}
}
+1 on Alfred's answer, but I think this is an improved version:
function hasInternet()
{
$hosts = ['1.1.1.1', '1.0.0.1', '8.8.8.8', '8.8.4.4'];
foreach ($hosts as $host) {
if ($connected = #fsockopen($host, 443)) {
fclose($connected);
return true;
}
}
return false;
}
My reasons:
This pings more than 1 server and will only fail if all 4 fails
If first host works, it will return true immediately
IP addresses are from CloudFlare and Google DNS which basically controls most of the internet and always online
1.1.1.1 is rated the fastest DNS resolver (Source)
Only doubt I have is to use port 443 or 80? Suggestions would be appreciated! :)
Very PHP way of doing it is
<?php
switch (connection_status())
{
case CONNECTION_NORMAL:
$txt = 'Connection is in a normal state';
break;
case CONNECTION_ABORTED:
$txt = 'Connection aborted';
break;
case CONNECTION_TIMEOUT:
$txt = 'Connection timed out';
break;
case (CONNECTION_ABORTED & CONNECTION_TIMEOUT):
$txt = 'Connection aborted and timed out';
break;
default:
$txt = 'Unknown';
break;
}
echo $txt;
?>
https://www.w3schools.com/php/func_misc_connection_status.asp

Verifying a Googlebot

I'm going to block all bots except the big search engines. One of my blocking methods will be to check for "language": Accept-Language: If it has no Accept-Language the bot's IP address will be blocked until 2037. Googlebot does not have Accept-Language, I want to verify it with DNS lookup
<?php
gethostbyaddr($_SERVER['REMOTE_ADDR']);
?>
Is it ok to use gethostbyaddr, can someone pass my "gethostbyaddr protection"?
function detectSearchBot($ip, $agent, &$hostname)
{
$hostname = $ip;
// check HTTP_USER_AGENT what not to touch gethostbyaddr in vain
if (preg_match('/(?:google|yandex)bot/iu', $agent)) {
// success - return host, fail - return ip or false
$hostname = gethostbyaddr($ip);
// https://support.google.com/webmasters/answer/80553
if ($hostname !== false && $hostname != $ip) {
// detect google and yandex search bots
if (preg_match('/\.((?:google(?:bot)?|yandex)\.(?:com|ru))$/iu', $hostname)) {
// success - return ip, fail - return hostname
$ip = gethostbyname($hostname);
if ($ip != $hostname) {
return true;
}
}
}
}
return false;
}
In my project, I use this function to identify Google and Yandex search bots.
The result of the detectSearchBot function is caching.
The algorithm is based on Google’s recommendation - https://support.google.com/webmasters/answer/80553
In addition to Cristian's answer:
function is_valid_google_ip($ip) {
$hostname = gethostbyaddr($ip); //"crawl-66-249-66-1.googlebot.com"
return preg_match('/\.googlebot|google\.com$/i', $hostname);
}
function is_valid_google_request($ip=null,$agent=null){
if(is_null($ip)){
$ip=$_SERVER['REMOTE_ADDR'];
}
if(is_null($agent)){
$agent=$_SERVER['HTTP_USER_AGENT'];
}
$is_valid_request=false;
if (strpos($agent, 'Google')!==false && is_valid_google_ip($ip)){
$is_valid_request=true;
}
return $is_valid_request;
}
Note
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];
}
https://www.php.net/manual/en/function.gethostbyaddr.php
The recommended way by Google is to do a reverse dns lookup (gethostbyaddr) in order to get the associated host name AND then resolve that name to an IP (gethostbyname) and compare it to the remote_addr (because reverse lookups can be faked, too).
But beware, end lokups take time and can severely slow down your webpage (maybe check for user agent first).
Google also publishes a machine readable file containing the IP addresses of their crawlers, see the link below.
See:
https://developers.google.com/search/docs/advanced/crawling/verifying-googlebot
https://webmasters.googleblog.com/2006/09/how-to-verify-googlebot.html
//The function
function is_google() {
return strpos($_SERVER['HTTP_USER_AGENT'],"Googlebot");
}
How to verify Googlebot.
If you have a site that has thousands of pages then going for reverse DNS will be costly, So I think the best method is to hard code ips list. (Php code example)
function googleBotIPsList(){
return "ips"; //hard coded IPs here.
}
Also you can make another function which gets the latest ips. Now upto you how frequently you call this function.
function getLatestGoogleBotIPsList(){
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL,"https://developers.google.com/static/search/apis/ipranges/googlebot.json");
$result=curl_exec($ch);
curl_close($ch);
$result = (json_decode($result, true));
$ips='';
for($i=0;$i<count($result['prefixes']);$i++) {
$ips .= ($result['prefixes'][$i]['ipv6Prefix'] ? $result['prefixes'][$i]['ipv6Prefix'] : $result['prefixes'][$i]['ipv4Prefix']).',';
}
return rtrim($ips,',');
}
Then use strpos to check from the hardcoded list
if(strpos(googleBotIPsList(),zen_get_ip_address()) !==false){
// Insert into your table etc.
}

Categories