summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-upnp/root/usr/share/rpcd
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2022-09-21 23:38:46 +0200
committerJo-Philipp Wich <jo@mein.io>2022-10-25 01:03:37 +0200
commit287775351bf39475f070ffc4ff4162f025ebfd81 (patch)
tree21a497bea4dc3058765e8c25f8a77f917c40e7d2 /applications/luci-app-upnp/root/usr/share/rpcd
parentcea2c3578efe7ab36219f06985d01a4771aeb070 (diff)
luci-app-upnp: convert rpcd backend script to ucode
Utilize the rpcd ucode plugin to reimplement the upnp backend ubus actions in ucode, simplifying the implementation and roughly halving the processing time for the `get_status` call. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'applications/luci-app-upnp/root/usr/share/rpcd')
-rw-r--r--applications/luci-app-upnp/root/usr/share/rpcd/ucode/luci.upnp139
1 files changed, 139 insertions, 0 deletions
diff --git a/applications/luci-app-upnp/root/usr/share/rpcd/ucode/luci.upnp b/applications/luci-app-upnp/root/usr/share/rpcd/ucode/luci.upnp
new file mode 100644
index 0000000000..9ee47f2968
--- /dev/null
+++ b/applications/luci-app-upnp/root/usr/share/rpcd/ucode/luci.upnp
@@ -0,0 +1,139 @@
+// 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 };
+
+