Beispiel #1
0
class DiscoveryClientService:
    _SERVICE_TYPE = '_http._tcp.local.'
    _SERVICE_NAME = 'sound-system-server._http._tcp.local.'

    def __init__(self, loop, notify):
        self._zeroconf = Zeroconf()
        self._listener = self.ServiceListener(loop, notify)
        self._browser = None

    def start(self):
        self._browser = ServiceBrowser(self._zeroconf, self._SERVICE_TYPE,
                                       self._listener)

    def stop(self):
        if self._browser is not None:
            self._browser.cancel()
            self._browser = None

    class ServiceListener:
        def __init__(self, loop, notify):
            self._loop = loop
            self._notify = notify

        def add_service(self, zeroconf, t, name):
            if name == DiscoveryClientService._SERVICE_NAME:
                info = zeroconf.get_service_info(t, name)
                self._loop.call_soon_threadsafe(self._notify, name, info)

        def remove_service(self, zeroconf, t, name):
            pass
Beispiel #2
0
def discover_homekit_devices(
        max_seconds: int = 10,
        zeroconf_instance: "Zeroconf" = None) -> List[Any]:
    """
    This method discovers all HomeKit Accessories. It browses for devices in the _hap._tcp.local. domain and checks if
    all required fields are set in the text record. It one field is missing, it will be excluded from the result list.

    :param max_seconds: the number of seconds we will wait for the devices to be discovered
    :return: a list of dicts containing all fields as described in table 5.7 page 69
    """
    zeroconf = zeroconf_instance or Zeroconf()
    listener = CollectingListener()
    service_browser = ServiceBrowser(zeroconf, HAP_TYPE, listener)
    sleep(max_seconds)
    tmp = []
    try:
        for info in listener.get_data():
            data = _build_data_from_service_info(info)

            if "c#" not in data or "md" not in data:
                continue
            logging.debug("found Homekit IP accessory %s", data)
            tmp.append(data)
    finally:
        service_browser.cancel()
        if not zeroconf_instance:
            zeroconf.close()
    return tmp
Beispiel #3
0
def _find_data_for_device_id(
        device_id: str,
        max_seconds: int = 10,
        zeroconf_instance: "Zeroconf" = None) -> Tuple[str, int]:
    """
    Try to find a HomeKit Accessory via Bonjour. The process is time boxed by the second parameter which sets an upper
    limit of `max_seconds` before it times out. The runtime of the function may be longer because of the Bonjour
    handling code.
    """
    zeroconf = zeroconf_instance or Zeroconf()
    found_device_event = threading.Event()
    listener = CollectingListener(device_id=device_id,
                                  found_device_event=found_device_event)
    service_browser = ServiceBrowser(zeroconf, HAP_TYPE, listener)
    found_device_event.wait(timeout=max_seconds)

    try:
        data = listener.get_data()
        for info in data:
            if info.properties[b"id"].decode() == device_id:
                logging.debug("Located Homekit IP accessory %s",
                              info.properties)
                return _build_data_from_service_info(info)
    finally:
        service_browser.cancel()
        if not zeroconf_instance:
            zeroconf.close()

    raise AccessoryNotFoundError(
        "Device not found via Bonjour within 10 seconds")
Beispiel #4
0
def test_integration():
    service_added = Event()
    service_removed = Event()

    type_ = "_http._tcp.local."
    registration_name = "xxxyyy.%s" % type_

    def on_service_state_change(zeroconf, service_type, state_change, name):
        if name == registration_name:
            if state_change is ServiceStateChange.Added:
                service_added.set()
            elif state_change is ServiceStateChange.Removed:
                service_removed.set()

    zeroconf_browser = Zeroconf()
    browser = ServiceBrowser(zeroconf_browser, type_, [on_service_state_change])

    zeroconf_registrar = Zeroconf()
    desc = {'path': '/~paulsm/'}
    info = ServiceInfo(
        type_, registration_name,
        socket.inet_aton("10.0.1.2"), 80, 0, 0,
        desc, "ash-2.local.")
    zeroconf_registrar.register_service(info)

    try:
        service_added.wait(1)
        assert service_added.is_set()
        # Don't remove service, allow close() to cleanup

    finally:
        zeroconf_registrar.close()
        browser.cancel()
        zeroconf_browser.close()
Beispiel #5
0
def discover_homekit_devices():
    """
    Search for HAP devices on the network.

    :returns: list of dicts containing service info
    """
    zeroconf = Zeroconf()
    listener = CollectingListener()
    browser = ServiceBrowser(zeroconf, '_hap._tcp.local.', listener)
    sleep(1)

    devices = []
    for info in listener.get_data():
        device = {
            'name': info.name,
            'address': inet_ntoa(info.address),
            'port': info.port,
            'c#': int(info.properties[b'c#'].decode()),
            'ff': int(info.properties[b'ff'].decode()),
            'id': info.properties[b'id'].decode(),
            'md': info.properties[b'md'].decode(),
            'pv': info.properties[b'pv'].decode(),
            's#': int(info.properties[b's#'].decode()),
            'sf': int(info.properties[b'sf'].decode()),
            'ci': int(info.properties[b'ci'].decode()),
        }
        devices.append(device)

    browser.cancel()
    zeroconf.close()
    return devices
Beispiel #6
0
def find_device_ip_and_port(device_id: str):
    """
    Find a specific device on the network.

    :param device_id: ID of device to search for
    :returns: dict containing IP and port, if found, else None
    """
    result = None
    zeroconf = Zeroconf()
    listener = CollectingListener()
    browser = ServiceBrowser(zeroconf, '_hap._tcp.local.', listener)
    counter = 0

    while result is None and counter < 10:
        sleep(1)
        data = listener.get_data()
        for info in data:
            if info.properties[b'id'].decode() == device_id:
                result = {'ip': inet_ntoa(info.address), 'port': info.port}
                break
        counter += 1

    browser.cancel()
    zeroconf.close()
    return result
