From 919ec7587709269acc5f223daf2e586b544a0c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Pi=C3=A9dallu?= Date: Sun, 26 Sep 2021 10:37:26 +0200 Subject: [PATCH 01/11] Revamp of yunomdns : * Use ifaddr (also used by zeroconf) to find ip addresses * Use python type hinting * small cleanups --- bin/yunomdns | 166 ++++++++++++++++----------------------------------- 1 file changed, 53 insertions(+), 113 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index 862a1f477..8202b93c4 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -4,104 +4,42 @@ Pythonic declaration of mDNS .local domains for YunoHost """ -import subprocess -import re import sys import yaml - -import socket from time import sleep from typing import List, Dict +import ifaddr from zeroconf import Zeroconf, ServiceInfo -# Helper command taken from Moulinette -def check_output(args, stderr=subprocess.STDOUT, shell=True, **kwargs): - """Run command with arguments and return its output as a byte string - Overwrite some of the arguments to capture standard error in the result - and use shell by default before calling subprocess.check_output. + +def get_network_local_interfaces() -> Dict[str, Dict[str, List[str]]]: """ - return ( - subprocess.check_output(args, stderr=stderr, shell=shell, **kwargs) - .decode("utf-8") - .strip() - ) - -# Helper command taken from Moulinette -def _extract_inet(string, skip_netmask=False, skip_loopback=True): + Returns interfaces with their associated local IPs """ - Extract IP addresses (v4 and/or v6) from a string limited to one - address by protocol - Keyword argument: - string -- String to search in - skip_netmask -- True to skip subnet mask extraction - skip_loopback -- False to include addresses reserved for the - loopback interface + def islocal(ip: str) -> bool: + local_prefixes = ["192.168.", "10.", "172.16.", "fc00:"] + return any(ip.startswith(prefix) for prefix in local_prefixes) - Returns: - A dict of {protocol: address} with protocol one of 'ipv4' or 'ipv6' - - """ - ip4_pattern = ( - r"((25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}" - ) - ip6_pattern = r"(((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::?((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)" - ip4_pattern += r"/[0-9]{1,2})" if not skip_netmask else ")" - ip6_pattern += r"/[0-9]{1,3})" if not skip_netmask else ")" - result = {} - - for m in re.finditer(ip4_pattern, string): - addr = m.group(1) - if skip_loopback and addr.startswith("127."): - continue - - # Limit to only one result - result["ipv4"] = addr - break - - for m in re.finditer(ip6_pattern, string): - addr = m.group(1) - if skip_loopback and addr == "::1": - continue - - # Limit to only one result - result["ipv6"] = addr - break - - return result - -# Helper command taken from Moulinette -def get_network_interfaces(): - - # Get network devices and their addresses (raw infos from 'ip addr') - devices_raw = {} - output = check_output("ip --brief a").split("\n") - for line in output: - line = line.split() - iname = line[0] - ips = ' '.join(line[2:]) - - devices_raw[iname] = ips - - # Parse relevant informations for each of them - devices = { - name: _extract_inet(addrs) - for name, addrs in devices_raw.items() - if name != "lo" + interfaces = { + adapter.name: { + "ipv4": [ip.ip for ip in adapter.ips if ip.is_IPv4 and islocal(ip.ip)], + "ipv6": [ip.ip[0] for ip in adapter.ips if ip.is_IPv6 and islocal(ip.ip[0])], + } + for adapter in ifaddr.get_adapters() + if adapter.name != "lo" } + return interfaces - return devices - -if __name__ == '__main__': +def main() -> bool: ### # CONFIG ### with open('/etc/yunohost/mdns.yml', 'r') as f: config = yaml.safe_load(f) or {} - updated = False required_fields = ["interfaces", "domains"] missing_fields = [field for field in required_fields if field not in config] @@ -111,47 +49,44 @@ if __name__ == '__main__': if config['interfaces'] is None: print('No interface listed for broadcast.') - sys.exit(0) + return True if 'yunohost.local' not in config['domains']: config['domains'].append('yunohost.local') - zcs = {} - interfaces = get_network_interfaces() + zcs: Dict[Zeroconf, List[ServiceInfo]] = {} + + interfaces = get_network_local_interfaces() for interface in config['interfaces']: - infos = [] # List of ServiceInfo objects, to feed Zeroconf - ips = [] # Human-readable IPs - b_ips = [] # Binary-convered IPs + if interface not in interfaces: + print(f'Interface {interface} of config file is not present on system.') + continue - ipv4 = interfaces[interface]['ipv4'].split('/')[0] - if ipv4: - ips.append(ipv4) - b_ips.append(socket.inet_pton(socket.AF_INET, ipv4)) - - ipv6 = interfaces[interface]['ipv6'].split('/')[0] - if ipv6: - ips.append(ipv6) - b_ips.append(socket.inet_pton(socket.AF_INET6, ipv6)) + ips: List[str] = interfaces[interface]['ipv4'] + interfaces[interface]['ipv6'] # If at least one IP is listed - if ips: - # Create a Zeroconf object, and store the ServiceInfos - zc = Zeroconf(interfaces=ips) - zcs[zc]=[] - for d in config['domains']: - d_domain=d.replace('.local','') - if '.' in d_domain: - print(d_domain+'.local: subdomains are not supported.') - else: - # Create a ServiceInfo object for each .local domain - zcs[zc].append(ServiceInfo( - type_='_device-info._tcp.local.', - name=interface+': '+d_domain+'._device-info._tcp.local.', - addresses=b_ips, - port=80, - server=d+'.', - )) - print('Adding '+d+' with addresses '+str(ips)+' on interface '+interface) + if not ips: + continue + # Create a Zeroconf object, and store the ServiceInfos + zc = Zeroconf(interfaces=ips) # type: ignore + zcs[zc] = [] + + for d in config['domains']: + d_domain = d.replace('.local', '') + if '.' in d_domain: + print(f'{d_domain}.local: subdomains are not supported.') + continue + # Create a ServiceInfo object for each .local domain + zcs[zc].append( + ServiceInfo( + type_='_device-info._tcp.local.', + name=f'{interface}: {d_domain}._device-info._tcp.local.', + parsed_addresses=ips, + port=80, + server=f'{d}.', + ) + ) + print(f'Adding {d} with addresses {ips} on interface {interface}') # Run registration print("Registering...") @@ -168,6 +103,11 @@ if __name__ == '__main__': finally: print("Unregistering...") for zc, infos in zcs.items(): - for info in infos: - zc.unregister_service(info) + zc.unregister_all_services() zc.close() + + return True + + +if __name__ == "__main__": + sys.exit(0 if main() else 1) From d4395f2b4aa69424245218601993e3c37c141df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Pi=C3=A9dallu?= Date: Sun, 26 Sep 2021 10:42:57 +0200 Subject: [PATCH 02/11] Use double-quotes --- bin/yunomdns | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index 8202b93c4..f31132514 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -38,31 +38,31 @@ def main() -> bool: # CONFIG ### - with open('/etc/yunohost/mdns.yml', 'r') as f: + with open("/etc/yunohost/mdns.yml", "r") as f: config = yaml.safe_load(f) or {} required_fields = ["interfaces", "domains"] missing_fields = [field for field in required_fields if field not in config] if missing_fields: - print("The fields %s are required" % ', '.join(missing_fields)) + print("The fields %s are required" % ", ".join(missing_fields)) - if config['interfaces'] is None: - print('No interface listed for broadcast.') + if config["interfaces"] is None: + print("No interface listed for broadcast.") return True - if 'yunohost.local' not in config['domains']: - config['domains'].append('yunohost.local') + if "yunohost.local" not in config["domains"]: + config["domains"].append("yunohost.local") zcs: Dict[Zeroconf, List[ServiceInfo]] = {} interfaces = get_network_local_interfaces() - for interface in config['interfaces']: + for interface in config["interfaces"]: if interface not in interfaces: - print(f'Interface {interface} of config file is not present on system.') + print(f"Interface {interface} of config file is not present on system.") continue - ips: List[str] = interfaces[interface]['ipv4'] + interfaces[interface]['ipv6'] + ips: List[str] = interfaces[interface]["ipv4"] + interfaces[interface]["ipv6"] # If at least one IP is listed if not ips: @@ -71,22 +71,22 @@ def main() -> bool: zc = Zeroconf(interfaces=ips) # type: ignore zcs[zc] = [] - for d in config['domains']: - d_domain = d.replace('.local', '') - if '.' in d_domain: - print(f'{d_domain}.local: subdomains are not supported.') + for d in config["domains"]: + d_domain = d.replace(".local", "") + if "." in d_domain: + print(f"{d_domain}.local: subdomains are not supported.") continue # Create a ServiceInfo object for each .local domain zcs[zc].append( ServiceInfo( - type_='_device-info._tcp.local.', - name=f'{interface}: {d_domain}._device-info._tcp.local.', + type_="_device-info._tcp.local.", + name=f"{interface}: {d_domain}._device-info._tcp.local.", parsed_addresses=ips, port=80, - server=f'{d}.', + server=f"{d}.", ) ) - print(f'Adding {d} with addresses {ips} on interface {interface}') + print(f"Adding {d} with addresses {ips} on interface {interface}") # Run registration print("Registering...") From 358885dc622d9e18c2cc283c4a1c52236537b73b Mon Sep 17 00:00:00 2001 From: tituspijean Date: Mon, 20 Sep 2021 22:07:45 +0200 Subject: [PATCH 03/11] [mdns] Avoid miserably failing if another service exists on the network also, service names do not clash anymore accross same device but different interfaces --- bin/yunomdns | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/yunomdns b/bin/yunomdns index f31132514..dfd58deee 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -92,7 +92,7 @@ def main() -> bool: print("Registering...") for zc, infos in zcs.items(): for info in infos: - zc.register_service(info) + zc.register_service(info, allow_name_change=True, cooperating_responders=True) try: print("Registered. Press Ctrl+C or stop service to stop.") From bcb48b4948a900fd153e87e325ba07c8c56f6895 Mon Sep 17 00:00:00 2001 From: tituspijean Date: Mon, 20 Sep 2021 22:14:00 +0200 Subject: [PATCH 04/11] [mdns] Make the service logs appear in systemd journal --- data/templates/mdns/yunomdns.service | 1 + 1 file changed, 1 insertion(+) diff --git a/data/templates/mdns/yunomdns.service b/data/templates/mdns/yunomdns.service index ce2641b5d..c1f1b7b06 100644 --- a/data/templates/mdns/yunomdns.service +++ b/data/templates/mdns/yunomdns.service @@ -6,6 +6,7 @@ After=network.target User=mdns Group=mdns Type=simple +Environment=PYTHONUNBUFFERED=1 ExecStart=/usr/bin/yunomdns StandardOutput=syslog From a313a86b8b6eb39b8836c47cc8c107058a6da6ed Mon Sep 17 00:00:00 2001 From: tituspijean Date: Tue, 21 Sep 2021 00:09:49 +0200 Subject: [PATCH 05/11] [mdns] Allow for multiple yunohost.local --- bin/yunomdns | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index dfd58deee..aa0697f5f 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -10,7 +10,7 @@ from time import sleep from typing import List, Dict import ifaddr -from zeroconf import Zeroconf, ServiceInfo +from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser def get_network_local_interfaces() -> Dict[str, Dict[str, List[str]]]: @@ -32,6 +32,23 @@ def get_network_local_interfaces() -> Dict[str, Dict[str, List[str]]]: } return interfaces +# Listener class, to detect duplicates on the network +# Stores the list of servers in its list property +class Listener: + + def __init__(self): + self.list = [] + + def remove_service(self, zeroconf, type, name): + info = zeroconf.get_service_info(type, name) + self.list.remove(info.server) + + def update_service(self, zeroconf, type, name): + pass + + def add_service(self, zeroconf, type, name): + info = zeroconf.get_service_info(type, name) + self.list.append(info.server[:-1]) def main() -> bool: ### @@ -51,8 +68,25 @@ def main() -> bool: print("No interface listed for broadcast.") return True - if "yunohost.local" not in config["domains"]: - config["domains"].append("yunohost.local") + # Let's discover currently published .local domains accross the network + zc = Zeroconf() + listener = Listener() + browser = ServiceBrowser(zc, "_device-info._tcp.local.", listener) + sleep(2) + browser.cancel() + zc.close() + # If yunohost.local already exists, try yunohost-2.local, and so on. + def yunohost_local(i): + return "yunohost.local" if i < 2 else "yunohost-"+str(i)+".local" + i=1 + while yunohost_local(i) in listener.list: + print("Uh oh, "+yunohost_local(i)+" already exists on the network...") + if yunohost_local(i) in config['domains']: + config['domains'].remove(yunohost_local(i)) + i += 1 + if yunohost_local(i) not in config['domains']: + print("Adding "+yunohost_local(i)+" to the domains to publish.") + config['domains'].append(yunohost_local(i)) zcs: Dict[Zeroconf, List[ServiceInfo]] = {} From 07c381cec4690df90022e11c0574dfca78906c2f Mon Sep 17 00:00:00 2001 From: tituspijean Date: Sun, 26 Sep 2021 12:05:35 +0200 Subject: [PATCH 06/11] Use ipaddress lib to find private addresses --- bin/yunomdns | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index aa0697f5f..f50afd964 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -10,6 +10,7 @@ from time import sleep from typing import List, Dict import ifaddr +from ipaddress import ip_address from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser @@ -18,14 +19,10 @@ def get_network_local_interfaces() -> Dict[str, Dict[str, List[str]]]: Returns interfaces with their associated local IPs """ - def islocal(ip: str) -> bool: - local_prefixes = ["192.168.", "10.", "172.16.", "fc00:"] - return any(ip.startswith(prefix) for prefix in local_prefixes) - interfaces = { adapter.name: { - "ipv4": [ip.ip for ip in adapter.ips if ip.is_IPv4 and islocal(ip.ip)], - "ipv6": [ip.ip[0] for ip in adapter.ips if ip.is_IPv6 and islocal(ip.ip[0])], + "ipv4": [ip.ip for ip in adapter.ips if ip.is_IPv4 and ip_address(ip.ip).is_private], + "ipv6": [ip.ip[0] for ip in adapter.ips if ip.is_IPv6 and ip_address(ip.ip[0]).is_private], } for adapter in ifaddr.get_adapters() if adapter.name != "lo" From 63504febaaf17a7efbf93f111c409764e84a0b17 Mon Sep 17 00:00:00 2001 From: tituspijean Date: Sun, 26 Sep 2021 19:40:46 +0200 Subject: [PATCH 07/11] [mdns] refine ipv6 selection with ipaddress lib Co-authored-by: =?UTF-8?q?F=C3=A9lix=20Pi=C3=A9dallu?= --- bin/yunomdns | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/yunomdns b/bin/yunomdns index f50afd964..f302f1f8c 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -22,7 +22,7 @@ def get_network_local_interfaces() -> Dict[str, Dict[str, List[str]]]: interfaces = { adapter.name: { "ipv4": [ip.ip for ip in adapter.ips if ip.is_IPv4 and ip_address(ip.ip).is_private], - "ipv6": [ip.ip[0] for ip in adapter.ips if ip.is_IPv6 and ip_address(ip.ip[0]).is_private], + "ipv6": [ip.ip[0] for ip in adapter.ips if ip.is_IPv6 and ip_address(ip.ip[0]).is_private and not ip_address(ip.ip[0]).is_link_local], } for adapter in ifaddr.get_adapters() if adapter.name != "lo" From ab1100048b91dc1b9404c0996642ccca68b09062 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 29 Sep 2021 19:00:05 +0200 Subject: [PATCH 08/11] yunomdns: Minor cleanups --- bin/yunomdns | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index f302f1f8c..41a9ede3e 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -29,6 +29,7 @@ def get_network_local_interfaces() -> Dict[str, Dict[str, List[str]]]: } return interfaces + # Listener class, to detect duplicates on the network # Stores the list of servers in its list property class Listener: @@ -47,6 +48,7 @@ class Listener: info = zeroconf.get_service_info(type, name) self.list.append(info.server[:-1]) + def main() -> bool: ### # CONFIG @@ -59,7 +61,8 @@ def main() -> bool: missing_fields = [field for field in required_fields if field not in config] if missing_fields: - print("The fields %s are required" % ", ".join(missing_fields)) + print(f"The fields {missing_fields} are required in mdns.yml") + return False if config["interfaces"] is None: print("No interface listed for broadcast.") @@ -72,17 +75,20 @@ def main() -> bool: sleep(2) browser.cancel() zc.close() + # If yunohost.local already exists, try yunohost-2.local, and so on. def yunohost_local(i): - return "yunohost.local" if i < 2 else "yunohost-"+str(i)+".local" - i=1 + return "yunohost.local" if i < 2 else f"yunohost-{i}.local" + + i = 1 while yunohost_local(i) in listener.list: - print("Uh oh, "+yunohost_local(i)+" already exists on the network...") + print(f"Uh oh, {yunohost_local(i)} already exists on the network...") if yunohost_local(i) in config['domains']: config['domains'].remove(yunohost_local(i)) i += 1 + if yunohost_local(i) not in config['domains']: - print("Adding "+yunohost_local(i)+" to the domains to publish.") + print(f"Adding {yunohost_local(i)} to the domains to publish.") config['domains'].append(yunohost_local(i)) zcs: Dict[Zeroconf, List[ServiceInfo]] = {} From f49666d22e7e17cedfab49a98a1789c12f62fc7e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 29 Sep 2021 19:14:21 +0200 Subject: [PATCH 09/11] yunomdns: fallback to domain-i.local if domain.local already published --- bin/yunomdns | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index 41a9ede3e..8a9682ff9 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -76,20 +76,27 @@ def main() -> bool: browser.cancel() zc.close() - # If yunohost.local already exists, try yunohost-2.local, and so on. - def yunohost_local(i): - return "yunohost.local" if i < 2 else f"yunohost-{i}.local" + # Always attempt to publish yunohost.local + if "yunohost.local" not in config["domains"]: + config["domains"].append("yunohost.local") - i = 1 - while yunohost_local(i) in listener.list: - print(f"Uh oh, {yunohost_local(i)} already exists on the network...") - if yunohost_local(i) in config['domains']: - config['domains'].remove(yunohost_local(i)) - i += 1 + def find_domain_not_already_published(domain): - if yunohost_local(i) not in config['domains']: - print(f"Adding {yunohost_local(i)} to the domains to publish.") - config['domains'].append(yunohost_local(i)) + # Try domain.local ... but if it's already published by another entity, + # try domain-2.local, domain-3.local, ... + + i = 1 + domain_i = domain + + while domain_i in listener.list: + print(f"Uh oh, {domain_i} already exists on the network...") + + i += 1 + domain_i = domain.replace(".local", f"-{i}.local") + + return domain_i + + config['domains'] = [find_domain_not_already_published(domain) for domain in config['domains']] zcs: Dict[Zeroconf, List[ServiceInfo]] = {} From a5e1d7e318158da38b1e3dc415a92f0e6250a460 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 29 Sep 2021 20:02:32 +0200 Subject: [PATCH 10/11] yunomdns: broadcast on all interfaces with local IP by default + add a 'ban_interfaces' setting in config --- bin/yunomdns | 23 +++++++++++++++++------ data/hooks/conf_regen/37-mdns | 7 ------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index 8a9682ff9..b9f8cf2a7 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -57,16 +57,23 @@ def main() -> bool: with open("/etc/yunohost/mdns.yml", "r") as f: config = yaml.safe_load(f) or {} - required_fields = ["interfaces", "domains"] + required_fields = ["domains"] missing_fields = [field for field in required_fields if field not in config] + interfaces = get_network_local_interfaces() if missing_fields: print(f"The fields {missing_fields} are required in mdns.yml") return False - if config["interfaces"] is None: - print("No interface listed for broadcast.") - return True + if "interfaces" not in config: + config["interfaces"] = [interface + for interface, local_ips in interfaces.items() + if local_ips["ipv4"]] + + if "ban_interfaces" in config: + config["interfaces"] = [interface + for interface in config["interfaces"] + if interface not in config["ban_interfaces"]] # Let's discover currently published .local domains accross the network zc = Zeroconf() @@ -100,10 +107,10 @@ def main() -> bool: zcs: Dict[Zeroconf, List[ServiceInfo]] = {} - interfaces = get_network_local_interfaces() for interface in config["interfaces"]: + if interface not in interfaces: - print(f"Interface {interface} of config file is not present on system.") + print(f"Interface {interface} listed in config file is not present on system.") continue ips: List[str] = interfaces[interface]["ipv4"] + interfaces[interface]["ipv6"] @@ -111,6 +118,10 @@ def main() -> bool: # If at least one IP is listed if not ips: continue + + print(f"Publishing on {interface} ...") + print(ips) + # Create a Zeroconf object, and store the ServiceInfos zc = Zeroconf(interfaces=ips) # type: ignore zcs[zc] = [] diff --git a/data/hooks/conf_regen/37-mdns b/data/hooks/conf_regen/37-mdns index 1d7381e26..b2a3efe95 100755 --- a/data/hooks/conf_regen/37-mdns +++ b/data/hooks/conf_regen/37-mdns @@ -12,13 +12,6 @@ _generate_config() { [[ "$domain" =~ ^[^.]+\.local$ ]] || continue echo " - $domain" done - - echo "interfaces:" - local_network_interfaces="$(ip --brief a | grep ' 10\.\| 192\.168\.' | awk '{print $1}')" - for interface in $local_network_interfaces - do - echo " - $interface" - done } do_init_regen() { From 391de52ff214a46efe1f990c6bd7164eebc852ad Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 29 Sep 2021 20:10:00 +0200 Subject: [PATCH 11/11] yunomdns: disable ipv6 for now + remove debug stuff --- bin/yunomdns | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index b9f8cf2a7..0aee28195 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -113,15 +113,17 @@ def main() -> bool: print(f"Interface {interface} listed in config file is not present on system.") continue - ips: List[str] = interfaces[interface]["ipv4"] + interfaces[interface]["ipv6"] + # Only broadcast IPv4 because IPv6 is buggy ... because we ain't using python3-ifaddr >= 0.1.7 + # Buster only ships 0.1.6 + # Bullseye ships 0.1.7 + # To be re-enabled once we're on bullseye... + # ips: List[str] = interfaces[interface]["ipv4"] + interfaces[interface]["ipv6"] + ips: List[str] = interfaces[interface]["ipv4"] # If at least one IP is listed if not ips: continue - print(f"Publishing on {interface} ...") - print(ips) - # Create a Zeroconf object, and store the ServiceInfos zc = Zeroconf(interfaces=ips) # type: ignore zcs[zc] = []