Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avahi dns_sd compatibility layer fails to run Browse callback

Background

I'm working on a cross-platform Zeroconf/Bonjour/DNS-SD library for Haskell, and figured my best bet would bet would be to target the dns_sd.h API. Under Linux, the implementation of this interface is provided by Avahi, which claims to support a subset of the Bonjour API.

Problem

As a sanity check for my library, I've written a small test program in C that just uses the bare bones of the API. It browses for any service on the network of type _http._tcp, prints a message as soon as it sees one, and then dies:

#include <dns_sd.h>
#include <stdio.h>
#include <stdlib.h>

void cb(DNSServiceRef sdRef,
        DNSServiceFlags flags,
        uint32_t interfaceIndex,
        DNSServiceErrorType errorCode,
        const char *serviceName,
        const char *regtype,
        const char *replyDomain,
        void *context) {
  printf("called!\n");
}

int main() {
  DNSServiceRef sd = malloc(sizeof(DNSServiceRef));
  const char *regtype = "_http._tcp";
  DNSServiceErrorType err1 = DNSServiceBrowse(&sd, 0, 0, regtype, NULL, &cb, NULL);
  printf("err1=%d\n", err1);
  DNSServiceErrorType err2 = DNSServiceProcessResult(sd);
  printf("err2=%d\n", err2);
  return 0;
}

On my Mac, this test program works fine in both C and the equivalent Haskell (it finds my printer; exciting!):

$ gcc test.c -o test
$ ./test
err1=0
called!
err2=0

But on my Linux machine, the program berates me before exiting without invoking the callback:

$ gcc test.c -o test -ldns_sd
$ ./test
*** WARNING *** The program 'test' uses the Apple Bonjour compatibility layer of Avahi.
*** WARNING *** Please fix your application to use the native API of Avahi!
*** WARNING *** For more information see <http://0pointer.de/avahi-compat?s=libdns_sd&e=test>
err1=0
err2=0

Questions

  1. Is the Avahi dns_sd compatibility layer still a suitable target for a cross-platform binding? Or is that warning message serious enough about using the native Avahi API that I should consider retargeting?
  2. What is the state of the art for cross-platform Zeroconf in C?
like image 309
acfoltzer Avatar asked Oct 11 '22 00:10

acfoltzer


1 Answers

By reason unknown to me, it works only with non-blocking calls. Below is the improved code. Socket from Avahi is set to a non-blocking mode and then select (3) is used to wait for available data. DNSServiceProcessResult(sd) has to be called each time there is data on the socket so it may have been pure luck that your example worked on other platforms.

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dns_sd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

static int set_nonblocking(int fd)
{
    int flags;
    /* If they have O_NONBLOCK, use the Posix way to do it */
#if defined(O_NONBLOCK)
    /* Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */
    if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
        flags = 0;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
    /* Otherwise, use the old way of doing it */
    flags = 1;
    return ioctl(fd, FIOBIO, &flags);
#endif
}     

void cb(DNSServiceRef sdRef,
        DNSServiceFlags flags,
        uint32_t interfaceIndex,
        DNSServiceErrorType errorCode,
        const char *serviceName,
        const char *regtype,
        const char *replyDomain,
        void *context) {
    printf("called %s %s!\n", serviceName, regtype);
}

int main() {
    DNSServiceRef sd = malloc(sizeof(DNSServiceRef));
    const char *regtype = "_http._tcp";
    DNSServiceErrorType err1 = DNSServiceBrowse(&sd, 0, 0, regtype, NULL, &cb, NULL);
    printf("err1=%d\n", err1);
    int socket = DNSServiceRefSockFD(sd);
    set_nonblocking(socket);

    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(socket, &read_fds);

    while(1) {
        if(select(socket+1, &read_fds, NULL, NULL, NULL)  < 0) {
            perror("select");  
        }
        DNSServiceErrorType err2 = DNSServiceProcessResult(sd);
        printf("err2=%d\n", err2);
        if(err2 != 0)
            return 2;
    }
    return 0;
}
like image 193
RushPL Avatar answered Oct 18 '22 01:10

RushPL