Skip to content

Commit 3b0d160

Browse files
committed
Update
1 parent ae4e5b2 commit 3b0d160

File tree

6 files changed

+102
-77
lines changed

6 files changed

+102
-77
lines changed

index.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
interface GeoIp2Location {
1+
export interface GeoIp2Location {
22
country: string;
33
region: string;
44
isEu: boolean;
55
timezone: string;
66
city: string;
7-
ll: [number | null, number | null];
7+
ll: [number, number] | [null, null];
88
metro: number | null;
99
area: number | null;
1010
}

index.js

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const privateRange4 = [
3434
const conf4 = {
3535
firstIP: null,
3636
lastIP: null,
37-
lastLine: 0,
37+
lastRecordIdx: 0,
3838
locationBuffer: null,
3939
locationRecordSize: 88,
4040
mainBuffer: null,
@@ -44,7 +44,7 @@ const conf4 = {
4444
const conf6 = {
4545
firstIP: null,
4646
lastIP: null,
47-
lastLine: 0,
47+
lastRecordIdx: 0,
4848
mainBuffer: null,
4949
recordSize: 48,
5050
};
@@ -56,8 +56,10 @@ const RECORD_SIZE = 10;
5656
const RECORD_SIZE6 = 34;
5757

5858
const lookup4 = ip => {
59+
if (!cache4.mainBuffer) return null;
60+
5961
let fline = 0;
60-
let cline = cache4.lastLine;
62+
let cline = cache4.lastRecordIdx;
6163
let floor;
6264
let ceil;
6365
let line, locId;
@@ -68,12 +70,13 @@ const lookup4 = ip => {
6870
const recordSize = cache4.recordSize;
6971
const locRecordSize = cache4.locationRecordSize;
7072

71-
const geoData = createGeoData();
7273
if (ip > cache4.lastIP || ip < cache4.firstIP) return null;
7374
for (let i = 0; i < privateRange.length; i++) {
7475
if (ip >= privateRange[i][0] && ip <= privateRange[i][1]) return null;
7576
}
7677

78+
const geoData = createGeoData();
79+
7780
while (true) {
7881
line = Math.round((cline - fline) / 2) + fline;
7982
const offset = line * recordSize;
@@ -82,7 +85,7 @@ const lookup4 = ip => {
8285

8386
if (floor <= ip && ceil >= ip) {
8487
if (recordSize === RECORD_SIZE) {
85-
geoData.country = buffer.toString('utf8', offset + 8, offset + 10);
88+
geoData.country = removeNullTerminator(buffer.toString('utf8', offset + 8, offset + 10));
8689
} else {
8790
locId = buffer.readUInt32BE(offset + 8);
8891
populateGeoDataFromLocation({
@@ -115,21 +118,23 @@ const lookup4 = ip => {
115118
};
116119

117120
const lookup6 = ip => {
121+
if (!cache6.mainBuffer) return null;
122+
118123
const buffer = cache6.mainBuffer;
119124
const recordSize = cache6.recordSize;
120125
const locBuffer = cache4.locationBuffer;
121126
const locRecordSize = cache4.locationRecordSize;
122127

123-
const geoData = createGeoData();
124-
125128
let fline = 0;
126-
let cline = cache6.lastLine;
129+
let cline = cache6.lastRecordIdx;
127130
let floor;
128131
let ceil;
129132
let line, locId;
130133

131134
if (cmp6(ip, cache6.lastIP) > 0 || cmp6(ip, cache6.firstIP) < 0) return null;
132135

136+
const geoData = createGeoData();
137+
133138
while (true) {
134139
line = Math.round((cline - fline) / 2) + fline;
135140
floor = readIp6(buffer, line, recordSize, 0);
@@ -179,17 +184,8 @@ const get4mapped = ip => {
179184
};
180185

181186
const readFileBuffer = async filePath => {
182-
const fileHandle = await fsPromises.open(filePath, 'r');
183-
try {
184-
const { size } = await fileHandle.stat();
185-
const buffer = Buffer.alloc(size);
186-
if (size > 0) {
187-
await fileHandle.read(buffer, 0, size, 0);
188-
}
189-
return { buffer, size };
190-
} finally {
191-
await fileHandle.close();
192-
}
187+
const buffer = await fsPromises.readFile(filePath);
188+
return { buffer, size: buffer.length };
193189
};
194190

195191
const isExpectedMissingDataError = err => err?.code === 'ENOENT' || err?.code === 'EBADF';
@@ -210,13 +206,14 @@ const preloadAsync = async () => {
210206
mainData = await readFileBuffer(dataFiles.city);
211207
} catch (err) {
212208
if (!isExpectedMissingDataError(err)) throw err;
209+
asyncCache.locationBuffer = null;
213210
mainData = await readFileBuffer(dataFiles.country);
214211
asyncCache.recordSize = RECORD_SIZE;
215212
}
216213

217214
asyncCache.mainBuffer = mainData.buffer;
218-
asyncCache.lastLine = (mainData.size / asyncCache.recordSize) - 1;
219-
asyncCache.lastIP = asyncCache.mainBuffer.readUInt32BE((asyncCache.lastLine * asyncCache.recordSize) + 4);
215+
asyncCache.lastRecordIdx = (mainData.size / asyncCache.recordSize) - 1;
216+
asyncCache.lastIP = asyncCache.mainBuffer.readUInt32BE((asyncCache.lastRecordIdx * asyncCache.recordSize) + 4);
220217
asyncCache.firstIP = asyncCache.mainBuffer.readUInt32BE(0);
221218
cache4 = asyncCache;
222219
};
@@ -248,6 +245,7 @@ const preload = callback => {
248245
throw err;
249246
}
250247

248+
cache4.locationBuffer = null;
251249
datFile = openSync(dataFiles.country, 'r');
252250
datSize = fstatSync(datFile).size;
253251
cache4.recordSize = RECORD_SIZE;
@@ -257,8 +255,8 @@ const preload = callback => {
257255
readSync(datFile, cache4.mainBuffer, 0, datSize, 0);
258256
closeSync(datFile);
259257

260-
cache4.lastLine = (datSize / cache4.recordSize) - 1;
261-
cache4.lastIP = cache4.mainBuffer.readUInt32BE((cache4.lastLine * cache4.recordSize) + 4);
258+
cache4.lastRecordIdx = (datSize / cache4.recordSize) - 1;
259+
cache4.lastIP = cache4.mainBuffer.readUInt32BE((cache4.lastRecordIdx * cache4.recordSize) + 4);
262260
cache4.firstIP = cache4.mainBuffer.readUInt32BE(0);
263261
}
264262
};
@@ -283,8 +281,8 @@ const preload6Async = async () => {
283281
}
284282

285283
asyncCache6.mainBuffer = mainData.buffer;
286-
asyncCache6.lastLine = (mainData.size / asyncCache6.recordSize) - 1;
287-
asyncCache6.lastIP = readIp6(asyncCache6.mainBuffer, asyncCache6.lastLine, asyncCache6.recordSize, 1);
284+
asyncCache6.lastRecordIdx = (mainData.size / asyncCache6.recordSize) - 1;
285+
asyncCache6.lastIP = readIp6(asyncCache6.mainBuffer, asyncCache6.lastRecordIdx, asyncCache6.recordSize, 1);
288286
asyncCache6.firstIP = readIp6(asyncCache6.mainBuffer, 0, asyncCache6.recordSize, 0);
289287
cache6 = asyncCache6;
290288
};
@@ -319,23 +317,25 @@ const preload6 = callback => {
319317
readSync(datFile, cache6.mainBuffer, 0, datSize, 0);
320318
closeSync(datFile);
321319

322-
cache6.lastLine = (datSize / cache6.recordSize) - 1;
323-
cache6.lastIP = readIp6(cache6.mainBuffer, cache6.lastLine, cache6.recordSize, 1);
320+
cache6.lastRecordIdx = (datSize / cache6.recordSize) - 1;
321+
cache6.lastIP = readIp6(cache6.mainBuffer, cache6.lastRecordIdx, cache6.recordSize, 1);
324322
cache6.firstIP = readIp6(cache6.mainBuffer, 0, cache6.recordSize, 0);
325323
}
326324
};
327325

328326
const runAsyncReload = callback => {
329-
preloadAsync()
330-
.then(() => preload6Async())
327+
Promise.all([preloadAsync(), preload6Async()])
331328
.then(() => callback())
332329
.catch(callback);
333330
};
334331

335332
module.exports = {
336333
lookup: ip => {
337334
if (ip === undefined || ip === null) throw new TypeError('lookup(ip) requires an IP address');
338-
if (typeof ip === 'number') return lookup4(ip);
335+
if (typeof ip === 'number') {
336+
if (!Number.isFinite(ip) || !Number.isInteger(ip) || ip < 0) return null;
337+
return lookup4(ip);
338+
}
339339

340340
const ipVersion = isIP(ip);
341341
if (ipVersion === 4) {
@@ -394,4 +394,3 @@ module.exports = {
394394

395395
preload();
396396
preload6();
397-

scripts/utils.js

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@ const utils = module.exports = {};
22

33
utils.aton4 = a => {
44
const parts = a.split('.');
5-
return ((parseInt(parts[0], 10) << 24) >>> 0) + ((parseInt(parts[1], 10) << 16) >>> 0) + ((parseInt(parts[2], 10) << 8) >>> 0) + (parseInt(parts[3], 10) >>> 0);
5+
return ((Number.parseInt(parts[0], 10) << 24) >>> 0) + ((Number.parseInt(parts[1], 10) << 16) >>> 0) + ((Number.parseInt(parts[2], 10) << 8) >>> 0) + (Number.parseInt(parts[3], 10) >>> 0);
66
};
77

88
utils.ntoa4 = n => {
99
return ((n >>> 24) & 0xff) + '.' + ((n >>> 16) & 0xff) + '.' + ((n >>> 8) & 0xff) + '.' + (n & 0xff);
1010
};
1111

1212
utils.aton6 = a => {
13-
a = a.replace(/"/g, '');
14-
1513
let parts;
1614
const omitStart = a.indexOf('::');
1715
if (omitStart >= 0) {
@@ -27,15 +25,15 @@ utils.aton6 = a => {
2725
}
2826

2927
for (let i = 0; i < 8; i++) {
30-
parts[i] = parseInt(parts[i] || '0', 16);
31-
}
32-
33-
const r = [];
34-
for (let i = 0; i < 4; i++) {
35-
r.push(((parts[2 * i] << 16) + parts[(2 * i) + 1]) >>> 0);
28+
parts[i] = Number.parseInt(parts[i] || '0', 16);
3629
}
3730

38-
return r;
31+
return [
32+
((parts[0] << 16) + parts[1]) >>> 0,
33+
((parts[2] << 16) + parts[3]) >>> 0,
34+
((parts[4] << 16) + parts[5]) >>> 0,
35+
((parts[6] << 16) + parts[7]) >>> 0,
36+
];
3937
};
4038

4139
const ipv4ToUint32Strict = ip => {
@@ -158,7 +156,7 @@ utils.ntoa6 = n => {
158156

159157
utils.cmp = (a, b) => {
160158
if (typeof a === 'number' && typeof b === 'number') return (a < b ? -1 : (a > b ? 1 : 0));
161-
if (a instanceof Array && b instanceof Array) return utils.cmp6(a, b);
159+
if (Array.isArray(a) && Array.isArray(b)) return utils.cmp6(a, b);
162160

163161
return null;
164162
};
@@ -181,12 +179,14 @@ utils.removeNullTerminator = str => {
181179
return nullIndex === -1 ? str : str.substring(0, nullIndex);
182180
};
183181

184-
utils.readIp6 = (buffer, line, recordSize, offset) => {
185-
const ipArray = [];
186-
for (let i = 0; i < 4; i++) {
187-
ipArray.push(buffer.readUInt32BE((line * recordSize) + (offset * 16) + (i * 4)));
188-
}
189-
return ipArray;
182+
utils.readIp6 = (buffer, line, recordSize, ipIndex) => {
183+
const base = (line * recordSize) + (ipIndex * 16);
184+
return [
185+
buffer.readUInt32BE(base),
186+
buffer.readUInt32BE(base + 4),
187+
buffer.readUInt32BE(base + 8),
188+
buffer.readUInt32BE(base + 12),
189+
];
190190
};
191191

192192
utils.createGeoData = () => ({

test/advanced.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,19 @@ describe('Advanced geoIp Tests', () => {
217217
expect(result).toBeNull();
218218
});
219219

220+
it('should return null for NaN input', () => {
221+
expect(geoIp.lookup(NaN)).toBeNull();
222+
});
223+
224+
it('should return null for float input', () => {
225+
expect(geoIp.lookup(1.5)).toBeNull();
226+
});
227+
228+
it('should return null for Infinity input', () => {
229+
expect(geoIp.lookup(Infinity)).toBeNull();
230+
expect(geoIp.lookup(-Infinity)).toBeNull();
231+
});
232+
220233
it('should handle numbers larger than max IPv4', () => {
221234
const result = geoIp.lookup(4294967296);
222235
expect(result).toBeNull();

test/index.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,19 @@ describe('GeoIP2', () => {
153153
const after6 = geoIp.lookup('::ffff:173.185.182.82');
154154
expect(before6).toEqual(after6);
155155
});
156+
157+
it('should return null for 0.0.0.0 after clear()', () => {
158+
geoIp.clear();
159+
expect(geoIp.lookup('0.0.0.0')).toBeNull();
160+
geoIp.reloadDataSync();
161+
});
162+
163+
it('should return null for native IPv6 after clear()', () => {
164+
geoIp.clear();
165+
expect(geoIp.lookup('2001:4860:4860::8888')).toBeNull();
166+
expect(geoIp.lookup('::1')).toBeNull();
167+
geoIp.reloadDataSync();
168+
});
156169
});
157170

158171
describe('#testAsyncReload', () => {

0 commit comments

Comments
 (0)