def __init__(self, zeroconf, listener, regtype): self.listener = listener self.regtype = regtype + ".local." self.zeroconf = zeroconf self._scheduleCallback() self.browser = ServiceBrowser(self.zeroconf, self.regtype, self.listener)
class MDNSSubscription(object): def __init__(self, zeroconf, listener, regtype): self.listener = listener self.regtype = regtype + ".local." self.zeroconf = zeroconf self.browser = ServiceBrowser(self.zeroconf, self.regtype, self.listener) def close(self): self.browser.cancel()
def do_dns_sd_advertisement_check(self, test, api, service_type): """Auth API advertises correctly via mDNS""" if CONFIG.DNS_SD_MODE != "multicast": return test.DISABLED( "This test cannot be performed when DNS_SD_MODE is not 'multicast'" ) ServiceBrowser(self.zc, service_type, self.zc_listener) sleep(CONFIG.DNS_SD_BROWSE_TIMEOUT) serv_list = self.zc_listener.get_service_list() for service in serv_list: port = service.port if port != api["port"]: continue for address in service.addresses: address = socket.inet_ntoa(address) if address != api["ip"]: continue properties = self.convert_bytes(service.properties) if "pri" not in properties: return test.FAIL( "No 'pri' TXT record found in {} advertisement.". format(api["name"])) try: priority = int(properties["pri"]) if priority < 0: return test.FAIL( "Priority ('pri') TXT record must be greater than zero." ) elif priority >= 100: return test.WARNING( "Priority ('pri') TXT record must be less than 100 for a production instance." ) except Exception: return test.FAIL( "Priority ('pri') TXT record is not an integer.") if "api_ver" not in properties: return test.FAIL( "No 'api_ver' TXT record found in {} advertisement.". format(api["name"])) elif api["version"] not in properties["api_ver"].split(","): return test.FAIL( "Auth Server does not claim to support version under test." ) if "api_proto" not in properties: return test.FAIL( "No 'api_proto' TXT record found in {} advertisement.". format(api["name"])) elif properties["api_proto"] != "https": return test.FAIL(""" API protocol ('api_proto') TXT record is {} and not 'https' """.format(properties["api_proto"])) return test.WARNING( "Authorization Server SHOULD NOT be advertised by mDNS based DNS-SD" ) return test.PASS()
class MDNSSubscription(object): def __init__(self, zeroconf, listener, regtype): self.listener = listener self.regtype = regtype + ".local." self.zeroconf = zeroconf self._scheduleCallback() self.browser = ServiceBrowser(self.zeroconf, self.regtype, self.listener) def _scheduleCallback(self): self.timer = Timer(3600, self._garbageCollect) self.timer.start() def _garbageCollect(self): self.listener.garbageCollect(self.zeroconf, self.regtype) self._scheduleCallback() def close(self): if self.timer: self.timer.cancel() self.browser.cancel()
def test_01(self, test): """Node advertises a Node type mDNS announcement with ver_* TXT records in the absence of a Registration API""" ServiceBrowser(self.zc, "_nmos-node._tcp.local.", self.zc_listener) time.sleep(CONFIG.DNS_SD_BROWSE_TIMEOUT) node_list = self.zc_listener.get_service_list() for node in node_list: address = socket.inet_ntoa(node.address) port = node.port if "/{}:{}/".format(address, port) in self.node_url: properties = self.convert_bytes(node.properties) for ver_txt in ["ver_slf", "ver_src", "ver_flw", "ver_dvc", "ver_snd", "ver_rcv"]: if ver_txt not in properties: return test.FAIL("No '{}' TXT record found in Node API advertisement.".format(ver_txt)) try: version = int(properties[ver_txt]) if version < 0: return test.FAIL("Version ('{}') TXT record must be greater than or equal to zero." .format(ver_txt)) elif version > 255: return test.WARNING("Version ('{}') TXT record must be less than or equal to 255." .format(ver_txt)) except Exception: return test.FAIL("Version ('{}') TXT record is not an integer.".format(ver_txt)) api = self.apis[NODE_API_KEY] if self.is04_utils.compare_api_version(api["version"], "v1.1") >= 0: if "api_ver" not in properties: return test.FAIL("No 'api_ver' TXT record found in Node API advertisement.") elif api["version"] not in properties["api_ver"].split(","): return test.FAIL("Node does not claim to support version under test.") if "api_proto" not in properties: return test.FAIL("No 'api_proto' TXT record found in Node API advertisement.") elif properties["api_proto"] != self.protocol: return test.FAIL("API protocol ('api_proto') TXT record is not '{}'.".format(self.protocol)) if self.is04_utils.compare_api_version(api["version"], "v1.3") >= 0: if "api_auth" not in properties: return test.FAIL("No 'api_auth' TXT record found in Node API advertisement.") elif not isinstance(properties["api_auth"], bool): # zeroconf translates 'true' to True and 'false' to False automatically return test.FAIL("API authorization ('api_auth') TXT record is not one of 'true' or 'false'.") return test.PASS() return test.FAIL("No matching mDNS announcement found for Node. Peer to peer mode will not function correctly.", NMOS_WIKI_URL + "/IS-04#nodes-peer-to-peer-mode")
def test_12(self): """Node advertises a Node type mDNS announcement with no ver_* TXT records in the presence of a Registration API""" test = Test("Node advertises a Node type mDNS announcement with no ver_* TXT records in the presence " "of a Registration API") browser = ServiceBrowser(self.zc, "_nmos-node._tcp.local.", self.zc_listener) time.sleep(1) node_list = self.zc_listener.get_service_list() for node in node_list: address = socket.inet_ntoa(node.address) port = node.port if "/{}:{}/".format(address, port) in self.node_url: properties = self.convert_bytes(node.properties) for prop in properties: if "ver_" in prop: return test.FAIL("Found 'ver_' TXT record while Node is registered.") api = self.apis[NODE_API_KEY] if self.is04_utils.compare_api_version(api["version"], "v1.1") >= 0: if "api_ver" not in properties: return test.FAIL("No 'api_ver' TXT record found in Node API advertisement.") elif api["version"] not in properties["api_ver"].split(","): return test.FAIL("Node does not claim to support version under test.") if "api_proto" not in properties: return test.FAIL("No 'api_proto' TXT record found in Node API advertisement.") elif properties["api_proto"] == "https": return test.MANUAL("API protocol is not advertised as 'http'. " "This test suite does not currently support 'https'.") elif properties["api_proto"] != "http": return test.FAIL("API protocol ('api_proto') TXT record is not 'http' or 'https'.") return test.PASS() return test.WARNING("No matching mDNS announcement found for Node. This will not affect operation in registered" " mode but may indicate a lack of support for peer to peer operation.", "https://github.com/amwa-tv/nmos/wiki/IS-04#nodes-peer-to-peer-mode")
def test_01_node_mdns_with_txt(self, test): """Node advertises a Node type mDNS announcement with ver_* TXT records in the absence of a Registration API""" browser = ServiceBrowser(self.zc, "_nmos-node._tcp.local.", self.zc_listener) time.sleep(1) node_list = self.zc_listener.get_service_list() for node in node_list: address = socket.inet_ntoa(node.address) port = node.port if "/{}:{}/".format(address, port) in self.node_url: properties = self.convert_bytes(node.properties) for ver_txt in [ "ver_slf", "ver_src", "ver_flw", "ver_dvc", "ver_snd", "ver_rcv" ]: if ver_txt not in properties: return test.FAIL( "No '{}' TXT record found in Node API advertisement." .format(ver_txt)) try: version = int(properties[ver_txt]) if version < 0: return test.FAIL( "Version ('{}') TXT record must be greater than or equal to zero." .format(ver_txt)) elif version > 255: return test.WARNING( "Version ('{}') TXT record must be less than or equal to 255." .format(ver_txt)) except Exception: return test.FAIL( "Version ('{}') TXT record is not an integer.". format(ver_txt)) api = self.apis[NODE_API_KEY] if self.is04_utils.compare_api_version(api["version"], "v1.1") >= 0: if "api_ver" not in properties: return test.FAIL( "No 'api_ver' TXT record found in Node API advertisement." ) elif api["version"] not in properties["api_ver"].split( ","): return test.FAIL( "Node does not claim to support version under test." ) if "api_proto" not in properties: return test.FAIL( "No 'api_proto' TXT record found in Node API advertisement." ) elif properties["api_proto"] != self.protocol: return test.FAIL( "API protocol ('api_proto') TXT record is not '{}'." .format(self.protocol)) return test.PASS() return test.FAIL( "No matching mDNS announcement found for Node. Peer to peer mode will not function correctly.", "https://github.com/amwa-tv/nmos/wiki/IS-04#nodes-peer-to-peer-mode" )
def test_01(self, test): """Node advertises a Node type mDNS announcement with ver_* TXT records in the absence of a Registration API""" api = self.apis[NODE_API_KEY] if CONFIG.DNS_SD_MODE != "multicast": return test.DISABLED( "This test cannot be performed when DNS_SD_MODE is not 'multicast'" ) ServiceBrowser(self.zc, "_nmos-node._tcp.local.", self.zc_listener) # Wait for n seconds for the Node to recognize it should adopt peer-to-peer operation start_time = time.time() while time.time() < start_time + CONFIG.DNS_SD_ADVERT_TIMEOUT: properties = None time.sleep(CONFIG.DNS_SD_BROWSE_TIMEOUT) node_list = self.zc_listener.get_service_list() # Iterate in reverse order to check the most recent advert first for node in reversed(node_list): port = node.port if port != api["port"]: continue for address in node.addresses: address = socket.inet_ntoa(address) if address != api["ip"]: continue properties = self.convert_bytes(node.properties) break if properties: break # If the Node is still advertising as for registered operation, loop around if properties and "ver_slf" in properties: for ver_txt in [ "ver_slf", "ver_src", "ver_flw", "ver_dvc", "ver_snd", "ver_rcv" ]: if ver_txt not in properties: return test.FAIL( "No '{}' TXT record found in Node API advertisement." .format(ver_txt)) try: version = int(properties[ver_txt]) if version < 0: return test.FAIL( "Version ('{}') TXT record must be greater than or equal to zero." .format(ver_txt)) elif version > 255: return test.WARNING( "Version ('{}') TXT record must be less than or equal to 255." .format(ver_txt)) except Exception: return test.FAIL( "Version ('{}') TXT record is not an integer.". format(ver_txt)) # Other TXT records only came in for IS-04 v1.1+ if self.is04_utils.compare_api_version(api["version"], "v1.1") >= 0: if "api_ver" not in properties: return test.FAIL( "No 'api_ver' TXT record found in Node API advertisement." ) elif api["version"] not in properties["api_ver"].split( ","): return test.FAIL( "Node does not claim to support version under test." ) if "api_proto" not in properties: return test.FAIL( "No 'api_proto' TXT record found in Node API advertisement." ) elif properties["api_proto"] != self.protocol: return test.FAIL( "API protocol ('api_proto') TXT record is not '{}'." .format(self.protocol)) if self.is04_utils.compare_api_version(api["version"], "v1.3") >= 0: if "api_auth" not in properties: return test.FAIL( "No 'api_auth' TXT record found in Node API advertisement." ) elif properties["api_auth"] not in ["true", "false"]: return test.FAIL( "API authorization ('api_auth') TXT record is not one of 'true' or 'false'." ) return test.PASS() return test.FAIL( "No matching mDNS announcement found for Node with IP/Port {}:{}. Peer to peer mode will not " "function correctly.".format(api["ip"], api["port"]), NMOS_WIKI_URL + "/IS-04#nodes-peer-to-peer-mode")
self._print_entry(name, info) else: self._print_entry(name) print("") def _print_entry(self, name, info=None): print(" - {}".format(name)) if info is not None: address = inet_ntoa(info.address) print(" Address: {}, Port: {}, TXT: {}".format( address, info.port, info.properties)) else: print(" Unresolvable") zeroconf = Zeroconf() listener = Listener() for srv_type in MONITOR_TYPES: browser = ServiceBrowser(zeroconf, srv_type + ".local.", listener) try: while True: listener.print_services() time.sleep(1) except KeyboardInterrupt: pass finally: print("* Shutting down, please wait...") zeroconf.close()
def test_12(self, test): """Node advertises a Node type mDNS announcement with no ver_* TXT records in the presence of a Registration API""" if not ENABLE_DNS_SD: return test.DISABLED( "This test cannot be performed when ENABLE_DNS_SD is False") registry = self.registries[0] registry_info = self._registry_mdns_info(registry.get_data().port, 0) # Reset the registry to clear previous data, although we won't be checking it registry.reset() registry.enable() if DNS_SD_MODE == "multicast": # Advertise a registry at pri 0 and allow the Node to do a basic registration self.zc.register_service(registry_info) # Wait for n seconds after advertising the service for the first POST from a Node time.sleep(DNS_SD_ADVERT_TIMEOUT) browser = ServiceBrowser(self.zc, "_nmos-node._tcp.local.", self.zc_listener) time.sleep(1) node_list = self.zc_listener.get_service_list() # Withdraw the registry advertisement now we've performed a browse for Node advertisements if DNS_SD_MODE == "multicast": self.zc.unregister_service(registry_info) registry.disable() for node in node_list: address = socket.inet_ntoa(node.address) port = node.port api = self.apis[NODE_API_KEY] if address == api["ip"] and port == api["port"]: properties = self.convert_bytes(node.properties) for prop in properties: if "ver_" in prop: return test.FAIL( "Found 'ver_' TXT record while Node is registered." ) if self.is04_utils.compare_api_version(api["version"], "v1.1") >= 0: if "api_ver" not in properties: return test.FAIL( "No 'api_ver' TXT record found in Node API advertisement." ) elif api["version"] not in properties["api_ver"].split( ","): return test.FAIL( "Node does not claim to support version under test." ) if "api_proto" not in properties: return test.FAIL( "No 'api_proto' TXT record found in Node API advertisement." ) elif properties["api_proto"] != self.protocol: return test.FAIL( "API protocol ('api_proto') TXT record is not '{}'." .format(self.protocol)) return test.PASS() return test.WARNING( "No matching mDNS announcement found for Node with IP/Port {}:{}. This will not affect " "operation in registered mode but may indicate a lack of support for peer to peer " "operation.".format(self.apis[NODE_API_KEY]["ip"], self.apis[NODE_API_KEY]["port"]), "https://github.com/amwa-tv/nmos/wiki/IS-04#nodes-peer-to-peer-mode" )