Comparing version 0.6.0 to 1.0.0
15
index.js
@@ -0,4 +1,15 @@ | ||
'use strict'; | ||
var ls = require('./lib/lookup_service.js'); | ||
var Reader = require('./lib/reader'); | ||
var ip = require('./lib/ip'); | ||
var utils = require('./lib/utils'); | ||
module.exports = ls; | ||
exports.open = function(database, opts) { | ||
return new Reader(database, opts); | ||
}; | ||
exports.init = function() { | ||
throw new Error(utils.legacyErrorMessage); | ||
}; | ||
exports.validate = ip.validate; |
105
lib/ip.js
@@ -1,5 +0,8 @@ | ||
var assert = require('assert'); | ||
'use strict'; | ||
var net = require('net'); | ||
var ipaddr = require('ip-address'); | ||
exports.v4ToLong = function(ip) { | ||
var parseIPv4 = function(ip) { | ||
ip = ip.split('.', 4); | ||
@@ -12,66 +15,68 @@ | ||
// assert(o0 > 0, 'Invalid IP address') | ||
// assert(o1 > 0, 'Invalid IP address') | ||
// assert(o2 > 0, 'Invalid IP address') | ||
// assert(o3 > 0, 'Invalid IP address') | ||
return [o0, o1, o2, o3]; | ||
}; | ||
return (o0 * 0x1000000) + (o1 * 0x10000) + (o2 * 0x100) + o3; | ||
var parseIPv6 = function(ip) { | ||
var v6Address = new ipaddr.Address6(ip); | ||
if (!v6Address.isValid()) { | ||
throw new Error('Invalid IPv6 address ' + ip); | ||
} | ||
return ipv6Buffer(v6Address.parsedAddress); | ||
}; | ||
exports.v6ToArray = function(ip) { | ||
// var a = Uint16Array(16); | ||
var a = Array.apply(null, new Array(16)).map(Number.prototype.valueOf, 0); | ||
function ipv6Buffer(groups) { | ||
var arr = new Buffer(16); | ||
arr.fill(0); | ||
groups.forEach(function part(hex, i) { | ||
if (hex === '') return; | ||
if (hex.length < 4) { | ||
hex = repeat('0', 4 - hex.length) + hex; | ||
} | ||
arr.write(hex, i * 2, 'hex'); | ||
}); | ||
return arr; | ||
} | ||
if (ip.indexOf('::') === 0) { | ||
var at = ip.indexOf('::ffff') > -1 ? 7 : 2; | ||
ip = ip.substring(at).split('.', 4); | ||
for (var i = 0; i < ip.length; i++) { | ||
a[16 - (4 - i)] = +ip[i]; | ||
}; | ||
return a; | ||
} | ||
var parts = ip.split('::', 2), | ||
left = parts[0], | ||
right = parts[1]; | ||
function repeat(c, l) { | ||
var str = '', i = 0; | ||
while (i++ < l) str += c; | ||
return str; | ||
} | ||
if (left) { | ||
var parsed = left.split(':'); | ||
for (var i = 0; i < parsed.length; i++) { | ||
var chunk = parseInt(parsed[i], 16); | ||
a[i * 2] = chunk >> 8; | ||
a[i * 2 + 1] = chunk & 0xff; | ||
}; | ||
} | ||
if (right) { | ||
var parsed = right.split(':'); | ||
var pl = parsed.length; | ||
for (var i = 0; i < parsed.length; i++) { | ||
var chunk = parseInt(parsed[i], 16); | ||
a[15 - (pl - i * 2 + 1)] = chunk >> 8; | ||
a[15 - (pl - i * 2)] = chunk & 0xff; | ||
}; | ||
exports.parse = function(ip) { | ||
if (ip.indexOf(':') === -1) { | ||
return parseIPv4(ip); | ||
} | ||
return a; | ||
return parseIPv6(ip); | ||
}; | ||
exports.v4toBinary = function(ip) { | ||
ip = ip.split('.', 4) | ||
exports.bitAt = function(rawAddress, idx) { | ||
// 8 bits per octet in the buffer (>>3 is slightly faster than Math.floor(idx/8)) | ||
var bufIdx = idx >> 3; | ||
var res = []; | ||
var o0 = (+ip[0]).toString(2) | ||
var o1 = (+ip[1]).toString(2) | ||
var o2 = (+ip[2]).toString(2) | ||
var o3 = (+ip[3]).toString(2) | ||
// Offset within the octet (basicallg equivalent to 8 - (idx % 8)) | ||
var bitIdx = 7 ^ (idx & 7); | ||
while (o0.length < 8) o0 = '0' + o0 | ||
while (o1.length < 8) o1 = '0' + o1 | ||
while (o2.length < 8) o2 = '0' + o2 | ||
while (o3.length < 8) o3 = '0' + o3 | ||
// Shift the offset rightwards by bitIdx bits and & it to grab the bit | ||
return (rawAddress[bufIdx] >>> bitIdx) & 1; | ||
}; | ||
return o0 + o1 + o2 + o3; | ||
exports.validate = function(ip) { | ||
var version = net.isIP(ip); | ||
switch (version) { | ||
case 4: | ||
return net.isIPv4(ip); | ||
case 6: | ||
return net.isIPv6(ip); | ||
default: | ||
return false; | ||
} | ||
}; |
{ | ||
"name": "maxmind", | ||
"version": "0.6.0", | ||
"version": "1.0.0", | ||
"homepage": "https://github.com/runk/node-maxmind", | ||
"description": "IP lookup using Maxmind databases", | ||
"keywords": ["maxmind", "geo", "geobase", "geo lookup", "ip base", "geocode", "timezone", "asn"], | ||
"keywords": [ | ||
"maxmind", | ||
"mmdb", | ||
"geo", | ||
"geoip", | ||
"geoip2", | ||
"geobase", | ||
"geo lookup", | ||
"ip base", | ||
"geocode", | ||
"timezone", | ||
"asn" | ||
], | ||
"author": "Dmitry Shirokov <deadrunk@gmail.com>", | ||
@@ -12,25 +24,37 @@ "contributors": [ | ||
], | ||
"dependencies": {}, | ||
"dependencies": { | ||
"big-integer": "^1.6.15", | ||
"lru-cache": "^4.0.1", | ||
"ip-address": "^5.8.0" | ||
}, | ||
"devDependencies": { | ||
"mocha": "latest", | ||
"jshint": "latest", | ||
"istanbul": "latest" | ||
"eslint": "^2.9.0", | ||
"istanbul": "^0.4.3", | ||
"mocha": "^2.4.5" | ||
}, | ||
"repository": { | ||
"type":"git", | ||
"url":"git@github.com:runk/node-maxmind.git" | ||
"type": "git", | ||
"url": "git@github.com:runk/node-maxmind.git" | ||
}, | ||
"bugs": { | ||
"mail":"deadrunk@gmail.com", | ||
"url":"http://github.com/runk/node-maxmind/issues" | ||
"mail": "deadrunk@gmail.com", | ||
"url": "http://github.com/runk/node-maxmind/issues" | ||
}, | ||
"main": "index.js", | ||
"engine": { "node": ">=0.8.0", "npm": "1" }, | ||
"engine": { | ||
"node": ">=0.8.0", | ||
"npm": "1" | ||
}, | ||
"license": "MIT", | ||
"scripts": { | ||
"lint": "jshint ./lib/*.js ./lib/**/*.js ./index.js ./examples/*.js", | ||
"test": "mocha -R spec --recursive", | ||
"coverage": "istanbul cover _mocha -- -R dot --recursive", | ||
"benchmark": "node benchmark" | ||
"benchmark": "node benchmark", | ||
"coverage": "istanbul cover _mocha -- -R spec --timeout 5000 --recursive", | ||
"coverage:check": "istanbul check-coverage", | ||
"lint": "eslint -c .eslintrc .", | ||
"prepublish": "npm run lint", | ||
"release:major": "npm run lint && npm test && npm version major && npm publish && git push --follow-tags", | ||
"release:minor": "npm run lint && npm test && npm version minor && npm publish && git push --follow-tags", | ||
"release:patch": "npm run lint && npm test && npm version patch && npm publish && git push --follow-tags", | ||
"test": "mocha -R spec --recursive --bail" | ||
} | ||
} |
133
README.md
node-maxmind [![Build Status](https://travis-ci.org/runk/node-maxmind.png)](https://travis-ci.org/runk/node-maxmind) | ||
======== | ||
Native Javascript module for IP GEO lookup using Maxmind databases. | ||
Up to [500% faster](https://github.com/runk/node-maxmind#performance--benchmark) than other GEO lookup libraries. | ||
No binary or whatsoever dependencies. | ||
Pure Javascript module for Geo IP lookup using Maxmind binary databases (aka mmdb or geoip2). | ||
Up to [8000% faster](https://github.com/runk/node-maxmind/tree/master/benchmark) than other Geo lookup libraries. Module natively works with binary Maxmind database format and doesn't require any "CSV - {specific lib format}" conversions as some other modules do. Maxmind binary databases are highly optimized for size and performance so there's no point working with other than that format. | ||
## GEO databases | ||
Free GEO databases are available for [download here](http://dev.maxmind.com/geoip/geolite). The npm package [maxmind-geolite-mirror](https://www.npmjs.com/package/maxmind-geolite-mirror) will mirror the databases locally and only re-fetch if the remote files are newer. | ||
Free GEO databases are available for [download here](http://dev.maxmind.com/geoip/geoip2/geolite2/). If you need better accuracy you should consider buying [commercial subscription](https://www.maxmind.com/en/geoip2-databases). | ||
@@ -18,93 +18,45 @@ | ||
## Main features | ||
- Country/Region/Location lookup by IP (v4 and v6) | ||
- Distance between two IP addresses (locations) | ||
- Timezone lookup by IP | ||
- Autonomous System Numbers (ASN) lookup by IP | ||
- Network speed lookup by IP | ||
Module written in pure Javascript with no dependencies. Being able to work with binary Maxmind databases it doesn't | ||
require any "CSV - {specific lib format}" conversions as other modules do. Maxmind binary databases are highly optimized | ||
for size and performance so there's no point working with other than that format. | ||
## Usage | ||
** see code samples in `./examples` directory ** | ||
```javascript | ||
var maxmind = require('maxmind'); | ||
// City/Location lookup | ||
maxmind.init('/path/to/GeoLiteCity.dat'); | ||
var location = maxmind.getLocation('66.6.44.4'); | ||
var cityLookup = maxmind.open('/path/to/GeoLite2-City.mmdb'); | ||
var city = lookup.get('66.6.44.4'); | ||
// Country Lookup | ||
maxmind.init('/path/to/GeoIP.dat'); | ||
var country = maxmind.getCountry('66.6.44.4'); | ||
var orgLookup = maxmind.open('/path/to/GeoOrg.mmdb'); | ||
var organization = orgLookup.get('66.6.44.4'); | ||
``` | ||
// Autonomous System Numbers (ASN) lookup | ||
maxmind.init('/path/to/GeoIPASNum.dat'); | ||
var org = maxmind.getAsn('66.6.44.4'); | ||
// Internet Service Provider (ISP) lookup | ||
maxmind.init('/path/to/GeoISP.dat'); | ||
var org = maxmind.getIsp('66.6.44.4'); | ||
// Net Speed lookup | ||
maxmind.init('/path/to/GeoIPNetSpeedCell.dat'); | ||
var speed = maxmind.getNetSpeed('89.66.148.0'); | ||
// Organization lookup | ||
maxmind.init('/path/to/GeoIPOrg.dat'); | ||
var org = maxmind.getOrganization('66.6.44.4'); | ||
``` | ||
## V6 Support | ||
Module is fully campatible with IPv6 maxmind databases. Make sure you initialize with | ||
proper IPv6 databases before making queries. | ||
Module is fully campatible with IPv6. There are no differences in API between IPv4 and IPv6. | ||
```javascript | ||
maxmind.init('/path/to/GeoLiteCityV6.dat'); | ||
var location = maxmind.getLocationV6('2001:4860:0:1001::3004:ef68'); | ||
var lookup = maxmind.open('/path/to/GeoLite2.mmdb'); | ||
var location = maxmind.get('2001:4860:0:1001::3004:ef68'); | ||
``` | ||
All methods works in the same way as for IPv4, the only difference is `V6` postfix in method names: | ||
`getCountryV6`, `getLocationV6` and `getOrganizationV6`. | ||
You can initialize module with several databases at once, and proper db will be automatically selected | ||
for particular query. If any option is given it applies to all databases you initialize. | ||
```javascript | ||
var maxmind = require('maxmind'); | ||
maxmind.init(['/path/to/GeoLiteCity.dat', '/path/to/GeoIPASNum.dat']); | ||
// now both org and location lookups will work | ||
var org = maxmind.getOrganization('66.6.44.4'); | ||
var location = maxmind.getLocation('66.6.44.4'); | ||
``` | ||
## Options | ||
By default module does not use cache, and works directly with file system. Enabling cache | ||
leads to better performance though consumes more memory. | ||
Right now the only option you can configure is cache. Module uses [lru-cache](https://github.com/isaacs/node-lru-cache). You can configure its settings by doing following: | ||
- `indexCache` saves in memory the country index only | ||
- `memoryCache` saves in memory full database file | ||
- `checkForUpdates` checks databases for updates (via fs mtime). Basically once you replace the old DB file with | ||
the new one module automamtically re-initialises. | ||
Options can be passed to `init` method: | ||
```javascript | ||
var maxmind = require('maxmind'); | ||
maxmind.init('/path/to/GeoIP.dat', {indexCache: true, checkForUpdates: true}); | ||
var lookup = maxmind.open('/path/to/GeoLite2.mmdb', { | ||
cache: { | ||
max: 1000, // max items in cache | ||
maxAge: 1000 * 60 * 60 // life time in milliseconds | ||
} | ||
}) | ||
lookup.get('1.1.1.1'); | ||
``` | ||
## IP addresses validation | ||
Module supports validation for both IPv4 and IPv6 via the same function: | ||
Module supports validation for both IPv4 and IPv6: | ||
``` | ||
```javascript | ||
maxmind.validate('66.6.44.4'); // returns true | ||
@@ -117,43 +69,16 @@ maxmind.validate('66.6.44.boom!'); // returns false | ||
## Performance / Benchmark | ||
Caching significantly increases performance, refer to this camparison which was made on average | ||
laptop: | ||
## GeoIP Legacy binary format | ||
- default: 20,000 lookups / second | ||
- `indexCache`: 115,000 lookups / second | ||
- `memoryCache`: 270,000 lookups / second | ||
In case you want to use legacy GeoIP binary databases you should use [maxmind@0.6](https://github.com/runk/node-maxmind/releases/tag/v0.6.0). | ||
Following benchmark is made for `GeoIPCity` database. Memory caching is enabled where possible. If you believe that | ||
benchmark is not realistic please post a PR and share your code :) | ||
``` | ||
node-maxmind 274649 op/sec | ||
geoip-lite 191681 op/sec 43.28% slower | ||
geoip 43483 op/sec 531.61% slower | ||
``` | ||
## Contributing | ||
Make sure you run `npm i` command in the project's dir before you begin, it'll install all dev dependencies. Currently | ||
code coverage is about **85%**, so new tests are essential when you add new functionality. There're several npm tasks | ||
which you can find useful: | ||
- `npm test` runs tests | ||
- `npm run lint` runs js linter | ||
- `npm run coverage` runs code coverage task and generates report | ||
- `npm run benchmark` runs basic benchmark | ||
One pull request per one feature, nothing unusual. | ||
## References | ||
- Timezones http://www.maxmind.com/timezone.txt | ||
- Region codes http://www.maxmind.com/app/iso3166_2 | ||
- Loosely based on https://github.com/PaddeK/node-maxmind-db | ||
- MaxMind DB file format specification http://maxmind.github.io/MaxMind-DB/ | ||
- MaxMind test/sample DB files https://github.com/maxmind/MaxMind-DB | ||
- GeoLite2 Free Downloadable Databases http://dev.maxmind.com/geoip/geoip2/geolite2/ | ||
## License | ||
MIT |
@@ -0,4 +1,23 @@ | ||
'use strict'; | ||
var assert = require('assert'); | ||
var maxmind = require('../index'); | ||
// Enable highlighted diffs in mocha | ||
assert.AssertionError.prototype.showDiff = true; | ||
describe('index', function() { | ||
describe('validate()', function() { | ||
it('should work fine for both IPv4 and IPv6', function() { | ||
assert.equal(maxmind.validate('64.4.4.4'), true); | ||
assert.equal(maxmind.validate('2001:4860:0:1001::3004:ef68'), true); | ||
assert.equal(maxmind.validate('whhaaaazza'), false); | ||
}); | ||
}); | ||
describe('init()', function() { | ||
it('should fail when someone tries to use legacy api', function() { | ||
assert.throws(function() { | ||
maxmind.init(); | ||
}, /Maxmind v1 module has changed API/); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Copyleft License
License(Experimental) Copyleft license information was found.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
Non-permissive License
License(Experimental) A license not known to be considered permissive was found.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
2013291
73
62563
0
3
3
70
83
3
2
+ Addedbig-integer@^1.6.15
+ Addedip-address@^5.8.0
+ Addedlru-cache@^4.0.1
+ Addedbig-integer@1.6.52(transitive)
+ Addedip-address@5.9.4(transitive)
+ Addedjsbn@1.1.0(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedlru-cache@4.1.5(transitive)
+ Addedpseudomap@1.0.2(transitive)
+ Addedsprintf-js@1.1.2(transitive)
+ Addedyallist@2.1.2(transitive)