]> git.meshlink.io Git - catta/blobdiff - avahi-compat-howl/compat.c
fix avahi_netlink_new to allow multiple netlinks per process
[catta] / avahi-compat-howl / compat.c
index f5415bf103b4dc579d8bbf2bf94c6b0c0ada1a15..9b8b18c5b5246495643c08f494a756bf265dd8e6 100644 (file)
@@ -1,18 +1,16 @@
-/* $Id$ */
-
 /***
   This file is part of avahi.
+
   avahi is free software; you can redistribute it and/or modify it
   under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
+
   avahi is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
   Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
   License along with avahi; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 #include <avahi-common/domain.h>
 #include <avahi-common/simple-watch.h>
 #include <avahi-common/error.h>
+#include <avahi-common/llist.h>
+
 #include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+#include <avahi-client/lookup.h>
 
 #include "howl.h"
 #include "warn.h"
@@ -42,7 +44,8 @@
 enum {
     COMMAND_POLL = 'p',
     COMMAND_QUIT = 'q',
-    COMMAND_POLL_DONE = 'P'
+    COMMAND_POLL_DONE = 'P',
+    COMMAND_POLL_FAILED = 'F'
 };
 
 typedef enum {
@@ -53,14 +56,26 @@ typedef enum {
     OID_ENTRY_GROUP
 } oid_type;
 
+typedef struct service_data service_data;
+
 typedef struct oid_data {
     oid_type type;
     sw_opaque extra;
     sw_discovery discovery;
     void *object;
     sw_result (*reply)(void);
+    service_data *service_data;
 } oid_data;
 
+
+struct service_data {
+    char *name, *regtype, *domain, *host;
+    uint16_t port;
+    AvahiIfIndex interface;
+    AvahiStringList *txt;
+    AVAHI_LLIST_FIELDS(service_data, services);
+};
+
 struct _sw_discovery {
     int n_ref;
     AvahiSimplePoll *simple_poll;
@@ -70,18 +85,22 @@ struct _sw_discovery {
     sw_discovery_oid oid_index;
 
     int thread_fd, main_fd;
-    
+
     pthread_t thread;
     int thread_running;
 
-    pthread_mutex_t mutex;
+    pthread_mutex_t mutex, salt_mutex;
 
+    AVAHI_LLIST_HEAD(service_data, services);
 };
 
 #define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); }
 
 #define OID_GET_INDEX(data) ((sw_discovery_oid) (((data) - ((data)->discovery->oid_table))))
 
+static sw_discovery discovery_ref(sw_discovery self);
+static void discovery_unref(sw_discovery self);
+
 static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) {
     if (!s)
         return NULL;
@@ -100,7 +119,7 @@ static sw_result map_error(int error) {
     switch (error) {
         case AVAHI_OK:
             return SW_OKAY;
-            
+
         case AVAHI_ERR_NO_MEMORY:
             return SW_E_MEM;
     }
@@ -113,7 +132,7 @@ static int read_command(int fd) {
     char command;
 
     assert(fd >= 0);
-    
+
     if ((r = read(fd, &command, 1)) != 1) {
         fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
         return -1;
@@ -136,9 +155,9 @@ static int write_command(int fd, char reply) {
 static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) {
     sw_discovery self = userdata;
     int ret;
-    
+
     assert(self);
-    
+
     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
     ret = poll(ufds, nfds, timeout);
     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
@@ -152,7 +171,7 @@ static void * thread_func(void *data) {
 
     sigfillset(&mask);
     pthread_sigmask(SIG_BLOCK, &mask, NULL);
-    
+
     self->thread = pthread_self();
     self->thread_running = 1;
 
@@ -163,30 +182,40 @@ static void * thread_func(void *data) {
             break;
 
 /*         fprintf(stderr, "Command: %c\n", command); */
