Radix cross Linux

The main Radix cross Linux repository contains the build scripts of packages, which have the most complete and common functionality for desktop machines

452 Commits   2 Branches   1 Tag
Index: avahi-core/browse-dns-server.c
===================================================================
--- avahi-core/browse-dns-server.c	(nonexistent)
+++ avahi-core/browse-dns-server.c	(revision 331)
@@ -0,0 +1,352 @@
+/***
+  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
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+#include "rr.h"
+
+typedef struct AvahiDNSServerInfo AvahiDNSServerInfo;
+
+struct AvahiDNSServerInfo {
+    AvahiSDNSServerBrowser *browser;
+
+    AvahiIfIndex interface;
+    AvahiProtocol protocol;
+    AvahiRecord *srv_record;
+    AvahiSHostNameResolver *host_name_resolver;
+    AvahiAddress address;
+    AvahiLookupResultFlags flags;
+
+    AVAHI_LLIST_FIELDS(AvahiDNSServerInfo, info);
+};
+
+struct AvahiSDNSServerBrowser {
+    AvahiServer *server;
+
+    AvahiSRecordBrowser *record_browser;
+    AvahiSDNSServerBrowserCallback callback;
+    void* userdata;
+    AvahiProtocol aprotocol;
+    AvahiLookupFlags user_flags;
+
+    unsigned n_info;
+
+    AVAHI_LLIST_FIELDS(AvahiSDNSServerBrowser, browser);
+    AVAHI_LLIST_HEAD(AvahiDNSServerInfo, info);
+};
+
+static AvahiDNSServerInfo* get_server_info(AvahiSDNSServerBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r) {
+    AvahiDNSServerInfo *i;
+
+    assert(b);
+    assert(r);
+
+    for (i = b->info; i; i = i->info_next)
+        if (i->interface == interface &&
+            i->protocol == protocol &&
+            avahi_record_equal_no_ttl(r, i->srv_record))
+            return i;
+
+    return NULL;
+}
+
+static void server_info_free(AvahiSDNSServerBrowser *b, AvahiDNSServerInfo *i) {
+    assert(b);
+    assert(i);
+
+    avahi_record_unref(i->srv_record);
+    if (i->host_name_resolver)
+        avahi_s_host_name_resolver_free(i->host_name_resolver);
+
+    AVAHI_LLIST_REMOVE(AvahiDNSServerInfo, info, b->info, i);
+
+    assert(b->n_info >= 1);
+    b->n_info--;
+
+    avahi_free(i);
+}
+
+static void host_name_resolver_callback(
+    AvahiSHostNameResolver *r,
+    AVAHI_GCC_UNUSED AvahiIfIndex interface,
+    AVAHI_GCC_UNUSED AvahiProtocol protocol,
+    AvahiResolverEvent event,
+    const char *host_name,
+    const AvahiAddress *a,
+    AvahiLookupResultFlags flags,
+    void* userdata) {
+
+    AvahiDNSServerInfo *i = userdata;
+
+    assert(r);
+    assert(host_name);
+    assert(i);
+
+    switch (event) {
+        case AVAHI_RESOLVER_FOUND: {
+            i->address = *a;
+
+            i->browser->callback(
+                i->browser,
+                i->interface,
+                i->protocol,
+                AVAHI_BROWSER_NEW,
+                i->srv_record->data.srv.name,
+                &i->address,
+                i->srv_record->data.srv.port,
+                i->flags | flags,
+                i->browser->userdata);
+
+            break;
+        }
+
+        case AVAHI_RESOLVER_FAILURE:
+            /* Ignore */
+            break;
+    }
+
+    avahi_s_host_name_resolver_free(i->host_name_resolver);
+    i->host_name_resolver = NULL;
+}
+
+static void record_browser_callback(
+    AvahiSRecordBrowser*rr,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiBrowserEvent event,
+    AvahiRecord *record,
+    AvahiLookupResultFlags flags,
+    void* userdata) {
+
+    AvahiSDNSServerBrowser *b = userdata;
+
+    assert(rr);
+    assert(b);
+
+    /* Filter flags */
+    flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
+
+    switch (event) {
+        case AVAHI_BROWSER_NEW: {
+            AvahiDNSServerInfo *i;
+
+            assert(record);
+            assert(record->key->type == AVAHI_DNS_TYPE_SRV);
+
+            if (get_server_info(b, interface, protocol, record))
+                return;
+
+            if (b->n_info >= 10)
+                return;
+
+            if (!(i = avahi_new(AvahiDNSServerInfo, 1)))
+                return; /* OOM */
+
+            i->browser = b;
+            i->interface = interface;
+            i->protocol = protocol;
+            i->srv_record = avahi_record_ref(record);
+            i->host_name_resolver = avahi_s_host_name_resolver_prepare(
+                b->server,
+                interface, protocol,
+                record->data.srv.name,
+                b->aprotocol,
+                b->user_flags,
+                host_name_resolver_callback, i);
+            i->flags = flags;
+
+            if(i->host_name_resolver)
+                avahi_s_host_name_resolver_start(i->host_name_resolver);
+
+            AVAHI_LLIST_PREPEND(AvahiDNSServerInfo, info, b->info, i);
+
+            b->n_info++;
+            break;
+        }
+
+        case AVAHI_BROWSER_REMOVE: {
+            AvahiDNSServerInfo *i;
+
+            assert(record);
+            assert(record->key->type == AVAHI_DNS_TYPE_SRV);
+
+            if (!(i = get_server_info(b, interface, protocol, record)))
+                return;
+
+            if (!i->host_name_resolver)
+                b->callback(
+                    b,
+                    interface,
+                    protocol,
+                    event,
+                    i->srv_record->data.srv.name,
+                    &i->address,
+                    i->srv_record->data.srv.port,
+                    i->flags | flags,
+                    b->userdata);
+
+            server_info_free(b, i);
+            break;
+        }
+
+        case AVAHI_BROWSER_FAILURE:
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+
+            b->callback(
+                b,
+                interface,
+                protocol,
+                event,
+                NULL,
+                NULL,
+                0,
+                flags,
+                b->userdata);
+
+            break;
+    }
+}
+
+AvahiSDNSServerBrowser *avahi_s_dns_server_browser_prepare(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *domain,
+    AvahiDNSServerType type,
+    AvahiProtocol aprotocol,
+    AvahiLookupFlags flags,
+    AvahiSDNSServerBrowserCallback callback,
+    void* userdata) {
+
+    static const char * const type_table[AVAHI_DNS_SERVER_MAX] = {
+        "_domain._udp",
+        "_dns-update._udp"
+    };
+
+    AvahiSDNSServerBrowser *b;
+    AvahiKey *k = NULL;
+    char n[AVAHI_DOMAIN_NAME_MAX];
+    int r;
+
+    assert(server);
+    assert(callback);
+
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DNS_SERVER_MAX, AVAHI_ERR_INVALID_FLAGS);
+
+    if (!domain)
+        domain = server->domain_name;
+
+    if ((r = avahi_service_name_join(n, sizeof(n), NULL, type_table[type], domain)) < 0) {
+        avahi_server_set_errno(server, r);
+        return NULL;
+    }
+
+    if (!(b = avahi_new(AvahiSDNSServerBrowser, 1))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        return NULL;
+    }
+
+    b->server = server;
+    b->callback = callback;
+    b->userdata = userdata;
+    b->aprotocol = aprotocol;
+    b->n_info = 0;
+    b->user_flags = flags;
+
+    AVAHI_LLIST_HEAD_INIT(AvahiDNSServerInfo, b->info);
+    AVAHI_LLIST_PREPEND(AvahiSDNSServerBrowser, browser, server->dns_server_browsers, b);
+
+    if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        goto fail;
+    }
+
+    if (!(b->record_browser = avahi_s_record_browser_prepare(server, interface, protocol, k, flags, record_browser_callback, b)))
+        goto fail;
+
+    avahi_key_unref(k);
+
+    return b;
+
+fail:
+
+    if (k)
+        avahi_key_unref(k);
+
+    avahi_s_dns_server_browser_free(b);
+    return NULL;
+}
+
+void avahi_s_dns_server_browser_start(AvahiSDNSServerBrowser *b) {
+    assert(b);
+
+    if(b->record_browser)
+        avahi_s_record_browser_start_query(b->record_browser);
+}
+
+void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b) {
+    assert(b);
+
+    while (b->info)
+        server_info_free(b, b->info);
+
+    AVAHI_LLIST_REMOVE(AvahiSDNSServerBrowser, browser, b->server->dns_server_browsers, b);
+
+    if (b->record_browser)
+        avahi_s_record_browser_free(b->record_browser);
+
+    avahi_free(b);
+}
+
+AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *domain,
+    AvahiDNSServerType type,
+    AvahiProtocol aprotocol,
+    AvahiLookupFlags flags,
+    AvahiSDNSServerBrowserCallback callback,
+    void* userdata) {
+        AvahiSDNSServerBrowser* b;
+
+        b = avahi_s_dns_server_browser_prepare(server, interface, protocol, domain, type, aprotocol, flags, callback, userdata);
+        if (!b)
+            return NULL;
+
+        avahi_s_dns_server_browser_start(b);
+
+        return b;
+}
Index: avahi-core/browse-domain.c
===================================================================
--- avahi-core/browse-domain.c	(nonexistent)
+++ avahi-core/browse-domain.c	(revision 331)
@@ -0,0 +1,262 @@
+/***
+  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
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+
+struct AvahiSDomainBrowser {
+    int ref;
+
+    AvahiServer *server;
+
+    AvahiSRecordBrowser *record_browser;
+
+    AvahiDomainBrowserType type;
+    AvahiSDomainBrowserCallback callback;
+    void* userdata;
+
+    AvahiTimeEvent *defer_event;
+
+    int all_for_now_scheduled;
+
+    AVAHI_LLIST_FIELDS(AvahiSDomainBrowser, browser);
+};
+
+static void inc_ref(AvahiSDomainBrowser *b) {
+    assert(b);
+    assert(b->ref >= 1);
+
+    b->ref++;
+}
+
+static void record_browser_callback(
+    AvahiSRecordBrowser*rr,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiBrowserEvent event,
+    AvahiRecord *record,
+    AvahiLookupResultFlags flags,
+    void* userdata) {
+
+    AvahiSDomainBrowser *b = userdata;
+    char *n = NULL;
+
+    assert(rr);
+    assert(b);
+
+    if (event == AVAHI_BROWSER_ALL_FOR_NOW &&
+        b->defer_event) {
+
+        b->all_for_now_scheduled = 1;
+        return;
+    }
+
+    /* Filter flags */
+    flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
+
+    if (record) {
+        assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+        n = record->data.ptr.name;
+
+        if (b->type == AVAHI_DOMAIN_BROWSER_BROWSE) {
+            AvahiStringList *l;
+
+            /* Filter out entries defined statically */
+
+            for (l = b->server->config.browse_domains; l; l = l->next)
+                if (avahi_domain_equal((char*) l->text, n))
+                    return;
+        }
+
+    }
+
+    b->callback(b, interface, protocol, event, n, flags, b->userdata);
+}
+
+static void defer_callback(AvahiTimeEvent *e, void *userdata) {
+    AvahiSDomainBrowser *b = userdata;
+    AvahiStringList *l;
+
+    assert(e);
+    assert(b);
+
+    assert(b->type == AVAHI_DOMAIN_BROWSER_BROWSE);
+
+    avahi_time_event_free(b->defer_event);
+    b->defer_event = NULL;
+
+    /* Increase ref counter */
+    inc_ref(b);
+
+    for (l = b->server->config.browse_domains; l; l = l->next) {
+
+        /* Check whether this object still exists outside our own
+         * stack frame */
+        if (b->ref <= 1)
+            break;
+
+        b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_NEW, (char*) l->text, AVAHI_LOOKUP_RESULT_STATIC, b->userdata);
+    }
+
+    if (b->ref > 1) {
+        /* If the ALL_FOR_NOW event has already been scheduled, execute it now */
+
+        if (b->all_for_now_scheduled)
+            b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_ALL_FOR_NOW, NULL, 0, b->userdata);
+    }
+
+    /* Decrease ref counter */
+    avahi_s_domain_browser_free(b);
+}
+
+AvahiSDomainBrowser *avahi_s_domain_browser_prepare(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *domain,
+    AvahiDomainBrowserType type,
+    AvahiLookupFlags flags,
+    AvahiSDomainBrowserCallback callback,
+    void* userdata) {
+
+    static const char * const type_table[AVAHI_DOMAIN_BROWSER_MAX] = {
+        "b",
+        "db",
+        "r",
+        "dr",
+        "lb"
+    };
+
+    AvahiSDomainBrowser *b;
+    AvahiKey *k = NULL;
+    char n[AVAHI_DOMAIN_NAME_MAX];
+    int r;
+
+    assert(server);
+    assert(callback);
+
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DOMAIN_BROWSER_MAX, AVAHI_ERR_INVALID_FLAGS);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+
+    if (!domain)
+        domain = server->domain_name;
+
+    if ((r = avahi_service_name_join(n, sizeof(n), type_table[type], "_dns-sd._udp", domain)) < 0) {
+        avahi_server_set_errno(server, r);
+        return NULL;
+    }
+
+    if (!(b = avahi_new(AvahiSDomainBrowser, 1))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        return NULL;
+    }
+
+    b->ref = 1;
+    b->server = server;
+    b->callback = callback;
+    b->userdata = userdata;
+    b->record_browser = NULL;
+    b->type = type;
+    b->all_for_now_scheduled = 0;
+    b->defer_event = NULL;
+
+    AVAHI_LLIST_PREPEND(AvahiSDomainBrowser, browser, server->domain_browsers, b);
+
+    if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        goto fail;
+    }
+
+    if (!(b->record_browser = avahi_s_record_browser_prepare(server, interface, protocol, k, flags, record_browser_callback, b)))
+        goto fail;
+
+    avahi_key_unref(k);
+
+    if (type == AVAHI_DOMAIN_BROWSER_BROWSE && b->server->config.browse_domains)
+        b->defer_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b);
+
+    return b;
+
+fail:
+
+    if (k)
+        avahi_key_unref(k);
+
+    avahi_s_domain_browser_free(b);
+
+    return NULL;
+}
+
+void avahi_s_domain_browser_start(AvahiSDomainBrowser *b) {
+    assert(b);
+
+    if(b->record_browser)
+        avahi_s_record_browser_start_query(b->record_browser);
+}
+
+void avahi_s_domain_browser_free(AvahiSDomainBrowser *b) {
+    assert(b);
+
+    assert(b->ref >= 1);
+    if (--b->ref > 0)
+        return;
+
+    AVAHI_LLIST_REMOVE(AvahiSDomainBrowser, browser, b->server->domain_browsers, b);
+
+    if (b->record_browser)
+        avahi_s_record_browser_free(b->record_browser);
+
+    if (b->defer_event)
+        avahi_time_event_free(b->defer_event);
+
+    avahi_free(b);
+}
+
+AvahiSDomainBrowser *avahi_s_domain_browser_new(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *domain,
+    AvahiDomainBrowserType type,
+    AvahiLookupFlags flags,
+    AvahiSDomainBrowserCallback callback,
+    void* userdata) {
+        AvahiSDomainBrowser *b;
+
+        b = avahi_s_domain_browser_prepare(server, interface, protocol, domain, type, flags, callback, userdata);
+        if (!b)
+            return NULL;
+
+        avahi_s_domain_browser_start(b);
+
+        return b;
+}
Index: avahi-core/browse-service-type.c
===================================================================
--- avahi-core/browse-service-type.c	(nonexistent)
+++ avahi-core/browse-service-type.c	(revision 331)
@@ -0,0 +1,181 @@
+/***
+  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
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+
+struct AvahiSServiceTypeBrowser {
+    AvahiServer *server;
+    char *domain_name;
+
+    AvahiSRecordBrowser *record_browser;
+
+    AvahiSServiceTypeBrowserCallback callback;
+    void* userdata;
+
+    AVAHI_LLIST_FIELDS(AvahiSServiceTypeBrowser, browser);
+};
+
+static void record_browser_callback(
+    AvahiSRecordBrowser*rr,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiBrowserEvent event,
+    AvahiRecord *record,
+    AvahiLookupResultFlags flags,
+    void* userdata) {
+
+    AvahiSServiceTypeBrowser *b = userdata;
+
+    assert(rr);
+    assert(b);
+
+    /* Filter flags */
+    flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
+
+    if (record) {
+        char type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX];
+
+        assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+
+        if (avahi_service_name_split(record->data.ptr.name, NULL, 0, type, sizeof(type), domain, sizeof(domain)) < 0) {
+            avahi_log_warn("Invalid service type '%s'", record->key->name);
+            return;
+        }
+
+        b->callback(b, interface, protocol, event, type, domain, flags, b->userdata);
+    } else
+        b->callback(b, interface, protocol, event, NULL, b->domain_name, flags, b->userdata);
+}
+
+AvahiSServiceTypeBrowser *avahi_s_service_type_browser_prepare(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *domain,
+    AvahiLookupFlags flags,
+    AvahiSServiceTypeBrowserCallback callback,
+    void* userdata) {
+
+    AvahiSServiceTypeBrowser *b;
+    AvahiKey *k = NULL;
+    char n[AVAHI_DOMAIN_NAME_MAX];
+    int r;
+
+    assert(server);
+    assert(callback);
+
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+
+    if (!domain)
+        domain = server->domain_name;
+
+    if ((r = avahi_service_name_join(n, sizeof(n), NULL, "_services._dns-sd._udp", domain)) < 0) {
+        avahi_server_set_errno(server, r);
+        return NULL;
+    }
+
+    if (!(b = avahi_new(AvahiSServiceTypeBrowser, 1))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        return NULL;
+    }
+
+    b->server = server;
+    b->callback = callback;
+    b->userdata = userdata;
+    b->record_browser = NULL;
+
+    AVAHI_LLIST_PREPEND(AvahiSServiceTypeBrowser, browser, server->service_type_browsers, b);
+
+    if (!(b->domain_name = avahi_normalize_name_strdup(domain))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        goto fail;
+    }
+
+    if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        goto fail;
+    }
+
+    if (!(b->record_browser = avahi_s_record_browser_prepare(server, interface, protocol, k, flags, record_browser_callback, b)))
+        goto fail;
+
+    avahi_key_unref(k);
+
+    return b;
+
+fail:
+    if (k)
+        avahi_key_unref(k);
+
+    avahi_s_service_type_browser_free(b);
+
+    return NULL;
+}
+
+void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b) {
+    assert(b);
+
+    AVAHI_LLIST_REMOVE(AvahiSServiceTypeBrowser, browser, b->server->service_type_browsers, b);
+
+    if (b->record_browser)
+        avahi_s_record_browser_free(b->record_browser);
+
+    avahi_free(b->domain_name);
+    avahi_free(b);
+}
+
+void avahi_s_service_type_browser_start(AvahiSServiceTypeBrowser *b) {
+    assert(b);
+
+    avahi_s_record_browser_start_query(b->record_browser);
+}
+
+AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *domain,
+    AvahiLookupFlags flags,
+    AvahiSServiceTypeBrowserCallback callback,
+    void* userdata) {
+        AvahiSServiceTypeBrowser *b;
+
+        b = avahi_s_service_type_browser_prepare(server, interface, protocol, domain, flags, callback, userdata);
+        if (!b)
+            return NULL;
+
+        avahi_s_service_type_browser_start(b);
+
+        return b;
+}
+
Index: avahi-core/browse-service.c
===================================================================
--- avahi-core/browse-service.c	(nonexistent)
+++ avahi-core/browse-service.c	(revision 331)
@@ -0,0 +1,193 @@
+/***
+  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
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+
+struct AvahiSServiceBrowser {
+    AvahiServer *server;
+    char *domain_name;
+    char *service_type;
+
+    AvahiSRecordBrowser *record_browser;
+
+    AvahiSServiceBrowserCallback callback;
+    void* userdata;
+
+    AVAHI_LLIST_FIELDS(AvahiSServiceBrowser, browser);
+};
+
+static void record_browser_callback(
+    AvahiSRecordBrowser*rr,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiBrowserEvent event,
+    AvahiRecord *record,
+    AvahiLookupResultFlags flags,
+    void* userdata) {
+
+    AvahiSServiceBrowser *b = userdata;
+
+    assert(rr);
+    assert(b);
+
+    /* Filter flags */
+    flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
+
+    if (record) {
+        char service[AVAHI_LABEL_MAX], type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX];
+
+        assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+
+        if (event == AVAHI_BROWSER_NEW && avahi_server_is_service_local(b->server, interface, protocol, record->data.ptr.name))
+            flags |= AVAHI_LOOKUP_RESULT_LOCAL;
+
+        if (avahi_service_name_split(record->data.ptr.name, service, sizeof(service), type, sizeof(type), domain, sizeof(domain)) < 0) {
+            avahi_log_warn("Failed to split '%s'", record->key->name);
+            return;
+        }
+
+        b->callback(b, interface, protocol, event, service, type, domain, flags, b->userdata);
+
+    } else
+        b->callback(b, interface, protocol, event, NULL, b->service_type, b->domain_name, flags, b->userdata);
+
+}
+
+AvahiSServiceBrowser *avahi_s_service_browser_prepare(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *service_type,
+    const char *domain,
+    AvahiLookupFlags flags,
+    AvahiSServiceBrowserCallback callback,
+    void* userdata) {
+
+    AvahiSServiceBrowser *b;
+    AvahiKey *k = NULL;
+    char n[AVAHI_DOMAIN_NAME_MAX];
+    int r;
+
+    assert(server);
+    assert(callback);
+    assert(service_type);
+
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_generic(service_type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+
+    if (!domain)
+        domain = server->domain_name;
+
+    if ((r = avahi_service_name_join(n, sizeof(n), NULL, service_type, domain)) < 0) {
+        avahi_server_set_errno(server, r);
+        return NULL;
+    }
+
+    if (!(b = avahi_new(AvahiSServiceBrowser, 1))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        return NULL;
+    }
+
+    b->server = server;
+    b->domain_name = b->service_type = NULL;
+    b->callback = callback;
+    b->userdata = userdata;
+    b->record_browser = NULL;
+
+    AVAHI_LLIST_PREPEND(AvahiSServiceBrowser, browser, server->service_browsers, b);
+
+    if (!(b->domain_name = avahi_normalize_name_strdup(domain)) ||
+        !(b->service_type = avahi_normalize_name_strdup(service_type))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        goto fail;
+    }
+
+    if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        goto fail;
+    }
+
+    if (!(b->record_browser = avahi_s_record_browser_prepare(server, interface, protocol, k, flags, record_browser_callback, b)))
+        goto fail;
+
+    avahi_key_unref(k);
+
+    return b;
+
+fail:
+
+    if (k)
+        avahi_key_unref(k);
+
+    avahi_s_service_browser_free(b);
+    return NULL;
+}
+
+void avahi_s_service_browser_free(AvahiSServiceBrowser *b) {
+    assert(b);
+
+    AVAHI_LLIST_REMOVE(AvahiSServiceBrowser, browser, b->server->service_browsers, b);
+
+    if (b->record_browser)
+        avahi_s_record_browser_free(b->record_browser);
+
+    avahi_free(b->domain_name);
+    avahi_free(b->service_type);
+    avahi_free(b);
+}
+
+void avahi_s_service_browser_start(AvahiSServiceBrowser *b) {
+    assert(b);
+
+    avahi_s_record_browser_start_query(b->record_browser);
+}
+
+AvahiSServiceBrowser *avahi_s_service_browser_new(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *service_type,
+    const char *domain,
+    AvahiLookupFlags flags,
+    AvahiSServiceBrowserCallback callback,
+    void* userdata) {
+        AvahiSServiceBrowser *b;
+
+        b = avahi_s_service_browser_prepare(server, interface, protocol, service_type, domain, flags, callback, userdata);
+        if (!b)
+            return NULL;
+
+        avahi_s_service_browser_start(b);
+
+        return b;
+}
Index: avahi-core/browse.c
===================================================================
--- avahi-core/browse.c	(nonexistent)
+++ avahi-core/browse.c	(revision 331)
@@ -0,0 +1,643 @@
+/***
+  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
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/rlist.h>
+#include <avahi-common/address.h>
+
+#include "browse.h"
+#include "log.h"
+#include "querier.h"
+#include "domain-util.h"
+#include "rr-util.h"
+
+#define AVAHI_LOOKUPS_PER_BROWSER_MAX 15
+
+struct AvahiSRBLookup {
+    AvahiSRecordBrowser *record_browser;
+
+    unsigned ref;
+
+    AvahiIfIndex interface;
+    AvahiProtocol protocol;
+    AvahiLookupFlags flags;
+
+    AvahiKey *key;
+
+    AvahiWideAreaLookup *wide_area;
+    AvahiMulticastLookup *multicast;
+
+    AvahiRList *cname_lookups;
+
+    AVAHI_LLIST_FIELDS(AvahiSRBLookup, lookups);
+};
+
+static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r);
+static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r);
+
+static void transport_flags_from_domain(AvahiServer *s, AvahiLookupFlags *flags, const char *domain) {
+    assert(flags);
+    assert(domain);
+
+    assert(!((*flags & AVAHI_LOOKUP_USE_MULTICAST) && (*flags & AVAHI_LOOKUP_USE_WIDE_AREA)));
+
+    if (*flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA))
+        return;
+
+    if (!s->wide_area_lookup_engine ||
+        !avahi_wide_area_has_servers(s->wide_area_lookup_engine) ||
+        avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) ||
+        avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) ||
+        avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6))
+        *flags |= AVAHI_LOOKUP_USE_MULTICAST;
+    else
+        *flags |= AVAHI_LOOKUP_USE_WIDE_AREA;
+}
+
+static AvahiSRBLookup* lookup_new(
+    AvahiSRecordBrowser *b,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiLookupFlags flags,
+    AvahiKey *key) {
+
+    AvahiSRBLookup *l;
+
+    assert(b);
+    assert(AVAHI_IF_VALID(interface));
+    assert(AVAHI_PROTO_VALID(protocol));
+
+    if (b->n_lookups >= AVAHI_LOOKUPS_PER_BROWSER_MAX)
+        /* We don't like cyclic CNAMEs */
+        return NULL;
+
+    if (!(l = avahi_new(AvahiSRBLookup, 1)))
+        return NULL;
+
+    l->ref = 1;
+    l->record_browser = b;
+    l->interface = interface;
+    l->protocol = protocol;
+    l->key = avahi_key_ref(key);
+    l->wide_area = NULL;
+    l->multicast = NULL;
+    l->cname_lookups = NULL;
+    l->flags = flags;
+
+    transport_flags_from_domain(b->server, &l->flags, key->name);
+
+    AVAHI_LLIST_PREPEND(AvahiSRBLookup, lookups, b->lookups, l);
+
+    b->n_lookups ++;
+
+    return l;
+}
+
+static void lookup_unref(AvahiSRBLookup *l) {
+    assert(l);
+    assert(l->ref >= 1);
+
+    if (--l->ref >= 1)
+        return;
+
+    AVAHI_LLIST_REMOVE(AvahiSRBLookup, lookups, l->record_browser->lookups, l);
+    l->record_browser->n_lookups --;
+
+    if (l->wide_area) {
+        avahi_wide_area_lookup_free(l->wide_area);
+        l->wide_area = NULL;
+    }
+
+    if (l->multicast) {
+        avahi_multicast_lookup_free(l->multicast);
+        l->multicast = NULL;
+    }
+
+    while (l->cname_lookups) {
+        lookup_unref(l->cname_lookups->data);
+        l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, l->cname_lookups);
+    }
+
+    avahi_key_unref(l->key);
+    avahi_free(l);
+}
+
+static AvahiSRBLookup* lookup_ref(AvahiSRBLookup *l) {
+    assert(l);
+    assert(l->ref >= 1);
+
+    l->ref++;
+    return l;
+}
+
+static AvahiSRBLookup *lookup_find(
+    AvahiSRecordBrowser *b,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiLookupFlags flags,
+    AvahiKey *key) {
+
+    AvahiSRBLookup *l;
+
+    assert(b);
+
+    for (l = b->lookups; l; l = l->lookups_next) {
+
+        if ((l->interface == AVAHI_IF_UNSPEC || l->interface == interface) &&
+            (l->interface == AVAHI_PROTO_UNSPEC || l->protocol == protocol) &&
+            l->flags == flags &&
+            avahi_key_equal(l->key, key))
+
+            return l;
+    }
+
+    return NULL;
+}
+
+static void browser_cancel(AvahiSRecordBrowser *b) {
+    assert(b);
+
+    if (b->root_lookup) {
+        lookup_unref(b->root_lookup);
+        b->root_lookup = NULL;
+    }
+
+    if (b->defer_time_event) {
+        avahi_time_event_free(b->defer_time_event);
+        b->defer_time_event = NULL;
+    }
+}
+
+static void lookup_wide_area_callback(
+    AvahiWideAreaLookupEngine *e,
+    AvahiBrowserEvent event,
+    AvahiLookupResultFlags flags,
+    AvahiRecord *r,
+    void *userdata) {
+
+    AvahiSRBLookup *l = userdata;
+    AvahiSRecordBrowser *b;
+
+    assert(e);
+    assert(l);
+    assert(l->ref >= 1);
+
+    b = l->record_browser;
+
+    if (b->dead)
+        return;
+
+    lookup_ref(l);
+
+    switch (event) {
+        case AVAHI_BROWSER_NEW:
+            assert(r);
+
+            if (r->key->clazz == AVAHI_DNS_CLASS_IN &&
+                r->key->type == AVAHI_DNS_TYPE_CNAME)
+                /* It's a CNAME record, so let's follow it. We only follow it on wide area DNS! */
+                lookup_handle_cname(l, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_USE_WIDE_AREA, r);
+            else {
+                /* It's a normal record, so let's call the user callback */
+                assert(avahi_key_equal(r->key, l->key));
+
+                b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, r, flags, b->userdata);
+            }
+            break;
+
+        case AVAHI_BROWSER_REMOVE:
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+            /* Not defined for wide area DNS */
+            abort();
+
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+        case AVAHI_BROWSER_FAILURE:
+
+            b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata);
+            break;
+    }
+
+    lookup_unref(l);
+
+}
+
+static void lookup_multicast_callback(
+    AvahiMulticastLookupEngine *e,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiBrowserEvent event,
+    AvahiLookupResultFlags flags,
+    AvahiRecord *r,
+    void *userdata) {
+
+    AvahiSRBLookup *l = userdata;
+    AvahiSRecordBrowser *b;
+
+    assert(e);
+    assert(l);
+
+    b = l->record_browser;
+
+    if (b->dead)
+        return;
+
+    lookup_ref(l);
+
+    switch (event) {
+        case AVAHI_BROWSER_NEW:
+            assert(r);
+
+            if (r->key->clazz == AVAHI_DNS_CLASS_IN &&
+                r->key->type == AVAHI_DNS_TYPE_CNAME)
+                /* It's a CNAME record, so let's follow it. We allow browsing on both multicast and wide area. */
+                lookup_handle_cname(l, interface, protocol, b->flags, r);
+            else {
+                /* It's a normal record, so let's call the user callback */
+
+                if (avahi_server_is_record_local(b->server, interface, protocol, r))
+                    flags |= AVAHI_LOOKUP_RESULT_LOCAL;
+
+                b->callback(b, interface, protocol, event, r, flags, b->userdata);
+            }
+            break;
+
+        case AVAHI_BROWSER_REMOVE:
+            assert(r);
+
+            if (r->key->clazz == AVAHI_DNS_CLASS_IN &&
+                r->key->type == AVAHI_DNS_TYPE_CNAME)
+                /* It's a CNAME record, so let's drop that query! */
+                lookup_drop_cname(l, interface, protocol, 0, r);
+            else {
+                /* It's a normal record, so let's call the user callback */
+                assert(avahi_key_equal(b->key, l->key));
+
+                b->callback(b, interface, protocol, event, r, flags, b->userdata);
+            }
+            break;
+
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+
+            b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata);
+            break;
+
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+        case AVAHI_BROWSER_FAILURE:
+            /* Not defined for multicast DNS */
+            abort();
+
+    }
+
+    lookup_unref(l);
+}
+
+static int lookup_start(AvahiSRBLookup *l) {
+    assert(l);
+
+    assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST));
+    assert(!l->wide_area && !l->multicast);
+
+    if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) {
+
+        if (!(l->wide_area = avahi_wide_area_lookup_new(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l)))
+            return -1;
+
+    } else {
+        assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST);
+
+        if (!(l->multicast = avahi_multicast_lookup_new(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l)))
+            return -1;
+    }
+
+    return 0;
+}
+
+static int lookup_scan_cache(AvahiSRBLookup *l) {
+    int n = 0;
+
+    assert(l);
+
+    assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST));
+
+
+    if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) {
+        n = (int) avahi_wide_area_scan_cache(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l);
+
+    } else {
+        assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST);
+        n = (int) avahi_multicast_lookup_engine_scan_cache(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l);
+    }
+
+    return n;
+}
+
+static AvahiSRBLookup* lookup_add(AvahiSRecordBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiKey *key) {
+    AvahiSRBLookup *l;
+
+    assert(b);
+    assert(!b->dead);
+
+    if ((l = lookup_find(b, interface, protocol, flags, key)))
+        return lookup_ref(l);
+
+    if (!(l = lookup_new(b, interface, protocol, flags, key)))
+        return NULL;
+
+    return l;
+}
+
+static int lookup_go(AvahiSRBLookup *l) {
+    int n = 0;
+    assert(l);
+
+    if (l->record_browser->dead)
+        return 0;
+
+    lookup_ref(l);
+
+    /* Browse the cache for the root request */
+    n = lookup_scan_cache(l);
+
+    /* Start the lookup */
+    if (!l->record_browser->dead && l->ref > 1) {
+
+        if ((l->flags & AVAHI_LOOKUP_USE_MULTICAST) || n == 0)
+            /* We do no start a query if the cache contained entries and we're on wide area */
+
+            if (lookup_start(l) < 0)
+                n = -1;
+    }
+
+    lookup_unref(l);
+
+    return n;
+}
+
+static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) {
+    AvahiKey *k;
+    AvahiSRBLookup *n;
+
+    assert(l);
+    assert(r);
+
+    assert(r->key->clazz == AVAHI_DNS_CLASS_IN);
+    assert(r->key->type == AVAHI_DNS_TYPE_CNAME);
+
+    k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type);
+    n = lookup_add(l->record_browser, interface, protocol, flags, k);
+    avahi_key_unref(k);
+
+    if (!n) {
+        avahi_log_debug(__FILE__": Failed to create SRBLookup.");
+        return;
+    }
+
+    l->cname_lookups = avahi_rlist_prepend(l->cname_lookups, lookup_ref(n));
+
+    lookup_go(n);
+    lookup_unref(n);
+}
+
+static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) {
+    AvahiKey *k;
+    AvahiSRBLookup *n = NULL;
+    AvahiRList *rl;
+
+    assert(r->key->clazz == AVAHI_DNS_CLASS_IN);
+    assert(r->key->type == AVAHI_DNS_TYPE_CNAME);
+
+    k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type);
+
+    for (rl = l->cname_lookups; rl; rl = rl->rlist_next) {
+        n = rl->data;
+
+        assert(n);
+
+        if ((n->interface == AVAHI_IF_UNSPEC || n->interface == interface) &&
+            (n->interface == AVAHI_PROTO_UNSPEC || n->protocol == protocol) &&
+            n->flags == flags &&
+            avahi_key_equal(n->key, k))
+            break;
+    }
+
+    avahi_key_unref(k);
+
+    if (rl) {
+        l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, rl);
+        lookup_unref(n);
+    }
+}
+
+static void defer_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) {
+    AvahiSRecordBrowser *b = userdata;
+    int n;
+
+    assert(b);
+    assert(!b->dead);
+
+    /* Remove the defer timeout */
+    if (b->defer_time_event) {
+        avahi_time_event_free(b->defer_time_event);
+        b->defer_time_event = NULL;
+    }
+
+    /* Create initial query */
+    assert(!b->root_lookup);
+    b->root_lookup = lookup_add(b, b->interface, b->protocol, b->flags, b->key);
+    assert(b->root_lookup);
+
+    n = lookup_go(b->root_lookup);
+
+    if (b->dead)
+        return;
+
+    if (n < 0) {
+        /* sending of the initial query failed */
+
+        avahi_server_set_errno(b->server, AVAHI_ERR_FAILURE);
+
+        b->callback(
+            b, b->interface, b->protocol, AVAHI_BROWSER_FAILURE, NULL,
+            b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_MULTICAST,
+            b->userdata);
+
+        browser_cancel(b);
+        return;
+    }
+
+    /* Tell the client that we're done with the cache */
+    b->callback(
+        b, b->interface, b->protocol, AVAHI_BROWSER_CACHE_EXHAUSTED, NULL,
+        b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_MULTICAST,
+        b->userdata);
+
+    if (!b->dead && b->root_lookup && b->root_lookup->flags & AVAHI_LOOKUP_USE_WIDE_AREA && n > 0) {
+
+        /* If we do wide area lookups and the the cache contained
+         * entries, we assume that it is complete, and tell the user
+         * so by firing ALL_FOR_NOW. */
+
+        b->callback(b, b->interface, b->protocol, AVAHI_BROWSER_ALL_FOR_NOW, NULL, AVAHI_LOOKUP_RESULT_WIDE_AREA, b->userdata);
+    }
+}
+
+void avahi_s_record_browser_restart(AvahiSRecordBrowser *b) {
+    assert(b);
+    assert(!b->dead);
+
+    browser_cancel(b);
+
+    /* Request a new iteration of the cache scanning */
+    if (!b->defer_time_event) {
+        b->defer_time_event = avahi_time_event_new(b->server->time_event_queue, NULL, defer_callback, b);
+        assert(b->defer_time_event);
+    }
+}
+
+AvahiSRecordBrowser *avahi_s_record_browser_prepare(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiKey *key,
+    AvahiLookupFlags flags,
+    AvahiSRecordBrowserCallback callback,
+    void* userdata) {
+
+    AvahiSRecordBrowser *b;
+
+    assert(server);
+    assert(key);
+    assert(callback);
+
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !avahi_key_is_pattern(key), AVAHI_ERR_IS_PATTERN);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_key_is_valid(key), AVAHI_ERR_INVALID_KEY);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !(flags & AVAHI_LOOKUP_USE_WIDE_AREA) || !(flags & AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+
+    if (!(b = avahi_new(AvahiSRecordBrowser, 1))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        return NULL;
+    }
+
+    b->dead = 0;
+    b->defer_time_event = NULL;
+    b->server = server;
+    b->interface = interface;
+    b->protocol = protocol;
+    b->key = avahi_key_ref(key);
+    b->flags = flags;
+    b->callback = callback;
+    b->userdata = userdata;
+    b->n_lookups = 0;
+    AVAHI_LLIST_HEAD_INIT(AvahiSRBLookup, b->lookups);
+    b->root_lookup = NULL;
+
+    AVAHI_LLIST_PREPEND(AvahiSRecordBrowser, browser, server->record_browsers, b);
+
+    return b;
+}
+
+
+void avahi_s_record_browser_start_query(AvahiSRecordBrowser *b) {
+    assert(b);
+    assert(!b->dead);
+
+    /* If the number of lookups greater than zero, the object has already been used.
+     * To restart querying, call only avahi_s_record_browser_restart */
+    if(b->n_lookups > 0)
+        return;
+
+    /* The currently cached entries are scanned a bit later, and than we will start querying, too */
+    avahi_s_record_browser_restart(b);
+}
+
+
+void avahi_s_record_browser_free(AvahiSRecordBrowser *b) {
+    assert(b);
+    assert(!b->dead);
+
+    b->dead = 1;
+    b->server->need_browser_cleanup = 1;
+
+    browser_cancel(b);
+}
+
+void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b) {
+    assert(b);
+
+    browser_cancel(b);
+
+    AVAHI_LLIST_REMOVE(AvahiSRecordBrowser, browser, b->server->record_browsers, b);
+
+    avahi_key_unref(b->key);
+
+    avahi_free(b);
+}
+
+void avahi_browser_cleanup(AvahiServer *server) {
+    AvahiSRecordBrowser *b;
+    AvahiSRecordBrowser *n;
+
+    assert(server);
+
+    while (server->need_browser_cleanup) {
+        server->need_browser_cleanup = 0;
+
+        for (b = server->record_browsers; b; b = n) {
+            n = b->browser_next;
+
+            if (b->dead)
+                avahi_s_record_browser_destroy(b);
+        }
+    }
+
+    if (server->wide_area_lookup_engine)
+        avahi_wide_area_cleanup(server->wide_area_lookup_engine);
+    avahi_multicast_lookup_engine_cleanup(server->multicast_lookup_engine);
+}
+
+AvahiSRecordBrowser *avahi_s_record_browser_new(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiKey *key,
+    AvahiLookupFlags flags,
+    AvahiSRecordBrowserCallback callback,
+    void* userdata) {
+        AvahiSRecordBrowser *b;
+
+        b = avahi_s_record_browser_prepare(server, interface, protocol, key, flags, callback, userdata);
+        if (!b)
+            return NULL;
+
+        avahi_s_record_browser_start_query(b);
+
+        return b;
+}
Index: avahi-core/resolve-address.c
===================================================================
--- avahi-core/resolve-address.c	(nonexistent)
+++ avahi-core/resolve-address.c	(revision 331)
@@ -0,0 +1,295 @@
+/***
+  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
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+
+#include "browse.h"
+
+#define TIMEOUT_MSEC 5000
+
+struct AvahiSAddressResolver {
+    AvahiServer *server;
+    AvahiAddress address;
+
+    AvahiSRecordBrowser *record_browser;
+
+    AvahiSAddressResolverCallback callback;
+    void* userdata;
+
+    AvahiRecord *ptr_record;
+    AvahiIfIndex interface;
+    AvahiProtocol protocol;
+    AvahiLookupResultFlags flags;
+
+    int retry_with_multicast;
+    AvahiKey *key;
+
+    AvahiTimeEvent *time_event;
+
+    AVAHI_LLIST_FIELDS(AvahiSAddressResolver, resolver);
+};
+
+static void finish(AvahiSAddressResolver *r, AvahiResolverEvent event) {
+    assert(r);
+
+    if (r->time_event) {
+        avahi_time_event_free(r->time_event);
+        r->time_event = NULL;
+    }
+
+    switch (event) {
+        case AVAHI_RESOLVER_FAILURE:
+            r->callback(r, r->interface, r->protocol, event, &r->address, NULL, r->flags, r->userdata);
+            break;
+
+        case AVAHI_RESOLVER_FOUND:
+            assert(r->ptr_record);
+            r->callback(r, r->interface, r->protocol, event, &r->address, r->ptr_record->data.ptr.name, r->flags, r->userdata);
+            break;
+    }
+}
+
+static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
+    AvahiSAddressResolver *r = userdata;
+
+    assert(e);
+    assert(r);
+
+    avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT);
+    finish(r, AVAHI_RESOLVER_FAILURE);
+}
+
+static void start_timeout(AvahiSAddressResolver *r) {
+    struct timeval tv;
+    assert(r);
+
+    if (r->time_event)
+        return;
+
+    avahi_elapse_time(&tv, TIMEOUT_MSEC, 0);
+    r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r);
+}
+
+static void record_browser_callback(
+    AvahiSRecordBrowser*rr,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiBrowserEvent event,
+    AvahiRecord *record,
+    AvahiLookupResultFlags flags,
+    void* userdata) {
+
+    AvahiSAddressResolver *r = userdata;
+
+    assert(rr);
+    assert(r);
+
+    switch (event) {
+        case AVAHI_BROWSER_NEW:
+            assert(record);
+            assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+
+            if (r->interface > 0 && interface != r->interface)
+                return;
+
+            if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
+                return;
+
+            if (r->interface <= 0)
+                r->interface = interface;
+
+            if (r->protocol == AVAHI_PROTO_UNSPEC)
+                r->protocol = protocol;
+
+            if (!r->ptr_record) {
+                r->ptr_record = avahi_record_ref(record);
+                r->flags = flags;
+
+                finish(r, AVAHI_RESOLVER_FOUND);
+            }
+            break;
+
+        case AVAHI_BROWSER_REMOVE:
+            assert(record);
+            assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+
+            if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) {
+                avahi_record_unref(r->ptr_record);
+                r->ptr_record = NULL;
+                r->flags = flags;
+
+                /** Look for a replacement */
+                avahi_s_record_browser_restart(r->record_browser);
+                start_timeout(r);
+            }
+
+            break;
+
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+            break;
+
+        case AVAHI_BROWSER_FAILURE:
+
+            if (r->retry_with_multicast) {
+                r->retry_with_multicast = 0;
+
+                avahi_s_record_browser_free(r->record_browser);
+                r->record_browser = avahi_s_record_browser_prepare(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_MULTICAST, record_browser_callback, r);
+
+                if (r->record_browser) {
+                    avahi_s_record_browser_start_query(r->record_browser);
+                    start_timeout(r);
+                    break;
+                }
+            }
+
+            r->flags = flags;
+            finish(r, AVAHI_RESOLVER_FAILURE);
+            break;
+    }
+}
+
+AvahiSAddressResolver *avahi_s_address_resolver_prepare(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const AvahiAddress *address,
+    AvahiLookupFlags flags,
+    AvahiSAddressResolverCallback callback,
+    void* userdata) {
+
+    AvahiSAddressResolver *r;
+    AvahiKey *k;
+    char n[AVAHI_DOMAIN_NAME_MAX];
+
+    assert(server);
+    assert(address);
+    assert(callback);
+
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6, AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+
+    avahi_reverse_lookup_name(address, n, sizeof(n));
+
+    if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        return NULL;
+    }
+
+    if (!(r = avahi_new(AvahiSAddressResolver, 1))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        avahi_key_unref(k);
+        return NULL;
+    }
+
+    r->server = server;
+    r->address = *address;
+    r->callback = callback;
+    r->userdata = userdata;
+    r->ptr_record = NULL;
+    r->interface = interface;
+    r->protocol = protocol;
+    r->flags = 0;
+    r->retry_with_multicast = 0;
+    r->key = k;
+
+    r->record_browser = NULL;
+    AVAHI_LLIST_PREPEND(AvahiSAddressResolver, resolver, server->address_resolvers, r);
+
+    r->time_event = NULL;
+
+    if (!(flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA))) {
+
+        if (!server->wide_area_lookup_engine || !avahi_wide_area_has_servers(server->wide_area_lookup_engine))
+            flags |= AVAHI_LOOKUP_USE_MULTICAST;
+        else {
+            flags |= AVAHI_LOOKUP_USE_WIDE_AREA;
+            r->retry_with_multicast = 1;
+        }
+    }
+
+    r->record_browser = avahi_s_record_browser_prepare(server, interface, protocol, k, flags, record_browser_callback, r);
+
+    if (!r->record_browser) {
+        avahi_s_address_resolver_free(r);
+        return NULL;
+    }
+
+    start_timeout(r);
+
+    return r;
+}
+
+void avahi_s_address_resolver_start(AvahiSAddressResolver *r) {
+    assert(r);
+
+    if(r->record_browser)
+        avahi_s_record_browser_start_query(r->record_browser);
+}
+
+void avahi_s_address_resolver_free(AvahiSAddressResolver *r) {
+    assert(r);
+
+    AVAHI_LLIST_REMOVE(AvahiSAddressResolver, resolver, r->server->address_resolvers, r);
+
+    if (r->record_browser)
+        avahi_s_record_browser_free(r->record_browser);
+
+    if (r->time_event)
+        avahi_time_event_free(r->time_event);
+
+    if (r->ptr_record)
+        avahi_record_unref(r->ptr_record);
+
+    if (r->key)
+        avahi_key_unref(r->key);
+
+    avahi_free(r);
+}
+
+AvahiSAddressResolver *avahi_s_address_resolver_new(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const AvahiAddress *address,
+    AvahiLookupFlags flags,
+    AvahiSAddressResolverCallback callback,
+    void* userdata) {
+        AvahiSAddressResolver *b;
+
+        b = avahi_s_address_resolver_prepare(server, interface, protocol, address, flags, callback, userdata);
+        if (!b)
+            return NULL;
+
+        avahi_s_address_resolver_start(b);
+
+        return b;
+}
Index: avahi-core/resolve-host-name.c
===================================================================
--- avahi-core/resolve-host-name.c	(nonexistent)
+++ avahi-core/resolve-host-name.c	(revision 331)
@@ -0,0 +1,327 @@
+/***
+  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
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+
+#define TIMEOUT_MSEC 5000
+
+struct AvahiSHostNameResolver {
+    AvahiServer *server;
+    char *host_name;
+
+    AvahiSRecordBrowser *record_browser_a;
+    AvahiSRecordBrowser *record_browser_aaaa;
+
+    AvahiSHostNameResolverCallback callback;
+    void* userdata;
+
+    AvahiRecord *address_record;
+    AvahiIfIndex interface;
+    AvahiProtocol protocol;
+    AvahiLookupResultFlags flags;
+
+    AvahiTimeEvent *time_event;
+
+    AVAHI_LLIST_FIELDS(AvahiSHostNameResolver, resolver);
+};
+
+static void finish(AvahiSHostNameResolver *r, AvahiResolverEvent event) {
+    assert(r);
+
+    if (r->time_event) {
+        avahi_time_event_free(r->time_event);
+        r->time_event = NULL;
+    }
+
+    switch (event) {
+        case AVAHI_RESOLVER_FOUND: {
+            AvahiAddress a;
+
+            assert(r->address_record);
+
+            switch (r->address_record->key->type) {
+                case AVAHI_DNS_TYPE_A:
+                    a.proto = AVAHI_PROTO_INET;
+                    a.data.ipv4 = r->address_record->data.a.address;
+                    break;
+
+                case AVAHI_DNS_TYPE_AAAA:
+                    a.proto = AVAHI_PROTO_INET6;
+                    a.data.ipv6 = r->address_record->data.aaaa.address;
+                    break;
+
+                default:
+                    abort();
+            }
+
+            r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_FOUND, r->address_record->key->name, &a, r->flags, r->userdata);
+            break;
+
+        }
+
+        case AVAHI_RESOLVER_FAILURE:
+
+            r->callback(r, r->interface, r->protocol, event, r->host_name, NULL, r->flags, r->userdata);
+            break;
+    }
+}
+
+static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
+    AvahiSHostNameResolver *r = userdata;
+
+    assert(e);
+    assert(r);
+
+    avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT);
+    finish(r, AVAHI_RESOLVER_FAILURE);
+}
+
+static void start_timeout(AvahiSHostNameResolver *r) {
+    struct timeval tv;
+    assert(r);
+
+    if (r->time_event)
+        return;
+
+    avahi_elapse_time(&tv, TIMEOUT_MSEC, 0);
+
+    r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r);
+}
+
+static void record_browser_callback(
+    AvahiSRecordBrowser*rr,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiBrowserEvent event,
+    AvahiRecord *record,
+    AvahiLookupResultFlags flags,
+    void* userdata) {
+
+    AvahiSHostNameResolver *r = userdata;
+
+    assert(rr);
+    assert(r);
+
+
+    switch (event) {
+        case AVAHI_BROWSER_NEW:
+            assert(record);
+            assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA);
+
+            if (r->interface > 0 && interface != r->interface)
+                return;
+
+            if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
+                return;
+
+            if (r->interface <= 0)
+                r->interface = interface;
+
+            if (r->protocol == AVAHI_PROTO_UNSPEC)
+                r->protocol = protocol;
+
+            if (!r->address_record) {
+                r->address_record = avahi_record_ref(record);
+                r->flags = flags;
+
+                finish(r, AVAHI_RESOLVER_FOUND);
+            }
+
+            break;
+
+        case AVAHI_BROWSER_REMOVE:
+            assert(record);
+            assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA);
+
+            if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) {
+                avahi_record_unref(r->address_record);
+                r->address_record = NULL;
+
+                r->flags = flags;
+
+
+                /** Look for a replacement */
+                if (r->record_browser_aaaa)
+                    avahi_s_record_browser_restart(r->record_browser_aaaa);
+                if (r->record_browser_a)
+                    avahi_s_record_browser_restart(r->record_browser_a);
+
+                start_timeout(r);
+            }
+
+            break;
+
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+            /* Ignore */
+            break;
+
+        case AVAHI_BROWSER_FAILURE:
+
+            /* Stop browsers */
+
+            if (r->record_browser_aaaa)
+                avahi_s_record_browser_free(r->record_browser_aaaa);
+            if (r->record_browser_a)
+                avahi_s_record_browser_free(r->record_browser_a);
+
+            r->record_browser_a = r->record_browser_aaaa = NULL;
+            r->flags = flags;
+
+            finish(r, AVAHI_RESOLVER_FAILURE);
+            break;
+    }
+}
+
+AvahiSHostNameResolver *avahi_s_host_name_resolver_prepare(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *host_name,
+    AvahiProtocol aprotocol,
+    AvahiLookupFlags flags,
+    AvahiSHostNameResolverCallback callback,
+    void* userdata) {
+
+    AvahiSHostNameResolver *r;
+    AvahiKey *k;
+
+    assert(server);
+    assert(host_name);
+    assert(callback);
+
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_fqdn(host_name), AVAHI_ERR_INVALID_HOST_NAME);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+
+    if (!(r = avahi_new(AvahiSHostNameResolver, 1))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        return NULL;
+    }
+
+    r->server = server;
+    r->host_name = avahi_normalize_name_strdup(host_name);
+    r->callback = callback;
+    r->userdata = userdata;
+    r->address_record = NULL;
+    r->interface = interface;
+    r->protocol = protocol;
+    r->flags = 0;
+
+    r->record_browser_a = r->record_browser_aaaa = NULL;
+
+    r->time_event = NULL;
+
+    AVAHI_LLIST_PREPEND(AvahiSHostNameResolver, resolver, server->host_name_resolvers, r);
+
+    r->record_browser_aaaa = r->record_browser_a = NULL;
+
+    if (aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_UNSPEC) {
+        k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
+        r->record_browser_a = avahi_s_record_browser_prepare(server, interface, protocol, k, flags, record_browser_callback, r);
+        avahi_key_unref(k);
+
+        if (!r->record_browser_a)
+            goto fail;
+    }
+
+    if (aprotocol == AVAHI_PROTO_INET6 || aprotocol == AVAHI_PROTO_UNSPEC) {
+        k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
+        r->record_browser_aaaa = avahi_s_record_browser_prepare(server, interface, protocol, k, flags, record_browser_callback, r);
+        avahi_key_unref(k);
+
+        if (!r->record_browser_aaaa)
+            goto fail;
+    }
+
+    assert(r->record_browser_aaaa || r->record_browser_a);
+
+    start_timeout(r);
+
+    return r;
+
+fail:
+    avahi_s_host_name_resolver_free(r);
+    return NULL;
+}
+
+void avahi_s_host_name_resolver_start(AvahiSHostNameResolver *r) {
+    assert(r);
+
+    if(r->record_browser_a)
+        avahi_s_record_browser_start_query(r->record_browser_a);
+
+    if(r->record_browser_aaaa)
+        avahi_s_record_browser_start_query(r->record_browser_aaaa);
+}
+
+void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r) {
+    assert(r);
+
+    AVAHI_LLIST_REMOVE(AvahiSHostNameResolver, resolver, r->server->host_name_resolvers, r);
+
+    if (r->record_browser_a)
+        avahi_s_record_browser_free(r->record_browser_a);
+
+    if (r->record_browser_aaaa)
+        avahi_s_record_browser_free(r->record_browser_aaaa);
+
+    if (r->time_event)
+        avahi_time_event_free(r->time_event);
+
+    if (r->address_record)
+        avahi_record_unref(r->address_record);
+
+    avahi_free(r->host_name);
+    avahi_free(r);
+}
+
+AvahiSHostNameResolver *avahi_s_host_name_resolver_new(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *host_name,
+    AvahiProtocol aprotocol,
+    AvahiLookupFlags flags,
+    AvahiSHostNameResolverCallback callback,
+    void* userdata) {
+        AvahiSHostNameResolver *b;
+
+        b = avahi_s_host_name_resolver_prepare(server, interface, protocol, host_name, aprotocol, flags, callback, userdata);
+        if (!b)
+            return NULL;
+
+        avahi_s_host_name_resolver_start(b);
+
+        return b;
+}
Index: avahi-core/resolve-service.c
===================================================================
--- avahi-core/resolve-service.c	(nonexistent)
+++ avahi-core/resolve-service.c	(revision 331)
@@ -0,0 +1,528 @@
+/***
+  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
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+
+#define TIMEOUT_MSEC 5000
+
+struct AvahiSServiceResolver {
+    AvahiServer *server;
+    char *service_name;
+    char *service_type;
+    char *domain_name;
+    AvahiProtocol address_protocol;
+
+    AvahiIfIndex interface;
+    AvahiProtocol protocol;
+
+    AvahiSRecordBrowser *record_browser_srv;
+    AvahiSRecordBrowser *record_browser_txt;
+    AvahiSRecordBrowser *record_browser_a;
+    AvahiSRecordBrowser *record_browser_aaaa;
+
+    AvahiRecord *srv_record, *txt_record, *address_record;
+    AvahiLookupResultFlags srv_flags, txt_flags, address_flags;
+
+    AvahiSServiceResolverCallback callback;
+    void* userdata;
+    AvahiLookupFlags user_flags;
+
+    AvahiTimeEvent *time_event;
+
+    AVAHI_LLIST_FIELDS(AvahiSServiceResolver, resolver);
+};
+
+static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) {
+    AvahiLookupResultFlags flags;
+
+    assert(r);
+
+    if (r->time_event) {
+        avahi_time_event_free(r->time_event);
+        r->time_event = NULL;
+    }
+
+    flags =
+        r->txt_flags |
+        r->srv_flags |
+        r->address_flags;
+
+    switch (event) {
+        case AVAHI_RESOLVER_FAILURE:
+
+            r->callback(
+                r,
+                r->interface,
+                r->protocol,
+                event,
+                r->service_name,
+                r->service_type,
+                r->domain_name,
+                NULL,
+                NULL,
+                0,
+                NULL,
+                flags,
+                r->userdata);
+
+            break;
+
+        case AVAHI_RESOLVER_FOUND: {
+            AvahiAddress a;
+
+            assert(event == AVAHI_RESOLVER_FOUND);
+
+            assert(r->srv_record);
+
+            if (r->address_record) {
+                switch (r->address_record->key->type) {
+                    case AVAHI_DNS_TYPE_A:
+                        a.proto = AVAHI_PROTO_INET;
+                        a.data.ipv4 = r->address_record->data.a.address;
+                        break;
+
+                    case AVAHI_DNS_TYPE_AAAA:
+                        a.proto = AVAHI_PROTO_INET6;
+                        a.data.ipv6 = r->address_record->data.aaaa.address;
+                        break;
+
+                    default:
+                        assert(0);
+                }
+            }
+
+            r->callback(
+                r,
+                r->interface,
+                r->protocol,
+                event,
+                r->service_name,
+                r->service_type,
+                r->domain_name,
+                r->srv_record->data.srv.name,
+                r->address_record ? &a : NULL,
+                r->srv_record->data.srv.port,
+                r->txt_record ? r->txt_record->data.txt.string_list : NULL,
+                flags,
+                r->userdata);
+
+            break;
+        }
+    }
+}
+
+static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
+    AvahiSServiceResolver *r = userdata;
+
+    assert(e);
+    assert(r);
+
+    avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT);
+    finish(r, AVAHI_RESOLVER_FAILURE);
+}
+
+static void start_timeout(AvahiSServiceResolver *r) {
+    struct timeval tv;
+    assert(r);
+
+    if (r->time_event)
+        return;
+
+    avahi_elapse_time(&tv, TIMEOUT_MSEC, 0);
+
+    r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r);
+}
+
+static void record_browser_callback(
+    AvahiSRecordBrowser*rr,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiBrowserEvent event,
+    AvahiRecord *record,
+    AvahiLookupResultFlags flags,
+    void* userdata) {
+
+    AvahiSServiceResolver *r = userdata;
+
+    assert(rr);
+    assert(r);
+
+    if (rr == r->record_browser_aaaa || rr == r->record_browser_a)
+        r->address_flags = flags;
+    else if (rr == r->record_browser_srv)
+        r->srv_flags = flags;
+    else if (rr == r->record_browser_txt)
+        r->txt_flags = flags;
+
+    switch (event) {
+
+        case AVAHI_BROWSER_NEW: {
+            int changed = 0;
+            assert(record);
+
+            if (r->interface > 0 && interface > 0 &&  interface != r->interface)
+                return;
+
+            if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
+                return;
+
+            if (r->interface <= 0)
+                r->interface = interface;
+
+            if (r->protocol == AVAHI_PROTO_UNSPEC)
+                r->protocol = protocol;
+
+            switch (record->key->type) {
+                case AVAHI_DNS_TYPE_SRV:
+                    if (!r->srv_record) {
+                        r->srv_record = avahi_record_ref(record);
+                        changed = 1;
+
+                        if (r->record_browser_a) {
+                            avahi_s_record_browser_free(r->record_browser_a);
+                            r->record_browser_a = NULL;
+                        }
+
+                        if (r->record_browser_aaaa) {
+                            avahi_s_record_browser_free(r->record_browser_aaaa);
+                            r->record_browser_aaaa = NULL;
+                        }
+
+                        if (!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)) {
+
+                            if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) {
+                                AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
+                                r->record_browser_a = avahi_s_record_browser_prepare(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r);
+                                if(r->record_browser_a)
+                                    avahi_s_record_browser_start_query(r->record_browser_a);
+                                avahi_key_unref(k);
+                            }
+
+                            if (r->address_protocol == AVAHI_PROTO_INET6 || r->address_protocol == AVAHI_PROTO_UNSPEC) {
+                                AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
+                                r->record_browser_aaaa = avahi_s_record_browser_prepare(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r);
+                                if(r->record_browser_aaaa)
+                                    avahi_s_record_browser_start_query(r->record_browser_aaaa);
+                                avahi_key_unref(k);
+                            }
+                        }
+                    }
+                    break;
+
+                case AVAHI_DNS_TYPE_TXT:
+
+                    assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT));
+
+                    if (!r->txt_record) {
+                        r->txt_record = avahi_record_ref(record);
+                        changed = 1;
+                    }
+                    break;
+
+                case AVAHI_DNS_TYPE_A:
+                case AVAHI_DNS_TYPE_AAAA:
+
+                    assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS));
+
+                    if (!r->address_record) {
+                        r->address_record = avahi_record_ref(record);
+                        changed = 1;
+                    }
+                    break;
+
+                default:
+                    abort();
+            }
+
+
+            if (changed &&
+                r->srv_record &&
+                (r->txt_record || (r->user_flags & AVAHI_LOOKUP_NO_TXT)) &&
+                (r->address_record || (r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)))
+                finish(r, AVAHI_RESOLVER_FOUND);
+
+            break;
+
+        }
+
+        case AVAHI_BROWSER_REMOVE:
+
+            assert(record);
+
+            switch (record->key->type) {
+                case AVAHI_DNS_TYPE_SRV:
+
+                    if (r->srv_record && avahi_record_equal_no_ttl(record, r->srv_record)) {
+                        avahi_record_unref(r->srv_record);
+                        r->srv_record = NULL;
+
+                        if (r->record_browser_a) {
+                            avahi_s_record_browser_free(r->record_browser_a);
+                            r->record_browser_a = NULL;
+                        }
+
+                        if (r->record_browser_aaaa) {
+                            avahi_s_record_browser_free(r->record_browser_aaaa);
+                            r->record_browser_aaaa = NULL;
+                        }
+
+                        /** Look for a replacement */
+                        avahi_s_record_browser_restart(r->record_browser_srv);
+                        start_timeout(r);
+                    }
+
+                    break;
+
+                case AVAHI_DNS_TYPE_TXT:
+
+                    assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT));
+
+                    if (r->txt_record && avahi_record_equal_no_ttl(record, r->txt_record)) {
+                        avahi_record_unref(r->txt_record);
+                        r->txt_record = NULL;
+
+                        /** Look for a replacement */
+                        avahi_s_record_browser_restart(r->record_browser_txt);
+                        start_timeout(r);
+                    }
+                    break;
+
+                case AVAHI_DNS_TYPE_A:
+                case AVAHI_DNS_TYPE_AAAA:
+
+                    assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS));
+
+                    if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) {
+                        avahi_record_unref(r->address_record);
+                        r->address_record = NULL;
+
+                        /** Look for a replacement */
+                        if (r->record_browser_aaaa)
+                            avahi_s_record_browser_restart(r->record_browser_aaaa);
+                        if (r->record_browser_a)
+                            avahi_s_record_browser_restart(r->record_browser_a);
+                        start_timeout(r);
+                    }
+                    break;
+
+                default:
+                    abort();
+            }
+
+            break;
+
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+            break;
+
+        case AVAHI_BROWSER_FAILURE:
+
+            if (rr == r->record_browser_a && r->record_browser_aaaa) {
+                /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */
+                avahi_s_record_browser_free(r->record_browser_a);
+                r->record_browser_a = NULL;
+                break;
+            }
+
+            if (rr == r->record_browser_aaaa && r->record_browser_a) {
+                /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */
+                avahi_s_record_browser_free(r->record_browser_aaaa);
+                r->record_browser_aaaa = NULL;
+                break;
+            }
+
+            /* Hmm, everything's lost, tell the user */
+
+            if (r->record_browser_srv)
+                avahi_s_record_browser_free(r->record_browser_srv);
+            if (r->record_browser_txt)
+                avahi_s_record_browser_free(r->record_browser_txt);
+            if (r->record_browser_a)
+                avahi_s_record_browser_free(r->record_browser_a);
+            if (r->record_browser_aaaa)
+                avahi_s_record_browser_free(r->record_browser_aaaa);
+
+            r->record_browser_srv = r->record_browser_txt = r->record_browser_a = r->record_browser_aaaa = NULL;
+
+            finish(r, AVAHI_RESOLVER_FAILURE);
+            break;
+    }
+}
+
+AvahiSServiceResolver *avahi_s_service_resolver_prepare(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *name,
+    const char *type,
+    const char *domain,
+    AvahiProtocol aprotocol,
+    AvahiLookupFlags flags,
+    AvahiSServiceResolverCallback callback,
+    void* userdata) {
+
+    AvahiSServiceResolver *r;
+    AvahiKey *k;
+    char n[AVAHI_DOMAIN_NAME_MAX];
+    int ret;
+
+    assert(server);
+    assert(type);
+    assert(callback);
+
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !name || avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), AVAHI_ERR_INVALID_FLAGS);
+
+    if (!domain)
+        domain = server->domain_name;
+
+    if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain)) < 0) {
+        avahi_server_set_errno(server, ret);
+        return NULL;
+    }
+
+    if (!(r = avahi_new(AvahiSServiceResolver, 1))) {
+        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+        return NULL;
+    }
+
+    r->server = server;
+    r->service_name = avahi_strdup(name);
+    r->service_type = avahi_normalize_name_strdup(type);
+    r->domain_name = avahi_normalize_name_strdup(domain);
+    r->callback = callback;
+    r->userdata = userdata;
+    r->address_protocol = aprotocol;
+    r->srv_record = r->txt_record = r->address_record = NULL;
+    r->srv_flags = r->txt_flags = r->address_flags = 0;
+    r->interface = interface;
+    r->protocol = protocol;
+    r->user_flags = flags;
+    r->record_browser_a = r->record_browser_aaaa = r->record_browser_srv = r->record_browser_txt = NULL;
+    r->time_event = NULL;
+    AVAHI_LLIST_PREPEND(AvahiSServiceResolver, resolver, server->service_resolvers, r);
+
+    k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
+    r->record_browser_srv = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r);
+    avahi_key_unref(k);
+
+    if (!r->record_browser_srv) {
+        avahi_s_service_resolver_free(r);
+        return NULL;
+    }
+
+    if (!(flags & AVAHI_LOOKUP_NO_TXT)) {
+        k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
+        r->record_browser_txt = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS),  record_browser_callback, r);
+        avahi_key_unref(k);
+
+        if (!r->record_browser_txt) {
+            avahi_s_service_resolver_free(r);
+            return NULL;
+        }
+    }
+
+    start_timeout(r);
+
+    return r;
+}
+
+void avahi_s_service_resolver_start(AvahiSServiceResolver *r) {
+    assert(r);
+
+    if (r->record_browser_srv)
+        avahi_s_record_browser_start_query(r->record_browser_srv);
+    if (r->record_browser_txt)
+        avahi_s_record_browser_start_query(r->record_browser_txt);
+    if (r->record_browser_a)
+        avahi_s_record_browser_start_query(r->record_browser_a);
+    if (r->record_browser_aaaa)
+        avahi_s_record_browser_start_query(r->record_browser_aaaa);
+}
+
+void avahi_s_service_resolver_free(AvahiSServiceResolver *r) {
+    assert(r);
+
+    AVAHI_LLIST_REMOVE(AvahiSServiceResolver, resolver, r->server->service_resolvers, r);
+
+    if (r->time_event)
+        avahi_time_event_free(r->time_event);
+
+    if (r->record_browser_srv)
+        avahi_s_record_browser_free(r->record_browser_srv);
+    if (r->record_browser_txt)
+        avahi_s_record_browser_free(r->record_browser_txt);
+    if (r->record_browser_a)
+        avahi_s_record_browser_free(r->record_browser_a);
+    if (r->record_browser_aaaa)
+        avahi_s_record_browser_free(r->record_browser_aaaa);
+
+    if (r->srv_record)
+        avahi_record_unref(r->srv_record);
+    if (r->txt_record)
+        avahi_record_unref(r->txt_record);
+    if (r->address_record)
+        avahi_record_unref(r->address_record);
+
+    avahi_free(r->service_name);
+    avahi_free(r->service_type);
+    avahi_free(r->domain_name);
+    avahi_free(r);
+}
+
+AvahiSServiceResolver *avahi_s_service_resolver_new(
+    AvahiServer *server,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    const char *name,
+    const char *type,
+    const char *domain,
+    AvahiProtocol aprotocol,
+    AvahiLookupFlags flags,
+    AvahiSServiceResolverCallback callback,
+    void* userdata) {
+        AvahiSServiceResolver *b;
+
+        b = avahi_s_service_resolver_prepare(server, interface, protocol, name, type, domain, aprotocol, flags, callback, userdata);
+        if (!b)
+            return NULL;
+
+        avahi_s_service_resolver_start(b);
+
+        return b;
+}