Beispiel #7
0
    def _main_loop():
        """Main Zeroconf service loop that starts browsing for WoT
        services and processes the register tasks queue."""

        browser = None

        try:
            browser = ServiceBrowser(
                zeroconf,
                DNSSDDiscoveryService.WOT_SERVICE_TYPE,
                handlers=[_on_service_change])

            while not close_event.is_set():
                try:
                    register_task = register_queue.get_nowait()

                    task_handler_map = {
                        TASK_REGISTER: _register,
                        TASK_UNREGISTER: _unregister
                    }

                    task_handler_map[register_task['type']](register_task)
                except queue.Empty:
                    pass

                close_event.wait(ITER_WAIT)
        finally:
            if browser is not None:
                browser.cancel()

            with registered_lock:
                for serv_info in registered:
                    zeroconf.unregister_service(serv_info)

            zeroconf.close()
Beispiel #8
0
def test_integration():
    service_added = Event()
    service_removed = Event()

    type_ = "_http._tcp.local."
    registration_name = "xxxyyy.%s" % type_

    def on_service_state_change(zeroconf, service_type, state_change, name):
        if name == registration_name:
            if state_change is ServiceStateChange.Added:
                service_added.set()
            elif state_change is ServiceStateChange.Removed:
                service_removed.set()

    zeroconf_browser = Zeroconf(interfaces=['127.0.0.1'])
    browser = ServiceBrowser(zeroconf_browser, type_,
                             [on_service_state_change])

    zeroconf_registrar = Zeroconf(interfaces=['127.0.0.1'])
    desc = {'path': '/~paulsm/'}
    info = ServiceInfo(type_, registration_name, socket.inet_aton("10.0.1.2"),
                       80, 0, 0, desc, "ash-2.local.")
    zeroconf_registrar.register_service(info)

    try:
        service_added.wait(1)
        assert service_added.is_set()
        # Don't remove service, allow close() to cleanup

    finally:
        zeroconf_registrar.close()
        browser.cancel()
        zeroconf_browser.close()
class Discoverer:
    ANNOUNCE_PERIOD_SECS = 1
    MAXIMUM_WAIT_CYCLES = 10
    SERVICE_TYPE = "_googlemdt._tcp.local."

    def __init__(self, listener=None):
        self.discoveries = {}
        self.listener = listener
        self.zeroconf = None

    def discover(self):
        self.zeroconf = Zeroconf()
        self.browser = ServiceBrowser(self.zeroconf, Discoverer.SERVICE_TYPE,
                                      self)
        self._heard_announcement = True
        cycle_count = 0

        # Keep waiting until we stop hearing announcements for a full second, or until we've waited 10 seconds
        while self._heard_announcement and cycle_count < Discoverer.MAXIMUM_WAIT_CYCLES:
            cycle_count += 1
            self._heard_announcement = False
            time.sleep(Discoverer.ANNOUNCE_PERIOD_SECS)

        self.browser.cancel()
        self.browser = None
        self.zeroconf = None

    def add_service(self, zeroconf, type, name):
        info = self.zeroconf.get_service_info(type, name)

        if info:
            hostname = info.server.split('.')[0]
            address = info.parsed_addresses()[0]

            # Prevent duplicate announcements from extending the discovery delay
            if hostname not in self.discoveries:
                self._heard_announcement = True

            self.discoveries[hostname] = address

            if self.listener and hasattr(self.listener, "add_device"):
                self.listener.add_device(hostname, address)

    def remove_service(self, zeroconf, type, name):
        info = self.zeroconf.get_service_info(type, name)

        if info:
            if self.listener and hasattr(self.listener, "remove_device"):
                self.listener.remove_device(info.server,
                                            self.discoveries[info.server])

            if info.server in self.discoveries:
                self._heard_announcement = True
                del (self.discoveries[info.server])

    def update_service(self, zeroconf, type, name):
        """TODO(jtgans): Add this method once zeroconf figures out what it's for."""
        pass
Beispiel #10
0
class DashboardImportDiscovery:
    def __init__(self, zc: Zeroconf) -> None:
        self.zc = zc
        self.service_browser = ServiceBrowser(
            self.zc, ESPHOME_SERVICE_TYPE, [self._on_update]
        )
        self.import_state = {}

    def _on_update(
        self,
        zeroconf: Zeroconf,
        service_type: str,
        name: str,
        state_change: ServiceStateChange,
    ) -> None:
        _LOGGER.debug(
            "service_update: type=%s name=%s state_change=%s",
            service_type,
            name,
            state_change,
        )
        if service_type != ESPHOME_SERVICE_TYPE:
            return
        if state_change == ServiceStateChange.Removed:
            self.import_state.pop(name, None)

        info = zeroconf.get_service_info(service_type, name)
        _LOGGER.debug("-> resolved info: %s", info)
        if info is None:
            return
        node_name = name[: -len(ESPHOME_SERVICE_TYPE) - 1]
        required_keys = [
            TXT_RECORD_PACKAGE_IMPORT_URL,
            TXT_RECORD_PROJECT_NAME,
            TXT_RECORD_PROJECT_VERSION,
        ]
        if any(key not in info.properties for key in required_keys):
            # Not a dashboard import device
            return

        import_url = info.properties[TXT_RECORD_PACKAGE_IMPORT_URL].decode()
        project_name = info.properties[TXT_RECORD_PROJECT_NAME].decode()
        project_version = info.properties[TXT_RECORD_PROJECT_VERSION].decode()

        self.import_state[name] = DiscoveredImport(
            device_name=node_name,
            package_import_url=import_url,
            project_name=project_name,
            project_version=project_version,
        )

    def cancel(self) -> None:
        self.service_browser.cancel()
