From b124cb3a7eef08b39bf91611f7f981d15ece1b7d Mon Sep 17 00:00:00 2001 From: Philip Tellis Date: Tue, 17 Oct 2023 14:36:29 -0400 Subject: [PATCH] Handle unassigned blocks. Issue #254 --- README.md | 2 +- lib/geoip.js | 44 +++++++++++++++------------ package.json | 2 +- scripts/updatedb.js | 4 +-- test/tests.js | 73 ++++++++++++++++++++++++++++++--------------- 5 files changed, 78 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index d3744d74..f689c109 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ installation Run `cd node_modules/geoip-lite && npm run-script updatedb license_key=YOUR_LICENSE_KEY` to update the data files. (Replace `YOUR_LICENSE_KEY` with your license key obtained from [maxmind.com](https://support.maxmind.com/hc/en-us/articles/4407111582235-Generate-a-License-Key)) -You can create maxmind account [here](https://www.maxmind.com/en/geolite2/signup) +You can create a maxmind account [here](https://www.maxmind.com/en/geolite2/signup) **NOTE** that this requires a lot of RAM. It is known to fail on on a Digital Ocean or AWS micro instance. There are no plans to change this. `geoip-lite` stores all data in RAM in order to be fast. diff --git a/lib/geoip.js b/lib/geoip.js index b52ae0c2..26c66579 100644 --- a/lib/geoip.js +++ b/lib/geoip.js @@ -77,7 +77,7 @@ function lookup4(ip) { eu:'', timezone:'', city: '', - ll: [0, 0] + ll: [null, null] }; // outside IPv4 range @@ -105,15 +105,18 @@ function lookup4(ip) { } else { locId = buffer.readUInt32BE((line * recordSize) + 8); - geodata.country = locBuffer.toString('utf8', (locId * locRecordSize) + 0, (locId * locRecordSize) + 2).replace(/\u0000.*/, ''); - geodata.region = locBuffer.toString('utf8', (locId * locRecordSize) + 2, (locId * locRecordSize) + 5).replace(/\u0000.*/, ''); - geodata.metro = locBuffer.readInt32BE((locId * locRecordSize) + 5); - geodata.ll[0] = buffer.readInt32BE((line * recordSize) + 12)/10000;//latitude - geodata.ll[1] = buffer.readInt32BE((line * recordSize) + 16)/10000; //longitude - geodata.area = buffer.readUInt32BE((line * recordSize) + 20); //longitude - geodata.eu = locBuffer.toString('utf8', (locId * locRecordSize) + 9, (locId * locRecordSize) + 10).replace(/\u0000.*/, ''); - geodata.timezone = locBuffer.toString('utf8', (locId * locRecordSize) + 10, (locId * locRecordSize) + 42).replace(/\u0000.*/, ''); - geodata.city = locBuffer.toString('utf8', (locId * locRecordSize) + 42, (locId * locRecordSize) + locRecordSize).replace(/\u0000.*/, ''); + // -1>>>0 is a marker for "No Location Info" + if(-1>>>0 > locId) { + geodata.country = locBuffer.toString('utf8', (locId * locRecordSize) + 0, (locId * locRecordSize) + 2).replace(/\u0000.*/, ''); + geodata.region = locBuffer.toString('utf8', (locId * locRecordSize) + 2, (locId * locRecordSize) + 5).replace(/\u0000.*/, ''); + geodata.metro = locBuffer.readInt32BE((locId * locRecordSize) + 5); + geodata.ll[0] = buffer.readInt32BE((line * recordSize) + 12)/10000;//latitude + geodata.ll[1] = buffer.readInt32BE((line * recordSize) + 16)/10000; //longitude + geodata.area = buffer.readUInt32BE((line * recordSize) + 20); //longitude + geodata.eu = locBuffer.toString('utf8', (locId * locRecordSize) + 9, (locId * locRecordSize) + 10).replace(/\u0000.*/, ''); + geodata.timezone = locBuffer.toString('utf8', (locId * locRecordSize) + 10, (locId * locRecordSize) + 42).replace(/\u0000.*/, ''); + geodata.city = locBuffer.toString('utf8', (locId * locRecordSize) + 42, (locId * locRecordSize) + locRecordSize).replace(/\u0000.*/, ''); + } } return geodata; @@ -182,15 +185,18 @@ function lookup6(ip) { } else { locId = buffer.readUInt32BE((line * recordSize) + 32); - geodata.country = locBuffer.toString('utf8', (locId * locRecordSize) + 0, (locId * locRecordSize) + 2).replace(/\u0000.*/, ''); - geodata.region = locBuffer.toString('utf8', (locId * locRecordSize) + 2, (locId * locRecordSize) + 5).replace(/\u0000.*/, ''); - geodata.metro = locBuffer.readInt32BE((locId * locRecordSize) + 5); - geodata.ll[0] = buffer.readInt32BE((line * recordSize) + 36)/10000;//latitude - geodata.ll[1] = buffer.readInt32BE((line * recordSize) + 40)/10000; //longitude - geodata.area = buffer.readUInt32BE((line * recordSize) + 44); //area - geodata.eu = locBuffer.toString('utf8', (locId * locRecordSize) + 9, (locId * locRecordSize) + 10).replace(/\u0000.*/, ''); - geodata.timezone = locBuffer.toString('utf8', (locId * locRecordSize) + 10, (locId * locRecordSize) + 42).replace(/\u0000.*/, ''); - geodata.city = locBuffer.toString('utf8', (locId * locRecordSize) + 42, (locId * locRecordSize) + locRecordSize).replace(/\u0000.*/, ''); + // -1>>>0 is a marker for "No Location Info" + if(-1>>>0 > locId) { + geodata.country = locBuffer.toString('utf8', (locId * locRecordSize) + 0, (locId * locRecordSize) + 2).replace(/\u0000.*/, ''); + geodata.region = locBuffer.toString('utf8', (locId * locRecordSize) + 2, (locId * locRecordSize) + 5).replace(/\u0000.*/, ''); + geodata.metro = locBuffer.readInt32BE((locId * locRecordSize) + 5); + geodata.ll[0] = buffer.readInt32BE((line * recordSize) + 36)/10000;//latitude + geodata.ll[1] = buffer.readInt32BE((line * recordSize) + 40)/10000; //longitude + geodata.area = buffer.readUInt32BE((line * recordSize) + 44); //area + geodata.eu = locBuffer.toString('utf8', (locId * locRecordSize) + 9, (locId * locRecordSize) + 10).replace(/\u0000.*/, ''); + geodata.timezone = locBuffer.toString('utf8', (locId * locRecordSize) + 10, (locId * locRecordSize) + 42).replace(/\u0000.*/, ''); + geodata.city = locBuffer.toString('utf8', (locId * locRecordSize) + 42, (locId * locRecordSize) + locRecordSize).replace(/\u0000.*/, ''); + } } // We do not currently have detailed region/city info for IPv6, but finally have coords return geodata; diff --git a/package.json b/package.json index 4a4821e9..4f173542 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name" : "geoip-lite", - "version" : "1.4.7", + "version" : "1.4.8", "description" : "A light weight native JavaScript implementation of GeoIP API from MaxMind", "keywords" : ["geo", "geoip", "ip", "ipv4", "ipv6", "geolookup", "maxmind", "geolite"], "homepage" : "https://github.com/geoip-lite/node-geoip", diff --git a/scripts/updatedb.js b/scripts/updatedb.js index 8e59ac1f..aab49be7 100644 --- a/scripts/updatedb.js +++ b/scripts/updatedb.js @@ -46,7 +46,7 @@ if (typeof geodatadir !== 'undefined') { } var tmpPath = process.env.GEOTMPDIR ? process.env.GEOTMPDIR : path.resolve(__dirname, '..', 'tmp'); var countryLookup = {}; -var cityLookup = {}; +var cityLookup = {NaN: -1}; var databases = [ { type: 'country', @@ -538,7 +538,7 @@ function processCityDataNames(src, dest, cb) { var sz = 88; var fields = CSVtoArray(line); if (!fields) { - //lot's of cities contain ` or ' in the name and can't be parsed correctly with current method + //lots of cities contain ` or ' in the name and can't be parsed correctly with current method console.log("weird line: %s::", line); return; } diff --git a/test/tests.js b/test/tests.js index a1a7262f..a8736ed0 100644 --- a/test/tests.js +++ b/test/tests.js @@ -26,21 +26,21 @@ module.exports = { var actual = geoip.lookup(ip); test.notStrictEqual(actual.range, undefined, 'should contain IPv4 range'); - + test.strictEqual(actual.country, 'US', "should match country"); - + test.strictEqual(actual.region, 'NY', "should match region"); - + test.strictEqual(actual.eu, '0', "should match eu"); - + test.strictEqual(actual.timezone, 'America/New_York', "should match timezone"); - + test.strictEqual(actual.city, 'New York', "should match city"); - + test.ok(actual.ll, 'should contain coordinates'); - + test.strictEqual(actual.metro, 501, "should match metro"); - + test.strictEqual(actual.area, 1, "should match area"); test.done(); @@ -54,21 +54,21 @@ module.exports = { var actual = geoip.lookup(ipv6); test.notStrictEqual(actual.range, undefined, 'should contain IPv6 range'); - + test.strictEqual(actual.country, 'NL', "should match country"); - + test.strictEqual(actual.region, 'NH', "should match region"); - + test.strictEqual(actual.eu, '1', "should match eu"); - + test.strictEqual(actual.timezone, 'Europe/Amsterdam', "should match timezone"); - + test.strictEqual(actual.city, 'Amsterdam', "should match city"); - + test.ok(actual.ll, 'should contain coordinates'); - + test.strictEqual(actual.metro, 0, "should match metro"); - + test.strictEqual(actual.area, 5, "should match area"); test.done(); @@ -115,22 +115,22 @@ module.exports = { //get original data var before4 = geoip.lookup("75.82.117.180"); test.notEqual(before4, null); - + var before6 = geoip.lookup("::ffff:173.185.182.82"); test.notEqual(before6, null); - + //clear data; geoip.clear(); - + //make sure data is cleared var none4 = geoip.lookup("75.82.117.180"); test.equal(none4, null); var none6 = geoip.lookup("::ffff:173.185.182.82"); test.equal(none6, null); - + //reload data synchronized geoip.reloadDataSync(); - + //make sure we have value from before var after4 = geoip.lookup("75.82.117.180"); test.deepEqual(before4, after4); @@ -148,16 +148,16 @@ module.exports = { test.notEqual(before4, null); var before6 = geoip.lookup("::ffff:173.185.182.82"); test.notEqual(before6, null); - + //clear data; geoip.clear(); - + //make sure data is cleared var none4 = geoip.lookup("75.82.117.180"); test.equal(none4, null); var none6 = geoip.lookup("::ffff:173.185.182.82"); test.equal(none6, null); - + //reload data asynchronously geoip.reloadData(function(){ //make sure we have value from before @@ -168,5 +168,30 @@ module.exports = { test.done(); }); + }, + + testUnassigned: function (test) { + test.expect(8); + + var ip = '1.1.1.1'; + + var actual = geoip.lookup(ip); + + test.notStrictEqual(actual.range, undefined, 'should contain IPv4 range'); + + test.strictEqual(actual.country, '', "should match empty country"); + + test.strictEqual(actual.region, '', "should match empty region"); + + test.strictEqual(actual.eu, '', "should match empty eu"); + + test.strictEqual(actual.timezone, '', "should match empty timezone"); + + test.strictEqual(actual.city, '', "should match empty city"); + + test.strictEqual(actual.ll[0], null, 'should contain empty coordinates'); + test.strictEqual(actual.ll[1], null, 'should contain empty coordinates'); + + test.done(); } };