-        
+
         switch (command) {
 
-            case COMMAND_POLL:
+            case COMMAND_POLL: {
+                int ret;
 
                 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
-                
-                if (avahi_simple_poll_run(self->simple_poll) < 0) {
-                    fprintf(stderr, __FILE__": avahi_simple_poll_run() failed.\n");
-                    ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
+
+                for (;;) {
+                    errno = 0;
+
+                    if ((ret = avahi_simple_poll_run(self->simple_poll)) < 0) {
+
+                        if (errno == EINTR)
+                            continue;
+
+                        fprintf(stderr, __FILE__": avahi_simple_poll_run() failed: %s\n", strerror(errno));
+                    }
+
                     break;
                 }
 
                 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
-                
-                if (write_command(self->thread_fd, COMMAND_POLL_DONE) < 0)
+
+                if (write_command(self->thread_fd, ret < 0 ? COMMAND_POLL_FAILED : COMMAND_POLL_DONE) < 0)
                     break;
-                
+
                 break;
+            }
 
             case COMMAND_QUIT:
                 return NULL;
         }
-        
+
     }
 
     return NULL;
@@ -200,13 +229,13 @@ static int oid_alloc(sw_discovery self, oid_type type) {
 
         while (self->oid_index >= OID_MAX)
             self->oid_index -= OID_MAX;
-        
+
         if (self->oid_table[self->oid_index].type == OID_UNUSED) {
             self->oid_table[self->oid_index].type = type;
             self->oid_table[self->oid_index].discovery = self;
 
             assert(OID_GET_INDEX(&self->oid_table[self->oid_index]) == self->oid_index);
-            
+
             return self->oid_index ++;
         }
 
@@ -214,7 +243,7 @@ static int oid_alloc(sw_discovery self, oid_type type) {
     }
 
     /* No free entry found */
-    
+
     return (sw_discovery_oid) -1;
 }
 
@@ -229,6 +258,7 @@ static void oid_release(sw_discovery self, sw_discovery_oid oid) {
     self->oid_table[oid].reply = NULL;
     self->oid_table[oid].object = NULL;
     self->oid_table[oid].extra = NULL;
+    self->oid_table[oid].service_data = NULL;
 }
 
 static oid_data* oid_get(sw_discovery self, sw_discovery_oid oid) {
@@ -239,25 +269,86 @@ static oid_data* oid_get(sw_discovery self, sw_discovery_oid oid) {
 
     if (self->oid_table[oid].type == OID_UNUSED)
         return NULL;
-    
+
     return &self->oid_table[oid];
 }
 
+static service_data* service_data_new(sw_discovery self) {
+    service_data *sdata;
+
+    assert(self);
+
+    if (!(sdata = avahi_new0(service_data, 1)))
+        return NULL;
+
+    AVAHI_LLIST_PREPEND(service_data, services, self->services, sdata);
+
+    return sdata;
+
+}
+
+static void service_data_free(sw_discovery self, service_data* sdata) {
+    assert(self);
+    assert(sdata);
+
+    AVAHI_LLIST_REMOVE(service_data, services, self->services, sdata);
+
+    avahi_free(sdata->name);
+    avahi_free(sdata->regtype);
+    avahi_free(sdata->domain);
+    avahi_free(sdata->host);
+    avahi_string_list_free(sdata->txt);
+    avahi_free(sdata);
+}
+
+static void reg_client_callback(oid_data *data, AvahiClientState state);
+
+static void client_callback(AvahiClient *s, AvahiClientState state, void* userdata) {
+    sw_discovery self = userdata;
+    sw_discovery_oid oid;
+
+    assert(s);
+    assert(self);
+
+    discovery_ref(self);
+
+    for (oid = 0; oid < OID_MAX; oid++) {
+
+        switch (self->oid_table[oid].type) {
+
+            case OID_ENTRY_GROUP:
+                reg_client_callback(&self->oid_table[oid], state);
+                break;
+
+            case OID_DOMAIN_BROWSER:
+            case OID_SERVICE_BROWSER:
+                ((sw_discovery_browse_reply) self->oid_table[oid].reply)(self, oid, SW_DISCOVERY_BROWSE_INVALID, 0, NULL, NULL, NULL, self->oid_table[oid].extra);
+                break;
+
+            case OID_SERVICE_RESOLVER:
+            case OID_UNUSED:
+                ;
+        }
+    }
+
+    discovery_unref(self);
+}
+
 sw_result sw_discovery_init(sw_discovery * self) {
     int fd[2] = { -1, -1};
     sw_result result = SW_E_UNKNOWN;
     pthread_mutexattr_t mutex_attr;
     int error;
-    
+
     assert(self);
-    
+
     AVAHI_WARN_LINKAGE;
 
     *self = NULL;
 
     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0)
         goto fail;
-    
+
     if (!(*self = avahi_new(struct _sw_discovery, 1))) {
         result = SW_E_MEM;
         goto fail;
@@ -272,23 +363,26 @@ sw_result sw_discovery_init(sw_discovery * self) {
 
     memset((*self)->oid_table, 0, sizeof((*self)->oid_table));
     (*self)->oid_index = 0;
-    
+
     (*self)->thread_running = 0;
 
+    AVAHI_LLIST_HEAD_INIT(service_info, (*self)->services);
+
     ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr));
     pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
     ASSERT_SUCCESS(pthread_mutex_init(&(*self)->mutex, &mutex_attr));
+    ASSERT_SUCCESS(pthread_mutex_init(&(*self)->salt_mutex, &mutex_attr));
 
     if (!((*self)->simple_poll = avahi_simple_poll_new()))
         goto fail;
 
     avahi_simple_poll_set_func((*self)->simple_poll, poll_func, *self);
 
-    if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), NULL, *self, &error))) {
+    if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), 0, client_callback, *self, &error))) {
         result = map_error(error);
         goto fail;
     }
