INET_ATON() and INET_NTOA() in PHP? - php

I want to store IP addresses in my database, but I also need to use them throughout my application. I read about using INET_ATON() and INET_NTOA() in my MySQL queries to get a 32-bit unsigned integer out of an IP address, which is exactly what I want as it will make searching through the database faster than using char(15).
The thing is, I can't find a function that does the same sort of thing in PHP. The only thing I came across is:
http://php.net/manual/en/function.ip2long.php
So I tested it:
$ip = $_SERVER['REMOTE_ADDR'];
echo ip2long($ip);
And it outputs nothing. In the example they gave it seems to work, but then again I'm not exactly sure if ip2long() does the same thing as INET_ATON().
Does someone know a PHP function that will do this? Or even a completely new solution to storing an IP address in a database?
Thanks.

There is an important distinction between ip2long, long2ip and the MySQL functions.
PHP's ip2long and long2ip deal with signed integers.
See http://php.net/manual/en/function.ip2long.php
"Because PHP's integer type is signed, and many IP addresses will result in negative integers on 32-bit architectures, you need to use the '%u' formatter of sprintf() or printf() to get the string representation of the unsigned IP address."
MySQL's INET_ATON() and INET_NTOA() deal with unsigned integers
See http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-aton
"To store values generated by INET_ATON(), use an INT UNSIGNED column rather than INT, which is signed. If you use a signed column, values corresponding to IP addresses for which the first octet is greater than 127 cannot be stored correctly."
Here are some functions you can use to work between the two.
If you inserted into the MySQL database, an IP using INET_ATON(), you can convert it back in PHP using the following:
long2ip(sprintf("%d", $ip_address));
And you can convert it to save it in the database from PHP using this:
sprintf("%u", ip2long($ip_address));
(Also important, don't type-cast the $ip_address to int as this might cause problems by wrapping the number if it's bigger than MAX_INT. If you must cast it, cast it to a long or float)

The ip2long() and long2ip() functions should work just fine.
Note : you should use those for IPv4 addresses -- make sure that, in your case, $_SERVER['REMOTE_ADDR'] actually contains a valid IPv4 address (and not some IPv6-stuff).
Trying on a google IP address :
var_dump(ip2long('209.85.227.147'));
var_dump(long2ip(3512066963));
I get the following output :
int(3512066963)
string(14) "209.85.227.147"

You should not have to deal with that inside PHP, that's what MySQL native funcions are for. See this example:
create table iptable (
ip int(32) unsigned not null,
comment varchar(32) not null
);
insert into iptable (ip, comment) values (inet_aton('10.0.0.3'), 'This is 10.0.0.3');
select * from iptable;
+-----------+------------------+
| ip | comment |
+-----------+------------------+
| 167772163 | This is 10.0.0.3 |
+-----------+------------------+
select inet_ntoa(ip) as ip, comment from iptable;
+----------+------------------+
| ip | comment |
+----------+------------------+
| 10.0.0.3 | This is 10.0.0.3 |
+----------+------------------+
If you want to deal with both ipv4 and ipv6 in the same field, and you are using Mysql 5.6 or higher, you can use varbinary(16) and the functions inet6_aton and inet6_ntoa. This is a better example of why you should use MySQL functions and not deal with binary data inside PHP:
create table iptable2 (
ip varbinary(16) not null,
comment varchar(32) not null
);
insert into iptable2 (ip, comment) values
(inet6_aton('192.168.1.254'), 'This is router 192.168.1.254'),
(inet6_aton('::1'), 'This is ipv6 localhost ::1'),
(inet6_aton('FE80:0000:0000:0000:0202:B3FF:FE1E:8329'), 'This is some large ipv6 example')
;
select * from iptable2;
+------------------+---------------------------------+
| ip | comment |
+------------------+---------------------------------+
| +¿?¦ | This is router 192.168.1.254 |
| ? | This is ipv6 localhost ::1 |
| ¦Ç ??¦ ¦?â) | This is some large ipv6 example |
+------------------+---------------------------------+
select inet6_ntoa(ip) as ip, comment from iptable2;
+--------------------------+---------------------------------+
| ip | comment |
+--------------------------+---------------------------------+
| 192.168.1.254 | This is router 192.168.1.254 |
| ::1 | This is ipv6 localhost ::1 |
| fe80::202:b3ff:fe1e:8329 | This is some large ipv6 example |
+--------------------------+---------------------------------+
You can see that by doing this, you can actually avoid having to evaluate ipv6 addresses in different formats, since MySQL converts them to binary and back to their simpliest expression.
I know this question has more than 2 years already, but I want to let this information be useful for others that come across.
HTH
Francisco Zarabozo

For IPv4 and IPv6 support use inet_pton() and inet_ntop(), these are availiable since PHP 5.1+ and mimic exactly the equivalent MySQL functions.
Otherwise just use ip2long() and long2ip().

