@@ -12,7 +12,7 @@ class Database
1212 *
1313 * @var string
1414 */
15- private const VERSION = '4.0.1 ' ;
15+ private const VERSION = '4.1.0 ' ;
1616
1717 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1818 // Error field constants ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -23,26 +23,40 @@ class Database
2323 *
2424 * @var string
2525 */
26- private const FIELD_NOT_SUPPORTED = 'NOT SUPPORTED ' ;
26+ public const FIELD_NOT_SUPPORTED = 'This parameter is unavailable in selected .BIN data file. Please upgrade data file. ' ;
2727
2828 /**
2929 * Unknown field message.
3030 *
3131 * @var string
3232 */
33- private const FIELD_NOT_KNOWN = 'This parameter does not exists. Please verify. ' ;
33+ public const FIELD_NOT_KNOWN = 'This parameter does not exists. Please verify. ' ;
3434
3535 /**
3636 * Invalid IP address message.
3737 *
3838 * @var string
3939 */
40- private const INVALID_IP_ADDRESS = 'INVALID IP ADDRESS ' ;
40+ public const INVALID_IP_ADDRESS = 'INVALID IP ADDRESS ' ;
4141
4242 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
4343 // Field selection constants ///////////////////////////////////////////////////////////////////////////////////////////////////////////
4444 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
4545
46+ /**
47+ * Maximum IPv4 number.
48+ *
49+ * @var int
50+ */
51+ public const MAX_IPV4_RANGE = 4294967295 ;
52+
53+ /**
54+ * MAximum IPv6 number.
55+ *
56+ * @var int
57+ */
58+ public const MAX_IPV6_RANGE = 340282366920938463463374607431768211455 ;
59+
4660 /**
4761 * Country code (ISO 3166-1 Alpha 2).
4862 *
@@ -134,6 +148,13 @@ class Database
134148 */
135149 public const THREAT = 13 ;
136150
151+ /**
152+ * Provider.
153+ *
154+ * @var int
155+ */
156+ public const PROVIDER = 14 ;
157+
137158 /**
138159 * Country name and code.
139160 *
@@ -243,6 +264,13 @@ class Database
243264 */
244265 private const EXCEPTION_NO_PATH = 10009 ;
245266
267+ /**
268+ * Invalid BIN database file.
269+ *
270+ * @var int
271+ */
272+ public const EXCEPTION_INVALID_BIN_DATABASE = 10010 ;
273+
246274 /**
247275 * BCMath extension not installed.
248276 *
@@ -306,18 +334,19 @@ class Database
306334 * @var array
307335 */
308336 private $ columns = [
309- self ::COUNTRY_CODE => [8 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 ],
310- self ::COUNTRY_NAME => [8 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 ],
311- self ::REGION_NAME => [0 , 0 , 16 , 16 , 16 , 16 , 16 , 16 , 16 , 16 ],
312- self ::CITY_NAME => [0 , 0 , 20 , 20 , 20 , 20 , 20 , 20 , 20 , 20 ],
313- self ::ISP => [0 , 0 , 0 , 24 , 24 , 24 , 24 , 24 , 24 , 24 ],
314- self ::PROXY_TYPE => [0 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 ],
315- self ::DOMAIN => [0 , 0 , 0 , 0 , 28 , 28 , 28 , 28 , 28 , 28 ],
316- self ::USAGE_TYPE => [0 , 0 , 0 , 0 , 0 , 32 , 32 , 32 , 32 , 32 ],
317- self ::ASN => [0 , 0 , 0 , 0 , 0 , 0 , 36 , 36 , 36 , 36 ],
318- self ::_AS => [0 , 0 , 0 , 0 , 0 , 0 , 40 , 40 , 40 , 40 ],
319- self ::LAST_SEEN => [0 , 0 , 0 , 0 , 0 , 0 , 0 , 44 , 44 , 44 ],
320- self ::THREAT => [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 48 , 48 ],
337+ self ::COUNTRY_CODE => [8 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 ],
338+ self ::COUNTRY_NAME => [8 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 ],
339+ self ::REGION_NAME => [0 , 0 , 16 , 16 , 16 , 16 , 16 , 16 , 16 , 16 , 16 ],
340+ self ::CITY_NAME => [0 , 0 , 20 , 20 , 20 , 20 , 20 , 20 , 20 , 20 , 20 ],
341+ self ::ISP => [0 , 0 , 0 , 24 , 24 , 24 , 24 , 24 , 24 , 24 , 24 ],
342+ self ::PROXY_TYPE => [0 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 ],
343+ self ::DOMAIN => [0 , 0 , 0 , 0 , 28 , 28 , 28 , 28 , 28 , 28 , 28 ],
344+ self ::USAGE_TYPE => [0 , 0 , 0 , 0 , 0 , 32 , 32 , 32 , 32 , 32 , 32 ],
345+ self ::ASN => [0 , 0 , 0 , 0 , 0 , 0 , 36 , 36 , 36 , 36 , 36 ],
346+ self ::_AS => [0 , 0 , 0 , 0 , 0 , 0 , 40 , 40 , 40 , 40 , 40 ],
347+ self ::LAST_SEEN => [0 , 0 , 0 , 0 , 0 , 0 , 0 , 44 , 44 , 44 , 44 ],
348+ self ::THREAT => [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 48 , 48 , 48 ],
349+ self ::PROVIDER => [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 52 ],
321350 ];
322351
323352 /**
@@ -339,6 +368,7 @@ class Database
339368 self ::_AS => 'as ' ,
340369 self ::LAST_SEEN => 'lastSeen ' ,
341370 self ::THREAT => 'threat ' ,
371+ self ::PROVIDER => 'provider ' ,
342372 self ::IP_ADDRESS => 'ipAddress ' ,
343373 self ::IP_VERSION => 'ipVersion ' ,
344374 self ::IP_NUMBER => 'ipNumber ' ,
@@ -351,6 +381,7 @@ class Database
351381 */
352382 private $ databases = [
353383 // IPv4 databases
384+ 'IP2PROXY-IP-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT-RESIDENTIAL-PROVIDER ' ,
354385 'IP2PROXY-IP-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT-RESIDENTIAL ' ,
355386 'IP2PROXY-IP-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT ' ,
356387 'IP2PROXY-IP-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN ' ,
@@ -363,6 +394,7 @@ class Database
363394 'IP2PROXY-IP-COUNTRY ' ,
364395
365396 // IPv6 databases
397+ 'IPV6-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT-RESIDENTIAL-PROVIDER ' ,
366398 'IPV6-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT-RESIDENTIAL ' ,
367399 'IPV6-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT ' ,
368400 'IPV6-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN ' ,
@@ -466,6 +498,27 @@ class Database
466498 private $ month ;
467499 private $ day ;
468500
501+ /**
502+ * Product code.
503+ *
504+ * @var string
505+ */
506+ private $ productCode ;
507+
508+ /**
509+ * License code.
510+ *
511+ * @var string
512+ */
513+ private $ licenseCode ;
514+
515+ /**
516+ * Database size.
517+ *
518+ * @var int
519+ */
520+ private $ databaseSize ;
521+
469522 // This variable will be used to hold the raw row of columns's positions
470523 private $ rawPositionsRow ;
471524
@@ -587,12 +640,23 @@ public function __construct($file = null, $mode = self::FILE_IO)
587640 $ this ->month = $ this ->readByte (4 );
588641 $ this ->day = $ this ->readByte (5 );
589642 $ this ->date = date ('Y-m-d ' , strtotime ("{$ this ->year }- {$ this ->month }- {$ this ->day }" ));
643+ $ this ->productCode = $ this ->readByte (30 );
644+ $ this ->licenseCode = $ this ->readByte (31 );
645+ $ this ->databaseSize = $ this ->readByte (32 );
590646 $ this ->ipCount [4 ] = $ this ->readWord (6 );
591647 $ this ->ipBase [4 ] = $ this ->readWord (10 );
592648 $ this ->ipCount [6 ] = $ this ->readWord (14 );
593649 $ this ->ipBase [6 ] = $ this ->readWord (18 );
594650 $ this ->indexBaseAddr [4 ] = $ this ->readWord (22 );
595651 $ this ->indexBaseAddr [6 ] = $ this ->readWord (26 );
652+
653+ if ($ this ->productCode == 2 ) {
654+ } else {
655+ if ($ this ->year <= 20 && $ this ->productCode == 0 ) {
656+ } else {
657+ throw new \Exception (__CLASS__ . ': Incorrect IP2Proxy BIN file format. Please make sure that you are using the latest IP2Proxy BIN file. ' , self ::EXCEPTION_INVALID_BIN_DATABASE );
658+ }
659+ }
596660 }
597661
598662 /**
@@ -705,6 +769,7 @@ public function lookup($ip, $fields = null, $asNamed = true)
705769 $ ifields [] = self ::_AS ;
706770 $ ifields [] = self ::LAST_SEEN ;
707771 $ ifields [] = self ::THREAT ;
772+ $ ifields [] = self ::PROVIDER ;
708773 $ ifields [] = self ::COUNTRY ;
709774 $ ifields [] = self ::IP_ADDRESS ;
710775 $ ifields [] = self ::IP_VERSION ;
@@ -728,6 +793,7 @@ public function lookup($ip, $fields = null, $asNamed = true)
728793 self ::_AS => false ,
729794 self ::LAST_SEEN => false ,
730795 self ::THREAT => false ,
796+ self ::PROVIDER => false ,
731797 self ::COUNTRY => false ,
732798 self ::IP_ADDRESS => false ,
733799 self ::IP_VERSION => false ,
@@ -859,6 +925,13 @@ public function lookup($ip, $fields = null, $asNamed = true)
859925 }
860926 break ;
861927
928+ case self ::PROVIDER :
929+ if (!$ done [self ::PROVIDER ]) {
930+ $ results [self ::PROVIDER ] = $ this ->readProvider ($ pointer );
931+ $ done [self ::PROVIDER ] = true ;
932+ }
933+ break ;
934+
862935 case self ::IP_ADDRESS :
863936 if (!$ done [self ::IP_ADDRESS ]) {
864937 $ results [self ::IP_ADDRESS ] = $ ip ;
@@ -1137,39 +1210,43 @@ private function ipBetween($version, $ip, $low, $high)
11371210 *
11381211 * @return array
11391212 */
1140- private function ipVersionAndNumber ($ ip )
1213+ private static function ipVersionAndNumber ($ ip )
11411214 {
11421215 if (filter_var ($ ip , FILTER_VALIDATE_IP , FILTER_FLAG_IPV4 )) {
1143- return [4 , sprintf ('%u ' , ip2long ($ ip ))];
1216+ $ number = sprintf ('%u ' , ip2long ($ ip ));
1217+
1218+ return [4 , ($ number == self ::MAX_IPV4_RANGE ) ? ($ number - 1 ) : $ number ];
11441219 } elseif (filter_var ($ ip , FILTER_VALIDATE_IP , FILTER_FLAG_IPV6 )) {
1145- // Expand IPv6 address
1146- $ ip = implode ( ' : ' , str_split ( unpack ( ' H*0 ' , inet_pton ( $ ip))[ 0 ], 4 ) );
1220+ $ result = 0 ;
1221+ $ ip = self :: expand ( $ ip );
11471222
11481223 // 6to4 Address - 2002::/16
11491224 if (substr ($ ip , 0 , 4 ) == '2002 ' ) {
1150- return [4 , (int ) (gmp_import (inet_pton ($ ip )) >> 80 ) & 4294967295 ];
1225+ foreach (str_split (bin2hex (inet_pton ($ ip )), 8 ) as $ word ) {
1226+ $ result = bcadd (bcmul ($ result , '4294967296 ' , 0 ), self ::wrap32 (hexdec ($ word )), 0 );
1227+ }
1228+
1229+ return [4 , bcmod (bcdiv ($ result , bcpow (2 , 80 )), '4294967296 ' )];
11511230 }
11521231
11531232 // Teredo Address - 2001:0::/32
11541233 if (substr ($ ip , 0 , 9 ) == '2001:0000 ' ) {
1155- return [4 , (~ hexdec (str_replace ( ' : ' , '' , substr ($ ip , - 9 ))) & 4294967295 )];
1234+ return [4 , hexdec (substr ( $ ip , 10 , 4 ) . substr ($ ip , 15 , 4 ) )];
11561235 }
11571236
1158- // IPv4 Address
1159- if (substr ($ ip , 0 , 9 ) == '0000:0000 ' ) {
1160- return [4 , hexdec (substr ($ ip , -9 ))];
1161- }
1162-
1163- // Common IPv6 Address
1164- $ result = 0 ;
1165-
11661237 foreach (str_split (bin2hex (inet_pton ($ ip )), 8 ) as $ word ) {
11671238 $ result = bcadd (bcmul ($ result , '4294967296 ' , 0 ), self ::wrap32 (hexdec ($ word )), 0 );
11681239 }
11691240
1241+ // IPv4 address in IPv6
1242+ if (bccomp ($ result , '281470681743360 ' ) >= 0 && bccomp ($ result , '281474976710655 ' ) <= 0 ) {
1243+ return [4 , bcsub ($ result , '281470681743360 ' )];
1244+ }
1245+
11701246 return [6 , $ result ];
11711247 }
1172- // Invalid IP address, return falses
1248+
1249+ // Invalid IP address, return false
11731250 return [false , false ];
11741251 }
11751252
@@ -1182,6 +1259,10 @@ private function ipVersionAndNumber($ip)
11821259 */
11831260 private function bcBin2Dec ($ data )
11841261 {
1262+ if (!$ data ) {
1263+ return ;
1264+ }
1265+
11851266 $ parts = [
11861267 unpack ('V ' , substr ($ data , 12 , 4 )),
11871268 unpack ('V ' , substr ($ data , 8 , 4 )),
@@ -1200,6 +1281,22 @@ private function bcBin2Dec($data)
12001281 return $ result ;
12011282 }
12021283
1284+ /**
1285+ * Return the decimal string representing the binary data given.
1286+ *
1287+ * @static
1288+ *
1289+ * @param mixed $ipv6
1290+ *
1291+ * @return string
1292+ */
1293+ private static function expand ($ ipv6 )
1294+ {
1295+ $ hex = unpack ('H*hex ' , inet_pton ($ ipv6 ));
1296+
1297+ return substr (preg_replace ('/([A-f0-9]{4})/ ' , '$1: ' , $ hex ['hex ' ]), 0 , -1 );
1298+ }
1299+
12031300 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
12041301 // Caching backend abstraction /////////////////////////////////////////////////////////////////////////////////////////////////////////
12051302 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1563,6 +1660,29 @@ private function readThreat($pointer)
15631660 return $ threat ;
15641661 }
15651662
1663+ /**
1664+ * High level function to fetch the Provider.
1665+ *
1666+ * @param int $pointer Position to read from, if false, return self::INVALID_IP_ADDRESS
1667+ *
1668+ * @return string
1669+ */
1670+ private function readProvider ($ pointer )
1671+ {
1672+ if ($ pointer === false ) {
1673+ // Deal with invalid IPs
1674+ $ provider = self ::INVALID_IP_ADDRESS ;
1675+ } elseif ($ this ->columns [self ::PROVIDER ][$ this ->type ] === 0 ) {
1676+ // If the field is not suported, return accordingly
1677+ $ provider = self ::FIELD_NOT_SUPPORTED ;
1678+ } else {
1679+ // Read the domain
1680+ $ provider = $ this ->readString ($ this ->columns [self ::PROVIDER ][$ this ->type ]);
1681+ }
1682+
1683+ return $ provider ;
1684+ }
1685+
15661686 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
15671687 // Binary search and support functions /////////////////////////////////////////////////////////////////////////////////////////////////
15681688 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0 commit comments