Beispiel #11
0
class DysonDiscovery:
    """Dyson device discovery."""
    def __init__(self):
        """Initialize the instance."""
        self._registered = {}
        self._discovered = {}
        self._lock = threading.Lock()
        self._browser = None

    def register_device(self, device: DysonDevice,
                        callback: Callable[[str], None]) -> None:
        """Register a device."""
        with self._lock:
            if device.serial in self._discovered:
                callback(self._discovered[device.serial])
            else:
                self._registered[device.serial] = callback

    def device_discovered(self, info: ServiceInfo) -> None:
        """Call when a device is discovered."""
        if info.type == TYPE_DYSON_360_EYE:
            serial = (info.name.split(".")[0]).split("-", 1)[1]
        else:  # TYPE_DYSON_FAN
            serial = (info.name.split(".")[0]).split("_")[1]
        address = socket.inet_ntoa(info.addresses[0])
        with self._lock:
            if serial in self._registered:
                callback = self._registered.pop(serial)
                callback(address)
            else:
                self._discovered[serial] = address

    def start_discovery(self,
                        zeroconf_instance: Optional[Zeroconf] = None) -> None:
        """Start discovery."""
        listener = DysonListener(self)
        zeroconf = zeroconf_instance or Zeroconf()
        self._browser = ServiceBrowser(
            zeroconf,
            [TYPE_DYSON_360_EYE, TYPE_DYSON_FAN],
            listener,
        )

    def stop_discovery(self) -> None:
        """Stop discovery."""
        try:
            self._browser.cancel()
        except RuntimeError:
            # Throws when called from callback
            # cannot join current thread
            pass
        self._browser.zc.close()
        self._browser = None
    def run(self):
        zeroconf = Zeroconf()
        browser = ServiceBrowser(zeroconf, GOOGLE_CAST_IDENTIFIER, self)

        try:
            with self.run_condition:
                self.run_condition.wait()

            self.logger.debug("end of run-body (discovery)")

        finally:
            browser.cancel()
            zeroconf.close()
Beispiel #13
0
class AirDropBrowser:
    def __init__(self, config):
        self.ip_addr = AirDropUtil.get_ip_for_interface(config.interface,
                                                        ipv6=True)
        if self.ip_addr is None:
            if config.interface == "awdl0":
                raise RuntimeError(
                    f"Interface {config.interface} does not have an IPv6 address. Make sure that `owl` is running."
                )
            else:
                raise RuntimeError(
                    f"Interface {config.interface} does not have an IPv6 address"
                )

        self.zeroconf = Zeroconf(
            interfaces=[str(self.ip_addr)],
            ip_version=IPVersion.V6Only,
            apple_p2p=platform.system() == "Darwin",
        )

        self.callback_add = None
        self.callback_remove = None
        self.browser = None

    def start(self, callback_add=None, callback_remove=None):
        """
        Start the AirDropBrowser to discover other AirDrop devices
        """
        if self.browser is not None:
            return  # already started
        self.callback_add = callback_add
        self.callback_remove = callback_remove
        self.browser = ServiceBrowser(self.zeroconf, "_airdrop._tcp.local.",
                                      self)

    def stop(self):
        self.browser.cancel()
        self.browser = None
        self.zeroconf.close()

    def add_service(self, zeroconf, service_type, name):
        info = zeroconf.get_service_info(service_type, name)
        logger.debug(f"Add service {name}")
        if self.callback_add is not None:
            self.callback_add(info)

    def remove_service(self, zeroconf, service_type, name):
        info = zeroconf.get_service_info(service_type, name)
        logger.debug(f"Remove service {name}")
        if self.callback_remove is not None:
            self.callback_remove(info)
def main():

    # Try to query server status...
    success = False
    try:

        # Alert user...
        print(_("Searching LAN for Helios servers... (ctrl-c to cancel)"))

        # Initialize Zeroconf...
        zeroconf = Zeroconf()

        # Construct listener...
        listener = LocalNetworkServiceListener()

        # Begin listening...
        browser = ServiceBrowser(zc=zeroconf,
                                 type_="_http._tcp.local.",
                                 listener=listener)
        browser_tls = ServiceBrowser(zc=zeroconf,
                                     type_="_https._tcp.local.",
                                     listener=listener)

        # Keep blocking while scanning...
        try:
            while True:
                sleep(0.1)

        # Unless the user requests to abort...
        except KeyboardInterrupt:
            print(_(F"\rAborting, please wait..."))

        # Cleanup Zeroconf...
        finally:
            browser.cancel()
            browser_tls.cancel()
            zeroconf.close()

        # Set exit status...
        success = True

    # Some other kind of exception...
    except Exception as some_exception:
        print(_(F"An unknown exception occurred: {print(some_exception)}"))

    # If unsuccessful, bail...
    if not success:
        sys.exit(1)

    # Done...
    sys.exit(0)
Beispiel #15
0
    def connect(self, num_retries):
        """
        Connects to the drone

        :param num_retries: maximum number of retries

        :return: True if the connection succeeded and False otherwise
        """

        if ("Mambo" not in self.drone_type):
            print("Setting up mDNS listener since this is not a Mambo")
            #parrot's latest mambo firmware (3.0.26 broke all of the mDNS services so this is (temporarily) commented
            #out but it is backwards compatible and will work with the hard-coded addresses for now.
            zeroconf = Zeroconf()
            listener = mDNSListener(self)

            print("Making a browser for %s" % self.mdns_address)

            browser = ServiceBrowser(zeroconf, self.mdns_address, listener)

            # basically have to sleep until the info comes through on the listener
            num_tries = 0
            while (num_tries < num_retries and not self.is_connected):
                time.sleep(1)
                num_tries += 1

            # if we didn't hear the listener, return False
            if (not self.is_connected):
                color_print(
                    "connection failed: did you remember to connect your machine to the Drone's wifi network?",
                    "ERROR")
                return False
            else:
                browser.cancel()

        # perform the handshake and get the UDP info
        handshake = self._handshake(num_retries)
        if (handshake):
            self._create_udp_connection()
            self.listener_thread = threading.Thread(target=self._listen_socket)
            self.listener_thread.start()

            color_print("Success in setting up the wifi network to the drone!",
                        "SUCCESS")
            return True
        else:
            color_print("Error: TCP handshake failed.", "ERROR")
            return False
Beispiel #16
0
class RioFinder:

    SERVICE_NAME = '_ni._tcp.local.'

    def __init__(self):
        self.zeroconf = Zeroconf()
        self.listener = Listener(self)
        self.browser = ServiceBrowser(self.zeroconf, RioFinder.SERVICE_NAME, self.listener)
        self.current_list = []

    def stop(self):
        self.browser.cancel()

    def on_new_rio(self, rio: dict):
        if json.dumps(rio, sort_keys=True) not in self.current_list:
            self.current_list.append(rio)
