From 9c50429abc62e15251e5c6e97c88948f70949d02 Mon Sep 17 00:00:00 2001
From: Scott Shambarger <devel@shambarger.net>
Date: Tue, 17 Jan 2017 01:36:03 -0500
Subject: [PATCH] [dnssd] Reworked resolve to use timeout, fixed network order
 on port

---
 src/mdns_dnssd.c | 453 ++++++++++++++++++++++++++++-------------------
 1 file changed, 268 insertions(+), 185 deletions(-)

diff --git a/src/mdns_dnssd.c b/src/mdns_dnssd.c
index efb76743..2b2d5b41 100644
--- a/src/mdns_dnssd.c
+++ b/src/mdns_dnssd.c
@@ -1,5 +1,5 @@
 /*
- * Avahi mDNS backend, with libevent polling
+ * Bonjour mDNS backend, with libevent polling
  *
  * Copyright (c) Scott Shambarger <devel@shambarger.net>
  * Copyright (C) 2009-2011 Julien BLACHE <jb@jblache.org>
@@ -20,13 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-/*
- * NOTE: dns_sd.h indicates that MoreComing is unified on shared
- * connecdtions; this means that the triggers to free structs when
- * MoreComing is not set may not be called if another operation has
- * pending data... we probably need a way to prevent leaks (how do you
- * know if it's safe to free a context, after timeout with cancel??)
- */
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
@@ -47,6 +40,9 @@
 
 #include <dns_sd.h>
 
+/* timeout for service resolves */
+#define MDNS_RESOLVE_TIMEOUT_SECS 5
+
 // Hack for FreeBSD, don't want to bother with sysconf()
 #ifndef HOST_NAME_MAX
 # include <limits.h>
@@ -61,62 +57,72 @@ extern struct event_base *evbase_main;
 static DNSServiceRef mdns_sdref_main;
 static struct event *mdns_ev_main;
 
+/* registered services last the life of the program */
 struct mdns_service {
-  DNSServiceRef sdref;
-  char *name;
-  char *regtype;
-  TXTRecordRef txtRecord;
-  struct event *ev;
   struct mdns_service *next;
+  /* allocated */
+  DNSServiceRef sdref;
+  TXTRecordRef txtRecord;
 };
 
 static struct mdns_service *mdns_services = NULL;
 
+/* we keep records forever to display names in logs when
+   registered or renamed */
 struct mdns_record {
+  struct mdns_record *next;
+  /* allocated */
+  char *name;
+  /* references */
   DNSRecordRef recRef;
   uint16_t rrtype;
-  char *name;
-  struct mdns_record *next;
 };
 
 static struct mdns_record *mdns_records = NULL;
 
-/* Avahi client callbacks & helpers */
+struct mdns_addr_lookup {
+  struct mdns_addr_lookup *next;
+  /* allocated */
+  DNSServiceRef sdref;
+  struct keyval txt_kv;
+  /* references */
+  u_int16_t port;
+  struct mdns_resolver *rs;
+};
 
+/* resolvers and address lookups clean themselves up */
+struct mdns_resolver
+{
+  struct mdns_resolver *next;
+  /* allocated */
+  DNSServiceRef sdref;
+  char *service;
+  char *regtype;
+  char *domain;
+  struct event *timer;
+  struct mdns_addr_lookup *lookups;
+  /* references */
+  void *uuid;
+  uint32_t interface;
+  struct mdns_browser *mb;
+};
+
+/* browsers keep running for the life of the program */
 struct mdns_browser
 {
+  struct mdns_browser *next;
+  /* allocated */
   DNSServiceRef sdref;
+  struct mdns_resolver *resolvers;
   char *regtype;
+  /* references */
   mdns_browse_cb cb;
   DNSServiceProtocol protocol;
-  struct event *ev;
-  struct mdns_browser *next;
+  void *res_uuid;
 };
 
 static struct mdns_browser *mdns_browsers = NULL;
 
