geoip-lite2
Advanced tools
Comparing version 2.0.3 to 2.1.0
@@ -1,78 +0,1 @@ | ||
const fs = require('node:fs'), | ||
path = require('node:path'), | ||
FSWatcher = {}; | ||
/** | ||
* Takes a directory/file and watch for change. Upon change, call the | ||
* callback. | ||
* | ||
* @param {String} name: name of this watcher | ||
* @param {String} directory: path to the directory to watch | ||
* @param {String} [filename]: (optional) specific filename to watch for, | ||
* watches for all files in the directory if unspecified | ||
* @param {Integer} cooldownDelay: delay to wait before triggering the callback | ||
* @param {Function} callback: function () : called when changes are detected | ||
**/ | ||
function makeFsWatchFilter(name, directory, filename, cooldownDelay, callback) { | ||
let cooldownId = null; | ||
// Delete the cooldownId and callback the outer function | ||
function timeoutCallback() { | ||
cooldownId = null; | ||
callback(); | ||
} | ||
// This function is called when there is a change in the data directory | ||
// It sets a timer to wait for the change to be completed | ||
function onWatchEvent(event, changedFile) { | ||
// Check to make sure changedFile is not null | ||
if (!changedFile) { | ||
return; | ||
} | ||
const filePath = path.join(directory, changedFile); | ||
if (!filename || filename === changedFile) { | ||
fs.exists(filePath, function onExists(exists) { | ||
if (!exists) return; // If the changed file no longer exists, it was a deletion. We ignore deleted files. | ||
// At this point, a new file system activity has been detected, | ||
// We have to wait for file transfer to be finished before moving on. | ||
// If a cooldownId already exists, we delete it | ||
if (cooldownId !== null) { | ||
clearTimeout(cooldownId); | ||
cooldownId = null; | ||
} | ||
// Once the cooldownDelay has passed, the timeoutCallback function will be called | ||
cooldownId = setTimeout(timeoutCallback, cooldownDelay); | ||
}); | ||
} | ||
} | ||
// Manage the case where filename is missing (because it's optional) | ||
if (typeof cooldownDelay === 'function') { | ||
callback = cooldownDelay; | ||
cooldownDelay = filename; | ||
filename = null; | ||
} | ||
if (FSWatcher[name]) { | ||
stopWatching(name); | ||
} | ||
FSWatcher[name] = fs.watch(directory, onWatchEvent); | ||
} | ||
/** | ||
* Take a FSWatcher object and close it. | ||
* | ||
* @param {string} name: name of the watcher to close | ||
* | ||
**/ | ||
function stopWatching(name) { | ||
FSWatcher[name].close(); | ||
} | ||
module.exports.makeFsWatchFilter = makeFsWatchFilter; | ||
module.exports.stopWatching = stopWatching; | ||
const n=require('node:fs'),t=require('node:path'),e={};function o(n){e[n].close()}module.exports.makeFsWatchFilter=function(u,l,i,c,s){let r=null;function f(){r=null,s()}'function'==typeof c&&(s=c,c=i,i=null),e[u]&&o(u),e[u]=n.watch(l,(function(e,o){if(!o)return;const u=t.join(l,o);i&&i!==o||n.exists(u,(function(n){n&&(null!==r&&(clearTimeout(r),r=null),r=setTimeout(f,c))}))}))},module.exports.stopWatching=o; |
534
lib/geoip.js
@@ -1,533 +0,1 @@ | ||
const fs = require('node:fs'); | ||
const net = require('node:net'); | ||
const path = require('node:path'); | ||
fs.existsSync = fs.existsSync || path.existsSync; | ||
const utils = require('./utils.js'); | ||
const fsWatcher = require('./fsWatcher.js'); | ||
const async = require('async'); | ||
const watcherName = 'dataWatcher'; | ||
const geodatadir = path.resolve( | ||
__dirname, | ||
global.geodatadir || process.env.GEODATADIR || '../data/', | ||
); | ||
const dataFiles = { | ||
city: path.join(geodatadir, 'geoip-city.dat'), | ||
city6: path.join(geodatadir, 'geoip-city6.dat'), | ||
cityNames: path.join(geodatadir, 'geoip-city-names.dat'), | ||
country: path.join(geodatadir, 'geoip-country.dat'), | ||
country6: path.join(geodatadir, 'geoip-country6.dat'), | ||
}; | ||
const privateRange4 = [ | ||
[utils.aton4('10.0.0.0'), utils.aton4('10.255.255.255')], | ||
[utils.aton4('172.16.0.0'), utils.aton4('172.31.255.255')], | ||
[utils.aton4('192.168.0.0'), utils.aton4('192.168.255.255')], | ||
]; | ||
const conf4 = { | ||
firstIP: null, | ||
lastIP: null, | ||
lastLine: 0, | ||
locationBuffer: null, | ||
locationRecordSize: 88, | ||
mainBuffer: null, | ||
recordSize: 24, | ||
}; | ||
const conf6 = { | ||
firstIP: null, | ||
lastIP: null, | ||
lastLine: 0, | ||
mainBuffer: null, | ||
recordSize: 48, | ||
}; | ||
// copy original configs | ||
let cache4 = JSON.parse(JSON.stringify(conf4)); | ||
let cache6 = JSON.parse(JSON.stringify(conf6)); | ||
const RECORD_SIZE = 10; | ||
const RECORD_SIZE6 = 34; | ||
function lookup4(ip) { | ||
let fline = 0; | ||
let floor = cache4.lastIP; | ||
let cline = cache4.lastLine; | ||
let ceil = cache4.firstIP; | ||
let line; | ||
let locId; | ||
const buffer = cache4.mainBuffer; | ||
const locBuffer = cache4.locationBuffer; | ||
const privateRange = privateRange4; | ||
const recordSize = cache4.recordSize; | ||
const locRecordSize = cache4.locationRecordSize; | ||
let i; | ||
const geodata = { | ||
range: '', | ||
country: '', | ||
region: '', | ||
eu:'', | ||
timezone:'', | ||
city: '', | ||
ll: [0, 0], | ||
}; | ||
// outside IPv4 range | ||
if (ip > cache4.lastIP || ip < cache4.firstIP) { | ||
return null; | ||
} | ||
// private IP | ||
for (i = 0; i < privateRange.length; i++) { | ||
if (ip >= privateRange[i][0] && ip <= privateRange[i][1]) { | ||
return null; | ||
} | ||
} | ||
do { | ||
line = Math.round((cline - fline) / 2) + fline; | ||
floor = buffer.readUInt32BE(line * recordSize); | ||
ceil = buffer.readUInt32BE((line * recordSize) + 4); | ||
if (floor <= ip && ceil >= ip) { | ||
geodata.range = [floor, ceil]; | ||
if (recordSize === RECORD_SIZE) { | ||
geodata.country = buffer.toString('utf8', (line * recordSize) + 8, (line * recordSize) + 10); | ||
} else { | ||
locId = buffer.readUInt32BE((line * recordSize) + 8); | ||
geodata.country = locBuffer.toString('utf8', (locId * locRecordSize), (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; | ||
} else if (fline === cline) { | ||
return null; | ||
} else if (fline === (cline - 1)) { | ||
if (line === fline) { | ||
fline = cline; | ||
} else { | ||
cline = fline; | ||
} | ||
} else if (floor > ip) { | ||
cline = line; | ||
} else if (ceil < ip) { | ||
fline = line; | ||
} | ||
} while (1); | ||
} | ||
function lookup6(ip) { | ||
const buffer = cache6.mainBuffer; | ||
const recordSize = cache6.recordSize; | ||
const locBuffer = cache4.locationBuffer; | ||
const locRecordSize = cache4.locationRecordSize; | ||
const geodata = { | ||
range: '', | ||
country: '', | ||
region: '', | ||
city: '', | ||
ll: [0, 0], | ||
}; | ||
function readip(line, offset) { | ||
let ii = 0; | ||
const ip = []; | ||
for (ii = 0; ii < 2; ii++) { | ||
ip.push(buffer.readUInt32BE((line * recordSize) + (offset * 16) + (ii * 4))); | ||
} | ||
return ip; | ||
} | ||
cache6.lastIP = readip(cache6.lastLine, 1); | ||
cache6.firstIP = readip(0, 0); | ||
let fline = 0; | ||
let floor = cache6.lastIP; | ||
let cline = cache6.lastLine; | ||
let ceil = cache6.firstIP; | ||
let line; | ||
let locId; | ||
if (utils.cmp6(ip, cache6.lastIP) > 0 || utils.cmp6(ip, cache6.firstIP) < 0) { | ||
return null; | ||
} | ||
do { | ||
line = Math.round((cline - fline) / 2) + fline; | ||
floor = readip(line, 0); | ||
ceil = readip(line, 1); | ||
if (utils.cmp6(floor, ip) <= 0 && utils.cmp6(ceil, ip) >= 0) { | ||
if (recordSize === RECORD_SIZE6) { | ||
geodata.country = buffer.toString('utf8', (line * recordSize) + 32, (line * recordSize) + 34).replace(/\u0000.*/, ''); | ||
} else { | ||
locId = buffer.readUInt32BE((line * recordSize) + 32); | ||
geodata.country = locBuffer.toString('utf8', (locId * locRecordSize), (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; | ||
} else if (fline === cline) { | ||
return null; | ||
} else if (fline === (cline - 1)) { | ||
if (line === fline) { | ||
fline = cline; | ||
} else { | ||
cline = fline; | ||
} | ||
} else if (utils.cmp6(floor, ip) > 0) { | ||
cline = line; | ||
} else if (utils.cmp6(ceil, ip) < 0) { | ||
fline = line; | ||
} | ||
} while (1); | ||
} | ||
function get4mapped(ip) { | ||
const ipv6 = ip.toUpperCase(); | ||
const v6prefixes = ['0:0:0:0:0:FFFF:', '::FFFF:']; | ||
for (let i = 0; i < v6prefixes.length; i++) { | ||
const v6prefix = v6prefixes[i]; | ||
if (ipv6.indexOf(v6prefix) == 0) { | ||
return ipv6.substring(v6prefix.length); | ||
} | ||
} | ||
return null; | ||
} | ||
function preload(callback) { | ||
let datFile; | ||
let datSize; | ||
const asyncCache = JSON.parse(JSON.stringify(conf4)); | ||
// when the preload function receives a callback, do the task asynchronously | ||
if (typeof arguments[0] === 'function') { | ||
async.series([ | ||
function(cb) { | ||
async.series([ | ||
function(cb2) { | ||
fs.open(dataFiles.cityNames, 'r', function(err, file) { | ||
datFile = file; | ||
cb2(err); | ||
}); | ||
}, | ||
function(cb2) { | ||
fs.fstat(datFile, function(err, stats) { | ||
datSize = stats.size; | ||
asyncCache.locationBuffer = Buffer.alloc(datSize); | ||
cb2(err); | ||
}); | ||
}, | ||
function(cb2) { | ||
fs.read(datFile, asyncCache.locationBuffer, 0, datSize, 0, cb2); | ||
}, | ||
function(cb2) { | ||
fs.close(datFile, cb2); | ||
}, | ||
function(cb2) { | ||
fs.open(dataFiles.city, 'r', function(err, file) { | ||
datFile = file; | ||
cb2(err); | ||
}); | ||
}, | ||
function(cb2) { | ||
fs.fstat(datFile, function(err, stats) { | ||
datSize = stats.size; | ||
cb2(err); | ||
}); | ||
}, | ||
], function(err) { | ||
if (err) { | ||
if (err.code !== 'ENOENT' && err.code !== 'EBADF') { | ||
throw err; | ||
} | ||
fs.open(dataFiles.country, 'r', function(err, file) { | ||
if (err) { | ||
cb(err); | ||
} else { | ||
datFile = file; | ||
fs.fstat(datFile, function(err, stats) { | ||
datSize = stats.size; | ||
asyncCache.recordSize = RECORD_SIZE; | ||
cb(); | ||
}); | ||
} | ||
}); | ||
} else { | ||
cb(); | ||
} | ||
}); | ||
}, | ||
function() { | ||
asyncCache.mainBuffer = Buffer.alloc(datSize); | ||
async.series([ | ||
function(cb2) { | ||
fs.read(datFile, asyncCache.mainBuffer, 0, datSize, 0, cb2); | ||
}, | ||
function(cb2) { | ||
fs.close(datFile, cb2); | ||
}, | ||
], function(err) { | ||
if (err) { | ||
// keep old cache | ||
} else { | ||
asyncCache.lastLine = (datSize / asyncCache.recordSize) - 1; | ||
asyncCache.lastIP = asyncCache.mainBuffer.readUInt32BE((asyncCache.lastLine * asyncCache.recordSize) + 4); | ||
asyncCache.firstIP = asyncCache.mainBuffer.readUInt32BE(0); | ||
cache4 = asyncCache; | ||
} | ||
callback(err); | ||
}); | ||
}, | ||
]); | ||
} else { | ||
try { | ||
datFile = fs.openSync(dataFiles.cityNames, 'r'); | ||
datSize = fs.fstatSync(datFile).size; | ||
if (datSize === 0) { | ||
throw { | ||
code: 'EMPTY_FILE', | ||
}; | ||
} | ||
cache4.locationBuffer = Buffer.alloc(datSize); | ||
fs.readSync(datFile, cache4.locationBuffer, 0, datSize, 0); | ||
fs.closeSync(datFile); | ||
datFile = fs.openSync(dataFiles.city, 'r'); | ||
datSize = fs.fstatSync(datFile).size; | ||
} catch (err) { | ||
if (err.code !== 'ENOENT' && err.code !== 'EBADF' && err.code !== 'EMPTY_FILE') { | ||
throw err; | ||
} | ||
datFile = fs.openSync(dataFiles.country, 'r'); | ||
datSize = fs.fstatSync(datFile).size; | ||
cache4.recordSize = RECORD_SIZE; | ||
} | ||
cache4.mainBuffer = Buffer.alloc(datSize); | ||
fs.readSync(datFile, cache4.mainBuffer, 0, datSize, 0); | ||
fs.closeSync(datFile); | ||
cache4.lastLine = (datSize / cache4.recordSize) - 1; | ||
cache4.lastIP = cache4.mainBuffer.readUInt32BE((cache4.lastLine * cache4.recordSize) + 4); | ||
cache4.firstIP = cache4.mainBuffer.readUInt32BE(0); | ||
} | ||
} | ||
function preload6(callback) { | ||
let datFile; | ||
let datSize; | ||
const asyncCache6 = JSON.parse(JSON.stringify(conf6)); | ||
// when the preload function receives a callback, do the task asynchronously | ||
if (typeof arguments[0] === 'function') { | ||
async.series([ | ||
function(cb) { | ||
async.series([ | ||
function(cb2) { | ||
fs.open(dataFiles.city6, 'r', function(err, file) { | ||
datFile = file; | ||
cb2(err); | ||
}); | ||
}, | ||
function(cb2) { | ||
fs.fstat(datFile, function(err, stats) { | ||
datSize = stats.size; | ||
cb2(err); | ||
}); | ||
}, | ||
], function(err) { | ||
if (err) { | ||
if (err.code !== 'ENOENT' && err.code !== 'EBADF') { | ||
throw err; | ||
} | ||
fs.open(dataFiles.country6, 'r', function(err, file) { | ||
if (err) { | ||
cb(err); | ||
} else { | ||
datFile = file; | ||
fs.fstat(datFile, function(err, stats) { | ||
datSize = stats.size; | ||
asyncCache6.recordSize = RECORD_SIZE6; | ||
cb(); | ||
}); | ||
} | ||
}); | ||
} else { | ||
cb(); | ||
} | ||
}); | ||
}, | ||
function() { | ||
asyncCache6.mainBuffer = Buffer.alloc(datSize); | ||
async.series([ | ||
function(cb2) { | ||
fs.read(datFile, asyncCache6.mainBuffer, 0, datSize, 0, cb2); | ||
}, | ||
function(cb2) { | ||
fs.close(datFile, cb2); | ||
}, | ||
], function(err) { | ||
if (err) { | ||
// keep old cache | ||
} else { | ||
asyncCache6.lastLine = (datSize / asyncCache6.recordSize) - 1; | ||
cache6 = asyncCache6; | ||
} | ||
callback(err); | ||
}); | ||
}, | ||
]); | ||
} else { | ||
try { | ||
datFile = fs.openSync(dataFiles.city6, 'r'); | ||
datSize = fs.fstatSync(datFile).size; | ||
if (datSize === 0) { | ||
throw { | ||
code: 'EMPTY_FILE', | ||
}; | ||
} | ||
} catch (err) { | ||
if (err.code !== 'ENOENT' && err.code !== 'EBADF' && err.code !== 'EMPTY_FILE') { | ||
throw err; | ||
} | ||
datFile = fs.openSync(dataFiles.country6, 'r'); | ||
datSize = fs.fstatSync(datFile).size; | ||
cache6.recordSize = RECORD_SIZE6; | ||
} | ||
cache6.mainBuffer = Buffer.alloc(datSize); | ||
fs.readSync(datFile, cache6.mainBuffer, 0, datSize, 0); | ||
fs.closeSync(datFile); | ||
cache6.lastLine = (datSize / cache6.recordSize) - 1; | ||
} | ||
} | ||
module.exports = { | ||
cmp: utils.cmp, | ||
lookup: function(ip) { | ||
if (!ip) { | ||
return null; | ||
} else if (typeof ip === 'number') { | ||
return lookup4(ip); | ||
} else if (net.isIP(ip) === 4) { | ||
return lookup4(utils.aton4(ip)); | ||
} else if (net.isIP(ip) === 6) { | ||
const ipv4 = get4mapped(ip); | ||
if (ipv4) { | ||
return lookup4(utils.aton4(ipv4)); | ||
} else { | ||
return lookup6(utils.aton6(ip)); | ||
} | ||
} | ||
return null; | ||
}, | ||
pretty: function(n) { | ||
if (typeof n === 'string') { | ||
return n; | ||
} else if (typeof n === 'number') { | ||
return utils.ntoa4(n); | ||
} else if (n instanceof Array) { | ||
return utils.ntoa6(n); | ||
} | ||
return n; | ||
}, | ||
// Start watching for data updates. The watcher waits one minute for file transfer to | ||
// completete before triggering the callback. | ||
startWatchingDataUpdate: function(callback) { | ||
fsWatcher.makeFsWatchFilter(watcherName, geodatadir, 60 * 1000, function() { | ||
// Reload data | ||
async.series([ | ||
function(cb) { | ||
preload(cb); | ||
}, | ||
function(cb) { | ||
preload6(cb); | ||
}, | ||
], callback); | ||
}); | ||
}, | ||
// Stop watching for data updates. | ||
stopWatchingDataUpdate: function() { | ||
fsWatcher.stopWatching(watcherName); | ||
}, | ||
// clear data | ||
clear: function() { | ||
cache4 = JSON.parse(JSON.stringify(conf4)); | ||
cache6 = JSON.parse(JSON.stringify(conf6)); | ||
}, | ||
// Reload data synchronously | ||
reloadDataSync: function() { | ||
preload(); | ||
preload6(); | ||
}, | ||
// Reload data asynchronously | ||
reloadData: function(callback) { | ||
// Reload data | ||
async.series([ | ||
function(cb) { | ||
preload(cb); | ||
}, | ||
function(cb) { | ||
preload6(cb); | ||
}, | ||
], callback); | ||
}, | ||
}; | ||
preload(); | ||
preload6(); | ||
// lookup4 = gen_lookup('geoip-country.dat', 4); | ||
// lookup6 = gen_lookup('geoip-country6.dat', 16); | ||
const e=require('node:fs'),t=require('node:net'),r=require('node:path'),n=require('async'),i=require('./utils.js'),o=require('./fsWatcher.js'),{version:a}=require('../package.json');e.existsSync=e.existsSync||r.existsSync;const c='dataWatcher',f=r.resolve(__dirname,global.geoDataDir||process.env.geoDataDIR||'../data/'),s={city:r.join(f,'geoip-city.dat'),city6:r.join(f,'geoip-city6.dat'),cityNames:r.join(f,'geoip-city-names.dat'),country:r.join(f,'geoip-country.dat'),country6:r.join(f,'geoip-country6.dat')},l=[[i.aton4('10.0.0.0'),i.aton4('10.255.255.255')],[i.aton4('172.16.0.0'),i.aton4('172.31.255.255')],[i.aton4('192.168.0.0'),i.aton4('192.168.255.255')]],u={firstIP:null,lastIP:null,lastLine:0,locationBuffer:null,locationRecordSize:88,mainBuffer:null,recordSize:24},d={firstIP:null,lastIP:null,lastLine:0,mainBuffer:null,recordSize:48};let y=JSON.parse(JSON.stringify(u)),S=JSON.parse(JSON.stringify(d));const p=e=>{let t,r,n=0,i=y.lastIP,o=y.lastLine,a=y.firstIP;const c=y.mainBuffer,f=y.locationBuffer,s=l,u=y.recordSize,d=y.locationRecordSize;let S;const p={range:'',country:'',region:'',eu:'',timezone:'',city:'',ll:[0,0]};if(e>y.lastIP||e<y.firstIP)return null;for(S=0;S<s.length;S++)if(e>=s[S][0]&&e<=s[S][1])return null;for(;;){if(t=Math.round((o-n)/2)+n,i=c.readUInt32BE(t*u),a=c.readUInt32BE(t*u+4),i<=e&&a>=e)return p.range=[i,a],10===u?p.country=c.toString('utf8',t*u+8,t*u+10):(r=c.readUInt32BE(t*u+8),p.country=f.toString('utf8',r*d,r*d+2).replace(/\u0000.*/,''),p.region=f.toString('utf8',r*d+2,r*d+5).replace(/\u0000.*/,''),p.metro=f.readInt32BE(r*d+5),p.ll[0]=c.readInt32BE(t*u+12)/1e4,p.ll[1]=c.readInt32BE(t*u+16)/1e4,p.area=c.readUInt32BE(t*u+20),p.eu=f.toString('utf8',r*d+9,r*d+10).replace(/\u0000.*/,''),p.timezone=f.toString('utf8',r*d+10,r*d+42).replace(/\u0000.*/,''),p.city=f.toString('utf8',r*d+42,r*d+d).replace(/\u0000.*/,'')),p;if(n===o)return null;n===o-1?t===n?n=o:o=n:i>e?o=t:a<e&&(n=t)}};function B(t){let r,i;const o=JSON.parse(JSON.stringify(u));if('function'==typeof arguments[0])n.series([t=>{n.series([t=>{e.open(s.cityNames,'r',((e,n)=>{r=n,t(e)}))},t=>{e.fstat(r,((e,r)=>{i=r.size,o.locationBuffer=Buffer.alloc(i),t(e)}))},t=>{e.read(r,o.locationBuffer,0,i,0,t)},t=>{e.close(r,t)},t=>{e.open(s.city,'r',((e,n)=>{r=n,t(e)}))},t=>{e.fstat(r,((e,r)=>{i=r.size,t(e)}))}],(n=>{if(n){if('ENOENT'!==n.code&&'EBADF'!==n.code)throw n;e.open(s.country,'r',((n,a)=>{n?t(n):(r=a,e.fstat(r,((e,r)=>{i=r.size,o.recordSize=10,t()})))}))}else t()}))},()=>{o.mainBuffer=Buffer.alloc(i),n.series([t=>{e.read(r,o.mainBuffer,0,i,0,t)},t=>{e.close(r,t)}],(e=>{e||(o.lastLine=i/o.recordSize-1,o.lastIP=o.mainBuffer.readUInt32BE(o.lastLine*o.recordSize+4),o.firstIP=o.mainBuffer.readUInt32BE(0),y=o),t(e)}))}]);else{try{if(r=e.openSync(s.cityNames,'r'),i=e.fstatSync(r).size,0===i)throw{code:'EMPTY_FILE'};y.locationBuffer=Buffer.alloc(i),e.readSync(r,y.locationBuffer,0,i,0),e.closeSync(r),r=e.openSync(s.city,'r'),i=e.fstatSync(r).size}catch(t){if('ENOENT'!==t.code&&'EBADF'!==t.code&&'EMPTY_FILE'!==t.code)throw t;r=e.openSync(s.country,'r'),i=e.fstatSync(r).size,y.recordSize=10}y.mainBuffer=Buffer.alloc(i),e.readSync(r,y.mainBuffer,0,i,0),e.closeSync(r),y.lastLine=i/y.recordSize-1,y.lastIP=y.mainBuffer.readUInt32BE(y.lastLine*y.recordSize+4),y.firstIP=y.mainBuffer.readUInt32BE(0)}}function g(t){let r,i;const o=JSON.parse(JSON.stringify(d));if('function'==typeof arguments[0])n.series([t=>{n.series([t=>{e.open(s.city6,'r',((e,n)=>{r=n,t(e)}))},t=>{e.fstat(r,((e,r)=>{i=r.size,t(e)}))}],(n=>{if(n){if('ENOENT'!==n.code&&'EBADF'!==n.code)throw n;e.open(s.country6,'r',((n,a)=>{n?t(n):(r=a,e.fstat(r,((e,r)=>{i=r.size,o.recordSize=34,t()})))}))}else t()}))},()=>{o.mainBuffer=Buffer.alloc(i),n.series([t=>{e.read(r,o.mainBuffer,0,i,0,t)},t=>{e.close(r,t)}],(e=>{e||(o.lastLine=i/o.recordSize-1,S=o),t(e)}))}]);else{try{if(r=e.openSync(s.city6,'r'),i=e.fstatSync(r).size,0===i)throw{code:'EMPTY_FILE'}}catch(t){if('ENOENT'!==t.code&&'EBADF'!==t.code&&'EMPTY_FILE'!==t.code)throw t;r=e.openSync(s.country6,'r'),i=e.fstatSync(r).size,S.recordSize=34}S.mainBuffer=Buffer.alloc(i),e.readSync(r,S.mainBuffer,0,i,0),e.closeSync(r),S.lastLine=i/S.recordSize-1}}module.exports={cmp:i.cmp,lookup:e=>{if(!e)return null;if('number'==typeof e)return p(e);if(4===t.isIP(e))return p(i.aton4(e));if(6===t.isIP(e)){const t=(e=>{const t=e.toUpperCase(),r=['0:0:0:0:0:FFFF:','::FFFF:'];for(let e=0;e<r.length;e++){const n=r[e];if(0===t.indexOf(n))return t.substring(n.length)}return null})(e);return t?p(i.aton4(t)):(e=>{const t=S.mainBuffer,r=S.recordSize,n=y.locationBuffer,o=y.locationRecordSize,a={range:'',country:'',region:'',city:'',ll:[0,0]},c=(e,n)=>{let i=0;const o=[];for(i=0;i<2;i++)o.push(t.readUInt32BE(e*r+16*n+4*i));return o};S.lastIP=c(S.lastLine,1),S.firstIP=c(0,0);let f,s,l=0,u=S.lastIP,d=S.lastLine,p=S.firstIP;if(i.cmp6(e,S.lastIP)>0||i.cmp6(e,S.firstIP)<0)return null;for(;;){if(f=Math.round((d-l)/2)+l,u=c(f,0),p=c(f,1),i.cmp6(u,e)<=0&&i.cmp6(p,e)>=0)return 34===r?a.country=t.toString('utf8',f*r+32,f*r+34).replace(/\u0000.*/,''):(s=t.readUInt32BE(f*r+32),a.country=n.toString('utf8',s*o,s*o+2).replace(/\u0000.*/,''),a.region=n.toString('utf8',s*o+2,s*o+5).replace(/\u0000.*/,''),a.metro=n.readInt32BE(s*o+5),a.ll[0]=t.readInt32BE(f*r+36)/1e4,a.ll[1]=t.readInt32BE(f*r+40)/1e4,a.area=t.readUInt32BE(f*r+44),a.eu=n.toString('utf8',s*o+9,s*o+10).replace(/\u0000.*/,''),a.timezone=n.toString('utf8',s*o+10,s*o+42).replace(/\u0000.*/,''),a.city=n.toString('utf8',s*o+42,s*o+o).replace(/\u0000.*/,'')),a;if(l===d)return null;l===d-1?f===l?l=d:d=l:i.cmp6(u,e)>0?d=f:i.cmp6(p,e)<0&&(l=f)}})(i.aton6(e))}return null},pretty:e=>'string'==typeof e?e:'number'==typeof e?i.ntoa4(e):Array.isArray(e)?i.ntoa6(e):e,startWatchingDataUpdate:e=>{o.makeFsWatchFilter(c,f,6e4,(()=>{n.series([e=>{B(e)},e=>{g(e)}],e)}))},stopWatchingDataUpdate:()=>{o.stopWatching(c)},clear:()=>{y=JSON.parse(JSON.stringify(u)),S=JSON.parse(JSON.stringify(d))},reloadDataSync:()=>{B(),g()},reloadData:e=>{n.series([e=>{B(e)},e=>{g(e)}],e)},version:a},B(),g(); |
@@ -1,87 +0,1 @@ | ||
const utils = module.exports = {}; | ||
utils.aton4 = function(a) { | ||
a = a.split(/\./); | ||
return ((parseInt(a[0], 10) << 24) >>> 0) + ((parseInt(a[1], 10) << 16) >>> 0) + ((parseInt(a[2], 10) << 8) >>> 0) + (parseInt(a[3], 10) >>> 0); | ||
}; | ||
utils.aton6 = function(a) { | ||
a = a.replace(/"/g, '').split(/:/); | ||
const l = a.length - 1; | ||
let i; | ||
if (a[l] === '') { | ||
a[l] = 0; | ||
} | ||
if (l < 7) { | ||
a.length = 8; | ||
for (i = l; i >= 0 && a[i] !== ''; i--) { | ||
a[7 - l + i] = a[i]; | ||
} | ||
} | ||
for (i = 0; i < 8; i++) { | ||
if (!a[i]) { | ||
a[i] = 0; | ||
} else { | ||
a[i] = parseInt(a[i], 16); | ||
} | ||
} | ||
const r = []; | ||
for (i = 0; i < 4; i++) { | ||
r.push(((a[2 * i] << 16) + a[2 * i + 1]) >>> 0); | ||
} | ||
return r; | ||
}; | ||
utils.cmp = function(a, b) { | ||
if (typeof a === 'number' && typeof b === 'number') return (a < b ? -1 : (a > b ? 1 : 0)); | ||
if (a instanceof Array && b instanceof Array) return this.cmp6(a, b); | ||
return null; | ||
}; | ||
utils.cmp6 = function(a, b) { | ||
for (let ii = 0; ii < 2; ii++) { | ||
if (a[ii] < b[ii]) return -1; | ||
if (a[ii] > b[ii]) return 1; | ||
} | ||
return 0; | ||
}; | ||
utils.isPrivateIP = function(addr) { | ||
addr = addr.toString(); | ||
return addr.match(/^10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/) != null || | ||
addr.match(/^192\.168\.([0-9]{1,3})\.([0-9]{1,3})/) != null || | ||
addr.match(/^172\.16\.([0-9]{1,3})\.([0-9]{1,3})/) != null || | ||
addr.match(/^127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/) != null || | ||
addr.match(/^169\.254\.([0-9]{1,3})\.([0-9]{1,3})/) != null || | ||
addr.match(/^fc00:/) != null || addr.match(/^fe80:/) != null; | ||
}; | ||
utils.ntoa4 = function(n) { | ||
n = n.toString(); | ||
n = '' + (n >>> 24 & 0xff) + '.' + (n >>> 16 & 0xff) + '.' + (n >>> 8 & 0xff) + '.' + (n & 0xff); | ||
return n; | ||
}; | ||
utils.ntoa6 = function(n) { | ||
let a = '['; | ||
for (let i = 0; i < n.length; i++) { | ||
a += (n[i] >>> 16).toString(16) + ':'; | ||
a += (n[i] & 0xffff).toString(16) + ':'; | ||
} | ||
a = a.replace(/:$/, ']').replace(/:0+/g, ':').replace(/::+/, '::'); | ||
return a; | ||
}; | ||
const t=module.exports={};t.aton4=function(t){return t=t.split(/\./),(parseInt(t[0],10)<<24>>>0)+(parseInt(t[1],10)<<16>>>0)+(parseInt(t[2],10)<<8>>>0)+(parseInt(t[3],10)>>>0)},t.aton6=function(t){const n=(t=t.replace(/"/g,'').split(/:/)).length-1;let r;if(''===t[n]&&(t[n]=0),n<7)for(t.length=8,r=n;r>=0&&''!==t[r];r--)t[7-n+r]=t[r];for(r=0;r<8;r++)t[r]?t[r]=parseInt(t[r],16):t[r]=0;const e=[];for(r=0;r<4;r++)e.push((t[2*r]<<16)+t[2*r+1]>>>0);return e},t.cmp=function(t,n){return'number'==typeof t&&'number'==typeof n?t<n?-1:t>n?1:0:t instanceof Array&&n instanceof Array?this.cmp6(t,n):null},t.cmp6=function(t,n){for(let r=0;r<2;r++){if(t[r]<n[r])return-1;if(t[r]>n[r])return 1}return 0},t.isPrivateIP=function(t){return null!=(t=t.toString()).match(/^10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/)||null!=t.match(/^192\.168\.([0-9]{1,3})\.([0-9]{1,3})/)||null!=t.match(/^172\.16\.([0-9]{1,3})\.([0-9]{1,3})/)||null!=t.match(/^127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/)||null!=t.match(/^169\.254\.([0-9]{1,3})\.([0-9]{1,3})/)||null!=t.match(/^fc00:/)||null!=t.match(/^fe80:/)},t.ntoa4=function(t){return t=((t=t.toString())>>>24&255)+'.'+(t>>>16&255)+'.'+(t>>>8&255)+'.'+(255&t)},t.ntoa6=function(t){let n='[';for(let r=0;r<t.length;r++)n+=(t[r]>>>16).toString(16)+':',n+=(65535&t[r]).toString(16)+':';return n=n.replace(/:$/,']').replace(/:0+/g,':').replace(/::+/,'::'),n}; |
{ | ||
"name": "geoip-lite2", | ||
"version": "2.0.3", | ||
"version": "2.1.0", | ||
"description": "A light weight native JavaScript implementation of GeoIP API from MaxMind.", | ||
"keywords": [ | ||
"city", | ||
"country", | ||
"fast-geoip", | ||
"geo", | ||
"geoip", | ||
"geolite", | ||
"geolite2", | ||
"geolocation", | ||
"geolookup", | ||
"ip", | ||
"ipv4", | ||
"ipv6", | ||
"geolookup", | ||
"location", | ||
"maxmind", | ||
"geolite", | ||
"geolite2", | ||
"fast-geoip" | ||
"region", | ||
"state" | ||
], | ||
"homepage": "https://github.com/sefinek24/geoip-lite2", | ||
"bugs": { | ||
"url": "https://github.com/sefinek24/geoip-lite2/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/sefinek24/geoip-lite2.git" | ||
}, | ||
"license": "Apache-2.0", | ||
"author": "Philip Tellis <philip@bluesmoon.info> (https://bluesmoon.info)", | ||
"main": "lib/geoip.js", | ||
"types": "index.d.ts", | ||
"directories": { | ||
"lib": "lib", | ||
"test": "test" | ||
}, | ||
"files": [ | ||
@@ -25,12 +45,6 @@ "lib/", | ||
], | ||
"main": "lib/geoip.js", | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/sefinek24/geoip-lite2.git" | ||
}, | ||
"engines": { | ||
"node": ">=5.10.0" | ||
}, | ||
"scripts": { | ||
"eslint": "eslint .", | ||
"minify-linux": "bash _Minify/linux.bash", | ||
"minify-win": "powershell.exe -ExecutionPolicy Bypass .\\_Minify\\windows.ps1", | ||
"test": "mocha test/main.js", | ||
@@ -51,17 +65,10 @@ "up": "ncu -u -x chalk && npm install && npm update && npm audit fix", | ||
}, | ||
"config": { | ||
"update": true | ||
}, | ||
"devDependencies": { | ||
"eslint": "^8.48.0", | ||
"mocha": "^10.2.0" | ||
"mocha": "^10.2.0", | ||
"terser": "^5.19.4" | ||
}, | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/sefinek24/geoip-lite2/issues" | ||
}, | ||
"directories": { | ||
"lib": "lib", | ||
"test": "test" | ||
"engines": { | ||
"node": ">=5.10.0" | ||
} | ||
} |
@@ -1,10 +0,7 @@ | ||
🗺️ GeoIP-Lite2 | ||
🗺️ GeoIP-Lite2 v2.1.0 - Now even faster! | ||
========== | ||
A native NodeJS API for the GeoLite data from MaxMind. | ||
A native Node.js API for the GeoLite data from MaxMind. | ||
This product includes GeoLite data created by MaxMind, available from https://www.maxmind.com | ||
This product includes GeoLite data created by MaxMind, available from: https://www.maxmind.com | ||
> **Note** | ||
> You MUST update the data files after installation. The MaxMind license does not allow us to distribute the latest version of the data files with this package. Follow the instructions under update the datafiles for details. | ||
🚀 Improved GeoIP Module by [Sefinek](https://sefinek.net) | ||
@@ -17,2 +14,4 @@ ------------ | ||
Now this module operates even faster because its files have been minified! | ||
Furthermore, the testing process has been improved by adopting the testing library Mocha. | ||
@@ -23,3 +22,9 @@ This change enhances testing and contributes to the overall reliability of the module. | ||
✨ Demonstration | ||
------------ | ||
You can see this module in action on my official API. Generally speaking, the API interface is public, and you can safely use it in your projects. Happy coding! | ||
> Endpoint : https://api.sefinek.net/api/v2/geoip/{ip} | ||
> Example : https://api.sefinek.net/api/v2/geoip/109.207.159.255 | ||
📑 Introduction | ||
@@ -31,3 +36,3 @@ ------------ | ||
GeoIP-Lite instead attempts to be a fully native JavaScript library. A converter script converts the CSV files from MaxMind into | ||
an internal binary format (note that this is different from the binary data format provided by MaxMind). The geoip module uses this | ||
an internal binary format (note that this is different from the binary data format provided by MaxMind). The geo-ip module uses this | ||
binary file to lookup IP addresses and return the country, region and city that it maps to. | ||
@@ -38,3 +43,6 @@ | ||
> **Warning** | ||
> **You MUST update the data files after installation.** The MaxMind license does not allow us to distribute the latest version of the data files with this package. Follow the instructions under update the datafiles for details. | ||
📚 Philosophy | ||
@@ -48,3 +56,3 @@ ---------- | ||
------------- | ||
GeoIP-Lite is a fully JavaScript implementation of the MaxMind geoip API. It is not as fully featured as bindings that use `libgeoip`. | ||
GeoIP-Lite is a fully JavaScript implementation of the MaxMind GeoIP API. It is not as fully featured as bindings that use `libgeoip`. | ||
By reducing scope, this package is about 40% faster at doing lookups. On average, an IP to Location lookup should take 20 microseconds on | ||
@@ -90,8 +98,10 @@ a Macbook Pro. IPv4 addresses take about 6 microseconds, while IPv6 addresses take about 30 microseconds. | ||
### 2. Update the datafiles (optional) | ||
Run `cd node_modules/geoip-lite2 && 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)) | ||
Run `cd node_modules/geoip-lite2 && npm run 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 maxmind account [here](https://www.maxmind.com/en/geolite2/signup). | ||
**NOTE** that this requires a lot of RAM. It is known to fail on a Digital Ocean or AWS micro instance. | ||
There are no plans to change this. GeoIP-Lite2 stores all data in RAM in order to be fast. | ||
> **Note** | ||
> This requires a lot of RAM. It is known to fail on a Digital Ocean or AWS micro instance. | ||
> There are no plans to change this. GeoIP-Lite2 stores all data in RAM in order to be fast. | ||
> If you need an external API that provides GeoIP, you can use [this](#-demonstration). | ||
@@ -101,3 +111,3 @@ | ||
--- | ||
geoip-lite2 is completely synchronous. There are no callbacks involved. | ||
GeoIP-Lite2 is completely synchronous. There are no callbacks involved. | ||
All blocking file IO is done at startup time, so all runtime calls are executed in-memory and are fast. | ||
@@ -186,3 +196,3 @@ Startup may take up to 200ms while it reads into memory and indexes data files. | ||
// Asynchronously | ||
geoip.reloadData(function() { | ||
geoip.reloadData(() => { | ||
console.log('Done'); | ||
@@ -209,3 +219,3 @@ }); | ||
Also note that on occassion, the library may take up to 5 seconds to load into memory. This is largely dependent on | ||
Also note that on occasion, the library may take up to 5 seconds to load into memory. This is largely dependent on | ||
how busy your disk is at that time. It can take as little as 200ms on a lightly loaded disk. This is a one time | ||
@@ -215,16 +225,18 @@ cost though, and you make it up at run time with very fast lookups. | ||
### Memory usage | ||
Quick test on memory consumption shows that library uses around 100Mb per process | ||
Quick test on memory consumption shows that library uses around 100Mb per process. | ||
```javascript | ||
const geoip2 = require('geoip-lite2'); | ||
console.log(process.memoryUsage()); | ||
/** | ||
* Outputs: | ||
* { | ||
* rss: 126365696, | ||
* heapTotal: 10305536, | ||
* heapUsed: 5168944, | ||
* external: 104347120 | ||
* } | ||
**/ | ||
const geoip2 = require('geoip-lite2'); | ||
console.log(process.memoryUsage()); | ||
/** | ||
* Output: | ||
* { | ||
* rss: 126365696, | ||
* heapTotal: 7753728, | ||
* heapUsed: 5844880, | ||
* external: 164098897, | ||
* arrayBuffers: 163675390 | ||
* } | ||
**/ | ||
``` | ||
@@ -231,0 +243,0 @@ |
@@ -1,627 +0,1 @@ | ||
// Fetches and converts maxmind lite databases | ||
'use strict'; | ||
const { name, version } = require('../package.json'); | ||
const user_agent = `Mozilla/5.0 (compatible; ${name}/${version}; +https://sefinek.net)`; | ||
const fs = require('node:fs'); | ||
const http = require('http'); | ||
const https = require('https'); | ||
const path = require('node:path'); | ||
const url = require('url'); | ||
const zlib = require('zlib'); | ||
fs.existsSync = fs.existsSync || path.existsSync; | ||
const async = require('async'); | ||
const chalk = require('chalk'); | ||
const iconv = require('iconv-lite'); | ||
const lazy = require('lazy'); | ||
const rimraf = require('rimraf').sync; | ||
const AdmZip = require('adm-zip'); | ||
const utils = require('../lib/utils.js'); | ||
const { Address6, Address4 } = require('ip-address'); | ||
const args = process.argv.slice(2); | ||
let license_key = args.find(function(arg) { | ||
return arg.match(/^license_key=[a-zA-Z0-9]+/) !== null; | ||
}); | ||
if (typeof license_key === 'undefined' && typeof process.env.LICENSE_KEY !== 'undefined') { | ||
license_key = 'license_key=' + process.env.LICENSE_KEY; | ||
} | ||
let geodatadir = args.find(function(arg) { | ||
return arg.match(/^geodatadir=[\w./]+/) !== null; | ||
}); | ||
if (typeof geodatadir === 'undefined' && typeof process.env.GEODATADIR !== 'undefined') { | ||
geodatadir = 'geodatadir=' + process.env.GEODATADIR; | ||
} | ||
let dataPath = path.resolve(__dirname, '..', 'data'); | ||
if (typeof geodatadir !== 'undefined') { | ||
dataPath = path.resolve(process.cwd(), geodatadir.split('=')[1]); | ||
if (!fs.existsSync(dataPath)) { | ||
console.log(chalk.red('ERROR') + ': Directory doesn\'t exist: ' + dataPath); | ||
process.exit(1); | ||
} | ||
} | ||
const tmpPath = path.resolve(__dirname, '..', 'tmp'); | ||
const countryLookup = {}; | ||
const cityLookup = {}; | ||
const databases = [{ | ||
type: 'country', | ||
url: 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&suffix=zip&' + license_key, | ||
checksum: 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&suffix=zip.sha256&' + license_key, | ||
fileName: 'GeoLite2-Country-CSV.zip', | ||
src: [ | ||
'GeoLite2-Country-Locations-en.csv', | ||
'GeoLite2-Country-Blocks-IPv4.csv', | ||
'GeoLite2-Country-Blocks-IPv6.csv', | ||
], | ||
dest: [ | ||
'', | ||
'geoip-country.dat', | ||
'geoip-country6.dat', | ||
], | ||
}, { | ||
type: 'city', | ||
url: 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City-CSV&suffix=zip&' + license_key, | ||
checksum: 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City-CSV&suffix=zip.sha256&' + license_key, | ||
fileName: 'GeoLite2-City-CSV.zip', | ||
src: [ | ||
'GeoLite2-City-Locations-en.csv', | ||
'GeoLite2-City-Blocks-IPv4.csv', | ||
'GeoLite2-City-Blocks-IPv6.csv', | ||
], | ||
dest: [ | ||
'geoip-city-names.dat', | ||
'geoip-city.dat', | ||
'geoip-city6.dat', | ||
], | ||
}]; | ||
function mkdir(dirName) { | ||
const dir = path.dirname(dirName); | ||
if (!fs.existsSync(dir)) fs.mkdirSync(dir); | ||
} | ||
// Ref: http://stackoverflow.com/questions/8493195/how-can-i-parse-a-csv-string-with-javascript | ||
// Return array of string values, or NULL if CSV string not well-formed. | ||
// Return array of string values, or NULL if CSV string not well-formed. | ||
function try_fixing_line(line) { | ||
let pos1 = 0; | ||
let pos2 = -1; | ||
// Escape quotes | ||
line = line.replace(/""/, '\\"').replace(/'/g, '\\\''); | ||
while (pos1 < line.length && pos2 < line.length) { | ||
pos1 = pos2; | ||
pos2 = line.indexOf(',', pos1 + 1); | ||
if (pos2 < 0) pos2 = line.length; | ||
if (line.indexOf('\'', (pos1 || 0)) > -1 && line.indexOf('\'', pos1) < pos2 && line[pos1 + 1] != '"' && line[pos2 - 1] != '"') { | ||
line = line.substr(0, pos1 + 1) + '"' + line.substr(pos1 + 1, pos2 - pos1 - 1) + '"' + line.substr(pos2, line.length - pos2); | ||
pos2 = line.indexOf(',', pos2 + 1); | ||
if (pos2 < 0) pos2 = line.length; | ||
} | ||
} | ||
return line; | ||
} | ||
function CSVtoArray(text) { | ||
const re_valid = /^\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*(?:,\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*)*$/; | ||
const re_value = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g; | ||
// Return NULL if input string is not well-formed CSV string. | ||
if (!re_valid.test(text)) { | ||
text = try_fixing_line(text); | ||
if (!re_valid.test(text)) | ||
{return null;} | ||
} | ||
const a = []; // Initialize array to receive values. | ||
text.replace(re_value, // "Walk" the string using replace with callback. | ||
function(m0, m1, m2, m3) { | ||
// Remove backslash from \' in single quoted values. | ||
if (m1 !== undefined) a.push(m1.replace(/\\'/g, '\'')); | ||
// Remove backslash from \" in double-quoted values. | ||
else if (m2 !== undefined) a.push(m2.replace(/\\"/g, '"').replace(/\\'/g, '\'')); | ||
else if (m3 !== undefined) a.push(m3); | ||
return ''; // Return empty string. | ||
}); | ||
// Handle special case of empty last value. | ||
if ((/,\s*$/).test(text)) a.push(''); | ||
return a; | ||
} | ||
function getHTTPOptions(downloadUrl) { | ||
const options = url.parse(downloadUrl); | ||
options.headers = { | ||
'User-Agent': user_agent, | ||
}; | ||
if (process.env.http_proxy || process.env.https_proxy) { | ||
try { | ||
const HttpsProxyAgent = require('node:https-proxy-agent'); | ||
options.agent = new HttpsProxyAgent(process.env.http_proxy || process.env.https_proxy); | ||
} | ||
catch (e) { | ||
console.error('Install https-proxy-agent to use an HTTP/HTTPS proxy'); | ||
process.exit(-1); | ||
} | ||
} | ||
return options; | ||
} | ||
function check(database, cb) { | ||
if (args.indexOf('force') !== -1) { | ||
// We are forcing database upgrade, | ||
// So not even using checksums | ||
return cb(null, database); | ||
} | ||
const checksumUrl = database.checksum; | ||
if (typeof checksumUrl === 'undefined') { | ||
// No checksum url to check, skipping | ||
return cb(null, database); | ||
} | ||
// Read existing checksum file | ||
fs.readFile(path.join(dataPath, database.type + '.checksum'), { encoding: 'utf8' }, function(err, data) { | ||
if (!err && data && data.length) database.checkValue = data; | ||
console.log('Checking ', database.fileName); | ||
function onResponse(response) { | ||
const status = response.statusCode; | ||
if (status !== 200) { | ||
console.log(chalk.red('ERROR') + response.data); | ||
console.log(chalk.red('ERROR') + ': HTTP Request Failed [%d %s]', status, http.STATUS_CODES[status]); | ||
client.abort(); | ||
process.exit(); | ||
} | ||
let str = ''; | ||
response.on('data', function(chunk) { | ||
str += chunk; | ||
}); | ||
response.on('end', function() { | ||
if (str && str.length) { | ||
if (str == database.checkValue) { | ||
console.log(chalk.green('Database "' + database.type + '" is up to date')); | ||
database.skip = true; | ||
} | ||
else { | ||
console.log(chalk.green('Database ' + database.type + ' has new data')); | ||
database.checkValue = str; | ||
} | ||
} | ||
else { | ||
console.log(chalk.red('ERROR') + ': Could not retrieve checksum for', database.type, chalk.red('Aborting')); | ||
console.log('Run with "force" to update without checksum'); | ||
client.abort(); | ||
process.exit(); | ||
} | ||
cb(null, database); | ||
}); | ||
} | ||
var client = https.get(getHTTPOptions(checksumUrl), onResponse); | ||
}); | ||
} | ||
function fetch(database, cb) { | ||
if (database.skip) return cb(null, null, null, database); | ||
const downloadUrl = database.url; | ||
let fileName = database.fileName; | ||
const gzip = path.extname(fileName) === '.gz'; | ||
if (gzip) fileName = fileName.replace('.gz', ''); | ||
const tmpFile = path.join(tmpPath, fileName); | ||
if (fs.existsSync(tmpFile)) return cb(null, tmpFile, fileName, database); | ||
console.log('Fetching ', fileName); | ||
function onResponse(response) { | ||
const status = response.statusCode; | ||
if (status !== 200) { | ||
console.error(chalk.red('ERROR') + ': HTTP Request Failed [%d %s]', status, http.STATUS_CODES[status]); | ||
client.abort(); | ||
process.exit(); | ||
} | ||
let tmpFilePipe; | ||
const tmpFileStream = fs.createWriteStream(tmpFile); | ||
if (gzip) { | ||
tmpFilePipe = response.pipe(zlib.createGunzip()).pipe(tmpFileStream); | ||
} else { | ||
tmpFilePipe = response.pipe(tmpFileStream); | ||
} | ||
tmpFilePipe.on('close', function() { | ||
console.log(chalk.green(' DONE')); | ||
cb(null, tmpFile, fileName, database); | ||
}); | ||
} | ||
mkdir(tmpFile); | ||
var client = https.get(getHTTPOptions(downloadUrl), onResponse); | ||
process.stdout.write('Retrieving ' + fileName + '...'); | ||
} | ||
function extract(tmpFile, tmpFileName, database, cb) { | ||
if (database.skip) { | ||
return cb(null, database); | ||
} | ||
if (path.extname(tmpFileName) !== '.zip') { | ||
cb(null, database); | ||
} else { | ||
process.stdout.write('Extracting ' + tmpFileName + '...'); | ||
const zip = new AdmZip(tmpFile); | ||
const zipEntries = zip.getEntries(); | ||
zipEntries.forEach((entry) => { | ||
if (entry.isDirectory) { | ||
// Skip directory entries | ||
return; | ||
} | ||
const filePath = entry.entryName.split('/'); | ||
const fileName = filePath[filePath.length - 1]; | ||
const destinationPath = path.join(tmpPath, fileName); | ||
fs.writeFileSync(destinationPath, entry.getData()); | ||
}); | ||
console.log(chalk.green(' DONE')); | ||
cb(null, database); | ||
} | ||
} | ||
function processLookupCountry(src, cb) { | ||
function processLine(line) { | ||
const fields = CSVtoArray(line); | ||
if (!fields || fields.length < 6) { | ||
console.log('weird line: %s::', line); | ||
return; | ||
} | ||
countryLookup[fields[0]] = fields[4]; | ||
} | ||
const tmpDataFile = path.join(tmpPath, src); | ||
process.stdout.write('Processing Lookup Data (may take a moment)...'); | ||
lazy(fs.createReadStream(tmpDataFile)) | ||
.lines | ||
.map(function(byteArray) { | ||
return iconv.decode(byteArray, 'latin1'); | ||
}) | ||
.skip(1) | ||
.map(processLine) | ||
.on('pipe', function() { | ||
console.log(chalk.green(' DONE')); | ||
cb(); | ||
}); | ||
} | ||
function processCountryData(src, dest, cb) { | ||
let lines = 0; | ||
function processLine(line) { | ||
const fields = CSVtoArray(line); | ||
if (!fields || fields.length < 6) { | ||
console.warn('weird line: %s::', line); | ||
return; | ||
} | ||
lines++; | ||
let sip; | ||
let eip; | ||
let rngip; | ||
const cc = countryLookup[fields[1]]; | ||
let b; | ||
let bsz; | ||
let i; | ||
if (cc) { | ||
if (fields[0].match(/:/)) { | ||
// IPv6 | ||
bsz = 34; | ||
rngip = new Address6(fields[0]); | ||
sip = utils.aton6(rngip.startAddress().correctForm()); | ||
eip = utils.aton6(rngip.endAddress().correctForm()); | ||
b = Buffer.alloc(bsz); | ||
for (i = 0; i < sip.length; i++) { | ||
b.writeUInt32BE(sip[i], i * 4); | ||
} | ||
for (i = 0; i < eip.length; i++) { | ||
b.writeUInt32BE(eip[i], 16 + (i * 4)); | ||
} | ||
} else { | ||
// IPv4 | ||
bsz = 10; | ||
rngip = new Address4(fields[0]); | ||
sip = parseInt(rngip.startAddress().bigInteger(), 10); | ||
eip = parseInt(rngip.endAddress().bigInteger(), 10); | ||
b = Buffer.alloc(bsz); | ||
b.fill(0); | ||
b.writeUInt32BE(sip, 0); | ||
b.writeUInt32BE(eip, 4); | ||
} | ||
b.write(cc, bsz - 2); | ||
fs.writeSync(datFile, b, 0, bsz, null); | ||
if (Date.now() - tstart > 5000) { | ||
tstart = Date.now(); | ||
process.stdout.write('\nStill working (' + lines + ')...'); | ||
} | ||
} | ||
} | ||
const dataFile = path.join(dataPath, dest); | ||
const tmpDataFile = path.join(tmpPath, src); | ||
rimraf(dataFile); | ||
mkdir(dataFile); | ||
process.stdout.write('Processing data (may take a moment)...'); | ||
var tstart = Date.now(); | ||
var datFile = fs.openSync(dataFile, 'w'); | ||
lazy(fs.createReadStream(tmpDataFile)) | ||
.lines | ||
.map(function(byteArray) { | ||
return iconv.decode(byteArray, 'latin1'); | ||
}) | ||
.skip(1) | ||
.map(processLine) | ||
.on('pipe', function() { | ||
console.log(chalk.green(' DONE')); | ||
cb(); | ||
}); | ||
} | ||
function processCityData(src, dest, cb) { | ||
let lines = 0; | ||
function processLine(line) { | ||
if (line.match(/^Copyright/) || !line.match(/\d/)) { | ||
return; | ||
} | ||
const fields = CSVtoArray(line); | ||
if (!fields) { | ||
console.warn('weird line: %s::', line); | ||
return; | ||
} | ||
let sip; | ||
let eip; | ||
let rngip; | ||
let locId; | ||
let b; | ||
let bsz; | ||
let lat; | ||
let lon; | ||
let area; | ||
let i; | ||
lines++; | ||
if (fields[0].match(/:/)) { | ||
// IPv6 | ||
let offset = 0; | ||
bsz = 48; | ||
rngip = new Address6(fields[0]); | ||
sip = utils.aton6(rngip.startAddress().correctForm()); | ||
eip = utils.aton6(rngip.endAddress().correctForm()); | ||
locId = parseInt(fields[1], 10); | ||
locId = cityLookup[locId]; | ||
b = Buffer.alloc(bsz); | ||
b.fill(0); | ||
for (i = 0; i < sip.length; i++) { | ||
b.writeUInt32BE(sip[i], offset); | ||
offset += 4; | ||
} | ||
for (i = 0; i < eip.length; i++) { | ||
b.writeUInt32BE(eip[i], offset); | ||
offset += 4; | ||
} | ||
b.writeUInt32BE(locId >>> 0, 32); | ||
lat = Math.round(parseFloat(fields[7]) * 10000); | ||
lon = Math.round(parseFloat(fields[8]) * 10000); | ||
area = parseInt(fields[9], 10); | ||
b.writeInt32BE(lat, 36); | ||
b.writeInt32BE(lon, 40); | ||
b.writeInt32BE(area, 44); | ||
} else { | ||
// IPv4 | ||
bsz = 24; | ||
rngip = new Address4(fields[0]); | ||
sip = parseInt(rngip.startAddress().bigInteger(), 10); | ||
eip = parseInt(rngip.endAddress().bigInteger(), 10); | ||
locId = parseInt(fields[1], 10); | ||
locId = cityLookup[locId]; | ||
b = Buffer.alloc(bsz); | ||
b.fill(0); | ||
b.writeUInt32BE(sip >>> 0, 0); | ||
b.writeUInt32BE(eip >>> 0, 4); | ||
b.writeUInt32BE(locId >>> 0, 8); | ||
lat = Math.round(parseFloat(fields[7]) * 10000); | ||
lon = Math.round(parseFloat(fields[8]) * 10000); | ||
area = parseInt(fields[9], 10); | ||
b.writeInt32BE(lat, 12); | ||
b.writeInt32BE(lon, 16); | ||
b.writeInt32BE(area, 20); | ||
} | ||
fs.writeSync(datFile, b, 0, b.length, null); | ||
if (Date.now() - tstart > 5000) { | ||
tstart = Date.now(); | ||
process.stdout.write('\nStill working (' + lines + ')...'); | ||
} | ||
} | ||
const dataFile = path.join(dataPath, dest); | ||
const tmpDataFile = path.join(tmpPath, src); | ||
rimraf(dataFile); | ||
process.stdout.write('Processing Data (may take a moment) ...'); | ||
var tstart = Date.now(); | ||
var datFile = fs.openSync(dataFile, 'w'); | ||
lazy(fs.createReadStream(tmpDataFile)) | ||
.lines | ||
.map(function(byteArray) { | ||
return iconv.decode(byteArray, 'latin1'); | ||
}) | ||
.skip(1) | ||
.map(processLine) | ||
.on('pipe', cb); | ||
} | ||
function processCityDataNames(src, dest, cb) { | ||
let locId = null; | ||
let linesCount = 0; | ||
function processLine(line) { | ||
if (line.match(/^Copyright/) || !line.match(/\d/)) { | ||
return; | ||
} | ||
const b = Buffer.alloc(88); | ||
const fields = CSVtoArray(line); | ||
if (!fields) { | ||
// Lots of cities contain ` or ' in the name and can't be parsed correctly with current method | ||
console.warn('weird line: %s::', line); | ||
return; | ||
} | ||
locId = parseInt(fields[0]); | ||
cityLookup[locId] = linesCount; | ||
const cc = fields[4]; | ||
const rg = fields[6]; | ||
const city = fields[10]; | ||
const metro = parseInt(fields[11]); | ||
// Other possible fields to include | ||
const tz = fields[12]; | ||
const eu = fields[13]; | ||
b.fill(0); | ||
b.write(cc, 0); // Country code | ||
b.write(rg, 2); // Region | ||
if (metro) b.writeInt32BE(metro, 5); | ||
b.write(eu, 9); // Is in eu | ||
b.write(tz, 10); // Timezone | ||
b.write(city, 42); // City name | ||
fs.writeSync(datFile, b, 0, b.length, null); | ||
linesCount++; | ||
} | ||
const dataFile = path.join(dataPath, dest); | ||
const tmpDataFile = path.join(tmpPath, src); | ||
rimraf(dataFile); | ||
var datFile = fs.openSync(dataFile, 'w'); | ||
lazy(fs.createReadStream(tmpDataFile)) | ||
.lines | ||
.map(function(byteArray) { | ||
return iconv.decode(byteArray, 'utf-8'); | ||
}) | ||
.skip(1) | ||
.map(processLine) | ||
.on('pipe', cb); | ||
} | ||
function processData(database, cb) { | ||
if (database.skip) { | ||
return cb(null, database); | ||
} | ||
const type = database.type; | ||
const src = database.src; | ||
const dest = database.dest; | ||
if (type === 'country') { | ||
if (Array.isArray(src)) { | ||
processLookupCountry(src[0], function() { | ||
processCountryData(src[1], dest[1], function() { | ||
processCountryData(src[2], dest[2], function() { | ||
cb(null, database); | ||
}); | ||
}); | ||
}); | ||
} | ||
else { | ||
processCountryData(src, dest, function() { | ||
cb(null, database); | ||
}); | ||
} | ||
} else if (type === 'city') { | ||
processCityDataNames(src[0], dest[0], function() { | ||
processCityData(src[1], dest[1], function() { | ||
console.log('city data processed'); | ||
processCityData(src[2], dest[2], function() { | ||
console.log(chalk.green(' DONE')); | ||
cb(null, database); | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
function updateChecksum(database, cb) { | ||
if (database.skip || !database.checkValue) { | ||
// don't need to update checksums cause it was not fetched or did not change | ||
return cb(); | ||
} | ||
fs.writeFile(path.join(dataPath, database.type + '.checksum'), database.checkValue, 'utf8', function(err) { | ||
if (err) console.log(chalk.red('Failed to Update checksums.'), 'Database:', database.type); | ||
cb(); | ||
}); | ||
} | ||
if (!license_key) { | ||
console.error(chalk.red('ERROR') + ': Missing license_key'); | ||
process.exit(1); | ||
} | ||
rimraf(tmpPath); | ||
mkdir(tmpPath); | ||
async.eachSeries(databases, function(database, nextDatabase) { | ||
async.seq(check, fetch, extract, processData, updateChecksum)(database, nextDatabase); | ||
}, function(err) { | ||
if (err) { | ||
console.error(chalk.red('Failed to Update Databases from MaxMind.'), err); | ||
process.exit(1); | ||
} else { | ||
console.log(chalk.green('Successfully Updated Databases from MaxMind.')); | ||
if (args.indexOf('debug') !== -1) { | ||
console.log(chalk.yellow.bold('Notice: temporary files are not deleted for debug purposes.')); | ||
} else { | ||
rimraf(tmpPath); | ||
} | ||
process.exit(0); | ||
} | ||
}); | ||
'use strict';const{name:e,version:t}=require('../package.json'),n=`Mozilla/5.0 (compatible; ${e}/${t}; +https://sefinek.net)`,o=require('node:fs'),r=require('node:http'),i=require('node:https'),s=require('node:path'),c=require('node:url'),l=require('node:zlib');o.existsSync=o.existsSync||s.existsSync;const a=require('async'),u=require('chalk'),d=require('iconv-lite'),p=require('lazy'),f=require('rimraf').sync,g=require('adm-zip'),m=require('../lib/utils.js'),{Address6:h,Address4:w}=require('ip-address'),y=process.argv.slice(2);let S=y.find((function(e){return null!==e.match(/^license_key=[a-zA-Z0-9]+/)}));void 0===S&&void 0!==process.env.LICENSE_KEY&&(S='license_key='+process.env.LICENSE_KEY);let v=y.find((function(e){return null!==e.match(/^geoDataDir=[\w./]+/)}));void 0===v&&void 0!==process.env.GEODATADIR&&(v='geoDataDir='+process.env.GEODATADIR);let x=s.resolve(__dirname,'..','data');void 0!==v&&(x=s.resolve(process.cwd(),v.split('=')[1]),o.existsSync(x)||(console.log(u.red('ERROR')+': Directory doesn\'t exist: '+x),process.exit(1)));const E=s.resolve(__dirname,'..','tmp'),k={},I={},D=[{type:'country',url:'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&suffix=zip&'+S,checksum:'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&suffix=zip.sha256&'+S,fileName:'GeoLite2-Country-CSV.zip',src:['GeoLite2-Country-Locations-en.csv','GeoLite2-Country-Blocks-IPv4.csv','GeoLite2-Country-Blocks-IPv6.csv'],dest:['','geoip-country.dat','geoip-country6.dat']},{type:'city',url:'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City-CSV&suffix=zip&'+S,checksum:'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City-CSV&suffix=zip.sha256&'+S,fileName:'GeoLite2-City-CSV.zip',src:['GeoLite2-City-Locations-en.csv','GeoLite2-City-Blocks-IPv4.csv','GeoLite2-City-Blocks-IPv6.csv'],dest:['geoip-city-names.dat','geoip-city.dat','geoip-city6.dat']}];function C(e){const t=s.dirname(e);o.existsSync(t)||o.mkdirSync(t)}function R(e){const t=/^\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*(?:,\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*)*$/;if(!t.test(e)&&(e=function(e){let t=0,n=-1;for(e=e.replace(/""/,'\\"').replace(/'/g,'\\\'');t<e.length&&n<e.length;)t=n,n=e.indexOf(',',t+1),n<0&&(n=e.length),e.indexOf('\'',t||0)>-1&&e.indexOf('\'',t)<n&&'"'!=e[t+1]&&'"'!=e[n-1]&&(n=(e=e.substr(0,t+1)+'"'+e.substr(t+1,n-t-1)+'"'+e.substr(n,e.length-n)).indexOf(',',n+1),n<0&&(n=e.length));return e}(e),!t.test(e)))return null;const n=[];return e.replace(/(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g,(function(e,t,o,r){return void 0!==t?n.push(t.replace(/\\'/g,'\'')):void 0!==o?n.push(o.replace(/\\"/g,'"').replace(/\\'/g,'\'')):void 0!==r&&n.push(r),''})),/,\s*$/.test(e)&&n.push(''),n}function B(e){const t=c.parse(e);if(t.headers={'User-Agent':n},process.env.http_proxy||process.env.https_proxy)try{const e=require('node:https-proxy-agent');t.agent=new e(process.env.http_proxy||process.env.https_proxy)}catch(e){console.error('Install https-proxy-agent to use an HTTP/HTTPS proxy'),process.exit(-1)}return t}function _(e,t){if(-1!==y.indexOf('force'))return t(null,e);const n=e.checksum;if(void 0===n)return t(null,e);o.readFile(s.join(x,e.type+'.checksum'),{encoding:'utf8'},(function(o,s){!o&&s&&s.length&&(e.checkValue=s),console.log('Checking ',e.fileName);var c=i.get(B(n),(function(n){const o=n.statusCode;200!==o&&(console.log(u.red('ERROR')+n.data),console.log(u.red('ERROR')+': HTTP Request Failed [%d %s]',o,r.STATUS_CODES[o]),c.abort(),process.exit(1));let i='';n.on('data',(function(e){i+=e})),n.on('end',(function(){i&&i.length?i==e.checkValue?(console.log(u.green('Database "'+e.type+'" is up to date')),e.skip=!0):(console.log(u.green('Database '+e.type+' has new data')),e.checkValue=i):(console.log(u.red('ERROR')+': Could not retrieve checksum for',e.type,u.red('Aborting')),console.log('Run with "force" to update without checksum'),c.abort(),process.exit(1)),t(null,e)}))}))}))}function b(e,t){if(e.skip)return t(null,null,null,e);const n=e.url;let c=e.fileName;const a='.gz'===s.extname(c);a&&(c=c.replace('.gz',''));const d=s.join(E,c);if(o.existsSync(d))return t(null,d,c,e);console.log('Fetching ',c),C(d);var p=i.get(B(n),(function(n){const i=n.statusCode;let s;200!==i&&(console.error(u.red('ERROR')+': HTTP Request Failed [%d %s]',i,r.STATUS_CODES[i]),p.abort(),process.exit(1));const f=o.createWriteStream(d);s=a?n.pipe(l.createGunzip()).pipe(f):n.pipe(f),s.on('close',(function(){console.log(u.green(' DONE')),t(null,d,c,e)}))}));process.stdout.write('Retrieving '+c+'...')}function A(e,t,n,r){if(n.skip)return r(null,n);if('.zip'!==s.extname(t))r(null,n);else{process.stdout.write('Extracting '+t+'...');new g(e).getEntries().forEach((e=>{if(e.isDirectory)return;const t=e.entryName.split('/'),n=t[t.length-1],r=s.join(E,n);o.writeFileSync(r,e.getData())})),console.log(u.green(' DONE')),r(null,n)}}function O(e,t,n){let r=0;const i=s.join(x,t),c=s.join(E,e);f(i),C(i),process.stdout.write('Processing data (may take a moment)...');var l=Date.now(),a=o.openSync(i,'w');p(o.createReadStream(c)).lines.map((function(e){return d.decode(e,'latin1')})).skip(1).map((function(e){const t=R(e);if(!t||t.length<6)return void console.warn('weird line: %s::',e);let n,i,s;r++;const c=k[t[1]];let u,d,p;if(c){if(t[0].match(/:/)){for(d=34,s=new h(t[0]),n=m.aton6(s.startAddress().correctForm()),i=m.aton6(s.endAddress().correctForm()),u=Buffer.alloc(d),p=0;p<n.length;p++)u.writeUInt32BE(n[p],4*p);for(p=0;p<i.length;p++)u.writeUInt32BE(i[p],16+4*p)}else d=10,s=new w(t[0]),n=parseInt(s.startAddress().bigInteger(),10),i=parseInt(s.endAddress().bigInteger(),10),u=Buffer.alloc(d),u.fill(0),u.writeUInt32BE(n,0),u.writeUInt32BE(i,4);u.write(c,d-2),o.writeSync(a,u,0,d,null),Date.now()-l>5e3&&(l=Date.now(),process.stdout.write('\nStill working ('+r+')...'))}})).on('pipe',(function(){console.log(u.green(' DONE')),n()}))}function q(e,t,n){let r=0;const i=s.join(x,t),c=s.join(E,e);f(i),process.stdout.write('Processing Data (may take a moment) ...');var l=Date.now(),a=o.openSync(i,'w');p(o.createReadStream(c)).lines.map((function(e){return d.decode(e,'latin1')})).skip(1).map((function(e){if(e.match(/^Copyright/)||!e.match(/\d/))return;const t=R(e);if(!t)return void console.warn('weird line: %s::',e);let n,i,s,c,u,d,p,f,g,y;if(r++,t[0].match(/:/)){let e=0;for(d=48,s=new h(t[0]),n=m.aton6(s.startAddress().correctForm()),i=m.aton6(s.endAddress().correctForm()),c=parseInt(t[1],10),c=I[c],u=Buffer.alloc(d),u.fill(0),y=0;y<n.length;y++)u.writeUInt32BE(n[y],e),e+=4;for(y=0;y<i.length;y++)u.writeUInt32BE(i[y],e),e+=4;u.writeUInt32BE(c>>>0,32),p=Math.round(1e4*parseFloat(t[7])),f=Math.round(1e4*parseFloat(t[8])),g=parseInt(t[9],10),u.writeInt32BE(p,36),u.writeInt32BE(f,40),u.writeInt32BE(g,44)}else d=24,s=new w(t[0]),n=parseInt(s.startAddress().bigInteger(),10),i=parseInt(s.endAddress().bigInteger(),10),c=parseInt(t[1],10),c=I[c],u=Buffer.alloc(d),u.fill(0),u.writeUInt32BE(n>>>0,0),u.writeUInt32BE(i>>>0,4),u.writeUInt32BE(c>>>0,8),p=Math.round(1e4*parseFloat(t[7])),f=Math.round(1e4*parseFloat(t[8])),g=parseInt(t[9],10),u.writeInt32BE(p,12),u.writeInt32BE(f,16),u.writeInt32BE(g,20);o.writeSync(a,u,0,u.length,null),Date.now()-l>5e3&&(l=Date.now(),process.stdout.write('\nStill working ('+r+')...'))})).on('pipe',n)}function L(e,t){if(e.skip)return t(null,e);const n=e.type,r=e.src,i=e.dest;'country'===n?Array.isArray(r)?function(e,t){const n=s.join(E,e);process.stdout.write('Processing Lookup Data (may take a moment)...'),p(o.createReadStream(n)).lines.map((function(e){return d.decode(e,'latin1')})).skip(1).map((function(e){const t=R(e);!t||t.length<6?console.log('weird line: %s::',e):k[t[0]]=t[4]})).on('pipe',(function(){console.log(u.green(' DONE')),t()}))}(r[0],(function(){O(r[1],i[1],(function(){O(r[2],i[2],(function(){t(null,e)}))}))})):O(r,i,(function(){t(null,e)})):'city'===n&&function(e,t,n){let r=null,i=0;const c=s.join(x,t),l=s.join(E,e);f(c);var a=o.openSync(c,'w');p(o.createReadStream(l)).lines.map((function(e){return d.decode(e,'utf-8')})).skip(1).map((function(e){if(e.match(/^Copyright/)||!e.match(/\d/))return;const t=Buffer.alloc(88),n=R(e);if(!n)return void console.warn('weird line: %s::',e);r=parseInt(n[0]),I[r]=i;const s=n[4],c=n[6],l=n[10],u=parseInt(n[11]),d=n[12],p=n[13];t.fill(0),t.write(s,0),t.write(c,2),u&&t.writeInt32BE(u,5),t.write(p,9),t.write(d,10),t.write(l,42),o.writeSync(a,t,0,t.length,null),i++})).on('pipe',n)}(r[0],i[0],(function(){q(r[1],i[1],(function(){console.log('city data processed'),q(r[2],i[2],(function(){console.log(u.green(' DONE')),t(null,e)}))}))}))}function F(e,t){if(e.skip||!e.checkValue)return t();o.writeFile(s.join(x,e.type+'.checksum'),e.checkValue,'utf8',(function(n){n&&console.log(u.red('Failed to Update checksums.'),'Database:',e.type),t()}))}S||(console.error(u.red('ERROR')+': Missing license_key'),process.exit(1)),f(E),C(E),a.eachSeries(D,(function(e,t){a.seq(_,b,A,L,F)(e,t)}),(function(e){e?(console.error(u.red('Failed to Update Databases from MaxMind.'),e),process.exit(1)):(console.log(u.green('Successfully Updated Databases from MaxMind.')),-1!==y.indexOf('debug')?console.log(u.yellow.bold('Notice: temporary files are not deleted for debug purposes.')):f(E),process.exit(0))})); |
@@ -1,56 +0,1 @@ | ||
const assert = require('assert'); | ||
const t1 = +new Date(); | ||
const geoIp2 = require('../lib/geoip.js'); | ||
const t2 = +new Date(); | ||
if (process.argv.length > 2) { | ||
console.dir(geoIp2.lookup(process.argv[2])); | ||
const t3 = +new Date(); | ||
console.log('Startup: %dms, exec: %dms', t2 - t1, t3 - t2); | ||
process.exit(); | ||
} | ||
const f = []; | ||
let ip; | ||
const n = 30000; | ||
const nf = []; | ||
let r; | ||
const ts = +new Date(); | ||
for (let i = 0; i < n; i++) { | ||
if ((i % 2) === 0) { | ||
ip = Math.round((Math.random() * 0xff000000) + 0xffffff); | ||
} else { | ||
ip = '2001:' + | ||
Math.round(Math.random() * 0xffff).toString(16) + ':' + | ||
Math.round(Math.random() * 0xffff).toString(16) + ':' + | ||
Math.round(Math.random() * 0xffff).toString(16) + ':' + | ||
Math.round(Math.random() * 0xffff).toString(16) + ':' + | ||
Math.round(Math.random() * 0xffff).toString(16) + ':' + | ||
Math.round(Math.random() * 0xffff).toString(16) + ':' + | ||
Math.round(Math.random() * 0xffff).toString(16) + ''; | ||
} | ||
r = geoIp2.lookup(ip); | ||
if (r === null) { | ||
nf.push(ip); | ||
continue; | ||
} | ||
f.push([ip, r]); | ||
assert.ok(geoIp2.cmp(ip, r.range[0]) >= 0, 'Problem with ' + geoIp2.pretty(ip) + ' < ' + geoIp2.pretty(r.range[0])); | ||
assert.ok(geoIp2.cmp(ip, r.range[1]) <= 0, 'Problem with ' + geoIp2.pretty(ip) + ' > ' + geoIp2.pretty(r.range[1])); | ||
} | ||
const te = +new Date(); | ||
/* | ||
f.forEach(function(ip) { | ||
console.log("%s bw %s & %s is %s", geoIp2.pretty(ip[0]), geoIp2.pretty(ip[1].range[0]), geoIp2.pretty(ip[1].range[1]), ip[1].country); | ||
}); | ||
*/ | ||
console.log('Found %d (%d/%d) ips in %dms (%s ip/s) (%sμs/ip)', n, f.length, nf.length, te - ts, (n * 1000 / (te - ts)).toFixed(3), ((te - ts) * 1000 / n).toFixed(0)); | ||
console.log('Took %d ms to startup', t2 - t1); | ||
const t=require('assert'),o=+new Date,n=require('../lib/geoip.js'),e=+new Date;if(process.argv.length>2){console.dir(n.lookup(process.argv[2]));const t=+new Date;console.log('Startup: %dms, exec: %dms',e-o,t-e),process.exit(1)}const r=[];let a;const s=3e4,d=[];let h;const i=+new Date;for(let o=0;o<s;o++)a=o%2==0?Math.round(4278190080*Math.random()+16777215):'2001:'+Math.round(65535*Math.random()).toString(16)+':'+Math.round(65535*Math.random()).toString(16)+':'+Math.round(65535*Math.random()).toString(16)+':'+Math.round(65535*Math.random()).toString(16)+':'+Math.round(65535*Math.random()).toString(16)+':'+Math.round(65535*Math.random()).toString(16)+':'+Math.round(65535*Math.random()).toString(16),h=n.lookup(a),null!==h?(r.push([a,h]),t.ok(n.cmp(a,h.range[0])>=0,'Problem with '+n.pretty(a)+' < '+n.pretty(h.range[0])),t.ok(n.cmp(a,h.range[1])<=0,'Problem with '+n.pretty(a)+' > '+n.pretty(h.range[1]))):d.push(a);const g=+new Date;console.log('Found %d (%d/%d) ips in %dms (%s ip/s) (%sμs/ip)',s,r.length,d.length,g-i,(1e3*s/(g-i)).toFixed(3),(1e3*(g-i)/s).toFixed(0)),console.log('Took %d ms to startup',e-o); |
250
test/main.js
@@ -1,249 +0,1 @@ | ||
const assert = require('assert'); | ||
const geoIp2 = require('../lib/geoip.js'); | ||
describe('GeoIP2', function() { | ||
describe('#testLookup()', function() { | ||
it('should return data about IPv4', function() { | ||
const ip = '1.1.1.1'; | ||
const actual = geoIp2.lookup(ip); | ||
assert.ok(actual); | ||
}); | ||
it('should return data about IPv6', function() { | ||
const ipv6 = '2606:4700:4700::64'; | ||
const actual = geoIp2.lookup(ipv6); | ||
assert.ok(actual); | ||
}); | ||
}); | ||
describe('#testDataIP4()', function() { | ||
it('should match data for IPv4 - US', function() { | ||
const actual = geoIp2.lookup('72.229.28.185'); | ||
assert.strictEqual(actual.range !== undefined, true); | ||
assert.strictEqual(actual.country, 'US'); | ||
assert.strictEqual(actual.region, 'NY'); | ||
assert.strictEqual(actual.eu, '0'); | ||
assert.strictEqual(actual.timezone, 'America/New_York'); | ||
assert.strictEqual(actual.city, 'New York'); | ||
assert.ok(actual.ll); | ||
assert.strictEqual(actual.metro, 501); | ||
assert.strictEqual(actual.area, 5); | ||
}); | ||
it('should match data for IPv4 - JP', function() { | ||
const actual = geoIp2.lookup('210.138.184.59'); | ||
assert.strictEqual(actual.range !== undefined, true); | ||
assert.strictEqual(actual.country, 'JP'); | ||
assert.strictEqual(actual.region, '13'); | ||
assert.strictEqual(actual.eu, '0'); | ||
assert.strictEqual(actual.timezone, 'Asia/Tokyo'); | ||
assert.strictEqual(actual.city, 'Chiyoda-ku'); | ||
assert.ok(actual.ll); | ||
assert.strictEqual(actual.metro, 0); | ||
assert.strictEqual(actual.area, 200); | ||
}); | ||
it('should match data for IPv4 - PL', function() { | ||
const actual = geoIp2.lookup('104.113.255.255'); | ||
assert.strictEqual(actual.range !== undefined, true); | ||
assert.strictEqual(actual.country, 'PL'); | ||
assert.strictEqual(actual.region, '14'); | ||
assert.strictEqual(actual.eu, '1'); | ||
assert.strictEqual(actual.timezone, 'Europe/Warsaw'); | ||
assert.strictEqual(actual.city, 'Warsaw'); | ||
assert.ok(actual.ll); | ||
assert.strictEqual(actual.metro, 0); | ||
assert.strictEqual(actual.area, 20); | ||
}); | ||
it('should match data for IPv4 - RU', function() { | ||
const actual = geoIp2.lookup('109.108.63.255'); | ||
assert.strictEqual(actual.range !== undefined, true); | ||
assert.strictEqual(actual.country, 'RU'); | ||
assert.strictEqual(actual.region, 'IVA'); | ||
assert.strictEqual(actual.eu, '0'); | ||
assert.strictEqual(actual.timezone, 'Europe/Moscow'); | ||
assert.strictEqual(actual.city, 'Kineshma'); | ||
assert.ok(actual.ll); | ||
assert.strictEqual(actual.metro, 0); | ||
assert.strictEqual(actual.area, 200); | ||
}); | ||
}); | ||
describe('#testDataIP6()', function() { | ||
it('should match data for IPv6', function() { | ||
const ipv6 = '2001:1c04:400::1'; | ||
const actual = geoIp2.lookup(ipv6); | ||
assert.strictEqual(actual.range !== undefined, true); | ||
assert.strictEqual(actual.country, 'NL'); | ||
assert.strictEqual(actual.region, 'NH'); | ||
assert.strictEqual(actual.eu, '1'); | ||
assert.strictEqual(actual.timezone, 'Europe/Amsterdam'); | ||
assert.strictEqual(actual.city, 'Zandvoort'); | ||
assert.ok(actual.ll); | ||
assert.strictEqual(actual.metro, 0); | ||
assert.strictEqual(actual.area, 5); | ||
}); | ||
it('should match data for IPv4 - JP', function() { | ||
const actual = geoIp2.lookup('2400:8500:1302:814:a163:44:173:238f'); | ||
assert.strictEqual(actual.range !== undefined, true); | ||
assert.strictEqual(actual.country, 'JP'); | ||
assert.strictEqual(actual.region, ''); | ||
assert.strictEqual(actual.eu, '0'); | ||
assert.strictEqual(actual.timezone, 'Asia/Tokyo'); | ||
assert.strictEqual(actual.city, ''); | ||
assert.ok(actual.ll); | ||
assert.strictEqual(actual.metro, 0); | ||
assert.strictEqual(actual.area, 500); | ||
}); | ||
it('should match data for IPv4 - JP', function() { | ||
const actual = geoIp2.lookup('1.79.255.115'); | ||
assert.strictEqual(actual.range !== undefined, true); | ||
assert.strictEqual(actual.country, 'JP'); | ||
assert.strictEqual(actual.region, ''); | ||
assert.strictEqual(actual.eu, '0'); | ||
assert.strictEqual(actual.timezone, 'Asia/Tokyo'); | ||
assert.strictEqual(actual.city, ''); | ||
assert.ok(actual.ll); | ||
assert.strictEqual(actual.metro, 0); | ||
assert.strictEqual(actual.area, 500); | ||
}); | ||
}); | ||
describe('#testUTF8()', function() { | ||
it('should return UTF8 city name', function() { | ||
const ip = '2.139.175.1'; | ||
const expected = 'Barbera Del Valles'; | ||
const actual = geoIp2.lookup(ip); | ||
assert.ok(actual); | ||
assert.strictEqual(actual.city, expected); | ||
}); | ||
}); | ||
describe('#testMetro()', function() { | ||
it('should match metro data', function() { | ||
const actual = geoIp2.lookup('23.240.63.68'); | ||
assert.strictEqual(actual.metro, 803); | ||
}); | ||
}); | ||
describe('#testIPv4MappedIPv6()', function() { | ||
it('should match IPv4 mapped IPv6 data', function() { | ||
const actual = geoIp2.lookup('195.16.170.74'); | ||
// assert.strictEqual(actual.city, ''); | ||
assert.strictEqual(actual.metro, 0); | ||
}); | ||
}); | ||
describe('#testSyncReload()', function() { | ||
it('should reload data synchronously', function() { | ||
const before4 = geoIp2.lookup('75.82.117.180'); | ||
assert.notStrictEqual(before4, null); | ||
const before6 = geoIp2.lookup('::ffff:173.185.182.82'); | ||
assert.notStrictEqual(before6, null); | ||
geoIp2.clear(); | ||
const none4 = geoIp2.lookup('75.82.117.180'); | ||
assert.strictEqual(none4, null); | ||
const none6 = geoIp2.lookup('::ffff:173.185.182.82'); | ||
assert.strictEqual(none6, null); | ||
geoIp2.reloadDataSync(); | ||
const after4 = geoIp2.lookup('75.82.117.180'); | ||
assert.deepStrictEqual(before4, after4); | ||
const after6 = geoIp2.lookup('::ffff:173.185.182.82'); | ||
assert.deepStrictEqual(before6, after6); | ||
}); | ||
}); | ||
describe('#testAsyncReload()', function() { | ||
it('should reload data asynchronously', function(done) { | ||
const before4 = geoIp2.lookup('75.82.117.180'); | ||
assert.notStrictEqual(before4, null); | ||
const before6 = geoIp2.lookup('::ffff:173.185.182.82'); | ||
assert.notStrictEqual(before6, null); | ||
geoIp2.clear(); | ||
const none4 = geoIp2.lookup('75.82.117.180'); | ||
assert.strictEqual(none4, null); | ||
const none6 = geoIp2.lookup('::ffff:173.185.182.82'); | ||
assert.strictEqual(none6, null); | ||
geoIp2.reloadData(function() { | ||
const after4 = geoIp2.lookup('75.82.117.180'); | ||
assert.deepStrictEqual(before4, after4); | ||
const after6 = geoIp2.lookup('::ffff:173.185.182.82'); | ||
assert.deepStrictEqual(before6, after6); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('#testInvalidIP()', function() { | ||
it('should return null for an invalid IP address', function() { | ||
const ip = 'invalid_ip_address'; | ||
const actual = geoIp2.lookup(ip); | ||
assert.strictEqual(actual, null); | ||
}); | ||
}); | ||
describe('#testEmptyIP()', function() { | ||
it('should return null for an empty IP address', function() { | ||
const ip = ''; | ||
const actual = geoIp2.lookup(ip); | ||
assert.strictEqual(actual, null); | ||
}); | ||
}); | ||
describe('#testNullIP()', function() { | ||
it('should return null for a null IP address', function() { | ||
const ip = null; | ||
const actual = geoIp2.lookup(ip); | ||
assert.strictEqual(actual, null); | ||
}); | ||
}); | ||
describe('#testUnknownIP()', function() { | ||
it('should return null for an unknown IP address', function() { | ||
const ip = '192.168.1.1'; // Private IP address | ||
const actual = geoIp2.lookup(ip); | ||
assert.strictEqual(actual, null); | ||
}); | ||
}); | ||
describe('#testNoDataForIP()', function() { | ||
it('should return null for an IP address with no data', function() { | ||
const ip = '203.0.113.0'; // Example IP with no data | ||
const actual = geoIp2.lookup(ip); | ||
assert.strictEqual(actual, null); | ||
}); | ||
}); | ||
describe('#testSpecialCharactersIP()', function() { | ||
it('should return null for an IP address with special characters', function() { | ||
const ip = '1.2.3.@'; // IP with special characters | ||
const actual = geoIp2.lookup(ip); | ||
assert.strictEqual(actual, null); | ||
}); | ||
}); | ||
}); | ||
const t=require('assert'),o=require('../lib/geoip.js');describe('GeoIP2',(function(){describe('#testLookup()',(function(){it('should return data about IPv4',(function(){const u=o.lookup('1.1.1.1');t.ok(u)})),it('should return data about IPv6',(function(){const u=o.lookup('2606:4700:4700::64');t.ok(u)}))})),describe('#testDataIP4()',(function(){it('should match data for IPv4 - US',(function(){const u=o.lookup('72.229.28.185');t.strictEqual(void 0!==u.range,!0),t.strictEqual(u.country,'US'),t.strictEqual(u.region,'NY'),t.strictEqual(u.eu,'0'),t.strictEqual(u.timezone,'America/New_York'),t.strictEqual(u.city,'New York'),t.ok(u.ll),t.strictEqual(u.metro,501),t.strictEqual(u.area,5)})),it('should match data for IPv4 - JP',(function(){const u=o.lookup('210.138.184.59');t.strictEqual(void 0!==u.range,!0),t.strictEqual(u.country,'JP'),t.strictEqual(u.region,'13'),t.strictEqual(u.eu,'0'),t.strictEqual(u.timezone,'Asia/Tokyo'),t.strictEqual(u.city,'Chiyoda-ku'),t.ok(u.ll),t.strictEqual(u.metro,0),t.strictEqual(u.area,200)})),it('should match data for IPv4 - PL',(function(){const u=o.lookup('104.113.255.255');t.strictEqual(void 0!==u.range,!0),t.strictEqual(u.country,'PL'),t.strictEqual(u.region,'14'),t.strictEqual(u.eu,'1'),t.strictEqual(u.timezone,'Europe/Warsaw'),t.strictEqual(u.city,'Warsaw'),t.ok(u.ll),t.strictEqual(u.metro,0),t.strictEqual(u.area,20)})),it('should match data for IPv4 - RU',(function(){const u=o.lookup('109.108.63.255');t.strictEqual(void 0!==u.range,!0),t.strictEqual(u.country,'RU'),t.strictEqual(u.region,'IVA'),t.strictEqual(u.eu,'0'),t.strictEqual(u.timezone,'Europe/Moscow'),t.strictEqual(u.city,'Kineshma'),t.ok(u.ll),t.strictEqual(u.metro,0),t.strictEqual(u.area,200)}))})),describe('#testDataIP6()',(function(){it('should match data for IPv6',(function(){const u=o.lookup('2001:1c04:400::1');t.strictEqual(void 0!==u.range,!0),t.strictEqual(u.country,'NL'),t.strictEqual(u.region,'NH'),t.strictEqual(u.eu,'1'),t.strictEqual(u.timezone,'Europe/Amsterdam'),t.strictEqual(u.city,'Zandvoort'),t.ok(u.ll),t.strictEqual(u.metro,0),t.strictEqual(u.area,5)})),it('should match data for IPv4 - JP',(function(){const u=o.lookup('2400:8500:1302:814:a163:44:173:238f');t.strictEqual(void 0!==u.range,!0),t.strictEqual(u.country,'JP'),t.strictEqual(u.region,''),t.strictEqual(u.eu,'0'),t.strictEqual(u.timezone,'Asia/Tokyo'),t.strictEqual(u.city,''),t.ok(u.ll),t.strictEqual(u.metro,0),t.strictEqual(u.area,500)})),it('should match data for IPv4 - JP',(function(){const u=o.lookup('1.79.255.115');t.strictEqual(void 0!==u.range,!0),t.strictEqual(u.country,'JP'),t.strictEqual(u.region,''),t.strictEqual(u.eu,'0'),t.strictEqual(u.timezone,'Asia/Tokyo'),t.strictEqual(u.city,''),t.ok(u.ll),t.strictEqual(u.metro,0),t.strictEqual(u.area,500)}))})),describe('#testUTF8()',(function(){it('should return UTF8 city name',(function(){const u=o.lookup('2.139.175.1');t.ok(u),t.strictEqual(u.city,'Barbera Del Valles')}))})),describe('#testMetro()',(function(){it('should match metro data',(function(){const u=o.lookup('23.240.63.68');t.strictEqual(u.metro,803)}))})),describe('#testIPv4MappedIPv6()',(function(){it('should match IPv4 mapped IPv6 data',(function(){const u=o.lookup('195.16.170.74');t.strictEqual(u.metro,0)}))})),describe('#testSyncReload()',(function(){it('should reload data synchronously',(function(){const u=o.lookup('75.82.117.180');t.notStrictEqual(u,null);const l=o.lookup('::ffff:173.185.182.82');t.notStrictEqual(l,null),o.clear();const c=o.lookup('75.82.117.180');t.strictEqual(c,null);const r=o.lookup('::ffff:173.185.182.82');t.strictEqual(r,null),o.reloadDataSync();const a=o.lookup('75.82.117.180');t.deepStrictEqual(u,a);const i=o.lookup('::ffff:173.185.182.82');t.deepStrictEqual(l,i)}))})),describe('#testAsyncReload()',(function(){it('should reload data asynchronously',(function(u){const l=o.lookup('75.82.117.180');t.notStrictEqual(l,null);const c=o.lookup('::ffff:173.185.182.82');t.notStrictEqual(c,null),o.clear();const r=o.lookup('75.82.117.180');t.strictEqual(r,null);const a=o.lookup('::ffff:173.185.182.82');t.strictEqual(a,null),o.reloadData((function(){const r=o.lookup('75.82.117.180');t.deepStrictEqual(l,r);const a=o.lookup('::ffff:173.185.182.82');t.deepStrictEqual(c,a),u()}))}))})),describe('#testInvalidIP()',(function(){it('should return null for an invalid IP address',(function(){const u=o.lookup('invalid_ip_address');t.strictEqual(u,null)}))})),describe('#testEmptyIP()',(function(){it('should return null for an empty IP address',(function(){const u=o.lookup('');t.strictEqual(u,null)}))})),describe('#testNullIP()',(function(){it('should return null for a null IP address',(function(){const u=o.lookup(null);t.strictEqual(u,null)}))})),describe('#testUnknownIP()',(function(){it('should return null for an unknown IP address',(function(){const u=o.lookup('192.168.1.1');t.strictEqual(u,null)}))})),describe('#testNoDataForIP()',(function(){it('should return null for an IP address with no data',(function(){const u=o.lookup('203.0.113.0');t.strictEqual(u,null)}))})),describe('#testSpecialCharactersIP()',(function(){it('should return null for an IP address with special characters',(function(){const u=o.lookup('1.2.3.@');t.strictEqual(u,null)}))}))})); |
@@ -1,4 +0,1 @@ | ||
// eslint-disable-next-line no-unused-vars | ||
const geoIp2 = require('../lib/geoip.js'); | ||
console.log(process.memoryUsage()); | ||
require('../lib/geoip.js');console.log(process.memoryUsage()); |
@@ -1,6 +0,1 @@ | ||
const geoIp2 = require('../lib/geoip.js'); | ||
const ip = '2003:6:2184:e6d5:5991:6779:38be:654'; | ||
const data = geoIp2.lookup(ip); | ||
console.log(data); | ||
const o=require('../lib/geoip.js').lookup('2003:6:2184:e6d5:5991:6779:38be:654');console.log(o); |
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
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
Sorry, the diff of this file is not supported yet
Empty package
Supply chain riskPackage does not contain any code. It may be removed, is name squatting, or the result of a faulty package publish.
Found 1 instance in 1 package
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 3 instances in 1 package
178328302
19
256
7
3
0
5