Locked. Comments on this question have been disabled, but it is still accepting new answers and other interactions. Learn more.
I'm trying to connect to a Tor hidden service using the following PHP code:
$url = 'http://jhiwjjlqpyawmpjx.onion/'
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PROXY, "http://127.0.0.1:9050/");
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
$output = curl_exec($ch);
$curl_error = curl_error($ch);
curl_close($ch);
print_r($output);
print_r($curl_error);
When I run it, I get the following error:
Couldn't resolve host name
However, when I run the following command from my command line in Ubuntu:
curl -v --socks5-hostname localhost:9050 http://jhiwjjlqpyawmpjx.onion
I get a response as expected.
The PHP cURL documentation says this:
--socks5-hostname
Use the specified SOCKS5 proxy (and let the proxy resolve the host name).
I believe the reason it works from the command line is because Tor (the proxy) is resolving the .onion hostname, which it recognizes. When running the PHP code above, my guess is that cURL or PHP is trying to resolve the .onion hostname and doesn't recognize it. I've searched for a way to tell cURL/PHP to let the proxy resolve the hostname, but I can't find a way.
There is a very similar Stack Overflow question, cURL request using socks5 proxy fails when using PHP, but it works through the command line.
You need to set option CURLOPT_PROXYTYPE to CURLPROXY_SOCKS5_HOSTNAME, which sadly wasn't defined in old PHP versions, circa pre-5.6; if you have earlier in but you can explicitly use its value, which is equal to 7:
curl_setopt($ch, CURLOPT_PROXYTYPE, 7);
I use Privoxy and cURL to scrape Tor pages:
<?php
$ch = curl_init('http://jhiwjjlqpyawmpjx.onion'); // Tormail URL
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
curl_setopt($ch, CURLOPT_PROXY, "localhost:8118"); // Default privoxy port
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
curl_exec($ch);
curl_close($ch);
?>
After installing Privoxy you need to add this line to the configuration file (/etc/privoxy/config). Note the space and '.' a the end of line.
forward-socks4a / localhost:9050 .
Then restart Privoxy.
/etc/init.d/privoxy restart
Try to add this:
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
TL;DR: Set CURLOPT_PROXYTYPE to use CURLPROXY_SOCKS5_HOSTNAME if you have a modern PHP, the value 7 otherwise, and/or correct the CURLOPT_PROXY value.
As you correctly deduced, you cannot resolve .onion domains via the normal DNS system, because this is a reserved top-level domain specifically for use by Tor and such domains by design have no IP addresses to map to.
Using CURLPROXY_SOCKS5 will direct the cURL command to send its traffic to the proxy, but will not do the same for domain name resolution. The DNS requests, which are emitted before cURL attempts to establish the actual connection with the Onion site, will still be sent to the system's normal DNS resolver. These DNS requests will surely fail, because the system's normal DNS resolver will not know what to do with a .onion address unless it, too, is specifically forwarding such queries to Tor.
Instead of CURLPROXY_SOCKS5, you must use CURLPROXY_SOCKS5_HOSTNAME. Alternatively, you can also use CURLPROXY_SOCKS4A, but SOCKS5 is much preferred. Either of these proxy types informs cURL to perform both its DNS lookups and its actual data transfer via the proxy. This is required to successfully resolve any .onion domain.
There are also two additional errors in the code in the original question that have yet to be corrected by previous commenters. These are:
Missing semicolon at end of line 1.
The proxy address value is set to an HTTP URL, but its type is SOCKS; these are incompatible. For SOCKS proxies, the value must be an IP or domain name and port number combination without a scheme/protocol/prefix.
Here is the correct code in full, with comments to indicate the changes.
<?php
$url = 'http://jhiwjjlqpyawmpjx.onion/'; // Note the addition of a semicolon.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PROXY, "127.0.0.1:9050"); // Note the address here is just `IP:port`, not an HTTP URL.
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME); // Note use of `CURLPROXY_SOCKS5_HOSTNAME`.
$output = curl_exec($ch);
$curl_error = curl_error($ch);
curl_close($ch);
print_r($output);
print_r($curl_error);
You can also omit setting CURLOPT_PROXYTYPE entirely by changing the CURLOPT_PROXY value to include the socks5h:// prefix:
// Note no trailing slash, as this is a SOCKS address, not an HTTP URL.
curl_setopt(CURLOPT_PROXY, 'socks5h://127.0.0.1:9050');
Here is a simple function to help you.
Tho to save time first of all you need to make sure that you check if the proxy is valid to not with simple check with fsocketopen()
try {
$fp = fsockopen($ip, $port, $errno, $errstr, 10);
fclose($fp);
return true;
} catch (\Throwable $th) {
return false;
}
If socket returns true then go for requestUrl function
private function requestUrl($url, $proxy)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_PROXY, $proxy);
curl_setopt($curl, CURLOPT_HEADER, 1);
curl_setopt($curl, CURLOPT_HTTPPROXYTUNNEL, 1);
curl_setopt($curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
$contents = curl_exec($curl);
//Check for errors.
// if (curl_errno($curl)) {
// return new \Exception(curl_error($curl));
// }
curl_close($curl);
return $contents;
}
Related
I'm trying to use PHP cURL to send a GET request to Apache Solr to receive search results, and I'm running into some trouble. This is more to do with PHP cURL I think than Solr...but i digress...here's what I have so far.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://example.com/solr/example/select?".$this->query);
curl_setopt($ch, CURLOPT_PORT, 8983);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_TIMEOUT, '4');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$data = json_decode(curl_exec($ch), TRUE);
curl_close($ch);
Currently, the request times out at 4 seconds...so I'm wondering if maybe the port# isn't being set properly...I also tried to include it in the URL itself with no luck. The weird part is that I can echo the constructed URL, add the port# manually, copy/paste into the browser and it works! But, for some reason it doesn't with the code above.
Any help would be greatly appreciated! Thanks in advance!
The above code actually works as intended...my issue was local firewall rules blocking remote connection to certain port #'s (as opposed to remote server firewall rules as skrilled suspected)
Once again, another question concerning cURL and SSL, as I cannot find matching answers to my problem.
I have working SSL on my webserver, with trusted cert and green signs on browsers address bar a.s.o., NOT self signed. So good, so far.
Now I want communicate with cURL and use the following function (POST data not added yet):
function ssltest(){
$post_data = '';
$url = 'https://myserver/test.php';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_MUTE, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
//curl_setopt($ch, CURLOPT_CAINFO, 'sslstuff/cacert.pem');
curl_setopt($ch, CURLOPT_CAINFO, 'sslstuff/false.pem');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
echo ssltest();
As the cacert.pem I use this one, which I found in my browser, which is obviously identical to what I found here http://curl.haxx.se/ca/cacert.pem
In the code shown above there is a false.pem to be seen. Now what ? If this file is empty, there's no response from Server, but I tested to paste the cert from another enterprise from the list on curl.haxx.se I get the same correct answer from the server as result, as when I use my correct .pem
What's the issue ? What I am missing ?
"there's no response from Server"
I think that's very unlikely. I suspect there is no HTTP response from the server, but that the SSL negotiation is failing - but you've got no error checking in your code. If $output===false, have a look at curl_error().
You might want to play around with VERIFYHOST and VERIFYPEER to pin down the exact cause of the problem.
I am trying to fetch a data from a site using curl method. Before i can able to get data by using curl.Recently client taken ssl certificate for that site from then onwards i can't able to get the data using curl. But when i try the URL in browser i can able to see data. Can any one tell me why it is not working.did i need to try in some another way.
Below is my code
// create curl resource
$ch = curl_init();
// set url
curl_setopt($ch, CURLOPT_URL, "https://qa.myhealth.today/myhealth-portal/nirvahak/public/validateSessionttt?username=pavithra#gmail.com");
//return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// $output contains the output string
$output = curl_exec($ch);
// close curl resource to free up system resources
curl_close($ch);
$output=json_decode($output);
print_r($output);
There are 2 fixes:
1) SSL certified websites do not trust any non-SSL requests. So, we need to win trust of SSL enabled client.
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
2) Provide him the security certificate.
Go the the respective site. Save the SSL certificate and pass the certificate path to the CURL request.
So, the final code should be:
// create curl resource
$ch = curl_init();
$url = "https://qa.myhealth.today/myhealth-portal/nirvahak/public/validateSessionttt?username=pavithra#gmail.com";
// set url
curl_setopt($ch, CURLOPT_URL, $url);
//return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_CAINFO, getcwd() . "/CAcerts/BuiltinObjectToken-EquifaxSecureCA.crt");
// $output contains the output string
$output = curl_exec($ch);
// close curl resource to free up system resources
curl_close($ch);
$output=json_decode($output);
print_r($output);
Detailed instructions on how to do it are place here:
Set CURLOPT_SSL_VERIFYHOST and CURLOPT_SSL_VERIFYPEER to false.
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
CURLOPT_SSL_VERIFYHOST
1 to check the existence of a common name in the SSL peer certificate. 2 to check the existence of a common name and also verify that it matches the hostname provided. In production environments the value of this option should be kept at 2 (default value).
CURLOPT_SSL_VERIFYPEER
FALSE to stop cURL from verifying the peer's certificate. Alternate certificates to verify against can be specified with the CURLOPT_CAINFO option or a certificate directory can be specified with the CURLOPT_CAPATH option.
Source: http://php.net/manual/en/function.curl-setopt.php
Curl has a feature for manually specifying which IP to resolve a host to. For example:
curl https://www.google.com --resolve "www.google.com:443:173.194.72.112"
This is especially useful when using HTTPS. If it was just a HTTP request, I could have achieved the same by specifying the IP address directly, and adding a host header. But in HTTPS that would break the connection since the SSL certificate host would be compared to the IP address and not the host header.
My question is, how can I do the same thing in PHP?
Although #deceze's answer is correct, a live example might be useful. I needed CURLOPT_RESOLVE because I was trying to connect directly to the IP address with an additional Host: www.example.com header, but since the server was using SNI, this didn't work.
I used CURLOPT_RESOLVE to solve my problem. This code allows me to connect to the SNI server on a IP address of my choosing:
$resolve = array(sprintf(
"%s:%d:%s",
$hostname,
$port,
$host_ip
));
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RESOLVE, $resolve);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
According to the changelog, support for CURLOPT_RESOLVE was added in 5.5.0.
Note that at the time of writing it's not even documented yet, but according to this bug report it takes an array as argument.
Even though it's an old question is worth noticing that when using CURL to multiple servers using the CURLOPT_RESOLVE option, there is a DNS cache which needs to be cleared before using CURL again, otherwise the CURL will point to the first server regardless of the curl_setopt($ch, CURLOPT_RESOLVE, $resolve); setting.
The only way to make this work is to add to the $resolve array a resolve string with the last sever used prefixed with a '-':
$servers = [ '192.0.2.1', '192.0.2.2', '192.0.2.3' ];
foreach ($servers as $idx => $server) {
$resolve = [];
// Remove the last server used from the DNS cache
if($idx){
$last_server = $server[$idx-1];
$resolve[] = "-example.com:443:{$last_server}";
}
// resolve the new server
$resolve[] = "example.com:443:{$server}";
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, 0);
curl_setopt($ch, CURLOPT_RESOLVE, $resolve);
curl_setopt($ch, CURLOPT_URL, "https://example.com/some/path");
curl_setopt($ch, CURLOPT_VERBOSE, 1);
$result = curl_exec($ch);
$info = curl_getinfo($ch);
echo $info['primary_ip']."\n";
curl_close($ch);
}
As pointed here: https://bugs.php.net/bug.php?id=74135
I have a strange problem which may or may not be tied to Plesk. This PHP script intends to fetch a page on the same server when executed on the same domain e.g. http://quotationsbook.com/sometestpage.php
<?php
error_reporting('E_ALL');
ini_set('display_errors', 1);
function curlFileGetContents($urlreq) {
$ch = curl_init();
$timeout = 5;
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 50);
curl_setopt($ch, CURLOPT_URL, $urlreq);
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$request_result = curl_exec($ch);
if (curl_errno($ch))
$response = 0;
else
$response = $request_result;
curl_close($ch);
return $response;
}
$url = 'http://quotationsbook.com';
$data = curlFileGetContents($url);
echo '<pre>';
print_r($data);
echo '</pre>';
?>
However, it does not fetch the page requested, it always fetches a Plesk error page.
In terms of the PHP var allow_url_fopen it is set to On.
The problem only occurs for the same domain where the code is hosted, not for other domains. i.e. it only occurs on http://quotationsbook.com where what I'm trying to fetch is under http://quotationsbook.com/*, it does not occur when I try to fetch say, http://google.com
The answer to your question is actually in your last paragraph.
The problem only occurs for the same domain where the code is hosted,
not for other domains. i.e. it only occurs on
http://quotationsbook.com where what I'm trying to fetch is under
http://quotationsbook.com/*, it does not occur when I try to fetch
say, http://google.com
That is your clue.
I must pose a question and that is why use CURL if you are on the same server? What are you trying to accomplish? There are probably better solutions out there than using CURL.
Assuming you need to use CURL, it's likely a firewall or other security issue. Make sure the port being accessed is open and not blocked.
See PHP Curl does not work on localhost?