[openwrt/openwrt] umdns: add cli module

LEDE Commits lede-commits at lists.infradead.org
Thu Feb 13 10:01:16 PST 2025


nbd pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/562ced3d8f7bceb0f5c072b0d4c2cf6ffb6c383d

commit 562ced3d8f7bceb0f5c072b0d4c2cf6ffb6c383d
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Wed Feb 12 20:04:10 2025 +0100

    umdns: add cli module
    
    This makes it easy to browse mDNS services and hosts on the local network
    
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
 package/network/services/umdns/Makefile      |   7 +-
 package/network/services/umdns/files/mdns.uc | 267 +++++++++++++++++++++++++++
 2 files changed, 273 insertions(+), 1 deletion(-)

diff --git a/package/network/services/umdns/Makefile b/package/network/services/umdns/Makefile
index 9a2763d5b2..60e0eb9ac4 100644
--- a/package/network/services/umdns/Makefile
+++ b/package/network/services/umdns/Makefile
@@ -37,7 +37,12 @@ define Package/umdns/conffiles
 endef
 
 define Package/umdns/install
-	$(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/init.d $(1)/etc/config
+	$(INSTALL_DIR) \
+		$(1)/usr/sbin \
+		$(1)/etc/init.d \
+		$(1)/etc/config \
+		$(1)/usr/share/ucode/cli/modules
+	$(INSTALL_DATA) ./files/mdns.uc $(1)/usr/share/ucode/cli/modules
 	$(INSTALL_BIN) $(PKG_BUILD_DIR)/umdns $(1)/usr/sbin/
 	$(INSTALL_BIN) ./files/umdns.init $(1)/etc/init.d/umdns
 	$(INSTALL_CONF) ./files/umdns.config $(1)/etc/config/umdns
diff --git a/package/network/services/umdns/files/mdns.uc b/package/network/services/umdns/files/mdns.uc
new file mode 100644
index 0000000000..5b767847aa
--- /dev/null
+++ b/package/network/services/umdns/files/mdns.uc
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd at nbd.name>
+'use strict';
+
+function mdns_data(model)
+{
+	return model.ubus.call("umdns", "browse", { array: true, address: false });
+}
+
+function refresh_timer(model)
+{
+	model.ubus.call("umdns", "update");
+
+	model.mdns.refresh_count++;
+	if (model.mdns.refresh_count < 3)
+		model.mdns.timer.set(500);
+
+	if (model.mdns.refresh_count < 2)
+		return;
+
+	let data = mdns_data(model);
+	for (let service_name, service_data in data) {
+		for (let host_name, host_data in service_data) {
+			let interface = host_data.iface;
+			if (!interface)
+				continue;
+			if (host_data.host)
+				continue;
+
+			let question = host_name + "." + service_name + ".local";
+			model.ubus.call("umdns", "query", { question, interface });
+		}
+	}
+}
+
+function refresh_start(model)
+{
+	model.mdns.refresh_count = 0;
+	model.mdns.timer ??= model.uloop.timer(500, () => refresh_timer(model));
+}
+
+function strip_local(name)
+{
+	if (substr(name, -6) == ".local")
+		name = substr(name, 0, -6);
+
+	return name;
+}
+
+function get_hosts(model)
+{
+	let data = model.ubus.call("umdns", "hosts", { array: true });
+	let ret = {};
+
+	for (let name, val in data) {
+		name = strip_local(name);
+		ret[lc(name)] = val;
+	}
+
+	return ret;
+}
+
+function get_host_names(model)
+{
+	let data = model.ubus.call("umdns", "hosts", { array: true });
+	let ret = {};
+
+	for (let name, val in data) {
+		name = strip_local(name);
+		ret[lc(name)] = name;
+	}
+
+	return ret;
+}
+
+function get_host_servicenames(model)
+{
+	let hosts = {};
+	let data = mdns_data(model);
+	for (let service_name, service_data in data) {
+		for (let host_name, host_data in service_data) {
+			let name = strip_local(host_data.host);
+
+			hosts[lc(name)] ??= [];
+			if (index(hosts[lc(name)], host_name) < 0)
+				push(hosts[lc(name)], host_name);
+		}
+	}
+	return hosts;
+}
+
+function get_service_hosts(model, name)
+{
+	name = lc(name);
+
+	let data = mdns_data(model);
+	for (let cur_name, hosts in data)
+		if (lc(cur_name) == name)
+			return hosts;
+}
+
+function get_host_services(model)
+{
+	let hosts = {};
+	let data = mdns_data(model);
+	for (let service_name, service_data in data) {
+		for (let host_name, host_data in service_data) {
+			host_data.name = host_name;
+			host_name = lc(strip_local(host_data.host));
+			hosts[host_name] ??= {};
+			hosts[host_name][service_name] = host_data;
+		}
+	}
+	return hosts;
+}
+
+function host_info(host)
+{
+	let ret = {};
+	if (host.ipv4)
+		ret.IPv4 = host.ipv4;
+	if (host.ipv6)
+		ret.IPv6 = host.ipv6;
+	return ret;
+}
+
+const host_arg = {
+	name: "host",
+	help: "host name",
+	type: "enum",
+	ignore_case: true,
+	value: () => values(get_host_names(model)),
+};
+
+const service_arg = {
+	name: "service",
+	help: "service name",
+	type: "enum",
+	ignore_case: true,
+	value: () => keys(mdns_data(model)),
+};
+
+function add_field(ret, name, val)
+{
+	if (val == null)
+		return;
+	if (type(ret) == "array")
+		push(ret, [ name, val ]);
+	else
+		ret[name] = val;
+}
+
+function service_info(data)
+{
+	let info = [];
+
+	add_field(info, "Name", data.name);
+	add_field(info, "Interface", data.iface);
+	add_field(info, "Port", data.port);
+	add_field(info, "Text", data.txt);
+
+	return info;
+}
+
+const MDNS = {
+	refresh: {
+		help: "Refresh service list by sending queries",
+		call: function(ctx, argv, named) {
+			refresh_start(model);
+			return ctx.ok("Querying hosts");
+		}
+	},
+	service: {
+		help: "Show service info",
+		args: [ service_arg ],
+		call: function (ctx, argv, named) {
+			let name = argv[0];
+			if (name != null) {
+				let data = get_service_hosts(model, name);
+				if (!data)
+					return ctx.not_found("Service not found: %s", name);
+
+				let ret = {};
+				for (let name, host in data) {
+					if (!host.host)
+						continue;
+					let host_name = strip_local(host.host);
+					host.name = name;
+					ret["Host " + host_name] = service_info(host);
+				}
+
+				return ctx.multi_table("Service " + name, ret);
+			}
+
+			let data = mdns_data(model);
+			let services = {};
+			for (let service_name, service_data in data) {
+				let hosts = [];
+				for (let name, host in service_data)
+					if (host.host)
+						push(hosts, `${name}(${strip_local(host.host)})`);
+				if (length(hosts))
+					services[service_name] = sort(hosts);
+			}
+
+			return ctx.table("Services", services);
+		}
+	},
+	host: {
+		help: "Host information",
+		args: [ host_arg ],
+		call: function (ctx, argv, named) {
+			let hosts_svc = get_host_services(model);
+			let hosts = get_hosts(model);
+			let host = argv[0];
+
+			if (host == null) {
+				let host_names = get_host_names(model);
+				let ret = {};
+
+				for (let lc_name, name in host_names) {
+					let data = hosts[lc_name];
+					if (!data)
+						continue;
+
+					let title = "Host " + name;
+					ret[title] = host_info(data);
+					let svc = hosts_svc[lc_name];
+					if (svc)
+						ret[title].services = keys(svc);
+				}
+				return ctx.multi_table("Hosts", ret);
+			}
+
+			let lc_host = lc(host);
+			let data = hosts[lc_host];
+			if (!data)
+				return ctx.not_found("Host not found: " + host);
+
+			let ret = {};
+			ret.Info = host_info(data);
+
+			for (let service_name, sdata in hosts_svc[lc_host])
+				ret["Service " + service_name] = service_info(sdata);
+
+			return ctx.multi_table("Host " + host, ret);
+		}
+	},
+};
+
+const Root = {
+	mdns: {
+		help: "Browse mdns hosts",
+		select_node: "MDNS",
+		select: function(ctx, argv) {
+			try {
+				refresh_start(model);
+			} catch (e) {
+				ctx.model.exception(e);
+			}
+			return true;
+		},
+	}
+};
+
+model.add_nodes({ Root, MDNS });
+model.mdns = {};




More information about the lede-commits mailing list