def registre_mdns(port): LOCAL_HOST = "alexa.local" LOCAL_PORT = port BASE_URL = "http://" + LOCAL_HOST + ":" + str(LOCAL_PORT) + "/" def get_local_address(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("www.amazon.com", 80)) res = s.getsockname()[0] s.close() return res ip = get_local_address() print(f"Local IP is {ip}") desc = { 'version': '0.1', 'base_url': "http://{}:{}/".format(ip, str(LOCAL_PORT)), 'path': '/' } info = ServiceInfo("_http._tcp.local.", "Alexa Device._http._tcp.local.", socket.inet_aton(ip), LOCAL_PORT, 0, 0, desc, LOCAL_HOST + ".") zeroconf = Zeroconf() zeroconf.register_service(info) print(f"Local mDNS is started, domain is {LOCAL_HOST}") print(f"Local HTTP is {BASE_URL}")
class HomeKitServer(ThreadingMixIn, HTTPServer): def __init__(self, config_file, logger=sys.stderr): """ Create a new server that acts like a homekit accessory. :param config_file: the file that contains the configuration data. Must be a string representing an absolute path to the file :param logger: this can be None to disable logging, sys.stderr to use the default behaviour of the python implementation or an instance of logging.Logger to use this. """ if logger is None or logger == sys.stderr or isinstance( logger, logging.Logger): self.logger = logger else: raise Exception('Invalid logger given.') self.data = HomeKitServerData(config_file) self.data.increase_configuration_number() self.sessions = {} self.zeroconf = Zeroconf() self.mdns_type = '_hap._tcp.local.' self.mdns_name = self.data.name + '._hap._tcp.local.' self.accessories = Accessories() HTTPServer.__init__(self, (self.data.ip, self.data.port), HomeKitRequestHandler) def publish_device(self): desc = { 'md': 'My Lightbulb', # model name of accessory 'ci': Categories[ 'Lightbulb'], # category identifier (page 254, 2 means bridge) 'pv': '1.0', # protocol version 'c#': str(self.data.configuration_number), # configuration (consecutive number, 1 or greater, must be changed on every configuration change) 'id': self.data. accessory_pairing_id_bytes, # id MUST look like Mac Address 'ff': '0', # feature flags 's#': '1', # must be 1 'sf': '1' # status flag, lowest bit encodes pairing status, 1 means unpaired } if self.data.is_paired: desc['sf'] = '0' info = ServiceInfo(self.mdns_type, self.mdns_name, socket.inet_aton(self.data.ip), self.data.port, 0, 0, desc, 'ash-2.local.') self.zeroconf.unregister_all_services() self.zeroconf.register_service(info) def unpublish_device(self): self.zeroconf.unregister_all_services() def shutdown(self): # tell all handlers to close the connection for session in self.sessions: self.sessions[session]['handler'].close_connection = True self.socket.close() HTTPServer.shutdown(self)
def test_integration_with_subtype_and_listener(self): subtype_ = "_subtype._sub" type_ = "_type._tcp.local." name = "xxxyyy" # Note: discovery returns only DNS-SD type not subtype discovery_type = "%s.%s" % (subtype_, type_) registration_name = "%s.%s" % (name, type_) zeroconf_registrar = Zeroconf(interfaces=["127.0.0.1"]) desc = {"path": "/~paulsm/"} info = ServiceInfo( discovery_type, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.", ) zeroconf_registrar.register_service(info) try: service_types = ZeroconfServiceTypes.find(interfaces=["127.0.0.1"], timeout=0.5) assert discovery_type in service_types service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=0.5) assert discovery_type in service_types finally: zeroconf_registrar.close()
def test_integration_with_subtype_and_listener(self): subtype_ = "_subtype._sub" type_ = "_type._tcp.local." name = "xxxyyy" # Note: discovery returns only DNS-SD type not subtype discovery_type = "%s.%s" % (subtype_, type_) registration_name = "%s.%s" % (name, type_) zeroconf_registrar = Zeroconf(interfaces=['127.0.0.1']) desc = {'path': '/~paulsm/'} info = ServiceInfo( discovery_type, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") zeroconf_registrar.register_service(info) try: service_types = ZeroconfServiceTypes.find( interfaces=['127.0.0.1'], timeout=0.5) assert discovery_type in service_types service_types = ZeroconfServiceTypes.find( zc=zeroconf_registrar, timeout=0.5) assert discovery_type in service_types finally: zeroconf_registrar.close()
def register_mdns(receiver_name): addresses = [] for ifen in ni.interfaces(): ifenaddr = ni.ifaddresses(ifen) if ni.AF_INET in ifenaddr: addresses.append( socket.inet_pton(ni.AF_INET, ifenaddr[ni.AF_INET][0]["addr"])) if ni.AF_INET6 in ifenaddr: addresses.append( socket.inet_pton( ni.AF_INET6, ifenaddr[ni.AF_INET6][0]["addr"].split("%")[0])) info = ServiceInfo( "_airplay._tcp.local.", "%s._airplay._tcp.local." % receiver_name, # addresses=[socket.inet_aton("127.0.0.1")], addresses=addresses, port=7000, properties=mdns_props, server="%s.local." % receiver_name, ) zeroconf = Zeroconf(ip_version=IPVersion.V4Only) zeroconf.register_service(info) print("mDNS service registered") return (zeroconf, info)
def advertise(self): postfix = self.config['global']['service_prefix'] self.port = int(self.config['global']['port']) #print(self.config['device']['hostname']+postfix) info = ServiceInfo(postfix, self.config['device']['hostname']+"."+postfix, socket.inet_aton(self.ip), self.port, 0, 0, {'info': self.config['device']['description']}, "hazc.local.") self.bindConnection() zeroconf = Zeroconf() zeroconf.register_service(info) try: while True: # try: print("Ready") self.conn, self.addr = self.webcontrol.accept() self.listen() self.conn.close() except KeyboardInterrupt: pass finally: print() print("Unregistering...") zeroconf.unregister_service(info) zeroconf.close() try: print("Shutting down socket") self.webcontrol.shutdown(socket.SHUT_RDWR) except Exception as e: print(e)
def setup(hass, config): """Set up Zeroconf and make Home Assistant discoverable.""" from zeroconf import Zeroconf, ServiceInfo zeroconf = Zeroconf() zeroconf_name = '{}.{}'.format(hass.config.location_name, ZEROCONF_TYPE) requires_api_password = hass.config.api.api_password is not None params = { 'version': __version__, 'base_url': hass.config.api.base_url, 'requires_api_password': requires_api_password, } info = ServiceInfo(ZEROCONF_TYPE, zeroconf_name, socket.inet_aton(hass.config.api.host), hass.config.api.port, 0, 0, params) zeroconf.register_service(info) def stop_zeroconf(event): """Stop Zeroconf.""" zeroconf.unregister_service(info) zeroconf.close() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zeroconf) return True
class Server: def __init__(self, name, address, port, description): self.zeroconf = Zeroconf() self.name = name self.description = description self.address = address self.port = port self.full_name = get_full_name(name) self.info = ServiceInfo(type_=TYPE, name=self.full_name, address=socket.inet_aton(self.address), port=self.port, weight=0, priority=0, properties=self.description) def register(self): logger.info("Broadcast server {} at {}:{}".format( self.full_name, self.address, self.port)) self.zeroconf.register_service(self.info) def unregister(self): self.zeroconf.unregister_service(self.info) def close(self): self.zeroconf.close()
class DartsConnectBonjour(): def stopBonjour(self): print("DartsConnectBonjour --> Unregistering...") self.zeroconf.unregister_service(self.info) self.zeroconf.close() def startBonjour(self): if len(sys.argv) > 1: assert sys.argv[1:] == ['--debug'] self.logging.getLogger('zeroconf').setLevel(logging.DEBUG) #desc = {'path': '/~paulsm/'} self.info = ServiceInfo("_dartsconnect._tcp.local.", "Dartboard._dartsconnect._tcp.local.", socket.inet_aton(self.ip), self.port, 0, 0, {}, None) self.zeroconf = Zeroconf() print("DartsConnectBonjour --> DartsConnect service registered") self.zeroconf.register_service(self.info) def __init__(self, ipAddr, port): self.ip = ipAddr self.port = port
def test_integration_with_listener(self): type_ = "_test-srvc-type._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) 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_types = ZeroconfServiceTypes.find(interfaces=["127.0.0.1"], timeout=0.5) assert type_ in service_types service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=0.5) assert type_ in service_types finally: zeroconf_registrar.close()
def register_service(ip, port, ssl=False, debug=False): global zeroconf, info if debug: logging.getLogger("zeroconf").setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG) desc = {"path": "/check"} typ = "_espupdater-http._tcp.local." if ssl: typ = "_espupdater-https._tcp.local." #TODO: all_ips = addresses() #TODO: match against config["host"] all_ips = [ip] logger.info(f"IPs: {all_ips}") info = ServiceInfo( typ, "ESP Swarm Updater." + typ, addresses=[socket.inet_aton(ip) for ip in all_ips], port=port, properties=desc, server="ash-2.local.", ) zeroconf = Zeroconf() logger.info("Registration of mDNS service") zeroconf.register_service(info)
def test_discover_homekit_devices_missing_c(self): zeroconf = Zeroconf() desc = { 'id': '00:00:01:00:00:03', 'md': 'unittest', 's#': '1', 'ci': '5', 'sf': '0' } info = ServiceInfo('_hap._tcp.local.', 'foo3._hap._tcp.local.', addresses=[socket.inet_aton('127.0.0.1')], port=1234, properties=desc, weight=0, priority=0) zeroconf.unregister_all_services() zeroconf.register_service(info, allow_name_change=True) result = discover_homekit_devices() test_device = self.find_device(desc, result) zeroconf.unregister_all_services() self.assertIsNone(test_device)
class AdvertiseServer: def __init__(self): self._zeroconf = Zeroconf() self._addresses = [socket.inet_aton(_my_ip())] self._id2info = {} def register_device(self, id, device): info = ServiceInfo(type_="_repl._tcp.local.", name=device.name + "." + "_repl._tcp.local.", port=Config.get_attr('server_port', '34567'), properties={ "uid": device.uid, "name": device.name }, addresses=self._addresses) self._id2info[id] = info self._zeroconf.register_service(info, allow_name_change=True) logger.debug(f"advertise {id}") def unregister_device(self, id): try: logger.debug(f"no longer advertise {id}") info = self._id2info[id] if info: self._zeroconf.unregister_service(info) del self._id2info[id] except AttributeError: pass
def setup(hass, config): """Set up Zeroconf and make Home Assistant discoverable.""" from zeroconf import Zeroconf, ServiceInfo zeroconf = Zeroconf() zeroconf_name = "{}.{}".format(hass.config.location_name, ZEROCONF_TYPE) requires_api_password = (hass.config.api.api_password is not None) params = {"version": __version__, "base_url": hass.config.api.base_url, "requires_api_password": requires_api_password} info = ServiceInfo(ZEROCONF_TYPE, zeroconf_name, socket.inet_aton(hass.config.api.host), hass.config.api.port, 0, 0, params) zeroconf.register_service(info) def stop_zeroconf(event): """Stop Zeroconf.""" zeroconf.unregister_service(info) zeroconf.close() hass.bus.listen_once(EVENT_BLUMATE_STOP, stop_zeroconf) return True
def setup(hass, config): """Set up Zeroconf and make Home Assistant discoverable.""" from zeroconf import Zeroconf, ServiceInfo zeroconf = Zeroconf() zeroconf_name = '{}.{}'.format(hass.config.location_name, ZEROCONF_TYPE) requires_api_password = hass.config.api.api_password is not None params = { 'version': __version__, 'base_url': hass.config.api.base_url, 'requires_api_password': requires_api_password, } 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, host_ip_pton, hass.http.server_port, 0, 0, params) zeroconf.register_service(info) def stop_zeroconf(event): """Stop Zeroconf.""" zeroconf.unregister_service(info) zeroconf.close() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zeroconf) return True
def setup(hass, config): """Set up Zeroconf and make Home Assistant discoverable.""" from zeroconf import Zeroconf, ServiceInfo zeroconf = Zeroconf() zeroconf_name = "{}.{}".format(hass.config.location_name, ZEROCONF_TYPE) requires_api_password = (hass.config.api.api_password is not None) params = { "version": __version__, "base_url": hass.config.api.base_url, "requires_api_password": requires_api_password } info = ServiceInfo(ZEROCONF_TYPE, zeroconf_name, socket.inet_aton(hass.config.api.host), hass.config.api.port, 0, 0, params) zeroconf.register_service(info) def stop_zeroconf(event): """Stop Zeroconf.""" zeroconf.unregister_service(info) zeroconf.close() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zeroconf) return True
def register_service(code, addresses, port): r"""Registers an Airshare Multicast-DNS service based in the local network. Parameters ---------- code : str Identifying code for the Airshare service. addresses : list List of local network IP Addresses for the service. port : int Port number for the Airshare service's server. Returns ------- info : zeroconf.ServiceInfo Details of the Airshare service. """ zeroconf = Zeroconf(ip_version=IPVersion.V4Only) service = "_airshare._http._tcp.local." info = ServiceInfo(service, code + service, addresses=addresses, port=port, server=code + ".local.") zeroconf.register_service(info) return info
def test_register_and_lookup_type_by_uppercase_name(self): # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) type_ = "_mylowertype._tcp.local." name = "Home" registration_name = "%s.%s" % (name, type_) info = ServiceInfo( type_, name=registration_name, server="random123.local.", addresses=[socket.inet_pton(socket.AF_INET, "1.2.3.4")], port=80, properties={"version": "1.0"}, ) zc.register_service(info) _clear_cache(zc) info = ServiceInfo(type_, registration_name) info.load_from_cache(zc) assert info.addresses == [] out = r.DNSOutgoing(const._FLAGS_QR_QUERY) out.add_question( r.DNSQuestion(type_.upper(), const._TYPE_PTR, const._CLASS_IN)) zc.send(out) time.sleep(0.5) info = ServiceInfo(type_, registration_name) info.load_from_cache(zc) assert info.addresses == [socket.inet_pton(socket.AF_INET, "1.2.3.4")] assert info.properties == {b"version": b"1.0"} zc.close()
def test_aaaa_query(): """Test that queries for AAAA records work.""" zc = Zeroconf(interfaces=['127.0.0.1']) type_ = "_knownservice._tcp.local." name = "knownname" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} server_name = "ash-2.local." ipv6_address = socket.inet_pton(socket.AF_INET6, "2001:db8::1") info = ServiceInfo(type_, registration_name, 80, 0, 0, desc, server_name, addresses=[ipv6_address]) zc.register_service(info) _clear_cache(zc) generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(server_name, const._TYPE_AAAA, const._CLASS_IN) generated.add_question(question) packets = generated.packets() _, multicast_out = zc.query_handler.response(r.DNSIncoming(packets[0]), "1.2.3.4", const._MDNS_PORT) assert multicast_out.answers[0][0].address == ipv6_address # unregister zc.unregister_service(info) zc.close()
def test_integration_with_listener_v6_records(self): type_ = "_test-srvc-type._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) addr = "2606:2800:220:1:248:1893:25c8:1946" # example.com zeroconf_registrar = Zeroconf(interfaces=['127.0.0.1']) desc = {'path': '/~paulsm/'} info = ServiceInfo( type_, registration_name, 80, 0, 0, desc, "ash-2.local.", addresses=[socket.inet_pton(socket.AF_INET6, addr)], ) zeroconf_registrar.register_service(info) _clear_cache(zeroconf_registrar) try: service_types = ZeroconfServiceTypes.find(interfaces=['127.0.0.1'], timeout=0.5) assert type_ in service_types _clear_cache(zeroconf_registrar) service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=0.5) assert type_ in service_types finally: zeroconf_registrar.close()
def createServer(self): self.serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 99999999) self.serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) print 'Socket created' # Bind socket to local host and port try: self.serverSocket.bind(("", self.PORT)) except socket.error as msg: print 'Bind failed. Error: ' + str(msg[0]) + ' Message ' + msg[1] sys.exit() print 'Socket bind complete' # Start listening on socket self.serverSocket.listen(1) print 'Socket now listening' # Register service IP = socket.inet_aton(ifaddresses(self.INTERFACE)[2][0]['addr']) serviceType = "_naoqi._tcp.local." name = "Fake robot._naoqi._tcp.local." info = ServiceInfo(serviceType, name, IP, self.PORT, 0, 0, {}) zeroconf = Zeroconf() print "Registration of service" zeroconf.register_service(info)
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()
def run(): try: zeroconf = Zeroconf(ip_version=IPVersion.All, interfaces=InterfaceChoice.All) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("1.1.1.1", 80)) ip = s.getsockname()[0] info = ServiceInfo( "_http._tcp.local.", "Member Matters Server._http._tcp.local.", addresses=[socket.inet_aton(ip)], properties={"ip": ip}, port=80, server="membermatters.local.", ) zeroconf.register_service(info) except: pass try: while True: time.sleep(0.1) except KeyboardInterrupt: pass finally: print("Unregistering...") zeroconf.unregister_service(info) zeroconf.close()
class ZeroconfService(): def register(self, port: int): self._zeroconf = Zeroconf() hostname = socket.gethostname() host = socket.gethostbyname(hostname) desc = { 'host': host, 'port': str(port), 'hostname': hostname, } self._info = ServiceInfo( "_mpv-http-router._tcp.local.", "MPV HTTP Router._mpv-http-router._tcp.local.", socket.inet_aton(host), port, 0, 0, desc) self._zeroconf.register_service(self._info) self._registered = True logger.info( "MPV HTTP Router has been registered in the network on %s:%s", hostname, port) def unregister(self): if self._zeroconf is None: return self._zeroconf.unregister_service(self._info) self._zeroconf.close() self._registered = False logger.info("MPV HTTP Router has been unregistered")
def test_integration_with_listener_ipv6(self): type_ = "_test-srvc-type._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) zeroconf_registrar = Zeroconf(ip_version=r.IPVersion.V6Only) desc = {'path': '/~paulsm/'} info = ServiceInfo( type_, registration_name, 80, 0, 0, desc, "ash-2.local.", addresses=[socket.inet_aton("10.0.1.2")], ) zeroconf_registrar.register_service(info) _clear_cache(zeroconf_registrar) try: service_types = ZeroconfServiceTypes.find( ip_version=r.IPVersion.V6Only, timeout=0.5) assert type_ in service_types _clear_cache(zeroconf_registrar) service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=0.5) assert type_ in service_types finally: zeroconf_registrar.close()
def __init__(self, sname, stype, port, txt): host_name = socket.gethostname() host = self.get_ip() service_name = "{}.{}".format(sname, stype) desc_txt = 'FogLAMP Service' if isinstance(txt, list): try: desc_txt = txt[0] except: pass desc = {'description': desc_txt} """ Create a service description. type_: fully qualified service type name name: fully qualified service name address: IP address as unsigned short, network byte order port: port that the service runs on weight: weight of the service priority: priority of the service properties: dictionary of properties (or a string holding the bytes for the text field) server: fully qualified name for service host (defaults to name) host_ttl: ttl used for A/SRV records other_ttl: ttl used for PTR/TXT records""" info = ServiceInfo(stype, service_name, socket.inet_aton(host), port, properties=desc, server="{}.local.".format(host_name)) zeroconf = Zeroconf() # Refresh zeroconf cache browser = ServiceBrowser(zeroconf, stype, handlers=[self.on_service_state_change]) zeroconf.register_service(info, allow_name_change=True)
class Advertise: def __init__(self, name, device_type): self.zeroconf = Zeroconf() self.name = name self.device_type = device_type def get_hostname_port(self): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("", 0)) hostname = socket.gethostname() port = s.getsockname()[1] s.close() return hostname, port def start(self): hostname, port = self.get_hostname_port() self.port = port desc = { 'service': 'RF Bridge', 'version': '0.0.1', 'name': self.name, 'device_type': self.device_type } s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ip_addr = s.getsockname()[0] s.close() info = ServiceInfo("_rfbridge._tcp.local.", hostname + "._rfbridge._tcp.local.", socket.inet_aton(ip_addr), port, 0, 0, desc, hostname + ".local.") self.zeroconf.register_service(info)
class mDNS(object): def __init__(self, port, mdns_desc, mdns_addr): try: ni.ifaddresses('wlan0') self.ip = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr'] except ValueError: self.ip = socket.gethostbyname(socket.gethostname()) self.port = port desc = { 'version': '0.1', 'base_url': "http://{}:{}/".format(self.ip, str(self.port)), 'path': '/' } self.info = ServiceInfo("_http._tcp.local.", "{}._http._tcp.local.".format(mdns_desc), socket.inet_aton(self.ip), self.port, 0, 0, desc, "{}.".format(mdns_addr)) self.zeroconf = Zeroconf() def register(self): self.zeroconf.register_service(self.info) def unregister(self): self.zeroconf.unregister_service(self.info)
def test_name_conflicts(self): # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) type_ = "_homeassistant._tcp.local." name = "Home" registration_name = "%s.%s" % (name, type_) info = ServiceInfo( type_, name=registration_name, server="random123.local.", addresses=[socket.inet_pton(socket.AF_INET, "1.2.3.4")], port=80, properties={"version": "1.0"}, ) zc.register_service(info) conflicting_info = ServiceInfo( type_, name=registration_name, server="random456.local.", addresses=[socket.inet_pton(socket.AF_INET, "4.5.6.7")], port=80, properties={"version": "1.0"}, ) with pytest.raises(r.NonUniqueNameException): zc.register_service(conflicting_info) zc.close()
class WGZeroconf(ClassLogger): def __init__(self, ifname: str, wg_iface: WGInterface): self._setLoggerName(ifname) self.ifname = ifname self.ifindex: int = IPRoute().link_lookup(ifname=ifname)[0] self.wg_iface = wg_iface self.addresses = self.get_addrs() self.zeroconf = Zeroconf([addr.compressed for addr in self.addresses]) self.listener = WGServiceListener(self) self.browser = ServiceBrowser(self.zeroconf, WG_TYPE, self.listener) digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) digest.update(MACHINE_ID.encode('utf-8')) digest.update(ifname.encode('utf-8')) self.hostname = digest.finalize()[:16].hex() self.service: WGServiceInfo = WGServiceInfo.new( self.hostname, addresses=[addr.packed for addr in self.addresses], hostname=HOSTNAME, config=wg_iface.config, ) self.zeroconf.register_service(self.service) def get_addrs( self, ) -> List[TAddress]: with IPRoute() as ip: return [ ipaddress.ip_address(addr.get_attr('IFA_ADDRESS')) for addr in ip.get_addr(label=self.ifname) ] def close(self) -> None: self.zeroconf.close()
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()
def broadcast_zeroconf(frigate_id): zeroconf = Zeroconf(interfaces=InterfaceChoice.Default, ip_version=IPVersion.V4Only) host_ip = get_local_ip() 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) info = ServiceInfo( ZEROCONF_TYPE, name=f"{frigate_id}.{ZEROCONF_TYPE}", addresses=[host_ip_pton], port=5000, ) logger.info("Starting Zeroconf broadcast") try: zeroconf.register_service(info) except NonUniqueNameException: logger.error( "Frigate instance with identical name present in the local network" ) return zeroconf
def __init__(self): """ """ self.hostname = socket.gethostname() self.address = socket.gethostbyname(self.hostname + "." + self.__net_suffix) self.port = PORT self.uid = "virtual_sensor_%s" % MAC_ADDRESS try: serviceInfo = ServiceInfo("_sensor._tcp.%s." % self.__net_suffix, self.uid + "._sensor._tcp.%s." % self.__net_suffix, addresses = [socket.inet_aton(self.address)], port = PORT, properties = { 'version': '0.0.1', 'id_page': '/id', 'settings' : '/set' } ) except: serviceInfo = ServiceInfo("_sensor._tcp.%s." % self.__net_suffix, self.uid + "._sensor._tcp.%s." % self.__net_suffix, address = socket.inet_aton(self.address), port = PORT, properties = { 'version': '0.0.1', 'id_page': '/id', 'settings' : '/set' } ) zeroconf = Zeroconf() zeroconf.register_service(serviceInfo)
class DiscoveryServerService: _SERVICE_TYPE = '_http._tcp.local.' _SERVICE_NAME = 'sound-system-server._http._tcp.local.' def __init__(self, host, port): self._host = host self._port = port self._zeroconf = None self._info = None self._registered = False def start(self): if not self._registered: self._zeroconf = Zeroconf() self._start_service() def stop(self): if self._registered: self._zeroconf.unregister_all_services() self._zeroconf = None self._registered = False def _start_service(self): self._info = ServiceInfo(self._SERVICE_TYPE, self._SERVICE_NAME, socket.inet_aton(self._host), self._port, 0, 0, {}) logging.debug('Registered zeroconf service %s host %s:%d', self._SERVICE_NAME, self._host, self._port) self._zeroconf.register_service(self._info) self._registered = True
def setup(hass, config): """Set up Zeroconf and make Home Assistant discoverable.""" from zeroconf import Zeroconf, ServiceInfo zeroconf = Zeroconf() zeroconf_name = '{}.{}'.format(uuid_util.get_mac_address(), ZEROCONF_TYPE) dev_uuid = uuid_util.get_uuid(hass.config.config_dir) params = { 'version': CUR_VERSION, 'uuid': dev_uuid } 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, host_ip_pton, hass.http.server_port, 0, 0, params) zeroconf.register_service(info) def stop_zeroconf(event): """Stop Zeroconf.""" zeroconf.unregister_service(info) zeroconf.close() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zeroconf) return True
class GlancesAutoDiscoverClient(object): """Implementation of the zeroconf protocol (client side for the Glances server).""" def __init__(self, hostname, args=None): if zeroconf_tag: zeroconf_bind_address = args.bind_address try: self.zeroconf = Zeroconf() except socket.error as e: logger.error("Cannot start zeroconf: {}".format(e)) # XXX *BSDs: Segmentation fault (core dumped) # -- https://bitbucket.org/al45tair/netifaces/issues/15 if not BSD: try: # -B @ overwrite the dynamic IPv4 choice if zeroconf_bind_address == '0.0.0.0': zeroconf_bind_address = self.find_active_ip_address() except KeyError: # Issue #528 (no network interface available) pass # Check IP v4/v6 address_family = socket.getaddrinfo(zeroconf_bind_address, args.port)[0][0] # Start the zeroconf service self.info = ServiceInfo( zeroconf_type, '{}:{}.{}'.format(hostname, args.port, zeroconf_type), address=socket.inet_pton(address_family, zeroconf_bind_address), port=args.port, weight=0, priority=0, properties={}, server=hostname) try: self.zeroconf.register_service(self.info) except socket.error as e: logger.error("Error while announcing Glances server: {}".format(e)) else: print("Announce the Glances server on the LAN (using {} IP address)".format(zeroconf_bind_address)) else: logger.error("Cannot announce Glances server on the network: zeroconf library not found.") @staticmethod def find_active_ip_address(): """Try to find the active IP addresses.""" import netifaces # Interface of the default gateway gateway_itf = netifaces.gateways()['default'][netifaces.AF_INET][1] # IP address for the interface return netifaces.ifaddresses(gateway_itf)[netifaces.AF_INET][0]['addr'] def close(self): if zeroconf_tag: self.zeroconf.unregister_service(self.info) self.zeroconf.close()
class ZeroconfService(AbstractZeroconfService): """ :class:`ZeroconfService` uses `python zeroconf`_ .. _python zeroconf: https://pypi.org/project/zeroconf/ Install:: .. code-block:: bash pip install zeroconf """ def __init__(self, name, port): super(ZeroconfService, self).__init__(name, port) self._zeroconf = None self._infos = [] @classmethod def has_support(cls): return support def start(self): self._zeroconf = Zeroconf() for index, ip in enumerate(self.ips): info = self._gerenate_service_info(index, ip) self._infos.append(info) self._zeroconf.register_service(info) self._log('Zeroconf {} - Registered service: name={}, regtype={}, domain={}', self.__class__.__name__, self.name, self.type, 'local.') self._log(' Network: {}', ip) def _gerenate_service_info(self, index, ip): name = '{}-{}.{}.local.'.format(self.name.lower(), index, self.type, '.local.') return ServiceInfo( self.type + '.local.', name, socket.inet_aton(ip), self.port, 0, 0, {} ) def close(self): for info in self._infos: self._zeroconf.unregister_service(info)
def createService(): zeroconf = Zeroconf() # Look up info's __init__ in python-zeroconf's documentation # info = ServiceInfo("_http._tcp.local.", "Takiyaki._http._tcp.local.", socket.inet_aton(socket.gethostbyname(socket.gethostname())), 8080,0,0,socket.gethostname() + ".local.") # Server is supported but not compulsory, set server as inputted name # print "Registered Service [" + info.name + "]" zeroconf.register_service(info) try: while True: sleep(0.1) except KeyboardInterrupt: zeroconf.unregister_service(info) print("Unregistered") zeroconf.close() print("Service registered") zeroconf.close()
class GlancesAutoDiscoverClient(object): """Implementation of the zeroconf protocol (client side for the Glances server).""" def __init__(self, hostname, args=None): if zeroconf_tag: zeroconf_bind_address = args.bind_address try: self.zeroconf = Zeroconf() except socket.error as e: logger.error("Cannot start zeroconf: {0}".format(e)) try: # -B @ overwrite the dynamic IPv4 choice if zeroconf_bind_address == '0.0.0.0': zeroconf_bind_address = self.find_active_ip_address() except KeyError: # Issue #528 (no network interface available) pass print("Announce the Glances server on the LAN (using {0} IP address)".format(zeroconf_bind_address)) self.info = ServiceInfo( zeroconf_type, '{0}:{1}.{2}'.format(hostname, args.port, zeroconf_type), address=socket.inet_aton(zeroconf_bind_address), port=args.port, weight=0, priority=0, properties={}, server=hostname) self.zeroconf.register_service(self.info) else: logger.error("Cannot announce Glances server on the network: zeroconf library not found.") @staticmethod def find_active_ip_address(): """Try to find the active IP addresses.""" if not 'freebsd' in sys.platform: import netifaces # Interface of the default gateway gateway_itf = netifaces.gateways()['default'][netifaces.AF_INET][1] # IP address for the interface return netifaces.ifaddresses(gateway_itf)[netifaces.AF_INET][0]['addr'] else: raise KeyError, 'On FreeBSD, this would segfault' def close(self): if zeroconf_tag: self.zeroconf.unregister_service(self.info) self.zeroconf.close()
class GlancesAutoDiscoverClient(object): """Implementation of the zeroconf protocol (client side for the Glances server).""" def __init__(self, hostname, args=None): if zeroconf_tag: zeroconf_bind_address = args.bind_address try: self.zeroconf = Zeroconf() except socket.error as e: logger.error("Cannot start zeroconf: {0}".format(e)) if netifaces_tag: # -B @ overwrite the dynamic IPv4 choice if zeroconf_bind_address == '0.0.0.0': zeroconf_bind_address = self.find_active_ip_address() else: logger.error("Couldn't find the active IP address: netifaces library not found.") logger.info("Announce the Glances server on the LAN (using {0} IP address)".format(zeroconf_bind_address)) print("Announce the Glances server on the LAN (using {0} IP address)".format(zeroconf_bind_address)) self.info = ServiceInfo( zeroconf_type, '{0}:{1}.{2}'.format(hostname, args.port, zeroconf_type), address=socket.inet_aton(zeroconf_bind_address), port=args.port, weight=0, priority=0, properties={}, server=hostname) self.zeroconf.register_service(self.info) else: logger.error("Cannot announce Glances server on the network: zeroconf library not found.") def find_active_ip_address(self): """Try to find the active IP addresses.""" try: # Interface of the default gateway gateway_itf = netifaces.gateways()['default'][netifaces.AF_INET][1] # IP address for the interface return netifaces.ifaddresses(gateway_itf)[netifaces.AF_INET][0]['addr'] except Exception: return None def close(self): if zeroconf_tag: self.zeroconf.unregister_service(self.info) self.zeroconf.close()
class Advertisement(object): def __init__(self, ip=None): """ :ip: if string `ip` given, register on given IP (if None: default route's IP). """ self.zeroconf = Zeroconf() self.info = build_service_info(ip=ip or main_ip()) def register(self): """Registers the service on the network. """ self.zeroconf.register_service(self.info) log.debug("Registered {} on {}:{}".format(self.info.name, self.ip, self.info.port)) def unregister(self): """Unregisters the service. """ self.zeroconf.unregister_service(self.info) log.debug("Unregistered touchoscbridge.") def update(self, ip=None): """Re-register the the service on the network. :ip: if string `ip` is given, use given IP when registering. """ self.unregister() self.info = build_service_info(ip=ip or main_ip()) self.register() def close(self): """Free resources. Advertisement.unregister() should be called before closing. """ self.zeroconf.close() def get_ip(self): """:return: the service's IP as a string. """ return socket.inet_ntoa(self.info.address) ip = property(get_ip)
class ServiceDiscoveryServer: def __init__(self, port): inet = netifaces.AF_INET hostname = socket.gethostname() def_gw = netifaces.gateways()['default'][inet][1] addr = netifaces.ifaddresses(def_gw)[inet][0]['addr'] addr = socket.inet_aton(addr) self.zeroconf = Zeroconf() self.info = ServiceInfo( SRV_TYPE, srv_fqname(), addr, port, 0, 0, {}, '{}.local.'.format(hostname) ) def start(self): self.zeroconf.register_service(self.info) def stop(self): self.zeroconf.unregister_service(self.info) self.zeroconf.close()
def test_integration_with_listener(self): type_ = "_test-srvc-type._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) 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_types = ZeroconfServiceTypes.find(interfaces=['127.0.0.1'], timeout=0.5) assert type_ in service_types service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=0.5) assert type_ in service_types finally: zeroconf_registrar.close()
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 bootstrap(self): try: zeroconf = Zeroconf(interfaces=[ self.bootstrap_ip ]) self._log.debug('Zeroconf instantiated.') except: self._log.error('Error instantiating zeroconf instance on interface: {0} with IP: {1}'.format(self.iface, self.bootstrap_ip)) raise try: zeroconf.register_service(self.service_discovery_def) discovery_browser = ServiceBrowser(zeroconf, self.service_discovery_type, handlers=[ self._election_handler ]) leader_browser = ServiceBrowser(zeroconf, self.service_leader_type, handlers=[ self._election_handler ]) except: self._log.error('Error encountered with registered zeroconf services or browser.') raise while True: sleep(.1) if self._discovery_peer_count <= 0: log.debug('All peers unregistered from discovery service.') break if self.election_id == self._leader_id: zeroconf.unregister_service(self.service_leader_def) self._log.debug('Leader service unregistered.') sleep(1) try: zeroconf.close() except: self._log.error('Error encountered closing zerconf instance.') raise self._log.debug('Zerconf instance close.') if self.election_id == self._leader_id: bootstrap_command = [ self.action ] + self.get_peer_addresses(self._peers) self._log.info('Leader performing bootstrap action: {}'.format(' '.join(bootstrap_command))) Popen(bootstrap_command)
class ZstStage(ZstNode): def __init__(self, stageName="stage", port=6000): ZstNode.__init__(self, stageName) self.zeroconf = Zeroconf() address = "tcp://*:" + str(port) self.reply.socket.bind(address) desc = {'name': self.name} addr = socket.gethostbyname(socket.gethostname()) servicename = "ShowtimeStage" self.stageServiceInfo = ServiceInfo("_zeromq._tcp.local.", servicename + "._zeromq._tcp.local.", socket.inet_aton(addr), port, 0, 0, desc) self.zeroconf.register_service(self.stageServiceInfo) print("Stage active on address " + str(self.reply.socket.getsockopt(zmq.LAST_ENDPOINT))) def close(self): self.zeroconf.unregister_service(self.stageServiceInfo) self.zeroconf.close() ZstNode.close(self)
class Zeroconf(object): """ A simple class to publish a network service using zeroconf. """ def __init__(self, name, port, **kwargs): self.zconf = None stype = kwargs.get('stype', "_http._tcp.local.") full_name = name if not name.endswith('.'): full_name += '.' + stype props = {'txtvers': '1', 'iTSh Version': '131073', #'196609' 'Machine Name': name, 'Password': '******'} self.svc_info = ServiceInfo(stype, full_name, find_local_ipaddress(), port, 0, 0, props) def publish(self): """ Publish the service to the network. """ if self.zconf is None: self.zconf = ZeroC() self.zconf.register_service(self.svc_info) def unpublish(self): """ Tell the network we're closed :-) """ if self.zconf is not None: self.zconf.unregister_service(self.svc_info) self.zconf.close() self.zconf = None
local_ip = get_ip() print 'Webserver listening on {}:{} ...'.format(local_ip, WEBPORT) print 'Webservice listening on {}:{} ...'.format(local_ip, WSPORT) serviceinfo = ServiceInfo( '_http._tcp.local.', SERVICENAME+'._http._tcp.local.', socket.inet_aton(local_ip), WEBPORT, 0, 0, {"desc":SERVICENAME}, "mute.local." ) serviceconf.register_service(serviceinfo) log.startLogging(sys.stdout) # factory = WebSocketServerFactory(u"ws://127.0.0.1:9000") factory = WebSocketServerFactory() factory.protocol = MuteServerProtocol # factory.setProtocolOptions(maxConnections=2) signal.signal(signal.SIGINT, customHandler) reactor.listenTCP(WSPORT, factory) webdir = File("../CLIENT") webdir.putChild('wsport.js', WSPortJs()) web = Site(webdir)
class Server: def run(self): self.find_plasma_trim() self.register_service() self.run_web_server() self.write_plasma_trim_changes() def find_plasma_trim(self): plasmas = plasmatrim.find() self.plasma = plasmas[0] def register_service(self): ip_address = socket.gethostbyname(socket.gethostname()) type = "_radiance._tcp.local." hostname = socket.gethostname() split_index = hostname.find(".local") if split_index > 0: hostname = hostname[:split_index] name = str("%s.%s") % (hostname, type) self.service = ServiceInfo(type, name, socket.inet_aton(ip_address), 8080, 0, 0, name) self.zeroconf = Zeroconf() self.zeroconf.register_service(self.service) def run_web_server(self): self.render = web.template.render('templates/') urls = ( '/', 'Home', '/api/color', 'API_Color', '/api/brightness', 'API_Brightness' ) self.app = web.application(urls, globals()) self.app.add_processor(web.loadhook(self.load_plasma_trim)) self.app.add_processor(web.loadhook(self.load_renderer)) self.app.add_processor(web.loadhook(self.load_main_thread_queue)) self.web_server_thread = Thread(None, self.app.run) self.web_server_thread.start() def write_plasma_trim_changes(self): signal.signal(signal.SIGINT, self.handle_ctrl_c) self.callback_queue = Queue.Queue() while True: try: callback = self.callback_queue.get(False) except Queue.Empty: continue callback() def load_plasma_trim(self): web.ctx['plasma'] = self.plasma def load_renderer(self): web.ctx['render'] = self.render def load_main_thread_queue(self): web.ctx['callback_queue'] = self.callback_queue def handle_ctrl_c(self, signal, frame): print "Exiting" os._exit(0)
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 # needs to be a list so that we can modify it in our phony send nbr_answers = [0, None] nbr_additionals = [0, None] nbr_authorities = [0, None] def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT): """Sends an outgoing packet.""" for answer, time_ in out.answers: nbr_answers[0] += 1 assert answer.ttl == expected_ttl for answer in out.additionals: nbr_additionals[0] += 1 assert answer.ttl == expected_ttl for answer in out.authorities: nbr_authorities[0] += 1 assert answer.ttl == expected_ttl old_send(out, addr=addr, port=port) # monkey patch the zeroconf send zc.send = send # register service with default TTL expected_ttl = r._DNS_TTL zc.register_service(info) assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 3 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 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(query, r._MDNS_ADDR, r._MDNS_PORT) assert nbr_answers[0] == 4 and nbr_additionals[0] == 1 and nbr_authorities[0] == 0 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0 # unregister expected_ttl = 0 zc.unregister_service(info) assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 0 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0 # register service with custom TTL expected_ttl = r._DNS_TTL * 2 assert expected_ttl != r._DNS_TTL zc.register_service(info, ttl=expected_ttl) assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 3 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 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(query, r._MDNS_ADDR, r._MDNS_PORT) assert nbr_answers[0] == 4 and nbr_additionals[0] == 1 and nbr_authorities[0] == 0 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0 # unregister expected_ttl = 0 zc.unregister_service(info) assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 0 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0
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 put( filename, output=None, interface=None, address=None, port=None, reporthook=None, timeout=None, ): """Send a file using the zget protocol. Parameters ---------- filename : string The filename to be transferred output : string The alias to share on the network. Optional. If empty, the input filename will be used. interface : string The network interface to use. Optional. address : string The network address to use. Optional. port : int The network port to use. Optional. reporthook : callable A hook that will be called during transfer. Handy for watching the transfer. See :code:`urllib.urlretrieve` for callback parameters. Optional. timeout : int Seconds to wait until process is aborted. A running transfer is not aborted even when timeout was hit. Optional. Raises ------- TimeoutException When a timeout occurred. """ if port is None: port = utils.config().getint('DEFAULT', 'port') if interface is None: interface = utils.config().get('DEFAULT', 'interface') if not 0 <= port <= 65535: raise ValueError(_("Port %d exceeds allowed range") % port) basename = os.path.basename(filename) filehashes = [] filehashes.append(hashlib.sha1(basename.encode('utf-8')).hexdigest()) if output is not None: filehashes.append(hashlib.sha1(output.encode('utf-8')).hexdigest()) if interface is None: interface = utils.default_interface() if address is None: address = utils.ip_addr(interface) server = StateHTTPServer((address, port), FileHandler) server.timeout = timeout server.filename = filename server.allowed_basenames.append(basename) if output is not None: server.allowed_basenames.append(output) server.reporthook = reporthook port = server.server_port utils.logger.debug( _("Using interface %s") % interface ) utils.logger.debug( _("Listening on %(a)s:%(p)d \n" "you may change address using --address and " "port using --port") % {'a': address, 'p': port} ) zeroconf = Zeroconf() infos = [] for filehash in filehashes: utils.logger.debug( _("Broadcasting as %s._zget._http._tcp.local.") % filehash ) infos.append(ServiceInfo( "_zget._http._tcp.local.", "%s._zget._http._tcp.local." % filehash, socket.inet_aton(address), port, 0, 0, {'path': None} )) try: for info in infos: zeroconf.register_service(info) server.handle_request() except KeyboardInterrupt: pass server.socket.close() for info in infos: zeroconf.unregister_service(info) zeroconf.close() if timeout is not None and not server.downloaded: raise utils.TimeoutException() else: utils.logger.info(_("Done."))
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...") print(" Getting ZOE service: %s" % ( r.get_service_info("_http._tcp.local.", "ZOE._http._tcp.local."))) print(" Query done.") print("3. Testing query of own service...") queried_info = r.get_service_info("_http._tcp.local.", "My Service Name._http._tcp.local.") assert queried_info print(" Getting self: %s" % (queried_info,)) print(" Query done.") print("4. Testing unregister of service information...") r.unregister_service(info) print(" Unregister done.") r.close()
def test_integration_with_listener_class(self): service_added = Event() service_removed = Event() service_updated = Event() subtype_name = "My special Subtype" type_ = "_http._tcp.local." subtype = subtype_name + "._sub." + type_ name = "xxxyyyæøå" registration_name = "%s.%s" % (name, subtype) class MyListener(r.ServiceListener): 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() class MySubListener(r.ServiceListener): def add_service(self, zeroconf, type, name): pass def remove_service(self, zeroconf, type, name): pass def update_service(self, zeroconf, type, name): service_updated.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/'} # type: r.ServicePropertiesType 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(3) # 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 is not None 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 is not None assert info.properties[b'prop_none'] is False # Begin material test addition sublistener = MySubListener() zeroconf_browser.add_service_listener(registration_name, sublistener) properties['prop_blank'] = b'an updated string' 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.update_service(info_service) service_updated.wait(1) assert service_updated.is_set() info = zeroconf_browser.get_service_info(type_, registration_name) assert info is not None assert info.properties[b'prop_blank'] == properties['prop_blank'] # End material test addition 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()
def test_integration_with_listener_class(self): service_added = Event() service_removed = Event() type_ = "_http._tcp.local." 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() zeroconf_browser = Zeroconf() zeroconf_browser.add_service_listener(type_, MyListener()) 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() desc = {'path': '/~paulsm/'} desc.update(properties) 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() # 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 zeroconf_registrar.unregister_service(info) service_removed.wait(1) assert service_removed.is_set() finally: zeroconf_registrar.close() 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, 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 == 1 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 == 1 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
import logging import socket import sys from time import sleep from zeroconf import 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) desc = {'version': '0.1'} info = ServiceInfo("_http._tcp.local.", "pispotify._http._tcp.local.", socket.inet_aton("192.168.0.17"), 80, 0, 0, desc) #use the right ip zeroconf = Zeroconf() print("Registration of a service, press Ctrl-C to exit...") zeroconf.register_service(info) try: while True: sleep(0.1) except KeyboardInterrupt: pass finally: print("Unregistering...") zeroconf.unregister_service(info) zeroconf.close()
class VoicePlayZeroConf(object): """ Very basic mDNS/Zeroconf check/registration """ def __init__(self): self.zeroconf = None self.known_servers = [] self.info = None self.threads = None self.exit = False @staticmethod def get_ip_address(): """ Get my IP """ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) return s.getsockname()[0] def service_info(self, hostname): """ Prepare service information for (my) hostname """ port = int(Config.cfg_data().get('webapp_port')) info = ServiceInfo("_http._tcp.local.", "{0!s}._http._tcp.local.".format(hostname), socket.inet_aton(self.get_ip_address()), port, 0, 0, {'path': '/'}, "{0!s}.local.".format(hostname)) return info def on_service_state_change(self, zeroconf, service_type, name, state_change): """ Service state change callback """ if state_change is ServiceStateChange.Added: info = zeroconf.get_service_info(service_type, name) if info and not info.server in self.known_servers: self.known_servers.append(info.server) def get_others(self): """ Wait for other services to make themselves visible """ zeroconf = Zeroconf() _ = ServiceBrowser(zeroconf, "_http._tcp.local.", handlers=[self.on_service_state_change]) for _ in range(1, 10 + 1): time.sleep(1) zeroconf.close() return self.known_servers def run(self): """ Actual Zeroconf runner """ # has to be here, otherwise quick start/stop will not provide self.zeroconf descriptor self.zeroconf = Zeroconf() servers = self.get_others() hostname = __title__.lower() if '{0!s}.local'.format(hostname) in servers: hostname = '{0!s}-{1!s}'.format(__title__.lower(), str(uuid.uuid4())) self.info = self.service_info(hostname) self.zeroconf.register_service(self.info) while not self.exit: try: time.sleep(0.1) except Exception as _: break def unregister(self): """ Unregister (remove) service from local network """ if self.info: self.zeroconf.unregister_service(self.info) self.zeroconf.close() def start(self): """ Start Zeroconf inside thread """ self.threads = ThreadGroup() self.threads.targets = [self.run] self.threads.start_all() def stop(self): """ Stop and unregister Zeroconf service """ self.exit = True self.unregister() if self.threads: self.threads.stop_all()
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()