geoip-lite
Version:
A light weight native JavaScript implementation of GeoIP API from MaxMind
205 lines (170 loc) • 4.31 kB
JavaScript
var fs = require('fs'),
path = require('path'),
net = require('net');
(function() {
var ifile = path.join(__dirname, '/../data/geoip-country.dat');
var ifd = fs.openSync(ifile, "r");
var sz = fs.fstatSync(ifd).size;
var buff = new Buffer(sz);
fs.readSync(ifd, buff, 0, sz, 0);
fs.closeSync(ifd);
var lastline = sz/10-1;
var lastip = buff.readUInt32(lastline*10+4, true);
var firstip = buff.readUInt32(0, true);
lookup4 = function(ip) {
var fline=0, floor=lastip, cline=lastline, ceil=firstip, line;
if(ip > lastip || ip < firstip) {
return null;
}
do {
line = Math.round((cline-fline)/2)+fline;
floor = buff.readUInt32(line*10, true);
ceil = buff.readUInt32(line*10+4, true);
if(floor <= ip && ceil >= ip) {
var cc = buff.toString('utf8', line*10+8, line*10+10);
return { range: [floor, ceil], country: cc };
}
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 aton4(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);
}
function ntoa4(n) {
n = "" + (n>>>24&0xff) + "." + (n>>>16&0xff) + "." + (n>>>8&0xff) + "." + (n&0xff);
return n;
}
(function() {
var ifile = path.join(__dirname, '/../data/geoip-country6.dat');
var ifd = fs.openSync(ifile, "r");
var sz = fs.fstatSync(ifd).size;
var buff = new Buffer(sz);
fs.readSync(ifd, buff, 0, sz, 0);
fs.closeSync(ifd);
var lastline = sz/34-1;
// XXX We only use the first 8 bytes of an IPv6 address
// This identifies the network, but not the host within
// the network. Unless at some point of time we have a
// global peace treaty and single subnets span multiple
// countries, this should not be a problem.
function readip(line, offset) {
var ii=0, ip=[];
for(ii=0; ii<2; ii++)
ip.push(buff.readUInt32(line*34+offset*16+ii*4, true));
return ip;
}
var lastip=readip(lastline, 1), firstip=readip(0, 0);
lookup6 = function(ip) {
var fline=0, floor=lastip, cline=lastline, ceil=firstip, line;
if(cmp6(ip, lastip) > 0 || cmp6(ip, firstip) < 0) {
return null;
}
do {
line = Math.round((cline-fline)/2)+fline;
floor = readip(line, 0);
ceil = readip(line, 1);
if(cmp6(floor, ip) <= 0 && cmp6(ceil, ip) >= 0) {
var cc = buff.toString('utf8', line*34+32, line*34+34);
return { range: [floor, ceil], country: cc };
}
else if(fline == cline) {
return null;
}
else if(fline == cline-1) {
if(line == fline)
fline = cline;
else
cline = fline;
}
else if(cmp6(floor, ip) > 0) {
cline = line;
}
else if(cmp6(ceil, ip) < 0) {
fline = line;
}
} while(1);
};
})();
//lookup4 = gen_lookup('geoip-country.dat', 4);
//lookup6 = gen_lookup('geoip-country6.dat', 16);
function cmp6(a, b) {
for(var ii=0; ii<2; ii++) {
if(a[ii] < b[ii]) return -1;
if(a[ii] > b[ii]) return 1;
}
return 0;
}
function aton6(a) {
var l, i;
a = a.replace(/"/g, '').split(/:/);
l = a.length-1;
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);
}
var r = [];
for(i=0; i<4; i++) {
r.push(((a[2*i]<<16)+a[2*i+1])>>>0);
}
return r;
}
function ntoa6(n) {
var a = "[";
for(var 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;
}
exports.lookup = function(ip) {
if(typeof ip == 'number')
return lookup4(ip);
if(net.isIP(ip) === 4)
return lookup4(aton4(ip));
if(net.isIP(ip) === 6)
return lookup6(aton6(ip));
return null;
};
exports.pretty = function(n) {
if(typeof n == 'string')
return n;
if(typeof n == 'number')
return ntoa4(n);
if(n instanceof Array)
return ntoa6(n);
return n;
}
exports.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 cmp6(a, b);
return null;
}