maxmind-db-reader
Advanced tools
Comparing version 0.0.2 to 0.1.1
35
index.js
@@ -6,12 +6,31 @@ 'use strict'; | ||
MaxmindDBReader = module.exports = function (database) { | ||
if (database === undefined) { | ||
throw new Error('MaxmindDBReader: No Database given'); | ||
} | ||
MaxmindDBReader = module.exports = function () { | ||
// allow creation without 'new' keyword | ||
if (!(this instanceof MaxmindDBReader)) | ||
return new MaxmindDBReader(); | ||
}; | ||
this.reader = new Reader(database); | ||
MaxmindDBReader.open = function(database,callback){ | ||
Reader.open(database,function(err, reader){ | ||
if(err){ | ||
return callback(err); | ||
} | ||
var mmdbreader = MaxmindDBReader(); | ||
mmdbreader.reader = reader; | ||
callback(null,mmdbreader); | ||
}); | ||
} | ||
MaxmindDBReader.openSync = function(database){ | ||
var mmdbreader = MaxmindDBReader(); | ||
mmdbreader.reader = Reader.openSync(database); | ||
return mmdbreader; | ||
} | ||
MaxmindDBReader.prototype.getGeoData = function getGeoData(ipAddress,callback) { | ||
this.reader.get(ipAddress,callback); | ||
}; | ||
MaxmindDBReader.prototype.getGeoData = function getGeoData(ipAddress) { | ||
return this.reader.get(ipAddress); | ||
MaxmindDBReader.prototype.getGeoDataSync = function getGeoDataSync(ipAddress) { | ||
return this.reader.getSync(ipAddress); | ||
}; | ||
@@ -21,2 +40,2 @@ | ||
return this.reader.getMetadata(); | ||
}; | ||
}; |
@@ -30,4 +30,5 @@ 'use strict'; | ||
Decoder.prototype.decode = function decode(offset) { | ||
Decoder.prototype.decode = function decode(offset,callback,nexttick) { | ||
var tmp, | ||
that = this, | ||
ctrlByte = this.fileStream[offset++], | ||
@@ -39,3 +40,7 @@ type = this.types[ctrlByte >> 5] | ||
tmp = this.decodePointer(ctrlByte, offset); | ||
return [this.decode(tmp[0])[0], tmp[1]]; | ||
this.decode(tmp[0],function(err,data){ | ||
if(err) return callback(err); | ||
callback(null, [data[0], tmp[1]]); | ||
},nexttick); | ||
return; | ||
} | ||
@@ -47,2 +52,67 @@ | ||
if (tmp < 8) { | ||
return callback(new Error('MaxmindDBReader: Invalid Extended Type at offset:' + offset)); | ||
} | ||
type = this.types[tmp]; | ||
offset++; | ||
} | ||
tmp = this.sizeFromCtrlByte(ctrlByte, offset); | ||
if(nexttick === false) | ||
this.decodeByType(type, tmp[1], tmp[0],callback); | ||
else | ||
process.nextTick(function(){ | ||
that.decodeByType(type, tmp[1], tmp[0],callback); | ||
}); | ||
}; | ||
Decoder.prototype.decodeByType = function decodeByType(type, offset, size,callback) { | ||
var newOffset = offset + size, | ||
bytes = this.read(offset, size) | ||
; | ||
switch (type) { | ||
case 'map': | ||
return this.decodeMap(size, offset,callback); | ||
case 'array': | ||
return this.decodeArray(size, offset,callback); | ||
case 'boolean': | ||
return callback(null,[this.decodeBoolean(size), offset]); | ||
case 'utf8_string': | ||
return callback(null,[this.decodeString(bytes), newOffset]); | ||
case 'double': | ||
return callback(null,[this.decodeDouble(bytes), newOffset]); | ||
case 'float': | ||
return callback(null,[this.decodeFloat(bytes), newOffset]); | ||
case 'bytes': | ||
return callback(null,[bytes, newOffset]); | ||
case 'uint16': | ||
return callback(null,[this.decodeUint16(bytes), newOffset]); | ||
case 'uint32': | ||
return callback(null,[this.decodeUint32(bytes), newOffset]); | ||
case 'int32': | ||
return callback(null,[this.decodeInt32(bytes), newOffset]); | ||
case 'uint64': | ||
return callback(null,[this.decodeUint64(bytes), newOffset]); | ||
case 'uint128': | ||
return callback(null,[this.decodeUint128(bytes), newOffset]); | ||
} | ||
callback(new Error("MaxmindDBReader: Unknown or unexpected type: " + type + ' at offset:' + offset)); | ||
}; | ||
Decoder.prototype.decodeSync = function decodeSync(offset) { | ||
var tmp, | ||
ctrlByte = this.fileStream[offset++], | ||
type = this.types[ctrlByte >> 5] | ||
; | ||
if (type === 'pointer') { | ||
tmp = this.decodePointer(ctrlByte, offset); | ||
return [this.decodeSync(tmp[0])[0], tmp[1]]; | ||
} | ||
if (type === 'extended') { | ||
tmp = this.fileStream[offset] + 7; | ||
if (tmp < 8) { | ||
throw new Error('MaxmindDBReader: Invalid Extended Type at offset:' + offset); | ||
@@ -57,6 +127,6 @@ } | ||
return this.decodeByType(type, tmp[1], tmp[0]); | ||
return this.decodeByTypeSync(type, tmp[1], tmp[0]); | ||
}; | ||
Decoder.prototype.decodeByType = function decodeByType(type, offset, size) { | ||
Decoder.prototype.decodeByTypeSync = function decodeByTypeSync(type, offset, size) { | ||
var newOffset = offset + size, | ||
@@ -67,4 +137,4 @@ bytes = this.read(offset, size) | ||
switch (type) { | ||
case 'map': return this.decodeMap(size, offset); | ||
case 'array': return this.decodeArray(size, offset); | ||
case 'map': return this.decodeMapSync(size, offset); | ||
case 'array': return this.decodeArraySync(size, offset); | ||
case 'boolean': return [this.decodeBoolean(size), offset]; | ||
@@ -136,3 +206,26 @@ case 'utf8_string': return [this.decodeString(bytes), newOffset]; | ||
Decoder.prototype.decodeArray = function decodeArray(size, offset) { | ||
Decoder.prototype.decodeArray = function decodeArray(size, offset,callback) { | ||
var that = this; | ||
process.nextTick(function(){ | ||
var tmp, | ||
i = 1, | ||
array = [], | ||
cb = function(err,tmp){ | ||
if(err){ | ||
return callback(err); | ||
} | ||
offset = tmp[1]; | ||
array.push(tmp[0]); | ||
if(i++ < size){ | ||
that.decode(offset,cb,i%20===0); | ||
}else{ | ||
callback(null, [array,offset]); | ||
} | ||
}; | ||
if(size === 0) callback(null,[[],offset]); | ||
that.decode(offset,cb,false); | ||
}); | ||
}; | ||
Decoder.prototype.decodeArraySync = function decodeArraySync(size, offset) { | ||
var tmp, | ||
@@ -144,3 +237,3 @@ i = 0, | ||
for(i; i < size; i++) { | ||
tmp = this.decode(offset); | ||
tmp = this.decodeSync(offset); | ||
offset = tmp[1]; | ||
@@ -165,3 +258,30 @@ array.push(tmp[0]); | ||
Decoder.prototype.decodeMap = function decodeMap(size, offset) { | ||
Decoder.prototype.decodeMap = function decodeMap(size, offset,callback) { | ||
var that = this; | ||
process.nextTick(function(){ | ||
var tmp, key, | ||
map = {}, | ||
i = 1, | ||
cb = function(err,tmp){ | ||
if(err){ | ||
return callback(err); | ||
} | ||
map[key] = tmp[0]; | ||
offset = tmp[1]; | ||
if(i++ < size){ | ||
tmp = that.decodeSync(offset); | ||
key = tmp[0].toString(); | ||
that.decode(tmp[1],cb,i%20===0); | ||
}else{ | ||
callback(null, [map,offset]); | ||
} | ||
}; | ||
if(size === 0) return callback(null,[{},offset]); | ||
tmp = that.decodeSync(offset); | ||
key = tmp[0].toString(); | ||
that.decode(tmp[1],cb,false); | ||
}); | ||
}; | ||
Decoder.prototype.decodeMapSync = function decodeMapSync(size, offset) { | ||
var tmp, key, | ||
@@ -173,5 +293,5 @@ map = {}, | ||
for (i; i < size; i++) { | ||
tmp = this.decode(offset); | ||
tmp = this.decodeSync(offset); | ||
key = tmp[0].toString(); | ||
tmp = this.decode(tmp[1]); | ||
tmp = this.decodeSync(tmp[1]); | ||
offset = tmp[1]; | ||
@@ -178,0 +298,0 @@ map[key] = tmp[0]; |
@@ -8,3 +8,3 @@ 'use strict'; | ||
this.binaryFormatMinorVersion = metadata.binary_format_minor_version; | ||
this.buildEpoch = metadata.build_epoch; | ||
this.buildEpoch = new Date(metadata.build_epoch*1000); | ||
this.databaseType = metadata.database_type; | ||
@@ -11,0 +11,0 @@ this.languages = metadata.languages; |
@@ -8,15 +8,38 @@ 'use strict'; | ||
Decoder = require('./Decoder.js'), | ||
IPParser = require('./IPParser.js'), | ||
Reader; | ||
Reader = module.exports = function (database) { | ||
Reader = module.exports = function () {}; | ||
Reader.open = function(database, callback){ | ||
var reader = new Reader(); | ||
var start, metadataDecoder, metadataArray; | ||
fs.readFile(database,function(err, data){ | ||
if(err){ | ||
callback && callback(err); | ||
return; | ||
} | ||
reader.fileHandle =data; | ||
start = reader.findMetadataStart(reader.fileHandle); | ||
metadataDecoder = new Decoder(reader.fileHandle, 0); | ||
metadataDecoder.decode(start,function(err,metadata){ | ||
reader.metadata = new Metadata(metadata[0]); | ||
reader.decoder = new Decoder(reader.fileHandle, reader.metadata.getSearchTreeSize() + DATA_SECTION_SEPARATOR_SIZE); | ||
callback && callback(null,reader); | ||
}); | ||
}); | ||
} | ||
this.fileHandle = fs.readFileSync(database); | ||
Reader.openSync = function (database) { | ||
var reader = new Reader(); | ||
var start, metadataDecoder, metadataArray; | ||
start = this.findMetadataStart(this.fileHandle); | ||
metadataDecoder = new Decoder(this.fileHandle, 0); | ||
metadataArray = metadataDecoder.decode(start); | ||
reader.fileHandle = fs.readFileSync(database); | ||
this.metadata = new Metadata(metadataArray[0]); | ||
this.decoder = new Decoder(this.fileHandle, this.metadata.getSearchTreeSize() + DATA_SECTION_SEPARATOR_SIZE); | ||
start = reader.findMetadataStart(reader.fileHandle); | ||
metadataDecoder = new Decoder(reader.fileHandle, 0); | ||
metadataArray = metadataDecoder.decodeSync(start); | ||
reader.metadata = new Metadata(metadataArray[0]); | ||
reader.decoder = new Decoder(reader.fileHandle, reader.metadata.getSearchTreeSize() + DATA_SECTION_SEPARATOR_SIZE); | ||
return reader; | ||
}; | ||
@@ -29,3 +52,2 @@ | ||
; | ||
while (found <= mlen && fsize--) { | ||
@@ -38,6 +60,16 @@ found += (file[fsize] === METADATA_START_MARKER[mlen - found]) ? 1 : -found; | ||
Reader.prototype.get = function get(ipAddress) { | ||
Reader.prototype.getSync = function getSync(ipAddress) { | ||
var pointer = this.findAddressInTree(ipAddress); | ||
return (pointer === 0) ? null : this.resolveDataPointerSync(pointer); | ||
}; | ||
return (pointer === 0) ? null : this.resolveDataPointer(pointer); | ||
Reader.prototype.get = function get(ipAddress,callback) { | ||
var pointer = this.findAddressInTree(ipAddress); | ||
if(pointer === 0){ | ||
process.nextTick(function(){ | ||
callback(null,null); | ||
}); | ||
}else{ | ||
this.resolveDataPointer(pointer,callback); | ||
}; | ||
}; | ||
@@ -47,3 +79,3 @@ | ||
var bit, tempBit, record, | ||
rawAddress = ipAddress.split('.').map(function(v) { return parseInt(v, 10); }), | ||
rawAddress = IPParser(ipAddress), | ||
countRaw = rawAddress.length, | ||
@@ -78,3 +110,3 @@ isIp4AddressInIp6Db = (countRaw === 4 && this.metadata.getIpVersion() === 6), | ||
throw new Error('MaxmindDBReader: Unable to find IP:' + ipAddress + ' in Database'); | ||
return null; | ||
}; | ||
@@ -103,3 +135,3 @@ | ||
case 32: | ||
return bytes.readUInt32BE(baseOffset + index * 4, true); | ||
return this.fileHandle.readUInt32BE(baseOffset + index * 4, true); | ||
default: | ||
@@ -110,10 +142,19 @@ throw new Error("MaxmindDBReader: Unknown Recordsize in DB"); | ||
Reader.prototype.resolveDataPointer = function resolveDataPointer(pointer) { | ||
Reader.prototype.resolveDataPointerSync = function resolveDataPointerSync(pointer) { | ||
var resolved = pointer - this.metadata.getNodeCount() + this.metadata.getSearchTreeSize(); | ||
return this.decoder.decode(resolved)[0]; | ||
return this.decoder.decodeSync(resolved)[0]; | ||
}; | ||
Reader.prototype.resolveDataPointer = function resolveDataPointer(pointer,callback) { | ||
var resolved = pointer - this.metadata.getNodeCount() + this.metadata.getSearchTreeSize(); | ||
this.decoder.decode(resolved,function(err,data){ | ||
if(err) return callback(err); | ||
callback(null,data[0]); | ||
}); | ||
}; | ||
Reader.prototype.getMetadata = function metadata() { | ||
return this.metadata; | ||
}; |
{ | ||
"name" : "maxmind-db-reader", | ||
"version" : "0.0.2", | ||
"version" : "0.1.1", | ||
"description" : "This is the pure Node API for reading MaxMind DB files. MaxMind DB is a binary file format that stores data indexed by IP address subnets (IPv4 or IPv6).", | ||
"main" : "./index.js", | ||
"contributors": [{ | ||
"name" : "Patrick Klös", | ||
"email" : "pkloes@web.de" | ||
},{ | ||
"name":"Corné 'EaterOfCode' Oppelaar", | ||
"email":"hello@eaterofco.de" | ||
}], | ||
"repository" : { | ||
@@ -10,2 +17,5 @@ "type" : "git", | ||
}, | ||
"bin":{ | ||
"mmdb-geoip":"./repl" | ||
}, | ||
"keywords": [ | ||
@@ -34,2 +44,2 @@ "Maxmind", | ||
} | ||
} | ||
} |
149
README.md
@@ -1,4 +0,149 @@ | ||
node-maxmind-db | ||
=============== | ||
# node-maxmind-db | ||
This is the pure Node API for reading MaxMind DB files. MaxMind DB is a binary file format that stores data indexed by IP address subnets (IPv4 or IPv6). | ||
# WARNING | ||
* `.open(Sync)` needs to be called once before abusing `.getGeoData(Sync)` | ||
* this is version 0.1.0! the API is not compitable with 0.0.* | ||
* Most IP's don't seem to have city data in the GeoLite2 City database. Only american cities are present, if your IP is american but not returning a city try to replace the last number of your IP by a 0 (only with a IPv4 address. for IPv6 address it doesn't seem to work). | ||
# Install | ||
npm i maxmind-db-reader | ||
# Example | ||
## Async | ||
```javascript | ||
// require the db reader | ||
var mmdbreader = require('maxmind-db-reader'); | ||
// open database | ||
mmdbreader.open('./countries.mmdb',function(err,countries){ | ||
// get geodata | ||
countries.getGeoData('128.101.101.101',function(err,geodata){ | ||
// log data :D | ||
console.log(geodata); | ||
}); | ||
}); | ||
``` | ||
## Sync | ||
```javascript | ||
// require the db reader | ||
var mmdbreader = require('maxmind-db-reader'); | ||
// open database | ||
var countries = mmdbreader.openSync('./countries.mmdb'); | ||
// get geodata | ||
var geodata = countries.getGeoDataSync('128.101.101.101'); | ||
// log data :D | ||
console.log(geodata); | ||
``` | ||
## Mixed (Sync init, Async request) | ||
```javascript | ||
// require the db reader | ||
var mmdbreader = require('maxmind-db-reader'); | ||
// open database | ||
var countries = mmdbreader.openSync('./countries.mmdb'); | ||
// get geodata | ||
countries.getGeoData('128.101.101.101',function(err,geodata){ | ||
// log data :D | ||
console.log(geodata); | ||
}); | ||
``` | ||
## Result | ||
```javascript | ||
{ | ||
"city": { | ||
"geoname_id": 5037649, | ||
"names": { | ||
"de": "Minneapolis", | ||
"en": "Minneapolis", | ||
"es": "Mineápolis", | ||
"fr": "Minneapolis", | ||
"ja": "ミネアポリス", | ||
"pt-BR": "Minneapolis", | ||
"ru": "Миннеаполис", | ||
"zh-CN": "明尼阿波利斯" | ||
} | ||
}, | ||
"continent": { | ||
"code": "NA", | ||
"geoname_id": 6255149, | ||
"names": { | ||
"de": "Nordamerika", | ||
"en": "North America", | ||
"es": "Norteamérica", | ||
"fr": "Amérique du Nord", | ||
"ja": "北アメリカ", | ||
"pt-BR": "América do Norte", | ||
"ru": "Северная Америка", | ||
"zh-CN": "北美洲" | ||
} | ||
}, | ||
"country": { | ||
"geoname_id": 6252001, | ||
"iso_code": "US", | ||
"names": { | ||
"de": "USA", | ||
"en": "United States", | ||
"es": "Estados Unidos", | ||
"fr": "États-Unis", | ||
"ja": "アメリカ合衆国", | ||
"pt-BR": "Estados Unidos", | ||
"ru": "США", | ||
"zh-CN": "美国" | ||
} | ||
}, | ||
"location": { | ||
"latitude": 44.9759, | ||
"longitude": -93.2166, | ||
"metro_code": "613", | ||
"time_zone": "America/Chicago" | ||
}, | ||
"postal": { | ||
"code": "55414" | ||
}, | ||
"registered_country": { | ||
"geoname_id": 6252001, | ||
"iso_code": "US", | ||
"names": { | ||
"de": "USA", | ||
"en": "United States", | ||
"es": "Estados Unidos", | ||
"fr": "États-Unis", | ||
"ja": "アメリカ合衆国", | ||
"pt-BR": "Estados Unidos", | ||
"ru": "США", | ||
"zh-CN": "美国" | ||
} | ||
}, | ||
"subdivisions": [ | ||
{ | ||
"geoname_id": 5037779, | ||
"iso_code": "MN", | ||
"names": { | ||
"en": "Minnesota", | ||
"es": "Minnesota", | ||
"ja": "ミネソタ州", | ||
"ru": "Миннесота" | ||
} | ||
} | ||
] | ||
} | ||
``` | ||
# API | ||
`maxmind-db-reader` | ||
* `.open (databasePath, callback(err, maxmind-db-reader)) void` | ||
* `.openSync (databasePath) maxmind-db-reader` | ||
`maxmind-db-reader()` | ||
* `.getGeoData (ip, callback(err,geodata)) void` | ||
* `.getGeoDataSync (ip) geodata` | ||
* `.getDatabaseMetadata() metadata` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
29608507
12
584
150
2