140 lines
3.2 KiB
Plaintext
140 lines
3.2 KiB
Plaintext
// Copyright 2022 Jo-Philipp Wich <jo@mein.io>
|
|
// Licensed to the public under the Apache License 2.0.
|
|
|
|
'use strict';
|
|
|
|
import { access, open, popen } from 'fs';
|
|
import { connect } from 'ubus';
|
|
import { cursor } from 'uci';
|
|
|
|
// Establish ubus connection persistently outside of the call handler scope to
|
|
// prevent premature GC'ing. Can be moved into `get_status` callback once
|
|
// https://github.com/jow-/ucode/commit/a58fe4709f661b5f28e26701ea8638efccf5aeb6
|
|
// is merged.
|
|
const ubus = connect();
|
|
|
|
const methods = {
|
|
get_status: {
|
|
call: function(req) {
|
|
const uci = cursor();
|
|
|
|
const rules = [];
|
|
const leases = [];
|
|
|
|
const leasefile = open(uci.get('upnpd', 'config', 'upnp_lease_file'), 'r');
|
|
|
|
if (leasefile) {
|
|
for (let line = leasefile.read('line'); length(line); line = leasefile.read('line')) {
|
|
const record = split(line, ':', 6);
|
|
|
|
if (length(record) == 6) {
|
|
push(leases, {
|
|
proto: uc(record[0]),
|
|
extport: +record[1],
|
|
intaddr: arrtoip(iptoarr(record[2])),
|
|
intport: +record[3],
|
|
expiry: +record[4],
|
|
description: trim(record[5])
|
|
});
|
|
}
|
|
}
|
|
|
|
leasefile.close();
|
|
}
|
|
|
|
const ipt = popen('iptables --line-numbers -t nat -xnvL MINIUPNPD 2>/dev/null');
|
|
|
|
if (ipt) {
|
|
for (let line = ipt.read('line'); length(line); line = ipt.read('line')) {
|
|
let m = match(line, /^([0-9]+)\s+([a-z]+).+dpt:([0-9]+) to:(\S+):([0-9]+)/);
|
|
|
|
if (m) {
|
|
push(rules, {
|
|
num: m[1],
|
|
proto: uc(m[2]),
|
|
extport: +m[3],
|
|
intaddr: arrtoip(iptoarr(m[4])),
|
|
intport: +m[5],
|
|
descr: ''
|
|
});
|
|
}
|
|
}
|
|
|
|
ipt.close();
|
|
}
|
|
|
|
const nft = popen('nft --handle list chain inet fw4 upnp_prerouting 2>/dev/null');
|
|
|
|
if (nft) {
|
|
for (let line = nft.read('line'), num = 1; length(line); line = nft.read('line')) {
|
|
let m = match(line, /^\t\tiif ".+" @nh,72,8 (0x6|0x11) th dport ([0-9]+) dnat ip to ([0-9.]+):([0-9]+)/);
|
|
|
|
if (m) {
|
|
push(rules, {
|
|
num: `${num}`,
|
|
proto: (m[1] == '0x6') ? 'TCP' : 'UDP',
|
|
extport: +m[2],
|
|
intaddr: arrtoip(iptoarr(m[3])),
|
|
intport: +m[4],
|
|
descr: ''
|
|
});
|
|
|
|
num++;
|
|
}
|
|
}
|
|
|
|
nft.close();
|
|
}
|
|
|
|
return ubus.defer('luci-rpc', 'getHostHints', {}, function(rc, host_hints) {
|
|
for (let rule in rules) {
|
|
for (let lease in leases) {
|
|
if (lease.proto == rule.proto &&
|
|
lease.intaddr == rule.intaddr &&
|
|
lease.intport == rule.intport &&
|
|
lease.extport == rule.extport)
|
|
{
|
|
rule.descr = lease.description;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (let mac, hint in host_hints) {
|
|
if (rule.intaddr in hint.ipaddrs) {
|
|
rule.host_hint = hint.name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
req.reply({ rules });
|
|
});
|
|
}
|
|
},
|
|
|
|
delete_rule: {
|
|
args: { token: 'token' },
|
|
call: function(req) {
|
|
const idx = +req.args?.token;
|
|
|
|
if (idx > 0) {
|
|
const uci = cursor();
|
|
const leasefile = uci.get('upnpd', 'config', 'upnp_lease_file');
|
|
|
|
if (access(leasefile)) {
|
|
system(['sed', '-i', '-e', `${idx}d`, leasefile]);
|
|
system(['/etc/init.d/miniupnpd', 'restart']);
|
|
}
|
|
|
|
return { result: 'OK' };
|
|
}
|
|
|
|
return { result: 'Bad request' };
|
|
}
|
|
}
|
|
};
|
|
|
|
return { 'luci.upnp': methods };
|
|
|
|
|