UNPKG

2.98 kBJavaScriptView Raw
1#!/usr/bin/env node
2"use strict";
3
4const fs = require("fs");
5const path = require("path");
6const url = require("url");
7
8const countries = require("country-data").countries;
9const fetch = require("make-fetch-happen");
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 = Object.assign({
22 url: "https://linuxnet.ca/ieee/oui.txt",
23 file: path.join(__dirname, "oui.json"),
24 }, opts);
25
26 const uri = url.parse(opts.url);
27 if (!uri.protocol || !uri.hostname) {
28 return reject(new Error(`Invalid source URL '${opts.url}'`));
29 }
30
31 fetch(opts.url).then(res => res.text()).then(body => {
32 if (!body || !body.length || !/^(OUI|[#]|[A-Fa-f0-9])/.exec(body)) {
33 throw new Error("Downloaded file does not look like a oui.txt file");
34 } else {
35 return parse(body.split("\n"));
36 }
37 }).then(result => {
38 if (opts.test) return resolve(result);
39
40 // save oui.js
41 fs.writeFile(opts.file, stringify(result, stringifyOpts), err => {
42 if (err) return reject(err);
43 if (!opts.web) return resolve(result);
44
45 // update oui.web.js
46 const resultShort = {};
47 Object.keys(result).map(key => {
48 resultShort[key] = result[key].match(/^.*$/m)[0];
49 });
50
51 const web = path.join(__dirname, "oui.web.js");
52 fs.readFile(web, "utf8", (err, js) => {
53 if (err) return reject(err);
54 js = js.replace(/var db =.+/, `var db = ${stringify(resultShort)};`);
55 fs.writeFile(web, js, err => {
56 if (err) return reject(err);
57 resolve(result);
58 });
59 });
60 });
61 }).catch(reject);
62 });
63};
64
65function isStart(firstLine, secondLine) {
66 if (firstLine === undefined || secondLine === undefined) return false;
67 return firstLine.trim().length === 0 && /([0-9A-F]{2}[-]){2}([0-9A-F]{2})/.test(secondLine);
68}
69
70function parse(lines) {
71 const result = {};
72 let i = 3;
73 while (i !== lines.length) {
74 if (isStart(lines[i], lines[i + 1])) {
75 let oui = lines[i + 2].substring(0, 6).trim();
76 let owner = lines[i + 1].replace(/\((hex|base 16)\)/, "").substring(10).trim();
77
78 i += 3;
79 while (!isStart(lines[i], lines[i + 1]) && i < lines.length) {
80 if (lines[i] && lines[i].trim()) owner += `\n${lines[i].trim()}`;
81 i++;
82 }
83
84 // ensure upper case on hex digits
85 oui = oui.toUpperCase();
86
87 // remove excessive whitespace
88 owner = owner.replace(/[ \t]+/gm, " ");
89
90 // replace country shortcodes
91 const shortCode = (/\n([A-Z]{2})$/.exec(owner) || [])[1];
92 if (shortCode && countries[shortCode]) {
93 owner = owner.replace(/\n.+$/, `\n${countries[shortCode].name}`);
94 }
95
96 result[oui] = owner;
97 }
98 }
99 return result;
100}