-struct mdns_resolver
-{
-  DNSServiceRef sdref;
-  char *name;
-  char *regtype;
-  char *domain;
-  struct mdns_browser *mb;
-  struct event *ev;
-  struct mdns_resolver *next;
-};
-
-struct mdns_addr_lookup {
-  DNSServiceRef sdref;
-  char *name;
-  char *regtype;
-  char *domain;
-  u_int16_t port;
-  struct keyval txt_kv;
-  struct mdns_browser *mb;
-  struct event *ev;
-};
-
 #define IPV4LL_NETWORK 0xA9FE0000
 #define IPV4LL_NETMASK 0xFFFF0000
 #define IPV6LL_NETWORK 0xFE80
@@ -143,26 +149,73 @@ mdns_service_free(struct mdns_service *s)
   if(! s)
     return -1;
 
-  /* free event, then sdref, then everything else */
-  event_free(s->ev);
-  DNSServiceRefDeallocate(s->sdref);
+  /* free sdref, then everything else */
+  if(s->sdref)
+    DNSServiceRefDeallocate(s->sdref);
   TXTRecordDeallocate(&(s->txtRecord));
-  free(s->name);
-  free(s->regtype);
   free(s);
 
   return -1;
 }
 
+static int
+mdns_addr_lookup_free(struct mdns_addr_lookup *lu)
+{
+  if (! lu)
+    return -1;
+
+  if(lu->sdref)
+    DNSServiceRefDeallocate(lu->sdref);
+  keyval_clear(&lu->txt_kv);
+  free(lu);
+
+  return -1;
+}
+
+static int
+mdns_resolver_free(struct mdns_resolver *rs) {
+
+  struct mdns_addr_lookup *lu;
+
+  if (! rs)
+    return -1;
+
+  /* free/cancel all lookups */
+  for(lu = rs->lookups; lu; lu = rs->lookups)
+    {
+      rs->lookups = lu->next;
+      mdns_addr_lookup_free(lu);
+    }
+
+  if(rs->timer)
+    event_free(rs->timer);
+  if(rs->sdref)
+    DNSServiceRefDeallocate(rs->sdref);
+  free(rs->service);
+  free(rs->regtype);
+  free(rs->domain);
+  free(rs);
+
+  return -1;
+}
+
 static int
 mdns_browser_free(struct mdns_browser *mb)
 {
+  struct mdns_resolver *rs;
+
   if(! mb)
     return -1;
 
-  /* free event, then sdref, then everything else */
-  event_free(mb->ev);
-  DNSServiceRefDeallocate(mb->sdref);
+  /* free all resolvers */
+  for(rs = mb->resolvers; rs; rs = mb->resolvers)
+    {
+      mb->resolvers = rs->next;
+      mdns_resolver_free(rs);
+    }
+  /* free sdref, then everything else */
+  if(mb->sdref)
+    DNSServiceRefDeallocate(mb->sdref);
   free(mb->regtype);
   free(mb);
 
@@ -206,9 +259,11 @@ mdns_main_free(void)
       mdns_record_free(r);
     }
 
-  event_free(mdns_ev_main);
+  if(mdns_ev_main)
+    event_free(mdns_ev_main);
   mdns_ev_main = NULL;
-  DNSServiceRefDeallocate(mdns_sdref_main);
+  if(mdns_sdref_main)
+    DNSServiceRefDeallocate(mdns_sdref_main);
   mdns_sdref_main = NULL;
 
   return -1;
@@ -222,30 +277,21 @@ mdns_deinit(void)
 
 static void
 mdns_event_cb(evutil_socket_t fd, short flags, void *data) {
-  DNSServiceProcessResult(*(DNSServiceRef *)data);
-}
 
