Publishing Services over mDNS in C

For some time now I’ve been looking into mDNS-advertised services. My aim was to advertise a service using mDNS in a C program.

Typically, doing this is really easy: call the Bonjour or Avahi APIs, and the system-wide daemon will do the rest for you. The problem arises when there is no system-wide daemon such as mDNSResponder (if you’re on a Mac or Windows) or Avahi (for the other OSes). This usually happens when you’re on an embedded system, like a router that runs Linux.

One way to solve this would be to cross-compile Avahi or Apple’s mDNSResponder for your platform. I chose another alternative – embedding a simple mDNS responder into the C program. Of course this implementation needs to be as lightweight as possible, meaning little or no external dependencies.

I thought it would be trivial to read the specifications and implement the mDNS protocol, but it turned out to be quite a bit of work. A few days into writing my own implementation, I found mdnsd written by Daniel Pelleg. I almost killed myself.

So here, I present my implementation that works – tinysvcmdns. To publish a service, you need only make 4 calls:

  1. mdnsd_start() to start the main thread
  2. mdnsd_set_hostname() to set the hostname and IP address
  3. mdnsd_register_svc() to register a service and begin an announcement
  4. mdnsd_stop() when you no longer need to respond to mDNS queries

It may be buggy, and it doesn’t do name collision detections, but it works. I hope to improve upon it as I use it in my project(s). tinysvcmdns is open-source, under the GNU LGPL “modified” BSD license (Update 2012-03-26: changed the software license).

mdnsd pretty much has a similar offering, but it implements features like name collision detection.

If you have the need to publish services over mDNS very simply, I hope you can benefit from this post, and will not re-invent the wheel like I did.

This entry was posted in code.