Beispiel #17
0
def wait_for_wireless(name,
                      service_name,
                      timeout=None):  # return (addresses, port)
    expecting_name = f"perfcat_{name}._{service_name}._tcp.local."
    print(f"[WIRELESS] expecting {expecting_name}")
    done = threading.Event()
    ctx = {}

    class MyListener:
        def add_service(self, zeroconf, type, name):
            # info = zeroconf.get_service_info(type, name)

            _info = MyServiceInfo(type, name)
            info = None
            if _info.request(zeroconf, 5000):
                info = _info
            if not info:
                if _info.request(zeroconf, 2000, True):
                    info = _info
            # _info = ServiceInfo(type, name, parsed_addresses=["10.3.3.230"])
            # if _info.request(zeroconf, 1000):
            #     info = _info
            # print(f"[Service] `{name}` added, service info: `{info}`")

            ctx['addresses'] = list(map(socket.inet_ntoa, info.addresses))
            print(f"[Service] `{name}` found, `{ctx}`")
            if name == expecting_name:
                ctx['addresses'] = list(map(socket.inet_ntoa, info.addresses))
                ctx['port'] = info.port
                print(f"[Service] `{name}` found, `{ctx}`")
                done.set()

    zero_conf = Zeroconf()
    # zero_conf._respond_sockets.pop(0)
    # zero_conf1 = zero_conf._respond_sockets
    # for index in zero_conf1:
    #     print(index)
    listener = MyListener()
    browser = ServiceBrowser(zero_conf, f"_{service_name}._tcp.local.",
                             listener)
    if not done.wait(timeout):
        ctx['addresses'] = []
        ctx['port'] = 0
    browser.cancel()
    zero_conf.close()

    return ctx['addresses'], ctx['port']
Beispiel #18
0
    def find_cloudlets(self, seconds_to_wait=3):
        print 'Started looking for cloudlets'
        self.services = {}
        zeroconf = Zeroconf()
        browser = ServiceBrowser(zeroconf, CloudletFinder.CLOUDLET_SERVICE_DNS, listener=self)

        # Wait to find cloudlets.
        print 'Waiting for results'
        time.sleep(seconds_to_wait)

        # Stop looking for cloudlets.
        browser.cancel()

        # Return the list of cloudlets found.
        print 'Cloudlets found: '
        print self.services
        return self.services
Beispiel #19
0
def Wait_for_privet_mdns_service(t_seconds,
                                 service,
                                 logger,
                                 wifi_interfaces=[]):
    """Listens for t_seconds and returns an information object for each service.

  This is the primary interface to discover mDNS services.  It blocks for
  t_seconds while listening, and returns a list of information objects, one
  for each service discovered.
  Args:
    t_seconds: Time to listen for mDNS records, in seconds.  Floating point ok.
    service: The service to wait for, if found, return early
    is_add: If True, wait for service to be added
            If False, wait for service to be removed
    wifi_interfaces: The interfaces to listen on as strings, if empty listen on
      all interfaces.  For example: ['192.168.1.2'].
  Returns:
    If Add event observed, return the Zeroconf information class;
    otherwise, return None
  """
    l = _Listener(logger)
    if not wifi_interfaces:
        z = Zeroconf()
    else:
        z = Zeroconf(wifi_interfaces)

    sb = ServiceBrowser(zc=z, type_='_privet._tcp.local.', listener=l)
    service_info = wait_for_service_add(t_seconds, service, l)
    sb.cancel()

    # Only method available to kill all threads pylint: disable=protected-access
    z._GLOBAL_DONE = True
    zeroconf_threads = _find_zeroconf_threads()

    # Wait up to 30 seconds for zeroconf to terminate its threads
    t_end = time.time() + 30
    while len(zeroconf_threads) > 1 and time.time() < t_end:
        time.sleep(0.01)
        zeroconf_threads = _find_zeroconf_threads()
    z.close()

    if len(zeroconf_threads) > 1:
        logger.info('Zeroconf failed to terminate its threads in 30 seconds.')
    else:
        logger.info('All listeners have been stopped.')
    return service_info
Beispiel #20
0
class ZeroConfListener():
    """
    The ZeroConfListener handles the browse off all Zeptrion Air.

    It listen for all Zeptrion Air devices available on zeroconf.

    """
    def __init__(self, add_service_handler):
        """
        Init the ZeroConfListener.

            :param add_service_handler: -- that gets the new found devices
        """
        self._add_service_handler = add_service_handler
        self._zeroconf = None
        self._browser = None
        self._service_type = "_zapp._tcp.local."

    def start_searching(self):
        """Start searching."""
        self._zeroconf = Zeroconf()
        self._browser = ServiceBrowser(self._zeroconf, self._service_type,
                                       self)

    def stop_searching(self):
        """Stop searching."""
        self._browser.cancel()
        self._zeroconf.close()

    @staticmethod
    def is_a_zapp_device(name):
        """Test id the device is a zeptrionAir device."""
        return name.startswith('zapp-') and name[5:13].isdigit()

    def remove_service(self, zeroconf, _type, name):
        """Remove a service."""
        pass

    def add_service(self, zeroconf, _type, name):
        """Add a zeptrion Service and call the add_service_handler."""
        info = zeroconf.get_service_info(_type, name)
        if info:
            if self.is_a_zapp_device(info.name):
                _ip = socket.inet_ntoa(info.address)
                self._add_service_handler(name, _ip, info.port)
Beispiel #21
0
def discover_network() -> dict[str, Device]:
    """
    Discover devices that expose the devolo device API via mDNS synchronous.

    :return: Devices accessible via serial number.
    """
    devices: dict[str, Device] = {}

    def add(zeroconf: Zeroconf, service_type: str, name: str,
            state_change: ServiceStateChange) -> None:
        """React on state changes."""
        _add(devices, zeroconf, service_type, name, state_change)

    browser = ServiceBrowser(Zeroconf(),
                             SERVICE_TYPE, [add],
                             question_type=DNSQuestionType.QM)
    time.sleep(3)
    browser.cancel()
    return devices