-static int
-mdns_event_add(DNSServiceRef *psdref, struct event **pev)
-{
-  int fd;
+  DNSServiceErrorType err;
 
-  fd = DNSServiceRefSockFD(*psdref);
-  *pev = event_new(evbase_main, fd, EV_PERSIST | EV_READ, mdns_event_cb,
-                   psdref);
-  if (!*pev)
-    {
-      DPRINTF(E_LOG, L_MDNS, "Could not make new event in mdns\n");
-      return -1;
-    }
+  err = DNSServiceProcessResult(mdns_sdref_main);
 
-  return event_add(*pev, NULL);
+  if (err != kDNSServiceErr_NoError)
+    DPRINTF(E_LOG, L_MDNS, "DNSServiceProcessResult error %d\n", err);
 }
 
 int
 mdns_init(void)
 {
   DNSServiceErrorType err;
+  int fd;
+  int ret;
 
   DPRINTF(E_DBG, L_MDNS, "Initializing DNS_SD mDNS\n");
 
@@ -262,8 +308,25 @@ mdns_init(void)
       return -1;
     }
 
-  if (mdns_event_add(&mdns_sdref_main, &mdns_ev_main) < 0)
-    return mdns_main_free();
+  fd = DNSServiceRefSockFD(mdns_sdref_main);
+  if (fd == -1) {
+      DPRINTF(E_LOG, L_MDNS, "DNSServiceRefSockFD failed\n");
+      return mdns_main_free();
+  }
+  mdns_ev_main = event_new(evbase_main, fd, EV_PERSIST | EV_READ,
+                           mdns_event_cb, NULL);
+  if (! mdns_ev_main)
+    {
+      DPRINTF(E_LOG, L_MDNS, "Could not make new event in mdns\n");
+      return mdns_main_free();
+    }
+
+  ret = event_add(mdns_ev_main, NULL);
+  if (ret != 0)
+    {
+      DPRINTF(E_LOG, L_MDNS, "Could not add new event in mdns\n");
+      return mdns_main_free();
+    }
 
   return 0;
 }
@@ -276,11 +339,14 @@ mdns_register_callback(DNSServiceRef sdRef, DNSServiceFlags flags,
 
   switch (errorCode) {
     case kDNSServiceErr_NoError:
-      DPRINTF(E_DBG, L_MDNS, "Successfully added mDNS services\n");
+      DPRINTF(E_DBG, L_MDNS, "Successfully added mDNS service '%s.%s'\n",
+              name, regtype);
       break;
 
     case kDNSServiceErr_NameConflict:
-      DPRINTF(E_DBG, L_MDNS, "Name collision - automatically assigning new name\n");
+      DPRINTF(E_DBG, L_MDNS,
+              "Name collision for service '%s.%s' - automatically assigning new name\n",
+              name, regtype);
       break;
 
     case kDNSServiceErr_NoMemory:
@@ -301,7 +367,7 @@ mdns_register(char *name, char *regtype, int port, char **txt)
   int i;
   char *eq;
 
-  DPRINTF(E_DBG, L_MDNS, "Adding mDNS service %s/%s\n", name, regtype);
+  DPRINTF(E_DBG, L_MDNS, "Adding mDNS service '%s.%s'\n", name, regtype);
 
   s = calloc(1, sizeof(*s));
   if (!s)
@@ -309,19 +375,6 @@ mdns_register(char *name, char *regtype, int port, char **txt)
       DPRINTF(E_LOG, L_MDNS, "Out of memory registering service.\n");
       return -1;
     }
-  s->name = strdup(name);
-  if (!(s->name))
-    {
-      DPRINTF(E_LOG, L_MDNS, "Out of memory registering service.\n");
-      return mdns_service_free(s);
-    }
-
-  s->regtype = strdup(regtype);
-  if (!(s->regtype))
-    {
-      DPRINTF(E_LOG, L_MDNS, "Out of memory registering service.\n");
-      return mdns_service_free(s);
-    }
   TXTRecordCreate(&(s->txtRecord), 0, NULL);
 
   for (i = 0; txt && txt[i]; i++)
@@ -342,21 +395,19 @@ mdns_register(char *name, char *regtype, int port, char **txt)
 
   s->sdref = mdns_sdref_main;
   err = DNSServiceRegister(&(s->sdref), kDNSServiceFlagsShareConnection, 0,
-                           s->name, s->regtype, NULL, NULL, htons(port),
+                           name, regtype, NULL, NULL, htons(port),
                            TXTRecordGetLength(&(s->txtRecord)),
                            TXTRecordGetBytesPtr(&(s->txtRecord)),
                            mdns_register_callback, NULL);
 
   if (err != kDNSServiceErr_NoError)
     {
-      DPRINTF(E_LOG, L_MDNS, "Error registering service %s\n", name);
+      DPRINTF(E_LOG, L_MDNS, "Error registering service '%s.%s'\n",
+              name, regtype);
       s->sdref = NULL;
       return mdns_service_free(s);
     }
 
-  if (mdns_event_add(&s->sdref, &s->ev) < 0)
-    return mdns_service_free(s);
-
   s->next = mdns_services;
   mdns_services = s;
 
@@ -428,6 +479,7 @@ mdns_register_record(uint16_t rrtype, const char *name, uint16_t rdlen,
       return mdns_record_free(r);
     }
 
+  /* keep these around so we can display r->name in the callback */
   r->next = mdns_records;
   mdns_records = r;
 
@@ -479,24 +531,6 @@ mdns_cname(char *name)
                               rdata);
 }
 