23 comments on “Publishing Services over mDNS in C

  1. maniacbug says:

    Great stuff. I am going to see if I can make this work on Arduino.

  2. ma says:

    Hi Darell,

    I have just started studying the mDNS, I want to learn the mDNS. As name collision detection is missing in your code, I can implement name collision detection. This way i will learn the mDNS and also a missing feature will be added to your code. I am also interested in implementing other missing things if any.

    So can you kindly guide me?
    Any help will be greatly appreciated.

    Thanks.

  3. matthieu526 says:

    Hello,

    I’m just starting working on mDNS. I’m trying to write my own mDNS queries, but I don’t succeed… I downloaded the sources but I can’t use them, what do I wrong? May you help me?

    Thanks

    • darell tan says:

      After you have downloaded the sources, you should be able to compile them on a Linux machine. From there, you should be able to rewrite code in mdnsd.c to generate the queries you want.

  4. Jens Kristian Søgaard says:

    Hi Darell,

    Thanks for creating this software!

    I was thinking of using tinysvcmdns in my application, and have been testing for some time. I have one problem that I cannot seem to figure out.

    I run tinysvcmdns on the server which advertises the service. The clients are regular Windows desktops with Apple’s Bonjour.

    When I have 1 server running tinysvcmdns – everything works as expected.

    When I have 2 servers running tinysvcmdns – something is not right. The Windows clients some times discover server 1, sometimes server 2 and some times find both servers. I was expecting them to always find both servers.

    I call mdns_set_hostname() with a seperate hostname (“Server1” and “Server2”) and with their specific IP-addresses (“192.168.0.10” and “192.168.0.20”). Then I call mdns_register_svc() with “_myprotocol._tpc.local” and port “1234” (the same on both servers).

    As far as I have understood it, this should not lead to a name collision.

    I hope you can help me in figuring out why I cannot make it work!

    Thanks in advance!

    • Jens Kristian Søgaard says:

      Hi again,

      There was a typo in the comment I wrote. The service I register is ofcourse “_myprotocol._tcp.local” (i.e. tcp and not tpc).

      The hostnames I register are actually “Server1.local” and “Server2.local”.

      I’m using only IPv4, so no IPv6 involved.

      • darell tan says:

        Hi Jens,

        The call to mdnsd_register_svc, is the instance name different? In the following example, the instance name is “My Website”:

        mdnsd_register_svc(svr, "My Website", "_http._tcp.local", 8080, NULL, txt);

        The instance name has to be different, even though their host names are already different.

        • Jens Kristian Søgaard says:

          Yes, the instance names are different. The first part of the instance name is the same, but the last part is different. The first server has “Server number – 1” and the other has “Server number – 2” as the instance name.

      • darell tan says:

        Hi Jens, there was a bug with the announcement packet. I have already fixed it and uploaded the changes. Please let me know if you find any more problems. Thanks!

  5. Jens Kristian Søgaard says:

    Hi Darell,

    Thank you very much!

    It works much better with the new version!

    I have tried 100 times to do discovery from the Windows client with 2 Linux servers running tinysvcmdns. It worked like expected 99 times, and only 1 time did it show just one of the servers. I gather that could be some sort of Windows glitch 🙂

    I’m impressed you were able to spot the bug so quickly!

    • Jens Kristian Søgaard says:

      Hi again,

      I did some more testing. It seems that I don’t do any service discovery from the client PC for a little while (a few minutes) and then do a discovery, it will only find one of the two servers.

      If I do a discovery again a second later, it will find both servers – and it will do that again and again without any problems… until I wait for a few minutes, then the first time I try it will only find 1 server again.

      Weird!

      • darell tan says:

        Hi Jens, I’ve fixed the bugs. For future issues, please use the issue tracker on the Bitbucket repository to avoid filling up the comments here. Thanks!

  6. Badreddine says:

    Hi Darell,

    I want to make a mDNS client, is it possible to use tinysvcmdns on a client side (to stream music from iPhone).

    • darell tan says:

      Hi, it is possible but I’m not sure this is what you’re looking for. iOS already has a mDNS service running, so all you need to do is to call the Bonjour API or whatever it is. The actual streaming of music is also not done by mDNS.

  7. aguaviva says:

    Hi! Thanks for this great code!

    Quick question, what is this for?

    struct rr_entry *a2_e = NULL;
    a2_e = rr_create_a(create_nlabel(hostname), inet_addr(“192.168.0.31”));
    mdnsd_add_rr(svr, a2_e);

  8. Raj says:

    Hi Darell,
    Thanks for this awesome code. I want to integreat this code base to my rtos code base which uses lwIP. So in my RTOS there is no “pipe” api. Also if i use connect and accept api(as alternative of pipe), these are blocking calls. So can you have any input for this.

    Thanks in advance.
    Raj

    • darell tan says:

      Hi Raj, the purpose of the pipe is to allow the main program to communicate with the thread handling the network calls. There should be some form of “tasks” on the RTOS and some method(s) for the tasks to communicate, and those can be used instead. Such changes would require some rewriting of the code. Hope that helps.

      • Raj says:

        Hi Darell,
        Thanks a lot for your valuable response. Some query I want to ask, if possible please give a response. 1. What all enhancement needs to done to make perfect mdns module ( like you only mention name collision needs to fix..). 2. Is service advertisement happens within network only or we can enhance it to other network also. 3. How to test sanity of code.

        Once again thanks darell, this small code has helped me to understand mdns working concept very well.

        Thanks,
        Raj

  9. Raj says:

    Hi Darell.
    If I don’t want to use “SO_REUSEADDR” option, because I have read this one is ugly hack. So what will be alternative way to do this. I also don’t have IP_ADD_MEMBERSHIP support.
    Can you suggest something.

    Thanks in advance.
    Raj

    • darell tan says:

      Hi Raj, SO_REUSEADDR is not critical – you can leave it out and it should work fine. IP_ADD_MEMBERSHIP is a basically an option that instructs the OS to handle multicast traffic. If you are using some embedded or custom TCP stack, you will need to handle this yourself, or add support to the stack/OS for multicast traffic.

  10. Thivya Ashok Kumar says:

    Hi Darell,
    This is such an awesome piece of code. Thank you! I’m very new to mDNS and this made me understand a great deal. For a quick prototype, I’m using PIC32MZ EF Starter kit along with MPLAB Harmony. I use their advertisement API to advertise my service. For some reason, I couldn’t find my service on a Bonjour browser. However, when I hit reload services on the browser, I can see the queries coming in on my serial terminal and the stack responds. I’m so lost as to what I’m missing.

  11. Krishna says:

    Hi,

    I successfully compiled on arm gcc. Below are the details of environment.
    1. Linux : Petalinux
    2. Kernel : 4.4.0

    When we run “tinysvcmdns” from the command prompt setsockopt in the mdnsd.c file is returning -1 and causing the program to exit.
    I am trying to use 192.168.255.1 as the device ip address.

    Where am i going wrong in this?
    Is there any other configuration that I need to do make this setsockopt call successful.

    Regards,
    Krishna

Leave a reply to darell tan Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.