3 import * as uloop from "uloop";
4 import * as libubus from "ubus";
5 import { readfile, glob, basename } from "fs";
6 let uht = require("uht");
7 push(REQUIRE_SEARCH_PATH, "/usr/share/ufp/*.uc");
10 let ubus = libubus.connect();
11 let fingerprints = {};
19 function get_weight(type) {
23 type = split(type, "-");
27 type = join("-", type);
33 function match_fingerprint(key)
38 fp = fingerprint_ht.get(null, key);
39 user_fp = fingerprints[key];
43 for (entry in user_fp)
55 fingerprints: fingerprints,
58 load_fingerprint_json: function(file) {
59 let data = json(readfile(file));
63 get_weight: get_weight,
65 add_weight: function(data) {
66 for (let entry in data)
67 weight[entry] = data[entry];
70 device_refresh: function(mac) {
72 let dev = devices[mac];
76 dev.timestamp = time();
79 device_add_data: function(mac, line) {
81 let dev = devices[mac];
83 dev = devices[mac] = {
88 let oui = "mac-oui-" + join("", slice(split(mac, ":"), 0, 3));
89 dev.data[oui] = `${oui}|1`;
92 if (substr(line, 0, 1) == "%") {
93 line = substr(line, 1);
94 let meta = split(line, "|", 3);
98 dev.meta[meta[0]] ??= {};
99 if (!get_weight(meta[1]))
102 dev.meta[meta[0]][meta[1]] = meta[2];
106 let fp = split(line, "|", 2);
110 dev.data[fp[0]] = line;
114 function load_plugins()
116 let plugins = glob("/usr/share/ufp/plugin_*.uc");
117 for (let name in plugins) {
118 name = substr(basename(name), 0, -3);
120 let plugin = require(name);
122 push(global.plugins, plugin);
124 warn(`Failed to load plugin ${name}: ${e}\n${e.stacktrace[0].context}\n`);
129 function refresh_plugins()
131 for (let plugin in global.plugins) {
138 warn(`Failed to refresh plugin: ${e}\n${e.stacktrace[0].context}\n`);
145 gc_timer.set(60 * 60 * 1000);
146 let timeout = time() - 60 * 60 * 24;
148 for (let mac in devices) {
149 if (devices[mac].timestamp < timeout)
154 // returns: { "<meta>": { "<val>": [ <weight>, [ <fingerprints> ] ] } }
155 function __device_match_list(mac)
157 let dev = devices[mac];
158 if (!dev || !length(dev))
165 for (let fp in data) {
166 let match = match_fingerprint(data[fp]);
170 for (let match_cur in match)
171 push(match_devs, [ match_cur, global.get_weight(fp), fp ]);
174 for (let meta in dev.meta) {
175 let meta_cur = dev.meta[meta];
176 for (let type in meta_cur) {
178 match[meta] = meta_cur[type];
179 push(match_devs, [ match, global.get_weight(type), type ]);
183 for (let i = 0; i < length(match_devs); i++) {
184 let match = match_devs[i];
185 let match_data = match[0];
186 let match_weight = match[1];
187 let match_fp = [ match[2] ];
190 for (let j = 0; j < length(match_devs); j++) {
194 let cur = match_devs[j];
195 let cur_data = cur[0];
196 for (let key in cur_data) {
197 if (lc(match_data[key]) == lc(cur_data[key])) {
198 match_weight += cur[1];
199 push(match_fp, cur[2]);
205 for (let key in match_data) {
206 let val = match_data[key];
208 let ret_key = ret[key];
210 ret_key[val] ??= [ 0.0, {} ];
211 let ret_val = ret_key[val];
213 ret_val[0] += match_weight;
214 for (let fp in match_fp)
219 for (let key in ret) {
220 let ret_key = ret[key];
221 for (let val in ret_key) {
222 let ret_val = ret_key[val];
223 ret_val[1] = keys(ret_val[1]);
230 function device_match_list(mac)
232 let match = __device_match_list(mac);
234 for (let meta in match) {
235 let match_meta = match[meta];
236 let meta_list = keys(match_meta);
237 sort(meta_list, (a, b) => match_meta[b][0] - match_meta[a][0]);
238 match[meta] = map(meta_list, (key) => [ key, match_meta[key][0], match_meta[key][1] ]);
244 global.ubus_object = {
249 call: function(req) {
250 let file = req.args.file;
252 return libubus.STATUS_INVALID_ARGUMENT;
255 global.load_fingerprint_json(file);
257 warn(`Exception in ubus function: ${e}\n${e.stacktrace[0].context}, file=${file}\n`);
258 return libubus.STATUS_INVALID_ARGUMENT;
269 call: function(req) {
270 let mac = req.args.macaddr;
277 let dev = devices[mac];
279 return libubus.STATUS_NOT_FOUND;
290 call: function(req) {
291 let mac = req.args.macaddr;
292 let data = req.args.data;
294 return libubus.STATUS_INVALID_ARGUMENT;
296 for (let line in data)
297 global.device_add_data(mac, line);
308 call: function(req) {
311 let mac_list = req.args.macaddr ? [ req.args.macaddr ] : keys(devices);
314 for (let mac in mac_list) {
315 let match_list = device_match_list(mac);
317 return libubus.STATUS_NOT_FOUND;
324 for (let meta in match_list) {
325 let match_meta = match_list[meta];
327 if (length(match_meta) < 1)
330 match_meta = match_meta[0];
332 cur_ret[meta] = match_meta[0];
334 cur_ret.weight[meta] = match_meta[1];
338 return req.args.macaddr ? ret[req.args.macaddr] : ret;
346 call: function(req) {
349 let mac_list = req.args.macaddr ? [ req.args.macaddr ] : keys(devices);
352 for (let mac in mac_list) {
353 let match_list = device_match_list(mac);
355 return libubus.STATUS_NOT_FOUND;
360 for (let meta in match_list)
361 cur_ret[meta] = match_list[meta];
364 return req.args.macaddr ? ret[req.args.macaddr] : ret;
370 fingerprint_ht = uht.open("/usr/share/ufp/devices.bin");
372 warn(`Failed to load fingerprints: ${e}\n${e.stacktrace[0].context}\n`);
375 ubus.publish("fingerprint", global.ubus_object);
376 gc_timer = uloop.timer(1000, device_gc);