UNPKG

2.44 kBJavaScriptView Raw
1#!/usr/bin/env node
2"use strict";
3
4const fs = require("node:fs");
5const path = require("node:path");
6const url = require("node:url");
7
8const countries = require("country-data").countries;
9const fetch = require("fetch-enhanced")(require("node-fetch"));
10const stringify = require("json-stable-stringify");
11
12const stringifyOpts = {
13 space: 1,
14 cmp: (a, b) => {
15 return parseInt(a.key, 16) > parseInt(b.key, 16) ? 1 : -1;
16 },
17};
18
19module.exports = function update(opts) {
20 return new Promise((resolve, reject) => {
21 opts = {url: "https://standards-oui.ieee.org/oui/oui.txt",
22 file: path.join(__dirname, "oui.json"), ...opts};
23
24 const uri = url.parse(opts.url);
25 if (!uri.protocol || !uri.hostname) {
26 return reject(new Error(`Invalid source URL '${opts.url}'`));
27 }
28
29 fetch(opts.url).then(res => res.text()).then(body => {
30 if (!body || !body.length || !/^(OUI|[#]|[A-Fa-f0-9])/.test(body)) {
31 throw new Error("Downloaded file does not look like a oui.txt file");
32 } else {
33 return parse(body.split("\n"));
34 }
35 }).then(result => {
36 if (opts.test) return resolve(result);
37
38 // save oui.js
39 fs.writeFile(opts.file, stringify(result, stringifyOpts), err => {
40 if (err) return reject(err);
41 resolve(result);
42 });
43 }).catch(reject);
44 });
45};
46
47function isStart(firstLine, secondLine) {
48 if (firstLine === undefined || secondLine === undefined) return false;
49 return firstLine.trim().length === 0 && /([0-9A-F]{2}[-]){2}([0-9A-F]{2})/.test(secondLine);
50}
51
52function parse(lines) {
53 const result = {};
54 let i = 3;
55 while (i !== lines.length) {
56 if (isStart(lines[i], lines[i + 1])) {
57 let oui = lines[i + 2].substring(0, 6).trim();
58 let owner = lines[i + 1].replace(/\((hex|base 16)\)/, "").substring(10).trim();
59
60 i += 3;
61 while (!isStart(lines[i], lines[i + 1]) && i < lines.length) {
62 if (lines[i] && lines[i].trim()) owner += `\n${lines[i].trim()}`;
63 i++;
64 }
65
66 // ensure upper case on hex digits
67 oui = oui.toUpperCase();
68
69 // remove excessive whitespace
70 owner = owner.replace(/[ \t]+/g, " ");
71
72 // replace country shortcodes
73 const shortCode = (/\n([A-Z]{2})$/.exec(owner) || [])[1];
74 if (shortCode && countries[shortCode]) {
75 owner = owner.replace(/\n.+$/, `\n${countries[shortCode].name}`);
76 }
77
78 result[oui] = owner;
79 }
80 }
81 return result;
82}