Beispiel #22
0
    def connect(self, num_retries):
        """
        Connects to the drone

        :param num_retries: maximum number of retries

        :return: True if the connection succeeded and False otherwise
        """

        zeroconf = Zeroconf()
        listener = mDNSListener(self)

        browser = ServiceBrowser(zeroconf, self.mdns_address, listener)

        # basically have to sleep until the info comes through on the listener
        num_tries = 0
        while (num_tries < num_retries and not self.is_connected):
            time.sleep(1)
            num_tries += 1

        # if we didn't hear the listener, return False
        if (not self.is_connected):
            color_print(
                "connection failed: did you remember to connect your machine to the Drone's wifi network?",
                "ERROR")
            return False
        else:
            browser.cancel()

        # perform the handshake and get the UDP info
        handshake = self._handshake(num_retries)
        if (handshake):
            self._create_udp_connection()
            self.listener_thread = threading.Thread(target=self._listen_socket)
            self.listener_thread.start()

            color_print("Success in setting up the wifi network to the drone!",
                        "SUCCESS")
            return True
        else:
            color_print("Error: TCP handshake failed.", "ERROR")
            return False
Beispiel #23
0
def discover_chromecasts(max_devices=None, timeout=DISCOVER_TIMEOUT):
    """ Discover chromecasts on the network. """
    try:
        zconf = Zeroconf()
        listener = CastListener()
        browser = ServiceBrowser(zconf, "_googlecast._tcp.local.", listener)

        if max_devices is None:
            time.sleep(timeout)
            return listener.devices

        else:
            start = time.time()

            while (time.time() - start < timeout and
                   listener.count < max_devices):
                time.sleep(.1)

            return listener.devices
    finally:
        browser.cancel()
        zconf.close()
def discover_chromecasts(max_devices=None, timeout=DISCOVER_TIMEOUT):
    """ Discover chromecasts on the network. """
    try:
        zconf = Zeroconf()
        listener = CastListener()
        browser = ServiceBrowser(zconf, "_googlecast._tcp.local.", listener)

        if max_devices is None:
            time.sleep(timeout)
            return listener.devices

        else:
            start = time.time()

            while (time.time() - start < timeout
                   and listener.count < max_devices):
                time.sleep(.1)

            return listener.devices
    finally:
        browser.cancel()
        zconf.close()
Beispiel #25
0
def test_integration():
    service_added = Event()
    service_removed = Event()

    type_ = "_http._tcp.local."
    registration_name = "xxxyyy.%s" % type_

    class MyListener(object):

        def remove_service(self, zeroconf, type_, name):
            if name == registration_name:
                service_removed.set()

        def add_service(self, zeroconf, type_, name):
            if name == registration_name:
                service_added.set()

    zeroconf_browser = Zeroconf()
    listener = MyListener()
    browser = ServiceBrowser(zeroconf_browser, type_, listener)

    zeroconf_registrar = Zeroconf()
    desc = {'path': '/~paulsm/'}
    info = ServiceInfo(
        type_, registration_name,
        socket.inet_aton("10.0.1.2"), 80, 0, 0,
        desc, "ash-2.local.")
    zeroconf_registrar.register_service(info)

    try:
        service_added.wait(1)
        assert service_added.is_set()
        zeroconf_registrar.unregister_service(info)
        service_removed.wait(1)
        assert service_removed.is_set()
    finally:
        zeroconf_registrar.close()
        browser.cancel()
        zeroconf_browser.close()
def main():
    # Catch CNTRL-C signel
    global SHUTDOWN
    signal.signal(signal.SIGINT, signal_cntrl_c)

    init_logging("", 2)
    context = zmq.Context.instance()
    zeroconf = Zeroconf()
    listener = ZeroConfListener(context)
    browser = ServiceBrowser(zeroconf, "_DashZMQ._tcp.local.", listener)

    b = zmq_tcpBridge(tcp_port=5001, context=context)

    while not SHUTDOWN:
        time.sleep(1)

    print("Goodbye")
    zeroconf.unregister_all_services()
    b.close()
    time.sleep(1)
    zeroconf.close()
    browser.cancel()
Beispiel #27
0
class YandexIOListener:
    add_handlerer = None
    browser = None

    def __init__(self, loop):
        self.loop = loop

    def start(self, handlerer: Callable, zeroconf: Zeroconf):
        self.add_handlerer = handlerer
        self.browser = ServiceBrowser(zeroconf,
                                      '_yandexio._tcp.local.',
                                      handlers=[self._zeroconf_handler])

    def stop(self, *args):
        self.browser.cancel()
        self.browser.zc.close()

    def _zeroconf_handler(self, zeroconf: Zeroconf, service_type: str,
                          name: str, state_change: ServiceStateChange):
        info = zeroconf.get_service_info(service_type, name)
        if not info:
            return

        properties = {
            k.decode(): v.decode() if isinstance(v, bytes) else v
            for k, v in info.properties.items()
        }

        coro = self.add_handlerer({
            'device_id':
            properties['deviceId'],
            'platform':
            properties['platform'],
            'host':
            str(ipaddress.ip_address(info.addresses[0])),
            'port':
            info.port
        })
        self.loop.create_task(coro)
Beispiel #28
0
def test_notify_listeners():
    """Test adding and removing notify listeners."""
    # instantiate a zeroconf instance
    zc = Zeroconf(interfaces=['127.0.0.1'])
    notify_called = 0

    class TestNotifyListener(r.NotifyListener):
        def notify_all(self):
            nonlocal notify_called
            notify_called += 1

    with pytest.raises(NotImplementedError):
        r.NotifyListener().notify_all()

    notify_listener = TestNotifyListener()

    zc.add_notify_listener(notify_listener)

    def on_service_state_change(zeroconf, service_type, state_change, name):
        """Dummy service callback."""

    # start a browser
    browser = ServiceBrowser(zc, "_http._tcp.local.",
                             [on_service_state_change])
    browser.cancel()

    assert notify_called
    zc.remove_notify_listener(notify_listener)

    notify_called = 0
    # start a browser
    browser = ServiceBrowser(zc, "_http._tcp.local.",
                             [on_service_state_change])
    browser.cancel()

    assert not notify_called

    zc.close()
