UNPKG

3.03 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: "http://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 if (opts.test) return resolve(); // ignore
34 throw new Error("Downloaded file does not look like a oui.txt file");
35 } else {
36 return parse(body.split("\n"));
37 }
38 }).then(result => {
39 if (opts.test) return resolve(result);
40
41 // save oui.js
42 fs.writeFile(opts.file, stringify(result, stringifyOpts), err => {
43 if (err) return reject(err);
44 if (!opts.web) return resolve(result);
45
46 // update oui.web.js
47 const resultShort = {};
48 Object.keys(result).map(key => {
49 resultShort[key] = result[key].match(/^.*$/m)[0];
50 });
51
52 const web = path.join(__dirname, "oui.web.js");
53 fs.readFile(web, "utf8", (err, js) => {
54 if (err) return reject(err);
55 js = js.replace(/var db =.+/, `var db = ${stringify(resultShort)};`);
56 fs.writeFile(web, js, err => {
57 if (err) return reject(err);
58 resolve(result);
59 });
60 });
61 });
62 }).catch(reject);
63 });
64};
65
66function isStart(firstLine, secondLine) {
67 if (firstLine === undefined || secondLine === undefined) return false;
68 return firstLine.trim().length === 0 && /([0-9A-F]{2}[-]){2}([0-9A-F]{2})/.test(secondLine);
69}
70
71function parse(lines) {
72 const result = {};
73 let i = 3;
74 while (i !== lines.length) {
75 if (isStart(lines[i], lines[i + 1])) {
76 let oui = lines[i + 2].substring(0, 6).trim();
77 let owner = lines[i + 1].replace(/\((hex|base 16)\)/, "").substring(10).trim();
78
79 i += 3;
80 while (!isStart(lines[i], lines[i + 1]) && i < lines.length) {
81 if (lines[i] && lines[i].trim()) owner += `\n${lines[i].trim()}`;
82 i++;
83 }
84
85 // ensure upper case on hex digits
86 oui = oui.toUpperCase();
87
88 // remove excessive whitespace
89 owner = owner.replace(/[ \t]+/gm, " ");
90
91 // replace country shortcodes
92 const shortCode = (/\n([A-Z]{2})$/.exec(owner) || [])[1];
93 if (shortCode && countries[shortCode]) {
94 owner = owner.replace(/\n.+$/, `\n${countries[shortCode].name}`);
95 }
96
97 result[oui] = owner;
98 }
99 }
100 return result;
101}