4 changed files with 121 additions and 10 deletions
@ -0,0 +1,7 @@
|
||||
// MaxMind GeoIP2 config |
||||
module.exports.maxmind = { |
||||
userId: 1234, |
||||
licenseKey: 'asdf', |
||||
service: 'city', |
||||
requestTimeout: 1000 |
||||
}; |
||||
@ -0,0 +1,94 @@
|
||||
//
|
||||
// GeoIP lookup service
|
||||
//
|
||||
|
||||
// GeoIP cache TTL in ms
|
||||
var CACHE_TTL = (60 * 60 * 24 * 60 * 1000); // 60 days
|
||||
|
||||
var config = require(__dirname + '/config.js'); |
||||
|
||||
if (!config.maxmind) { |
||||
console.error('FATAL: only MaxMind GeoIP2 is currently supported and is not configured in config.js'); |
||||
process.exit(1); |
||||
} |
||||
var geo = require('geoip2ws')(config.maxmind); |
||||
|
||||
var cache = require('levelup')(__dirname + '/cache.leveldb'); |
||||
|
||||
function lookup(ip,callback) |
||||
{ |
||||
cache.get(ip,function(err,cachedEntryJson) { |
||||
if ((!err)&&(cachedEntryJson)) { |
||||
try { |
||||
var cachedEntry = JSON.parse(cachedEntryJson.toString()); |
||||
if (cachedEntry) { |
||||
var ts = cachedEntry.ts; |
||||
var r = cachedEntry.r; |
||||
if ((ts)&&(r)) { |
||||
if ((Date.now() - ts) < CACHE_TTL) { |
||||
r._cached = true; |
||||
return callback(null,r); |
||||
} |
||||
} |
||||
} |
||||
} catch (e) {} |
||||
} |
||||
|
||||
geo(ip,function(err,result) { |
||||
if (err) |
||||
return callback(err,null); |
||||
if ((!result)||(!result.location)) |
||||
return callback(new Error('null result'),null); |
||||
|
||||
cache.put(ip,JSON.stringify({ |
||||
ts: Date.now(), |
||||
r: result |
||||
}),function(err) { |
||||
if (err) |
||||
console.error('Error saving to cache: '+err); |
||||
return callback(null,result); |
||||
}); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
var linebuf = ''; |
||||
process.stdin.on('readable',function() { |
||||
var chunk; |
||||
while (null !== (chunk = process.stdin.read())) { |
||||
for(var i=0;i<chunk.length;++i) { |
||||
var c = chunk[i]; |
||||
if ((c == 0x0d)||(c == 0x0a)) { |
||||
if (linebuf.length > 0) { |
||||
var ip = linebuf; |
||||
lookup(ip,function(err,result) { |
||||
if ((err)||(!result)||(!result.location)) { |
||||
return process.stdout.write(ip+',0,0,0,0,0,0\n'); |
||||
} else { |
||||
var lat = parseFloat(result.location.latitude); |
||||
var lon = parseFloat(result.location.longitude); |
||||
|
||||
// Convert to X,Y,Z coordinates from Earth's origin, Earth-as-sphere approximation.
|
||||
var latRadians = lat * 0.01745329251994; // PI / 180
|
||||
var lonRadians = lon * 0.01745329251994; // PI / 180
|
||||
var cosLat = Math.cos(latRadians); |
||||
var x = Math.round((-6371.0) * cosLat * Math.cos(lonRadians)); // 6371 == Earth's approximate radius in kilometers
|
||||
var y = Math.round(6371.0 * Math.sin(latRadians)); |
||||
var z = Math.round(6371.0 * cosLat * Math.sin(lonRadians)); |
||||
|
||||
return process.stdout.write(ip+',1,'+lat+','+lon+','+x+','+y+','+z+'\n'); |
||||
} |
||||
}); |
||||
} |
||||
linebuf = ''; |
||||
} else { |
||||
linebuf += String.fromCharCode(c); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
|
||||
process.stdin.on('end',function() { |
||||
cache.close(); |
||||
process.exit(0); |
||||
}); |
||||
@ -0,0 +1,16 @@
|
||||
{ |
||||
"name": "cluster-geo", |
||||
"version": "1.0.0", |
||||
"description": "Cluster GEO-IP Query Service", |
||||
"main": "index.js", |
||||
"scripts": { |
||||
"test": "echo \"Error: no test specified\" && exit 1" |
||||
}, |
||||
"author": "ZeroTier, Inc.", |
||||
"license": "GPL-3.0", |
||||
"dependencies": { |
||||
"geoip2ws": "^1.7.1", |
||||
"leveldown": "^1.4.2", |
||||
"levelup": "^1.2.1" |
||||
} |
||||
} |
||||
Loading…
Reference in new issue