-static int
-mdns_addr_lookup_free(struct mdns_addr_lookup *lu)
-{
-  if (! lu)
-    return -1;
-
-  /* free event, then sdref, then everything else */
-  event_free(lu->ev);
-  DNSServiceRefDeallocate(lu->sdref);
-  keyval_clear(&lu->txt_kv);
-  free(lu->domain);
-  free(lu->regtype);
-  free(lu->name);
-  free(lu);
-
-  return -1;
-}
-
 static void
 mdns_browse_call_cb(struct mdns_addr_lookup *lu, const char *hostname,
                     const struct sockaddr *address)
@@ -514,15 +548,17 @@ mdns_browse_call_cb(struct mdns_addr_lookup *lu, const char *hostname,
           return;
         }
 
-      if (!(lu->mb->protocol & kDNSServiceProtocol_IPv4))
+      if (!(lu->rs->mb->protocol & kDNSServiceProtocol_IPv4))
         {
-          DPRINTF(E_DBG, L_MDNS, "Discarding IPv4, not interested (service %s)\n",
-                  lu->name);
+          DPRINTF(E_DBG, L_MDNS,
+                  "Discarding IPv4, not interested (service %s)\n",
+                  lu->rs->service);
           return;
         }
       else if (is_v4ll(&addr->sin_addr))
         {
-          DPRINTF(E_WARN, L_MDNS, "Ignoring announcement from %s, address %s is link-local\n",
+          DPRINTF(E_WARN, L_MDNS,
+                  "Ignoring announcement from %s, address %s is link-local\n",
                   hostname, addr_str);
           return;
         }
@@ -539,26 +575,28 @@ mdns_browse_call_cb(struct mdns_addr_lookup *lu, const char *hostname,
           return;
         }
 
-      if (!(lu->mb->protocol & kDNSServiceProtocol_IPv6))
+      if (!(lu->rs->mb->protocol & kDNSServiceProtocol_IPv6))
         {
-          DPRINTF(E_DBG, L_MDNS, "Discarding IPv6, not interested (service %s)\n",
-                  lu->name);
+          DPRINTF(E_DBG, L_MDNS,
+                  "Discarding IPv6, not interested (service %s)\n",
+                  lu->rs->service);
           return;
         }
       else if (is_v6ll(&addr6->sin6_addr))
         {
-          DPRINTF(E_WARN, L_MDNS, "Ignoring announcement from %s, address %s is link-local\n",
+          DPRINTF(E_WARN, L_MDNS,
+                  "Ignoring announcement from %s, address %s is link-local\n",
                   hostname, addr_str);
           return;
         }
     }
 
   DPRINTF(E_DBG, L_MDNS, "Service %s, hostname %s resolved to %s\n",
-          lu->name, hostname, addr_str);
+          lu->rs->service, hostname, addr_str);
 
   /* Execute callback (mb->cb) with all the data */
