LinuxQuestions.org
Support LQ: Use code LQ3 and save $3 on Domain Registration
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices



Reply
 
Search this Thread
Old 09-12-2008, 03:48 PM   #1
win32sux
Guru
 
Registered: Jul 2003
Location: Los Angeles
Distribution: Ubuntu
Posts: 9,870

Rep: Reputation: 371Reputation: 371Reputation: 371Reputation: 371
Question How to check client IP against list of IP ranges in PHP?


I'm using $_SERVER['REMOTE_ADDR'] to get the client's IP. I would like to set a certain variable to TRUE if the IP is within one of the ranges I've got in a text file (I guess I could put the ranges into the database if needed). My list is currently formatted like this:
Code:
123.123.123.123-123.123.200.200
124.129.0.123-124.129.200.200
231.160.124.1-231.255.255.255
The list consists of a few hundred IP ranges (one range per line, as shown).

How would you recommend I tackle this? Your help is greatly appreciated.

Last edited by win32sux; 09-12-2008 at 06:06 PM.
 
Old 09-12-2008, 05:51 PM   #2
keefaz
Senior Member
 
Registered: Mar 2004
Distribution: Slackware
Posts: 4,617

Rep: Reputation: 136Reputation: 136
http://www.php.net/manual/en/function.ip2long.php
Have a look at the in_ip_range() function posted by mhakopian in user comments
 
Old 09-12-2008, 06:05 PM   #3
win32sux
Guru
 
Registered: Jul 2003
Location: Los Angeles
Distribution: Ubuntu
Posts: 9,870

Original Poster
Rep: Reputation: 371Reputation: 371Reputation: 371Reputation: 371
Hey man that's awesome - thanks! I had looked at the ip2long() function before posting, but I didn't realize what it did. I thought it was for doing reverse DNS or something like that. I think what I'm gonna do is put my ranges in the database and then write a function which uses ip2long() to check whether $_SERVER['REMOTE_ADDR'] is in the range. I'll post whatever I come-up with tonight. Once again, thank you very much!
 
Old 09-12-2008, 06:07 PM   #4
keefaz
Senior Member
 
Registered: Mar 2004
Distribution: Slackware
Posts: 4,617

Rep: Reputation: 136Reputation: 136
Thank mhakopian, not me
 
Old 09-12-2008, 08:08 PM   #5
win32sux
Guru
 
Registered: Jul 2003
Location: Los Angeles
Distribution: Ubuntu
Posts: 9,870

Original Poster
Rep: Reputation: 371Reputation: 371Reputation: 371Reputation: 371
Quote:
Originally Posted by keefaz View Post
Thank mhakopian, not me
Haha well I'll give him some credit because his example made it easy for me to learn to use ip2long(), but you're the one that pointed me toward the function so you were more vital. In any case, thanks to both of you!

Okay, so I put all my IP ranges in the database and wrote a function which takes an IP as input and checks the database to see if the IP is in the listed ranges. It returns TRUE if the IP is not in any of the ranges. The table where I put the ranges looks like:
Code:
+-----+-----------------+-----------------+
| ID  | StartIP         | EndIP           |
+-----+-----------------+-----------------+
|   1 | 123.123.123.123 | 123.123.200.200 |
|   2 | 124.129.0.123   | 124.129.200.200 |
|   3 | 231.160.124.1   | 231.255.255.255 |
+-----+-----------------+-----------------+

The PHP function which I wrote to do the lookup/check is:
PHP Code:
function check_ip($IP)
    {
        
$IP ip2long($IP);
        
$result mysql_query("SELECT * FROM IPRangeTable");
        while(
$row mysql_fetch_array($result))
            {
                
$StartIP ip2long($row['StartIP']);
                
$EndIP   ip2long($row['EndIP']);
                if (
$IP >= $StartIP && $IP <= $EndIP)
                    {
                        return 
FALSE;
                        break;
                    }
            }
        return 
TRUE;
    } 
Hopefully some Google user might find it useful.

