PHP: Get Domain (without subdomain) of any Address available as String - php

Recently a question has been asked, how to get the Domain of any URL available as a String.
Unfortunately the question has been closed, and the so far linked answers only pointed to solutions using Regex (which fails for special cases like .co.uk) and static solutions, considering those exceptions (which ofc. might change over time).
So, I was searching for a generic solution for this question, that will work at any time and found one. (At least a couple of tests are positive)
If you find a domain for which the attempted solution does not work, feel free to mention it, and I'll try to imrpove the snipped to cover that case as well.

To find the domain of any string given, a three-step solution seems to work best:
First, get the actual Hostname, using parse_url (http://php.net/manual/en/function.parse-url.php)
Second, query any DNS-Server for the "Top-Most" A-Record available. (I used checkdnsrr for this purpose: http://php.net/manual/en/function.checkdnsrr.php)
Last but not least: Perform some validations to make sure you are not running into some "default response".
I performed only some tests and it seems like the result is as expected. The method directly generates the output, but can be modified to return the domain name instead of generating output:
<?php
getDomain("http://www.stackoverflow.com");
getDomain("http://www.google.co.uk");
getDomain("http://books.google.co.uk");
getDomain("http://a.b.c.google.co.uk");
getDomain("http://www.nominet.org.uk/intelligence/statistics/registration/");
getDomain("http://invalid.fail.pooo");
getDomain("http://AnotherOneThatShouldFail.com");
function getDomain($url){
echo "Searching Domain for '".$url."': ";
//Step 1: Get the actual hostname
$url = parse_url($url);
$actualHostname = $url["host"];
//step 2: Top-Down approach: check DNS Records for the first valid A-record.
//Re-Assemble url step-by-step, i.e. for www.google.co.uk, check:
// - uk
// - co.uk
// - google.co.uk (will match here)
// - www.google.co.uk (will be skipped)
$domainParts = explode(".", $actualHostname);
for ($i= count($domainParts)-1; $i>=0; $i--){
$domain = "";
$currentCountry = null;
for ($j = count($domainParts)-1; $j>=$i; $j--){
$domain = $domainParts[$j] . "." . $domain;
if ($currentCountry == null){
$currentCountry = $domainParts[$j];
}
}
$domain = trim($domain, ".");
$validRecord = checkdnsrr($domain, "A"); //looking for Class A records
if ($validRecord){
//If the host can be resolved to an ip, it seems valid.
//if hostname is returned, its invalid.
$hostIp = gethostbyname($domain);
$validRecord &= ($hostIp != $domain);
if ($validRecord){
//last check: DNS server might answer with one of ISPs default server ips for invalid domains.
//perform a test on this by querying a domain of the same "country" that is invalid for sure to obtain an
//ip list of ISPs default servers. Then compare with the response of current $domain.
$validRecord &= !(in_array($hostIp, gethostbynamel("iiiiiiiiiiiiiiiiiinvaliddomain." . $currentCountry)));
}
}
//valid record?
if ($validRecord){
//return $domain;
echo $domain."<br />";
return;
}
}
//return null;
echo " not resolved.<br />";
}
?>
Output of the example above:
Searching Domain for 'http://www.stackoverflow.com': stackoverflow.com
Searching Domain for 'http://www.google.co.uk': google.co.uk
Searching Domain for 'http://books.google.co.uk': google.co.uk
Searching Domain for 'http://a.b.c.google.co.uk': google.co.uk
Searching Domain for 'http://www.nominet.org.uk/intelligence/statistics/registration/': nominet.org.uk
Searching Domain for 'http://invalid.fail.pooo': not resolved.
Searching Domain for 'http://AnotherOneThatShouldFail.com': not resolved.
This is only a very limited set of test-cases but I cannot imagine a case, where a domain has no A-record.
As a nice side-effect, this also validates urls and does not just rely on theoretically valid formats like the last examples are showing.
best,
dognose

Related

Invalid IP Address with multi-line IP Address

I am stuck with this error while trying to read a file which holds IP addresses separated by a new line. What I want to do, is to read my file that holds bunch of IP's and check does they have proper record. This is my code:
$file = "test.sh";
if (is_file($file) && is_readable($file)) {
$get_file_data = file_get_contents($file);
$turn_to_array = explode("\n", $get_file_data);
foreach ($turn_to_array as $key => $value) {
if (filter_var($value, FILTER_VALIDATE_IP)) {
echo gethostbyaddr(trim($value));
} else {
echo "IP invalid";
}
}
}
My test.sh file looks like following:
EDIT :
example IP's
180.1.1.1
181.1.1.2
Do I need to add some specific tests to parse a file or there is some other issue?
The error caused by the very unique method used:
Warning: gethostbyaddr(): Address is not a valid IPv4 or IPv6 PHP
Solved.
My code was working, I wasn't getting rdns record properly, since it didn't exist for those IP's. I've checked it with host 185.1.1.1 and it returned actual IP not domain. Then checked IP's for whom I was sure that they have PTR records, and it woked. But I am not sure exactly how error was fixed actually.
I'm going to be honest, I think exploding the raw file data and hoping for the best may not be the best way to accomplish this. Instead, you should try to extract the exact data you need using regular expressions so you are certain you are extracting the correct values.
Try something like this:
$data = file_get_contents($file);
// This regex matches things that resemble an IP4 address
if (preg_match_all("/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/", $data, $addresses)) {
foreach ($addresses as $address) {
// Need to verify it ACTUALLY is a valid IP address
if (filter_var($address, FILTER_VALIDATE_IP)) {
echo gethostbyaddr($address);
}
}
}
NOTE: The regex used here was written by #alex here: Regex to match an IP address
If this doesn't help, then it may be a configuration issue. A few other things to check that might help with troubleshooting are:
Does gethostbyaddr('127.0.0.1'); throw the same error?
Do you have a firewall that prevents the DNS request from going through?
Can you use the dig command from the server to issue a lookup on the IP?

Tool Suggest Top-Level Domains

I am creating a tool for our customers to check if their domain name exists. I plan on using the gethostbyname($domain) function. But, I want to pre-program other TLD's that would be checked along with their preferred TLD (sent through a form). I don't really know where to start here so I included my example below for user suggestions.
// SUPPORTED TOP-LEVEL DOMAIN NAMES (TLDS)
$TLD['COMING_SOON'] = ".REALTY, .CONSTRUCTION";
$TLD['CURRENT'] = ".COM, .ORG, .US";
// RECEIVE FORM DATA AND STRIP TAGS
$SOURCE = $_SERVER['HTTP_REFERER'];
$DOMAIN = strip_tags($_POST['domain_name']);
$TLD = strip_tags($_POST['tld']);
$REQ = $DOMAIN.$TLD;
// CHECK CLIENT'S PREFERRED DOMAIN
if ( gethostbyname($REQ) != $REQ ) {
echo "DNS Record found";
} else {
echo "NO DNS Record found";
}
// TO DO: CHECK ALTERNATIVE TOP-LEVEL DOMAINS
// TO DO: SOMEHOW SEARCH THROUGH THE $TLD ARRAY, COMPARE, AND GIVE RESULT
gethostbyname($domain) will not be sufficient; it will only tell you if the domain is active, not whether it is available for purchase or not.
To find out whether it's actually available to buy, you will need to query the relevant WHOIS service for each domain type. But this will differ for each domain type, and parsing the resulting data can be a pain, so you would be better off using an API
The registrars that you're using to register the domains should also be able to provide you with an API that you can call. Contact them and find out what they have and how to use it. That's the best option.

How do I get the external IP of my server using PHP?

I often hear people say to use "$_SERVER['SERVER_ADDR']", but that returns the LAN IP of my server (e.g. 192.168.1.100). I want the external IP.
There is NO way to get your underlying IP Address that has been designated by your ISP via conventional PHP if you are using a router. A way to get the external IP is to find a service that will obtain it for you and echo the address back to you. I found a handy service which does just that. http://ipecho.net/
You can use:
$realIP = file_get_contents("http://ipecho.net/plain");
Just query a host that returns your IP address:
$externalContent = file_get_contents('http://checkip.dyndns.com/');
preg_match('/Current IP Address: \[?([:.0-9a-fA-F]+)\]?/', $externalContent, $m);
$externalIp = $m[1];
or, set up a service that simply echoes just the IP, and use it like this:
$externalIp = file_get_contents('http://yourdomain.example/ip/');
Set up the service yourself by simply echoing the remote IP address, or pay someone to host it. Do not use somebody else's server without permission. Previously, this answer linked to a service of mine that's now being hit multiple times a second.
Note that in an IP network with one or more NATs, you may have multiple external IP addresses. This will give you just one of them.
Also, this solution of course depends on the remote host being available. However, since there is no widely implemented standard (no ISP and only some home routers implement UPnP), there is no other way to get your external IP address. Even if you could talk to your local NAT, you couldn't be sure that there isn't another NAT behind it.
I'm going to add a solution because the others weren't quite right for me because they:
need to be run on a server with a domain name e.g. gethostbyname() (if you think about it, you don't actually have the problem if you know this a priori)
can't be used on the CLI (rely on something in $_SERVER to be set by a web server)
assume a specific format of ifconfig output, which can include multiple non-public interfaces
depend on parsing someone's website (who may disappear, change the URL, change the format, start lying to you, etc, all without notice)
This is what I would suggest:
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$res = socket_connect($sock, '8.8.8.8', 53);
// You might want error checking code here based on the value of $res
socket_getsockname($sock, $addr);
socket_shutdown($sock);
socket_close($sock);
echo $addr; // Ta-da! The IP address you're connecting from
The IP address there is a Google public DNS server. I trust they'll be around and running it for a while. It shouldn't really matter what address you use there as long as its a public IP address that belongs to someone who doesn't mind random connection attempts too much (yourself maybe?).
This is based on an answer I came across when I had a similar problem in Python.
P.S.: I'm not sure how well the above would work if there is sorcery going on between your machine and the internet.
You could parse it from a service like ip6.me:
<?php
// Pull contents from ip6.me
$file = file_get_contents('http://ip6.me/');
// Trim IP based on HTML formatting
$pos = strpos( $file, '+3' ) + 3;
$ip = substr( $file, $pos, strlen( $file ) );
// Trim IP based on HTML formatting
$pos = strpos( $ip, '</' );
$ip = substr( $ip, 0, $pos );
// Output the IP address of your box
echo "My IP address is $ip";
// Debug only -- all lines following can be removed
echo "\r\n<br/>\r\n<br/>Full results from ip6.me:\r\n<br/>";
echo $file;
You could try this:
$ip = gethostbyname('www.example.com');
echo $ip;
to get the IP address associated with your domain name.
I know this question is old and long answered, but I was googling for the same thing and want to add my own "hack". For this to work your webrequest has to come from an external IP address or you have to alter $own_url to a url that does an external request to itself.
The point is, if you let the script do a request to itself than you get it's external IP address.
<?php
if (isset($_GET['ip'])) {
die($_SERVER['REMOTE_ADDR']);
}
$own_url = (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'];
$ExternalIP = file_get_contents($own_url.'?ip=1');
echo $ExternalIP;
?>
I think there is much code for this things in others answers, but my answer is short, but you need to execute a command in shell to get the ip...
but it is short and fast, I think that...
php execute bash > bash run > bash get ip > php get ip
echo shell_exec( "dig +short myip.opendns.com #resolver1.opendns.com");
Sorry my for my english, I hope it help all you...
Reference: How can I get my external IP address in a shell script?
This is an old thread, but for what it's worth, I'm adding a simple function that I use. It uses outside services (which is inevitable it seems), but it provides a backup service as well, and local caching to avoid repetitive HTTP calls.
/*
USAGE
$ip = this_servers_public_ip(); // Get public IP, and store it locally for subsequent calls.
$ip = this_servers_public_ip(true); // Force remote query and refresh local cache if exists.
*/
function this_servers_public_ip($purge=false) {
$local = sys_get_temp_dir().'/this.servers.public.ip';
if ( $purge===true && realpath($local) ) {
unlink($local);
}
if ( realpath($local) ) {
return file_get_contents($local);
}
// Primary IP checker query.
$ip = trim( file_get_contents('https://checkip.amazonaws.com') );
if ( (filter_var($ip, FILTER_VALIDATE_IP) !== false) ) {
file_put_contents($local,$ip);
return $ip;
}
// Secondary IP checker query.
$ip_json = trim( file_get_contents('https://ipinfo.io/json') );
$ip_arr = json_decode($ip_json,true);
$ip=$ip_arr['ip'];
if ( (filter_var($ip, FILTER_VALIDATE_IP) !== false) ) {
file_put_contents($local,$ip);
return $ip;
}
return false; // Something went terribly wrong.
}
If your server have a domain name you can ping it or you can use:
$sMyServerIP = gethostbyname('yourdomain.com');
echo $sMyServerIP;
It will return your outer IP address.
Assuming your PHP is running on a Linux server you can call ifconfig using PHP's exec function. This will give public IP without need to contact some external website/service. E.g.:
$command = "ifconfig"; /// see notes below
$interface = "eth0"; //
exec($command, $output);
$output = implode("\n",$output);
if ( preg_match('/'.preg_quote($interface).'(.+?)[\r\n]{2,}/s', $output, $ifaddrsMatch)
&& preg_match_all('/inet(6)? addr\s*\:\s*([a-z0-9\.\:\/]+)/is', $ifaddrsMatch[1], $ipMatches, PREG_SET_ORDER) )
{
foreach ( $ipMatches as $ipMatch )
echo 'public IPv'.($ipMatch[1]==6?'6':'4').': '.$ipMatch[2].'<br>';
}
Note that sometimes as $command you have to specify full path of ifconfig. You can find this by executing
whereis ifconfig
from shell prompt. Furthermore $interface should be set to the name of your server's main network interface that has the link to the WAN.
On Windows you can do something similar of course, using ipconfig instead of ifconfig (and corresponding adjusted regex).
Also you could get IP via DNS A record based on hostname or server name:
$dnsARecord = dns_get_record($_SERVER['HTTP_HOST'],DNS_A);
if ( $dnsARecord ) echo 'IPv4: '.$dnsARecord[0]['ip'];
$dnsARecord = dns_get_record($_SERVER['HTTP_HOST'],DNS_AAAA);
if ( $dnsARecord ) echo 'IPv6: '.$dnsARecord[0]['ip'];
You could also use SERVER_NAME instead of HTTP_HOST if one of the two does not give what you want.
However, this is assuming that your server is configured correctly in correspondence with DNS. This may not always be the case. See my other answer using ifconfig that is perhaps better.
Have you tried:
gethostbyname(php_uname('n'));
?

PHP - allow domains, not subdomains

I would appreciate any help that can be provided with this matter.
I am creating a registration form, one field is for the users domain which I will verify is valid with FILTER_VALIDATE_URL and that it exists with dns_check_record.
However a problem I'm having is that using these two methods will also allow subdomains to be submitted to the form which I don't want.
Does anyone know a way to allow domains but not subdomains?
I've tested the following function, from http://syntax.cwarn23.net/PHP/Strip_URL_to_Domain:
function domain($domainb)
{
$bits = explode('/', $domainb);
if ($bits[0]=='http:' || $bits[0]=='https:')
{
$domainb= $bits[2];
} else {
$domainb= $bits[0];
}
unset($bits);
$bits = explode('.', $domainb);
$idz=count($bits);
$idz-=3;
if (strlen($bits[($idz+2)])==2) {
$url=$bits[$idz].'.'.$bits[($idz+1)].'.'.$bits[($idz+2)];
} else if (strlen($bits[($idz+2)])==0) {
$url=$bits[($idz)].'.'.$bits[($idz+1)];
} else {
$url=$bits[($idz+1)].'.'.$bits[($idz+2)];
}
return $url;
However this isn't perfect as any domains such as www.domain.uk.com will appear as uk.com (I know not a common domain extension).
Does anyone know a method better than the above function?
As pointed by Micheal Mior, you have to check for .co.uk, .com.br and many others.
Some browser vendors are maintaining a list of such non-TLD that are effectively TLD: http://publicsuffix.org/. The list is quite huge.
There is a library here that uses this effective TLD list to implement the function you are looking for (download are here). (Found via https://wiki.mozilla.org/Gecko:Effective_TLD_Service.)
Combine them.
dns_check_record will fail on '.co.uk', so you can split your string on the dots, check the domain you get when you combine the last two parts, and if that fails, use a third part too, if any.
You will do a double check for invalid domains, but I assume that won't be an issue.
first you could use parse_url() to get only the host name: http://www.stackoverflow.com -> $url['host'] = 'www.stackoverflow.com'
Second you could count the amount of points in the hostname: explode() --> count() or substr_count()
Has the host more than 1 point a subdomain could be exist.
Now you could use the solution mentioned by GolezTrol or arnaud576875.

How do I detect subdomain and filter it?

I do have a domain search function. In search box you have the option to enter any kind of domain names. what I am looking into is how do I filter sub domain from search or else trim sub domain and keep only main.
for example if a user entered mail.yahoo.com then that to be convert to yahoo.com or it can be omitted from search.
Here's a more concise way to grab the domain and a likely subdomain from a URL.
function find_subdomain($url) {
$parts = parse_url($url);
$domain_parts = explode('.', $parts['host']);
while(count($domain_parts) > 4)
array_shift($domain_parts);
return join('.', $domain_parts);
}
Keep in mind that not everything that looks like a subdomain is really a subdomain. Some countries have their own country-specific domains that everyone uses, like .co.uk and .com.au. You can not rely on the number of dots in the URL to tell you what is and is not a subdomain. In fact, you might need the opposite approach - first remove the top-level domain, then see what's left. Unfortunately then you're left with the second-level domain problem.
Can you tell us more about what exactly you are trying to accomplish? Why are you trying to detect subdomains? You mentioned a search box. What is being searched?
Edit: I have updated the function to up to four of the right-most parts of the domain. Given "http://one.two.three.four.five.six.com" it will return 'four.five.six.com'
I customized an utility function that i'm using, it's close to perfection (but that's what you could get without hard-coding all the possible list of domain extensions).
Here's the catch: the assumes that the main domain contains at least 4 characters. i.e for: sub.mail.com, it returns mail.com But for sub.aol.com it returns sub.aol.com
function get_main_domain($host='') {
if(empty($host))$host=$_SERVER['HTTP_HOST'];
$domain_parts = explode('.',$host);
$count=count($domain_parts);
if($count<=2)return $host;
$permit=0;
for($i=$count-1;$i>=0;$i--){
$permit++;
if(strlen($domain_parts[$i])>3)break;
}
while(count($domain_parts) >$permit)array_shift($domain_parts);
return join('.', $domain_parts);
}
Well that doesnt work for all domain if you forgot to mention it in array...
Here is my solution...but I need to compress it to few lines...is it possible??
function subdomain($domainb){$bits = explode('/', $domainb);
if ($bits[0]=='http:' || $bits[0]=='https:'){
$domainb= $bits[2];
} else {$domainb= $bits[0];}
unset($bits);
$bits = explode('.', $domainb); $idz=0;
while (isset($bits[$idz])){$idz+=1;}
$idz-=4; $idy=0;
while ($idy<$idz){ unset($bits[$idy]);
$idy+=1;} $part=array();
foreach ($bits AS $bit){$part[]=$bit;}
unset($bit); unset($bits); unset($domainb);
if (strlen($part[1])>4){ unset($part[0]);}
foreach($part AS $bit){$domainb.=$bit.'.';}
unset($bit);
return preg_replace('/(.*)\./','$1',$domainb);}

Categories