@@ -11,14 +11,15 @@ ip2location = {
1111 ipv4databaseaddr = 0 ,
1212 ipv6databasecount = 0 ,
1313 ipv6databaseaddr = 0 ,
14+ ipv4indexed = false ,
15+ ipv6indexed = false ,
1416 ipv4indexbaseaddr = 0 ,
1517 ipv6indexbaseaddr = 0 ,
1618 ipv4columnsize = 0 ,
1719 ipv6columnsize = 0 ,
1820 productcode = 0 ,
1921 producttype = 0 ,
2022 filesize = 0 ,
21- columnsize_without_ip = 0 ,
2223 country_position_offset = 0 ,
2324 region_position_offset = 0 ,
2425 city_position_offset = 0 ,
@@ -121,7 +122,7 @@ local usagetype_position = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
121122local addresstype_position = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 21 }
122123local category_position = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 22 }
123124
124- local api_version = " 8.5 .0"
125+ local api_version = " 8.6 .0"
125126
126127local modes = {
127128 countryshort = 0x000001 ,
@@ -161,6 +162,13 @@ local function printme(stuff)
161162 print (inspect (stuff ))
162163end
163164
165+ -- read row
166+ local function readrow (myfile , pos , len )
167+ myfile :seek (" set" , pos - 1 )
168+ local bytestr = myfile :read (len )
169+ return bytestr
170+ end
171+
164172-- read byte
165173local function readuint8 (pos , myfile )
166174 myfile :seek (" set" , pos - 1 )
@@ -172,6 +180,17 @@ local function readuint8(pos, myfile)
172180 return value
173181end
174182
183+ -- read byte
184+ local function readuint8header (pos , row )
185+ local pos2 = pos + 1 -- due to index starting with 1
186+ local bytestr = string.sub (row , pos2 , pos2 ) -- strip 1 byte
187+ local value = 0 -- no need BigNum
188+ if bytestr ~= nil then
189+ value = string.byte (bytestr )
190+ end
191+ return value
192+ end
193+
175194-- read unsigned 32-bit integer
176195local function readuint32 (pos , myfile )
177196 myfile :seek (" set" , pos - 1 )
@@ -183,6 +202,17 @@ local function readuint32(pos, myfile)
183202 return value
184203end
185204
205+ -- read unsigned 32-bit integer
206+ local function readuint32header (pos , row )
207+ local pos2 = pos + 1 -- due to index starting with 1
208+ local bytestr = string.sub (row , pos2 , pos2 + 3 ) -- strip 4 bytes
209+ local value = 0 -- no need BigNum
210+ if bytestr ~= nil then
211+ value = string.unpack (" <I4" , bytestr )
212+ end
213+ return value
214+ end
215+
186216-- read unsigned 32-bit integer
187217local function readuint32row (pos , row )
188218 local pos2 = pos + 1 -- due to index starting with 1
@@ -205,16 +235,27 @@ local function readuint128(pos, myfile)
205235 return value
206236end
207237
238+ -- read unsigned 128-bit integer
239+ local function readuint128row (pos , row )
240+ local pos2 = pos + 1 -- due to index starting with 1
241+ local bytestr = string.sub (row , pos2 , pos2 + 15 ) -- strip 16 bytes
242+ local value = bn .ZERO
243+ if bytestr ~= nil then
244+ value = bn (string.unpack (" <I8" , bytestr )) + (bn (string.unpack (" <I8" , bytestr , 9 )) << 64 ) -- cannot read 16 bytes at once so split into 2
245+ end
246+ return value
247+ end
248+
208249-- read string
209250local function readstr (pos , myfile )
210251 myfile :seek (" set" , pos )
211- local len = myfile :read (1 )
252+ local data = myfile :read (256 ) -- max size of string field + 1 byte for length
212253 local strlen = 0
254+ local len = string.sub (data , 1 , 1 )
213255 if len ~= nil then
214256 strlen = string.byte (len )
215257 end
216- myfile :seek (" set" , pos + 1 )
217- local bytestr = myfile :read (strlen )
258+ local bytestr = string.sub (data , 2 , (strlen + 1 ))
218259 local value = ' '
219260 if bytestr ~= nil then
220261 value = bytestr
@@ -246,30 +287,40 @@ function ip2location:new(dbpath)
246287 else
247288 x .f = file
248289 end
249- x .databasetype = readuint8 (1 , x .f )
250- x .databasecolumn = readuint8 (2 , x .f )
251- x .databaseyear = readuint8 (3 , x .f )
252- x .databasemonth = readuint8 (4 , x .f )
253- x .databaseday = readuint8 (5 , x .f )
254-
255- x .ipv4databasecount = readuint32 (6 , x .f ):asnumber ()
256- x .ipv4databaseaddr = readuint32 (10 , x .f ):asnumber ()
257- x .ipv6databasecount = readuint32 (14 , x .f ):asnumber ()
258- x .ipv6databaseaddr = readuint32 (18 , x .f ):asnumber ()
259- x .ipv4indexbaseaddr = readuint32 (22 , x .f ):asnumber ()
260- x .ipv6indexbaseaddr = readuint32 (26 , x .f ):asnumber ()
261- x .productcode = readuint8 (30 , x .f )
262- x .producttype = readuint8 (31 , x .f )
263- x .filesize = readuint32 (32 , x .f ):asnumber ()
290+
291+ local row = readrow (x .f , 1 , 64 ) -- 64-byte header
292+
293+ x .databasetype = readuint8header (0 , row )
294+ x .databasecolumn = readuint8header (1 , row )
295+ x .databaseyear = readuint8header (2 , row )
296+ x .databasemonth = readuint8header (3 , row )
297+ x .databaseday = readuint8header (4 , row )
298+
299+ x .ipv4databasecount = readuint32header (5 , row )
300+ x .ipv4databaseaddr = readuint32header (9 , row )
301+ x .ipv6databasecount = readuint32header (13 , row )
302+ x .ipv6databaseaddr = readuint32header (17 , row )
303+ x .ipv4indexbaseaddr = readuint32header (21 , row )
304+ x .ipv6indexbaseaddr = readuint32header (25 , row )
305+ x .productcode = readuint8header (29 , row )
306+ x .producttype = readuint8header (30 , row )
307+ x .filesize = readuint32header (31 , row )
264308
265309 -- check if is correct BIN (should be 1 for IP2Location BIN file), also checking for zipped file (PK being the first 2 chars)
266310 if (x .productcode ~= 1 and x .databaseyear >= 21 ) or (x .databasetype == 80 and x .databasecolumn == 75 ) then -- only BINs from Jan 2021 onwards have this byte set
267311 error (invalid_bin )
268312 end
269313
314+ if x .ipv4indexbaseaddr > 0 then
315+ x .ipv4indexed = true
316+ end
317+
318+ if x .ipv6databasecount > 0 and x .ipv6indexbaseaddr > 0 then
319+ x .ipv6indexed = true
320+ end
321+
270322 x .ipv4columnsize = x .databasecolumn * 4 -- 4 bytes each column
271323 x .ipv6columnsize = 16 + ((x .databasecolumn - 1 ) * 4 ) -- 4 bytes each column, except IPFrom column which is 16 bytes
272- x .columnsize_without_ip = (x .databasecolumn - 1 ) * 4 -- 4 bytes each column, minus the IPFrom column
273324
274325 local dbt = x .databasetype
275326
@@ -387,7 +438,7 @@ function ip2location:checkip(ip)
387438 end
388439
389440 local ipindex = 0 ;
390- if self .ipv4indexbaseaddr > 0 then
441+ if self .ipv4indexed then
391442 ipindex = ((ipnum >> 16 ) << 3 ):asnumber () + self .ipv4indexbaseaddr
392443 end
393444 return R .IPV4 , ipnum , ipindex
@@ -405,7 +456,7 @@ function ip2location:checkip(ip)
405456 end
406457
407458 local ipindex = 0 ;
408- if self .ipv4indexbaseaddr > 0 then
459+ if self .ipv4indexed then
409460 ipindex = ((ipnum >> 16 ) << 3 ):asnumber () + self .ipv4indexbaseaddr
410461 end
411462 return R .IPV4 , ipnum , ipindex
@@ -470,12 +521,12 @@ function ip2location:checkip(ip)
470521
471522 local ipindex = 0 ;
472523 if override == 1 then
473- if self .ipv4indexbaseaddr > 0 then
524+ if self .ipv4indexed then
474525 ipindex = ((ipnum >> 16 ) << 3 ):asnumber () + self .ipv4indexbaseaddr
475526 end
476527 return R .IPV4 , ipnum , ipindex
477528 else
478- if self .ipv6indexbaseaddr > 0 then
529+ if self .ipv6indexed then
479530 ipindex = ((ipnum >> 112 ) << 3 ):asnumber () + self .ipv6indexbaseaddr
480531 end
481532 return R .IPV6 , ipnum , ipindex
@@ -555,7 +606,10 @@ function ip2location:query(ipaddress, mode)
555606 local ipfrom = bn .ZERO
556607 local ipto = bn .ZERO
557608 local maxip = bn .ZERO
558- local firstcol = 4
609+ local firstcol = 4 -- 4 bytes for IP From
610+ local fullrow
611+ local row
612+ local readlen = 0
559613
560614 -- printme(self)
561615
@@ -565,6 +619,7 @@ function ip2location:query(ipaddress, mode)
565619 maxip = max_ipv4_range
566620 colsize = self .ipv4columnsize
567621 else
622+ firstcol = 16 -- 16 bytes for IP From
568623 baseaddr = self .ipv6databaseaddr
569624 high = self .ipv6databasecount
570625 maxip = max_ipv6_range
@@ -573,8 +628,9 @@ function ip2location:query(ipaddress, mode)
573628
574629 -- reading index
575630 if ipindex > 0 then
576- low = readuint32 (ipindex , self .f ):asnumber ()
577- high = readuint32 (ipindex + 4 , self .f ):asnumber ()
631+ row = readrow (self .f , ipindex , 8 ) -- 4 bytes for each IP From/To
632+ low = readuint32row (0 , row ):asnumber ()
633+ high = readuint32row (4 , row ):asnumber ()
578634 end
579635
580636 if ipno >= maxip then
@@ -584,23 +640,22 @@ function ip2location:query(ipaddress, mode)
584640 while low <= high do
585641 mid = round ((low + high ) / 2 )
586642 rowoffset = baseaddr + (mid * colsize )
587- rowoffset2 = rowoffset + colsize
643+
644+ -- reading IP From + whole row + next IP From
645+ readlen = colsize + firstcol
646+ fullrow = readrow (self .f , rowoffset , readlen )
588647
589648 if iptype == 4 then
590- ipfrom = readuint32 ( rowoffset , self . f )
591- ipto = readuint32 ( rowoffset2 , self . f )
649+ ipfrom = readuint32row ( 0 , fullrow )
650+ ipto = readuint32row ( colsize , fullrow )
592651 else
593- ipfrom = readuint128 ( rowoffset , self . f )
594- ipto = readuint128 ( rowoffset2 , self . f )
652+ ipfrom = readuint128row ( 0 , fullrow )
653+ ipto = readuint128row ( colsize , fullrow )
595654 end
596655
597656 if (ipno >= ipfrom ) and (ipno < ipto ) then
598- if iptype == 6 then
599- firstcol = 16
600- end
601-
602- self .f :seek (" set" , rowoffset + firstcol - 1 )
603- local row = self .f :read (self .columnsize_without_ip )
657+ rowlen = colsize - firstcol
658+ row = string.sub (fullrow , firstcol + 1 , (firstcol + rowlen + 1 )) -- extract the actual row data
604659
605660 if (mode &modes .countryshort == 1 ) and (self .country_enabled == true ) then
606661 result .country_short = readstr (readuint32row (self .country_position_offset , row ):asnumber (), self .f )
0 commit comments