Beispiel #29
0
def discover_chromecasts(max_devices=None, timeout=DISCOVER_TIMEOUT):
    try:
        zconf = Zeroconf()
        listener = CastListener()
        browser = ServiceBrowser(zconf, "_googlecast._tcp.local.", listener)

        t = 0

        if max_devices is None:
            time.sleep(DISCOVER_TIMEOUT)
            return listener.devices

        else:
            while t < DISCOVER_TIMEOUT:
                time.sleep(.1)

                if listener.count >= max_devices:
                    return listener.devices

            return listener.devices
    finally:
        browser.cancel()
        zconf.close()
Beispiel #30
0
        def browse(localname):
            services = []

            def handler(zeroconf, service_type, name, state_change):
                if state_change is ServiceStateChange.Added:
                    services.append(name)

            sb = ServiceBrowser(self.zeroconf, localname, [handler])
            time.sleep(timeout)
            sb.cancel()

            answers, additional = [], []
            for service in services:
                answers.append(
                    dns.RRHeader(name=localname[:-6] + domain,
                                 ttl=ttl,
                                 type=dns.PTR,
                                 payload=dns.Record_PTR(name=service[:-6] +
                                                        domain)))
                #txt_ans, _, _ = txt(service)
                #srv_ans, _, a_ans = srv(service)
                #additional += a_ans + txt_ans + srv_ans
            return answers, [], additional
def test_integration():
    service_added = Event()
    service_removed = Event()
    unexpected_ttl = Event()
    got_query = Event()

    type_ = "_http._tcp.local."
    registration_name = "xxxyyy.%s" % type_

    def on_service_state_change(zeroconf, service_type, state_change, name):
        if name == registration_name:
            if state_change is ServiceStateChange.Added:
                service_added.set()
            elif state_change is ServiceStateChange.Removed:
                service_removed.set()

    zeroconf_browser = Zeroconf(interfaces=["127.0.0.1"])

    # we are going to monkey patch the zeroconf send to check packet sizes
    old_send = zeroconf_browser.send

    time_offset = 0

    def current_time_millis():
        """Current system time in milliseconds"""
        return time.time() * 1000 + time_offset * 1000

    expected_ttl = r._DNS_TTL

    # needs to be a list so that we can modify it in our phony send
    nbr_queries = [0, None]

    def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT):
        """Sends an outgoing packet."""
        pout = r.DNSIncoming(out.packet())

        for answer in pout.answers:
            nbr_queries[0] += 1
            if not answer.ttl > expected_ttl / 2:
                unexpected_ttl.set()

        got_query.set()
        old_send(out, addr=addr, port=port)

    # monkey patch the zeroconf send
    zeroconf_browser.send = send

    # monkey patch the zeroconf current_time_millis
    r.current_time_millis = current_time_millis

    service_added = Event()
    service_removed = Event()

    browser = ServiceBrowser(zeroconf_browser, type_,
                             [on_service_state_change])

    zeroconf_registrar = Zeroconf(interfaces=["127.0.0.1"])
    desc = {"path": "/~paulsm/"}
    info = ServiceInfo(
        type_,
        registration_name,
        socket.inet_aton("10.0.1.2"),
        80,
        0,
        0,
        desc,
        "ash-2.local.",
    )
    zeroconf_registrar.register_service(info)

    try:
        service_added.wait(1)
        assert service_added.is_set()

        sleep_count = 0
        while nbr_queries[0] < 50:
            time_offset += expected_ttl / 4
            zeroconf_browser.notify_all()
            sleep_count += 1
            got_query.wait(1)
            got_query.clear()
        assert not unexpected_ttl.is_set()

        # Don't remove service, allow close() to cleanup

    finally:
        zeroconf_registrar.close()
        service_removed.wait(1)
        assert service_removed.is_set()
        browser.cancel()
        zeroconf_browser.close()
    def test_lots_of_names(self):

        # instantiate a zeroconf instance
        zc = Zeroconf(interfaces=["127.0.0.1"])

        # create a bunch of servers
        type_ = "_my-service._tcp.local."
        name = "a wonderful service"
        server_count = 300
        self.generate_many_hosts(zc, type_, name, server_count)

        # verify that name changing works
        self.verify_name_change(zc, type_, name, server_count)

        # we are going to monkey patch the zeroconf send to check packet sizes
        old_send = zc.send

        # needs to be a list so that we can modify it in our phony send
        longest_packet = [0, None]

        def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT):
            """Sends an outgoing packet."""
            packet = out.packet()
            if longest_packet[0] < len(packet):
                longest_packet[0] = len(packet)
                longest_packet[1] = out
            old_send(out, addr=addr, port=port)

        # monkey patch the zeroconf send
        zc.send = send

        # dummy service callback
        def on_service_state_change(zeroconf, service_type, state_change,
                                    name):
            pass

        # start a browser
        browser = ServiceBrowser(zc, type_, [on_service_state_change])

        # wait until the browse request packet has maxed out in size
        sleep_count = 0
        while sleep_count < 100 and longest_packet[
                0] < r._MAX_MSG_ABSOLUTE - 100:
            sleep_count += 1
            time.sleep(0.1)

        browser.cancel()
        time.sleep(0.5)

        import zeroconf

        zeroconf.log.debug("sleep_count %d, sized %d", sleep_count,
                           longest_packet[0])

        # now the browser has sent at least one request, verify the size
        assert longest_packet[0] <= r._MAX_MSG_ABSOLUTE
        assert longest_packet[0] >= r._MAX_MSG_ABSOLUTE - 100

        # mock zeroconf's logger warning() and debug()
        from mock import patch

        patch_warn = patch("zeroconf.log.warning")
        patch_debug = patch("zeroconf.log.debug")
        mocked_log_warn = patch_warn.start()
        mocked_log_debug = patch_debug.start()

        # now that we have a long packet in our possession, let's verify the
        # exception handling.
        out = longest_packet[1]
        out.data.append(b"\0" * 1000)

        # mock the zeroconf logger and check for the correct logging backoff
        call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count
        # try to send an oversized packet
        zc.send(out)
        assert mocked_log_warn.call_count == call_counts[0] + 1
        assert mocked_log_debug.call_count == call_counts[0]
        zc.send(out)
        assert mocked_log_warn.call_count == call_counts[0] + 1
        assert mocked_log_debug.call_count == call_counts[0] + 1

        # force a receive of an oversized packet
        packet = out.packet()
        s = zc._respond_sockets[0]

        # mock the zeroconf logger and check for the correct logging backoff
        call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count
        # force receive on oversized packet
        s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT))
        s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT))
        time.sleep(2.0)
        zeroconf.log.debug(
            "warn %d debug %d was %s",
            mocked_log_warn.call_count,
            mocked_log_debug.call_count,
            call_counts,
        )
        assert mocked_log_debug.call_count > call_counts[0]

        # close our zeroconf which will close the sockets
        zc.close()

        # pop the big chunk off the end of the data and send on a closed socket
        out.data.pop()
        zc._GLOBAL_DONE = False

        # mock the zeroconf logger and check for the correct logging backoff
        call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count
        # send on a closed socket (force a socket error)
        zc.send(out)
        zeroconf.log.debug(
            "warn %d debug %d was %s",
            mocked_log_warn.call_count,
            mocked_log_debug.call_count,
            call_counts,
        )
        assert mocked_log_warn.call_count > call_counts[0]
        assert mocked_log_debug.call_count > call_counts[0]
        zc.send(out)
        zeroconf.log.debug(
            "warn %d debug %d was %s",
            mocked_log_warn.call_count,
            mocked_log_debug.call_count,
            call_counts,
        )
        assert mocked_log_debug.call_count > call_counts[0] + 2

        mocked_log_warn.stop()
        mocked_log_debug.stop()
