UNPKG

@lxe/maxmind-db-reader

Version:

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).

157 lines (130 loc) 5.07 kB
'use strict'; var DATA_SECTION_SEPARATOR_SIZE = 16, METADATA_START_MARKER = new Buffer('ABCDEF4D61784D696E642E636F6D', 'hex'), fs = require('fs'), Metadata = require('./Metadata.js'), Decoder = require('./Decoder.js'), IPParser = require('./IPParser.js'); module.exports = Reader; function Reader() { } Reader.open = function openAsync(database, callback) { var reader = new Reader(); var start, metadataDecoder; fs.readFile(database, function db(err, data) { if (err) { return (callback && callback(err)); } reader.fileHandle = data; start = reader.findMetadataStart(reader.fileHandle); metadataDecoder = new Decoder(reader.fileHandle, 0); metadataDecoder.decode(start, function decoded(err, metadata) { if (err) { return (callback && callback(err, null)); } reader.metadata = new Metadata(metadata[0]); reader.decoder = new Decoder(reader.fileHandle, reader.metadata.getSearchTreeSize() + DATA_SECTION_SEPARATOR_SIZE); callback && callback(null, reader); }); }); }; Reader.openSync = function openSync(database) { var reader = new Reader(); var start, metadataDecoder, metadataArray; reader.fileHandle = fs.readFileSync(database); 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; }; Reader.prototype.findMetadataStart = function findMetadataStart(file) { var found = 0, mlen = METADATA_START_MARKER.length - 1, fsize = file.length - 1 ; while (found <= mlen && fsize-- > 0) { found += (file[fsize] === METADATA_START_MARKER[mlen - found]) ? 1 : -found; } return fsize + found; }; Reader.prototype.getSync = function getSync(ipAddress) { var pointer = this.findAddressInTree(ipAddress); return (pointer === 0) ? null : this.resolveDataPointerSync(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); } }; Reader.prototype.findAddressInTree = function findAddressInTree(ipAddress) { var bit, tempBit, record, rawAddress = IPParser(ipAddress), countRaw = rawAddress.length, isIp4AddressInIp6Db = (countRaw === 4 && this.metadata.getIpVersion() === 6), ipStartBit = isIp4AddressInIp6Db ? 96 : 0, nodeNum = 0, i = 0, len = countRaw * 8 + ipStartBit ; for (i; i < len; i++) { bit = 0; if (i >= ipStartBit) { tempBit = 0xFF & rawAddress[parseInt((i - ipStartBit) / 8, 10)]; bit = 1 & (tempBit >> 7 - (i % 8)); } record = this.readNode(nodeNum, bit); if (record === this.metadata.getNodeCount()) { return 0; } if (record > this.metadata.getNodeCount()) { return record; } nodeNum = record; } return null; }; Reader.prototype.readNode = function readNode(nodeNumber, index) { var bytes, middle, buffer = new Buffer(4), baseOffset = nodeNumber * this.metadata.getNodeByteSize() ; buffer.fill(0); switch (this.metadata.getRecordSize()) { case 24: bytes = baseOffset + index * 3; this.fileHandle.copy(buffer, 1, bytes, bytes + 3); return buffer.readUInt32BE(0, true); case 28: middle = this.fileHandle.readUInt8(baseOffset + 3, true); middle = (index === 0) ? (0xF0 & middle) >> 4 : 0x0F & middle; bytes = baseOffset + index * 4; this.fileHandle.copy(buffer, 1, bytes, bytes + 3); buffer.writeUInt8(middle, 0); return buffer.readUInt32BE(0, true); case 32: return this.fileHandle.readUInt32BE(baseOffset + index * 4, true); default: throw new Error("MaxmindDBReader: Unknown Recordsize in DB"); } }; Reader.prototype.resolveDataPointerSync = function resolveDataPointerSync(pointer) { var resolved = pointer - this.metadata.getNodeCount() + this.metadata.getSearchTreeSize(); 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; };