GithubHelp home page GithubHelp logo

python-zeroconf / python-zeroconf Goto Github PK

View Code? Open in Web Editor NEW
596.0 18.0 220.0 2.43 MB

A pure python implementation of multicast DNS service discovery

License: GNU Lesser General Public License v2.1

Makefile 0.01% Python 95.86% Cython 4.13%
python python3 zeroconf bonjour avahi service-discovery library

python-zeroconf's Introduction

python-zeroconf

Documentation.

This is fork of pyzeroconf, Multicast DNS Service Discovery for Python, originally by Paul Scott-Murphy (https://github.com/paulsm/pyzeroconf), modified by William McBrine (https://github.com/wmcbrine/pyzeroconf).

The original William McBrine's fork note:

This fork is used in all of my TiVo-related projects: HME for Python
(and therefore HME/VLC), Network Remote, Remote Proxy, and pyTivo.
Before this, I was tracking the changes for zeroconf.py in three
separate repos. I figured I should have an authoritative source.

Although I make changes based on my experience with TiVos, I expect that
they're generally applicable. This version also includes patches found
on the now-defunct (?) Launchpad repo of pyzeroconf, and elsewhere
around the net -- not always well-documented, sorry.

Compatible with:

  • Bonjour
  • Avahi

Compared to some other Zeroconf/Bonjour/Avahi Python packages, python-zeroconf:

  • isn't tied to Bonjour or Avahi
  • doesn't use D-Bus
  • doesn't force you to use particular event loop or Twisted (asyncio is used under the hood but not required)
  • is pip-installable
  • has PyPI distribution
  • has an optional cython extension for performance (pure python is supported as well)

Python compatibility

  • CPython 3.8+
  • PyPy3.8 7.3+

Versioning

This project uses semantic versioning.

Status

This project is actively maintained.

Traffic Reduction

Before version 0.32, most traffic reduction techniques described in https://datatracker.ietf.org/doc/html/rfc6762#section-7 where not implemented which could lead to excessive network traffic. It is highly recommended that version 0.32 or later is used if this is a concern.

IPv6 support

IPv6 support is relatively new and currently limited, specifically:

  • InterfaceChoice.All is an alias for InterfaceChoice.Default on non-POSIX systems.
  • Dual-stack IPv6 sockets are used, which may not be supported everywhere (some BSD variants do not have them).
  • Listening on localhost (::1) does not work. Help with understanding why is appreciated.

How to get python-zeroconf?

The easiest way to install python-zeroconf is using pip:

pip install zeroconf

How do I use it?

Here's an example of browsing for a service:

from zeroconf import ServiceBrowser, ServiceListener, Zeroconf


class MyListener(ServiceListener):

    def update_service(self, zc: Zeroconf, type_: str, name: str) -> None:
        print(f"Service {name} updated")

    def remove_service(self, zc: Zeroconf, type_: str, name: str) -> None:
        print(f"Service {name} removed")

    def add_service(self, zc: Zeroconf, type_: str, name: str) -> None:
        info = zc.get_service_info(type_, name)
        print(f"Service {name} added, service info: {info}")


zeroconf = Zeroconf()
listener = MyListener()
browser = ServiceBrowser(zeroconf, "_http._tcp.local.", listener)
try:
    input("Press enter to exit...\n\n")
finally:
    zeroconf.close()

Note

Discovery and service registration use all available network interfaces by default. If you want to customize that you need to specify interfaces argument when constructing Zeroconf object (see the code for details).

If you don't know the name of the service you need to browse for, try:

from zeroconf import ZeroconfServiceTypes
print('\n'.join(ZeroconfServiceTypes.find()))

See examples directory for more.

Changelog

Changelog

License

LGPL, see COPYING file for details.

python-zeroconf's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-zeroconf's Issues

Examples don't seem to be working

I have copied the examples and using the released version 0.15.1, but the the browser methods add_service(), remove_service() neither their forms addService() removeService() are not working - there is no callback happening. Without those examples it is unclear what needs to be done to be able to use the library.

I'm creating a virtual env and installing zeroconf wheel using pip. Python-3.4.1 Starting browser and
I'm tried both ip as in examples for the registration and also actual IP socket.inet_aton(socket.gethostbyname(socket.gethostname())) on port 5555

Please advise!

Thanks!

ImportError with zeroconf 0

On my CentOS 7 configuration with Zeroconf 0.17.1 and Six 1.9.0:

± ipython
Python 2.7.5 (default, Jun 17 2014, 18:11:42) 
Type "copyright", "credits" or "license" for more information.

IPython 2.3.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import zeroconf
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-1-c556a850dc86> in <module>()
----> 1 import zeroconf

/usr/lib/python2.7/site-packages/zeroconf.py in <module>()
     34 
     35 import netifaces
---> 36 from six import binary_type, indexbytes, int2byte, iteritems, text_type
     37 from six.moves import xrange
     38 

ImportError: cannot import name indexbytes

No service found under Windows but working under Ubuntu VM.

I am facing some issues in setting up a client/ServiceBrowser that works under both Win and Linux.

I have the following set-up:

  • Win 7 connected to domain
    • Ethernet config uses IPv4 with Subnet Mask 255.255.255.0
  • Ubuntu VM running on the same system in Oracle VirtualBox with Win Ethernet as Bridged Adapter
    enp0s3 Link encap:Ethernet HWaddr 08:00:27:55:62:63 inet addr:10.121.44.95 Bcast:10.121.44.255 Mask:255.255.255.0 inet6 addr: fe80::aa6b:92c:17c4:cfc8/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1837029 errors:0 dropped:0 overruns:0 frame:0 TX packets:110395 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:467243091 (467.2 MB) TX bytes:7871423 (7.8 MB)
  • The broadcasting systems are running on different Debian systems connected to the network domain

If I run the ServiceBrowser from Windows I don't get any return back. I used the example Browser to test the set-up.
`from zeroconf import ServiceBrowser, Zeroconf

class MyListener(object):

def remove_service(self, zeroconf, type, name):
    print("Service %s removed" % (name,))

def add_service(self, zeroconf, type, name):
    info = zeroconf.get_service_info(type, name)
    print("Service %s added, service info: %s" % (name, info))

zeroconf = Zeroconf()
listener = MyListener()
browser = ServiceBrowser(zeroconf, "_tntsrv._tcp.local.", listener)
try:
input("Press enter to exit...\n\n")
finally:
zeroconf.close()`
Running from withing Ubuntu, it returns all the available services (as expected).

I noticed that if I change HOST_ONLY_NETWORK_MASK from 255.255.255.255 to 255.255.255.0 the opposite happens, where it works under Windows but not under Ubuntu.

This seems to ocurr in line 1603
if addr.get('netmask') != HOST_ONLY_NETWORK_MASK

I'm not experienced enough to find a common ground, but is there any posibility so that it'll work under both systems?

Fix Python 3 compatibility

Traceback (most recent call last):
  File "/home/travis/build/jstasiak/python-zeroconf/zeroconf.py", line 878, in handle_read
    data, (addr, port) = self.zc.socket.recvfrom(_MAX_MSG_ABSOLUTE)
OSError: [Errno 9] Bad file descriptor
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/home/travis/build/jstasiak/python-zeroconf/zeroconf.py", line 832, in run
    self.readers[socket_].handle_read()
  File "/home/travis/build/jstasiak/python-zeroconf/zeroconf.py", line 883, in handle_read
    if e[0] == socket.EBADF:
TypeError: 'OSError' object is not subscriptable

call register_service twice, it will crash


TYPE = "_motion._tcp.local."
NAME = "_yyyyXXXX." + TYPE
PORT = 4321

zeroconf = Zeroconf()

def reg_srv():
    socket_addr = socket.gethostbyname(socket.gethostname())
    info = ServiceInfo(TYPE,
            NAME,
            socket.inet_aton(socket_addr), PORT, 0, 0,
            {"desc":"HOME Bonjour test"})


    zeroconf.register_service(info)

    print("broadcasting: " + socket_addr + " :" + str(PORT))

def main():
    reg_srv()
    print("listening on 4321 ...\n")

    reactor.listenTCP(4321, MotionServiceFactory())
    reactor.run()
    print("closing zeroconf")
    zeroconf.close()

when I called main() twice, it will crash:

motion_server.py in reg_srv()
     25
     26
---> 27     zeroconf.register_service(info)
     28
     29     print("broadcasting: " + socket_addr + " :" + str(PORT))

/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/zeroconf-0.17.4-py3.4.egg/zeroconf.py in register_service(self, info, ttl)
   1430         information for that service.  The name of the service may be
   1431         changed if needed to make it unique on the network."""
-> 1432         self.check_service(info)
   1433         self.services[info.name.lower()] = info
   1434         if info.type in self.servicetypes:

/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/zeroconf-0.17.4-py3.4.egg/zeroconf.py in check_service(self, info)
   1546             out.add_authorative_answer(DNSPointer(info.type, _TYPE_PTR,
   1547                                                   _CLASS_IN, _DNS_TTL, info.name))
-> 1548             self.send(out)
   1549             i += 1
   1550             next_time += _CHECK_TIME

/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/zeroconf-0.17.4-py3.4.egg/zeroconf.py in send(self, out, addr, port)
   1666         log.debug('Sending %r as %r...', out, packet)
   1667         for s in self._respond_sockets:
-> 1668             bytes_sent = s.sendto(packet, 0, (addr, port))
   1669             if bytes_sent != len(packet):
   1670                 raise Error(

OSError: [Errno 9] Bad file descriptor

Handling of "bad" packets.

I was testing some stuff with this zeroconf library on our local network at the office. And I noticed a lot of error messages like this:

ERROR:zeroconf:Unknown error, possibly benign: IndexError('index out of range',)
Traceback (most recent call last):
  File "/home/ultimaker/zeroconf.py", line 832, in run
    self.readers[socket_].handle_read(socket_)
  File "/home/ultimaker/zeroconf.py", line 886, in handle_read
    msg = DNSIncoming(data)
  File "/home/ultimaker/zeroconf.py", line 464, in __init__
    self.read_questions()
  File "/home/ultimaker/zeroconf.py", line 481, in read_questions
    name = self.read_name()
  File "/home/ultimaker/zeroconf.py", line 560, in read_name
    length = indexbytes(self.data, off)
IndexError: index out of range

I quickly captured the data with wireshark, and noticed I was getting broadcast UDP packages on port 5353 with the contents:

{"command","broadcast"}

Yes, exactly that text contents. No binary MDNS encoding around it.

The packets seem to origin from an AppleTV device. No idea why they wouldn't be using normal MDNS packets. But it's common enough to say that the library needs some extra hardening on the parsing area.

I can make a patch, but what would be the preferred way to handle this?
(I also have a bugfix for windows, but I'll post that later when I'm 100% sure about the solution)

Services cannot be searched by bonjour

I'm trying zeroconf (repo version) with bonjour under macosx

for code:

TYPE = "_http._tcp.local."
NAME = "XXXX_http._tcp.local."
PORT = 4321

def reg_srv():
    zeroconf = Zeroconf()

    info = ServiceInfo(TYPE,
            NAME,
            socket.inet_aton("127.0.0.1"), PORT, 0, 0,
            {"name":"HOME Bonjour test"})


    zeroconf.register_service(info)


def on_service_state_change(zeroconf, service_type, name, state_change):
    print("Service %s of type %s state changed: %s" % (name, service_type, state_change))

    if state_change is ServiceStateChange.Added:
        info = zeroconf.get_service_info(service_type, name)
        print(info)
        if info:
            print("  Address: %s:%d" % (socket.inet_ntoa(info.address), info.port))

def brs_srv():
    from time import sleep

    zeroconf = Zeroconf()
    browser = ServiceBrowser(zeroconf, TYPE, handlers = [on_service_state_change])

    try:
        while True:
            sleep(0.1)
    except KeyboardInterrupt:
        pass
    finally:
        zeroconf.close()

I can use brs_srv to discover advertised service, but bonjour (dns-sd) cannot.

I used $dns-sd -B _http._tcp local. to set up browser under macosx 10.11

I used $dns-sd -R "TTTT" "_http._tcp" "local" 4321 to test browser if works or not, and it works.

What I am missing ?

Register_service take long time sleep when system time moves backwards or forward abruptly

I start up a linux VM but sometimes zeroconf block in register_service(), according to my investigate, linux system got ntp time update during zeroconf register service, it looks like below code got a long sleep, I think the next_time should update the current time every loop.

now = current_time_millis()
next_time = now    # next_time should be update once now be changed
i = 0
while i < 3:
    if now < next_time:
         self.wait(next_time - now)
         now = current_time_millis()
         continue
    ...
    next_time += _REGISTER_TIME

send a interrupt signal after block

    zeroconf.register_service(info)
  File "/usr/lib/python2.7/site-packages/zeroconf.py", line 1619, in register_service
    self.wait(next_time - now)
  File "/usr/lib/python2.7/site-packages/zeroconf.py", line 1569, in wait
    self.condition.wait(timeout / 1000.0)
  File "/usr/lib/python2.7/threading.py", line 359, in wait
    _sleep(delay)
KeyboardInterrupt

ServiceBrowser initialization will crash when an answer arrives too fast

Sometimes after initializing a ServiceBrowser instance, we get the following exception:

Exception in thread Thread-3:
Traceback (most recent call last):
  File "/usr/lib/python3.2/threading.py", line 740, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3/dist-packages/zeroconf.py", line 1063, in run
    handler(self.zc)
  File "/usr/lib/python3/dist-packages/zeroconf.py", line 1010, in <lambda>
    lambda zeroconf: self._service_state_changed.fire(
AttributeError: 'ServiceBrowser' object has no attribute '_service_state_changed'

This is because the ServiceBrowser._service_state_changed signal gets initialized after a listener gets added to the Zeroconf instance, which in turn can register a service change before the initialization.

Allow substring/regexp in ServiceBrowser service type

It would be nice to add an ability to create a service browser using a substring of service type. This way one could create a set of custom services with types like this:

_mygroup._bob._tcp.
_mygroup._bob1._tcp.
_mygroup._alice._tcp.

So that the discovery would return
all three for "mygroup."
only two for "bob" or for "_mygroup._bob*"
ad only one for "alice" or "bob1"

This would allow for comprehensive managements of services of different types, while allowing to filter out something that is not interesting for particular browser.

Implement Name Compression in DNSOutgoing()

To make packets smaller should implement name compression from rfc6762

_18.14. Name Compression_

When generating Multicast DNS messages, implementations SHOULD use
name compression wherever possible to compress the names of resource
records, by replacing some or all of the resource record name with a
compact two-byte reference to an appearance of that data somewhere
earlier in the message [RFC1035].

request to increment version to indicate changes

Hello,
it appears that version 0.15.1 has been used since before the snake_case migration took place. There are still libs out there with the camelCase api. It would simplify things if I could test zeroconf.version to determine if I need to wrap the handler methods to accomodate older libs.
Best Regards, and great work on this lib

Race condition in ServiceBrowser.__init__()

It seems that there's a race between the thread executing ServiceBrowser.__init__() and the new thread executing ServiceBrowser.run() I'm using this script:

import zeroconf, socket, threading


def main():
    print(zeroconf.__version__)

    z = zeroconf.Zeroconf()

    # Register some service.
    z.register_service(
        zeroconf.ServiceInfo(
            '_test._tcp.local.',
            'test._test._tcp.local.',
            socket.inet_aton('127.0.0.1'),
            80,
            properties = { }))

    class listener:
        @staticmethod
        def add_service(*args):
            print('add_service():', args)

        @staticmethod
        def remove_service(*args):
            print('remove_service():', args)

    # Immediately browse for services of the same type.
    z.add_service_listener('_test._tcp.local.', listener)

    # Wait forever.
    threading.Event().wait()


if __name__ == '__main__':
    main()

When running this, sometimes I get this output:

0.17.2
Exception in thread Thread-3:
Traceback (most recent call last):
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 920, in _bootstrap_inner
    self.run()
  File "/Users/michi/Desktop/Abfahrtsanzeiger_Roeschtibach/doorswitch/venv3/lib/python3.4/site-packages/zeroconf.py", line 1063, in run
    handler(self.zc)
  File "/Users/michi/Desktop/Abfahrtsanzeiger_Roeschtibach/doorswitch/venv3/lib/python3.4/site-packages/zeroconf.py", line 1010, in <lambda>
    lambda zeroconf: self._service_state_changed.fire(
AttributeError: 'ServiceBrowser' object has no attribute '_service_state_changed'

It seems that the newly started thread tries to access self._service_state_changed before it has been initialized by self.__init__().

Service Not Found - Help to debug

For a while I was using the How do I se it example to discover IoT devices as esp8266 (with _arduino as service name), but I'm having a problem since last week. None device is found when I run the example.

The first I thought, it was problem of my device, but when I run the Arduino IDE, it's listed as mDNS service.

In the Arduino's github shows that _arduino is the name of the service: https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266mDNS/ESP8266mDNS.cpp#L891

I've disabled my firewall thinking it may be the problem, but same results. And now other people using nodemcu devices have reported me the same issue, so I guess isn't my configuration.

My question is, do you have any recomendation to know where is the problem?

I've used --debug as argument but the output isn't very clear to me:

DEBUG:zeroconf:Sending <zeroconf.DNSOutgoing object at 0x02A1A690> as '\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x08_arduino\x04_tcp\x05local\x00\x00\x0c\x00\x01'...

zeroconf.unregister_all_services() causes spurious registrations

Calling unregister_all_services while using ServiceBrowser causing spurious add / remove events
Looping over unregister_service does not have this problem.

PR pending - I'm resolving this by looking at how unregister_service addresses the problem.

Test code:

import socket
import time
from zeroconf import Zeroconf, ServiceInfo, ServiceStateChange, ServiceBrowser

class ServiceRegistry(object):
    def __init__(self, zc):
        self._zeroconf = zc

    def register(self, name, port=8080, regtype='zeroconf._tcp.local.'):
        host = socket.gethostname()
        info = ServiceInfo(type=regtype,
                           name=name + '.' + regtype,
                           address=socket.inet_aton(socket.gethostbyname(host)),
                           port=port,
                           properties={},
                           server=host)
        self._zeroconf.register_service(info)
        print("registration complete")

    def shutdown(self):
        self._zeroconf.unregister_all_services()

class Browser(object):
    def __init__(self, zc):
        self._zeroconf

    def browse(self, regtype='zeroconf._tcp.local.'):
        self._browser = ServiceBrowser(self._zeroconf, regtype, self)

    def shutdown(self):
        self._browser.cancel()

    def add_service(self, zeroconf, type, name):
        print("service {} added".format(name))

    def remove_service(self, zeroconf, type, name):
        print("Service {} removed".format(name))

if __name__ == '__main__':
    try:
        zc = Zeroconf()
        registry = ServiceRegistry(zc)
        registry.register('test-case-1')
        registry.register('test-case-2')
        registry.register('test-case-3')
        registry.register('test-case-4')
        registry.register('test-case-5')
        browser = Browser(zc)
        browser.browse()
        print('Done - ^C to exit')
        while True:
            time.sleep(0.1)
    except KeyboardInterrupt:
        pass
    finally:
        registry.shutdown()
        time.sleep(1)
        browser.shutdown()
        zc.close()

Results in this output

$ python unregister_test_case.py 
registration complete
registration complete
registration complete
registration complete
registration complete
Done - ^C to exit
service test-case-1.zeroconf._tcp.local. added
service test-case-2.zeroconf._tcp.local. added
service test-case-3.zeroconf._tcp.local. added
service test-case-4.zeroconf._tcp.local. added
service test-case-5.zeroconf._tcp.local. added
^CService test-case-1.zeroconf._tcp.local. removed
Service test-case-5.zeroconf._tcp.local. removed
Service test-case-4.zeroconf._tcp.local. removed
Service test-case-3.zeroconf._tcp.local. removed
Service test-case-2.zeroconf._tcp.local. removed
service test-case-1.zeroconf._tcp.local. added
service test-case-5.zeroconf._tcp.local. added
service test-case-4.zeroconf._tcp.local. added
service test-case-3.zeroconf._tcp.local. added
service test-case-2.zeroconf._tcp.local. added
Service test-case-1.zeroconf._tcp.local. removed
Service test-case-5.zeroconf._tcp.local. removed
Service test-case-4.zeroconf._tcp.local. removed
Service test-case-3.zeroconf._tcp.local. removed
Service test-case-2.zeroconf._tcp.local. removed
service test-case-1.zeroconf._tcp.local. added
service test-case-5.zeroconf._tcp.local. added
service test-case-4.zeroconf._tcp.local. added
service test-case-3.zeroconf._tcp.local. added
service test-case-2.zeroconf._tcp.local. added

Where expected output is this:

$ python unregister_test_case.py 
registration complete
registration complete
registration complete
registration complete
registration complete
Done - ^C to exit
service test-case-1.zeroconf._tcp.local. added
service test-case-2.zeroconf._tcp.local. added
service test-case-3.zeroconf._tcp.local. added
service test-case-4.zeroconf._tcp.local. added
service test-case-5.zeroconf._tcp.local. added
^CService test-case-1.zeroconf._tcp.local. removed
Service test-case-5.zeroconf._tcp.local. removed
Service test-case-4.zeroconf._tcp.local. removed
Service test-case-3.zeroconf._tcp.local. removed
Service test-case-2.zeroconf._tcp.local. removed

edit: Zeroconf controls its threads with a global shutdown flag, so running two Zeroconf instances causes a shutdown race condition. The edit changes the code to use one shared Zeroconf instance.

unpack without bounds check stops ServiceBrowser

This exception is occuring at intervals since 0.17.6; stops the browser thread. Exception always occurs in a pair.
May be a bad packet, since the unpack looks to be unpacking based on packet information.

Exception in thread zeroconf-Engine:
Traceback (most recent call last):
  File "//anaconda/envs/py34/lib/python3.4/threading.py", line 911, in _bootstrap_inner
    self.run()
  File "/Users/gbiddison/source/sygo/tools/BeagleDiff/zeroconf.py", line 957, in run
    reader.handle_read(socket_)
  File "/Users/gbiddison/source/sygo/tools/BeagleDiff/zeroconf.py", line 994, in handle_read
    msg = DNSIncoming(data)
  File "/Users/gbiddison/source/sygo/tools/BeagleDiff/zeroconf.py", line 569, in __init__
    self.read_others()
  File "/Users/gbiddison/source/sygo/tools/BeagleDiff/zeroconf.py", line 618, in read_others
    type_, class_, ttl, length = self.unpack(b'!HHiH')
  File "/Users/gbiddison/source/sygo/tools/BeagleDiff/zeroconf.py", line 574, in unpack
    format_, self.data[self.offset:self.offset + length])
struct.error: unpack requires a bytes object of length 10

Services registered w/ 17.7.dev are not visible to ServiceBrowser

I am debugging this issue at the moment to try to get more details, I am having a problem in 17.7.dev either with browsing or registration.

I have approx. 30 services running 0.17.6. If my service browser is running 0.17.6 or 0.17.7.dev, I recover all of these services.

However, if any of the services are upgraded to 0.17.7.dev, then the service browser no longer recovers that specific service.

Using 'bonjour browser' on OSX, I do see all 30 services regardless of zeroconf version, so the registration appears to be succeeding.

I will continue to debug this, but any suggestions are welcome.

Implement automatic renaming of duplicate instance names

Bonjour automatically renames DNS-SD instances with conflicting names. E.g if I use two instances of the dns-sd command line tool to register two instances with the same name, the first one will show this:

michi ~$ dns-sd -R test _foo '' 10001
Registering Service test._foo._tcp port 10001
DATE: ---Mon 28 Sep 2015---
 1:06:49.909  ...STARTING...
 1:06:50.827  Got a reply for service test._foo._tcp.local.: Name now registered and active

While The second one will show this:

michi ~$ dns-sd -R test _foo '' 10002
Registering Service test._foo._tcp port 10002
DATE: ---Mon 28 Sep 2015---
 1:06:55.814  ...STARTING...
 1:06:56.990  Got a reply for service test (2)._foo._tcp.local.: Name now registered and active

I.e. the instance has been renamed to "test (2)".

I think it would be great if that feature was also added to this library.

Logging broken/incomplete

I noticed your recent commit that adds better logging using the python logging package.

  1. There only seem to be exception logs, there's no debug-level logging to tell what's happening internally.
  2. The way the log objects and handlers are setup (at the module-level), it's not possible to setup logging in the scripts that use zeroconf.

I'm going to try to fix this in https://github.com/jantman/python-zeroconf/tree/more_logging and cut a PR once I have something working.

Connecting to Multiple Interfaces is Broken

When connecting to multiple interfaces, this library is currently treating those interfaces as the same network. This is very broken, as any single broadcast domain must be treated independently. Any service name is only unique on a single broadcast domain and to assume otherwise does not work. If connecting on multiple interfaces, then the name spaces must not be combined. (there are other issues with this approach, but one show stopper is enough, I won't bother describing others)

Workaround:

This library should always have a specific interface address specified when initializing a ZeroConf() instance on a multi-homed host. The only exception would be when the ZeroConf() instance will only be used for browsing for services, and then only if connecting on a multi-homed host in which only one interface can have zeroconf services.

Exception in send causing thread to stop...

Just quickly dumping these here while I have the backtrace.

Exception in thread Thread-11:
Traceback (most recent call last):
File "C:\Software\Cura\python\lib\threading.py", line 552, in __bootstrap_inner
self.run()
File "C:\Software\Cura_WiFi_Dev\zeroconf\zeroconf.py", line 1061, in run
self.zc.send(out)
File "C:\Software\Cura_WiFi_Dev\zeroconf\zeroconf.py", line 1661, in send
bytes_sent = s.sendto(packet, 0, (addr, port))
error: [Errno 10065] Er is geprobeerd een socketbewerking uit te voeren op een onbereikbare host

Not sure what the implications are. But I got this exception somewhere today while running the python-zeroconf. (On windows, on bad-wifi)

Reaper cache removal needs lock?

Exception in thread Thread-29:
Traceback (most recent call last):
  File "/usr/lib/python3.4/threading.py", line 920, in _bootstrap_inner
    self.run()
  File "/usr/local/lib/python3.4/dist-packages/zeroconf.py", line 919, in run
    for record in self.zc.cache.entries():
  File "/usr/local/lib/python3.4/dist-packages/zeroconf.py", line 793, in entries
    return reduce(lambda a, b: a + b, self.cache.values())
RuntimeError: dictionary changed size during iteration

socket error 10022 on windows with multiple ip addresses on an interface

The following sample code throws an exception when running on a windows with multiple ip addresses on one ethernet interface.

img_26072016_001656

code

print('\n'.join(ZeroconfServiceTypes.find()))

exception

File "C:\Python27\lib\site-packages\zeroconf.py", line 1406, in find
local_zc = zc or Zeroconf()
File "C:\Python27\lib\site-packages\zeroconf.py", line 1523, in init
socket.inet_aton(_MDNS_ADDR) + socket.inet_aton(i))
File "C:\Python27\lib\socket.py", line 228, in meth
return getattr(self._sock,name)(*args)
socket.error: [Errno 10022] An invalid argument was supplied

The code runs without problems when I remove one of the ip addresses.
I found this bug accidentally and won't have any setups like that normally.
Hope this helps someone who also has this exception.

deque mutated during iteration

I'm seeing this error infrequently (17.7.dev):
It looks like this stops the service advertisement completely.

Exception in thread zeroconf-Engine:
Traceback (most recent call last):
  File "/usr/lib/python3.4/threading.py", line 920, in _bootstrap_inner
    self.run()
  File "/usr/local/lib/python3.4/dist-packages/zeroconf.py", line 1106, in run
    reader.handle_read(socket_)
  File "/usr/local/lib/python3.4/dist-packages/zeroconf.py", line 1164, in handle_read
    self.zc.handle_response(msg)
  File "/usr/local/lib/python3.4/dist-packages/zeroconf.py", line 1937, in handle_response
    self.update_record(now, record)
  File "/usr/local/lib/python3.4/dist-packages/zeroconf.py", line 1918, in update_record
    self.notify_all()
  File "/usr/local/lib/python3.4/dist-packages/zeroconf.py", line 1720, in notify_all
    self.condition.notify_all()
  File "/usr/lib/python3.4/threading.py", line 358, in notify_all
    self.notify(len(self._waiters))
  File "/usr/lib/python3.4/threading.py", line 341, in notify
    waiters_to_notify = _deque(_islice(all_waiters, n))
RuntimeError: deque mutated during iteration

[Errno 92] Protocol not available

File "/home/luckydonald/tools/py2.7.venv/local/lib/python2.7/site-packages/zeroconf.py", line 1339, in __init__
    self._listen_socket = new_socket()
  File "/home/luckydonald/tools/py2.7.venv/local/lib/python2.7/site-packages/zeroconf.py", line 1304, in new_socket
    s.setsockopt(socket.SOL_SOCKET, reuseport, 1)
  File "/usr/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 92] Protocol not available

I found pull request #26 related to that.

Possible error when checking length of service name

I’ve been using version 0.17.5 your zeroconf library – very helpful! When I upgraded to the 0.17.6 version, I received errors that the length of the service name was greater than 15 characters (zeroconf.py around line 223). However, in reading the mDNS spec (RFC 6762), mDNS increased the length of the name to 255 characters. Am I understanding this correctly?

Discover IPv6 services

I'm trying to discover IPv6-only and dual-stacked services and only the IPv4 address of the dual-stacked services are being discovered. Using avahi I can discover the IPv6 services and both addresses of dual-stacked services, and I can use those services on the IPv6 addresses. This happens with every service I've tried (_airplay._tcp, _ipp._tcp, _http._tcp, _ssh._tcp).

Ideally, I would like to be able to specify how to handle IPv6 services:

  • Do not show IPv6, only IPv4
  • Prefer IPv4, search for both (default)
  • Prefer IPv6, search for both
  • Do not show IPv4, only IPv6

When both exist, only one service should be returned according to the precedence desired.

OSError: [Errno 101] Network is unreachable when listening on *down* interfaces

I have multiple network interfaces (a bridge, subinterfaces on eth0, an ipv6 tunnel and openvpn). I assume this causes the following issue:

$ python3 test.py
Press enter to exit...

Exception in thread Thread-3:
Traceback (most recent call last):
  File "/usr/lib/python3.4/threading.py", line 920, in _bootstrap_inner
    self.run()
  File "/home/michael/.local/lib/python3.4/site-packages/zeroconf.py", line 1057, in run
    self.zc.send(out)
  File "/home/michael/.local/lib/python3.4/site-packages/zeroconf.py", line 1657, in send
    bytes_sent = s.sendto(packet, 0, (addr, port))
OSError: [Errno 101] Network is unreachable

When I specify my main IP address in ZeroConf(["192.168.1.254"]), it works fine.

I guess it should silently ignore interfaces that can't be used (because they have no IP maybe?) instead of stopping completly.

remove_service not working?

I'm running the example in the main page:

from six.moves import input
from zeroconf import ServiceBrowser, Zeroconf


class MyListener(object):

    def remove_service(self, zeroconf, type, name):
        print("Service %s removed" % (name,))

    def add_service(self, zeroconf, type, name):
        info = zeroconf.get_service_info(type, name)
        print("Service %s added, service info: %s" % (name, info))


zeroconf = Zeroconf()
listener = MyListener()
browser = ServiceBrowser(zeroconf, "_http._tcp.local.", listener)
try:
    input("Press enter to exit...\n\n")
finally:
    zeroconf.close()

I don't know really if the remove_service listener is not working or I'm doing something wrong.

I've a 'Nodemcu' device, I'm browsing in _arduino._tcp.local. so when I turn on the device I can see all the available information. But when I unplug the device, nothing happens.

The Service name removed text should appears inmediatle or should I wait?

How could I solve this issue?

I'm using Py 2.7

Discover arduino services

Hi, I'm new with mDNS, I've found this library but I'm not sure how to do what I want to do.

I need to list all the Arduino devices in my network, same as the official arduino IDE does. I've tried the examples, but none of them show me the arduino device, I know it's all fine with the device because when I open the IDE it shows me the device with it corresponding ip

Can this library do this?

Any help will be appreciated.

ServiceBrowser Exception in OSX if browser generates long packets

My network has ~135 devices in the type category I am trying to browse.

On OSX 10.11 (El Capitan) hosts, ServiceBrowser generates the following exception regularly because it appears to generate a very large (~11kb) single datagram by accumulating 'add_answer_at_time' responses and sending them all out as one event. I don't have specialist knowledge of the OS to know if this is exceeding some UDP size limit.

This error does not occur on Ubuntu 14.04 hosts.

I have another type category that contains ~75 devices and the exception never occurs while browsing that group (maximum packet length there is ~4305 bytes).

Normal single response packets are ~75 bytes.

Traceback (most recent call last):
  File "/Users/gbiddison/source/browser_test.py", line 100, in run
    super().run()
  File "//anaconda/envs/py34/lib/python3.4/site-packages/zeroconf.py", line 1060, in run
    self.zc.send(out)
  File "//anaconda/envs/py34/lib/python3.4/site-packages/zeroconf.py", line 1670, in send
    bytes_sent = s.sendto(packet, 0, (addr, port))
OSError: [Errno 40] Message too long

This prevents the browser from producing complete results .

In proof-of-concept code, I have eliminated this exception by grouping the responses by a threshold count value and will post the PR when I've cleaned it up.

remove service

Hello,

Did you receive "remove service" information when you unplug network or power of any device ?

politeness function called in ./manage shell

from zeroconf import ServiceBrowser, Zeroconf, ZeroconfServiceTypes

class ServiceListener(object):
    def remove_service(self, zeroconf, type, name):
       # job on remove

    def add_service(self, zeroconf, type, name):
       # job on add


def bonjour(zeroconf, service=None, listener=None):
    if service is None:
        service = "_http._tcp.local."
    if listener is None:
        listener = ServiceListener()
    print "bonjour %s" % service
    ServiceBrowser(zeroconf, service, listener)


def politeness():
    print "Politeness"
    zeroconf = Zeroconf()
    listener = ServiceListener()
    for service in ZeroconfServiceTypes.find():
       bonjour(zeroconf, service, listener)

Regards

tb from browser example

I sucked down a copy of examples/browser.py and did a pi

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/zeroconf.py", line 833, in run
    self.readers[socket_].handle_read()
  File "/usr/local/lib/python2.7/dist-packages/zeroconf.py", line 889, in handle_read
    msg = DNSIncoming(data)
  File "/usr/local/lib/python2.7/dist-packages/zeroconf.py", line 458, in __init__
    self.readOthers()
  File "/usr/local/lib/python2.7/dist-packages/zeroconf.py", line 505, in readOthers
    domain = self.readName()
  File "/usr/local/lib/python2.7/dist-packages/zeroconf.py", line 554, in readName
    length = byte_ord(self.data[off])
IndexError: string index out of range

I can't figure out how to get pdb to attach to it since it happens in an Engine thread, so I'll leave it to you to suggest a fix. Or more debugging steps - I'm good with whichever.

Travis build failing due to pinned flake8-import-order

This has started failing PRs.

Here's from a build that's the same as latest master:

$ make flake8
flake8 --max-line-length=110 examples *.py
Traceback (most recent call last):
  File "/home/travis/virtualenv/python2.7.9/bin/flake8", line 11, in <module>
    sys.exit(main())
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/flake8/main/cli.py", line 16, in main
    app.run(argv)
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/flake8/main/application.py", line 322, in run
    self._run(argv)
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/flake8/main/application.py", line 305, in _run
    self.initialize(argv)
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/flake8/main/application.py", line 296, in initialize
    self.register_plugin_options()
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/flake8/main/application.py", line 154, in register_plugin_options
    self.check_plugins.register_options(self.option_manager)
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/flake8/plugins/manager.py", line 488, in register_options
    list(self.manager.map(register_and_enable))
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/flake8/plugins/manager.py", line 284, in map
    yield func(self.plugins[name], *args, **kwargs)
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/flake8/plugins/manager.py", line 484, in register_and_enable
    call_register_options(plugin)
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/flake8/plugins/manager.py", line 380, in generated_function
    return method(optmanager, *args, **kwargs)
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/flake8/plugins/manager.py", line 230, in register_options
    add_options(optmanager)
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/flake8_import_order/flake8_linter.py", line 29, in add_options
    parser.config_options.append("application-import-names")
AttributeError: 'OptionManager' object has no attribute 'config_options'
make: *** [flake8] Error 1

https://travis-ci.org/hugovk/python-zeroconf/builds/179008513

flake8-import-order <0.6.0 doesn't work with flake8 3.0: PyCQA/flake8-import-order#79

Quick fix is to pin flake8. Proper fix is to unpin both, decide on a style and set that for flake8-import-order.

Bash on Windows, socket.error: [Errno 22] Invalid argument

Under Bash on Windows, when simply creating a zeroconf.Zeroconf() object, I see the following error:

Python 2.7.6 (default, Jun 22 2015, 17:58:13)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import zeroconf as z
>>> z.Zeroconf()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/nils/.local/lib/python2.7/site-packages/zeroconf.py", line 1513, in __init__
    self._listen_socket = new_socket()
  File "/home/nils/.local/lib/python2.7/site-packages/zeroconf.py", line 1472, in new_socket
    s.setsockopt(socket.SOL_SOCKET, reuseport, 1)
  File "/usr/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 22] Invalid argument

using zeroconf version 0.17.6

It seems to be linked to the use of SO_REUSEPORT [1] [2]

>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 22] Invalid argument

I don't know enough about all those socket options, but the following two options are thrown around in the stackoverflow answers above, and they both work:

>>> s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
>>> s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

Confusion about repeat broadcasts

I'm a bit confused by the examples, specifically where they start waiting.

If I start the browser.py example on one host on my LAN and then start registration.py on another, the output from browser.py shows the discovery of the Paul's Test Web Site._http._tcp.local. service as I expect (as well as a bunch of other services from devices on my LAN).

However, if I start registration.py first, wait 30 seconds or so, and then run browser.py, the output of browser.py shows a bunch of services from other devices on my network (Brother printer, RaspBMC on a RaspberryPi, etc.) but never shows the Paul's Test Web Site._http._tcp.local. service.

  1. With the logging I added (https://github.com/jantman/python-zeroconf/tree/more_logging) I see that browser.py is sending an outgoing request (Sending: DNSOutgoing( QUESTIONS: question[ptr,in,_http._tcp.local.]) (addr=224.0.0.251 port=5353)) which the other services on my LAN respond to, but registration.py doesn't seem to be. Is there something amiss in the example? After the initial call to register_service() it doesn't seem to be listening for or responding to queries (or maybe this is just a bug with threading and how I'm trying to do logging?)
  2. Shouldn't register_service() be rebroadcasting the DNS at regular intervals? Or is this something that I have to do manually?

ServiceBrowser IndexError in read_name

Exception while reading name from incoming packet.
Appears to be failure to bounds-check.

Exception in thread zeroconf-Engine:
Traceback (most recent call last):
  File "//anaconda/envs/py34/lib/python3.4/threading.py", line 911, in _bootstrap_inner
    self.run()
  File "/Users/gbiddison/source/sygo/tools/BeagleDiff/zeroconf.py", line 953, in run
    reader.handle_read(socket_)
  File "/Users/gbiddison/source/sygo/tools/BeagleDiff/zeroconf.py", line 990, in handle_read
    msg = DNSIncoming(data)
  File "/Users/gbiddison/source/sygo/tools/BeagleDiff/zeroconf.py", line 565, in __init__
    self.read_others()
  File "/Users/gbiddison/source/sygo/tools/BeagleDiff/zeroconf.py", line 622, in read_others
    domain, type_, class_, ttl, self.read_name())
  File "/Users/gbiddison/source/sygo/tools/BeagleDiff/zeroconf.py", line 667, in read_name
    length = indexbytes(self.data, off)
IndexError: index out of range

Proposed patch:

diff --git a/zeroconf.py b/zeroconf.py
index 789fc2d..e86fbdb 100644
--- a/zeroconf.py
+++ b/zeroconf.py
@@ -663,10 +663,10 @@ class DNSIncoming(object):
         next_ = -1
         first = off

-        while True:
+        while off < len(self.data):
             length = indexbytes(self.data, off)
             off += 1
-            if length == 0:
+            if length == 0 or off >= len(self.data):
                 break
             t = length & 0xC0
             if t == 0x00:

ImportError: cannot import name ServiceBrowser

Hi,

I am trying to run the code below:

from six.moves import input
from zeroconf import ServiceBrowser, Zeroconf


class MyListener(object):

    def remove_service(self, zeroconf, type, name):
        print("Service %s removed" % (name,))

    def add_service(self, zeroconf, type, name):
        info = zeroconf.get_service_info(type, name)
        print("Service %s added, service info: %s" % (name, info))


zeroconf = Zeroconf()
listener = MyListener()
browser = ServiceBrowser(zeroconf, "_http._tcp.local.", listener)
try:
    input("Press enter to exit...\n\n")
finally:
    zeroconf.close()

And getting the error below:

Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import zeroconf
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "zeroconf.py", line 2, in <module>
    from zeroconf import ServiceBrowser, Zeroconf
ImportError: cannot import name ServiceBrowser

connection is refused

I set up a service like this:

TYPE = "_motion._tcp.local."
NAME = "_yyyyXXXX." + TYPE
PORT = 4321

logging.basicConfig(level=logging.DEBUG)
logging.getLogger("zeroconf").setLevel(logging.DEBUG)

zeroconf = Zeroconf()

def reg_srv():
    info = ServiceInfo(TYPE,
            NAME,
            socket.inet_aton("127.0.0.1"), PORT, 0, 0,
            {"desc":"HOME Bonjour test"})


    zeroconf.register_service(info)

I use nc -l 4321 to receive connection.

But socket ( from a remote computer or phone ) reported connection refused.

When I turned to use dns-sd -R "XXX" "_motion._tcp" "local" 4321 it works well.

What am I missing ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.