def test_integration():
    service_added = Event()
    service_removed = Event()
    unexpected_ttl = Event()
    got_query = Event()

    type_ = "_http._tcp.local."
    registration_name = "xxxyyy.%s" % type_

    def on_service_state_change(zeroconf, service_type, state_change, name):
        if name == registration_name:
            if state_change is ServiceStateChange.Added:
                service_added.set()
            elif state_change is ServiceStateChange.Removed:
                service_removed.set()

    zeroconf_browser = Zeroconf(interfaces=['127.0.0.1'])

    # we are going to monkey patch the zeroconf send to check packet sizes
    old_send = zeroconf_browser.send

    time_offset = 0

    def current_time_millis():
        """Current system time in milliseconds"""
        return time.time() * 1000 + time_offset * 1000

    expected_ttl = r._DNS_TTL

    # needs to be a list so that we can modify it in our phony send
    nbr_queries = [0, None]

    def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT):
        """Sends an outgoing packet."""
        pout = r.DNSIncoming(out.packet())

        for answer in pout.answers:
            nbr_queries[0] += 1
            if not answer.ttl > expected_ttl / 2:
                unexpected_ttl.set()

        got_query.set()
        old_send(out, addr=addr, port=port)

    # monkey patch the zeroconf send
    zeroconf_browser.send = send

    # monkey patch the zeroconf current_time_millis
    r.current_time_millis = current_time_millis

    service_added = Event()
    service_removed = Event()

    browser = ServiceBrowser(zeroconf_browser, type_, [on_service_state_change])

    zeroconf_registrar = Zeroconf(interfaces=['127.0.0.1'])
    desc = {'path': '/~paulsm/'}
    info = ServiceInfo(
        type_, registration_name,
        socket.inet_aton("10.0.1.2"), 80, 0, 0,
        desc, "ash-2.local.")
    zeroconf_registrar.register_service(info)

    try:
        service_added.wait(1)
        assert service_added.is_set()

        sleep_count = 0
        while nbr_queries[0] < 50:
            time_offset += expected_ttl / 4
            zeroconf_browser.notify_all()
            sleep_count += 1
            got_query.wait(1)
            got_query.clear()
        assert not unexpected_ttl.is_set()

        # Don't remove service, allow close() to cleanup

    finally:
        zeroconf_registrar.close()
        browser.cancel()
        zeroconf_browser.close()
    def test_lots_of_names(self):

        # instantiate a zeroconf instance
        zc = Zeroconf(interfaces=['127.0.0.1'])

        # create a bunch of servers
        type_ = "_my-service._tcp.local."
        name = 'a wonderful service'
        server_count = 300
        self.generate_many_hosts(zc, type_, name, server_count)

        # verify that name changing works
        self.verify_name_change(zc, type_, name, server_count)

        # we are going to monkey patch the zeroconf send to check packet sizes
        old_send = zc.send

        longest_packet_len = 0
        longest_packet = None  # type: Optional[r.DNSOutgoing]

        def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT):
            """Sends an outgoing packet."""
            packet = out.packet()
            nonlocal longest_packet_len, longest_packet
            if longest_packet_len < len(packet):
                longest_packet_len = len(packet)
                longest_packet = out
            old_send(out, addr=addr, port=port)

        # monkey patch the zeroconf send
        setattr(zc, "send", send)

        # dummy service callback
        def on_service_state_change(zeroconf, service_type, state_change, name):
            pass

        # start a browser
        browser = ServiceBrowser(zc, type_, [on_service_state_change])

        # wait until the browse request packet has maxed out in size
        sleep_count = 0
        while sleep_count < 100 and longest_packet_len < r._MAX_MSG_ABSOLUTE - 100:
            sleep_count += 1
            time.sleep(0.1)

        browser.cancel()
        time.sleep(0.5)

        import zeroconf

        zeroconf.log.debug('sleep_count %d, sized %d', sleep_count, longest_packet_len)

        # now the browser has sent at least one request, verify the size
        assert longest_packet_len <= r._MAX_MSG_ABSOLUTE
        assert longest_packet_len >= r._MAX_MSG_ABSOLUTE - 100

        # mock zeroconf's logger warning() and debug()
        from unittest.mock import patch

        patch_warn = patch('zeroconf.log.warning')
        patch_debug = patch('zeroconf.log.debug')
        mocked_log_warn = patch_warn.start()
        mocked_log_debug = patch_debug.start()

        # now that we have a long packet in our possession, let's verify the
        # exception handling.
        out = longest_packet
        assert out is not None
        out.data.append(b'\0' * 1000)

        # mock the zeroconf logger and check for the correct logging backoff
        call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count
        # try to send an oversized packet
        zc.send(out)
        assert mocked_log_warn.call_count == call_counts[0] + 1
        assert mocked_log_debug.call_count == call_counts[0]
        zc.send(out)
        assert mocked_log_warn.call_count == call_counts[0] + 1
        assert mocked_log_debug.call_count == call_counts[0] + 1

        # force a receive of an oversized packet
        packet = out.packet()
        s = zc._respond_sockets[0]

        # mock the zeroconf logger and check for the correct logging backoff
        call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count
        # force receive on oversized packet
        s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT))
        s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT))
        time.sleep(2.0)
        zeroconf.log.debug(
            'warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts
        )
        assert mocked_log_debug.call_count > call_counts[0]

        # close our zeroconf which will close the sockets
        zc.close()

        # pop the big chunk off the end of the data and send on a closed socket
        out.data.pop()
        zc._GLOBAL_DONE = False

        # mock the zeroconf logger and check for the correct logging backoff
        call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count
        # send on a closed socket (force a socket error)
        zc.send(out)
        zeroconf.log.debug(
            'warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts
        )
        assert mocked_log_warn.call_count > call_counts[0]
        assert mocked_log_debug.call_count > call_counts[0]
        zc.send(out)
        zeroconf.log.debug(
            'warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts
        )
        assert mocked_log_debug.call_count > call_counts[0] + 2

        mocked_log_warn.stop()
        mocked_log_debug.stop()
