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 setUpClass(cls): cls.browser = Zeroconf(interfaces=['127.0.0.1'])
def setup(hass, config): """Set up Zeroconf and make AIS dom discoverable.""" if not ais_global.has_root(): return True from zeroconf import Zeroconf, ServiceInfo zero_config = Zeroconf() host_ip = util.get_local_ip() try: return_value = subprocess.check_output( "getprop net.hostname", timeout=15, shell=True, # nosec ) host_name = return_value.strip().decode("utf-8") except subprocess.CalledProcessError: host_name = socket.gethostname() if len(host_name) == 0: # get the mac address import uuid host_name = "".join([ "{:02x}".format((uuid.getnode() >> i) & 0xFF) for i in range(0, 8 * 6, 8) ][::-1]) if host_name.endswith(".local"): host_name = host_name[:-len(".local")] hass.states.async_set( "sensor.local_host_name", host_name.upper(), { "friendly_name": "Lokalna nazwa hosta", "icon": "mdi:dns" }, ) try: host_ip_pton = socket.inet_pton(socket.AF_INET, host_ip) except OSError: host_ip_pton = socket.inet_pton(socket.AF_INET6, host_ip) try: gate_id = ais_global.get_sercure_android_id_dom() except Exception: gate_id = "xxx" params = { "location_name": hass.config.location_name, "version": __version__, "company_url": "https://www.ai-speaker.com", "gate_id": gate_id, } # HTTP http_info = ServiceInfo( "_http._tcp.local.", name=host_name + "._http._tcp.local.", server=f"{host_name}.local.", addresses=[host_ip_pton], port=80, properties=params, ) # FTP ftp_info = ServiceInfo( "_ftp._tcp.local.", name=host_name + "._ftp._tcp.local.", server=f"{host_name}.local.", addresses=[host_ip_pton], port=21, properties=params, ) def zeroconf_hass_start(_event): """Expose Home Assistant on zeroconf when it starts. Wait till started or otherwise HTTP is not up and running. """ _LOGGER.info("Starting Zeroconf broadcast") try: zero_config.register_service(http_info) zero_config.register_service(ftp_info) except NonUniqueNameException: _LOGGER.error( "Home Assistant instance with identical name present in the local network" ) hass.bus.listen_once(EVENT_HOMEASSISTANT_START, zeroconf_hass_start) def stop_zeroconf(event): """Stop Zeroconf.""" zero_config.unregister_service(http_info) zero_config.unregister_service(ftp_info) zero_config.close() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zeroconf) return True
def __init__(self): super(MainWindow, self).__init__() self.showLogs = True available_out_ports = midiout.get_ports() self.midiin = rtmidi.MidiIn() available_in_ports = self.midiin.get_ports() # WIDGETS # top groupbox (utils) utilsGroupbox = QGroupBox("Utils") utilsWidgetContainer = QHBoxLayout() utilsGroupbox.setLayout(utilsWidgetContainer) # utils widgets btnRefreshMidiSources = QPushButton("Refresh available midi devices") btnRefreshMidiSources.clicked.connect(self.refreshMidiDevices) # create virtual midi ports btn btnCreateVirtualMidi = QPushButton("Create virtual ALSA midi ports") btnCreateVirtualMidi.clicked.connect(self.createAlsaVirtualMidiPorts) # remove virtual midi ports btn btnRemoveVirtualMidi = QPushButton("Remove virtual ALSA midi ports") btnRemoveVirtualMidi.clicked.connect(self.removeAlsaVirtualMidiPorts) utilsWidgetContainer.addWidget(btnRefreshMidiSources) utilsWidgetContainer.addWidget(btnCreateVirtualMidi) utilsWidgetContainer.addWidget(btnRemoveVirtualMidi) # MIDI SOURCES (BRIDGE-IN) midiInGroupbox = QGroupBox("Select midi bridge source") midiInGroupboxContainer = QHBoxLayout() midiInGroupbox.setLayout(midiInGroupboxContainer) self.comboIn = QComboBox() for min in available_in_ports: self.comboIn.addItem(min) self.comboIn.adjustSize() self.comboIn.activated[int].connect(self.selectMidiIn) midiInGroupboxContainer.addWidget(self.comboIn) midiInGroupbox.setLayout(midiInGroupboxContainer) # MIDI DEST (BRIDGE-OUT) midiOutGroupbox = QGroupBox("Select midi bridge destination") midiOutGroupboxContainer = QHBoxLayout() midiOutGroupbox.setLayout(midiOutGroupboxContainer) self.comboOut = QComboBox() for out in available_out_ports: self.comboOut.addItem(out) self.comboOut.adjustSize() self.comboOut.activated[int].connect(self.selectMidiOut) midiOutGroupboxContainer.addWidget(self.comboOut) midiOutGroupbox.setLayout(midiOutGroupboxContainer) # instanciate zeronconf zeroconf = Zeroconf() zero_listener = ZeroConfListener() # browse for _apple-midi._udp service browser = ServiceBrowser(zeroconf, "_apple-midi._udp.local.", zero_listener) grid = QGridLayout() grid.addWidget(utilsGroupbox, 0, 0) grid.addWidget(midiInGroupbox, 1, 0) grid.addWidget(midiOutGroupbox, 2, 0) widget = QWidget() widget.setLayout(grid) self.setCentralWidget(widget) self.setWindowTitle("RT-MIDI-BRIDGE UI") self.show()
# self.Close(True) return 0 if __name__ == "__main__": HOST, PORT = socket.gethostbyname(socket.gethostname()), 9999 app = App() vis = FeatherVisualization(None, title=f"JoyWing @ {HOST} {PORT}", size=(800, 600)) vis.Show() #Prepare the broadcast of our service bonjour = Zeroconf() # Create the server, binding to localhost on port 9999 with ThreadedUDPServer((HOST, PORT), FeatherUDPHandler) as server: server_thread = threading.Thread(target=server.serve_forever) # Exit the server thread when the main thread terminates server_thread.daemon = True server_thread.start() #Now that the server is reachable desc = {'version': '0.10'} addresses = [socket.inet_aton(HOST)] expected = {HOST} if socket.has_ipv6: addresses.append(socket.inet_pton(socket.AF_INET6, '::1'))
def test_known_answer_supression(): zc = Zeroconf(interfaces=['127.0.0.1']) type_ = "_knownanswersv8._tcp.local." name = "knownname" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} server_name = "ash-2.local." info = ServiceInfo( type_, registration_name, 80, 0, 0, desc, server_name, addresses=[socket.inet_aton("10.0.1.2")] ) zc.registry.add(info) now = current_time_millis() _clear_cache(zc) # Test PTR supression generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(type_, const._TYPE_PTR, const._CLASS_IN) generated.add_question(question) packets = generated.packets() unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in packets], "1.2.3.4", const._MDNS_PORT ) assert unicast_out is None assert multicast_out is not None and multicast_out.answers generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(type_, const._TYPE_PTR, const._CLASS_IN) generated.add_question(question) generated.add_answer_at_time(info.dns_pointer(), now) packets = generated.packets() unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in packets], "1.2.3.4", const._MDNS_PORT ) assert unicast_out is None # If the answer is suppressed, the additional should be suppresed as well assert not multicast_out or not multicast_out.answers # Test A supression generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(server_name, const._TYPE_A, const._CLASS_IN) generated.add_question(question) packets = generated.packets() unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in packets], "1.2.3.4", const._MDNS_PORT ) assert unicast_out is None assert multicast_out is not None and multicast_out.answers generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(server_name, const._TYPE_A, const._CLASS_IN) generated.add_question(question) for dns_address in info.dns_addresses(): generated.add_answer_at_time(dns_address, now) packets = generated.packets() unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in packets], "1.2.3.4", const._MDNS_PORT ) assert unicast_out is None assert not multicast_out or not multicast_out.answers # Test SRV supression generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(registration_name, const._TYPE_SRV, const._CLASS_IN) generated.add_question(question) packets = generated.packets() unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in packets], "1.2.3.4", const._MDNS_PORT ) assert unicast_out is None assert multicast_out is not None and multicast_out.answers generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(registration_name, const._TYPE_SRV, const._CLASS_IN) generated.add_question(question) generated.add_answer_at_time(info.dns_service(), now) packets = generated.packets() unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in packets], "1.2.3.4", const._MDNS_PORT ) assert unicast_out is None # If the answer is suppressed, the additional should be suppresed as well assert not multicast_out or not multicast_out.answers # Test TXT supression generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(registration_name, const._TYPE_TXT, const._CLASS_IN) generated.add_question(question) packets = generated.packets() unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in packets], "1.2.3.4", const._MDNS_PORT ) assert unicast_out is None assert multicast_out is not None and multicast_out.answers generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(registration_name, const._TYPE_TXT, const._CLASS_IN) generated.add_question(question) generated.add_answer_at_time(info.dns_text(), now) packets = generated.packets() unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in packets], "1.2.3.4", const._MDNS_PORT ) assert unicast_out is None assert not multicast_out or not multicast_out.answers # unregister zc.registry.remove(info) zc.close()
def test_qu_response_only_sends_additionals_if_sends_answer(): """Test that a QU response does not send additionals unless it sends the answer as well.""" # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) type_ = "_addtest1._tcp.local." name = "knownname" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} server_name = "ash-2.local." info = ServiceInfo( type_, registration_name, 80, 0, 0, desc, server_name, addresses=[socket.inet_aton("10.0.1.2")] ) zc.registry.add(info) type_2 = "_addtest2._tcp.local." name = "knownname" registration_name2 = "%s.%s" % (name, type_2) desc = {'path': '/~paulsm/'} server_name2 = "ash-3.local." info2 = ServiceInfo( type_2, registration_name2, 80, 0, 0, desc, server_name2, addresses=[socket.inet_aton("10.0.1.2")] ) zc.registry.add(info2) ptr_record = info.dns_pointer() # Add the PTR record to the cache zc.cache.add(ptr_record) # Add the A record to the cache with 50% ttl remaining a_record = info.dns_addresses()[0] a_record.set_created_ttl(current_time_millis() - (a_record.ttl * 1000 / 2), a_record.ttl) assert not a_record.is_recent(current_time_millis()) zc.cache.add(a_record) # With QU should respond to only unicast when the answer has been recently multicast # even if the additional has not been recently multicast query = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN) question.unicast = True # Set the QU bit assert question.unicast is True query.add_question(question) unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT ) assert multicast_out is None assert a_record in unicast_out.additionals assert unicast_out.answers[0][0] == ptr_record # Remove the 50% A record and add a 100% A record zc.cache.remove(a_record) a_record = info.dns_addresses()[0] assert a_record.is_recent(current_time_millis()) zc.cache.add(a_record) # With QU should respond to only unicast when the answer has been recently multicast # even if the additional has not been recently multicast query = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN) question.unicast = True # Set the QU bit assert question.unicast is True query.add_question(question) unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT ) assert multicast_out is None assert a_record in unicast_out.additionals assert unicast_out.answers[0][0] == ptr_record # Remove the 100% PTR record and add a 50% PTR record zc.cache.remove(ptr_record) ptr_record.set_created_ttl(current_time_millis() - (ptr_record.ttl * 1000 / 2), ptr_record.ttl) assert not ptr_record.is_recent(current_time_millis()) zc.cache.add(ptr_record) # With QU should respond to only multicast since the has less # than 75% of its ttl remaining query = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN) question.unicast = True # Set the QU bit assert question.unicast is True query.add_question(question) unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT ) assert multicast_out.answers[0][0] == ptr_record assert a_record in multicast_out.additionals assert info.dns_text() in multicast_out.additionals assert info.dns_service() in multicast_out.additionals assert unicast_out is None # Ask 2 QU questions, with info the PTR is at 50%, with info2 the PTR is at 100% # We should get back a unicast reply for info2, but info should be multicasted since its within 75% of its TTL # With QU should respond to only multicast since the has less # than 75% of its ttl remaining query = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN) question.unicast = True # Set the QU bit assert question.unicast is True query.add_question(question) question = r.DNSQuestion(info2.type, const._TYPE_PTR, const._CLASS_IN) question.unicast = True # Set the QU bit assert question.unicast is True query.add_question(question) zc.cache.add(info2.dns_pointer()) # Add 100% TTL for info2 to the cache unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT ) assert multicast_out.answers[0][0] == info.dns_pointer() assert info.dns_addresses()[0] in multicast_out.additionals assert info.dns_text() in multicast_out.additionals assert info.dns_service() in multicast_out.additionals assert unicast_out.answers[0][0] == info2.dns_pointer() assert info2.dns_addresses()[0] in unicast_out.additionals assert info2.dns_text() in unicast_out.additionals assert info2.dns_service() in unicast_out.additionals # unregister zc.registry.remove(info) zc.close()
async def test_async_with_sync_passed_in() -> None: """Test we can create and close the instance when passing in a sync Zeroconf.""" zc = Zeroconf(interfaces=['127.0.0.1']) aiozc = AsyncZeroconf(zc=zc) assert aiozc.zeroconf is zc await aiozc.async_close()
import atexit import ipaddress from zeroconf import Zeroconf, ServiceInfo ZCONF = Zeroconf() class Provider(object): """ Multi-cast DNS Service Provider Provide a multicast DNS service with the given name and type listening on the given port with additional information in the data record. :param name: Name of service :param service_type: Service Type string :param port: Service port Kwargs: - data: Additional data to make available to clients - unique: bool, only one permitted, collisoin if more than one """ def __init__(self, name, service_type, port, data=None, unique=False, delay=1): super().__init__()
def discover(self): print( "{status} Smart Module hosting asset {asset_id} {asset_type} {asset_context}." .format(status="Mock" if self.rtc.mock else "Real", asset_id=self.asset.id, asset_type=self.asset.type, asset_context=self.asset.context)) try: max_sleep_time = 3 # Calling sleep should be reviewed. zeroconf = Zeroconf() Log.info("Performing Broker discovery...") self.find_broker(zeroconf) time.sleep(max_sleep_time ) # Wait for max_sleep_time to see if we found it. if self.comm.broker_name or self.comm.broker_ip: # Found it. Log.info("MQTT Broker: {broker_name} IP: {broker_ip}.".format( broker_name=self.comm.broker_name, broker_ip=self.comm.broker_ip)) else: # Make necessary actions to become the broker. Log.info("Broker not found. Becoming the broker.") self.become_broker() time.sleep(max_sleep_time) self.comm.connect() # Now it's time to connect to the broker. except Exception as excpt: Log.exception("[Exiting] Trying to find or become the broker.") finally: Log.info("Closing Zeroconf connection.") zeroconf.close() t_end = time.time() + 10 while (time.time() < t_end) and not self.comm.is_connected: time.sleep(1) self.comm.subscribe("SCHEDULER/RESPONSE") self.comm.send("SCHEDULER/QUERY", "Where are you?") Log.info("Waiting for Scheduler response...") time.sleep(5) # Just wait for reply... Need a review? self.comm.send("ANNOUNCE", self.hostname + " is online.") t_end = time.time() + 2 while (time.time() < t_end) and not self.comm.is_connected: time.sleep(1) if not self.comm.scheduler_found: # Become the Scheduler (necessary actions as Scheduler) try: Log.info("No Scheduler found. Becoming the Scheduler.") self.scheduler = Scheduler() self.scheduler.smart_module = self self.scheduler.prepare_jobs(self.scheduler.load_schedule()) self.comm.scheduler_found = True self.comm.subscribe("SCHEDULER/QUERY") self.comm.unsubscribe("SCHEDULER/RESPONSE") self.comm.subscribe("STATUS/RESPONSE" + "/#") self.comm.subscribe("ASSET/RESPONSE" + "/#") self.comm.subscribe("ALERT" + "/#") self.comm.send("SCHEDULER/RESPONSE", self.hostname) self.comm.send("ANNOUNCE", self.hostname + " is running the Scheduler.") Log.info("Scheduler program loaded.") except Exception as excpt: Log.exception("Error initializing scheduler. %s.", excpt)
def __init__(self, callbackadd, callbackremove): self.zeroconf = Zeroconf() self.SERVICEBROWSER = None self.listener = self.LmrListener(callbackadd, callbackremove)
def test_tc_bit_defers_last_response_missing(): zc = Zeroconf(interfaces=['127.0.0.1']) type_ = "_knowndefer._tcp.local." name = "knownname" name2 = "knownname2" name3 = "knownname3" registration_name = "%s.%s" % (name, type_) registration2_name = "%s.%s" % (name2, type_) registration3_name = "%s.%s" % (name3, type_) desc = {'path': '/~paulsm/'} server_name = "ash-2.local." server_name2 = "ash-3.local." server_name3 = "ash-4.local." info = r.ServiceInfo(type_, registration_name, 80, 0, 0, desc, server_name, addresses=[socket.inet_aton("10.0.1.2")]) info2 = r.ServiceInfo(type_, registration2_name, 80, 0, 0, desc, server_name2, addresses=[socket.inet_aton("10.0.1.2")]) info3 = r.ServiceInfo(type_, registration3_name, 80, 0, 0, desc, server_name3, addresses=[socket.inet_aton("10.0.1.2")]) zc.registry.add(info) zc.registry.add(info2) zc.registry.add(info3) def threadsafe_query(*args): async def make_query(): zc.handle_query(*args) asyncio.run_coroutine_threadsafe(make_query(), zc.loop).result() now = r.current_time_millis() _clear_cache(zc) source_ip = '203.0.113.12' generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(type_, const._TYPE_PTR, const._CLASS_IN) generated.add_question(question) for _ in range(300): # Add so many answers we end up with another packet generated.add_answer_at_time(info.dns_pointer(), now) generated.add_answer_at_time(info2.dns_pointer(), now) generated.add_answer_at_time(info3.dns_pointer(), now) packets = generated.packets() assert len(packets) == 4 expected_deferred = [] next_packet = r.DNSIncoming(packets.pop(0)) expected_deferred.append(next_packet) threadsafe_query(next_packet, source_ip, const._MDNS_PORT) assert zc._deferred[source_ip] == expected_deferred timer1 = zc._timers[source_ip] next_packet = r.DNSIncoming(packets.pop(0)) expected_deferred.append(next_packet) threadsafe_query(next_packet, source_ip, const._MDNS_PORT) assert zc._deferred[source_ip] == expected_deferred timer2 = zc._timers[source_ip] if sys.version_info >= (3, 7): assert timer1.cancelled() assert timer2 != timer1 # Send the same packet again to similar multi interfaces threadsafe_query(next_packet, source_ip, const._MDNS_PORT) assert zc._deferred[source_ip] == expected_deferred assert source_ip in zc._timers timer3 = zc._timers[source_ip] if sys.version_info >= (3, 7): assert not timer3.cancelled() assert timer3 == timer2 next_packet = r.DNSIncoming(packets.pop(0)) expected_deferred.append(next_packet) threadsafe_query(next_packet, source_ip, const._MDNS_PORT) assert zc._deferred[source_ip] == expected_deferred assert source_ip in zc._timers timer4 = zc._timers[source_ip] if sys.version_info >= (3, 7): assert timer3.cancelled() assert timer4 != timer3 for _ in range(8): time.sleep(0.1) if source_ip not in zc._timers: break assert source_ip not in zc._deferred assert source_ip not in zc._timers # unregister zc.registry.remove(info) zc.close()
def test_get_service_info_failure_path(): """Verify get_service_info return None when the underlying call returns False.""" zc = Zeroconf(interfaces=['127.0.0.1']) assert zc.get_service_info("_neverused._tcp.local.", "xneverused._neverused._tcp.local.", 10) is None zc.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()
def test_ttl(self): # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) # service definition type_ = "_test-srvc-type._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} info = ServiceInfo( type_, registration_name, 80, 0, 0, desc, "ash-2.local.", addresses=[socket.inet_aton("10.0.1.2")], ) nbr_answers = nbr_additionals = nbr_authorities = 0 def get_ttl(record_type): if expected_ttl is not None: return expected_ttl elif record_type in [const._TYPE_A, const._TYPE_SRV]: return const._DNS_HOST_TTL else: return const._DNS_OTHER_TTL def _process_outgoing_packet(out): """Sends an outgoing packet.""" nonlocal nbr_answers, nbr_additionals, nbr_authorities for answer, time_ in out.answers: nbr_answers += 1 assert answer.ttl == get_ttl(answer.type) for answer in out.additionals: nbr_additionals += 1 assert answer.ttl == get_ttl(answer.type) for answer in out.authorities: nbr_authorities += 1 assert answer.ttl == get_ttl(answer.type) # register service with default TTL expected_ttl = None for _ in range(3): _process_outgoing_packet(zc.generate_service_query(info)) zc.registry.add(info) for _ in range(3): _process_outgoing_packet(zc.generate_service_broadcast(info, None)) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 3 nbr_answers = nbr_additionals = nbr_authorities = 0 # query query = r.DNSOutgoing(const._FLAGS_QR_QUERY | const._FLAGS_AA) assert query.is_query() is True query.add_question(r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, const._TYPE_SRV, const._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, const._TYPE_TXT, const._CLASS_IN)) query.add_question(r.DNSQuestion(info.server, const._TYPE_A, const._CLASS_IN)) multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], None, const._MDNS_PORT )[1] _process_outgoing_packet(multicast_out) # The additonals should all be suppresed since they are all in the answers section # assert nbr_answers == 4 and nbr_additionals == 0 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0 # unregister expected_ttl = 0 zc.registry.remove(info) for _ in range(3): _process_outgoing_packet(zc.generate_service_broadcast(info, 0)) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0 expected_ttl = None for _ in range(3): _process_outgoing_packet(zc.generate_service_query(info)) zc.registry.add(info) # register service with custom TTL expected_ttl = const._DNS_HOST_TTL * 2 assert expected_ttl != const._DNS_HOST_TTL for _ in range(3): _process_outgoing_packet(zc.generate_service_broadcast(info, expected_ttl)) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 3 nbr_answers = nbr_additionals = nbr_authorities = 0 # query expected_ttl = None query = r.DNSOutgoing(const._FLAGS_QR_QUERY | const._FLAGS_AA) query.add_question(r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, const._TYPE_SRV, const._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, const._TYPE_TXT, const._CLASS_IN)) query.add_question(r.DNSQuestion(info.server, const._TYPE_A, const._CLASS_IN)) _process_outgoing_packet( zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], None, const._MDNS_PORT )[1] ) assert nbr_answers == 4 and nbr_additionals == 0 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0 # unregister expected_ttl = 0 zc.registry.remove(info) for _ in range(3): _process_outgoing_packet(zc.generate_service_broadcast(info, 0)) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0 zc.close()
def setup(hass, config): """Set up Zeroconf and make Home Assistant discoverable.""" zeroconf_name = '{}.{}'.format(hass.config.location_name, ZEROCONF_TYPE) params = { 'version': __version__, 'base_url': hass.config.api.base_url, # always needs authentication 'requires_api_password': True, } host_ip = util.get_local_ip() try: host_ip_pton = socket.inet_pton(socket.AF_INET, host_ip) except socket.error: host_ip_pton = socket.inet_pton(socket.AF_INET6, host_ip) info = ServiceInfo(ZEROCONF_TYPE, zeroconf_name, None, addresses=[host_ip_pton], port=hass.http.server_port, properties=params) zeroconf = Zeroconf() zeroconf.register_service(info) def service_update(zeroconf, service_type, name, state_change): """Service state changed.""" if state_change != ServiceStateChange.Added: return service_info = zeroconf.get_service_info(service_type, name) info = info_from_service(service_info) _LOGGER.debug("Discovered new device %s %s", name, info) # If we can handle it as a HomeKit discovery, we do that here. if service_type == HOMEKIT_TYPE and handle_homekit(hass, info): return for domain in ZEROCONF[service_type]: hass.add_job( hass.config_entries.flow.async_init(domain, context={'source': DOMAIN}, data=info)) for service in ZEROCONF: ServiceBrowser(zeroconf, service, handlers=[service_update]) if HOMEKIT_TYPE not in ZEROCONF: ServiceBrowser(zeroconf, HOMEKIT_TYPE, handlers=[service_update]) def stop_zeroconf(_): """Stop Zeroconf.""" zeroconf.unregister_service(info) zeroconf.close() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zeroconf) return True
def test_qu_response(): """Handle multicast incoming with the QU bit set.""" # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) # service definition type_ = "_test-srvc-type._tcp.local." other_type_ = "_notthesame._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) registration_name2 = "%s.%s" % (name, other_type_) desc = {'path': '/~paulsm/'} info = ServiceInfo( type_, registration_name, 80, 0, 0, desc, "ash-2.local.", addresses=[socket.inet_aton("10.0.1.2")] ) info2 = ServiceInfo( other_type_, registration_name2, 80, 0, 0, desc, "ash-other.local.", addresses=[socket.inet_aton("10.0.4.2")], ) # register zc.register_service(info) def _validate_complete_response(query, out): assert out.id == query.id has_srv = has_txt = has_a = False nbr_additionals = 0 nbr_answers = len(out.answers) nbr_authorities = len(out.authorities) for answer in out.additionals: nbr_additionals += 1 if answer.type == const._TYPE_SRV: has_srv = True elif answer.type == const._TYPE_TXT: has_txt = True elif answer.type == const._TYPE_A: has_a = True assert nbr_answers == 1 and nbr_additionals == 3 and nbr_authorities == 0 assert has_srv and has_txt and has_a # With QU should respond to only unicast when the answer has been recently multicast query = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN) question.unicast = True # Set the QU bit assert question.unicast is True query.add_question(question) unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT ) assert multicast_out is None _validate_complete_response(query, unicast_out) _clear_cache(zc) # With QU should respond to only multicast since the response hasn't been seen since 75% of the ttl query = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN) question.unicast = True # Set the QU bit assert question.unicast is True query.add_question(question) unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT ) assert unicast_out is None _validate_complete_response(query, multicast_out) # With QU set and an authorative answer (probe) should respond to both unitcast and multicast since the response hasn't been seen since 75% of the ttl query = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN) question.unicast = True # Set the QU bit assert question.unicast is True query.add_question(question) query.add_authorative_answer(info2.dns_pointer()) unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT ) _validate_complete_response(query, unicast_out) _validate_complete_response(query, multicast_out) _inject_response(zc, r.DNSIncoming(multicast_out.packets()[0])) # With the cache repopulated; should respond to only unicast when the answer has been recently multicast query = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN) question.unicast = True # Set the QU bit assert question.unicast is True query.add_question(question) unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT ) assert multicast_out is None _validate_complete_response(query, unicast_out) # unregister zc.unregister_service(info) zc.close()
def __init__(self, parent=None, **func_task): super(mDNS_BrowserThread, self).__init__(parent) self.ID_list = [] self.zeroconf = Zeroconf() self.listener = MyListener()
def test_known_answer_supression_service_type_enumeration_query(): zc = Zeroconf(interfaces=['127.0.0.1']) type_ = "_otherknown._tcp.local." name = "knownname" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} server_name = "ash-2.local." info = ServiceInfo( type_, registration_name, 80, 0, 0, desc, server_name, addresses=[socket.inet_aton("10.0.1.2")] ) zc.registry.add(info) type_2 = "_otherknown2._tcp.local." name = "knownname" registration_name2 = "%s.%s" % (name, type_2) desc = {'path': '/~paulsm/'} server_name2 = "ash-3.local." info2 = ServiceInfo( type_2, registration_name2, 80, 0, 0, desc, server_name2, addresses=[socket.inet_aton("10.0.1.2")] ) zc.registry.add(info2) now = current_time_millis() _clear_cache(zc) # Test PTR supression generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(const._SERVICE_TYPE_ENUMERATION_NAME, const._TYPE_PTR, const._CLASS_IN) generated.add_question(question) packets = generated.packets() unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in packets], "1.2.3.4", const._MDNS_PORT ) assert unicast_out is None assert multicast_out is not None and multicast_out.answers generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(const._SERVICE_TYPE_ENUMERATION_NAME, const._TYPE_PTR, const._CLASS_IN) generated.add_question(question) generated.add_answer_at_time( r.DNSPointer( const._SERVICE_TYPE_ENUMERATION_NAME, const._TYPE_PTR, const._CLASS_IN, const._DNS_OTHER_TTL, type_, ), now, ) generated.add_answer_at_time( r.DNSPointer( const._SERVICE_TYPE_ENUMERATION_NAME, const._TYPE_PTR, const._CLASS_IN, const._DNS_OTHER_TTL, type_2, ), now, ) packets = generated.packets() unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in packets], "1.2.3.4", const._MDNS_PORT ) assert unicast_out is None assert not multicast_out or not multicast_out.answers # unregister zc.registry.remove(info) zc.registry.remove(info2) zc.close()
def start_zeroconf(self): self.zeroconf = Zeroconf() self.info = ServiceInfo("_http._tcp.local.", self.service_name, socket.inet_aton(util.get_ip()), prefs.get_port(), 0, 0, {}, "somehost.local.") self.zeroconf.register_service(self.info)
def PYRO_connector_factory(uri, confnodesroot): """ This returns the connector to Pyro style PLCobject """ confnodesroot.logger.write(_("PYRO connecting to URI : %s\n") % uri) servicetype, location = uri.split("://") if servicetype == "PYROS": schemename = "PYROLOCSSL" # Protect against name->IP substitution in Pyro3 Pyro.config.PYRO_DNS_URI = True # Beware Pyro lib need str path, not unicode # don't rely on PYRO_STORAGE ! see documentation Pyro.config.PYROSSL_CERTDIR = os.path.abspath( str(confnodesroot.ProjectPath) + '/certs') if not os.path.exists(Pyro.config.PYROSSL_CERTDIR): confnodesroot.logger.write_error( 'Error : the directory %s is missing for SSL certificates (certs_dir).' 'Please fix it in your project.\n' % Pyro.config.PYROSSL_CERTDIR) return None else: confnodesroot.logger.write( _("PYRO using certificates in '%s' \n") % (Pyro.config.PYROSSL_CERTDIR)) Pyro.config.PYROSSL_CERT = "client.crt" Pyro.config.PYROSSL_KEY = "client.key" # Ugly Monkey Patching def _gettimeout(self): return self.timeout def _settimeout(self, timeout): self.timeout = timeout from M2Crypto.SSL import Connection # pylint: disable=import-error Connection.timeout = None Connection.gettimeout = _gettimeout Connection.settimeout = _settimeout # M2Crypto.SSL.Checker.WrongHost: Peer certificate commonName does not # match host, expected 127.0.0.1, got server Connection.clientPostConnectionCheck = None else: schemename = "PYROLOC" if location.find(service_type) != -1: try: from zeroconf import Zeroconf r = Zeroconf() i = r.get_service_info(service_type, location) if i is None: raise Exception("'%s' not found" % location) ip = str(socket.inet_ntoa(i.address)) port = str(i.port) newlocation = ip + ':' + port confnodesroot.logger.write( _("'{a1}' is located at {a2}\n").format(a1=location, a2=newlocation)) location = newlocation r.close() except Exception: confnodesroot.logger.write_error( _("MDNS resolution failure for '%s'\n") % location) confnodesroot.logger.write_error(traceback.format_exc()) return None # Try to get the proxy object try: RemotePLCObjectProxy = Pyro.core.getAttrProxyForURI(schemename + "://" + location + "/PLCObject") except Exception: confnodesroot.logger.write_error( _("Connection to '%s' failed.\n") % location) confnodesroot.logger.write_error(traceback.format_exc()) return None def PyroCatcher(func, default=None): """ A function that catch a Pyro exceptions, write error to logger and return default value when it happen """ def catcher_func(*args, **kwargs): try: return func(*args, **kwargs) except Pyro.errors.ConnectionClosedError, e: confnodesroot.logger.write_error(_("Connection lost!\n")) confnodesroot._SetConnector(None) except Pyro.errors.ProtocolError, e: confnodesroot.logger.write_error(_("Pyro exception: %s\n") % e) except Exception, e: # confnodesroot.logger.write_error(traceback.format_exc()) errmess = ''.join(Pyro.util.getPyroTraceback(e)) confnodesroot.logger.write_error(errmess + "\n") print(errmess) confnodesroot._SetConnector(None)
"username": "******", "password": "******" }}, upsert=True), UpdateOne({"username": "******"}, { "$setOnInsert": { "username": "******", "password": "******" } }, upsert=True), UpdateOne( {"username": "******"}, {"$setOnInsert": { "username": "******", "password": "******" }}, upsert=True) ] self.collection.bulk_write(operations) def getCollection(self): return self.collection if __name__ == "__main__": authenticator = MyAuth() listener = MyListener() browser = ServiceBrowser(Zeroconf(), "_http._tcp.local.", listener) app.run(host='0.0.0.0', port=8080, debug=False)
def __init__(self): self.zeroconf = Zeroconf() self.context = pyudev.Context() self.cpi = ConsolePi()
def add_service(self, zeroconf, type, name): zeroconf = Zeroconf() listener = MyListener(cap) browser = ServiceBrowser(zeroconf, "_yulinmei._tcp.local.", listener)
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 init_search_for_cameras(self): self.zeroconf = Zeroconf() self.listener = CameraListener(self) self.browser = ServiceBrowser(self.zeroconf, "_http._tcp.local.", self.listener)
def test_integration_with_listener_class(self): service_added = Event() service_removed = Event() subtype_name = "My special Subtype" type_ = "_http._tcp.local." subtype = subtype_name + "._sub." + type_ name = "xxxyyy" registration_name = "%s.%s" % (name, type_) class MyListener(object): def add_service(self, zeroconf, type_, name): zeroconf.get_service_info(type, name) service_added.set() def remove_service(self, zeroconf, type_, name): service_removed.set() listener = MyListener() zeroconf_browser = Zeroconf(interfaces=['127.0.0.1']) zeroconf_browser.add_service_listener(subtype, listener) properties = dict( prop_none=None, prop_string=b'a_prop', prop_float=1.0, prop_blank=b'a blanked string', prop_true=1, prop_false=0, ) zeroconf_registrar = Zeroconf(interfaces=['127.0.0.1']) desc = {'path': '/~paulsm/'} desc.update(properties) info_service = ServiceInfo(subtype, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") zeroconf_registrar.register_service(info_service) try: service_added.wait(1) assert service_added.is_set() # short pause to allow multicast timers to expire time.sleep(2) # clear the answer cache to force query for record in zeroconf_browser.cache.entries(): zeroconf_browser.cache.remove(record) # get service info without answer cache info = zeroconf_browser.get_service_info(type_, registration_name) assert info.properties[b'prop_none'] is False assert info.properties[b'prop_string'] == properties['prop_string'] assert info.properties[b'prop_float'] is False assert info.properties[b'prop_blank'] == properties['prop_blank'] assert info.properties[b'prop_true'] is True assert info.properties[b'prop_false'] is False info = zeroconf_browser.get_service_info(subtype, registration_name) assert info.properties[b'prop_none'] is False zeroconf_registrar.unregister_service(info_service) service_removed.wait(1) assert service_removed.is_set() finally: zeroconf_registrar.close() zeroconf_browser.remove_service_listener(listener) zeroconf_browser.close()
import socket import sys from zeroconf import __version__, ServiceInfo, Zeroconf if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG) if len(sys.argv) > 1: assert sys.argv[1:] == ["--debug"] logging.getLogger("zeroconf").setLevel(logging.DEBUG) # Test a few module features, including service registration, service # query (for Zoe), and service unregistration. print("Multicast DNS Service Discovery for Python, version %s" % (__version__, )) r = Zeroconf() print("1. Testing registration of a service...") desc = {"version": "0.10", "a": "test value", "b": "another value"} info = ServiceInfo( "_http._tcp.local.", "My Service Name._http._tcp.local.", socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc, ) print(" Registering service...") r.register_service(info) print(" Registration done.") print("2. Testing query of service information...")
address = None # Sets global address when the service is found class MyListener: def remove_service(self, zeroconf, type, name): pass def add_service(self, zeroconf, type, name): global address info = zeroconf.get_service_info(type, name) address = "{}:{}".format(socket.inet_ntoa(info.address), info.port) zeroconf = Zeroconf() listener = MyListener() browser = ServiceBrowser(zeroconf, "{}.local.".format(SERVICE_TYPE), listener) # Loop for 30s and wait for the service, if not found, exit try: start = time.time() while time.time() - start < 30: if address is not None: break time.sleep(1) finally: zeroconf.close() if address is not None: print(address)
def test_ttl(self): # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) # service definition type_ = "_test-srvc-type._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} info = ServiceInfo(type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") # we are going to monkey patch the zeroconf send to check packet sizes old_send = zc.send nbr_answers = nbr_additionals = nbr_authorities = 0 def get_ttl(record_type): if expected_ttl is not None: return expected_ttl elif record_type in [r._TYPE_A, r._TYPE_SRV]: return r._DNS_HOST_TTL else: return r._DNS_OTHER_TTL def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT): """Sends an outgoing packet.""" nonlocal nbr_answers, nbr_additionals, nbr_authorities for answer, time_ in out.answers: nbr_answers += 1 assert answer.ttl == get_ttl(answer.type) for answer in out.additionals: nbr_additionals += 1 assert answer.ttl == get_ttl(answer.type) for answer in out.authorities: nbr_authorities += 1 assert answer.ttl == get_ttl(answer.type) old_send(out, addr=addr, port=port) # monkey patch the zeroconf send setattr(zc, "send", send) # register service with default TTL expected_ttl = None zc.register_service(info) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 3 nbr_answers = nbr_additionals = nbr_authorities = 0 # query query = r.DNSOutgoing(r._FLAGS_QR_QUERY | r._FLAGS_AA) query.add_question(r.DNSQuestion(info.type, r._TYPE_PTR, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_SRV, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_TXT, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.server, r._TYPE_A, r._CLASS_IN)) zc.handle_query(r.DNSIncoming(query.packet()), r._MDNS_ADDR, r._MDNS_PORT) assert nbr_answers == 4 and nbr_additionals == 4 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0 # unregister expected_ttl = 0 zc.unregister_service(info) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0 # register service with custom TTL expected_ttl = r._DNS_HOST_TTL * 2 assert expected_ttl != r._DNS_HOST_TTL zc.register_service(info, ttl=expected_ttl) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 3 nbr_answers = nbr_additionals = nbr_authorities = 0 # query query = r.DNSOutgoing(r._FLAGS_QR_QUERY | r._FLAGS_AA) query.add_question(r.DNSQuestion(info.type, r._TYPE_PTR, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_SRV, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_TXT, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.server, r._TYPE_A, r._CLASS_IN)) zc.handle_query(r.DNSIncoming(query.packet()), r._MDNS_ADDR, r._MDNS_PORT) assert nbr_answers == 4 and nbr_additionals == 4 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0 # unregister expected_ttl = 0 zc.unregister_service(info) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0