-  lu->mb->cb(lu->name, lu->mb->regtype, lu->domain, hostname,
-             address->sa_family, addr_str, lu->port, &lu->txt_kv);
+  lu->rs->mb->cb(lu->rs->service, lu->rs->regtype, lu->rs->domain, hostname,
+                 address->sa_family, addr_str, lu->port, &lu->txt_kv);
 }
 
 static void
@@ -569,22 +607,17 @@ mdns_lookup_callback(DNSServiceRef sdRef, DNSServiceFlags flags,
 {
   struct mdns_addr_lookup *lu;
 
+  lu = context;
+
   if (errorCode != kDNSServiceErr_NoError )
     {
-      DPRINTF(E_LOG, L_MDNS, "Error resolving service address, error %d\n",
-              errorCode);
+      DPRINTF(E_LOG, L_MDNS, "Error resolving hostname '%s', error %d\n",
+              hostname, errorCode);
       return;
     }
 
-  lu = context;
-
   if (flags & kDNSServiceFlagsAdd)
-    mdns_browse_call_cb(lu, hostname, address);
-
-  /* If we are done with address lookups for this resolve,
-     terminate the address lookup */
-  if (!(flags & kDNSServiceFlagsMoreComing))
-    mdns_addr_lookup_free(lu);
+        mdns_browse_call_cb(lu, hostname, address);
 }
 
 static int
@@ -606,26 +639,8 @@ mdns_addr_lookup_start(struct mdns_resolver *rs, uint32_t interfaceIndex,
       DPRINTF(E_LOG, L_MDNS, "Out of memory creating address lookup.\n");
       return -1;
     }
-  lu->name = strdup(rs->name);
-  if (!lu->name)
-    {
-      DPRINTF(E_LOG, L_MDNS, "Out of memory creating address lookup.\n");
-      return mdns_addr_lookup_free(lu);
-    }
-  lu->regtype = strdup(rs->regtype);
-  if (!lu->regtype)
-    {
-      DPRINTF(E_LOG, L_MDNS, "Out of memory creating address lookup.\n");
-      return mdns_addr_lookup_free(lu);
-    }
-  lu->domain = strdup(rs->domain);
-  if (!lu->domain)
-    {
-      DPRINTF(E_LOG, L_MDNS, "Out of memory creating address lookup.\n");
-      return mdns_addr_lookup_free(lu);
-    }
   lu->port = port;
-  lu->mb = rs->mb;
+  lu->rs = rs;
 
   for (i=0; TXTRecordGetItemAtIndex(txtLen, txtRecord, i, sizeof(key),
                                     key, &valueLen, (const void **)&value)
@@ -650,26 +665,51 @@ mdns_addr_lookup_start(struct mdns_resolver *rs, uint32_t interfaceIndex,
       return mdns_addr_lookup_free(lu);
     }
 
-  if (mdns_event_add(&lu->sdref, &lu->ev) < 0)
-    return mdns_addr_lookup_free(lu);
+  /* resolver now owns the lookup */
+  lu->next = rs->lookups;
+  rs->lookups = lu;
 
   return 0;
 }
 
-static int
-mdns_resolver_free(struct mdns_resolver *rs) {
+static void
+mdns_resolver_remove(struct mdns_resolver *rs)
+{
+  struct mdns_resolver *cur;
 
-  if (! rs)
-    return -1;
+  /* remove from browser's resolver list */
+  if(rs->mb->resolvers == rs)
+    rs->mb->resolvers = rs->next;
+  else
+    {
+      for(cur = rs->mb->resolvers; cur; cur = cur->next)
+        if (cur->next == rs)
+          {
+            cur->next = rs->next;
+            break;
+          }
+    }
 
-  event_free(rs->ev);
-  DNSServiceRefDeallocate(rs->sdref);
-  free(rs->name);
-  free(rs->regtype);
-  free(rs->domain);
-  free(rs);
+  /* free resolver (which cancels resolve) */
+  mdns_resolver_free(rs);
+}
 
-  return -1;
+static void
+mdns_resolve_timeout_cb(evutil_socket_t fd, short flags, void *uuid) {
+
+  struct mdns_browser *mb;
+  struct mdns_resolver *rs = NULL;
+
+  for(mb = mdns_browsers; mb && !rs; mb = mb->next)
+    for(rs = mb->resolvers; rs; rs = rs->next)
+      if(rs->uuid == uuid)
+        {
+          DPRINTF(E_DBG, L_MDNS,
+                  "Resolve finished for '%s' type '%s' interface %d\n",
+                  rs->service, rs->regtype, rs->interface);
+          mdns_resolver_remove(rs);
+          break;
+        }
 }
 
 static void
@@ -677,26 +717,28 @@ mdns_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags,
                       uint32_t interfaceIndex, DNSServiceErrorType errorCode,
                       const char *fullname, const char *hosttarget,
                       uint16_t port, uint16_t txtLen,
-                      const unsigned char *txtRecord, void *context )
+                      const unsigned char *txtRecord, void *context)
 {
   struct mdns_resolver *rs;
 
-  if (errorCode != kDNSServiceErr_NoError )
-    {
-      DPRINTF(E_LOG, L_MDNS, "Error resolving mdns service, error %d\n",
-              errorCode);
-      return;
-    }
-
   rs = context;
 
-  mdns_addr_lookup_start(rs, interfaceIndex, hosttarget, port,
-                         txtLen, txtRecord);
+  /* convert port to host order */
+  port = ntohs(port);
 
-  /* If we are done resolving this service, terminate the resolve
-     and free the resolver resources */
-  if (!(flags & kDNSServiceFlagsMoreComing))
-    mdns_resolver_free(rs);
+  if (errorCode != kDNSServiceErr_NoError)
+    {
+      DPRINTF(E_LOG, L_MDNS, "Error resolving service '%s', error %d\n",
+              rs->service, errorCode);
+    }
+  else
+    {
+      DPRINTF(E_DBG, L_MDNS, "Bonjour resolved '%s' as '%s:%u' on interface %d\n",
+              fullname, hosttarget, port, interfaceIndex);
+
+      mdns_addr_lookup_start(rs, interfaceIndex, hosttarget, port,
+                             txtLen, txtRecord);
+    }
 }
 
 static int
