La capa de compatibilidad de Avahi DNS_SD no puede ejecutar la devolución de llamada de navegación

StackOverflow https://stackoverflow.com/questions/7391079

Pregunta

Fondo

Estoy trabajando en una biblioteca de Zeroconf/Bonjour/DNS-SD multiplataforma para Haskell, y pensé que mi mejor apuesta sería apuntar a la dns_sd.h API. En Linux, la implementación de esta interfaz es proporcionada por Avahi, que afirma apoyar un subconjunto de la API de Bonjour.

Problema

Como verificación de cordura para mi biblioteca, he escrito un pequeño programa de prueba en C que solo usa los huesos de la API. Explora cualquier servicio en la red de tipo _http._tcp, imprime un mensaje tan pronto como lo ve, y luego muere:

#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;
}

En mi Mac, este programa de prueba funciona bien tanto en C como en el equivalente Haskell (¡encuentra mi impresora; emocionante!):

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

Pero en mi máquina Linux, el programa me regaña antes de salir sin invocar la devolución de llamada:

$ 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

Preguntas

  1. Es el avahi dns_sd ¿La capa de compatibilidad sigue siendo un objetivo adecuado para una unión multiplataforma? ¿O es ese mensaje de advertencia lo suficientemente grave sobre el uso de la API de Avahi nativa que debería considerar el reorganización?
  2. ¿Cuál es el estado del arte para Zeroconf multiplataforma en C?
¿Fue útil?

Solución

Por razones desconocidas para mí, solo funciona con llamadas que no son de bloqueo. A continuación se muestra el código mejorado. El socket de Avahi está configurado en un modo no bloqueado y luego select (3) se usa para esperar los datos disponibles. DNSServiceProcessResult(sd) tiene que llamarse cada vez que hay datos en el socket, por lo que puede haber sido pura suerte que su ejemplo haya funcionado en otras plataformas.

#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;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top