Here PHP alternative functions (simple copy/paste in your program) -
function inet_aton($ip)
{
$ip = trim($ip);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) return 0;
return sprintf("%u", ip2long($ip));
}
function inet_ntoa($num)
{
$num = trim($num);
if ($num == "0") return "0.0.0.0";
return long2ip(-(4294967295 - ($num - 1)));
}

ip2long is equivalent to inet_aton().
ip2long only works with IPv4. I suspect your system is using IPv6 for loopback. Try to print REMOTE_ADDR.

Related

Limit Laravel 5 Encryption length

I have a Laravel5 web application of Business directory.
When I Encrypting a value like
$cryptval = Crypt::encrypt(1);
result = eyJpdiI6IndhaFZFNlhIRDlURzdXanJVMEhBM0E9PSIsInZhbHVlIjoidWF3VzRFZDhyRHltUlwveDdyV0VVWnc9PSIsIm1hYyI6IjE5YjA2YWIyN2Q0MTBlYjdhNDJiNDE5ZjY2OGQ2MDA2NzQ3ZTA4ODc4NzY0ZTIwMjBiMzQxN2RjNmM5ZDg3ZjYifQ==
its giving a long string about 250 length.
Is there any way to limit the length of this string in laravel?
My Client needs to add the URL with encrypted value in a mail function.
eg:
www.example.com/varify/eyJpdiI6IndhaFZFNlhIRDlURzdXanJVMEhBM0E9PSIsInZhbHVlIjoidWF3VzRFZDhyRHltUlwveDdyV0VVWnc9PSIsIm1hYyI6IjE5YjA2YWIyN2Q0MTBlYjdhNDJiNDE5ZjY2OGQ2MDA2NzQ3ZTA4ODc4NzY0ZTIwMjBiMzQxN2RjNmM5ZDg3ZjYifQ==
But the mail function only allow some length of URL :(
One solution is to store the hashed values in a table, and then reference the hash by the auto-incrementing ID of the hash value.
| id | hash | timestamp | random_key |
| 1 | some-hash | 125346164 | 21415 |
| 2 | some-other-hash | 123513515 | 25151 |
So now, instead of using:
www.example.com/verify/some-hash
You can use:
www.example.com/verify/1
The id should really be obfuscated, and not used just as an integer - which is where the timestamp and random_key can help.
$id = 1;
$timestamp = 125346164;
$randomKey = 21415;
$key = base64_encode($timestamp . $randomKey . $id);
echo 'http://www.domain.com/verify/' . $key;
// http://www.domain.com/verify/MTI1MzQ2MTY0MjE0MTUx
All that being said, my recommendation would be to try to work around the limitation put in place by the e-mail delivery platform as URL's can support an address length of around 2000 characters. The example you gave only had a length of 32 and falls well within the lengths acceptable by any modern browser.
Edit: Just generate a uuid using a package like this rather than trying to create your own random id. This will produce a string such as d3d29d70-1d25-11e3-8591-034165a3a613.
I think dont need to store nothing in database, that is a hard work,
In my case a use base64_encode in blase and use base64_decode in controller to show the real value to method and continue the process.
I just faced the same problem. I simply added a column 'hash' in my database table. Then I filled it with a md5(encrypt($model->id))
The md5 value is much shorter, and because it also uses Laravel's crypt, it can't be guessed.

How to convert binary to integer in mysql?

To put my question into perspective:
I have a PHP app, which stores IP's of users in a MySQL table.
The column type is VARBINARY(16), and the app uses PHP's inet_pton to form a binary string.
That is the string has 4 bytes for a typical IP4 address.
How to retrieve these IPs from the table, displaying them in a human readable form?
My current solution is:
select INET_NTOA(CONV(HEX(ip),16,10)) from operation_ip;
Is there a more direct way to do that?
In particular is CONV(HEX(x),16,10) the easiest way to change 4 bytes into an integer (actually I believe it is not even an integer, but a string which looks like integer).
(I use VARBINARY(16), as PHP's inet_pton can return 16-bytes for IPv6 addresses. AFAIK MySQL's INET_NTOA does not support IPv6, but at the moment this is not my biggest concern).
MySQL has nice built-in functions to handle this case:
INSERT INTO t(ip) VALUE (INET_ATON('1.2.3.4')); -- stores integer 16909060
SELECT INET_NTOA(ip) FROM t; -- returns string '1.2.3.4'
Or the PHP way:
$sql = 'INSERT INTO t(ip) VALUE (' . ip2long('1.2.3.4') . '))';
// ip2long('1.2.3.4') = 16909060
// reverse conversion: long2ip(16909060) returns '1.2.3.4'

SQL string match numbers, varying length

I am looking up which exchange services which telephone numbers, from a table of fragmentary numbers that show which exchange services them.
So my table contains, for example:
id |exchcode |exchname |easting|northin|leadin |
-----------------------------------------------------------------
12122 |SNL/UC |SANDAL |43430 |41306 |1924240 |
12123 |SNL/UC |SANDAL |43430 |41306 |1924241 |
881 |SNL/UD |SANDAL |43430 |41306 |1924249 |
2456 |BD/BCC/1 |BRADFORD CABLE |41627 |43262 |192421 |
4313 |NEY/UB |NORMANTON |43847 |42289 |192422 |
12124 |SNL/UC |SANDAL |43430 |41306 |192425 |
9949 |OBE/UB |HORBURY OSSETT |42857 |41971 |192428 |
9987 |OBE/UB |WAKEFIELD |42857 |41971 |1924 |
(sorry, formatting a bit rubbish)
leadin is the leading part of the phone number I have to match (stored as a VARCHAR, not a number)
And I am supplied with a phone number 1924283777 (not real)
how do I query to get the best match from the above table (It should pick exchange id 9949), or do I deal with it in code after I've done the query (php)
tl;dr: variable length for values of leadin column, want best match with a number longer than leadin.
I would think something like
WHERE ? LIKE concat(leadin, '%') order by length(leadin) desc limit 1
(I haven't checked the function names, and I'm not certain that this will work in MYSQL - I'm pretty sure it will work in one of the SQL's I've used).

PHP TZ setting length

I was wonder what is the maximum length of the timezone settings in PHP? I'm storing the string in a database, and would like to keep the length as short as possible, but i see timezones like "Canada/East-Saskatchewan", which goes beyond our current limit.
If I could just get a list of all the supported timezone string, I can sort them, but they are currently split on to several different pages.
linky: http://www.php.net/manual/en/timezones.php
Edit June 2021 Answer is 64. Why? That's the width of the column used in MySQL to store those timezone name strings.
The zoneinfo database behind those time zone strings just added new prefixes. To America/Argentina/ComodRivadavia, formerly the longest timezone string, they added posix/America/Argentina/ComodRivadavia and right/America/Argentina/ComodRivadavia, both with a length of 38. This is up from 32, the previous longest string.
And here is the complete PHP code to find that:
<?php
$timezone_identifiers = DateTimeZone::listIdentifiers();
$maxlen = 0;
foreach($timezone_identifiers as $id)
{
if(strlen($id) > $maxlen)
$maxlen = strlen($id);
}
echo "Max Length: $maxlen";
/*
Output:
Max Length: 32
*/
?>
The Olson database — available from ftp://ftp.iana.org/tz/releases/ or http://www.iana.org/time-zones (but see also http://www.twinsun.com/tz/tz-link.htm* and http://en.wikipedia.org/wiki/Tz_database) — is the source of these names. The documentation in the file Theory includes a description of how the zone names are formed. This would help you establish how long names can be.
The longest 'current' names are 30 characters (America/Argentina/Buenos_Aires,
America/Argentina/Rio_Gallegos, America/North_Dakota/New_Salem); the longest 'backwards compatibility' name is 32 characters (America/Argentina/ComodRivadavia).
* Note that the TwinSun site has not been updated for some time and has some outdated links (such as suggesting that the Olson database is available from ftp://ftp.elsie.nci.nih.gov — it is now available from IANA instead).
From the manual:
<?php
$timezone_identifiers = DateTimeZone::listIdentifiers();
for ($i=0; $i < 5; $i++) {
echo "$timezone_identifiers[$i]\n";
}
?>
If you are using MySQL with PHP, consider that MySQL already stores Olson timezone names in the "mysql" system database, in a table called "time_zone_name", in a column called "name". One option is to choose the length of your column to match the length that MySQL uses. In MySQL 5.7, the timezone name length is 64 characters. To determine the length in use on your MySQL installation, follow the example below:
mysql> use mysql
Database changed
mysql> describe time_zone_name;
+--------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------------+------+-----+---------+-------+
| Name | char(64) | NO | PRI | NULL | |
| Time_zone_id | int(10) unsigned | NO | | NULL | |
+--------------+------------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

Is php deg2rad() equal to mysql radians()

Are these functions the same? If not, what is an appropriate php equivalent to mysql's radians()
Judging from their documentations (deg2rad, radians), they seem to do the same.
And a quick verification on a simple test-case :
mysql> select radians(0), radians(45), radians(90);
+------------+-------------------+-----------------+
| radians(0) | radians(45) | radians(90) |
+------------+-------------------+-----------------+
| 0 | 0.785398163397448 | 1.5707963267949 |
+------------+-------------------+-----------------+
1 row in set (0,00 sec)
And, in PHP :
var_dump(deg2rad(0), deg2rad(45), deg2rad(90));
also gives :
float 0
float 0.785398163397
float 1.57079632679
So, it seems they do quite the same...
Consulting the documentation:
MySQL's RADIANS(x): returns the argument x, converted from degrees to radians.
PHP's DEG2RAD(): converts the number in degrees to the radian equivalent
...so yes, they are equivalent.
Was there something more specific you were looking for?

Categories