@@ -706,6 +748,7 @@ mdns_resolve_start(struct mdns_browser *mb, uint32_t interfaceIndex,
 {
   DNSServiceErrorType err;
   struct mdns_resolver *rs;
+  struct timeval tv;
 
   rs = calloc(1, sizeof(*rs));
   if (!rs)
@@ -714,8 +757,8 @@ mdns_resolve_start(struct mdns_browser *mb, uint32_t interfaceIndex,
       return -1;
     }
 
-  rs->name = strdup(serviceName);
-  if (!rs->name)
+  rs->service = strdup(serviceName);
+  if (!rs->service)
     {
       DPRINTF(E_LOG, L_MDNS, "Out of memory creating service resolver.\n");
       return mdns_resolver_free(rs);
@@ -727,12 +770,22 @@ mdns_resolve_start(struct mdns_browser *mb, uint32_t interfaceIndex,
       return mdns_resolver_free(rs);
     }
   rs->domain = strdup(replyDomain);
-  if (!rs->name)
+  if (!rs->domain)
     {
       DPRINTF(E_LOG, L_MDNS, "Out of memory creating service resolver.\n");
       return mdns_resolver_free(rs);
     }
   rs->mb = mb;
+  rs->interface = interfaceIndex;
+  /* create a timer with a uuid, so we can search for resolver without
+     leaking */
+  rs->uuid = ++(mb->res_uuid);
+  rs->timer = evtimer_new(evbase_main, mdns_resolve_timeout_cb, rs->uuid);
+  if(! rs->timer)
+    {
+      DPRINTF(E_LOG, L_MDNS, "Out of memory creating service resolver timer.\n");
+      return mdns_resolver_free(rs);
+    }
 
   rs->sdref = mdns_sdref_main;
   err = DNSServiceResolve(&(rs->sdref), kDNSServiceFlagsShareConnection,
@@ -745,23 +798,54 @@ mdns_resolve_start(struct mdns_browser *mb, uint32_t interfaceIndex,
       return mdns_resolver_free(rs);
     }
 
-  if (mdns_event_add(&rs->sdref, &rs->ev) < 0)
-    return mdns_resolver_free(rs);
+  /* add to browser's resolvers */
+  rs->next = mb->resolvers;
+  mb->resolvers = rs;
+
+  /* setup a timeout to cancel the resolve */
+  tv.tv_sec = MDNS_RESOLVE_TIMEOUT_SECS;
+  tv.tv_usec = 0;
+  evtimer_add(rs->timer, &tv);
 
   return 0;
 }
 
+static void
+mdns_resolve_cancel(const struct mdns_browser *mb, uint32_t interfaceIndex,
+                    const char *serviceName, const char *regtype,
+                    const char *replyDomain) {
+
+  struct mdns_resolver *rs;
+
+  for(rs = mb->resolvers; rs; rs = rs->next)
+    {
+      if ((rs->interface == interfaceIndex)
+          && (! strcasecmp(rs->service, serviceName))
+          && (! strcmp(rs->regtype, regtype))
+          && (! strcasecmp(rs->domain, replyDomain)))
+        {
+          /* remove from resolvers, and free (which cancels resolve) */
+          DPRINTF(E_DBG, L_MDNS, "Cancelling resolve for '%s'\n", rs->service);
+          mdns_resolver_remove(rs);
+          break;
+        }
+    }
+
+  return;
+}
+
 static void
 mdns_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags,
                      uint32_t interfaceIndex, DNSServiceErrorType errorCode,
                      const char *serviceName, const char *regtype,
-                     const char *replyDomain, void *context )
+                     const char *replyDomain, void *context)
 {
   struct mdns_browser *mb;
 
   if (errorCode != kDNSServiceErr_NoError)
     {
-      DPRINTF(E_LOG, L_MDNS, "DNS-SD browsing error %d\n", errorCode);
+      // FIXME: if d/c, we sould recreate the browser?
+      DPRINTF(E_LOG, L_MDNS, "Bonjour browsing error %d\n", errorCode);
       return;
     }
 
@@ -770,7 +854,7 @@ mdns_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags,
   if (flags & kDNSServiceFlagsAdd)
     {
       DPRINTF(E_DBG, L_MDNS,
-              "DNS-SD Browser: NEW service '%s' type '%s' interface %d\n",
+              "Bonjour Browser: NEW service '%s' type '%s' interface %d\n",
               serviceName, regtype, interfaceIndex);
       mdns_resolve_start(mb, interfaceIndex, serviceName, regtype,
                          replyDomain);
@@ -778,8 +862,10 @@ mdns_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags,
   else
     {
       DPRINTF(E_DBG, L_MDNS,
-              "Avahi Browser: REMOVE service '%s' type '%s' interface %d\n",
+              "Bonjour Browser: REMOVE service '%s' type '%s' interface %d\n",
               serviceName, regtype, interfaceIndex);
+      mdns_resolve_cancel(mb, interfaceIndex, serviceName, regtype,
+                          replyDomain);
       mb->cb(serviceName, regtype, replyDomain, NULL, 0, NULL, -1, NULL);
     }
 }
@@ -833,9 +919,6 @@ mdns_browse(char *regtype, int family, mdns_browse_cb cb)
       return mdns_browser_free(mb);
     }
 
-  if (mdns_event_add(&mb->sdref, &mb->ev) < 0)
-    return mdns_browser_free(mb);
-
   mb->next = mdns_browsers;
   mdns_browsers = mb;