BTW, any corrections are welcome (I haven't tested it that much yet).

Last edited by win32sux; 09-12-2008 at 08:24 PM.
 
Old 09-12-2008, 08:20 PM   #6
win32sux
Guru
 
Registered: Jul 2003
Location: Los Angeles
Distribution: Ubuntu
Posts: 9,870

Original Poster
Rep: Reputation: 371Reputation: 371Reputation: 371Reputation: 371
Is that break necessary there?

Or does the while loop stop running when return is executed?
 
Old 09-12-2008, 08:31 PM   #7
jiml8
Senior Member
 
Registered: Sep 2003
Posts: 3,171

Rep: Reputation: 115Reputation: 115
Something similar that is more efficient:

$sql = 'SELECT
startip,endip
FROM
iprangetable
WHERE
startip < INET_ATON("'.$_SERVER['REMOTE_ADDR'].'")
ORDER BY
startip DESC
LIMIT 0,1';



In this situation, the IP addresses are stored in the database as internet addresses (a 32 bit number - see the usage for inet_aton to understand), and the database is queried exactly once and returns exactly one value, which is the correct value.

You would make this the startIP, then you would only have to check once to see if your IP was less than the end IP, after you got your return.
 
Old 09-12-2008, 08:40 PM   #8
jiml8
Senior Member
 
Registered: Sep 2003
Posts: 3,171

Rep: Reputation: 115Reputation: 115
Quote:
Originally Posted by win32sux View Post
Is that break necessary there?

Or does the while loop stop running when return is executed?
Returning from the middle of a while loop is bad form.

It is OK, so long as the scripting language cleans up its stack properly on function exit, but I have in the past encountered subtle and pernicious errors by doing this, when the scripting language did NOT clean up its stack properly on this kind of exit.

Better to exit from the top level only, in all cases. I don't know that PHP would have a problem with it, but why buy trouble?
 
Old 09-12-2008, 09:26 PM   #9
win32sux
Guru
 
Registered: Jul 2003
Location: Los Angeles
Distribution: Ubuntu
Posts: 9,870

Original Poster
Rep: Reputation: 371Reputation: 371Reputation: 371Reputation: 371
Thanks for that very educational feedback, jiml8. I'm reading up on INET_ATON right now, and will then read up on LIMIT. I haven't used either of those yet in my journey as a PHP/MySQL newbie. One question: Why did you choose to only select rows where StartIP is lower? In other words, is there a specific reason why you didn't just do the whole less than or equal to and greater than or equal to comparison with both EndIP and StartIP in the query? I'm thinking then one could have the function return TRUE/FALSE depending on whether the query returned an empty set or not.

EDIT: Wait, I haven't fully understood your query (yet) it seems. I need to do a lot more reading.

Last edited by win32sux; 09-12-2008 at 09:44 PM.
 
Old 09-12-2008, 10:07 PM   #10
graemef
Senior Member
 
Registered: Nov 2005
Location: Hanoi
Distribution: Fedora 13, Ubuntu 10.04
Posts: 2,379

Rep: Reputation: 148Reputation: 148
Quote:
Originally Posted by win32sux View Post
Is that break necessary there?

Or does the while loop stop running when return is executed?
The break is not necessary and the code will work without it. As a matter of style it is often considered poor form to have multiple return statements, however with a function as small as yours there is nothing particularly wrong with the return being where it is.

As for the SQL that jiml8 gave I think that it is possibly missing the check on the upper bound, maybe that was left as an exercise

Since the limit was set to 1 I woudl suggest that the ORDER BY is not required.
 
Old 09-13-2008, 01:09 AM   #11
jiml8
Senior Member
 
Registered: Sep 2003
Posts: 3,171

Rep: Reputation: 115Reputation: 115
The purpose of that request is to sort in descending order through the startip index. Since the sort is in descending order, the startip address should be greater than the IP address you are testing, UNTIL you reach and pass your IP address as the count descends.

The very first IP address in the startip field that is LESS THAN your IP address is the ONLY IP address you are interested in (unless you have overlapping IP ranges, which shouldn't be the case).

So you return THAT ROW ONLY, then test the endip field IN THAT ROW to see if that address is greater than the address you are testing. If it is, you have a winner; this IP address is one you are interested in.

Let the database do the work; that is what it is for. This saves a scripted while loop and a whole bunch of database queries. You need the ORDER BY because you are specifying a descending search so you have to be explicit about the index.
 
Old 09-13-2008, 01:59 AM   #12
graemef
Senior Member
 
Registered: Nov 2005
Location: Hanoi
Distribution: Fedora 13, Ubuntu 10.04
Posts: 2,379

Rep: Reputation: 148Reputation: 148
I have just removed my comment since I was wrong.

Personally I think that it can be confusing (since I was initially) to mix the checking between the SQL and the code. Whilst it works as jiml8 stated I would prefer both the upper and lower checks to be in the SQL. That would also remove the need for the overlapping caveat.

Last edited by graemef; 09-13-2008 at 02:04 AM.
 
Old 09-13-2008, 02:53 AM   #13
jiml8
Senior Member
 
Registered: Sep 2003
Posts: 3,171

Rep: Reputation: 115Reputation: 115
Checking both bounds in the query is certainly possible. I rather suspect it is faster to do it as I did it, but that could be wrong.

Also, I find it to be more readable to check the upper bound in the PHP after finding the right record with the query, but that is just me.

Simply a matter of opinion, I think.
 
Old 09-13-2008, 03:49 PM   #14
rubadub
Member
 
Registered: Jun 2004
Posts: 233

Rep: Reputation: 33
Sorry for dumping some code, but i've just written some such code today for a tutorial on my site, it'll look a little like this:
Code:
$host = 'localhost';
$user = 'user';
$pass = 'pass';
$db = 'db';

$conn = mysql_connect($host, $user, $pass) or die(mysql_error());
mysql_select_db($db, $conn) or die(mysql_error());

$s = "DROP TABLE test_ip_range";
mysql_query($s, $conn);

$s = "CREATE TABLE test_ip_range (id int not null primary key auto_increment, start_ip INT UNSIGNED, end_ip INT UNSIGNED, owner varchar(128) )";
if(mysql_query($s, $conn))
{
	$s = "INSERT INTO test_ip_range VALUES 
			('', INET_ATON('209.85.128.0'), INET_ATON('209.85.255.255'), 'Google Inc.'), 
			('', INET_ATON('86.160.0.0'), INET_ATON('86.171.255.255'), 'BT-CENTRAL-PLUS'), 
			('', INET_ATON('202.88.128.0'), INET_ATON('202.88.191.255'), 'HATHWAY-NET'), 
			('', INET_ATON('90.192.0.0'), INET_ATON('90.199.255.255'), 'BSKYB-BROADBAND') ";
	$res = mysql_query($s, $conn) or die(mysql_error());
}
else
{
	print "Table creation failed<br>";
	print mysql_error();
}

print "<br>";

$feedback = "";
$ip = "";
if(isset($_POST['SUBMIT']))
{
	$ip = $_POST['ip'];
	
	$s = "SELECT id, INET_NTOA(start_ip) as start_ip, INET_NTOA(end_ip) as end_ip, owner FROM test_ip_range WHERE start_ip <= INET_ATON('".$ip."') AND end_ip >= INET_ATON('".$ip."')";
	$res = mysql_query($s, $conn) or die(mysql_error());
	$feedback .= "<b>Results for: ".$ip."</b><br><table cellspacing='0px' cellpadding='3px' width='100%'>";
	if(mysql_num_rows($res)>0)
	{
		$feedback .= "<tr bgcolor='#efefef'><td>ID</td><td>START IP</td><td>END IP</td><td>OWNER</td></tr>";
		while ($a = mysql_fetch_array($res))
		{
			$feedback .= "<tr><td>".$a['id']."</td><td>".$a['start_ip']."</td><td>".$a['end_ip']."</td><td>".$a['owner']."</td></tr>";
		}
	}
	else
	{
		$feedback .= "<tr><td>No match</td></tr>";
	}
	$feedback .= "</table>";
}
print "<form method='POST' action=''><table>";
print "<tr><td align='right'>IP Address</td><td><input type='text' name='ip' size='32' value='".$ip."'></td></tr>";
print "<tr><td align='right'></td><td><input type='submit' name='SUBMIT' value='Submit'></td></tr>";
print "</table></form><br>";
print "<br>".$feedback;

print "<br>";
print "<table cellspacing='0px' cellpadding='3px' width='100%'><tr bgcolor='#efefef'><td>ID</td><td>START IP</td><td>END IP</td><td>OWNER</td></tr>";
$s = "SELECT id, INET_NTOA(start_ip) as start_ip, INET_NTOA(end_ip) as end_ip, owner FROM test_ip_range ORDER BY start_ip";
$res = mysql_query($s, $conn) or die(mysql_error());
while ($a = mysql_fetch_array($res))
{
	print "<tr><td>".$a['id']."</td><td>".$a['start_ip']."</td><td>".$a['end_ip']."</td><td>".$a['owner']."</td></tr>";
}
print "</table>";
The choice of ip ranges were the first 4 in a recent log to whois thing.
 
Old 04-28-2013, 08:52 PM   #15
rkane
Member
 
Registered: May 2003
Location: Ohio
Distribution: Slackware
Posts: 47

Rep: Reputation: 15
INET_ATON mysql

I just setup a table in mysql that has a startip and endip associated with each user.
And in order to see if they are coming from the ip range I can do this.

SELECT * FROM `users` WHERE INET_ATON( '192.168.1.50' ) BETWEEN startip AND endip AND user='$user'

So if it returns a row then it is good.
You could easily flip the logic "NOT BETWEEN" and it would work for your case.
 
  


Reply

Tags
ip


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
how to check link list ic circular or not amit_pansuria Programming 6 07-04-2007 03:28 AM
ProFTPd ... FTP client fails to connect: timeout after client sends 'LIST' nutnut Linux - Software 2 01-01-2006 08:09 PM
The HorizSync key word must followed by a list of numbers or ranges comox *BSD 2 03-14-2005 04:57 AM
Production Check List myrtle Linux - Newbie 2 12-04-2003 01:42 PM
Refresh client user list at autentication client/server network. robertoneto123 Linux - Networking 0 11-11-2003 11:38 AM


All times are GMT -5. The time now is 06:45 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration