From: Felix Fietkau Date: Wed, 12 Feb 2025 19:04:10 +0000 (+0100) Subject: umdns: add cli module X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=562ced3d8f7bceb0f5c072b0d4c2cf6ffb6c383d;p=openwrt%2Fstaging%2Fstintel.git umdns: add cli module This makes it easy to browse mDNS services and hosts on the local network Signed-off-by: Felix Fietkau --- 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 +'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 = {};