def test_backoff():
    got_query = Event()

    type_ = "_http._tcp.local."
    zeroconf_browser = Zeroconf(interfaces=['127.0.0.1'])

    # we are going to monkey patch the zeroconf send to check query transmission
    old_send = zeroconf_browser.send

    time_offset = 0.0
    start_time = time.time() * 1000
    initial_query_interval = r._BROWSER_TIME / 1000

    def current_time_millis():
        """Current system time in milliseconds"""
        return start_time + time_offset * 1000

    def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT):
        """Sends an outgoing packet."""
        got_query.set()
        old_send(out, addr=addr, port=port)

    # monkey patch the zeroconf send
    setattr(zeroconf_browser, "send", send)

    # monkey patch the zeroconf current_time_millis
    r.current_time_millis = current_time_millis

    # monkey patch the backoff limit to prevent test running forever
    r._BROWSER_BACKOFF_LIMIT = 10  # seconds

    # dummy service callback
    def on_service_state_change(zeroconf, service_type, state_change, name):
        pass

    browser = ServiceBrowser(zeroconf_browser, type_, [on_service_state_change])

    try:
        # Test that queries are sent at increasing intervals
        sleep_count = 0
        next_query_interval = 0.0
        expected_query_time = 0.0
        while True:
            zeroconf_browser.notify_all()
            sleep_count += 1
            got_query.wait(0.1)
            if time_offset == expected_query_time:
                assert got_query.is_set()
                got_query.clear()
                if next_query_interval == r._BROWSER_BACKOFF_LIMIT:
                    # Only need to test up to the point where we've seen a query
                    # after the backoff limit has been hit
                    break
                elif next_query_interval == 0:
                    next_query_interval = initial_query_interval
                    expected_query_time = initial_query_interval
                else:
                    next_query_interval = min(2 * next_query_interval, r._BROWSER_BACKOFF_LIMIT)
                    expected_query_time += next_query_interval
            else:
                assert not got_query.is_set()
            time_offset += initial_query_interval

    finally:
        browser.cancel()
        zeroconf_browser.close()
def test_integration():
    service_added = Event()
    service_removed = Event()
    unexpected_ttl = Event()
    got_query = Event()

    type_ = "_http._tcp.local."
    registration_name = "xxxyyy.%s" % type_

    def on_service_state_change(zeroconf, service_type, state_change, name):
        if name == registration_name:
            if state_change is ServiceStateChange.Added:
                service_added.set()
            elif state_change is ServiceStateChange.Removed:
                service_removed.set()

    zeroconf_browser = Zeroconf(interfaces=['127.0.0.1'])

    # we are going to monkey patch the zeroconf send to check packet sizes
    old_send = zeroconf_browser.send

    time_offset = 0.0

    def current_time_millis():
        """Current system time in milliseconds"""
        return time.time() * 1000 + time_offset * 1000

    expected_ttl = r._DNS_HOST_TTL

    nbr_answers = 0

    def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT):
        """Sends an outgoing packet."""
        pout = r.DNSIncoming(out.packet())
        nonlocal nbr_answers
        for answer in pout.answers:
            nbr_answers += 1
            if not answer.ttl > expected_ttl / 2:
                unexpected_ttl.set()

        got_query.set()
        old_send(out, addr=addr, port=port)

    # monkey patch the zeroconf send
    setattr(zeroconf_browser, "send", send)

    # monkey patch the zeroconf current_time_millis
    r.current_time_millis = current_time_millis

    # monkey patch the backoff limit to ensure we always get one query every 1/4 of the DNS TTL
    r._BROWSER_BACKOFF_LIMIT = int(expected_ttl / 4)

    service_added = Event()
    service_removed = Event()

    browser = ServiceBrowser(zeroconf_browser, type_, [on_service_state_change])

    zeroconf_registrar = Zeroconf(interfaces=['127.0.0.1'])
    desc = {'path': '/~paulsm/'}
    info = ServiceInfo(type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.")
    zeroconf_registrar.register_service(info)

    try:
        service_added.wait(1)
        assert service_added.is_set()

        # Test that we receive queries containing answers only if the remaining TTL
        # is greater than half the original TTL
        sleep_count = 0
        test_iterations = 50
        while nbr_answers < test_iterations:
            # Increase simulated time shift by 1/4 of the TTL in seconds
            time_offset += expected_ttl / 4
            zeroconf_browser.notify_all()
            sleep_count += 1
            got_query.wait(0.1)
            got_query.clear()
            # Prevent the test running indefinitely in an error condition
            assert sleep_count < test_iterations * 4
        assert not unexpected_ttl.is_set()

        # Don't remove service, allow close() to cleanup

    finally:
        zeroconf_registrar.close()
        service_removed.wait(1)
        assert service_removed.is_set()
        browser.cancel()
        zeroconf_browser.close()