-    
+
     /* Start simple poll */
     if (avahi_simple_poll_prepare((*self)->simple_poll, -1) < 0)
         goto fail;
@@ -296,12 +390,12 @@ sw_result sw_discovery_init(sw_discovery * self) {
     /* Queue an initial POLL command for the thread */
     if (write_command((*self)->main_fd, COMMAND_POLL) < 0)
         goto fail;
-    
+
     if (pthread_create(&(*self)->thread, NULL, thread_func, *self) != 0)
         goto fail;
 
     (*self)->thread_running = 1;
-    
+
     return SW_OKAY;
 
 fail:
@@ -320,15 +414,15 @@ static int stop_thread(sw_discovery self) {
 
     if (write_command(self->main_fd, COMMAND_QUIT) < 0)
         return -1;
-    
+
     avahi_simple_poll_wakeup(self->simple_poll);
-    
+
     ASSERT_SUCCESS(pthread_join(self->thread, NULL));
     self->thread_running = 0;
     return 0;
 }
 
-static sw_discovery discover_ref(sw_discovery self) {
+static sw_discovery discovery_ref(sw_discovery self) {
     assert(self);
     assert(self->n_ref >= 1);
 
@@ -337,7 +431,7 @@ static sw_discovery discover_ref(sw_discovery self) {
     return self;
 }
 
-static void discover_unref(sw_discovery self) {
+static void discovery_unref(sw_discovery self) {
     assert(self);
     assert(self->n_ref >= 1);
 
@@ -359,24 +453,28 @@ static void discover_unref(sw_discovery self) {
         close(self->main_fd);
 
     ASSERT_SUCCESS(pthread_mutex_destroy(&self->mutex));
-    
+    ASSERT_SUCCESS(pthread_mutex_destroy(&self->salt_mutex));
+
+    while (self->services)
+        service_data_free(self, self->services);
+
     avahi_free(self);
 }
 
 sw_result sw_discovery_fina(sw_discovery self) {
     assert(self);
-    
+
     AVAHI_WARN_LINKAGE;
 
     stop_thread(self);
-    discover_unref(self);
-    
+    discovery_unref(self);
+
     return SW_OKAY;
 }
 
 sw_result sw_discovery_run(sw_discovery self) {
     assert(self);
-    
+
     AVAHI_WARN_LINKAGE;
 
     return sw_salt_run((sw_salt) self);
@@ -384,7 +482,7 @@ sw_result sw_discovery_run(sw_discovery self) {
 
 sw_result sw_discovery_stop_run(sw_discovery self) {
     assert(self);
-    
+
     AVAHI_WARN_LINKAGE;
 
     return sw_salt_stop_run((sw_salt) self);
@@ -392,7 +490,7 @@ sw_result sw_discovery_stop_run(sw_discovery self) {
 
 int sw_discovery_socket(sw_discovery self) {
     assert(self);
-    
+
     AVAHI_WARN_LINKAGE;
 
     return self->main_fd;
@@ -400,17 +498,17 @@ int sw_discovery_socket(sw_discovery self) {
 
 sw_result sw_discovery_read_socket(sw_discovery self) {
     sw_result result = SW_E_UNKNOWN;
-    
+
     assert(self);
 
-    discover_ref(self);
+    discovery_ref(self);
 
     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
-    
+
     /* Cleanup notification socket */
     if (read_command(self->main_fd) != COMMAND_POLL_DONE)
         goto finish;
-    
+
     if (avahi_simple_poll_dispatch(self->simple_poll) < 0)
         goto finish;
 
@@ -425,26 +523,26 @@ sw_result sw_discovery_read_socket(sw_discovery self) {
         /* Request the poll */
         if (write_command(self->main_fd, COMMAND_POLL) < 0)
             goto finish;
-    
+
     result = SW_OKAY;
-    
+
 finish:
 
     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
 
-    discover_unref(self);
-    
+    discovery_unref(self);
+
     return result;
 }
 
 sw_result sw_discovery_salt(sw_discovery self, sw_salt *salt) {
     assert(self);
     assert(salt);
-    
+
     AVAHI_WARN_LINKAGE;
 
     *salt = (sw_salt) self;
-    
+
     return SW_OKAY;
 }
 
@@ -457,42 +555,44 @@ sw_result sw_salt_step(sw_salt self, sw_uint32 * msec) {
 
     if (!((sw_discovery) self)->thread_running)
         return SW_E_UNKNOWN;
-    
+
     memset(&p, 0, sizeof(p));
     p.fd = ((sw_discovery) self)->main_fd;
     p.events = POLLIN;
 
     if ((r = poll(&p, 1, msec ? (int) *msec : -1)) < 0) {
-        
+
         /* Don't treat EINTR as error */
         if (errno == EINTR)
             return SW_OKAY;
-        
+
         return SW_E_UNKNOWN;
-        
+
     } else if (r == 0) {
-        
+
         /* Timeoout */
         return SW_OKAY;
 
     } else {
         /* Success */
-    
+
         if (p.revents != POLLIN)
             return SW_E_UNKNOWN;
 
         if ((result = sw_discovery_read_socket((sw_discovery) self)) != SW_OKAY)
             return result;
     }
-    
+
     return SW_OKAY;
 }
 
 sw_result sw_salt_run(sw_salt self) {
     sw_result ret;
-    
+
     AVAHI_WARN_LINKAGE;
-    
+
+    assert(self);
+
     for (;;)
         if ((ret = sw_salt_step(self, NULL)) != SW_OKAY)
             return ret;
@@ -501,12 +601,156 @@ sw_result sw_salt_run(sw_salt self) {
 sw_result sw_salt_stop_run(sw_salt self) {
     AVAHI_WARN_LINKAGE;
 
+    assert(self);
+
     if (stop_thread((sw_discovery) self) < 0)
         return SW_E_UNKNOWN;
 
     return SW_OKAY;
 }
 
+sw_result sw_salt_lock(sw_salt self) {
+    AVAHI_WARN_LINKAGE;
+
+    assert(self);
+    ASSERT_SUCCESS(pthread_mutex_lock(&((sw_discovery) self)->salt_mutex));
+
+    return SW_OKAY;
+}
+
+sw_result sw_salt_unlock(sw_salt self) {
+    assert(self);
+
+    AVAHI_WARN_LINKAGE;
+
+    ASSERT_SUCCESS(pthread_mutex_unlock(&((sw_discovery) self)->salt_mutex));
+
+    return SW_OKAY;
+}
+
+static void reg_report_status(oid_data *data, sw_discovery_publish_status status) {
+    sw_discovery_publish_reply reply;
+
+    assert(data);
+
+    reply = (sw_discovery_publish_reply) data->reply;
+
+    reply(data->discovery,
+          OID_GET_INDEX(data),
+          status,
+          data->extra);
+}
+
+static int reg_create_service(oid_data *data) {
+    int ret;
+    const char *real_type;
+
+    assert(data);
+
+    real_type = avahi_get_type_from_subtype(data->service_data->regtype);
+
+    if ((ret = avahi_entry_group_add_service_strlst(
+             data->object,
+             data->service_data->interface,
+             AVAHI_PROTO_INET,
+             0,
+             data->service_data->name,
+             real_type ? real_type : data->service_data->regtype,
+             data->service_data->domain,
+             data->service_data->host,
+             data->service_data->port,
+             data->service_data->txt)) < 0)
+        return ret;
+
+    if (real_type) {
+        /* Create a subtype entry */
+
+        if (avahi_entry_group_add_service_subtype(
+                data->object,
+                data->service_data->interface,
+                AVAHI_PROTO_INET,
+                0,
+                data->service_data->name,
+                real_type,
+                data->service_data->domain,
+                data->service_data->regtype) < 0)
+            return ret;
+
+    }
+
+    if ((ret = avahi_entry_group_commit(data->object)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static void reg_client_callback(oid_data *data, AvahiClientState state) {
+    assert(data);
+
+    /* We've not been setup completely */
+    if (!data->object)
+        return;
+
+    switch (state) {
+        case AVAHI_CLIENT_FAILURE:
+            reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
+            break;
+
+        case AVAHI_CLIENT_S_RUNNING: {
+            int ret;
+
+            /* Register the service */
+            if ((ret = reg_create_service(data)) < 0) {
+                reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
+                return;
+            }
+
+            break;
+        }
+
+        case AVAHI_CLIENT_S_COLLISION:
+        case AVAHI_CLIENT_S_REGISTERING:
+
+            /* Remove our entry */
+            avahi_entry_group_reset(data->object);
+            break;
+
+        case AVAHI_CLIENT_CONNECTING:
+            /* Ignore */
+            break;
+    }
+
+}
+
+static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
+    oid_data *data = userdata;
+
+    assert(g);
+    assert(data);
+
+    switch (state) {
+        case AVAHI_ENTRY_GROUP_ESTABLISHED:
+
+            reg_report_status(data, SW_DISCOVERY_PUBLISH_STARTED);
+            break;
+
+        case AVAHI_ENTRY_GROUP_COLLISION:
+
+            reg_report_status(data, SW_DISCOVERY_PUBLISH_NAME_COLLISION);
+            break;
+
+        case AVAHI_ENTRY_GROUP_REGISTERING:
+        case AVAHI_ENTRY_GROUP_UNCOMMITED:
+            /* Ignore */
+            break;
+
+        case AVAHI_ENTRY_GROUP_FAILURE:
+            reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
+            break;
+
+    }
+}
+
 sw_result sw_discovery_publish(
     sw_discovery self,
     sw_uint32 interface_index,
@@ -520,8 +764,117 @@ sw_result sw_discovery_publish(
     sw_discovery_publish_reply reply,
     sw_opaque extra,
     sw_discovery_oid * oid) {
-    AVAHI_WARN_UNSUPPORTED;
-    return SW_E_NO_IMPL;
+
+    oid_data *data;
+    sw_result result = SW_E_UNKNOWN;
+    service_data *sdata;
+    AvahiStringList *txt = NULL;
+
+    assert(self);
+    assert(name);
+    assert(type);
+    assert(reply);
+    assert(oid);
+
+    AVAHI_WARN_LINKAGE;
+
+    if (text_record && text_record_len > 0)
+        if (avahi_string_list_parse(text_record, text_record_len, &txt) < 0)
+            return SW_E_UNKNOWN;
+
+    if ((*oid = oid_alloc(self, OID_ENTRY_GROUP)) == (sw_discovery_oid) -1) {
+        avahi_string_list_free(txt);
+        return SW_E_UNKNOWN;
+    }
+
+    if (!(sdata = service_data_new(self))) {
+        avahi_string_list_free(txt);
+        oid_release(self, *oid);
+        return SW_E_MEM;
+    }
+
+    data = oid_get(self, *oid);
+    assert(data);
+    data->reply = (sw_result (*)(void)) reply;
+    data->extra = extra;
+    data->service_data = sdata;
+
+    sdata->interface = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
+    sdata->name = avahi_strdup(name);
+    sdata->regtype = type ? avahi_normalize_name_strdup(type) : NULL;
+    sdata->domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
+    sdata->host = host ? avahi_normalize_name_strdup(host) : NULL;
+    sdata->port = port;
+    sdata->txt = txt;
+
+    /* Some OOM checking would be cool here */
+
+    ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
+
+    if (!(data->object = avahi_entry_group_new(self->client, reg_entry_group_callback, data))) {
+        result = map_error(avahi_client_errno(self->client));
+        goto finish;
+    }
+
+    if (avahi_client_get_state(self->client) == AVAHI_CLIENT_S_RUNNING) {
+        int error;
+
+        if ((error = reg_create_service(data)) < 0) {
+            result = map_error(error);
+            goto finish;
+        }
+    }
+
+    result = SW_OKAY;
+
+finish:
+
+    ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
+
+    if (result != SW_OKAY)
+        if (*oid != (sw_discovery_oid) -1)
+            sw_discovery_cancel(self, *oid);
+
+    return result;
+}
+
+static void domain_browser_callback(
+    AvahiDomainBrowser *b,
+    AvahiIfIndex interface,
+    AVAHI_GCC_UNUSED AvahiProtocol protocol,
+    AvahiBrowserEvent event,
+    const char *domain,
+    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+    void *userdata) {
+
+    oid_data* data = userdata;
+    sw_discovery_browse_reply reply;
+    static char domain_fixed[AVAHI_DOMAIN_NAME_MAX];
+
+    assert(b);
+    assert(data);
+
+    reply = (sw_discovery_browse_reply) data->reply;
+
+    domain  = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
+
+    switch (event) {
+        case AVAHI_BROWSER_NEW:
+            reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_DOMAIN, interface, NULL, NULL, domain, data->extra);
+            break;
+
+        case AVAHI_BROWSER_REMOVE:
+            reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_DOMAIN, interface, NULL, NULL, domain, data->extra);
+            break;
+
+        case AVAHI_BROWSER_FAILURE:
+            reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, NULL, NULL, domain, data->extra);
+            break;
+
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+            break;
+    }
 }
 
 sw_result sw_discovery_browse_domains(
@@ -530,14 +883,51 @@ sw_result sw_discovery_browse_domains(
     sw_discovery_browse_reply reply,
     sw_opaque extra,
     sw_discovery_oid * oid) {
-    AVAHI_WARN_UNSUPPORTED;
-    return SW_E_NO_IMPL;
+
+    oid_data *data;
+    AvahiIfIndex ifindex;
+    sw_result result = SW_E_UNKNOWN;
+
+    assert(self);
+    assert(reply);
+    assert(oid);
+
+    AVAHI_WARN_LINKAGE;
+
+    if ((*oid = oid_alloc(self, OID_DOMAIN_BROWSER)) == (sw_discovery_oid) -1)
+        return SW_E_UNKNOWN;
+
+    data = oid_get(self, *oid);
+    assert(data);
+    data->reply = (sw_result (*)(void)) reply;
+    data->extra = extra;
+
+    ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
+
+    ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
+
+    if (!(data->object = avahi_domain_browser_new(self->client, ifindex, AVAHI_PROTO_INET, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browser_callback, data))) {
+        result = map_error(avahi_client_errno(self->client));
+        goto finish;
+    }
+
+    result = SW_OKAY;
+
+finish:
+
+    ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
+
+    if (result != SW_OKAY)
+        if (*oid != (sw_discovery_oid) -1)
+            sw_discovery_cancel(self, *oid);
+
+    return result;
 }
 
 static void service_resolver_callback(
     AvahiServiceResolver *r,
     AvahiIfIndex interface,
-    AvahiProtocol protocol,
+    AVAHI_GCC_UNUSED AvahiProtocol protocol,
     AvahiResolverEvent event,
     const char *name,
     const char *type,
@@ -546,12 +936,12 @@ static void service_resolver_callback(
     const AvahiAddress *a,
     uint16_t port,
     AvahiStringList *txt,
-    AvahiLookupResultFlags flags,
+    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
     void *userdata) {
 
     oid_data* data = userdata;
     sw_discovery_resolve_reply reply;
-    
+
     assert(r);
     assert(data);
 
@@ -568,7 +958,7 @@ static void service_resolver_callback(
             sw_ipv4_address_init_from_saddr(&addr, a->data.ipv4.address);
 
             host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed));
-            
+
             if ((p = avahi_new0(uint8_t, (l = avahi_string_list_serialize(txt, NULL, 0))+1)))
                 avahi_string_list_serialize(txt, p, l);
 
@@ -581,7 +971,7 @@ static void service_resolver_callback(
         case AVAHI_RESOLVER_FAILURE:
 
             /* Apparently there is no way in HOWL to inform about failed resolvings ... */
-            
+
             avahi_warn("A service failed to resolve in the HOWL compatiblity layer of Avahi which is used by '%s'. "
                        "Since the HOWL API doesn't offer any means to inform the application about this, we have to ignore the failure. "
                        "Please fix your application to use the native API of Avahi!",
@@ -604,13 +994,13 @@ sw_result sw_discovery_resolve(
     oid_data *data;
     AvahiIfIndex ifindex;
     sw_result result = SW_E_UNKNOWN;
-    
+
     assert(self);
     assert(name);
     assert(type);
     assert(reply);
     assert(oid);
-    
+
     AVAHI_WARN_LINKAGE;
 
     if ((*oid = oid_alloc(self, OID_SERVICE_RESOLVER)) == (sw_discovery_oid) -1)
@@ -620,25 +1010,25 @@ sw_result sw_discovery_resolve(
     assert(data);
     data->reply = (sw_result (*)(void)) reply;
     data->extra = extra;
-    
+
     ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
 
     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
-    
+
     if (!(data->object = avahi_service_resolver_new(self->client, ifindex, AVAHI_PROTO_INET, name, type, domain, AVAHI_PROTO_INET, 0, service_resolver_callback, data))) {
         result = map_error(avahi_client_errno(self->client));
         goto finish;
     }
 
     result = SW_OKAY;
-    
+
 finish:
 
     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
-    
+
     if (result != SW_OKAY)
         if (*oid != (sw_discovery_oid) -1)
-            oid_release(self, *oid);
+            sw_discovery_cancel(self, *oid);
 
     return result;
 }
@@ -646,18 +1036,18 @@ finish:
 static void service_browser_callback(
     AvahiServiceBrowser *b,
     AvahiIfIndex interface,
-    AvahiProtocol protocol,
+    AVAHI_GCC_UNUSED AvahiProtocol protocol,
     AvahiBrowserEvent event,
     const char *name,
     const char *type,
     const char *domain,
-    AvahiLookupResultFlags flags,
+    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
     void *userdata) {
 
     oid_data* data = userdata;
     char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
     sw_discovery_browse_reply reply;
-    
+
     assert(b);
     assert(data);
 
@@ -678,7 +1068,7 @@ static void service_browser_callback(
         case AVAHI_BROWSER_FAILURE:
             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, name, type, domain, data->extra);
             break;
-            
+
         case AVAHI_BROWSER_CACHE_EXHAUSTED:
         case AVAHI_BROWSER_ALL_FOR_NOW:
             break;
@@ -697,12 +1087,12 @@ sw_result sw_discovery_browse(
     oid_data *data;
     AvahiIfIndex ifindex;
     sw_result result = SW_E_UNKNOWN;
-    
+
     assert(self);
     assert(type);
     assert(reply);
     assert(oid);
-    
+
     AVAHI_WARN_LINKAGE;
 
     if ((*oid = oid_alloc(self, OID_SERVICE_BROWSER)) == (sw_discovery_oid) -1)
@@ -712,52 +1102,81 @@ sw_result sw_discovery_browse(
     assert(data);
     data->reply = (sw_result (*)(void)) reply;
     data->extra = extra;
-    
+
     ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
 
     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
-    
+
     if (!(data->object = avahi_service_browser_new(self->client, ifindex, AVAHI_PROTO_INET, type, domain, 0, service_browser_callback, data))) {
         result = map_error(avahi_client_errno(self->client));
         goto finish;
     }
 
     result = SW_OKAY;
-    
+
 finish:
 
     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
-    
+
     if (result != SW_OKAY)
         if (*oid != (sw_discovery_oid) -1)
-            oid_release(self, *oid);
+            sw_discovery_cancel(self, *oid);
 
     return result;
 }
 
 sw_result sw_discovery_cancel(sw_discovery self, sw_discovery_oid oid) {
     oid_data *data;
-    assert(self);                             
+    assert(self);
 
     AVAHI_WARN_LINKAGE;
 
     if (!(data = oid_get(self, oid)))
         return SW_E_UNKNOWN;
 
-    switch (data->type) {
-        case OID_SERVICE_BROWSER:
-            avahi_service_browser_free(data->object);
-            break;
+    if (data->object) {
+        switch (data->type) {
+            case OID_SERVICE_BROWSER:
+                avahi_service_browser_free(data->object);
+                break;
 
-        case OID_SERVICE_RESOLVER:
-            avahi_service_resolver_free(data->object);
-            break;
+            case OID_SERVICE_RESOLVER:
+                avahi_service_resolver_free(data->object);
+                break;
+
+            case OID_DOMAIN_BROWSER:
+                avahi_domain_browser_free(data->object);
+                break;
 
-        case OID_UNUSED:
+            case OID_ENTRY_GROUP:
+                avahi_entry_group_free(data->object);
+                break;
+
+            case OID_UNUSED:
             ;
+        }
+    }
+
+    if (data->service_data) {
+        assert(data->type == OID_ENTRY_GROUP);
+        service_data_free(self, data->service_data);
     }
 
     oid_release(self, oid);
-    
+
     return SW_OKAY;
 }
+
+sw_result sw_discovery_init_with_flags(
+    sw_discovery * self,
+    sw_discovery_init_flags flags) {
+
+    assert(self);
+
+    AVAHI_WARN_LINKAGE;
+
+    if (flags != SW_DISCOVERY_USE_SHARED_SERVICE)
+        return SW_E_NO_IMPL;
+
+    return sw_discovery_init(self);
+}