Exemple #1
0
 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)
Exemple #2
0
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()
Exemple #3
0
    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()
Exemple #4
0
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()
Exemple #5
0
    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")
Exemple #6
0
    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")
Exemple #7
0
    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"
        )
Exemple #8
0
    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")
Exemple #9
0
                        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"
        )